caspian-utils 0.0.3 → 0.0.5
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/components.md +29 -9
- package/dist/docs/index.md +10 -6
- package/dist/docs/project-structure.md +16 -0
- package/dist/docs/pulsepoint.md +129 -45
- package/dist/docs/routing.md +30 -2
- package/package.json +1 -1
package/dist/docs/components.md
CHANGED
|
@@ -94,7 +94,7 @@ def Counter(label: str = "Clicks") -> str:
|
|
|
94
94
|
{count}
|
|
95
95
|
</button>
|
|
96
96
|
|
|
97
|
-
<script
|
|
97
|
+
<script>
|
|
98
98
|
const [count, setCount] = pp.state(0);
|
|
99
99
|
</script>
|
|
100
100
|
</div>
|
|
@@ -108,16 +108,18 @@ Use this split when:
|
|
|
108
108
|
|
|
109
109
|
This keeps the component easy to read: Python owns the server-side logic and template context, while the HTML file owns the rendered UI and browser behavior.
|
|
110
110
|
|
|
111
|
-
## Auto-Injected `pp-component` And PulsePoint
|
|
111
|
+
## Auto-Injected `pp-component` And PulsePoint Script Type
|
|
112
112
|
|
|
113
|
-
Treat `pp-component="componentName"` as framework output, not authored source.
|
|
113
|
+
Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source.
|
|
114
114
|
|
|
115
|
-
- Do not manually add `pp-component` to route templates or component HTML files.
|
|
115
|
+
- Do not manually add `pp-component="..."` to route templates, layout templates, or component HTML files.
|
|
116
|
+
- Do not manually add `type="text/pp"` to authored PulsePoint scripts.
|
|
116
117
|
- The Python render pipeline injects `pp-component` onto the single root element during render.
|
|
117
|
-
-
|
|
118
|
+
- `main.py` runs `transform_scripts(...)`, which rewrites authored body `<script>` tags to `<script type="text/pp">` before the browser runtime mounts.
|
|
119
|
+
- Add a plain `<script>` only when the route or component actually needs PulsePoint logic.
|
|
118
120
|
- When you do add a PulsePoint script, keep it inside that single root element.
|
|
119
121
|
|
|
120
|
-
Examples in runtime docs often show rendered HTML that already contains `pp-component`. That is the final output shape, not the normal authoring pattern.
|
|
122
|
+
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.
|
|
121
123
|
|
|
122
124
|
### Authored Source vs Rendered Output
|
|
123
125
|
|
|
@@ -132,7 +134,7 @@ Author this:
|
|
|
132
134
|
{count}
|
|
133
135
|
</button>
|
|
134
136
|
|
|
135
|
-
<script
|
|
137
|
+
<script>
|
|
136
138
|
const [count, setCount] = pp.state(0);
|
|
137
139
|
</script>
|
|
138
140
|
</div>
|
|
@@ -161,15 +163,33 @@ The same rule applies to route templates such as `src/app/**/index.html`: one ro
|
|
|
161
163
|
|
|
162
164
|
This is not just style guidance. The installed compiler injects `pp-component` onto the root element, and it raises an error when the template has no root, multiple sibling roots, stray top-level text, or a component tag as the root.
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent element. If the template needs a PulsePoint script, keep that script inside the same parent root.
|
|
167
|
+
|
|
168
|
+
Good:
|
|
165
169
|
|
|
166
170
|
```html
|
|
167
171
|
<div class="card">
|
|
168
172
|
<h2>Title</h2>
|
|
173
|
+
|
|
174
|
+
<script>
|
|
175
|
+
const [open, setOpen] = pp.state(false);
|
|
176
|
+
</script>
|
|
169
177
|
</div>
|
|
170
178
|
```
|
|
171
179
|
|
|
172
|
-
|
|
180
|
+
Bad:
|
|
181
|
+
|
|
182
|
+
```html
|
|
183
|
+
<div class="card">
|
|
184
|
+
<h2>Title</h2>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<script>
|
|
188
|
+
const [open, setOpen] = pp.state(false);
|
|
189
|
+
</script>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Also bad:
|
|
173
193
|
|
|
174
194
|
```html
|
|
175
195
|
<h2>Title</h2>
|
package/dist/docs/index.md
CHANGED
|
@@ -13,6 +13,8 @@ related:
|
|
|
13
13
|
|
|
14
14
|
This directory contains the local Caspian documentation set for quick reference and AI-aware routing.
|
|
15
15
|
|
|
16
|
+
Before making feature, tooling, or file-placement decisions in a Caspian workspace, read `./caspian.config.json` almost immediately. That file is the project feature gate and tells you which capabilities are enabled, such as Prisma, MCP, TypeScript, Tailwind, backend-only mode, and component scan directories.
|
|
17
|
+
|
|
16
18
|
## Default Stack
|
|
17
19
|
|
|
18
20
|
When generating or editing a Caspian app, treat these as the default choices unless the task explicitly requires something else:
|
|
@@ -37,14 +39,14 @@ The packaged Caspian docs distributed by the current toolchain also live here:
|
|
|
37
39
|
- `commands.md` - Main Caspian CLI workflows for project creation, generation, updates, and config-aware maintenance
|
|
38
40
|
- `database.md` - Prisma schema, migration, seed, and client-generation workflow for the current workspace, plus Python-side helper caveats
|
|
39
41
|
- `auth.md` - Session-backed authentication with `casp.auth`, centralized `auth_config.py`, decorators, RBAC, and OAuth provider helpers
|
|
40
|
-
- `components.md` - Create reusable Python components, template-backed UI, JSX-style imports, and single-root component
|
|
42
|
+
- `components.md` - Create reusable Python components, template-backed UI, JSX-style imports, and the single-parent root rule for component HTML files
|
|
41
43
|
- `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, and client-side behaviors
|
|
42
44
|
- `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
|
|
43
45
|
- `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
|
|
44
46
|
- `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
|
|
45
47
|
- `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
|
|
46
48
|
- `metadata.md` - Static and dynamic metadata, SEO inheritance, and Open Graph or Twitter card tags
|
|
47
|
-
- `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, route groups, and
|
|
49
|
+
- `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, route groups, nested layouts, and single-root route templates
|
|
48
50
|
- `project-structure.md` - Default Caspian layout and where routes, templates, shared code, and database files belong
|
|
49
51
|
|
|
50
52
|
## AI Awareness Notes
|
|
@@ -54,10 +56,12 @@ If an AI tool needs Caspian project documentation, start with this directory and
|
|
|
54
56
|
Preferred lookup order:
|
|
55
57
|
|
|
56
58
|
1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
|
|
57
|
-
2. Read
|
|
58
|
-
3.
|
|
59
|
-
4.
|
|
60
|
-
5.
|
|
59
|
+
2. Read `./caspian.config.json` before making any feature assumption. Use it to confirm which project capabilities are enabled and which directories or tooling rules apply.
|
|
60
|
+
3. Read `database.md` for Prisma setup and ORM usage, `auth.md` for session auth and route protection, `components.md` for reusable UI authoring, `pulsepoint.md` for browser-side reactive runtime behavior, `fetch-data.md` for data flows, `state.md` for transient request-scoped state, `cache.md` for page caching, and `validation.md` for input boundaries.
|
|
61
|
+
4. For authored component, route, and layout HTML files, generate exactly one top-level lowercase HTML element. Think React-style single parent wrapper: keep any owned PulsePoint logic in a plain `<script>` inside that root, and do not handwrite `pp-component="..."` or `type="text/pp"` because Caspian injects those runtime attributes automatically.
|
|
62
|
+
5. Prefer local docs before generating code, commands, or migration guidance.
|
|
63
|
+
6. Check `node_modules/caspian-utils/dist/docs/` for packaged Caspian docs when local docs need more detail.
|
|
64
|
+
7. Only fall back to upstream documentation when local and packaged markdown do not cover the topic.
|
|
61
65
|
|
|
62
66
|
## Maintenance
|
|
63
67
|
|
|
@@ -24,6 +24,8 @@ In that layout, the default stack is Python components for reusable UI, PulsePoi
|
|
|
24
24
|
|
|
25
25
|
For public pages that can safely reuse rendered HTML, Caspian also supports route-level page caching through `casp.cache_handler`.
|
|
26
26
|
|
|
27
|
+
Before an AI agent decides which Caspian features are available in a workspace, it should read `./caspian.config.json` almost immediately. That file is the feature gate for project capabilities such as `backendOnly`, `tailwindcss`, `mcp`, `prisma`, `typescript`, and `componentScanDirs`.
|
|
28
|
+
|
|
27
29
|
## Top-Level Areas
|
|
28
30
|
|
|
29
31
|
- `src/` contains routes, page templates, styles, and shared libraries.
|
|
@@ -84,6 +86,10 @@ This is the main application area. It contains route files, templates, styles, a
|
|
|
84
86
|
|
|
85
87
|
This directory handles file-based routing. Route templates and route-specific backend logic live here.
|
|
86
88
|
|
|
89
|
+
When authoring a route HTML file such as `src/app/**/index.html`, keep the whole template inside exactly one top-level lowercase HTML element. Treat it the same way you would a React component returning one parent element: wrap the route markup and any owned PulsePoint script in the same root.
|
|
90
|
+
|
|
91
|
+
Do not handwrite `pp-component="..."` or `type="text/pp"` in those source templates. Author a plain `<script>` inside the root when you need PulsePoint logic, and let Caspian add the runtime attributes during render.
|
|
92
|
+
|
|
87
93
|
See `routing.md` for the full App Router-style rules for dynamic segments, route groups, and nested layouts.
|
|
88
94
|
|
|
89
95
|
### `src/components/`
|
|
@@ -92,6 +98,10 @@ Use this folder for reusable UI components that should be imported into route te
|
|
|
92
98
|
|
|
93
99
|
The common Caspian pattern is a Python file such as `Button.py` with `@component`, optionally paired with a same-name HTML file such as `Button.html` when the component has richer markup or PulsePoint behavior.
|
|
94
100
|
|
|
101
|
+
For component HTML files, follow the same one-parent rule as route HTML files: one top-level lowercase HTML element only, with no sibling roots and no top-level script sitting outside that root.
|
|
102
|
+
|
|
103
|
+
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.
|
|
104
|
+
|
|
95
105
|
This workspace's component tooling scans `src/` based on `caspian.config.json`, so `src/components/` is a conventionally clean location, not a hard-coded runtime requirement.
|
|
96
106
|
|
|
97
107
|
### `src/lib/`
|
|
@@ -139,6 +149,10 @@ Use `main.py` for auth bootstrap and middleware-order changes. Use `src/lib/auth
|
|
|
139
149
|
|
|
140
150
|
The core feature configuration file for the application.
|
|
141
151
|
|
|
152
|
+
AI agents should read this file before making almost any feature-level decision. Use it to confirm which capabilities are enabled, which code generation paths make sense, and which directories should be scanned for components or other project assets.
|
|
153
|
+
|
|
154
|
+
In the current workspace, `caspian.config.json` shows `backendOnly: false`, `tailwindcss: true`, `mcp: false`, `prisma: true`, `typescript: false`, and `componentScanDirs: ["src"]`.
|
|
155
|
+
|
|
142
156
|
### `src/lib/auth/auth_config.py`
|
|
143
157
|
|
|
144
158
|
The project auth configuration file. Use this path when changing authentication behavior.
|
|
@@ -180,9 +194,11 @@ The packaged Caspian documentation location distributed with the current toolcha
|
|
|
180
194
|
|
|
181
195
|
If an AI agent is deciding where to make changes, use these rules first.
|
|
182
196
|
|
|
197
|
+
- Read `caspian.config.json` almost immediately before making feature, tooling, or file-placement decisions. It tells you which Caspian features are enabled in the current workspace.
|
|
183
198
|
- Put route templates and route-specific backend logic in `src/app/`.
|
|
184
199
|
- Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
|
|
185
200
|
- Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, import comments, and single-root template rules.
|
|
201
|
+
- For route and component HTML files, always emit one top-level lowercase HTML element. Good: one wrapper containing the content and a plain `<script>` when needed. Bad: a wrapper element followed by a sibling top-level `<script>`, or handwritten `pp-component="..."` and `type="text/pp"` attributes in source.
|
|
186
202
|
- Put shared helpers and reusable libraries in `src/lib/`.
|
|
187
203
|
- Use PulsePoint conventions in route templates for reactive frontend behavior.
|
|
188
204
|
- Use `@rpc()` in route/backend code and `pp.rpc()` in client code for browser-triggered data flows.
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: PulsePoint Runtime Guide
|
|
3
|
-
description: Learn how AI agents should
|
|
3
|
+
description: Learn how AI agents should author PulsePoint against the shipped browser runtime in `public/js/pp-reactive-v2.js` and the Caspian render pipeline that injects runtime attributes automatically.
|
|
4
4
|
related:
|
|
5
5
|
title: Related docs
|
|
6
6
|
description: Read the components, routing, data-fetching, and project-structure docs alongside the PulsePoint runtime contract.
|
|
@@ -14,16 +14,31 @@ related:
|
|
|
14
14
|
|
|
15
15
|
## Purpose
|
|
16
16
|
|
|
17
|
-
This file documents the PulsePoint
|
|
17
|
+
This file documents the current PulsePoint contract for this workspace. Treat it as the AI-facing source of truth when generating or reviewing interactive Caspian UI.
|
|
18
18
|
|
|
19
19
|
If a task involves `pp.state`, `pp.effect`, `pp.layoutEffect`, `pp-ref`, `pp-spread`, `pp-for`, context, portals, SPA navigation, or component boundary behavior, read this page first and keep generated code aligned with the runtime implemented in this repo.
|
|
20
20
|
|
|
21
|
-
Use `components.md` for authoring Python `@component` files and same-name HTML templates. Use this page for the browser-side
|
|
21
|
+
Use `components.md` for authoring Python `@component` files and same-name HTML templates. Use this page for the browser-side PulsePoint contract, the authoring rules that feed it, and the React-style mental model used by the current runtime.
|
|
22
22
|
|
|
23
|
-
PulsePoint is the default reactive frontend layer for Caspian.
|
|
23
|
+
PulsePoint is the default reactive frontend layer for Caspian. In this workspace it follows a React-like component pattern, but it is HTML-first rather than JSX-first.
|
|
24
24
|
|
|
25
25
|
Do not assume React, Vue, Svelte, JSX, Alpine, HTMX, or older PulsePoint docs unless the task explicitly asks for a different frontend contract.
|
|
26
26
|
|
|
27
|
+
## Source Of Truth
|
|
28
|
+
|
|
29
|
+
For the current workspace, follow this order when documenting or generating PulsePoint code:
|
|
30
|
+
|
|
31
|
+
- `public/js/pp-reactive-v2.js` is the shipped browser runtime contract AI should follow.
|
|
32
|
+
- `main.py` is the render-pipeline source of truth for how Caspian injects runtime attributes and rewrites scripts before the browser sees the HTML.
|
|
33
|
+
- 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.
|
|
34
|
+
|
|
35
|
+
Important current facts:
|
|
36
|
+
|
|
37
|
+
- `public/js/pp-reactive-v2.js` exposes the global `pp` runtime and auto-mounts on DOM ready.
|
|
38
|
+
- `main.py` renders the final HTML, runs `transform_components(...)`, then runs `transform_scripts(...)` before returning the response.
|
|
39
|
+
|
|
40
|
+
If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
|
|
41
|
+
|
|
27
42
|
## Default Frontend Rule
|
|
28
43
|
|
|
29
44
|
When a Caspian page needs reactive browser behavior, use PulsePoint.
|
|
@@ -32,13 +47,46 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
|
|
|
32
47
|
- Keep server-rendered HTML plus PulsePoint enhancement as the baseline architecture.
|
|
33
48
|
- Only introduce another frontend runtime when the user explicitly asks for it or the project already depends on one.
|
|
34
49
|
|
|
35
|
-
##
|
|
50
|
+
## Authoring Model
|
|
51
|
+
|
|
52
|
+
PulsePoint authoring in this repo is split into two layers:
|
|
53
|
+
|
|
54
|
+
- The authored layer: route, layout, and component templates under `src/` with plain HTML plus a plain `<script>`.
|
|
55
|
+
- The runtime layer: the browser sees `pp-component` roots and `script[type="text/pp"]` after Caspian transforms the HTML.
|
|
56
|
+
|
|
57
|
+
For authored Caspian templates:
|
|
58
|
+
|
|
59
|
+
- Keep exactly one top-level lowercase HTML root element.
|
|
60
|
+
- Put the component logic inside a plain `<script>` inside that same root.
|
|
61
|
+
- Do not handwrite `pp-component="..."`.
|
|
62
|
+
- Do not handwrite `type="text/pp"`.
|
|
63
|
+
|
|
64
|
+
Caspian already handles those details for you during render.
|
|
65
|
+
|
|
66
|
+
That means AI-generated examples should default to authored template source, not raw runtime HTML.
|
|
67
|
+
|
|
68
|
+
Authored example:
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<section>
|
|
72
|
+
<h2>{title}</h2>
|
|
73
|
+
<p>Count: {count}</p>
|
|
74
|
+
|
|
75
|
+
<button onclick="setCount(count + 1)">Increment</button>
|
|
76
|
+
<button onclick="reset()">Reset</button>
|
|
77
|
+
|
|
78
|
+
<script>
|
|
79
|
+
const { title = "Counter" } = pp.props;
|
|
80
|
+
const [count, setCount] = pp.state(0);
|
|
81
|
+
|
|
82
|
+
function reset() {
|
|
83
|
+
setCount(0);
|
|
84
|
+
}
|
|
85
|
+
</script>
|
|
86
|
+
</section>
|
|
87
|
+
```
|
|
36
88
|
|
|
37
|
-
|
|
38
|
-
- This page describes the bundled browser runtime shipped in `public/js/pp-reactive-v2.js`.
|
|
39
|
-
- When source-layer examples conflict with the bundled runtime, follow the bundled runtime.
|
|
40
|
-
- In authored route and component templates, do not add `pp-component` manually. The Python side injects it onto the template root during render.
|
|
41
|
-
- In a `pp-component` root, component logic must live in `<script>`, not plain `<script>`.
|
|
89
|
+
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.
|
|
42
90
|
|
|
43
91
|
## Runtime shape
|
|
44
92
|
|
|
@@ -56,16 +104,16 @@ Important:
|
|
|
56
104
|
|
|
57
105
|
## Component roots and scripts
|
|
58
106
|
|
|
59
|
-
- Each runtime component root should have at most one
|
|
60
|
-
-
|
|
107
|
+
- Each runtime component root should have at most one owned script.
|
|
108
|
+
- At runtime, the owned script is `script[type="text/pp"]`.
|
|
61
109
|
- The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
|
|
62
|
-
- If multiple matching scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
63
|
-
-
|
|
110
|
+
- If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
111
|
+
- Authored route, layout, and component templates still need one top-level lowercase HTML root so Caspian can inject the component boundary correctly.
|
|
64
112
|
- 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.
|
|
65
113
|
- Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
|
|
66
114
|
- The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
|
|
67
115
|
- `pp.props` contains the current prop bag for the component.
|
|
68
|
-
- `pp.props.children` contains the root's initial inner HTML before the
|
|
116
|
+
- `pp.props.children` contains the root's initial inner HTML before the owned script is removed from the render template.
|
|
69
117
|
|
|
70
118
|
Bindings exported to the template:
|
|
71
119
|
|
|
@@ -83,7 +131,7 @@ Bindings that should not be assumed:
|
|
|
83
131
|
Example:
|
|
84
132
|
|
|
85
133
|
```html
|
|
86
|
-
<div
|
|
134
|
+
<div>
|
|
87
135
|
<h2>{title}</h2>
|
|
88
136
|
<p>Count: {count}</p>
|
|
89
137
|
<button onclick="setCount(count + 1)">Increment</button>
|
|
@@ -99,10 +147,12 @@ Example:
|
|
|
99
147
|
</div>
|
|
100
148
|
```
|
|
101
149
|
|
|
102
|
-
That
|
|
150
|
+
That is the authored form. In browser-inspected runtime HTML, Caspian will already have added the component id and the owned script type.
|
|
103
151
|
|
|
104
152
|
## Hooks and runtime API
|
|
105
153
|
|
|
154
|
+
PulsePoint uses a React-style mental model inside each component script: stateful render scope, dependency-based effects, refs, reducer-style updates, context consumption, and portals.
|
|
155
|
+
|
|
106
156
|
Hooks exposed inside component scripts through `pp`:
|
|
107
157
|
|
|
108
158
|
- `pp.state(initial)` returns `[value, setValue]`.
|
|
@@ -113,11 +163,10 @@ Hooks exposed inside component scripts through `pp`:
|
|
|
113
163
|
- `pp.callback(callback, deps)` memoizes a function.
|
|
114
164
|
- `pp.reducer(reducer, initialState)` returns `[state, dispatch]`.
|
|
115
165
|
- `pp.context(token)` resolves a provided context value from ancestor components.
|
|
116
|
-
- `pp.provideContext(token, value)` provides a context value to descendant components.
|
|
117
166
|
- `pp.portal(ref, target?)` registers a ref-managed element for portal rendering and returns an object that includes `sourceParent`.
|
|
118
167
|
- `pp.props` exposes the current props.
|
|
119
168
|
|
|
120
|
-
Global helpers exposed
|
|
169
|
+
Global helpers exposed through the `pp` singleton and also merged into the component runtime:
|
|
121
170
|
|
|
122
171
|
- `pp.createContext(defaultValue)` creates a context token.
|
|
123
172
|
- `pp.mount()` bootstraps the runtime. It is idempotent.
|
|
@@ -130,21 +179,22 @@ Global helpers exposed on the `pp` singleton:
|
|
|
130
179
|
|
|
131
180
|
Notes:
|
|
132
181
|
|
|
133
|
-
- The global `pp` singleton auto-mounts once
|
|
182
|
+
- The global `pp` singleton auto-mounts once the runtime is loaded and the DOM is ready. Manual `pp.mount()` is still safe because it short-circuits after the first mount.
|
|
134
183
|
- `pp.state` setters accept either a value or an updater function.
|
|
135
184
|
- `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Returned promises are not awaited.
|
|
136
185
|
- `pp.portal(ref)` defaults to `document.body` when no target is provided.
|
|
137
186
|
- Older docs may call the RPC helper `pp.fetchFunction()`. In the current bundled runtime the implemented global API is `pp.rpc()`.
|
|
138
187
|
- Keep template-facing bindings at the top level so the AST-based exporter can see them.
|
|
188
|
+
- For predictable code generation, prefer passing an explicit dependency array to `pp.effect`, `pp.layoutEffect`, `pp.memo`, and `pp.callback`.
|
|
139
189
|
|
|
140
190
|
## Context
|
|
141
191
|
|
|
142
|
-
Context is implemented in the current runtime.
|
|
192
|
+
Context is implemented in the current runtime, but the current API follows a React-style `Context.Provider` pattern rather than a legacy `pp.provideContext(...)` helper.
|
|
143
193
|
|
|
144
194
|
How it works:
|
|
145
195
|
|
|
146
196
|
- Create a token with `pp.createContext(defaultValue)`.
|
|
147
|
-
- Provide a value
|
|
197
|
+
- Provide a value with `Context.Provider`.
|
|
148
198
|
- Read it in a descendant component with `pp.context(token)`.
|
|
149
199
|
- Resolution walks the logical component parent chain stored in the registry, not the live DOM.
|
|
150
200
|
- Portaled descendants still resolve providers through component ancestry.
|
|
@@ -157,30 +207,39 @@ Important:
|
|
|
157
207
|
- The same token object must be shared between provider and consumer.
|
|
158
208
|
- Context is component-level, not directive-based. There is no `pp-context` attribute.
|
|
159
209
|
- `pp.context(token)` resolves from ancestors. A component does not consume the value it provides in the same render.
|
|
160
|
-
- If provider and consumer live in different component script scopes, pass the token through props or store it in shared
|
|
210
|
+
- If provider and consumer live in different component script scopes, pass the token through props or store it in shared outer or global state.
|
|
211
|
+
- The preferred authoring style is a provider tag in the template. The runtime also supports imperative `ThemeContext.Provider({ value })` calls during render, but the tag form is clearer for AI-generated authored templates.
|
|
212
|
+
- Do not invent or document `pp.provideContext`. The current runtime explicitly does not expose it.
|
|
161
213
|
|
|
162
|
-
|
|
214
|
+
Provider example:
|
|
163
215
|
|
|
164
216
|
```html
|
|
165
|
-
<section
|
|
166
|
-
<div pp-component="theme-label-1" theme-token="{ThemeContext}">
|
|
167
|
-
<p>{theme}</p>
|
|
168
|
-
|
|
169
|
-
<script>
|
|
170
|
-
const { themeToken } = pp.props;
|
|
171
|
-
const theme = pp.context(themeToken);
|
|
172
|
-
</script>
|
|
173
|
-
</div>
|
|
174
|
-
|
|
217
|
+
<section>
|
|
175
218
|
<script>
|
|
176
219
|
const ThemeContext = pp.createContext("light");
|
|
177
220
|
const [theme] = pp.state("dark");
|
|
178
|
-
|
|
179
|
-
pp.provideContext(ThemeContext, theme);
|
|
180
221
|
</script>
|
|
222
|
+
|
|
223
|
+
<ThemeContext.Provider value="{theme}">
|
|
224
|
+
<p>This subtree receives the provided theme.</p>
|
|
225
|
+
</ThemeContext.Provider>
|
|
181
226
|
</section>
|
|
182
227
|
```
|
|
183
228
|
|
|
229
|
+
Consumer example for a child component that receives the token through props:
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<div>
|
|
233
|
+
<p>{theme}</p>
|
|
234
|
+
|
|
235
|
+
<script>
|
|
236
|
+
const theme = pp.context(pp.props.themeToken);
|
|
237
|
+
</script>
|
|
238
|
+
</div>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
When a child component needs the same token object, pass it from the provider scope as a prop such as `theme-token="{ThemeContext}"`.
|
|
242
|
+
|
|
184
243
|
## Props and nested components
|
|
185
244
|
|
|
186
245
|
- Child component props are derived from DOM attributes.
|
|
@@ -215,7 +274,7 @@ Nested components:
|
|
|
215
274
|
Example:
|
|
216
275
|
|
|
217
276
|
```html
|
|
218
|
-
<div
|
|
277
|
+
<div>
|
|
219
278
|
<button pp-spread="{...buttonAttrs}" hidden="{isLoading}">Save</button>
|
|
220
279
|
|
|
221
280
|
<script>
|
|
@@ -244,7 +303,7 @@ Example:
|
|
|
244
303
|
Example:
|
|
245
304
|
|
|
246
305
|
```html
|
|
247
|
-
<div
|
|
306
|
+
<div>
|
|
248
307
|
<input pp-ref="nameInput" />
|
|
249
308
|
<button onclick="nameInput.current?.focus()">Focus</button>
|
|
250
309
|
|
|
@@ -268,7 +327,7 @@ Example:
|
|
|
268
327
|
Example:
|
|
269
328
|
|
|
270
329
|
```html
|
|
271
|
-
<div
|
|
330
|
+
<div>
|
|
272
331
|
<ul>
|
|
273
332
|
<template pp-for="(todo, index) in todos">
|
|
274
333
|
<li key="{todo.id}">
|
|
@@ -326,22 +385,43 @@ Notes:
|
|
|
326
385
|
- Root-layout mismatches during SPA navigation trigger a hard reload.
|
|
327
386
|
- `pp.mount()` bootstraps every `[pp-component]` it finds, so generated code should call it only through the global runtime if you are manually mounting at all.
|
|
328
387
|
|
|
388
|
+
## Runtime Output And Debugging
|
|
389
|
+
|
|
390
|
+
When you inspect rendered HTML in the browser, you should expect to see runtime-managed attributes and elements that are not part of authored source.
|
|
391
|
+
|
|
392
|
+
Normal runtime output includes:
|
|
393
|
+
|
|
394
|
+
- `pp-component="..."` on mounted roots.
|
|
395
|
+
- `script[type="text/pp"]` for owned component scripts.
|
|
396
|
+
- internal attributes such as captured ref tokens and event-owner bookkeeping.
|
|
397
|
+
|
|
398
|
+
These are runtime details.
|
|
399
|
+
|
|
400
|
+
- Mention them in docs when explaining how the browser runtime works.
|
|
401
|
+
- Do not use them as the default form in authored examples.
|
|
402
|
+
- Do not tell AI to handwrite them in route, layout, or component templates unless the task is explicitly about raw runtime HTML or runtime internals.
|
|
403
|
+
|
|
329
404
|
## AI rules
|
|
330
405
|
|
|
331
406
|
Use these rules when generating or editing PulsePoint runtime code:
|
|
332
407
|
|
|
333
408
|
- Treat PulsePoint as the default reactive frontend for Caspian app code.
|
|
334
|
-
- Use
|
|
335
|
-
-
|
|
336
|
-
-
|
|
409
|
+
- Use `public/js/pp-reactive-v2.js` as the shipped runtime contract AI should follow.
|
|
410
|
+
- Keep `main.py` in view because it injects the runtime-facing attributes and rewrites authored scripts before the browser sees them.
|
|
411
|
+
- 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.
|
|
412
|
+
- In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
|
|
413
|
+
- If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
|
|
414
|
+
- In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
|
|
337
415
|
- Keep template-facing variables at top level.
|
|
338
|
-
-
|
|
416
|
+
- Follow the React-style context pattern: `pp.createContext(...)`, `<Context.Provider value="{...}">`, and `pp.context(token)`.
|
|
417
|
+
- Do not invent `pp.provideContext`, `pp-context`, or other legacy context helpers.
|
|
339
418
|
- Keep `pp-for` on `<template>` and use plain `key`.
|
|
340
419
|
- Use native `on*` attributes, not framework-specific event syntax.
|
|
341
420
|
- Use refs and portals only through the implemented `pp` APIs.
|
|
342
421
|
- Use `pp.rpc()` for the bundled runtime API instead of older `pp.fetchFunction()` wording.
|
|
343
422
|
- Avoid generating internal runtime attributes.
|
|
344
423
|
- Avoid scriptless nested components when the child template contains its own bindings.
|
|
424
|
+
- Prefer authored-template examples over runtime-inspected HTML examples unless the doc is specifically explaining runtime internals.
|
|
345
425
|
|
|
346
426
|
## What to avoid
|
|
347
427
|
|
|
@@ -351,12 +431,13 @@ Do not generate these unless the current source explicitly adds support:
|
|
|
351
431
|
- `pp-context`
|
|
352
432
|
- `pp-key`
|
|
353
433
|
- `data-pp-ref`
|
|
434
|
+
- `pp-context-provider`
|
|
354
435
|
- `pp-owner`
|
|
355
436
|
- `pp-event-owner`
|
|
356
437
|
- `pp-dynamic-script`
|
|
357
438
|
- `pp-dynamic-meta`
|
|
358
439
|
- `pp-dynamic-link`
|
|
359
|
-
-
|
|
440
|
+
- handwritten `pp-component="..."` or `type="text/pp"` in authored route, layout, or component templates
|
|
360
441
|
- `pp.fetchFunction()` as the current raw runtime helper name
|
|
361
442
|
- made-up hooks, directives, or globals not present in the current bundled runtime
|
|
362
443
|
|
|
@@ -365,10 +446,13 @@ Do not generate these unless the current source explicitly adds support:
|
|
|
365
446
|
These are current runtime caveats that matter for authors and AI tools:
|
|
366
447
|
|
|
367
448
|
- `pp-component` is the registry key for instances, state, parent tracking, and templates. Treat it as unique per mounted root.
|
|
368
|
-
-
|
|
449
|
+
- `public/js/pp-reactive-v2.js` is the runtime surface that ships and should be assumed to exist.
|
|
450
|
+
- Caspian already injects `pp-component` and rewrites owned scripts to `type="text/pp"` during render.
|
|
451
|
+
- Nested roots without their own `script[type="text/pp"]` block are not fully isolated during parent template compilation.
|
|
369
452
|
- The global `pp` singleton auto-mounts on DOM ready, and `pp.mount()` is idempotent.
|
|
370
453
|
- `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Their callbacks are not promise-aware.
|
|
371
454
|
- `pp.context()` resolves through ancestor components, not the current component's own pending providers.
|
|
455
|
+
- `pp.provideContext` is not part of the current runtime API. Use `Context.Provider`.
|
|
372
456
|
- `pp.portal()` preserves logical ancestry through the registry, so context and prop refresh behavior continue to work through portaled descendants.
|
|
373
457
|
|
|
374
458
|
## Final reminder
|
package/dist/docs/routing.md
CHANGED
|
@@ -78,8 +78,36 @@ Route templates can import reusable Python components with `<!-- @import ... -->
|
|
|
78
78
|
|
|
79
79
|
Do not manually add `pp-component="..."` to the route root. The Python render pipeline injects that attribute onto the route's single top-level lowercase HTML element.
|
|
80
80
|
|
|
81
|
+
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.
|
|
82
|
+
|
|
81
83
|
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.
|
|
82
84
|
|
|
85
|
+
For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent element that contains the entire route markup.
|
|
86
|
+
|
|
87
|
+
Good:
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<section class="dashboard-shell">
|
|
91
|
+
<h1>Dashboard</h1>
|
|
92
|
+
|
|
93
|
+
<script>
|
|
94
|
+
const [filter, setFilter] = pp.state("all");
|
|
95
|
+
</script>
|
|
96
|
+
</section>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Bad:
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<section class="dashboard-shell">
|
|
103
|
+
<h1>Dashboard</h1>
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
<script>
|
|
107
|
+
const [filter, setFilter] = pp.state("all");
|
|
108
|
+
</script>
|
|
109
|
+
```
|
|
110
|
+
|
|
83
111
|
Example authored route template:
|
|
84
112
|
|
|
85
113
|
```html
|
|
@@ -88,7 +116,7 @@ Example authored route template:
|
|
|
88
116
|
<section class="dashboard-shell">
|
|
89
117
|
<StatsCard title="Users" value="42" />
|
|
90
118
|
|
|
91
|
-
<script
|
|
119
|
+
<script>
|
|
92
120
|
const [filter, setFilter] = pp.state("all");
|
|
93
121
|
</script>
|
|
94
122
|
</section>
|
|
@@ -108,7 +136,7 @@ Rendered shape at runtime:
|
|
|
108
136
|
</section>
|
|
109
137
|
```
|
|
110
138
|
|
|
111
|
-
Write the first form. Caspian produces the second form.
|
|
139
|
+
Write the first form. Caspian produces the second form by injecting `pp-component` on the root and `type="text/pp"` on the owned script.
|
|
112
140
|
|
|
113
141
|
### `index.py`
|
|
114
142
|
|