@spfn/core 0.1.0-alpha.63 → 0.1.0-alpha.64
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.
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { join
|
|
1
|
+
import { join } from 'path';
|
|
2
2
|
import { existsSync, mkdirSync, accessSync, constants, writeFileSync, unlinkSync, createWriteStream, statSync, readdirSync, renameSync, readFileSync } from 'fs';
|
|
3
|
-
import {
|
|
3
|
+
import { readdir, stat, mkdir, writeFile } from 'fs/promises';
|
|
4
4
|
import * as ts from 'typescript';
|
|
5
5
|
import pino from 'pino';
|
|
6
6
|
|
|
7
7
|
// src/codegen/generators/contract-generator.ts
|
|
8
|
-
async function scanContracts(
|
|
9
|
-
const contractFiles = await scanContractFiles(
|
|
8
|
+
async function scanContracts(contractsDir) {
|
|
9
|
+
const contractFiles = await scanContractFiles(contractsDir);
|
|
10
10
|
const mappings = [];
|
|
11
11
|
for (let i = 0; i < contractFiles.length; i++) {
|
|
12
12
|
const filePath = contractFiles[i];
|
|
@@ -24,7 +24,6 @@ async function scanContracts(routesDir) {
|
|
|
24
24
|
contractName: contractExport.name,
|
|
25
25
|
contractImportPath: getImportPath(filePath),
|
|
26
26
|
routeFile: "",
|
|
27
|
-
// Not needed anymore
|
|
28
27
|
contractFile: filePath,
|
|
29
28
|
hasQuery: contractExport.hasQuery,
|
|
30
29
|
hasBody: contractExport.hasBody,
|
|
@@ -37,19 +36,14 @@ async function scanContracts(routesDir) {
|
|
|
37
36
|
async function scanContractFiles(dir, files = []) {
|
|
38
37
|
try {
|
|
39
38
|
const entries = await readdir(dir);
|
|
40
|
-
const isLibContracts = dir.includes("/lib/contracts");
|
|
41
39
|
for (let i = 0; i < entries.length; i++) {
|
|
42
40
|
const entry = entries[i];
|
|
43
41
|
const fullPath = join(dir, entry);
|
|
44
42
|
const fileStat = await stat(fullPath);
|
|
45
43
|
if (fileStat.isDirectory()) {
|
|
46
44
|
await scanContractFiles(fullPath, files);
|
|
47
|
-
} else if (isLibContracts) {
|
|
48
|
-
if ((entry.endsWith(".ts") || entry.endsWith(".js") || entry.endsWith(".mjs")) && !entry.endsWith(".d.ts") && !entry.endsWith(".test.ts") && !entry.endsWith(".test.js") && !entry.endsWith(".test.mjs")) {
|
|
49
|
-
files.push(fullPath);
|
|
50
|
-
}
|
|
51
45
|
} else {
|
|
52
|
-
if (entry
|
|
46
|
+
if ((entry.endsWith(".ts") || entry.endsWith(".js") || entry.endsWith(".mjs")) && !entry.endsWith(".d.ts") && !entry.endsWith(".test.ts") && !entry.endsWith(".test.js") && !entry.endsWith(".test.mjs")) {
|
|
53
47
|
files.push(fullPath);
|
|
54
48
|
}
|
|
55
49
|
}
|
|
@@ -176,7 +170,7 @@ function extractContractData(objectLiteral) {
|
|
|
176
170
|
function isContractName(name) {
|
|
177
171
|
return name.indexOf("Contract") !== -1 || name.indexOf("contract") !== -1 || name.endsWith("Schema") || name.endsWith("schema");
|
|
178
172
|
}
|
|
179
|
-
function getImportPath(filePath
|
|
173
|
+
function getImportPath(filePath) {
|
|
180
174
|
const srcIndex = filePath.indexOf("/src/");
|
|
181
175
|
if (srcIndex === -1) {
|
|
182
176
|
throw new Error(`Cannot determine import path for ${filePath}: /src/ directory not found`);
|
|
@@ -250,13 +244,7 @@ async function generateClient(mappings, options) {
|
|
|
250
244
|
const startTime = Date.now();
|
|
251
245
|
const grouped = groupByResource(mappings);
|
|
252
246
|
const resourceNames = Object.keys(grouped);
|
|
253
|
-
|
|
254
|
-
const code = generateClientCode(mappings, grouped, options);
|
|
255
|
-
await mkdir(dirname(options.outputPath), { recursive: true });
|
|
256
|
-
await writeFile(options.outputPath, code, "utf-8");
|
|
257
|
-
} else {
|
|
258
|
-
await generateSplitClient(mappings, grouped, options);
|
|
259
|
-
}
|
|
247
|
+
await generateSplitClient(mappings, grouped, options);
|
|
260
248
|
return {
|
|
261
249
|
routesScanned: mappings.length,
|
|
262
250
|
contractsFound: mappings.length,
|
|
@@ -266,17 +254,6 @@ async function generateClient(mappings, options) {
|
|
|
266
254
|
duration: Date.now() - startTime
|
|
267
255
|
};
|
|
268
256
|
}
|
|
269
|
-
function generateClientCode(mappings, grouped, options) {
|
|
270
|
-
let code = "";
|
|
271
|
-
code += generateHeader();
|
|
272
|
-
code += generateImports(mappings);
|
|
273
|
-
{
|
|
274
|
-
code += generateTypes(mappings);
|
|
275
|
-
}
|
|
276
|
-
code += generateApiObject(grouped, options);
|
|
277
|
-
code += generateFooter();
|
|
278
|
-
return code;
|
|
279
|
-
}
|
|
280
257
|
function generateHeader() {
|
|
281
258
|
return `/**
|
|
282
259
|
* Auto-generated API Client
|
|
@@ -289,28 +266,6 @@ function generateHeader() {
|
|
|
289
266
|
|
|
290
267
|
`;
|
|
291
268
|
}
|
|
292
|
-
function generateImports(mappings, options) {
|
|
293
|
-
let code = "";
|
|
294
|
-
code += `import { client } from '@spfn/core/client';
|
|
295
|
-
`;
|
|
296
|
-
{
|
|
297
|
-
code += `import type { InferContract } from '@spfn/core';
|
|
298
|
-
`;
|
|
299
|
-
}
|
|
300
|
-
code += `
|
|
301
|
-
`;
|
|
302
|
-
const importGroups = groupContractsByImportPath(mappings);
|
|
303
|
-
const importPaths = Object.keys(importGroups);
|
|
304
|
-
for (let i = 0; i < importPaths.length; i++) {
|
|
305
|
-
const importPath = importPaths[i];
|
|
306
|
-
const contracts = importGroups[importPath];
|
|
307
|
-
code += `import { ${contracts.join(", ")} } from '${importPath}';
|
|
308
|
-
`;
|
|
309
|
-
}
|
|
310
|
-
code += `
|
|
311
|
-
`;
|
|
312
|
-
return code;
|
|
313
|
-
}
|
|
314
269
|
function groupContractsByImportPath(mappings) {
|
|
315
270
|
const groups = {};
|
|
316
271
|
for (let i = 0; i < mappings.length; i++) {
|
|
@@ -329,38 +284,6 @@ function groupContractsByImportPath(mappings) {
|
|
|
329
284
|
}
|
|
330
285
|
return result;
|
|
331
286
|
}
|
|
332
|
-
function generateTypes(mappings, _options) {
|
|
333
|
-
let code = "";
|
|
334
|
-
code += `// ============================================
|
|
335
|
-
`;
|
|
336
|
-
code += `// Auto-generated Types
|
|
337
|
-
`;
|
|
338
|
-
code += `// ============================================
|
|
339
|
-
|
|
340
|
-
`;
|
|
341
|
-
for (let i = 0; i < mappings.length; i++) {
|
|
342
|
-
const mapping = mappings[i];
|
|
343
|
-
const typeName = generateTypeName(mapping);
|
|
344
|
-
const contractType = `typeof ${mapping.contractName}`;
|
|
345
|
-
code += `export type ${typeName}Response = InferContract<${contractType}>['response'];
|
|
346
|
-
`;
|
|
347
|
-
if (mapping.hasQuery) {
|
|
348
|
-
code += `export type ${typeName}Query = InferContract<${contractType}>['query'];
|
|
349
|
-
`;
|
|
350
|
-
}
|
|
351
|
-
if (mapping.hasParams || mapping.path.includes(":")) {
|
|
352
|
-
code += `export type ${typeName}Params = InferContract<${contractType}>['params'];
|
|
353
|
-
`;
|
|
354
|
-
}
|
|
355
|
-
if (mapping.hasBody) {
|
|
356
|
-
code += `export type ${typeName}Body = InferContract<${contractType}>['body'];
|
|
357
|
-
`;
|
|
358
|
-
}
|
|
359
|
-
code += `
|
|
360
|
-
`;
|
|
361
|
-
}
|
|
362
|
-
return code;
|
|
363
|
-
}
|
|
364
287
|
function generateTypeName(mapping) {
|
|
365
288
|
let name = mapping.contractName;
|
|
366
289
|
if (name.endsWith("Contract")) {
|
|
@@ -371,42 +294,13 @@ function generateTypeName(mapping) {
|
|
|
371
294
|
}
|
|
372
295
|
return name;
|
|
373
296
|
}
|
|
374
|
-
function generateApiObject(grouped, options) {
|
|
375
|
-
let code = "";
|
|
376
|
-
code += `/**
|
|
377
|
-
* Type-safe API client
|
|
378
|
-
*/
|
|
379
|
-
export const api = {
|
|
380
|
-
`;
|
|
381
|
-
const resourceNames = Object.keys(grouped);
|
|
382
|
-
for (let i = 0; i < resourceNames.length; i++) {
|
|
383
|
-
const resourceName = resourceNames[i];
|
|
384
|
-
const routes = grouped[resourceName];
|
|
385
|
-
code += ` ${resourceName}: {
|
|
386
|
-
`;
|
|
387
|
-
for (let j = 0; j < routes.length; j++) {
|
|
388
|
-
const route = routes[j];
|
|
389
|
-
code += generateMethodCode(route, options);
|
|
390
|
-
}
|
|
391
|
-
code += ` }`;
|
|
392
|
-
if (i < resourceNames.length - 1) {
|
|
393
|
-
code += `,`;
|
|
394
|
-
}
|
|
395
|
-
code += `
|
|
396
|
-
`;
|
|
397
|
-
}
|
|
398
|
-
code += `} as const;
|
|
399
|
-
|
|
400
|
-
`;
|
|
401
|
-
return code;
|
|
402
|
-
}
|
|
403
297
|
function generateMethodCode(mapping, options) {
|
|
404
298
|
const methodName = generateMethodName(mapping);
|
|
405
299
|
const hasParams = mapping.hasParams || mapping.path.includes(":");
|
|
406
300
|
const hasQuery = mapping.hasQuery || false;
|
|
407
301
|
const hasBody = mapping.hasBody || false;
|
|
408
302
|
let code = "";
|
|
409
|
-
|
|
303
|
+
{
|
|
410
304
|
code += ` /**
|
|
411
305
|
`;
|
|
412
306
|
code += ` * ${mapping.method} ${mapping.path}
|
|
@@ -461,27 +355,6 @@ function generateMethodName(mapping) {
|
|
|
461
355
|
}
|
|
462
356
|
return method;
|
|
463
357
|
}
|
|
464
|
-
function generateFooter() {
|
|
465
|
-
return `/**
|
|
466
|
-
* Export client instance for advanced usage
|
|
467
|
-
*
|
|
468
|
-
* Use this to add interceptors or customize the client:
|
|
469
|
-
*
|
|
470
|
-
* @example
|
|
471
|
-
* \`\`\`ts
|
|
472
|
-
* import { client } from './api';
|
|
473
|
-
* import { createAuthInterceptor } from '@spfn/auth/nextjs';
|
|
474
|
-
* import { NextJSCookieProvider } from '@spfn/auth/nextjs';
|
|
475
|
-
*
|
|
476
|
-
* client.use(createAuthInterceptor({
|
|
477
|
-
* cookieProvider: new NextJSCookieProvider(),
|
|
478
|
-
* encryptionKey: process.env.ENCRYPTION_KEY!
|
|
479
|
-
* }));
|
|
480
|
-
* \`\`\`
|
|
481
|
-
*/
|
|
482
|
-
export { client };
|
|
483
|
-
`;
|
|
484
|
-
}
|
|
485
358
|
function countUniqueContractFiles(mappings) {
|
|
486
359
|
const files = /* @__PURE__ */ new Set();
|
|
487
360
|
for (let i = 0; i < mappings.length; i++) {
|
|
@@ -499,7 +372,7 @@ async function generateSplitClient(_mappings, grouped, options) {
|
|
|
499
372
|
for (let i = 0; i < resourceNames.length; i++) {
|
|
500
373
|
const resourceName = resourceNames[i];
|
|
501
374
|
const routes = grouped[resourceName];
|
|
502
|
-
const code = generateResourceFile(resourceName, routes
|
|
375
|
+
const code = generateResourceFile(resourceName, routes);
|
|
503
376
|
const filePath = `${outputDir}/${resourceName}.ts`;
|
|
504
377
|
await writeFile(filePath, code, "utf-8");
|
|
505
378
|
}
|
|
@@ -568,7 +441,7 @@ function generateResourceFile(resourceName, routes, options) {
|
|
|
568
441
|
`;
|
|
569
442
|
for (let i = 0; i < routes.length; i++) {
|
|
570
443
|
const route = routes[i];
|
|
571
|
-
code += generateMethodCode(route
|
|
444
|
+
code += generateMethodCode(route);
|
|
572
445
|
}
|
|
573
446
|
code += `} as const;
|
|
574
447
|
`;
|
|
@@ -1408,10 +1281,7 @@ function createContractGenerator(config = {}) {
|
|
|
1408
1281
|
return;
|
|
1409
1282
|
}
|
|
1410
1283
|
const stats = await generateClient(allContracts, {
|
|
1411
|
-
|
|
1412
|
-
outputPath,
|
|
1413
|
-
includeJsDoc: true
|
|
1414
|
-
});
|
|
1284
|
+
outputPath});
|
|
1415
1285
|
if (options.debug) {
|
|
1416
1286
|
contractLogger.info("Client generated", {
|
|
1417
1287
|
endpoints: stats.methodsGenerated,
|