caspian-utils 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/ai-validation-checklist.md +1 -0
- package/dist/docs/commands.md +6 -3
- package/dist/docs/core-runtime-map.md +5 -2
- package/dist/docs/fetch-data.md +4 -1
- package/dist/docs/index.md +17 -13
- package/dist/docs/project-structure.md +11 -8
- package/dist/docs/websockets.md +165 -0
- package/package.json +1 -1
|
@@ -64,6 +64,7 @@ Use prompts like these to check whether AI lands on the correct docs and files.
|
|
|
64
64
|
| Decide whether a dashboard child route should use page caching. | [index.md](./index.md), [cache.md](./cache.md), [routing.md](./routing.md), [fetch-data.md](./fetch-data.md) | `main.py`, `.venv/Lib/site-packages/casp/cache_handler.py`, the route's `index.py` | route-level cache ownership, cacheable HTML scope, and invalidation points |
|
|
65
65
|
| Add a PulsePoint context provider to a reusable component. | [index.md](./index.md), [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md) | `ts/TemplateCompiler.ts`, `public/js/pp-reactive-v2.js`, component `.html`, `.venv/Lib/site-packages/casp/components_compiler.py` | lowercase HTML-first `*.provider` usage, logical ancestry, single-root template shape |
|
|
66
66
|
| Add upload progress to an existing file manager. | [index.md](./index.md), [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md), [file-uploads.md](./file-uploads.md), [fetch-data.md](./fetch-data.md) | `src/app/**/index.py`, `src/app/**/index.html`, `src/lib/**`, `public/js/pp-reactive-v2.js` | `pp.rpc` upload options, state replacement, persisted metadata, BrowserSync ignore rules |
|
|
67
|
+
| Add or debug a live WebSocket channel. | [index.md](./index.md), [websockets.md](./websockets.md), [core-runtime-map.md](./core-runtime-map.md), [pulsepoint.md](./pulsepoint.md), [auth.md](./auth.md) | `caspian.config.json`, `main.py`, `src/lib/websocket/**`, `src/app/**`, `settings/bs-config.json` | `websocket` feature gate, endpoint registration, origin checks, auth/session handling, native `WebSocket` client lifecycle, and BrowserSync URL selection |
|
|
67
68
|
| Decide whether MCP files should be created. | [index.md](./index.md), [mcp.md](./mcp.md), [commands.md](./commands.md), [project-structure.md](./project-structure.md) | `caspian.config.json`, `settings/restart-mcp.ts`, `package.json`, `src/lib/mcp/**` when enabled | `mcp` feature gate, update workflow, nested FastMCP config ownership |
|
|
68
69
|
|
|
69
70
|
Treat the table as a prompt pack for spot checks, not as a full validation matrix.
|
package/dist/docs/commands.md
CHANGED
|
@@ -179,9 +179,12 @@ In skip-prompt mode, the default feature values are:
|
|
|
179
179
|
|
|
180
180
|
- `backendOnly: false`
|
|
181
181
|
- `tailwindcss: false`
|
|
182
|
-
- `typescript: false`
|
|
183
|
-
- `mcp: false`
|
|
184
|
-
- `prisma: false`
|
|
182
|
+
- `typescript: false`
|
|
183
|
+
- `mcp: false`
|
|
184
|
+
- `prisma: false`
|
|
185
|
+
- `websocket: false`
|
|
186
|
+
|
|
187
|
+
If a project needs WebSockets after creation, set `websocket: true` in `caspian.config.json`, then run the project update workflow so framework-managed files and any scaffolded socket surfaces stay aligned before assuming `@app.websocket(...)` routes or `src/lib/websocket/**` exist.
|
|
185
188
|
|
|
186
189
|
### 4.2 Backend-only combinations
|
|
187
190
|
|
|
@@ -7,8 +7,9 @@ related:
|
|
|
7
7
|
links:
|
|
8
8
|
- /docs/project-structure
|
|
9
9
|
- /docs/routing
|
|
10
|
-
- /docs/auth
|
|
10
|
+
- /docs/auth
|
|
11
11
|
- /docs/fetch-data
|
|
12
|
+
- /docs/websockets
|
|
12
13
|
- /docs/pulsepoint
|
|
13
14
|
- /docs/pulsepoint-runtime-map
|
|
14
15
|
- /docs/index
|
|
@@ -59,6 +60,7 @@ Use this table when the task names a framework feature but the owning file is no
|
|
|
59
60
|
| Baseline response headers and safe public-file serving | `main.py` imports from `casp.runtime_security` | `.venv/Lib/site-packages/casp/runtime_security.py` | [auth.md](./auth.md), [project-structure.md](./project-structure.md) |
|
|
60
61
|
| RPC and server actions | route or component Python modules with `@rpc()` | `casp.rpc`, `main.py` middleware | [fetch-data.md](./fetch-data.md), [file-uploads.md](./file-uploads.md) |
|
|
61
62
|
| Streaming | route `page()` generators, RPC generators | `casp.streaming`, `casp.rpc`, `main.py` | [fetch-data.md](./fetch-data.md) |
|
|
63
|
+
| WebSockets | `main.py` `@app.websocket(...)` endpoints when `caspian.config.json` has `websocket: true`, `src/lib/**` socket helpers, route-owned browser clients in `src/app/**` | FastAPI app-owned ASGI endpoints in `main.py` | [websockets.md](./websockets.md), [auth.md](./auth.md), [pulsepoint.md](./pulsepoint.md) |
|
|
62
64
|
| Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
|
|
63
65
|
| Page caching | route-level `Cache(...)` declarations | `casp.cache_handler`, `main.py` cache check/save | [cache.md](./cache.md) |
|
|
64
66
|
| Validation | route and RPC input boundaries | `casp.validate` | [validation.md](./validation.md) |
|
|
@@ -111,7 +113,8 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
|
|
|
111
113
|
| Runtime area | Verify these behaviors |
|
|
112
114
|
| --- | --- |
|
|
113
115
|
| `main.py` routing and request flow | route registration, path and query injection, static asset handling, session-middleware wiring, response-header middleware, and exception rendering |
|
|
114
|
-
| `
|
|
116
|
+
| `main.py` WebSockets | `cfg.websocket` route-registration gate, `@app.websocket(...)` paths, origin checks before `accept()`, auth/session extraction, message-size and idle-timeout handling, close codes, and connection cleanup |
|
|
117
|
+
| `public/js/pp-reactive-v2.js` browser runtime | SPA interception, history-vs-push scroll behavior, `pp-reset-scroll` semantics, and browser-side `pp.rpc()` behavior |
|
|
115
118
|
| `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
|
|
116
119
|
| `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
|
|
117
120
|
| `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
|
package/dist/docs/fetch-data.md
CHANGED
|
@@ -23,6 +23,8 @@ This page explains how data fetching works in Caspian. Use route functions for i
|
|
|
23
23
|
|
|
24
24
|
Treat RPC as the default way for browser code to talk to Python in Caspian. For CRUD operations and any browser-initiated backend reads after first render, default to `@rpc()` on the server and `pp.rpc()` in PulsePoint code. Do not reach for ad hoc fetch calls to custom JSON endpoints, alternate transport layers, or older helper names unless the task explicitly requires that shape.
|
|
25
25
|
|
|
26
|
+
WebSockets are the main exception for long-lived bidirectional live channels when `caspian.config.json` has `websocket: true`. If the task needs a native browser `WebSocket`, persistent broadcast channel, presence, live chat, or socket origin/auth handling, confirm that flag and read [websockets.md](./websockets.md) before adding endpoint or client code. Do not use WebSockets as a replacement for ordinary RPC form submits or CRUD work.
|
|
27
|
+
|
|
26
28
|
When `caspian.config.json` has `prisma: true`, the Python side of those route and RPC actions must use the generated Prisma Python ORM from `src/lib/prisma/**` for database reads and writes. The browser asks Python through `pp.rpc(...)`; Python asks the database through Prisma. Do not replace either side with a custom browser fetch, direct database driver, hand-written SQL helper, JSON active store, or second app-owned data-fetch abstraction.
|
|
27
29
|
|
|
28
30
|
Browser-triggered data work should still be PulsePoint-first at the event layer. Bind the initiating click, submit, input, upload, refresh, filter, or pagination control in authored HTML with `onclick`, `onsubmit`, `oninput`, `onchange`, or another native `on*` attribute handled by PulsePoint. For normal form submissions, read named controls with `Object.fromEntries(new FormData(event.currentTarget).entries())` in the submit handler and pass that object directly to `pp.rpc(...)`; the route's Python `@rpc()` action should validate, normalize, and decide what to persist. Do not set up first-party data actions by assigning ids and then wiring `querySelector(...)`, `addEventListener(...)`, manual `fetch(...)`, manual DOM repainting, or per-input `pp-ref` payload collection.
|
|
@@ -184,7 +186,8 @@ Important:
|
|
|
184
186
|
|
|
185
187
|
- `pp.rpc()` posts to the current route RPC bridge.
|
|
186
188
|
- Older docs may refer to `pp.fetchFunction()`. In this repo's current runtime, the supported helper is `pp.rpc()`.
|
|
187
|
-
- Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when debugging browser-side RPC options such as streaming, upload progress, abort behavior, redirects, or SPA interaction.
|
|
189
|
+
- Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when debugging browser-side RPC options such as streaming, upload progress, abort behavior, redirects, or SPA interaction.
|
|
190
|
+
- Use [websockets.md](./websockets.md) when `caspian.config.json` has `websocket: true` and the browser and server need a persistent bidirectional channel instead of request/response RPC or one-way SSE streaming.
|
|
188
191
|
|
|
189
192
|
## Streaming Responses
|
|
190
193
|
|
package/dist/docs/index.md
CHANGED
|
@@ -11,8 +11,9 @@ related:
|
|
|
11
11
|
- /docs/core-runtime-map
|
|
12
12
|
- /docs/pulsepoint-runtime-map
|
|
13
13
|
- /docs/mcp
|
|
14
|
-
- /docs/file-uploads
|
|
15
|
-
- /docs/
|
|
14
|
+
- /docs/file-uploads
|
|
15
|
+
- /docs/websockets
|
|
16
|
+
- /docs/file-conventions
|
|
16
17
|
- /docs/project-structure
|
|
17
18
|
- /docs/components
|
|
18
19
|
---
|
|
@@ -23,7 +24,7 @@ Treat these docs as reusable Caspian feature guidance. Treat `./caspian.config.j
|
|
|
23
24
|
|
|
24
25
|
The docs can mention optional features even when those features are disabled in a project. Their job is to explain how a feature works, when a doc applies, and which files to inspect next once the feature is confirmed as relevant.
|
|
25
26
|
|
|
26
|
-
Before making feature, tooling, or file-placement decisions in a Caspian project, read `./caspian.config.json` almost immediately. That file tells you which optional capabilities are enabled, such as Prisma, MCP, TypeScript, Tailwind, backend-only mode, and component scan directories.
|
|
27
|
+
Before making feature, tooling, or file-placement decisions in a Caspian project, read `./caspian.config.json` almost immediately. That file tells you which optional capabilities are enabled, such as Prisma, MCP, WebSockets, TypeScript, Tailwind, backend-only mode, and component scan directories.
|
|
27
28
|
|
|
28
29
|
## Default Stack
|
|
29
30
|
|
|
@@ -34,6 +35,7 @@ When generating or editing a Caspian app, treat these as the default choices unl
|
|
|
34
35
|
- Treat every authored route, layout, and component HTML file like a React component return value: exactly one top-level parent HTML element or one imported `x-*` root, with any owned plain `<script>` kept inside that same root.
|
|
35
36
|
- When `caspian.config.json` has `tailwindcss: true`, use Python `merge_classes(...)` plus browser `twMerge(...)` as the only supported Tailwind class-merging path.
|
|
36
37
|
- Use `@rpc()` plus `pp.rpc()` for browser-triggered reads, writes, streaming, and uploads.
|
|
38
|
+
- When `caspian.config.json` has `websocket: true`, use app-owned FastAPI WebSocket endpoints only for long-lived bidirectional live channels; keep normal browser-triggered data work on RPC.
|
|
37
39
|
- When `caspian.config.json` has `prisma: true`, use the generated Prisma Python ORM from `src/lib/prisma/**` for Python-side database reads and writes. Do not invent a second fetch layer, raw driver wrapper, JSON active store, or browser-side data path that bypasses Prisma.
|
|
38
40
|
- Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
|
|
39
41
|
|
|
@@ -83,8 +85,9 @@ The packaged Caspian docs referenced by this index live here:
|
|
|
83
85
|
- `file-conventions.md` - quick decision guide for `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, and `error.html`, plus the owning runtime files to verify
|
|
84
86
|
- `components.md` - Create reusable Python components, template-backed UI, HTML-first `x-*` component tags, the single-parent authored-root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
|
|
85
87
|
- `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, first-party `on*` events, state, effects, directives, SPA navigation scroll restoration, `pp-reset-scroll`, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
|
|
86
|
-
- `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
|
|
87
|
-
- `
|
|
88
|
+
- `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
|
|
89
|
+
- `websockets.md` - WebSocket feature-gate guidance for projects where `caspian.config.json` has `websocket: true`, app-owned FastAPI endpoint placement, browser `WebSocket` clients inside PulsePoint templates, origin checks, auth/session handling, and when to choose sockets over RPC or SSE
|
|
90
|
+
- `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
|
|
88
91
|
- `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
|
|
89
92
|
- `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
|
|
90
93
|
- `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
|
|
@@ -103,14 +106,15 @@ Preferred lookup order:
|
|
|
103
106
|
3. Before authoring or editing any `src/app/**` or component HTML template, apply the single-root invariant: one authored root only, any owned `<script>` inside that root, and no handwritten `pp-component` or `type="text/pp"`.
|
|
104
107
|
4. Before inventing browser JavaScript, check whether the interaction is first-party UI behavior. If it is, use PulsePoint `on*` attributes, `pp.state`, refs, effects, directives, and `pp.rpc()` rather than id-driven DOM scripting.
|
|
105
108
|
5. When Prisma is enabled, route all Python database work through the generated Prisma Python ORM. Use `database.md` before schema, migration, seed, or ORM decisions, and use `fetch-data.md` before wiring route data or RPC flows.
|
|
106
|
-
6.
|
|
107
|
-
7.
|
|
108
|
-
8. If the task
|
|
109
|
-
9.
|
|
110
|
-
10.
|
|
111
|
-
11.
|
|
112
|
-
12.
|
|
113
|
-
13.
|
|
109
|
+
6. If the task asks for WebSockets, live bidirectional channels, socket origin checks, or native browser `WebSocket` clients, confirm `caspian.config.json` has `websocket: true`, read `websockets.md`, then inspect `main.py`, `src/lib/**`, the owning `src/app/**` route, and `settings/bs-config.json`. If `websocket` is false and the user wants it, ask first, then enable the config flag and follow the update workflow.
|
|
110
|
+
7. Treat `caspian.config.json` as the single source of truth for optional features. Use feature-specific docs only when the matching flag is enabled. If a feature is disabled and the user wants it, ask first, then update `caspian.config.json` and follow the update workflow in `commands.md`.
|
|
111
|
+
8. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read `core-runtime-map.md` to jump to the controlling runtime file and the matching feature doc.
|
|
112
|
+
9. If the task names a PulsePoint feature or directive, read `pulsepoint-runtime-map.md` for the fastest feature-to-runtime lookup, then read `pulsepoint.md` for authoring rules.
|
|
113
|
+
10. After the feature is confirmed, inspect the actual project files that decide behavior, such as `package.json`, `main.py`, `src/app/**`, `src/lib/**`, `settings/**`, `prisma/**`, and the installed `casp` runtime.
|
|
114
|
+
11. Use `file-conventions.md` for quick decisions about `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, and `error.html`; use `commands.md` for scaffold and update workflows, `project-structure.md` for placement decisions, and the feature docs such as `mcp.md`, `database.md`, `auth.md`, `fetch-data.md`, `websockets.md`, and `file-uploads.md` for task-specific guidance.
|
|
115
|
+
12. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
|
|
116
|
+
13. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
|
|
117
|
+
14. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
|
|
114
118
|
|
|
115
119
|
## Maintenance
|
|
116
120
|
|
|
@@ -31,7 +31,7 @@ In that layout, the default stack is Python components for reusable UI, PulsePoi
|
|
|
31
31
|
|
|
32
32
|
For public pages that can safely reuse rendered HTML, Caspian also supports route-level page caching through `casp.cache_handler`.
|
|
33
33
|
|
|
34
|
-
Before an AI agent decides which Caspian features are available in a workspace, it should read `./caspian.config.json` almost immediately. That file is the feature gate for project capabilities such as `backendOnly`, `tailwindcss`, `mcp`, `prisma`, `typescript`, and `componentScanDirs`.
|
|
34
|
+
Before an AI agent decides which Caspian features are available in a workspace, it should read `./caspian.config.json` almost immediately. That file is the feature gate for project capabilities such as `backendOnly`, `tailwindcss`, `mcp`, `prisma`, `typescript`, `websocket`, and `componentScanDirs`.
|
|
35
35
|
|
|
36
36
|
Treat `caspian.config.json` as the single source of truth for optional feature enablement. Use feature-specific files and docs only after the matching flag is confirmed as enabled. If a feature is disabled and the user wants it, ask first, then update `caspian.config.json` and follow the update workflow in `commands.md`.
|
|
37
37
|
|
|
@@ -40,9 +40,10 @@ Treat `caspian.config.json` as the single source of truth for optional feature e
|
|
|
40
40
|
- `src/` contains routes, page templates, styles, reusable components, and shared libraries.
|
|
41
41
|
- `src/components/` contains reusable application UI components and optional same-name HTML templates.
|
|
42
42
|
- `src/lib/` contains reusable non-UI code such as helpers, services, validators, adapters, and shared support modules.
|
|
43
|
-
- `src/lib/auth/auth_config.py` contains auth-specific configuration for the app.
|
|
44
|
-
- `src/lib/mcp/` contains the app-owned FastMCP server and nested FastMCP config when MCP is enabled.
|
|
45
|
-
- `
|
|
43
|
+
- `src/lib/auth/auth_config.py` contains auth-specific configuration for the app.
|
|
44
|
+
- `src/lib/mcp/` contains the app-owned FastMCP server and nested FastMCP config when MCP is enabled.
|
|
45
|
+
- `src/lib/websocket/` contains reusable socket helpers when WebSockets are enabled and the project includes shared session, auth, connection, or broadcast utilities.
|
|
46
|
+
- `prisma/` contains the Prisma schema and seed scripts.
|
|
46
47
|
- `public/` contains static assets served directly.
|
|
47
48
|
- `settings/` contains BrowserSync, build, restart, and generated-project helper files.
|
|
48
49
|
- `main.py` is the application entry point.
|
|
@@ -77,9 +78,11 @@ my-app/
|
|
|
77
78
|
lib/
|
|
78
79
|
auth/
|
|
79
80
|
auth_config.py
|
|
80
|
-
mcp/
|
|
81
|
-
fastmcp.json
|
|
82
|
-
mcp_server.py
|
|
81
|
+
mcp/
|
|
82
|
+
fastmcp.json
|
|
83
|
+
mcp_server.py
|
|
84
|
+
websocket/
|
|
85
|
+
websocket_security.py
|
|
83
86
|
prisma/
|
|
84
87
|
__init__.py
|
|
85
88
|
db.py
|
|
@@ -94,7 +97,7 @@ my-app/
|
|
|
94
97
|
docs/
|
|
95
98
|
```
|
|
96
99
|
|
|
97
|
-
Optional directories such as `src/lib/mcp/` appear only when the relevant feature flag is enabled in `caspian.config.json
|
|
100
|
+
Optional directories such as `src/lib/mcp/` and `src/lib/websocket/` appear only when the relevant feature flag is enabled in `caspian.config.json` and the project needs that app-owned surface.
|
|
98
101
|
|
|
99
102
|
## Directory Breakdown
|
|
100
103
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WebSockets
|
|
3
|
+
description: Use this page when the task mentions FastAPI WebSocket endpoints, live channels, broadcast managers, browser `WebSocket`, origin checks, or authenticated socket sessions in Caspian.
|
|
4
|
+
related:
|
|
5
|
+
title: Related docs
|
|
6
|
+
description: Use routing and PulsePoint for client placement, auth for session policy, and the core runtime map for endpoint ownership in `main.py`.
|
|
7
|
+
links:
|
|
8
|
+
- /docs/routing
|
|
9
|
+
- /docs/pulsepoint
|
|
10
|
+
- /docs/auth
|
|
11
|
+
- /docs/fetch-data
|
|
12
|
+
- /docs/core-runtime-map
|
|
13
|
+
- /docs/index
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
WebSockets in Caspian are gated by `caspian.config.json` and implemented as app-owned FastAPI endpoints, not the default RPC data path.
|
|
17
|
+
|
|
18
|
+
Use `@rpc()` plus `pp.rpc()` for ordinary browser-triggered reads, writes, uploads, and Server-Sent Event streams. Use WebSockets only when the task needs a bidirectional, long-lived channel such as live chat, collaboration, multiplayer state, server push that is not a good SSE fit, or presence.
|
|
19
|
+
|
|
20
|
+
Treat `caspian.config.json` as the single source of truth for whether the WebSocket feature is enabled. If `websocket` is false, this page is reference material only; do not assume `@app.websocket(...)` endpoints, `src/lib/websocket/**`, or socket demo routes exist until the user chooses to enable the feature and the update workflow has run.
|
|
21
|
+
|
|
22
|
+
## Source Of Truth
|
|
23
|
+
|
|
24
|
+
Before adding, debugging, or documenting a WebSocket feature:
|
|
25
|
+
|
|
26
|
+
- Read `caspian.config.json` first and confirm `websocket: true`.
|
|
27
|
+
- If `websocket` is false and the user wants live channels, ask for confirmation before enabling it, then update the config and follow the project update workflow.
|
|
28
|
+
- Inspect `main.py` for `@app.websocket(...)` routes, origin checks, close codes, message limits, idle timeout, and middleware interactions.
|
|
29
|
+
- Inspect `src/lib/**` for app-owned socket helpers such as session extraction, auth payload checks, connection managers, or broadcast utilities.
|
|
30
|
+
- Inspect the owning route under `src/app/**` for the browser client, first-render URL selection, and PulsePoint state around the native `WebSocket`.
|
|
31
|
+
- Check `settings/bs-config.json` before testing development URLs through BrowserSync or a proxy.
|
|
32
|
+
|
|
33
|
+
Packaged docs should describe this workflow generally. Keep endpoint names, route names, ports, and demo inventory project-owned; do not treat one app's route tree as the framework convention.
|
|
34
|
+
|
|
35
|
+
## Recommended Shape
|
|
36
|
+
|
|
37
|
+
When `caspian.config.json` has `websocket: true`, put socket endpoints in `main.py` when they are application-level ASGI routes:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from fastapi import WebSocket, WebSocketDisconnect, status
|
|
41
|
+
|
|
42
|
+
WEBSOCKET_PATH = "/ws/channel"
|
|
43
|
+
|
|
44
|
+
@app.websocket(WEBSOCKET_PATH)
|
|
45
|
+
async def websocket_live_endpoint(websocket: WebSocket):
|
|
46
|
+
if not is_origin_allowed(websocket):
|
|
47
|
+
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
await manager.connect(websocket)
|
|
51
|
+
try:
|
|
52
|
+
while True:
|
|
53
|
+
raw_message = await websocket.receive_text()
|
|
54
|
+
...
|
|
55
|
+
except WebSocketDisconnect:
|
|
56
|
+
return
|
|
57
|
+
finally:
|
|
58
|
+
manager.disconnect(websocket)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Keep reusable socket helpers in `src/lib/**` when they are shared by more than one endpoint or route. Good candidates include connection managers, session helpers, auth-payload extraction, payload normalization, and shared broadcast code.
|
|
62
|
+
|
|
63
|
+
Keep route-specific browser UI in `src/app/**/index.html` and route-specific first-render values in the matching `index.py`. For example, an `index.py` can pass `websocket_path` and a development-only BrowserSync-aware `websocket_url` into the template with `render_page(__file__, {...})`.
|
|
64
|
+
|
|
65
|
+
## Browser Client Pattern
|
|
66
|
+
|
|
67
|
+
Use PulsePoint for the component state and lifecycle, while using the native browser `WebSocket` object for the socket itself.
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<main>
|
|
71
|
+
<button onclick="{connectSocket()}" disabled="{isConnected}">Connect</button>
|
|
72
|
+
<button onclick="{disconnectSocket()}" disabled="{!isConnected}">Disconnect</button>
|
|
73
|
+
|
|
74
|
+
<script>
|
|
75
|
+
const websocketPath = "{{ websocket_path }}";
|
|
76
|
+
const socketRef = pp.ref(null);
|
|
77
|
+
const [status, setStatus] = pp.state("idle");
|
|
78
|
+
const isConnected = status === "connected";
|
|
79
|
+
|
|
80
|
+
function buildWebSocketUrl(path) {
|
|
81
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
82
|
+
return new URL(`${protocol}//${window.location.host}${path}`, window.location.href).toString();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function connectSocket() {
|
|
86
|
+
const socket = new WebSocket(buildWebSocketUrl(websocketPath));
|
|
87
|
+
socketRef.current = socket;
|
|
88
|
+
|
|
89
|
+
socket.addEventListener("message", (event) => {
|
|
90
|
+
const payload = JSON.parse(event.data);
|
|
91
|
+
...
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function disconnectSocket() {
|
|
96
|
+
socketRef.current?.close(1000, "Client disconnect");
|
|
97
|
+
socketRef.current = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
pp.effect(() => {
|
|
101
|
+
return () => socketRef.current?.close(1000, "Page disposed");
|
|
102
|
+
}, []);
|
|
103
|
+
</script>
|
|
104
|
+
</main>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
This is one of the narrow cases where direct browser APIs and event listeners are expected. Keep them inside the owning PulsePoint component script, store the socket in `pp.ref(...)`, and keep rendered UI state in `pp.state(...)`.
|
|
108
|
+
|
|
109
|
+
## Auth And Sessions
|
|
110
|
+
|
|
111
|
+
HTTP route privacy does not automatically make a WebSocket endpoint private. If a socket is authenticated, the endpoint must explicitly inspect the session or auth payload available on the WebSocket scope and close unauthenticated clients.
|
|
112
|
+
|
|
113
|
+
Use close code `1008` for policy violations such as failed origin checks or missing authentication. If the endpoint accepts before rejecting, send a small JSON error payload first so the browser UI can show a useful message.
|
|
114
|
+
|
|
115
|
+
If the app refreshes auth sessions, verify whether the WebSocket helper refreshes session expiry and whether that session mutation is persisted in the current middleware stack. Do not assume HTTP `AuthMiddleware` behavior applies to `scope["type"] == "websocket"`.
|
|
116
|
+
|
|
117
|
+
## Origin And Proxy Checks
|
|
118
|
+
|
|
119
|
+
WebSocket endpoints should validate the `Origin` header before accepting connections, especially in production.
|
|
120
|
+
|
|
121
|
+
Common allowed origins:
|
|
122
|
+
|
|
123
|
+
- same-origin derived from the socket URL
|
|
124
|
+
- explicit values from environment variables such as `WEBSOCKET_ALLOWED_ORIGINS`, `CORS_ALLOWED_ORIGINS`, `APP_BASE_URL`, `PUBLIC_BASE_URL`, or `SITE_URL`
|
|
125
|
+
- localhost HTTP origins in development when the app intentionally permits local BrowserSync or proxy testing
|
|
126
|
+
|
|
127
|
+
When testing locally, read `settings/bs-config.json` first and build `ws://` or `wss://` URLs from its active `local` value. Do not assume the BrowserSync port is the default.
|
|
128
|
+
|
|
129
|
+
## Message Contract
|
|
130
|
+
|
|
131
|
+
Document the JSON message contract near the endpoint and client that own it.
|
|
132
|
+
|
|
133
|
+
Useful baseline conventions:
|
|
134
|
+
|
|
135
|
+
- Clients send JSON objects, usually `{ "type": "message", "text": "..." }` or `{ "type": "ping" }`.
|
|
136
|
+
- Servers send `ready`, `message`, `error`, and `pong` payloads as JSON.
|
|
137
|
+
- Enforce a maximum incoming message size before parsing or broadcasting.
|
|
138
|
+
- Close idle sockets after a configured timeout when the app does not need long silent connections.
|
|
139
|
+
- Sanitize or trim outbound text so one client cannot broadcast unbounded data.
|
|
140
|
+
- Disconnect stale sockets when sends fail.
|
|
141
|
+
|
|
142
|
+
## WebSockets Vs RPC/SSE
|
|
143
|
+
|
|
144
|
+
Choose the transport deliberately:
|
|
145
|
+
|
|
146
|
+
| Need | Preferred transport |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| CRUD, form submits, button actions, upload progress | `@rpc()` plus `pp.rpc()` |
|
|
149
|
+
| Server sends one-way progress chunks for a request | RPC streaming / SSE |
|
|
150
|
+
| Browser and server both send messages over a persistent channel | WebSocket |
|
|
151
|
+
| Presence, collaboration, live chat, multiplayer state | WebSocket |
|
|
152
|
+
|
|
153
|
+
Do not replace normal Caspian RPC with WebSockets just because a page is interactive.
|
|
154
|
+
|
|
155
|
+
## AI Retrieval Notes
|
|
156
|
+
|
|
157
|
+
If an AI agent is working on WebSockets in a Caspian app:
|
|
158
|
+
|
|
159
|
+
- Start with `caspian.config.json`. Continue only after confirming `websocket: true`, unless the task is specifically to enable the feature.
|
|
160
|
+
- Use [core-runtime-map.md](./core-runtime-map.md) to connect `main.py` behavior back to this doc.
|
|
161
|
+
- Use [routing.md](./routing.md) before changing the route files that render the client.
|
|
162
|
+
- Use [pulsepoint.md](./pulsepoint.md) for authored template rules and component lifecycle cleanup.
|
|
163
|
+
- Use [auth.md](./auth.md) before changing authenticated socket behavior.
|
|
164
|
+
- Keep reusable connection/session helpers in `src/lib/websocket/**` when they are shared; keep route UI in whichever `src/app/**` route owns the live experience; keep app-level ASGI endpoints in `main.py` unless the runtime intentionally provides another registration layer.
|
|
165
|
+
- Verify local test URLs from `settings/bs-config.json`.
|