caspian-utils 0.0.3 → 0.0.4

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.
@@ -94,7 +94,7 @@ def Counter(label: str = "Clicks") -> str:
94
94
  {count}
95
95
  </button>
96
96
 
97
- <script type="text/pp">
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 Scripts
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
- - Add `<script type="text/pp">` only when the route or component actually needs PulsePoint logic.
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 type="text/pp">
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
- Valid:
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
- Invalid:
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>
@@ -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 templates
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 nested layouts
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 `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.
58
- 3. Prefer local docs before generating code, commands, or migration guidance.
59
- 4. Check `node_modules/caspian-utils/dist/docs/` for packaged Caspian docs when local docs need more detail.
60
- 5. Only fall back to upstream documentation when local and packaged markdown do not cover the topic.
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.
@@ -37,8 +37,9 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
37
37
  - Source authoring under `src/` may use convenience syntax that is transformed before it reaches the browser.
38
38
  - This page describes the bundled browser runtime shipped in `public/js/pp-reactive-v2.js`.
39
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>`.
40
+ - In authored route, layout, and component templates, do not add `pp-component` manually. The Python side injects it onto the template root during render.
41
+ - In authored templates, write PulsePoint logic in a plain `<script>` inside that root. `main.py` runs `transform_scripts(...)`, so the runtime receives `script[type="text/pp"]`.
42
+ - When reading or debugging runtime HTML, look for `pp-component` roots and `script[type="text/pp"]`.
42
43
 
43
44
  ## Runtime shape
44
45
 
@@ -56,11 +57,12 @@ Important:
56
57
 
57
58
  ## Component roots and scripts
58
59
 
59
- - Each runtime component root should have at most one `<script>` block.
60
- - The runtime only treats `script` as component logic.
60
+ - Each runtime component root should have at most one `script[type="text/pp"]` block.
61
+ - The runtime only treats `script[type="text/pp"]` as component logic.
61
62
  - 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
63
  - If multiple matching scripts exist in the same root, the first matching owned script wins. Generate one script per root.
63
- - Component HTML templates must have exactly one top-level lowercase HTML root because the compiler injects `pp-component` onto that root during render.
64
+ - Authored component and route HTML templates must have exactly one top-level lowercase HTML root because the compiler injects `pp-component` onto that root during render. Think React-style single parent wrapper, not sibling top-level tags.
65
+ - In authored Caspian templates, write plain `<script>` inside the single root and let the render pipeline rewrite it before mount.
64
66
  - 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
67
  - Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
66
68
  - The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
@@ -88,7 +90,7 @@ Example:
88
90
  <p>Count: {count}</p>
89
91
  <button onclick="setCount(count + 1)">Increment</button>
90
92
 
91
- <script>
93
+ <script type="text/pp">
92
94
  const { title } = pp.props;
93
95
  const [count, setCount] = pp.state(0);
94
96
 
@@ -99,7 +101,7 @@ Example:
99
101
  </div>
100
102
  ```
101
103
 
102
- That example shows runtime HTML after mountable roots already exist. In authored Caspian templates, you normally write the root without `pp-component` and let the Python renderer inject it before the browser runtime sees the HTML.
104
+ That example shows runtime HTML after mountable roots already exist. In authored Caspian templates, you normally write the root without `pp-component` and keep the logic in a plain `<script>` so the Python render path can inject both runtime attributes before the browser runtime sees the HTML.
103
105
 
104
106
  ## Hooks and runtime API
105
107
 
@@ -332,8 +334,9 @@ Use these rules when generating or editing PulsePoint runtime code:
332
334
 
333
335
  - Treat PulsePoint as the default reactive frontend for Caspian app code.
334
336
  - Use the bundled runtime contract in `public/js/pp-reactive-v2.js`.
335
- - Generate unique `pp-component` values per live instance.
336
- - Use only `<script>` for component logic inside a `pp-component` root.
337
+ - In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
338
+ - If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
339
+ - In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
337
340
  - Keep template-facing variables at top level.
338
341
  - Use `pp.createContext`, `pp.context`, and `pp.provideContext` for context. Do not invent `pp-context`.
339
342
  - Keep `pp-for` on `<template>` and use plain `key`.
@@ -356,7 +359,7 @@ Do not generate these unless the current source explicitly adds support:
356
359
  - `pp-dynamic-script`
357
360
  - `pp-dynamic-meta`
358
361
  - `pp-dynamic-link`
359
- - plain `<script>` for component logic inside a `pp-component` root
362
+ - handwritten `pp-component="..."` or `type="text/pp"` in authored route, layout, or component templates
360
363
  - `pp.fetchFunction()` as the current raw runtime helper name
361
364
  - made-up hooks, directives, or globals not present in the current bundled runtime
362
365
 
@@ -365,7 +368,7 @@ Do not generate these unless the current source explicitly adds support:
365
368
  These are current runtime caveats that matter for authors and AI tools:
366
369
 
367
370
  - `pp-component` is the registry key for instances, state, parent tracking, and templates. Treat it as unique per mounted root.
368
- - Nested roots without their own `script` block are not fully isolated during parent template compilation.
371
+ - Nested roots without their own `script[type="text/pp"]` block are not fully isolated during parent template compilation.
369
372
  - The global `pp` singleton auto-mounts on DOM ready, and `pp.mount()` is idempotent.
370
373
  - `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Their callbacks are not promise-aware.
371
374
  - `pp.context()` resolves through ancestor components, not the current component's own pending providers.
@@ -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 type="text/pp">
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {