@classytic/arc 1.0.0
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 +900 -0
- package/bin/arc.js +344 -0
- package/dist/adapters/index.d.ts +237 -0
- package/dist/adapters/index.js +668 -0
- package/dist/arcCorePlugin-DTPWXcZN.d.ts +273 -0
- package/dist/audit/index.d.ts +195 -0
- package/dist/audit/index.js +319 -0
- package/dist/auth/index.d.ts +47 -0
- package/dist/auth/index.js +174 -0
- package/dist/cli/commands/docs.d.ts +11 -0
- package/dist/cli/commands/docs.js +474 -0
- package/dist/cli/commands/introspect.d.ts +8 -0
- package/dist/cli/commands/introspect.js +338 -0
- package/dist/cli/index.d.ts +43 -0
- package/dist/cli/index.js +520 -0
- package/dist/createApp-pzUAkzbz.d.ts +77 -0
- package/dist/docs/index.d.ts +166 -0
- package/dist/docs/index.js +650 -0
- package/dist/errors-8WIxGS_6.d.ts +122 -0
- package/dist/events/index.d.ts +117 -0
- package/dist/events/index.js +89 -0
- package/dist/factory/index.d.ts +38 -0
- package/dist/factory/index.js +1664 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +199 -0
- package/dist/idempotency/index.d.ts +323 -0
- package/dist/idempotency/index.js +500 -0
- package/dist/index-DkAW8BXh.d.ts +1302 -0
- package/dist/index.d.ts +331 -0
- package/dist/index.js +4734 -0
- package/dist/migrations/index.d.ts +185 -0
- package/dist/migrations/index.js +274 -0
- package/dist/org/index.d.ts +129 -0
- package/dist/org/index.js +220 -0
- package/dist/permissions/index.d.ts +144 -0
- package/dist/permissions/index.js +100 -0
- package/dist/plugins/index.d.ts +46 -0
- package/dist/plugins/index.js +1069 -0
- package/dist/policies/index.d.ts +398 -0
- package/dist/policies/index.js +196 -0
- package/dist/presets/index.d.ts +336 -0
- package/dist/presets/index.js +382 -0
- package/dist/presets/multiTenant.d.ts +39 -0
- package/dist/presets/multiTenant.js +112 -0
- package/dist/registry/index.d.ts +16 -0
- package/dist/registry/index.js +253 -0
- package/dist/testing/index.d.ts +618 -0
- package/dist/testing/index.js +48032 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +8 -0
- package/dist/types-0IPhH_NR.d.ts +143 -0
- package/dist/types-B99TBmFV.d.ts +76 -0
- package/dist/utils/index.d.ts +655 -0
- package/dist/utils/index.js +905 -0
- package/package.json +227 -0
package/bin/arc.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Arc CLI - Smart Backend Framework
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* arc generate resource <name> [options] Generate a new resource
|
|
8
|
+
* arc generate controller <name> Generate a controller only
|
|
9
|
+
* arc generate model <name> Generate a model only
|
|
10
|
+
* arc introspect Show all registered resources
|
|
11
|
+
* arc docs [output-path] Export OpenAPI specification
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* arc generate resource product --module catalog
|
|
15
|
+
* arc generate resource invoice --presets softDelete,multiTenant
|
|
16
|
+
* arc introspect
|
|
17
|
+
* arc docs ./openapi.json
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
|
|
24
|
+
function getPackageVersion() {
|
|
25
|
+
try {
|
|
26
|
+
const pkgPath = new URL('../package.json', import.meta.url);
|
|
27
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
28
|
+
return pkg?.version || 'unknown';
|
|
29
|
+
} catch {
|
|
30
|
+
return 'unknown';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const VERSION = getPackageVersion();
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Argument Parsing
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
const args = process.argv.slice(2);
|
|
41
|
+
|
|
42
|
+
// Version flag
|
|
43
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
44
|
+
console.log(`Arc CLI v${VERSION}`);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Help flag or no args
|
|
49
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
50
|
+
printHelp();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Command Routing
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
const [command, subcommand, ...rest] = args;
|
|
59
|
+
|
|
60
|
+
async function main() {
|
|
61
|
+
try {
|
|
62
|
+
switch (command) {
|
|
63
|
+
case 'generate':
|
|
64
|
+
case 'g':
|
|
65
|
+
await handleGenerate(subcommand, rest);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'introspect':
|
|
69
|
+
case 'i':
|
|
70
|
+
await handleIntrospect(rest);
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case 'docs':
|
|
74
|
+
case 'd':
|
|
75
|
+
await handleDocs(subcommand ? [subcommand, ...rest] : rest);
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
default:
|
|
79
|
+
console.error(`❌ Unknown command: ${command}`);
|
|
80
|
+
console.error('Run "arc --help" for usage');
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error(`❌ Error: ${err.message}`);
|
|
85
|
+
if (process.env.DEBUG) {
|
|
86
|
+
console.error(err.stack);
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Command Handlers
|
|
94
|
+
// ============================================================================
|
|
95
|
+
|
|
96
|
+
async function handleGenerate(type, args) {
|
|
97
|
+
if (!type) {
|
|
98
|
+
console.error('❌ Missing type argument');
|
|
99
|
+
console.log('\nUsage: arc generate <resource|controller|model> <name> [options]');
|
|
100
|
+
console.log('\nExamples:');
|
|
101
|
+
console.log(' arc generate resource product --module catalog');
|
|
102
|
+
console.log(' arc g r invoice --presets softDelete,multiTenant');
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Normalize type shortcuts
|
|
107
|
+
const typeMap = {
|
|
108
|
+
r: 'resource',
|
|
109
|
+
c: 'controller',
|
|
110
|
+
m: 'model',
|
|
111
|
+
resource: 'resource',
|
|
112
|
+
controller: 'controller',
|
|
113
|
+
model: 'model',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const normalizedType = typeMap[type.toLowerCase()];
|
|
117
|
+
if (!normalizedType) {
|
|
118
|
+
console.error(`❌ Unknown type: ${type}`);
|
|
119
|
+
console.log('Available types: resource (r), controller (c), model (m)');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const name = args[0];
|
|
124
|
+
if (!name) {
|
|
125
|
+
console.error('❌ Missing name argument');
|
|
126
|
+
console.log(`\nUsage: arc generate ${normalizedType} <name> [options]`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const options = parseGenerateOptions(args.slice(1));
|
|
131
|
+
|
|
132
|
+
// Import and run
|
|
133
|
+
const { generate } = await import('../dist/cli/index.js');
|
|
134
|
+
await generate(normalizedType, name, options);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function handleIntrospect(args) {
|
|
138
|
+
// Check for --entry flag
|
|
139
|
+
const entryIndex = args.findIndex(arg => arg === '--entry' || arg === '-e');
|
|
140
|
+
if (entryIndex !== -1 && args[entryIndex + 1]) {
|
|
141
|
+
const entryPath = args[entryIndex + 1];
|
|
142
|
+
// Resolve path relative to CWD and convert to file URL for ESM import
|
|
143
|
+
const absolutePath = resolve(process.cwd(), entryPath);
|
|
144
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
145
|
+
|
|
146
|
+
console.log(`📦 Loading resources from: ${entryPath}\n`);
|
|
147
|
+
try {
|
|
148
|
+
await import(fileUrl);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(`❌ Failed to load entry file: ${err.message}`);
|
|
151
|
+
if (process.env.DEBUG) {
|
|
152
|
+
console.error(err.stack);
|
|
153
|
+
}
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { introspect } = await import('../dist/cli/commands/introspect.js');
|
|
159
|
+
await introspect(args.filter((arg, i) => arg !== '--entry' && arg !== '-e' && i !== entryIndex + 1));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function handleDocs(args) {
|
|
163
|
+
// Check for --entry flag
|
|
164
|
+
const entryIndex = args.findIndex(arg => arg === '--entry' || arg === '-e');
|
|
165
|
+
if (entryIndex !== -1 && args[entryIndex + 1]) {
|
|
166
|
+
const entryPath = args[entryIndex + 1];
|
|
167
|
+
// Resolve path relative to CWD and convert to file URL for ESM import
|
|
168
|
+
const absolutePath = resolve(process.cwd(), entryPath);
|
|
169
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
170
|
+
|
|
171
|
+
console.log(`📦 Loading resources from: ${entryPath}\n`);
|
|
172
|
+
try {
|
|
173
|
+
await import(fileUrl);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.error(`❌ Failed to load entry file: ${err.message}`);
|
|
176
|
+
if (process.env.DEBUG) {
|
|
177
|
+
console.error(err.stack);
|
|
178
|
+
}
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const filteredArgs = args.filter((arg, i) => arg !== '--entry' && arg !== '-e' && i !== entryIndex + 1);
|
|
184
|
+
const outputPath = filteredArgs[0] || './openapi.json';
|
|
185
|
+
const { exportDocs } = await import('../dist/cli/commands/docs.js');
|
|
186
|
+
await exportDocs([outputPath]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ============================================================================
|
|
190
|
+
// Option Parsing
|
|
191
|
+
// ============================================================================
|
|
192
|
+
|
|
193
|
+
function parseGenerateOptions(args) {
|
|
194
|
+
const opts = {
|
|
195
|
+
module: undefined,
|
|
196
|
+
presets: [],
|
|
197
|
+
parentField: 'parent',
|
|
198
|
+
withTests: true,
|
|
199
|
+
dryRun: false,
|
|
200
|
+
force: false,
|
|
201
|
+
typescript: true, // Default to TypeScript
|
|
202
|
+
outputDir: process.cwd(),
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < args.length; i++) {
|
|
206
|
+
const arg = args[i];
|
|
207
|
+
const next = args[i + 1];
|
|
208
|
+
|
|
209
|
+
switch (arg) {
|
|
210
|
+
case '--module':
|
|
211
|
+
case '-m':
|
|
212
|
+
opts.module = next;
|
|
213
|
+
i++;
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case '--presets':
|
|
217
|
+
case '-p':
|
|
218
|
+
opts.presets = next?.split(',').map((p) => p.trim()).filter(Boolean) || [];
|
|
219
|
+
i++;
|
|
220
|
+
break;
|
|
221
|
+
|
|
222
|
+
case '--parent-field':
|
|
223
|
+
opts.parentField = next;
|
|
224
|
+
i++;
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case '--output':
|
|
228
|
+
case '-o':
|
|
229
|
+
opts.outputDir = next;
|
|
230
|
+
i++;
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
case '--no-tests':
|
|
234
|
+
opts.withTests = false;
|
|
235
|
+
break;
|
|
236
|
+
|
|
237
|
+
case '--dry-run':
|
|
238
|
+
opts.dryRun = true;
|
|
239
|
+
break;
|
|
240
|
+
|
|
241
|
+
case '--force':
|
|
242
|
+
case '-f':
|
|
243
|
+
opts.force = true;
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
case '--js':
|
|
247
|
+
case '--javascript':
|
|
248
|
+
opts.typescript = false;
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case '--ts':
|
|
252
|
+
case '--typescript':
|
|
253
|
+
opts.typescript = true;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return opts;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Help
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
function printHelp() {
|
|
266
|
+
console.log(`
|
|
267
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
268
|
+
║ 🔥 Arc CLI v${VERSION} ║
|
|
269
|
+
║ Resource-Oriented Backend Framework ║
|
|
270
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
271
|
+
|
|
272
|
+
USAGE
|
|
273
|
+
arc <command> [options]
|
|
274
|
+
|
|
275
|
+
COMMANDS
|
|
276
|
+
generate, g Generate resources, controllers, or models
|
|
277
|
+
introspect, i Show all registered resources
|
|
278
|
+
docs, d Export OpenAPI specification
|
|
279
|
+
|
|
280
|
+
GLOBAL OPTIONS
|
|
281
|
+
--entry, -e <path> Entry file to load before running command
|
|
282
|
+
(loads resources into registry for introspect/docs)
|
|
283
|
+
--version, -v Show version
|
|
284
|
+
--help, -h Show this help
|
|
285
|
+
|
|
286
|
+
GENERATE SUBCOMMANDS
|
|
287
|
+
resource, r Generate full resource (model, repo, controller, routes)
|
|
288
|
+
controller, c Generate controller only
|
|
289
|
+
model, m Generate model only
|
|
290
|
+
|
|
291
|
+
GENERATE OPTIONS
|
|
292
|
+
--module, -m <name> Parent module (e.g., catalog, sales)
|
|
293
|
+
--presets, -p <list> Comma-separated presets:
|
|
294
|
+
• softDelete - Soft delete with restore
|
|
295
|
+
• slugLookup - GET by slug endpoint
|
|
296
|
+
• ownedByUser - User ownership checks
|
|
297
|
+
• multiTenant - Organization scoping
|
|
298
|
+
• tree - Hierarchical data support
|
|
299
|
+
• audited - Audit logging
|
|
300
|
+
--parent-field <name> Custom parent field for tree preset
|
|
301
|
+
--output, -o <path> Output directory (default: cwd)
|
|
302
|
+
--no-tests Skip test file generation
|
|
303
|
+
--dry-run Preview without creating files
|
|
304
|
+
--force, -f Overwrite existing files
|
|
305
|
+
--js, --javascript Generate JavaScript (default: TypeScript)
|
|
306
|
+
|
|
307
|
+
EXAMPLES
|
|
308
|
+
# Generate a product resource in catalog module
|
|
309
|
+
arc generate resource product --module catalog
|
|
310
|
+
|
|
311
|
+
# Generate with presets (shorthand)
|
|
312
|
+
arc g r invoice -m finance -p softDelete,multiTenant
|
|
313
|
+
|
|
314
|
+
# Generate controller only
|
|
315
|
+
arc g controller auth
|
|
316
|
+
|
|
317
|
+
# Preview what would be generated
|
|
318
|
+
arc g r order --dry-run
|
|
319
|
+
|
|
320
|
+
# Export OpenAPI spec (load resources first)
|
|
321
|
+
arc docs ./docs/openapi.json --entry ./index.js
|
|
322
|
+
|
|
323
|
+
# Show registered resources (load resources first)
|
|
324
|
+
arc introspect --entry ./index.js
|
|
325
|
+
|
|
326
|
+
# Quick introspect (if resources already loaded)
|
|
327
|
+
arc introspect
|
|
328
|
+
|
|
329
|
+
PRESETS EXPLAINED
|
|
330
|
+
softDelete Adds: deletedAt field, GET /deleted, POST /:id/restore
|
|
331
|
+
slugLookup Adds: slug field, GET /slug/:slug endpoint
|
|
332
|
+
ownedByUser Adds: createdBy field, ownership validation
|
|
333
|
+
multiTenant Adds: organizationId field, org scoping middleware
|
|
334
|
+
tree Adds: parent field, GET /tree, GET /:id/children
|
|
335
|
+
audited Adds: audit log entries for all mutations
|
|
336
|
+
|
|
337
|
+
MORE INFO
|
|
338
|
+
Documentation: https://github.com/classytic/arc
|
|
339
|
+
Issues: https://github.com/classytic/arc/issues
|
|
340
|
+
`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Run
|
|
344
|
+
main();
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { D as DataAdapter, f as CrudRepository, i as RepositoryLike, p as SchemaMetadata, g as RouteSchemaOptions, af as OpenApiSchemas, h as QueryParserInterface, ae as ParsedQuery, V as ValidationResult } from '../index-DkAW8BXh.js';
|
|
2
|
+
export { ag as AdapterFactory, F as FieldMetadata, q as RelationMetadata } from '../index-DkAW8BXh.js';
|
|
3
|
+
import { Model } from 'mongoose';
|
|
4
|
+
import 'fastify';
|
|
5
|
+
import '../types-B99TBmFV.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Mongoose Adapter - Type-Safe Database Adapter
|
|
9
|
+
*
|
|
10
|
+
* Bridges Mongoose models with Arc's resource system.
|
|
11
|
+
* Proper generics eliminate the need for 'as any' casts.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Options for creating a Mongoose adapter
|
|
16
|
+
*
|
|
17
|
+
* @typeParam TDoc - The document type (inferred or explicit)
|
|
18
|
+
*/
|
|
19
|
+
interface MongooseAdapterOptions<TDoc = unknown> {
|
|
20
|
+
/** Mongoose model instance */
|
|
21
|
+
model: Model<any>;
|
|
22
|
+
/** Repository implementing CRUD operations - accepts any repository-like object */
|
|
23
|
+
repository: CrudRepository<TDoc> | RepositoryLike;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Mongoose data adapter with proper type safety
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TDoc - The document type
|
|
29
|
+
*/
|
|
30
|
+
declare class MongooseAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
31
|
+
readonly type: "mongoose";
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly model: Model<any>;
|
|
34
|
+
readonly repository: CrudRepository<TDoc> | RepositoryLike;
|
|
35
|
+
constructor(options: MongooseAdapterOptions<TDoc>);
|
|
36
|
+
/**
|
|
37
|
+
* Get schema metadata from Mongoose model
|
|
38
|
+
*/
|
|
39
|
+
getSchemaMetadata(): SchemaMetadata;
|
|
40
|
+
/**
|
|
41
|
+
* Generate OpenAPI schemas from Mongoose model
|
|
42
|
+
*/
|
|
43
|
+
generateSchemas(schemaOptions?: RouteSchemaOptions): OpenApiSchemas | null;
|
|
44
|
+
/**
|
|
45
|
+
* Extract relation metadata
|
|
46
|
+
*/
|
|
47
|
+
private extractRelations;
|
|
48
|
+
/**
|
|
49
|
+
* Convert Mongoose type to OpenAPI type
|
|
50
|
+
*/
|
|
51
|
+
private mongooseTypeToOpenApi;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create Mongoose adapter with flexible type acceptance
|
|
55
|
+
*
|
|
56
|
+
* Accepts any repository with CRUD methods - no 'as any' needed.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Works with any MongoKit repository - no cast needed
|
|
61
|
+
* const adapter = createMongooseAdapter({
|
|
62
|
+
* model: ProductModel,
|
|
63
|
+
* repository: productRepository,
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function createMongooseAdapter<TDoc = unknown>(options: MongooseAdapterOptions<TDoc>): DataAdapter<TDoc>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Prisma Adapter - PostgreSQL/MySQL/SQLite Implementation
|
|
71
|
+
*
|
|
72
|
+
* Bridges Prisma Client with Arc's DataAdapter interface.
|
|
73
|
+
* Supports Prisma 5+ with all database providers.
|
|
74
|
+
*
|
|
75
|
+
* Features:
|
|
76
|
+
* ✅ Schema generation (OpenAPI docs from DMMF)
|
|
77
|
+
* ✅ Health checks (database connectivity)
|
|
78
|
+
* ✅ Query parsing (URL params → Prisma where/orderBy)
|
|
79
|
+
* ✅ Policy filter translation
|
|
80
|
+
* ✅ Soft delete preset support
|
|
81
|
+
* ✅ Multi-tenant preset support
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* import { PrismaClient, Prisma } from '@prisma/client';
|
|
86
|
+
* import { createPrismaAdapter, PrismaQueryParser } from '@classytic/arc/adapters';
|
|
87
|
+
*
|
|
88
|
+
* const prisma = new PrismaClient();
|
|
89
|
+
*
|
|
90
|
+
* const userAdapter = createPrismaAdapter({
|
|
91
|
+
* client: prisma,
|
|
92
|
+
* modelName: 'user',
|
|
93
|
+
* repository: new UserRepository(prisma),
|
|
94
|
+
* dmmf: Prisma.dmmf, // For schema generation
|
|
95
|
+
* queryParser: new PrismaQueryParser(), // Optional: custom parser
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Options for PrismaQueryParser
|
|
102
|
+
*/
|
|
103
|
+
interface PrismaQueryParserOptions {
|
|
104
|
+
/** Maximum allowed limit value (default: 1000) */
|
|
105
|
+
maxLimit?: number;
|
|
106
|
+
/** Default limit for pagination (default: 20) */
|
|
107
|
+
defaultLimit?: number;
|
|
108
|
+
/** Enable soft delete filtering by default (default: true) */
|
|
109
|
+
softDeleteEnabled?: boolean;
|
|
110
|
+
/** Field name for soft delete (default: 'deletedAt') */
|
|
111
|
+
softDeleteField?: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Prisma Query Parser - Converts URL parameters to Prisma query format
|
|
115
|
+
*
|
|
116
|
+
* Translates Arc's query format to Prisma's where/orderBy/take/skip structure.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const parser = new PrismaQueryParser();
|
|
121
|
+
*
|
|
122
|
+
* // URL: ?status=active&price[gte]=100&sort=-createdAt&page=2&limit=10
|
|
123
|
+
* const prismaQuery = parser.toPrismaQuery(parsedQuery);
|
|
124
|
+
* // Returns:
|
|
125
|
+
* // {
|
|
126
|
+
* // where: { status: 'active', price: { gte: 100 }, deletedAt: null },
|
|
127
|
+
* // orderBy: { createdAt: 'desc' },
|
|
128
|
+
* // take: 10,
|
|
129
|
+
* // skip: 10,
|
|
130
|
+
* // }
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
declare class PrismaQueryParser implements QueryParserInterface {
|
|
134
|
+
private readonly maxLimit;
|
|
135
|
+
private readonly defaultLimit;
|
|
136
|
+
private readonly softDeleteEnabled;
|
|
137
|
+
private readonly softDeleteField;
|
|
138
|
+
/** Map Arc operators to Prisma operators */
|
|
139
|
+
private readonly operatorMap;
|
|
140
|
+
constructor(options?: PrismaQueryParserOptions);
|
|
141
|
+
/**
|
|
142
|
+
* Parse URL query parameters (delegates to ArcQueryParser format)
|
|
143
|
+
*/
|
|
144
|
+
parse(query: Record<string, unknown> | null | undefined): ParsedQuery;
|
|
145
|
+
/**
|
|
146
|
+
* Convert ParsedQuery to Prisma query options
|
|
147
|
+
*/
|
|
148
|
+
toPrismaQuery(parsed: ParsedQuery, policyFilters?: Record<string, unknown>): PrismaQueryOptions;
|
|
149
|
+
/**
|
|
150
|
+
* Translate Arc/MongoDB-style filters to Prisma where clause
|
|
151
|
+
*/
|
|
152
|
+
private translateFilters;
|
|
153
|
+
private parseNumber;
|
|
154
|
+
private parseSort;
|
|
155
|
+
private parseSelect;
|
|
156
|
+
private parseFilters;
|
|
157
|
+
private coerceValue;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Prisma query options returned by toPrismaQuery
|
|
161
|
+
*/
|
|
162
|
+
interface PrismaQueryOptions {
|
|
163
|
+
where?: Record<string, unknown>;
|
|
164
|
+
orderBy?: Array<Record<string, 'asc' | 'desc'>>;
|
|
165
|
+
take?: number;
|
|
166
|
+
skip?: number;
|
|
167
|
+
select?: Record<string, boolean>;
|
|
168
|
+
include?: Record<string, boolean>;
|
|
169
|
+
}
|
|
170
|
+
interface PrismaAdapterOptions<TModel> {
|
|
171
|
+
/** Prisma client instance */
|
|
172
|
+
client: any;
|
|
173
|
+
/** Model name (e.g., 'user', 'product') */
|
|
174
|
+
modelName: string;
|
|
175
|
+
/** Repository instance implementing CRUD operations */
|
|
176
|
+
repository: CrudRepository<TModel>;
|
|
177
|
+
/** Optional: Prisma DMMF (Data Model Meta Format) for schema extraction */
|
|
178
|
+
dmmf?: any;
|
|
179
|
+
/** Optional: Custom query parser (default: PrismaQueryParser) */
|
|
180
|
+
queryParser?: PrismaQueryParser;
|
|
181
|
+
/** Enable soft delete filtering (default: true) */
|
|
182
|
+
softDeleteEnabled?: boolean;
|
|
183
|
+
/** Field name for soft delete (default: 'deletedAt') */
|
|
184
|
+
softDeleteField?: string;
|
|
185
|
+
}
|
|
186
|
+
declare class PrismaAdapter<TModel = any> implements DataAdapter<TModel> {
|
|
187
|
+
readonly type: "prisma";
|
|
188
|
+
readonly name: string;
|
|
189
|
+
readonly repository: CrudRepository<TModel>;
|
|
190
|
+
readonly queryParser: PrismaQueryParser;
|
|
191
|
+
private client;
|
|
192
|
+
private modelName;
|
|
193
|
+
private dmmf?;
|
|
194
|
+
private softDeleteEnabled;
|
|
195
|
+
private softDeleteField;
|
|
196
|
+
constructor(options: PrismaAdapterOptions<TModel>);
|
|
197
|
+
/**
|
|
198
|
+
* Parse URL query parameters and convert to Prisma query options
|
|
199
|
+
*/
|
|
200
|
+
parseQuery(query: Record<string, unknown>, policyFilters?: Record<string, unknown>): PrismaQueryOptions;
|
|
201
|
+
/**
|
|
202
|
+
* Apply policy filters to existing Prisma where clause
|
|
203
|
+
* Used for multi-tenant, ownership, and other security filters
|
|
204
|
+
*/
|
|
205
|
+
applyPolicyFilters(where: Record<string, unknown>, policyFilters: Record<string, unknown>): Record<string, unknown>;
|
|
206
|
+
generateSchemas(options?: RouteSchemaOptions): OpenApiSchemas | null;
|
|
207
|
+
getSchemaMetadata(): SchemaMetadata | null;
|
|
208
|
+
validate(data: unknown): Promise<ValidationResult>;
|
|
209
|
+
healthCheck(): Promise<boolean>;
|
|
210
|
+
close(): Promise<void>;
|
|
211
|
+
private buildEntitySchema;
|
|
212
|
+
private buildCreateSchema;
|
|
213
|
+
private buildUpdateSchema;
|
|
214
|
+
private shouldSkipField;
|
|
215
|
+
private convertPrismaFieldToJsonSchema;
|
|
216
|
+
private convertPrismaFieldToMetadata;
|
|
217
|
+
private mapPrismaTypeToMetadataType;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Factory function to create Prisma adapter
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* import { PrismaClient } from '@prisma/client';
|
|
224
|
+
* import { createPrismaAdapter } from '@classytic/arc';
|
|
225
|
+
*
|
|
226
|
+
* const prisma = new PrismaClient();
|
|
227
|
+
*
|
|
228
|
+
* const userAdapter = createPrismaAdapter({
|
|
229
|
+
* client: prisma,
|
|
230
|
+
* modelName: 'user',
|
|
231
|
+
* repository: userRepository,
|
|
232
|
+
* dmmf: Prisma.dmmf, // Optional: for schema generation
|
|
233
|
+
* });
|
|
234
|
+
*/
|
|
235
|
+
declare function createPrismaAdapter<TModel>(options: PrismaAdapterOptions<TModel>): PrismaAdapter<TModel>;
|
|
236
|
+
|
|
237
|
+
export { DataAdapter, MongooseAdapter, type MongooseAdapterOptions, PrismaAdapter, type PrismaAdapterOptions, type PrismaQueryOptions, PrismaQueryParser, type PrismaQueryParserOptions, RepositoryLike, SchemaMetadata, ValidationResult, createMongooseAdapter, createPrismaAdapter };
|