@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.10
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 +106 -0
- package/CHANGELOG.md +37 -0
- package/README.md +5 -1
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +53 -70
- package/bin/apifuse-submit-check.ts +510 -45
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +8 -18
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/config/loader.d.ts +149 -8
- package/dist/config/loader.js +378 -89
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +8 -1
- package/dist/define.js +119 -122
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +4 -1
- package/dist/errors.js +48 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +44 -42
- package/dist/index.js +39 -37
- package/dist/lint.js +8 -15
- package/dist/provider.d.ts +11 -11
- package/dist/provider.js +9 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +1 -1
- package/dist/runtime/auth-flow.js +3 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +31 -35
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +15 -3
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +144 -348
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +56 -19
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +35 -0
- package/dist/runtime/proxy-nodemaven.js +128 -0
- package/dist/runtime/proxy-retry-policy.d.ts +40 -0
- package/dist/runtime/proxy-retry-policy.js +326 -0
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +15 -4
- package/dist/runtime/stealth.d.ts +2 -2
- package/dist/runtime/stealth.js +108 -212
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +8 -3
- package/dist/server/serve.js +105 -59
- package/dist/server/types.d.ts +10 -9
- package/dist/server/types.js +3 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +1 -1
- package/dist/testing/run.js +7 -14
- package/dist/types.d.ts +87 -1
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +9 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/config/loader.ts +543 -208
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +247 -505
- package/src/dev.ts +4 -9
- package/src/errors.ts +58 -5
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +49 -42
- package/src/lint.ts +89 -159
- package/src/provider.ts +15 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +5 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +23 -20
- package/src/runtime/http.ts +195 -559
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +78 -65
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +29 -42
- package/src/runtime/proxy-nodemaven.ts +178 -0
- package/src/runtime/proxy-retry-policy.ts +469 -0
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +162 -377
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +12 -7
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +172 -245
- package/src/server/types.ts +7 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +19 -67
- package/src/types.ts +88 -1
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
execFileSync,
|
|
6
|
-
spawn,
|
|
7
|
-
spawnSync,
|
|
8
|
-
} from "node:child_process";
|
|
9
|
-
import {
|
|
10
|
-
existsSync,
|
|
11
|
-
mkdirSync,
|
|
12
|
-
mkdtempSync,
|
|
13
|
-
readFileSync,
|
|
14
|
-
rmSync,
|
|
15
|
-
writeFileSync,
|
|
16
|
-
} from "node:fs";
|
|
3
|
+
import { type ChildProcess, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
17
5
|
import { createServer } from "node:net";
|
|
18
6
|
import { tmpdir } from "node:os";
|
|
19
7
|
import { join, resolve } from "node:path";
|
|
@@ -41,9 +29,7 @@ const PING_RESPONSE_SCHEMA = z.object({
|
|
|
41
29
|
|
|
42
30
|
const KEEP_TEMP = process.env.APIFUSE__PACK_SMOKE__KEEP_TEMP === "1";
|
|
43
31
|
|
|
44
|
-
const tempRoot = mkdtempSync(
|
|
45
|
-
join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"),
|
|
46
|
-
);
|
|
32
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-smoke-"));
|
|
47
33
|
const packDir = join(tempRoot, "pack");
|
|
48
34
|
const consumerDir = join(tempRoot, "consumer");
|
|
49
35
|
const externalWorkspaceDir = join(tempRoot, "external-workspace");
|
|
@@ -81,15 +67,7 @@ try {
|
|
|
81
67
|
|
|
82
68
|
run(
|
|
83
69
|
"bun",
|
|
84
|
-
[
|
|
85
|
-
cliBin,
|
|
86
|
-
"create",
|
|
87
|
-
"dx-smoke",
|
|
88
|
-
"--yes",
|
|
89
|
-
"--json",
|
|
90
|
-
"--sdk-specifier",
|
|
91
|
-
tarballSpecifier,
|
|
92
|
-
],
|
|
70
|
+
[cliBin, "create", "dx-smoke", "--yes", "--json", "--sdk-specifier", tarballSpecifier],
|
|
93
71
|
consumerDir,
|
|
94
72
|
);
|
|
95
73
|
|
|
@@ -99,11 +77,7 @@ try {
|
|
|
99
77
|
run("bun", ["run", "test"], generatedProviderDir);
|
|
100
78
|
assertGeneratedReadme(generatedProviderDir);
|
|
101
79
|
await smokeGeneratedDevServer(generatedProviderDir);
|
|
102
|
-
assertExternalWorkspaceTopology(
|
|
103
|
-
cliBin,
|
|
104
|
-
externalWorkspaceDir,
|
|
105
|
-
tarballSpecifier,
|
|
106
|
-
);
|
|
80
|
+
assertExternalWorkspaceTopology(cliBin, externalWorkspaceDir, tarballSpecifier);
|
|
107
81
|
|
|
108
82
|
console.log(
|
|
109
83
|
`Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
|
|
@@ -148,15 +122,8 @@ function assertExternalWorkspaceTopology(
|
|
|
148
122
|
externalWorkspaceDir,
|
|
149
123
|
);
|
|
150
124
|
|
|
151
|
-
const generatedProviderDir = join(
|
|
152
|
-
|
|
153
|
-
"external-workspace-smoke",
|
|
154
|
-
);
|
|
155
|
-
const forbiddenProviderDir = join(
|
|
156
|
-
externalWorkspaceDir,
|
|
157
|
-
"providers",
|
|
158
|
-
"external-workspace-smoke",
|
|
159
|
-
);
|
|
125
|
+
const generatedProviderDir = join(externalWorkspaceDir, "external-workspace-smoke");
|
|
126
|
+
const forbiddenProviderDir = join(externalWorkspaceDir, "providers", "external-workspace-smoke");
|
|
160
127
|
if (!existsSync(generatedProviderDir)) {
|
|
161
128
|
throw new Error(
|
|
162
129
|
"Public create must generate a one-provider repository at <name>/ even when providers/ exists.",
|
|
@@ -168,9 +135,7 @@ function assertExternalWorkspaceTopology(
|
|
|
168
135
|
);
|
|
169
136
|
}
|
|
170
137
|
|
|
171
|
-
const packageJson = JSON.parse(
|
|
172
|
-
readFileSync(join(generatedProviderDir, "package.json"), "utf8"),
|
|
173
|
-
);
|
|
138
|
+
const packageJson = JSON.parse(readFileSync(join(generatedProviderDir, "package.json"), "utf8"));
|
|
174
139
|
const sdkDependency = packageJson?.dependencies?.["@apifuse/provider-sdk"];
|
|
175
140
|
if (sdkDependency !== tarballSpecifier) {
|
|
176
141
|
throw new Error(
|
|
@@ -178,9 +143,7 @@ function assertExternalWorkspaceTopology(
|
|
|
178
143
|
);
|
|
179
144
|
}
|
|
180
145
|
if (JSON.stringify(packageJson).includes("workspace:")) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
"External bounty workspace scaffold must not contain workspace: dependencies.",
|
|
183
|
-
);
|
|
146
|
+
throw new Error("External bounty workspace scaffold must not contain workspace: dependencies.");
|
|
184
147
|
}
|
|
185
148
|
|
|
186
149
|
run("bun", ["install"], generatedProviderDir);
|
|
@@ -199,30 +162,20 @@ function assertExternalWorkspaceTopology(
|
|
|
199
162
|
},
|
|
200
163
|
);
|
|
201
164
|
if (monorepoAttempt.status === 0) {
|
|
202
|
-
throw new Error(
|
|
203
|
-
"--preset monorepo must reject outside the private APIFuse monorepo.",
|
|
204
|
-
);
|
|
165
|
+
throw new Error("--preset monorepo must reject outside the private APIFuse monorepo.");
|
|
205
166
|
}
|
|
206
167
|
const rejectionOutput = `${monorepoAttempt.stdout}\n${monorepoAttempt.stderr}`;
|
|
207
|
-
if (
|
|
208
|
-
!rejectionOutput.includes(
|
|
209
|
-
"Monorepo preset is internal to the APIFuse repository",
|
|
210
|
-
)
|
|
211
|
-
) {
|
|
168
|
+
if (!rejectionOutput.includes("Monorepo preset is internal to the APIFuse repository")) {
|
|
212
169
|
throw new Error(`Unexpected monorepo rejection output: ${rejectionOutput}`);
|
|
213
170
|
}
|
|
214
171
|
}
|
|
215
172
|
|
|
216
173
|
function packSdk(destination: string): { filename: string } {
|
|
217
|
-
const raw = execFileSync(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
encoding: "utf8",
|
|
223
|
-
stdio: ["ignore", "pipe", "inherit"],
|
|
224
|
-
},
|
|
225
|
-
);
|
|
174
|
+
const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
|
|
175
|
+
cwd: process.cwd(),
|
|
176
|
+
encoding: "utf8",
|
|
177
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
178
|
+
});
|
|
226
179
|
const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
|
|
227
180
|
const first = parsed[0];
|
|
228
181
|
if (!first) {
|
|
@@ -252,29 +205,19 @@ function run(command: string, args: string[], cwd: string): void {
|
|
|
252
205
|
function assertGeneratedReadme(providerDir: string): void {
|
|
253
206
|
const readme = readFileSync(join(providerDir, "README.md"), "utf8");
|
|
254
207
|
if (!readme.includes('"requestId":"req_local_ping"')) {
|
|
255
|
-
throw new Error(
|
|
256
|
-
"Generated README is missing requestId in local smoke docs.",
|
|
257
|
-
);
|
|
208
|
+
throw new Error("Generated README is missing requestId in local smoke docs.");
|
|
258
209
|
}
|
|
259
210
|
if (readme.includes('"connection":null')) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
"Generated README must not document connection:null for no-auth local smoke.",
|
|
262
|
-
);
|
|
211
|
+
throw new Error("Generated README must not document connection:null for no-auth local smoke.");
|
|
263
212
|
}
|
|
264
213
|
if (!readme.includes("bunx playwright install chromium")) {
|
|
265
|
-
throw new Error(
|
|
266
|
-
"Generated README is missing browser runtime troubleshooting guidance.",
|
|
267
|
-
);
|
|
214
|
+
throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
|
|
268
215
|
}
|
|
269
216
|
if (!readme.includes("impit")) {
|
|
270
|
-
throw new Error(
|
|
271
|
-
"Generated README is missing impit stealth runtime guidance.",
|
|
272
|
-
);
|
|
217
|
+
throw new Error("Generated README is missing impit stealth runtime guidance.");
|
|
273
218
|
}
|
|
274
219
|
if (!readme.includes("bun run submit-check")) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
"Generated README must document the submit-check pre-submission workflow.",
|
|
277
|
-
);
|
|
220
|
+
throw new Error("Generated README must document the submit-check pre-submission workflow.");
|
|
278
221
|
}
|
|
279
222
|
if (!readme.includes("bun run record -- --operation <operation>")) {
|
|
280
223
|
throw new Error(
|
|
@@ -368,9 +311,7 @@ async function waitForHttp(
|
|
|
368
311
|
|
|
369
312
|
while (Date.now() < deadline) {
|
|
370
313
|
if (server.exitCode !== null) {
|
|
371
|
-
throw new Error(
|
|
372
|
-
`Dev server exited early with code ${server.exitCode}\n${getOutput()}`,
|
|
373
|
-
);
|
|
314
|
+
throw new Error(`Dev server exited early with code ${server.exitCode}\n${getOutput()}`);
|
|
374
315
|
}
|
|
375
316
|
|
|
376
317
|
try {
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// Guards the published artifact against the nodenext type-resolution regression
|
|
4
|
+
// shipped in 2.2.0-beta.1/beta.2: emitted dist/*.d.ts (and dist/*.js) carried
|
|
5
|
+
// EXTENSIONLESS relative imports, so external provider repos compiling with
|
|
6
|
+
// moduleResolution nodenext could not resolve them. With skipLibCheck the
|
|
7
|
+
// errors were masked and types silently degraded — re-exported ProviderError
|
|
8
|
+
// lost its inherited Error members (name/message) and re-exported zod stopped
|
|
9
|
+
// inferring .refine() callback params (~17 false type errors in
|
|
10
|
+
// apifuse-provider-tabelog, see its PR #16).
|
|
11
|
+
//
|
|
12
|
+
// Three layers, all against the packed tarball (what consumers actually get):
|
|
13
|
+
// 1. @arethetypeswrong/cli — resolution/module-kind audit of every exports
|
|
14
|
+
// subpath, including internal resolution of relative specifiers in d.ts.
|
|
15
|
+
// 2. A fixture consumer compiled with real tsc under moduleResolution
|
|
16
|
+
// nodenext and skipLibCheck:false, asserting ProviderError keeps
|
|
17
|
+
// inherited Error members and zod re-exports keep .refine() inference.
|
|
18
|
+
// 3. A Node (not bun) ESM runtime import — node does not tolerate
|
|
19
|
+
// extensionless relative specifiers, bun does.
|
|
20
|
+
//
|
|
21
|
+
// A deliberate negative control proves the fixture compiler actually fails on
|
|
22
|
+
// type errors, so this check cannot rot into a false-positive green.
|
|
23
|
+
|
|
24
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
25
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { tmpdir } from "node:os";
|
|
27
|
+
import { join, resolve } from "node:path";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
|
|
30
|
+
const PACK_RESULT_SCHEMA = z.array(
|
|
31
|
+
z.object({
|
|
32
|
+
filename: z.string(),
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const KEEP_TEMP = process.env.APIFUSE__PACK_TYPES__KEEP_TEMP === "1";
|
|
37
|
+
const sdkRoot = process.cwd();
|
|
38
|
+
|
|
39
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-types-"));
|
|
40
|
+
const packDir = join(tempRoot, "pack");
|
|
41
|
+
const consumerDir = join(tempRoot, "consumer");
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
mkdirSync(packDir, { recursive: true });
|
|
45
|
+
mkdirSync(consumerDir, { recursive: true });
|
|
46
|
+
|
|
47
|
+
const packed = packSdk(packDir);
|
|
48
|
+
const tarballPath = resolve(packDir, packed.filename);
|
|
49
|
+
|
|
50
|
+
runAreTheTypesWrong(tarballPath);
|
|
51
|
+
setUpFixtureConsumer(consumerDir, tarballPath);
|
|
52
|
+
run("bun", ["install"], consumerDir);
|
|
53
|
+
compileFixtureConsumer(consumerDir);
|
|
54
|
+
assertNegativeControlFails(consumerDir);
|
|
55
|
+
runNodeRuntimeImport(consumerDir);
|
|
56
|
+
|
|
57
|
+
console.log(`Packed artifact types OK under nodenext: ${tarballPath}`);
|
|
58
|
+
} finally {
|
|
59
|
+
if (KEEP_TEMP) {
|
|
60
|
+
console.log(`Keeping pack-types temp directory: ${tempRoot}`);
|
|
61
|
+
} else {
|
|
62
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function runAreTheTypesWrong(tarballPath: string): void {
|
|
67
|
+
// esm-only profile: this package publishes ESM only, so CJS-consumer
|
|
68
|
+
// resolution failures are expected and out of scope for this guard.
|
|
69
|
+
run(
|
|
70
|
+
"bun",
|
|
71
|
+
[join(sdkRoot, "node_modules", ".bin", "attw"), tarballPath, "--profile", "esm-only"],
|
|
72
|
+
sdkRoot,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
|
|
77
|
+
writeFileSync(
|
|
78
|
+
join(consumerDir, "package.json"),
|
|
79
|
+
`${JSON.stringify(
|
|
80
|
+
{
|
|
81
|
+
private: true,
|
|
82
|
+
type: "module",
|
|
83
|
+
dependencies: {
|
|
84
|
+
"@apifuse/provider-sdk": `file:${tarballPath}`,
|
|
85
|
+
},
|
|
86
|
+
devDependencies: {
|
|
87
|
+
// Pinned to TS 5.x independently of the repo's own TypeScript 7
|
|
88
|
+
// toolchain: this must reflect what external provider polyrepos
|
|
89
|
+
// (e.g. apifuse-provider-tabelog) run in their CI.
|
|
90
|
+
typescript: "^5.9.3",
|
|
91
|
+
// skipLibCheck:false type-checks transitive deps too (ioredis
|
|
92
|
+
// references Buffer), so the fixture needs node types like any
|
|
93
|
+
// real provider repo.
|
|
94
|
+
"@types/node": "^25.9.3",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
null,
|
|
98
|
+
2,
|
|
99
|
+
)}\n`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
writeFileSync(
|
|
103
|
+
join(consumerDir, "tsconfig.json"),
|
|
104
|
+
`${JSON.stringify(
|
|
105
|
+
{
|
|
106
|
+
compilerOptions: {
|
|
107
|
+
target: "ES2022",
|
|
108
|
+
module: "nodenext",
|
|
109
|
+
moduleResolution: "nodenext",
|
|
110
|
+
strict: true,
|
|
111
|
+
// skipLibCheck must stay off: it is exactly what masked the
|
|
112
|
+
// extensionless-d.ts regression downstream.
|
|
113
|
+
skipLibCheck: false,
|
|
114
|
+
noEmit: true,
|
|
115
|
+
},
|
|
116
|
+
include: ["consumer.ts"],
|
|
117
|
+
},
|
|
118
|
+
null,
|
|
119
|
+
2,
|
|
120
|
+
)}\n`,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
writeFileSync(
|
|
124
|
+
join(consumerDir, "consumer.ts"),
|
|
125
|
+
[
|
|
126
|
+
'import { ProviderError, SessionExpiredError, z } from "@apifuse/provider-sdk";',
|
|
127
|
+
'import { defineCredentialsAuth } from "@apifuse/provider-sdk/provider";',
|
|
128
|
+
'import { extractProviderContract } from "@apifuse/provider-sdk/contract";',
|
|
129
|
+
'import { AUTH_TURN_SCHEMA } from "@apifuse/provider-sdk/auth-turn";',
|
|
130
|
+
'import { serve } from "@apifuse/provider-sdk/server";',
|
|
131
|
+
'import { runStandardTests } from "@apifuse/provider-sdk/testing";',
|
|
132
|
+
"",
|
|
133
|
+
"// ProviderError must keep its inherited Error members under nodenext.",
|
|
134
|
+
"// When dist d.ts imports fail to resolve, the class type degrades and",
|
|
135
|
+
"// name/message disappear (the tabelog false-error mode).",
|
|
136
|
+
'const providerError = new ProviderError("boom");',
|
|
137
|
+
"const inheritedName: string = providerError.name;",
|
|
138
|
+
"const inheritedMessage: string = providerError.message;",
|
|
139
|
+
"const inheritedStack: string | undefined = providerError.stack;",
|
|
140
|
+
"const isError: Error = providerError;",
|
|
141
|
+
"const sessionExpired: ProviderError = new SessionExpiredError();",
|
|
142
|
+
"",
|
|
143
|
+
"// Re-exported zod must keep .refine() callback parameter inference.",
|
|
144
|
+
"const refined = z.object({ shopId: z.string() }).refine((value) => value.shopId.length > 0);",
|
|
145
|
+
'const refinedString = z.string().refine((value) => value.startsWith("tabelog:"));',
|
|
146
|
+
"",
|
|
147
|
+
"export const witnesses = {",
|
|
148
|
+
" inheritedName,",
|
|
149
|
+
" inheritedMessage,",
|
|
150
|
+
" inheritedStack,",
|
|
151
|
+
" isError,",
|
|
152
|
+
" sessionExpired,",
|
|
153
|
+
" refined,",
|
|
154
|
+
" refinedString,",
|
|
155
|
+
" defineCredentialsAuth,",
|
|
156
|
+
" extractProviderContract,",
|
|
157
|
+
" AUTH_TURN_SCHEMA,",
|
|
158
|
+
" serve,",
|
|
159
|
+
" runStandardTests,",
|
|
160
|
+
"};",
|
|
161
|
+
"",
|
|
162
|
+
].join("\n"),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
writeFileSync(
|
|
166
|
+
join(consumerDir, "negative-control.ts"),
|
|
167
|
+
[
|
|
168
|
+
'import { ProviderError } from "@apifuse/provider-sdk";',
|
|
169
|
+
"",
|
|
170
|
+
"// Intentionally wrong: name is a string. If this file compiles, the",
|
|
171
|
+
"// fixture consumer is not actually type-checking and the guard is void.",
|
|
172
|
+
'export const mustNotCompile: number = new ProviderError("boom").name;',
|
|
173
|
+
"",
|
|
174
|
+
].join("\n"),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function compileFixtureConsumer(consumerDir: string): void {
|
|
179
|
+
run(
|
|
180
|
+
"bun",
|
|
181
|
+
[join(consumerDir, "node_modules", ".bin", "tsc"), "-p", "tsconfig.json"],
|
|
182
|
+
consumerDir,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function assertNegativeControlFails(consumerDir: string): void {
|
|
187
|
+
const result = spawnSync(
|
|
188
|
+
"bun",
|
|
189
|
+
[
|
|
190
|
+
join(consumerDir, "node_modules", ".bin", "tsc"),
|
|
191
|
+
"--target",
|
|
192
|
+
"ES2022",
|
|
193
|
+
"--module",
|
|
194
|
+
"nodenext",
|
|
195
|
+
"--moduleResolution",
|
|
196
|
+
"nodenext",
|
|
197
|
+
"--strict",
|
|
198
|
+
"--noEmit",
|
|
199
|
+
"negative-control.ts",
|
|
200
|
+
],
|
|
201
|
+
{ cwd: consumerDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
202
|
+
);
|
|
203
|
+
if (result.status === 0) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"Negative control compiled cleanly: the fixture consumer is not detecting type errors, so the nodenext guard proves nothing.",
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (!`${result.stdout}\n${result.stderr}`.includes("TS2322")) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Negative control failed for an unexpected reason (wanted TS2322):\n${result.stdout}\n${result.stderr}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function runNodeRuntimeImport(consumerDir: string): void {
|
|
216
|
+
// Node, not bun: bun resolves extensionless relative specifiers in
|
|
217
|
+
// published dist/*.js, node correctly refuses them (ERR_MODULE_NOT_FOUND).
|
|
218
|
+
run(
|
|
219
|
+
"node",
|
|
220
|
+
[
|
|
221
|
+
"--input-type=module",
|
|
222
|
+
"-e",
|
|
223
|
+
[
|
|
224
|
+
'const sdk = await import("@apifuse/provider-sdk");',
|
|
225
|
+
'const error = new sdk.ProviderError("boom");',
|
|
226
|
+
'if (error.name !== "ProviderError" || error.message !== "boom" || !(error instanceof Error)) {',
|
|
227
|
+
' throw new Error("ProviderError runtime shape is wrong: " + JSON.stringify({ name: error.name, message: error.message }));',
|
|
228
|
+
"}",
|
|
229
|
+
'console.log("node ESM runtime import OK");',
|
|
230
|
+
].join("\n"),
|
|
231
|
+
],
|
|
232
|
+
consumerDir,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function packSdk(destination: string): { filename: string } {
|
|
237
|
+
const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
|
|
238
|
+
cwd: sdkRoot,
|
|
239
|
+
encoding: "utf8",
|
|
240
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
241
|
+
});
|
|
242
|
+
const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
|
|
243
|
+
const first = parsed[0];
|
|
244
|
+
if (!first) {
|
|
245
|
+
throw new Error("npm pack --json returned no package metadata.");
|
|
246
|
+
}
|
|
247
|
+
return first;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function run(command: string, args: string[], cwd: string): void {
|
|
251
|
+
const result = spawnSync(command, args, {
|
|
252
|
+
cwd,
|
|
253
|
+
env: process.env,
|
|
254
|
+
stdio: "inherit",
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
if (result.error) {
|
|
258
|
+
throw result.error;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (result.status !== 0) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Command failed (${[command, ...args].join(" ")}) in ${cwd} with exit code ${result.status}`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|