@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.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 +487 -0
- package/CHANGELOG.md +90 -0
- package/README.md +49 -3
- 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 +36 -81
- package/bin/apifuse-pack-types.ts +305 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +659 -111
- package/bin/apifuse-submit-check.ts +546 -48
- 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 +9 -1
- package/dist/ceremonies/index.js +65 -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 +176 -8
- package/dist/config/loader.js +424 -95
- 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 +9 -1
- package/dist/define.js +304 -119
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +2 -0
- package/dist/error-resolution.js +90 -0
- package/dist/errors.d.ts +22 -1
- package/dist/errors.js +88 -0
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -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 +46 -42
- package/dist/index.js +41 -37
- package/dist/lint.d.ts +1 -1
- package/dist/lint.js +8 -15
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +12 -11
- package/dist/provider.js +10 -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 +2 -1
- package/dist/runtime/auth-flow.js +4 -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 +31 -4
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +387 -47
- 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 +345 -22
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network.d.ts +127 -0
- package/dist/runtime/native-network.js +1298 -0
- 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 +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- 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 +238 -26
- package/dist/runtime/stealth.d.ts +5 -3
- package/dist/runtime/stealth.js +423 -143
- 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-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- 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 +116 -4
- package/dist/server/serve.js +799 -128
- package/dist/server/types.d.ts +34 -9
- package/dist/server/types.js +8 -1
- package/dist/stateful/errors.d.ts +14 -0
- package/dist/stateful/errors.js +14 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
- package/dist/stateful/stateful-provider-session-routing.js +353 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- 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 +32 -2
- package/dist/testing/run.js +478 -28
- package/dist/types.d.ts +342 -14
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +103 -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 +652 -204
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +499 -498
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +91 -0
- package/src/errors.ts +110 -5
- package/src/fixture-sanitization.ts +247 -0
- 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 +112 -42
- package/src/lint.ts +88 -152
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +66 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +7 -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 +43 -20
- package/src/runtime/http.ts +494 -57
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +495 -66
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network.ts +1686 -0
- 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 +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +353 -133
- package/src/runtime/stealth.ts +505 -154
- 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 +29 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +1151 -328
- package/src/server/types.ts +12 -13
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +23 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +555 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +10 -26
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +10 -2
- package/src/testing/run.ts +653 -74
- package/src/types.ts +408 -28
- 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
package/README.md
CHANGED
|
@@ -119,7 +119,11 @@ the bad request path; provider/runtime failures include `code`, `message`, and
|
|
|
119
119
|
local-only values through `connection.secrets`. Read them in handlers with
|
|
120
120
|
`ctx.credential.get("key")` or `ctx.credential.getAccessToken()`.
|
|
121
121
|
- **Provider env secrets**: declare `secrets[]`, set values in your shell or
|
|
122
|
-
`.env`, and read only those names through `ctx.env.get("NAME")`.
|
|
122
|
+
`.env`, and read only those names through `ctx.env.get("NAME")`. The SDK
|
|
123
|
+
enforces presence of `required: true` declarations before handlers and auth
|
|
124
|
+
flows run, failing the invocation with a structured `MISSING_SECRET` error
|
|
125
|
+
(HTTP 400, category `credential_unavailable`) — do not re-check presence in
|
|
126
|
+
handlers.
|
|
123
127
|
- **Credentials auth flows**: prefer `defineCredentialsAuth()` over hand-written
|
|
124
128
|
`auth.flow`. Declare the form fields and credential keys once, then put the
|
|
125
129
|
upstream login/session creation in `login(ctx, input)`. Return
|
|
@@ -134,14 +138,56 @@ the bad request path; provider/runtime failures include `code`, `message`, and
|
|
|
134
138
|
- **Stealth-sensitive providers**: use `ctx.http` for normal JSON/REST calls and
|
|
135
139
|
`ctx.stealth.fetch()` when you need browser-like session or cookie control.
|
|
136
140
|
`ctx.stealth.fetch()` uses the impit-backed browser stealth transport and
|
|
137
|
-
accepts request controls for `params`, `proxy`, `timeout`, `profile`,
|
|
138
|
-
`redirect`, `throwOnHttpError`, and
|
|
141
|
+
accepts request controls for `params`, `sensitiveParams`, `proxy`, `timeout`, `profile`,
|
|
142
|
+
`maxBodyBytes`, `redirect`, `throwOnHttpError`, and
|
|
143
|
+
`stealth.insecureSkipVerify`. For login
|
|
139
144
|
flows that must inspect intermediate `Location`/`Set-Cookie` headers, create
|
|
140
145
|
a session with `ctx.stealth.createSession()` and use `session.redirects.run()`;
|
|
141
146
|
inspect accumulated cookies through `session.cookies`. Select an SDK stealth
|
|
142
147
|
`profile` such as `chrome-146`; do not tune JA3, HTTP/2 SETTINGS, or
|
|
143
148
|
pseudo-header order in provider code. Chrome/Firefox-style profiles are
|
|
144
149
|
supported; use `ctx.browser` when Safari-specific behavior is required.
|
|
150
|
+
- **Query-parameter credentials**: when an upstream requires a credential in
|
|
151
|
+
its URL query, pass it through `sensitiveParams`, not `params` and never a
|
|
152
|
+
hand-built URL. It is sent as a normal query parameter while the SDK redacts
|
|
153
|
+
its value from transport errors, traces, and recorded fixtures:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const response = await ctx.http.get("/openapi/service", {
|
|
157
|
+
params: { page: 1 },
|
|
158
|
+
sensitiveParams: { serviceKey: ctx.env.get("APIFUSE__PROVIDER__EXAMPLE__API_KEY")! },
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Use this only when the upstream offers no header or body credential channel.
|
|
163
|
+
Declared query-key positions and exact scalar diagnostics are always redacted.
|
|
164
|
+
Free-text values of four or more characters are redacted as substrings; shorter
|
|
165
|
+
values require token boundaries so low-entropy values do not corrupt unrelated
|
|
166
|
+
words or timestamps. Consequently, a sub-four-character secret embedded in a
|
|
167
|
+
larger alphanumeric token can remain in free text; prefer higher-entropy or
|
|
168
|
+
non-query credentials when possible. `sensitiveParams: {}` is equivalent to
|
|
169
|
+
omitting the option. Redirect results structurally redact declared keys and
|
|
170
|
+
common response-only credential keys from hop URLs; use
|
|
171
|
+
`redirects.run({ stopWhen })` to inspect a real intermediate `Location` during
|
|
172
|
+
the run when a login flow needs a rotated value.
|
|
173
|
+
- **Proxy URLs for non-stealth consumers**: use `resolveProxy()` when a
|
|
174
|
+
provider-owned client outside `ctx.stealth` needs the provider's proxy, such
|
|
175
|
+
as a CAPTCHA solver that must use matching egress. Pass the provider proxy
|
|
176
|
+
policy used by the provider and consume the returned `url`; the SDK owns
|
|
177
|
+
vendor selection, allocation, failover, and URL formats. Never call proxy
|
|
178
|
+
allocator APIs or hardcode proxy vendor hostnames in provider code.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { resolveProxy } from "@apifuse/provider-sdk"
|
|
182
|
+
|
|
183
|
+
const resolvedProxy = await resolveProxy({
|
|
184
|
+
proxyPolicy,
|
|
185
|
+
affinityKey: connectionId,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
if (!resolvedProxy.url) throw new Error("This login requires proxy egress")
|
|
189
|
+
const captchaTask = { proxy: resolvedProxy.url }
|
|
190
|
+
```
|
|
145
191
|
- **Browser providers**: for TypeScript Providers use `runtime: "browser"` plus
|
|
146
192
|
`browser.engine: "playwright-stealth"`; `nodriver` is a Python-runtime path.
|
|
147
193
|
Install local browser assets with `bunx playwright install chromium` when
|
package/SUBMISSION.md
CHANGED
|
@@ -50,7 +50,7 @@ Fix all blockers before submitting:
|
|
|
50
50
|
- High-confidence secret or token material in source, README, package metadata, or fixtures.
|
|
51
51
|
- SDK-native source blockers: prefixed Provider ids, `vendor/` SDK shims or imports, raw `.describe()` prose instead of `describeKey`, raw global `fetch()` calls, and excessive `as Type` assertions.
|
|
52
52
|
|
|
53
|
-
Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts
|
|
53
|
+
Warnings do not fail the command, but they should be addressed when practical. For example, the generated starter `ping` operation warns because it is not a real upstream-backed bounty Operation. SDK-native warnings also flag moderate `as Type` assertion counts, credentialed Providers that never reference `ctx.credential`, and provider-local re-validation of declared env secrets (`sdk-owned-secret-presence`, 0 points): the SDK already rejects invocations with a structured `MISSING_SECRET` error when a declared `required: true` secret is unset, so `requireServiceKey`-style presence guards are deprecated dead weight — delete the guard and read the value directly with `ctx.env.get()`, or acknowledge a deliberate exception with `// @apifuse-allow sdk-owned-secret-presence: <reason>`.
|
|
54
54
|
|
|
55
55
|
## Measured local smoke
|
|
56
56
|
|
package/bin/apifuse-check.ts
CHANGED
|
@@ -6,9 +6,14 @@ import { pathToFileURL } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import {
|
|
10
|
+
formatPromptAssetIssues,
|
|
11
|
+
PROMPT_ASSET_SYNC_REMEDIATION,
|
|
12
|
+
verifyPromptAssets,
|
|
13
|
+
} from "../src/cli/prompt-assets.js";
|
|
14
|
+
import type { ProviderDefinition } from "../src/index.js";
|
|
15
|
+
import { lintProvider, type ProviderLintMode } from "../src/lint.js";
|
|
16
|
+
import { safeParseSchemaSync } from "../src/schema.js";
|
|
12
17
|
|
|
13
18
|
const HELP_TEXT = `Usage: apifuse check [path]
|
|
14
19
|
Example: apifuse check providers/korea-air-quality
|
|
@@ -24,9 +29,7 @@ export type RunChecksOptions = {
|
|
|
24
29
|
lintMode?: ProviderLintMode;
|
|
25
30
|
};
|
|
26
31
|
|
|
27
|
-
type SafeParseResult =
|
|
28
|
-
| { success: true; data: unknown }
|
|
29
|
-
| { success: false; error: unknown };
|
|
32
|
+
type SafeParseResult = { success: true; data: unknown } | { success: false; error: unknown };
|
|
30
33
|
|
|
31
34
|
export async function main() {
|
|
32
35
|
const args = normalizeArgs(process.argv.slice(2));
|
|
@@ -63,7 +66,7 @@ function normalizeArgs(argv: string[]): string[] {
|
|
|
63
66
|
return argv[0] === "check" ? argv.slice(1) : argv;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
function resolveProviderRoot(inputPath: string): string {
|
|
69
|
+
export function resolveProviderRoot(inputPath: string): string {
|
|
67
70
|
const resolvedInput = resolveFromParents(inputPath);
|
|
68
71
|
|
|
69
72
|
if (!existsSync(resolvedInput)) {
|
|
@@ -131,9 +134,26 @@ export async function runChecks(
|
|
|
131
134
|
checkProviderMetadata(provider),
|
|
132
135
|
checkDockerfile(dockerfilePath),
|
|
133
136
|
checkPackageJson(packageJsonPath),
|
|
137
|
+
checkPromptAssets(providerRoot),
|
|
134
138
|
];
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
export const PROMPT_ASSETS_CHECK_MESSAGE =
|
|
142
|
+
"Agent prompt assets match the installed SDK version";
|
|
143
|
+
|
|
144
|
+
function checkPromptAssets(providerRoot: string): CheckResult {
|
|
145
|
+
const verification = verifyPromptAssets(providerRoot);
|
|
146
|
+
if (verification.ok) {
|
|
147
|
+
return { message: PROMPT_ASSETS_CHECK_MESSAGE, passed: true };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
message: PROMPT_ASSETS_CHECK_MESSAGE,
|
|
152
|
+
passed: false,
|
|
153
|
+
details: [...formatPromptAssetIssues(verification), PROMPT_ASSET_SYNC_REMEDIATION],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
137
157
|
function isScannableProviderSourceFile(relativePath: string): boolean {
|
|
138
158
|
return (
|
|
139
159
|
/\.(?:ts|tsx|js|jsx|mjs|cjs|sh|bash)$/.test(relativePath) ||
|
|
@@ -142,17 +162,12 @@ function isScannableProviderSourceFile(relativePath: string): boolean {
|
|
|
142
162
|
);
|
|
143
163
|
}
|
|
144
164
|
|
|
145
|
-
function collectProviderSourceFiles(
|
|
146
|
-
providerRoot: string,
|
|
147
|
-
): Record<string, string> {
|
|
165
|
+
function collectProviderSourceFiles(providerRoot: string): Record<string, string> {
|
|
148
166
|
const sources: Record<string, string> = {};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"build",
|
|
154
|
-
".next",
|
|
155
|
-
]);
|
|
167
|
+
// `.agents`/`.apifuse` are deliberately not skipped: managed content there
|
|
168
|
+
// is markdown/JSON (never matched by isScannableProviderSourceFile), and a
|
|
169
|
+
// planted `.ts`/`.sh` under those directories must stay in scanner scope.
|
|
170
|
+
const skipDirectories = new Set([".git", "node_modules", "dist", "build", ".next"]);
|
|
156
171
|
const visit = (directory: string) => {
|
|
157
172
|
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
158
173
|
const path = resolve(directory, entry.name);
|
|
@@ -162,10 +177,7 @@ function collectProviderSourceFiles(
|
|
|
162
177
|
}
|
|
163
178
|
continue;
|
|
164
179
|
}
|
|
165
|
-
if (
|
|
166
|
-
!entry.isFile() ||
|
|
167
|
-
!isScannableProviderSourceFile(path.slice(providerRoot.length + 1))
|
|
168
|
-
) {
|
|
180
|
+
if (!entry.isFile() || !isScannableProviderSourceFile(path.slice(providerRoot.length + 1))) {
|
|
169
181
|
continue;
|
|
170
182
|
}
|
|
171
183
|
sources[path.slice(providerRoot.length + 1)] = readFileSync(path, "utf8");
|
|
@@ -175,10 +187,7 @@ function collectProviderSourceFiles(
|
|
|
175
187
|
return sources;
|
|
176
188
|
}
|
|
177
189
|
|
|
178
|
-
function checkIndex(
|
|
179
|
-
indexPath: string,
|
|
180
|
-
provider: ProviderDefinition | undefined,
|
|
181
|
-
): CheckResult {
|
|
190
|
+
function checkIndex(indexPath: string, provider: ProviderDefinition | undefined): CheckResult {
|
|
182
191
|
if (!existsSync(indexPath)) {
|
|
183
192
|
return {
|
|
184
193
|
message: "index.ts exists and exports default defineProvider",
|
|
@@ -200,9 +209,7 @@ function checkIndex(
|
|
|
200
209
|
};
|
|
201
210
|
}
|
|
202
211
|
|
|
203
|
-
function checkOperations(
|
|
204
|
-
provider: ProviderDefinition | undefined,
|
|
205
|
-
): CheckResult {
|
|
212
|
+
function checkOperations(provider: ProviderDefinition | undefined): CheckResult {
|
|
206
213
|
if (!provider) {
|
|
207
214
|
return {
|
|
208
215
|
message: "All operations have handler, input, output",
|
|
@@ -277,20 +284,14 @@ function checkSchemas(provider: ProviderDefinition | undefined): CheckResult {
|
|
|
277
284
|
continue;
|
|
278
285
|
}
|
|
279
286
|
|
|
280
|
-
const requestResult = parseFixture(
|
|
281
|
-
operation.input,
|
|
282
|
-
operation.fixtures.request,
|
|
283
|
-
);
|
|
287
|
+
const requestResult = parseFixture(operation.input, operation.fixtures.request);
|
|
284
288
|
if (!requestResult.success) {
|
|
285
289
|
failures.push(
|
|
286
290
|
`${operationId}: request fixture invalid (${formatSchemaError(requestResult.error)})`,
|
|
287
291
|
);
|
|
288
292
|
}
|
|
289
293
|
|
|
290
|
-
const responseResult = parseFixture(
|
|
291
|
-
operation.output,
|
|
292
|
-
operation.fixtures.response,
|
|
293
|
-
);
|
|
294
|
+
const responseResult = parseFixture(operation.output, operation.fixtures.response);
|
|
294
295
|
if (!responseResult.success) {
|
|
295
296
|
failures.push(
|
|
296
297
|
`${operationId}: response fixture invalid (${formatSchemaError(responseResult.error)})`,
|
|
@@ -317,13 +318,8 @@ function checkAuthoringLint(
|
|
|
317
318
|
};
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
const diagnostics = lintProvider(
|
|
321
|
-
|
|
322
|
-
{ mode: lintMode },
|
|
323
|
-
);
|
|
324
|
-
const errors = diagnostics.filter(
|
|
325
|
-
(diagnostic) => diagnostic.level === "error",
|
|
326
|
-
);
|
|
321
|
+
const diagnostics = lintProvider({ ...provider, providerSourceFiles }, { mode: lintMode });
|
|
322
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.level === "error");
|
|
327
323
|
const details = diagnostics.map((diagnostic) => {
|
|
328
324
|
const field = diagnostic.field ? `${diagnostic.field}: ` : "";
|
|
329
325
|
return `${diagnostic.level.toUpperCase()} ${diagnostic.rule} ${field}${diagnostic.message}`;
|
|
@@ -336,9 +332,7 @@ function checkAuthoringLint(
|
|
|
336
332
|
};
|
|
337
333
|
}
|
|
338
334
|
|
|
339
|
-
function checkProviderMetadata(
|
|
340
|
-
provider: ProviderDefinition | undefined,
|
|
341
|
-
): CheckResult {
|
|
335
|
+
function checkProviderMetadata(provider: ProviderDefinition | undefined): CheckResult {
|
|
342
336
|
if (!provider) {
|
|
343
337
|
return {
|
|
344
338
|
message: "Provider metadata is declared in defineProvider",
|
|
@@ -425,18 +419,12 @@ function checkPackageJson(packageJsonPath: string): CheckResult {
|
|
|
425
419
|
}
|
|
426
420
|
}
|
|
427
421
|
|
|
428
|
-
function assertProviderDefinition(
|
|
429
|
-
value: unknown,
|
|
430
|
-
): ProviderDefinition | undefined {
|
|
422
|
+
function assertProviderDefinition(value: unknown): ProviderDefinition | undefined {
|
|
431
423
|
return isProviderDefinition(value) ? value : undefined;
|
|
432
424
|
}
|
|
433
425
|
|
|
434
426
|
function isProviderDefinition(value: unknown): value is ProviderDefinition {
|
|
435
|
-
if (
|
|
436
|
-
!isRecord(value) ||
|
|
437
|
-
!isRecord(value.meta) ||
|
|
438
|
-
!isRecord(value.operations)
|
|
439
|
-
) {
|
|
427
|
+
if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
|
|
440
428
|
return false;
|
|
441
429
|
}
|
|
442
430
|
|
|
@@ -457,8 +445,7 @@ function hasSchemaParser(value: unknown): boolean {
|
|
|
457
445
|
return (
|
|
458
446
|
isRecord(value) &&
|
|
459
447
|
(typeof value.safeParse === "function" ||
|
|
460
|
-
(isRecord(value["~standard"]) &&
|
|
461
|
-
typeof value["~standard"].validate === "function"))
|
|
448
|
+
(isRecord(value["~standard"]) && typeof value["~standard"].validate === "function"))
|
|
462
449
|
);
|
|
463
450
|
}
|
|
464
451
|
|
|
@@ -470,9 +457,7 @@ function formatSchemaError(error: unknown): string {
|
|
|
470
457
|
if (Array.isArray(error)) {
|
|
471
458
|
return error
|
|
472
459
|
.map((issue) =>
|
|
473
|
-
isRecord(issue) && typeof issue.message === "string"
|
|
474
|
-
? issue.message
|
|
475
|
-
: String(issue),
|
|
460
|
+
isRecord(issue) && typeof issue.message === "string" ? issue.message : String(issue),
|
|
476
461
|
)
|
|
477
462
|
.join(", ");
|
|
478
463
|
}
|
package/bin/apifuse-create.ts
CHANGED
package/bin/apifuse-dev.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { dirname, relative, resolve } from "node:path";
|
|
5
|
-
import type { ProviderDefinition } from "../src";
|
|
5
|
+
import type { ProviderDefinition } from "../src/index.js";
|
|
6
6
|
import {
|
|
7
7
|
createBrowserClient,
|
|
8
8
|
createCredentialContext,
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
createSttClientFromEnv,
|
|
15
15
|
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
16
16
|
ProviderError,
|
|
17
|
-
} from "../src";
|
|
18
|
-
import { createMemoryProviderRuntimeState } from "../src/runtime/state";
|
|
19
|
-
import { createTraceContext } from "../src/runtime/trace";
|
|
20
|
-
import type { BrowserClient, ProviderContext } from "../src/types";
|
|
17
|
+
} from "../src/index.js";
|
|
18
|
+
import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
|
|
19
|
+
import { createTraceContext } from "../src/runtime/trace.js";
|
|
20
|
+
import type { BrowserClient, ProviderContext } from "../src/types.js";
|
|
21
21
|
|
|
22
22
|
const HELP_TEXT = `Usage: apifuse dev [path]
|
|
23
23
|
Example: apifuse dev providers/korea-air-quality
|
|
@@ -33,12 +33,9 @@ export async function main() {
|
|
|
33
33
|
|
|
34
34
|
const providerPath = resolveProviderPath(args[0] ?? ".");
|
|
35
35
|
const providerModule = await import(resolve(providerPath, "index.ts"));
|
|
36
|
-
const provider = assertProviderDefinition(
|
|
37
|
-
providerModule.default,
|
|
38
|
-
providerPath,
|
|
39
|
-
);
|
|
36
|
+
const provider = assertProviderDefinition(providerModule.default, providerPath);
|
|
40
37
|
|
|
41
|
-
const { startDevServer } = await import("../src/dev");
|
|
38
|
+
const { startDevServer } = await import("../src/dev.js");
|
|
42
39
|
const port = Number(process.env.APIFUSE__RUNTIME__PORT) || 3900;
|
|
43
40
|
|
|
44
41
|
startDevServer(provider, { port });
|
|
@@ -57,8 +54,7 @@ export async function main() {
|
|
|
57
54
|
|
|
58
55
|
const firstOperation = Object.keys(provider.operations)[0];
|
|
59
56
|
if (firstOperation) {
|
|
60
|
-
const sampleInput =
|
|
61
|
-
provider.operations[firstOperation]?.fixtures?.request ?? {};
|
|
57
|
+
const sampleInput = provider.operations[firstOperation]?.fixtures?.request ?? {};
|
|
62
58
|
const sampleBody = JSON.stringify({
|
|
63
59
|
requestId: `req_local_${firstOperation}`,
|
|
64
60
|
input: sampleInput,
|
|
@@ -149,9 +145,7 @@ function renderHotReloadCommand(providerPath: string, port: number): string {
|
|
|
149
145
|
const devEntry = resolve(providerPath, "dev.ts");
|
|
150
146
|
if (existsSync(devEntry)) {
|
|
151
147
|
const relativeDevEntry = relative(process.cwd(), devEntry) || "dev.ts";
|
|
152
|
-
const portPrefix = process.env.APIFUSE__RUNTIME__PORT
|
|
153
|
-
? `APIFUSE__RUNTIME__PORT=${port} `
|
|
154
|
-
: "";
|
|
148
|
+
const portPrefix = process.env.APIFUSE__RUNTIME__PORT ? `APIFUSE__RUNTIME__PORT=${port} ` : "";
|
|
155
149
|
return `${portPrefix}bun --hot ${relativeDevEntry}`;
|
|
156
150
|
}
|
|
157
151
|
return "rerun `apifuse dev` after edits (no dev.ts entrypoint found)";
|
|
@@ -166,40 +160,28 @@ function createUnsupportedBrowserStub(): BrowserClient {
|
|
|
166
160
|
engine: "playwright-stealth",
|
|
167
161
|
async close() {},
|
|
168
162
|
async newPage() {
|
|
169
|
-
throw new ProviderError(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
fix: 'Set provider runtime to "browser" to use ctx.browser',
|
|
174
|
-
},
|
|
175
|
-
);
|
|
163
|
+
throw new ProviderError("Browser runtime is not enabled for this provider", {
|
|
164
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
165
|
+
fix: 'Set provider runtime to "browser" to use ctx.browser',
|
|
166
|
+
});
|
|
176
167
|
},
|
|
177
168
|
async rawPage() {
|
|
178
|
-
throw new ProviderError(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
|
|
183
|
-
},
|
|
184
|
-
);
|
|
169
|
+
throw new ProviderError("Browser runtime is not enabled for this provider", {
|
|
170
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
171
|
+
fix: 'Set provider runtime to "browser" and APIFUSE__CDP_POOL__URL to use ctx.browser.rawPage',
|
|
172
|
+
});
|
|
185
173
|
},
|
|
186
174
|
async withIsolatedContext() {
|
|
187
|
-
throw new ProviderError(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
|
|
192
|
-
},
|
|
193
|
-
);
|
|
175
|
+
throw new ProviderError("Browser runtime is not enabled for this provider", {
|
|
176
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
177
|
+
fix: 'Set provider runtime to "browser" to use ctx.browser.withIsolatedContext',
|
|
178
|
+
});
|
|
194
179
|
},
|
|
195
180
|
async solveChallenge() {
|
|
196
|
-
throw new ProviderError(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
|
|
201
|
-
},
|
|
202
|
-
);
|
|
181
|
+
throw new ProviderError("Browser runtime is not enabled for this provider", {
|
|
182
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
183
|
+
fix: 'Set provider runtime to "browser" to use ctx.browser.solveChallenge',
|
|
184
|
+
});
|
|
203
185
|
},
|
|
204
186
|
};
|
|
205
187
|
}
|
|
@@ -216,10 +198,7 @@ function createUnsupportedAuthStub() {
|
|
|
216
198
|
};
|
|
217
199
|
}
|
|
218
200
|
|
|
219
|
-
function assertProviderDefinition(
|
|
220
|
-
value: unknown,
|
|
221
|
-
providerPath: string,
|
|
222
|
-
): ProviderDefinition {
|
|
201
|
+
function assertProviderDefinition(value: unknown, providerPath: string): ProviderDefinition {
|
|
223
202
|
if (!isProviderDefinition(value)) {
|
|
224
203
|
throw new Error(
|
|
225
204
|
`Expected ${resolve(providerPath, "index.ts")} to export default defineProvider(...)`,
|
|
@@ -230,11 +209,7 @@ function assertProviderDefinition(
|
|
|
230
209
|
}
|
|
231
210
|
|
|
232
211
|
function isProviderDefinition(value: unknown): value is ProviderDefinition {
|
|
233
|
-
if (
|
|
234
|
-
!isRecord(value) ||
|
|
235
|
-
!isRecord(value.meta) ||
|
|
236
|
-
!isRecord(value.operations)
|
|
237
|
-
) {
|
|
212
|
+
if (!isRecord(value) || !isRecord(value.meta) || !isRecord(value.operations)) {
|
|
238
213
|
return false;
|
|
239
214
|
}
|
|
240
215
|
|
|
@@ -48,6 +48,13 @@ const requiredPaths = [
|
|
|
48
48
|
"src/cli/templates/provider/operations/ping.ts.tpl",
|
|
49
49
|
"src/cli/templates/provider/schemas/ping.ts.tpl",
|
|
50
50
|
"src/cli/templates/provider/upstream/README.md.tpl",
|
|
51
|
+
"src/cli/templates/provider/AGENTS.md.tpl",
|
|
52
|
+
"src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
|
|
53
|
+
"src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
|
|
54
|
+
"src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
|
|
55
|
+
"src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
|
|
56
|
+
"src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
|
|
57
|
+
"src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
|
|
51
58
|
"dist/cli/templates/provider/.dockerignore.tpl",
|
|
52
59
|
"dist/cli/templates/provider/.gitignore.tpl",
|
|
53
60
|
"dist/cli/templates/provider/Dockerfile.tpl",
|
|
@@ -60,6 +67,13 @@ const requiredPaths = [
|
|
|
60
67
|
"dist/cli/templates/provider/operations/ping.ts.tpl",
|
|
61
68
|
"dist/cli/templates/provider/schemas/ping.ts.tpl",
|
|
62
69
|
"dist/cli/templates/provider/upstream/README.md.tpl",
|
|
70
|
+
"dist/cli/templates/provider/AGENTS.md.tpl",
|
|
71
|
+
"dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl",
|
|
72
|
+
"dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl",
|
|
73
|
+
"dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl",
|
|
74
|
+
"dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl",
|
|
75
|
+
"dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl",
|
|
76
|
+
"dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl",
|
|
63
77
|
"dist/auth-turn/index.js",
|
|
64
78
|
"dist/auth-turn/index.d.ts",
|
|
65
79
|
"dist/auth-turn/auth-turn.v1.schema.json",
|
|
@@ -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");
|
|
@@ -73,6 +59,20 @@ try {
|
|
|
73
59
|
);
|
|
74
60
|
|
|
75
61
|
run("bun", ["install"], consumerDir);
|
|
62
|
+
run(
|
|
63
|
+
"bun",
|
|
64
|
+
[
|
|
65
|
+
"--eval",
|
|
66
|
+
[
|
|
67
|
+
'import { resolveProxy } from "@apifuse/provider-sdk";',
|
|
68
|
+
'if (typeof resolveProxy !== "function") throw new Error("resolveProxy is not exported");',
|
|
69
|
+
'const resolved = await resolveProxy({ proxy: "http://127.0.0.1:8080" });',
|
|
70
|
+
'if (resolved.url !== "http://127.0.0.1:8080") throw new Error("resolveProxy returned the wrong URL");',
|
|
71
|
+
'console.log("packed root resolveProxy export OK");',
|
|
72
|
+
].join("\n"),
|
|
73
|
+
],
|
|
74
|
+
consumerDir,
|
|
75
|
+
);
|
|
76
76
|
|
|
77
77
|
const cliBin = join(consumerDir, "node_modules", ".bin", "apifuse");
|
|
78
78
|
if (!existsSync(cliBin)) {
|
|
@@ -81,15 +81,7 @@ try {
|
|
|
81
81
|
|
|
82
82
|
run(
|
|
83
83
|
"bun",
|
|
84
|
-
[
|
|
85
|
-
cliBin,
|
|
86
|
-
"create",
|
|
87
|
-
"dx-smoke",
|
|
88
|
-
"--yes",
|
|
89
|
-
"--json",
|
|
90
|
-
"--sdk-specifier",
|
|
91
|
-
tarballSpecifier,
|
|
92
|
-
],
|
|
84
|
+
[cliBin, "create", "dx-smoke", "--yes", "--json", "--sdk-specifier", tarballSpecifier],
|
|
93
85
|
consumerDir,
|
|
94
86
|
);
|
|
95
87
|
|
|
@@ -99,11 +91,7 @@ try {
|
|
|
99
91
|
run("bun", ["run", "test"], generatedProviderDir);
|
|
100
92
|
assertGeneratedReadme(generatedProviderDir);
|
|
101
93
|
await smokeGeneratedDevServer(generatedProviderDir);
|
|
102
|
-
assertExternalWorkspaceTopology(
|
|
103
|
-
cliBin,
|
|
104
|
-
externalWorkspaceDir,
|
|
105
|
-
tarballSpecifier,
|
|
106
|
-
);
|
|
94
|
+
assertExternalWorkspaceTopology(cliBin, externalWorkspaceDir, tarballSpecifier);
|
|
107
95
|
|
|
108
96
|
console.log(
|
|
109
97
|
`Provider SDK packed-artifact smoke passed: ${tarballPath} -> ${generatedProviderDir}`,
|
|
@@ -148,15 +136,8 @@ function assertExternalWorkspaceTopology(
|
|
|
148
136
|
externalWorkspaceDir,
|
|
149
137
|
);
|
|
150
138
|
|
|
151
|
-
const generatedProviderDir = join(
|
|
152
|
-
|
|
153
|
-
"external-workspace-smoke",
|
|
154
|
-
);
|
|
155
|
-
const forbiddenProviderDir = join(
|
|
156
|
-
externalWorkspaceDir,
|
|
157
|
-
"providers",
|
|
158
|
-
"external-workspace-smoke",
|
|
159
|
-
);
|
|
139
|
+
const generatedProviderDir = join(externalWorkspaceDir, "external-workspace-smoke");
|
|
140
|
+
const forbiddenProviderDir = join(externalWorkspaceDir, "providers", "external-workspace-smoke");
|
|
160
141
|
if (!existsSync(generatedProviderDir)) {
|
|
161
142
|
throw new Error(
|
|
162
143
|
"Public create must generate a one-provider repository at <name>/ even when providers/ exists.",
|
|
@@ -168,9 +149,7 @@ function assertExternalWorkspaceTopology(
|
|
|
168
149
|
);
|
|
169
150
|
}
|
|
170
151
|
|
|
171
|
-
const packageJson = JSON.parse(
|
|
172
|
-
readFileSync(join(generatedProviderDir, "package.json"), "utf8"),
|
|
173
|
-
);
|
|
152
|
+
const packageJson = JSON.parse(readFileSync(join(generatedProviderDir, "package.json"), "utf8"));
|
|
174
153
|
const sdkDependency = packageJson?.dependencies?.["@apifuse/provider-sdk"];
|
|
175
154
|
if (sdkDependency !== tarballSpecifier) {
|
|
176
155
|
throw new Error(
|
|
@@ -178,9 +157,7 @@ function assertExternalWorkspaceTopology(
|
|
|
178
157
|
);
|
|
179
158
|
}
|
|
180
159
|
if (JSON.stringify(packageJson).includes("workspace:")) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
"External bounty workspace scaffold must not contain workspace: dependencies.",
|
|
183
|
-
);
|
|
160
|
+
throw new Error("External bounty workspace scaffold must not contain workspace: dependencies.");
|
|
184
161
|
}
|
|
185
162
|
|
|
186
163
|
run("bun", ["install"], generatedProviderDir);
|
|
@@ -199,30 +176,20 @@ function assertExternalWorkspaceTopology(
|
|
|
199
176
|
},
|
|
200
177
|
);
|
|
201
178
|
if (monorepoAttempt.status === 0) {
|
|
202
|
-
throw new Error(
|
|
203
|
-
"--preset monorepo must reject outside the private APIFuse monorepo.",
|
|
204
|
-
);
|
|
179
|
+
throw new Error("--preset monorepo must reject outside the private APIFuse monorepo.");
|
|
205
180
|
}
|
|
206
181
|
const rejectionOutput = `${monorepoAttempt.stdout}\n${monorepoAttempt.stderr}`;
|
|
207
|
-
if (
|
|
208
|
-
!rejectionOutput.includes(
|
|
209
|
-
"Monorepo preset is internal to the APIFuse repository",
|
|
210
|
-
)
|
|
211
|
-
) {
|
|
182
|
+
if (!rejectionOutput.includes("Monorepo preset is internal to the APIFuse repository")) {
|
|
212
183
|
throw new Error(`Unexpected monorepo rejection output: ${rejectionOutput}`);
|
|
213
184
|
}
|
|
214
185
|
}
|
|
215
186
|
|
|
216
187
|
function packSdk(destination: string): { filename: string } {
|
|
217
|
-
const raw = execFileSync(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
encoding: "utf8",
|
|
223
|
-
stdio: ["ignore", "pipe", "inherit"],
|
|
224
|
-
},
|
|
225
|
-
);
|
|
188
|
+
const raw = execFileSync("npm", ["pack", "--json", "--pack-destination", destination], {
|
|
189
|
+
cwd: process.cwd(),
|
|
190
|
+
encoding: "utf8",
|
|
191
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
192
|
+
});
|
|
226
193
|
const parsed = PACK_RESULT_SCHEMA.parse(JSON.parse(raw));
|
|
227
194
|
const first = parsed[0];
|
|
228
195
|
if (!first) {
|
|
@@ -252,29 +219,19 @@ function run(command: string, args: string[], cwd: string): void {
|
|
|
252
219
|
function assertGeneratedReadme(providerDir: string): void {
|
|
253
220
|
const readme = readFileSync(join(providerDir, "README.md"), "utf8");
|
|
254
221
|
if (!readme.includes('"requestId":"req_local_ping"')) {
|
|
255
|
-
throw new Error(
|
|
256
|
-
"Generated README is missing requestId in local smoke docs.",
|
|
257
|
-
);
|
|
222
|
+
throw new Error("Generated README is missing requestId in local smoke docs.");
|
|
258
223
|
}
|
|
259
224
|
if (readme.includes('"connection":null')) {
|
|
260
|
-
throw new Error(
|
|
261
|
-
"Generated README must not document connection:null for no-auth local smoke.",
|
|
262
|
-
);
|
|
225
|
+
throw new Error("Generated README must not document connection:null for no-auth local smoke.");
|
|
263
226
|
}
|
|
264
227
|
if (!readme.includes("bunx playwright install chromium")) {
|
|
265
|
-
throw new Error(
|
|
266
|
-
"Generated README is missing browser runtime troubleshooting guidance.",
|
|
267
|
-
);
|
|
228
|
+
throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
|
|
268
229
|
}
|
|
269
230
|
if (!readme.includes("impit")) {
|
|
270
|
-
throw new Error(
|
|
271
|
-
"Generated README is missing impit stealth runtime guidance.",
|
|
272
|
-
);
|
|
231
|
+
throw new Error("Generated README is missing impit stealth runtime guidance.");
|
|
273
232
|
}
|
|
274
233
|
if (!readme.includes("bun run submit-check")) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
"Generated README must document the submit-check pre-submission workflow.",
|
|
277
|
-
);
|
|
234
|
+
throw new Error("Generated README must document the submit-check pre-submission workflow.");
|
|
278
235
|
}
|
|
279
236
|
if (!readme.includes("bun run record -- --operation <operation>")) {
|
|
280
237
|
throw new Error(
|
|
@@ -368,9 +325,7 @@ async function waitForHttp(
|
|
|
368
325
|
|
|
369
326
|
while (Date.now() < deadline) {
|
|
370
327
|
if (server.exitCode !== null) {
|
|
371
|
-
throw new Error(
|
|
372
|
-
`Dev server exited early with code ${server.exitCode}\n${getOutput()}`,
|
|
373
|
-
);
|
|
328
|
+
throw new Error(`Dev server exited early with code ${server.exitCode}\n${getOutput()}`);
|
|
374
329
|
}
|
|
375
330
|
|
|
376
331
|
try {
|