@an5/orm 1.3.2 → 1.3.3

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.
Files changed (62) hide show
  1. package/README.md +74 -11
  2. package/dist/cleanup.d.ts +1 -0
  3. package/dist/cleanup.js +177 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +147 -0
  6. package/dist/generator/src/attributes.d.ts +57 -0
  7. package/dist/generator/src/attributes.js +225 -0
  8. package/dist/generator/src/code-generator.js +49 -16
  9. package/dist/generator/src/config.d.ts +2 -2
  10. package/dist/generator/src/config.js +4 -3
  11. package/dist/generator/src/dialect.d.ts +52 -1
  12. package/dist/generator/src/dialect.js +136 -3
  13. package/dist/generator/src/dotnet-generator.js +67 -23
  14. package/dist/generator/src/golang-generator.d.ts +8 -0
  15. package/dist/generator/src/golang-generator.js +86 -21
  16. package/dist/generator/src/java-generator.d.ts +8 -0
  17. package/dist/generator/src/java-generator.js +108 -3
  18. package/dist/generator/src/kotlin-generator.js +51 -0
  19. package/dist/generator/src/parser.d.ts +1 -0
  20. package/dist/generator/src/parser.js +90 -35
  21. package/dist/generator/src/pull-schema.d.ts +54 -0
  22. package/dist/generator/src/pull-schema.js +116 -0
  23. package/dist/generator/src/push-apply.d.ts +4 -2
  24. package/dist/generator/src/push-apply.js +53 -4
  25. package/dist/generator/src/push-schema.d.ts +21 -9
  26. package/dist/generator/src/push-schema.js +132 -32
  27. package/dist/generator/src/python-generator.js +35 -22
  28. package/dist/generator/src/rust-generator.js +79 -74
  29. package/dist/generator/src/swift-generator.js +54 -3
  30. package/dist/generator/src/types.d.ts +10 -0
  31. package/dist/generator/src/view-order.d.ts +33 -0
  32. package/dist/generator/src/view-order.js +123 -0
  33. package/dist/migrate.d.ts +12 -0
  34. package/dist/migrate.js +442 -0
  35. package/dist/migration-core.d.ts +32 -3
  36. package/dist/migration-core.js +213 -43
  37. package/dist/provider-support.d.ts +0 -1
  38. package/dist/provider-support.js +0 -1
  39. package/dist/pull.d.ts +1 -0
  40. package/dist/pull.js +425 -0
  41. package/dist/push.d.ts +1 -0
  42. package/dist/push.js +93 -0
  43. package/dist/seed.d.ts +1 -0
  44. package/dist/seed.js +33 -0
  45. package/generator/src/attributes.ts +231 -0
  46. package/generator/src/code-generator.ts +47 -16
  47. package/generator/src/config.ts +5 -3
  48. package/generator/src/dialect.ts +188 -5
  49. package/generator/src/dotnet-generator.ts +68 -24
  50. package/generator/src/golang-generator.ts +89 -22
  51. package/generator/src/java-generator.ts +109 -3
  52. package/generator/src/kotlin-generator.ts +51 -0
  53. package/generator/src/parser.ts +102 -30
  54. package/generator/src/pull-schema.ts +127 -0
  55. package/generator/src/push-apply.ts +62 -5
  56. package/generator/src/push-schema.ts +160 -32
  57. package/generator/src/python-generator.ts +33 -20
  58. package/generator/src/rust-generator.ts +79 -73
  59. package/generator/src/swift-generator.ts +55 -4
  60. package/generator/src/types.ts +10 -0
  61. package/generator/src/view-order.ts +133 -0
  62. package/package.json +19 -14
package/README.md CHANGED
@@ -69,38 +69,58 @@ DATABASE_URL=sqlserver://localhost:1433;database=mydb;user=sa;password=Secret123
69
69
 
70
70
  ### CLI Commands
71
71
 
72
+ You can run AN5 commands directly using `npx @an5/orm` (or `npx an5`):
73
+
72
74
  ```bash
73
75
  # Generate client code from .an5 schemas
74
- npm run generate
76
+ npx @an5/orm generate
77
+ # or: npx an5 generate
75
78
 
76
79
  # Push schema directly to database
77
- npm run db:push
80
+ npx @an5/orm db:push
78
81
 
79
82
  # Pull database schema into .an5 files
80
- npm run db:pull
83
+ npx @an5/orm db:pull
81
84
 
82
85
  # Check schema diff
83
- npm run db:migrate diff
86
+ npx @an5/orm db:migrate diff
84
87
 
85
88
  # Generate new migration script
86
- npm run db:migrate:generate
89
+ npx @an5/orm db:migrate generate
87
90
 
88
91
  # Apply pending migrations
89
- npm run db:migrate:apply
92
+ npx @an5/orm db:migrate apply
90
93
 
91
94
  # Rollback last migration
92
- npm run db:migrate:rollback
95
+ npx @an5/orm db:migrate rollback
93
96
 
94
97
  # View migration status
95
- npm run db:migrate:status
98
+ npx @an5/orm db:migrate status
96
99
 
97
100
  # Run database seed script
98
- npm run db:seed
101
+ npx @an5/orm db:seed
99
102
 
100
- # Clean up database tables
101
- npm run db:cleanup
103
+ # Clean up extra tables and views
104
+ npx @an5/orm db:cleanup
102
105
  ```
103
106
 
107
+ You can also add these commands to your project's `package.json` scripts:
108
+
109
+ ```json
110
+ {
111
+ "scripts": {
112
+ "generate": "an5 generate",
113
+ "db:push": "an5 db:push",
114
+ "db:pull": "an5 db:pull",
115
+ "db:migrate": "an5 db:migrate",
116
+ "db:seed": "an5 db:seed",
117
+ "db:cleanup": "an5 db:cleanup"
118
+ }
119
+ }
120
+ ```
121
+
122
+ Then run them with `npm run generate`, `npm run db:push`, etc.
123
+
104
124
  ## Runtime Database Usage (via `@an5/adapters`)
105
125
 
106
126
  To connect to your database and perform queries, use `@an5/adapters`:
@@ -169,6 +189,49 @@ model Order {
169
189
  }
170
190
  ```
171
191
 
192
+ Views are declared with the same field syntax, so generated clients can type their
193
+ rows, plus the `SELECT` that defines them. A view is read-only: writes are not
194
+ generated, and `db:push`/`db:migrate` create the view after the tables it reads —
195
+ and after any view it reads. `db:pull` writes a database view back as a `view`
196
+ block, with its columns and the SQL body from the catalog. `db:cleanup` drops
197
+ views that are no longer in the schema, before the tables they might read.
198
+
199
+ `@@sql` holds the `SELECT` body; a full `CREATE VIEW ... AS ...` statement pasted
200
+ from a catalog is reduced to that body, so its header and object name are not
201
+ repeated in the generated DDL.
202
+
203
+ Generated clients expose a view through a read-only client — `ViewClient` in
204
+ TypeScript, Python, .NET, Java and Kotlin, `ViewModelClient` in Swift, and
205
+ `ViewClient[T]` in Go and Rust. Those types have the read methods only, so a write
206
+ to a view is rejected at compile time rather than by the database.
207
+
208
+ ```an5
209
+ view ActiveUser {
210
+ id NVARCHAR(1000)
211
+ email NVARCHAR(255)
212
+
213
+ @@map("active_users")
214
+ @@sql("SELECT id, email FROM users WHERE active = 1")
215
+ }
216
+ ```
217
+
218
+ A longer query can be written over several lines with triple quotes; the indentation
219
+ shared by the SQL lines is removed before it reaches the database:
220
+
221
+ ```an5
222
+ view ActiveUser {
223
+ id NVARCHAR(1000)
224
+ email NVARCHAR(255)
225
+
226
+ @@map("active_users")
227
+ @@sql("""
228
+ SELECT id, email
229
+ FROM users
230
+ WHERE active = 1
231
+ """)
232
+ }
233
+ ```
234
+
172
235
  ## License
173
236
 
174
237
  MIT
@@ -0,0 +1 @@
1
+ import "dotenv/config";
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ require("dotenv/config");
7
+ const adapters_1 = require("@an5/adapters");
8
+ const config_1 = require("./generator/src/config");
9
+ const provider_support_1 = require("./provider-support");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const rootDir = process.cwd();
13
+ const { config, outputs } = (0, config_1.loadConfig)();
14
+ const schemaDir = outputs.schemaDir;
15
+ let _adapter = null;
16
+ async function getDb() {
17
+ if (!_adapter) {
18
+ _adapter = new adapters_1.An5Adapter({ connectionString: (0, config_1.resolveConnectionString)(config, process.env, "db:cleanup") });
19
+ await _adapter.$connect();
20
+ }
21
+ return _adapter;
22
+ }
23
+ /**
24
+ * Prints why a command cannot run against this provider and exits.
25
+ *
26
+ * The decision and the wording live in `./provider-support`, which stays free of Node
27
+ * globals so it can be imported and tested anywhere; only the command files, which
28
+ * are Node programs, do the exiting.
29
+ */
30
+ function refuseIfNotSqlServer(provider, command) {
31
+ const message = (0, provider_support_1.sqlServerOnlyError)(provider, command);
32
+ if (message === null)
33
+ return;
34
+ console.error(message);
35
+ process.exit(1);
36
+ }
37
+ /**
38
+ * The tables and views the schema files declare, lower-cased.
39
+ *
40
+ * SQL Server compares object names case-insensitively in the default collation,
41
+ * so `Users` and `users` are the same object here; a case-sensitive comparison
42
+ * would drop a table the schema still declares.
43
+ */
44
+ function declaredObjects(schemaText) {
45
+ const tables = new Set();
46
+ const views = new Set();
47
+ let block = null;
48
+ let blockName = '';
49
+ for (const rawLine of schemaText.split(/\r?\n/)) {
50
+ const line = rawLine.trim();
51
+ if (!line || line.startsWith('//'))
52
+ continue;
53
+ const start = /^(model|view)\s+(\w+)\s*\{/.exec(line);
54
+ if (start) {
55
+ block = start[1];
56
+ blockName = start[2] ?? '';
57
+ if (block === 'model')
58
+ tables.add(blockName.toLowerCase() + 's');
59
+ else
60
+ views.add(blockName.toLowerCase() + 's');
61
+ continue;
62
+ }
63
+ if (line === '}') {
64
+ block = null;
65
+ continue;
66
+ }
67
+ const mapped = /^@@map\(\s*"([^"]+)"\s*\)/.exec(line);
68
+ if (mapped?.[1]) {
69
+ // A mapped name replaces the default one, but `@@map` outside a block is
70
+ // still read as a table name: older schema files may have it there.
71
+ const target = block === 'view' ? views : tables;
72
+ target.add(mapped[1].toLowerCase());
73
+ }
74
+ }
75
+ return { tables, views };
76
+ }
77
+ /** Quotes a catalog identifier, doubling `]` the way T-SQL expects. */
78
+ function quoteName(name) {
79
+ return `[${name.replace(/]/g, ']]')}]`;
80
+ }
81
+ async function cleanup() {
82
+ // Drops tables by comparing sys.tables with the schema files, so a non-SQL
83
+ // Server connection string is a mistake worth naming first.
84
+ refuseIfNotSqlServer((0, config_1.providerFromConfig)(config, process.env), "db:cleanup");
85
+ console.log("🧹 Starting database cleanup...");
86
+ let schemaText = "";
87
+ if (fs_1.default.existsSync(schemaDir)) {
88
+ const files = fs_1.default.readdirSync(schemaDir).filter(f => f.endsWith(".an5"));
89
+ for (const file of files) {
90
+ schemaText += fs_1.default.readFileSync(path_1.default.join(schemaDir, file), "utf8") + "\n";
91
+ }
92
+ }
93
+ else {
94
+ const schemaPath = path_1.default.join(__dirname, "schema.an5");
95
+ if (fs_1.default.existsSync(schemaPath)) {
96
+ schemaText = fs_1.default.readFileSync(schemaPath, "utf8");
97
+ }
98
+ else {
99
+ console.error("No schema found!");
100
+ process.exit(1);
101
+ }
102
+ }
103
+ const { tables: validTables, views: validViews } = declaredObjects(schemaText);
104
+ // Also include the capitalization variations from an5Metadata if we can,
105
+ // but schema.an5 is the source of truth.
106
+ console.log("Valid tables from schema:", Array.from(validTables));
107
+ console.log("Valid views from schema:", Array.from(validViews));
108
+ const dbTables = await (await getDb()).$queryRawUnsafe(`
109
+ SELECT name FROM sys.tables WHERE is_ms_shipped = 0
110
+ `);
111
+ const dbViews = await (await getDb()).$queryRawUnsafe(`
112
+ SELECT name FROM sys.views WHERE is_ms_shipped = 0
113
+ `);
114
+ const tablesToDrop = dbTables
115
+ .map(t => t.name)
116
+ .filter(name => !validTables.has(name.toLowerCase()));
117
+ const viewsToDrop = dbViews
118
+ .map(v => v.name)
119
+ .filter(name => !validViews.has(name.toLowerCase()));
120
+ // Views go first: one of them may read a table that is about to be dropped, and
121
+ // an orphan view is exactly what this command is meant to remove.
122
+ if (viewsToDrop.length > 0) {
123
+ console.log("Views to be DROPPED:", viewsToDrop);
124
+ for (const view of viewsToDrop) {
125
+ console.log(`Dropping view [${view}]...`);
126
+ try {
127
+ await (await getDb()).$executeRawUnsafe(`DROP VIEW IF EXISTS ${quoteName(view)}`);
128
+ console.log(`✅ Dropped [${view}]`);
129
+ }
130
+ catch (err) {
131
+ console.error(`❌ Failed to drop view [${view}]: ${err.message}`);
132
+ }
133
+ }
134
+ }
135
+ if (tablesToDrop.length === 0) {
136
+ console.log(viewsToDrop.length === 0
137
+ ? "✅ No extra tables found. Database is clean."
138
+ : "✅ No extra tables found.");
139
+ console.log("✅ Cleanup completed.");
140
+ process.exit(0);
141
+ }
142
+ console.log("Tables to be DROPPED:", tablesToDrop);
143
+ for (const table of tablesToDrop) {
144
+ console.log(`Dropping table [${table}]...`);
145
+ try {
146
+ // We need to handle foreign key constraints.
147
+ // For a thorough cleanup, we might need to drop constraints first.
148
+ // But for now, we'll try a simple DROP TABLE.
149
+ await (await getDb()).$executeRawUnsafe(`DROP TABLE ${quoteName(table)}`);
150
+ console.log(`✅ Dropped [${table}]`);
151
+ }
152
+ catch (err) {
153
+ console.error(`❌ Failed to drop [${table}]: ${err.message}`);
154
+ if (err.message.includes("FOREIGN KEY constraint")) {
155
+ console.log(`Attempting to find and drop constraints for [${table}]...`);
156
+ const constraints = await (await getDb()).$queryRawUnsafe(`
157
+ SELECT fk.name AS constraint_name, OBJECT_NAME(fk.parent_object_id) AS table_name
158
+ FROM sys.foreign_keys AS fk
159
+ WHERE OBJECT_NAME(fk.referenced_object_id) = @p_0 OR OBJECT_NAME(fk.parent_object_id) = @p_0
160
+ `, table);
161
+ for (const c of constraints) {
162
+ console.log(`Dropping constraint [${c.constraint_name}] on [${c.table_name}]...`);
163
+ await (await getDb()).$executeRawUnsafe(`ALTER TABLE ${quoteName(c.table_name)} DROP CONSTRAINT ${quoteName(c.constraint_name)}`);
164
+ }
165
+ // Try dropping again
166
+ await (await getDb()).$executeRawUnsafe(`DROP TABLE ${quoteName(table)}`);
167
+ console.log(`✅ Dropped [${table}] after removing constraints.`);
168
+ }
169
+ }
170
+ }
171
+ console.log("✅ Cleanup completed.");
172
+ process.exit(0);
173
+ }
174
+ cleanup().catch(err => {
175
+ console.error("❌ Cleanup failed:", err);
176
+ process.exit(1);
177
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(rawArgs?: string[]): void;
package/dist/cli.js ADDED
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.main = main;
8
+ const child_process_1 = require("child_process");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ function printHelp() {
12
+ console.log(`AN5 ORM CLI
13
+
14
+ Usage:
15
+ an5 <command> [options]
16
+ npx @an5/orm <command> [options]
17
+
18
+ Commands:
19
+ generate, codegen Generate client code from .an5 schemas
20
+ db:push, push Push schema directly to database
21
+ db:pull, pull Pull database schema into .an5 files
22
+ db:migrate, migrate Manage database migrations:
23
+ diff Show differences between schema and database
24
+ generate Generate a new migration script
25
+ apply [--preview] Apply pending migrations
26
+ rollback [--preview] Roll back applied migrations
27
+ status View migration status
28
+ db:seed, seed Run project seed script (seed.ts / seed.js)
29
+ db:cleanup, cleanup Clean up database tables
30
+
31
+ Options:
32
+ -v, --version Show version number
33
+ -h, --help Show help
34
+ `);
35
+ }
36
+ function printVersion() {
37
+ try {
38
+ const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'package.json'), 'utf8'));
39
+ console.log(`@an5/orm v${pkg.version}`);
40
+ }
41
+ catch {
42
+ console.log('@an5/orm');
43
+ }
44
+ }
45
+ function resolveCommandScript(baseName) {
46
+ // If running from dist/cli.js, command files are right next to it
47
+ const distCandidate = path_1.default.join(__dirname, `${baseName}.js`);
48
+ if (fs_1.default.existsSync(distCandidate)) {
49
+ return distCandidate;
50
+ }
51
+ if (baseName === 'generator') {
52
+ const genDist = path_1.default.join(__dirname, 'generator', 'src', 'index.js');
53
+ if (fs_1.default.existsSync(genDist))
54
+ return genDist;
55
+ const genTs = path_1.default.join(__dirname, '..', 'generator', 'src', 'index.ts');
56
+ if (fs_1.default.existsSync(genTs))
57
+ return genTs;
58
+ }
59
+ const rootTs = path_1.default.join(__dirname, '..', `${baseName}.ts`);
60
+ if (fs_1.default.existsSync(rootTs)) {
61
+ return rootTs;
62
+ }
63
+ const localTs = path_1.default.join(__dirname, `${baseName}.ts`);
64
+ if (fs_1.default.existsSync(localTs)) {
65
+ return localTs;
66
+ }
67
+ return distCandidate;
68
+ }
69
+ function runScript(scriptPath, args) {
70
+ const isTs = scriptPath.endsWith('.ts');
71
+ const runner = isTs ? (process.platform === 'win32' ? 'npx.cmd' : 'npx') : process.execPath;
72
+ const runnerArgs = isTs ? ['tsx', scriptPath, ...args] : [scriptPath, ...args];
73
+ const result = (0, child_process_1.spawnSync)(runner, runnerArgs, {
74
+ stdio: 'inherit',
75
+ env: process.env,
76
+ });
77
+ if (result.error) {
78
+ console.error(`❌ Failed to execute command: ${result.error.message}`);
79
+ process.exit(1);
80
+ }
81
+ process.exit(result.status ?? (result.signal ? 1 : 0));
82
+ }
83
+ function main(rawArgs = process.argv.slice(2)) {
84
+ const [firstArg, ...rest] = rawArgs;
85
+ if (!firstArg || firstArg === '--help' || firstArg === '-h' || firstArg === 'help') {
86
+ printHelp();
87
+ process.exit(0);
88
+ }
89
+ if (firstArg === '--version' || firstArg === '-v' || firstArg === 'version') {
90
+ printVersion();
91
+ process.exit(0);
92
+ }
93
+ // Handle db:migrate:... shorthand (e.g. db:migrate:apply -> migrate apply)
94
+ if (firstArg.startsWith('db:migrate:')) {
95
+ const sub = firstArg.slice('db:migrate:'.length);
96
+ const script = resolveCommandScript('migrate');
97
+ runScript(script, [sub, ...rest]);
98
+ return;
99
+ }
100
+ switch (firstArg) {
101
+ case 'generate':
102
+ case 'codegen':
103
+ case 'g': {
104
+ const script = resolveCommandScript('generator');
105
+ runScript(script, rest);
106
+ break;
107
+ }
108
+ case 'db:push':
109
+ case 'push': {
110
+ const script = resolveCommandScript('push');
111
+ runScript(script, rest);
112
+ break;
113
+ }
114
+ case 'db:pull':
115
+ case 'pull': {
116
+ const script = resolveCommandScript('pull');
117
+ runScript(script, rest);
118
+ break;
119
+ }
120
+ case 'db:seed':
121
+ case 'seed': {
122
+ const script = resolveCommandScript('seed');
123
+ runScript(script, rest);
124
+ break;
125
+ }
126
+ case 'db:cleanup':
127
+ case 'cleanup': {
128
+ const script = resolveCommandScript('cleanup');
129
+ runScript(script, rest);
130
+ break;
131
+ }
132
+ case 'db:migrate':
133
+ case 'migrate': {
134
+ const script = resolveCommandScript('migrate');
135
+ runScript(script, rest);
136
+ break;
137
+ }
138
+ default: {
139
+ console.error(`Unknown command: "${firstArg}"`);
140
+ console.error('Run "an5 --help" for a list of available commands.');
141
+ process.exit(1);
142
+ }
143
+ }
144
+ }
145
+ if (require.main === module) {
146
+ main();
147
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Reading `@attribute(...)` arguments from a `.an5` field or model line.
3
+ *
4
+ * The schema used to be read with regexes such as `/@default\((.*)\)/`, which
5
+ * ran to the *last* `)` on the line. A field with `@default(now())` followed by
6
+ * `@description("...")` therefore had `now()) @description("..."` as its default
7
+ * expression, and `db:push` emitted SQL no database accepts. The closing
8
+ * parenthesis has to be the one that matches the opening one — parentheses nest
9
+ * (`now()`, `uuid()`, `lower(...)`) and quoted strings may contain both.
10
+ *
11
+ * These helpers only read attributes; they do not validate the schema language.
12
+ */
13
+ /** Whether `line` has the attribute, with or without arguments. */
14
+ export declare function hasAttribute(line: string, name: string): boolean;
15
+ /**
16
+ * The raw argument text of the first `name(...)` on `line`.
17
+ *
18
+ * Returns `undefined` when the attribute is absent, or when it is written
19
+ * without parentheses. The returned text keeps its quotes, so callers decide
20
+ * whether they are reading a string, a number or an expression.
21
+ */
22
+ export declare function attributeArgs(line: string, name: string): string | undefined;
23
+ /** Strips one pair of surrounding double quotes, if present. */
24
+ export declare function unquote(value: string | undefined): string | undefined;
25
+ /**
26
+ * Splits schema text into logical lines, keeping a triple-quoted `@@sql` body on
27
+ * one line.
28
+ *
29
+ * The parsers read a schema line by line. Without this, a multi-line view body
30
+ * would end the directive at its first newline and the rest of the `SELECT`
31
+ * would be read as fields of the view. Newlines inside the quotes are preserved;
32
+ * only the split points change.
33
+ */
34
+ export declare function splitSchemaLines(text: string): string[];
35
+ /**
36
+ * Reads a named string option from an attribute argument list, e.g.
37
+ * `map: "UQ_users_email"` in `@unique(map: "UQ_users_email")`.
38
+ */
39
+ export declare function stringOption(args: string | undefined, key: string): string | undefined;
40
+ /** Reads a named field-list option, e.g. `include: [email, tenantId]`. */
41
+ export declare function fieldListOption(args: string | undefined, key: string): string[] | undefined;
42
+ /** One `@@unique` or `@@index` directive, with the options it accepts. */
43
+ export interface IndexDirective {
44
+ fields: string[];
45
+ name?: string;
46
+ includeFields?: string[];
47
+ filter?: string;
48
+ options?: string;
49
+ }
50
+ /**
51
+ * Parses the inside of `@@unique([a, b], map: "…", include: […], …)`.
52
+ *
53
+ * The directive is the one place a schema can carry raw SQL (`filter`,
54
+ * `options`), so its argument list is balanced rather than cut at the first
55
+ * `)`. A filter such as `(deletedAt IS NULL)` used to truncate the directive.
56
+ */
57
+ export declare function parseIndexDirective(args: string | undefined): IndexDirective | null;