caspian-utils 0.1.25 → 0.2.0

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
@@ -119,7 +119,7 @@ Important behavior from the current implementation:
119
119
  - `token_auto_refresh` only changes behavior when the request lifecycle calls `auth.refresh_session()`. In the installed `auth.py`, the flag alone does not refresh expiry by itself.
120
120
  - The framework `AuthSettings` dataclass defaults `is_all_routes_private=True`, but the project example above explicitly changes that to `False`.
121
121
  - In generated app-owned starter config, `src/lib/auth/auth_config.py` starts with `is_all_routes_private=False`, so routes are public by default until the app chooses stricter route protection.
122
- - `public_routes`, `auth_routes`, `private_routes`, and `role_based_routes` are exact path matches in the installed `Auth` methods.
122
+ - `public_routes`, `auth_routes`, `private_routes`, and `role_based_routes` are matched as route **scopes**, not exact paths, in the installed `Auth` methods. An entry covers the path itself *and* everything nested under it, so `/admin` also matches `/admin/users`. Segment boundaries are respected (`/admin` does not match `/administration`), and dynamic segments work (`/orders/[id]` matches `/orders/42` and below). Root `/` is the one exception: it matches only `/`, so listing it in `public_routes` does not make the whole site public. Verify the exact rule in `_route_scope_matches_request` before relying on an entry to protect or expose a subtree.
123
123
  - `private_routes` matters only when `is_all_routes_private=False`.
124
124
  - `role_based_routes` currently expects `PATH -> [ROLES]`, not role names keyed to paths the other way around.
125
125
  - `role_identifier` defaults to `role`, and the current `auth.sign_in(...)` flow also normalizes `userRole` into `role` when possible.
@@ -189,7 +189,7 @@ The installed auth code reads several values from `.env` when explicit values ar
189
189
  - App-owned startup helpers may also validate `AUTH_SECRET` and refuse production startup when the value is missing or still on a default placeholder.
190
190
  - `AUTH_COOKIE_NAME` backs `AuthSettings.cookie_name`.
191
191
  - `SESSION_LIFETIME_HOURS` controls `SessionMiddleware.max_age` in the current `main.py` bootstrap.
192
- - `APP_ENV=production` enables secure session cookies and the `Secure` flag on the current CSRF cookie.
192
+ - `APP_ENV` selects the environment, resolved **fail-closed** by `casp.runtime_security.is_production_environment()`: only an explicit development value (`dev`, `development`, `local`, `staging`, `test`, `testing`) turns the development relaxations on, so an unset or misspelled value is treated as production. Production enables secure session cookies and the `Secure` flag on the CSRF cookie.
193
193
  - `CASPIAN_BROWSER_SYNC_PORT` can override the development cookie scope suffix used by the current `main.py` bootstrap.
194
194
  - `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_REDIRECT_URI` back `GoogleProvider`.
195
195
  - `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` back `GithubProvider`.
@@ -249,11 +249,11 @@ The current `main.py` also wires the session middleware directly, and some apps
249
249
 
250
250
  ```python
251
251
  from starlette.middleware.sessions import SessionMiddleware
252
- from casp.runtime_security import get_session_secret
252
+ from casp.runtime_security import get_session_secret, is_production_environment
253
253
 
254
254
 
255
255
  SESSION_LIFETIME_HOURS = int(os.getenv("SESSION_LIFETIME_HOURS", 7))
256
- IS_PRODUCTION = os.getenv("APP_ENV") == "production"
256
+ IS_PRODUCTION = is_production_environment()
257
257
 
258
258
 
259
259
  app.add_middleware(
@@ -344,7 +344,7 @@ The current installed methods are:
344
344
  | `auth.is_authenticated()` | Check current auth state | Returns `False` when the payload is missing, malformed, or expired, and clears invalid session data. |
345
345
  | `auth.get_payload()` | Read the signed-in payload | Returns the stored dict payload, or wraps non-dict payloads as `{"value": data}`. |
346
346
  | `auth.refresh_session()` | Extend expiration | Only updates expiry when `token_auto_refresh=True`. |
347
- | `auth.check_role(user, allowed_roles)` | Check RBAC access | Reads the configured role field from the payload and compares it to the allowed roles. |
347
+ | `auth.check_role(user, allowed_roles)` | Check RBAC access | Reads the configured role field (a single role or a list of them) and grants access on any overlap. |
348
348
 
349
349
  Sign-in example:
350
350
 
@@ -715,7 +715,8 @@ In the installed implementation:
715
715
 
716
716
  - `auth.check_role(...)` reads `user[role_identifier]` when the payload is a dict.
717
717
  - If you pass a plain string instead of a dict, that string is treated as the role directly.
718
- - `role_based_routes` is an exact-path lookup with `dict.get(path)`.
718
+ - That field may hold a single role or a list/tuple/set of them, so a user who is both `editor` and `auditor` passes a check for either. Access is granted when the user's roles and the allowed roles intersect.
719
+ - `role_based_routes` keys are matched as route *scopes*, not exact paths: `get_required_roles(path)` ranks every matching key by specificity and returns the most specific one. A key therefore also covers nested paths (`/admin` gates `/admin/users`), and dynamic segments such as `/orders/[id]` match. A static key wins over a dynamic one, which wins over a catch-all.
719
720
  - The installed payload normalization helps when your Prisma include returns a `userRole` object and you want a plain `role` field in the session payload.
720
721
 
721
722
  ## OAuth Providers
@@ -10,6 +10,7 @@ related:
10
10
  - /docs/mcp
11
11
  - /docs/file-uploads
12
12
  - /docs/routing
13
+ - /docs/static-export
13
14
  - /docs/project-structure
14
15
  - /docs/index
15
16
  ---
@@ -98,29 +99,51 @@ Only use this when the current project's `package.json` actually defines `npm ru
98
99
 
99
100
  Do not use `npm run build` as the default validation step for routine route, feature, or documentation edits.
100
101
 
102
+ ### Export the app to static HTML and preview it
103
+
104
+ ```bash
105
+ # Export to static/ (SSG, like Next.js `output: export`)
106
+ npm run static
107
+
108
+ # Serve the exported static/ folder over HTTP on an auto-selected free port
109
+ npm run static:serve
110
+ ```
111
+
112
+ Use `npm run static` when the user wants a folder of static HTML for a static host, and `npm run static:serve` to preview that folder locally.
113
+
114
+ This is an **app-owned build convention**, not a shipped Caspian feature and not gated by a `caspian.config.json` flag. Only use these when the current project's `package.json` actually defines them and `settings/build-static.py` (exporter) and `settings/serve-static.py` (preview server) exist.
115
+
116
+ Two things AI must keep correct:
117
+
118
+ - The `static` script must run `npm run build` (Tailwind **and** `projectName`) before `settings/build-static.py`, because the exporter walks the route index in `settings/files-list.json` that `projectName` regenerates. A build that only runs `tailwind:build` can export from a stale route index.
119
+ - `settings/serve-static.py` auto-selects a free port starting at a preferred default (8000) and binds loopback `127.0.0.1` by default; read the port it prints rather than assuming 8000 or reading `settings/bs-config.json` (that file is the dev BrowserSync source of truth, not the static preview).
120
+
121
+ See [static-export.md](./static-export.md) for the full export scope policy, `static_paths` dynamic-route pre-rendering, and the preview-server security and port behavior.
122
+
101
123
  ### Regenerate ORM after schema changes
102
124
 
103
125
  ```bash
104
126
  npx prisma migrate dev
105
127
 
106
- # If the change requires refreshed seed data:
107
- npx prisma generate
108
- # Destructive-data warning: ask the user for explicit approval before running this.
109
- npx prisma db seed
110
-
111
- npx ppy generate
112
- ```
113
-
114
- Use when `prisma/schema.prisma` changes and you need migrations, seed flow, and the generated Python ORM layer to stay aligned.
115
-
116
- `npx prisma db seed` is a delicate operation because the configured seed script may clean tables or replace existing rows before inserting seed data. Before running it, an AI agent must say the exact command it plans to run, warn that it can delete or overwrite database data, confirm the active datasource when practical, and wait for explicit user approval.
128
+ # If the change requires refreshed seed data:
129
+ npx prisma generate
130
+ # Destructive-data warning: ask the user for explicit approval before running this.
131
+ npx prisma db seed
132
+
133
+ npx ppy generate
134
+ ```
135
+
136
+ Use when `prisma/schema.prisma` changes and you need migrations, seed flow, and the generated Python ORM layer to stay aligned.
137
+
138
+ `npx prisma db seed` is a delicate operation because the configured seed script may clean tables or replace existing rows before inserting seed data. Before running it, an AI agent must say the exact command it plans to run, warn that it can delete or overwrite database data, confirm the active datasource when practical, and wait for explicit user approval.
117
139
 
118
140
  ## 2. Script Guardrails
119
141
 
120
142
  - Before using an optional feature, confirm its flag in `caspian.config.json`.
121
143
  - If the flag is false and the user wants that feature, ask first, then update `caspian.config.json` and run `npx casp update project` before assuming feature-managed files or scripts exist.
122
144
  - Do not run `package.json` scripts by default just because source files changed.
123
- - Treat `npm run dev` and `npm run build` as opt-in workflows.
145
+ - Treat `npm run dev`, `npm run build`, `npm run static`, and `npm run static:serve` as opt-in workflows.
146
+ - Use `npm run static` only when the user wants a static HTML export, and `npm run static:serve` only to preview it. Both are app-owned conventions; confirm the scripts and `settings/build-static.py` / `settings/serve-static.py` exist first.
124
147
  - Use `npm run dev` only when the user explicitly asks to start the local stack or the task truly needs that running workflow.
125
148
  - Use `npm run build` only for deployment prep or an explicit build request.
126
149
  - Treat `public/css/styles.css`, `settings/component-map.json`, `settings/files-list.json`, `__pycache__/`, and `.pyc` files as generated outputs when a script intentionally runs.
@@ -182,12 +205,12 @@ In skip-prompt mode, the default feature values are:
182
205
 
183
206
  - `backendOnly: false`
184
207
  - `tailwindcss: false`
185
- - `typescript: false`
186
- - `mcp: false`
187
- - `prisma: false`
188
- - `websocket: false`
189
-
190
- If a project needs WebSockets after creation, set `websocket: true` in `caspian.config.json`, then run the project update workflow so framework-managed files and any scaffolded socket surfaces stay aligned before assuming `@app.websocket(...)` routes or `src/lib/websocket/**` exist.
208
+ - `typescript: false`
209
+ - `mcp: false`
210
+ - `prisma: false`
211
+ - `websocket: false`
212
+
213
+ If a project needs WebSockets after creation, set `websocket: true` in `caspian.config.json`, then run the project update workflow so framework-managed files and any scaffolded socket surfaces stay aligned before assuming `@app.websocket(...)` routes or `src/lib/websocket/**` exist.
191
214
 
192
215
  ### 4.2 Backend-only combinations
193
216
 
@@ -598,18 +621,18 @@ The create and update commands above are not the whole maintenance story for a P
598
621
  ```bash
599
622
  npx prisma migrate dev
600
623
 
601
- # Only when seed flow or prisma/seed.ts depends on the new schema:
602
- npx prisma generate
603
- # Destructive-data warning: ask the user for explicit approval before running this.
604
- npx prisma db seed
605
-
606
- npx ppy generate
607
- ```
624
+ # Only when seed flow or prisma/seed.ts depends on the new schema:
625
+ npx prisma generate
626
+ # Destructive-data warning: ask the user for explicit approval before running this.
627
+ npx prisma db seed
628
+
629
+ npx ppy generate
630
+ ```
608
631
 
609
632
  Use this order:
610
633
 
611
634
  1. Run `npx prisma migrate dev` first so migrations and the development database stay aligned.
612
- 2. If `prisma/seed.ts` or seed data depends on the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed` because the seed script may delete or overwrite database data.
635
+ 2. If `prisma/seed.ts` or seed data depends on the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed` because the seed script may delete or overwrite database data.
613
636
  3. Run `npx ppy generate` last so the generated Python ORM layer matches the updated Prisma schema.
614
637
 
615
638
  Do not manually create or edit these generated files:
@@ -637,6 +660,8 @@ See [database.md](./database.md) for the full schema, migration, seed, and async
637
660
  | Update current project to beta safely in PowerShell | `npx casp update project --tag beta` |
638
661
  | Update current project to an exact version | `npx casp update project --version 1.2.3 -y` |
639
662
  | Regenerate Python ORM after schema changes | `npx prisma migrate dev` then optional seed commands, then `npx ppy generate` |
663
+ | Export the app to static HTML (SSG) | `npm run static` (when the project defines it) |
664
+ | Preview the exported static build over HTTP | `npm run static:serve` (auto-selects a free port) |
640
665
 
641
666
  ## 10. Configuration Notes
642
667
 
@@ -682,8 +707,9 @@ If an AI agent is deciding which command flow to use, apply these rules first.
682
707
  - Treat `--tailwindcss` and `--typescript` as frontend-oriented flags. They are not meaningful in normal backend-only scaffolds.
683
708
  - Treat starter kit defaults as a base layer that can be overridden by explicit flags.
684
709
  - Read `caspian.config.json` before running update commands.
685
- - When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first, then any required seed refresh, then `npx ppy generate`. Before running `npx prisma db seed`, warn the user that it can delete or overwrite data and wait for explicit approval.
710
+ - When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first, then any required seed refresh, then `npx ppy generate`. Before running `npx prisma db seed`, warn the user that it can delete or overwrite data and wait for explicit approval.
686
711
  - Never hand-edit generated Python ORM files under `src/lib/prisma/` or `settings/prisma-schema.json`.
687
712
  - Read [database.md](./database.md) before generating Prisma schema, migration, seed, or ORM guidance.
713
+ - Read [static-export.md](./static-export.md) before generating or explaining static export (`npm run static`), dynamic-route pre-rendering with `static_paths`, or the `npm run static:serve` preview server. Confirm the scripts and `settings/build-static.py` / `settings/serve-static.py` exist first.
688
714
  - Read [routing.md](./routing.md) before generating or modifying route folders under `src/app/`.
689
715
  - Read [project-structure.md](./project-structure.md) before placing generated files into the project.