@apifuse/provider-sdk 2.2.0-beta.5 → 2.2.0-beta.50
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 +537 -33
- package/CHANGELOG.md +201 -1
- package/README.md +83 -22
- package/SUBMISSION.md +2 -2
- package/bin/apifuse-check.ts +165 -11
- package/bin/apifuse-dev.ts +57 -13
- package/bin/apifuse-migrate-operation-declaration.ts +55 -0
- package/bin/apifuse-migrate-shape.ts +202 -0
- package/bin/apifuse-pack-check.ts +22 -2
- package/bin/apifuse-pack-smoke.ts +57 -2
- package/bin/apifuse-pack-types.ts +357 -38
- package/bin/apifuse-perf.ts +14 -13
- package/bin/apifuse-record.ts +709 -72
- package/bin/apifuse-submit-check.ts +2344 -324
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/submit-check-delimited-text.ts +50 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +67 -0
- package/dist/ceremonies/index.d.ts +16 -0
- package/dist/ceremonies/index.js +141 -36
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +27 -0
- package/dist/cli/create.d.ts +4 -1
- package/dist/cli/create.js +42 -37
- package/dist/cli/migrate-operation-declaration.d.ts +59 -0
- package/dist/cli/migrate-operation-declaration.js +1178 -0
- package/dist/cli/migrate-operation-shape.d.ts +44 -0
- package/dist/cli/migrate-operation-shape.js +113 -0
- package/dist/cli/migrate-provider-shape.d.ts +52 -0
- package/dist/cli/migrate-provider-shape.js +578 -0
- 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/cli/templates/provider/Dockerfile.tpl +1 -1
- package/dist/cli/templates/provider/README.md.tpl +5 -5
- package/dist/cli/templates/provider/index.ts.tpl +6 -3
- package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/dist/cli/templates/provider/provider.json.tpl +6 -0
- package/dist/config/loader.d.ts +179 -16
- package/dist/config/loader.js +435 -127
- package/dist/contract-serialization.js +4 -8
- package/dist/contract-types.d.ts +12 -5
- package/dist/contract.js +24 -10
- package/dist/declaration-validation.d.ts +33 -0
- package/dist/declaration-validation.js +266 -0
- package/dist/define.d.ts +66 -29
- package/dist/define.js +778 -69
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +1 -1
- package/dist/engine.d.ts +78 -0
- package/dist/engine.js +133 -0
- package/dist/error-observability.d.ts +7 -0
- package/dist/error-observability.js +61 -0
- package/dist/error-resolution.d.ts +4 -0
- package/dist/error-resolution.js +122 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +40 -0
- package/dist/fixture-sanitization.d.ts +28 -0
- package/dist/fixture-sanitization.js +227 -0
- package/dist/health-scenario.d.ts +1842 -0
- package/dist/health-scenario.js +624 -0
- package/dist/index.d.ts +21 -10
- package/dist/index.js +12 -7
- package/dist/lint.d.ts +23 -11
- package/dist/lint.js +572 -69
- 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 +10 -2
- package/dist/provider.js +4 -1
- package/dist/runtime/auth-flow.d.ts +5 -1
- package/dist/runtime/auth-flow.js +6 -0
- package/dist/runtime/browser.d.ts +1 -0
- package/dist/runtime/browser.js +492 -49
- package/dist/runtime/cache.d.ts +1 -0
- package/dist/runtime/cache.js +169 -15
- package/dist/runtime/choice-wordlist.d.ts +9 -0
- package/dist/runtime/choice-wordlist.js +138 -0
- package/dist/runtime/choice.d.ts +13 -1
- package/dist/runtime/choice.js +490 -102
- package/dist/runtime/chrome149-header-order.d.ts +58 -0
- package/dist/runtime/chrome149-header-order.js +289 -0
- package/dist/runtime/env.js +12 -0
- package/dist/runtime/executor.d.ts +3 -2
- package/dist/runtime/executor.js +26 -25
- package/dist/runtime/http.d.ts +1 -0
- package/dist/runtime/http.js +515 -53
- package/dist/runtime/insights.js +2 -2
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +366 -8
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +96 -0
- package/dist/runtime/native-network.js +1232 -0
- package/dist/runtime/ocr.d.ts +29 -0
- package/dist/runtime/ocr.js +440 -0
- package/dist/runtime/otlp.d.ts +71 -2
- package/dist/runtime/otlp.js +397 -16
- package/dist/runtime/proxy-errors.js +6 -2
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-telemetry.d.ts +80 -1
- package/dist/runtime/proxy-telemetry.js +154 -47
- 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 +4 -2
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/resolver-config.d.ts +6 -0
- package/dist/runtime/resolver-config.js +6 -0
- package/dist/runtime/resolver-public.d.ts +1 -0
- package/dist/runtime/resolver-public.js +1 -0
- package/dist/runtime/resolver-shared.d.ts +14 -0
- package/dist/runtime/resolver-shared.js +12 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
- package/dist/runtime/resolver-vendors/bindings.js +40 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +22 -0
- package/dist/runtime/resolver-vendors/browser.js +377 -0
- package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
- package/dist/runtime/resolver-vendors/capsolver.js +531 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +408 -0
- package/dist/runtime/resolver-vendors/types.d.ts +94 -0
- package/dist/runtime/resolver-vendors/types.js +96 -0
- package/dist/runtime/resolver.d.ts +72 -0
- package/dist/runtime/resolver.js +771 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +277 -71
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.d.ts +41 -5
- package/dist/runtime/stealth.js +997 -291
- package/dist/runtime/stt.js +1 -12
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace-config.d.ts +12 -0
- package/dist/runtime/trace-config.js +62 -0
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +43 -10
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/error-observability.d.ts +1 -0
- package/dist/server/error-observability.js +1 -0
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.js +2 -2
- 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.d.ts +14 -3
- package/dist/server/self-test.js +129 -58
- package/dist/server/serve-implementation.d.ts +230 -0
- package/dist/server/serve-implementation.js +2265 -0
- package/dist/server/serve.d.ts +1 -70
- package/dist/server/serve.js +1 -1131
- package/dist/server/trace-output.d.ts +6 -0
- package/dist/server/trace-output.js +79 -0
- package/dist/server/types.d.ts +30 -5
- package/dist/server/types.js +13 -1
- package/dist/stateful/errors.d.ts +19 -0
- package/dist/stateful/errors.js +24 -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 +67 -0
- package/dist/stateful/stateful-provider-session-routing.js +345 -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 +9 -4
- package/dist/stealth/profiles.js +160 -211
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +489 -21
- package/dist/trace-sanitization.d.ts +5 -0
- package/dist/trace-sanitization.js +45 -0
- package/dist/types.d.ts +673 -131
- package/dist/types.js +1 -0
- package/package.json +45 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +118 -0
- package/src/ceremonies/index.ts +189 -46
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
- package/src/cli/commands.ts +33 -0
- package/src/cli/create.ts +50 -37
- package/src/cli/migrate-operation-declaration.ts +1654 -0
- package/src/cli/migrate-operation-shape.ts +184 -0
- package/src/cli/migrate-provider-shape.ts +772 -0
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/cli/templates/provider/Dockerfile.tpl +1 -1
- package/src/cli/templates/provider/README.md.tpl +5 -5
- package/src/cli/templates/provider/index.ts.tpl +6 -3
- package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/src/cli/templates/provider/provider.json.tpl +6 -0
- package/src/config/loader.ts +690 -163
- package/src/contract-serialization.ts +5 -7
- package/src/contract-types.ts +12 -5
- package/src/contract.ts +24 -10
- package/src/declaration-validation.ts +330 -0
- package/src/define.ts +1048 -131
- package/src/dev.ts +4 -1
- package/src/engine.ts +279 -0
- package/src/error-observability.ts +64 -0
- package/src/error-resolution.ts +127 -0
- package/src/errors.ts +68 -0
- package/src/fixture-sanitization.ts +264 -0
- package/src/health-scenario.ts +875 -0
- package/src/index.ts +220 -13
- package/src/lint.ts +698 -90
- 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 +161 -3
- package/src/runtime/auth-flow.ts +12 -0
- package/src/runtime/browser.ts +661 -63
- package/src/runtime/cache.ts +189 -14
- package/src/runtime/choice-wordlist.ts +145 -0
- package/src/runtime/choice.ts +631 -120
- package/src/runtime/chrome149-header-order.ts +330 -0
- package/src/runtime/env.ts +13 -0
- package/src/runtime/executor.ts +43 -31
- package/src/runtime/http.ts +641 -61
- package/src/runtime/insights.ts +2 -2
- package/src/runtime/instrumentation.ts +520 -15
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +1605 -0
- package/src/runtime/ocr.ts +523 -0
- package/src/runtime/otlp.ts +467 -21
- package/src/runtime/proxy-errors.ts +12 -4
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-telemetry.ts +244 -75
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +7 -2
- package/src/runtime/request-options.ts +680 -1
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-public.ts +23 -0
- package/src/runtime/resolver-shared.ts +33 -0
- package/src/runtime/resolver-vendors/bindings.ts +56 -0
- package/src/runtime/resolver-vendors/browser.ts +533 -0
- package/src/runtime/resolver-vendors/capsolver.ts +707 -0
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +540 -0
- package/src/runtime/resolver-vendors/types.ts +212 -0
- package/src/runtime/resolver.ts +1157 -0
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +394 -77
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +1280 -336
- package/src/runtime/stt.ts +1 -19
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace-config.ts +78 -0
- package/src/runtime/trace.ts +57 -17
- package/src/serve.ts +6 -1
- package/src/server/error-observability.ts +1 -0
- package/src/server/index.ts +34 -2
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test.ts +191 -68
- package/src/server/serve-implementation.ts +3378 -0
- package/src/server/serve.ts +1 -1632
- package/src/server/trace-output.ts +129 -0
- package/src/server/types.ts +13 -1
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +35 -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 +546 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +202 -231
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -1
- package/src/testing/index.ts +10 -1
- package/src/testing/run.ts +658 -15
- package/src/trace-sanitization.ts +63 -0
- package/src/types.ts +788 -174
- 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
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
const ts = await loadTypeScript();
|
|
2
|
+
async function loadTypeScript() {
|
|
3
|
+
try {
|
|
4
|
+
return await import("typescript");
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
console.error("apifuse migrate-shape requires typescript; install it in the workspace running the CLI (bun add -d typescript)");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const DECLARATION_BUILDER_NAME = "buildProvider";
|
|
12
|
+
const PROVIDER_CONTEXT_TYPE_NAME = "ProviderContext";
|
|
13
|
+
const PROVIDER_CONTEXT_OF_TYPE_NAME = "ProviderContextOf";
|
|
14
|
+
const PROVIDER_SDK_PROVIDER_SUBPATH = "@apifuse/provider-sdk/provider";
|
|
15
|
+
/**
|
|
16
|
+
* Migrate one provider `index.ts` to the two-phase authoring shape.
|
|
17
|
+
*
|
|
18
|
+
* Returns the rewritten source on success. Callers MUST treat `skipped` as a
|
|
19
|
+
* hard stop for that provider — a skipped provider needs a human, and pairing
|
|
20
|
+
* a pin bump with a skipped migration produces an unloadable module.
|
|
21
|
+
*/
|
|
22
|
+
export function migrateProviderShape(sourceText, fileName = "index.ts") {
|
|
23
|
+
const source = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest,
|
|
24
|
+
/* setParentNodes */ true, ts.ScriptKind.TS);
|
|
25
|
+
const syntaxError = firstSyntaxError(source);
|
|
26
|
+
if (syntaxError !== undefined) {
|
|
27
|
+
return { status: "skipped", reason: syntaxError };
|
|
28
|
+
}
|
|
29
|
+
const calls = collectDefineProviderCalls(source);
|
|
30
|
+
if (calls.length === 0) {
|
|
31
|
+
return {
|
|
32
|
+
status: "skipped",
|
|
33
|
+
reason: "No defineProvider(...) call found; this file does not declare a provider.",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (calls.length > 1) {
|
|
37
|
+
return {
|
|
38
|
+
status: "skipped",
|
|
39
|
+
reason: `Found ${calls.length} defineProvider(...) calls; the transform rewrites exactly one declaration.`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const call = calls[0];
|
|
43
|
+
if (call === undefined) {
|
|
44
|
+
return { status: "skipped", reason: "Internal: defineProvider call vanished." };
|
|
45
|
+
}
|
|
46
|
+
const exportAssignment = findDefaultExport(source);
|
|
47
|
+
if (exportAssignment === undefined) {
|
|
48
|
+
return {
|
|
49
|
+
status: "skipped",
|
|
50
|
+
reason: "No `export default` found; the provider module must default-export its provider.",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const declaration = call.arguments[0];
|
|
54
|
+
if (declaration === undefined || !ts.isObjectLiteralExpression(declaration)) {
|
|
55
|
+
return {
|
|
56
|
+
status: "skipped",
|
|
57
|
+
reason: "defineProvider(...) is called with a non-literal argument, so the declaration's operations key cannot be located.",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const operationsProperty = findOperationsProperty(declaration);
|
|
61
|
+
// Already migrated: the declaration carries no operations key and the
|
|
62
|
+
// default export path calls a builder variable rather than defineProvider.
|
|
63
|
+
if (operationsProperty === undefined &&
|
|
64
|
+
isAlreadyTwoPhase(source, exportAssignment, call)) {
|
|
65
|
+
return { status: "unchanged", kind: "two-phase", code: sourceText };
|
|
66
|
+
}
|
|
67
|
+
if (operationsProperty === undefined) {
|
|
68
|
+
return {
|
|
69
|
+
status: "skipped",
|
|
70
|
+
reason: "The declaration has no `operations` key and the default export does not call a declaration builder, so the intended shape is ambiguous.",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const operationsText = operationsPropertyValueText(operationsProperty, source);
|
|
74
|
+
if (operationsText === undefined) {
|
|
75
|
+
return {
|
|
76
|
+
status: "skipped",
|
|
77
|
+
reason: "The `operations` property uses a form the transform cannot relocate (getter, setter, method, spread, or computed name).",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const shape = classifyShape(source, call, exportAssignment);
|
|
81
|
+
if (shape.status === "skipped") {
|
|
82
|
+
return shape;
|
|
83
|
+
}
|
|
84
|
+
const edits = [];
|
|
85
|
+
const builderName = pickBuilderName(source);
|
|
86
|
+
// 1. Remove `operations` from the declaration literal.
|
|
87
|
+
edits.push(...removeOperationsProperty(operationsProperty, declaration, source));
|
|
88
|
+
// 2. Bind the declaration to `const buildProvider = defineProvider({...})`.
|
|
89
|
+
edits.push(...introduceBuilder(source, call, shape, builderName));
|
|
90
|
+
// 3. Route the default export through `buildProvider({ operations })`.
|
|
91
|
+
edits.push(...rewriteDefaultExport(source, exportAssignment, shape, builderName, operationsText));
|
|
92
|
+
// 4. `export type ProviderContext = ProviderContextOf<typeof buildProvider>;`
|
|
93
|
+
// plus the type-only import, when neither is already present.
|
|
94
|
+
edits.push(...ensureProviderContextType(source, call, shape, builderName));
|
|
95
|
+
const code = applyEdits(sourceText, edits);
|
|
96
|
+
// Re-parse the output: a transform that emits unparseable source is worse
|
|
97
|
+
// than one that skips, because the pin bump would ship alongside it.
|
|
98
|
+
const verified = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
99
|
+
const outputError = firstSyntaxError(verified);
|
|
100
|
+
if (outputError !== undefined) {
|
|
101
|
+
return {
|
|
102
|
+
status: "skipped",
|
|
103
|
+
reason: `Transform produced source that does not parse (${outputError}); refusing to emit a partial migration.`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
status: "migrated",
|
|
108
|
+
kind: shape.kind,
|
|
109
|
+
code,
|
|
110
|
+
operationsExpression: operationsText,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function classifyShape(source, call, exportAssignment) {
|
|
114
|
+
const variableStatement = enclosingVariableStatement(call);
|
|
115
|
+
if (variableStatement === undefined) {
|
|
116
|
+
// `export default defineProvider({...})`
|
|
117
|
+
if (exportAssignment.expression === call) {
|
|
118
|
+
return { status: "ok", kind: "single-phase-default-export" };
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status: "skipped",
|
|
122
|
+
reason: "defineProvider(...) is neither bound to a variable nor the default-export expression, so the transform cannot place the builder.",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const declarations = variableStatement.declarationList.declarations;
|
|
126
|
+
if (declarations.length !== 1) {
|
|
127
|
+
return {
|
|
128
|
+
status: "skipped",
|
|
129
|
+
reason: "The defineProvider(...) result is declared alongside other bindings in one statement; split the declaration first.",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const declaration = declarations[0];
|
|
133
|
+
if (declaration === undefined || !ts.isIdentifier(declaration.name)) {
|
|
134
|
+
return {
|
|
135
|
+
status: "skipped",
|
|
136
|
+
reason: "The defineProvider(...) result is bound to a destructuring pattern.",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (declaration.initializer !== call) {
|
|
140
|
+
return {
|
|
141
|
+
status: "skipped",
|
|
142
|
+
reason: "defineProvider(...) is nested inside a larger initializer expression the transform cannot rewrite.",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const variableName = declaration.name.text;
|
|
146
|
+
const exported = exportAssignment.expression;
|
|
147
|
+
if (ts.isIdentifier(exported) && exported.text === variableName) {
|
|
148
|
+
return {
|
|
149
|
+
status: "ok",
|
|
150
|
+
kind: "single-phase-variable-export",
|
|
151
|
+
variableName,
|
|
152
|
+
variableStatement,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (ts.isObjectLiteralExpression(exported)) {
|
|
156
|
+
const spreads = exported.properties.filter(ts.isSpreadAssignment);
|
|
157
|
+
const spreadsProvider = spreads.some((property) => ts.isIdentifier(property.expression) &&
|
|
158
|
+
property.expression.text === variableName);
|
|
159
|
+
if (!spreadsProvider) {
|
|
160
|
+
return {
|
|
161
|
+
status: "skipped",
|
|
162
|
+
reason: `The default export is an object literal that does not spread \`${variableName}\`, so the provider value it exports is unclear.`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (spreads.length > 1) {
|
|
166
|
+
return {
|
|
167
|
+
status: "skipped",
|
|
168
|
+
reason: "The default export spreads more than one value; the transform cannot tell which carries the provider.",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const extras = exported.properties.filter((property) => !ts.isSpreadAssignment(property));
|
|
172
|
+
return {
|
|
173
|
+
status: "ok",
|
|
174
|
+
kind: "single-phase-variable-spread-export",
|
|
175
|
+
variableName,
|
|
176
|
+
variableStatement,
|
|
177
|
+
spreadExportExtras: extras
|
|
178
|
+
.map((property) => property.getText(source))
|
|
179
|
+
.join(",\n "),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
status: "skipped",
|
|
184
|
+
reason: `The default export is neither \`${variableName}\` nor an object literal spreading it.`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function collectDefineProviderCalls(source) {
|
|
188
|
+
const calls = [];
|
|
189
|
+
const visit = (node) => {
|
|
190
|
+
if (ts.isCallExpression(node) &&
|
|
191
|
+
ts.isIdentifier(node.expression) &&
|
|
192
|
+
node.expression.text === "defineProvider") {
|
|
193
|
+
calls.push(node);
|
|
194
|
+
}
|
|
195
|
+
ts.forEachChild(node, visit);
|
|
196
|
+
};
|
|
197
|
+
visit(source);
|
|
198
|
+
return calls;
|
|
199
|
+
}
|
|
200
|
+
function findDefaultExport(source) {
|
|
201
|
+
for (const statement of source.statements) {
|
|
202
|
+
if (ts.isExportAssignment(statement) && statement.isExportEquals !== true) {
|
|
203
|
+
return statement;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
function defaultExportCallsBuilder(exportAssignment, declarationCall) {
|
|
209
|
+
const expression = exportAssignment.expression;
|
|
210
|
+
if (!ts.isCallExpression(expression))
|
|
211
|
+
return false;
|
|
212
|
+
if (expression === declarationCall)
|
|
213
|
+
return false;
|
|
214
|
+
return ts.isIdentifier(expression.expression);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* True when the module is already in the two-phase shape: the default export
|
|
218
|
+
* resolves to a builder-call result. Covers the three migrated layouts —
|
|
219
|
+
* `export default buildProvider({...})`, an intermediate
|
|
220
|
+
* `const provider = buildProvider({...}); export default provider;`, and the
|
|
221
|
+
* spread export `export default { ...provider, deployment }` over such a
|
|
222
|
+
* binding. Without the latter two, re-running the transform on its own
|
|
223
|
+
* spread-shape output reports "ambiguous" instead of "unchanged", which
|
|
224
|
+
* breaks idempotency for repeated fan-out runs.
|
|
225
|
+
*/
|
|
226
|
+
function isAlreadyTwoPhase(source, exportAssignment, declarationCall) {
|
|
227
|
+
if (defaultExportCallsBuilder(exportAssignment, declarationCall))
|
|
228
|
+
return true;
|
|
229
|
+
const exported = exportAssignment.expression;
|
|
230
|
+
const candidateNames = [];
|
|
231
|
+
if (ts.isIdentifier(exported)) {
|
|
232
|
+
candidateNames.push(exported.text);
|
|
233
|
+
}
|
|
234
|
+
else if (ts.isObjectLiteralExpression(exported)) {
|
|
235
|
+
for (const property of exported.properties) {
|
|
236
|
+
if (ts.isSpreadAssignment(property) && ts.isIdentifier(property.expression)) {
|
|
237
|
+
candidateNames.push(property.expression.text);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (candidateNames.length === 0)
|
|
242
|
+
return false;
|
|
243
|
+
// Does any spread/exported identifier bind a call to an identifier other
|
|
244
|
+
// than defineProvider — i.e. a builder call?
|
|
245
|
+
let found = false;
|
|
246
|
+
const visit = (node) => {
|
|
247
|
+
if (found)
|
|
248
|
+
return;
|
|
249
|
+
if (ts.isVariableDeclaration(node) &&
|
|
250
|
+
ts.isIdentifier(node.name) &&
|
|
251
|
+
candidateNames.includes(node.name.text) &&
|
|
252
|
+
node.initializer !== undefined &&
|
|
253
|
+
ts.isCallExpression(node.initializer) &&
|
|
254
|
+
node.initializer !== declarationCall &&
|
|
255
|
+
ts.isIdentifier(node.initializer.expression) &&
|
|
256
|
+
node.initializer.expression.text !== "defineProvider") {
|
|
257
|
+
found = true;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
ts.forEachChild(node, visit);
|
|
261
|
+
};
|
|
262
|
+
visit(source);
|
|
263
|
+
return found;
|
|
264
|
+
}
|
|
265
|
+
function findOperationsProperty(declaration) {
|
|
266
|
+
for (const property of declaration.properties) {
|
|
267
|
+
if (ts.isSpreadAssignment(property))
|
|
268
|
+
continue;
|
|
269
|
+
const name = property.name;
|
|
270
|
+
if (name === undefined)
|
|
271
|
+
continue;
|
|
272
|
+
if ((ts.isIdentifier(name) || ts.isStringLiteral(name)) &&
|
|
273
|
+
name.text === "operations") {
|
|
274
|
+
return property;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Source text of the value to hand the builder. Shorthand becomes the bare
|
|
281
|
+
* identifier so `{ operations }` stays idiomatic; a property assignment keeps
|
|
282
|
+
* its initializer verbatim, including a multi-line inline map.
|
|
283
|
+
*/
|
|
284
|
+
function operationsPropertyValueText(property, source) {
|
|
285
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
286
|
+
return property.name.text;
|
|
287
|
+
}
|
|
288
|
+
if (ts.isPropertyAssignment(property)) {
|
|
289
|
+
return property.initializer.getText(source);
|
|
290
|
+
}
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
function removeOperationsProperty(property, declaration, source) {
|
|
294
|
+
const properties = declaration.properties;
|
|
295
|
+
const index = properties.indexOf(property);
|
|
296
|
+
const start = property.getFullStart();
|
|
297
|
+
let end = property.getEnd();
|
|
298
|
+
// Absorb the trailing comma so the remaining literal stays well-formed,
|
|
299
|
+
// whether the key sat mid-list or last.
|
|
300
|
+
const text = source.getFullText();
|
|
301
|
+
let cursor = end;
|
|
302
|
+
while (cursor < text.length && /\s/.test(text.charAt(cursor)))
|
|
303
|
+
cursor += 1;
|
|
304
|
+
if (text.charAt(cursor) === ",") {
|
|
305
|
+
end = cursor + 1;
|
|
306
|
+
}
|
|
307
|
+
else if (index > 0) {
|
|
308
|
+
// Last property with no trailing comma: drop the preceding one instead.
|
|
309
|
+
const previous = properties[index - 1];
|
|
310
|
+
if (previous !== undefined) {
|
|
311
|
+
let back = previous.getEnd();
|
|
312
|
+
while (back < text.length && /\s/.test(text.charAt(back)))
|
|
313
|
+
back += 1;
|
|
314
|
+
if (text.charAt(back) === ",") {
|
|
315
|
+
return [{ start: back, end, text: "" }];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return [{ start, end, text: "" }];
|
|
320
|
+
}
|
|
321
|
+
function introduceBuilder(source, call, shape, builderName) {
|
|
322
|
+
if (shape.kind === "single-phase-default-export") {
|
|
323
|
+
// `export default defineProvider({...})` becomes a standalone builder
|
|
324
|
+
// declaration; the default export is re-appended separately.
|
|
325
|
+
const exportStatement = call.parent;
|
|
326
|
+
if (!ts.isExportAssignment(exportStatement))
|
|
327
|
+
return [];
|
|
328
|
+
return [
|
|
329
|
+
{
|
|
330
|
+
start: exportStatement.getStart(source),
|
|
331
|
+
end: call.getStart(source),
|
|
332
|
+
text: `const ${builderName} = `,
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
}
|
|
336
|
+
const statement = shape.variableStatement;
|
|
337
|
+
if (statement === undefined || shape.variableName === undefined)
|
|
338
|
+
return [];
|
|
339
|
+
// Rename the existing binding to the builder name. The old name is
|
|
340
|
+
// re-introduced for the built provider only in the spread-export shape,
|
|
341
|
+
// which needs an intermediate value to spread.
|
|
342
|
+
const declaration = statement.declarationList.declarations[0];
|
|
343
|
+
if (declaration === undefined || !ts.isIdentifier(declaration.name))
|
|
344
|
+
return [];
|
|
345
|
+
return [
|
|
346
|
+
{
|
|
347
|
+
start: declaration.name.getStart(source),
|
|
348
|
+
end: declaration.name.getEnd(),
|
|
349
|
+
text: builderName,
|
|
350
|
+
},
|
|
351
|
+
];
|
|
352
|
+
}
|
|
353
|
+
function rewriteDefaultExport(source, exportAssignment, shape, builderName, operationsText) {
|
|
354
|
+
const operationsArgument = operationsText === "operations"
|
|
355
|
+
? "{ operations }"
|
|
356
|
+
: `{ operations: ${operationsText} }`;
|
|
357
|
+
const call = `${builderName}(${operationsArgument})`;
|
|
358
|
+
if (shape.kind === "single-phase-default-export") {
|
|
359
|
+
// The builder declaration replaced the `export default` prefix; the
|
|
360
|
+
// export itself is appended after what is now the builder statement.
|
|
361
|
+
return [
|
|
362
|
+
{
|
|
363
|
+
start: exportAssignment.getEnd(),
|
|
364
|
+
end: exportAssignment.getEnd(),
|
|
365
|
+
text: `\n\nexport default ${call};`,
|
|
366
|
+
},
|
|
367
|
+
];
|
|
368
|
+
}
|
|
369
|
+
if (shape.kind === "single-phase-variable-export") {
|
|
370
|
+
return [
|
|
371
|
+
{
|
|
372
|
+
start: exportAssignment.expression.getStart(source),
|
|
373
|
+
end: exportAssignment.expression.getEnd(),
|
|
374
|
+
text: call,
|
|
375
|
+
},
|
|
376
|
+
];
|
|
377
|
+
}
|
|
378
|
+
// Spread export: re-introduce the provider binding so the extra properties
|
|
379
|
+
// (currently `deployment`) still spread over a built provider.
|
|
380
|
+
const providerName = shape.variableName ?? "provider";
|
|
381
|
+
const extras = shape.spreadExportExtras ?? "";
|
|
382
|
+
const rebuilt = extras.length > 0
|
|
383
|
+
? `{\n ...${providerName},\n ${extras},\n}`
|
|
384
|
+
: `{ ...${providerName} }`;
|
|
385
|
+
return [
|
|
386
|
+
{
|
|
387
|
+
start: exportAssignment.getStart(source),
|
|
388
|
+
end: exportAssignment.getStart(source),
|
|
389
|
+
text: `const ${providerName} = ${call};\n\n`,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
start: exportAssignment.expression.getStart(source),
|
|
393
|
+
end: exportAssignment.expression.getEnd(),
|
|
394
|
+
text: rebuilt,
|
|
395
|
+
},
|
|
396
|
+
];
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Add `export type ProviderContext = ProviderContextOf<typeof buildProvider>`
|
|
400
|
+
* and the type-only import when absent. Operation handlers reference this
|
|
401
|
+
* type, so a migration that omits it leaves the provider without the context
|
|
402
|
+
* type the two-phase shape exists to provide.
|
|
403
|
+
*/
|
|
404
|
+
function ensureProviderContextType(source, call, shape, builderName) {
|
|
405
|
+
const text = source.getFullText();
|
|
406
|
+
const edits = [];
|
|
407
|
+
const hasContextTypeAlias = source.statements.some((statement) => ts.isTypeAliasDeclaration(statement) &&
|
|
408
|
+
statement.name.text === PROVIDER_CONTEXT_TYPE_NAME);
|
|
409
|
+
// A named import of `ProviderContext` (the deprecated SDK-root context
|
|
410
|
+
// type, imported by legacy sources) occupies the same name: adding the
|
|
411
|
+
// alias alongside it is TS2440. The import must yield to the derived
|
|
412
|
+
// alias — the whole point of the two-phase shape — so drop it and let the
|
|
413
|
+
// alias own the name.
|
|
414
|
+
const conflictingImportSpecifier = findNamedImportSpecifier(source, PROVIDER_CONTEXT_TYPE_NAME);
|
|
415
|
+
if (!hasContextTypeAlias) {
|
|
416
|
+
// After the builder statement — which is the variable statement when the
|
|
417
|
+
// declaration was bound, or the rewritten export statement otherwise.
|
|
418
|
+
const anchor = shape.variableStatement ??
|
|
419
|
+
(ts.isExportAssignment(call.parent) ? call.parent : undefined);
|
|
420
|
+
if (anchor !== undefined) {
|
|
421
|
+
const insertAt = anchor.getEnd();
|
|
422
|
+
edits.push({
|
|
423
|
+
start: insertAt,
|
|
424
|
+
end: insertAt,
|
|
425
|
+
text: `\n\nexport type ${PROVIDER_CONTEXT_TYPE_NAME} = ${PROVIDER_CONTEXT_OF_TYPE_NAME}<typeof ${builderName}>;`,
|
|
426
|
+
});
|
|
427
|
+
if (conflictingImportSpecifier !== undefined) {
|
|
428
|
+
edits.push(removeImportSpecifier(source, conflictingImportSpecifier));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (text.includes(PROVIDER_CONTEXT_OF_TYPE_NAME)) {
|
|
433
|
+
return edits;
|
|
434
|
+
}
|
|
435
|
+
const providerImport = findProviderSdkImport(source);
|
|
436
|
+
if (providerImport === undefined) {
|
|
437
|
+
// No named import from the provider subpath to extend; adding a new
|
|
438
|
+
// import line without knowing the module's style is riskier than
|
|
439
|
+
// leaving the type import to `bun run check` feedback.
|
|
440
|
+
return edits;
|
|
441
|
+
}
|
|
442
|
+
const named = providerImport.importClause?.namedBindings;
|
|
443
|
+
if (named === undefined || !ts.isNamedImports(named)) {
|
|
444
|
+
return edits;
|
|
445
|
+
}
|
|
446
|
+
const last = named.elements[named.elements.length - 1];
|
|
447
|
+
if (last === undefined) {
|
|
448
|
+
return edits;
|
|
449
|
+
}
|
|
450
|
+
edits.push({
|
|
451
|
+
start: last.getEnd(),
|
|
452
|
+
end: last.getEnd(),
|
|
453
|
+
text: `, type ${PROVIDER_CONTEXT_OF_TYPE_NAME}`,
|
|
454
|
+
});
|
|
455
|
+
return edits;
|
|
456
|
+
}
|
|
457
|
+
function findProviderSdkImport(source) {
|
|
458
|
+
for (const statement of source.statements) {
|
|
459
|
+
if (!ts.isImportDeclaration(statement))
|
|
460
|
+
continue;
|
|
461
|
+
const moduleSpecifier = statement.moduleSpecifier;
|
|
462
|
+
if (!ts.isStringLiteral(moduleSpecifier))
|
|
463
|
+
continue;
|
|
464
|
+
if (moduleSpecifier.text !== PROVIDER_SDK_PROVIDER_SUBPATH)
|
|
465
|
+
continue;
|
|
466
|
+
const named = statement.importClause?.namedBindings;
|
|
467
|
+
if (named !== undefined && ts.isNamedImports(named)) {
|
|
468
|
+
const importsDefineProvider = named.elements.some((element) => element.name.text === "defineProvider");
|
|
469
|
+
if (importsDefineProvider)
|
|
470
|
+
return statement;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return undefined;
|
|
474
|
+
}
|
|
475
|
+
/** Named import specifier binding `localName` in any import declaration. */
|
|
476
|
+
function findNamedImportSpecifier(source, localName) {
|
|
477
|
+
for (const statement of source.statements) {
|
|
478
|
+
if (!ts.isImportDeclaration(statement))
|
|
479
|
+
continue;
|
|
480
|
+
const named = statement.importClause?.namedBindings;
|
|
481
|
+
if (named === undefined || !ts.isNamedImports(named))
|
|
482
|
+
continue;
|
|
483
|
+
for (const element of named.elements) {
|
|
484
|
+
if (element.name.text === localName)
|
|
485
|
+
return element;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Edit removing one specifier from its named-import list, absorbing one
|
|
492
|
+
* neighboring comma so the list stays well-formed. Callers guarantee the
|
|
493
|
+
* list has at least one other specifier (legacy sources always import
|
|
494
|
+
* defineProvider alongside the context type).
|
|
495
|
+
*/
|
|
496
|
+
function removeImportSpecifier(source, specifier) {
|
|
497
|
+
const list = specifier.parent;
|
|
498
|
+
const index = list.elements.indexOf(specifier);
|
|
499
|
+
const text = source.getFullText();
|
|
500
|
+
let start = specifier.getFullStart();
|
|
501
|
+
let end = specifier.getEnd();
|
|
502
|
+
let cursor = end;
|
|
503
|
+
while (cursor < text.length && /\s/.test(text.charAt(cursor)))
|
|
504
|
+
cursor += 1;
|
|
505
|
+
if (text.charAt(cursor) === ",") {
|
|
506
|
+
end = cursor + 1;
|
|
507
|
+
}
|
|
508
|
+
else if (index > 0) {
|
|
509
|
+
const previous = list.elements[index - 1];
|
|
510
|
+
if (previous !== undefined) {
|
|
511
|
+
let back = previous.getEnd();
|
|
512
|
+
while (back < text.length && /\s/.test(text.charAt(back)))
|
|
513
|
+
back += 1;
|
|
514
|
+
if (text.charAt(back) === ",")
|
|
515
|
+
start = back;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return { start, end, text: "" };
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* `buildProvider` unless the module already binds that name, in which case a
|
|
522
|
+
* numbered suffix keeps the transform from shadowing an existing binding.
|
|
523
|
+
*/
|
|
524
|
+
function pickBuilderName(source) {
|
|
525
|
+
const taken = new Set();
|
|
526
|
+
const visit = (node) => {
|
|
527
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
528
|
+
taken.add(node.name.text);
|
|
529
|
+
}
|
|
530
|
+
if (ts.isFunctionDeclaration(node) && node.name !== undefined) {
|
|
531
|
+
taken.add(node.name.text);
|
|
532
|
+
}
|
|
533
|
+
if (ts.isImportSpecifier(node)) {
|
|
534
|
+
taken.add(node.name.text);
|
|
535
|
+
}
|
|
536
|
+
ts.forEachChild(node, visit);
|
|
537
|
+
};
|
|
538
|
+
visit(source);
|
|
539
|
+
if (!taken.has(DECLARATION_BUILDER_NAME))
|
|
540
|
+
return DECLARATION_BUILDER_NAME;
|
|
541
|
+
for (let suffix = 2; suffix < 100; suffix += 1) {
|
|
542
|
+
const candidate = `${DECLARATION_BUILDER_NAME}${suffix}`;
|
|
543
|
+
if (!taken.has(candidate))
|
|
544
|
+
return candidate;
|
|
545
|
+
}
|
|
546
|
+
return `${DECLARATION_BUILDER_NAME}Migrated`;
|
|
547
|
+
}
|
|
548
|
+
function enclosingVariableStatement(node) {
|
|
549
|
+
let current = node.parent;
|
|
550
|
+
while (current !== undefined) {
|
|
551
|
+
if (ts.isVariableStatement(current))
|
|
552
|
+
return current;
|
|
553
|
+
if (ts.isSourceFile(current))
|
|
554
|
+
return undefined;
|
|
555
|
+
current = current.parent;
|
|
556
|
+
}
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
559
|
+
function firstSyntaxError(source) {
|
|
560
|
+
const diagnostics = source.parseDiagnostics;
|
|
561
|
+
if (diagnostics === undefined || diagnostics.length === 0)
|
|
562
|
+
return undefined;
|
|
563
|
+
const first = diagnostics[0];
|
|
564
|
+
if (first === undefined)
|
|
565
|
+
return undefined;
|
|
566
|
+
const message = ts.flattenDiagnosticMessageText(first.messageText, " ");
|
|
567
|
+
const { line } = source.getLineAndCharacterOfPosition(first.start);
|
|
568
|
+
return `${message} (line ${line + 1})`;
|
|
569
|
+
}
|
|
570
|
+
/** Apply edits back-to-front so earlier offsets stay valid. */
|
|
571
|
+
function applyEdits(text, edits) {
|
|
572
|
+
const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
|
|
573
|
+
let output = text;
|
|
574
|
+
for (const edit of ordered) {
|
|
575
|
+
output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);
|
|
576
|
+
}
|
|
577
|
+
return output;
|
|
578
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK-managed agent prompt assets.
|
|
3
|
+
*
|
|
4
|
+
* `apifuse create` scaffolds these files, `apifuse sync-assets` regenerates
|
|
5
|
+
* them in an existing provider root, and `apifuse check` / `submit-check`
|
|
6
|
+
* enforce that they byte-match the installed SDK version (fail closed).
|
|
7
|
+
*
|
|
8
|
+
* Layout contract:
|
|
9
|
+
* - AGENTS.md — real file (agent guide)
|
|
10
|
+
* - CLAUDE.md — symlink -> AGENTS.md
|
|
11
|
+
* - .agents/skills/<skill>/SKILL.md — real files
|
|
12
|
+
* - .agents/skills/upstream-notes/README.md — real file
|
|
13
|
+
* - .claude / .codex — symlinks -> .agents
|
|
14
|
+
* - .apifuse/prompt-assets.json — manifest (schema v2), written last
|
|
15
|
+
*/
|
|
16
|
+
export declare const PROMPT_ASSET_MANIFEST_PATH = ".apifuse/prompt-assets.json";
|
|
17
|
+
export declare const PROMPT_ASSET_MANIFEST_SCHEMA_VERSION = 2;
|
|
18
|
+
export declare const PROMPT_ASSET_SYNC_REMEDIATION = "Run `bun run sync-assets` (or `bunx apifuse sync-assets .`) to regenerate the SDK-managed agent prompt assets.";
|
|
19
|
+
export declare const PROMPT_ASSET_SYMLINKS: Readonly<Record<string, string>>;
|
|
20
|
+
/** Relative asset paths whose content is rendered from `<path>.tpl`. */
|
|
21
|
+
export declare const PROMPT_ASSET_FILE_PATHS: readonly string[];
|
|
22
|
+
export type PromptAssetKind = "file" | "symlink";
|
|
23
|
+
export type PromptAssetEntry = {
|
|
24
|
+
/** Provider-root-relative path (POSIX separators). */
|
|
25
|
+
path: string;
|
|
26
|
+
/** File content, or the symlink target for kind "symlink". */
|
|
27
|
+
content: string;
|
|
28
|
+
kind: PromptAssetKind;
|
|
29
|
+
};
|
|
30
|
+
export type PromptAssetTemplateRenderer = (fileName: string, values: Record<string, string>) => string | Promise<string>;
|
|
31
|
+
export type PromptAssetVerification = {
|
|
32
|
+
ok: boolean;
|
|
33
|
+
/** Expected paths (or the manifest) absent on disk. */
|
|
34
|
+
missing: string[];
|
|
35
|
+
/** Manifest recorded for a different SDK version than the installed one. */
|
|
36
|
+
stale: string[];
|
|
37
|
+
/** Files/symlinks/manifest whose bytes or target differ from the regenerated set. */
|
|
38
|
+
modified: string[];
|
|
39
|
+
/** Legacy layout remnants (top-level skills/). */
|
|
40
|
+
legacy: string[];
|
|
41
|
+
/** Entries inside `.agents/` that are not part of the managed asset set. */
|
|
42
|
+
unexpected: string[];
|
|
43
|
+
};
|
|
44
|
+
export type PromptAssetSyncResult = {
|
|
45
|
+
changed: boolean;
|
|
46
|
+
removed: string[];
|
|
47
|
+
wroteFiles: string[];
|
|
48
|
+
createdSymlinks: string[];
|
|
49
|
+
manifestPath: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Build the full managed asset entry list (files first, symlinks after,
|
|
53
|
+
* manifest excluded). The renderer is injected so `apifuse create` can reuse
|
|
54
|
+
* its own template renderer; sync/verify use the SDK-internal renderer.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildPromptAssetPlanEntries(renderTemplate: PromptAssetTemplateRenderer): Promise<PromptAssetEntry[]>;
|
|
57
|
+
export declare function buildPromptAssetPlanEntriesSync(): PromptAssetEntry[];
|
|
58
|
+
/**
|
|
59
|
+
* Deterministic manifest serialization: schema v2, 2-space JSON, trailing
|
|
60
|
+
* newline, sorted paths (including symlink paths, excluding the manifest
|
|
61
|
+
* itself). `sdkVersion` + `paths` keep their v1 semantics so older parsers
|
|
62
|
+
* keep working; `schemaVersion` and `symlinks` are additive.
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildPromptAssetManifest(entries: readonly PromptAssetEntry[], sdkVersion: string): string;
|
|
65
|
+
export declare function installedSdkVersion(): string;
|
|
66
|
+
/**
|
|
67
|
+
* Verify the on-disk prompt assets against the set regenerated from the
|
|
68
|
+
* installed SDK. Uses lstat/readlink for symlinks (never follows), byte
|
|
69
|
+
* comparison for files, and exact-version comparison for the manifest.
|
|
70
|
+
*/
|
|
71
|
+
export declare function verifyPromptAssets(providerRoot: string): PromptAssetVerification;
|
|
72
|
+
export declare function formatPromptAssetIssues(verification: PromptAssetVerification): string[];
|
|
73
|
+
/**
|
|
74
|
+
* Regenerate the full managed asset set for the installed SDK version in an
|
|
75
|
+
* existing provider root. Deletes legacy managed paths first (top-level
|
|
76
|
+
* skills/**, plus manifest-listed paths that left the set — restricted to
|
|
77
|
+
* managed namespaces with no symlinked ancestors), writes files, replaces
|
|
78
|
+
* symlinks, and writes the manifest last. Idempotent.
|
|
79
|
+
*/
|
|
80
|
+
export declare function syncPromptAssets(providerRoot: string): PromptAssetSyncResult;
|