caspian-utils 0.1.4 → 0.1.6
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/auth.md +47 -20
- package/dist/docs/components.md +100 -16
- package/dist/docs/core-runtime-map.md +73 -73
- package/dist/docs/fetch-data.md +102 -64
- package/dist/docs/index.md +28 -28
- package/dist/docs/pulsepoint.md +1 -1
- package/package.json +1 -1
package/dist/docs/auth.md
CHANGED
|
@@ -20,14 +20,14 @@ Treat `casp.auth` as the default authentication layer in Caspian app code. Do no
|
|
|
20
20
|
|
|
21
21
|
## Overview
|
|
22
22
|
|
|
23
|
-
Caspian authentication has two main layers:
|
|
24
|
-
|
|
25
|
-
- app-level policy, controlled by `src/lib/auth/auth_config.py`
|
|
26
|
-
- framework runtime behavior, implemented by `.venv/Lib/site-packages/casp/auth.py`
|
|
23
|
+
Caspian authentication has two main layers:
|
|
24
|
+
|
|
25
|
+
- app-level policy, controlled by `src/lib/auth/auth_config.py`
|
|
26
|
+
- framework runtime behavior, implemented by `.venv/Lib/site-packages/casp/auth.py`
|
|
27
27
|
|
|
28
28
|
The main public API includes:
|
|
29
29
|
|
|
30
|
-
- `AuthSettings` for centralized app auth policy
|
|
30
|
+
- `AuthSettings` for centralized app auth policy
|
|
31
31
|
- `configure_auth(...)` and `get_auth_settings()` for app startup and reads
|
|
32
32
|
- the global `auth` object for session lifecycle work
|
|
33
33
|
- `require_auth`, `require_role`, and `guest_only` for page-level protection
|
|
@@ -49,7 +49,7 @@ from casp.auth import (
|
|
|
49
49
|
|
|
50
50
|
## Default Auth Rule
|
|
51
51
|
|
|
52
|
-
- Define app-wide auth behavior in `src/lib/auth/auth_config.py` through `build_auth_settings()` and apply it once at startup with `configure_auth(...)`.
|
|
52
|
+
- Define app-wide auth behavior in `src/lib/auth/auth_config.py` through `build_auth_settings()` and apply it once at startup with `configure_auth(...)`.
|
|
53
53
|
- Use `auth.sign_in(...)` and `auth.sign_out(...)` instead of setting or clearing session keys directly.
|
|
54
54
|
- Prefer `pp.rpc(...)` plus `@rpc(require_auth=True)` for signout buttons or menus rendered in pages or components. Use a dedicated signout route only when you need a plain HTML form POST or a no-JavaScript fallback.
|
|
55
55
|
- Use `@require_auth`, `@require_role`, and `@guest_only` for page access rules.
|
|
@@ -61,15 +61,15 @@ from casp.auth import (
|
|
|
61
61
|
|
|
62
62
|
## Framework Internals Note
|
|
63
63
|
|
|
64
|
-
- The centralized app auth policy controller is `src/lib/auth/auth_config.py`.
|
|
65
|
-
- The installed framework implementation lives in `.venv/Lib/site-packages/casp/auth.py`.
|
|
66
|
-
- Treat `auth_config.py` as project code and `casp/auth.py` as framework runtime code. Do not edit `casp/auth.py` to control app route privacy, redirects, or RBAC.
|
|
64
|
+
- The centralized app auth policy controller is `src/lib/auth/auth_config.py`.
|
|
65
|
+
- The installed framework implementation lives in `.venv/Lib/site-packages/casp/auth.py`.
|
|
66
|
+
- Treat `auth_config.py` as project code and `casp/auth.py` as framework runtime code. Do not edit `casp/auth.py` to control app route privacy, redirects, or RBAC.
|
|
67
67
|
- If upstream docs and the installed implementation disagree, prefer the installed implementation for local project guidance.
|
|
68
68
|
- Use [core-runtime-map.md](./core-runtime-map.md) when an auth task crosses `main.py` bootstrap behavior such as development cookie scoping or middleware ownership.
|
|
69
69
|
|
|
70
70
|
## Centralized Auth Settings
|
|
71
71
|
|
|
72
|
-
Keep application auth policy in `src/lib/auth/auth_config.py`. This file is the controller for route privacy, redirects, and RBAC.
|
|
72
|
+
Keep application auth policy in `src/lib/auth/auth_config.py`. This file is the controller for route privacy, redirects, and RBAC.
|
|
73
73
|
|
|
74
74
|
Example:
|
|
75
75
|
|
|
@@ -80,12 +80,12 @@ from casp.auth import AuthSettings
|
|
|
80
80
|
|
|
81
81
|
def build_auth_settings() -> AuthSettings:
|
|
82
82
|
"""
|
|
83
|
-
Centralized app auth policy controller.
|
|
84
|
-
|
|
85
|
-
Keep secrets (AUTH_SECRET, AUTH_COOKIE_NAME) in .env.
|
|
86
|
-
Keep app-level session settings in .env (SESSION_LIFETIME_HOURS, etc).
|
|
87
|
-
Decide route privacy, redirects, and RBAC here at app setup time instead of
|
|
88
|
-
changing Caspian core runtime files.
|
|
83
|
+
Centralized app auth policy controller.
|
|
84
|
+
|
|
85
|
+
Keep secrets (AUTH_SECRET, AUTH_COOKIE_NAME) in .env.
|
|
86
|
+
Keep app-level session settings in .env (SESSION_LIFETIME_HOURS, etc).
|
|
87
|
+
Decide route privacy, redirects, and RBAC here at app setup time instead of
|
|
88
|
+
changing Caspian core runtime files.
|
|
89
89
|
"""
|
|
90
90
|
|
|
91
91
|
return AuthSettings(
|
|
@@ -103,7 +103,7 @@ def build_auth_settings() -> AuthSettings:
|
|
|
103
103
|
is_role_based=False,
|
|
104
104
|
role_identifier="role",
|
|
105
105
|
|
|
106
|
-
# RBAC policy is app-owned here; the runtime expects ROUTE/PATTERN -> [ROLES].
|
|
106
|
+
# RBAC policy is app-owned here; the runtime expects ROUTE/PATTERN -> [ROLES].
|
|
107
107
|
role_based_routes={},
|
|
108
108
|
|
|
109
109
|
# Redirects / prefixes
|
|
@@ -152,7 +152,7 @@ Make this decision at app setup time in `src/lib/auth/auth_config.py`.
|
|
|
152
152
|
- In the current runtime, `auth_routes=["/signin", "/signup"]` stays public by default, and most apps do not need to change it unless the user explicitly asks for different auth routes.
|
|
153
153
|
- In all-private mode, the default `public_routes=["/"]` keeps the home page public unless you change that list.
|
|
154
154
|
- `token_auto_refresh=True` does not make routes private. It only enables sliding-session refresh when the request lifecycle calls `auth.refresh_session()`.
|
|
155
|
-
- Do not modify Caspian core files for this decision. Keep the policy in `src/lib/auth/auth_config.py`.
|
|
155
|
+
- Do not modify Caspian core files for this decision. Keep the policy in `src/lib/auth/auth_config.py`.
|
|
156
156
|
- If you customize `src/lib/auth/auth_config.py`, add it to `excludeFiles` in `caspian.config.json` so update commands do not overwrite your local auth policy.
|
|
157
157
|
|
|
158
158
|
Example all-private setup with a few public exceptions:
|
|
@@ -720,6 +720,33 @@ In the installed implementation:
|
|
|
720
720
|
|
|
721
721
|
The installed `casp.auth` file includes provider helpers for Google and GitHub OAuth.
|
|
722
722
|
|
|
723
|
+
In the app-owned starter bootstrap, both providers are already registered. Treat Google and GitHub sign-in as a shipped feature, not something to build from scratch. The current `main.py` calls:
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
Auth.set_providers(GithubProvider(), GoogleProvider())
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
so `AuthMiddleware` already delegates the provider signin and callback paths through `auth.auth_providers(...)` on every request. To add a working social sign-in to a page, you usually need only two things:
|
|
730
|
+
|
|
731
|
+
- a link or button that navigates to the provider signin path, and
|
|
732
|
+
- the provider credentials in `.env`.
|
|
733
|
+
|
|
734
|
+
The signin and callback paths live under `api_auth_prefix` (default `/api/auth`):
|
|
735
|
+
|
|
736
|
+
- Google sign-in link target: `/api/auth/signin/google`
|
|
737
|
+
- GitHub sign-in link target: `/api/auth/signin/github`
|
|
738
|
+
- Google callback (provider-side redirect URI): `/api/auth/callback/google`
|
|
739
|
+
- GitHub callback (provider-side redirect URI): `/api/auth/callback/github`
|
|
740
|
+
|
|
741
|
+
Minimal sign-in button:
|
|
742
|
+
|
|
743
|
+
```html
|
|
744
|
+
<a href="/api/auth/signin/google">Continue with Google</a>
|
|
745
|
+
<a href="/api/auth/signin/github">Continue with GitHub</a>
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Do not reinvent this flow. When a task asks for Google or GitHub login, do not add `authlib`, a hand-written `httpx` token-exchange, a parallel session writer, or custom `/callback` route handlers. The shipped providers plus `auth.auth_providers(...)` already perform the redirect, the code exchange, payload normalization, `auth.sign_in(...)`, and the post-login redirect to `default_signin_redirect`. Reuse them and keep credentials in `.env`.
|
|
749
|
+
|
|
723
750
|
Available provider classes:
|
|
724
751
|
|
|
725
752
|
- `GoogleProvider(client_id, client_secret, redirect_uri, max_age="30d")`
|
|
@@ -767,7 +794,7 @@ Behavior:
|
|
|
767
794
|
|
|
768
795
|
Use this helper when custom form or fetch flows need access to the session CSRF token.
|
|
769
796
|
|
|
770
|
-
## Current Implementation Notes
|
|
797
|
+
## Current Implementation Notes
|
|
771
798
|
|
|
772
799
|
- The installed auth runtime is session-backed. It stores the auth payload and CSRF token in `request.session`.
|
|
773
800
|
- Expiration uses timestamps and the current duration parser only accepts `s`, `m`, `h`, and `d` units.
|
|
@@ -810,7 +837,7 @@ If an AI agent is deciding how to handle auth in Caspian, apply these rules firs
|
|
|
810
837
|
- Treat `public_routes` as the public exception list for all-private apps. In the current defaults, `/` stays public and `auth_routes=["/signin", "/signup"]` stays public too.
|
|
811
838
|
- For protected grouped sections, follow the section layout pattern in [routing.md](./routing.md) and keep auth-specific route policy in `src/lib/auth/auth_config.py`.
|
|
812
839
|
- Apply settings at startup in `main.py` with `configure_auth(build_auth_settings())`.
|
|
813
|
-
-
|
|
840
|
+
- Treat Google and GitHub OAuth as shipped: the starter `main.py` already registers both providers with `Auth.set_providers(GithubProvider(), GoogleProvider())`. For social sign-in, link to `/api/auth/signin/{google,github}` and set `.env` credentials; do not hand-roll OAuth, token exchange, or callback routes. Only call `Auth.set_providers(...)` yourself if you are changing which providers are registered.
|
|
814
841
|
- Use `auth.sign_in(...)` and `auth.sign_out(...)` for session lifecycle changes.
|
|
815
842
|
- Prefer `pp.rpc("signout")` plus `@rpc(require_auth=True)` for logout buttons or menus in pages and components.
|
|
816
843
|
- Use a dedicated `/signout` route only for plain HTML form POST, no-JavaScript fallback, or other full-navigation edge cases.
|
package/dist/docs/components.md
CHANGED
|
@@ -7,10 +7,10 @@ related:
|
|
|
7
7
|
links:
|
|
8
8
|
- /docs/project-structure
|
|
9
9
|
- /docs/core-runtime-map
|
|
10
|
-
- /docs/routing
|
|
11
|
-
- /docs/pulsepoint
|
|
12
|
-
- /docs/pulsepoint-runtime-map
|
|
13
|
-
- /docs/fetch-data
|
|
10
|
+
- /docs/routing
|
|
11
|
+
- /docs/pulsepoint
|
|
12
|
+
- /docs/pulsepoint-runtime-map
|
|
13
|
+
- /docs/fetch-data
|
|
14
14
|
- /docs/index
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -26,20 +26,21 @@ As the app grows, treat `src/components/` as the default home for reusable appli
|
|
|
26
26
|
|
|
27
27
|
## Mental Model
|
|
28
28
|
|
|
29
|
-
- Use a Python component when you want a reusable server-rendered UI building block.
|
|
30
|
-
- Return an HTML string directly for small presentational components.
|
|
31
|
-
- Use `
|
|
32
|
-
-
|
|
33
|
-
-
|
|
29
|
+
- Use a Python component when you want a reusable server-rendered UI building block.
|
|
30
|
+
- Return an HTML string directly for small presentational components.
|
|
31
|
+
- Use `html(...)` to keep markup, server interpolation, and a PulsePoint `<script>` inline in one Python file (single-file component) for small and medium UI. It renders through Caspian's Jinja env, so `{{ ... }}` is server-side and `{ ... }` stays for PulsePoint.
|
|
32
|
+
- Use `render_html(...)` with a same-name `.html` file when the component has large markup, a long script, or you want clearer separation between Python logic and UI.
|
|
33
|
+
- When a component needs first-party interactivity, bind events in the component template with PulsePoint-handled `on*` attributes and keep state in `pp.state(...)`; do not build id-driven `querySelector(...)` or `addEventListener(...)` wiring for normal component behavior.
|
|
34
|
+
- Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
|
|
34
35
|
|
|
35
36
|
## Framework Internals Note
|
|
36
37
|
|
|
37
38
|
When the task is about component internals rather than normal app-owned components, these runtime files are the most relevant:
|
|
38
39
|
|
|
39
|
-
- `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, and component loading.
|
|
40
|
-
- `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution, root validation, and `pp-component` injection.
|
|
40
|
+
- `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, the inline `html(...)` helper, and component loading.
|
|
41
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution (including resolving a component's own tags from its Python module imports), parent-scope expansion of slot content, root validation, and `pp-component` injection.
|
|
41
42
|
- `.venv/Lib/site-packages/casp/html_attrs.py` owns `get_attributes(...)` and the Python-side `merge_classes(...)` contract.
|
|
42
|
-
Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader Python runtime-module map. Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when a component task is specifically about browser-side PulsePoint state, refs, context, portals, events, RPC, or SPA behavior.
|
|
43
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader Python runtime-module map. Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when a component task is specifically about browser-side PulsePoint state, refs, context, portals, events, RPC, or SPA behavior.
|
|
43
44
|
|
|
44
45
|
## Basic Component
|
|
45
46
|
|
|
@@ -183,7 +184,7 @@ Do not invent folder-level imports for symbols that do not have their own file.
|
|
|
183
184
|
|
|
184
185
|
## Template-Backed Components
|
|
185
186
|
|
|
186
|
-
When a component
|
|
187
|
+
When a component has large markup or a long script, keep the Python file focused on props or server-side preparation and move the markup into a same-name HTML file. For small and medium components, including ones with PulsePoint behavior, prefer the single-file `html(...)` form described in the next section instead.
|
|
187
188
|
|
|
188
189
|
`Counter.py`
|
|
189
190
|
|
|
@@ -220,6 +221,89 @@ Use this split when:
|
|
|
220
221
|
|
|
221
222
|
This keeps the component easy to read: Python owns the server-side logic and template context, while the HTML file owns the rendered UI and browser behavior.
|
|
222
223
|
|
|
224
|
+
## Single-File Components With `html(...)`
|
|
225
|
+
|
|
226
|
+
When a component is small or medium, keep the markup, server-side interpolation, and the PulsePoint `<script>` inline in the Python file instead of a sibling `.html`. Import `html` from `casp.component_decorator` and return `html("""...""", **context)`.
|
|
227
|
+
|
|
228
|
+
`html(...)` renders the string through the same Jinja environment Caspian uses for template files, so three brace dialects coexist without colliding:
|
|
229
|
+
|
|
230
|
+
- `{{ value }}` is server render (Python to HTML).
|
|
231
|
+
- `{{ value | json }}` safely serializes a server value into a `<script>`.
|
|
232
|
+
- `{# comment #}` is a Jinja comment, stripped from output.
|
|
233
|
+
- `{ value }` is left untouched for PulsePoint client reactivity.
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from casp.component_decorator import component, html
|
|
237
|
+
from casp.html_attrs import get_attributes, merge_classes
|
|
238
|
+
|
|
239
|
+
@component
|
|
240
|
+
def UserCard(user, **props):
|
|
241
|
+
attrs = get_attributes({
|
|
242
|
+
"class": merge_classes("card", props.pop("class", "")),
|
|
243
|
+
}, props)
|
|
244
|
+
|
|
245
|
+
# html
|
|
246
|
+
return html("""
|
|
247
|
+
<div {{ attrs }}>
|
|
248
|
+
<h3>{{ user.name }}</h3>
|
|
249
|
+
<button onclick="setLikes(likes + 1)">Likes: {likes}</button>
|
|
250
|
+
<script>
|
|
251
|
+
const [likes, setLikes] = pp.state({{ user.likes | json }});
|
|
252
|
+
</script>
|
|
253
|
+
</div>
|
|
254
|
+
""", attrs=attrs, user=user)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The returned value is `Markup`, so the normal pipeline still injects `pp-component` on the single root and `transform_scripts(...)` rewrites the plain `<script>` to `type="text/pp"`. A single-file component renders identically to the two-file `render_html(...)` form; the choice is purely about readability.
|
|
258
|
+
|
|
259
|
+
Rules for inline `html(...)`:
|
|
260
|
+
|
|
261
|
+
- The single-root rule still applies: exactly one top-level element with any `<script>` nested inside it.
|
|
262
|
+
- Autoescaping is ON (Jinja default), so `{{ value }}` escapes HTML automatically and user text is safe without `| e`. The flip side: trusted HTML you want rendered as-is must be `Markup(...)` or piped through `| safe`.
|
|
263
|
+
- A `children` value is auto-marked safe (parity with `render_html(...)`), so `{{ children }}` renders nested component markup correctly without `| safe`.
|
|
264
|
+
- Do not use a Python f-string for the markup. PulsePoint single braces `{ ... }` would collide with f-string interpolation. Use a plain triple-quoted string passed to `html(...)`.
|
|
265
|
+
- Prefer `render_html(...)` with a same-name `.html` file when the markup is large or the `<script>` carries real logic. A long client script is a smell regardless of file shape; move heavy work into `@rpc()` actions or smaller composed components.
|
|
266
|
+
|
|
267
|
+
The leading `# html` comment above the string is an optional editor hint: some editors color the HTML inside a string tagged that way (JetBrains uses `# language=HTML`). It has no runtime effect.
|
|
268
|
+
|
|
269
|
+
## Component Imports: Python Imports Or `@import`
|
|
270
|
+
|
|
271
|
+
A component can render other components with `x-*` tags in either the two-file `.html` or the inline `html(...)` form. There are two ways to tell Caspian which component a tag refers to.
|
|
272
|
+
|
|
273
|
+
1. The `<!-- @import Name from "path" -->` HTML comment, resolved by path string. This works in both `.html` templates and inline strings.
|
|
274
|
+
2. A real Python import at the top of the component module. A component's own `x-*` tags resolve from the components imported into that module, with no `@import` comment needed.
|
|
275
|
+
|
|
276
|
+
Prefer the Python import inside single-file components. It is the unambiguous source of truth for which component a tag refers to, it gives normal editor navigation, and it prevents same-name collisions across directories. If `variantA/Tag.py` and `variantB/Tag.py` both export `Tag`, the Python import in each consumer file decides which `Tag` its `<x-tag>` resolves to.
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from casp.component_decorator import component, html
|
|
280
|
+
from src.components.variantA.Tag import Tag # resolves <x-tag> below
|
|
281
|
+
|
|
282
|
+
@component
|
|
283
|
+
def Panel(children="", **props):
|
|
284
|
+
# No @import comment. <x-tag> resolves from the Python import above,
|
|
285
|
+
# so it is unambiguously variantA's Tag.
|
|
286
|
+
# html
|
|
287
|
+
return html("""
|
|
288
|
+
<div class="panel">
|
|
289
|
+
<x-tag>inside-panel</x-tag>
|
|
290
|
+
{{ children }}
|
|
291
|
+
</div>
|
|
292
|
+
""", children=children)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Resolution precedence for an `x-*` tag inside a component's own output, lowest to highest:
|
|
296
|
+
|
|
297
|
+
- inherited components from an ancestor template
|
|
298
|
+
- the component's own Python module imports
|
|
299
|
+
- a local `<!-- @import ... -->` in that same template
|
|
300
|
+
|
|
301
|
+
So a component's Python import wins over an inherited same-name component, and an explicit local `@import` wins over both.
|
|
302
|
+
|
|
303
|
+
### Slot Content Resolves In The Parent Scope
|
|
304
|
+
|
|
305
|
+
Component tags written as slot content (children passed between a component's opening and closing tags) resolve in the scope where they were authored, not in the child component's scope. This matches slot semantics in other component systems: a `<x-tag>` written by the parent stays bound to the parent's `Tag` even when it is slotted into a child that has its own `Tag`. The component that authors a tag in markup must import that component, the same way it would for any tag it renders directly.
|
|
306
|
+
|
|
223
307
|
## Auto-Injected `pp-component` And PulsePoint Script Type
|
|
224
308
|
|
|
225
309
|
Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source. The canonical authored-vs-runtime explanation lives in [pulsepoint.md](./pulsepoint.md).
|
|
@@ -353,9 +437,9 @@ Keep synchronous components as the default. Switch to `async def` only when the
|
|
|
353
437
|
|
|
354
438
|
- Put reusable components in `src/components/` and keep route files in `src/app/`.
|
|
355
439
|
- For dashboards, admin areas, account sections, and route groups with child routes, put the shared shell in the parent folder's `layout.html` and compose it from reusable components there instead of repeating the same shell in every child `index.html`.
|
|
356
|
-
-
|
|
357
|
-
- For component clicks, inputs, menus, toggles, filters, and list updates, use PulsePoint events and directives inside
|
|
358
|
-
- Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
|
|
440
|
+
- Default to a single Python file with inline `html(...)` for most components, including ones with PulsePoint behavior. Move to a thin Python wrapper plus a same-name `.html` template only when the markup is large or the `<script>` carries real logic. Both forms render identically, so this is a readability choice, not a capability one.
|
|
441
|
+
- For component clicks, inputs, menus, toggles, filters, and list updates, use PulsePoint events and directives inside the component template (inline `html(...)` or the `.html` file). Avoid manual DOM selection, manual listener setup, and manual `innerHTML` rendering unless integrating a third-party imperative widget.
|
|
442
|
+
- Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
|
|
359
443
|
- Accept `children` or `**props` when the component should support nested content.
|
|
360
444
|
- Keep page-level data loading in `page()` when the data is not intrinsic to the component itself.
|
|
361
445
|
- If you add `@rpc()` functions inside a component file, keep their names globally unique because component RPCs are not route-scoped.
|
|
@@ -7,12 +7,12 @@ related:
|
|
|
7
7
|
links:
|
|
8
8
|
- /docs/project-structure
|
|
9
9
|
- /docs/routing
|
|
10
|
-
- /docs/auth
|
|
11
|
-
- /docs/fetch-data
|
|
12
|
-
- /docs/websockets
|
|
13
|
-
- /docs/pulsepoint
|
|
14
|
-
- /docs/pulsepoint-runtime-map
|
|
15
|
-
- /docs/index
|
|
10
|
+
- /docs/auth
|
|
11
|
+
- /docs/fetch-data
|
|
12
|
+
- /docs/websockets
|
|
13
|
+
- /docs/pulsepoint
|
|
14
|
+
- /docs/pulsepoint-runtime-map
|
|
15
|
+
- /docs/index
|
|
16
16
|
---
|
|
17
17
|
|
|
18
18
|
This page maps the app entry point and the installed Caspian runtime modules to the packaged docs that explain them.
|
|
@@ -26,85 +26,85 @@ Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib
|
|
|
26
26
|
- the task is about framework internals rather than normal app-owned route or component code
|
|
27
27
|
- AI needs to know which packaged doc should be kept aligned with a core-file change
|
|
28
28
|
|
|
29
|
-
## App Entry Point
|
|
29
|
+
## App Entry Point
|
|
30
30
|
|
|
31
31
|
| Concern | Core file | Read first | Why it matters |
|
|
32
32
|
| --- | --- | --- | --- |
|
|
33
|
-
| App bootstrap and request flow | [main.py](../../../../main.py) plus imported runtime helpers such as `casp.runtime_security` | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, middleware wiring, route registration, cache check and save, exception handlers, and final HTML transforms live here. Package-owned helpers imported by `main.py` may own safe public-file serving, baseline non-CSP response headers, production-safe error messages, or production session-secret enforcement. |
|
|
34
|
-
| Browser runtime, SPA navigation, and scroll restoration | [public/js/pp-reactive-v2.js](../../../../public/js/pp-reactive-v2.js) | [pulsepoint.md](./pulsepoint.md), [fetch-data.md](./fetch-data.md) | The shipped `pp` runtime, same-origin SPA interception, per-history-entry scroll state, `pp-reset-scroll` behavior, and browser RPC helpers live here. |
|
|
35
|
-
|
|
36
|
-
Important current `main.py` behaviors AI should keep in mind:
|
|
37
|
-
|
|
38
|
-
- In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
|
|
39
|
-
- `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
|
|
40
|
-
- The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
|
|
41
|
-
- Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
|
|
42
|
-
- Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
|
|
43
|
-
- Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
|
|
44
|
-
- Baseline non-CSP response headers may be built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. Do not assume that helper is a third-party browser resource or domain registry; check the current helper before adding CSP behavior.
|
|
45
|
-
- Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `SecurityHeadersMiddleware`, so the effective request order is reversed at runtime.
|
|
46
|
-
- `public/js/pp-reactive-v2.js` saves scroll positions per history entry, resets window scroll on push navigation, and uses `pp-reset-scroll="true"` to opt specific containers or the whole body into reset behavior.
|
|
47
|
-
|
|
48
|
-
## Caspian Core Feature Map
|
|
49
|
-
|
|
50
|
-
Use this table when the task names a framework feature but the owning file is not obvious yet.
|
|
51
|
-
|
|
52
|
-
| Core feature | App-owned entry points | Runtime owner | Packaged docs |
|
|
53
|
-
| --- | --- | --- | --- |
|
|
54
|
-
| Feature flags and generated surface area | `caspian.config.json` | `casp.caspian_config` | [commands.md](./commands.md), [project-structure.md](./project-structure.md) |
|
|
55
|
-
| File-based routing | `src/app/**/index.html`, `src/app/**/index.py` | `main.py`, `casp.layout`, `casp.caspian_config` | [routing.md](./routing.md) |
|
|
56
|
-
| Nested layouts | `src/app/**/layout.html`, `src/app/**/layout.py` | `casp.layout`, `main.py` | [routing.md](./routing.md), [metadata.md](./metadata.md) |
|
|
57
|
-
| Metadata | route or layout `metadata`, runtime metadata helpers | `casp.layout`, `main.py` | [metadata.md](./metadata.md), [routing.md](./routing.md) |
|
|
58
|
-
| Component imports and `x-*` tags | `src/components/**`, `src/app/**/*.html` | `casp.components_compiler`, `casp.component_decorator` | [components.md](./components.md), [routing.md](./routing.md) |
|
|
59
|
-
| Auth and route protection | `src/lib/auth/auth_config.py`, `main.py`, route decorators | `casp.auth`, `main.py` middleware | [auth.md](./auth.md) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
64
|
-
| Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
|
|
65
|
-
| Page caching | route-level `Cache(...)` declarations | `casp.cache_handler`, `main.py` cache check/save | [cache.md](./cache.md) |
|
|
66
|
-
| Validation | route and RPC input boundaries | `casp.validate` | [validation.md](./validation.md) |
|
|
67
|
-
| Tailwind class merge | Python components and PulsePoint templates | `casp.html_attrs`, browser `twMerge(...)` | [components.md](./components.md), [pulsepoint.md](./pulsepoint.md) |
|
|
68
|
-
| Prisma persistence | `prisma/**`, `src/lib/prisma/**`, route/RPC code | generated Prisma and Python clients | [database.md](./database.md), [fetch-data.md](./fetch-data.md) |
|
|
69
|
-
| MCP tools | `src/lib/mcp/**` when enabled | app-owned FastMCP server | [mcp.md](./mcp.md), [commands.md](./commands.md) |
|
|
70
|
-
|
|
71
|
-
For browser-side PulsePoint details, use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) instead of expanding this Python-runtime map.
|
|
72
|
-
|
|
73
|
-
## Task-To-File Examples
|
|
74
|
-
|
|
75
|
-
Protected dashboard route:
|
|
76
|
-
|
|
77
|
-
1. Check `caspian.config.json` for enabled features such as Prisma and Tailwind.
|
|
78
|
-
2. Read [routing.md](./routing.md) for the section layout shape.
|
|
79
|
-
3. Read [auth.md](./auth.md) for route privacy and decorators.
|
|
80
|
-
4. Update `src/app/dashboard/layout.html` for the shared shell and child `index.html` or `index.py` files for page-specific behavior.
|
|
81
|
-
5. Verify auth bootstrap and middleware ownership in `main.py` only if route protection behaves unexpectedly.
|
|
82
|
-
|
|
83
|
-
Interactive CRUD page:
|
|
84
|
-
|
|
85
|
-
1. Read [fetch-data.md](./fetch-data.md) for the `page()` plus `@rpc()` split.
|
|
86
|
-
2. Read [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) for browser-side `pp.rpc(...)` behavior.
|
|
87
|
-
3. Keep first-render data in `src/app/**/index.py`, visible markup in `src/app/**/index.html`, reusable helpers in `src/lib/**`, and database schema changes in `prisma/**`.
|
|
33
|
+
| App bootstrap and request flow | [main.py](../../../../main.py) plus imported runtime helpers such as `casp.runtime_security` | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, middleware wiring, route registration, cache check and save, exception handlers, and final HTML transforms live here. Package-owned helpers imported by `main.py` may own safe public-file serving, baseline non-CSP response headers, production-safe error messages, or production session-secret enforcement. |
|
|
34
|
+
| Browser runtime, SPA navigation, and scroll restoration | [public/js/pp-reactive-v2.js](../../../../public/js/pp-reactive-v2.js) | [pulsepoint.md](./pulsepoint.md), [fetch-data.md](./fetch-data.md) | The shipped `pp` runtime, same-origin SPA interception, per-history-entry scroll state, `pp-reset-scroll` behavior, and browser RPC helpers live here. |
|
|
35
|
+
|
|
36
|
+
Important current `main.py` behaviors AI should keep in mind:
|
|
37
|
+
|
|
38
|
+
- In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
|
|
39
|
+
- `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
|
|
40
|
+
- The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
|
|
41
|
+
- Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
|
|
42
|
+
- Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
|
|
43
|
+
- Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
|
|
44
|
+
- Baseline non-CSP response headers may be built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. Do not assume that helper is a third-party browser resource or domain registry; check the current helper before adding CSP behavior.
|
|
45
|
+
- Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `SecurityHeadersMiddleware`, so the effective request order is reversed at runtime.
|
|
46
|
+
- `public/js/pp-reactive-v2.js` saves scroll positions per history entry, resets window scroll on push navigation, and uses `pp-reset-scroll="true"` to opt specific containers or the whole body into reset behavior.
|
|
47
|
+
|
|
48
|
+
## Caspian Core Feature Map
|
|
49
|
+
|
|
50
|
+
Use this table when the task names a framework feature but the owning file is not obvious yet.
|
|
51
|
+
|
|
52
|
+
| Core feature | App-owned entry points | Runtime owner | Packaged docs |
|
|
53
|
+
| --- | --- | --- | --- |
|
|
54
|
+
| Feature flags and generated surface area | `caspian.config.json` | `casp.caspian_config` | [commands.md](./commands.md), [project-structure.md](./project-structure.md) |
|
|
55
|
+
| File-based routing | `src/app/**/index.html`, `src/app/**/index.py` | `main.py`, `casp.layout`, `casp.caspian_config` | [routing.md](./routing.md) |
|
|
56
|
+
| Nested layouts | `src/app/**/layout.html`, `src/app/**/layout.py` | `casp.layout`, `main.py` | [routing.md](./routing.md), [metadata.md](./metadata.md) |
|
|
57
|
+
| Metadata | route or layout `metadata`, runtime metadata helpers | `casp.layout`, `main.py` | [metadata.md](./metadata.md), [routing.md](./routing.md) |
|
|
58
|
+
| Component imports and `x-*` tags | `src/components/**`, `src/app/**/*.html` | `casp.components_compiler`, `casp.component_decorator` | [components.md](./components.md), [routing.md](./routing.md) |
|
|
59
|
+
| Auth and route protection | `src/lib/auth/auth_config.py`, `main.py`, route decorators | `casp.auth`, `main.py` middleware | [auth.md](./auth.md) |
|
|
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) |
|
|
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) |
|
|
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) |
|
|
64
|
+
| Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
|
|
65
|
+
| Page caching | route-level `Cache(...)` declarations | `casp.cache_handler`, `main.py` cache check/save | [cache.md](./cache.md) |
|
|
66
|
+
| Validation | route and RPC input boundaries | `casp.validate` | [validation.md](./validation.md) |
|
|
67
|
+
| Tailwind class merge | Python components and PulsePoint templates | `casp.html_attrs`, browser `twMerge(...)` | [components.md](./components.md), [pulsepoint.md](./pulsepoint.md) |
|
|
68
|
+
| Prisma persistence | `prisma/**`, `src/lib/prisma/**`, route/RPC code | generated Prisma and Python clients | [database.md](./database.md), [fetch-data.md](./fetch-data.md) |
|
|
69
|
+
| MCP tools | `src/lib/mcp/**` when enabled | app-owned FastMCP server | [mcp.md](./mcp.md), [commands.md](./commands.md) |
|
|
70
|
+
|
|
71
|
+
For browser-side PulsePoint details, use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) instead of expanding this Python-runtime map.
|
|
72
|
+
|
|
73
|
+
## Task-To-File Examples
|
|
74
|
+
|
|
75
|
+
Protected dashboard route:
|
|
76
|
+
|
|
77
|
+
1. Check `caspian.config.json` for enabled features such as Prisma and Tailwind.
|
|
78
|
+
2. Read [routing.md](./routing.md) for the section layout shape.
|
|
79
|
+
3. Read [auth.md](./auth.md) for route privacy and decorators.
|
|
80
|
+
4. Update `src/app/dashboard/layout.html` for the shared shell and child `index.html` or `index.py` files for page-specific behavior.
|
|
81
|
+
5. Verify auth bootstrap and middleware ownership in `main.py` only if route protection behaves unexpectedly.
|
|
82
|
+
|
|
83
|
+
Interactive CRUD page:
|
|
84
|
+
|
|
85
|
+
1. Read [fetch-data.md](./fetch-data.md) for the `page()` plus `@rpc()` split.
|
|
86
|
+
2. Read [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) for browser-side `pp.rpc(...)` behavior.
|
|
87
|
+
3. Keep first-render data in `src/app/**/index.py`, visible markup in `src/app/**/index.html`, reusable helpers in `src/lib/**`, and database schema changes in `prisma/**`.
|
|
88
88
|
|
|
89
89
|
## Installed Runtime Map
|
|
90
90
|
|
|
91
91
|
| Runtime file | Primary responsibility | Read these docs |
|
|
92
92
|
| --- | --- | --- |
|
|
93
|
-
| [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, sync or async `layout()` results, and parser-based `<slot />` replacement | [routing.md](./routing.md), [metadata.md](./metadata.md) |
|
|
94
|
-
| [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
|
|
95
|
-
| [.venv/Lib/site-packages/casp/runtime_security.py](../../../../.venv/Lib/site-packages/casp/runtime_security.py) | safe public-file serving, baseline non-CSP response headers, production-safe error messages, and production session-secret enforcement used by `main.py` | [project-structure.md](./project-structure.md), [auth.md](./auth.md) |
|
|
96
|
-
| [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
|
|
93
|
+
| [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, sync or async `layout()` results, and parser-based `<slot />` replacement | [routing.md](./routing.md), [metadata.md](./metadata.md) |
|
|
94
|
+
| [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
|
|
95
|
+
| [.venv/Lib/site-packages/casp/runtime_security.py](../../../../.venv/Lib/site-packages/casp/runtime_security.py) | safe public-file serving, baseline non-CSP response headers, production-safe error messages, and production session-secret enforcement used by `main.py` | [project-structure.md](./project-structure.md), [auth.md](./auth.md) |
|
|
96
|
+
| [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
|
|
97
97
|
| [.venv/Lib/site-packages/casp/streaming.py](../../../../.venv/Lib/site-packages/casp/streaming.py) | `SSE`, `ServerSentEvent`, and generator-to-event-stream wrapping | [fetch-data.md](./fetch-data.md) |
|
|
98
98
|
| [.venv/Lib/site-packages/casp/state_manager.py](../../../../.venv/Lib/site-packages/casp/state_manager.py) | request-scoped state, session bucket persistence, listener lifecycle, and wire-request reset behavior | [state.md](./state.md) |
|
|
99
99
|
| [.venv/Lib/site-packages/casp/cache_handler.py](../../../../.venv/Lib/site-packages/casp/cache_handler.py) | `Cache`, cache manifest handling, filename generation, disk-backed HTML storage, and invalidation | [cache.md](./cache.md) |
|
|
100
100
|
| [.venv/Lib/site-packages/casp/validate.py](../../../../.venv/Lib/site-packages/casp/validate.py) | `Validate`, `Rule`, sanitization, and file-validation internals | [validation.md](./validation.md) |
|
|
101
|
-
| [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, component loading, and prop normalization before component calls | [components.md](./components.md) |
|
|
102
|
-
| [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
|
|
101
|
+
| [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, the inline `html(...)` single-file helper, component loading, and prop normalization before component calls | [components.md](./components.md) |
|
|
102
|
+
| [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution (including resolving a component's own tags from its Python module imports), parent-scope expansion of slot content, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
|
|
103
103
|
| [.venv/Lib/site-packages/casp/scripts_type.py](../../../../.venv/Lib/site-packages/casp/scripts_type.py) | rewriting authored body `<script>` tags to `type="text/pp"` in rendered HTML | [pulsepoint.md](./pulsepoint.md), [routing.md](./routing.md), [components.md](./components.md) |
|
|
104
104
|
| [.venv/Lib/site-packages/casp/html_attrs.py](../../../../.venv/Lib/site-packages/casp/html_attrs.py) | `get_attributes(...)`, alias normalization, and the Python-side `merge_classes(...)` contract | [components.md](./components.md) |
|
|
105
105
|
| [.venv/Lib/site-packages/casp/caspian_config.py](../../../../.venv/Lib/site-packages/casp/caspian_config.py) | typed config loading, feature-flag reads, `settings/files-list.json` parsing, and route rule derivation | [project-structure.md](./project-structure.md), [commands.md](./commands.md), [routing.md](./routing.md) |
|
|
106
106
|
|
|
107
|
-
Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. `html_native.py` owns BeautifulSoup-backed fragment parsing used by component/root transforms and layout slot replacement. Read these helpers only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
|
|
107
|
+
Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. `html_native.py` owns BeautifulSoup-backed fragment parsing used by component/root transforms and layout slot replacement. Read these helpers only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
|
|
108
108
|
|
|
109
109
|
## Verification Focus
|
|
110
110
|
|
|
@@ -112,13 +112,13 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
|
|
|
112
112
|
|
|
113
113
|
| Runtime area | Verify these behaviors |
|
|
114
114
|
| --- | --- |
|
|
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 |
|
|
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
|
+
| `main.py` routing and request flow | route registration, path and query injection, static asset handling, session-middleware wiring, response-header middleware, and exception rendering |
|
|
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 |
|
|
118
118
|
| `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
|
|
119
119
|
| `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
|
|
120
120
|
| `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
|
|
121
|
-
| `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
|
|
121
|
+
| `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, Python-module-import tag resolution, parent-scope slot resolution, inline `html(...)` rendering and children-safe handling, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
|
|
122
122
|
| `casp.state_manager` | wire vs non-wire reset behavior, request-state persistence assumptions, and AttributeDict access |
|
|
123
123
|
| `casp.cache_handler` | filename generation, manifest writes, TTL handling, and invalidation behavior |
|
|
124
124
|
| `casp.caspian_config` | config parsing, files index building, and Next.js-style route inventory behavior |
|
package/dist/docs/fetch-data.md
CHANGED
|
@@ -21,13 +21,13 @@ related:
|
|
|
21
21
|
|
|
22
22
|
This page explains how data fetching works in Caspian. Use route functions for initial page data and use RPC actions for browser-triggered reads, writes, streams, uploads, and normal CRUD work.
|
|
23
23
|
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
29
|
-
|
|
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.
|
|
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
|
+
|
|
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
|
+
|
|
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.
|
|
29
|
+
|
|
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.
|
|
31
31
|
|
|
32
32
|
MCP is a separate integration surface. Do not place app-owned FastMCP tools in route `index.py` files or treat `@rpc()` actions as a replacement for MCP tools. Use `mcp.md` and `src/lib/mcp/` only when `caspian.config.json` has `mcp: true`. If `mcp` is false, do not assume those files exist.
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ MCP is a separate integration surface. Do not place app-owned FastMCP tools in r
|
|
|
35
35
|
|
|
36
36
|
Caspian has two main data-loading paths:
|
|
37
37
|
|
|
38
|
-
- `page()` for initial-render data, plus `layout()` for shared props or metadata during the render
|
|
38
|
+
- `page()` for initial-render data, plus `layout()` for shared props or metadata during the render
|
|
39
39
|
- `@rpc()` plus `pp.rpc()` for interactive fetches after the page is already loaded
|
|
40
40
|
|
|
41
41
|
In practice, most pages use both:
|
|
@@ -50,13 +50,13 @@ When a page belongs to a grouped subtree such as a dashboard, account area, admi
|
|
|
50
50
|
|
|
51
51
|
## Default Data Rule
|
|
52
52
|
|
|
53
|
-
- Use `page()` for route-level data required before HTML renders, and use `layout()` only for shared subtree props or metadata.
|
|
53
|
+
- Use `page()` for route-level data required before HTML renders, and use `layout()` only for shared subtree props or metadata.
|
|
54
54
|
- When a route renders UI and also needs backend work, keep the HTML in the sibling `index.html`; `index.py` should prepare data and call `render_page(__file__, ...)`, not inline the route markup.
|
|
55
|
-
- Use `@rpc()` on the server and `pp.rpc()` in PulsePoint code for all browser-triggered data work after first render, including CRUD operations and follow-up reads.
|
|
56
|
-
- When Prisma is enabled, route all Python database I/O inside `page()`, `layout()` when truly shared, `@rpc()` actions, and reusable helpers through the generated Prisma Python ORM.
|
|
57
|
-
- Trigger those browser actions through PulsePoint event attributes in the HTML, not through a separate DOM listener layer.
|
|
58
|
-
- For simple form submits, prefer `onsubmit="{submitForm(event)}"` plus `Object.fromEntries(new FormData(event.currentTarget).entries())` over `pp-ref` fields and effect-managed submit listeners.
|
|
59
|
-
- Keep custom REST or other endpoint patterns as explicit exceptions, not the baseline Caspian approach.
|
|
55
|
+
- Use `@rpc()` on the server and `pp.rpc()` in PulsePoint code for all browser-triggered data work after first render, including CRUD operations and follow-up reads.
|
|
56
|
+
- When Prisma is enabled, route all Python database I/O inside `page()`, `layout()` when truly shared, `@rpc()` actions, and reusable helpers through the generated Prisma Python ORM.
|
|
57
|
+
- Trigger those browser actions through PulsePoint event attributes in the HTML, not through a separate DOM listener layer.
|
|
58
|
+
- For simple form submits, prefer `onsubmit="{submitForm(event)}"` plus `Object.fromEntries(new FormData(event.currentTarget).entries())` over `pp-ref` fields and effect-managed submit listeners.
|
|
59
|
+
- Keep custom REST or other endpoint patterns as explicit exceptions, not the baseline Caspian approach.
|
|
60
60
|
|
|
61
61
|
## Initial Data In `index.py`
|
|
62
62
|
|
|
@@ -87,11 +87,11 @@ If a route's first-render HTML is public and stable enough to reuse across reque
|
|
|
87
87
|
Notes:
|
|
88
88
|
|
|
89
89
|
- Prefer `async def page()` when your database or API client is async-capable.
|
|
90
|
-
- Put shared section-level props in `layout.py` when multiple child routes need the same payload. The current layout engine supports synchronous and async `layout()` results, but route-specific data should stay in `page()`.
|
|
90
|
+
- Put shared section-level props in `layout.py` when multiple child routes need the same payload. The current layout engine supports synchronous and async `layout()` results, but route-specific data should stay in `page()`.
|
|
91
91
|
- Use a normal parent folder such as `dashboard/` when the section name should appear in the URL. Use a route-group folder such as `(reports)/` only when the shared wrapper should organize child routes without adding a URL segment.
|
|
92
92
|
- Keep reusable database or API clients under `src/lib/`; keep route-specific orchestration in `src/app/`.
|
|
93
93
|
|
|
94
|
-
If the data source is Prisma, confirm `caspian.config.json` has `prisma: true`, then see `database.md` for the project's schema, migration, and generation workflow. If the project includes an app-owned Python database layer under `src/lib/prisma/`, reuse it instead of creating another helper. In Prisma-enabled projects, direct driver access and custom fetch helpers are exceptions to avoid, not alternate defaults.
|
|
94
|
+
If the data source is Prisma, confirm `caspian.config.json` has `prisma: true`, then see `database.md` for the project's schema, migration, and generation workflow. If the project includes an app-owned Python database layer under `src/lib/prisma/`, reuse it instead of creating another helper. In Prisma-enabled projects, direct driver access and custom fetch helpers are exceptions to avoid, not alternate defaults.
|
|
95
95
|
|
|
96
96
|
## Interactive Data With RPC
|
|
97
97
|
|
|
@@ -113,18 +113,18 @@ from src.lib.prisma import prisma
|
|
|
113
113
|
async def list_todos():
|
|
114
114
|
return await prisma.todo.find_many()
|
|
115
115
|
|
|
116
|
-
@rpc(require_auth=True, limits="20/minute")
|
|
117
|
-
async def create_todo(title: str):
|
|
118
|
-
title = title.strip()
|
|
119
|
-
if not title:
|
|
120
|
-
raise ValueError("Title is required.")
|
|
121
|
-
|
|
122
|
-
return await prisma.todo.create(
|
|
123
|
-
data={"title": title, "completed": False}
|
|
124
|
-
)
|
|
116
|
+
@rpc(require_auth=True, limits="20/minute")
|
|
117
|
+
async def create_todo(title: str):
|
|
118
|
+
title = title.strip()
|
|
119
|
+
if not title:
|
|
120
|
+
raise ValueError("Title is required.")
|
|
121
|
+
|
|
122
|
+
return await prisma.todo.create(
|
|
123
|
+
data={"title": title, "completed": False}
|
|
124
|
+
)
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
Call it from the client with `pp.rpc()`:
|
|
127
|
+
Call it from the client with `pp.rpc()`:
|
|
128
128
|
|
|
129
129
|
```html
|
|
130
130
|
<script>
|
|
@@ -137,39 +137,39 @@ Call it from the client with `pp.rpc()`:
|
|
|
137
137
|
const todo = await pp.rpc("create_todo", { title });
|
|
138
138
|
console.log(todo);
|
|
139
139
|
}
|
|
140
|
-
</script>
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Do not call `fetch("/api/todos")` from the browser for normal Caspian CRUD when the route can expose an `@rpc()` action. Do not use `fetch()` or a database driver in Python to work around Prisma when `src/lib/prisma/**` already provides the generated ORM client.
|
|
144
|
-
|
|
145
|
-
For form submissions, let the form event provide the submitted values. The inputs' `name` attributes define the RPC payload keys, and the Python action owns trimming, validation, coercion, and persistence decisions.
|
|
146
|
-
|
|
147
|
-
```html
|
|
148
|
-
<form onsubmit="{createTodoFromForm(event)}" novalidate>
|
|
149
|
-
<input name="title" required />
|
|
150
|
-
<button type="submit" disabled="{isSaving}">Add</button>
|
|
151
|
-
|
|
152
|
-
<script>
|
|
153
|
-
const [isSaving, setIsSaving] = pp.state(false);
|
|
154
|
-
|
|
155
|
-
async function createTodoFromForm(event) {
|
|
156
|
-
event.preventDefault();
|
|
157
|
-
|
|
158
|
-
const data = Object.fromEntries(new FormData(event.currentTarget).entries());
|
|
159
|
-
if (isSaving) return;
|
|
160
|
-
|
|
161
|
-
setIsSaving(true);
|
|
162
|
-
try {
|
|
163
|
-
await pp.rpc("create_todo", data, { abortPrevious: true });
|
|
164
|
-
} finally {
|
|
165
|
-
setIsSaving(false);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
</script>
|
|
169
|
-
</form>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Use RPC for:
|
|
140
|
+
</script>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Do not call `fetch("/api/todos")` from the browser for normal Caspian CRUD when the route can expose an `@rpc()` action. Do not use `fetch()` or a database driver in Python to work around Prisma when `src/lib/prisma/**` already provides the generated ORM client.
|
|
144
|
+
|
|
145
|
+
For form submissions, let the form event provide the submitted values. The inputs' `name` attributes define the RPC payload keys, and the Python action owns trimming, validation, coercion, and persistence decisions.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<form onsubmit="{createTodoFromForm(event)}" novalidate>
|
|
149
|
+
<input name="title" required />
|
|
150
|
+
<button type="submit" disabled="{isSaving}">Add</button>
|
|
151
|
+
|
|
152
|
+
<script>
|
|
153
|
+
const [isSaving, setIsSaving] = pp.state(false);
|
|
154
|
+
|
|
155
|
+
async function createTodoFromForm(event) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
|
|
158
|
+
const data = Object.fromEntries(new FormData(event.currentTarget).entries());
|
|
159
|
+
if (isSaving) return;
|
|
160
|
+
|
|
161
|
+
setIsSaving(true);
|
|
162
|
+
try {
|
|
163
|
+
await pp.rpc("create_todo", data, { abortPrevious: true });
|
|
164
|
+
} finally {
|
|
165
|
+
setIsSaving(false);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
</script>
|
|
169
|
+
</form>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Use RPC for:
|
|
173
173
|
|
|
174
174
|
- CRUD reads and writes after the initial render
|
|
175
175
|
- Button-triggered refreshes
|
|
@@ -186,8 +186,8 @@ Important:
|
|
|
186
186
|
|
|
187
187
|
- `pp.rpc()` posts to the current route RPC bridge.
|
|
188
188
|
- Older docs may refer to `pp.fetchFunction()`. In this repo's current runtime, the supported helper is `pp.rpc()`.
|
|
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.
|
|
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.
|
|
191
191
|
|
|
192
192
|
## Streaming Responses
|
|
193
193
|
|
|
@@ -227,6 +227,43 @@ Client example:
|
|
|
227
227
|
|
|
228
228
|
Use streaming when the user should see partial progress instead of waiting for a single final payload.
|
|
229
229
|
|
|
230
|
+
This is the shipped, default mechanism for AI/LLM/chat token streaming in Caspian. When a task asks for a chat app, an assistant reply, or any "stream the model's tokens as they arrive" experience, use this RPC streaming path. Do not reinvent one-way streaming with raw `fetch` + `ReadableStream`, a browser `EventSource`, or a WebSocket. WebSockets are only for genuinely bidirectional channels (see [websockets.md](./websockets.md)); a one-way token stream from server to browser belongs in a generator `@rpc()` consumed by `pp.rpc(..., { onStream })`.
|
|
231
|
+
|
|
232
|
+
Bridging a Python LLM/SDK stream is the common case: `async for` over the provider's streaming response inside the `@rpc()` action and `yield` each chunk. The runtime turns the generator into an SSE response automatically.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from casp.rpc import rpc
|
|
236
|
+
from anthropic import AsyncAnthropic
|
|
237
|
+
|
|
238
|
+
client = AsyncAnthropic()
|
|
239
|
+
|
|
240
|
+
@rpc(require_auth=True, limits="20/minute")
|
|
241
|
+
async def chat(message: str):
|
|
242
|
+
async with client.messages.stream(
|
|
243
|
+
model="claude-opus-4-8",
|
|
244
|
+
max_tokens=1024,
|
|
245
|
+
messages=[{"role": "user", "content": message}],
|
|
246
|
+
) as stream:
|
|
247
|
+
async for text in stream.text_stream:
|
|
248
|
+
yield text
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Client side, append each chunk to reactive state so the UI grows token by token:
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<script>
|
|
255
|
+
const [answer, setAnswer] = pp.state("");
|
|
256
|
+
|
|
257
|
+
async function send(message) {
|
|
258
|
+
setAnswer("");
|
|
259
|
+
await pp.rpc("chat", { message }, {
|
|
260
|
+
onStream: (chunk) => setAnswer((current) => current + chunk),
|
|
261
|
+
onStreamComplete: () => console.log("done"),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
</script>
|
|
265
|
+
```
|
|
266
|
+
|
|
230
267
|
In the current runtime, generator and async-generator RPC results are wrapped with `SSE(...)` inside `casp.rpc.py`, and route-level generator results are also wrapped by `main.py` before the response is returned.
|
|
231
268
|
|
|
232
269
|
## File Uploads
|
|
@@ -334,7 +371,7 @@ For initial route rendering with `render_page(...)`, prefer passing plain templa
|
|
|
334
371
|
|
|
335
372
|
## Recommended Decision Rule
|
|
336
373
|
|
|
337
|
-
Use `page()` when route-specific data is part of the page render. Use `layout()` for shared subtree props or metadata. Use `@rpc()` plus `pp.rpc()` when the browser needs to ask the server for more data after the page is already visible.
|
|
374
|
+
Use `page()` when route-specific data is part of the page render. Use `layout()` for shared subtree props or metadata. Use `@rpc()` plus `pp.rpc()` when the browser needs to ask the server for more data after the page is already visible.
|
|
338
375
|
|
|
339
376
|
A common pattern is:
|
|
340
377
|
|
|
@@ -366,8 +403,8 @@ If the first-render HTML is expensive to produce and safe to share between visit
|
|
|
366
403
|
If an AI agent is choosing how to load data in Caspian, apply these rules first.
|
|
367
404
|
|
|
368
405
|
- Put first-render data loading in `src/app/**/index.py`.
|
|
369
|
-
- Put shared section props in `layout.py` only when multiple child routes need the same shared data.
|
|
370
|
-
- Keep route-specific async I/O in `page()` or `@rpc()` so `layout.py` stays focused on shared subtree props or metadata.
|
|
406
|
+
- Put shared section props in `layout.py` only when multiple child routes need the same shared data.
|
|
407
|
+
- Keep route-specific async I/O in `page()` or `@rpc()` so `layout.py` stays focused on shared subtree props or metadata.
|
|
371
408
|
- For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md) before deciding where `page()` data and `@rpc()` actions belong.
|
|
372
409
|
- Treat RPC as the default read and write layer between PulsePoint code and Python route logic, especially for CRUD and interactive backend reads.
|
|
373
410
|
- Use `@rpc()` for backend functions that should be callable from the browser.
|
|
@@ -377,6 +414,7 @@ If an AI agent is choosing how to load data in Caspian, apply these rules first.
|
|
|
377
414
|
- Use [cache.md](./cache.md) when a route's initial HTML should be reused across requests and invalidated after writes.
|
|
378
415
|
- Use [state.md](./state.md) when an RPC mutation needs transient request-scoped success or error state outside the direct response payload.
|
|
379
416
|
- Use `onStream` for streamed responses and `onUploadProgress` for upload-aware client calls.
|
|
417
|
+
- For AI/LLM/chat token streaming, default to a generator `@rpc()` that `yield`s chunks (bridge a Python SDK stream with `async for ... yield`) consumed by `pp.rpc(..., { onStream })`. Do not reinvent one-way streaming with raw `fetch`/`ReadableStream`, `EventSource`, or WebSockets.
|
|
380
418
|
- Add `require_auth`, `allowed_roles`, and `limits` to sensitive RPC actions.
|
|
381
419
|
- Keep reusable database clients and service wrappers in `src/lib/`.
|
|
382
420
|
- Use `.venv/Lib/site-packages/casp/rpc.py` only when the task is about Caspian core RPC internals or framework debugging.
|
package/dist/docs/index.md
CHANGED
|
@@ -11,9 +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/websockets
|
|
16
|
-
- /docs/file-conventions
|
|
14
|
+
- /docs/file-uploads
|
|
15
|
+
- /docs/websockets
|
|
16
|
+
- /docs/file-conventions
|
|
17
17
|
- /docs/project-structure
|
|
18
18
|
- /docs/components
|
|
19
19
|
---
|
|
@@ -24,20 +24,20 @@ Treat these docs as reusable Caspian feature guidance. Treat `./caspian.config.j
|
|
|
24
24
|
|
|
25
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.
|
|
26
26
|
|
|
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
|
+
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.
|
|
28
28
|
|
|
29
29
|
## Default Stack
|
|
30
30
|
|
|
31
31
|
When generating or editing a Caspian app, treat these as the default choices unless the task explicitly requires something else:
|
|
32
32
|
|
|
33
|
-
- Use PulsePoint for reactive frontend behavior.
|
|
34
|
-
- For first-party HTML events and reactivity, use PulsePoint `on*` attributes, state, refs, effects, directives, and `pp.rpc()` instead of ordinary DOM wiring with ids, `data-*` state, `querySelector`, `addEventListener`, or manual `innerHTML`.
|
|
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.
|
|
33
|
+
- Use PulsePoint for reactive frontend behavior.
|
|
34
|
+
- For first-party HTML events and reactivity, use PulsePoint `on*` attributes, state, refs, effects, directives, and `pp.rpc()` instead of ordinary DOM wiring with ids, `data-*` state, `querySelector`, `addEventListener`, or manual `innerHTML`.
|
|
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.
|
|
36
36
|
- When `caspian.config.json` has `tailwindcss: true`, use Python `merge_classes(...)` plus browser `twMerge(...)` as the only supported Tailwind class-merging path.
|
|
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.
|
|
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.
|
|
40
|
-
- Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
|
|
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.
|
|
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.
|
|
40
|
+
- Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
|
|
41
41
|
|
|
42
42
|
## AI Doc Shape
|
|
43
43
|
|
|
@@ -83,11 +83,11 @@ The packaged Caspian docs referenced by this index live here:
|
|
|
83
83
|
- `database.md` - Prisma schema, migration, seed, and client-generation workflow for projects where `caspian.config.json` enables Prisma, plus Python-side helper caveats
|
|
84
84
|
- `auth.md` - Session-backed authentication with `casp.auth`, centralized `auth_config.py`, public-vs-private route mode guidance, RPC-first signout guidance, RBAC, and OAuth provider helpers
|
|
85
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
|
|
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
|
|
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
|
|
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
|
|
86
|
+
- `components.md` - Create reusable Python components, template-backed UI, single-file components with the inline `html(...)` helper, HTML-first `x-*` component tags, Python-import vs `@import` tag resolution and parent-scope slot resolution, the single-parent authored-root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
|
|
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
|
|
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
|
|
91
91
|
- `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
|
|
92
92
|
- `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
|
|
93
93
|
- `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
|
|
@@ -103,18 +103,18 @@ Preferred lookup order:
|
|
|
103
103
|
|
|
104
104
|
1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
|
|
105
105
|
2. Read `./caspian.config.json` before making any feature assumption. A doc existing in the package does not mean that feature is enabled in the current project.
|
|
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"`.
|
|
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.
|
|
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.
|
|
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.
|
|
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"`.
|
|
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.
|
|
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.
|
|
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.
|
|
118
118
|
|
|
119
119
|
## Maintenance
|
|
120
120
|
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -751,7 +751,7 @@ Upload with progress pattern:
|
|
|
751
751
|
</section>
|
|
752
752
|
```
|
|
753
753
|
|
|
754
|
-
Streaming pattern for server generators that return `text/event-stream
|
|
754
|
+
Streaming pattern for server generators that return `text/event-stream`. This is the default path for AI/LLM/chat token streaming: a generator `@rpc()` that `yield`s tokens (often by `async for`-ing over a Python SDK stream) consumed by `pp.rpc(..., { onStream })` that appends each chunk to reactive state. Do not reinvent one-way streaming with raw `fetch`/`ReadableStream`, `EventSource`, or a WebSocket; see [fetch-data.md](./fetch-data.md) for the Python side.
|
|
755
755
|
|
|
756
756
|
```html
|
|
757
757
|
<section>
|