caspian-utils 0.0.30 → 0.0.32

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/auth.md CHANGED
@@ -165,8 +165,9 @@ return AuthSettings(
165
165
 
166
166
  The installed auth code reads several values from `.env` when explicit values are not passed.
167
167
 
168
- - `AUTH_SECRET` backs `AuthSettings.secret_key`.
169
- - `AUTH_COOKIE_NAME` backs `AuthSettings.cookie_name`.
168
+ - `AUTH_SECRET` backs `AuthSettings.secret_key`.
169
+ - App-owned startup helpers may also validate `AUTH_SECRET` and refuse production startup when the value is missing or still on a default placeholder.
170
+ - `AUTH_COOKIE_NAME` backs `AuthSettings.cookie_name`.
170
171
  - `SESSION_LIFETIME_HOURS` controls `SessionMiddleware.max_age` in the current `main.py` bootstrap.
171
172
  - `APP_ENV=production` enables secure session cookies and the `Secure` flag on the current CSRF cookie.
172
173
  - `CASPIAN_BROWSER_SYNC_PORT` can override the development cookie scope suffix used by the current `main.py` bootstrap.
@@ -224,28 +225,29 @@ This does two things:
224
225
  - applies the centralized settings once at startup
225
226
  - registers OAuth providers once so `AuthMiddleware` can delegate signin and callback paths through `auth.auth_providers(...)`
226
227
 
227
- The current `main.py` also wires the session middleware directly:
228
-
229
- ```python
230
- from starlette.middleware.sessions import SessionMiddleware
231
-
232
-
233
- SESSION_LIFETIME_HOURS = int(os.getenv("SESSION_LIFETIME_HOURS", 7))
234
- IS_PRODUCTION = os.getenv("APP_ENV") == "production"
235
-
236
-
237
- app.add_middleware(
238
- SessionMiddleware,
239
- secret_key=os.getenv("AUTH_SECRET", "change-me"),
240
- session_cookie=os.getenv("AUTH_COOKIE_NAME", "session"),
241
- max_age=SESSION_LIFETIME_HOURS * 3600,
242
- same_site="lax",
228
+ The current `main.py` also wires the session middleware directly, and some apps delegate the secret lookup to an app-owned helper:
229
+
230
+ ```python
231
+ from starlette.middleware.sessions import SessionMiddleware
232
+ from casp.runtime_security import get_session_secret
233
+
234
+
235
+ SESSION_LIFETIME_HOURS = int(os.getenv("SESSION_LIFETIME_HOURS", 7))
236
+ IS_PRODUCTION = os.getenv("APP_ENV") == "production"
237
+
238
+
239
+ app.add_middleware(
240
+ SessionMiddleware,
241
+ secret_key=get_session_secret(is_production=IS_PRODUCTION),
242
+ session_cookie=os.getenv("AUTH_COOKIE_NAME", "session"),
243
+ max_age=SESSION_LIFETIME_HOURS * 3600,
244
+ same_site="lax",
243
245
  https_only=IS_PRODUCTION,
244
246
  path="/",
245
247
  )
246
248
  ```
247
249
 
248
- Keep this wiring in `main.py`. Keep only the policy values in `src/lib/auth/auth_config.py`.
250
+ Keep this wiring in `main.py`. Keep only the policy values in `src/lib/auth/auth_config.py`. Runtime security helpers for session-secret enforcement, safe public-file serving, production-safe error messages, and baseline response headers are package-owned by `casp.runtime_security`; keep the middleware attachment itself in `main.py`.
249
251
 
250
252
  ## Session Lifetime Rules
251
253
 
@@ -258,37 +260,40 @@ The effective authenticated session lasts only while both are valid.
258
260
 
259
261
  - If the session cookie expires first, the session disappears even if the auth payload expiration was longer.
260
262
  - If the auth payload expires first, `auth.is_authenticated()` clears it even if the session cookie still exists.
261
- - In the pasted `main.py`, `token_auto_refresh=True` still does nothing by itself because the request flow does not call `auth.refresh_session()`.
263
+ - In apps where the request flow does not call `auth.refresh_session()`, `token_auto_refresh=True` still does nothing by itself.
262
264
 
263
265
  Keep `SESSION_LIFETIME_HOURS` and `default_token_validity` aligned unless you intentionally want one boundary to end earlier than the other.
264
266
 
265
267
  ## Middleware Order
266
268
 
267
- The current `main.py` adds middleware in this source order:
268
-
269
- ```python
270
- app.add_middleware(RPCMiddleware)
271
- app.add_middleware(AuthMiddleware)
272
- app.add_middleware(CSRFMiddleware)
273
- app.add_middleware(SessionMiddleware, ...)
274
- ```
275
-
276
- Because Starlette runs the last-added middleware first, the effective request order is:
277
-
278
- 1. `SessionMiddleware`
279
- 2. `CSRFMiddleware`
280
- 3. `AuthMiddleware`
281
- 4. `RPCMiddleware`
282
- 5. route handler or RPC endpoint
283
-
284
- Current behavior by layer:
285
-
286
- - `SessionMiddleware` provides `request.session` for the rest of the stack.
287
- - `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a scoped CSRF cookie based on `pp_csrf`, for example `pp_csrf_5091` in development or plain `pp_csrf` when no dev scope is active.
288
- - `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, skips configured static asset paths, and enforces public, auth, private, and role-based route redirects.
289
- - `RPCMiddleware` handles `POST` requests with `X-PP-RPC: true` and forwards them to Caspian's RPC handler after auth and session setup are already available.
290
-
291
- Keep `SessionMiddleware` outermost. If CSRF, auth, or RPC handling runs before it, `request.session` will not be available.
269
+ The current `main.py` adds middleware in this source order:
270
+
271
+ ```python
272
+ app.add_middleware(RPCMiddleware)
273
+ app.add_middleware(AuthMiddleware)
274
+ app.add_middleware(CSRFMiddleware)
275
+ app.add_middleware(SessionMiddleware, ...)
276
+ app.add_middleware(SecurityHeadersMiddleware)
277
+ ```
278
+
279
+ Because Starlette runs the last-added middleware first, the effective request order is:
280
+
281
+ 1. `SecurityHeadersMiddleware`
282
+ 2. `SessionMiddleware`
283
+ 3. `CSRFMiddleware`
284
+ 4. `AuthMiddleware`
285
+ 5. `RPCMiddleware`
286
+ 6. route handler or RPC endpoint
287
+
288
+ Current behavior by layer:
289
+
290
+ - `SecurityHeadersMiddleware` can attach baseline response headers from `casp.runtime_security`, such as `X-Content-Type-Options`, framing policy, referrer policy, permissions policy, and production HSTS, while preserving any headers already set by the response. Do not assume this layer emits CSP or owns third-party browser resource allowlists; verify the installed package helper first.
291
+ - `SessionMiddleware` provides `request.session` for the rest of the stack.
292
+ - `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a scoped CSRF cookie based on `pp_csrf`, for example `pp_csrf_5091` in development or plain `pp_csrf` when no dev scope is active.
293
+ - `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, skips configured static asset paths, and enforces public, auth, private, and role-based route redirects.
294
+ - `RPCMiddleware` handles `POST` requests with `X-PP-RPC: true` and forwards them to Caspian's RPC handler after auth and session setup are already available.
295
+
296
+ Keep `SessionMiddleware` immediately inside any outer response-header wrapper such as `SecurityHeadersMiddleware`. If CSRF, auth, or RPC handling runs before the session layer, `request.session` will not be available.
292
297
 
293
298
  ## Current `AuthMiddleware` Flow
294
299
 
@@ -760,7 +765,7 @@ Prefer `AuthSettings`, `configure_auth(...)`, and `auth.settings` in new code.
760
765
  - Expired or malformed payloads are removed during `auth.is_authenticated()` checks.
761
766
  - `auth.get_payload()` returns `None` for missing payloads, a dict for dict payloads, and `{"value": ...}` for non-dict payloads.
762
767
  - OAuth callback helpers return `None` on failure, so calling routes should be prepared for a normal page render or explicit error state when provider login fails.
763
- - The current app bootstrap adds `SessionMiddleware`, `CSRFMiddleware`, `AuthMiddleware`, and `RPCMiddleware`; auth-aware code depends on that ordering so sessions and CSRF state exist before route checks or RPC handling.
768
+ - The current app bootstrap may wrap auth and session handling in an outer `SecurityHeadersMiddleware`; auth-aware code still depends on `SessionMiddleware`, `CSRFMiddleware`, `AuthMiddleware`, and `RPCMiddleware` staying in the correct relative order so sessions and CSRF state exist before route checks or RPC handling.
764
769
  - Password reset, email verification, and other account-recovery workflows are application responsibilities layered on top of `casp.auth`, not built-in auth runtime features.
765
770
  - Route lists and RBAC maps are exact-path checks, not wildcard or prefix rules.
766
771
 
@@ -803,7 +808,7 @@ If an AI agent is deciding how to handle auth in Caspian, apply these rules firs
803
808
  - Do not treat `token_auto_refresh` as a route-privacy switch. In the current app it only affects sliding-session refresh if `auth.refresh_session()` is called.
804
809
  - Use `@require_auth`, `@require_role`, and `@guest_only` for page-level access rules.
805
810
  - Use `@rpc(require_auth=True, allowed_roles=[...])` for protected browser-triggered actions.
806
- - Keep `SessionMiddleware` outermost so auth, CSRF, and RPC handlers can read `request.session`.
811
+ - Keep `SessionMiddleware` immediately inside any outer response-header wrapper so auth, CSRF, and RPC handlers can read `request.session`.
807
812
  - Use [state.md](./state.md) only for transient auth-adjacent request state, not as the auth session store.
808
813
  - Align `SESSION_LIFETIME_HOURS` with `default_token_validity` unless you intentionally want different cookie and auth-payload expiry windows.
809
814
  - Prefer exact route strings in `public_routes`, `auth_routes`, `private_routes`, and `role_based_routes`.
@@ -811,6 +816,6 @@ If an AI agent is deciding how to handle auth in Caspian, apply these rules firs
811
816
  - Keep auth secrets and OAuth provider credentials in `.env`.
812
817
  - Set `AUTH_COOKIE_NAME` explicitly so the session middleware cookie name and auth settings stay aligned.
813
818
  - Use `.venv/Lib/site-packages/casp/auth.py` only when the task is about framework auth internals or debugging installed behavior.
814
- - Use `main.py` when the task is about auth bootstrap, session middleware, or middleware execution order.
819
+ - Use `main.py` plus `casp.runtime_security` when the task is about auth bootstrap, session middleware, safe public-file serving, response headers, or middleware execution order.
815
820
  - Pair auth work with `fetch-data.md` for RPC forms and `validation.md` for credential validation.
816
821
  - Check `routing.md` and `project-structure.md` before creating new auth routes or shared auth helpers.
@@ -29,16 +29,19 @@ Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib
29
29
 
30
30
  | Concern | Core file | Read first | Why it matters |
31
31
  | --- | --- | --- | --- |
32
- | App bootstrap and request flow | [main.py](../../../../main.py) | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, static asset routes, middleware order, route registration, cache check and save, exception handlers, and final HTML transforms all live here. |
32
+ | App bootstrap and request flow | [main.py](../../../../main.py) plus imported runtime helpers such as `casp.runtime_security` | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, middleware wiring, route registration, cache check and save, exception handlers, and final HTML transforms live here. Package-owned helpers imported by `main.py` may own safe public-file serving, baseline non-CSP response headers, production-safe error messages, or production session-secret enforcement. |
33
33
  | Browser runtime, SPA navigation, and scroll restoration | [public/js/pp-reactive-v2.js](../../../../public/js/pp-reactive-v2.js) | [pulsepoint.md](./pulsepoint.md), [fetch-data.md](./fetch-data.md) | The shipped `pp` runtime, same-origin SPA interception, per-history-entry scroll state, `pp-reset-scroll` behavior, and browser RPC helpers live here. |
34
34
 
35
35
  Important current `main.py` behaviors AI should keep in mind:
36
36
 
37
- - 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`.
38
- - `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.
39
- - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
40
- - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
41
- - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, so the effective request order is reversed at runtime.
37
+ - 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`.
38
+ - `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.
39
+ - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
40
+ - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
41
+ - Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
42
+ - 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.
43
+ - Baseline non-CSP response headers may be built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. Do not assume that helper is a third-party browser resource or domain registry; check the current helper before adding CSP behavior.
44
+ - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `SecurityHeadersMiddleware`, so the effective request order is reversed at runtime.
42
45
  - `public/js/pp-reactive-v2.js` saves scroll positions per history entry, resets window scroll on push navigation, and uses `pp-reset-scroll="true"` to opt specific containers or the whole body into reset behavior.
43
46
 
44
47
  ## Caspian Core Feature Map
@@ -53,6 +56,7 @@ Use this table when the task names a framework feature but the owning file is no
53
56
  | Metadata | route or layout `metadata`, runtime metadata helpers | `casp.layout`, `main.py` | [metadata.md](./metadata.md), [routing.md](./routing.md) |
54
57
  | Component imports and `x-*` tags | `src/components/**`, `src/app/**/*.html` | `casp.components_compiler`, `casp.component_decorator` | [components.md](./components.md), [routing.md](./routing.md) |
55
58
  | Auth and route protection | `src/lib/auth/auth_config.py`, `main.py`, route decorators | `casp.auth`, `main.py` middleware | [auth.md](./auth.md) |
59
+ | Baseline response headers and safe public-file serving | `main.py` imports from `casp.runtime_security` | `.venv/Lib/site-packages/casp/runtime_security.py` | [auth.md](./auth.md), [project-structure.md](./project-structure.md) |
56
60
  | RPC and server actions | route or component Python modules with `@rpc()` | `casp.rpc`, `main.py` middleware | [fetch-data.md](./fetch-data.md), [file-uploads.md](./file-uploads.md) |
57
61
  | Streaming | route `page()` generators, RPC generators | `casp.streaming`, `casp.rpc`, `main.py` | [fetch-data.md](./fetch-data.md) |
58
62
  | Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
@@ -85,8 +89,9 @@ Interactive CRUD page:
85
89
  | Runtime file | Primary responsibility | Read these docs |
86
90
  | --- | --- | --- |
87
91
  | [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, and the synchronous `layout()` contract | [routing.md](./routing.md), [metadata.md](./metadata.md) |
88
- | [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
89
- | [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
92
+ | [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
93
+ | [.venv/Lib/site-packages/casp/runtime_security.py](../../../../.venv/Lib/site-packages/casp/runtime_security.py) | safe public-file serving, baseline non-CSP response headers, production-safe error messages, and production session-secret enforcement used by `main.py` | [project-structure.md](./project-structure.md), [auth.md](./auth.md) |
94
+ | [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
90
95
  | [.venv/Lib/site-packages/casp/streaming.py](../../../../.venv/Lib/site-packages/casp/streaming.py) | `SSE`, `ServerSentEvent`, and generator-to-event-stream wrapping | [fetch-data.md](./fetch-data.md) |
91
96
  | [.venv/Lib/site-packages/casp/state_manager.py](../../../../.venv/Lib/site-packages/casp/state_manager.py) | request-scoped state, session bucket persistence, listener lifecycle, and wire-request reset behavior | [state.md](./state.md) |
92
97
  | [.venv/Lib/site-packages/casp/cache_handler.py](../../../../.venv/Lib/site-packages/casp/cache_handler.py) | `Cache`, cache manifest handling, filename generation, disk-backed HTML storage, and invalidation | [cache.md](./cache.md) |
@@ -106,7 +111,7 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
106
111
 
107
112
  | Runtime area | Verify these behaviors |
108
113
  | --- | --- |
109
- | `main.py` routing and request flow | route registration, path and query injection, static asset handling, and exception rendering |
114
+ | `main.py` routing and request flow | route registration, path and query injection, static asset handling, session-middleware wiring, response-header middleware, and exception rendering |
110
115
  | `public/js/pp-reactive-v2.js` browser runtime | SPA interception, history-vs-push scroll behavior, `pp-reset-scroll` semantics, and browser-side `pp.rpc()` behavior |
111
116
  | `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
112
117
  | `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
@@ -80,10 +80,10 @@ my-app/
80
80
  mcp/
81
81
  fastmcp.json
82
82
  mcp_server.py
83
- prisma/
84
- __init__.py
85
- db.py
86
- models.py
83
+ prisma/
84
+ __init__.py
85
+ db.py
86
+ models.py
87
87
  .venv/
88
88
  Lib/
89
89
  site-packages/
@@ -130,7 +130,7 @@ For component HTML files, follow the component authoring rules in [components.md
130
130
 
131
131
  The directories listed in `componentScanDirs` determine where component tooling scans. When that list includes `src/`, `src/components/` is a conventionally clean location, not a hard-coded runtime requirement.
132
132
 
133
- ### `src/lib/`
133
+ ### `src/lib/`
134
134
 
135
135
  Use this folder for shared helpers, reusable validators, RPC-facing service wrappers, data-access helpers, formatting utilities, and other app-level support code that is not itself a reusable rendered component.
136
136
 
@@ -140,7 +140,9 @@ For file upload and manager flows, keep route-owned `@rpc()` actions in `src/app
140
140
 
141
141
  When a project includes an app-owned Python database layer under `src/lib/prisma/`, reuse that package for Python-side data access and keep any additional shared database helpers in `src/lib/`.
142
142
 
143
- When MCP is enabled for the project, this folder also contains the app-owned FastMCP server under `src/lib/mcp/`.
143
+ When MCP is enabled for the project, this folder also contains the app-owned FastMCP server under `src/lib/mcp/`.
144
+
145
+ Do not add `src/lib/security/runtime_security.py` for normal app work. Runtime security helpers for safe public-file serving, production session-secret enforcement, production-safe error messages, and baseline non-CSP response headers are package-owned by `casp.runtime_security`.
144
146
 
145
147
  ### Shared Database Helpers
146
148
 
@@ -185,16 +187,31 @@ If the local BrowserSync stack is running, keep that upload directory in `settin
185
187
 
186
188
  The application entry point for the project.
187
189
 
188
- In the current Caspian app shape, this file is where startup wiring happens:
189
-
190
- - load environment variables
191
- - call `configure_auth(build_auth_settings())`
192
- - register OAuth providers with `Auth.set_providers(...)`
193
- - create the FastAPI app
194
- - register routes and RPC handlers
195
- - add `SessionMiddleware`, CSRF middleware, auth middleware, and RPC middleware
196
-
197
- Use `main.py` for auth bootstrap and middleware-order changes. Use `src/lib/auth/auth_config.py` for auth policy values such as public routes, redirects, and RBAC maps.
190
+ In the current Caspian app shape, this file is where startup wiring happens:
191
+
192
+ - load environment variables
193
+ - call `configure_auth(build_auth_settings())`
194
+ - register OAuth providers with `Auth.set_providers(...)`
195
+ - create the FastAPI app
196
+ - register routes and RPC handlers
197
+ - add response headers middleware, `SessionMiddleware`, CSRF middleware, auth middleware, and RPC middleware
198
+
199
+ Use `main.py` for auth bootstrap and middleware-order changes. Use `src/lib/auth/auth_config.py` for auth policy values such as public routes, redirects, and RBAC maps.
200
+
201
+ ### `.venv/Lib/site-packages/casp/runtime_security.py`
202
+
203
+ The installed Caspian package owns runtime security helpers that `main.py` can import as `casp.runtime_security`. Keep this file in the package instead of copying it into `src/lib/`, because normal app users should not need to edit it.
204
+
205
+ This helper is not a registry for third-party browser resources. Do not add Google, YouTube, CDN, image-host, API, or iframe origins here just because the browser or server uses them. If an app later chooses to enforce a Content Security Policy, document and implement that as an explicit project policy rather than assuming `runtime_security.py` owns a domain allowlist.
206
+
207
+ Use this package module when the task is about:
208
+
209
+ - safe serving of files from `public/**`
210
+ - production session-secret enforcement
211
+ - user-facing vs production-safe exception messaging helpers
212
+ - baseline response headers such as permissions, referrer, MIME sniffing, framing, or HSTS behavior
213
+
214
+ Because this file is framework-owned, edit it only for Caspian runtime work or when documentation must match the installed package. App-specific auth policy still belongs in `src/lib/auth/auth_config.py`, and app-specific upload or storage behavior should live in route-owned code or other `src/lib/**` helpers.
198
215
 
199
216
  ### `caspian.config.json`
200
217
 
@@ -7,9 +7,9 @@ related:
7
7
  links:
8
8
  - /docs/components
9
9
  - /docs/routing
10
- - /docs/fetch-data
11
- - /docs/pulsepoint-runtime-map
12
- - /docs/core-runtime-map
10
+ - /docs/fetch-data
11
+ - /docs/pulsepoint-runtime-map
12
+ - /docs/core-runtime-map
13
13
  - /docs/project-structure
14
14
  - /docs/index
15
15
  ---
@@ -44,9 +44,9 @@ Important current facts:
44
44
 
45
45
  If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
46
46
 
47
- Use [core-runtime-map.md](./core-runtime-map.md) when the controlling runtime file is not obvious yet.
48
-
49
- Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when the task names a specific PulsePoint feature or directive and you need a quick feature-to-runtime lookup before reading the full guide.
47
+ Use [core-runtime-map.md](./core-runtime-map.md) when the controlling runtime file is not obvious yet.
48
+
49
+ Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when the task names a specific PulsePoint feature or directive and you need a quick feature-to-runtime lookup before reading the full guide.
50
50
 
51
51
  ## Default Frontend Rule
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {