@friehub/blueprint 0.1.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/.github/workflows/ci.yml +122 -0
- package/.github/workflows/publish.yml +24 -0
- package/README.md +266 -0
- package/adapters/analytics/amplitude.yaml +44 -0
- package/adapters/analytics/mixpanel.yaml +47 -0
- package/adapters/analytics/segment.yaml +40 -0
- package/adapters/auth/auth0.yaml +56 -0
- package/adapters/auth/clerk.yaml +53 -0
- package/adapters/auth/supertokens.yaml +55 -0
- package/adapters/billing/paddle.yaml +57 -0
- package/adapters/billing/stripe.yaml +49 -0
- package/adapters/caching/memcached.yaml +28 -0
- package/adapters/caching/redis.yaml +37 -0
- package/adapters/chargebacks/chargebacks911.yaml +45 -0
- package/adapters/chargebacks/stripe.yaml +45 -0
- package/adapters/crm_leads/hubspot.yaml +43 -0
- package/adapters/crm_leads/salesforce.yaml +60 -0
- package/adapters/customer_support/intercom.yaml +44 -0
- package/adapters/customer_support/zendesk.yaml +51 -0
- package/adapters/donations/paypal.yaml +47 -0
- package/adapters/donations/stripe.yaml +47 -0
- package/adapters/emails/mailgun.yaml +47 -0
- package/adapters/emails/resend.yaml +43 -0
- package/adapters/emails/sendgrid.yaml +43 -0
- package/adapters/error_tracking/bugsnag.yaml +52 -0
- package/adapters/error_tracking/sentry.yaml +58 -0
- package/adapters/feature_flags/flagsmith.yaml +41 -0
- package/adapters/feature_flags/launchdarkly.yaml +41 -0
- package/adapters/feature_flags/unleash.yaml +41 -0
- package/adapters/fraud_detection/riskified.yaml +41 -0
- package/adapters/fraud_detection/sift.yaml +40 -0
- package/adapters/fulfillment/easyship.yaml +51 -0
- package/adapters/fulfillment/shipengine.yaml +51 -0
- package/adapters/incident_management/opsgenie.yaml +49 -0
- package/adapters/incident_management/pagerduty.yaml +48 -0
- package/adapters/invoicing/freshbooks.yaml +54 -0
- package/adapters/invoicing/stripe.yaml +47 -0
- package/adapters/ip_intelligence/ipinfo.yaml +37 -0
- package/adapters/ip_intelligence/maxmind.yaml +39 -0
- package/adapters/jobs/bullmq.yaml +54 -0
- package/adapters/jobs/temporal.yaml +53 -0
- package/adapters/kyc/jumio.yaml +54 -0
- package/adapters/kyc/onfido.yaml +53 -0
- package/adapters/media/cloudinary.yaml +48 -0
- package/adapters/media/imgix.yaml +47 -0
- package/adapters/notifications/firebase.yaml +45 -0
- package/adapters/notifications/novu.yaml +46 -0
- package/adapters/notifications/onesignal.yaml +45 -0
- package/adapters/payments/adyen.yaml +46 -0
- package/adapters/payments/paystack.yaml +45 -0
- package/adapters/payments/stripe.yaml +49 -0
- package/adapters/payouts/paypal.yaml +49 -0
- package/adapters/payouts/stripe.yaml +49 -0
- package/adapters/projects/asana.yaml +49 -0
- package/adapters/projects/jira.yaml +58 -0
- package/adapters/projects/linear.yaml +49 -0
- package/adapters/queues/bullmq.yaml +47 -0
- package/adapters/queues/rabbitmq.yaml +51 -0
- package/adapters/queues/sqs.yaml +45 -0
- package/adapters/rate_limiting/cloudflare.yaml +37 -0
- package/adapters/rate_limiting/upstash.yaml +35 -0
- package/adapters/search/algolia.yaml +39 -0
- package/adapters/search/meilisearch.yaml +39 -0
- package/adapters/search/typesense.yaml +42 -0
- package/adapters/shipping/easyship.yaml +45 -0
- package/adapters/shipping/shipengine.yaml +45 -0
- package/adapters/sms/twilio.yaml +41 -0
- package/adapters/sms/vonage.yaml +41 -0
- package/adapters/storage/azure-blob.yaml +42 -0
- package/adapters/storage/gcs.yaml +41 -0
- package/adapters/storage/s3.yaml +49 -0
- package/adapters/subscriptions/chargebee.yaml +32 -0
- package/adapters/subscriptions/stripe.yaml +37 -0
- package/adapters/tasks/asana.yaml +50 -0
- package/adapters/tasks/jira.yaml +59 -0
- package/adapters/tasks/linear.yaml +50 -0
- package/adapters/taxation/avalara.yaml +52 -0
- package/adapters/taxation/taxjar.yaml +48 -0
- package/adapters/trace_query/datadog.yaml +49 -0
- package/adapters/trace_query/honeycomb.yaml +42 -0
- package/adapters/trace_query/jaeger.yaml +42 -0
- package/adapters/web_analytics/google-analytics.yaml +42 -0
- package/adapters/web_analytics/plausible.yaml +34 -0
- package/adapters/web_analytics/posthog.yaml +34 -0
- package/adapters/webhooks/relay.yaml +35 -0
- package/adapters/webhooks/svix.yaml +41 -0
- package/blueprint.json +5 -0
- package/blueprinter_system_design.svg +139 -0
- package/catalog.json +37943 -0
- package/dist/cli/commands.js +362 -0
- package/dist/cli/help.js +211 -0
- package/dist/cli/render.js +109 -0
- package/dist/cli.js +69 -0
- package/dist/core/adapters/adapter-audit.test.js +85 -0
- package/dist/core/adapters/adapter.test.js +66 -0
- package/dist/core/adapters/index.js +4 -0
- package/dist/core/adapters/load.js +131 -0
- package/dist/core/adapters/resolve.js +78 -0
- package/dist/core/adapters/select.js +80 -0
- package/dist/core/adapters/types.js +1 -0
- package/dist/core/adapters/validate.js +121 -0
- package/dist/core/catalog.js +3 -0
- package/dist/core/collectors.js +126 -0
- package/dist/core/discovery.js +57 -0
- package/dist/core/edge-cases.test.js +147 -0
- package/dist/core/envelope.js +1 -0
- package/dist/core/graph.js +123 -0
- package/dist/core/graph.test.js +62 -0
- package/dist/core/implement.js +48 -0
- package/dist/core/index.js +9 -0
- package/dist/core/load-catalog.js +24 -0
- package/dist/core/parse-document.js +114 -0
- package/dist/core/parse-document.test.js +104 -0
- package/dist/core/parser.js +6 -0
- package/dist/core/parser.test.js +134 -0
- package/dist/core/resolve.js +119 -0
- package/dist/core/resolve.test.js +108 -0
- package/dist/core/scanner.js +163 -0
- package/dist/core/search.js +34 -0
- package/dist/core/search.test.js +43 -0
- package/dist/core/section-body.js +258 -0
- package/dist/core/sections.js +53 -0
- package/dist/core/verify-implement.test.js +123 -0
- package/dist/core/verify.js +156 -0
- package/dist/generators/engine.js +86 -0
- package/dist/generators/generator.test.js +112 -0
- package/dist/generators/index.js +3 -0
- package/dist/generators/prototype/index.js +242 -0
- package/dist/generators/render.js +146 -0
- package/dist/generators/types.js +124 -0
- package/dist/generators/typescript/helpers.js +125 -0
- package/dist/generators/typescript/index.js +206 -0
- package/dist/index.js +8 -0
- package/dist/mcp/server.js +202 -0
- package/dist/mcp/server.test.js +136 -0
- package/dist/utils/args.js +142 -0
- package/package.json +38 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { resolve, detectCycles } from "../core/resolve.js";
|
|
2
|
+
import { buildGraph, renderAscii, renderMermaid } from "../core/graph.js";
|
|
3
|
+
import { searchModules } from "../core/search.js";
|
|
4
|
+
import { verifyImplementation } from "../core/verify.js";
|
|
5
|
+
import { generateImplementPrompts } from "../core/implement.js";
|
|
6
|
+
import { loadAdapters, loadSelection, addAdapter, removeAdapter, resolveAdapters, listAdaptersByModule } from "../core/adapters/index.js";
|
|
7
|
+
import { generateAndWrite } from "../generators/engine.js";
|
|
8
|
+
import { generatePrototype } from "../generators/prototype/index.js";
|
|
9
|
+
import { renderList, renderAdapterList, minimalCatalog, generateJsonSchema } from "./render.js";
|
|
10
|
+
import { writeFile, stat, mkdir } from "node:fs/promises";
|
|
11
|
+
import { join, dirname } from "node:path";
|
|
12
|
+
import * as readline from "node:readline";
|
|
13
|
+
async function readStdin() {
|
|
14
|
+
if (process.stdin.destroyed || process.stdin.readableEnded)
|
|
15
|
+
return "";
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const chunks = [];
|
|
18
|
+
process.stdin.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
19
|
+
process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
20
|
+
process.stdin.on("error", () => resolve(""));
|
|
21
|
+
process.stdin.resume();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function parseStdinModules(input) {
|
|
25
|
+
return input.split(/[\n,]/).map((s) => s.trim()).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
async function interactivePicker(query, results) {
|
|
28
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
29
|
+
const question = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
|
|
30
|
+
console.log(`\nFound ${results.length} modules matching "${query}":\n`);
|
|
31
|
+
for (let i = 0; i < results.length; i++) {
|
|
32
|
+
const r = results[i];
|
|
33
|
+
console.log(` ${i + 1}. ${r.module.name}${r.module.summary ? ` - ${r.module.summary}` : ""}`);
|
|
34
|
+
}
|
|
35
|
+
console.log("\nEnter numbers (e.g., 1,3,5), or 'all':");
|
|
36
|
+
const answer = await question("> ");
|
|
37
|
+
rl.close();
|
|
38
|
+
const trimmed = answer.trim().toLowerCase();
|
|
39
|
+
if (trimmed === "all")
|
|
40
|
+
return results.map((r) => r.module.name);
|
|
41
|
+
return trimmed.split(",").map((s) => parseInt(s.trim(), 10) - 1)
|
|
42
|
+
.filter((i) => i >= 0 && i < results.length)
|
|
43
|
+
.map((i) => results[i].module.name);
|
|
44
|
+
}
|
|
45
|
+
async function writeOutput(outputData, outputPath) {
|
|
46
|
+
if (!outputPath) {
|
|
47
|
+
console.log(outputData);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
await stat(dirname(outputPath));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
console.error(`Error: output directory does not exist: ${dirname(outputPath)}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await writeFile(outputPath, outputData, "utf8");
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error(`Error: could not write to ${outputPath}: ${error instanceof Error ? error.message : error}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function checkErrors(result, strict, quiet) {
|
|
66
|
+
if (result.issues.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
const errors = result.issues.filter((i) => i.severity === "error");
|
|
69
|
+
const warnings = result.issues.filter((i) => i.severity === "warning");
|
|
70
|
+
if (errors.length > 0) {
|
|
71
|
+
console.error("Errors:");
|
|
72
|
+
errors.forEach((e) => console.error(` - ${e.message}`));
|
|
73
|
+
if (strict)
|
|
74
|
+
process.exit(1);
|
|
75
|
+
else
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
if (warnings.length > 0 && !quiet) {
|
|
79
|
+
console.warn("Warnings:");
|
|
80
|
+
warnings.forEach((w) => console.warn(` - ${w.message}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export async function handleList(result, config, root) {
|
|
84
|
+
const outputData = renderList(result.value);
|
|
85
|
+
await writeOutput(outputData, config.output);
|
|
86
|
+
}
|
|
87
|
+
export async function handleInspect(result, config, root) {
|
|
88
|
+
if (!config.target) {
|
|
89
|
+
console.error("Error: module name is required. Example: blueprint inspect billing");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
const mod = result.value.modules.find((m) => m.name === config.target);
|
|
93
|
+
if (!mod) {
|
|
94
|
+
console.error(`Module not found: ${config.target}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const outputData = JSON.stringify(mod, null, config.compact ? undefined : 2);
|
|
98
|
+
await writeOutput(outputData, config.output);
|
|
99
|
+
}
|
|
100
|
+
export async function handleGraph(result, config, root) {
|
|
101
|
+
if (!config.target) {
|
|
102
|
+
console.error("Error: module name is required. Example: blueprint graph billing");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const graph = buildGraph(result.value, config.target);
|
|
106
|
+
if (graph.nodes.length === 0) {
|
|
107
|
+
console.error(`Module not found: ${config.target}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
const outputData = config.format === "mermaid" ? renderMermaid(graph, config.target) : renderAscii(graph, config.target);
|
|
111
|
+
await writeOutput(outputData, config.output);
|
|
112
|
+
}
|
|
113
|
+
export async function handleSearch(result, config, root) {
|
|
114
|
+
let query = config.query;
|
|
115
|
+
if (!query && !process.stdin.isTTY) {
|
|
116
|
+
const input = await readStdin();
|
|
117
|
+
query = input.trim().split("\n")[0]?.trim();
|
|
118
|
+
}
|
|
119
|
+
if (!query) {
|
|
120
|
+
console.error("Error: search query is required. Example: blueprint search billing");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const results = searchModules(result.value, query);
|
|
124
|
+
if (results.length === 0) {
|
|
125
|
+
console.error(`No modules found matching "${query}"`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
if (process.stdin.isTTY) {
|
|
129
|
+
const selected = await interactivePicker(query, results);
|
|
130
|
+
if (selected.length === 0) {
|
|
131
|
+
console.error("No modules selected.");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
const cycles = detectCycles(result.value);
|
|
135
|
+
if (cycles.length > 0) {
|
|
136
|
+
console.error("Cycle detected:");
|
|
137
|
+
cycles.forEach((c) => console.error(` ${c.join(" → ")}`));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const resolved = resolve(result.value, selected);
|
|
141
|
+
if (resolved.errors.length > 0) {
|
|
142
|
+
console.error("Resolve errors:");
|
|
143
|
+
resolved.errors.forEach((e) => console.error(` - ${e}`));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
await writeOutput(JSON.stringify(resolved, null, config.compact ? undefined : 2), config.output);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
const names = results.map((r) => r.module.name);
|
|
150
|
+
await writeOutput(JSON.stringify(names, null, config.compact ? undefined : 2), config.output);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function handleResolve(result, config, root) {
|
|
154
|
+
let modules = config.modules;
|
|
155
|
+
if (modules.length === 0 && !process.stdin.isTTY) {
|
|
156
|
+
modules = parseStdinModules(await readStdin());
|
|
157
|
+
}
|
|
158
|
+
if (modules.length === 0) {
|
|
159
|
+
console.error("Error: --modules is required. Example: blueprint resolve --modules billing,payments,users");
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
const cycles = detectCycles(result.value);
|
|
163
|
+
if (cycles.length > 0) {
|
|
164
|
+
console.error("Cycle detected:");
|
|
165
|
+
cycles.forEach((c) => console.error(` ${c.join(" → ")}`));
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
const resolved = resolve(result.value, modules);
|
|
169
|
+
if (resolved.errors.length > 0) {
|
|
170
|
+
console.error("Resolve errors:");
|
|
171
|
+
resolved.errors.forEach((e) => console.error(` - ${e}`));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
if (resolved.warnings.length > 0 && !config.quiet) {
|
|
175
|
+
resolved.warnings.forEach((w) => console.warn(` - ${w}`));
|
|
176
|
+
}
|
|
177
|
+
await writeOutput(JSON.stringify(resolved, null, config.compact ? undefined : 2), config.output);
|
|
178
|
+
}
|
|
179
|
+
export async function handleAdapters(result, config, root) {
|
|
180
|
+
const adaptersDir = join(root, "adapters");
|
|
181
|
+
const { adapters, errors: loadErrors } = await loadAdapters(adaptersDir);
|
|
182
|
+
if (loadErrors.length > 0 && !config.quiet)
|
|
183
|
+
loadErrors.forEach((e) => console.warn(` - ${e}`));
|
|
184
|
+
if (config.adapterSubcommand === "list") {
|
|
185
|
+
const byModule = listAdaptersByModule(adapters);
|
|
186
|
+
await writeOutput(renderAdapterList(byModule, config.query), config.output);
|
|
187
|
+
}
|
|
188
|
+
else if (config.adapterSubcommand === "add") {
|
|
189
|
+
if (!config.provider || !config.module) {
|
|
190
|
+
console.error("Error: provider and module required. Example: blueprint adapters add stripe payments");
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
const { selection, error } = await addAdapter(root, config.module, config.provider);
|
|
194
|
+
if (error) {
|
|
195
|
+
console.error(`Error: ${error}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
console.log(`Added ${config.provider} as adapter for ${config.module}`);
|
|
199
|
+
await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
|
|
200
|
+
}
|
|
201
|
+
else if (config.adapterSubcommand === "remove") {
|
|
202
|
+
if (!config.module) {
|
|
203
|
+
console.error("Error: module is required. Example: blueprint adapters remove payments");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
const { selection, error } = await removeAdapter(root, config.module);
|
|
207
|
+
if (error) {
|
|
208
|
+
console.error(`Error: ${error}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
console.log(`Removed adapter for ${config.module}`);
|
|
212
|
+
await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
|
|
213
|
+
}
|
|
214
|
+
else if (config.adapterSubcommand === "show") {
|
|
215
|
+
const { selection } = await loadSelection(root);
|
|
216
|
+
await writeOutput(JSON.stringify(selection, null, config.compact ? undefined : 2), config.output);
|
|
217
|
+
}
|
|
218
|
+
else if (config.adapterSubcommand === "verify") {
|
|
219
|
+
const { selection } = await loadSelection(root);
|
|
220
|
+
const resolution = resolveAdapters(selection, adapters, result.value);
|
|
221
|
+
if (resolution.issues.length > 0) {
|
|
222
|
+
resolution.issues.forEach((issue) => {
|
|
223
|
+
const msg = `${issue.adapter ? `${issue.adapter} → ` : ""}${issue.module}: ${issue.message}`;
|
|
224
|
+
if (issue.severity === "error")
|
|
225
|
+
console.error(` ERROR: ${msg}`);
|
|
226
|
+
else if (!config.quiet)
|
|
227
|
+
console.warn(` WARN: ${msg}`);
|
|
228
|
+
});
|
|
229
|
+
if (resolution.issues.some((i) => i.severity === "error"))
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log("All adapters verified successfully.");
|
|
234
|
+
}
|
|
235
|
+
await writeOutput(JSON.stringify(resolution, null, config.compact ? undefined : 2), config.output);
|
|
236
|
+
}
|
|
237
|
+
else if (config.adapterSubcommand === "search") {
|
|
238
|
+
const query = config.query ?? "";
|
|
239
|
+
const results = adapters.filter((a) => a.name.includes(query) || a.module.includes(query) || a.description?.includes(query));
|
|
240
|
+
await writeOutput(JSON.stringify(results, null, config.compact ? undefined : 2), config.output);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
console.error("Error: adapter subcommand required. Available: list, add, remove, show, verify, search");
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export async function handleGenerate(result, config, root) {
|
|
248
|
+
const language = config.language ?? "typescript";
|
|
249
|
+
const type = config.generateSubcommand ?? "all";
|
|
250
|
+
const outputDir = config.output ?? join(root, "generated");
|
|
251
|
+
const adaptersDir = join(root, "adapters");
|
|
252
|
+
const { adapters } = await loadAdapters(adaptersDir);
|
|
253
|
+
const { written, errors } = await generateAndWrite(result.value, adapters, { language, type, module: config.module, provider: config.provider, outputDir });
|
|
254
|
+
if (errors.length > 0) {
|
|
255
|
+
console.error("Generation errors:");
|
|
256
|
+
errors.forEach((e) => console.error(` - ${e}`));
|
|
257
|
+
}
|
|
258
|
+
console.log(`Generated ${written} files to ${outputDir}`);
|
|
259
|
+
}
|
|
260
|
+
export async function handlePrototype(result, config, root) {
|
|
261
|
+
const { selection } = await loadSelection(root);
|
|
262
|
+
const adaptersDir = join(root, "adapters");
|
|
263
|
+
const { adapters } = await loadAdapters(adaptersDir);
|
|
264
|
+
const selectedAdapters = {};
|
|
265
|
+
for (const [module, adapterRef] of Object.entries(selection.adapters)) {
|
|
266
|
+
selectedAdapters[module] = typeof adapterRef === "string" ? adapterRef : adapterRef.primary;
|
|
267
|
+
}
|
|
268
|
+
if (Object.keys(selectedAdapters).length === 0) {
|
|
269
|
+
console.error("Error: No adapters selected. Use 'blueprint adapters add' first.");
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
const moduleName = config.target ?? "my-project";
|
|
273
|
+
const outputDir = config.output ?? join(root, moduleName);
|
|
274
|
+
const { files, errors } = generatePrototype(result.value, adapters, {
|
|
275
|
+
name: moduleName, modules: Object.keys(selectedAdapters), adapters: selectedAdapters, outputDir,
|
|
276
|
+
});
|
|
277
|
+
if (errors.length > 0) {
|
|
278
|
+
console.error("Prototype generation errors:");
|
|
279
|
+
errors.forEach((e) => console.error(` - ${e}`));
|
|
280
|
+
}
|
|
281
|
+
for (const file of files) {
|
|
282
|
+
try {
|
|
283
|
+
const fullPath = join(outputDir, file.path);
|
|
284
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
285
|
+
await writeFile(fullPath, file.content, "utf8");
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
console.error(`Failed to write ${file.path}: ${error instanceof Error ? error.message : error}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
console.log(`Generated prototype with ${files.length} files to ${outputDir}`);
|
|
292
|
+
}
|
|
293
|
+
export async function handleSchema(result, config, root) {
|
|
294
|
+
const outputData = JSON.stringify(generateJsonSchema(result.value), null, config.compact ? undefined : 2);
|
|
295
|
+
await writeOutput(outputData, config.output);
|
|
296
|
+
}
|
|
297
|
+
export async function handleBuild(result, config, root) {
|
|
298
|
+
if (process.exitCode) {
|
|
299
|
+
console.error("Catalog has errors. Use --strict to fail on errors, or fix the issues above.");
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
const outputData = config.minimal
|
|
303
|
+
? JSON.stringify(minimalCatalog(result.value), null, config.compact ? undefined : 2)
|
|
304
|
+
: JSON.stringify(result.value, null, config.compact ? undefined : 2);
|
|
305
|
+
await writeOutput(outputData, config.output);
|
|
306
|
+
}
|
|
307
|
+
export async function handleImplement(result, config, root) {
|
|
308
|
+
if (!config.module) {
|
|
309
|
+
console.error("Error: --module is required. Example: blueprint implement payments --adapter stripe --prompts");
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
if (!config.provider) {
|
|
313
|
+
console.error("Error: --adapter is required. Example: blueprint implement payments --adapter stripe --prompts");
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const adaptersDir = join(root, "adapters");
|
|
317
|
+
const { adapters } = await loadAdapters(adaptersDir);
|
|
318
|
+
const prompts = generateImplementPrompts(result.value, adapters, config.module, config.provider);
|
|
319
|
+
if (prompts.length === 0) {
|
|
320
|
+
console.error(`No functions to implement for ${config.provider}/${config.module}`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
if (config.prompts) {
|
|
324
|
+
console.log(`# Implementation prompts for ${config.provider} → ${config.module}`);
|
|
325
|
+
console.log(`# ${prompts.length} functions need implementation\n`);
|
|
326
|
+
for (const p of prompts) {
|
|
327
|
+
console.log(`--- ${p.function} ---`);
|
|
328
|
+
console.log(p.prompt);
|
|
329
|
+
console.log("");
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
console.log(`${prompts.length} functions ready to implement for ${config.provider}/${config.module}`);
|
|
334
|
+
console.log("Use --prompts to generate AI prompts.");
|
|
335
|
+
for (const p of prompts) {
|
|
336
|
+
console.log(` - ${p.function}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
export async function handleVerify(result, config, root) {
|
|
341
|
+
if (!config.target) {
|
|
342
|
+
console.error("Error: implementation file is required. Example: blueprint verify ./src/adapters/stripe.ts --module payments");
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
if (!config.module) {
|
|
346
|
+
console.error("Error: --module is required. Example: blueprint verify ./src/adapters/stripe.ts --module payments");
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
const verification = await verifyImplementation(config.target, config.module, result.value);
|
|
350
|
+
if (verification.issues.length > 0) {
|
|
351
|
+
console.error(`Verification issues for ${config.module}:`);
|
|
352
|
+
for (const issue of verification.issues) {
|
|
353
|
+
if (issue.kind === "missing") {
|
|
354
|
+
console.error(` MISSING: ${issue.message}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (verification.valid) {
|
|
359
|
+
console.log(`\n${config.module}: All ${result.value.modules.find((m) => m.name === config.module).functions.length} functions implemented. ✓`);
|
|
360
|
+
}
|
|
361
|
+
await writeOutput(JSON.stringify(verification, null, config.compact ? undefined : 2), config.output);
|
|
362
|
+
}
|
package/dist/cli/help.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export function printHelp(command) {
|
|
2
|
+
if (command === "resolve") {
|
|
3
|
+
console.log(`
|
|
4
|
+
Usage: blueprint resolve [options]
|
|
5
|
+
|
|
6
|
+
Options:
|
|
7
|
+
--modules <list> Comma-separated module names to resolve
|
|
8
|
+
--root <path> Project root directory (default: current directory)
|
|
9
|
+
--output <file> Write the resolved set to this file instead of stdout
|
|
10
|
+
--compact Output compact JSON (no indentation)
|
|
11
|
+
--quiet Suppress warnings
|
|
12
|
+
|
|
13
|
+
Reads module names from stdin if --modules is not provided.
|
|
14
|
+
Examples:
|
|
15
|
+
blueprint resolve --modules billing,payments,users
|
|
16
|
+
echo billing,payments | blueprint resolve
|
|
17
|
+
cat modules.txt | blueprint resolve --output resolved.json
|
|
18
|
+
`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (command === "inspect") {
|
|
22
|
+
console.log(`
|
|
23
|
+
Usage: blueprint inspect <module> [options]
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--root <path> Project root directory (default: current directory)
|
|
27
|
+
--output <file> Write the contract to this file instead of stdout
|
|
28
|
+
--compact Output compact JSON (no indentation)
|
|
29
|
+
|
|
30
|
+
Shows the full contract for a single module.
|
|
31
|
+
`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (command === "graph") {
|
|
35
|
+
console.log(`
|
|
36
|
+
Usage: blueprint graph <module> [options]
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
--format <fmt> Output format: ascii (default) or mermaid
|
|
40
|
+
--root <path> Project root directory (default: current directory)
|
|
41
|
+
--output <file> Write the graph to this file instead of stdout
|
|
42
|
+
|
|
43
|
+
Shows the dependency graph for a module.
|
|
44
|
+
`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (command === "search") {
|
|
48
|
+
console.log(`
|
|
49
|
+
Usage: blueprint search <query> [options]
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
--root <path> Project root directory (default: current directory)
|
|
53
|
+
--output <file> Write the resolved set to this file instead of stdout
|
|
54
|
+
--compact Output compact JSON (no indentation)
|
|
55
|
+
|
|
56
|
+
Searches for modules matching the query and interactively picks which to resolve.
|
|
57
|
+
In non-interactive mode (piped input), outputs matching module names as JSON.
|
|
58
|
+
Examples:
|
|
59
|
+
blueprint search billing
|
|
60
|
+
blueprint search "user management"
|
|
61
|
+
echo "payment" | blueprint search
|
|
62
|
+
`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (command === "adapters") {
|
|
66
|
+
console.log(`
|
|
67
|
+
Usage: blueprint adapters <subcommand> [options]
|
|
68
|
+
|
|
69
|
+
Subcommands:
|
|
70
|
+
list [module] List available adapters
|
|
71
|
+
add <provider> <module> Select an adapter for a module
|
|
72
|
+
remove <module> Remove adapter selection
|
|
73
|
+
show Show current adapter selections
|
|
74
|
+
verify [module] Verify adapters against contracts
|
|
75
|
+
search <query> Search for adapters
|
|
76
|
+
|
|
77
|
+
Options:
|
|
78
|
+
--root <path> Project root directory (default: current directory)
|
|
79
|
+
--compact Output compact JSON (no indentation)
|
|
80
|
+
--quiet Suppress warnings
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
blueprint adapters list
|
|
84
|
+
blueprint adapters list payments
|
|
85
|
+
blueprint adapters add stripe payments
|
|
86
|
+
blueprint adapters remove payments
|
|
87
|
+
blueprint adapters show
|
|
88
|
+
blueprint adapters verify
|
|
89
|
+
blueprint adapters search stripe
|
|
90
|
+
`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (command === "generate") {
|
|
94
|
+
console.log(`
|
|
95
|
+
Usage: blueprint generate [subcommand] [options]
|
|
96
|
+
|
|
97
|
+
Subcommands:
|
|
98
|
+
interfaces Generate language interfaces from contracts
|
|
99
|
+
adapters Generate adapter skeletons
|
|
100
|
+
tests Generate conformance tests
|
|
101
|
+
all Generate all (default)
|
|
102
|
+
|
|
103
|
+
Options:
|
|
104
|
+
--lang <language> Target language: typescript (default), rust, python, go
|
|
105
|
+
--module <module> Generate for specific module only
|
|
106
|
+
--output <dir> Output directory (default: ./generated)
|
|
107
|
+
--root <path> Project root directory (default: current directory)
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
blueprint generate
|
|
111
|
+
blueprint generate --lang typescript
|
|
112
|
+
blueprint generate interfaces --lang typescript
|
|
113
|
+
blueprint generate adapter stripe payments --lang typescript
|
|
114
|
+
blueprint generate tests --lang typescript
|
|
115
|
+
blueprint generate --module billing --lang typescript
|
|
116
|
+
`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (command === "verify") {
|
|
120
|
+
console.log(`
|
|
121
|
+
Usage: blueprint verify <file> --module <module> [options]
|
|
122
|
+
|
|
123
|
+
Options:
|
|
124
|
+
--module <module> Module name to verify against (required)
|
|
125
|
+
--root <path> Project root directory (default: current directory)
|
|
126
|
+
--compact Output compact JSON (no indentation)
|
|
127
|
+
|
|
128
|
+
Checks that an implementation file covers all contract functions.
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
blueprint verify ./src/adapters/payments/stripe.ts --module payments
|
|
132
|
+
blueprint verify ./src/adapters/payments/stripe.ts --module payments --compact
|
|
133
|
+
`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (command === "schema") {
|
|
137
|
+
console.log(`
|
|
138
|
+
Usage: blueprint schema [options]
|
|
139
|
+
|
|
140
|
+
Options:
|
|
141
|
+
--output <file> Write schema to file instead of stdout
|
|
142
|
+
--compact Output compact JSON (no indentation)
|
|
143
|
+
--root <path> Project root directory (default: current directory)
|
|
144
|
+
|
|
145
|
+
Exports the catalog as a JSON Schema for programmatic validation.
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
blueprint schema
|
|
149
|
+
blueprint schema --output catalog.schema.json
|
|
150
|
+
blueprint schema --compact
|
|
151
|
+
`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (command === "prototype") {
|
|
155
|
+
console.log(`
|
|
156
|
+
Usage: blueprint prototype [options]
|
|
157
|
+
|
|
158
|
+
Options:
|
|
159
|
+
--name <name> Project name (default: my-project)
|
|
160
|
+
--output <dir> Output directory (default: ./<name>)
|
|
161
|
+
--root <path> Project root directory (default: current directory)
|
|
162
|
+
|
|
163
|
+
Generates a project scaffold based on selected adapters.
|
|
164
|
+
Requires adapters to be selected first with 'blueprint adapters add'.
|
|
165
|
+
|
|
166
|
+
Examples:
|
|
167
|
+
blueprint prototype
|
|
168
|
+
blueprint prototype --name my-saas
|
|
169
|
+
blueprint prototype --output ./my-project
|
|
170
|
+
`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log(`
|
|
174
|
+
Usage: blueprint [command] [options]
|
|
175
|
+
|
|
176
|
+
Commands:
|
|
177
|
+
build (default) Load all contracts and output catalog.json
|
|
178
|
+
list List all modules with dependencies
|
|
179
|
+
search <query> Search for modules and interactively pick to resolve
|
|
180
|
+
inspect <module> Show full contract for a module
|
|
181
|
+
graph <module> Show dependency graph for a module
|
|
182
|
+
resolve Resolve specific modules with dependencies
|
|
183
|
+
adapters Manage adapter selections
|
|
184
|
+
generate Generate code from contracts
|
|
185
|
+
prototype Generate project scaffold
|
|
186
|
+
schema Export catalog as JSON Schema
|
|
187
|
+
verify <file> Verify an implementation against its contract
|
|
188
|
+
|
|
189
|
+
Options:
|
|
190
|
+
--root <path> Project root directory (default: current directory)
|
|
191
|
+
--strict Exit with code 1 if there are any errors (warnings do not affect exit code)
|
|
192
|
+
--output <file> Write output to this file instead of stdout
|
|
193
|
+
--modules <list> Comma-separated module names (resolve command only)
|
|
194
|
+
--format <fmt> Output format for graph: ascii (default) or mermaid
|
|
195
|
+
--compact Output compact JSON (no indentation)
|
|
196
|
+
--quiet Suppress warnings
|
|
197
|
+
--minimal Output minimal JSON without raw sections
|
|
198
|
+
--help, -h Show this help message
|
|
199
|
+
--version, -v Show version number
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
blueprint list
|
|
203
|
+
blueprint inspect billing
|
|
204
|
+
blueprint graph billing
|
|
205
|
+
blueprint graph billing --format mermaid
|
|
206
|
+
blueprint resolve --modules billing,payments,users
|
|
207
|
+
blueprint resolve --modules billing,payments,users --output resolved.json
|
|
208
|
+
echo billing,payments | blueprint resolve --compact
|
|
209
|
+
blueprint schema --output catalog.schema.json
|
|
210
|
+
`);
|
|
211
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export function renderList(catalog) {
|
|
2
|
+
const lines = [];
|
|
3
|
+
lines.push("Modules:");
|
|
4
|
+
for (const mod of catalog.modules.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5
|
+
const deps = mod.hardDeps.length > 0 ? mod.hardDeps.join(", ") : "(none)";
|
|
6
|
+
const soft = mod.softDeps.length > 0 ? mod.softDeps.join(", ") : "(none)";
|
|
7
|
+
const coreInherits = mod.coreInherits.length > 0 ? mod.coreInherits.join(", ") : "(none)";
|
|
8
|
+
const summary = mod.functions.length > 0 ? ` (${mod.functions.length} functions)` : "";
|
|
9
|
+
lines.push(` ${mod.name}${summary}`);
|
|
10
|
+
lines.push(` deps: ${deps}`);
|
|
11
|
+
lines.push(` recommends: ${soft}`);
|
|
12
|
+
lines.push(` inherits: ${coreInherits}`);
|
|
13
|
+
}
|
|
14
|
+
lines.push("");
|
|
15
|
+
lines.push("Core contracts:");
|
|
16
|
+
for (const c of catalog.core.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17
|
+
lines.push(` ${c.name}${c.implicit ? " (implicit)" : ""}`);
|
|
18
|
+
}
|
|
19
|
+
return lines.join("\n");
|
|
20
|
+
}
|
|
21
|
+
export function renderAdapterList(byModule, filter) {
|
|
22
|
+
const lines = [];
|
|
23
|
+
const modules = Object.keys(byModule).sort();
|
|
24
|
+
lines.push("Available adapters:");
|
|
25
|
+
lines.push("");
|
|
26
|
+
for (const module of modules) {
|
|
27
|
+
if (filter && !module.includes(filter)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const adapters = byModule[module];
|
|
31
|
+
lines.push(` ${module}`);
|
|
32
|
+
for (const adapter of adapters) {
|
|
33
|
+
lines.push(` - ${adapter}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return lines.join("\n");
|
|
37
|
+
}
|
|
38
|
+
export function minimalCatalog(catalog) {
|
|
39
|
+
return {
|
|
40
|
+
modules: catalog.modules.map((mod) => ({
|
|
41
|
+
name: mod.name,
|
|
42
|
+
version: mod.version,
|
|
43
|
+
functions: mod.functions.map((fn) => ({
|
|
44
|
+
name: fn.name,
|
|
45
|
+
params: fn.params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type ?? "unknown"}`),
|
|
46
|
+
returns: fn.returns,
|
|
47
|
+
})),
|
|
48
|
+
hardDeps: mod.hardDeps,
|
|
49
|
+
softDeps: mod.softDeps,
|
|
50
|
+
coreInherits: mod.coreInherits,
|
|
51
|
+
})),
|
|
52
|
+
core: catalog.core.map((c) => ({
|
|
53
|
+
name: c.name,
|
|
54
|
+
implicit: c.implicit,
|
|
55
|
+
})),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function generateJsonSchema(catalog) {
|
|
59
|
+
return {
|
|
60
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
61
|
+
title: "Engineering Blueprinter Catalog",
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
modules: {
|
|
65
|
+
type: "array",
|
|
66
|
+
items: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
name: { type: "string" },
|
|
70
|
+
functions: {
|
|
71
|
+
type: "array",
|
|
72
|
+
items: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
name: { type: "string" },
|
|
76
|
+
params: {
|
|
77
|
+
type: "array",
|
|
78
|
+
items: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
name: { type: "string" },
|
|
82
|
+
type: { type: ["string", "null"] },
|
|
83
|
+
optional: { type: "boolean" },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
returns: { type: "string" },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
hardDeps: { type: "array", items: { type: "string" } },
|
|
92
|
+
softDeps: { type: "array", items: { type: "string" } },
|
|
93
|
+
coreInherits: { type: "array", items: { type: "string" } },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
core: {
|
|
98
|
+
type: "array",
|
|
99
|
+
items: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
name: { type: "string" },
|
|
103
|
+
implicit: { type: "boolean" },
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|