caspian-utils 0.0.22 → 0.0.24
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 +79 -0
- package/dist/docs/auth.md +32 -2
- package/dist/docs/cache.md +7 -2
- package/dist/docs/components.md +42 -44
- package/dist/docs/core-runtime-map.md +83 -0
- package/dist/docs/fetch-data.md +26 -1
- package/dist/docs/file-uploads.md +17 -0
- package/dist/docs/index.md +16 -7
- package/dist/docs/metadata.md +17 -1
- package/dist/docs/project-structure.md +37 -7
- package/dist/docs/pulsepoint.md +28 -2
- package/dist/docs/routing.md +121 -33
- package/dist/docs/state.md +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: AI Validation Checklist
|
|
3
|
+
description: Use this page when testing whether AI can find the right Caspian docs, core files, and verification checkpoints for a task after documentation or runtime changes.
|
|
4
|
+
related:
|
|
5
|
+
title: Related docs
|
|
6
|
+
description: Start with the manifest, use the runtime map when needed, then validate against the matching feature guide and owning runtime behavior.
|
|
7
|
+
links:
|
|
8
|
+
- /docs/index
|
|
9
|
+
- /docs/core-runtime-map
|
|
10
|
+
- /docs/routing
|
|
11
|
+
- /docs/auth
|
|
12
|
+
- /docs/fetch-data
|
|
13
|
+
- /docs/pulsepoint
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
This page gives AI a small validation workflow for checking whether the packaged Caspian docs lead to the correct source files and runtime behaviors.
|
|
17
|
+
|
|
18
|
+
Use it after changing packaged docs, after changing `main.py` or installed `casp` internals, or when you want to verify that AI can resolve a task without drifting across unrelated files.
|
|
19
|
+
|
|
20
|
+
## Goal
|
|
21
|
+
|
|
22
|
+
The goal is not to restate feature docs. The goal is to verify four things quickly:
|
|
23
|
+
|
|
24
|
+
- AI starts from the correct manifest and feature gate.
|
|
25
|
+
- AI reaches the correct packaged feature doc.
|
|
26
|
+
- AI reaches the controlling app or runtime file.
|
|
27
|
+
- AI reaches a concrete behavior checkpoint before making assumptions about behavior.
|
|
28
|
+
|
|
29
|
+
## Fast Workflow
|
|
30
|
+
|
|
31
|
+
1. Read [index.md](./index.md) to discover the local Caspian docs.
|
|
32
|
+
2. Read `./caspian.config.json` before assuming any optional feature is enabled.
|
|
33
|
+
3. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read [core-runtime-map.md](./core-runtime-map.md).
|
|
34
|
+
4. Read the matching feature doc such as [routing.md](./routing.md), [auth.md](./auth.md), [fetch-data.md](./fetch-data.md), [pulsepoint.md](./pulsepoint.md), or [file-uploads.md](./file-uploads.md).
|
|
35
|
+
5. Verify the claim against the actual project code and the installed runtime.
|
|
36
|
+
6. Confirm the relevant behavior in the owning files surfaced by [core-runtime-map.md](./core-runtime-map.md) before treating the behavior as settled.
|
|
37
|
+
|
|
38
|
+
## Pass Criteria
|
|
39
|
+
|
|
40
|
+
A documentation path is in good shape when AI can do all of these without broad repo wandering:
|
|
41
|
+
|
|
42
|
+
- identify whether the feature is enabled from `caspian.config.json`
|
|
43
|
+
- choose the correct packaged doc from [index.md](./index.md)
|
|
44
|
+
- jump to the controlling runtime file through [core-runtime-map.md](./core-runtime-map.md) when needed
|
|
45
|
+
- distinguish app-owned files from installed framework internals
|
|
46
|
+
- identify the concrete behavior that must be verified in the owning file
|
|
47
|
+
- avoid suggesting changes to `main.py` or `.venv/Lib/site-packages/casp/**` unless the task truly requires runtime edits
|
|
48
|
+
|
|
49
|
+
## Representative Prompt Suite
|
|
50
|
+
|
|
51
|
+
Use prompts like these to check whether AI lands on the correct docs and files.
|
|
52
|
+
|
|
53
|
+
| Prompt | First docs AI should read | Controlling files AI should reach | Verification focus |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| Create a protected dashboard section with child routes and a shared shell. | [index.md](./index.md), [routing.md](./routing.md), [auth.md](./auth.md), [project-structure.md](./project-structure.md) | `src/app/**`, `src/lib/auth/auth_config.py`, `main.py`, `.venv/Lib/site-packages/casp/layout.py` | section layout ownership, route privacy mode, and child-route wrapping |
|
|
56
|
+
| Create a new contact page with interactive form behavior. | [index.md](./index.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md) | `src/app/**/index.html`, `main.py`, `.venv/Lib/site-packages/casp/components_compiler.py`, `.venv/Lib/site-packages/casp/scripts_type.py` | single-root template shape, script-inside-root authoring, and authored-vs-runtime boundaries |
|
|
57
|
+
| Add a file manager page with upload progress and persisted metadata. | [index.md](./index.md), [file-uploads.md](./file-uploads.md), [fetch-data.md](./fetch-data.md), [database.md](./database.md) | `src/app/**/index.html`, `src/app/**/index.py`, `src/lib/**`, `prisma/schema.prisma` when Prisma applies | route-owned upload actions, persisted metadata flow, and Prisma-backed storage boundaries |
|
|
58
|
+
| Explain why authored Caspian templates use a plain `<script>` instead of `type="text/pp"`. | [index.md](./index.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md), [routing.md](./routing.md) | `main.py`, `.venv/Lib/site-packages/casp/scripts_type.py`, `.venv/Lib/site-packages/casp/components_compiler.py` | script rewriting, `pp-component` injection, and authored-vs-runtime boundaries |
|
|
59
|
+
| Debug why `StateManager` does not persist across a full redirect. | [index.md](./index.md), [state.md](./state.md), [auth.md](./auth.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/state_manager.py` | wire vs non-wire reset behavior and `request.state.session` dependency |
|
|
60
|
+
| Trace the auth cookie and CSRF cookie names used in development. | [index.md](./index.md), [auth.md](./auth.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/auth.py` | development cookie scoping and CSRF or session naming ownership |
|
|
61
|
+
| Find how route params and query params reach `page()`. | [index.md](./index.md), [routing.md](./routing.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/layout.py` | path-dict delivery, query coercion, and `request` injection |
|
|
62
|
+
| 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 |
|
|
63
|
+
|
|
64
|
+
Treat the table as a prompt pack for spot checks, not as a full validation matrix.
|
|
65
|
+
|
|
66
|
+
## Common Failure Modes
|
|
67
|
+
|
|
68
|
+
- AI skips `caspian.config.json` and assumes an optional feature is enabled because a packaged doc exists.
|
|
69
|
+
- AI reads only the packaged feature doc and never checks `main.py` or the installed runtime.
|
|
70
|
+
- AI edits framework internals when the task only requires app-owned route or helper changes.
|
|
71
|
+
- AI treats runtime HTML examples as authored template examples.
|
|
72
|
+
- AI generates a route or component template with a valid-looking root element but leaves a sibling top-level `<script>` or second top-level element after it.
|
|
73
|
+
- AI decides behavior from memory without checking the owning implementation details.
|
|
74
|
+
|
|
75
|
+
## Decision Rule
|
|
76
|
+
|
|
77
|
+
- If the task is feature-oriented, start from [index.md](./index.md) and the matching feature doc.
|
|
78
|
+
- If the task is behavior-oriented and the owner is unclear, jump to [core-runtime-map.md](./core-runtime-map.md).
|
|
79
|
+
- If the task is still ambiguous after that, verify the controlling file and the specific behavior in scope before widening the search.
|
package/dist/docs/auth.md
CHANGED
|
@@ -5,6 +5,7 @@ related:
|
|
|
5
5
|
title: Related docs
|
|
6
6
|
description: Use the fetch-data guide when sign-in or signout happens through RPC, then use the state guide for transient auth-adjacent request state, validation to guard credentials, and routing or project structure to place auth files correctly.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/core-runtime-map
|
|
8
9
|
- /docs/fetch-data
|
|
9
10
|
- /docs/state
|
|
10
11
|
- /docs/validation
|
|
@@ -63,6 +64,7 @@ from casp.auth import (
|
|
|
63
64
|
- The installed framework implementation lives in `.venv/Lib/site-packages/casp/auth.py`.
|
|
64
65
|
- Treat `auth_config.py` as project code and `casp/auth.py` as framework code.
|
|
65
66
|
- If upstream docs and the installed implementation disagree, prefer the installed implementation for local project guidance.
|
|
67
|
+
- 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.
|
|
66
68
|
|
|
67
69
|
## Centralized Auth Settings
|
|
68
70
|
|
|
@@ -167,6 +169,7 @@ The installed auth code reads several values from `.env` when explicit values ar
|
|
|
167
169
|
- `AUTH_COOKIE_NAME` backs `AuthSettings.cookie_name`.
|
|
168
170
|
- `SESSION_LIFETIME_HOURS` controls `SessionMiddleware.max_age` in the current `main.py` bootstrap.
|
|
169
171
|
- `APP_ENV=production` enables secure session cookies and the `Secure` flag on the current CSRF cookie.
|
|
172
|
+
- `CASPIAN_BROWSER_SYNC_PORT` can override the development cookie scope suffix used by the current `main.py` bootstrap.
|
|
170
173
|
- `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_REDIRECT_URI` back `GoogleProvider`.
|
|
171
174
|
- `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` back `GithubProvider`.
|
|
172
175
|
|
|
@@ -174,6 +177,8 @@ Keep these secrets out of route files and out of committed source.
|
|
|
174
177
|
|
|
175
178
|
In the current bootstrap, set `AUTH_COOKIE_NAME` explicitly in `.env`. The pasted `main.py` uses `session` as the fallback `SessionMiddleware` cookie name, while `AuthSettings` falls back to `auth_token` when the env var is absent.
|
|
176
179
|
|
|
180
|
+
In development, the current `main.py` does not always use those base cookie names directly. `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name when `APP_ENV` is not `production`. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then from `settings/bs-config.json`, then from `PORT`. That means a local stack can emit names such as `session_5091` and `pp_csrf_5091` instead of the unsuffixed production names.
|
|
181
|
+
|
|
177
182
|
## Startup Wiring
|
|
178
183
|
|
|
179
184
|
Apply the centralized settings once during startup, before normal request handling begins.
|
|
@@ -279,7 +284,7 @@ Because Starlette runs the last-added middleware first, the effective request or
|
|
|
279
284
|
Current behavior by layer:
|
|
280
285
|
|
|
281
286
|
- `SessionMiddleware` provides `request.session` for the rest of the stack.
|
|
282
|
-
- `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a `pp_csrf`
|
|
287
|
+
- `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a scoped CSRF cookie based on `pp_csrf`, for example `pp_csrf_5091` in development or plain `pp_csrf` when no dev scope is active.
|
|
283
288
|
- `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, skips configured static asset paths, and enforces public, auth, private, and role-based route redirects.
|
|
284
289
|
- `RPCMiddleware` handles `POST` requests with `X-PP-RPC: true` and forwards them to Caspian's RPC handler after auth and session setup are already available.
|
|
285
290
|
|
|
@@ -449,6 +454,7 @@ A typical credential-based auth setup often uses this route shape:
|
|
|
449
454
|
src/
|
|
450
455
|
app/
|
|
451
456
|
(auth)/
|
|
457
|
+
layout.html
|
|
452
458
|
signin/
|
|
453
459
|
index.py
|
|
454
460
|
index.html
|
|
@@ -458,12 +464,17 @@ src/
|
|
|
458
464
|
signout/
|
|
459
465
|
index.py
|
|
460
466
|
dashboard/
|
|
461
|
-
index.py
|
|
462
467
|
index.html
|
|
468
|
+
layout.html
|
|
469
|
+
settings/
|
|
470
|
+
index.py
|
|
471
|
+
index.html
|
|
463
472
|
```
|
|
464
473
|
|
|
465
474
|
Use `guest_only()` on signin and signup routes, use `auth.sign_in(...)` inside the owning RPC action after validation and credential checks, prefer RPC for signout UI actions, and protect private destinations with `require_auth()` or central route policy.
|
|
466
475
|
|
|
476
|
+
When auth pages share a wrapper without adding a URL segment, use a route group such as `(auth)/layout.html`. When a protected area such as `dashboard` owns multiple child routes, apply the section layout pattern from [routing.md](./routing.md): put the shared shell in `dashboard/layout.html` and place child routes such as `dashboard/settings/index.html` beneath it.
|
|
477
|
+
|
|
467
478
|
Preferred signout pattern: auth-protected RPC from a page or component.
|
|
468
479
|
|
|
469
480
|
Use this when the logout trigger lives in a page, layout, header, dropdown, or reusable component. The HTML can live at page level or component level.
|
|
@@ -531,6 +542,24 @@ def page():
|
|
|
531
542
|
})
|
|
532
543
|
```
|
|
533
544
|
|
|
545
|
+
If the protected destination is a section rather than a single page, prefer a folder-level layout for the shared shell.
|
|
546
|
+
|
|
547
|
+
```text
|
|
548
|
+
src/
|
|
549
|
+
app/
|
|
550
|
+
dashboard/
|
|
551
|
+
layout.html
|
|
552
|
+
index.html
|
|
553
|
+
settings/
|
|
554
|
+
index.py
|
|
555
|
+
index.html
|
|
556
|
+
billing/
|
|
557
|
+
index.py
|
|
558
|
+
index.html
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
In that pattern, `dashboard/layout.html` owns the shared sidebar, header, and frame, while each child route decides whether it needs its own `index.py` for auth checks, metadata, RPC actions, or server-side data.
|
|
562
|
+
|
|
534
563
|
For signup flows, use the same route ownership pattern as signin: validate the input, create the user, build a safe payload without password fields, and then call `auth.sign_in(...)` with the redirect target you want.
|
|
535
564
|
|
|
536
565
|
Signup example:
|
|
@@ -765,6 +794,7 @@ If an AI agent is deciding how to handle auth in Caspian, apply these rules firs
|
|
|
765
794
|
- Decide route mode early in `src/lib/auth/auth_config.py`: use `is_all_routes_private=True` when most routes should require auth, otherwise keep `is_all_routes_private=False` and list protected routes in `private_routes`.
|
|
766
795
|
- In app-owned starter config, treat `is_all_routes_private=False` as the default starting point, which means routes begin public until the app opts into stricter protection.
|
|
767
796
|
- 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.
|
|
797
|
+
- 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`.
|
|
768
798
|
- Apply settings at startup in `main.py` with `configure_auth(build_auth_settings())`.
|
|
769
799
|
- Register provider instances in `main.py` with `Auth.set_providers(...)` when Google or GitHub OAuth is enabled.
|
|
770
800
|
- Use `auth.sign_in(...)` and `auth.sign_out(...)` for session lifecycle changes.
|
package/dist/docs/cache.md
CHANGED
|
@@ -5,6 +5,7 @@ related:
|
|
|
5
5
|
title: Related docs
|
|
6
6
|
description: Use the routing guide to place route-level cache declarations correctly, then pair cache with fetch-data when deciding what can be safely served from reused HTML.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/core-runtime-map
|
|
8
9
|
- /docs/routing
|
|
9
10
|
- /docs/fetch-data
|
|
10
11
|
- /docs/project-structure
|
|
@@ -27,6 +28,8 @@ from casp.cache_handler import Cache, CacheHandler
|
|
|
27
28
|
|
|
28
29
|
The current installed implementation lives in `.venv/Lib/site-packages/casp/cache_handler.py`.
|
|
29
30
|
|
|
31
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when a cache task crosses `main.py` request flow, cache lookup timing, and the installed cache-handler internals.
|
|
32
|
+
|
|
30
33
|
The real API surface is:
|
|
31
34
|
|
|
32
35
|
- declarative route config with `Cache(ttl=..., enabled=...)`
|
|
@@ -41,7 +44,8 @@ Use caching when a route is expensive to render but the final HTML is stable eno
|
|
|
41
44
|
|
|
42
45
|
- Use `Cache(...)` at module scope in a route's `index.py` when you want that route to participate in Caspian's page cache.
|
|
43
46
|
- Cache public HTML that is safe to share between visitors.
|
|
44
|
-
- Avoid caching dashboards, auth callbacks, request-specific error states, flash-message flows, or any page whose HTML changes per user.
|
|
47
|
+
- Avoid caching personalized section shells such as authenticated dashboards, auth callbacks, request-specific error states, flash-message flows, or any page whose HTML changes per user.
|
|
48
|
+
- If a dashboard, account area, admin area, or grouped subtree uses a parent `layout.html`, evaluate each child route separately. Public child pages may still be cacheable, but personalized section layouts generally are not.
|
|
45
49
|
- Invalidate cached pages after writes, publishes, deletes, or admin actions that change what those routes should render.
|
|
46
50
|
- Treat page caching as HTML reuse, not as a replacement for database caching or RPC response caching.
|
|
47
51
|
|
|
@@ -237,7 +241,7 @@ Good candidates for page caching are:
|
|
|
237
241
|
|
|
238
242
|
Poor candidates are:
|
|
239
243
|
|
|
240
|
-
- authenticated dashboards
|
|
244
|
+
- authenticated dashboards and other personalized section layouts
|
|
241
245
|
- per-user feeds or notification views
|
|
242
246
|
- forms whose server-rendered HTML includes request-specific or sensitive state
|
|
243
247
|
- pages that need sub-second freshness unless you have reliable invalidation hooks
|
|
@@ -249,6 +253,7 @@ If an AI agent is deciding whether or how to cache a Caspian route, apply these
|
|
|
249
253
|
- Use `Cache` from `casp.cache_handler` for route-level page caching.
|
|
250
254
|
- Prefer `cache_settings = Cache(...)` at module scope over a bare call when writing maintained project code.
|
|
251
255
|
- Use `enabled=False` for pages whose HTML is personalized, auth-sensitive, or too volatile to reuse safely.
|
|
256
|
+
- When a user asks about caching a dashboard, admin area, account area, or grouped subtree, check whether the shared `layout.html` is personalized before caching any child route. Shared authenticated shells are usually poor cache candidates even when some public child pages are cacheable.
|
|
252
257
|
- Use `CacheHandler.invalidate_by_uri(...)` after writes that change cached public content.
|
|
253
258
|
- Use `CacheHandler.reset_cache()` only for targeted maintenance, debugging, or full cache clears.
|
|
254
259
|
- Use leading-slash URIs consistently when interacting with `CacheHandler`.
|
package/dist/docs/components.md
CHANGED
|
@@ -6,6 +6,7 @@ related:
|
|
|
6
6
|
description: Use the structure guide for file placement, the routing guide for route templates, the PulsePoint guide for browser-side scripts, and the data guide for component-owned RPC flows.
|
|
7
7
|
links:
|
|
8
8
|
- /docs/project-structure
|
|
9
|
+
- /docs/core-runtime-map
|
|
9
10
|
- /docs/routing
|
|
10
11
|
- /docs/pulsepoint
|
|
11
12
|
- /docs/fetch-data
|
|
@@ -29,6 +30,17 @@ As the app grows, treat `src/components/` as the default home for reusable appli
|
|
|
29
30
|
- Use `render_html(...)` with a same-name `.html` file when the component has more markup, PulsePoint behavior, or clearer separation between Python logic and UI.
|
|
30
31
|
- Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
|
|
31
32
|
|
|
33
|
+
## Framework Internals Note
|
|
34
|
+
|
|
35
|
+
When the task is about component internals rather than normal app-owned components, these runtime files are the most relevant:
|
|
36
|
+
|
|
37
|
+
- `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, and component loading.
|
|
38
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution, root validation, and `pp-component` injection.
|
|
39
|
+
- `.venv/Lib/site-packages/casp/html_attrs.py` owns `get_attributes(...)` and the Python-side `merge_classes(...)` contract.
|
|
40
|
+
- `.venv/Lib/site-packages/casp/syntax_compiler.py` owns Caspian template syntax transpilation before Jinja render.
|
|
41
|
+
|
|
42
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader runtime-module map.
|
|
43
|
+
|
|
32
44
|
## Basic Component
|
|
33
45
|
|
|
34
46
|
This is the simplest pattern: accept props, assemble the final class value, and return HTML.
|
|
@@ -106,6 +118,8 @@ Place component imports at the top of the HTML template, above the authored root
|
|
|
106
118
|
|
|
107
119
|
The import comment is the bridge between the Python component export and the `x-*` tag you author in HTML.
|
|
108
120
|
|
|
121
|
+
In section-based apps, follow the same mental model as the Next.js App Router: import shared shell components such as sidebars, topbars, breadcrumbs, and section frames in the parent folder's `layout.html`, then import page-specific components in each child route's `index.html`.
|
|
122
|
+
|
|
109
123
|
Treat `<!-- @import ... -->` as a file-level directive, not as rendered markup. It does not count as the template root, and it should not be nested inside the root wrapper element.
|
|
110
124
|
|
|
111
125
|
- `<!-- @import Container from "../components" -->` resolves `Container.py` from that folder.
|
|
@@ -208,62 +222,35 @@ This keeps the component easy to read: Python owns the server-side logic and tem
|
|
|
208
222
|
|
|
209
223
|
## Auto-Injected `pp-component` And PulsePoint Script Type
|
|
210
224
|
|
|
211
|
-
Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source.
|
|
225
|
+
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).
|
|
212
226
|
|
|
213
227
|
- Do not manually add `pp-component="..."` to route templates, layout templates, or component HTML files.
|
|
214
228
|
- Do not manually add `type="text/pp"` to authored PulsePoint scripts.
|
|
215
229
|
- The Python render pipeline injects `pp-component` onto the single root element during render.
|
|
216
230
|
- `main.py` runs `transform_scripts(...)`, which rewrites authored body `<script>` tags to `<script type="text/pp">` before the browser runtime mounts.
|
|
217
|
-
- Add a plain `<script>` only when the route or component actually needs PulsePoint logic.
|
|
218
|
-
-
|
|
219
|
-
|
|
220
|
-
Examples in runtime docs often show rendered HTML that already contains `pp-component` and `type="text/pp"`. That is the final output shape, not the normal authoring pattern.
|
|
221
|
-
|
|
222
|
-
### Authored Source vs Rendered Output
|
|
223
|
-
|
|
224
|
-
Author this:
|
|
225
|
-
|
|
226
|
-
`Counter.html`
|
|
227
|
-
|
|
228
|
-
```html
|
|
229
|
-
<div>
|
|
230
|
-
<h3>[[ label ]]</h3>
|
|
231
|
-
<button onclick="setCount(count + 1)">
|
|
232
|
-
{count}
|
|
233
|
-
</button>
|
|
234
|
-
|
|
235
|
-
<script>
|
|
236
|
-
const [count, setCount] = pp.state(0);
|
|
237
|
-
</script>
|
|
238
|
-
</div>
|
|
239
|
-
```
|
|
231
|
+
- Add a plain `<script>` only when the route or component actually needs PulsePoint logic, and keep it inside that single root element.
|
|
232
|
+
- If a runtime example already shows `pp-component` and `type="text/pp"`, treat it as rendered output rather than authored source.
|
|
240
233
|
|
|
241
|
-
|
|
234
|
+
## Single-Root Rule
|
|
242
235
|
|
|
243
|
-
|
|
244
|
-
<div pp-component="counter_ab12cd34">
|
|
245
|
-
<h3>Clicks</h3>
|
|
246
|
-
<button onclick="setCount(count + 1)">0</button>
|
|
236
|
+
Every component HTML template must render exactly one authored top-level parent node.
|
|
247
237
|
|
|
248
|
-
|
|
249
|
-
const [count, setCount] = pp.state(0);
|
|
250
|
-
</script>
|
|
251
|
-
</div>
|
|
252
|
-
```
|
|
238
|
+
The same rule applies to route and layout templates such as `src/app/**/index.html` and `src/app/**/layout.html`. See [routing.md](./routing.md) and [pulsepoint.md](./pulsepoint.md) for the non-component versions of the same authoring contract.
|
|
253
239
|
|
|
254
|
-
|
|
240
|
+
Top-of-file `<!-- @import ... -->` directives are allowed before that root element and do not violate the single-root rule.
|
|
255
241
|
|
|
256
|
-
|
|
242
|
+
In source, that parent may be a native HTML element or a single imported `x-*` component tag. After component expansion, the template must still resolve to one final native HTML root so Caspian has exactly one place to inject `pp-component`.
|
|
257
243
|
|
|
258
|
-
|
|
244
|
+
This is not just style guidance. The installed compiler injects `pp-component` onto the final root element, and it raises an error when the template has no root, multiple sibling roots, a sibling script after the root, or stray top-level text.
|
|
259
245
|
|
|
260
|
-
|
|
246
|
+
For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent node. If the template needs a PulsePoint script, keep that script inside the same parent root.
|
|
261
247
|
|
|
262
|
-
|
|
248
|
+
Failure shape to avoid:
|
|
263
249
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
250
|
+
- one root element followed by a sibling `<script>`
|
|
251
|
+
- two top-level sibling elements
|
|
252
|
+
- top-level text outside the root
|
|
253
|
+
- an imported `x-*` root that expands to multiple native siblings
|
|
267
254
|
|
|
268
255
|
Good:
|
|
269
256
|
|
|
@@ -296,9 +283,19 @@ Also bad:
|
|
|
296
283
|
<p>Body</p>
|
|
297
284
|
```
|
|
298
285
|
|
|
299
|
-
Also
|
|
286
|
+
Also bad:
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<x-card>
|
|
290
|
+
<h2>Title</h2>
|
|
291
|
+
</x-card>
|
|
292
|
+
|
|
293
|
+
<script>
|
|
294
|
+
const [open, setOpen] = pp.state(false);
|
|
295
|
+
</script>
|
|
296
|
+
```
|
|
300
297
|
|
|
301
|
-
|
|
298
|
+
If you choose an imported `x-*` component as the authored root of the file, make sure it resolves to one native HTML root when compiled. Do not leave an unresolved component tag as the root, and do not emit multiple top-level component siblings.
|
|
302
299
|
|
|
303
300
|
## Props, Types, And Children
|
|
304
301
|
|
|
@@ -355,6 +352,7 @@ Keep synchronous components as the default. Switch to `async def` only when the
|
|
|
355
352
|
## Best Practices
|
|
356
353
|
|
|
357
354
|
- Put reusable components in `src/components/` and keep route files in `src/app/`.
|
|
355
|
+
- 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`.
|
|
358
356
|
- If the component includes PulsePoint behavior, prefer a thin Python wrapper plus a same-name `.html` template.
|
|
359
357
|
- Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
|
|
360
358
|
- Accept `children` or `**props` when the component should support nested content.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Core Runtime Map
|
|
3
|
+
description: Use this page when the task touches `main.py`, installed `casp` internals, or when AI needs to know which core file controls a behavior and which packaged doc explains it.
|
|
4
|
+
related:
|
|
5
|
+
title: Related docs
|
|
6
|
+
description: Start with the structure guide, then jump from the runtime file to the matching feature doc.
|
|
7
|
+
links:
|
|
8
|
+
- /docs/project-structure
|
|
9
|
+
- /docs/routing
|
|
10
|
+
- /docs/auth
|
|
11
|
+
- /docs/fetch-data
|
|
12
|
+
- /docs/pulsepoint
|
|
13
|
+
- /docs/index
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
This page maps the app entry point and the installed Caspian runtime modules to the packaged docs that explain them.
|
|
17
|
+
|
|
18
|
+
Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib/site-packages/casp/**` and you need the fastest path from symptom to source file.
|
|
19
|
+
|
|
20
|
+
## When To Read This Page
|
|
21
|
+
|
|
22
|
+
- a packaged doc mentions a runtime contract and you need the exact file that implements it
|
|
23
|
+
- `main.py` or the installed `casp` package disagrees with older notes or examples
|
|
24
|
+
- the task is about framework internals rather than normal app-owned route or component code
|
|
25
|
+
- AI needs to know which packaged doc should be kept aligned with a core-file change
|
|
26
|
+
|
|
27
|
+
## App Entry Point
|
|
28
|
+
|
|
29
|
+
| Concern | Core file | Read first | Why it matters |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| App bootstrap and request flow | [main.py](../../../../main.py) | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, static asset routes, middleware order, route registration, cache check and save, exception handlers, and final HTML transforms all live here. |
|
|
32
|
+
|
|
33
|
+
Important current `main.py` behaviors AI should keep in mind:
|
|
34
|
+
|
|
35
|
+
- 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`.
|
|
36
|
+
- `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.
|
|
37
|
+
- The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
|
|
38
|
+
- Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
|
|
39
|
+
- Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, so the effective request order is reversed at runtime.
|
|
40
|
+
|
|
41
|
+
## Installed Runtime Map
|
|
42
|
+
|
|
43
|
+
| Runtime file | Primary responsibility | Read these docs |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, and the synchronous `layout()` contract | [routing.md](./routing.md), [metadata.md](./metadata.md) |
|
|
46
|
+
| [.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) |
|
|
47
|
+
| [.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) |
|
|
48
|
+
| [.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) |
|
|
49
|
+
| [.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) |
|
|
50
|
+
| [.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) |
|
|
51
|
+
| [.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) |
|
|
52
|
+
| [.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) |
|
|
53
|
+
| [.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) |
|
|
54
|
+
| [.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) |
|
|
55
|
+
| [.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) |
|
|
56
|
+
| [.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) |
|
|
57
|
+
| [.venv/Lib/site-packages/casp/syntax_compiler.py](../../../../.venv/Lib/site-packages/casp/syntax_compiler.py) | transpiling Caspian `<[[ ... ]]>` and `<template casp-*>` syntax before template render | [components.md](./components.md), [routing.md](./routing.md) |
|
|
58
|
+
|
|
59
|
+
Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. Read them only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
|
|
60
|
+
|
|
61
|
+
## Verification Focus
|
|
62
|
+
|
|
63
|
+
Use these behavior checkpoints when AI needs the fastest verification path for a runtime behavior before widening the search.
|
|
64
|
+
|
|
65
|
+
| Runtime area | Verify these behaviors |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `main.py` routing and request flow | route registration, path and query injection, static asset handling, and exception rendering |
|
|
68
|
+
| `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
|
|
69
|
+
| `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
|
|
70
|
+
| `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
|
|
71
|
+
| `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, syntax transpilation, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
|
|
72
|
+
| `casp.state_manager` | wire vs non-wire reset behavior, request-state persistence assumptions, and AttributeDict access |
|
|
73
|
+
| `casp.cache_handler` | filename generation, manifest writes, TTL handling, and invalidation behavior |
|
|
74
|
+
| `casp.caspian_config` | config parsing, files index building, and Next.js-style route inventory behavior |
|
|
75
|
+
|
|
76
|
+
When a runtime or packaged-doc change touches one of these areas, verify the relevant behavior in the owning source file before inferring behavior from memory.
|
|
77
|
+
|
|
78
|
+
## Decision Rule
|
|
79
|
+
|
|
80
|
+
- Start with the packaged feature doc that matches the task.
|
|
81
|
+
- Use this runtime map when that doc needs to point AI to the controlling source file.
|
|
82
|
+
- Prefer app code and `main.py` over packaged docs when the task is about the current workspace behavior.
|
|
83
|
+
- Do not modify `main.py` or `.venv/Lib/site-packages/casp/**` just because a packaged doc mentions them. Edit those files only when the user asks for a runtime change or when the task is explicitly about framework behavior.
|
package/dist/docs/fetch-data.md
CHANGED
|
@@ -8,6 +8,7 @@ related:
|
|
|
8
8
|
- /docs/auth
|
|
9
9
|
- /docs/mcp
|
|
10
10
|
- /docs/state
|
|
11
|
+
- /docs/core-runtime-map
|
|
11
12
|
- /docs/database
|
|
12
13
|
- /docs/file-uploads
|
|
13
14
|
- /docs/cache
|
|
@@ -38,6 +39,8 @@ In practice, most pages use both:
|
|
|
38
39
|
|
|
39
40
|
If a route only needs UI and does not need first-render data, metadata, or other backend behavior, skip `index.py` and keep the page in `index.html` alone.
|
|
40
41
|
|
|
42
|
+
When a page belongs to a grouped subtree such as a dashboard, account area, admin section, or docs section, apply the section layout pattern from [routing.md](./routing.md): keep the shared shell in the parent folder's `layout.html` and let each child route own only its route-specific `index.html` and optional `index.py`.
|
|
43
|
+
|
|
41
44
|
## Default Data Rule
|
|
42
45
|
|
|
43
46
|
- Use `page()` for async or route-level data required before HTML renders, and use `layout()` only for synchronous shared props or metadata.
|
|
@@ -75,6 +78,7 @@ Notes:
|
|
|
75
78
|
|
|
76
79
|
- Prefer `async def page()` when your database or API client is async-capable.
|
|
77
80
|
- Put shared section-level props in `layout.py` when multiple child routes need the same synchronous payload. The current layout engine does not await `layout()`.
|
|
81
|
+
- 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.
|
|
78
82
|
- Keep reusable database or API clients under `src/lib/`; keep route-specific orchestration in `src/app/`.
|
|
79
83
|
|
|
80
84
|
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.
|
|
@@ -86,7 +90,8 @@ Use RPC when the browser needs to call Python after the initial page load.
|
|
|
86
90
|
Framework internals note:
|
|
87
91
|
|
|
88
92
|
- The `@rpc()` decorator and Caspian RPC bridge internals live in `.venv/Lib/site-packages/casp/rpc.py`.
|
|
89
|
-
-
|
|
93
|
+
- Streamed response helpers such as `SSE` and `ServerSentEvent` live in `.venv/Lib/site-packages/casp/streaming.py`.
|
|
94
|
+
- Treat those files as framework code. Read them when you need to understand or debug Caspian's RPC or streaming behavior, not when you are adding normal app-level actions.
|
|
90
95
|
|
|
91
96
|
Define a server action with `@rpc()`:
|
|
92
97
|
|
|
@@ -177,6 +182,8 @@ Client example:
|
|
|
177
182
|
|
|
178
183
|
Use streaming when the user should see partial progress instead of waiting for a single final payload.
|
|
179
184
|
|
|
185
|
+
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.
|
|
186
|
+
|
|
180
187
|
## File Uploads
|
|
181
188
|
|
|
182
189
|
RPC actions can accept FastAPI upload types, but the preferred Caspian pattern is route-local upload actions in `src/app/**/index.py`, shared persistence helpers in `src/lib/`, and reactive client updates through `pp.rpc()`.
|
|
@@ -288,6 +295,23 @@ A common pattern is:
|
|
|
288
295
|
- Render that data into the HTML template
|
|
289
296
|
- Use RPC for subsequent filters, mutations, refreshes, streaming, or uploads
|
|
290
297
|
|
|
298
|
+
For section-based apps, that pattern usually lives inside a parent layout:
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
src/
|
|
302
|
+
app/
|
|
303
|
+
dashboard/
|
|
304
|
+
layout.html
|
|
305
|
+
reports/
|
|
306
|
+
index.py
|
|
307
|
+
index.html
|
|
308
|
+
users/
|
|
309
|
+
index.py
|
|
310
|
+
index.html
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
In that pattern, `dashboard/layout.html` owns the shared frame while each child route handles its own first-render data in `index.py` and its own interactive updates through `@rpc()`.
|
|
314
|
+
|
|
291
315
|
If the first-render HTML is expensive to produce and safe to share between visitors, add route-level caching with [cache.md](./cache.md) and invalidate affected URIs after successful mutations.
|
|
292
316
|
|
|
293
317
|
## AI Retrieval Notes
|
|
@@ -297,6 +321,7 @@ If an AI agent is choosing how to load data in Caspian, apply these rules first.
|
|
|
297
321
|
- Put first-render data loading in `src/app/**/index.py`.
|
|
298
322
|
- Put shared section props in `layout.py` only when multiple child routes need the same synchronous data.
|
|
299
323
|
- Keep async I/O in `page()` or `@rpc()` because the current layout engine does not await `layout()`.
|
|
324
|
+
- For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md) before deciding where `page()` data and `@rpc()` actions belong.
|
|
300
325
|
- Treat RPC as the default read and write layer between PulsePoint code and Python route logic, especially for CRUD and interactive backend reads.
|
|
301
326
|
- Use `@rpc()` for backend functions that should be callable from the browser.
|
|
302
327
|
- Use `pp.rpc()` for client-side calls; do not prefer older `pp.fetchFunction()` wording.
|
|
@@ -18,6 +18,8 @@ This page documents the recommended file upload and file manager pattern for Cas
|
|
|
18
18
|
|
|
19
19
|
Treat uploads as normal route behavior. Keep the owning browser UI in the route template, keep upload and delete `@rpc()` actions in the owning route `index.py`, and move reusable storage or persistence helpers into `src/lib/`.
|
|
20
20
|
|
|
21
|
+
If the file manager lives inside a grouped subtree such as a dashboard, account area, or admin area, apply the section layout pattern from [routing.md](./routing.md): keep the shared shell in the parent folder's `layout.html` and keep the file-manager route itself in a child folder.
|
|
22
|
+
|
|
21
23
|
## Default Pattern
|
|
22
24
|
|
|
23
25
|
- Keep first-render file manager data in `page()` so the initial HTML already contains the current asset list and storage summary.
|
|
@@ -52,6 +54,20 @@ The normal Caspian file-manager flow is:
|
|
|
52
54
|
6. The RPC action returns refreshed manager data.
|
|
53
55
|
7. PulsePoint replaces the route state and `pp-for` rerenders the list.
|
|
54
56
|
|
|
57
|
+
Section example:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
src/
|
|
61
|
+
app/
|
|
62
|
+
dashboard/
|
|
63
|
+
layout.html
|
|
64
|
+
files/
|
|
65
|
+
index.py
|
|
66
|
+
index.html
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
In that pattern, `dashboard/layout.html` owns the shared dashboard shell, while `dashboard/files/index.py` owns the initial file-manager payload and the upload or delete `@rpc()` actions.
|
|
70
|
+
|
|
55
71
|
## Example Route-Local RPC
|
|
56
72
|
|
|
57
73
|
```python
|
|
@@ -171,4 +187,5 @@ const PUBLIC_IGNORE_DIRS = ["uploads"];
|
|
|
171
187
|
- Use `database.md` when Prisma models or relations must change for upload metadata.
|
|
172
188
|
- Use `validation.md` for MIME, extension, and other boundary checks, then keep explicit size and auth checks in the owning RPC action.
|
|
173
189
|
- Use `project-structure.md` for placement rules, especially `src/app/` versus `src/lib/` and `public/uploads/`.
|
|
190
|
+
- For grouped file-manager sections, follow the section layout pattern in [routing.md](./routing.md) and keep the upload page in a child route folder beneath the shared shell.
|
|
174
191
|
- Use `commands.md` and `settings/bs-config.ts` when uploads should not trigger BrowserSync reloads during `npm run dev`.
|
package/dist/docs/index.md
CHANGED
|
@@ -5,8 +5,10 @@ related:
|
|
|
5
5
|
title: Next Steps
|
|
6
6
|
description: Start with installation, review the CLI commands, then use the structure and component guides to place Caspian files correctly.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/ai-validation-checklist
|
|
8
9
|
- /docs/installation
|
|
9
10
|
- /docs/commands
|
|
11
|
+
- /docs/core-runtime-map
|
|
10
12
|
- /docs/mcp
|
|
11
13
|
- /docs/file-uploads
|
|
12
14
|
- /docs/project-structure
|
|
@@ -26,6 +28,7 @@ Before making feature, tooling, or file-placement decisions in a Caspian project
|
|
|
26
28
|
When generating or editing a Caspian app, treat these as the default choices unless the task explicitly requires something else:
|
|
27
29
|
|
|
28
30
|
- Use PulsePoint for reactive frontend behavior.
|
|
31
|
+
- 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.
|
|
29
32
|
- When `caspian.config.json` has `tailwindcss: true`, use Python `merge_classes(...)` plus browser `twMerge(...)` as the only supported Tailwind class-merging path.
|
|
30
33
|
- Use `@rpc()` plus `pp.rpc()` for browser-triggered reads, writes, streaming, and uploads.
|
|
31
34
|
- Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
|
|
@@ -38,12 +41,15 @@ The packaged Caspian docs referenced by this index live here:
|
|
|
38
41
|
|
|
39
42
|
## Available Documents
|
|
40
43
|
|
|
44
|
+
- `index.md` - packaged docs manifest and the default first entry point for AI retrieval in this directory
|
|
45
|
+
- `ai-validation-checklist.md` - workflow and representative prompts for checking whether AI can find the correct Caspian docs, core files, and verification checkpoints
|
|
41
46
|
- `installation.md` - First-time setup flow for creating a new Caspian application
|
|
42
47
|
- `commands.md` - Main Caspian CLI workflows for project creation, generation, updates, and config-aware maintenance
|
|
48
|
+
- `core-runtime-map.md` - map of `main.py` plus installed `casp` modules to the packaged docs that explain them and the behaviors AI should trace there
|
|
43
49
|
- `mcp.md` - MCP-specific layout, launch flow, and AI routing rules for projects where `caspian.config.json` enables MCP
|
|
44
50
|
- `database.md` - Prisma schema, migration, seed, and client-generation workflow for projects where `caspian.config.json` enables Prisma, plus Python-side helper caveats
|
|
45
51
|
- `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
|
|
46
|
-
- `components.md` - Create reusable Python components, template-backed UI, HTML-first `x-*` component tags, the single-parent root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
|
|
52
|
+
- `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
|
|
47
53
|
- `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, client-side behaviors, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
|
|
48
54
|
- `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
|
|
49
55
|
- `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
|
|
@@ -51,7 +57,7 @@ The packaged Caspian docs referenced by this index live here:
|
|
|
51
57
|
- `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
|
|
52
58
|
- `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
|
|
53
59
|
- `metadata.md` - Static and dynamic metadata, SEO inheritance, and Open Graph or Twitter card tags
|
|
54
|
-
- `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, route groups, nested layouts,
|
|
60
|
+
- `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, dashboard and section layouts, route groups, nested layouts, single-parent authored templates, and backend Python companions that do not own visible markup
|
|
55
61
|
- `project-structure.md` - Default Caspian layout and where route files, reusable UI in `src/components/`, reusable non-UI code in `src/lib/`, and database files belong
|
|
56
62
|
|
|
57
63
|
## AI Retrieval Notes
|
|
@@ -62,11 +68,14 @@ Preferred lookup order:
|
|
|
62
68
|
|
|
63
69
|
1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
|
|
64
70
|
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.
|
|
65
|
-
3.
|
|
66
|
-
4.
|
|
67
|
-
5.
|
|
68
|
-
6.
|
|
69
|
-
7.
|
|
71
|
+
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"`.
|
|
72
|
+
4. 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`.
|
|
73
|
+
5. 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.
|
|
74
|
+
6. 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.
|
|
75
|
+
7. 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`, and `file-uploads.md` for task-specific guidance.
|
|
76
|
+
8. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
|
|
77
|
+
9. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
|
|
78
|
+
10. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
|
|
70
79
|
|
|
71
80
|
## Maintenance
|
|
72
81
|
|
package/dist/docs/metadata.md
CHANGED
|
@@ -31,7 +31,9 @@ Metadata is typically defined in one of two files:
|
|
|
31
31
|
- `layout.py` for defaults shared by everything below that folder
|
|
32
32
|
- `index.py` for route-specific metadata
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
For dashboard sections, account areas, docs sections, and route groups, apply the section layout pattern from [routing.md](./routing.md): put shared metadata defaults in the section's `layout.py`, then override them in child `index.py` files only when a leaf page needs different SEO.
|
|
35
|
+
|
|
36
|
+
On routes that render UI, keep the page markup in the sibling `index.html` and keep shared wrapper markup in `layout.html`. `index.py` and `layout.py` are metadata and backend companions, not the places to store visible route or layout HTML. If a route needs no metadata or backend behavior, omit `index.py` and keep the page as `index.html` only.
|
|
35
37
|
|
|
36
38
|
The current `Metadata` implementation supports three fields:
|
|
37
39
|
|
|
@@ -118,6 +120,8 @@ Priority order:
|
|
|
118
120
|
2. Nested `layout.py` files override those defaults for a section.
|
|
119
121
|
3. `index.py` metadata or runtime `Metadata(...)` inside the route has the highest priority.
|
|
120
122
|
|
|
123
|
+
For example, a dashboard section usually keeps shared defaults in `src/app/dashboard/layout.py`, while a grouping folder that should stay out of the URL can keep its shared defaults in `src/app/(marketing)/layout.py`.
|
|
124
|
+
|
|
121
125
|
Within a single file, the layout engine applies static metadata first and then applies runtime metadata from `Metadata(...)`, so runtime values win for matching keys.
|
|
122
126
|
|
|
123
127
|
If a child route overrides only the title, the description and other fields continue to fall through from the nearest parent that defines them.
|
|
@@ -128,6 +132,14 @@ Example structure:
|
|
|
128
132
|
src/
|
|
129
133
|
app/
|
|
130
134
|
layout.py
|
|
135
|
+
(marketing)/
|
|
136
|
+
layout.py
|
|
137
|
+
pricing/
|
|
138
|
+
index.py
|
|
139
|
+
dashboard/
|
|
140
|
+
layout.py
|
|
141
|
+
settings/
|
|
142
|
+
index.py
|
|
131
143
|
blog/
|
|
132
144
|
layout.py
|
|
133
145
|
[slug]/
|
|
@@ -137,6 +149,7 @@ src/
|
|
|
137
149
|
Typical result:
|
|
138
150
|
|
|
139
151
|
- Root layout defines the site-wide title template and default description.
|
|
152
|
+
- Route-group or section layouts such as `(marketing)/layout.py` or `dashboard/layout.py` define shared defaults for that subtree.
|
|
140
153
|
- Blog layout overrides section-level fields for `/blog/*`.
|
|
141
154
|
- Blog page overrides the final post title and any page-specific social tags.
|
|
142
155
|
|
|
@@ -185,6 +198,8 @@ Keep visual layout data and SEO metadata separate.
|
|
|
185
198
|
|
|
186
199
|
- Put site-wide defaults in `src/app/layout.py`.
|
|
187
200
|
- Put section defaults in nested `layout.py` files such as `src/app/blog/layout.py`.
|
|
201
|
+
- For dashboard, admin, account, docs, or grouped sections with child routes, put the shared defaults in that section folder's `layout.py` and keep the shared wrapper in the sibling `layout.html`.
|
|
202
|
+
- Use a route-group `layout.py` such as `src/app/(marketing)/layout.py` only when the grouping folder should not appear in the final URL.
|
|
188
203
|
- Put page-specific static metadata in `index.py`.
|
|
189
204
|
- Put dynamic metadata inside `page()` after you have route params or fetched data.
|
|
190
205
|
|
|
@@ -196,6 +211,7 @@ If an AI agent is deciding where to put SEO fields, apply these rules first.
|
|
|
196
211
|
- Prefer module-level `metadata = Metadata(...)` for static routes.
|
|
197
212
|
- Instantiate `Metadata(...)` inside `page()` when metadata depends on params, fetched records, or generated content.
|
|
198
213
|
- Put shared defaults in `layout.py` and let leaf pages override only what they need.
|
|
214
|
+
- For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md) and keep shared metadata defaults in the parent folder's `layout.py`.
|
|
199
215
|
- If a single route only needs to tweak a wrapping layout, return `(render_page(__file__, ...), {"dashboard_body_class": ...})` from `page()` instead of moving that prop into metadata.
|
|
200
216
|
- Use `extra` for Open Graph and Twitter card tags.
|
|
201
217
|
- Access `extra` values in templates with bracket syntax such as `metadata['og:image']`.
|
|
@@ -6,6 +6,7 @@ related:
|
|
|
6
6
|
description: Start with installation for new apps, then use the component guide for reusable UI, the auth guide for bootstrap and session wiring, the MCP guide for server layout, the routing guide to map URLs correctly, and the cache guide when route HTML should be reused safely.
|
|
7
7
|
links:
|
|
8
8
|
- /docs/installation
|
|
9
|
+
- /docs/core-runtime-map
|
|
9
10
|
- /docs/components
|
|
10
11
|
- /docs/auth
|
|
11
12
|
- /docs/mcp
|
|
@@ -105,11 +106,13 @@ This is the main application area. It contains route files, templates, styles, a
|
|
|
105
106
|
|
|
106
107
|
This directory handles file-based routing. Route templates and route-specific backend logic live here.
|
|
107
108
|
|
|
108
|
-
For any route that renders UI, keep that markup in `src/app/**/index.html`. If the route is UI-only, `index.html` alone is enough. Add `src/app/**/index.py` only as a companion when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side behavior. Use a lone `index.py` only for non-visual routes such as redirect-only or action-only handlers.
|
|
109
|
+
For any route that renders UI, keep that markup in `src/app/**/index.html`. If the route is UI-only, `index.html` alone is enough. Add `src/app/**/index.py` only as a companion when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side behavior. Keep shared wrappers in `layout.html` and use `layout.py` only for shared synchronous props or metadata. Use a lone `index.py` only for non-visual routes such as redirect-only or action-only handlers.
|
|
109
110
|
|
|
110
|
-
When
|
|
111
|
+
When a folder represents a section with child routes, such as `dashboard`, `account`, `settings`, or `docs`, create `layout.html` in that folder and let the child routes live beneath it. See [routing.md](./routing.md) for the canonical section layout pattern.
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
If the shared wrapper should not add a URL segment, use a parenthesized route-group folder such as `(marketing)/layout.html` instead of a normal folder name. Use a normal folder such as `dashboard/` only when that segment should be part of the public URL.
|
|
114
|
+
|
|
115
|
+
When authoring route or layout HTML, follow the authoring contract documented in [routing.md](./routing.md) and [pulsepoint.md](./pulsepoint.md): keep one authored root, keep `<!-- @import ... -->` directives above that root, use a plain `<script>` inside the root when needed, and do not handwrite `pp-component` or `type="text/pp"`.
|
|
113
116
|
|
|
114
117
|
See `routing.md` for the full App Router-style rules for dynamic segments, route groups, and nested layouts.
|
|
115
118
|
|
|
@@ -123,9 +126,7 @@ The common Caspian pattern is a Python file such as `Button.py` with `@component
|
|
|
123
126
|
|
|
124
127
|
One Python file can also export multiple related `@component` functions. When that happens, import those tags from that exact file path in HTML, for example `<!-- @import { Breadcrumb, BreadcrumbItem, BreadcrumbList } from "../components/Breadcrumb.py" -->`, and render them as `<x-breadcrumb />`, `<x-breadcrumb-item />`, and `<x-breadcrumb-list />` instead of assuming each tag has its own sibling `.py` file.
|
|
125
128
|
|
|
126
|
-
For component HTML files, follow the
|
|
127
|
-
|
|
128
|
-
Do not handwrite `pp-component="..."` or `type="text/pp"` in component source templates either. Write plain `<script>` inside the single root and let the render pipeline inject the runtime shape.
|
|
129
|
+
For component HTML files, follow the component authoring rules in [components.md](./components.md): one top-level parent node, no sibling roots, plain `<script>` inside that root when needed, and no handwritten `pp-component` or `type="text/pp"`.
|
|
129
130
|
|
|
130
131
|
The directories listed in `componentScanDirs` determine where component tooling scans. When that list includes `src/`, `src/components/` is a conventionally clean location, not a hard-coded runtime requirement.
|
|
131
132
|
|
|
@@ -211,6 +212,12 @@ When runtime uploads write into `public/uploads/`, keep the public-root-relative
|
|
|
211
212
|
|
|
212
213
|
`settings/bs-config.ts` matches ignore entries against paths relative to the `public/` root, so nested uploaded files do not trigger reloads.
|
|
213
214
|
|
|
215
|
+
### `settings/bs-config.json`
|
|
216
|
+
|
|
217
|
+
When the local BrowserSync stack is running, this generated file is the active URL snapshot.
|
|
218
|
+
|
|
219
|
+
Use it when AI needs the current local, external, or UI URL instead of the configured defaults, and use it when tracing the development cookie scope logic in `main.py` because `_dev_cookie_scope()` can read the active port from this file.
|
|
220
|
+
|
|
214
221
|
### `src/lib/auth/auth_config.py`
|
|
215
222
|
|
|
216
223
|
The project auth configuration file. Use this path when changing authentication behavior.
|
|
@@ -231,6 +238,16 @@ The root layout shared across pages.
|
|
|
231
238
|
|
|
232
239
|
If this layout imports reusable components, place each `<!-- @import ... -->` comment above the authored root `<html>` element instead of nesting the comment inside `<html>` or `<body>`.
|
|
233
240
|
|
|
241
|
+
Nested section layouts follow the same pattern in child folders such as `src/app/dashboard/layout.html` or `src/app/(marketing)/layout.html`. Use those nested layouts for shared dashboard shells, grouped page wrappers, sidebars, headers, or section-level metadata defaults.
|
|
242
|
+
|
|
243
|
+
Keep visible wrapper markup in `layout.html`, not in `layout.py`.
|
|
244
|
+
|
|
245
|
+
### `src/app/layout.py`
|
|
246
|
+
|
|
247
|
+
The backend companion for a layout. Use this file for shared synchronous props, metadata defaults, and other server-side preparation for the sibling `layout.html`.
|
|
248
|
+
|
|
249
|
+
Do not store layout HTML in `layout.py`. Keep the authored wrapper in `layout.html` and let `layout.py` return props or metadata.
|
|
250
|
+
|
|
234
251
|
### `src/app/index.py`
|
|
235
252
|
|
|
236
253
|
The backend logic companion for the route. Use this file when the same route needs first-render data, metadata, `@rpc()` actions, auth helpers, `casp.validate`, route-level `Cache(...)` declarations, redirects, or other server behavior.
|
|
@@ -247,6 +264,8 @@ If a route renders UI and needs no backend behavior, this file alone is sufficie
|
|
|
247
264
|
|
|
248
265
|
Treat import comments as top-of-file directives that belong above the route's single authored root element.
|
|
249
266
|
|
|
267
|
+
That authored root may be a native HTML element or a single imported `x-*` component tag, but after component expansion it must still resolve to one final HTML root.
|
|
268
|
+
|
|
250
269
|
Use `components.md` when the task involves authoring reusable `<x-my-component />` tags backed by Python files.
|
|
251
270
|
|
|
252
271
|
### `src/app/globals.css`
|
|
@@ -257,12 +276,21 @@ Global application styles.
|
|
|
257
276
|
|
|
258
277
|
The installed Caspian framework package. This is framework internals, not normal application code.
|
|
259
278
|
|
|
279
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when you need the fastest concern-to-file lookup across this directory.
|
|
280
|
+
|
|
260
281
|
Notable internal files include:
|
|
261
282
|
|
|
262
283
|
- `.venv/Lib/site-packages/casp/rpc.py` for the RPC decorator and related server-action internals
|
|
263
284
|
- `.venv/Lib/site-packages/casp/layout.py` for layout rendering, metadata handling, and routing-related internals
|
|
264
285
|
- `.venv/Lib/site-packages/casp/cache_handler.py` for route-level cache declarations, cache manifest handling, disk-backed HTML writes, and invalidation internals
|
|
265
286
|
- `.venv/Lib/site-packages/casp/validate.py` for direct validators, rule-based validation, sanitization, and file-validation internals
|
|
287
|
+
- `.venv/Lib/site-packages/casp/auth.py` for auth settings, route-protection checks, and OAuth internals
|
|
288
|
+
- `.venv/Lib/site-packages/casp/state_manager.py` for request-scoped server state and persistence caveats
|
|
289
|
+
- `.venv/Lib/site-packages/casp/component_decorator.py` for `@component`, `render_html(...)`, and component loading
|
|
290
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` for `@import` parsing, `x-*` resolution, root validation, and `pp-component` injection
|
|
291
|
+
- `.venv/Lib/site-packages/casp/scripts_type.py` for converting authored `<script>` tags to `type="text/pp"`
|
|
292
|
+
- `.venv/Lib/site-packages/casp/caspian_config.py` for config loading and route inventory parsing
|
|
293
|
+
- `.venv/Lib/site-packages/casp/streaming.py` for `SSE` and streamed response helpers
|
|
266
294
|
|
|
267
295
|
### `node_modules/caspian-utils/dist/docs/`
|
|
268
296
|
|
|
@@ -280,13 +308,14 @@ If an AI agent is deciding where to make changes, use these rules first.
|
|
|
280
308
|
- Inspect `settings/component-map.json` and `settings/files-list.json` when you need the generated component or route inventory, but do not hand-edit them. The workspace regenerates them from `settings/component-map.ts` and `settings/files-list.ts`.
|
|
281
309
|
- Put route templates and route-specific backend logic in `src/app/`.
|
|
282
310
|
- As the app grows, keep route-owned code in `src/app/`, reusable rendered UI in `src/components/`, and reusable non-UI support code in `src/lib/`.
|
|
311
|
+
- When the user asks for a dashboard, admin area, account area, or any grouped set of child routes, create a parent folder in `src/app/` with `layout.html` and place the child routes beneath it. Use `(group)/layout.html` only when that parent should not appear in the URL.
|
|
283
312
|
- Read [file-uploads.md](./file-uploads.md) when the task involves upload widgets, media libraries, or file-manager flows.
|
|
284
313
|
- Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
|
|
285
314
|
- Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, `x-*` component tags, import comments, and single-root template rules.
|
|
286
315
|
- When deciding between `src/components/` and `src/lib/`, use `src/components/` for anything rendered as reusable UI and `src/lib/` for helpers, services, validators, adapters, and shared business logic.
|
|
287
316
|
- Use [mcp.md](./mcp.md) only when `caspian.config.json` enables MCP and the task involves FastMCP tool definitions, nested config discovery, or local MCP commands.
|
|
288
317
|
- Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files.
|
|
289
|
-
-
|
|
318
|
+
- Follow the single-root authoring contract in [routing.md](./routing.md), [components.md](./components.md), and [pulsepoint.md](./pulsepoint.md): one authored root, any owned `<script>` inside that root, and no handwritten `pp-component` or `type="text/pp"` in source templates.
|
|
290
319
|
- Put shared helpers and reusable libraries in `src/lib/`.
|
|
291
320
|
- Use `settings/bs-config.ts` when uploaded public assets should not trigger BrowserSync reloads during the local stack.
|
|
292
321
|
- Put app-owned FastMCP code in `src/lib/mcp/` only when `caspian.config.json` enables MCP.
|
|
@@ -301,6 +330,7 @@ If an AI agent is deciding where to make changes, use these rules first.
|
|
|
301
330
|
- Put static files in `public/`.
|
|
302
331
|
- Put entry-point changes in `main.py`.
|
|
303
332
|
- Put feature and framework-level project configuration in `caspian.config.json`.
|
|
333
|
+
- Read [core-runtime-map.md](./core-runtime-map.md) when the task touches `main.py` or installed `casp` internals and the controlling file is not obvious yet.
|
|
304
334
|
- Treat `.venv/Lib/site-packages/casp/` as framework internals unless the task is specifically about Caspian core behavior.
|
|
305
335
|
- Use `.venv/Lib/site-packages/casp/rpc.py` when investigating or documenting Caspian RPC internals.
|
|
306
336
|
- Use `.venv/Lib/site-packages/casp/layout.py` when investigating or documenting Caspian routing, layout, or metadata internals.
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -8,6 +8,7 @@ related:
|
|
|
8
8
|
- /docs/components
|
|
9
9
|
- /docs/routing
|
|
10
10
|
- /docs/fetch-data
|
|
11
|
+
- /docs/core-runtime-map
|
|
11
12
|
- /docs/project-structure
|
|
12
13
|
- /docs/index
|
|
13
14
|
---
|
|
@@ -36,10 +37,14 @@ Important current facts:
|
|
|
36
37
|
|
|
37
38
|
- `public/js/pp-reactive-v2.js` exposes the global `pp` runtime and auto-mounts on DOM ready.
|
|
38
39
|
- `main.py` renders the final HTML, runs `transform_components(...)`, then runs `transform_scripts(...)` before returning the response.
|
|
40
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` on the final resolved root after component expansion.
|
|
41
|
+
- `.venv/Lib/site-packages/casp/scripts_type.py` rewrites authored body `<script>` tags to `type="text/pp"`.
|
|
39
42
|
- Authored route and component templates compose reusable server components as HTML-first `x-*` tags before the browser runtime mounts.
|
|
40
43
|
|
|
41
44
|
If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
|
|
42
45
|
|
|
46
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when the controlling runtime file is not obvious yet.
|
|
47
|
+
|
|
43
48
|
## Default Frontend Rule
|
|
44
49
|
|
|
45
50
|
When a Caspian page needs reactive browser behavior, use PulsePoint.
|
|
@@ -48,10 +53,13 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
|
|
|
48
53
|
- Use PulsePoint state, effects, refs, and template directives as the default reactivity model in authored Caspian templates.
|
|
49
54
|
- When the browser needs CRUD operations or follow-up reads from the backend, call `pp.rpc()` from PulsePoint code and back it with route or backend `@rpc()` actions.
|
|
50
55
|
- Keep server-rendered HTML plus PulsePoint enhancement as the baseline architecture.
|
|
56
|
+
- For dashboards, admin areas, account sections, docs sections, and other grouped subtrees, keep shared shell markup and shared PulsePoint behavior in the parent folder's `layout.html`, then keep child-route PulsePoint state local to each `index.html`. Follow the same mental model as the Next.js App Router.
|
|
51
57
|
- Only introduce another frontend runtime when the user explicitly asks for it or the project already depends on one.
|
|
52
58
|
|
|
53
59
|
## Authoring Model
|
|
54
60
|
|
|
61
|
+
Treat this section as the canonical authored-vs-runtime contract for Caspian templates. When another packaged doc needs this rule set, link here instead of restating the full explanation.
|
|
62
|
+
|
|
55
63
|
PulsePoint authoring is split into two layers:
|
|
56
64
|
|
|
57
65
|
- The authored layer: route, layout, and component templates under `src/` with plain HTML plus a plain `<script>`.
|
|
@@ -59,11 +67,16 @@ PulsePoint authoring is split into two layers:
|
|
|
59
67
|
|
|
60
68
|
For authored Caspian templates:
|
|
61
69
|
|
|
62
|
-
- Keep exactly one top-level
|
|
70
|
+
- Keep exactly one authored top-level parent node.
|
|
71
|
+
- In source, that parent may be a native HTML element or a single imported `x-*` component tag, but after component expansion it must resolve to one final HTML root.
|
|
63
72
|
- Put the component logic inside a plain `<script>` inside that same root.
|
|
64
73
|
- Do not handwrite `pp-component="..."`.
|
|
65
74
|
- Do not handwrite `type="text/pp"`.
|
|
66
75
|
|
|
76
|
+
Treat that single-root rule as a hard invariant for AI-generated templates. A sibling `<script>` after the root or any second top-level element will break Caspian's `pp-component` injection and fail the render.
|
|
77
|
+
|
|
78
|
+
Keep visible route, layout, and component markup in the HTML templates. Treat `index.py` and `layout.py` as backend companions for data, metadata, props, RPC actions, auth, caching, redirects, and other server-side preparation, not as template bodies.
|
|
79
|
+
|
|
67
80
|
Caspian already handles those details for you during render.
|
|
68
81
|
|
|
69
82
|
That means AI-generated examples should default to authored template source, not raw runtime HTML.
|
|
@@ -91,6 +104,18 @@ Authored example:
|
|
|
91
104
|
|
|
92
105
|
When that template reaches the browser, Caspian will already have injected the component id and rewritten the owned script to `type="text/pp"`. Those runtime attributes are for the rendered output, not for authored source examples.
|
|
93
106
|
|
|
107
|
+
Invalid authored shape:
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<section>
|
|
111
|
+
<h2>{title}</h2>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
<script>
|
|
115
|
+
const { title = "Counter" } = pp.props;
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
94
119
|
## Runtime shape
|
|
95
120
|
|
|
96
121
|
PulsePoint is a browser-side component runtime. It executes one component script per root, renders HTML from the component scope, morphs the DOM in place, binds native event handlers, restores focus, updates refs, applies portals, and then runs layout/effect hooks.
|
|
@@ -111,7 +136,7 @@ Important:
|
|
|
111
136
|
- At runtime, the owned script is `script[type="text/pp"]`.
|
|
112
137
|
- The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
|
|
113
138
|
- If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
114
|
-
- Authored route, layout, and component templates still need one top-level
|
|
139
|
+
- Authored route, layout, and component templates still need one top-level parent node so Caspian can inject the component boundary correctly after component expansion.
|
|
115
140
|
- A scriptless component root still mounts and can receive props, refs, events, and nested children, but it has no local runtime scope beyond its props.
|
|
116
141
|
- Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
|
|
117
142
|
- The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
|
|
@@ -421,6 +446,7 @@ Use these rules when generating or editing PulsePoint runtime code:
|
|
|
421
446
|
- Keep `main.py` in view because it injects the runtime-facing attributes and rewrites authored scripts before the browser sees them.
|
|
422
447
|
- If a development-only source tree exists behind the shipped runtime, treat it as optional implementation detail rather than something generated apps are guaranteed to contain.
|
|
423
448
|
- In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
|
|
449
|
+
- For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md), keep the shared interactive shell in the parent folder's `layout.html`, and keep route-specific PulsePoint code in each child `index.html`.
|
|
424
450
|
- Prefer PulsePoint state and template directives over manual DOM mutation for reactive updates.
|
|
425
451
|
- If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
|
|
426
452
|
- In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
|
package/dist/docs/routing.md
CHANGED
|
@@ -5,6 +5,7 @@ related:
|
|
|
5
5
|
title: Related docs
|
|
6
6
|
description: Read the structure guide first, then use the components guide for reusable UI, the metadata guide for SEO fields, the cache guide for route-level HTML reuse, and the PulsePoint runtime guide for interactive route templates.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/core-runtime-map
|
|
8
9
|
- /docs/project-structure
|
|
9
10
|
- /docs/components
|
|
10
11
|
- /docs/cache
|
|
@@ -29,8 +30,21 @@ Start with these rules:
|
|
|
29
30
|
- If a route is UI-only, `index.html` by itself is enough.
|
|
30
31
|
- Add `index.py` only when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side logic.
|
|
31
32
|
- Use a standalone `index.py` only for non-visual routes such as redirects or action-only handlers.
|
|
32
|
-
-
|
|
33
|
+
- When a folder owns child routes, use `layout.html` to wrap them. This is the default pattern for dashboards, admin sections, account areas, settings trees, and route groups.
|
|
33
34
|
- Use `layout.py` when a layout needs shared synchronous props or metadata before rendering.
|
|
35
|
+
- Keep visible route and layout markup in `index.html` and `layout.html`. Treat `index.py` and `layout.py` as backend companions, not as places to author visible HTML.
|
|
36
|
+
- Treat every authored route and layout template like a React component body: it must have exactly one top-level parent HTML element or one imported `x-*` root, and any owned plain `<script>` must live inside that same root.
|
|
37
|
+
|
|
38
|
+
## Hard Template Invariant
|
|
39
|
+
|
|
40
|
+
For authored route and layout templates, the single-root rule is a hard runtime requirement.
|
|
41
|
+
|
|
42
|
+
- Use exactly one authored parent node.
|
|
43
|
+
- Keep any `<!-- @import ... -->` directives above that root.
|
|
44
|
+
- Keep any owned plain `<script>` inside that root, not after it.
|
|
45
|
+
- Do not leave sibling top-level HTML tags, sibling component tags, or stray top-level text.
|
|
46
|
+
|
|
47
|
+
If AI generates this incorrectly, Caspian fails render with errors like `must have exactly one top-level HTML element so Caspian can inject pp-component`.
|
|
34
48
|
|
|
35
49
|
Use [cache.md](./cache.md) when an `index.py` route also needs declarative page caching via `Cache(...)`.
|
|
36
50
|
|
|
@@ -38,6 +52,7 @@ Framework internals note:
|
|
|
38
52
|
|
|
39
53
|
- Caspian's layout and route-resolution internals live in `.venv/Lib/site-packages/casp/layout.py`.
|
|
40
54
|
- Treat that file as framework code. Read it when the task is about routing internals, layout resolution, or metadata behavior inside Caspian itself.
|
|
55
|
+
- Use [core-runtime-map.md](./core-runtime-map.md) when a routing task crosses `main.py` route registration, parameter injection, and installed layout internals.
|
|
41
56
|
|
|
42
57
|
See [metadata.md](./metadata.md) when a page or layout needs SEO fields.
|
|
43
58
|
|
|
@@ -61,6 +76,75 @@ This means most App Router habits carry over directly:
|
|
|
61
76
|
- Use nested layouts for shared wrappers.
|
|
62
77
|
- Use route groups to organize code without changing the public path.
|
|
63
78
|
|
|
79
|
+
## Section Layout Pattern
|
|
80
|
+
|
|
81
|
+
Treat this section as the canonical grouped-subtree structure rule for the packaged docs. Other pages should point here instead of repeating the full folder pattern.
|
|
82
|
+
|
|
83
|
+
When a user asks for a dashboard, admin area, account section, docs section, or any grouped set of child routes, model it exactly like a Next.js App Router subtree.
|
|
84
|
+
|
|
85
|
+
- Create a parent folder for the section.
|
|
86
|
+
- Add `layout.html` in that folder for the shared shell.
|
|
87
|
+
- Add `layout.py` only when that shared shell needs synchronous props or metadata.
|
|
88
|
+
- Put each child page in its own route folder with `index.html` and an optional `index.py` companion.
|
|
89
|
+
- Use a normal folder name such as `dashboard/` when the section name should appear in the URL.
|
|
90
|
+
- Use a route-group folder such as `(marketing)/` when the folder should organize code and own a layout without adding a URL segment.
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
src/
|
|
96
|
+
app/
|
|
97
|
+
dashboard/
|
|
98
|
+
layout.html
|
|
99
|
+
index.html
|
|
100
|
+
settings/
|
|
101
|
+
index.html
|
|
102
|
+
users/
|
|
103
|
+
index.html
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This produces `/dashboard`, `/dashboard/settings`, and `/dashboard/users`, all wrapped by the `dashboard/layout.html` shell.
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
src/
|
|
110
|
+
app/
|
|
111
|
+
(marketing)/
|
|
112
|
+
layout.html
|
|
113
|
+
pricing/
|
|
114
|
+
index.html
|
|
115
|
+
about/
|
|
116
|
+
index.html
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This produces `/pricing` and `/about`, both wrapped by the `(marketing)/layout.html` shell even though `(marketing)` does not appear in the URL.
|
|
120
|
+
|
|
121
|
+
Canonical end-to-end example:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
src/
|
|
125
|
+
app/
|
|
126
|
+
layout.html
|
|
127
|
+
layout.py
|
|
128
|
+
(marketing)/
|
|
129
|
+
layout.html
|
|
130
|
+
pricing/
|
|
131
|
+
index.html
|
|
132
|
+
about/
|
|
133
|
+
index.html
|
|
134
|
+
dashboard/
|
|
135
|
+
layout.html
|
|
136
|
+
layout.py
|
|
137
|
+
index.html
|
|
138
|
+
settings/
|
|
139
|
+
index.html
|
|
140
|
+
index.py
|
|
141
|
+
reports/
|
|
142
|
+
index.html
|
|
143
|
+
index.py
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Use this pattern when the app has a public grouped section that should stay out of the URL and a private dashboard section that should appear in the URL. The root layout owns app-wide chrome, `(marketing)/layout.html` owns the shared public marketing shell, and `dashboard/layout.html` owns the shared dashboard shell for `/dashboard/*` child routes.
|
|
147
|
+
|
|
64
148
|
## Core Concepts
|
|
65
149
|
|
|
66
150
|
Every route lives inside `src/app`. For routes that render UI, `index.html` owns the markup and `index.py` is only an optional companion for server logic or metadata.
|
|
@@ -93,13 +177,9 @@ Route templates can import reusable Python components with `<!-- @import ... -->
|
|
|
93
177
|
|
|
94
178
|
Place those import comments at the top of the file, above the authored root element. They are file-level directives, not children of the route root.
|
|
95
179
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Do not manually add `type="text/pp"` to a route-owned script either. In source templates, write a plain `<script>` inside the root and let `main.py` call `transform_scripts(...)` before the browser runtime sees the HTML.
|
|
99
|
-
|
|
100
|
-
That means route templates follow the same single-root discipline as component templates: one root element, no sibling roots, and PulsePoint scripts kept inside that root when needed.
|
|
180
|
+
Route templates follow the same authored-vs-runtime contract documented in [pulsepoint.md](./pulsepoint.md) and the same single-root discipline documented in [components.md](./components.md): keep one authored parent node, keep any `<!-- @import ... -->` directives above that root, use a plain `<script>` inside that root when needed, and do not handwrite `pp-component` or `type="text/pp"`. That root may be a native HTML element or a single imported `x-*` component tag, but after expansion it must resolve to one final HTML root.
|
|
101
181
|
|
|
102
|
-
For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent
|
|
182
|
+
For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent node that contains the entire route markup. This includes any owned script.
|
|
103
183
|
|
|
104
184
|
Good:
|
|
105
185
|
|
|
@@ -129,40 +209,22 @@ Also bad:
|
|
|
129
209
|
|
|
130
210
|
```html
|
|
131
211
|
<section class="dashboard-shell">
|
|
132
|
-
|
|
133
|
-
<x-stats-card title="Users" value="42" />
|
|
212
|
+
<h1>Dashboard</h1>
|
|
134
213
|
</section>
|
|
214
|
+
|
|
215
|
+
<aside class="dashboard-help">Tips</aside>
|
|
135
216
|
```
|
|
136
217
|
|
|
137
|
-
|
|
218
|
+
Also bad:
|
|
138
219
|
|
|
139
220
|
```html
|
|
140
|
-
<!-- @import StatsCard from "../components" -->
|
|
141
|
-
|
|
142
221
|
<section class="dashboard-shell">
|
|
222
|
+
<!-- @import StatsCard from "../components" -->
|
|
143
223
|
<x-stats-card title="Users" value="42" />
|
|
144
|
-
|
|
145
|
-
<script>
|
|
146
|
-
const [filter, setFilter] = pp.state("all");
|
|
147
|
-
</script>
|
|
148
|
-
</section>
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
Rendered shape at runtime:
|
|
152
|
-
|
|
153
|
-
```html
|
|
154
|
-
<section pp-component="page_a1b2c3d4" class="dashboard-shell">
|
|
155
|
-
<div pp-component="statscard_e5f6g7h8" title="Users" value="42">
|
|
156
|
-
...
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
<script type="text/pp">
|
|
160
|
-
const [filter, setFilter] = pp.state("all");
|
|
161
|
-
</script>
|
|
162
224
|
</section>
|
|
163
225
|
```
|
|
164
226
|
|
|
165
|
-
|
|
227
|
+
Use [pulsepoint.md](./pulsepoint.md) when you need the full authored-vs-rendered example instead of this routing-focused reminder.
|
|
166
228
|
|
|
167
229
|
### `index.py`
|
|
168
230
|
|
|
@@ -280,14 +342,29 @@ Route groups are useful when you want to:
|
|
|
280
342
|
- Split routes under different layout boundaries.
|
|
281
343
|
- Improve project organization without changing the public URL structure.
|
|
282
344
|
|
|
345
|
+
If the group needs a shared wrapper, put `layout.html` inside the `(group)` folder. That layout applies to every child route in the group while the group name stays out of the URL.
|
|
346
|
+
|
|
347
|
+
Use this decision rule:
|
|
348
|
+
|
|
349
|
+
- Use `dashboard/`, `account/`, or another normal folder when that segment should be part of the public path.
|
|
350
|
+
- Use `(dashboard)`, `(marketing)`, or another parenthesized folder when you want shared organization or a shared layout without adding a public path segment.
|
|
351
|
+
|
|
283
352
|
## Layouts And Nesting
|
|
284
353
|
|
|
285
354
|
Layouts work like the Next.js App Router layout system. A `layout.html` file wraps the routes beneath its folder, and nested layouts compose automatically.
|
|
286
355
|
|
|
356
|
+
In practice, this means a dashboard is usually a folder-level layout, not a single oversized page. Put the shared sidebar, header, and frame in `dashboard/layout.html`, then create child routes such as `dashboard/settings/index.html` and `dashboard/reports/index.html` beneath it.
|
|
357
|
+
|
|
287
358
|
When a layout imports components, keep each `<!-- @import ... -->` comment above the layout's authored wrapper element, such as the root `<section>` in a nested layout or the root `<html>` in the app layout.
|
|
288
359
|
|
|
289
360
|
Resolved SEO fields are exposed to layouts as `[[ metadata.* ]]`, while values returned from `layout.py` are exposed separately as `[[ layout.* ]]`.
|
|
290
361
|
|
|
362
|
+
### `layout.html`
|
|
363
|
+
|
|
364
|
+
Use `layout.html` for the shared wrapper markup of a subtree. Keep the visible shell here, not in `layout.py`.
|
|
365
|
+
|
|
366
|
+
Follow the same authoring contract used by route templates: one authored parent node, top-of-file `<!-- @import ... -->` directives above that root, plain `<script>` inside the root when needed, and no handwritten `pp-component` or `type="text/pp"`. See [pulsepoint.md](./pulsepoint.md) for the canonical authored-vs-runtime explanation.
|
|
367
|
+
|
|
291
368
|
For example, a page inside `/dashboard/settings` is wrapped by the root layout first and then by the dashboard layout.
|
|
292
369
|
|
|
293
370
|
Example root layout:
|
|
@@ -308,7 +385,7 @@ Example root layout:
|
|
|
308
385
|
|
|
309
386
|
### `layout.py`
|
|
310
387
|
|
|
311
|
-
If a layout needs shared synchronous props or metadata, add a `layout.py` file next to the HTML layout.
|
|
388
|
+
If a layout needs shared synchronous props or metadata, add a `layout.py` file next to the HTML layout. Treat it as the backend companion for the layout, not as the place to author visible wrapper markup.
|
|
312
389
|
|
|
313
390
|
Example:
|
|
314
391
|
|
|
@@ -376,6 +453,10 @@ src/
|
|
|
376
453
|
index.html
|
|
377
454
|
about/
|
|
378
455
|
index.html
|
|
456
|
+
(marketing)/
|
|
457
|
+
layout.html
|
|
458
|
+
pricing/
|
|
459
|
+
index.html
|
|
379
460
|
users/
|
|
380
461
|
[id]/
|
|
381
462
|
index.html
|
|
@@ -395,9 +476,13 @@ src/
|
|
|
395
476
|
index.py
|
|
396
477
|
dashboard/
|
|
397
478
|
layout.html
|
|
479
|
+
layout.py
|
|
480
|
+
index.html
|
|
398
481
|
settings/
|
|
399
482
|
index.html
|
|
400
483
|
index.py
|
|
484
|
+
reports/
|
|
485
|
+
index.html
|
|
401
486
|
```
|
|
402
487
|
|
|
403
488
|
## AI Retrieval Notes
|
|
@@ -408,11 +493,14 @@ If an AI agent is choosing where to add or update route code, apply these rules
|
|
|
408
493
|
- Use folder names to model URL segments.
|
|
409
494
|
- If a route renders UI, create or update `index.html` for the markup.
|
|
410
495
|
- Add `index.py` only when the same route needs metadata or server behavior; do not place route HTML in `index.py`.
|
|
496
|
+
- Keep visible page markup in `index.html` and shared subtree shells in `layout.html`; do not place route HTML in `index.py` or layout HTML in `layout.py`.
|
|
497
|
+
- When the user asks for a dashboard, admin area, account section, or any grouped subtree of child routes, create a parent folder with `layout.html` and place the child routes beneath it. Follow the same mental model as the Next.js App Router.
|
|
498
|
+
- Use a normal folder such as `dashboard/` when the segment should appear in the URL. Use `(group)/` only when the folder should organize or wrap child routes without adding a path segment.
|
|
411
499
|
- Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
|
|
412
500
|
- Use `layout.html` for shared wrappers and `layout.py` for layout-level synchronous props or metadata.
|
|
413
501
|
- When one route needs to change a parent layout, return `(render_page(__file__, ...), {"dashboard_body_class": ...})` from `page()` and read that value as `[[ layout.dashboard_body_class ]]` in the wrapping `layout.html`.
|
|
414
502
|
- Use `layout.py` for layout props that should apply across an entire subtree. Use `render_layout(__file__, {...})` only when the layout should consume direct local variables such as `[[ my_class ]]` instead of the standard `[[ layout.* ]]` namespace.
|
|
415
|
-
- Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored
|
|
503
|
+
- Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored parent node.
|
|
416
504
|
- Use [metadata.md](./metadata.md) when a route or layout needs SEO fields.
|
|
417
505
|
- Use `[segment]` for single dynamic parameters.
|
|
418
506
|
- Use `[...segment]` for catch-all route matching.
|
package/dist/docs/state.md
CHANGED
|
@@ -5,6 +5,7 @@ related:
|
|
|
5
5
|
title: Related docs
|
|
6
6
|
description: Pair server request state with auth and RPC flows, then read the PulsePoint guide when the state really belongs in the browser instead of the request lifecycle.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/core-runtime-map
|
|
8
9
|
- /docs/auth
|
|
9
10
|
- /docs/fetch-data
|
|
10
11
|
- /docs/pulsepoint
|
|
@@ -28,6 +29,8 @@ from casp.state_manager import StateManager
|
|
|
28
29
|
|
|
29
30
|
The current installed implementation lives in `.venv/Lib/site-packages/casp/state_manager.py`.
|
|
30
31
|
|
|
32
|
+
Use [core-runtime-map.md](./core-runtime-map.md) when a state task crosses `main.py` middleware setup, wire-request behavior, and the installed state-manager internals.
|
|
33
|
+
|
|
31
34
|
The real API surface is:
|
|
32
35
|
|
|
33
36
|
- request initialization with `StateManager.init(request)`
|