@ingenyus/swarm-wasp 1.0.3 → 1.2.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.
Files changed (51) hide show
  1. package/README.md +14 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/common/index.d.ts +1 -0
  4. package/dist/common/index.d.ts.map +1 -1
  5. package/dist/common/index.js +81 -0
  6. package/dist/common/wasp-compatibility.d.ts +10 -0
  7. package/dist/common/wasp-compatibility.d.ts.map +1 -0
  8. package/dist/common/wasp-compatibility.js +82 -0
  9. package/dist/generators/action/action-generator.d.ts.map +1 -1
  10. package/dist/generators/action/action-generator.js +102 -13
  11. package/dist/generators/action/index.js +102 -13
  12. package/dist/generators/action/schema.js +7 -0
  13. package/dist/generators/api/api-generator.d.ts.map +1 -1
  14. package/dist/generators/api/api-generator.js +104 -15
  15. package/dist/generators/api/index.js +104 -15
  16. package/dist/generators/api/schema.js +7 -0
  17. package/dist/generators/api-namespace/api-namespace-generator.d.ts.map +1 -1
  18. package/dist/generators/api-namespace/api-namespace-generator.js +104 -15
  19. package/dist/generators/api-namespace/index.js +104 -15
  20. package/dist/generators/api-namespace/schema.js +7 -0
  21. package/dist/generators/base/component-generator.base.js +101 -13
  22. package/dist/generators/base/index.js +101 -13
  23. package/dist/generators/base/operation-generator.base.js +101 -13
  24. package/dist/generators/base/wasp-generator.base.d.ts +5 -0
  25. package/dist/generators/base/wasp-generator.base.d.ts.map +1 -1
  26. package/dist/generators/base/wasp-generator.base.js +93 -6
  27. package/dist/generators/config/index.js +11 -4
  28. package/dist/generators/config/wasp-config-generator.js +11 -4
  29. package/dist/generators/crud/crud-generator.d.ts.map +1 -1
  30. package/dist/generators/crud/crud-generator.js +102 -13
  31. package/dist/generators/crud/index.js +102 -13
  32. package/dist/generators/crud/schema.js +7 -0
  33. package/dist/generators/feature/feature-generator.d.ts.map +1 -1
  34. package/dist/generators/feature/feature-generator.js +98 -10
  35. package/dist/generators/feature/index.js +98 -10
  36. package/dist/generators/feature/schema.js +7 -0
  37. package/dist/generators/index.js +112 -17
  38. package/dist/generators/job/index.js +102 -13
  39. package/dist/generators/job/job-generator.d.ts.map +1 -1
  40. package/dist/generators/job/job-generator.js +102 -13
  41. package/dist/generators/job/schema.js +7 -0
  42. package/dist/generators/query/index.js +102 -13
  43. package/dist/generators/query/query-generator.d.ts.map +1 -1
  44. package/dist/generators/query/query-generator.js +102 -13
  45. package/dist/generators/query/schema.js +7 -0
  46. package/dist/generators/route/index.js +102 -13
  47. package/dist/generators/route/route-generator.d.ts.map +1 -1
  48. package/dist/generators/route/route-generator.js +102 -13
  49. package/dist/generators/route/schema.js +7 -0
  50. package/dist/index.js +120 -25
  51. package/package.json +11 -6
package/dist/index.js CHANGED
@@ -396,22 +396,102 @@ var TemplateUtility = class {
396
396
  }
397
397
  };
398
398
 
399
+ // src/common/wasp-compatibility.ts
400
+ import { findPackageJson, getVersion } from "@ingenyus/swarm";
401
+ import { execSync } from "child_process";
402
+ import path4 from "path";
403
+ import { fileURLToPath } from "url";
404
+ import * as semver from "semver";
405
+ var cachedSupportedRange = null;
406
+ function getWaspSupportedRange() {
407
+ if (cachedSupportedRange) {
408
+ return cachedSupportedRange;
409
+ }
410
+ const currentFile = fileURLToPath(import.meta.url);
411
+ const currentDir = path4.dirname(currentFile);
412
+ const result = findPackageJson(currentDir, {
413
+ packageName: "@ingenyus/swarm-wasp"
414
+ });
415
+ if (!result) {
416
+ throw new Error(
417
+ "Unable to find package.json for @ingenyus/swarm-wasp. Wasp supported version range must be specified in swarm.wasp field."
418
+ );
419
+ }
420
+ const swarm = result.packageJson.swarm;
421
+ const waspRange = swarm?.wasp;
422
+ if (!waspRange) {
423
+ throw new Error(
424
+ `Wasp supported version range not found in package.json. Please specify swarm.wasp field in @ingenyus/swarm-wasp package.json (e.g., "swarm": { "wasp": ">=0.18.0 <0.20.0" }).`
425
+ );
426
+ }
427
+ cachedSupportedRange = waspRange;
428
+ return cachedSupportedRange;
429
+ }
430
+ function getInstalledWaspVersion(logger) {
431
+ try {
432
+ const output = execSync("wasp version", {
433
+ encoding: "utf8",
434
+ stdio: "pipe"
435
+ });
436
+ const firstLine = output.split("\n")[0]?.trim();
437
+ if (!firstLine) {
438
+ logger.error(
439
+ "Unable to parse Wasp version from command output. Expected version number on first line."
440
+ );
441
+ throw new Error("Unable to parse Wasp version from command output");
442
+ }
443
+ if (!semver.valid(firstLine)) {
444
+ logger.error(
445
+ `Invalid Wasp version format: "${firstLine}". Expected a valid semver version (e.g., "0.18.2").`
446
+ );
447
+ throw new Error("Invalid Wasp version format");
448
+ }
449
+ return firstLine;
450
+ } catch (error) {
451
+ if (error.code === "ENOENT" || error.message?.includes("wasp")) {
452
+ logger.error(
453
+ "Wasp CLI not found. Install using: curl -sSL https://get.wasp.sh/installer.sh | sh -s"
454
+ );
455
+ throw new Error("Wasp CLI not found");
456
+ }
457
+ throw new Error("Unable to determine installed Wasp version");
458
+ }
459
+ }
460
+ function isTestEnvironment() {
461
+ return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || typeof process.env.VITEST !== "undefined";
462
+ }
463
+ function assertWaspCompatible(logger) {
464
+ if (isTestEnvironment()) {
465
+ return;
466
+ }
467
+ const version = getInstalledWaspVersion(logger);
468
+ const supportedRange = getWaspSupportedRange();
469
+ if (!semver.satisfies(version, supportedRange)) {
470
+ const startDir = path4.dirname(fileURLToPath(import.meta.url));
471
+ const packageVersion = getVersion("@ingenyus/swarm-wasp", startDir);
472
+ logger.error(
473
+ `Incompatible Wasp version detected: ${version}. @ingenyus/swarm-wasp@${packageVersion} supports Wasp ${supportedRange}.`
474
+ );
475
+ throw new Error("Incompatible Wasp version");
476
+ }
477
+ }
478
+
399
479
  // src/generators/base/component-generator.base.ts
400
480
  import {
401
481
  hasHelperMethodCall,
402
482
  toKebabCase as toKebabCase2
403
483
  } from "@ingenyus/swarm";
404
- import path6 from "path";
484
+ import path7 from "path";
405
485
 
406
486
  // src/generators/feature/feature-generator.ts
407
487
  import { handleFatalError as handleFatalError2 } from "@ingenyus/swarm";
408
- import path5 from "path";
488
+ import path6 from "path";
409
489
 
410
490
  // src/generators/base/wasp-generator.base.ts
411
491
  import {
412
492
  GeneratorBase,
413
- getConfigManager,
414
- TemplateResolver
493
+ TemplateResolver,
494
+ getConfigManager
415
495
  } from "@ingenyus/swarm";
416
496
 
417
497
  // src/generators/config/wasp-config-generator.ts
@@ -420,14 +500,14 @@ import {
420
500
  handleFatalError,
421
501
  parseHelperMethodDefinition
422
502
  } from "@ingenyus/swarm";
423
- import path4 from "path";
503
+ import path5 from "path";
424
504
  var WaspConfigGenerator = class {
425
505
  constructor(logger = getCLILogger(), fileSystem = realFileSystem) {
426
506
  this.logger = logger;
427
507
  this.fileSystem = fileSystem;
428
508
  this.templateUtility = new TemplateUtility(fileSystem);
429
509
  }
430
- path = path4;
510
+ path = path5;
431
511
  templateUtility;
432
512
  /**
433
513
  * Gets the template path for feature config templates.
@@ -456,7 +536,7 @@ var WaspConfigGenerator = class {
456
536
  this.logger.error(`Template not found: ${templatePath}`);
457
537
  return;
458
538
  }
459
- const configFilePath = path4.join(featureDir, `feature.wasp.ts`);
539
+ const configFilePath = path5.join(featureDir, `feature.wasp.ts`);
460
540
  if (this.fileSystem.existsSync(configFilePath)) {
461
541
  this.logger.warn(`Feature config already exists: ${configFilePath}`);
462
542
  return;
@@ -472,7 +552,7 @@ var WaspConfigGenerator = class {
472
552
  */
473
553
  update(featurePath, declaration) {
474
554
  const configDir = getFeatureDir(this.fileSystem, featurePath);
475
- const configFilePath = path4.join(configDir, `feature.wasp.ts`);
555
+ const configFilePath = path5.join(configDir, `feature.wasp.ts`);
476
556
  if (!this.fileSystem.existsSync(configFilePath)) {
477
557
  const templatePath = this.getTemplatePath("feature.wasp.eta");
478
558
  if (!this.fileSystem.existsSync(templatePath)) {
@@ -868,6 +948,13 @@ var WaspGeneratorBase = class extends GeneratorBase {
868
948
  this.templateUtility = new TemplateUtility(this.fileSystem);
869
949
  this.templateResolver = new TemplateResolver(this.fileSystem);
870
950
  }
951
+ /**
952
+ * Ensures that the installed Wasp version is compatible with this package.
953
+ * Should be called at the start of generator execution.
954
+ */
955
+ ensureWaspCompatible() {
956
+ assertWaspCompatible(this.logger);
957
+ }
871
958
  async loadConfig() {
872
959
  if (this.configLoaded) return;
873
960
  const configManager = getConfigManager();
@@ -984,21 +1071,22 @@ var FeatureGenerator = class extends WaspGeneratorBase {
984
1071
  * @param target - The target path of the generated directory
985
1072
  */
986
1073
  async generate(args) {
1074
+ this.ensureWaspCompatible();
987
1075
  const { target } = args;
988
1076
  const segments = validateFeaturePath(target);
989
1077
  const normalisedPath = normaliseFeaturePath(target);
990
- const sourceRoot = path5.join(findWaspRoot(this.fileSystem), "src");
1078
+ const sourceRoot = path6.join(findWaspRoot(this.fileSystem), "src");
991
1079
  if (segments.length > 1) {
992
1080
  const parentPath = segments.slice(0, -1).join("/");
993
1081
  const parentNormalisedPath = normaliseFeaturePath(parentPath);
994
- const parentFeatureDir = path5.join(sourceRoot, parentNormalisedPath);
1082
+ const parentFeatureDir = path6.join(sourceRoot, parentNormalisedPath);
995
1083
  if (!this.fileSystem.existsSync(parentFeatureDir)) {
996
1084
  handleFatalError2(
997
1085
  `Parent feature '${parentPath}' does not exist. Please create it first.`
998
1086
  );
999
1087
  }
1000
1088
  }
1001
- const featureDir = path5.join(sourceRoot, normalisedPath);
1089
+ const featureDir = path6.join(sourceRoot, normalisedPath);
1002
1090
  this.fileSystem.mkdirSync(featureDir, { recursive: true });
1003
1091
  this.configGenerator.generate(normalisedPath);
1004
1092
  this.logger.success(`Generated feature: ${normalisedPath}`);
@@ -1033,7 +1121,7 @@ var ComponentGeneratorBase = class extends WaspGeneratorBase {
1033
1121
  const currentPath = pathSegments.join("/");
1034
1122
  const featureName = pathSegments[pathSegments.length - 1];
1035
1123
  const featureDir = getFeatureDir(this.fileSystem, currentPath);
1036
- const configPath = path6.join(featureDir, `feature.wasp.ts`);
1124
+ const configPath = path7.join(featureDir, `feature.wasp.ts`);
1037
1125
  if (this.fileSystem.existsSync(configPath)) {
1038
1126
  return configPath;
1039
1127
  }
@@ -1105,7 +1193,7 @@ var ComponentGeneratorBase = class extends WaspGeneratorBase {
1105
1193
  const featureDir = getFeatureDir(this.fileSystem, normalisedPath);
1106
1194
  const typeKey = type.toLowerCase();
1107
1195
  const typeDirectory = TYPE_DIRECTORIES[typeKey];
1108
- const targetDirectory = path6.join(featureDir, typeDirectory);
1196
+ const targetDirectory = path7.join(featureDir, typeDirectory);
1109
1197
  const importDirectory = `@src/${normalisedPath}/${typeDirectory}`;
1110
1198
  return { targetDirectory, importDirectory };
1111
1199
  }
@@ -1471,6 +1559,7 @@ var ActionGenerator = class extends OperationGeneratorBase {
1471
1559
  this.componentType,
1472
1560
  operationName,
1473
1561
  async () => {
1562
+ this.ensureWaspCompatible();
1474
1563
  const configPath = this.validateFeatureConfig(feature);
1475
1564
  const { targetDirectory: operationsDir, importDirectory } = this.ensureTargetDirectory(feature, operationType);
1476
1565
  const importPath = `${importDirectory}/${operationName}`;
@@ -1565,6 +1654,7 @@ var ApiGenerator = class extends ComponentGeneratorBase {
1565
1654
  async generate(args) {
1566
1655
  const apiName = toCamelCase(args.name);
1567
1656
  return this.handleGeneratorError(this.componentType, apiName, async () => {
1657
+ this.ensureWaspCompatible();
1568
1658
  const configPath = this.validateFeatureConfig(args.feature);
1569
1659
  const {
1570
1660
  targetDirectory: apiTargetDirectory,
@@ -1611,7 +1701,7 @@ var ApiGenerator = class extends ComponentGeneratorBase {
1611
1701
  force = false,
1612
1702
  entities,
1613
1703
  method,
1614
- path: path9,
1704
+ path: path10,
1615
1705
  auth,
1616
1706
  customMiddleware
1617
1707
  } = args;
@@ -1621,7 +1711,7 @@ var ApiGenerator = class extends ComponentGeneratorBase {
1621
1711
  feature,
1622
1712
  Array.isArray(entities) ? entities : entities ? [entities] : [],
1623
1713
  method,
1624
- path9,
1714
+ path10,
1625
1715
  apiFile,
1626
1716
  auth,
1627
1717
  importPath,
@@ -1677,7 +1767,7 @@ var ApiGenerator = class extends ComponentGeneratorBase {
1677
1767
 
1678
1768
  // src/generators/api-namespace/api-namespace-generator.ts
1679
1769
  import { toCamelCase as toCamelCase2 } from "@ingenyus/swarm";
1680
- import path7 from "path";
1770
+ import path8 from "path";
1681
1771
 
1682
1772
  // src/generators/api-namespace/schema.ts
1683
1773
  import { registerSchemaMetadata as registerSchemaMetadata4 } from "@ingenyus/swarm";
@@ -1714,12 +1804,13 @@ var ApiNamespaceGenerator = class extends ComponentGeneratorBase {
1714
1804
  this.componentType,
1715
1805
  namespaceName,
1716
1806
  async () => {
1807
+ this.ensureWaspCompatible();
1717
1808
  const configPath = this.validateFeatureConfig(feature);
1718
1809
  const {
1719
1810
  targetDirectory: apiTargetDirectory,
1720
1811
  importDirectory: apiImportDirectory
1721
1812
  } = this.ensureTargetDirectory(feature, "api");
1722
- const middlewareTargetDirectory = path7.join(
1813
+ const middlewareTargetDirectory = path8.join(
1723
1814
  apiTargetDirectory,
1724
1815
  "middleware"
1725
1816
  );
@@ -1871,6 +1962,7 @@ var CrudGenerator = class extends OperationGeneratorBase {
1871
1962
  const crudName = name || toCamelCase3(getPlural2(dataType));
1872
1963
  const crudType = toPascalCase4(crudName);
1873
1964
  return this.handleGeneratorError(this.componentType, crudName, async () => {
1965
+ this.ensureWaspCompatible();
1874
1966
  const configPath = this.validateFeatureConfig(feature);
1875
1967
  const { targetDirectory } = this.ensureTargetDirectory(
1876
1968
  feature,
@@ -2119,6 +2211,7 @@ var JobGenerator = class extends ComponentGeneratorBase {
2119
2211
  async generate(args) {
2120
2212
  const jobName = toCamelCase4(args.name);
2121
2213
  return this.handleGeneratorError(this.componentType, jobName, async () => {
2214
+ this.ensureWaspCompatible();
2122
2215
  const configPath = this.validateFeatureConfig(args.feature);
2123
2216
  const { targetDirectory } = this.ensureTargetDirectory(
2124
2217
  args.feature,
@@ -2260,6 +2353,7 @@ var QueryGenerator = class extends OperationGeneratorBase {
2260
2353
  this.componentType,
2261
2354
  operationName,
2262
2355
  async () => {
2356
+ this.ensureWaspCompatible();
2263
2357
  const configPath = this.validateFeatureConfig(feature);
2264
2358
  const { targetDirectory: operationsDir, importDirectory } = this.ensureTargetDirectory(feature, operationType);
2265
2359
  const importPath = `${importDirectory}/${operationName}`;
@@ -2336,6 +2430,7 @@ var RouteGenerator = class extends ComponentGeneratorBase {
2336
2430
  this.componentType,
2337
2431
  routeName,
2338
2432
  async () => {
2433
+ this.ensureWaspCompatible();
2339
2434
  const configPath = this.validateFeatureConfig(feature);
2340
2435
  const { targetDirectory } = this.ensureTargetDirectory(feature, "page");
2341
2436
  const targetFile = `${targetDirectory}/${fileName}`;
@@ -2404,7 +2499,7 @@ var wasp = createPlugin(
2404
2499
 
2405
2500
  // src/wasp-config/app.ts
2406
2501
  import fs3 from "fs";
2407
- import path8 from "path";
2502
+ import path9 from "path";
2408
2503
  import {
2409
2504
  App as WaspApp
2410
2505
  } from "wasp-config";
@@ -2513,7 +2608,7 @@ var App = class _App extends WaspApp {
2513
2608
  const middlewareImportPath = this.getFeatureImportPath(
2514
2609
  featureName,
2515
2610
  "server",
2516
- path8.join("apis", "middleware"),
2611
+ path9.join("apis", "middleware"),
2517
2612
  name
2518
2613
  );
2519
2614
  super.api(name, {
@@ -2655,7 +2750,7 @@ var App = class _App extends WaspApp {
2655
2750
  const importPath = this.getFeatureImportPath(
2656
2751
  featureName,
2657
2752
  "server",
2658
- path8.join("apis", "middleware"),
2753
+ path9.join("apis", "middleware"),
2659
2754
  name
2660
2755
  );
2661
2756
  super.apiNamespace(name, {
@@ -2697,7 +2792,7 @@ var App = class _App extends WaspApp {
2697
2792
  * Configures all feature modules by scanning the features directory
2698
2793
  */
2699
2794
  async configureFeatures() {
2700
- const featuresDir = path8.join(process.cwd(), "src", "features");
2795
+ const featuresDir = path9.join(process.cwd(), "src", "features");
2701
2796
  if (!fs3.existsSync(featuresDir)) {
2702
2797
  console.warn(
2703
2798
  "Features directory not found, skipping feature configuration"
@@ -2708,11 +2803,11 @@ var App = class _App extends WaspApp {
2708
2803
  let results = [];
2709
2804
  const list = fs3.readdirSync(dir, { withFileTypes: true });
2710
2805
  for (const entry of list) {
2711
- const fullPath = path8.join(dir, entry.name);
2806
+ const fullPath = path9.join(dir, entry.name);
2712
2807
  if (entry.isDirectory()) {
2713
2808
  results = results.concat(getAllFeatureFiles(fullPath));
2714
2809
  } else if (entry.isFile() && entry.name.endsWith(".wasp.ts")) {
2715
- results.push(path8.relative(featuresDir, fullPath));
2810
+ results.push(path9.relative(featuresDir, fullPath));
2716
2811
  }
2717
2812
  }
2718
2813
  return results;
@@ -2720,8 +2815,8 @@ var App = class _App extends WaspApp {
2720
2815
  const featureFiles = getAllFeatureFiles(featuresDir);
2721
2816
  for (const file of featureFiles) {
2722
2817
  try {
2723
- const featureName = path8.dirname(file);
2724
- const modulePath = path8.join(
2818
+ const featureName = path9.dirname(file);
2819
+ const modulePath = path9.join(
2725
2820
  process.cwd(),
2726
2821
  ".wasp",
2727
2822
  "src",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ingenyus/swarm-wasp",
3
3
  "type": "module",
4
- "version": "1.0.3",
4
+ "version": "1.2.0",
5
5
  "description": "Wasp-specific plugins for Swarm - Feature generators, commands, MCP tools, and enhanced Wasp configuration for Wasp development",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -29,20 +29,24 @@
29
29
  "license": "MIT",
30
30
  "repository": {
31
31
  "type": "git",
32
- "url": "https://github.com/genyus/swarm.git",
32
+ "url": "https://github.com/Genyus/swarm",
33
33
  "directory": "packages/swarm-wasp"
34
34
  },
35
- "homepage": "https://github.com/genyus/swarm/tree/main/packages/swarm-wasp#readme",
35
+ "homepage": "https://github.com/Genyus/swarm/tree/main/packages/swarm-wasp#readme",
36
36
  "bugs": {
37
- "url": "https://github.com/genyus/swarm/issues"
37
+ "url": "https://github.com/Genyus/swarm/issues"
38
38
  },
39
39
  "engines": {
40
- "node": ">=18.0.0"
40
+ "node": ">=22.12.0"
41
+ },
42
+ "swarm": {
43
+ "wasp": ">=0.18.0 <0.21.0"
41
44
  },
42
45
  "dependencies": {
43
46
  "@mrleebo/prisma-ast": "^0.13.1",
44
47
  "eta": "^4.4.1",
45
- "@ingenyus/swarm": "1.0.2"
48
+ "semver": "^7.6.3",
49
+ "@ingenyus/swarm": "1.0.4"
46
50
  },
47
51
  "peerDependencies": {
48
52
  "wasp-config": "*",
@@ -55,6 +59,7 @@
55
59
  },
56
60
  "devDependencies": {
57
61
  "@types/node": "^24.10.1",
62
+ "@types/semver": "^7.5.8",
58
63
  "@vitest/coverage-v8": "^3.2.4",
59
64
  "glob": "^11.0.3",
60
65
  "rimraf": "^6.1.0",