caspian-utils 0.2.6 → 0.2.7
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 +2 -2
- package/dist/docs/components.md +9 -9
- package/dist/docs/core-runtime-map.md +2 -3
- package/dist/docs/file-conventions.md +1 -1
- package/dist/docs/index.md +1 -1
- package/dist/docs/project-structure.md +4 -4
- package/dist/docs/pulsepoint-runtime-map.md +6 -7
- package/dist/docs/pulsepoint.md +21 -19
- package/dist/docs/routing.md +2 -2
- package/package.json +1 -1
|
@@ -54,13 +54,13 @@ Use prompts like these to check whether AI lands on the correct docs and files.
|
|
|
54
54
|
| --- | --- | --- | --- |
|
|
55
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
56
|
| Make a grouped shell keep sidebar scroll while resetting page content on child-route navigation. | [index.md](./index.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md), [core-runtime-map.md](./core-runtime-map.md) | `src/app/**/layout.html`, `public/js/pp-reactive-v2.js`, `main.py` | `pp-reset-scroll` placement, push-vs-history scroll behavior, and shared-shell ownership |
|
|
57
|
-
| 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`,
|
|
57
|
+
| 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`, `public/js/pp-reactive-v2.js` | single-root template shape, plain-script-inside-root authoring, and safe template materialization |
|
|
58
58
|
| Add a button, filter, or form interaction to a route template. | [index.md](./index.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md), [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) | `src/app/**/index.html`, `public/js/pp-reactive-v2.js`, `main.py` | PulsePoint `on*` event attributes, `pp.state`, directives, `Object.fromEntries(new FormData(...).entries())` for simple form submits, and avoiding id-driven `querySelector` or `addEventListener` wiring |
|
|
59
59
|
| A debounced server search pauses the first key typed after the timer fires. Determine whether the component or runtime owns the problem. | [index.md](./index.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md), [fetch-data.md](./fetch-data.md), [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) | Owning `src/app/**` or `src/components/**`; `public/js/pp-reactive-v2.js` only when profiling points to runtime reconciliation | State versus ref ownership, debounce frequency versus render cost, stale-response generations, the smallest useful render owner, and `pp.enablePerf()` evidence before a runtime edit |
|
|
60
60
|
| 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 mandatory Prisma Python ORM use for database reads and writes when Prisma is enabled |
|
|
61
61
|
| Add `public/icons/app.png` and make it browser-accessible without editing routes. | [index.md](./index.md), [project-structure.md](./project-structure.md), [core-runtime-map.md](./core-runtime-map.md), [auth.md](./auth.md) | `public/**`, `main.py`, `.venv/Lib/site-packages/casp/runtime_security.py` | root-relative URL mapping, no per-directory route or prefix registry, traversal and symlink containment, `GET`/`HEAD` only, and middleware order |
|
|
62
62
|
| Store untrusted uploads in a custom public directory. | [index.md](./index.md), [file-uploads.md](./file-uploads.md), [project-structure.md](./project-structure.md), [core-runtime-map.md](./core-runtime-map.md) | `src/app/**/index.py`, `src/lib/**`, `main.py`, `.venv/Lib/site-packages/casp/runtime_security.py` | top-level `inline_safe_subdirectories` registration, attachment mode for HTML/SVG/unknown types, `nosniff`, path containment, and BrowserSync ignore rules |
|
|
63
|
-
| Explain
|
|
63
|
+
| Explain how a plain component `<script>` stays inert during template materialization. | [index.md](./index.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md), [routing.md](./routing.md) | `main.py`, `.venv/Lib/site-packages/casp/components_compiler.py`, `public/js/pp-reactive-v2.js` | component-root deferral, script-source capture, native-execution prevention, and component-scope evaluation |
|
|
64
64
|
| 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 |
|
|
65
65
|
| 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 |
|
|
66
66
|
| 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 |
|
package/dist/docs/components.md
CHANGED
|
@@ -258,7 +258,7 @@ def UserCard(user, **props):
|
|
|
258
258
|
""", attrs=attrs, user=user)
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
The returned value is `Markup`, so the normal pipeline still injects `pp-component` on the single root and
|
|
261
|
+
The returned value is `Markup`, so the normal pipeline still injects `pp-component` on the single root and defers that root inside an inert template. The plain component `<script>` remains plain; PulsePoint captures its source before materialization and evaluates it in component scope. A single-file component renders identically to the two-file `render_html(...)` form; the choice is purely about readability.
|
|
262
262
|
|
|
263
263
|
### Receiving Props In A Python Component
|
|
264
264
|
|
|
@@ -608,16 +608,16 @@ For ref purposes, a component's root is the single native element that receives
|
|
|
608
608
|
|
|
609
609
|
The default takes precedence over catch-all `**props`: `pp-ref` is reserved for root ref forwarding and is not delivered in that dictionary. A component that intentionally wants `pp-ref` as ordinary data can opt out by declaring an explicit camel-case `ppRef` parameter, for example `def Inspector(ppRef="", **props)`. That explicit declaration consumes the prop, disables automatic root forwarding for that component invocation, and makes the component responsible for how the value is used. Use this escape hatch sparingly; ordinary UI primitives should keep the standard root-ref contract.
|
|
610
610
|
|
|
611
|
-
## Auto-Injected `pp-component` And
|
|
611
|
+
## Auto-Injected `pp-component` And Plain Component Scripts
|
|
612
612
|
|
|
613
|
-
Treat `pp-component="componentName"`
|
|
613
|
+
Treat `pp-component="componentName"` as framework output, not authored source. The canonical authored-vs-runtime explanation lives in [pulsepoint.md](./pulsepoint.md).
|
|
614
614
|
|
|
615
615
|
- Do not manually add `pp-component="..."` to route templates, layout templates, or component HTML files.
|
|
616
|
-
-
|
|
617
|
-
-
|
|
618
|
-
- `main.py`
|
|
619
|
-
-
|
|
620
|
-
- If a runtime example
|
|
616
|
+
- The Python render pipeline injects `pp-component` onto the single root element during render.
|
|
617
|
+
- Add a plain, untyped `<script>` only when the route or component actually needs PulsePoint logic, and keep it inside that single root element.
|
|
618
|
+
- `main.py` preserves that plain script while deferring the root inside an inert template.
|
|
619
|
+
- Before materialization or morph insertion, PulsePoint captures and empties the script so the browser cannot execute it natively, then evaluates the captured source in component scope.
|
|
620
|
+
- If a runtime example shows `pp-component`, treat that attribute as rendered output rather than authored source.
|
|
621
621
|
|
|
622
622
|
## Single-Root Rule
|
|
623
623
|
|
|
@@ -754,5 +754,5 @@ Keep synchronous components as the default. Switch to `async def` only when the
|
|
|
754
754
|
|
|
755
755
|
- Read [project-structure.md](./project-structure.md) for where reusable components should live.
|
|
756
756
|
- Read [routing.md](./routing.md) when importing components into route templates.
|
|
757
|
-
- Read [pulsepoint.md](./pulsepoint.md) for the runtime contract of `pp-component`,
|
|
757
|
+
- Read [pulsepoint.md](./pulsepoint.md) for the runtime contract of `pp-component`, plain owned scripts, props, and browser-side reactivity.
|
|
758
758
|
- Read [fetch-data.md](./fetch-data.md) when a component needs browser-triggered `pp.rpc()` calls or component-owned server actions.
|
|
@@ -37,7 +37,7 @@ Important current `main.py` behaviors AI should keep in mind:
|
|
|
37
37
|
|
|
38
38
|
- In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
|
|
39
39
|
- `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
|
|
40
|
-
- The render pipeline
|
|
40
|
+
- The render pipeline transforms components, renders nested layouts, and finally defers outermost component roots inside inert templates.
|
|
41
41
|
- Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
|
|
42
42
|
- Safe public-file helpers live in `casp.runtime_security`. `PublicFilesMiddleware` maps every existing nested file under `public/**` to the same root-relative URL without per-directory routes, rejects traversal and symlink escape, handles only `GET`/`HEAD`, and falls through when no file exists so page and mounted-app routing still works. User-upload directories must retain their restricted inline-media policy.
|
|
43
43
|
- Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
|
|
@@ -100,7 +100,6 @@ Interactive CRUD page:
|
|
|
100
100
|
| [.venv/Lib/site-packages/casp/validate.py](../../../../.venv/Lib/site-packages/casp/validate.py) | `Validate`, `Rule`, sanitization, and file-validation internals | [validation.md](./validation.md) |
|
|
101
101
|
| [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, the inline `html(...)` single-file helper (with direct-call scope capture), component loading, and prop normalization before component calls | [components.md](./components.md) |
|
|
102
102
|
| [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution (including resolving a component's own tags from its Python module imports and the scope captured by directly-called components), parent-scope expansion of slot content, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
|
|
103
|
-
| [.venv/Lib/site-packages/casp/scripts_type.py](../../../../.venv/Lib/site-packages/casp/scripts_type.py) | rewriting authored body `<script>` tags to `type="text/pp"` in rendered HTML | [pulsepoint.md](./pulsepoint.md), [routing.md](./routing.md), [components.md](./components.md) |
|
|
104
103
|
| [.venv/Lib/site-packages/casp/html_attrs.py](../../../../.venv/Lib/site-packages/casp/html_attrs.py) | `get_attributes(...)`, alias normalization, and the Python-side `merge_classes(...)` contract | [components.md](./components.md) |
|
|
105
104
|
| [.venv/Lib/site-packages/casp/caspian_config.py](../../../../.venv/Lib/site-packages/casp/caspian_config.py) | typed config loading, feature-flag reads, `settings/files-list.json` parsing, and route rule derivation | [project-structure.md](./project-structure.md), [commands.md](./commands.md), [routing.md](./routing.md) |
|
|
106
105
|
|
|
@@ -118,7 +117,7 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
|
|
|
118
117
|
| `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
|
|
119
118
|
| `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
|
|
120
119
|
| `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
|
|
121
|
-
| `casp.components_compiler`, `casp.component_decorator`, `
|
|
120
|
+
| `casp.components_compiler`, `casp.component_decorator`, `main.py` template-root deferral, and the browser runtime | `@import` parsing, `x-*` expansion, Python-module-import tag resolution, parent-scope slot resolution, inline `html(...)` rendering and children-safe handling, deterministic root keys, `pp-component` injection, plain component-script preservation, and safe script capture during materialization |
|
|
122
121
|
| `casp.state_manager` | wire vs non-wire reset behavior, request-state persistence assumptions, and AttributeDict access |
|
|
123
122
|
| `casp.cache_handler` | filename generation, manifest writes, TTL handling, and invalidation behavior |
|
|
124
123
|
| `casp.caspian_config` | config parsing, files index building, and Next.js-style route inventory behavior |
|
|
@@ -44,7 +44,7 @@ For authored route, layout, loading, not-found, and error HTML files, keep exact
|
|
|
44
44
|
|
|
45
45
|
- Keep any `<!-- @import ... -->` directives above that root. They are header directives for the file, not children of the root element.
|
|
46
46
|
- Keep any owned plain `<script>` inside that root, not after it.
|
|
47
|
-
- Do not handwrite `pp-component
|
|
47
|
+
- Do not handwrite `pp-component`; keep owned component logic in a plain `<script>` inside the single root.
|
|
48
48
|
|
|
49
49
|
This is a runtime requirement, not a style preference.
|
|
50
50
|
|
package/dist/docs/index.md
CHANGED
|
@@ -109,7 +109,7 @@ Preferred lookup order:
|
|
|
109
109
|
|
|
110
110
|
1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
|
|
111
111
|
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.
|
|
112
|
-
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
|
|
112
|
+
3. Before authoring or editing any `src/app/**` or component HTML template, apply the single-root invariant: one authored root only, any owned plain `<script>` inside that root, and no handwritten `pp-component`.
|
|
113
113
|
4. Before inventing browser JavaScript, check whether the interaction is first-party UI behavior. If it is, use PulsePoint `on*` attributes, `pp.state`, refs, effects, directives, and `pp.rpc()` rather than id-driven DOM scripting.
|
|
114
114
|
5. When Prisma is enabled, route all Python database work through the generated Prisma Python ORM. Use `database.md` before schema, migration, seed, or ORM decisions, and use `fetch-data.md` before wiring route data or RPC flows.
|
|
115
115
|
6. If the task asks for WebSockets, live bidirectional channels, socket origin checks, or native browser `WebSocket` clients, confirm `caspian.config.json` has `websocket: true`, read `websockets.md`, then inspect `main.py`, `src/lib/**`, the owning `src/app/**` route, and `settings/bs-config.json`. If `websocket` is false and the user wants it, ask first, then enable the config flag and follow the update workflow.
|
|
@@ -117,7 +117,7 @@ When a folder represents a section with child routes, such as `dashboard`, `acco
|
|
|
117
117
|
|
|
118
118
|
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.
|
|
119
119
|
|
|
120
|
-
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
|
|
120
|
+
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`.
|
|
121
121
|
|
|
122
122
|
See `routing.md` for the full App Router-style rules for dynamic segments, route groups, and nested layouts.
|
|
123
123
|
|
|
@@ -133,7 +133,7 @@ The common Caspian pattern is a Python file such as `Button.py` with `@component
|
|
|
133
133
|
|
|
134
134
|
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.
|
|
135
135
|
|
|
136
|
-
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
|
|
136
|
+
For component HTML files, follow the component authoring rules in [components.md](./components.md): one top-level parent node, no sibling roots, a plain `<script>` inside that root when needed, and no handwritten `pp-component`.
|
|
137
137
|
|
|
138
138
|
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.
|
|
139
139
|
|
|
@@ -332,7 +332,7 @@ Notable internal files include:
|
|
|
332
332
|
- `.venv/Lib/site-packages/casp/state_manager.py` for request-scoped server state and persistence caveats
|
|
333
333
|
- `.venv/Lib/site-packages/casp/component_decorator.py` for `@component`, `render_html(...)`, and component loading
|
|
334
334
|
- `.venv/Lib/site-packages/casp/components_compiler.py` for `@import` parsing, `x-*` resolution, root validation, and `pp-component` injection
|
|
335
|
-
-
|
|
335
|
+
- `main.py` for final inert-template deferral and `public/js/pp-reactive-v2.js` for safe plain-script capture and component-scope evaluation
|
|
336
336
|
- `.venv/Lib/site-packages/casp/caspian_config.py` for config loading and route inventory parsing
|
|
337
337
|
- `.venv/Lib/site-packages/casp/streaming.py` for `SSE` and streamed response helpers
|
|
338
338
|
|
|
@@ -361,7 +361,7 @@ If an AI agent is deciding where to make changes, use these rules first.
|
|
|
361
361
|
- 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.
|
|
362
362
|
- 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.
|
|
363
363
|
- Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files. Do not nest them inside `<html>`, `<body>`, `<section>`, or any other parent wrapper.
|
|
364
|
-
- 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`
|
|
364
|
+
- 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 plain `<script>` inside that root, and no handwritten `pp-component` in source templates.
|
|
365
365
|
- Put shared helpers and reusable libraries in `src/lib/`.
|
|
366
366
|
- Use `settings/bs-config.ts` when uploaded public assets should not trigger BrowserSync reloads during the local stack.
|
|
367
367
|
- Put app-owned FastMCP code in `src/lib/mcp/` only when `caspian.config.json` enables MCP.
|
|
@@ -20,10 +20,9 @@ Use [pulsepoint.md](./pulsepoint.md) for the full authoring contract. Use this f
|
|
|
20
20
|
## Source Of Truth
|
|
21
21
|
|
|
22
22
|
- `public/js/pp-reactive-v2.js` is the shipped browser runtime.
|
|
23
|
-
- `main.py` owns
|
|
24
|
-
- `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` after component expansion and validates the single-root contract.
|
|
25
|
-
- `.venv/Lib/site-packages/casp/
|
|
26
|
-
- `.venv/Lib/site-packages/casp/html_attrs.py` owns Python-side class and attribute helpers such as `merge_classes(...)`.
|
|
23
|
+
- `main.py` owns component transformation and final inert-template deferral.
|
|
24
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` after component expansion and validates the single-root contract.
|
|
25
|
+
- `.venv/Lib/site-packages/casp/html_attrs.py` owns Python-side class and attribute helpers such as `merge_classes(...)`.
|
|
27
26
|
|
|
28
27
|
If an inspected browser DOM disagrees with authored template source, remember that the runtime DOM includes framework-managed output. Do not copy runtime-only attributes back into authored templates.
|
|
29
28
|
|
|
@@ -32,7 +31,7 @@ If an inspected browser DOM disagrees with authored template source, remember th
|
|
|
32
31
|
| PulsePoint feature | Authoring surface | Runtime owner | Verify before changing |
|
|
33
32
|
| --- | --- | --- | --- |
|
|
34
33
|
| Component roots | `src/app/**/index.html`, `layout.html`, component `.html` files | `components_compiler.py`, `public/js/pp-reactive-v2.js` | one authored root, final expanded root receives one `pp-component`, no sibling scripts |
|
|
35
|
-
| Component scripts | plain `<script>` inside the authored root | `
|
|
34
|
+
| Component scripts | plain, untyped `<script>` inside the authored root | `main.py`, `public/js/pp-reactive-v2.js` | the server preserves the plain script inside an inert component template; before materialization or morph insertion, PulsePoint captures and empties its source to prevent native execution, then evaluates it in component scope; one owned script per root |
|
|
36
35
|
| Template expressions | text and attributes with `{...}` | `public/js/pp-reactive-v2.js` | top-level script bindings are exported, nested bindings are not; every destructuring shape is supported at the top level (array, object, nested, defaults, rest, holes), so any tuple-returning hook reaches template scope |
|
|
37
36
|
| State | `pp.state(initial)` | `public/js/pp-reactive-v2.js` | setters accept values or updater functions, state belongs to the component instance |
|
|
38
37
|
| Effects | `pp.effect(...)`, `pp.layoutEffect(...)` | `public/js/pp-reactive-v2.js` | callbacks may return cleanup functions, promises are not awaited |
|
|
@@ -76,7 +75,7 @@ If an inspected browser DOM disagrees with authored template source, remember th
|
|
|
76
75
|
- **Templates are plain HTML, not JSX.** PulsePoint borrows React's *hook API* and *component decomposition*, never its markup syntax. Quote every brace attribute (`class="{…}"`, never `class={…}` — the unquoted form is invalid HTML and blanks the page with no console error), use `hidden="{…}"` instead of `{cond && <div/>}`, and `<template pp-for="…">` instead of `{list.map(...)}`. See [pulsepoint.md](./pulsepoint.md) "PulsePoint Is Not JSX".
|
|
77
76
|
- Author one root element or one imported `x-*` root per route, layout, or component template.
|
|
78
77
|
- Keep any owned plain `<script>` inside that same root.
|
|
79
|
-
- Do not handwrite `pp-component`, `
|
|
78
|
+
- Do not handwrite `pp-component`, `data-pp-ref`, `pp-owner`, `pp-event-owner`, or other runtime-managed attributes.
|
|
80
79
|
- Use `pp.rpc(...)` for current browser-to-server calls.
|
|
81
80
|
- Use native `on*` attributes for button clicks, form submits, input changes, filters, toggles, and menus instead of adding ids and manual listeners.
|
|
82
81
|
- For ordinary form submits, bind `onsubmit` on the form and read named fields with `Object.fromEntries(new FormData(event.currentTarget).entries())`; reserve `pp-ref` for imperative access rather than routine payload collection.
|
|
@@ -182,7 +181,7 @@ Grouped shell scroll reset:
|
|
|
182
181
|
Use these prompts after docs or runtime changes to confirm AI can route correctly:
|
|
183
182
|
|
|
184
183
|
- "Create an interactive filter in a Caspian route template."
|
|
185
|
-
- "Explain
|
|
184
|
+
- "Explain how a plain component `<script>` stays inert while its deferred root is materialized."
|
|
186
185
|
- "Debug a dashboard sidebar losing scroll during child-route navigation."
|
|
187
186
|
- "Add an upload widget with progress and a reactive file list."
|
|
188
187
|
- "Use context to share theme state between parent and child components."
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -31,7 +31,7 @@ Do not assume React, Vue, Svelte, JSX, Alpine, HTMX, or older PulsePoint docs un
|
|
|
31
31
|
Apply these before generating any template, even without reading the rest of this page:
|
|
32
32
|
|
|
33
33
|
1. One authored top-level root per route, layout, or component template; the owned plain `<script>` lives inside that root, never as a sibling.
|
|
34
|
-
2. Never handwrite `pp-component`, `
|
|
34
|
+
2. Never handwrite `pp-component`, `data-pp-ref`, or other runtime-managed attributes; the render pipeline injects them. Keep owned component logic in a plain, untyped `<script>` inside the root.
|
|
35
35
|
3. Bind first-party events with native `on*` attributes in the HTML; never wire normal UI with ids, `querySelector`, `addEventListener`, or manual `innerHTML`.
|
|
36
36
|
4. For ordinary form submits, use `onsubmit="{handler(event)}"` plus `Object.fromEntries(new FormData(event.currentTarget).entries())`; refs are for imperative access only.
|
|
37
37
|
5. Keep reactive values in `pp.state(...)`; keep template-facing bindings at the top level of the script.
|
|
@@ -108,7 +108,7 @@ There is **no** `pp-if`, `pp-show`, `pp-else`, `pp-model`, `pp-bind`, `pp-class`
|
|
|
108
108
|
|
|
109
109
|
The render pipeline or the browser runtime writes these. Handwriting them corrupts instance tracking:
|
|
110
110
|
|
|
111
|
-
`pp-component`, `
|
|
111
|
+
`pp-component`, `pp-owner`, `pp-event-owner`, `pp-ref-owner`, `pp-ref-forward`, `pp-context-provider` (the generated tag), `pp-context-token`, `pp-context-value`, `pp-root-layout`, `pp-fragment-root`, `data-pp-ref`, `data-pp-input-value`, `data-pp-default-value`, `data-pp-checked-value`, `data-pp-default-checked`, `data-pp-select-value`, `pp-dynamic-script`, `pp-dynamic-meta`, `pp-dynamic-link`.
|
|
112
112
|
|
|
113
113
|
`pp-ref-forward` appears in the author-facing table only because you will see it in rendered HTML and in composition components; the server component compiler sets it on a root whose own root is another `x-*` component. Do not write it yourself.
|
|
114
114
|
|
|
@@ -131,15 +131,15 @@ Inside an `on*` attribute the runtime provides `event`, plus the aliases `e`, `$
|
|
|
131
131
|
When documenting or generating PulsePoint code, follow this order:
|
|
132
132
|
|
|
133
133
|
- `public/js/pp-reactive-v2.js` is the shipped browser runtime contract AI should follow.
|
|
134
|
-
- `main.py` is the render-pipeline source of truth for
|
|
134
|
+
- `main.py` is the render-pipeline source of truth for component transformation, runtime-attribute injection, and final inert-template deferral.
|
|
135
135
|
- If you are working inside PulsePoint or Caspian runtime development code and there is an authoring source tree behind the shipped files, use it only as an implementation detail. Do not assume that source tree exists in generated apps or shipped framework output.
|
|
136
136
|
|
|
137
137
|
Important current facts:
|
|
138
138
|
|
|
139
139
|
- `public/js/pp-reactive-v2.js` exposes the global `pp` runtime and auto-mounts on DOM ready.
|
|
140
|
-
- `main.py` renders the final HTML,
|
|
141
|
-
- `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` on the final resolved root after component expansion.
|
|
142
|
-
-
|
|
140
|
+
- `main.py` renders the final HTML, transforms components, and defers outermost component roots inside inert templates before returning the response.
|
|
141
|
+
- `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` on the final resolved root after component expansion.
|
|
142
|
+
- `public/js/pp-reactive-v2.js` captures and empties plain component scripts before materialization or morph insertion, then evaluates their source in component scope.
|
|
143
143
|
- Authored route and component templates compose reusable server components as HTML-first `x-*` tags before the browser runtime mounts.
|
|
144
144
|
|
|
145
145
|
If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
|
|
@@ -359,7 +359,7 @@ Treat this section as the canonical authored-vs-runtime contract for Caspian tem
|
|
|
359
359
|
PulsePoint authoring is split into two layers:
|
|
360
360
|
|
|
361
361
|
- The authored layer: route, layout, and component templates under `src/` with plain HTML plus a plain `<script>`.
|
|
362
|
-
- The runtime layer: the browser sees `pp-component` roots
|
|
362
|
+
- The runtime layer: the browser sees `pp-component` roots with plain owned scripts whose source PulsePoint captures before the roots become live.
|
|
363
363
|
|
|
364
364
|
For authored Caspian templates:
|
|
365
365
|
|
|
@@ -367,7 +367,6 @@ For authored Caspian templates:
|
|
|
367
367
|
- 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.
|
|
368
368
|
- Put the component logic inside a plain `<script>` inside that same root.
|
|
369
369
|
- Do not handwrite `pp-component="..."`.
|
|
370
|
-
- Do not handwrite `type="text/pp"`.
|
|
371
370
|
|
|
372
371
|
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.
|
|
373
372
|
|
|
@@ -398,7 +397,7 @@ Authored example:
|
|
|
398
397
|
</section>
|
|
399
398
|
```
|
|
400
399
|
|
|
401
|
-
When that template reaches the browser, Caspian
|
|
400
|
+
When that template reaches the browser, Caspian has injected the component id and deferred the root inside an inert template. PulsePoint captures the plain script source before materialization so native execution cannot occur, then evaluates that source in component scope.
|
|
402
401
|
|
|
403
402
|
Invalid authored shape:
|
|
404
403
|
|
|
@@ -429,7 +428,8 @@ Important:
|
|
|
429
428
|
## Component roots and scripts
|
|
430
429
|
|
|
431
430
|
- Each runtime component root should have at most one owned script.
|
|
432
|
-
-
|
|
431
|
+
- The owned script remains a plain, untyped `<script>`.
|
|
432
|
+
- Before a deferred root or later morph becomes live, PulsePoint captures and empties that script; the captured source is evaluated only through the component runtime.
|
|
433
433
|
- The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
|
|
434
434
|
- If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
435
435
|
- Authored route, layout, and component templates still need one top-level parent node so Caspian can inject the component boundary correctly after component expansion.
|
|
@@ -476,7 +476,7 @@ Example:
|
|
|
476
476
|
</div>
|
|
477
477
|
```
|
|
478
478
|
|
|
479
|
-
That is the authored form. In browser-inspected runtime HTML, Caspian will already have added the component id
|
|
479
|
+
That is the authored form. In browser-inspected runtime HTML, Caspian will already have added the component id; the owned script remains plain and is removed after PulsePoint captures its source.
|
|
480
480
|
|
|
481
481
|
## Hooks and runtime API
|
|
482
482
|
|
|
@@ -1195,7 +1195,8 @@ Example:
|
|
|
1195
1195
|
The render pipeline wraps each top-level `pp-component` root in an inert `<template pp-component="…">`, and the runtime materializes it back into live DOM on `mount()` (and on every SPA navigation) before it scans for roots. The browser never parses, validates, or fetches the contents of a `<template>`, so raw `{...}` placeholders never reach live DOM at first paint.
|
|
1196
1196
|
|
|
1197
1197
|
- Because of this, `{...}` is safe in **any** attribute or position — including slots the browser would otherwise validate eagerly: SVG geometry (`d`, `viewBox`, `points`, `transform`), URL attributes (`src`, `srcset`, `href`, `poster`), form `value` on `date`/`number`/`color` inputs, and text placed directly inside `<table>` or `<select>`.
|
|
1198
|
-
-
|
|
1198
|
+
- Before materialization, PulsePoint captures and empties each plain component script so inserting the fragment cannot trigger native browser execution; scripts introduced by later morphs receive the same treatment.
|
|
1199
|
+
- Do not add per-tag workarounds to dodge first-paint validation (static-path `hidden` toggles instead of binding `d`, `data-*` URL holders, `hidden`-gated `<img src>`, or an SSR-resolved initial value). The deferral removes the whole class at once.
|
|
1199
1200
|
- `pp-style` and the `<input>`/`<select>`/`checked`/`defaultvalue`/`<textarea>` value rewrites still apply — but for different reasons that deferral does not replace: authoring-source tooling (`style="{...}"` breaks HTML/CSS linters) and attribute-vs-property correctness for controlled form fields.
|
|
1200
1201
|
|
|
1201
1202
|
## Refs
|
|
@@ -1430,7 +1431,7 @@ When you inspect rendered HTML in the browser, you should expect to see runtime-
|
|
|
1430
1431
|
Normal runtime output includes:
|
|
1431
1432
|
|
|
1432
1433
|
- `pp-component="..."` on mounted roots.
|
|
1433
|
-
-
|
|
1434
|
+
- plain owned component `<script>` elements during deferred bootstrap; the runtime removes them from the rendered component template after capturing their source.
|
|
1434
1435
|
- internal attributes such as captured ref tokens and event-owner bookkeeping.
|
|
1435
1436
|
|
|
1436
1437
|
These are runtime details.
|
|
@@ -1449,9 +1450,9 @@ Use these rules when generating or editing PulsePoint runtime code:
|
|
|
1449
1450
|
- For simple forms, bind `onsubmit` in the authored HTML and read named fields with `Object.fromEntries(new FormData(event.currentTarget).entries())`; do not generate per-input refs or effect-managed submit listeners just to gather values.
|
|
1450
1451
|
- Treat `pp.rpc()` as the default browser-to-server path for CRUD operations and interactive backend reads.
|
|
1451
1452
|
- Use `public/js/pp-reactive-v2.js` as the shipped runtime contract AI should follow.
|
|
1452
|
-
- Keep `main.py` in view because it injects
|
|
1453
|
+
- Keep `main.py` in view because it injects runtime-facing component attributes and defers roots before the browser sees live component DOM.
|
|
1453
1454
|
- 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.
|
|
1454
|
-
- In authored Caspian templates, do not handwrite `pp-component
|
|
1455
|
+
- In authored Caspian templates, do not handwrite `pp-component`; let the render pipeline inject it, and keep owned logic in a plain `<script>`.
|
|
1455
1456
|
- 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`.
|
|
1456
1457
|
- For grouped shells with independent shell and content scrolling, put `pp-reset-scroll="true"` on the content pane rather than the whole shell when only the page content should reset between child-route navigations.
|
|
1457
1458
|
- Prefer PulsePoint state and template directives over manual DOM mutation for reactive updates.
|
|
@@ -1462,7 +1463,7 @@ Use these rules when generating or editing PulsePoint runtime code:
|
|
|
1462
1463
|
- Avoid generating ids, `data-*` state, `querySelector`, `getElementById`, `addEventListener`, manual `innerHTML`, or custom event buses for normal Caspian UI behavior.
|
|
1463
1464
|
- Avoid data-attribute click wiring such as `data-action="save"` plus a delegated listener. Use `onclick="save()"` or `onsubmit="{save(event)}"` in authored HTML and keep reactive state in `pp.state(...)`.
|
|
1464
1465
|
- If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
|
|
1465
|
-
- In authored templates, use a plain `<script>` inside the root.
|
|
1466
|
+
- In authored templates, use a plain, untyped `<script>` inside the root. PulsePoint safely captures it during deferred bootstrap and morph insertion.
|
|
1466
1467
|
- Keep template-facing variables at top level.
|
|
1467
1468
|
- Follow the HTML-first context pattern: `pp.createContext(...)`, a lowercase provider tag such as `<themecontext.provider value="{...}">`, and `pp.context(token)`.
|
|
1468
1469
|
- Do not invent `pp.provideContext`, `pp-context`, or other legacy context helpers.
|
|
@@ -1503,7 +1504,7 @@ Also avoid:
|
|
|
1503
1504
|
- `pp-dynamic-script`
|
|
1504
1505
|
- `pp-dynamic-meta`
|
|
1505
1506
|
- `pp-dynamic-link`
|
|
1506
|
-
- handwritten `pp-component="..."`
|
|
1507
|
+
- handwritten `pp-component="..."` in authored route, layout, or component templates
|
|
1507
1508
|
- `pp.fetchFunction()` as the current raw runtime helper name
|
|
1508
1509
|
- made-up hooks, directives, or globals not present in the current bundled runtime
|
|
1509
1510
|
|
|
@@ -1513,8 +1514,9 @@ These are current runtime caveats that matter for authors and AI tools:
|
|
|
1513
1514
|
|
|
1514
1515
|
- `pp-component` is the registry key for instances, state, parent tracking, and templates. Treat it as unique per mounted root.
|
|
1515
1516
|
- `public/js/pp-reactive-v2.js` is the runtime surface that ships and should be assumed to exist.
|
|
1516
|
-
- Caspian
|
|
1517
|
-
-
|
|
1517
|
+
- Caspian injects `pp-component` and preserves owned scripts as plain elements inside deferred roots.
|
|
1518
|
+
- PulsePoint captures and empties plain scripts before a deferred root or later morph becomes live, preventing native browser execution while preserving component-scope evaluation.
|
|
1519
|
+
- Nested roots without their own plain owned `<script>` block are not fully isolated during parent template compilation.
|
|
1518
1520
|
- The global `pp` singleton auto-mounts on DOM ready, and `pp.mount()` is idempotent.
|
|
1519
1521
|
- `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Their callbacks are not promise-aware.
|
|
1520
1522
|
- Callback refs may return synchronous cleanup functions, which run instead of a later `callback(null)` detach call.
|
package/dist/docs/routing.md
CHANGED
|
@@ -185,7 +185,7 @@ Place those import comments at the top of the file, above the authored root elem
|
|
|
185
185
|
|
|
186
186
|
Keep route templates as composition, not as the place where all section markup accumulates. A route with tabs, dashboard panels, forms, tables, or repeated cards should import focused components for those responsibilities and assemble them with `x-*` tags. For example, a dashboard route can import `<x-dashboard-header />`, `<x-metric-strip />`, `<x-activity-tab />`, and `<x-settings-tab />` rather than placing every tab panel's full markup in `index.html` or one giant Python component.
|
|
187
187
|
|
|
188
|
-
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
|
|
188
|
+
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`. 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.
|
|
189
189
|
|
|
190
190
|
When a route needs button clicks, form submits, input changes, filters, tabs, menus, uploads, polling, or reactive list updates, author those interactions as PulsePoint behavior in `index.html`. Use `onclick`, `oninput`, `onchange`, `onsubmit`, `pp.state(...)`, `pp-for`, refs, effects, and `pp.rpc(...)` instead of starting with `id` attributes plus `document.querySelector(...)` or `addEventListener(...)`. For simple form submits, prefer `onsubmit="{submitForm(event)}"` and `Object.fromEntries(new FormData(event.currentTarget).entries())` over per-input `pp-ref` payload collection.
|
|
191
191
|
|
|
@@ -389,7 +389,7 @@ Resolved SEO fields are exposed to layouts as `{{ metadata.* }}`, while values r
|
|
|
389
389
|
|
|
390
390
|
Use `layout.html` for the shared wrapper markup of a subtree. Keep the visible shell here, not in `layout.py`.
|
|
391
391
|
|
|
392
|
-
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
|
|
392
|
+
Follow the same authoring contract used by route templates: one authored parent node, top-of-file `<!-- @import ... -->` directives above that root, a plain `<script>` inside the root when needed, and no handwritten `pp-component`. See [pulsepoint.md](./pulsepoint.md) for the canonical authored-vs-runtime explanation.
|
|
393
393
|
|
|
394
394
|
Place child routes with a plain HTML `<slot />` tag. Caspian replaces that layout slot with the current child route or nested layout while rendering.
|
|
395
395
|
|