@rhino-dev/rhino-nestjs 0.2.5
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/LICENSE +21 -0
- package/README.md +427 -0
- package/dist/blueprint/blueprint-parser.d.ts +130 -0
- package/dist/blueprint/blueprint-parser.js +276 -0
- package/dist/blueprint/blueprint-parser.js.map +1 -0
- package/dist/blueprint/blueprint-runner.d.ts +51 -0
- package/dist/blueprint/blueprint-runner.js +238 -0
- package/dist/blueprint/blueprint-runner.js.map +1 -0
- package/dist/blueprint/blueprint-validator.d.ts +19 -0
- package/dist/blueprint/blueprint-validator.js +163 -0
- package/dist/blueprint/blueprint-validator.js.map +1 -0
- package/dist/blueprint/generators/policy-generator.d.ts +40 -0
- package/dist/blueprint/generators/policy-generator.js +157 -0
- package/dist/blueprint/generators/policy-generator.js.map +1 -0
- package/dist/blueprint/generators/prisma-schema-generator.d.ts +43 -0
- package/dist/blueprint/generators/prisma-schema-generator.js +197 -0
- package/dist/blueprint/generators/prisma-schema-generator.js.map +1 -0
- package/dist/blueprint/generators/resource-definition-generator.d.ts +52 -0
- package/dist/blueprint/generators/resource-definition-generator.js +229 -0
- package/dist/blueprint/generators/resource-definition-generator.js.map +1 -0
- package/dist/blueprint/generators/seeder-generator.d.ts +21 -0
- package/dist/blueprint/generators/seeder-generator.js +135 -0
- package/dist/blueprint/generators/seeder-generator.js.map +1 -0
- package/dist/blueprint/generators/test-generator.d.ts +28 -0
- package/dist/blueprint/generators/test-generator.js +275 -0
- package/dist/blueprint/generators/test-generator.js.map +1 -0
- package/dist/blueprint/manifest-manager.d.ts +58 -0
- package/dist/blueprint/manifest-manager.js +137 -0
- package/dist/blueprint/manifest-manager.js.map +1 -0
- package/dist/cli/commands/blueprint.command.d.ts +16 -0
- package/dist/cli/commands/blueprint.command.js +35 -0
- package/dist/cli/commands/blueprint.command.js.map +1 -0
- package/dist/cli/commands/export-postman.command.d.ts +5 -0
- package/dist/cli/commands/export-postman.command.js +39 -0
- package/dist/cli/commands/export-postman.command.js.map +1 -0
- package/dist/cli/commands/export-types.command.d.ts +4 -0
- package/dist/cli/commands/export-types.command.js +36 -0
- package/dist/cli/commands/export-types.command.js.map +1 -0
- package/dist/cli/commands/generate.command.d.ts +6 -0
- package/dist/cli/commands/generate.command.js +212 -0
- package/dist/cli/commands/generate.command.js.map +1 -0
- package/dist/cli/commands/install.command.d.ts +8 -0
- package/dist/cli/commands/install.command.js +205 -0
- package/dist/cli/commands/install.command.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.js +123 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/io.d.ts +12 -0
- package/dist/cli/utils/io.js +74 -0
- package/dist/cli/utils/io.js.map +1 -0
- package/dist/cli/utils/prompt.d.ts +12 -0
- package/dist/cli/utils/prompt.js +78 -0
- package/dist/cli/utils/prompt.js.map +1 -0
- package/dist/constants/tokens.d.ts +3 -0
- package/dist/constants/tokens.js +7 -0
- package/dist/constants/tokens.js.map +1 -0
- package/dist/controllers/auth.controller.d.ts +59 -0
- package/dist/controllers/auth.controller.js +121 -0
- package/dist/controllers/auth.controller.js.map +1 -0
- package/dist/controllers/global.controller.d.ts +44 -0
- package/dist/controllers/global.controller.js +357 -0
- package/dist/controllers/global.controller.js.map +1 -0
- package/dist/controllers/invitation.controller.d.ts +30 -0
- package/dist/controllers/invitation.controller.js +111 -0
- package/dist/controllers/invitation.controller.js.map +1 -0
- package/dist/controllers/nested.controller.d.ts +12 -0
- package/dist/controllers/nested.controller.js +69 -0
- package/dist/controllers/nested.controller.js.map +1 -0
- package/dist/decorators/index.d.ts +33 -0
- package/dist/decorators/index.js +79 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/errors/rhino-exception.d.ts +24 -0
- package/dist/errors/rhino-exception.js +47 -0
- package/dist/errors/rhino-exception.js.map +1 -0
- package/dist/exporters/postman-exporter.d.ts +21 -0
- package/dist/exporters/postman-exporter.js +396 -0
- package/dist/exporters/postman-exporter.js.map +1 -0
- package/dist/exporters/typescript-exporter.d.ts +23 -0
- package/dist/exporters/typescript-exporter.js +129 -0
- package/dist/exporters/typescript-exporter.js.map +1 -0
- package/dist/guards/jwt-auth.guard.d.ts +16 -0
- package/dist/guards/jwt-auth.guard.js +61 -0
- package/dist/guards/jwt-auth.guard.js.map +1 -0
- package/dist/guards/resource-policy.guard.d.ts +12 -0
- package/dist/guards/resource-policy.guard.js +111 -0
- package/dist/guards/resource-policy.guard.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/hidden-columns.interceptor.d.ts +18 -0
- package/dist/interceptors/hidden-columns.interceptor.js +66 -0
- package/dist/interceptors/hidden-columns.interceptor.js.map +1 -0
- package/dist/interceptors/response.interceptor.d.ts +19 -0
- package/dist/interceptors/response.interceptor.js +47 -0
- package/dist/interceptors/response.interceptor.js.map +1 -0
- package/dist/interfaces/rhino-config.interface.d.ts +103 -0
- package/dist/interfaces/rhino-config.interface.js +3 -0
- package/dist/interfaces/rhino-config.interface.js.map +1 -0
- package/dist/interfaces/rhino-request.interface.d.ts +12 -0
- package/dist/interfaces/rhino-request.interface.js +3 -0
- package/dist/interfaces/rhino-request.interface.js.map +1 -0
- package/dist/middleware/resolve-organization.middleware.d.ts +15 -0
- package/dist/middleware/resolve-organization.middleware.js +38 -0
- package/dist/middleware/resolve-organization.middleware.js.map +1 -0
- package/dist/middleware/route-group.middleware.d.ts +17 -0
- package/dist/middleware/route-group.middleware.js +60 -0
- package/dist/middleware/route-group.middleware.js.map +1 -0
- package/dist/middleware/tenant-route-rewrite.d.ts +76 -0
- package/dist/middleware/tenant-route-rewrite.js +124 -0
- package/dist/middleware/tenant-route-rewrite.js.map +1 -0
- package/dist/policies/resource-policy.d.ts +25 -0
- package/dist/policies/resource-policy.js +77 -0
- package/dist/policies/resource-policy.js.map +1 -0
- package/dist/prisma/prisma-soft-delete.extension.d.ts +16 -0
- package/dist/prisma/prisma-soft-delete.extension.js +57 -0
- package/dist/prisma/prisma-soft-delete.extension.js.map +1 -0
- package/dist/prisma/prisma-uuid.extension.d.ts +9 -0
- package/dist/prisma/prisma-uuid.extension.js +28 -0
- package/dist/prisma/prisma-uuid.extension.js.map +1 -0
- package/dist/prisma/prisma.service.d.ts +38 -0
- package/dist/prisma/prisma.service.js +100 -0
- package/dist/prisma/prisma.service.js.map +1 -0
- package/dist/rhino.config.d.ts +43 -0
- package/dist/rhino.config.js +130 -0
- package/dist/rhino.config.js.map +1 -0
- package/dist/rhino.module.d.ts +61 -0
- package/dist/rhino.module.js +268 -0
- package/dist/rhino.module.js.map +1 -0
- package/dist/services/audit.service.d.ts +35 -0
- package/dist/services/audit.service.js +92 -0
- package/dist/services/audit.service.js.map +1 -0
- package/dist/services/auth.service.d.ts +33 -0
- package/dist/services/auth.service.js +157 -0
- package/dist/services/auth.service.js.map +1 -0
- package/dist/services/invitation.service.d.ts +37 -0
- package/dist/services/invitation.service.js +143 -0
- package/dist/services/invitation.service.js.map +1 -0
- package/dist/services/nested.service.d.ts +34 -0
- package/dist/services/nested.service.js +163 -0
- package/dist/services/nested.service.js.map +1 -0
- package/dist/services/organization.service.d.ts +13 -0
- package/dist/services/organization.service.js +60 -0
- package/dist/services/organization.service.js.map +1 -0
- package/dist/services/query-builder.service.d.ts +31 -0
- package/dist/services/query-builder.service.js +151 -0
- package/dist/services/query-builder.service.js.map +1 -0
- package/dist/services/resource.service.d.ts +38 -0
- package/dist/services/resource.service.js +230 -0
- package/dist/services/resource.service.js.map +1 -0
- package/dist/services/route-registration.service.d.ts +40 -0
- package/dist/services/route-registration.service.js +68 -0
- package/dist/services/route-registration.service.js.map +1 -0
- package/dist/services/scope.service.d.ts +16 -0
- package/dist/services/scope.service.js +32 -0
- package/dist/services/scope.service.js.map +1 -0
- package/dist/services/serializer.service.d.ts +36 -0
- package/dist/services/serializer.service.js +90 -0
- package/dist/services/serializer.service.js.map +1 -0
- package/dist/services/validation.service.d.ts +50 -0
- package/dist/services/validation.service.js +203 -0
- package/dist/services/validation.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/utils/fk-chain-walker.d.ts +30 -0
- package/dist/utils/fk-chain-walker.js +23 -0
- package/dist/utils/fk-chain-walker.js.map +1 -0
- package/dist/utils/format.d.ts +7 -0
- package/dist/utils/format.js +28 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/model-builder.d.ts +64 -0
- package/dist/utils/model-builder.js +67 -0
- package/dist/utils/model-builder.js.map +1 -0
- package/dist/utils/permission-matcher.d.ts +46 -0
- package/dist/utils/permission-matcher.js +120 -0
- package/dist/utils/permission-matcher.js.map +1 -0
- package/dist/utils/scope-discovery.d.ts +34 -0
- package/dist/utils/scope-discovery.js +102 -0
- package/dist/utils/scope-discovery.js.map +1 -0
- package/package.json +64 -0
- package/stubs/skills/rhino-audit.md +184 -0
- package/stubs/skills/rhino-bugfix.md +117 -0
- package/stubs/skills/rhino-deploy.md +5 -0
- package/stubs/skills/rhino-docs.md +147 -0
- package/stubs/skills/rhino-feature.md +146 -0
- package/stubs/skills/rhino-migrate.md +181 -0
- package/stubs/skills/rhino-model.md +180 -0
- package/stubs/skills/rhino-plan.md +5 -0
- package/stubs/skills/rhino-policy.md +183 -0
- package/stubs/skills/rhino-refactor.md +147 -0
- package/stubs/skills/rhino-review.md +113 -0
- package/stubs/skills/rhino-scope.md +140 -0
- package/stubs/skills/rhino-test.md +233 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Blueprint } from '../blueprint-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a TypeScript file that exports a `ModelRegistration` object.
|
|
4
|
+
*
|
|
5
|
+
* The output file:
|
|
6
|
+
* - imports `z` from zod and `ModelRegistration` from the library
|
|
7
|
+
* - builds `validationStore` and `validationUpdate` as `Record<string, ZodSchema>`
|
|
8
|
+
* keyed by role (from `permissions.*.create_fields` / `update_fields`)
|
|
9
|
+
* - sets `allowedFilters`, `allowedSorts`, `allowedSearch` from column flags
|
|
10
|
+
* - sets `belongsToOrganization`, `softDeletes`, `hasAuditTrail` flags
|
|
11
|
+
*
|
|
12
|
+
* Design decisions:
|
|
13
|
+
* - Field entries with value `'required'` → `z.string()` (non-optional)
|
|
14
|
+
* - Field entries with value `'nullable'` → `z.string().nullable().optional()`
|
|
15
|
+
* - Field entries with value `'sometimes'` → `z.string().optional()`
|
|
16
|
+
* - When create_fields / update_fields is `['*']` or empty, we emit a
|
|
17
|
+
* passthrough schema `z.object({}).passthrough()` as a permissive default.
|
|
18
|
+
* - If all roles share the same schema we still emit a role-keyed map for
|
|
19
|
+
* uniformity (the runtime ValidationService uses the map form).
|
|
20
|
+
*/
|
|
21
|
+
export declare class ResourceDefinitionGenerator {
|
|
22
|
+
generate(blueprint: Blueprint): string;
|
|
23
|
+
/**
|
|
24
|
+
* Build a map of role → Zod schema code string for a given field surface
|
|
25
|
+
* ('create_fields' or 'update_fields').
|
|
26
|
+
*/
|
|
27
|
+
private buildRoleSchemas;
|
|
28
|
+
private buildZodSchema;
|
|
29
|
+
/**
|
|
30
|
+
* BP-003: derive presence from a column's `nullable` flag when no explicit
|
|
31
|
+
* modifier was given.
|
|
32
|
+
*
|
|
33
|
+
* nullable: false, no default → 'required' on create, 'sometimes' on update
|
|
34
|
+
* nullable: false, has default → 'sometimes' on both (DB will fill in)
|
|
35
|
+
* nullable: true → 'nullable' on both surfaces
|
|
36
|
+
*
|
|
37
|
+
* Fallback to 'optional' when the column isn't declared (unlikely but safe).
|
|
38
|
+
*/
|
|
39
|
+
private inferPresenceFromColumn;
|
|
40
|
+
/**
|
|
41
|
+
* Determine the Zod type for a single field based on its column definition and presence modifier.
|
|
42
|
+
*
|
|
43
|
+
* Presence values:
|
|
44
|
+
* 'required' → z.string() (must be present, not null)
|
|
45
|
+
* 'nullable' → z.string().nullable().optional()
|
|
46
|
+
* 'sometimes' → z.string().optional()
|
|
47
|
+
* 'optional' → z.string().optional() (when presence not specified)
|
|
48
|
+
*/
|
|
49
|
+
private zodTypeForColumn;
|
|
50
|
+
private zodBaseType;
|
|
51
|
+
private camelCase;
|
|
52
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResourceDefinitionGenerator = void 0;
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// ResourceDefinitionGenerator
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* Generates a TypeScript file that exports a `ModelRegistration` object.
|
|
9
|
+
*
|
|
10
|
+
* The output file:
|
|
11
|
+
* - imports `z` from zod and `ModelRegistration` from the library
|
|
12
|
+
* - builds `validationStore` and `validationUpdate` as `Record<string, ZodSchema>`
|
|
13
|
+
* keyed by role (from `permissions.*.create_fields` / `update_fields`)
|
|
14
|
+
* - sets `allowedFilters`, `allowedSorts`, `allowedSearch` from column flags
|
|
15
|
+
* - sets `belongsToOrganization`, `softDeletes`, `hasAuditTrail` flags
|
|
16
|
+
*
|
|
17
|
+
* Design decisions:
|
|
18
|
+
* - Field entries with value `'required'` → `z.string()` (non-optional)
|
|
19
|
+
* - Field entries with value `'nullable'` → `z.string().nullable().optional()`
|
|
20
|
+
* - Field entries with value `'sometimes'` → `z.string().optional()`
|
|
21
|
+
* - When create_fields / update_fields is `['*']` or empty, we emit a
|
|
22
|
+
* passthrough schema `z.object({}).passthrough()` as a permissive default.
|
|
23
|
+
* - If all roles share the same schema we still emit a role-keyed map for
|
|
24
|
+
* uniformity (the runtime ValidationService uses the map form).
|
|
25
|
+
*/
|
|
26
|
+
class ResourceDefinitionGenerator {
|
|
27
|
+
generate(blueprint) {
|
|
28
|
+
const lines = [];
|
|
29
|
+
lines.push(`// Generated by Rhino Blueprint — do not edit directly.`);
|
|
30
|
+
lines.push(`// Source: .rhino/blueprints/${blueprint.source_file}`);
|
|
31
|
+
lines.push(`import { z } from 'zod';`);
|
|
32
|
+
lines.push(`import type { ModelRegistration } from '@rhino-dev/rhino-nestjs';`);
|
|
33
|
+
lines.push('');
|
|
34
|
+
// Policy import (generated separately)
|
|
35
|
+
lines.push(`import { ${blueprint.model}Policy } from '../policies/${blueprint.model}Policy';`);
|
|
36
|
+
lines.push('');
|
|
37
|
+
// Zod schemas per role
|
|
38
|
+
const storeSchemas = this.buildRoleSchemas(blueprint, 'create_fields');
|
|
39
|
+
const updateSchemas = this.buildRoleSchemas(blueprint, 'update_fields');
|
|
40
|
+
lines.push('// ---------------------------------------------------------------------------');
|
|
41
|
+
lines.push('// Validation schemas per role');
|
|
42
|
+
lines.push('// ---------------------------------------------------------------------------');
|
|
43
|
+
lines.push('');
|
|
44
|
+
lines.push('const validationStore: Record<string, z.ZodTypeAny> = {');
|
|
45
|
+
for (const [role, schemaCode] of Object.entries(storeSchemas)) {
|
|
46
|
+
lines.push(` ${role}: ${schemaCode},`);
|
|
47
|
+
}
|
|
48
|
+
lines.push('};');
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push('const validationUpdate: Record<string, z.ZodTypeAny> = {');
|
|
51
|
+
for (const [role, schemaCode] of Object.entries(updateSchemas)) {
|
|
52
|
+
lines.push(` ${role}: ${schemaCode},`);
|
|
53
|
+
}
|
|
54
|
+
lines.push('};');
|
|
55
|
+
lines.push('');
|
|
56
|
+
// Allowed query options from column flags
|
|
57
|
+
const allowedFilters = blueprint.columns.filter((c) => c.filterable).map((c) => c.name);
|
|
58
|
+
const allowedSorts = blueprint.columns.filter((c) => c.sortable).map((c) => c.name);
|
|
59
|
+
const allowedSearch = blueprint.columns.filter((c) => c.searchable).map((c) => c.name);
|
|
60
|
+
lines.push('// ---------------------------------------------------------------------------');
|
|
61
|
+
lines.push('// ModelRegistration');
|
|
62
|
+
lines.push('// ---------------------------------------------------------------------------');
|
|
63
|
+
lines.push('');
|
|
64
|
+
lines.push(`export const ${this.camelCase(blueprint.slug)}Registration: ModelRegistration = {`);
|
|
65
|
+
lines.push(` model: '${this.camelCase(blueprint.model)}',`);
|
|
66
|
+
lines.push(` policy: ${blueprint.model}Policy,`);
|
|
67
|
+
lines.push(` validationStore,`);
|
|
68
|
+
lines.push(` validationUpdate,`);
|
|
69
|
+
if (allowedFilters.length > 0) {
|
|
70
|
+
lines.push(` allowedFilters: ${JSON.stringify(allowedFilters)},`);
|
|
71
|
+
}
|
|
72
|
+
if (allowedSorts.length > 0) {
|
|
73
|
+
lines.push(` allowedSorts: ${JSON.stringify(allowedSorts)},`);
|
|
74
|
+
}
|
|
75
|
+
if (allowedSearch.length > 0) {
|
|
76
|
+
lines.push(` allowedSearch: ${JSON.stringify(allowedSearch)},`);
|
|
77
|
+
}
|
|
78
|
+
lines.push(` belongsToOrganization: ${blueprint.options.belongs_to_organization},`);
|
|
79
|
+
lines.push(` softDeletes: ${blueprint.options.soft_deletes},`);
|
|
80
|
+
lines.push(` hasAuditTrail: ${blueprint.options.audit_trail},`);
|
|
81
|
+
if (blueprint.options.has_uuid) {
|
|
82
|
+
// BP-005: surface the flag so ResourceService.castId preserves string IDs
|
|
83
|
+
lines.push(` hasUuid: true,`);
|
|
84
|
+
}
|
|
85
|
+
// BP-004: emit `owner` / `fkConstraints` from YAML when declared so
|
|
86
|
+
// ValidationService.verifyTenantFks (and the FK chain walker) can do
|
|
87
|
+
// cross-tenant validation without a hand-authored override.
|
|
88
|
+
const ownerStr = blueprint.options.owner_chain ?? blueprint.options.owner ?? null;
|
|
89
|
+
if (ownerStr) {
|
|
90
|
+
lines.push(` owner: ${JSON.stringify(ownerStr)},`);
|
|
91
|
+
}
|
|
92
|
+
if (blueprint.fk_constraints?.length > 0) {
|
|
93
|
+
lines.push(` fkConstraints: ${JSON.stringify(blueprint.fk_constraints)},`);
|
|
94
|
+
}
|
|
95
|
+
if (blueprint.options.except_actions?.length > 0) {
|
|
96
|
+
lines.push(` exceptActions: ${JSON.stringify(blueprint.options.except_actions)},`);
|
|
97
|
+
}
|
|
98
|
+
lines.push(`};`);
|
|
99
|
+
lines.push('');
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
// -------------------------------------------------------------------------
|
|
103
|
+
// Private helpers
|
|
104
|
+
// -------------------------------------------------------------------------
|
|
105
|
+
/**
|
|
106
|
+
* Build a map of role → Zod schema code string for a given field surface
|
|
107
|
+
* ('create_fields' or 'update_fields').
|
|
108
|
+
*/
|
|
109
|
+
buildRoleSchemas(blueprint, surface) {
|
|
110
|
+
const result = {};
|
|
111
|
+
for (const [role, perm] of Object.entries(blueprint.permissions)) {
|
|
112
|
+
result[role] = this.buildZodSchema(perm, surface, blueprint.columns);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
buildZodSchema(perm, surface, columns) {
|
|
117
|
+
const fields = perm[surface];
|
|
118
|
+
// Wildcard → passthrough permissive schema
|
|
119
|
+
if (Array.isArray(fields) && fields[0] === '*') {
|
|
120
|
+
return `z.object({}).passthrough()`;
|
|
121
|
+
}
|
|
122
|
+
// Array form (no presence modifiers) — BP-003: infer required from
|
|
123
|
+
// the column's `nullable: false` declaration. This lets blueprints
|
|
124
|
+
// express "required title" purely via the column definition, without
|
|
125
|
+
// forcing the object form { title: required } on every permission.
|
|
126
|
+
if (Array.isArray(fields)) {
|
|
127
|
+
if (fields.length === 0)
|
|
128
|
+
return `z.object({})`;
|
|
129
|
+
const entries = fields.map((f) => {
|
|
130
|
+
const col = columns.find((c) => c.name === f);
|
|
131
|
+
// For create surface: non-nullable column → required; nullable → optional/nullable.
|
|
132
|
+
// For update surface: always optional (partial update semantics); keep nullable.
|
|
133
|
+
const presence = this.inferPresenceFromColumn(col, surface);
|
|
134
|
+
return ` ${f}: ${this.zodTypeForColumn(f, columns, presence)}`;
|
|
135
|
+
});
|
|
136
|
+
return `z.object({\n${entries.join(',\n')},\n })`;
|
|
137
|
+
}
|
|
138
|
+
// Object form: { fieldName: 'required' | 'nullable' | 'sometimes' }
|
|
139
|
+
const entries = Object.entries(fields ?? {}).map(([field, presence]) => {
|
|
140
|
+
const zodType = this.zodTypeForColumn(field, columns, presence);
|
|
141
|
+
return ` ${field}: ${zodType}`;
|
|
142
|
+
});
|
|
143
|
+
// Empty object — role has no writable fields (e.g. read-only roles)
|
|
144
|
+
if (entries.length === 0)
|
|
145
|
+
return `z.object({})`;
|
|
146
|
+
return `z.object({\n${entries.join(',\n')},\n })`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* BP-003: derive presence from a column's `nullable` flag when no explicit
|
|
150
|
+
* modifier was given.
|
|
151
|
+
*
|
|
152
|
+
* nullable: false, no default → 'required' on create, 'sometimes' on update
|
|
153
|
+
* nullable: false, has default → 'sometimes' on both (DB will fill in)
|
|
154
|
+
* nullable: true → 'nullable' on both surfaces
|
|
155
|
+
*
|
|
156
|
+
* Fallback to 'optional' when the column isn't declared (unlikely but safe).
|
|
157
|
+
*/
|
|
158
|
+
inferPresenceFromColumn(col, surface) {
|
|
159
|
+
if (!col)
|
|
160
|
+
return 'optional';
|
|
161
|
+
if (col.nullable)
|
|
162
|
+
return 'nullable';
|
|
163
|
+
// BP-003 followup: columns with a default value can be omitted on create
|
|
164
|
+
// — the DB (or Prisma @default) supplies the value.
|
|
165
|
+
if (col.default !== null && col.default !== undefined)
|
|
166
|
+
return 'sometimes';
|
|
167
|
+
return surface === 'create_fields' ? 'required' : 'sometimes';
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Determine the Zod type for a single field based on its column definition and presence modifier.
|
|
171
|
+
*
|
|
172
|
+
* Presence values:
|
|
173
|
+
* 'required' → z.string() (must be present, not null)
|
|
174
|
+
* 'nullable' → z.string().nullable().optional()
|
|
175
|
+
* 'sometimes' → z.string().optional()
|
|
176
|
+
* 'optional' → z.string().optional() (when presence not specified)
|
|
177
|
+
*/
|
|
178
|
+
zodTypeForColumn(fieldName, columns, presence) {
|
|
179
|
+
const col = columns.find((c) => c.name === fieldName);
|
|
180
|
+
const baseType = col ? this.zodBaseType(col) : 'z.string()';
|
|
181
|
+
switch (presence) {
|
|
182
|
+
case 'required':
|
|
183
|
+
return baseType;
|
|
184
|
+
case 'nullable':
|
|
185
|
+
return `${baseType}.nullable().optional()`;
|
|
186
|
+
case 'sometimes':
|
|
187
|
+
case 'optional':
|
|
188
|
+
return `${baseType}.optional()`;
|
|
189
|
+
default:
|
|
190
|
+
return `${baseType}.optional()`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
zodBaseType(col) {
|
|
194
|
+
switch (col.type) {
|
|
195
|
+
case 'string':
|
|
196
|
+
case 'text':
|
|
197
|
+
case 'uuid':
|
|
198
|
+
return 'z.string()';
|
|
199
|
+
case 'integer':
|
|
200
|
+
case 'bigInteger':
|
|
201
|
+
case 'foreignId':
|
|
202
|
+
return 'z.number().int()';
|
|
203
|
+
case 'float':
|
|
204
|
+
case 'decimal':
|
|
205
|
+
return 'z.number()';
|
|
206
|
+
case 'boolean':
|
|
207
|
+
return 'z.boolean()';
|
|
208
|
+
case 'date':
|
|
209
|
+
case 'datetime':
|
|
210
|
+
case 'timestamp':
|
|
211
|
+
return 'z.string().datetime({ offset: true }).or(z.date())';
|
|
212
|
+
case 'json':
|
|
213
|
+
return 'z.record(z.unknown())';
|
|
214
|
+
case 'enum':
|
|
215
|
+
if (col.values?.length) {
|
|
216
|
+
const vals = col.values.map((v) => `'${v}'`).join(', ');
|
|
217
|
+
return `z.enum([${vals}])`;
|
|
218
|
+
}
|
|
219
|
+
return 'z.string()';
|
|
220
|
+
default:
|
|
221
|
+
return 'z.string()';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
camelCase(str) {
|
|
225
|
+
return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase());
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
exports.ResourceDefinitionGenerator = ResourceDefinitionGenerator;
|
|
229
|
+
//# sourceMappingURL=resource-definition-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-definition-generator.js","sourceRoot":"","sources":["../../../src/blueprint/generators/resource-definition-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Blueprint } from '../blueprint-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a TypeScript seeder file for a Blueprint resource.
|
|
4
|
+
*
|
|
5
|
+
* The generated file exports an async `seed` function that creates a small
|
|
6
|
+
* number of sample rows via the Prisma client.
|
|
7
|
+
*
|
|
8
|
+
* Design decisions:
|
|
9
|
+
* - We generate 3 sample rows with slightly varied data.
|
|
10
|
+
* - `organizationId` defaults to 1 when `belongs_to_organization` is true.
|
|
11
|
+
* - The seeder is a plain async function — not a NestJS service — so it can
|
|
12
|
+
* be run standalone via `ts-node src/seeders/<Model>Seeder.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SeederGenerator {
|
|
15
|
+
generate(blueprint: Blueprint): string;
|
|
16
|
+
private buildRow;
|
|
17
|
+
private sampleValue;
|
|
18
|
+
private humanize;
|
|
19
|
+
private formatObject;
|
|
20
|
+
private camelCase;
|
|
21
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SeederGenerator = void 0;
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// SeederGenerator
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* Generates a TypeScript seeder file for a Blueprint resource.
|
|
9
|
+
*
|
|
10
|
+
* The generated file exports an async `seed` function that creates a small
|
|
11
|
+
* number of sample rows via the Prisma client.
|
|
12
|
+
*
|
|
13
|
+
* Design decisions:
|
|
14
|
+
* - We generate 3 sample rows with slightly varied data.
|
|
15
|
+
* - `organizationId` defaults to 1 when `belongs_to_organization` is true.
|
|
16
|
+
* - The seeder is a plain async function — not a NestJS service — so it can
|
|
17
|
+
* be run standalone via `ts-node src/seeders/<Model>Seeder.ts`.
|
|
18
|
+
*/
|
|
19
|
+
class SeederGenerator {
|
|
20
|
+
generate(blueprint) {
|
|
21
|
+
const { model, options, columns } = blueprint;
|
|
22
|
+
const prismaModel = this.camelCase(model);
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push(`// Generated by Rhino Blueprint — do not edit directly.`);
|
|
25
|
+
lines.push(`// Source: .rhino/blueprints/${blueprint.source_file}`);
|
|
26
|
+
lines.push(`import { PrismaClient } from '@prisma/client';`);
|
|
27
|
+
lines.push(``);
|
|
28
|
+
lines.push(`const prisma = new PrismaClient();`);
|
|
29
|
+
lines.push(``);
|
|
30
|
+
lines.push(`/**`);
|
|
31
|
+
lines.push(` * Seed sample ${model} rows.`);
|
|
32
|
+
lines.push(` * Run: npx ts-node src/seeders/${model}Seeder.ts`);
|
|
33
|
+
lines.push(` */`);
|
|
34
|
+
lines.push(`export async function seed(): Promise<void> {`);
|
|
35
|
+
lines.push(` console.log('Seeding ${model}...');`);
|
|
36
|
+
lines.push(``);
|
|
37
|
+
const rows = [1, 2, 3].map((i) => this.buildRow(blueprint, i));
|
|
38
|
+
for (let i = 0; i < rows.length; i++) {
|
|
39
|
+
lines.push(` await prisma.${prismaModel}.upsert({`);
|
|
40
|
+
// BP-005: UUID-PK models need string ids, not integer ids.
|
|
41
|
+
if (blueprint.options.has_uuid) {
|
|
42
|
+
const uuid = `00000000-0000-0000-0000-00000000000${i + 1}`;
|
|
43
|
+
lines.push(` where: { id: '${uuid}' },`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
lines.push(` where: { id: ${i + 1} },`);
|
|
47
|
+
}
|
|
48
|
+
lines.push(` update: {},`);
|
|
49
|
+
lines.push(` create: ${this.formatObject(rows[i], 4)},`);
|
|
50
|
+
lines.push(` });`);
|
|
51
|
+
lines.push(``);
|
|
52
|
+
}
|
|
53
|
+
lines.push(` console.log('✓ ${model} seeded (${rows.length} rows)');`);
|
|
54
|
+
lines.push(`}`);
|
|
55
|
+
lines.push(``);
|
|
56
|
+
lines.push(`// Allow running directly`);
|
|
57
|
+
lines.push(`if (require.main === module) {`);
|
|
58
|
+
lines.push(` seed()`);
|
|
59
|
+
lines.push(` .catch(console.error)`);
|
|
60
|
+
lines.push(` .finally(() => prisma.$disconnect());`);
|
|
61
|
+
lines.push(`}`);
|
|
62
|
+
lines.push(``);
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
65
|
+
// -------------------------------------------------------------------------
|
|
66
|
+
// Private helpers
|
|
67
|
+
// -------------------------------------------------------------------------
|
|
68
|
+
buildRow(blueprint, index) {
|
|
69
|
+
// BP-005: UUID-PK models use deterministic UUID strings so seed is
|
|
70
|
+
// idempotent (upsert where id:'00...001' etc.).
|
|
71
|
+
const id = blueprint.options.has_uuid
|
|
72
|
+
? `00000000-0000-0000-0000-00000000000${index}`
|
|
73
|
+
: index;
|
|
74
|
+
const row = { id };
|
|
75
|
+
if (blueprint.options.belongs_to_organization) {
|
|
76
|
+
row['organizationId'] = 1;
|
|
77
|
+
}
|
|
78
|
+
for (const col of blueprint.columns) {
|
|
79
|
+
row[col.name] = this.sampleValue(col, index);
|
|
80
|
+
}
|
|
81
|
+
return row;
|
|
82
|
+
}
|
|
83
|
+
sampleValue(col, index) {
|
|
84
|
+
if (col.default !== null && col.default !== undefined) {
|
|
85
|
+
// Use default when available
|
|
86
|
+
return col.default;
|
|
87
|
+
}
|
|
88
|
+
switch (col.type) {
|
|
89
|
+
case 'string':
|
|
90
|
+
return `Sample ${this.humanize(col.name)} ${index}`;
|
|
91
|
+
case 'text':
|
|
92
|
+
return `Sample ${this.humanize(col.name)} content for row ${index}.`;
|
|
93
|
+
case 'integer':
|
|
94
|
+
case 'bigInteger':
|
|
95
|
+
return index * 10;
|
|
96
|
+
case 'foreignId':
|
|
97
|
+
return 1; // reference first record
|
|
98
|
+
case 'float':
|
|
99
|
+
case 'decimal':
|
|
100
|
+
return parseFloat((index * 9.99).toFixed(2));
|
|
101
|
+
case 'boolean':
|
|
102
|
+
return index % 2 === 1;
|
|
103
|
+
case 'date':
|
|
104
|
+
case 'datetime':
|
|
105
|
+
case 'timestamp':
|
|
106
|
+
return new Date(`2024-0${index}-01T00:00:00Z`).toISOString();
|
|
107
|
+
case 'json':
|
|
108
|
+
return { sample: true, index };
|
|
109
|
+
case 'uuid':
|
|
110
|
+
return `00000000-0000-0000-0000-00000000000${index}`;
|
|
111
|
+
case 'enum':
|
|
112
|
+
return col.values?.[0] ?? 'draft';
|
|
113
|
+
default:
|
|
114
|
+
return `value_${index}`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
humanize(name) {
|
|
118
|
+
return name.replace(/[_-]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
119
|
+
}
|
|
120
|
+
formatObject(obj, indent) {
|
|
121
|
+
const pad = ' '.repeat(indent);
|
|
122
|
+
const innerPad = ' '.repeat(indent + 2);
|
|
123
|
+
const entries = Object.entries(obj).map(([k, v]) => {
|
|
124
|
+
const val = JSON.stringify(v);
|
|
125
|
+
return `${innerPad}${k}: ${val}`;
|
|
126
|
+
});
|
|
127
|
+
return `{\n${entries.join(',\n')},\n${pad}}`;
|
|
128
|
+
}
|
|
129
|
+
camelCase(str) {
|
|
130
|
+
const pascal = str.replace(/[-_](.)/g, (_, c) => c.toUpperCase());
|
|
131
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.SeederGenerator = SeederGenerator;
|
|
135
|
+
//# sourceMappingURL=seeder-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seeder-generator.js","sourceRoot":"","sources":["../../../src/blueprint/generators/seeder-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Blueprint } from '../blueprint-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a Jest spec for a blueprint resource.
|
|
4
|
+
*
|
|
5
|
+
* Produces a self-contained file using the library's `buildEnv` helper, which
|
|
6
|
+
* the library ships under `test/helpers/make-controller.ts`. Consumer apps
|
|
7
|
+
* can copy that helper into their own test tree or replace the import path
|
|
8
|
+
* with their own equivalent — a comment in the generated file explains how.
|
|
9
|
+
*
|
|
10
|
+
* Tests produced:
|
|
11
|
+
* 1. CRUD happy-path (index / show / store / update / destroy)
|
|
12
|
+
* 2. 403 when a restricted role attempts a write action
|
|
13
|
+
* 3. 404 for cross-tenant access (when `belongs_to_organization: true`)
|
|
14
|
+
*
|
|
15
|
+
* Controller invocation uses the real `GlobalController` public method
|
|
16
|
+
* signatures: `index(modelSlug, query, req)`, `show(modelSlug, id, query, req)`,
|
|
17
|
+
* `store(modelSlug, body, req)`, `update(modelSlug, id, body, req)`,
|
|
18
|
+
* `destroy(modelSlug, id, req)`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class TestGenerator {
|
|
21
|
+
generate(blueprint: Blueprint): string;
|
|
22
|
+
private permissionsFor;
|
|
23
|
+
private buildSampleRow;
|
|
24
|
+
private buildCreatePayload;
|
|
25
|
+
private buildUpdatePayload;
|
|
26
|
+
private sampleValue;
|
|
27
|
+
private camelCase;
|
|
28
|
+
}
|