@apifuse/provider-sdk 2.2.0-beta.41 → 2.2.0-beta.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/bin/apifuse-check.ts +1 -1
- package/bin/apifuse-dev.ts +1 -1
- package/bin/apifuse-migrate-shape.ts +141 -23
- package/bin/apifuse-submit-check.ts +99 -86
- package/dist/cli/commands.js +1 -1
- package/dist/cli/create.d.ts +1 -1
- package/dist/cli/create.js +2 -2
- 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.js +64 -1
- package/dist/define.d.ts +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/server/serve-implementation.d.ts +11 -11
- package/dist/server/serve-implementation.js +50 -7
- package/dist/types.d.ts +2 -2
- package/package.json +3 -3
- package/src/cli/commands.ts +1 -1
- package/src/cli/create.ts +2 -2
- package/src/cli/migrate-operation-shape.ts +184 -0
- package/src/cli/migrate-provider-shape.ts +107 -36
- package/src/define.ts +10 -0
- package/src/index.ts +2 -0
- package/src/provider.ts +2 -0
- package/src/server/serve-implementation.ts +90 -36
- package/src/types.ts +2 -2
|
@@ -8,7 +8,7 @@ import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
|
|
10
10
|
import * as acorn from "acorn";
|
|
11
|
-
import
|
|
11
|
+
import type TS from "typescript";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
14
|
import packageJson from "../package.json";
|
|
@@ -29,6 +29,19 @@ import { type CheckResult, PROMPT_ASSETS_CHECK_MESSAGE, runChecks } from "./apif
|
|
|
29
29
|
import { hasSubstantiveDelimitedTextStructure } from "./submit-check-delimited-text.js";
|
|
30
30
|
import { hasSubstantiveXmlStructure } from "./submit-check-xml.js";
|
|
31
31
|
|
|
32
|
+
const ts: typeof import("typescript") = await loadTypeScript();
|
|
33
|
+
|
|
34
|
+
async function loadTypeScript(): Promise<typeof import("typescript")> {
|
|
35
|
+
try {
|
|
36
|
+
return await import("typescript");
|
|
37
|
+
} catch {
|
|
38
|
+
console.error(
|
|
39
|
+
"apifuse submit-check requires typescript; install it in the workspace running the CLI (bun add -d typescript)",
|
|
40
|
+
);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
const TIERS = ["bronze", "silver", "gold", "diamond"] as const;
|
|
33
46
|
const TIER_VALUES: ReadonlySet<string> = new Set(TIERS);
|
|
34
47
|
type BountyTier = (typeof TIERS)[number];
|
|
@@ -515,7 +528,7 @@ const DYNAMIC_CODE_GLOBAL_OBJECTS: ReadonlySet<string> = new Set([
|
|
|
515
528
|
"global",
|
|
516
529
|
]);
|
|
517
530
|
|
|
518
|
-
function unwrapLocalExpression(expression:
|
|
531
|
+
function unwrapLocalExpression(expression: TS.Expression): TS.Expression {
|
|
519
532
|
let current = expression;
|
|
520
533
|
while (
|
|
521
534
|
ts.isParenthesizedExpression(current) ||
|
|
@@ -532,18 +545,18 @@ function unwrapLocalExpression(expression: ts.Expression): ts.Expression {
|
|
|
532
545
|
}
|
|
533
546
|
|
|
534
547
|
type LocalBinding = {
|
|
535
|
-
declaration:
|
|
536
|
-
initializer?:
|
|
548
|
+
declaration: TS.Identifier;
|
|
549
|
+
initializer?: TS.Expression;
|
|
537
550
|
destructuredProperty?: string;
|
|
538
551
|
mutable: boolean;
|
|
539
552
|
reassigned: boolean;
|
|
540
553
|
};
|
|
541
554
|
|
|
542
555
|
type LocalBindings = {
|
|
543
|
-
scopes: Map<
|
|
556
|
+
scopes: Map<TS.Node, Map<string, LocalBinding[]>>;
|
|
544
557
|
};
|
|
545
558
|
|
|
546
|
-
function isLexicalScope(node:
|
|
559
|
+
function isLexicalScope(node: TS.Node): boolean {
|
|
547
560
|
return (
|
|
548
561
|
ts.isSourceFile(node) ||
|
|
549
562
|
ts.isFunctionLike(node) ||
|
|
@@ -557,8 +570,8 @@ function isLexicalScope(node: ts.Node): boolean {
|
|
|
557
570
|
);
|
|
558
571
|
}
|
|
559
572
|
|
|
560
|
-
function enclosingScope(node:
|
|
561
|
-
let current:
|
|
573
|
+
function enclosingScope(node: TS.Node, functionScoped: boolean): TS.Node {
|
|
574
|
+
let current: TS.Node | undefined = node.parent;
|
|
562
575
|
while (current !== undefined) {
|
|
563
576
|
if (
|
|
564
577
|
ts.isSourceFile(current) ||
|
|
@@ -571,11 +584,11 @@ function enclosingScope(node: ts.Node, functionScoped: boolean): ts.Node {
|
|
|
571
584
|
return node.getSourceFile();
|
|
572
585
|
}
|
|
573
586
|
|
|
574
|
-
function collectLocalBindings(sourceFile:
|
|
587
|
+
function collectLocalBindings(sourceFile: TS.SourceFile): LocalBindings {
|
|
575
588
|
const bindings: LocalBindings = { scopes: new Map() };
|
|
576
589
|
const addBinding = (
|
|
577
|
-
scope:
|
|
578
|
-
name:
|
|
590
|
+
scope: TS.Node,
|
|
591
|
+
name: TS.Identifier,
|
|
579
592
|
binding: Omit<LocalBinding, "declaration" | "reassigned">,
|
|
580
593
|
): void => {
|
|
581
594
|
let scopeBindings = bindings.scopes.get(scope);
|
|
@@ -592,8 +605,8 @@ function collectLocalBindings(sourceFile: ts.SourceFile): LocalBindings {
|
|
|
592
605
|
}
|
|
593
606
|
};
|
|
594
607
|
const addBindingName = (
|
|
595
|
-
bindingName:
|
|
596
|
-
scope:
|
|
608
|
+
bindingName: TS.BindingName,
|
|
609
|
+
scope: TS.Node,
|
|
597
610
|
binding: Omit<LocalBinding, "declaration" | "reassigned">,
|
|
598
611
|
): void => {
|
|
599
612
|
if (ts.isIdentifier(bindingName)) {
|
|
@@ -619,7 +632,7 @@ function collectLocalBindings(sourceFile: ts.SourceFile): LocalBindings {
|
|
|
619
632
|
addBindingName(element.name, scope, { mutable: binding.mutable });
|
|
620
633
|
}
|
|
621
634
|
};
|
|
622
|
-
const visit = (node:
|
|
635
|
+
const visit = (node: TS.Node): void => {
|
|
623
636
|
if (ts.isVariableDeclaration(node)) {
|
|
624
637
|
const declarationList = ts.isVariableDeclarationList(node.parent) ? node.parent : undefined;
|
|
625
638
|
const isConstBinding =
|
|
@@ -649,12 +662,12 @@ function collectLocalBindings(sourceFile: ts.SourceFile): LocalBindings {
|
|
|
649
662
|
};
|
|
650
663
|
visit(sourceFile);
|
|
651
664
|
|
|
652
|
-
const markReassigned = (identifier:
|
|
665
|
+
const markReassigned = (identifier: TS.Identifier): void => {
|
|
653
666
|
for (const binding of lookupLocalBindings(identifier, bindings) ?? []) {
|
|
654
667
|
binding.reassigned = true;
|
|
655
668
|
}
|
|
656
669
|
};
|
|
657
|
-
const markAssignmentTarget = (node:
|
|
670
|
+
const markAssignmentTarget = (node: TS.Node): void => {
|
|
658
671
|
const target = ts.isExpression(node) ? unwrapLocalExpression(node) : node;
|
|
659
672
|
if (ts.isIdentifier(target)) {
|
|
660
673
|
markReassigned(target);
|
|
@@ -678,7 +691,7 @@ function collectLocalBindings(sourceFile: ts.SourceFile): LocalBindings {
|
|
|
678
691
|
}
|
|
679
692
|
}
|
|
680
693
|
};
|
|
681
|
-
const visitAssignments = (node:
|
|
694
|
+
const visitAssignments = (node: TS.Node): void => {
|
|
682
695
|
if (
|
|
683
696
|
ts.isBinaryExpression(node) &&
|
|
684
697
|
node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
@@ -704,10 +717,10 @@ function collectLocalBindings(sourceFile: ts.SourceFile): LocalBindings {
|
|
|
704
717
|
}
|
|
705
718
|
|
|
706
719
|
function lookupLocalBindings(
|
|
707
|
-
reference:
|
|
720
|
+
reference: TS.Identifier,
|
|
708
721
|
bindings: LocalBindings,
|
|
709
722
|
): readonly LocalBinding[] | undefined {
|
|
710
|
-
let current:
|
|
723
|
+
let current: TS.Node | undefined = reference;
|
|
711
724
|
while (current !== undefined) {
|
|
712
725
|
const localBindings = bindings.scopes.get(current)?.get(reference.text);
|
|
713
726
|
if (localBindings !== undefined && localBindings.length > 0) {
|
|
@@ -718,7 +731,7 @@ function lookupLocalBindings(
|
|
|
718
731
|
return undefined;
|
|
719
732
|
}
|
|
720
733
|
|
|
721
|
-
function propertyNameText(name:
|
|
734
|
+
function propertyNameText(name: TS.PropertyName | TS.BindingName): string | undefined {
|
|
722
735
|
if (
|
|
723
736
|
ts.isIdentifier(name) ||
|
|
724
737
|
ts.isStringLiteral(name) ||
|
|
@@ -736,7 +749,7 @@ function propertyNameText(name: ts.PropertyName | ts.BindingName): string | unde
|
|
|
736
749
|
return undefined;
|
|
737
750
|
}
|
|
738
751
|
|
|
739
|
-
function isUnshadowedGlobalObject(reference:
|
|
752
|
+
function isUnshadowedGlobalObject(reference: TS.Expression, bindings: LocalBindings): boolean {
|
|
740
753
|
const receiver = unwrapLocalExpression(reference);
|
|
741
754
|
return (
|
|
742
755
|
ts.isIdentifier(receiver) &&
|
|
@@ -746,13 +759,13 @@ function isUnshadowedGlobalObject(reference: ts.Expression, bindings: LocalBindi
|
|
|
746
759
|
}
|
|
747
760
|
|
|
748
761
|
function isGlobalMemberSink(
|
|
749
|
-
expression:
|
|
762
|
+
expression: TS.Expression,
|
|
750
763
|
sinkNames: ReadonlySet<string>,
|
|
751
764
|
bindings: LocalBindings,
|
|
752
765
|
): boolean {
|
|
753
766
|
const callee = unwrapLocalExpression(expression);
|
|
754
767
|
let sinkName: string | undefined;
|
|
755
|
-
let receiver:
|
|
768
|
+
let receiver: TS.Expression | undefined;
|
|
756
769
|
if (ts.isPropertyAccessExpression(callee)) {
|
|
757
770
|
sinkName = callee.name.text;
|
|
758
771
|
receiver = callee.expression;
|
|
@@ -773,7 +786,7 @@ function isGlobalMemberSink(
|
|
|
773
786
|
}
|
|
774
787
|
|
|
775
788
|
function isGlobalSinkReference(
|
|
776
|
-
expression:
|
|
789
|
+
expression: TS.Expression,
|
|
777
790
|
sinkNames: ReadonlySet<string>,
|
|
778
791
|
bindings: LocalBindings,
|
|
779
792
|
resolving: ReadonlySet<LocalBinding>,
|
|
@@ -789,7 +802,7 @@ function isGlobalSinkReference(
|
|
|
789
802
|
}
|
|
790
803
|
|
|
791
804
|
function isGlobalSinkIdentifier(
|
|
792
|
-
reference:
|
|
805
|
+
reference: TS.Identifier,
|
|
793
806
|
sinkNames: ReadonlySet<string>,
|
|
794
807
|
bindings: LocalBindings,
|
|
795
808
|
resolving: ReadonlySet<LocalBinding>,
|
|
@@ -821,7 +834,7 @@ function isGlobalSinkIdentifier(
|
|
|
821
834
|
}
|
|
822
835
|
|
|
823
836
|
function isGlobalSinkCallee(
|
|
824
|
-
expression:
|
|
837
|
+
expression: TS.Expression,
|
|
825
838
|
sinkNames: ReadonlySet<string>,
|
|
826
839
|
bindings: LocalBindings,
|
|
827
840
|
): boolean {
|
|
@@ -836,7 +849,7 @@ function isGlobalSinkCallee(
|
|
|
836
849
|
}
|
|
837
850
|
|
|
838
851
|
function resolveConstStringIdentifier(
|
|
839
|
-
reference:
|
|
852
|
+
reference: TS.Identifier,
|
|
840
853
|
bindings: LocalBindings,
|
|
841
854
|
resolving: ReadonlySet<LocalBinding> = new Set(),
|
|
842
855
|
): string | undefined {
|
|
@@ -889,7 +902,7 @@ function findGlobalSinkCalls(
|
|
|
889
902
|
ts.ScriptKind.TS,
|
|
890
903
|
);
|
|
891
904
|
const bindings = collectLocalBindings(sourceFile);
|
|
892
|
-
const visit = (node:
|
|
905
|
+
const visit = (node: TS.Node): void => {
|
|
893
906
|
if (findings.length >= MAX_SOURCE_FINDING_EVIDENCE) {
|
|
894
907
|
return;
|
|
895
908
|
}
|
|
@@ -1688,13 +1701,13 @@ function spreadIdentifierResolvesToFactory(
|
|
|
1688
1701
|
return false;
|
|
1689
1702
|
}
|
|
1690
1703
|
|
|
1691
|
-
function isDefineProviderCall(node:
|
|
1704
|
+
function isDefineProviderCall(node: TS.CallExpression): boolean {
|
|
1692
1705
|
return ts.isIdentifier(node.expression) && node.expression.text === "defineProvider";
|
|
1693
1706
|
}
|
|
1694
1707
|
|
|
1695
1708
|
function resolveDefineProviderImplementationCall(
|
|
1696
|
-
candidate:
|
|
1697
|
-
):
|
|
1709
|
+
candidate: TS.CallExpression,
|
|
1710
|
+
): TS.CallExpression | undefined {
|
|
1698
1711
|
let root = candidate;
|
|
1699
1712
|
while (ts.isCallExpression(root.expression)) {
|
|
1700
1713
|
root = root.expression;
|
|
@@ -1702,9 +1715,9 @@ function resolveDefineProviderImplementationCall(
|
|
|
1702
1715
|
return isDefineProviderCall(root) ? candidate : undefined;
|
|
1703
1716
|
}
|
|
1704
1717
|
|
|
1705
|
-
function findProviderImplementationCall(sourceFile:
|
|
1718
|
+
function findProviderImplementationCall(sourceFile: TS.SourceFile): TS.CallExpression | undefined {
|
|
1706
1719
|
const defaultExports = sourceFile.statements.filter(
|
|
1707
|
-
(statement): statement is
|
|
1720
|
+
(statement): statement is TS.ExportAssignment =>
|
|
1708
1721
|
ts.isExportAssignment(statement) && !statement.isExportEquals,
|
|
1709
1722
|
);
|
|
1710
1723
|
|
|
@@ -1770,8 +1783,8 @@ function findProviderImplementationCall(sourceFile: ts.SourceFile): ts.CallExpre
|
|
|
1770
1783
|
|
|
1771
1784
|
// Structural fixtures without a recognizable default export retain the
|
|
1772
1785
|
// previous fallback to the first defineProvider call in source order.
|
|
1773
|
-
let firstCall:
|
|
1774
|
-
const visit = (node:
|
|
1786
|
+
let firstCall: TS.CallExpression | undefined;
|
|
1787
|
+
const visit = (node: TS.Node): void => {
|
|
1775
1788
|
if (firstCall !== undefined) {
|
|
1776
1789
|
return;
|
|
1777
1790
|
}
|
|
@@ -1788,7 +1801,7 @@ function findProviderImplementationCall(sourceFile: ts.SourceFile): ts.CallExpre
|
|
|
1788
1801
|
return firstCall;
|
|
1789
1802
|
}
|
|
1790
1803
|
|
|
1791
|
-
function unwrapImplementationExpression(expression:
|
|
1804
|
+
function unwrapImplementationExpression(expression: TS.Expression): TS.Expression {
|
|
1792
1805
|
let current = expression;
|
|
1793
1806
|
while (
|
|
1794
1807
|
ts.isSatisfiesExpression(current) ||
|
|
@@ -1802,8 +1815,8 @@ function unwrapImplementationExpression(expression: ts.Expression): ts.Expressio
|
|
|
1802
1815
|
}
|
|
1803
1816
|
|
|
1804
1817
|
function isOperationsProperty(
|
|
1805
|
-
property:
|
|
1806
|
-
): property is
|
|
1818
|
+
property: TS.ObjectLiteralElementLike,
|
|
1819
|
+
): property is TS.PropertyAssignment | TS.ShorthandPropertyAssignment {
|
|
1807
1820
|
if (ts.isShorthandPropertyAssignment(property)) {
|
|
1808
1821
|
return property.name.text === "operations";
|
|
1809
1822
|
}
|
|
@@ -1817,8 +1830,8 @@ function isOperationsProperty(
|
|
|
1817
1830
|
}
|
|
1818
1831
|
|
|
1819
1832
|
function isOperationsAccessorOrMethod(
|
|
1820
|
-
property:
|
|
1821
|
-
): property is
|
|
1833
|
+
property: TS.ObjectLiteralElementLike,
|
|
1834
|
+
): property is TS.GetAccessorDeclaration | TS.SetAccessorDeclaration | TS.MethodDeclaration {
|
|
1822
1835
|
if (
|
|
1823
1836
|
!ts.isGetAccessorDeclaration(property) &&
|
|
1824
1837
|
!ts.isSetAccessorDeclaration(property) &&
|
|
@@ -1832,7 +1845,7 @@ function isOperationsAccessorOrMethod(
|
|
|
1832
1845
|
);
|
|
1833
1846
|
}
|
|
1834
1847
|
|
|
1835
|
-
function isSourceEnumerableStaticObjectLiteral(expression:
|
|
1848
|
+
function isSourceEnumerableStaticObjectLiteral(expression: TS.Expression): boolean {
|
|
1836
1849
|
const unwrapped = unwrapImplementationExpression(expression);
|
|
1837
1850
|
if (!ts.isObjectLiteralExpression(unwrapped)) {
|
|
1838
1851
|
return false;
|
|
@@ -1849,8 +1862,8 @@ function isSourceEnumerableStaticObjectLiteral(expression: ts.Expression): boole
|
|
|
1849
1862
|
}
|
|
1850
1863
|
|
|
1851
1864
|
function findComputedImplementationProperty(
|
|
1852
|
-
objectLiteral:
|
|
1853
|
-
):
|
|
1865
|
+
objectLiteral: TS.ObjectLiteralExpression,
|
|
1866
|
+
): TS.ObjectLiteralElementLike | undefined {
|
|
1854
1867
|
for (const property of objectLiteral.properties) {
|
|
1855
1868
|
if (property.name !== undefined && ts.isComputedPropertyName(property.name)) {
|
|
1856
1869
|
return property;
|
|
@@ -1869,20 +1882,20 @@ function findComputedImplementationProperty(
|
|
|
1869
1882
|
}
|
|
1870
1883
|
|
|
1871
1884
|
function findEffectiveOperationsProperty(
|
|
1872
|
-
objectLiteral:
|
|
1885
|
+
objectLiteral: TS.ObjectLiteralExpression,
|
|
1873
1886
|
):
|
|
1874
|
-
|
|
|
1875
|
-
|
|
|
1876
|
-
|
|
|
1877
|
-
|
|
|
1878
|
-
|
|
|
1887
|
+
| TS.PropertyAssignment
|
|
1888
|
+
| TS.ShorthandPropertyAssignment
|
|
1889
|
+
| TS.GetAccessorDeclaration
|
|
1890
|
+
| TS.SetAccessorDeclaration
|
|
1891
|
+
| TS.MethodDeclaration
|
|
1879
1892
|
| undefined {
|
|
1880
1893
|
let effective:
|
|
1881
|
-
|
|
|
1882
|
-
|
|
|
1883
|
-
|
|
|
1884
|
-
|
|
|
1885
|
-
|
|
|
1894
|
+
| TS.PropertyAssignment
|
|
1895
|
+
| TS.ShorthandPropertyAssignment
|
|
1896
|
+
| TS.GetAccessorDeclaration
|
|
1897
|
+
| TS.SetAccessorDeclaration
|
|
1898
|
+
| TS.MethodDeclaration
|
|
1886
1899
|
| undefined;
|
|
1887
1900
|
for (const property of objectLiteral.properties) {
|
|
1888
1901
|
if (isOperationsProperty(property) || isOperationsAccessorOrMethod(property)) {
|
|
@@ -1994,7 +2007,7 @@ function scoreFlatOperationComposition(providerRoot: string): SubmitCheck {
|
|
|
1994
2007
|
// exposes all of its property names to this source-only gate. Identifiers,
|
|
1995
2008
|
// calls, and other expressions are opaque and therefore fail closed.
|
|
1996
2009
|
const opaqueSpread = implementationArg.properties.find(
|
|
1997
|
-
(property): property is
|
|
2010
|
+
(property): property is TS.SpreadAssignment =>
|
|
1998
2011
|
ts.isSpreadAssignment(property) &&
|
|
1999
2012
|
!isSourceEnumerableStaticObjectLiteral(property.expression),
|
|
2000
2013
|
);
|
|
@@ -2210,9 +2223,9 @@ function scoreCredentialUsage(providerRoot: string, provider: ProviderDefinition
|
|
|
2210
2223
|
);
|
|
2211
2224
|
}
|
|
2212
2225
|
|
|
2213
|
-
function isDirectCredentialReference(expression:
|
|
2226
|
+
function isDirectCredentialReference(expression: TS.Expression): boolean {
|
|
2214
2227
|
const reference = unwrapLocalExpression(expression);
|
|
2215
|
-
let receiver:
|
|
2228
|
+
let receiver: TS.Expression | undefined;
|
|
2216
2229
|
let memberName: string | undefined;
|
|
2217
2230
|
if (ts.isPropertyAccessExpression(reference)) {
|
|
2218
2231
|
receiver = reference.expression;
|
|
@@ -2237,7 +2250,7 @@ function isDirectCredentialReference(expression: ts.Expression): boolean {
|
|
|
2237
2250
|
}
|
|
2238
2251
|
|
|
2239
2252
|
function isCredentialReference(
|
|
2240
|
-
expression:
|
|
2253
|
+
expression: TS.Expression,
|
|
2241
2254
|
bindings: LocalBindings,
|
|
2242
2255
|
resolving: ReadonlySet<LocalBinding> = new Set(),
|
|
2243
2256
|
): boolean {
|
|
@@ -2284,7 +2297,7 @@ function findCredentialReferences(providerRoot: string): SourceFinding[] {
|
|
|
2284
2297
|
ts.ScriptKind.TS,
|
|
2285
2298
|
);
|
|
2286
2299
|
const bindings = collectLocalBindings(sourceFile);
|
|
2287
|
-
const visit = (node:
|
|
2300
|
+
const visit = (node: TS.Node): void => {
|
|
2288
2301
|
if (findings.length >= MAX_SOURCE_FINDING_EVIDENCE) {
|
|
2289
2302
|
return;
|
|
2290
2303
|
}
|
|
@@ -3332,12 +3345,12 @@ function findZObjectLiterals(source: string): ZObjectLiteral[] {
|
|
|
3332
3345
|
}
|
|
3333
3346
|
|
|
3334
3347
|
function zObjectAppearsPublicOutput(
|
|
3335
|
-
sourceFile:
|
|
3348
|
+
sourceFile: TS.SourceFile,
|
|
3336
3349
|
zObject: ZObjectLiteral,
|
|
3337
3350
|
relPath: string,
|
|
3338
3351
|
): boolean {
|
|
3339
|
-
let zObjectCall:
|
|
3340
|
-
const findCall = (node:
|
|
3352
|
+
let zObjectCall: TS.CallExpression | undefined;
|
|
3353
|
+
const findCall = (node: TS.Node): void => {
|
|
3341
3354
|
if (
|
|
3342
3355
|
zObjectCall === undefined &&
|
|
3343
3356
|
ts.isCallExpression(node) &&
|
|
@@ -3353,7 +3366,7 @@ function zObjectAppearsPublicOutput(
|
|
|
3353
3366
|
return true;
|
|
3354
3367
|
}
|
|
3355
3368
|
|
|
3356
|
-
let expression:
|
|
3369
|
+
let expression: TS.Expression = zObjectCall;
|
|
3357
3370
|
while (
|
|
3358
3371
|
ts.isParenthesizedExpression(expression.parent) ||
|
|
3359
3372
|
ts.isAsExpression(expression.parent) ||
|
|
@@ -3411,8 +3424,8 @@ const PARSE_LIKE_SCHEMA_METHODS: ReadonlySet<string> = new Set([
|
|
|
3411
3424
|
]);
|
|
3412
3425
|
|
|
3413
3426
|
function isExportedVariableBinding(
|
|
3414
|
-
sourceFile:
|
|
3415
|
-
declaration:
|
|
3427
|
+
sourceFile: TS.SourceFile,
|
|
3428
|
+
declaration: TS.VariableDeclaration,
|
|
3416
3429
|
): boolean {
|
|
3417
3430
|
const declarationStatement = declaration.parent.parent;
|
|
3418
3431
|
if (
|
|
@@ -3428,7 +3441,7 @@ function isExportedVariableBinding(
|
|
|
3428
3441
|
}
|
|
3429
3442
|
const name = declaration.name.text;
|
|
3430
3443
|
let exported = false;
|
|
3431
|
-
const visit = (node:
|
|
3444
|
+
const visit = (node: TS.Node): void => {
|
|
3432
3445
|
if (exported) {
|
|
3433
3446
|
return;
|
|
3434
3447
|
}
|
|
@@ -3457,13 +3470,13 @@ function isExportedVariableBinding(
|
|
|
3457
3470
|
return exported;
|
|
3458
3471
|
}
|
|
3459
3472
|
|
|
3460
|
-
function isPublicOutputPropertyName(name:
|
|
3473
|
+
function isPublicOutputPropertyName(name: TS.PropertyName): boolean {
|
|
3461
3474
|
const text = propertyNameText(name);
|
|
3462
3475
|
return text === "output" || text === "response";
|
|
3463
3476
|
}
|
|
3464
3477
|
|
|
3465
|
-
function findEnclosingVariableDeclaration(node:
|
|
3466
|
-
let current:
|
|
3478
|
+
function findEnclosingVariableDeclaration(node: TS.Node): TS.VariableDeclaration | undefined {
|
|
3479
|
+
let current: TS.Node | undefined = node;
|
|
3467
3480
|
while (current !== undefined && !ts.isSourceFile(current)) {
|
|
3468
3481
|
if (ts.isVariableDeclaration(current)) {
|
|
3469
3482
|
return current;
|
|
@@ -3474,9 +3487,9 @@ function findEnclosingVariableDeclaration(node: ts.Node): ts.VariableDeclaration
|
|
|
3474
3487
|
}
|
|
3475
3488
|
|
|
3476
3489
|
function localSchemaBindingReachability(
|
|
3477
|
-
sourceFile:
|
|
3490
|
+
sourceFile: TS.SourceFile,
|
|
3478
3491
|
name: string,
|
|
3479
|
-
declaration:
|
|
3492
|
+
declaration: TS.VariableDeclaration,
|
|
3480
3493
|
resolving: ReadonlySet<string>,
|
|
3481
3494
|
): SchemaReachability {
|
|
3482
3495
|
if (resolving.has(name)) {
|
|
@@ -3485,7 +3498,7 @@ function localSchemaBindingReachability(
|
|
|
3485
3498
|
const nextResolving = new Set(resolving);
|
|
3486
3499
|
nextResolving.add(name);
|
|
3487
3500
|
let result: SchemaReachability = "internal";
|
|
3488
|
-
const visit = (node:
|
|
3501
|
+
const visit = (node: TS.Node): void => {
|
|
3489
3502
|
if (result === "public") {
|
|
3490
3503
|
return;
|
|
3491
3504
|
}
|
|
@@ -3550,7 +3563,7 @@ function localSchemaBindingReachability(
|
|
|
3550
3563
|
return result;
|
|
3551
3564
|
}
|
|
3552
3565
|
|
|
3553
|
-
function isParseLikeSchemaReceiver(identifier:
|
|
3566
|
+
function isParseLikeSchemaReceiver(identifier: TS.Identifier): boolean {
|
|
3554
3567
|
const access = identifier.parent;
|
|
3555
3568
|
let methodName: string | undefined;
|
|
3556
3569
|
if (ts.isPropertyAccessExpression(access) && access.expression === identifier) {
|
|
@@ -3574,7 +3587,7 @@ function isParseLikeSchemaReceiver(identifier: ts.Identifier): boolean {
|
|
|
3574
3587
|
|
|
3575
3588
|
function vendorKeyFindingsForObject(
|
|
3576
3589
|
source: string,
|
|
3577
|
-
sourceFile:
|
|
3590
|
+
sourceFile: TS.SourceFile,
|
|
3578
3591
|
zObject: ZObjectLiteral,
|
|
3579
3592
|
bindings: LocalBindings,
|
|
3580
3593
|
): Array<{ key: string; line: number }> {
|
|
@@ -3640,7 +3653,7 @@ function isAllowedPublicOutputKeyName(name: string): boolean {
|
|
|
3640
3653
|
|
|
3641
3654
|
function collectTopLevelObjectKeys(
|
|
3642
3655
|
source: string,
|
|
3643
|
-
sourceFile:
|
|
3656
|
+
sourceFile: TS.SourceFile,
|
|
3644
3657
|
objectStart: number,
|
|
3645
3658
|
objectEnd: number,
|
|
3646
3659
|
bindings: LocalBindings,
|
|
@@ -3702,11 +3715,11 @@ function collectTopLevelObjectKeys(
|
|
|
3702
3715
|
}
|
|
3703
3716
|
|
|
3704
3717
|
function computedPropertyExpressionAtOffset(
|
|
3705
|
-
sourceFile:
|
|
3718
|
+
sourceFile: TS.SourceFile,
|
|
3706
3719
|
offset: number,
|
|
3707
|
-
):
|
|
3708
|
-
let expression:
|
|
3709
|
-
const visit = (node:
|
|
3720
|
+
): TS.Expression | undefined {
|
|
3721
|
+
let expression: TS.Expression | undefined;
|
|
3722
|
+
const visit = (node: TS.Node): void => {
|
|
3710
3723
|
if (expression !== undefined || offset < node.getFullStart() || offset >= node.getEnd()) {
|
|
3711
3724
|
return;
|
|
3712
3725
|
}
|
|
@@ -3770,7 +3783,7 @@ function findVendorTimestampLeakFindings(providerRoot: string): SourceFinding[]
|
|
|
3770
3783
|
}
|
|
3771
3784
|
}
|
|
3772
3785
|
|
|
3773
|
-
const visitAliases = (node:
|
|
3786
|
+
const visitAliases = (node: TS.Node): void => {
|
|
3774
3787
|
if (findings.length >= MAX_SOURCE_FINDING_EVIDENCE) {
|
|
3775
3788
|
return;
|
|
3776
3789
|
}
|
|
@@ -3808,7 +3821,7 @@ function findVendorTimestampLeakFindings(providerRoot: string): SourceFinding[]
|
|
|
3808
3821
|
return findings;
|
|
3809
3822
|
}
|
|
3810
3823
|
|
|
3811
|
-
function isIdentifierValueUse(identifier:
|
|
3824
|
+
function isIdentifierValueUse(identifier: TS.Identifier): boolean {
|
|
3812
3825
|
const parent = identifier.parent;
|
|
3813
3826
|
return !(
|
|
3814
3827
|
(ts.isPropertyAssignment(parent) && parent.name === identifier) ||
|
|
@@ -3830,8 +3843,8 @@ function isIdentifierValueUse(identifier: ts.Identifier): boolean {
|
|
|
3830
3843
|
);
|
|
3831
3844
|
}
|
|
3832
3845
|
|
|
3833
|
-
function propertyNameForValueExpression(expression:
|
|
3834
|
-
let current:
|
|
3846
|
+
function propertyNameForValueExpression(expression: TS.Expression): string | undefined {
|
|
3847
|
+
let current: TS.Expression = expression;
|
|
3835
3848
|
while (
|
|
3836
3849
|
ts.isParenthesizedExpression(current.parent) ||
|
|
3837
3850
|
ts.isAsExpression(current.parent) ||
|
|
@@ -4113,14 +4126,14 @@ function computeMaskedSource(
|
|
|
4113
4126
|
if (chars[index] !== "\n" && chars[index] !== "\r") chars[index] = " ";
|
|
4114
4127
|
}
|
|
4115
4128
|
};
|
|
4116
|
-
const commentRanges = new Map<string,
|
|
4117
|
-
const addCommentRanges = (ranges: readonly
|
|
4129
|
+
const commentRanges = new Map<string, TS.CommentRange>();
|
|
4130
|
+
const addCommentRanges = (ranges: readonly TS.CommentRange[] | undefined): void => {
|
|
4118
4131
|
for (const range of ranges ?? []) {
|
|
4119
4132
|
commentRanges.set(`${range.pos}:${range.end}`, range);
|
|
4120
4133
|
}
|
|
4121
4134
|
};
|
|
4122
4135
|
|
|
4123
|
-
const visit = (node:
|
|
4136
|
+
const visit = (node: TS.Node): void => {
|
|
4124
4137
|
addCommentRanges(ts.getLeadingCommentRanges(source, node.getFullStart()));
|
|
4125
4138
|
addCommentRanges(ts.getTrailingCommentRanges(source, node.getEnd()));
|
|
4126
4139
|
|
package/dist/cli/commands.js
CHANGED
|
@@ -32,7 +32,7 @@ export const COMMAND_MANIFEST = {
|
|
|
32
32
|
},
|
|
33
33
|
"migrate-shape": {
|
|
34
34
|
name: "migrate-shape",
|
|
35
|
-
summary: "Migrate a provider
|
|
35
|
+
summary: "Migrate a provider to the phase-separated SDK: two-phase defineProvider in index.ts and curried defineOperation across sources.",
|
|
36
36
|
usage: "apifuse migrate-shape [path] [--check] [--json]",
|
|
37
37
|
examples: ["apifuse migrate-shape .", "apifuse migrate-shape . --check"],
|
|
38
38
|
modulePath: "./apifuse-migrate-shape",
|
package/dist/cli/create.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const PROVIDER_NAME_REGEX: RegExp;
|
|
2
2
|
export declare const CATEGORY_OPTIONS: readonly ["developer-tools", "finance", "commerce", "productivity", "marketing", "data", "communication", "other"];
|
|
3
3
|
export declare const AUTH_MODE_OPTIONS: readonly ["none", "platform-managed", "credentials", "oauth2"];
|
|
4
|
-
export declare const RUNTIME_OPTIONS: readonly ["standard", "browser"];
|
|
4
|
+
export declare const RUNTIME_OPTIONS: readonly ["standard", "shared", "browser"];
|
|
5
5
|
export declare const PRESET_OPTIONS: readonly ["standalone", "monorepo"];
|
|
6
6
|
export type CreateCategory = (typeof CATEGORY_OPTIONS)[number];
|
|
7
7
|
export type CreateAuthMode = (typeof AUTH_MODE_OPTIONS)[number];
|
package/dist/cli/create.js
CHANGED
|
@@ -19,7 +19,7 @@ export const CATEGORY_OPTIONS = [
|
|
|
19
19
|
"other",
|
|
20
20
|
];
|
|
21
21
|
export const AUTH_MODE_OPTIONS = ["none", "platform-managed", "credentials", "oauth2"];
|
|
22
|
-
export const RUNTIME_OPTIONS = ["standard", "browser"];
|
|
22
|
+
export const RUNTIME_OPTIONS = ["standard", "shared", "browser"];
|
|
23
23
|
export const PRESET_OPTIONS = ["standalone", "monorepo"];
|
|
24
24
|
const CREATE_CONFIG_SCHEMA = z.object({
|
|
25
25
|
authMode: z.enum(AUTH_MODE_OPTIONS).optional(),
|
|
@@ -46,7 +46,7 @@ Options:
|
|
|
46
46
|
--display-name <name>
|
|
47
47
|
--category <category>
|
|
48
48
|
--auth-mode <mode>
|
|
49
|
-
--runtime <standard|browser>
|
|
49
|
+
--runtime <standard|shared|browser>
|
|
50
50
|
--yes
|
|
51
51
|
--dry-run
|
|
52
52
|
--json
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operation authoring-shape migration.
|
|
3
|
+
*
|
|
4
|
+
* `defineOperation` / `defineStreamOperation` changed alongside the provider
|
|
5
|
+
* builder in 2.2.0-beta.37: the identity helper became a curried,
|
|
6
|
+
* context-typed factory.
|
|
7
|
+
*
|
|
8
|
+
* before: defineOperation({ ...config })
|
|
9
|
+
* after: defineOperation<ProviderContext>()({ ...config })
|
|
10
|
+
*
|
|
11
|
+
* The old call still type-checks against an older pin, but under the new SDK
|
|
12
|
+
* it returns the INNER FACTORY FUNCTION with the config swallowed as an
|
|
13
|
+
* ignored argument, so every operation in the map becomes a function and the
|
|
14
|
+
* provider fails `finalizeProvider` with a misleading "declares neither
|
|
15
|
+
* healthCheck nor healthCheckUnsupported" error. Measured fleet blast radius
|
|
16
|
+
* (2026-08-29): 53 of 84 repositories, 450 legacy call sites.
|
|
17
|
+
*
|
|
18
|
+
* Same contract as the provider-shape transform: rewrite only what is fully
|
|
19
|
+
* understood, report a reasoned skip otherwise, never emit a partial file.
|
|
20
|
+
*/
|
|
21
|
+
export type OperationShapeMigration = {
|
|
22
|
+
readonly status: "migrated";
|
|
23
|
+
readonly code: string;
|
|
24
|
+
/** Number of call sites rewritten in this file. */
|
|
25
|
+
readonly rewrites: number;
|
|
26
|
+
/** True when a `ProviderContext` type import was added. */
|
|
27
|
+
readonly importAdded: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
readonly status: "unchanged";
|
|
30
|
+
readonly code: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly status: "skipped";
|
|
33
|
+
readonly reason: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Migrate legacy `defineOperation(config)` calls in one source file to the
|
|
37
|
+
* curried `defineOperation<ProviderContext>()(config)` form.
|
|
38
|
+
*
|
|
39
|
+
* @param contextImportSpecifier module specifier the `ProviderContext` type
|
|
40
|
+
* import should come from when one has to be added — `"../index"` for
|
|
41
|
+
* operation modules, `"./index"` is never needed because index.ts declares
|
|
42
|
+
* the alias itself. Callers pass the correct relative path per file.
|
|
43
|
+
*/
|
|
44
|
+
export declare function migrateOperationShape(sourceText: string, fileName: string, contextImportSpecifier: string): OperationShapeMigration;
|