supa-forge 0.1.1
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 +65 -0
- package/bin/cli.js +116 -0
- package/dist/index.d.ts +210 -0
- package/dist/index.js +79 -0
- package/dist/index.mjs +1364 -0
- package/dist/typeorm.d.ts +26 -0
- package/dist/typeorm.js +1 -0
- package/dist/typeorm.mjs +15 -0
- package/examples/Only CLS/package-lock.json +3140 -0
- package/examples/Only CLS/package.json +25 -0
- package/examples/Only CLS/src/employee.test.ts +109 -0
- package/examples/Only CLS/src/entities/Employee.entity.ts +27 -0
- package/examples/Only CLS/src/index.ts +45 -0
- package/examples/Only CLS/tsconfig.json +17 -0
- package/examples/Only CLS/vite.config.ts +8 -0
- package/examples/Only RLS/package-lock.json +3140 -0
- package/examples/Only RLS/package.json +25 -0
- package/examples/Only RLS/src/entities/Note.entity.ts +40 -0
- package/examples/Only RLS/src/entities/User.entity.ts +29 -0
- package/examples/Only RLS/src/index.ts +51 -0
- package/examples/Only RLS/src/notes.test.ts +123 -0
- package/examples/Only RLS/tsconfig.json +17 -0
- package/examples/Only RLS/vite.config.ts +8 -0
- package/examples/RBAC + CLS + RLS/package-lock.json +3140 -0
- package/examples/RBAC + CLS + RLS/package.json +21 -0
- package/examples/RBAC + CLS + RLS/src/entities/Project.entity.ts +47 -0
- package/examples/RBAC + CLS + RLS/src/entities/User.entity.ts +31 -0
- package/examples/RBAC + CLS + RLS/src/entities/UserRole.entity.ts +110 -0
- package/examples/RBAC + CLS + RLS/src/index.ts +42 -0
- package/examples/RBAC + CLS + RLS/src/project.test.ts +117 -0
- package/examples/RBAC + CLS + RLS/tsconfig.json +17 -0
- package/examples/RBAC + CLS + RLS/vite.config.ts +8 -0
- package/examples/Without Security/package-lock.json +3940 -0
- package/examples/Without Security/package.json +25 -0
- package/examples/Without Security/src/entities/Pharmacy.entity.ts +20 -0
- package/examples/Without Security/src/index.ts +52 -0
- package/examples/Without Security/src/pharmacy.test.ts +104 -0
- package/examples/Without Security/tsconfig.json +17 -0
- package/examples/Without Security/vite.config.ts +8 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# SupaForge
|
|
2
|
+
|
|
3
|
+
Advanced security integration framework for TypeORM and Supabase. SupaForge provides highly deterministic compilation of PostgreSQL Policies, Roles, and Privileges inferred organically from your application-tier schema entities.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start (CLI)
|
|
6
|
+
|
|
7
|
+
The easiest way to start a new project with SupaForge is to use our interactive CLI:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx supaforge
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This will guide you through:
|
|
14
|
+
1. Naming your project.
|
|
15
|
+
2. Selecting a template (`basic`, `backend`, or `frontend`).
|
|
16
|
+
3. Automatically running `npm install`.
|
|
17
|
+
|
|
18
|
+
## 📦 Library Usage
|
|
19
|
+
|
|
20
|
+
You can also install SupaForge as a dependency in your existing project:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install supaforge
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Core Concepts
|
|
27
|
+
|
|
28
|
+
SupaForge uses decorators to define security policies directly on your TypeORM entities. It then translates these into robust PostgreSQL RLS (Row-Level Security) and CLS (Column-Level Security) policies.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { Security, Policy, Protect } from 'supaforge';
|
|
32
|
+
|
|
33
|
+
@Security({
|
|
34
|
+
policies: [
|
|
35
|
+
{ name: 'Allow select for authenticated', role: 'authenticated', check: 'SELECT' }
|
|
36
|
+
]
|
|
37
|
+
})
|
|
38
|
+
@Protect()
|
|
39
|
+
export class User {
|
|
40
|
+
// ...
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Core Functions
|
|
45
|
+
|
|
46
|
+
- **`runSupaforge()`**: The main orchestrator that analyzes your entities and synchronizes your Supabase instance with the generated policies.
|
|
47
|
+
- **`buildSecuritySql()`**: Generates pure PostgreSQL SQL for policies, roles, and grants based on your decorators.
|
|
48
|
+
- **`introspectEntities()`**: Analyzes TypeORM metadata to find `@Security` and `@Protect` decorators.
|
|
49
|
+
- **`createMigrationTemplate()`**: Scaffolds a new migration file with both SQL and TypeScript wrappers.
|
|
50
|
+
|
|
51
|
+
### How the CLI Works
|
|
52
|
+
The CLI (`npx supaforge`) is designed for instant scaffolding:
|
|
53
|
+
1. **Templates**: Uses the `examples/` directory inside the package as blueprints (`basic`, `backend`, `frontend`).
|
|
54
|
+
2. **Project Setup**: Copies the selected template to your chosen folder and updates the `package.json` with your custom project name.
|
|
55
|
+
3. **Dependency Sync**: Offers to run `npm install` automatically so you can start working right away.
|
|
56
|
+
|
|
57
|
+
## 🛠️ Development
|
|
58
|
+
|
|
59
|
+
If you are contributing to SupaForge:
|
|
60
|
+
- `npm run build`: Build the core library.
|
|
61
|
+
- `npm run cli`: Run the CLI locally.
|
|
62
|
+
|
|
63
|
+
## 📄 License
|
|
64
|
+
|
|
65
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
|
|
9
|
+
function showHelp() {
|
|
10
|
+
console.log(chalk.blue.bold('\nSupaforge CLI - Help'));
|
|
11
|
+
console.log(`
|
|
12
|
+
Usage:
|
|
13
|
+
npx supaforge [options]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-h, --help Show this help message
|
|
17
|
+
(interactive) If no options are provided, interactive project setup will start.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
${chalk.cyan('Only CLS')} - Example with only Column Level Security
|
|
21
|
+
${chalk.cyan('Only RLS')} - Example with only Row Level Security
|
|
22
|
+
${chalk.cyan('RBAC + CLS + RLS')} - Comprehensive security with Roles, CLS and RLS
|
|
23
|
+
${chalk.cyan('Without Security')} - Basic CRUD without security layers
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
// Check for help flag
|
|
29
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
30
|
+
showHelp();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(chalk.blue.bold('Supaforge v0.1.0'));
|
|
35
|
+
|
|
36
|
+
const choices = [
|
|
37
|
+
'Only CLS',
|
|
38
|
+
'Only RLS',
|
|
39
|
+
'RBAC + CLS + RLS',
|
|
40
|
+
'Without Security'
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const answers = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
name: 'projectName',
|
|
47
|
+
message: 'What is your project name?',
|
|
48
|
+
default: 'my-supaforge-app',
|
|
49
|
+
validate: (input) => {
|
|
50
|
+
if (/^([A-Za-z\-\\_\d])+$/.test(input)) return true;
|
|
51
|
+
return 'Project name may only include letters, numbers, underscores and hashes.';
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'list',
|
|
56
|
+
name: 'example',
|
|
57
|
+
message: 'Which template would you like to use?',
|
|
58
|
+
choices: choices
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'confirm',
|
|
62
|
+
name: 'runInstall',
|
|
63
|
+
message: 'Would you like to run "npm install" now?',
|
|
64
|
+
default: true
|
|
65
|
+
}
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const { projectName, example, runInstall } = answers;
|
|
69
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
70
|
+
const exampleDir = path.join(__dirname, '..', 'examples', example);
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(targetDir)) {
|
|
73
|
+
console.error(chalk.red(`\nError: Directory "${projectName}" already exists. Please choose a different name.`));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.cyan(`\nScaffolding project in ${targetDir}...`));
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Check if example exists
|
|
81
|
+
if (!fs.existsSync(exampleDir)) {
|
|
82
|
+
console.error(chalk.red(`\nError: Template "${example}" does not exist in ${exampleDir}`));
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await fs.copy(exampleDir, targetDir);
|
|
87
|
+
|
|
88
|
+
// Update package.json name if it exists
|
|
89
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
90
|
+
if (await fs.pathExists(pkgPath)) {
|
|
91
|
+
const pkg = await fs.readJson(pkgPath);
|
|
92
|
+
pkg.name = projectName;
|
|
93
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(chalk.green(`\nProject "${projectName}" created successfully!`));
|
|
97
|
+
|
|
98
|
+
if (runInstall) {
|
|
99
|
+
console.log(chalk.yellow(`\nRunning npm install in ${projectName}...`));
|
|
100
|
+
try {
|
|
101
|
+
execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
|
|
102
|
+
console.log(chalk.green(`\nAll dependencies installed!`));
|
|
103
|
+
} catch (err) {
|
|
104
|
+
console.error(chalk.red('\nnpm install failed. You may need to run it manually.'));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(chalk.blue.bold(`\nHappy coding! CD into ${projectName} to get started.\n`));
|
|
109
|
+
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red('\nError during project creation:'), error);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { DataSource } from 'typeorm';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Executes previously generated SQL migrations from the output directory.
|
|
6
|
+
*/
|
|
7
|
+
export declare function applyGeneratedMigrations(options: RunnerOptions): Promise<void>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds a valid TypeORM Migration TS file wrapping the SQL payload
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildMigrationFile(className: string, upSql: string, downSql?: string): string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Main function to sequence full security SQL layout
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildSecuritySql(entities: EntitySecurity[], options?: {
|
|
18
|
+
cls?: boolean;
|
|
19
|
+
rls?: boolean;
|
|
20
|
+
}): string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Property decorator for Column Level Security (CLS).
|
|
24
|
+
* @param rules An object mapping roles to permission strings, e.g. { anon: 'r', authenticated: 'ru' }
|
|
25
|
+
*/
|
|
26
|
+
export declare function cls(rules: Record<string, string> | Array<Record<string, string>>): PropertyDecorator;
|
|
27
|
+
|
|
28
|
+
export declare interface CLSRule {
|
|
29
|
+
role: string;
|
|
30
|
+
permissions: CrudOp[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export declare interface CLSSecurity {
|
|
34
|
+
propertyKey: string;
|
|
35
|
+
databaseName: string;
|
|
36
|
+
rules: CLSRule[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Creates a blank structural TypeORM shell.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createMigrationTemplate(options: RunnerOptions): void;
|
|
43
|
+
|
|
44
|
+
/** Supported core operations for Supabase RLS/CLS */
|
|
45
|
+
export declare const CRUD_OPS: readonly ["c", "r", "u", "d"];
|
|
46
|
+
|
|
47
|
+
export declare type CrudOp = (typeof CRUD_OPS)[number];
|
|
48
|
+
|
|
49
|
+
export declare interface CustomSqlDefinition {
|
|
50
|
+
name: string;
|
|
51
|
+
definition: () => string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare type DbConfig = z.infer<typeof DbConfigSchema>;
|
|
55
|
+
|
|
56
|
+
export declare const DbConfigSchema: z.ZodObject<{
|
|
57
|
+
host: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
58
|
+
port: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
59
|
+
user: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
60
|
+
password: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
61
|
+
database: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
62
|
+
}, "strip", z.ZodTypeAny, {
|
|
63
|
+
host: string;
|
|
64
|
+
port: number;
|
|
65
|
+
user: string;
|
|
66
|
+
password: string;
|
|
67
|
+
database: string;
|
|
68
|
+
}, {
|
|
69
|
+
host?: string | undefined;
|
|
70
|
+
port?: number | undefined;
|
|
71
|
+
user?: string | undefined;
|
|
72
|
+
password?: string | undefined;
|
|
73
|
+
database?: string | undefined;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
export declare interface DocGenColumn {
|
|
77
|
+
name: string;
|
|
78
|
+
databaseName: string;
|
|
79
|
+
type: string;
|
|
80
|
+
isText: boolean;
|
|
81
|
+
length?: string | number;
|
|
82
|
+
isUnique: boolean;
|
|
83
|
+
isNullable: boolean;
|
|
84
|
+
isPrimary: boolean;
|
|
85
|
+
cls: CLSRule[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export declare interface DocGenEntity {
|
|
89
|
+
entityName: string;
|
|
90
|
+
tableName: string;
|
|
91
|
+
columns: DocGenColumn[];
|
|
92
|
+
rls: DocGenRLS[];
|
|
93
|
+
runSql: DocGenRunSQL[];
|
|
94
|
+
checks: {
|
|
95
|
+
name: string;
|
|
96
|
+
expression: string;
|
|
97
|
+
}[];
|
|
98
|
+
isSecurityEnabled: boolean;
|
|
99
|
+
securitySql: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export declare interface DocGenOutput {
|
|
103
|
+
documentation: string;
|
|
104
|
+
entities: DocGenEntity[];
|
|
105
|
+
generatedAt: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export declare interface DocGenRLS {
|
|
109
|
+
name: string;
|
|
110
|
+
crud: CrudOp[];
|
|
111
|
+
role: string | string[];
|
|
112
|
+
expression: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export declare interface DocGenRunSQL {
|
|
116
|
+
name: string;
|
|
117
|
+
sql: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export declare interface EntitySecurity {
|
|
121
|
+
tableName: string;
|
|
122
|
+
entityName: string;
|
|
123
|
+
policies: RLSPolicy[];
|
|
124
|
+
columns: CLSSecurity[];
|
|
125
|
+
customSql: CustomSqlDefinition[];
|
|
126
|
+
isSecurityEnabled: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Utility to extract runner flags from command line arguments (e.g. process.argv)
|
|
131
|
+
*/
|
|
132
|
+
export declare function extractCliOptions(args?: string[]): Partial<RunnerOptions>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generates a JSON documentation of entities, their properties, and security policies.
|
|
136
|
+
*/
|
|
137
|
+
export declare function generateDocumentation(options: RunnerOptions): Promise<void>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Extracts Supaforge security metadata from TypeORM loaded entities.
|
|
141
|
+
*/
|
|
142
|
+
export declare function introspectEntities(dataSource: DataSource): EntitySecurity[];
|
|
143
|
+
|
|
144
|
+
/** Schema for validating permission strings (e.g. "cru") */
|
|
145
|
+
export declare const PermissionsSchema: z.ZodEffects<z.ZodString, ("c" | "r" | "u" | "d")[], string>;
|
|
146
|
+
|
|
147
|
+
export declare interface PolicyResult {
|
|
148
|
+
role: string | string[];
|
|
149
|
+
expression: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Registry to retrieve metadata safely attached by decorators
|
|
154
|
+
*/
|
|
155
|
+
export declare const registry: {
|
|
156
|
+
isSecurityEnabled(target: new (...args: unknown[]) => unknown): boolean;
|
|
157
|
+
getRLS(target: new (...args: unknown[]) => unknown): RLSPolicy[];
|
|
158
|
+
getCLS(target: new (...args: unknown[]) => unknown): CLSSecurity[];
|
|
159
|
+
getCustomSql(target: new (...args: unknown[]) => unknown): CustomSqlDefinition[];
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Method decorator to attach an RLS policy.
|
|
164
|
+
* @param permissions String representation of CRUD ops: "c", "r", "u", "d"
|
|
165
|
+
*/
|
|
166
|
+
export declare function rls(permissions: string): MethodDecorator;
|
|
167
|
+
|
|
168
|
+
export declare interface RLSPolicy {
|
|
169
|
+
name: string;
|
|
170
|
+
crud: CrudOp[];
|
|
171
|
+
definition: () => PolicyResult;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Executes a query as a specific Supabase role.
|
|
176
|
+
* Useful for testing RLS and CLS policies natively within automated tests.
|
|
177
|
+
*/
|
|
178
|
+
export declare function runAsRole(querier: {
|
|
179
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
180
|
+
rows: Record<string, unknown>[];
|
|
181
|
+
}>;
|
|
182
|
+
}, role: string | string[], uid: string, query: string, values?: unknown[]): Promise<Record<string, unknown>[]>;
|
|
183
|
+
|
|
184
|
+
export declare interface RunnerOptions {
|
|
185
|
+
dbConfig: DbConfig;
|
|
186
|
+
entityPath: string;
|
|
187
|
+
outputDir: string;
|
|
188
|
+
apply?: boolean;
|
|
189
|
+
syncSchema?: boolean;
|
|
190
|
+
migrationName?: string;
|
|
191
|
+
cls?: boolean;
|
|
192
|
+
rls?: boolean;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Method decorator for injecting manual custom SQL.
|
|
197
|
+
*/
|
|
198
|
+
export declare function runSql(): MethodDecorator;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Core command runner to analyze security posture and build migrations.
|
|
202
|
+
*/
|
|
203
|
+
export declare function runSupaforge(options: RunnerOptions): Promise<string>;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Class decorator to enable security policies on an entity.
|
|
207
|
+
*/
|
|
208
|
+
export declare function security(): ClassDecorator;
|
|
209
|
+
|
|
210
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require("reflect-metadata");const y=require("chalk"),de=require("typeorm"),Me=require("pg"),Pe=require("fs"),ke=require("path"),I=require("zod");function ye(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const i=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,i.get?i:{enumerable:!0,get:()=>e[n]})}}return r.default=e,Object.freeze(r)}const g=ye(Pe),O=ye(ke),R={security:Symbol("supaforge:security"),rls:Symbol("supaforge:rls"),cls:Symbol("supaforge:cls"),runSql:Symbol("supaforge:runsql")};function ge(e){const r=e.toLowerCase().trim().split(""),n=["c","r","u","d"],i=r.filter(o=>!n.includes(o));if(i.length>0)throw new Error(`Invalid permissions: ${i.join(", ")}. Expected only ${n.join(", ")}.`);const t=[...new Set(r)].sort((o,a)=>n.indexOf(o)-n.indexOf(a));return t.includes("u")&&!t.includes("r")&&console.warn(y.yellow("Security Audit: 'u' permission found without 'r' on a security rule. In PostgreSQL, updates generally require select permissions on the columns used in the WHERE clause. If this is intentional (e.g., blinded updates), ensure your RLS/CLS allows it.")),t}function qe(){return e=>{Reflect.defineMetadata(R.security,!0,e)}}function Ue(e){return(r,n)=>{const i=ge(e),t=typeof r=="function"?r:r.constructor,o=Reflect.getMetadata(R.rls,t)??[];o.push({name:String(n),crud:i,definition:r[n]}),Reflect.defineMetadata(R.rls,o,t)}}function Ye(e){return(r,n)=>{const t=(Array.isArray(e)?e:[e]).flatMap(l=>Object.entries(l).map(([s,u])=>({role:s,permissions:ge(u)}))),o=r.constructor,a=Reflect.getMetadata(R.cls,o)??[];a.push({propertyKey:String(n),databaseName:"",rules:t}),Reflect.defineMetadata(R.cls,a,o)}}function je(){return(e,r)=>{const n=typeof e=="function"?e:e.constructor,i=Reflect.getMetadata(R.runSql,n)??[];i.push({name:String(r),definition:e[r]}),Reflect.defineMetadata(R.runSql,i,n)}}const w={isSecurityEnabled(e){return Reflect.getMetadata(R.security,e)===!0},getRLS(e){return Reflect.getMetadata(R.rls,e)??[]},getCLS(e){return Reflect.getMetadata(R.cls,e)??[]},getCustomSql(e){return Reflect.getMetadata(R.runSql,e)??[]}},z=["anon","authenticated","service_role","postgres","supabase_admin","authenticator"],He=`
|
|
2
|
+
CREATE OR REPLACE FUNCTION supaforge_create_role(target_role_name text)
|
|
3
|
+
RETURNS void
|
|
4
|
+
LANGUAGE plpgsql
|
|
5
|
+
AS $$
|
|
6
|
+
BEGIN
|
|
7
|
+
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = target_role_name) THEN
|
|
8
|
+
EXECUTE format('CREATE ROLE %I', target_role_name);
|
|
9
|
+
END IF;
|
|
10
|
+
|
|
11
|
+
EXECUTE format('GRANT %I TO postgres', target_role_name);
|
|
12
|
+
|
|
13
|
+
IF EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'authenticated') THEN
|
|
14
|
+
EXECUTE format('REVOKE authenticated FROM %I', target_role_name);
|
|
15
|
+
END IF;
|
|
16
|
+
|
|
17
|
+
IF EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'authenticator') THEN
|
|
18
|
+
EXECUTE format('GRANT %I TO authenticator', target_role_name);
|
|
19
|
+
END IF;
|
|
20
|
+
|
|
21
|
+
EXECUTE format('GRANT USAGE ON SCHEMA public TO %I', target_role_name);
|
|
22
|
+
EXECUTE format('GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO %I', target_role_name);
|
|
23
|
+
END;
|
|
24
|
+
$$;
|
|
25
|
+
`.trim(),he={c:"INSERT",r:"SELECT",u:"UPDATE",d:"DELETE"},F=e=>`"${e.replace(/"/g,'""')}"`,L=e=>`'${e.replace(/'/g,"''")}'`;function P(e){return z.includes(e.toLowerCase())}function Be(e){const r=[],n=`public.${F(e.tableName)}`,i={};for(const t of e.columns)for(const o of t.rules){const a=o.role;Object.prototype.hasOwnProperty.call(i,a)||(i[a]={c:new Set,r:new Set,u:new Set,d:new Set});for(const l of o.permissions)i[a][l].add(t.databaseName)}for(const[t,o]of Object.entries(i)){const a=F(t);for(const[l,s]of Object.entries(o)){if(s.size===0)continue;const u=he[l];if(l==="d")r.push(`GRANT DELETE ON ${n} TO ${a};`);else{const f=Array.from(s).map(F).join(", ");r.push(`GRANT ${u} (${f}) ON ${n} TO ${a};`)}}}return r}function Ke(e){const r=[],n=`public.${F(e.tableName)}`;for(const i of e.policies){const{role:t,expression:o}=i.definition(),a=Array.isArray(t)?t:[t],l=a.map(F).join(", "),s=Array.isArray(t)?t.join("_"):t;for(const u of i.crud){const f=he[u],p=F(`supaforge_${e.tableName}_${s}_${u}_${i.name}`);r.push(`DROP POLICY IF EXISTS ${p} ON ${n};`);const c=`CREATE POLICY ${p} ON ${n} FOR ${f} TO ${l}`;let d=`(${o})`;if(a.some(h=>!P(h))){const h=a.filter(A=>!P(A)).map(A=>`(pg_has_role(current_user, ${L(A)}, 'MEMBER') AND (coalesce(auth.jwt() ->> 'user_role', 'none') = ${L(A)} OR (auth.jwt() -> 'user_roles') ? ${L(A)}))`);d=`((${[...a.filter(A=>P(A)).map(A=>`pg_has_role(current_user, ${L(A)}, 'MEMBER')`),...h].join(" OR ")}) AND ${d})`}u==="c"?r.push(`${c} WITH CHECK (${d});`):u==="u"?r.push(`${c} USING (${d}) WITH CHECK (${d});`):r.push(`${c} USING (${d});`)}}return r}function ee(e,r={}){const{cls:n=!0,rls:i=!0}=r,t=[];t.push("-- Supaforge Security Migration"),t.push(""),t.push(He),t.push("");const o=new Set;for(const a of e){for(const l of a.columns)for(const s of l.rules)P(s.role)||o.add(s.role);for(const l of a.policies){const{role:s}=l.definition();Array.isArray(s)?s.forEach(u=>{P(u)||o.add(u)}):P(s)||o.add(s)}}if(o.size>0){t.push("-- Create custom security roles");for(const a of o)t.push(`SELECT supaforge_create_role(${L(a)});`);t.push("")}for(const a of e){t.push(`-- Table: ${a.tableName}`);const l=`public.${F(a.tableName)}`;i&&a.isSecurityEnabled?t.push(`ALTER TABLE ${l} ENABLE ROW LEVEL SECURITY;`):i||t.push(`ALTER TABLE ${l} DISABLE ROW LEVEL SECURITY;`),n&&t.push(`REVOKE ALL ON TABLE ${l} FROM anon, authenticated, public;`),t.push(`
|
|
26
|
+
DO $$ DECLARE pol RECORD;
|
|
27
|
+
BEGIN
|
|
28
|
+
FOR pol IN (
|
|
29
|
+
SELECT policyname FROM pg_policies
|
|
30
|
+
WHERE schemaname = 'public'
|
|
31
|
+
AND tablename = ${L(a.tableName)}
|
|
32
|
+
AND policyname LIKE 'supaforge_%'
|
|
33
|
+
) LOOP
|
|
34
|
+
EXECUTE format('DROP POLICY IF EXISTS %I ON %s', pol.policyname, ${L(l)});
|
|
35
|
+
END LOOP;
|
|
36
|
+
END $$;`.trim()),n&&t.push(...Be(a)),i&&t.push(...Ke(a));for(const s of a.customSql)t.push(`-- Custom SQL: ${s.name}`),t.push(s.definition());t.push("")}return t.join(`
|
|
37
|
+
`)}function re(e,r,n="-- Revert logic generally requires manual review"){return['import { MigrationInterface, QueryRunner } from "typeorm";',"",`export class ${e} implements MigrationInterface {`,` name = '${e}'`,""," public async up(queryRunner: QueryRunner): Promise<void> {"," await queryRunner.query(`",r.replace(/`/g,"\\\\`")," `);"," }",""," public async down(queryRunner: QueryRunner): Promise<void> {"," await queryRunner.query(`",n.replace(/`/g,"\\\\`")," `);"," }","}"].join(`
|
|
38
|
+
`)}/*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */function Ee(e){return typeof e>"u"||e===null}function Ge(e){return typeof e=="object"&&e!==null}function ze(e){return Array.isArray(e)?e:Ee(e)?[]:[e]}function Qe(e,r){var n,i,t,o;if(r)for(o=Object.keys(r),n=0,i=o.length;n<i;n+=1)t=o[n],e[t]=r[t];return e}function We(e,r){var n="",i;for(i=0;i<r;i+=1)n+=e;return n}function Ve(e){return e===0&&Number.NEGATIVE_INFINITY===1/e}var Xe=Ee,Ze=Ge,Je=ze,er=We,rr=Ve,nr=Qe,$={isNothing:Xe,isObject:Ze,toArray:Je,repeat:er,isNegativeZero:rr,extend:nr};function Se(e,r){var n="",i=e.reason||"(unknown reason)";return e.mark?(e.mark.name&&(n+='in "'+e.mark.name+'" '),n+="("+(e.mark.line+1)+":"+(e.mark.column+1)+")",!r&&e.mark.snippet&&(n+=`
|
|
39
|
+
|
|
40
|
+
`+e.mark.snippet),i+" "+n):i}function Y(e,r){Error.call(this),this.name="YAMLException",this.reason=e,this.mark=r,this.message=Se(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}Y.prototype=Object.create(Error.prototype);Y.prototype.constructor=Y;Y.prototype.toString=function(r){return this.name+": "+Se(this,r)};var N=Y,tr=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],ir=["scalar","sequence","mapping"];function ar(e){var r={};return e!==null&&Object.keys(e).forEach(function(n){e[n].forEach(function(i){r[String(i)]=n})}),r}function or(e,r){if(r=r||{},Object.keys(r).forEach(function(n){if(tr.indexOf(n)===-1)throw new N('Unknown option "'+n+'" is met in definition of "'+e+'" YAML type.')}),this.options=r,this.tag=e,this.kind=r.kind||null,this.resolve=r.resolve||function(){return!0},this.construct=r.construct||function(n){return n},this.instanceOf=r.instanceOf||null,this.predicate=r.predicate||null,this.represent=r.represent||null,this.representName=r.representName||null,this.defaultStyle=r.defaultStyle||null,this.multi=r.multi||!1,this.styleAliases=ar(r.styleAliases||null),ir.indexOf(this.kind)===-1)throw new N('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}var S=or;function ie(e,r){var n=[];return e[r].forEach(function(i){var t=n.length;n.forEach(function(o,a){o.tag===i.tag&&o.kind===i.kind&&o.multi===i.multi&&(t=a)}),n[t]=i}),n}function lr(){var e={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}},r,n;function i(t){t.multi?(e.multi[t.kind].push(t),e.multi.fallback.push(t)):e[t.kind][t.tag]=e.fallback[t.tag]=t}for(r=0,n=arguments.length;r<n;r+=1)arguments[r].forEach(i);return e}function Q(e){return this.extend(e)}Q.prototype.extend=function(r){var n=[],i=[];if(r instanceof S)i.push(r);else if(Array.isArray(r))i=i.concat(r);else if(r&&(Array.isArray(r.implicit)||Array.isArray(r.explicit)))r.implicit&&(n=n.concat(r.implicit)),r.explicit&&(i=i.concat(r.explicit));else throw new N("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");n.forEach(function(o){if(!(o instanceof S))throw new N("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(o.loadKind&&o.loadKind!=="scalar")throw new N("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(o.multi)throw new N("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")}),i.forEach(function(o){if(!(o instanceof S))throw new N("Specified list of YAML types (or a single Type object) contains a non-Type object.")});var t=Object.create(Q.prototype);return t.implicit=(this.implicit||[]).concat(n),t.explicit=(this.explicit||[]).concat(i),t.compiledImplicit=ie(t,"implicit"),t.compiledExplicit=ie(t,"explicit"),t.compiledTypeMap=lr(t.compiledImplicit,t.compiledExplicit),t};var sr=Q,ur=new S("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return e!==null?e:""}}),cr=new S("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return e!==null?e:[]}}),fr=new S("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return e!==null?e:{}}}),pr=new sr({explicit:[ur,cr,fr]});function mr(e){if(e===null)return!0;var r=e.length;return r===1&&e==="~"||r===4&&(e==="null"||e==="Null"||e==="NULL")}function dr(){return null}function yr(e){return e===null}var gr=new S("tag:yaml.org,2002:null",{kind:"scalar",resolve:mr,construct:dr,predicate:yr,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"});function hr(e){if(e===null)return!1;var r=e.length;return r===4&&(e==="true"||e==="True"||e==="TRUE")||r===5&&(e==="false"||e==="False"||e==="FALSE")}function Er(e){return e==="true"||e==="True"||e==="TRUE"}function Sr(e){return Object.prototype.toString.call(e)==="[object Boolean]"}var xr=new S("tag:yaml.org,2002:bool",{kind:"scalar",resolve:hr,construct:Er,predicate:Sr,represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"});function vr(e){return 48<=e&&e<=57||65<=e&&e<=70||97<=e&&e<=102}function Ar(e){return 48<=e&&e<=55}function Nr(e){return 48<=e&&e<=57}function Rr(e){if(e===null)return!1;var r=e.length,n=0,i=!1,t;if(!r)return!1;if(t=e[n],(t==="-"||t==="+")&&(t=e[++n]),t==="0"){if(n+1===r)return!0;if(t=e[++n],t==="b"){for(n++;n<r;n++)if(t=e[n],t!=="_"){if(t!=="0"&&t!=="1")return!1;i=!0}return i&&t!=="_"}if(t==="x"){for(n++;n<r;n++)if(t=e[n],t!=="_"){if(!vr(e.charCodeAt(n)))return!1;i=!0}return i&&t!=="_"}if(t==="o"){for(n++;n<r;n++)if(t=e[n],t!=="_"){if(!Ar(e.charCodeAt(n)))return!1;i=!0}return i&&t!=="_"}}if(t==="_")return!1;for(;n<r;n++)if(t=e[n],t!=="_"){if(!Nr(e.charCodeAt(n)))return!1;i=!0}return!(!i||t==="_")}function wr(e){var r=e,n=1,i;if(r.indexOf("_")!==-1&&(r=r.replace(/_/g,"")),i=r[0],(i==="-"||i==="+")&&(i==="-"&&(n=-1),r=r.slice(1),i=r[0]),r==="0")return 0;if(i==="0"){if(r[1]==="b")return n*parseInt(r.slice(2),2);if(r[1]==="x")return n*parseInt(r.slice(2),16);if(r[1]==="o")return n*parseInt(r.slice(2),8)}return n*parseInt(r,10)}function _r(e){return Object.prototype.toString.call(e)==="[object Number]"&&e%1===0&&!$.isNegativeZero(e)}var Tr=new S("tag:yaml.org,2002:int",{kind:"scalar",resolve:Rr,construct:wr,predicate:_r,represent:{binary:function(e){return e>=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),Or=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");function Cr(e){return!(e===null||!Or.test(e)||e[e.length-1]==="_")}function br(e){var r,n;return r=e.replace(/_/g,"").toLowerCase(),n=r[0]==="-"?-1:1,"+-".indexOf(r[0])>=0&&(r=r.slice(1)),r===".inf"?n===1?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:r===".nan"?NaN:n*parseFloat(r,10)}var Ir=/^[-+]?[0-9]+e/;function Lr(e,r){var n;if(isNaN(e))switch(r){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(r){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(r){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if($.isNegativeZero(e))return"-0.0";return n=e.toString(10),Ir.test(n)?n.replace("e",".e"):n}function Fr(e){return Object.prototype.toString.call(e)==="[object Number]"&&(e%1!==0||$.isNegativeZero(e))}var $r=new S("tag:yaml.org,2002:float",{kind:"scalar",resolve:Cr,construct:br,predicate:Fr,represent:Lr,defaultStyle:"lowercase"}),Dr=pr.extend({implicit:[gr,xr,Tr,$r]}),Mr=Dr,xe=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),ve=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");function Pr(e){return e===null?!1:xe.exec(e)!==null||ve.exec(e)!==null}function kr(e){var r,n,i,t,o,a,l,s=0,u=null,f,p,c;if(r=xe.exec(e),r===null&&(r=ve.exec(e)),r===null)throw new Error("Date resolve error");if(n=+r[1],i=+r[2]-1,t=+r[3],!r[4])return new Date(Date.UTC(n,i,t));if(o=+r[4],a=+r[5],l=+r[6],r[7]){for(s=r[7].slice(0,3);s.length<3;)s+="0";s=+s}return r[9]&&(f=+r[10],p=+(r[11]||0),u=(f*60+p)*6e4,r[9]==="-"&&(u=-u)),c=new Date(Date.UTC(n,i,t,o,a,l,s)),u&&c.setTime(c.getTime()-u),c}function qr(e){return e.toISOString()}var Ur=new S("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:Pr,construct:kr,instanceOf:Date,represent:qr});function Yr(e){return e==="<<"||e===null}var jr=new S("tag:yaml.org,2002:merge",{kind:"scalar",resolve:Yr}),ne=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
|
|
41
|
+
\r`;function Hr(e){if(e===null)return!1;var r,n,i=0,t=e.length,o=ne;for(n=0;n<t;n++)if(r=o.indexOf(e.charAt(n)),!(r>64)){if(r<0)return!1;i+=6}return i%8===0}function Br(e){var r,n,i=e.replace(/[\r\n=]/g,""),t=i.length,o=ne,a=0,l=[];for(r=0;r<t;r++)r%4===0&&r&&(l.push(a>>16&255),l.push(a>>8&255),l.push(a&255)),a=a<<6|o.indexOf(i.charAt(r));return n=t%4*6,n===0?(l.push(a>>16&255),l.push(a>>8&255),l.push(a&255)):n===18?(l.push(a>>10&255),l.push(a>>2&255)):n===12&&l.push(a>>4&255),new Uint8Array(l)}function Kr(e){var r="",n=0,i,t,o=e.length,a=ne;for(i=0;i<o;i++)i%3===0&&i&&(r+=a[n>>18&63],r+=a[n>>12&63],r+=a[n>>6&63],r+=a[n&63]),n=(n<<8)+e[i];return t=o%3,t===0?(r+=a[n>>18&63],r+=a[n>>12&63],r+=a[n>>6&63],r+=a[n&63]):t===2?(r+=a[n>>10&63],r+=a[n>>4&63],r+=a[n<<2&63],r+=a[64]):t===1&&(r+=a[n>>2&63],r+=a[n<<4&63],r+=a[64],r+=a[64]),r}function Gr(e){return Object.prototype.toString.call(e)==="[object Uint8Array]"}var zr=new S("tag:yaml.org,2002:binary",{kind:"scalar",resolve:Hr,construct:Br,predicate:Gr,represent:Kr}),Qr=Object.prototype.hasOwnProperty,Wr=Object.prototype.toString;function Vr(e){if(e===null)return!0;var r=[],n,i,t,o,a,l=e;for(n=0,i=l.length;n<i;n+=1){if(t=l[n],a=!1,Wr.call(t)!=="[object Object]")return!1;for(o in t)if(Qr.call(t,o))if(!a)a=!0;else return!1;if(!a)return!1;if(r.indexOf(o)===-1)r.push(o);else return!1}return!0}function Xr(e){return e!==null?e:[]}var Zr=new S("tag:yaml.org,2002:omap",{kind:"sequence",resolve:Vr,construct:Xr}),Jr=Object.prototype.toString;function en(e){if(e===null)return!0;var r,n,i,t,o,a=e;for(o=new Array(a.length),r=0,n=a.length;r<n;r+=1){if(i=a[r],Jr.call(i)!=="[object Object]"||(t=Object.keys(i),t.length!==1))return!1;o[r]=[t[0],i[t[0]]]}return!0}function rn(e){if(e===null)return[];var r,n,i,t,o,a=e;for(o=new Array(a.length),r=0,n=a.length;r<n;r+=1)i=a[r],t=Object.keys(i),o[r]=[t[0],i[t[0]]];return o}var nn=new S("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:en,construct:rn}),tn=Object.prototype.hasOwnProperty;function an(e){if(e===null)return!0;var r,n=e;for(r in n)if(tn.call(n,r)&&n[r]!==null)return!1;return!0}function on(e){return e!==null?e:{}}var ln=new S("tag:yaml.org,2002:set",{kind:"mapping",resolve:an,construct:on}),sn=Mr.extend({implicit:[Ur,jr],explicit:[zr,Zr,nn,ln]});function ae(e){return e===48?"\0":e===97?"\x07":e===98?"\b":e===116||e===9?" ":e===110?`
|
|
42
|
+
`:e===118?"\v":e===102?"\f":e===114?"\r":e===101?"\x1B":e===32?" ":e===34?'"':e===47?"/":e===92?"\\":e===78?"
":e===95?" ":e===76?"\u2028":e===80?"\u2029":""}var un=new Array(256),cn=new Array(256);for(var D=0;D<256;D++)un[D]=ae(D)?1:0,cn[D]=ae(D);var Ae=Object.prototype.toString,Ne=Object.prototype.hasOwnProperty,te=65279,fn=9,j=10,pn=13,mn=32,dn=33,yn=34,W=35,gn=37,hn=38,En=39,Sn=42,Re=44,xn=45,K=58,vn=61,An=62,Nn=63,Rn=64,we=91,_e=93,wn=96,Te=123,_n=124,Oe=125,x={};x[0]="\\0";x[7]="\\a";x[8]="\\b";x[9]="\\t";x[10]="\\n";x[11]="\\v";x[12]="\\f";x[13]="\\r";x[27]="\\e";x[34]='\\"';x[92]="\\\\";x[133]="\\N";x[160]="\\_";x[8232]="\\L";x[8233]="\\P";var Tn=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],On=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;function Cn(e,r){var n,i,t,o,a,l,s;if(r===null)return{};for(n={},i=Object.keys(r),t=0,o=i.length;t<o;t+=1)a=i[t],l=String(r[a]),a.slice(0,2)==="!!"&&(a="tag:yaml.org,2002:"+a.slice(2)),s=e.compiledTypeMap.fallback[a],s&&Ne.call(s.styleAliases,l)&&(l=s.styleAliases[l]),n[a]=l;return n}function bn(e){var r,n,i;if(r=e.toString(16).toUpperCase(),e<=255)n="x",i=2;else if(e<=65535)n="u",i=4;else if(e<=4294967295)n="U",i=8;else throw new N("code point within a string may not be greater than 0xFFFFFFFF");return"\\"+n+$.repeat("0",i-r.length)+r}var In=1,H=2;function Ln(e){this.schema=e.schema||sn,this.indent=Math.max(1,e.indent||2),this.noArrayIndent=e.noArrayIndent||!1,this.skipInvalid=e.skipInvalid||!1,this.flowLevel=$.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=Cn(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.condenseFlow=e.condenseFlow||!1,this.quotingType=e.quotingType==='"'?H:In,this.forceQuotes=e.forceQuotes||!1,this.replacer=typeof e.replacer=="function"?e.replacer:null,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function oe(e,r){for(var n=$.repeat(" ",r),i=0,t=-1,o="",a,l=e.length;i<l;)t=e.indexOf(`
|
|
43
|
+
`,i),t===-1?(a=e.slice(i),i=l):(a=e.slice(i,t+1),i=t+1),a.length&&a!==`
|
|
44
|
+
`&&(o+=n),o+=a;return o}function V(e,r){return`
|
|
45
|
+
`+$.repeat(" ",e.indent*r)}function Fn(e,r){var n,i,t;for(n=0,i=e.implicitTypes.length;n<i;n+=1)if(t=e.implicitTypes[n],t.resolve(r))return!0;return!1}function G(e){return e===mn||e===fn}function B(e){return 32<=e&&e<=126||161<=e&&e<=55295&&e!==8232&&e!==8233||57344<=e&&e<=65533&&e!==te||65536<=e&&e<=1114111}function le(e){return B(e)&&e!==te&&e!==pn&&e!==j}function se(e,r,n){var i=le(e),t=i&&!G(e);return(n?i:i&&e!==Re&&e!==we&&e!==_e&&e!==Te&&e!==Oe)&&e!==W&&!(r===K&&!t)||le(r)&&!G(r)&&e===W||r===K&&t}function $n(e){return B(e)&&e!==te&&!G(e)&&e!==xn&&e!==Nn&&e!==K&&e!==Re&&e!==we&&e!==_e&&e!==Te&&e!==Oe&&e!==W&&e!==hn&&e!==Sn&&e!==dn&&e!==_n&&e!==vn&&e!==An&&e!==En&&e!==yn&&e!==gn&&e!==Rn&&e!==wn}function Dn(e){return!G(e)&&e!==K}function U(e,r){var n=e.charCodeAt(r),i;return n>=55296&&n<=56319&&r+1<e.length&&(i=e.charCodeAt(r+1),i>=56320&&i<=57343)?(n-55296)*1024+i-56320+65536:n}function Ce(e){var r=/^\n* /;return r.test(e)}var be=1,X=2,Ie=3,Le=4,M=5;function Mn(e,r,n,i,t,o,a,l){var s,u=0,f=null,p=!1,c=!1,d=i!==-1,E=-1,h=$n(U(e,0))&&Dn(U(e,e.length-1));if(r||a)for(s=0;s<e.length;u>=65536?s+=2:s++){if(u=U(e,s),!B(u))return M;h=h&&se(u,f,l),f=u}else{for(s=0;s<e.length;u>=65536?s+=2:s++){if(u=U(e,s),u===j)p=!0,d&&(c=c||s-E-1>i&&e[E+1]!==" ",E=s);else if(!B(u))return M;h=h&&se(u,f,l),f=u}c=c||d&&s-E-1>i&&e[E+1]!==" "}return!p&&!c?h&&!a&&!t(e)?be:o===H?M:X:n>9&&Ce(e)?M:a?o===H?M:X:c?Le:Ie}function Pn(e,r,n,i,t){e.dump=function(){if(r.length===0)return e.quotingType===H?'""':"''";if(!e.noCompatMode&&(Tn.indexOf(r)!==-1||On.test(r)))return e.quotingType===H?'"'+r+'"':"'"+r+"'";var o=e.indent*Math.max(1,n),a=e.lineWidth===-1?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-o),l=i||e.flowLevel>-1&&n>=e.flowLevel;function s(u){return Fn(e,u)}switch(Mn(r,l,e.indent,a,s,e.quotingType,e.forceQuotes&&!i,t)){case be:return r;case X:return"'"+r.replace(/'/g,"''")+"'";case Ie:return"|"+ue(r,e.indent)+ce(oe(r,o));case Le:return">"+ue(r,e.indent)+ce(oe(kn(r,a),o));case M:return'"'+qn(r)+'"';default:throw new N("impossible error: invalid scalar style")}}()}function ue(e,r){var n=Ce(e)?String(r):"",i=e[e.length-1]===`
|
|
46
|
+
`,t=i&&(e[e.length-2]===`
|
|
47
|
+
`||e===`
|
|
48
|
+
`),o=t?"+":i?"":"-";return n+o+`
|
|
49
|
+
`}function ce(e){return e[e.length-1]===`
|
|
50
|
+
`?e.slice(0,-1):e}function kn(e,r){for(var n=/(\n+)([^\n]*)/g,i=function(){var u=e.indexOf(`
|
|
51
|
+
`);return u=u!==-1?u:e.length,n.lastIndex=u,fe(e.slice(0,u),r)}(),t=e[0]===`
|
|
52
|
+
`||e[0]===" ",o,a;a=n.exec(e);){var l=a[1],s=a[2];o=s[0]===" ",i+=l+(!t&&!o&&s!==""?`
|
|
53
|
+
`:"")+fe(s,r),t=o}return i}function fe(e,r){if(e===""||e[0]===" ")return e;for(var n=/ [^ ]/g,i,t=0,o,a=0,l=0,s="";i=n.exec(e);)l=i.index,l-t>r&&(o=a>t?a:l,s+=`
|
|
54
|
+
`+e.slice(t,o),t=o+1),a=l;return s+=`
|
|
55
|
+
`,e.length-t>r&&a>t?s+=e.slice(t,a)+`
|
|
56
|
+
`+e.slice(a+1):s+=e.slice(t),s.slice(1)}function qn(e){for(var r="",n=0,i,t=0;t<e.length;n>=65536?t+=2:t++)n=U(e,t),i=x[n],!i&&B(n)?(r+=e[t],n>=65536&&(r+=e[t+1])):r+=i||bn(n);return r}function Un(e,r,n){var i="",t=e.tag,o,a,l;for(o=0,a=n.length;o<a;o+=1)l=n[o],e.replacer&&(l=e.replacer.call(n,String(o),l)),(_(e,r,l,!1,!1)||typeof l>"u"&&_(e,r,null,!1,!1))&&(i!==""&&(i+=","+(e.condenseFlow?"":" ")),i+=e.dump);e.tag=t,e.dump="["+i+"]"}function pe(e,r,n,i){var t="",o=e.tag,a,l,s;for(a=0,l=n.length;a<l;a+=1)s=n[a],e.replacer&&(s=e.replacer.call(n,String(a),s)),(_(e,r+1,s,!0,!0,!1,!0)||typeof s>"u"&&_(e,r+1,null,!0,!0,!1,!0))&&((!i||t!=="")&&(t+=V(e,r)),e.dump&&j===e.dump.charCodeAt(0)?t+="-":t+="- ",t+=e.dump);e.tag=o,e.dump=t||"[]"}function Yn(e,r,n){var i="",t=e.tag,o=Object.keys(n),a,l,s,u,f;for(a=0,l=o.length;a<l;a+=1)f="",i!==""&&(f+=", "),e.condenseFlow&&(f+='"'),s=o[a],u=n[s],e.replacer&&(u=e.replacer.call(n,s,u)),_(e,r,s,!1,!1)&&(e.dump.length>1024&&(f+="? "),f+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),_(e,r,u,!1,!1)&&(f+=e.dump,i+=f));e.tag=t,e.dump="{"+i+"}"}function jn(e,r,n,i){var t="",o=e.tag,a=Object.keys(n),l,s,u,f,p,c;if(e.sortKeys===!0)a.sort();else if(typeof e.sortKeys=="function")a.sort(e.sortKeys);else if(e.sortKeys)throw new N("sortKeys must be a boolean or a function");for(l=0,s=a.length;l<s;l+=1)c="",(!i||t!=="")&&(c+=V(e,r)),u=a[l],f=n[u],e.replacer&&(f=e.replacer.call(n,u,f)),_(e,r+1,u,!0,!0,!0)&&(p=e.tag!==null&&e.tag!=="?"||e.dump&&e.dump.length>1024,p&&(e.dump&&j===e.dump.charCodeAt(0)?c+="?":c+="? "),c+=e.dump,p&&(c+=V(e,r)),_(e,r+1,f,!0,p)&&(e.dump&&j===e.dump.charCodeAt(0)?c+=":":c+=": ",c+=e.dump,t+=c));e.tag=o,e.dump=t||"{}"}function me(e,r,n){var i,t,o,a,l,s;for(t=n?e.explicitTypes:e.implicitTypes,o=0,a=t.length;o<a;o+=1)if(l=t[o],(l.instanceOf||l.predicate)&&(!l.instanceOf||typeof r=="object"&&r instanceof l.instanceOf)&&(!l.predicate||l.predicate(r))){if(n?l.multi&&l.representName?e.tag=l.representName(r):e.tag=l.tag:e.tag="?",l.represent){if(s=e.styleMap[l.tag]||l.defaultStyle,Ae.call(l.represent)==="[object Function]")i=l.represent(r,s);else if(Ne.call(l.represent,s))i=l.represent[s](r,s);else throw new N("!<"+l.tag+'> tag resolver accepts not "'+s+'" style');e.dump=i}return!0}return!1}function _(e,r,n,i,t,o,a){e.tag=null,e.dump=n,me(e,n,!1)||me(e,n,!0);var l=Ae.call(e.dump),s=i,u;i&&(i=e.flowLevel<0||e.flowLevel>r);var f=l==="[object Object]"||l==="[object Array]",p,c;if(f&&(p=e.duplicates.indexOf(n),c=p!==-1),(e.tag!==null&&e.tag!=="?"||c||e.indent!==2&&r>0)&&(t=!1),c&&e.usedDuplicates[p])e.dump="*ref_"+p;else{if(f&&c&&!e.usedDuplicates[p]&&(e.usedDuplicates[p]=!0),l==="[object Object]")i&&Object.keys(e.dump).length!==0?(jn(e,r,e.dump,t),c&&(e.dump="&ref_"+p+e.dump)):(Yn(e,r,e.dump),c&&(e.dump="&ref_"+p+" "+e.dump));else if(l==="[object Array]")i&&e.dump.length!==0?(e.noArrayIndent&&!a&&r>0?pe(e,r-1,e.dump,t):pe(e,r,e.dump,t),c&&(e.dump="&ref_"+p+e.dump)):(Un(e,r,e.dump),c&&(e.dump="&ref_"+p+" "+e.dump));else if(l==="[object String]")e.tag!=="?"&&Pn(e,e.dump,r,o,s);else{if(l==="[object Undefined]")return!1;if(e.skipInvalid)return!1;throw new N("unacceptable kind of an object to dump "+l)}e.tag!==null&&e.tag!=="?"&&(u=encodeURI(e.tag[0]==="!"?e.tag.slice(1):e.tag).replace(/!/g,"%21"),e.tag[0]==="!"?u="!"+u:u.slice(0,18)==="tag:yaml.org,2002:"?u="!!"+u.slice(18):u="!<"+u+">",e.dump=u+" "+e.dump)}return!0}function Hn(e,r){var n=[],i=[],t,o;for(Z(e,n,i),t=0,o=i.length;t<o;t+=1)r.duplicates.push(n[i[t]]);r.usedDuplicates=new Array(o)}function Z(e,r,n){var i,t,o;if(e!==null&&typeof e=="object")if(t=r.indexOf(e),t!==-1)n.indexOf(t)===-1&&n.push(t);else if(r.push(e),Array.isArray(e))for(t=0,o=e.length;t<o;t+=1)Z(e[t],r,n);else for(i=Object.keys(e),t=0,o=i.length;t<o;t+=1)Z(e[i[t]],r,n)}function Bn(e,r){r=r||{};var n=new Ln(r);n.noRefs||Hn(e,n);var i=e;return n.replacer&&(i=n.replacer.call({"":i},"",i)),_(n,0,i,!0,!0)?n.dump+`
|
|
57
|
+
`:""}var Kn=Bn,Gn={dump:Kn},zn=Gn.dump;function Fe(e){const r=[];for(const n of e.entityMetadatas){if(typeof n.target=="string")continue;const i=n.target,t=w.isSecurityEnabled(i),o=w.getRLS(i),a=w.getCLS(i),l=w.getCustomSql(i);if(!t&&(o.length>0||a.length>0)){console.warn(y.yellow(`${n.name}: Found Security decorators (@rls / @cls) but missing @security class decorator. Skipping.`));continue}if(!t&&l.length===0)continue;const s=a.map(u=>{const f=n.columns.find(c=>c.propertyName===u.propertyKey);if(f)return{...u,databaseName:f.databaseName};const p=n.relations.find(c=>c.propertyName===u.propertyKey);if(p){if(p.joinColumns.length>0)return{...u,databaseName:p.joinColumns[0].databaseName};const c=n.columns.find(d=>d.relationMetadata===p);if(c)return{...u,databaseName:c.databaseName}}throw console.error(y.red(`
|
|
58
|
+
[supaforge debug] Property ${n.name}.${u.propertyKey} not found.`)),console.error(y.gray(`Available columns: ${n.columns.map(c=>c.propertyName).join(", ")}`)),console.error(y.gray(`Available relations: ${n.relations.map(c=>c.propertyName).join(", ")}`)),new Error(`Internal Sync Error: Database column not found for property ${n.name}.${u.propertyKey}`)});r.push({tableName:n.tableName,entityName:n.name,policies:o,columns:s,customSql:l,isSecurityEnabled:t})}return r}async function $e(e){const r=[];if(!g.existsSync(e))throw new Error(`Cannot load entities. Path does not exist: ${e}`);const i=g.statSync(e).isDirectory()?g.readdirSync(e).filter(t=>t.endsWith(".ts")||t.endsWith(".js")).map(t=>O.join(e,t)):[e];for(const t of i)try{const o=await import(t);for(const a of Object.values(o))typeof a=="function"&&a.name&&r.push(a)}catch{console.warn(y.red(`Could not import entity file ${t}. Skipped.`))}return r}function Qn(e){if(!g.existsSync(e))return null;const n=g.readdirSync(e).map(i=>{const t=i.match(/^(\d+)-(.+)\.ts$/);return t&&!i.toLowerCase().includes("_sec")?{timestamp:t[1],name:t[2],filename:i}:null}).filter(i=>i!==null).sort((i,t)=>Number(t.timestamp)-Number(i.timestamp));return n.length>0?{timestamp:n[0].timestamp,name:n[0].name}:null}async function Wn(e){const{entityPath:r,outputDir:n,dbConfig:i}=e,t=O.resolve(r);console.log(y.cyan("Syncing security..."));const o=await $e(t);if(o.length===0)return console.info(y.yellow(`No entities found at ${t}`)),"";const a=new de.DataSource({type:"postgres",...i,username:i.user,synchronize:!!e.syncSchema,entities:o,logging:!1});await a.initialize(),e.syncSchema&&(console.log(y.gray("Synchronizing schema...")),await a.synchronize());try{const l=Fe(a);if(l.length===0)return console.info(y.green("No security decorators found.")),"";const s=ee(l,{cls:e.cls,rls:e.rls}),u=Qn(n),f=u?u.timestamp:String(Date.now()),c=`${e.migrationName||(u?u.name:"SecuritySync")}_sec`.replace(/_sec_sec$/,"_sec"),d=`${c}${f}`,E=`${f}-${c}`;g.existsSync(n)||g.mkdirSync(n,{recursive:!0});const h=O.join(n,`${E}.ts`),v=O.join(n,`${E}.sql`),C=re(d,s);return g.writeFileSync(h,C),g.writeFileSync(v,s),console.log(y.green("Migration artifacts generated:")),console.log(y.gray(` TS -> ${h}`)),console.log(y.gray(` SQL -> ${v}`)),s}finally{await a.destroy()}}function Vn(e){const{outputDir:r,migrationName:n="ManualMigration"}=e;g.existsSync(r)||g.mkdirSync(r,{recursive:!0});const i=String(Date.now()),t=`${n}${i}`,o=O.join(r,`${i}-${n}.ts`),a=re(t,"-- Manual migration (Up)","-- Manual migration (Down)");g.writeFileSync(o,a),console.log(y.green(`Blank migration stub ready at ${o}`))}async function Xn(e,r,n,i,t=[]){const o=Array.isArray(r)?r:[r],a=o[0];await e.query("RESET ROLE"),await e.query("BEGIN");try{if(a!=="postgres"&&a!=="supabase_admin"){const u=o.every(v=>z.includes(v.toLowerCase())),f=o.length===1&&!u?o[0]:null,p=o.filter(v=>!z.includes(v.toLowerCase())),c={sub:n,role:u?o[0]:"authenticated"};f&&(c.user_role=f),p.length>0&&(c.user_roles=p);const E=JSON.stringify(c).replace(/'/g,"''"),h=n.replace(/'/g,"''");if(await e.query(`SET LOCAL request.jwt.claims = '${E}'`),await e.query(`SELECT set_config('request.jwt.claim.sub', '${h}', true)`),o.length===1)await e.query(`SET LOCAL ROLE ${o[0]}`);else{const v=`supaforge_session_${Math.random().toString(36).substring(7)}`;await e.query(`CREATE ROLE ${v} NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT`);for(const C of o)await e.query(`GRANT ${C} TO ${v}`);await e.query(`SET LOCAL ROLE ${v}`)}}const l=await e.query(i,t);if(await e.query("COMMIT"),Array.isArray(l)&&l.length>0&&Array.isArray(l[0]))return l[0];const s=l;return s!=null&&s.rows?s.rows:Array.isArray(l)?l:[]}catch(l){throw await e.query("ROLLBACK"),l}finally{await e.query("RESET ROLE")}}async function Zn(e){const{outputDir:r,dbConfig:n}=e;if(!g.existsSync(r)){console.warn(y.yellow(`Migration directory not found: ${r}`));return}const i=g.readdirSync(r).filter(o=>o.endsWith("_sec.sql")).sort((o,a)=>o.localeCompare(a));if(i.length===0){console.info(y.blue("No migrations found to apply."));return}console.log(y.cyan("Applying migrations..."));const t=new Me.Client(n);await t.connect();try{for(const o of i){const a=O.join(r,o);console.log(y.gray(`Applying ${o}...`));const l=g.readFileSync(a,"utf8");await t.query(l)}console.log(y.green(`
|
|
59
|
+
Successfully applied ${String(i.length)} migration(s).`))}catch(o){const a=o instanceof Error?o.message:String(o);throw console.error(y.red(`
|
|
60
|
+
Migration Execution Fault: ${a}`)),o}finally{await t.end()}}async function Jn(e){const{entityPath:r,outputDir:n,dbConfig:i}=e,t=O.resolve(r);console.log(y.cyan("Generating documentation..."));const o=await $e(t);if(o.length===0){console.info(y.yellow(`No entities found at ${t}`));return}const a=new de.DataSource({type:"postgres",...i,username:i.user,synchronize:!1,entities:o,logging:!1});await a.initialize();try{const l=e.rls!==!1,s=e.cls!==!1,f={documentation:`
|
|
61
|
+
Supaforge Documentation and Rules:
|
|
62
|
+
|
|
63
|
+
1. PROHIBITED: SELECT (*) USAGE
|
|
64
|
+
- SELECT (*) is strictly forbidden across the framework.
|
|
65
|
+
- Reason: Column Level Security (CLS) revokes all public access by default. Accessing an unauthorized column triggers a Database Error.
|
|
66
|
+
- Requirement: You must explicitly specify columns in your queries (e.g., SELECT id, name).
|
|
67
|
+
|
|
68
|
+
2. RLS (Row Level Security) Status: ${l?"ACTIVE":"PASSIVE"}
|
|
69
|
+
- ${l?"ACTIVE: PostgreSQL filters every row based on JWT claims (role, sub/id). Users only see authorized data.":"PASSIVE: The table is open to all roles with table-level access. HIGH SECURITY RISK."}
|
|
70
|
+
|
|
71
|
+
3. CLS (Column Level Security) Status: ${s?"ACTIVE":"PASSIVE"}
|
|
72
|
+
- ${s?"ACTIVE: Operates on a 'Zero-Trust' model. All access is revoked, then selectively granted via @CLS tags found in the 'columns' section.":"PASSIVE: Standard PostgreSQL column permissions apply."}
|
|
73
|
+
|
|
74
|
+
4. JSON Attributes Explanation:
|
|
75
|
+
- 'columns': Reflects TypeORM metadata and @cls rules (Role-based permissions).
|
|
76
|
+
- 'rls': Lists @rls rules with their SQL 'USING' or 'CHECK' expressions.
|
|
77
|
+
- 'runSql': Displays custom SQL for Triggers, Functions, or Hooks defined via @runSql.
|
|
78
|
+
- 'securitySql': The final, raw PostgreSQL code generated to implement the above security posture.
|
|
79
|
+
`.trim(),entities:[],generatedAt:"Deterministic output for stability"};for(const d of a.entityMetadatas){if(typeof d.target=="string")continue;const E=d.target,h=w.isSecurityEnabled(E),v=w.getRLS(E),C=w.getCLS(E),A=w.getCustomSql(E),k={entityName:d.name,tableName:d.tableName,isSecurityEnabled:h,columns:[],rls:[],runSql:[],checks:d.checks.map(m=>({name:m.name||"",expression:m.expression||""})),securitySql:""},De={tableName:d.tableName,entityName:d.name,policies:v,columns:C.map(m=>{const T=d.columns.find(b=>b.propertyName===m.propertyKey);if(T)return{...m,databaseName:T.databaseName};const q=d.relations.find(b=>b.propertyName===m.propertyKey);return q&&q.joinColumns.length>0?{...m,databaseName:q.joinColumns[0].databaseName}:{...m,databaseName:m.propertyKey}}),customSql:A,isSecurityEnabled:h};k.securitySql=ee([De],e);for(const m of d.columns){const T=C.find(b=>b.propertyKey===m.propertyName),q={name:m.propertyName,databaseName:m.databaseName,type:String(m.type),isText:m.type==="text"||m.type===String||typeof m.type=="string"&&m.type.toLowerCase().includes("char"),length:m.length,isUnique:d.uniques.some(b=>b.columns.includes(m)),isNullable:m.isNullable,isPrimary:m.isPrimary,cls:T?T.rules:[]};k.columns.push(q)}for(const m of v){const T=m.definition();k.rls.push({name:m.name,crud:m.crud,role:T.role,expression:T.expression})}for(const m of A)k.runSql.push({name:m.name,sql:m.definition()});f.entities.push(k)}g.existsSync(n)||g.mkdirSync(n,{recursive:!0});const p=O.join(n,"supaforge-docs.yaml"),c=zn(f,{noRefs:!0,lineWidth:-1,styles:{str:"folded"}});g.writeFileSync(p,c),console.log(y.green("Documentation generated successfully:")),console.log(y.gray(` YAML -> ${p}`))}finally{await a.destroy()}}function et(e=process.argv){const r=e,n=()=>{const o=r.indexOf("--name");return o>-1&&r[o+1]?r[o+1]:void 0},i=r.includes("--sync")||r.includes("--temp");return{apply:r.includes("--apply"),syncSchema:i,migrationName:n()}}const J=["c","r","u","d"],rt=I.z.string().transform(e=>{const n=e.toLowerCase().trim().split(""),i=n.filter(t=>!J.includes(t));if(i.length>0)throw new Error(`Invalid permission flag(s): ${i.join(", ")}. Allowed flags: ${J.join(", ")}.`);return[...new Set(n)]}),nt=I.z.object({host:I.z.string().optional().default("localhost"),port:I.z.number().int().optional().default(5432),user:I.z.string().optional().default("postgres"),password:I.z.string().optional().default("postgres"),database:I.z.string().optional().default("postgres")});exports.CRUD_OPS=J;exports.DbConfigSchema=nt;exports.PermissionsSchema=rt;exports.applyGeneratedMigrations=Zn;exports.buildMigrationFile=re;exports.buildSecuritySql=ee;exports.cls=Ye;exports.createMigrationTemplate=Vn;exports.extractCliOptions=et;exports.generateDocumentation=Jn;exports.introspectEntities=Fe;exports.registry=w;exports.rls=Ue;exports.runAsRole=Xn;exports.runSql=je;exports.runSupaforge=Wn;exports.security=qe;
|