schematic-pg 0.1.16 → 0.1.17
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/README.md +4 -1
- package/dist/api/middleware/cors.d.ts +6 -0
- package/dist/api/middleware/cors.js +35 -0
- package/dist/api-generator/app-generator.js +2 -0
- package/dist/cli/templates/agents.md +2 -1
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -425,7 +425,7 @@ The `init` command creates everything you need to get running:
|
|
|
425
425
|
|------------------|---------|
|
|
426
426
|
| `AGENTS.md` | Agent-oriented guide for working with schematic-pg in this project |
|
|
427
427
|
| `app.schema` | Starter schema (one `User` model) — edit this |
|
|
428
|
-
| `.env` | `DATABASE_URL`, JWT settings |
|
|
428
|
+
| `.env` | `DATABASE_URL`, JWT settings, `CORS_ORIGIN` |
|
|
429
429
|
| `docker-compose.yml` | Local PostgreSQL on `:5432` |
|
|
430
430
|
| `Makefile` | `make dev` — docker compose (with health wait) + `schematic-pg dev` |
|
|
431
431
|
| `tsconfig.json` | TypeScript config for `generated/` and `src/routes/` |
|
|
@@ -456,9 +456,12 @@ Generated code imports the runtime from the `schematic-pg` package (`schematic-p
|
|
|
456
456
|
| `AUTH_ACCESS_TOKEN_TTL` | `1h` | Access token lifetime (`15m`, `1h`, or seconds) |
|
|
457
457
|
| `JWT_ROLE_CLAIM` | `role` | JWT claim mapped to `auth.role` |
|
|
458
458
|
| `JWT_USER_ID_CLAIM` | `sub` | JWT claim mapped to `auth.user.id` |
|
|
459
|
+
| `CORS_ORIGIN` | — (disabled) | Allowed browser origins. Unset disables CORS. Use `*` for any origin, or a comma-separated list (`http://localhost:5173,https://app.example.com`) |
|
|
459
460
|
|
|
460
461
|
Set these in `.env` before running `dev`, `start`, or `db:bootstrap`.
|
|
461
462
|
|
|
463
|
+
Browser frontends on another origin need `CORS_ORIGIN`. The generated app reads it at runtime (no regenerate). Preflight `OPTIONS` is handled automatically; `Authorization` and `Content-Type` are allowed. See [CORS](docs/rest-api.md#cors).
|
|
464
|
+
|
|
462
465
|
---
|
|
463
466
|
|
|
464
467
|
## Authentication
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MiddlewareHandler } from 'hono';
|
|
2
|
+
import type { AppEnv } from '../types.js';
|
|
3
|
+
export declare function parseCorsOrigin(raw?: string | undefined): string | string[] | null;
|
|
4
|
+
export declare function createCorsMiddleware(options?: {
|
|
5
|
+
origin?: string | string[] | null;
|
|
6
|
+
}): MiddlewareHandler<AppEnv>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { cors } from 'hono/cors';
|
|
2
|
+
const CORS_ALLOW_HEADERS = ['Authorization', 'Content-Type'];
|
|
3
|
+
const CORS_ALLOW_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
|
|
4
|
+
const CORS_MAX_AGE_SECONDS = 86400;
|
|
5
|
+
export function parseCorsOrigin(raw = process.env.CORS_ORIGIN) {
|
|
6
|
+
const trimmed = raw?.trim() ?? '';
|
|
7
|
+
if (!trimmed) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (trimmed === '*') {
|
|
11
|
+
return '*';
|
|
12
|
+
}
|
|
13
|
+
const origins = trimmed
|
|
14
|
+
.split(',')
|
|
15
|
+
.map((origin) => origin.trim())
|
|
16
|
+
.filter((origin) => origin.length > 0);
|
|
17
|
+
if (origins.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return origins.length === 1 ? origins[0] : origins;
|
|
21
|
+
}
|
|
22
|
+
export function createCorsMiddleware(options = {}) {
|
|
23
|
+
const origin = options.origin !== undefined ? options.origin : parseCorsOrigin();
|
|
24
|
+
if (origin == null) {
|
|
25
|
+
return async (_c, next) => {
|
|
26
|
+
await next();
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return cors({
|
|
30
|
+
origin,
|
|
31
|
+
allowHeaders: CORS_ALLOW_HEADERS,
|
|
32
|
+
allowMethods: CORS_ALLOW_METHODS,
|
|
33
|
+
maxAge: CORS_MAX_AGE_SECONDS,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -46,6 +46,7 @@ export class AppGenerator {
|
|
|
46
46
|
`import { createAuthMiddleware } from '${PACKAGE_NAME}/api/auth/middleware';`,
|
|
47
47
|
`import { createJwtResolver } from '${PACKAGE_NAME}/api/auth/jwt-resolver';`,
|
|
48
48
|
`import type { AuthResolver } from '${PACKAGE_NAME}/api/auth/types';`,
|
|
49
|
+
`import { createCorsMiddleware } from '${PACKAGE_NAME}/api/middleware/cors';`,
|
|
49
50
|
`import { createDbMiddleware } from '${PACKAGE_NAME}/api/middleware/db';`,
|
|
50
51
|
`import { handleError } from '${PACKAGE_NAME}/api/middleware/errors';`,
|
|
51
52
|
`import { mountApiDocs } from '${PACKAGE_NAME}/api/openapi';`,
|
|
@@ -61,6 +62,7 @@ export class AppGenerator {
|
|
|
61
62
|
'',
|
|
62
63
|
'export function createApp(options: CreateAppOptions = {}): Hono<AppEnv> {',
|
|
63
64
|
' const app = new Hono<AppEnv>();',
|
|
65
|
+
' app.use(createCorsMiddleware());',
|
|
64
66
|
' mountApiDocs(app, openApiDocument);',
|
|
65
67
|
' app.use(logger());',
|
|
66
68
|
' app.use(prettyJSON());',
|
|
@@ -25,7 +25,7 @@ my-app/
|
|
|
25
25
|
├── app.schema # Source of truth — edit this
|
|
26
26
|
├── schema.sql # Generated PostgreSQL DDL (read-only)
|
|
27
27
|
├── AGENTS.md # This file
|
|
28
|
-
├── .env # DATABASE_URL, JWT_
|
|
28
|
+
├── .env # DATABASE_URL, JWT_*, CORS_ORIGIN
|
|
29
29
|
├── docker-compose.yml # Local PostgreSQL
|
|
30
30
|
├── generated/ # Generated — do not edit
|
|
31
31
|
│ ├── db.ts # createDbClient(pool)
|
|
@@ -59,6 +59,7 @@ npx schematic-pg dev
|
|
|
59
59
|
| `JWT_SECRET` | HMAC secret for Bearer JWT auth |
|
|
60
60
|
| `JWT_ROLE_CLAIM` | JWT claim for role (default `role`) |
|
|
61
61
|
| `JWT_USER_ID_CLAIM` | JWT claim for user id (default `sub`) |
|
|
62
|
+
| `CORS_ORIGIN` | Allowed browser origins (`*` or comma-separated). Unset disables CORS |
|
|
62
63
|
|
|
63
64
|
## Schema DSL Essentials
|
|
64
65
|
|
package/dist/cli/templates.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const AGENTS_TEMPLATE: string;
|
|
2
2
|
export declare const APP_SCHEMA_TEMPLATE = "extensions {\n\n}\n\nenums {\n UserRole { ADMIN, USER }\n}\n\nmodels {\n model User {\n id: UUID @id @default(gen_random_uuid())\n email: VARCHAR(255) @unique @regex(pattern: \"^[\\w.-]+@[\\w.-]+\\.\\w+$\", message: \"Invalid email address\")\n name: VARCHAR(150)?\n role: UserRole @default(USER)\n passwordHash: VARCHAR(255)? @omit @unfilterable\n createdAt: TIMESTAMP @default(now())\n\n @rest(except: [create, update, delete])\n @policy(role: USER, allow: [select], where: \"id = {{auth.user.id}}\")\n @policy(role: ADMIN, allow: all)\n }\n}\n";
|
|
3
|
-
export declare const ENV_TEMPLATE = "DATABASE_URL=postgresql://postgrest:postgrest@localhost:5432/postgrest\nJWT_SECRET=\nAUTH_PEPPER=\nAUTH_ACCESS_TOKEN_TTL=1h\nJWT_ROLE_CLAIM=role\nJWT_USER_ID_CLAIM=sub\n";
|
|
3
|
+
export declare const ENV_TEMPLATE = "DATABASE_URL=postgresql://postgrest:postgrest@localhost:5432/postgrest\nJWT_SECRET=\nAUTH_PEPPER=\nAUTH_ACCESS_TOKEN_TTL=1h\nJWT_ROLE_CLAIM=role\nJWT_USER_ID_CLAIM=sub\nCORS_ORIGIN=\n";
|
|
4
4
|
export declare const GITIGNORE_TEMPLATE = "node_modules/\ndist/\n.env\ndocker_data/\n.DS_Store\n*.log\nnpm-debug.log*\n";
|
|
5
5
|
export declare const DOCKER_COMPOSE_TEMPLATE = "services:\n postgres:\n image: postgres:18.4-bookworm\n container_name: schematic-pg\n restart: unless-stopped\n ports:\n - \"5432:5432\"\n environment:\n POSTGRES_USER: postgrest\n POSTGRES_PASSWORD: postgrest\n POSTGRES_DB: postgrest\n volumes:\n - ./docker_data/postgres:/var/lib/postgresql\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U postgrest -d postgrest\"]\n interval: 5s\n timeout: 5s\n retries: 5\n";
|
|
6
6
|
export declare const TSCONFIG_TEMPLATE = "{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \".\"\n },\n \"include\": [\"generated/**/*\", \"src/**/*\"]\n}\n";
|
package/dist/cli/templates.js
CHANGED