@apifuse/provider-sdk 2.1.0-beta.18 → 2.1.0-beta.19
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/CHANGELOG.md +4 -0
- package/README.md +3 -3
- package/SUBMISSION.md +10 -11
- package/bin/apifuse-submit-check.ts +561 -279
- package/dist/cli/create.js +10 -26
- package/dist/cli/templates/provider/README.md.tpl +6 -4
- package/package.json +1 -1
- package/src/cli/create.ts +25 -93
- package/src/cli/templates/provider/README.md.tpl +6 -4
package/dist/cli/create.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { cancel, intro, isCancel, note, outro, select, text
|
|
6
|
+
import { cancel, intro, isCancel, note, outro, select, text } from "@clack/prompts";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import packageJson from "../../package.json";
|
|
9
9
|
export const PROVIDER_NAME_REGEX = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
@@ -17,12 +17,7 @@ export const CATEGORY_OPTIONS = [
|
|
|
17
17
|
"communication",
|
|
18
18
|
"other",
|
|
19
19
|
];
|
|
20
|
-
export const AUTH_MODE_OPTIONS = [
|
|
21
|
-
"none",
|
|
22
|
-
"platform-managed",
|
|
23
|
-
"credentials",
|
|
24
|
-
"oauth2",
|
|
25
|
-
];
|
|
20
|
+
export const AUTH_MODE_OPTIONS = ["none", "platform-managed", "credentials", "oauth2"];
|
|
26
21
|
export const RUNTIME_OPTIONS = ["standard", "browser"];
|
|
27
22
|
export const PRESET_OPTIONS = ["standalone", "monorepo"];
|
|
28
23
|
const CREATE_CONFIG_SCHEMA = z.object({
|
|
@@ -64,9 +59,7 @@ export async function main() {
|
|
|
64
59
|
return;
|
|
65
60
|
}
|
|
66
61
|
const parsed = parseArgs(normalizedArgs);
|
|
67
|
-
const config = parsed.configPath
|
|
68
|
-
? await loadConfig(parsed.configPath)
|
|
69
|
-
: undefined;
|
|
62
|
+
const config = parsed.configPath ? await loadConfig(parsed.configPath) : undefined;
|
|
70
63
|
const resolved = await resolveCreateOptions(parsed, config, process.cwd());
|
|
71
64
|
const plan = await buildProviderCreatePlan(resolved, process.cwd());
|
|
72
65
|
if (resolved.dryRun) {
|
|
@@ -174,9 +167,7 @@ async function resolveCreateOptions(parsed, config, cwd) {
|
|
|
174
167
|
category: parsed.category ?? config?.category,
|
|
175
168
|
authMode: parsed.authMode ?? config?.authMode,
|
|
176
169
|
runtime: parsed.runtime ?? config?.runtime,
|
|
177
|
-
sdkSpecifier: parsed.sdkSpecifier ??
|
|
178
|
-
config?.sdkSpecifier ??
|
|
179
|
-
process.env.APIFUSE__SDK__SPECIFIER,
|
|
170
|
+
sdkSpecifier: parsed.sdkSpecifier ?? config?.sdkSpecifier ?? process.env.APIFUSE__SDK__SPECIFIER,
|
|
180
171
|
dryRun: parsed.dryRun,
|
|
181
172
|
json: parsed.json,
|
|
182
173
|
yes: parsed.yes,
|
|
@@ -267,9 +258,7 @@ async function promptValue(prompt) {
|
|
|
267
258
|
return result;
|
|
268
259
|
}
|
|
269
260
|
export async function buildProviderCreatePlan(options, cwd) {
|
|
270
|
-
const resolvedWorkspaceRoot = options.preset === "monorepo"
|
|
271
|
-
? findApifuseInternalWorkspaceRoot(cwd)
|
|
272
|
-
: undefined;
|
|
261
|
+
const resolvedWorkspaceRoot = options.preset === "monorepo" ? findApifuseInternalWorkspaceRoot(cwd) : undefined;
|
|
273
262
|
if (options.preset === "monorepo" && !resolvedWorkspaceRoot) {
|
|
274
263
|
throw new Error("Monorepo preset is internal to the APIFuse repository. External bounty workspaces are one-provider repositories; use the standalone default create flow.");
|
|
275
264
|
}
|
|
@@ -278,9 +267,7 @@ export async function buildProviderCreatePlan(options, cwd) {
|
|
|
278
267
|
if (options.outputDir) {
|
|
279
268
|
providerRoot = resolve(cwd, options.outputDir);
|
|
280
269
|
installCwd =
|
|
281
|
-
options.preset === "monorepo" && resolvedWorkspaceRoot
|
|
282
|
-
? resolvedWorkspaceRoot
|
|
283
|
-
: providerRoot;
|
|
270
|
+
options.preset === "monorepo" && resolvedWorkspaceRoot ? resolvedWorkspaceRoot : providerRoot;
|
|
284
271
|
}
|
|
285
272
|
else if (options.preset === "monorepo" && resolvedWorkspaceRoot) {
|
|
286
273
|
providerRoot = resolve(resolvedWorkspaceRoot, "providers", options.name);
|
|
@@ -293,8 +280,7 @@ export async function buildProviderCreatePlan(options, cwd) {
|
|
|
293
280
|
if (existsSync(providerRoot)) {
|
|
294
281
|
throw new Error(`Target directory already exists: ${providerRoot}`);
|
|
295
282
|
}
|
|
296
|
-
if (options.sdkSpecifier?.startsWith("workspace:") &&
|
|
297
|
-
!resolvedWorkspaceRoot) {
|
|
283
|
+
if (options.sdkSpecifier?.startsWith("workspace:") && !resolvedWorkspaceRoot) {
|
|
298
284
|
throw new Error("workspace:* is only valid inside the APIFuse monorepo because public Provider SDK scaffolds must install from npm or an explicit tarball/file specifier.");
|
|
299
285
|
}
|
|
300
286
|
const sdkSpecifier = options.sdkSpecifier ??
|
|
@@ -434,7 +420,7 @@ export async function buildProviderCreatePlan(options, cwd) {
|
|
|
434
420
|
validationCommands: [
|
|
435
421
|
"bun run check",
|
|
436
422
|
"bun run type-check",
|
|
437
|
-
"bun run submit-check",
|
|
423
|
+
"bun run submit-check -- --smoke",
|
|
438
424
|
"bun run test",
|
|
439
425
|
],
|
|
440
426
|
workspaceRoot: resolvedWorkspaceRoot,
|
|
@@ -715,9 +701,7 @@ async function runCommand(command, cwd, jsonMode) {
|
|
|
715
701
|
resolvePromise();
|
|
716
702
|
return;
|
|
717
703
|
}
|
|
718
|
-
rejectPromise(new Error(`Command failed (${command}) in ${cwd}${stdout || stderr
|
|
719
|
-
? `\n${[stdout, stderr].filter(Boolean).join("\n")}`
|
|
720
|
-
: ""}`));
|
|
704
|
+
rejectPromise(new Error(`Command failed (${command}) in ${cwd}${stdout || stderr ? `\n${[stdout, stderr].filter(Boolean).join("\n")}` : ""}`));
|
|
721
705
|
});
|
|
722
706
|
});
|
|
723
707
|
}
|
|
@@ -751,7 +735,7 @@ function printResult(plan, jsonMode, dryRun) {
|
|
|
751
735
|
console.log(`Validation: (cd ${plan.providerRoot} && ${command})`);
|
|
752
736
|
}
|
|
753
737
|
console.log(`Next local dev: ${plan.nextDevCommand}`);
|
|
754
|
-
console.log("Submission evidence: run `bun run submit-check
|
|
738
|
+
console.log("Submission evidence: run `bun run submit-check -- --smoke` to archive measured `/health` and `POST /v1/{operation}` results.");
|
|
755
739
|
if (plan.files.some((file) => file.content.includes('runtime: "browser"'))) {
|
|
756
740
|
console.log("Browser runtime: run `bunx playwright install chromium` locally or set `APIFUSE__CDP_POOL__URL` before browser-backed smoke tests.");
|
|
757
741
|
}
|
|
@@ -8,7 +8,7 @@ Generated with `apifuse create`.
|
|
|
8
8
|
bun run dev
|
|
9
9
|
bun run check
|
|
10
10
|
bun run test
|
|
11
|
-
bun run submit-check
|
|
11
|
+
bun run submit-check -- --smoke
|
|
12
12
|
bunx apifuse perf . --operation <operation-id> --runs 3
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -35,15 +35,17 @@ review when `index.ts` remains a short composition root.
|
|
|
35
35
|
Before posting bounty evidence, run:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
bun run submit-check
|
|
38
|
+
bun run submit-check -- --smoke
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
This writes `submission-report.md` with a review-readiness score, blockers,
|
|
42
42
|
warnings, health coverage notes, fixture/schema evidence, and remediation. A
|
|
43
43
|
score is not a payout guarantee; blockers must be fixed before maintainer
|
|
44
44
|
review. The generated `ping` starter intentionally warns until you replace it
|
|
45
|
-
with real upstream-backed Operations.
|
|
46
|
-
|
|
45
|
+
with real upstream-backed Operations. `APIFUSE__PROVIDER__*` env vars enable
|
|
46
|
+
live upstream calls; without them, structured provider errors can still verify
|
|
47
|
+
runtime routing. The full public-only checklist is shipped in
|
|
48
|
+
`node_modules/@apifuse/provider-sdk/SUBMISSION.md`.
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
## Operation guide
|
package/package.json
CHANGED
package/src/cli/create.ts
CHANGED
|
@@ -4,15 +4,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
cancel,
|
|
9
|
-
intro,
|
|
10
|
-
isCancel,
|
|
11
|
-
note,
|
|
12
|
-
outro,
|
|
13
|
-
select,
|
|
14
|
-
text,
|
|
15
|
-
} from "@clack/prompts";
|
|
7
|
+
import { cancel, intro, isCancel, note, outro, select, text } from "@clack/prompts";
|
|
16
8
|
import { z } from "zod";
|
|
17
9
|
|
|
18
10
|
import packageJson from "../../package.json";
|
|
@@ -28,12 +20,7 @@ export const CATEGORY_OPTIONS = [
|
|
|
28
20
|
"communication",
|
|
29
21
|
"other",
|
|
30
22
|
] as const;
|
|
31
|
-
export const AUTH_MODE_OPTIONS = [
|
|
32
|
-
"none",
|
|
33
|
-
"platform-managed",
|
|
34
|
-
"credentials",
|
|
35
|
-
"oauth2",
|
|
36
|
-
] as const;
|
|
23
|
+
export const AUTH_MODE_OPTIONS = ["none", "platform-managed", "credentials", "oauth2"] as const;
|
|
37
24
|
export const RUNTIME_OPTIONS = ["standard", "browser"] as const;
|
|
38
25
|
export const PRESET_OPTIONS = ["standalone", "monorepo"] as const;
|
|
39
26
|
|
|
@@ -95,9 +82,7 @@ export type ProviderCreatePlan = {
|
|
|
95
82
|
workspaceRoot?: string;
|
|
96
83
|
};
|
|
97
84
|
|
|
98
|
-
const TEMPLATE_DIR = fileURLToPath(
|
|
99
|
-
new URL("./templates/provider/", import.meta.url),
|
|
100
|
-
);
|
|
85
|
+
const TEMPLATE_DIR = fileURLToPath(new URL("./templates/provider/", import.meta.url));
|
|
101
86
|
const HELP_TEXT = `Usage: apifuse create <provider-name> [options]
|
|
102
87
|
Examples:
|
|
103
88
|
apifuse create my-provider
|
|
@@ -126,9 +111,7 @@ export async function main() {
|
|
|
126
111
|
}
|
|
127
112
|
|
|
128
113
|
const parsed = parseArgs(normalizedArgs);
|
|
129
|
-
const config = parsed.configPath
|
|
130
|
-
? await loadConfig(parsed.configPath)
|
|
131
|
-
: undefined;
|
|
114
|
+
const config = parsed.configPath ? await loadConfig(parsed.configPath) : undefined;
|
|
132
115
|
const resolved = await resolveCreateOptions(parsed, config, process.cwd());
|
|
133
116
|
const plan = await buildProviderCreatePlan(resolved, process.cwd());
|
|
134
117
|
|
|
@@ -195,18 +178,10 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
195
178
|
parsed.displayName = ensureValue(flag, consumeValue());
|
|
196
179
|
break;
|
|
197
180
|
case "--category":
|
|
198
|
-
parsed.category = parseEnum(
|
|
199
|
-
"category",
|
|
200
|
-
consumeValue(),
|
|
201
|
-
CATEGORY_OPTIONS,
|
|
202
|
-
);
|
|
181
|
+
parsed.category = parseEnum("category", consumeValue(), CATEGORY_OPTIONS);
|
|
203
182
|
break;
|
|
204
183
|
case "--auth-mode":
|
|
205
|
-
parsed.authMode = parseEnum(
|
|
206
|
-
"auth mode",
|
|
207
|
-
consumeValue(),
|
|
208
|
-
AUTH_MODE_OPTIONS,
|
|
209
|
-
);
|
|
184
|
+
parsed.authMode = parseEnum("auth mode", consumeValue(), AUTH_MODE_OPTIONS);
|
|
210
185
|
break;
|
|
211
186
|
case "--runtime":
|
|
212
187
|
parsed.runtime = parseEnum("runtime", consumeValue(), RUNTIME_OPTIONS);
|
|
@@ -253,9 +228,7 @@ function parseEnum<T extends readonly string[]>(
|
|
|
253
228
|
const resolvedValue = ensureValue(`--${label}`, value);
|
|
254
229
|
const matchedValue = options.find((option) => option === resolvedValue);
|
|
255
230
|
if (!matchedValue) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Invalid ${label}: ${resolvedValue}. Expected one of: ${options.join(", ")}`,
|
|
258
|
-
);
|
|
231
|
+
throw new Error(`Invalid ${label}: ${resolvedValue}. Expected one of: ${options.join(", ")}`);
|
|
259
232
|
}
|
|
260
233
|
return matchedValue;
|
|
261
234
|
}
|
|
@@ -282,9 +255,7 @@ async function resolveCreateOptions(
|
|
|
282
255
|
authMode: parsed.authMode ?? config?.authMode,
|
|
283
256
|
runtime: parsed.runtime ?? config?.runtime,
|
|
284
257
|
sdkSpecifier:
|
|
285
|
-
parsed.sdkSpecifier ??
|
|
286
|
-
config?.sdkSpecifier ??
|
|
287
|
-
process.env.APIFUSE__SDK__SPECIFIER,
|
|
258
|
+
parsed.sdkSpecifier ?? config?.sdkSpecifier ?? process.env.APIFUSE__SDK__SPECIFIER,
|
|
288
259
|
dryRun: parsed.dryRun,
|
|
289
260
|
json: parsed.json,
|
|
290
261
|
yes: parsed.yes,
|
|
@@ -298,9 +269,7 @@ async function resolveCreateOptions(
|
|
|
298
269
|
|
|
299
270
|
if (partial.yes) {
|
|
300
271
|
if (!partial.name) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
"--yes requires a provider name (positional or via config).",
|
|
303
|
-
);
|
|
272
|
+
throw new Error("--yes requires a provider name (positional or via config).");
|
|
304
273
|
}
|
|
305
274
|
|
|
306
275
|
return {
|
|
@@ -410,9 +379,7 @@ export async function buildProviderCreatePlan(
|
|
|
410
379
|
cwd: string,
|
|
411
380
|
): Promise<ProviderCreatePlan> {
|
|
412
381
|
const resolvedWorkspaceRoot =
|
|
413
|
-
options.preset === "monorepo"
|
|
414
|
-
? findApifuseInternalWorkspaceRoot(cwd)
|
|
415
|
-
: undefined;
|
|
382
|
+
options.preset === "monorepo" ? findApifuseInternalWorkspaceRoot(cwd) : undefined;
|
|
416
383
|
if (options.preset === "monorepo" && !resolvedWorkspaceRoot) {
|
|
417
384
|
throw new Error(
|
|
418
385
|
"Monorepo preset is internal to the APIFuse repository. External bounty workspaces are one-provider repositories; use the standalone default create flow.",
|
|
@@ -424,9 +391,7 @@ export async function buildProviderCreatePlan(
|
|
|
424
391
|
if (options.outputDir) {
|
|
425
392
|
providerRoot = resolve(cwd, options.outputDir);
|
|
426
393
|
installCwd =
|
|
427
|
-
options.preset === "monorepo" && resolvedWorkspaceRoot
|
|
428
|
-
? resolvedWorkspaceRoot
|
|
429
|
-
: providerRoot;
|
|
394
|
+
options.preset === "monorepo" && resolvedWorkspaceRoot ? resolvedWorkspaceRoot : providerRoot;
|
|
430
395
|
} else if (options.preset === "monorepo" && resolvedWorkspaceRoot) {
|
|
431
396
|
providerRoot = resolve(resolvedWorkspaceRoot, "providers", options.name);
|
|
432
397
|
installCwd = resolvedWorkspaceRoot;
|
|
@@ -439,10 +404,7 @@ export async function buildProviderCreatePlan(
|
|
|
439
404
|
throw new Error(`Target directory already exists: ${providerRoot}`);
|
|
440
405
|
}
|
|
441
406
|
|
|
442
|
-
if (
|
|
443
|
-
options.sdkSpecifier?.startsWith("workspace:") &&
|
|
444
|
-
!resolvedWorkspaceRoot
|
|
445
|
-
) {
|
|
407
|
+
if (options.sdkSpecifier?.startsWith("workspace:") && !resolvedWorkspaceRoot) {
|
|
446
408
|
throw new Error(
|
|
447
409
|
"workspace:* is only valid inside the APIFuse monorepo because public Provider SDK scaffolds must install from npm or an explicit tarball/file specifier.",
|
|
448
410
|
);
|
|
@@ -592,17 +554,14 @@ export async function buildProviderCreatePlan(
|
|
|
592
554
|
validationCommands: [
|
|
593
555
|
"bun run check",
|
|
594
556
|
"bun run type-check",
|
|
595
|
-
"bun run submit-check",
|
|
557
|
+
"bun run submit-check -- --smoke",
|
|
596
558
|
"bun run test",
|
|
597
559
|
],
|
|
598
560
|
workspaceRoot: resolvedWorkspaceRoot,
|
|
599
561
|
};
|
|
600
562
|
}
|
|
601
563
|
|
|
602
|
-
async function renderTemplate(
|
|
603
|
-
fileName: string,
|
|
604
|
-
values: Record<string, string>,
|
|
605
|
-
): Promise<string> {
|
|
564
|
+
async function renderTemplate(fileName: string, values: Record<string, string>): Promise<string> {
|
|
606
565
|
const templatePath = resolve(TEMPLATE_DIR, fileName);
|
|
607
566
|
const template = await readFile(templatePath, "utf8");
|
|
608
567
|
return template.replace(/\{\{([A-Z_]+)\}\}/g, (_match, key: string) => {
|
|
@@ -610,10 +569,7 @@ async function renderTemplate(
|
|
|
610
569
|
});
|
|
611
570
|
}
|
|
612
571
|
|
|
613
|
-
function renderPackageJson(input: {
|
|
614
|
-
packageName: string;
|
|
615
|
-
sdkSpecifier: string;
|
|
616
|
-
}): string {
|
|
572
|
+
function renderPackageJson(input: { packageName: string; sdkSpecifier: string }): string {
|
|
617
573
|
return `${JSON.stringify(
|
|
618
574
|
{
|
|
619
575
|
name: input.packageName,
|
|
@@ -625,8 +581,7 @@ function renderPackageJson(input: {
|
|
|
625
581
|
dev: "apifuse dev .",
|
|
626
582
|
check: "apifuse check . && bun run type-check",
|
|
627
583
|
"type-check": "tsc --noEmit",
|
|
628
|
-
"submit-check":
|
|
629
|
-
"apifuse submit-check . --markdown submission-report.md",
|
|
584
|
+
"submit-check": "apifuse submit-check . --markdown submission-report.md",
|
|
630
585
|
test: "apifuse test .",
|
|
631
586
|
record: "apifuse record .",
|
|
632
587
|
start: "bun start.ts",
|
|
@@ -665,10 +620,7 @@ function renderTsconfig(): string {
|
|
|
665
620
|
)}\n`;
|
|
666
621
|
}
|
|
667
622
|
|
|
668
|
-
function renderStarterLocaleCatalog(
|
|
669
|
-
displayName: string,
|
|
670
|
-
locale: "en" | "ko",
|
|
671
|
-
): string {
|
|
623
|
+
function renderStarterLocaleCatalog(displayName: string, locale: "en" | "ko"): string {
|
|
672
624
|
const catalog = {
|
|
673
625
|
meta: {
|
|
674
626
|
displayName,
|
|
@@ -870,9 +822,7 @@ function isApifuseInternalWorkspaceRoot(workspaceRoot: string): boolean {
|
|
|
870
822
|
return false;
|
|
871
823
|
}
|
|
872
824
|
try {
|
|
873
|
-
const packageJson = JSON.parse(
|
|
874
|
-
readFileSync(providerSdkPackageJsonPath, "utf8"),
|
|
875
|
-
);
|
|
825
|
+
const packageJson = JSON.parse(readFileSync(providerSdkPackageJsonPath, "utf8"));
|
|
876
826
|
return (
|
|
877
827
|
typeof packageJson === "object" &&
|
|
878
828
|
packageJson !== null &&
|
|
@@ -891,27 +841,17 @@ async function writePlan(plan: ProviderCreatePlan): Promise<void> {
|
|
|
891
841
|
}
|
|
892
842
|
}
|
|
893
843
|
|
|
894
|
-
async function installDependencies(
|
|
895
|
-
plan: ProviderCreatePlan,
|
|
896
|
-
jsonMode: boolean,
|
|
897
|
-
): Promise<void> {
|
|
844
|
+
async function installDependencies(plan: ProviderCreatePlan, jsonMode: boolean): Promise<void> {
|
|
898
845
|
await runCommand(plan.installCommand, plan.installCwd, jsonMode);
|
|
899
846
|
}
|
|
900
847
|
|
|
901
|
-
async function runBaselineValidation(
|
|
902
|
-
plan: ProviderCreatePlan,
|
|
903
|
-
jsonMode: boolean,
|
|
904
|
-
): Promise<void> {
|
|
848
|
+
async function runBaselineValidation(plan: ProviderCreatePlan, jsonMode: boolean): Promise<void> {
|
|
905
849
|
for (const command of plan.validationCommands) {
|
|
906
850
|
await runCommand(command, plan.providerRoot, jsonMode);
|
|
907
851
|
}
|
|
908
852
|
}
|
|
909
853
|
|
|
910
|
-
async function runCommand(
|
|
911
|
-
command: string,
|
|
912
|
-
cwd: string,
|
|
913
|
-
jsonMode: boolean,
|
|
914
|
-
): Promise<void> {
|
|
854
|
+
async function runCommand(command: string, cwd: string, jsonMode: boolean): Promise<void> {
|
|
915
855
|
const [binary, ...args] = command.split(" ");
|
|
916
856
|
if (!binary) {
|
|
917
857
|
throw new Error(`Cannot run empty command in ${cwd}`);
|
|
@@ -943,9 +883,7 @@ async function runCommand(
|
|
|
943
883
|
rejectPromise(
|
|
944
884
|
new Error(
|
|
945
885
|
`Command failed (${command}) in ${cwd}${
|
|
946
|
-
stdout || stderr
|
|
947
|
-
? `\n${[stdout, stderr].filter(Boolean).join("\n")}`
|
|
948
|
-
: ""
|
|
886
|
+
stdout || stderr ? `\n${[stdout, stderr].filter(Boolean).join("\n")}` : ""
|
|
949
887
|
}`,
|
|
950
888
|
),
|
|
951
889
|
);
|
|
@@ -953,11 +891,7 @@ async function runCommand(
|
|
|
953
891
|
});
|
|
954
892
|
}
|
|
955
893
|
|
|
956
|
-
function printResult(
|
|
957
|
-
plan: ProviderCreatePlan,
|
|
958
|
-
jsonMode: boolean,
|
|
959
|
-
dryRun: boolean,
|
|
960
|
-
) {
|
|
894
|
+
function printResult(plan: ProviderCreatePlan, jsonMode: boolean, dryRun: boolean) {
|
|
961
895
|
const payload = {
|
|
962
896
|
success: true,
|
|
963
897
|
dryRun,
|
|
@@ -974,9 +908,7 @@ function printResult(
|
|
|
974
908
|
},
|
|
975
909
|
validationCommands: plan.validationCommands,
|
|
976
910
|
nextDevCommand: plan.nextDevCommand,
|
|
977
|
-
files: plan.files.map(
|
|
978
|
-
(file) => relative(plan.providerRoot, file.path) || file.path,
|
|
979
|
-
),
|
|
911
|
+
files: plan.files.map((file) => relative(plan.providerRoot, file.path) || file.path),
|
|
980
912
|
};
|
|
981
913
|
|
|
982
914
|
if (jsonMode) {
|
|
@@ -992,7 +924,7 @@ function printResult(
|
|
|
992
924
|
}
|
|
993
925
|
console.log(`Next local dev: ${plan.nextDevCommand}`);
|
|
994
926
|
console.log(
|
|
995
|
-
"Submission evidence: run `bun run submit-check
|
|
927
|
+
"Submission evidence: run `bun run submit-check -- --smoke` to archive measured `/health` and `POST /v1/{operation}` results.",
|
|
996
928
|
);
|
|
997
929
|
if (plan.files.some((file) => file.content.includes('runtime: "browser"'))) {
|
|
998
930
|
console.log(
|
|
@@ -8,7 +8,7 @@ Generated with `apifuse create`.
|
|
|
8
8
|
bun run dev
|
|
9
9
|
bun run check
|
|
10
10
|
bun run test
|
|
11
|
-
bun run submit-check
|
|
11
|
+
bun run submit-check -- --smoke
|
|
12
12
|
bunx apifuse perf . --operation <operation-id> --runs 3
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -35,15 +35,17 @@ review when `index.ts` remains a short composition root.
|
|
|
35
35
|
Before posting bounty evidence, run:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
bun run submit-check
|
|
38
|
+
bun run submit-check -- --smoke
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
This writes `submission-report.md` with a review-readiness score, blockers,
|
|
42
42
|
warnings, health coverage notes, fixture/schema evidence, and remediation. A
|
|
43
43
|
score is not a payout guarantee; blockers must be fixed before maintainer
|
|
44
44
|
review. The generated `ping` starter intentionally warns until you replace it
|
|
45
|
-
with real upstream-backed Operations.
|
|
46
|
-
|
|
45
|
+
with real upstream-backed Operations. `APIFUSE__PROVIDER__*` env vars enable
|
|
46
|
+
live upstream calls; without them, structured provider errors can still verify
|
|
47
|
+
runtime routing. The full public-only checklist is shipped in
|
|
48
|
+
`node_modules/@apifuse/provider-sdk/SUBMISSION.md`.
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
## Operation guide
|