@topogram/cli 0.3.43 → 0.3.45
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/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -595,9 +595,9 @@ function printGeneratorHelp() {
|
|
|
595
595
|
console.log("Inspects generator manifests and checks generator pack conformance.");
|
|
596
596
|
console.log("");
|
|
597
597
|
console.log("Notes:");
|
|
598
|
-
console.log(" - list shows bundled generators plus installed package-backed generators declared in package.json.");
|
|
599
|
-
console.log(" - show accepts an installed package name or a bundled fallback generator id.");
|
|
600
|
-
console.log(" - check validates a local generator package path or an already installed package.");
|
|
598
|
+
console.log(" - list shows bundled generators plus installed package-backed generators declared in package.json; it reads manifests only.");
|
|
599
|
+
console.log(" - show accepts an installed package name or a bundled fallback generator id; it does not load adapter code.");
|
|
600
|
+
console.log(" - check validates a local generator package path or an already installed package by loading the adapter and running smoke generation.");
|
|
601
601
|
console.log(" - Topogram does not install generator packages during show or check.");
|
|
602
602
|
console.log(` - package-backed project generators are governed by ${GENERATOR_POLICY_FILE}; bundled topogram/* generators are allowed.`);
|
|
603
603
|
console.log("");
|
|
@@ -1250,6 +1250,7 @@ function printGeneratorCheck(payload) {
|
|
|
1250
1250
|
console.log(`Projection platforms: ${payload.manifest.projectionPlatforms.join(", ")}`);
|
|
1251
1251
|
console.log(`Source mode: ${payload.manifest.source}`);
|
|
1252
1252
|
}
|
|
1253
|
+
console.log("Executes package code: yes (loads adapter and runs smoke generate)");
|
|
1253
1254
|
console.log("");
|
|
1254
1255
|
console.log("Checks:");
|
|
1255
1256
|
for (const check of payload.checks || []) {
|
|
@@ -1285,6 +1286,8 @@ function generatorManifestSummary(manifest, metadata = {}) {
|
|
|
1285
1286
|
stack: manifest.stack || {},
|
|
1286
1287
|
capabilities: manifest.capabilities || {},
|
|
1287
1288
|
source: manifest.source,
|
|
1289
|
+
loadsAdapter: false,
|
|
1290
|
+
executesPackageCode: false,
|
|
1288
1291
|
...(manifest.profile ? { profile: manifest.profile } : {}),
|
|
1289
1292
|
...(manifest.package ? { package: manifest.package } : {}),
|
|
1290
1293
|
...(installCommand ? { installCommand } : {}),
|
|
@@ -1496,6 +1499,8 @@ function printGeneratorList(payload) {
|
|
|
1496
1499
|
const stack = Object.values(generator.stack || {}).join(" + ") || "not declared";
|
|
1497
1500
|
console.log(`- ${id}${generator.version ? `@${generator.version}` : ""} (${generator.surface || "unknown"}, ${status})`);
|
|
1498
1501
|
console.log(` Source: ${generator.source}`);
|
|
1502
|
+
console.log(" Adapter loaded: no");
|
|
1503
|
+
console.log(" Executes package code: no");
|
|
1499
1504
|
if (generator.source === "package") {
|
|
1500
1505
|
console.log(` Installed: ${generator.installed ? "yes" : "no"}`);
|
|
1501
1506
|
}
|
|
@@ -1529,6 +1534,8 @@ function printGeneratorShow(payload) {
|
|
|
1529
1534
|
console.log(`Generator: ${generator.id}@${generator.version}`);
|
|
1530
1535
|
console.log(`Surface: ${generator.surface}`);
|
|
1531
1536
|
console.log(`Source: ${generator.source}${generator.planned ? " (planned)" : ""}`);
|
|
1537
|
+
console.log("Adapter loaded: no");
|
|
1538
|
+
console.log("Executes package code: no");
|
|
1532
1539
|
if (generator.source === "package") {
|
|
1533
1540
|
console.log(`Installed: ${generator.installed ? "yes" : "no"}`);
|
|
1534
1541
|
}
|
|
@@ -1631,7 +1638,7 @@ function dependencySpecForPackage(projectPackage, packageName) {
|
|
|
1631
1638
|
* @param {string} packageName
|
|
1632
1639
|
* @returns {{ version: string|null, resolved: string|null, integrity: string|null, entryPath: string|null }}
|
|
1633
1640
|
*/
|
|
1634
|
-
function
|
|
1641
|
+
function npmLockfileInfoForPackage(lockfile, packageName) {
|
|
1635
1642
|
const packageEntryPath = `node_modules/${packageName}`;
|
|
1636
1643
|
const packageEntry = lockfile?.packages?.[packageEntryPath];
|
|
1637
1644
|
if (packageEntry && typeof packageEntry === "object") {
|
|
@@ -1659,16 +1666,62 @@ function lockfileInfoForPackage(lockfile, packageName) {
|
|
|
1659
1666
|
};
|
|
1660
1667
|
}
|
|
1661
1668
|
|
|
1669
|
+
/**
|
|
1670
|
+
* @param {string} projectRoot
|
|
1671
|
+
* @returns {{ kind: "npm"|"pnpm"|"yarn"|"bun"|null, path: string|null, data: Record<string, any>|null, note: string|null }}
|
|
1672
|
+
*/
|
|
1673
|
+
function lockfileMetadataForProject(projectRoot) {
|
|
1674
|
+
const npmLockfilePath = path.join(projectRoot, "package-lock.json");
|
|
1675
|
+
if (fs.existsSync(npmLockfilePath)) {
|
|
1676
|
+
return {
|
|
1677
|
+
kind: "npm",
|
|
1678
|
+
path: npmLockfilePath,
|
|
1679
|
+
data: readJsonIfPresent(npmLockfilePath),
|
|
1680
|
+
note: null
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
const candidates = [
|
|
1684
|
+
{ kind: "pnpm", file: "pnpm-lock.yaml" },
|
|
1685
|
+
{ kind: "yarn", file: "yarn.lock" },
|
|
1686
|
+
{ kind: "bun", file: "bun.lock" },
|
|
1687
|
+
{ kind: "bun", file: "bun.lockb" }
|
|
1688
|
+
];
|
|
1689
|
+
for (const candidate of candidates) {
|
|
1690
|
+
const lockfilePath = path.join(projectRoot, candidate.file);
|
|
1691
|
+
if (fs.existsSync(lockfilePath)) {
|
|
1692
|
+
return {
|
|
1693
|
+
kind: /** @type {"pnpm"|"yarn"|"bun"} */ (candidate.kind),
|
|
1694
|
+
path: lockfilePath,
|
|
1695
|
+
data: null,
|
|
1696
|
+
note: `${candidate.file} found; package versions are not inspected by this command.`
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
return {
|
|
1701
|
+
kind: null,
|
|
1702
|
+
path: null,
|
|
1703
|
+
data: null,
|
|
1704
|
+
note: null
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1662
1708
|
/**
|
|
1663
1709
|
* @param {string} projectRoot
|
|
1664
1710
|
* @param {string} packageName
|
|
1665
|
-
* @returns {{ dependencyField: string|null, dependencySpec: string|null, installedVersion: string|null, installedPackageJsonPath: string|null, lockfileVersion: string|null, lockfileResolved: string|null, lockfileIntegrity: string|null, lockfileEntryPath: string|null }}
|
|
1711
|
+
* @returns {{ dependencyField: string|null, dependencySpec: string|null, installedVersion: string|null, installedPackageJsonPath: string|null, lockfileKind: "npm"|"pnpm"|"yarn"|"bun"|null, lockfilePath: string|null, lockfileVersion: string|null, lockfileResolved: string|null, lockfileIntegrity: string|null, lockfileEntryPath: string|null, lockfileNote: string|null }}
|
|
1666
1712
|
*/
|
|
1667
1713
|
function packageInfoForGenerator(projectRoot, packageName) {
|
|
1668
1714
|
const projectPackage = readJsonIfPresent(path.join(projectRoot, "package.json"));
|
|
1669
1715
|
const dependency = dependencySpecForPackage(projectPackage, packageName);
|
|
1670
|
-
const lockfile =
|
|
1671
|
-
const lockfileInfo =
|
|
1716
|
+
const lockfile = lockfileMetadataForProject(projectRoot);
|
|
1717
|
+
const lockfileInfo = lockfile.kind === "npm"
|
|
1718
|
+
? npmLockfileInfoForPackage(lockfile.data, packageName)
|
|
1719
|
+
: {
|
|
1720
|
+
version: null,
|
|
1721
|
+
resolved: null,
|
|
1722
|
+
integrity: null,
|
|
1723
|
+
entryPath: null
|
|
1724
|
+
};
|
|
1672
1725
|
const installedPackageJsonPath = path.join(projectRoot, "node_modules", ...packageName.split("/"), "package.json");
|
|
1673
1726
|
const installedPackage = readJsonIfPresent(installedPackageJsonPath);
|
|
1674
1727
|
return {
|
|
@@ -1676,13 +1729,31 @@ function packageInfoForGenerator(projectRoot, packageName) {
|
|
|
1676
1729
|
dependencySpec: dependency.spec,
|
|
1677
1730
|
installedVersion: typeof installedPackage?.version === "string" ? installedPackage.version : null,
|
|
1678
1731
|
installedPackageJsonPath: installedPackage ? installedPackageJsonPath : null,
|
|
1732
|
+
lockfileKind: lockfile.kind,
|
|
1733
|
+
lockfilePath: lockfile.path,
|
|
1679
1734
|
lockfileVersion: lockfileInfo.version,
|
|
1680
1735
|
lockfileResolved: lockfileInfo.resolved,
|
|
1681
1736
|
lockfileIntegrity: lockfileInfo.integrity,
|
|
1682
|
-
lockfileEntryPath: lockfileInfo.entryPath
|
|
1737
|
+
lockfileEntryPath: lockfileInfo.entryPath,
|
|
1738
|
+
lockfileNote: lockfile.note
|
|
1683
1739
|
};
|
|
1684
1740
|
}
|
|
1685
1741
|
|
|
1742
|
+
/**
|
|
1743
|
+
* @param {ReturnType<typeof packageInfoForGenerator>} packageInfo
|
|
1744
|
+
* @returns {string}
|
|
1745
|
+
*/
|
|
1746
|
+
function formatGeneratorPackageLockfile(packageInfo) {
|
|
1747
|
+
if (!packageInfo.lockfileKind || !packageInfo.lockfilePath) {
|
|
1748
|
+
return "(not found)";
|
|
1749
|
+
}
|
|
1750
|
+
const label = path.basename(packageInfo.lockfilePath);
|
|
1751
|
+
if (packageInfo.lockfileVersion) {
|
|
1752
|
+
return `${packageInfo.lockfileKind} ${packageInfo.lockfileVersion}`;
|
|
1753
|
+
}
|
|
1754
|
+
return `${label} (version not inspected)`;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1686
1757
|
/**
|
|
1687
1758
|
* @param {string} projectRoot
|
|
1688
1759
|
* @param {import("./generator-policy.js").GeneratorPolicy} policy
|
|
@@ -1724,11 +1795,68 @@ function annotateGeneratorPolicyDiagnostics(diagnostics, bindings) {
|
|
|
1724
1795
|
packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
|
|
1725
1796
|
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1726
1797
|
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1798
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1799
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1727
1800
|
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1728
1801
|
};
|
|
1729
1802
|
});
|
|
1730
1803
|
}
|
|
1731
1804
|
|
|
1805
|
+
/**
|
|
1806
|
+
* @param {Array<ReturnType<typeof generatorPolicyBindingStatus>>} bindings
|
|
1807
|
+
* @returns {any[]}
|
|
1808
|
+
*/
|
|
1809
|
+
function generatorPolicyPackageMetadataDiagnostics(bindings) {
|
|
1810
|
+
const diagnostics = [];
|
|
1811
|
+
for (const binding of bindings) {
|
|
1812
|
+
if (!binding.packageInfo.dependencySpec) {
|
|
1813
|
+
diagnostics.push({
|
|
1814
|
+
code: "generator_package_dependency_missing",
|
|
1815
|
+
severity: "warning",
|
|
1816
|
+
message: `Component '${binding.componentId}' generator package '${binding.packageName}' is not declared in package.json dependencies.`,
|
|
1817
|
+
path: binding.packageInfo.installedPackageJsonPath,
|
|
1818
|
+
suggestedFix: `Declare '${binding.packageName}' in package.json devDependencies so generator adoption is visible in package review.`,
|
|
1819
|
+
step: "generator-policy",
|
|
1820
|
+
componentId: binding.componentId,
|
|
1821
|
+
generatorId: binding.generatorId,
|
|
1822
|
+
packageName: binding.packageName,
|
|
1823
|
+
version: binding.version,
|
|
1824
|
+
packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
|
|
1825
|
+
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1826
|
+
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1827
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1828
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1829
|
+
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
if (
|
|
1833
|
+
binding.packageInfo.installedVersion &&
|
|
1834
|
+
binding.packageInfo.lockfileVersion &&
|
|
1835
|
+
binding.packageInfo.installedVersion !== binding.packageInfo.lockfileVersion
|
|
1836
|
+
) {
|
|
1837
|
+
diagnostics.push({
|
|
1838
|
+
code: "generator_package_version_drift",
|
|
1839
|
+
severity: "warning",
|
|
1840
|
+
message: `Component '${binding.componentId}' generator package '${binding.packageName}' is installed at '${binding.packageInfo.installedVersion}', but package-lock records '${binding.packageInfo.lockfileVersion}'.`,
|
|
1841
|
+
path: binding.packageInfo.lockfilePath,
|
|
1842
|
+
suggestedFix: "Run the package manager install command and review the resulting lockfile before pinning generator policy.",
|
|
1843
|
+
step: "generator-policy",
|
|
1844
|
+
componentId: binding.componentId,
|
|
1845
|
+
generatorId: binding.generatorId,
|
|
1846
|
+
packageName: binding.packageName,
|
|
1847
|
+
version: binding.version,
|
|
1848
|
+
packageVersion: binding.packageInfo.installedVersion,
|
|
1849
|
+
packageDependencyField: binding.packageInfo.dependencyField,
|
|
1850
|
+
packageDependencySpec: binding.packageInfo.dependencySpec,
|
|
1851
|
+
packageLockfileKind: binding.packageInfo.lockfileKind,
|
|
1852
|
+
packageLockfilePath: binding.packageInfo.lockfilePath,
|
|
1853
|
+
packageLockVersion: binding.packageInfo.lockfileVersion
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
return diagnostics;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1732
1860
|
/**
|
|
1733
1861
|
* @param {string} projectPath
|
|
1734
1862
|
* @returns {{ ok: boolean, path: string, exists: boolean, policy: any, defaulted: boolean, bindings: Array<ReturnType<typeof generatorPolicyBindingStatus>>, diagnostics: any[], errors: string[] }}
|
|
@@ -1771,6 +1899,7 @@ function buildGeneratorPolicyCheckPayload(projectPath) {
|
|
|
1771
1899
|
});
|
|
1772
1900
|
}
|
|
1773
1901
|
diagnostics.push(...generatorPolicyDiagnosticsForBindings(policyInfo, rawBindings, "generator-policy"));
|
|
1902
|
+
diagnostics.push(...generatorPolicyPackageMetadataDiagnostics(bindings));
|
|
1774
1903
|
const annotatedDiagnostics = annotateGeneratorPolicyDiagnostics(diagnostics, bindings);
|
|
1775
1904
|
const errors = annotatedDiagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
|
|
1776
1905
|
return {
|
|
@@ -1868,7 +1997,9 @@ function printGeneratorPolicyCheckPayload(payload) {
|
|
|
1868
1997
|
console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
|
|
1869
1998
|
}
|
|
1870
1999
|
if (binding.packageInfo.lockfileVersion) {
|
|
1871
|
-
console.log(` lockfile: ${binding.packageInfo
|
|
2000
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
2001
|
+
} else if (binding.packageInfo.lockfileKind) {
|
|
2002
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
1872
2003
|
}
|
|
1873
2004
|
}
|
|
1874
2005
|
for (const diagnostic of payload.diagnostics) {
|
|
@@ -1906,7 +2037,7 @@ function printGeneratorPolicyStatusPayload(payload) {
|
|
|
1906
2037
|
console.log(` allowed: ${binding.allowed ? "yes" : "no"}`);
|
|
1907
2038
|
console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
|
|
1908
2039
|
console.log(` dependency: ${binding.packageInfo.dependencyField && binding.packageInfo.dependencySpec ? `${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}` : "(not declared)"}`);
|
|
1909
|
-
console.log(` lockfile: ${binding.packageInfo
|
|
2040
|
+
console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
|
|
1910
2041
|
console.log(` policy pin: ${binding.pin.version ? `${binding.pin.key}@${binding.pin.version}` : "(none)"}`);
|
|
1911
2042
|
}
|
|
1912
2043
|
for (const diagnostic of payload.diagnostics) {
|
|
@@ -1918,6 +2049,9 @@ function printGeneratorPolicyStatusPayload(payload) {
|
|
|
1918
2049
|
if (diagnostic.packageDependencySpec) {
|
|
1919
2050
|
console.log(` dependency: ${diagnostic.packageDependencyField} ${diagnostic.packageDependencySpec}`);
|
|
1920
2051
|
}
|
|
2052
|
+
if (diagnostic.packageLockfilePath) {
|
|
2053
|
+
console.log(` lockfile: ${path.basename(diagnostic.packageLockfilePath)}${diagnostic.packageLockVersion ? ` ${diagnostic.packageLockVersion}` : ""}`);
|
|
2054
|
+
}
|
|
1921
2055
|
if (diagnostic.suggestedFix) {
|
|
1922
2056
|
console.log(` fix: ${diagnostic.suggestedFix}`);
|
|
1923
2057
|
}
|
|
@@ -2057,10 +2191,6 @@ function buildGeneratorPolicyPinPayload(projectPath, spec) {
|
|
|
2057
2191
|
if (!allowedPackages.includes(pin.packageName)) {
|
|
2058
2192
|
allowedPackages.push(pin.packageName);
|
|
2059
2193
|
}
|
|
2060
|
-
const scope = packageScopeFromName(pin.packageName);
|
|
2061
|
-
if (scope && !allowedPackageScopes.includes(scope)) {
|
|
2062
|
-
allowedPackageScopes.push(scope);
|
|
2063
|
-
}
|
|
2064
2194
|
pinnedVersions[pin.packageName] = pin.version;
|
|
2065
2195
|
}
|
|
2066
2196
|
const nextPolicy = {
|
|
@@ -10,6 +10,10 @@ import {
|
|
|
10
10
|
resolveGeneratorManifestForBinding,
|
|
11
11
|
validateGeneratorManifest
|
|
12
12
|
} from "./registry.js";
|
|
13
|
+
import {
|
|
14
|
+
generatorPolicyDiagnosticsForBindings,
|
|
15
|
+
loadGeneratorPolicy
|
|
16
|
+
} from "../generator-policy.js";
|
|
13
17
|
import { generateDbContractGraph } from "./surfaces/databases/contract.js";
|
|
14
18
|
import { generateDbLifecyclePlan } from "./surfaces/databases/lifecycle-shared.js";
|
|
15
19
|
import {
|
|
@@ -252,6 +256,26 @@ function loadPackageGeneratorAdapter(manifest, component, options = {}) {
|
|
|
252
256
|
throw new Error(`Component '${component?.id || "unknown"}' generator '${manifest.id}@${manifest.version}' is package-backed but does not declare a package.`);
|
|
253
257
|
}
|
|
254
258
|
const rootDir = options.configDir || options.rootDir || process.cwd();
|
|
259
|
+
const diagnostics = generatorPolicyDiagnosticsForBindings(
|
|
260
|
+
loadGeneratorPolicy(rootDir),
|
|
261
|
+
[{
|
|
262
|
+
componentId: String(component?.id || "unknown"),
|
|
263
|
+
componentType: String(component?.type || manifest.surface || "unknown"),
|
|
264
|
+
projection: String(component?.projection?.id || component?.projection || "unknown"),
|
|
265
|
+
generatorId: String(component?.generator?.id || manifest.id),
|
|
266
|
+
version: String(component?.generator?.version || manifest.version),
|
|
267
|
+
packageName
|
|
268
|
+
}],
|
|
269
|
+
"generator-adapter"
|
|
270
|
+
);
|
|
271
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
272
|
+
if (errors.length > 0) {
|
|
273
|
+
throw new Error(errors.map((diagnostic) =>
|
|
274
|
+
diagnostic.suggestedFix
|
|
275
|
+
? `${diagnostic.message} Suggested fix: ${diagnostic.suggestedFix}`
|
|
276
|
+
: diagnostic.message
|
|
277
|
+
).join("\n"));
|
|
278
|
+
}
|
|
255
279
|
let moduleValue;
|
|
256
280
|
try {
|
|
257
281
|
moduleValue = requireFromProject(rootDir)(packageName);
|
package/src/generator/check.js
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
* @property {Array<{ name: string, ok: boolean, message: string }>} checks
|
|
26
26
|
* @property {string[]} errors
|
|
27
27
|
* @property {{ files: number, artifacts: number, diagnostics: number }|null} smoke
|
|
28
|
+
* @property {boolean} executesPackageCode
|
|
28
29
|
*/
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -238,7 +239,8 @@ export function checkGeneratorPack(sourceSpec, options = {}) {
|
|
|
238
239
|
manifest: null,
|
|
239
240
|
checks: [],
|
|
240
241
|
errors: [],
|
|
241
|
-
smoke: null
|
|
242
|
+
smoke: null,
|
|
243
|
+
executesPackageCode: true
|
|
242
244
|
};
|
|
243
245
|
|
|
244
246
|
/** @type {any|null} */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import { getProjection } from "../shared.js";
|
|
4
|
+
import { generateServerContract } from "./server-contract.js";
|
|
4
5
|
|
|
5
6
|
function renderPackageJson(profile) {
|
|
6
7
|
const dependencies = profile === "express"
|
|
@@ -41,10 +42,10 @@ function routePath(path) {
|
|
|
41
42
|
return String(path || "/").replace(/:([A-Za-z0-9_]+)/g, ":$1");
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
function renderHonoIndex(projection) {
|
|
45
|
-
const routes = (
|
|
45
|
+
function renderHonoIndex(projection, contract) {
|
|
46
|
+
const routes = (contract.routes || []).map((route) => {
|
|
46
47
|
const method = String(route.method || "GET").toLowerCase();
|
|
47
|
-
return `app.${method}("${routePath(route.path)}", (c) => c.json({ ok: true, capability: "${route.capabilityId}", input: { params: c.req.param(), query: c.req.query() } }, ${route.
|
|
48
|
+
return `app.${method}("${routePath(route.path)}", (c) => c.json({ ok: true, capability: "${route.capabilityId}", input: { params: c.req.param(), query: c.req.query() } }, ${route.successStatus || 200} as any));`;
|
|
48
49
|
}).join("\n");
|
|
49
50
|
return `import { serve } from "@hono/node-server";
|
|
50
51
|
import { Hono } from "hono";
|
|
@@ -65,10 +66,10 @@ function expressPath(path) {
|
|
|
65
66
|
return routePath(path);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function renderExpressIndex(projection) {
|
|
69
|
-
const routes = (
|
|
69
|
+
function renderExpressIndex(projection, contract) {
|
|
70
|
+
const routes = (contract.routes || []).map((route) => {
|
|
70
71
|
const method = String(route.method || "GET").toLowerCase();
|
|
71
|
-
return `app.${method}("${expressPath(route.path)}", (req, res) => res.status(${route.
|
|
72
|
+
return `app.${method}("${expressPath(route.path)}", (req, res) => res.status(${route.successStatus || 200}).json({ ok: true, capability: "${route.capabilityId}", input: { params: req.params, query: req.query } }));`;
|
|
72
73
|
}).join("\n");
|
|
73
74
|
return `import express from "express";
|
|
74
75
|
|
|
@@ -88,10 +89,11 @@ app.listen(port, () => {
|
|
|
88
89
|
|
|
89
90
|
export function generateStatelessServer(graph, options = {}) {
|
|
90
91
|
const projection = getProjection(graph, options.projectionId);
|
|
92
|
+
const contract = generateServerContract(graph, { ...options, projectionId: projection.id });
|
|
91
93
|
const profile = options.profile === "express" ? "express" : "hono";
|
|
92
94
|
return {
|
|
93
95
|
"package.json": renderPackageJson(profile),
|
|
94
96
|
"tsconfig.json": renderTsconfig(),
|
|
95
|
-
"src/index.ts": profile === "express" ? renderExpressIndex(projection) : renderHonoIndex(projection)
|
|
97
|
+
"src/index.ts": profile === "express" ? renderExpressIndex(projection, contract) : renderHonoIndex(projection, contract)
|
|
96
98
|
};
|
|
97
99
|
}
|