@spfn/core 0.1.0-alpha.65 → 0.1.0-alpha.68

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,16 +1,16 @@
1
- import { c as createContractGenerator } from '../../generator-DHiAqhKv.js';
2
- export { C as ContractGeneratorConfig } from '../../generator-DHiAqhKv.js';
1
+ import { c as createContractGenerator } from '../../index-DHiAqhKv.js';
2
+ export { C as ContractGeneratorConfig } from '../../index-DHiAqhKv.js';
3
3
 
4
4
  /**
5
- * SPFN Core Generators Registry
5
+ * Built-in Generators Export
6
6
  *
7
- * Exports generators for use with package-based naming convention
8
- * e.g., @spfn/core:contract
7
+ * Provides a registry of all built-in generators
9
8
  */
10
9
 
11
10
  /**
12
- * Generators registry
13
- * Maps generator names to their factory functions
11
+ * Registry of available generators
12
+ *
13
+ * Used by package-based generator loading (e.g., "@spfn/core:contract")
14
14
  */
15
15
  declare const generators: {
16
16
  contract: typeof createContractGenerator;
@@ -1,14 +1,13 @@
1
- import { join, dirname } from 'path';
1
+ import { join } from 'path';
2
2
  import { existsSync, mkdirSync, accessSync, constants, writeFileSync, unlinkSync, createWriteStream, statSync, readdirSync, renameSync, readFileSync } from 'fs';
3
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
- // src/codegen/generators/contract/generator.ts
8
- async function scanContracts(contractsDir) {
7
+ // src/codegen/built-in/contract/index.ts
8
+ async function scanContracts(contractsDir, packagePrefix) {
9
9
  const contractFiles = await scanContractFiles(contractsDir);
10
10
  const mappings = [];
11
- const packagePrefix = getPackagePrefix(contractsDir);
12
11
  for (let i = 0; i < contractFiles.length; i++) {
13
12
  const filePath = contractFiles[i];
14
13
  const exports = extractContractExports(filePath);
@@ -19,10 +18,14 @@ async function scanContracts(contractsDir) {
19
18
  `Contract '${contractExport.name}' in ${filePath} must use absolute path. Found: '${contractExport.path}'. Use '/your-path' instead.`
20
19
  );
21
20
  }
22
- const finalPath = packagePrefix ? `${packagePrefix}${contractExport.path}` : contractExport.path;
21
+ if (packagePrefix && !contractExport.path.startsWith(packagePrefix)) {
22
+ throw new Error(
23
+ `Contract '${contractExport.name}' in ${filePath} must include package prefix. Expected path to start with '${packagePrefix}', but found: '${contractExport.path}'. Example: path: '${packagePrefix}/${contractExport.path}'`
24
+ );
25
+ }
23
26
  mappings.push({
24
27
  method: contractExport.method,
25
- path: finalPath,
28
+ path: contractExport.path,
26
29
  contractName: contractExport.name,
27
30
  contractImportPath: getImportPath(filePath),
28
31
  routeFile: "",
@@ -172,24 +175,6 @@ function extractContractData(objectLiteral) {
172
175
  function isContractName(name) {
173
176
  return name.indexOf("Contract") !== -1 || name.indexOf("contract") !== -1 || name.endsWith("Schema") || name.endsWith("schema");
174
177
  }
175
- function getPackagePrefix(contractsDir) {
176
- try {
177
- let currentDir = dirname(contractsDir);
178
- for (let i = 0; i < 5; i++) {
179
- const packageJsonPath = join(currentDir, "package.json");
180
- try {
181
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
182
- if (packageJson.spfn?.prefix) {
183
- return packageJson.spfn.prefix;
184
- }
185
- } catch {
186
- }
187
- currentDir = dirname(currentDir);
188
- }
189
- } catch {
190
- }
191
- return "";
192
- }
193
178
  function getImportPath(filePath) {
194
179
  const srcIndex = filePath.indexOf("/src/");
195
180
  if (srcIndex === -1) {
@@ -206,7 +191,7 @@ function getImportPath(filePath) {
206
191
  return "@/" + cleanPath;
207
192
  }
208
193
 
209
- // src/codegen/scanners/route-scanner.ts
194
+ // src/codegen/built-in/contract/helpers.ts
210
195
  function groupByResource(mappings) {
211
196
  const grouped = {};
212
197
  for (let i = 0; i < mappings.length; i++) {
@@ -220,7 +205,11 @@ function groupByResource(mappings) {
220
205
  return grouped;
221
206
  }
222
207
  function extractResourceName(path) {
223
- const segments = path.slice(1).split("/").filter((s) => s && s !== "*");
208
+ let processedPath = path;
209
+ if (!processedPath.startsWith("/")) {
210
+ processedPath = "/" + processedPath;
211
+ }
212
+ const segments = processedPath.slice(1).split("/").filter((s) => s && s !== "*");
224
213
  const staticSegments = [];
225
214
  for (let i = 0; i < segments.length; i++) {
226
215
  const seg = segments[i];
@@ -258,6 +247,8 @@ function toCamelCase(str, capitalize) {
258
247
  }
259
248
  return result.join("");
260
249
  }
250
+
251
+ // src/codegen/built-in/contract/emitter.ts
261
252
  async function generateClient(mappings, options) {
262
253
  const startTime = Date.now();
263
254
  const grouped = groupByResource(mappings);
@@ -382,6 +373,12 @@ function countUniqueContractFiles(mappings) {
382
373
  }
383
374
  return files.size;
384
375
  }
376
+ function toKebabCase(str) {
377
+ if (str.length === 0) {
378
+ return str;
379
+ }
380
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
381
+ }
385
382
  async function generateSplitClient(_mappings, grouped, options) {
386
383
  const outputPath = options.outputPath;
387
384
  const outputDir = outputPath.endsWith(".ts") || outputPath.endsWith(".js") ? outputPath.replace(/\.[jt]s$/, "") : outputPath;
@@ -391,7 +388,8 @@ async function generateSplitClient(_mappings, grouped, options) {
391
388
  const resourceName = resourceNames[i];
392
389
  const routes = grouped[resourceName];
393
390
  const code = generateResourceFile(resourceName, routes, options);
394
- const filePath = `${outputDir}/${resourceName}.ts`;
391
+ const kebabName = toKebabCase(resourceName);
392
+ const filePath = `${outputDir}/${kebabName}.ts`;
395
393
  await writeFile(filePath, code, "utf-8");
396
394
  }
397
395
  const indexCode = generateIndexFile(resourceNames, options);
@@ -480,14 +478,16 @@ function generateIndexFile(resourceNames, options) {
480
478
  `;
481
479
  for (let i = 0; i < resourceNames.length; i++) {
482
480
  const resourceName = resourceNames[i];
483
- code += `export * from './${resourceName}.js';
481
+ const kebabName = toKebabCase(resourceName);
482
+ code += `export * from './${kebabName}.js';
484
483
  `;
485
484
  }
486
485
  code += `
487
486
  `;
488
487
  for (let i = 0; i < resourceNames.length; i++) {
489
488
  const resourceName = resourceNames[i];
490
- code += `import { ${resourceName} } from './${resourceName}.js';
489
+ const kebabName = toKebabCase(resourceName);
490
+ code += `import { ${resourceName} } from './${kebabName}.js';
491
491
  `;
492
492
  }
493
493
  code += `
@@ -1281,7 +1281,7 @@ function initializeLogger() {
1281
1281
  }
1282
1282
  var logger = initializeLogger();
1283
1283
 
1284
- // src/codegen/generators/contract/generator.ts
1284
+ // src/codegen/built-in/contract/index.ts
1285
1285
  var contractLogger = logger.child("contract-gen");
1286
1286
  var DEFAULT_CONTRACTS_DIR = "src/lib/contracts";
1287
1287
  var DEFAULT_OUTPUT_PATH = "src/lib/api";
@@ -1343,6 +1343,8 @@ function createContractGenerator(config = {}) {
1343
1343
  const cwd = options.cwd;
1344
1344
  const fullContractsDir = join(cwd, contractsDir);
1345
1345
  const fullOutputPath = join(cwd, outputPath);
1346
+ const prefix = readPrefixFromPackageJson(cwd);
1347
+ const apiName = generateApiName(prefix);
1346
1348
  try {
1347
1349
  if (!existsSync(fullContractsDir)) {
1348
1350
  if (options.debug) {
@@ -1364,6 +1366,7 @@ function createContractGenerator(config = {}) {
1364
1366
  outputPath: fullOutputPath,
1365
1367
  changedFilePath: changedFile.path,
1366
1368
  baseUrl: config.baseUrl,
1369
+ apiName,
1367
1370
  debug: options.debug
1368
1371
  });
1369
1372
  if (success) {
@@ -1376,7 +1379,7 @@ function createContractGenerator(config = {}) {
1376
1379
  contractLogger.info("Incremental update failed, doing full regen");
1377
1380
  }
1378
1381
  }
1379
- const allContracts = await scanContracts(fullContractsDir);
1382
+ const allContracts = await scanContracts(fullContractsDir, prefix);
1380
1383
  if (allContracts.length === 0) {
1381
1384
  if (options.debug) {
1382
1385
  contractLogger.warn("No contracts found");
@@ -1384,8 +1387,6 @@ function createContractGenerator(config = {}) {
1384
1387
  contractCache = null;
1385
1388
  return;
1386
1389
  }
1387
- const prefix = readPrefixFromPackageJson(cwd);
1388
- const apiName = generateApiName(prefix);
1389
1390
  const clientOptions = createClientOptions(fullContractsDir, fullOutputPath, config.baseUrl, apiName);
1390
1391
  const stats = await generateClient(allContracts, clientOptions);
1391
1392
  contractCache = {
@@ -1409,7 +1410,7 @@ function createContractGenerator(config = {}) {
1409
1410
  };
1410
1411
  }
1411
1412
  async function attemptIncrementalUpdate(options) {
1412
- const { cwd, contractsDir, outputPath, changedFilePath, baseUrl, debug } = options;
1413
+ const { cwd, contractsDir, outputPath, changedFilePath, baseUrl, apiName, debug } = options;
1413
1414
  if (!contractCache) {
1414
1415
  return false;
1415
1416
  }
@@ -1434,7 +1435,7 @@ async function attemptIncrementalUpdate(options) {
1434
1435
  }
1435
1436
  return true;
1436
1437
  }
1437
- const clientOptions = createClientOptions(contractsDir, outputPath, baseUrl);
1438
+ const clientOptions = createClientOptions(contractsDir, outputPath, baseUrl, apiName);
1438
1439
  const stats = await generateClient(updatedContracts, clientOptions);
1439
1440
  contractCache = {
1440
1441
  contracts: updatedContracts,