@apifuse/provider-sdk 2.1.0-beta.2 → 2.1.0-beta.21
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/AUTHORING.md +330 -8
- package/CHANGELOG.md +89 -1
- package/README.md +64 -17
- package/SUBMISSION.md +86 -0
- package/bin/apifuse-check.ts +60 -6
- package/bin/apifuse-dev.ts +58 -8
- package/bin/apifuse-pack-check.ts +32 -2
- package/bin/apifuse-pack-smoke.ts +133 -6
- package/bin/apifuse-perf.ts +142 -49
- package/bin/apifuse-record.ts +182 -104
- package/bin/apifuse-submit-check.ts +3243 -0
- package/bin/apifuse.ts +1 -1
- package/dist/auth.d.ts +76 -0
- package/dist/auth.js +436 -0
- package/dist/ceremonies/index.d.ts +41 -0
- package/dist/ceremonies/index.js +490 -0
- package/dist/choice-token.d.ts +24 -0
- package/dist/choice-token.js +74 -0
- package/dist/cli/commands.d.ts +10 -0
- package/dist/cli/commands.js +80 -0
- package/dist/cli/create.d.ts +47 -0
- package/dist/cli/create.js +777 -0
- package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
- package/dist/cli/templates/provider/.gitignore.tpl +22 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +87 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +1 -0
- package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
- package/dist/cli/templates/provider/README.md.tpl +163 -0
- package/dist/cli/templates/provider/dev.ts.tpl +5 -0
- package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
- package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
- package/dist/cli/templates/provider/index.ts.tpl +15 -0
- package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
- package/dist/cli/templates/provider/meta.ts.tpl +7 -0
- package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
- package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
- package/dist/cli/templates/provider/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/dist/cli/templates/provider/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/dist/cli/templates/provider/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/dist/cli/templates/provider/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/dist/cli/templates/provider/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/dist/cli/templates/provider/skills/upstream-notes/README.md.tpl +13 -0
- package/dist/cli/templates/provider/start.ts.tpl +5 -0
- package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
- package/dist/config/loader.d.ts +107 -0
- package/dist/config/loader.js +935 -0
- package/dist/contract-json.d.ts +9 -0
- package/dist/contract-json.js +51 -0
- package/dist/contract-serialization.d.ts +4 -0
- package/dist/contract-serialization.js +78 -0
- package/dist/contract-types.d.ts +49 -0
- package/dist/contract-types.js +1 -0
- package/dist/contract.d.ts +6 -0
- package/dist/contract.js +156 -0
- package/dist/define.d.ts +100 -0
- package/dist/define.js +1383 -0
- package/dist/dev.d.ts +9 -0
- package/dist/dev.js +15 -0
- package/dist/errors.d.ts +59 -0
- package/dist/errors.js +97 -0
- package/dist/i18n/catalog.d.ts +29 -0
- package/dist/i18n/catalog.js +159 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/keys.d.ts +10 -0
- package/dist/i18n/keys.js +34 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +38 -0
- package/dist/lint.d.ts +74 -0
- package/dist/lint.js +729 -0
- package/dist/observability.d.ts +5 -0
- package/dist/observability.js +39 -0
- package/dist/provider.d.ts +11 -0
- package/dist/provider.js +9 -0
- package/dist/public-schema-field-lint.d.ts +2 -0
- package/dist/public-schema-field-lint.js +158 -0
- package/dist/recipes/gov-api.d.ts +19 -0
- package/dist/recipes/gov-api.js +72 -0
- package/dist/recipes/rest-api.d.ts +21 -0
- package/dist/recipes/rest-api.js +115 -0
- package/dist/runtime/auth-flow.d.ts +14 -0
- package/dist/runtime/auth-flow.js +46 -0
- package/dist/runtime/browser.d.ts +25 -0
- package/dist/runtime/browser.js +1237 -0
- package/dist/runtime/cache.d.ts +10 -0
- package/dist/runtime/cache.js +372 -0
- package/dist/runtime/choice.d.ts +15 -0
- package/dist/runtime/choice.js +435 -0
- package/dist/runtime/credential.d.ts +8 -0
- package/dist/runtime/credential.js +61 -0
- package/dist/runtime/env.d.ts +2 -0
- package/dist/runtime/env.js +10 -0
- package/dist/runtime/executor.d.ts +16 -0
- package/dist/runtime/executor.js +51 -0
- package/dist/runtime/http.d.ts +8 -0
- package/dist/runtime/http.js +726 -0
- package/dist/runtime/insights.d.ts +9 -0
- package/dist/runtime/insights.js +324 -0
- package/dist/runtime/instrumentation.d.ts +8 -0
- package/dist/runtime/instrumentation.js +269 -0
- package/dist/runtime/key-derivation.d.ts +24 -0
- package/dist/runtime/key-derivation.js +73 -0
- package/dist/runtime/keyring.d.ts +25 -0
- package/dist/runtime/keyring.js +93 -0
- package/dist/runtime/namespace.d.ts +9 -0
- package/dist/runtime/namespace.js +19 -0
- package/dist/runtime/otlp.d.ts +39 -0
- package/dist/runtime/otlp.js +103 -0
- package/dist/runtime/perf.d.ts +12 -0
- package/dist/runtime/perf.js +52 -0
- package/dist/runtime/prevalidate.d.ts +12 -0
- package/dist/runtime/prevalidate.js +173 -0
- package/dist/runtime/provider.d.ts +2 -0
- package/dist/runtime/provider.js +11 -0
- package/dist/runtime/proxy-errors.d.ts +21 -0
- package/dist/runtime/proxy-errors.js +83 -0
- package/dist/runtime/proxy-telemetry.d.ts +8 -0
- package/dist/runtime/proxy-telemetry.js +174 -0
- package/dist/runtime/redis.d.ts +17 -0
- package/dist/runtime/redis.js +82 -0
- package/dist/runtime/request-options.d.ts +3 -0
- package/dist/runtime/request-options.js +42 -0
- package/dist/runtime/state.d.ts +17 -0
- package/dist/runtime/state.js +344 -0
- package/dist/runtime/stealth.d.ts +21 -0
- package/dist/runtime/stealth.js +980 -0
- package/dist/runtime/stt.d.ts +22 -0
- package/dist/runtime/stt.js +480 -0
- package/dist/runtime/trace.d.ts +26 -0
- package/dist/runtime/trace.js +142 -0
- package/dist/runtime/waterfall.d.ts +12 -0
- package/dist/runtime/waterfall.js +147 -0
- package/dist/schema.d.ts +74 -0
- package/dist/schema.js +243 -0
- package/dist/serve.d.ts +1 -0
- package/dist/serve.js +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +2 -0
- package/dist/server/serve.d.ts +64 -0
- package/dist/server/serve.js +1118 -0
- package/dist/server/types.d.ts +136 -0
- package/dist/server/types.js +86 -0
- package/dist/stealth/profiles.d.ts +4 -0
- package/dist/stealth/profiles.js +259 -0
- package/dist/stream.d.ts +44 -0
- package/dist/stream.js +151 -0
- package/dist/testing/helpers.d.ts +23 -0
- package/dist/testing/helpers.js +95 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/run.d.ts +34 -0
- package/dist/testing/run.js +307 -0
- package/dist/types.d.ts +1467 -0
- package/dist/types.js +61 -0
- package/dist/utils/date.d.ts +6 -0
- package/dist/utils/date.js +101 -0
- package/dist/utils/parse.d.ts +16 -0
- package/dist/utils/parse.js +51 -0
- package/dist/utils/text.d.ts +4 -0
- package/dist/utils/text.js +14 -0
- package/dist/utils/transform.d.ts +8 -0
- package/dist/utils/transform.js +48 -0
- package/package.json +57 -29
- package/src/auth.ts +786 -0
- package/src/ceremonies/index.ts +8 -2
- package/src/choice-token.ts +165 -0
- package/src/cli/commands.ts +34 -11
- package/src/cli/create.ts +254 -128
- package/src/cli/templates/provider/.dockerignore.tpl +22 -0
- package/src/cli/templates/provider/.gitignore.tpl +22 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +87 -0
- package/src/cli/templates/provider/CLAUDE.md.tpl +1 -0
- package/src/cli/templates/provider/README.md.tpl +87 -7
- package/src/cli/templates/provider/dev.ts.tpl +1 -1
- package/src/cli/templates/provider/domain/README.md.tpl +3 -0
- package/src/cli/templates/provider/index.ts.tpl +5 -47
- package/src/cli/templates/provider/mappers/README.md.tpl +3 -0
- package/src/cli/templates/provider/meta.ts.tpl +7 -0
- package/src/cli/templates/provider/operations/index.ts.tpl +5 -0
- package/src/cli/templates/provider/operations/ping.ts.tpl +24 -0
- package/src/cli/templates/provider/schemas/ping.ts.tpl +24 -0
- package/src/cli/templates/provider/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
- package/src/cli/templates/provider/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
- package/src/cli/templates/provider/skills/normalization-standards/SKILL.md.tpl +57 -0
- package/src/cli/templates/provider/skills/pagination-and-counts/SKILL.md.tpl +52 -0
- package/src/cli/templates/provider/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
- package/src/cli/templates/provider/skills/upstream-notes/README.md.tpl +13 -0
- package/src/cli/templates/provider/start.ts.tpl +1 -1
- package/src/cli/templates/provider/upstream/README.md.tpl +3 -0
- package/src/config/loader.ts +1224 -9
- package/src/contract-json.ts +75 -0
- package/src/contract-serialization.ts +89 -0
- package/src/contract-types.ts +52 -0
- package/src/contract.ts +216 -0
- package/src/define.ts +1820 -70
- package/src/errors.ts +27 -0
- package/src/i18n/catalog.ts +277 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/keys.ts +64 -0
- package/src/index.ts +189 -9
- package/src/lint.ts +580 -73
- package/src/observability.ts +41 -0
- package/src/provider.ts +131 -4
- package/src/public-schema-field-lint.ts +237 -0
- package/src/runtime/auth-flow.ts +9 -0
- package/src/runtime/browser.ts +1054 -51
- package/src/runtime/cache.ts +528 -0
- package/src/runtime/choice.ts +760 -0
- package/src/runtime/executor.ts +32 -3
- package/src/runtime/http.ts +980 -195
- package/src/runtime/insights.ts +11 -11
- package/src/runtime/instrumentation.ts +12 -4
- package/src/runtime/key-derivation.ts +1 -1
- package/src/runtime/keyring.ts +4 -3
- package/src/runtime/proxy-errors.ts +132 -0
- package/src/runtime/proxy-telemetry.ts +253 -0
- package/src/runtime/redis.ts +116 -0
- package/src/runtime/request-options.ts +66 -0
- package/src/runtime/state.ts +563 -0
- package/src/runtime/stealth.ts +1336 -0
- package/src/runtime/stt.ts +629 -0
- package/src/runtime/trace.ts +1 -1
- package/src/schema.ts +363 -1
- package/src/server/serve.ts +1192 -75
- package/src/server/types.ts +37 -0
- package/src/stream.ts +210 -0
- package/src/testing/run.ts +40 -6
- package/src/types.ts +1283 -59
- package/src/runtime/tls.ts +0 -434
- package/src/types/playwright-stealth.d.ts +0 -9
package/src/cli/create.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
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
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,17 +82,13 @@ 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
|
|
104
|
-
apifuse create my-provider --preset monorepo
|
|
105
89
|
apifuse create --config ./apifuse.create.json --json
|
|
106
90
|
|
|
107
91
|
Options:
|
|
108
|
-
--preset <standalone|monorepo>
|
|
109
92
|
--config <path>
|
|
110
93
|
--output-dir <path>
|
|
111
94
|
--display-name <name>
|
|
@@ -115,7 +98,7 @@ Options:
|
|
|
115
98
|
--yes
|
|
116
99
|
--dry-run
|
|
117
100
|
--json
|
|
118
|
-
--sdk-specifier <specifier> # internal/testing override for
|
|
101
|
+
--sdk-specifier <specifier> # internal/testing override for dependency resolution
|
|
119
102
|
--help, -h`;
|
|
120
103
|
|
|
121
104
|
export async function main() {
|
|
@@ -128,9 +111,7 @@ export async function main() {
|
|
|
128
111
|
}
|
|
129
112
|
|
|
130
113
|
const parsed = parseArgs(normalizedArgs);
|
|
131
|
-
const config = parsed.configPath
|
|
132
|
-
? await loadConfig(parsed.configPath)
|
|
133
|
-
: undefined;
|
|
114
|
+
const config = parsed.configPath ? await loadConfig(parsed.configPath) : undefined;
|
|
134
115
|
const resolved = await resolveCreateOptions(parsed, config, process.cwd());
|
|
135
116
|
const plan = await buildProviderCreatePlan(resolved, process.cwd());
|
|
136
117
|
|
|
@@ -197,18 +178,10 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
197
178
|
parsed.displayName = ensureValue(flag, consumeValue());
|
|
198
179
|
break;
|
|
199
180
|
case "--category":
|
|
200
|
-
parsed.category = parseEnum(
|
|
201
|
-
"category",
|
|
202
|
-
consumeValue(),
|
|
203
|
-
CATEGORY_OPTIONS,
|
|
204
|
-
);
|
|
181
|
+
parsed.category = parseEnum("category", consumeValue(), CATEGORY_OPTIONS);
|
|
205
182
|
break;
|
|
206
183
|
case "--auth-mode":
|
|
207
|
-
parsed.authMode = parseEnum(
|
|
208
|
-
"auth mode",
|
|
209
|
-
consumeValue(),
|
|
210
|
-
AUTH_MODE_OPTIONS,
|
|
211
|
-
);
|
|
184
|
+
parsed.authMode = parseEnum("auth mode", consumeValue(), AUTH_MODE_OPTIONS);
|
|
212
185
|
break;
|
|
213
186
|
case "--runtime":
|
|
214
187
|
parsed.runtime = parseEnum("runtime", consumeValue(), RUNTIME_OPTIONS);
|
|
@@ -255,9 +228,7 @@ function parseEnum<T extends readonly string[]>(
|
|
|
255
228
|
const resolvedValue = ensureValue(`--${label}`, value);
|
|
256
229
|
const matchedValue = options.find((option) => option === resolvedValue);
|
|
257
230
|
if (!matchedValue) {
|
|
258
|
-
throw new Error(
|
|
259
|
-
`Invalid ${label}: ${resolvedValue}. Expected one of: ${options.join(", ")}`,
|
|
260
|
-
);
|
|
231
|
+
throw new Error(`Invalid ${label}: ${resolvedValue}. Expected one of: ${options.join(", ")}`);
|
|
261
232
|
}
|
|
262
233
|
return matchedValue;
|
|
263
234
|
}
|
|
@@ -273,39 +244,32 @@ async function resolveCreateOptions(
|
|
|
273
244
|
config: CreateConfigFile | undefined,
|
|
274
245
|
cwd: string,
|
|
275
246
|
): Promise<CreateResolvedOptions> {
|
|
276
|
-
const
|
|
277
|
-
const detectedPreset: CreatePreset = detectedWorkspaceRoot
|
|
278
|
-
? "monorepo"
|
|
279
|
-
: "standalone";
|
|
247
|
+
const internalWorkspaceRoot = findApifuseInternalWorkspaceRoot(cwd);
|
|
280
248
|
|
|
281
249
|
const partial: Partial<CreateResolvedOptions> = {
|
|
282
250
|
name: parsed.name ?? config?.name,
|
|
283
|
-
preset: parsed.preset ?? config?.preset ??
|
|
251
|
+
preset: parsed.preset ?? config?.preset ?? "standalone",
|
|
284
252
|
outputDir: parsed.outputDir ?? config?.outputDir,
|
|
285
253
|
displayName: parsed.displayName ?? config?.displayName,
|
|
286
254
|
category: parsed.category ?? config?.category,
|
|
287
255
|
authMode: parsed.authMode ?? config?.authMode,
|
|
288
256
|
runtime: parsed.runtime ?? config?.runtime,
|
|
289
257
|
sdkSpecifier:
|
|
290
|
-
parsed.sdkSpecifier ??
|
|
291
|
-
config?.sdkSpecifier ??
|
|
292
|
-
process.env.APIFUSE_SDK_SPECIFIER,
|
|
258
|
+
parsed.sdkSpecifier ?? config?.sdkSpecifier ?? process.env.APIFUSE__SDK__SPECIFIER,
|
|
293
259
|
dryRun: parsed.dryRun,
|
|
294
260
|
json: parsed.json,
|
|
295
261
|
yes: parsed.yes,
|
|
296
262
|
};
|
|
297
263
|
|
|
298
|
-
if (partial.preset === "monorepo" && !
|
|
264
|
+
if (partial.preset === "monorepo" && !internalWorkspaceRoot) {
|
|
299
265
|
throw new Error(
|
|
300
|
-
"Monorepo preset
|
|
266
|
+
"Monorepo preset is internal to the APIFuse repository. External bounty workspaces are one-provider repositories; use the standalone default create flow.",
|
|
301
267
|
);
|
|
302
268
|
}
|
|
303
269
|
|
|
304
270
|
if (partial.yes) {
|
|
305
271
|
if (!partial.name) {
|
|
306
|
-
throw new Error(
|
|
307
|
-
"--yes requires a provider name (positional or via config).",
|
|
308
|
-
);
|
|
272
|
+
throw new Error("--yes requires a provider name (positional or via config).");
|
|
309
273
|
}
|
|
310
274
|
|
|
311
275
|
return {
|
|
@@ -314,7 +278,7 @@ async function resolveCreateOptions(
|
|
|
314
278
|
category: partial.category ?? "other",
|
|
315
279
|
authMode: partial.authMode ?? "none",
|
|
316
280
|
runtime: partial.runtime ?? "standard",
|
|
317
|
-
preset: partial.preset ??
|
|
281
|
+
preset: partial.preset ?? "standalone",
|
|
318
282
|
outputDir: partial.outputDir,
|
|
319
283
|
dryRun: partial.dryRun ?? false,
|
|
320
284
|
json: partial.json ?? false,
|
|
@@ -324,10 +288,10 @@ async function resolveCreateOptions(
|
|
|
324
288
|
}
|
|
325
289
|
|
|
326
290
|
if (!partial.json) {
|
|
327
|
-
intro("Create a new
|
|
291
|
+
intro("Create a new APIFuse provider");
|
|
328
292
|
note(
|
|
329
|
-
|
|
330
|
-
"
|
|
293
|
+
"External bounty workspaces are one-provider repositories. The public create flow defaults to standalone.",
|
|
294
|
+
"Provider workspace",
|
|
331
295
|
);
|
|
332
296
|
}
|
|
333
297
|
|
|
@@ -392,22 +356,7 @@ async function resolveCreateOptions(
|
|
|
392
356
|
initialValue: "standard",
|
|
393
357
|
}),
|
|
394
358
|
)),
|
|
395
|
-
preset:
|
|
396
|
-
partial.preset ??
|
|
397
|
-
(await promptValue(
|
|
398
|
-
select({
|
|
399
|
-
message: "Generation preset",
|
|
400
|
-
options: PRESET_OPTIONS.map((value) => ({
|
|
401
|
-
label: value,
|
|
402
|
-
value,
|
|
403
|
-
hint:
|
|
404
|
-
value === "standalone"
|
|
405
|
-
? "Create a clean-room npm-ready provider package."
|
|
406
|
-
: "Create the provider inside the current ApiFuse monorepo.",
|
|
407
|
-
})),
|
|
408
|
-
initialValue: detectedPreset,
|
|
409
|
-
}),
|
|
410
|
-
)),
|
|
359
|
+
preset: partial.preset ?? "standalone",
|
|
411
360
|
outputDir: partial.outputDir,
|
|
412
361
|
dryRun: partial.dryRun ?? false,
|
|
413
362
|
json: partial.json ?? false,
|
|
@@ -429,24 +378,20 @@ export async function buildProviderCreatePlan(
|
|
|
429
378
|
options: CreateResolvedOptions,
|
|
430
379
|
cwd: string,
|
|
431
380
|
): Promise<ProviderCreatePlan> {
|
|
432
|
-
const
|
|
433
|
-
options.preset === "monorepo" ?
|
|
434
|
-
if (options.preset === "monorepo" && !
|
|
381
|
+
const resolvedWorkspaceRoot =
|
|
382
|
+
options.preset === "monorepo" ? findApifuseInternalWorkspaceRoot(cwd) : undefined;
|
|
383
|
+
if (options.preset === "monorepo" && !resolvedWorkspaceRoot) {
|
|
435
384
|
throw new Error(
|
|
436
|
-
"Monorepo preset
|
|
385
|
+
"Monorepo preset is internal to the APIFuse repository. External bounty workspaces are one-provider repositories; use the standalone default create flow.",
|
|
437
386
|
);
|
|
438
387
|
}
|
|
439
|
-
const resolvedWorkspaceRoot =
|
|
440
|
-
options.preset === "monorepo" ? workspaceRoot : undefined;
|
|
441
388
|
let providerRoot: string;
|
|
442
389
|
let installCwd: string;
|
|
443
390
|
|
|
444
391
|
if (options.outputDir) {
|
|
445
392
|
providerRoot = resolve(cwd, options.outputDir);
|
|
446
393
|
installCwd =
|
|
447
|
-
options.preset === "monorepo" && resolvedWorkspaceRoot
|
|
448
|
-
? resolvedWorkspaceRoot
|
|
449
|
-
: providerRoot;
|
|
394
|
+
options.preset === "monorepo" && resolvedWorkspaceRoot ? resolvedWorkspaceRoot : providerRoot;
|
|
450
395
|
} else if (options.preset === "monorepo" && resolvedWorkspaceRoot) {
|
|
451
396
|
providerRoot = resolve(resolvedWorkspaceRoot, "providers", options.name);
|
|
452
397
|
installCwd = resolvedWorkspaceRoot;
|
|
@@ -459,33 +404,101 @@ export async function buildProviderCreatePlan(
|
|
|
459
404
|
throw new Error(`Target directory already exists: ${providerRoot}`);
|
|
460
405
|
}
|
|
461
406
|
|
|
407
|
+
if (options.sdkSpecifier?.startsWith("workspace:") && !resolvedWorkspaceRoot) {
|
|
408
|
+
throw new Error(
|
|
409
|
+
"workspace:* is only valid inside the APIFuse monorepo because public Provider SDK scaffolds must install from npm or an explicit tarball/file specifier.",
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
462
413
|
const sdkSpecifier =
|
|
463
414
|
options.sdkSpecifier ??
|
|
464
|
-
(options.preset === "monorepo"
|
|
415
|
+
(options.preset === "monorepo" && resolvedWorkspaceRoot
|
|
416
|
+
? "workspace:*"
|
|
417
|
+
: `^${packageJson.version}`);
|
|
465
418
|
const relativeProviderRoot = relative(cwd, providerRoot) || options.name;
|
|
466
419
|
const nextDevCommand = `cd ${relativeProviderRoot} && bun run dev`;
|
|
467
420
|
const packageName =
|
|
468
421
|
options.preset === "monorepo"
|
|
469
422
|
? `@apifuse/provider-${options.name}`
|
|
470
423
|
: `apifuse-provider-${options.name}`;
|
|
424
|
+
const templateValues = {
|
|
425
|
+
PROVIDER_ID: options.name,
|
|
426
|
+
DISPLAY_NAME: escapeTemplate(options.displayName),
|
|
427
|
+
CATEGORY: options.category,
|
|
428
|
+
RUNTIME: options.runtime,
|
|
429
|
+
BROWSER_BLOCK:
|
|
430
|
+
options.runtime === "browser"
|
|
431
|
+
? ',\n browser: {\n engine: "playwright-stealth",\n }'
|
|
432
|
+
: "",
|
|
433
|
+
SECRETS_BLOCK: renderSecretsBlock(options.authMode),
|
|
434
|
+
CREDENTIAL_BLOCK: renderCredentialBlock(options.authMode),
|
|
435
|
+
AUTH_BLOCK: renderAuthBlock(options.authMode),
|
|
436
|
+
};
|
|
471
437
|
|
|
472
438
|
const files: ProviderPlanFile[] = [
|
|
439
|
+
{
|
|
440
|
+
path: resolve(providerRoot, ".dockerignore"),
|
|
441
|
+
content: await renderTemplate(".dockerignore.tpl", {}),
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
path: resolve(providerRoot, ".gitignore"),
|
|
445
|
+
content: await renderTemplate(".gitignore.tpl", {}),
|
|
446
|
+
},
|
|
473
447
|
{
|
|
474
448
|
path: resolve(providerRoot, "index.ts"),
|
|
475
|
-
content: await renderTemplate("index.ts.tpl",
|
|
449
|
+
content: await renderTemplate("index.ts.tpl", templateValues),
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
path: resolve(providerRoot, "meta.ts"),
|
|
453
|
+
content: await renderTemplate("meta.ts.tpl", {
|
|
476
454
|
PROVIDER_ID: options.name,
|
|
477
455
|
DISPLAY_NAME: escapeTemplate(options.displayName),
|
|
478
456
|
CATEGORY: options.category,
|
|
479
|
-
|
|
480
|
-
|
|
457
|
+
}),
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
path: resolve(providerRoot, "operations", "index.ts"),
|
|
461
|
+
content: await renderTemplate("operations/index.ts.tpl", {}),
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
path: resolve(providerRoot, "operations", "ping.ts"),
|
|
465
|
+
content: await renderTemplate("operations/ping.ts.tpl", {
|
|
466
|
+
DISPLAY_NAME: escapeTemplate(options.displayName),
|
|
467
|
+
HANDLER_CTX: options.runtime === "browser" ? "ctx" : "_ctx",
|
|
468
|
+
BROWSER_HANDLER_BLOCK:
|
|
481
469
|
options.runtime === "browser"
|
|
482
|
-
? '
|
|
470
|
+
? '\n const page = await ctx.browser.newPage();\n await page.goto("https://example.com");\n const title = await page.title();\n const frames = await page.frames();\n await page.close();\n'
|
|
471
|
+
: "",
|
|
472
|
+
BROWSER_RESPONSE_FIELDS:
|
|
473
|
+
options.runtime === "browser"
|
|
474
|
+
? ",\n pageTitle: title,\n frameCount: frames.length"
|
|
483
475
|
: "",
|
|
484
|
-
SECRETS_BLOCK: renderSecretsBlock(options.authMode),
|
|
485
|
-
CREDENTIAL_BLOCK: renderCredentialBlock(options.authMode),
|
|
486
|
-
AUTH_BLOCK: renderAuthBlock(options.authMode),
|
|
487
476
|
}),
|
|
488
477
|
},
|
|
478
|
+
{
|
|
479
|
+
path: resolve(providerRoot, "schemas", "ping.ts"),
|
|
480
|
+
content: await renderTemplate("schemas/ping.ts.tpl", {}),
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
path: resolve(providerRoot, "upstream", "README.md"),
|
|
484
|
+
content: await renderTemplate("upstream/README.md.tpl", {}),
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
path: resolve(providerRoot, "mappers", "README.md"),
|
|
488
|
+
content: await renderTemplate("mappers/README.md.tpl", {}),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
path: resolve(providerRoot, "domain", "README.md"),
|
|
492
|
+
content: await renderTemplate("domain/README.md.tpl", {}),
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
path: resolve(providerRoot, "locales", "en.json"),
|
|
496
|
+
content: renderStarterLocaleCatalog(options.displayName, "en"),
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
path: resolve(providerRoot, "locales", "ko.json"),
|
|
500
|
+
content: renderStarterLocaleCatalog(options.displayName, "ko"),
|
|
501
|
+
},
|
|
489
502
|
{
|
|
490
503
|
path: resolve(providerRoot, "package.json"),
|
|
491
504
|
content: renderPackageJson({
|
|
@@ -525,6 +538,38 @@ export async function buildProviderCreatePlan(
|
|
|
525
538
|
PROVIDER_ID: options.name,
|
|
526
539
|
}),
|
|
527
540
|
},
|
|
541
|
+
{
|
|
542
|
+
path: resolve(providerRoot, "AGENTS.md"),
|
|
543
|
+
content: await renderTemplate("AGENTS.md.tpl", {}),
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
path: resolve(providerRoot, "CLAUDE.md"),
|
|
547
|
+
content: await renderTemplate("CLAUDE.md.tpl", {}),
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
path: resolve(providerRoot, "skills", "normalization-standards", "SKILL.md"),
|
|
551
|
+
content: await renderTemplate("skills/normalization-standards/SKILL.md.tpl", {}),
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
path: resolve(providerRoot, "skills", "upstream-contract-verification", "SKILL.md"),
|
|
555
|
+
content: await renderTemplate("skills/upstream-contract-verification/SKILL.md.tpl", {}),
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
path: resolve(providerRoot, "skills", "fixtures-and-recording", "SKILL.md"),
|
|
559
|
+
content: await renderTemplate("skills/fixtures-and-recording/SKILL.md.tpl", {}),
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
path: resolve(providerRoot, "skills", "pagination-and-counts", "SKILL.md"),
|
|
563
|
+
content: await renderTemplate("skills/pagination-and-counts/SKILL.md.tpl", {}),
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
path: resolve(providerRoot, "skills", "health-checks-and-fail-closed", "SKILL.md"),
|
|
567
|
+
content: await renderTemplate("skills/health-checks-and-fail-closed/SKILL.md.tpl", {}),
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
path: resolve(providerRoot, "skills", "upstream-notes", "README.md"),
|
|
571
|
+
content: await renderTemplate("skills/upstream-notes/README.md.tpl", {}),
|
|
572
|
+
},
|
|
528
573
|
];
|
|
529
574
|
|
|
530
575
|
return {
|
|
@@ -538,15 +583,17 @@ export async function buildProviderCreatePlan(
|
|
|
538
583
|
packageName,
|
|
539
584
|
preset: options.preset,
|
|
540
585
|
providerRoot,
|
|
541
|
-
validationCommands: [
|
|
586
|
+
validationCommands: [
|
|
587
|
+
"bun run check",
|
|
588
|
+
"bun run type-check",
|
|
589
|
+
"bun run submit-check -- --smoke",
|
|
590
|
+
"bun run test",
|
|
591
|
+
],
|
|
542
592
|
workspaceRoot: resolvedWorkspaceRoot,
|
|
543
593
|
};
|
|
544
594
|
}
|
|
545
595
|
|
|
546
|
-
async function renderTemplate(
|
|
547
|
-
fileName: string,
|
|
548
|
-
values: Record<string, string>,
|
|
549
|
-
): Promise<string> {
|
|
596
|
+
async function renderTemplate(fileName: string, values: Record<string, string>): Promise<string> {
|
|
550
597
|
const templatePath = resolve(TEMPLATE_DIR, fileName);
|
|
551
598
|
const template = await readFile(templatePath, "utf8");
|
|
552
599
|
return template.replace(/\{\{([A-Z_]+)\}\}/g, (_match, key: string) => {
|
|
@@ -554,10 +601,7 @@ async function renderTemplate(
|
|
|
554
601
|
});
|
|
555
602
|
}
|
|
556
603
|
|
|
557
|
-
function renderPackageJson(input: {
|
|
558
|
-
packageName: string;
|
|
559
|
-
sdkSpecifier: string;
|
|
560
|
-
}): string {
|
|
604
|
+
function renderPackageJson(input: { packageName: string; sdkSpecifier: string }): string {
|
|
561
605
|
return `${JSON.stringify(
|
|
562
606
|
{
|
|
563
607
|
name: input.packageName,
|
|
@@ -567,10 +611,11 @@ function renderPackageJson(input: {
|
|
|
567
611
|
main: "./index.ts",
|
|
568
612
|
scripts: {
|
|
569
613
|
dev: "apifuse dev .",
|
|
570
|
-
check: "apifuse check .",
|
|
614
|
+
check: "apifuse check . && bun run type-check",
|
|
615
|
+
"type-check": "tsc --noEmit",
|
|
616
|
+
"submit-check": "apifuse submit-check . --markdown submission-report.md",
|
|
571
617
|
test: "apifuse test .",
|
|
572
618
|
record: "apifuse record .",
|
|
573
|
-
"perf:sample": "apifuse perf . --operation ping --runs 3",
|
|
574
619
|
start: "bun start.ts",
|
|
575
620
|
},
|
|
576
621
|
dependencies: {
|
|
@@ -578,6 +623,7 @@ function renderPackageJson(input: {
|
|
|
578
623
|
},
|
|
579
624
|
devDependencies: {
|
|
580
625
|
"@types/bun": "latest",
|
|
626
|
+
typescript: "^6.0.3",
|
|
581
627
|
},
|
|
582
628
|
},
|
|
583
629
|
null,
|
|
@@ -596,6 +642,7 @@ function renderTsconfig(): string {
|
|
|
596
642
|
noEmit: true,
|
|
597
643
|
skipLibCheck: true,
|
|
598
644
|
resolveJsonModule: true,
|
|
645
|
+
types: ["bun"],
|
|
599
646
|
},
|
|
600
647
|
include: ["**/*.ts"],
|
|
601
648
|
exclude: ["node_modules"],
|
|
@@ -605,6 +652,61 @@ function renderTsconfig(): string {
|
|
|
605
652
|
)}\n`;
|
|
606
653
|
}
|
|
607
654
|
|
|
655
|
+
function renderStarterLocaleCatalog(displayName: string, locale: "en" | "ko"): string {
|
|
656
|
+
const catalog = {
|
|
657
|
+
meta: {
|
|
658
|
+
displayName,
|
|
659
|
+
description:
|
|
660
|
+
locale === "ko"
|
|
661
|
+
? `${displayName} APIFuse 커뮤니티 기여용 provider starter입니다.`
|
|
662
|
+
: `${displayName} provider starter for APIFuse community contributions.`,
|
|
663
|
+
},
|
|
664
|
+
operations: {
|
|
665
|
+
ping: {
|
|
666
|
+
description:
|
|
667
|
+
locale === "ko"
|
|
668
|
+
? "생성된 provider wiring이 APIFuse runtime contract를 통해 작은 샘플 payload를 정상적으로 round-trip하는지 확인합니다. 로컬 개발, baseline check, 첫 bounty scaffold 검증에 사용합니다. production data retrieval이나 upstream-specific workflow에는 사용하지 마세요. 이 starter operation은 생성된 프로젝트가 compile, serve, input/output round-trip을 수행하는지 증명하기 위한 용도입니다."
|
|
669
|
+
: "Confirms the generated provider wiring is operational by echoing a small sample payload through the APIFuse runtime contract. Use when validating local development, baseline checks, or first-pass bounty scaffolds. Do NOT use for production data retrieval or upstream-specific workflows because this starter operation exists only to prove the generated project compiles, serves, and round-trips input/output correctly.",
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
schemaDescriptions: {
|
|
673
|
+
input: {
|
|
674
|
+
root:
|
|
675
|
+
locale === "ko"
|
|
676
|
+
? "생성된 ping operation의 입력 payload"
|
|
677
|
+
: "Input payload for the generated ping operation.",
|
|
678
|
+
value:
|
|
679
|
+
locale === "ko"
|
|
680
|
+
? "생성된 provider scaffold wiring 검증에 사용하는 샘플 입력값"
|
|
681
|
+
: "Sample input value used to verify the generated provider scaffold is wired correctly.",
|
|
682
|
+
},
|
|
683
|
+
output: {
|
|
684
|
+
root:
|
|
685
|
+
locale === "ko"
|
|
686
|
+
? "생성된 ping operation이 반환하는 출력 payload"
|
|
687
|
+
: "Output payload returned by the generated ping operation.",
|
|
688
|
+
ok:
|
|
689
|
+
locale === "ko"
|
|
690
|
+
? "생성된 provider가 샘플 요청을 성공적으로 처리했는지 여부"
|
|
691
|
+
: "Whether the generated provider handled the sample request successfully.",
|
|
692
|
+
message:
|
|
693
|
+
locale === "ko"
|
|
694
|
+
? "생성된 provider가 샘플 payload를 round-trip했음을 보여주는 사람이 읽을 수 있는 확인 메시지"
|
|
695
|
+
: "Human-readable confirmation that the generated provider round-tripped the sample payload.",
|
|
696
|
+
pageTitle:
|
|
697
|
+
locale === "ko"
|
|
698
|
+
? "browser 런타임 provider일 때 로드된 페이지의 제목 (해당되지 않으면 생략)"
|
|
699
|
+
: "Title of the loaded page when the provider uses the browser runtime; omitted otherwise.",
|
|
700
|
+
frameCount:
|
|
701
|
+
locale === "ko"
|
|
702
|
+
? "browser 런타임 provider일 때 로드된 페이지의 frame 개수 (해당되지 않으면 생략)"
|
|
703
|
+
: "Number of frames in the loaded page when the provider uses the browser runtime; omitted otherwise.",
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
return `${JSON.stringify(catalog, null, 2)}\n`;
|
|
708
|
+
}
|
|
709
|
+
|
|
608
710
|
function renderAuthBlock(authMode: CreateAuthMode): string {
|
|
609
711
|
switch (authMode) {
|
|
610
712
|
case "none":
|
|
@@ -615,8 +717,8 @@ function renderAuthBlock(authMode: CreateAuthMode): string {
|
|
|
615
717
|
return `{
|
|
616
718
|
mode: "credentials",
|
|
617
719
|
flow: {
|
|
618
|
-
start: async () => ({
|
|
619
|
-
kind: "
|
|
720
|
+
start: async (_ctx) => ({
|
|
721
|
+
kind: "form",
|
|
620
722
|
turnId: crypto.randomUUID(),
|
|
621
723
|
expectedInput: {
|
|
622
724
|
schema: {
|
|
@@ -642,13 +744,24 @@ function renderAuthBlock(authMode: CreateAuthMode): string {
|
|
|
642
744
|
},
|
|
643
745
|
hint: "Generated placeholder credential flow completed. Replace this with real auth logic.",
|
|
644
746
|
}),
|
|
747
|
+
refresh: async () => ({
|
|
748
|
+
kind: "complete",
|
|
749
|
+
turnId: crypto.randomUUID(),
|
|
750
|
+
data: {
|
|
751
|
+
credential: {
|
|
752
|
+
username: "replace-with-refreshed-username",
|
|
753
|
+
password: "replace-with-refreshed-password",
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
hint: "Return refreshed credential data here, or throw AuthError with code AUTH_REQUIRED when silent refresh is not possible.",
|
|
757
|
+
}),
|
|
645
758
|
},
|
|
646
759
|
}`;
|
|
647
760
|
case "oauth2":
|
|
648
761
|
return `{
|
|
649
762
|
mode: "oauth2",
|
|
650
763
|
flow: {
|
|
651
|
-
start: async () => ({
|
|
764
|
+
start: async (_ctx) => ({
|
|
652
765
|
kind: "redirect",
|
|
653
766
|
turnId: crypto.randomUUID(),
|
|
654
767
|
data: {
|
|
@@ -713,11 +826,11 @@ export function toDisplayName(name: string): string {
|
|
|
713
826
|
.join(" ");
|
|
714
827
|
}
|
|
715
828
|
|
|
716
|
-
function
|
|
829
|
+
function findApifuseInternalWorkspaceRoot(cwd: string): string | undefined {
|
|
717
830
|
let currentDirectory = cwd;
|
|
718
831
|
|
|
719
832
|
while (true) {
|
|
720
|
-
if (
|
|
833
|
+
if (isApifuseInternalWorkspaceRoot(currentDirectory)) {
|
|
721
834
|
return currentDirectory;
|
|
722
835
|
}
|
|
723
836
|
|
|
@@ -730,6 +843,29 @@ function findWorkspaceRoot(cwd: string): string | undefined {
|
|
|
730
843
|
}
|
|
731
844
|
}
|
|
732
845
|
|
|
846
|
+
function isApifuseInternalWorkspaceRoot(workspaceRoot: string): boolean {
|
|
847
|
+
const providerSdkPackageJsonPath = resolve(
|
|
848
|
+
workspaceRoot,
|
|
849
|
+
"packages",
|
|
850
|
+
"provider-sdk",
|
|
851
|
+
"package.json",
|
|
852
|
+
);
|
|
853
|
+
if (!existsSync(providerSdkPackageJsonPath)) {
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
857
|
+
const packageJson = JSON.parse(readFileSync(providerSdkPackageJsonPath, "utf8"));
|
|
858
|
+
return (
|
|
859
|
+
typeof packageJson === "object" &&
|
|
860
|
+
packageJson !== null &&
|
|
861
|
+
"name" in packageJson &&
|
|
862
|
+
packageJson.name === "@apifuse/provider-sdk"
|
|
863
|
+
);
|
|
864
|
+
} catch {
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
733
869
|
async function writePlan(plan: ProviderCreatePlan): Promise<void> {
|
|
734
870
|
for (const file of plan.files) {
|
|
735
871
|
await mkdir(dirname(file.path), { recursive: true });
|
|
@@ -737,27 +873,17 @@ async function writePlan(plan: ProviderCreatePlan): Promise<void> {
|
|
|
737
873
|
}
|
|
738
874
|
}
|
|
739
875
|
|
|
740
|
-
async function installDependencies(
|
|
741
|
-
plan: ProviderCreatePlan,
|
|
742
|
-
jsonMode: boolean,
|
|
743
|
-
): Promise<void> {
|
|
876
|
+
async function installDependencies(plan: ProviderCreatePlan, jsonMode: boolean): Promise<void> {
|
|
744
877
|
await runCommand(plan.installCommand, plan.installCwd, jsonMode);
|
|
745
878
|
}
|
|
746
879
|
|
|
747
|
-
async function runBaselineValidation(
|
|
748
|
-
plan: ProviderCreatePlan,
|
|
749
|
-
jsonMode: boolean,
|
|
750
|
-
): Promise<void> {
|
|
880
|
+
async function runBaselineValidation(plan: ProviderCreatePlan, jsonMode: boolean): Promise<void> {
|
|
751
881
|
for (const command of plan.validationCommands) {
|
|
752
882
|
await runCommand(command, plan.providerRoot, jsonMode);
|
|
753
883
|
}
|
|
754
884
|
}
|
|
755
885
|
|
|
756
|
-
async function runCommand(
|
|
757
|
-
command: string,
|
|
758
|
-
cwd: string,
|
|
759
|
-
jsonMode: boolean,
|
|
760
|
-
): Promise<void> {
|
|
886
|
+
async function runCommand(command: string, cwd: string, jsonMode: boolean): Promise<void> {
|
|
761
887
|
const [binary, ...args] = command.split(" ");
|
|
762
888
|
if (!binary) {
|
|
763
889
|
throw new Error(`Cannot run empty command in ${cwd}`);
|
|
@@ -789,9 +915,7 @@ async function runCommand(
|
|
|
789
915
|
rejectPromise(
|
|
790
916
|
new Error(
|
|
791
917
|
`Command failed (${command}) in ${cwd}${
|
|
792
|
-
stdout || stderr
|
|
793
|
-
? `\n${[stdout, stderr].filter(Boolean).join("\n")}`
|
|
794
|
-
: ""
|
|
918
|
+
stdout || stderr ? `\n${[stdout, stderr].filter(Boolean).join("\n")}` : ""
|
|
795
919
|
}`,
|
|
796
920
|
),
|
|
797
921
|
);
|
|
@@ -799,11 +923,7 @@ async function runCommand(
|
|
|
799
923
|
});
|
|
800
924
|
}
|
|
801
925
|
|
|
802
|
-
function printResult(
|
|
803
|
-
plan: ProviderCreatePlan,
|
|
804
|
-
jsonMode: boolean,
|
|
805
|
-
dryRun: boolean,
|
|
806
|
-
) {
|
|
926
|
+
function printResult(plan: ProviderCreatePlan, jsonMode: boolean, dryRun: boolean) {
|
|
807
927
|
const payload = {
|
|
808
928
|
success: true,
|
|
809
929
|
dryRun,
|
|
@@ -820,9 +940,7 @@ function printResult(
|
|
|
820
940
|
},
|
|
821
941
|
validationCommands: plan.validationCommands,
|
|
822
942
|
nextDevCommand: plan.nextDevCommand,
|
|
823
|
-
files: plan.files.map(
|
|
824
|
-
(file) => relative(plan.providerRoot, file.path) || file.path,
|
|
825
|
-
),
|
|
943
|
+
files: plan.files.map((file) => relative(plan.providerRoot, file.path) || file.path),
|
|
826
944
|
};
|
|
827
945
|
|
|
828
946
|
if (jsonMode) {
|
|
@@ -837,6 +955,14 @@ function printResult(
|
|
|
837
955
|
console.log(`Validation: (cd ${plan.providerRoot} && ${command})`);
|
|
838
956
|
}
|
|
839
957
|
console.log(`Next local dev: ${plan.nextDevCommand}`);
|
|
958
|
+
console.log(
|
|
959
|
+
"Submission evidence: run `bun run submit-check -- --smoke` to archive measured `/health` and `POST /v1/{operation}` results.",
|
|
960
|
+
);
|
|
961
|
+
if (plan.files.some((file) => file.content.includes('runtime: "browser"'))) {
|
|
962
|
+
console.log(
|
|
963
|
+
"Browser runtime: run `bunx playwright install chromium` locally or set `APIFUSE__CDP_POOL__URL` before browser-backed smoke tests.",
|
|
964
|
+
);
|
|
965
|
+
}
|
|
840
966
|
}
|
|
841
967
|
|
|
842
968
|
function escapeTemplate(value: string): string {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
.git/
|
|
3
|
+
.github/
|
|
4
|
+
|
|
5
|
+
# Environment and local secrets
|
|
6
|
+
.env
|
|
7
|
+
.env.*
|
|
8
|
+
!.env.example
|
|
9
|
+
|
|
10
|
+
# Local reports and generated artifacts
|
|
11
|
+
submission-report.md
|
|
12
|
+
coverage/
|
|
13
|
+
dist/
|
|
14
|
+
.cache/
|
|
15
|
+
.turbo/
|
|
16
|
+
.bun/
|
|
17
|
+
*.tsbuildinfo
|
|
18
|
+
|
|
19
|
+
# OS/editor junk
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
*.swp
|