caspian-utils 0.0.17 → 0.0.18

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.
@@ -17,9 +17,9 @@ related:
17
17
  - /docs/index
18
18
  ---
19
19
 
20
- This page explains how data fetching works in Caspian. Use route functions for initial page data and use RPC actions for browser-triggered reads, writes, streams, and uploads.
20
+ This page explains how data fetching works in Caspian. Use route functions for initial page data and use RPC actions for browser-triggered reads, writes, streams, uploads, and normal CRUD work.
21
21
 
22
- Treat RPC as the default way for browser code to talk to Python in Caspian. Do not reach for ad hoc fetch calls to custom JSON endpoints, alternate transport layers, or older helper names unless the task explicitly requires that shape.
22
+ Treat RPC as the default way for browser code to talk to Python in Caspian. For CRUD operations and any browser-initiated backend reads after first render, default to `@rpc()` on the server and `pp.rpc()` in PulsePoint code. Do not reach for ad hoc fetch calls to custom JSON endpoints, alternate transport layers, or older helper names unless the task explicitly requires that shape.
23
23
 
24
24
  MCP is a separate integration surface. Do not place app-owned FastMCP tools in route `index.py` files or treat `@rpc()` actions as a replacement for MCP tools. Use `mcp.md` and `src/lib/mcp/` only when `caspian.config.json` has `mcp: true`. If `mcp` is false, do not assume those files exist.
25
25
 
@@ -36,15 +36,18 @@ In practice, most pages use both:
36
36
  2. Render that data into `index.html`.
37
37
  3. Call `pp.rpc()` for refreshes, form submits, toggles, infinite scroll, or streamed updates.
38
38
 
39
+ If a route only needs UI and does not need first-render data, metadata, or other backend behavior, skip `index.py` and keep the page in `index.html` alone.
40
+
39
41
  ## Default Data Rule
40
42
 
41
43
  - Use `page()` for async or route-level data required before HTML renders, and use `layout()` only for synchronous shared props or metadata.
42
- - Use `@rpc()` on the server and `pp.rpc()` in PulsePoint code for all browser-triggered data work after first render.
44
+ - When a route renders UI and also needs backend work, keep the HTML in the sibling `index.html`; `index.py` should prepare data and call `render_page(__file__, ...)`, not inline the route markup.
45
+ - Use `@rpc()` on the server and `pp.rpc()` in PulsePoint code for all browser-triggered data work after first render, including CRUD operations and follow-up reads.
43
46
  - Keep custom REST or other endpoint patterns as explicit exceptions, not the baseline Caspian approach.
44
47
 
45
48
  ## Initial Data In `index.py`
46
49
 
47
- Use the route's backend file for data that should exist before the template is rendered.
50
+ Use the route's backend file for data that should exist before the template is rendered. Keep the rendered page markup in the sibling `index.html`; if no backend data or logic is needed, omit `index.py` entirely.
48
51
 
49
52
  Example:
50
53
 
@@ -120,6 +123,7 @@ Call it from the client with `pp.rpc()`:
120
123
 
121
124
  Use RPC for:
122
125
 
126
+ - CRUD reads and writes after the initial render
123
127
  - Button-triggered refreshes
124
128
  - Form submissions and mutations
125
129
  - Polling or background refreshes
@@ -292,7 +296,7 @@ If an AI agent is choosing how to load data in Caspian, apply these rules first.
292
296
  - Put first-render data loading in `src/app/**/index.py`.
293
297
  - Put shared section props in `layout.py` only when multiple child routes need the same synchronous data.
294
298
  - Keep async I/O in `page()` or `@rpc()` because the current layout engine does not await `layout()`.
295
- - Treat RPC as the default read and write layer between PulsePoint code and Python route logic.
299
+ - Treat RPC as the default read and write layer between PulsePoint code and Python route logic, especially for CRUD and interactive backend reads.
296
300
  - Use `@rpc()` for backend functions that should be callable from the browser.
297
301
  - Use `pp.rpc()` for client-side calls; do not prefer older `pp.fetchFunction()` wording.
298
302
  - Prefer route-render data plus RPC over inventing parallel REST endpoints for normal Caspian page interactions.
@@ -31,6 +31,8 @@ Metadata is typically defined in one of two files:
31
31
  - `layout.py` for defaults shared by everything below that folder
32
32
  - `index.py` for route-specific metadata
33
33
 
34
+ On routes that render UI, keep the page markup in the sibling `index.html`. `index.py` is the metadata and backend companion, not the place to store route HTML. If a route needs no metadata or backend behavior, omit `index.py` and keep the page as `index.html` only.
35
+
34
36
  The current `Metadata` implementation supports three fields:
35
37
 
36
38
  - `title`
@@ -26,7 +26,7 @@ Caspian uses a lean project layout that keeps application code in `src`, databas
26
26
 
27
27
  As an app grows, keep reusable rendered UI in `src/components/`, keep reusable non-UI support code in `src/lib/`, and keep route-owned files in `src/app/`. That split keeps page composition separate from shared component and service code.
28
28
 
29
- In that layout, the default stack is Python components for reusable UI, PulsePoint in templates for reactive browser behavior, RPC for browser-triggered server calls, and `casp.validate` for input validation at route and action boundaries.
29
+ In that layout, the default stack is Python components for reusable UI, PulsePoint in templates for reactive browser behavior, RPC for CRUD operations and browser-triggered backend reads, and `casp.validate` for input validation at route and action boundaries.
30
30
 
31
31
  For public pages that can safely reuse rendered HTML, Caspian also supports route-level page caching through `casp.cache_handler`.
32
32
 
@@ -105,6 +105,8 @@ This is the main application area. It contains route files, templates, styles, a
105
105
 
106
106
  This directory handles file-based routing. Route templates and route-specific backend logic live here.
107
107
 
108
+ For any route that renders UI, keep that markup in `src/app/**/index.html`. If the route is UI-only, `index.html` alone is enough. Add `src/app/**/index.py` only as a companion when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side behavior. Use a lone `index.py` only for non-visual routes such as redirect-only or action-only handlers.
109
+
108
110
  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.
109
111
 
110
112
  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.
@@ -227,7 +229,9 @@ If this layout imports reusable components, place each `<!-- @import ... -->` co
227
229
 
228
230
  ### `src/app/index.py`
229
231
 
230
- The backend logic for the route. This is where route behavior can load first-render data, expose `@rpc()` actions, and import framework features such as auth helpers, `casp.validate`, and route-level `Cache(...)` declarations.
232
+ The backend logic companion for the route. Use this file when the same route needs first-render data, metadata, `@rpc()` actions, auth helpers, `casp.validate`, route-level `Cache(...)` declarations, redirects, or other server behavior.
233
+
234
+ If the route renders UI, keep the markup in the sibling `index.html` and let `index.py` call `render_page(__file__, ...)` with whatever context the template needs. Do not store route HTML in `index.py`. A lone `index.py` should be reserved for non-visual routes.
231
235
 
232
236
  When a route owns a file manager or upload UI, keep the owning upload and delete `@rpc()` actions in that route's `index.py` and move reusable filesystem or Prisma helpers into `src/lib/`. Do not move ordinary upload behavior into `main.py`.
233
237
 
@@ -235,6 +239,8 @@ When a route owns a file manager or upload UI, keep the owning upload and delete
235
239
 
236
240
  The route template. It supports standard HTML, `<!-- @import ... -->` component imports, and PulsePoint directives, and it should be the default place for reactive frontend behavior in Caspian.
237
241
 
242
+ If a route renders UI and needs no backend behavior, this file alone is sufficient. If the route also has an `index.py` companion, keep the visible page markup here.
243
+
238
244
  Treat import comments as top-of-file directives that belong above the route's single authored root element.
239
245
 
240
246
  Use `components.md` when the task involves authoring reusable `<MyComponent />` tags backed by Python files.
@@ -281,7 +287,7 @@ If an AI agent is deciding where to make changes, use these rules first.
281
287
  - Use `settings/bs-config.ts` when uploaded public assets should not trigger BrowserSync reloads during the local stack.
282
288
  - Put app-owned FastMCP code in `src/lib/mcp/` only when `caspian.config.json` enables MCP.
283
289
  - Use PulsePoint conventions in route templates for reactive frontend behavior.
284
- - Use `@rpc()` in route/backend code and `pp.rpc()` in client code for browser-triggered data flows.
290
+ - Use `@rpc()` in route/backend code and `pp.rpc()` in client code for browser-triggered data flows, especially CRUD operations and interactive backend reads.
285
291
  - Use `casp.cache_handler` when a route's first-render HTML should be reused safely across requests.
286
292
  - Use `casp.validate` at route and RPC boundaries, and move reusable validators into `src/lib/` when multiple routes share them.
287
293
  - Use `database.md` when the task involves Prisma schema changes, migrations, seed logic, or project-specific database helper conventions.
@@ -44,6 +44,8 @@ If docs, generated examples, or older notes disagree with `public/js/pp-reactive
44
44
  When a Caspian page needs reactive browser behavior, use PulsePoint.
45
45
 
46
46
  - Use PulsePoint component roots, scripts, directives, and runtime helpers for interactive UI.
47
+ - Use PulsePoint state, effects, refs, and template directives as the default reactivity model in authored Caspian templates.
48
+ - When the browser needs CRUD operations or follow-up reads from the backend, call `pp.rpc()` from PulsePoint code and back it with route or backend `@rpc()` actions.
47
49
  - Keep server-rendered HTML plus PulsePoint enhancement as the baseline architecture.
48
50
  - Only introduce another frontend runtime when the user explicitly asks for it or the project already depends on one.
49
51
 
@@ -413,10 +415,12 @@ These are runtime details.
413
415
  Use these rules when generating or editing PulsePoint runtime code:
414
416
 
415
417
  - Treat PulsePoint as the default reactive frontend for Caspian app code.
418
+ - Treat `pp.rpc()` as the default browser-to-server path for CRUD operations and interactive backend reads.
416
419
  - Use `public/js/pp-reactive-v2.js` as the shipped runtime contract AI should follow.
417
420
  - Keep `main.py` in view because it injects the runtime-facing attributes and rewrites authored scripts before the browser sees them.
418
421
  - 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.
419
422
  - In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
423
+ - Prefer PulsePoint state and template directives over manual DOM mutation for reactive updates.
420
424
  - If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
421
425
  - In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
422
426
  - Keep template-facing variables at top level.
@@ -25,8 +25,10 @@ Caspian uses a high-performance file-system router built on top of FastAPI. Your
25
25
  Start with these rules:
26
26
 
27
27
  - Put application routes in `src/app/`.
28
- - Use `index.html` for a template-only route.
29
- - Use `index.py` when the route needs metadata or async server-side logic.
28
+ - For any route that renders a page, put the markup in `index.html`.
29
+ - If a route is UI-only, `index.html` by itself is enough.
30
+ - Add `index.py` only when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side logic.
31
+ - Use a standalone `index.py` only for non-visual routes such as redirects or action-only handlers.
30
32
  - Use `layout.html` to wrap child routes.
31
33
  - Use `layout.py` when a layout needs shared synchronous props or metadata before rendering.
32
34
 
@@ -46,7 +48,7 @@ If you already know the Next.js App Router, use this translation layer:
46
48
  | Next.js concept | Caspian equivalent |
47
49
  | --- | --- |
48
50
  | `app/` | `src/app/` |
49
- | `page.tsx` | `index.html` or `index.py` |
51
+ | `page.tsx` | `index.html` plus optional `index.py` companion |
50
52
  | `layout.tsx` | `layout.html` and optional `layout.py` |
51
53
  | `[id]` | `[id]` |
52
54
  | `[...slug]` | `[...slug]` |
@@ -61,20 +63,32 @@ This means most App Router habits carry over directly:
61
63
 
62
64
  ## Core Concepts
63
65
 
64
- Every route lives inside `src/app`. A route is a folder that contains either an `index.html` file, an `index.py` file, or both as part of the route implementation.
66
+ Every route lives inside `src/app`. For routes that render UI, `index.html` owns the markup and `index.py` is only an optional companion for server logic or metadata.
67
+
68
+ Use this decision rule when creating routes:
69
+
70
+ | Route shape | Files |
71
+ | --- | --- |
72
+ | UI only | `index.html` |
73
+ | UI plus backend logic or metadata | `index.html` and `index.py` |
74
+ | No rendered page, backend only | `index.py` |
65
75
 
66
76
  Examples:
67
77
 
68
- | File | URL |
78
+ | Files | URL |
69
79
  | --- | --- |
70
80
  | `src/app/index.html` | `/` |
71
- | `src/app/about/index.py` | `/about` |
81
+ | `src/app/about/index.html` | `/about` |
82
+ | `src/app/dashboard/index.html` and `src/app/dashboard/index.py` | `/dashboard` |
72
83
  | `src/app/blog/posts/index.html` | `/blog/posts` |
84
+ | `src/app/(auth)/signout/index.py` | `/signout` |
73
85
 
74
86
  ### `index.html`
75
87
 
76
88
  Use `index.html` for the route template. This is the route's view layer.
77
89
 
90
+ If a route renders visible page content, that content belongs here even when the route also has an `index.py` companion.
91
+
78
92
  Route templates can import reusable Python components with `<!-- @import ... -->` comments and render them with JSX-style tags such as `<Button />`. Use [components.md](./components.md) for the component authoring rules.
79
93
 
80
94
  Place those import comments at the top of the file, above the authored root element. They are file-level directives, not children of the route root.
@@ -152,7 +166,9 @@ Write the first form. Caspian produces the second form by injecting `pp-componen
152
166
 
153
167
  ### `index.py`
154
168
 
155
- Use `index.py` when the route needs metadata or async server-side logic. Because Caspian runs on FastAPI, the page entry should be async.
169
+ Use `index.py` as the backend companion when a route needs metadata or async server-side logic. For routes that render UI, keep the markup in the sibling `index.html` and let `page()` call `render_page(__file__, ...)`. Do not inline route HTML inside `index.py`.
170
+
171
+ Use `index.py` by itself only for non-visual routes such as redirects or action-only handlers. Because Caspian runs on FastAPI, the page entry should be async when it performs async work.
156
172
 
157
173
  Example:
158
174
 
@@ -168,7 +184,7 @@ async def page():
168
184
  return render_page(__file__)
169
185
  ```
170
186
 
171
- Use this pattern when the route needs to fetch data, compute metadata, or do other non-blocking server work before rendering.
187
+ Use this pattern when the route needs to fetch data, compute metadata, or do other non-blocking server work before rendering the sibling `index.html`.
172
188
 
173
189
  When a route owns a file manager or upload UI, keep the owning upload and delete `@rpc()` actions in that same `index.py` and move reusable filesystem or Prisma helpers into `src/lib/`. Do not move ordinary upload behavior into `main.py`. See [file-uploads.md](./file-uploads.md).
174
190
 
@@ -186,10 +202,12 @@ Wrap a folder name in brackets to make it variable.
186
202
 
187
203
  | File | Example URL |
188
204
  | --- | --- |
189
- | `src/app/users/[id]/index.py` | `/users/123` |
205
+ | `src/app/users/[id]/index.html` | `/users/123` |
190
206
 
191
207
  These segments are compiled into FastAPI path parameters for efficient route matching.
192
208
 
209
+ Add a sibling `index.py` when the dynamic route needs params during render, metadata, or other backend logic.
210
+
193
211
  In the current `main.py` router, path params are collected into a single dict and passed as the first positional argument to `page()`. Matching query params can still be injected by name, and `request` is passed by keyword when declared.
194
212
 
195
213
  Example:
@@ -208,20 +226,22 @@ Use an ellipsis inside brackets to match multiple path parts.
208
226
 
209
227
  | File | Example URL |
210
228
  | --- | --- |
211
- | `src/app/docs/[...slug]/index.py` | `/docs/getting-started/setup` |
229
+ | `src/app/docs/[...slug]/index.html` | `/docs/getting-started/setup` |
212
230
 
213
231
  Use catch-all routes when the number of path segments is not fixed ahead of time.
214
232
 
233
+ Add a sibling `index.py` when that catch-all route also needs backend logic or metadata.
234
+
215
235
  ## Route Groups
216
236
 
217
237
  Wrap a folder name in parentheses to organize code without adding that segment to the URL.
218
238
 
219
239
  Examples:
220
240
 
221
- | File | URL |
241
+ | Files | URL |
222
242
  | --- | --- |
223
- | `src/app/(auth)/login/index.py` | `/login` |
224
- | `src/app/(auth)/register/index.py` | `/register` |
243
+ | `src/app/(auth)/signin/index.html` and `src/app/(auth)/signin/index.py` | `/signin` |
244
+ | `src/app/(auth)/signup/index.html` and `src/app/(auth)/signup/index.py` | `/signup` |
225
245
 
226
246
  Route groups are useful when you want to:
227
247
 
@@ -292,18 +312,25 @@ src/
292
312
  index.html
293
313
  users/
294
314
  [id]/
315
+ index.html
295
316
  index.py
296
317
  docs/
297
318
  [...slug]/
319
+ index.html
298
320
  index.py
299
321
  (auth)/
300
322
  login/
323
+ index.html
301
324
  index.py
302
325
  register/
326
+ index.html
327
+ index.py
328
+ signout/
303
329
  index.py
304
330
  dashboard/
305
331
  layout.html
306
332
  settings/
333
+ index.html
307
334
  index.py
308
335
  ```
309
336
 
@@ -313,7 +340,8 @@ If an AI agent is choosing where to add or update route code, apply these rules
313
340
 
314
341
  - Treat `src/app/` as the routing source of truth.
315
342
  - Use folder names to model URL segments.
316
- - Use `index.html` for route templates and `index.py` for route-level async logic.
343
+ - If a route renders UI, create or update `index.html` for the markup.
344
+ - Add `index.py` only when the same route needs metadata or server behavior; do not place route HTML in `index.py`.
317
345
  - Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
318
346
  - Use `layout.html` for shared wrappers and `layout.py` for layout-level synchronous props or metadata.
319
347
  - Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored root element.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {