schemock 0.0.4-alpha.16 → 0.0.4-alpha.17
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/dist/cli/index.d.mts +12 -1
- package/dist/cli/index.d.ts +12 -1
- package/dist/cli/index.js +124 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +124 -1
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli.js +125 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -609,6 +609,71 @@ export const HealthEndpoint = defineEndpoint('/api/health', {
|
|
|
609
609
|
- Easier debugging with meaningful stack traces
|
|
610
610
|
- Cleaner generated code
|
|
611
611
|
|
|
612
|
+
### Inline Resolver Limitations
|
|
613
|
+
|
|
614
|
+
When using inline `mockResolver` functions with local helper functions, be aware of these limitations:
|
|
615
|
+
|
|
616
|
+
#### 1. Only Directly-Used Functions Are Copied
|
|
617
|
+
|
|
618
|
+
Local functions are only copied if they are **directly called** in a resolver. Transitive dependencies (functions called by other functions) are not automatically detected.
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
// ❌ Problem: innerHelper won't be copied
|
|
622
|
+
function innerHelper(x: string) { return x.toUpperCase(); }
|
|
623
|
+
function outerHelper(x: string) { return innerHelper(x); }
|
|
624
|
+
|
|
625
|
+
export const MyEndpoint = defineEndpoint('/api/test', {
|
|
626
|
+
mockResolver: async ({ body }) => ({
|
|
627
|
+
result: outerHelper(body.input) // Only outerHelper is detected
|
|
628
|
+
})
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// ✅ Solution: Use named exported function
|
|
632
|
+
export async function myResolver({ body, db }) {
|
|
633
|
+
return { result: outerHelper(body.input) };
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export const MyEndpoint = defineEndpoint('/api/test', {
|
|
637
|
+
mockResolver: myResolver // Function is imported, not serialized
|
|
638
|
+
});
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
#### 2. Arrow Functions Need Parentheses
|
|
642
|
+
|
|
643
|
+
Arrow functions without parentheses around parameters are not detected:
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
// ❌ Not detected
|
|
647
|
+
const double = x => x * 2;
|
|
648
|
+
|
|
649
|
+
// ✅ Detected
|
|
650
|
+
const double = (x) => x * 2;
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### 3. Recommended: Use Named Functions for Complex Logic
|
|
654
|
+
|
|
655
|
+
For resolvers with helper functions, use named exported functions instead of inline resolvers:
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
// Recommended approach
|
|
659
|
+
export async function loginResolver({ body, db }) {
|
|
660
|
+
const user = db.user.findFirst({ where: { email: { equals: body.email } } });
|
|
661
|
+
if (!verifyPassword(body.password, user.hash)) {
|
|
662
|
+
throw new Error('Invalid credentials');
|
|
663
|
+
}
|
|
664
|
+
return { token: generateToken(user.id), user };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export const LoginEndpoint = defineEndpoint('/api/auth/login', {
|
|
668
|
+
mockResolver: loginResolver // ✅ Imported directly, full IDE support
|
|
669
|
+
});
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
Benefits of named functions:
|
|
673
|
+
- All dependencies are available (no serialization)
|
|
674
|
+
- Full IDE support (go-to-definition, refactoring)
|
|
675
|
+
- Better stack traces for debugging
|
|
676
|
+
|
|
612
677
|
### Row-Level Security (RLS)
|
|
613
678
|
|
|
614
679
|
**Scope-based RLS (simple - recommended):**
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -423,6 +423,15 @@ interface ResolverDependency {
|
|
|
423
423
|
/** Whether it's a default import */
|
|
424
424
|
isDefault?: boolean;
|
|
425
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Local function definition used by inline resolver
|
|
428
|
+
*/
|
|
429
|
+
interface LocalFunction {
|
|
430
|
+
/** Function name */
|
|
431
|
+
name: string;
|
|
432
|
+
/** Full function source code */
|
|
433
|
+
source: string;
|
|
434
|
+
}
|
|
426
435
|
/**
|
|
427
436
|
* Fully analyzed endpoint with all computed properties
|
|
428
437
|
*/
|
|
@@ -455,6 +464,8 @@ interface AnalyzedEndpoint {
|
|
|
455
464
|
sourceFile?: string;
|
|
456
465
|
/** Dependencies detected in inline resolver (functions used but not defined locally) */
|
|
457
466
|
resolverDependencies?: ResolverDependency[];
|
|
467
|
+
/** Local functions used by inline resolver */
|
|
468
|
+
localFunctions?: LocalFunction[];
|
|
458
469
|
/** Description */
|
|
459
470
|
description?: string;
|
|
460
471
|
}
|
|
@@ -1068,4 +1079,4 @@ declare function generateFetchClient(schemas: AnalyzedSchema[], config: FetchAda
|
|
|
1068
1079
|
*/
|
|
1069
1080
|
declare function generateFormSchemas(schemas: AnalyzedSchema[]): string;
|
|
1070
1081
|
|
|
1071
|
-
export { type AnalyzedComputed, type AnalyzedEndpoint, type AnalyzedEndpointField, type AnalyzedField, type AnalyzedIndex, type AnalyzedRLS, type AnalyzedRPC, type AnalyzedRelation, type AnalyzedSchema, type AuthProviderConfig, CodeBuilder, type FakerMapping, type FetchAdapterConfig, type FirebaseAdapterConfig, type FrameworkType, type GenerateOptions, type GenerateSQLOptions, type GenerationTarget, type GraphQLAdapterConfig, type MockAdapterConfig, type PGliteAdapterConfig, type PluralizeConfig, RLSBypass, RLSConfig, RLSScopeMapping, type ResolverDependency, type SQLGeneratorResult, type SchemockConfig, type SupabaseAdapterConfig, type TargetMiddlewareConfig, type TargetType, analyzeSchemas, defineConfig, discoverSchemas, fieldToFakerCall, fieldToTsType, generate, generateClaudeMd, generateCursorRules, generateFetchClient, generateFirebaseClient, generateFormSchemas, generateHooks, generateMockClient, generateMockDb, generateMockHandlers, generateSchemockSection, generateSeed, generateSupabaseClient, generateTypes, getDefaultConfig, getRelativePath, loadConfig, mergeClaudeMdContent, pluralize, primitiveToTs, setupAI, toCamelCase, toPascalCase };
|
|
1082
|
+
export { type AnalyzedComputed, type AnalyzedEndpoint, type AnalyzedEndpointField, type AnalyzedField, type AnalyzedIndex, type AnalyzedRLS, type AnalyzedRPC, type AnalyzedRelation, type AnalyzedSchema, type AuthProviderConfig, CodeBuilder, type FakerMapping, type FetchAdapterConfig, type FirebaseAdapterConfig, type FrameworkType, type GenerateOptions, type GenerateSQLOptions, type GenerationTarget, type GraphQLAdapterConfig, type LocalFunction, type MockAdapterConfig, type PGliteAdapterConfig, type PluralizeConfig, RLSBypass, RLSConfig, RLSScopeMapping, type ResolverDependency, type SQLGeneratorResult, type SchemockConfig, type SupabaseAdapterConfig, type TargetMiddlewareConfig, type TargetType, analyzeSchemas, defineConfig, discoverSchemas, fieldToFakerCall, fieldToTsType, generate, generateClaudeMd, generateCursorRules, generateFetchClient, generateFirebaseClient, generateFormSchemas, generateHooks, generateMockClient, generateMockDb, generateMockHandlers, generateSchemockSection, generateSeed, generateSupabaseClient, generateTypes, getDefaultConfig, getRelativePath, loadConfig, mergeClaudeMdContent, pluralize, primitiveToTs, setupAI, toCamelCase, toPascalCase };
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -423,6 +423,15 @@ interface ResolverDependency {
|
|
|
423
423
|
/** Whether it's a default import */
|
|
424
424
|
isDefault?: boolean;
|
|
425
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Local function definition used by inline resolver
|
|
428
|
+
*/
|
|
429
|
+
interface LocalFunction {
|
|
430
|
+
/** Function name */
|
|
431
|
+
name: string;
|
|
432
|
+
/** Full function source code */
|
|
433
|
+
source: string;
|
|
434
|
+
}
|
|
426
435
|
/**
|
|
427
436
|
* Fully analyzed endpoint with all computed properties
|
|
428
437
|
*/
|
|
@@ -455,6 +464,8 @@ interface AnalyzedEndpoint {
|
|
|
455
464
|
sourceFile?: string;
|
|
456
465
|
/** Dependencies detected in inline resolver (functions used but not defined locally) */
|
|
457
466
|
resolverDependencies?: ResolverDependency[];
|
|
467
|
+
/** Local functions used by inline resolver */
|
|
468
|
+
localFunctions?: LocalFunction[];
|
|
458
469
|
/** Description */
|
|
459
470
|
description?: string;
|
|
460
471
|
}
|
|
@@ -1068,4 +1079,4 @@ declare function generateFetchClient(schemas: AnalyzedSchema[], config: FetchAda
|
|
|
1068
1079
|
*/
|
|
1069
1080
|
declare function generateFormSchemas(schemas: AnalyzedSchema[]): string;
|
|
1070
1081
|
|
|
1071
|
-
export { type AnalyzedComputed, type AnalyzedEndpoint, type AnalyzedEndpointField, type AnalyzedField, type AnalyzedIndex, type AnalyzedRLS, type AnalyzedRPC, type AnalyzedRelation, type AnalyzedSchema, type AuthProviderConfig, CodeBuilder, type FakerMapping, type FetchAdapterConfig, type FirebaseAdapterConfig, type FrameworkType, type GenerateOptions, type GenerateSQLOptions, type GenerationTarget, type GraphQLAdapterConfig, type MockAdapterConfig, type PGliteAdapterConfig, type PluralizeConfig, RLSBypass, RLSConfig, RLSScopeMapping, type ResolverDependency, type SQLGeneratorResult, type SchemockConfig, type SupabaseAdapterConfig, type TargetMiddlewareConfig, type TargetType, analyzeSchemas, defineConfig, discoverSchemas, fieldToFakerCall, fieldToTsType, generate, generateClaudeMd, generateCursorRules, generateFetchClient, generateFirebaseClient, generateFormSchemas, generateHooks, generateMockClient, generateMockDb, generateMockHandlers, generateSchemockSection, generateSeed, generateSupabaseClient, generateTypes, getDefaultConfig, getRelativePath, loadConfig, mergeClaudeMdContent, pluralize, primitiveToTs, setupAI, toCamelCase, toPascalCase };
|
|
1082
|
+
export { type AnalyzedComputed, type AnalyzedEndpoint, type AnalyzedEndpointField, type AnalyzedField, type AnalyzedIndex, type AnalyzedRLS, type AnalyzedRPC, type AnalyzedRelation, type AnalyzedSchema, type AuthProviderConfig, CodeBuilder, type FakerMapping, type FetchAdapterConfig, type FirebaseAdapterConfig, type FrameworkType, type GenerateOptions, type GenerateSQLOptions, type GenerationTarget, type GraphQLAdapterConfig, type LocalFunction, type MockAdapterConfig, type PGliteAdapterConfig, type PluralizeConfig, RLSBypass, RLSConfig, RLSScopeMapping, type ResolverDependency, type SQLGeneratorResult, type SchemockConfig, type SupabaseAdapterConfig, type TargetMiddlewareConfig, type TargetType, analyzeSchemas, defineConfig, discoverSchemas, fieldToFakerCall, fieldToTsType, generate, generateClaudeMd, generateCursorRules, generateFetchClient, generateFirebaseClient, generateFormSchemas, generateHooks, generateMockClient, generateMockDb, generateMockHandlers, generateSchemockSection, generateSeed, generateSupabaseClient, generateTypes, getDefaultConfig, getRelativePath, loadConfig, mergeClaudeMdContent, pluralize, primitiveToTs, setupAI, toCamelCase, toPascalCase };
|
package/dist/cli/index.js
CHANGED
|
@@ -1263,6 +1263,83 @@ function parseImportsFromFile(filePath) {
|
|
|
1263
1263
|
importCache.set(filePath, imports);
|
|
1264
1264
|
return imports;
|
|
1265
1265
|
}
|
|
1266
|
+
var localFunctionCache = /* @__PURE__ */ new Map();
|
|
1267
|
+
function parseLocalFunctions(filePath) {
|
|
1268
|
+
if (localFunctionCache.has(filePath)) {
|
|
1269
|
+
return localFunctionCache.get(filePath);
|
|
1270
|
+
}
|
|
1271
|
+
const functions = /* @__PURE__ */ new Map();
|
|
1272
|
+
try {
|
|
1273
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1274
|
+
const funcDeclRegex = /^(export\s+)?function\s+(\w+)\s*\([^)]*\)[^{]*\{/gm;
|
|
1275
|
+
let match;
|
|
1276
|
+
while ((match = funcDeclRegex.exec(content)) !== null) {
|
|
1277
|
+
const name = match[2];
|
|
1278
|
+
const source = extractFunctionBody2(content, match.index);
|
|
1279
|
+
if (source) functions.set(name, source);
|
|
1280
|
+
}
|
|
1281
|
+
const arrowRegex = /^(export\s+)?(const|let)\s+(\w+)\s*=\s*(async\s*)?\([^)]*\)[^=]*=>/gm;
|
|
1282
|
+
while ((match = arrowRegex.exec(content)) !== null) {
|
|
1283
|
+
const name = match[3];
|
|
1284
|
+
const source = extractArrowFunction(content, match.index);
|
|
1285
|
+
if (source) functions.set(name, source);
|
|
1286
|
+
}
|
|
1287
|
+
} catch {
|
|
1288
|
+
console.warn(`Warning: Could not parse local functions from ${filePath}`);
|
|
1289
|
+
}
|
|
1290
|
+
localFunctionCache.set(filePath, functions);
|
|
1291
|
+
return functions;
|
|
1292
|
+
}
|
|
1293
|
+
function extractFunctionBody2(content, startIndex) {
|
|
1294
|
+
let braceCount = 0;
|
|
1295
|
+
let started = false;
|
|
1296
|
+
for (let i = startIndex; i < content.length; i++) {
|
|
1297
|
+
if (content[i] === "{") {
|
|
1298
|
+
braceCount++;
|
|
1299
|
+
started = true;
|
|
1300
|
+
} else if (content[i] === "}") {
|
|
1301
|
+
braceCount--;
|
|
1302
|
+
if (started && braceCount === 0) {
|
|
1303
|
+
return content.slice(startIndex, i + 1).trim();
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
function extractArrowFunction(content, startIndex) {
|
|
1310
|
+
const arrowIndex = content.indexOf("=>", startIndex);
|
|
1311
|
+
if (arrowIndex === -1) return null;
|
|
1312
|
+
let i = arrowIndex + 2;
|
|
1313
|
+
while (i < content.length && /\s/.test(content[i])) i++;
|
|
1314
|
+
if (content[i] === "{") {
|
|
1315
|
+
let braceCount = 0;
|
|
1316
|
+
let started = false;
|
|
1317
|
+
for (let j = i; j < content.length; j++) {
|
|
1318
|
+
if (content[j] === "{") {
|
|
1319
|
+
braceCount++;
|
|
1320
|
+
started = true;
|
|
1321
|
+
} else if (content[j] === "}") {
|
|
1322
|
+
braceCount--;
|
|
1323
|
+
if (started && braceCount === 0) {
|
|
1324
|
+
return content.slice(startIndex, j + 1).trim();
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return null;
|
|
1329
|
+
} else {
|
|
1330
|
+
let end = i;
|
|
1331
|
+
let parenCount = 0;
|
|
1332
|
+
while (end < content.length) {
|
|
1333
|
+
if (content[end] === "(") parenCount++;
|
|
1334
|
+
else if (content[end] === ")") parenCount--;
|
|
1335
|
+
else if ((content[end] === ";" || content[end] === "\n") && parenCount === 0) {
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
end++;
|
|
1339
|
+
}
|
|
1340
|
+
return content.slice(startIndex, end).trim() + ";";
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1266
1343
|
function detectUsedIdentifiers(functionSource) {
|
|
1267
1344
|
const withoutStrings = functionSource.replace(/'[^']*'/g, "").replace(/"[^"]*"/g, "").replace(/`[^`]*`/g, "");
|
|
1268
1345
|
const identifiers = /* @__PURE__ */ new Set();
|
|
@@ -1382,6 +1459,7 @@ function analyzeEndpoint(endpoint, endpointFiles) {
|
|
|
1382
1459
|
let mockResolverName;
|
|
1383
1460
|
let mockResolverImportPath;
|
|
1384
1461
|
let resolverDependencies;
|
|
1462
|
+
let localFunctions;
|
|
1385
1463
|
if (isNamedFunction) {
|
|
1386
1464
|
mockResolverName = resolverName;
|
|
1387
1465
|
if (sourceFile) {
|
|
@@ -1391,16 +1469,26 @@ function analyzeEndpoint(endpoint, endpointFiles) {
|
|
|
1391
1469
|
const usedIdentifiers = detectUsedIdentifiers(mockResolverSource);
|
|
1392
1470
|
if (usedIdentifiers.length > 0) {
|
|
1393
1471
|
const fileImports = parseImportsFromFile(sourceFile);
|
|
1472
|
+
const localFuncs = parseLocalFunctions(sourceFile);
|
|
1394
1473
|
const deps = [];
|
|
1474
|
+
const locals = [];
|
|
1395
1475
|
for (const identifier of usedIdentifiers) {
|
|
1396
1476
|
const importPath = fileImports.get(identifier);
|
|
1397
1477
|
if (importPath) {
|
|
1398
1478
|
deps.push({ name: identifier, from: importPath });
|
|
1479
|
+
} else {
|
|
1480
|
+
const localSource = localFuncs.get(identifier);
|
|
1481
|
+
if (localSource) {
|
|
1482
|
+
locals.push({ name: identifier, source: localSource });
|
|
1483
|
+
}
|
|
1399
1484
|
}
|
|
1400
1485
|
}
|
|
1401
1486
|
if (deps.length > 0) {
|
|
1402
1487
|
resolverDependencies = deps;
|
|
1403
1488
|
}
|
|
1489
|
+
if (locals.length > 0) {
|
|
1490
|
+
localFunctions = locals;
|
|
1491
|
+
}
|
|
1404
1492
|
}
|
|
1405
1493
|
}
|
|
1406
1494
|
return {
|
|
@@ -1417,6 +1505,7 @@ function analyzeEndpoint(endpoint, endpointFiles) {
|
|
|
1417
1505
|
mockResolverImportPath,
|
|
1418
1506
|
sourceFile,
|
|
1419
1507
|
resolverDependencies,
|
|
1508
|
+
localFunctions,
|
|
1420
1509
|
description: endpoint.description
|
|
1421
1510
|
};
|
|
1422
1511
|
}
|
|
@@ -2952,6 +3041,18 @@ function generateRouteHelpers(code, schemas) {
|
|
|
2952
3041
|
}
|
|
2953
3042
|
|
|
2954
3043
|
// src/cli/generators/mock/endpoints.ts
|
|
3044
|
+
function injectResolverTypeAnnotation(source, contextType) {
|
|
3045
|
+
const match = source.match(/^(async\s*)?\(([^)]*)\)\s*=>/);
|
|
3046
|
+
if (match) {
|
|
3047
|
+
const [fullMatch, asyncPart = "", params] = match;
|
|
3048
|
+
if (params.includes(":")) {
|
|
3049
|
+
return source;
|
|
3050
|
+
}
|
|
3051
|
+
const replacement = `${asyncPart}(${params}: ${contextType}) =>`;
|
|
3052
|
+
return source.replace(fullMatch, replacement);
|
|
3053
|
+
}
|
|
3054
|
+
return source;
|
|
3055
|
+
}
|
|
2955
3056
|
function generateEndpointTypes(endpoints) {
|
|
2956
3057
|
const code = new CodeBuilder();
|
|
2957
3058
|
code.line();
|
|
@@ -3322,6 +3423,24 @@ function generateEndpointResolvers(endpoints, outputDir) {
|
|
|
3322
3423
|
code.line(`import { ${names.join(", ")} } from '${resolvedPath}';`);
|
|
3323
3424
|
}
|
|
3324
3425
|
}
|
|
3426
|
+
const allLocalFunctions = /* @__PURE__ */ new Map();
|
|
3427
|
+
for (const endpoint of endpoints) {
|
|
3428
|
+
if (endpoint.localFunctions) {
|
|
3429
|
+
for (const fn of endpoint.localFunctions) {
|
|
3430
|
+
if (!allLocalFunctions.has(fn.name)) {
|
|
3431
|
+
allLocalFunctions.set(fn.name, fn.source);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
if (allLocalFunctions.size > 0) {
|
|
3437
|
+
code.line();
|
|
3438
|
+
code.comment("Local helper functions (copied from source files)");
|
|
3439
|
+
for (const [, source] of allLocalFunctions) {
|
|
3440
|
+
code.line(source);
|
|
3441
|
+
code.line();
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3325
3444
|
code.line();
|
|
3326
3445
|
code.comment("Typed resolver function types");
|
|
3327
3446
|
for (const endpoint of endpoints) {
|
|
@@ -3348,7 +3467,11 @@ function generateEndpointResolvers(endpoints, outputDir) {
|
|
|
3348
3467
|
if (endpoint.mockResolverName) {
|
|
3349
3468
|
code.line(`${endpoint.name}: ${endpoint.mockResolverName} as ${endpoint.pascalName}ResolverFn,`);
|
|
3350
3469
|
} else {
|
|
3351
|
-
|
|
3470
|
+
const typedSource = injectResolverTypeAnnotation(
|
|
3471
|
+
endpoint.mockResolverSource,
|
|
3472
|
+
`${endpoint.pascalName}ResolverContext`
|
|
3473
|
+
);
|
|
3474
|
+
code.line(`${endpoint.name}: ${typedSource},`);
|
|
3352
3475
|
}
|
|
3353
3476
|
code.line();
|
|
3354
3477
|
}
|