@stackweld/core 0.3.1 → 0.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @stackweld/core
2
2
 
3
- [![Version](https://img.shields.io/badge/version-0.3.0-blue.svg)](https://github.com/mundowise/Stackweld/releases)
3
+ [![Version](https://img.shields.io/badge/version-0.4.0-blue.svg)](https://github.com/mundowise/Stackweld/releases)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
6
  The core engine for Stackweld. Handles stack persistence, compatibility rules, scaffolding orchestration, Docker lifecycle, and all analysis modules.
@@ -1,7 +1,3 @@
1
- /**
2
- * Compose Generator — Pure function that generates docker-compose.yml content.
3
- * No disk I/O — returns structured data for preview or writing.
4
- */
5
1
  export interface ComposePreviewResult {
6
2
  yaml: string;
7
3
  services: string[];
@@ -24,10 +20,6 @@ interface ComposeTechnology {
24
20
  };
25
21
  port?: number;
26
22
  }
27
- /**
28
- * Generate a docker-compose.yml preview from a list of technologies.
29
- * Only includes technologies that have a `dockerImage`.
30
- */
31
23
  export declare function generateComposePreview(technologies: ComposeTechnology[], projectName: string): ComposePreviewResult;
32
24
  export {};
33
25
  //# sourceMappingURL=compose-generator.d.ts.map
@@ -1,17 +1,10 @@
1
- /**
2
- * Compose Generator — Pure function that generates docker-compose.yml content.
3
- * No disk I/O — returns structured data for preview or writing.
4
- */
1
+ // No disk I/O — returns structured data, caller decides what to write.
5
2
  const DATA_MOUNTS = {
6
3
  postgresql: "/var/lib/postgresql/data",
7
4
  mysql: "/var/lib/mysql",
8
5
  mongodb: "/data/db",
9
6
  redis: "/data",
10
7
  };
11
- /**
12
- * Generate a docker-compose.yml preview from a list of technologies.
13
- * Only includes technologies that have a `dockerImage`.
14
- */
15
8
  export function generateComposePreview(technologies, projectName) {
16
9
  const services = [];
17
10
  const ports = {};
@@ -26,13 +19,11 @@ export function generateComposePreview(technologies, projectName) {
26
19
  lines.push(` ${tech.id}:`);
27
20
  lines.push(` image: ${tech.dockerImage}`);
28
21
  lines.push(" restart: unless-stopped");
29
- // Ports
30
22
  if (port) {
31
23
  lines.push(" ports:");
32
24
  lines.push(` - "${port}:${port}"`);
33
25
  ports[tech.id] = port;
34
26
  }
35
- // Environment variables
36
27
  const envVars = tech.envVars ? Object.entries(tech.envVars) : [];
37
28
  if (envVars.length > 0) {
38
29
  lines.push(" environment:");
@@ -40,7 +31,6 @@ export function generateComposePreview(technologies, projectName) {
40
31
  lines.push(` ${key}: "${value}"`);
41
32
  }
42
33
  }
43
- // Health check
44
34
  if (tech.healthCheck) {
45
35
  lines.push(" healthcheck:");
46
36
  if (tech.healthCheck.command) {
@@ -59,7 +49,6 @@ export function generateComposePreview(technologies, projectName) {
59
49
  lines.push(` retries: ${tech.healthCheck.retries}`);
60
50
  }
61
51
  }
62
- // Volumes for databases
63
52
  const mountPath = DATA_MOUNTS[tech.id];
64
53
  if (mountPath && tech.category === "database") {
65
54
  const volName = `${tech.id}_data`;
@@ -67,12 +56,10 @@ export function generateComposePreview(technologies, projectName) {
67
56
  lines.push(` - ${volName}:${mountPath}`);
68
57
  volumes.push(volName);
69
58
  }
70
- // Network
71
59
  lines.push(" networks:");
72
60
  lines.push(` - ${networkName}`);
73
61
  lines.push("");
74
62
  }
75
- // Named volumes
76
63
  if (volumes.length > 0) {
77
64
  lines.push("volumes:");
78
65
  for (const vol of volumes) {
@@ -80,7 +67,6 @@ export function generateComposePreview(technologies, projectName) {
80
67
  }
81
68
  lines.push("");
82
69
  }
83
- // Network
84
70
  lines.push("networks:");
85
71
  lines.push(` ${networkName}:`);
86
72
  lines.push(" driver: bridge");
@@ -0,0 +1,29 @@
1
+ export type LicenseTier = "community" | "pro" | "enterprise";
2
+ export interface LicenseInfo {
3
+ tier: LicenseTier;
4
+ email: string | null;
5
+ validUntil: string | null;
6
+ active: boolean;
7
+ }
8
+ export interface FeatureGate {
9
+ feature: string;
10
+ requiredTier: LicenseTier;
11
+ description: string;
12
+ }
13
+ export declare function activateLicense(key: string, email: string): {
14
+ success: boolean;
15
+ message: string;
16
+ };
17
+ export declare function deactivateLicense(): {
18
+ success: boolean;
19
+ message: string;
20
+ };
21
+ export declare function getLicenseInfo(): LicenseInfo;
22
+ export declare function checkFeatureAccess(feature: string): {
23
+ allowed: boolean;
24
+ requiredTier: LicenseTier;
25
+ message: string;
26
+ };
27
+ export declare function listFeatures(): FeatureGate[];
28
+ export declare function getFeatureTier(feature: string): LicenseTier;
29
+ //# sourceMappingURL=license-manager.d.ts.map
@@ -0,0 +1,130 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ const PRO_FEATURES = [
5
+ {
6
+ feature: "ai",
7
+ requiredTier: "pro",
8
+ description: "AI-powered suggestions, README generation, and architecture explanations",
9
+ },
10
+ {
11
+ feature: "infra",
12
+ requiredTier: "pro",
13
+ description: "Infrastructure as Code generation (VPS, AWS, GCP)",
14
+ },
15
+ { feature: "cost", requiredTier: "pro", description: "Hosting cost estimation across providers" },
16
+ { feature: "benchmark", requiredTier: "pro", description: "Stack performance profiling" },
17
+ { feature: "migrate", requiredTier: "pro", description: "Technology migration planner" },
18
+ { feature: "deploy", requiredTier: "pro", description: "Deployment configuration generation" },
19
+ { feature: "plugin-install", requiredTier: "pro", description: "Install community plugins" },
20
+ { feature: "cloud-sync", requiredTier: "enterprise", description: "Sync stacks across machines" },
21
+ {
22
+ feature: "team-standards",
23
+ requiredTier: "enterprise",
24
+ description: "Enforce team-wide stack standards",
25
+ },
26
+ {
27
+ feature: "audit-log",
28
+ requiredTier: "enterprise",
29
+ description: "Detailed audit trail of all operations",
30
+ },
31
+ ];
32
+ const TIER_HIERARCHY = {
33
+ community: 0,
34
+ pro: 1,
35
+ enterprise: 2,
36
+ };
37
+ function getLicensePath() {
38
+ const home = process.env.HOME || process.env.USERPROFILE || "~";
39
+ return path.join(home, ".stackweld", "license.json");
40
+ }
41
+ function hashKey(key) {
42
+ return createHash("sha256").update(key).digest("hex");
43
+ }
44
+ function validateKeyFormat(key) {
45
+ // Format: SW-PRO-XXXX-XXXX-XXXX or SW-ENT-XXXX-XXXX-XXXX
46
+ return /^SW-(PRO|ENT)-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
47
+ }
48
+ function tierFromKey(key) {
49
+ if (key.startsWith("SW-ENT-"))
50
+ return "enterprise";
51
+ if (key.startsWith("SW-PRO-"))
52
+ return "pro";
53
+ return "community";
54
+ }
55
+ export function activateLicense(key, email) {
56
+ if (!validateKeyFormat(key)) {
57
+ return {
58
+ success: false,
59
+ message: "Invalid license key format. Expected: SW-PRO-XXXX-XXXX-XXXX",
60
+ };
61
+ }
62
+ const tier = tierFromKey(key);
63
+ const licensePath = getLicensePath();
64
+ const dir = path.dirname(licensePath);
65
+ if (!fs.existsSync(dir))
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ const license = {
68
+ keyHash: hashKey(key),
69
+ tier,
70
+ email,
71
+ activatedAt: new Date().toISOString(),
72
+ validUntil: null,
73
+ };
74
+ fs.writeFileSync(licensePath, JSON.stringify(license, null, 2), "utf-8");
75
+ return {
76
+ success: true,
77
+ message: `${tier.charAt(0).toUpperCase() + tier.slice(1)} license activated for ${email}`,
78
+ };
79
+ }
80
+ export function deactivateLicense() {
81
+ const licensePath = getLicensePath();
82
+ if (fs.existsSync(licensePath)) {
83
+ fs.unlinkSync(licensePath);
84
+ return { success: true, message: "License deactivated. Reverted to Community tier." };
85
+ }
86
+ return { success: false, message: "No active license found." };
87
+ }
88
+ export function getLicenseInfo() {
89
+ const licensePath = getLicensePath();
90
+ if (!fs.existsSync(licensePath)) {
91
+ return { tier: "community", email: null, validUntil: null, active: false };
92
+ }
93
+ try {
94
+ const data = JSON.parse(fs.readFileSync(licensePath, "utf-8"));
95
+ const expired = data.validUntil ? new Date(data.validUntil) < new Date() : false;
96
+ return {
97
+ tier: expired ? "community" : data.tier,
98
+ email: data.email,
99
+ validUntil: data.validUntil,
100
+ active: !expired,
101
+ };
102
+ }
103
+ catch {
104
+ return { tier: "community", email: null, validUntil: null, active: false };
105
+ }
106
+ }
107
+ export function checkFeatureAccess(feature) {
108
+ const gate = PRO_FEATURES.find((f) => f.feature === feature);
109
+ if (!gate)
110
+ return { allowed: true, requiredTier: "community", message: "" };
111
+ const license = getLicenseInfo();
112
+ const userLevel = TIER_HIERARCHY[license.tier];
113
+ const requiredLevel = TIER_HIERARCHY[gate.requiredTier];
114
+ if (userLevel >= requiredLevel) {
115
+ return { allowed: true, requiredTier: gate.requiredTier, message: "" };
116
+ }
117
+ return {
118
+ allowed: false,
119
+ requiredTier: gate.requiredTier,
120
+ message: `"${gate.description}" requires a ${gate.requiredTier} license. Upgrade at https://stackweld.dev/pro`,
121
+ };
122
+ }
123
+ export function listFeatures() {
124
+ return PRO_FEATURES;
125
+ }
126
+ export function getFeatureTier(feature) {
127
+ const gate = PRO_FEATURES.find((f) => f.feature === feature);
128
+ return gate?.requiredTier ?? "community";
129
+ }
130
+ //# sourceMappingURL=license-manager.js.map
@@ -1,16 +1,8 @@
1
- /**
2
- * Stack Engine — CRUD operations for stack definitions.
3
- * Handles creation, validation, versioning, and persistence.
4
- */
5
1
  import type { StackDefinition, StackProfile, StackTechnology, StackVersion, ValidationResult } from "../types/index.js";
6
2
  import type { RulesEngine } from "./rules-engine.js";
7
3
  export declare class StackEngine {
8
4
  private rules;
9
5
  constructor(rules: RulesEngine);
10
- /**
11
- * Create a new stack definition.
12
- * Validates technologies and auto-resolves dependencies.
13
- */
14
6
  create(opts: {
15
7
  name: string;
16
8
  description?: string;
@@ -21,21 +13,9 @@ export declare class StackEngine {
21
13
  stack: StackDefinition;
22
14
  validation: ValidationResult;
23
15
  };
24
- /**
25
- * Get a stack by ID.
26
- */
27
16
  get(id: string): StackDefinition | null;
28
- /**
29
- * List all stacks.
30
- */
31
17
  list(): StackDefinition[];
32
- /**
33
- * Delete a stack.
34
- */
35
18
  delete(id: string): boolean;
36
- /**
37
- * Update a stack. Auto-increments version and saves snapshot.
38
- */
39
19
  update(id: string, changes: Partial<Pick<StackDefinition, "name" | "description" | "profile" | "technologies" | "tags">>): {
40
20
  stack: StackDefinition;
41
21
  validation: ValidationResult;
@@ -1,7 +1,3 @@
1
- /**
2
- * Stack Engine — CRUD operations for stack definitions.
3
- * Handles creation, validation, versioning, and persistence.
4
- */
5
1
  import { randomUUID } from "node:crypto";
6
2
  import { getDatabase } from "../db/database.js";
7
3
  export class StackEngine {
@@ -9,13 +5,8 @@ export class StackEngine {
9
5
  constructor(rules) {
10
6
  this.rules = rules;
11
7
  }
12
- /**
13
- * Create a new stack definition.
14
- * Validates technologies and auto-resolves dependencies.
15
- */
16
8
  create(opts) {
17
9
  const validation = this.rules.validate(opts.technologies);
18
- // Add auto-resolved dependencies
19
10
  const allTechs = [...opts.technologies];
20
11
  for (const depId of validation.resolvedDependencies) {
21
12
  const tech = this.rules.getTechnology(depId);
@@ -27,7 +18,6 @@ export class StackEngine {
27
18
  });
28
19
  }
29
20
  }
30
- // Apply port assignments to existing techs
31
21
  for (const t of allTechs) {
32
22
  if (validation.portAssignments[t.technologyId]) {
33
23
  t.port = validation.portAssignments[t.technologyId];
@@ -50,9 +40,6 @@ export class StackEngine {
50
40
  }
51
41
  return { stack, validation };
52
42
  }
53
- /**
54
- * Get a stack by ID.
55
- */
56
43
  get(id) {
57
44
  const db = getDatabase();
58
45
  const row = db.prepare("SELECT * FROM stacks WHERE id = ?").get(id);
@@ -78,25 +65,16 @@ export class StackEngine {
78
65
  })),
79
66
  };
80
67
  }
81
- /**
82
- * List all stacks.
83
- */
84
68
  list() {
85
69
  const db = getDatabase();
86
70
  const rows = db.prepare("SELECT id FROM stacks ORDER BY updated_at DESC").all();
87
71
  return rows.map((r) => this.get(r.id)).filter(Boolean);
88
72
  }
89
- /**
90
- * Delete a stack.
91
- */
92
73
  delete(id) {
93
74
  const db = getDatabase();
94
75
  const result = db.prepare("DELETE FROM stacks WHERE id = ?").run(id);
95
76
  return result.changes > 0;
96
77
  }
97
- /**
98
- * Update a stack. Auto-increments version and saves snapshot.
99
- */
100
78
  update(id, changes) {
101
79
  const existing = this.get(id);
102
80
  if (!existing)
@@ -112,7 +90,6 @@ export class StackEngine {
112
90
  updated_at = ?, tags = ?
113
91
  WHERE id = ?
114
92
  `).run(changes.name || existing.name, changes.description ?? existing.description, changes.profile || existing.profile, newVersion, now, JSON.stringify(changes.tags || existing.tags), id);
115
- // Replace technologies
116
93
  db.prepare("DELETE FROM stack_technologies WHERE stack_id = ?").run(id);
117
94
  for (const t of techs) {
118
95
  db.prepare(`
@@ -120,7 +97,6 @@ export class StackEngine {
120
97
  VALUES (?, ?, ?, ?, ?)
121
98
  `).run(id, t.technologyId, t.version, t.port || null, JSON.stringify(t.config || {}));
122
99
  }
123
- // Save version snapshot
124
100
  const updatedStack = this.get(id);
125
101
  this.saveVersionSnapshot(updatedStack, `Updated to v${newVersion}`);
126
102
  return { stack: updatedStack, validation };
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export type { HealthCheck, HealthReport } from "./engine/health-checker.js";
11
11
  export { checkProjectHealth } from "./engine/health-checker.js";
12
12
  export type { DeployTarget, InfraOutput } from "./engine/infra-generator.js";
13
13
  export { generateInfra } from "./engine/infra-generator.js";
14
+ export type { FeatureGate, LicenseInfo, LicenseTier } from "./engine/license-manager.js";
15
+ export { activateLicense, checkFeatureAccess, deactivateLicense, getLicenseInfo, listFeatures, } from "./engine/license-manager.js";
14
16
  export type { MigrationPlan, MigrationStep } from "./engine/migration-planner.js";
15
17
  export { planMigration } from "./engine/migration-planner.js";
16
18
  export type { PerformanceProfile, TechPerformance, } from "./engine/performance-profiler.js";
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ export { estimateCost } from "./engine/cost-estimator.js";
7
7
  export { checkDangerous, parseEnvFile, syncEnv } from "./engine/env-analyzer.js";
8
8
  export { checkProjectHealth } from "./engine/health-checker.js";
9
9
  export { generateInfra } from "./engine/infra-generator.js";
10
+ export { activateLicense, checkFeatureAccess, deactivateLicense, getLicenseInfo, listFeatures, } from "./engine/license-manager.js";
10
11
  export { planMigration } from "./engine/migration-planner.js";
11
12
  export { profilePerformance } from "./engine/performance-profiler.js";
12
13
  export { getPluginDir, getPluginInfo, installPlugin, listPlugins, loadPlugin, loadPluginTechnologies, removePlugin, } from "./engine/plugin-loader.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackweld/core",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Core engine for Stackweld — rules, scaffold orchestrator, runtime manager, tech installer, and SQLite persistence.",
5
5
  "license": "MIT",
6
6
  "author": "Orlando Fernandez <hello@xplustechnologies.com>",
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025-2026 Orlando Fernandez / XPlus Technologies LLC
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.