schematic-pg 0.1.14 → 0.1.16
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 +383 -194
- package/dist/api-generator/app-generator.d.ts +5 -0
- package/dist/api-generator/app-generator.js +14 -6
- package/dist/api-generator/custom-route-scanner.d.ts +9 -0
- package/dist/api-generator/custom-route-scanner.js +17 -1
- package/dist/api-generator/index.js +10 -4
- package/dist/api-generator/openapi-generator.js +74 -52
- package/dist/api-generator/route-generator.d.ts +11 -4
- package/dist/api-generator/route-generator.js +151 -51
- package/dist/api-generator/utils/rest.d.ts +9 -0
- package/dist/api-generator/utils/rest.js +93 -0
- package/dist/cli/generate.d.ts +1 -0
- package/dist/cli/generate.js +10 -3
- package/dist/cli/templates/agents.md +6 -2
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.js +2 -1
- package/package.json +1 -1
package/dist/cli/generate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, readdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parse } from '../schema-dsl/index.js';
|
|
4
4
|
import { generateApiFiles } from '../api-generator/index.js';
|
|
@@ -38,10 +38,17 @@ export async function generateApi(schemaPath) {
|
|
|
38
38
|
await writeFile(path.join(schemasDir, 'validation.ts'), files.validation, 'utf8');
|
|
39
39
|
await writeFile(path.join(outputDir, 'openapi.ts'), files.openapiTs, 'utf8');
|
|
40
40
|
await writeFile(path.join(outputDir, 'openapi.json'), files.openapiJson, 'utf8');
|
|
41
|
-
|
|
41
|
+
await syncGeneratedRouteFiles(routesDir, files.routes);
|
|
42
|
+
console.log(`Generated API files in ${outputDir}`);
|
|
43
|
+
}
|
|
44
|
+
export async function syncGeneratedRouteFiles(routesDir, routes) {
|
|
45
|
+
for (const [fileName, content] of routes) {
|
|
42
46
|
await writeFile(path.join(routesDir, fileName), content, 'utf8');
|
|
43
47
|
}
|
|
44
|
-
|
|
48
|
+
const existing = await readdir(routesDir);
|
|
49
|
+
await Promise.all(existing
|
|
50
|
+
.filter((fileName) => fileName.endsWith('.ts') && !routes.has(fileName))
|
|
51
|
+
.map((fileName) => unlink(path.join(routesDir, fileName))));
|
|
45
52
|
}
|
|
46
53
|
export async function generateAll(schemaPath) {
|
|
47
54
|
const resolvedSchemaPath = resolveSchemaPath(schemaPath);
|
|
@@ -77,7 +77,8 @@ models {
|
|
|
77
77
|
|
|
78
78
|
orders: Order[]
|
|
79
79
|
|
|
80
|
-
@
|
|
80
|
+
@rest(except: [create, update, delete])
|
|
81
|
+
@policy(role: USER, allow: [select], where: "id = {{auth.user.id}}")
|
|
81
82
|
@policy(role: ADMIN, allow: all)
|
|
82
83
|
|
|
83
84
|
@@index(fields: [role])
|
|
@@ -93,6 +94,7 @@ models {
|
|
|
93
94
|
**Key concepts:**
|
|
94
95
|
|
|
95
96
|
- **Relations:** Put `@relation(fields: [...], references: [...])` on the FK-owning side. The inverse side is inferred.
|
|
97
|
+
- **REST surface:** `@rest(only: [...])`, `@rest(except: [...])`, or `@rest(false)` controls which CRUD handlers are generated (`list`/`get`/`create`/`update`/`delete`).
|
|
96
98
|
- **Policies:** `@policy(role: ..., allow: [select|insert|update|delete|all], where: "...")` — `where` supports `{{auth.user.id}}`.
|
|
97
99
|
- **Validation:** `@regex(...)`, `@range(min: ..., max: ...)` flow into generated Zod schemas.
|
|
98
100
|
- **Indexes / triggers:** `@@index(...)`, `@@trigger { timing, event, level, execute: """...""" }`.
|
|
@@ -225,8 +227,9 @@ Also available: `ForeignKeyConstraintError`, `DatabaseError`.
|
|
|
225
227
|
| Location | `src/routes/**/*.ts` |
|
|
226
228
|
| Export | `export default router` (`Hono<AppEnv>`) |
|
|
227
229
|
| Mount path | File path relative to `src/routes/` |
|
|
230
|
+
| Same-path overlay | `src/routes/users.ts` merges into `generated/routes/users.ts` |
|
|
228
231
|
|
|
229
|
-
`src/routes/health.ts` → `GET /health
|
|
232
|
+
`src/routes/health.ts` → `GET /health` (standalone). When the file name matches a model route (`users.ts`), it is imported into the generated model router after remaining `@rest` handlers. Regenerate after adding files.
|
|
230
233
|
|
|
231
234
|
```typescript
|
|
232
235
|
import { Hono } from 'hono';
|
|
@@ -276,6 +279,7 @@ schematic-pg hooks:add [--model X] # scaffold src/hooks/{Model}.ts
|
|
|
276
279
|
- Unauthenticated requests default to `{ role: 'PUBLIC' }`.
|
|
277
280
|
- `@policy` `allow` maps to HTTP: GET→select, POST→insert, PUT→update, DELETE→delete.
|
|
278
281
|
- Row-level `where` is injected on read/update/delete; POST checks permission only.
|
|
282
|
+
- `@rest` controls which handlers exist; disabled methods return `404` and are omitted from OpenAPI.
|
|
279
283
|
|
|
280
284
|
## Generated Outputs (Read-Only)
|
|
281
285
|
|
package/dist/cli/templates.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const AGENTS_TEMPLATE: string;
|
|
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 @policy(role: USER, allow: [select
|
|
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
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";
|
|
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";
|
package/dist/cli/templates.js
CHANGED
|
@@ -21,7 +21,8 @@ models {
|
|
|
21
21
|
passwordHash: VARCHAR(255)? @omit @unfilterable
|
|
22
22
|
createdAt: TIMESTAMP @default(now())
|
|
23
23
|
|
|
24
|
-
@
|
|
24
|
+
@rest(except: [create, update, delete])
|
|
25
|
+
@policy(role: USER, allow: [select], where: "id = {{auth.user.id}}")
|
|
25
26
|
@policy(role: ADMIN, allow: all)
|
|
26
27
|
}
|
|
27
28
|
}
|