@emeryld/rrroutes-contract 2.7.6 → 2.7.8
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/README.md +1 -0
- package/dist/export/defaultViewerTemplate.d.ts +1 -1
- package/dist/export/exportFinalizedLeaves.d.ts +7 -1
- package/dist/export/extractLeafSourceByAst.d.ts +8 -0
- package/dist/index.cjs +248 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +248 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tools/finalized-leaves-viewer.html +55 -29
package/README.md
CHANGED
|
@@ -390,6 +390,7 @@ const payload = await exportFinalizedLeaves(registry, {
|
|
|
390
390
|
`payload` contains:
|
|
391
391
|
|
|
392
392
|
- `_meta`: export/documentation metadata
|
|
393
|
+
- when source extraction is enabled, `_meta.sourceExtraction` also includes diagnostics (`reason`, `stats`)
|
|
393
394
|
- `leaves`: contract-native serialized leaves
|
|
394
395
|
- `schemaFlatByLeaf`: flattened schema map per leaf
|
|
395
396
|
- `sourceByLeaf` (when `includeSource` is true): AST-derived definition + schema source metadata keyed by `METHOD path`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const DEFAULT_VIEWER_TEMPLATE = "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Finalized Leaves Viewer</title>\n <style>\n :root {\n --bg: #
|
|
1
|
+
export declare const DEFAULT_VIEWER_TEMPLATE = "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Finalized Leaves Viewer</title>\n <style>\n :root {\n --bg: #212121;\n --surface: #2a2a2a;\n --border: #4a4a4a;\n --text: #fffafa;\n --muted: #c8c2c2;\n --accent: #a764d3;\n }\n body {\n margin: 0;\n font-family: 'Iosevka Web', 'SFMono-Regular', Menlo, Consolas, monospace;\n color: var(--text);\n background: var(--bg);\n }\n .wrap { max-width: 1100px; margin: 0 auto; padding: 20px; }\n .card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }\n .meta { color: var(--muted); font-size: 12px; }\n #results { margin-top: 12px; display: grid; gap: 8px; }\n details { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }\n summary { cursor: pointer; font-weight: 700; color: var(--accent); }\n pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #303030; color: var(--text); }\n </style>\n </head>\n <body>\n <div class=\"wrap\">\n <h1>Finalized Leaves Viewer (Baked)</h1>\n <div class=\"card\">\n <div id=\"status\" class=\"meta\">Waiting for baked payload...</div>\n </div>\n <div id=\"results\"></div>\n </div>\n\n <!--__FINALIZED_LEAVES_BAKED_PAYLOAD__-->\n\n <script>\n const statusEl = document.getElementById('status')\n const resultsEl = document.getElementById('results')\n const payload = window.__FINALIZED_LEAVES_PAYLOAD\n\n if (!payload || !Array.isArray(payload.leaves)) {\n statusEl.textContent = 'No baked payload found in this HTML file.'\n } else {\n statusEl.textContent = 'Loaded baked payload with ' + payload.leaves.length + ' routes.'\n\n const toHref = (source) => {\n if (!source || !source.file) return null\n const normalizedPath = String(source.file).replace(/\\\\/g, '/')\n const prefix = normalizedPath.startsWith('/') ? 'file://' : 'file:///'\n return prefix + encodeURI(normalizedPath)\n }\n\n const sourceDisplay = (source) => {\n return ''\n }\n\n payload.leaves.forEach((leaf) => {\n const details = document.createElement('details')\n const summary = document.createElement('summary')\n summary.textContent = String(leaf.method || '').toUpperCase() + ' ' + (leaf.path || '')\n\n const pre = document.createElement('pre')\n pre.textContent = JSON.stringify(leaf, null, 2)\n\n const source = payload.sourceByLeaf && payload.sourceByLeaf[leaf.key]\n let sourceWrap = null\n if (source) {\n sourceWrap = document.createElement('div')\n sourceWrap.className = 'meta'\n\n const definitionHref = toHref(source.definition)\n if (definitionHref) {\n const label = document.createElement('div')\n const link = document.createElement('a')\n link.href = definitionHref\n link.target = '_blank'\n link.rel = 'noopener noreferrer'\n link.textContent = 'definition'\n label.appendChild(link)\n sourceWrap.appendChild(label)\n }\n\n if (source.schemas && typeof source.schemas === 'object') {\n Object.entries(source.schemas).forEach(([name, schema]) => {\n if (!schema) return\n const href = toHref(schema)\n const row = document.createElement('div')\n if (href) {\n const link = document.createElement('a')\n link.href = href\n link.target = '_blank'\n link.rel = 'noopener noreferrer'\n link.textContent =\n name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')\n row.appendChild(link)\n } else {\n row.textContent = name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')\n }\n sourceWrap.appendChild(row)\n })\n }\n\n }\n\n details.appendChild(summary)\n if (sourceWrap) details.appendChild(sourceWrap)\n details.appendChild(pre)\n resultsEl.appendChild(details)\n })\n }\n </script>\n </body>\n</html>\n";
|
|
@@ -2,7 +2,7 @@ import type { AnyLeafLowProfile } from '../core/routesV3.core';
|
|
|
2
2
|
import type { FinalizedRegistry } from '../core/routesV3.finalize';
|
|
3
3
|
import { type FlatSchemaMap } from './flattenSchema';
|
|
4
4
|
import { type SerializeLeafContractOptions, type SerializedLeafContract } from './serializeLeafContract';
|
|
5
|
-
import { type LeafSourceByKey } from './extractLeafSourceByAst';
|
|
5
|
+
import { type LeafSourceByKey, type SourceExtractionReason } from './extractLeafSourceByAst';
|
|
6
6
|
export type ExportFinalizedLeavesInput = readonly AnyLeafLowProfile[] | FinalizedRegistry<readonly AnyLeafLowProfile[]>;
|
|
7
7
|
export type ExportFinalizedLeavesMeta = {
|
|
8
8
|
generatedAt: string;
|
|
@@ -14,6 +14,12 @@ export type ExportFinalizedLeavesMeta = {
|
|
|
14
14
|
exportName?: string;
|
|
15
15
|
tsconfigPath?: string;
|
|
16
16
|
resolvedLeafCount: number;
|
|
17
|
+
reason?: SourceExtractionReason;
|
|
18
|
+
stats?: {
|
|
19
|
+
visitedSymbols: number;
|
|
20
|
+
visitedFiles: number;
|
|
21
|
+
unresolvedReferences: number;
|
|
22
|
+
};
|
|
17
23
|
};
|
|
18
24
|
fieldCatalog: {
|
|
19
25
|
leaf: string[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
declare const SCHEMA_KEYS: readonly ["bodySchema", "querySchema", "paramsSchema", "outputSchema", "outputMetaSchema", "queryExtensionSchema"];
|
|
2
|
+
export type SourceExtractionReason = 'module_not_in_program' | 'export_not_found' | 'unsupported_expression_shape' | 'resolved_zero_leaves';
|
|
2
3
|
type SchemaKey = (typeof SCHEMA_KEYS)[number];
|
|
3
4
|
export type LeafSourceLocation = {
|
|
4
5
|
file: string;
|
|
@@ -22,9 +23,16 @@ export type ExtractLeafSourceByAstOptions = {
|
|
|
22
23
|
tsconfigPath?: string;
|
|
23
24
|
cwd?: string;
|
|
24
25
|
};
|
|
26
|
+
export type ExtractLeafSourceByAstStats = {
|
|
27
|
+
visitedSymbols: number;
|
|
28
|
+
visitedFiles: number;
|
|
29
|
+
unresolvedReferences: number;
|
|
30
|
+
};
|
|
25
31
|
export type ExtractLeafSourceByAstResult = {
|
|
26
32
|
sourceByLeaf: LeafSourceByKey;
|
|
27
33
|
tsconfigPath?: string;
|
|
34
|
+
reason?: SourceExtractionReason;
|
|
35
|
+
stats: ExtractLeafSourceByAstStats;
|
|
28
36
|
};
|
|
29
37
|
export declare function extractLeafSourceByAst({ modulePath, exportName, tsconfigPath, cwd, }: ExtractLeafSourceByAstOptions): ExtractLeafSourceByAstResult;
|
|
30
38
|
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -754,18 +754,18 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
754
754
|
<title>Finalized Leaves Viewer</title>
|
|
755
755
|
<style>
|
|
756
756
|
:root {
|
|
757
|
-
--bg: #
|
|
758
|
-
--surface: #
|
|
759
|
-
--border: #
|
|
760
|
-
--text: #
|
|
761
|
-
--muted: #
|
|
762
|
-
--accent: #
|
|
757
|
+
--bg: #212121;
|
|
758
|
+
--surface: #2a2a2a;
|
|
759
|
+
--border: #4a4a4a;
|
|
760
|
+
--text: #fffafa;
|
|
761
|
+
--muted: #c8c2c2;
|
|
762
|
+
--accent: #a764d3;
|
|
763
763
|
}
|
|
764
764
|
body {
|
|
765
765
|
margin: 0;
|
|
766
766
|
font-family: 'Iosevka Web', 'SFMono-Regular', Menlo, Consolas, monospace;
|
|
767
767
|
color: var(--text);
|
|
768
|
-
background:
|
|
768
|
+
background: var(--bg);
|
|
769
769
|
}
|
|
770
770
|
.wrap { max-width: 1100px; margin: 0 auto; padding: 20px; }
|
|
771
771
|
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px; }
|
|
@@ -773,7 +773,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
773
773
|
#results { margin-top: 12px; display: grid; gap: 8px; }
|
|
774
774
|
details { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }
|
|
775
775
|
summary { cursor: pointer; font-weight: 700; color: var(--accent); }
|
|
776
|
-
pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #
|
|
776
|
+
pre { margin: 10px 0 0; overflow: auto; border: 1px solid var(--border); border-radius: 8px; padding: 10px; background: #303030; color: var(--text); }
|
|
777
777
|
</style>
|
|
778
778
|
</head>
|
|
779
779
|
<body>
|
|
@@ -804,6 +804,10 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
804
804
|
return prefix + encodeURI(normalizedPath)
|
|
805
805
|
}
|
|
806
806
|
|
|
807
|
+
const sourceDisplay = (source) => {
|
|
808
|
+
return ''
|
|
809
|
+
}
|
|
810
|
+
|
|
807
811
|
payload.leaves.forEach((leaf) => {
|
|
808
812
|
const details = document.createElement('details')
|
|
809
813
|
const summary = document.createElement('summary')
|
|
@@ -825,13 +829,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
825
829
|
link.href = definitionHref
|
|
826
830
|
link.target = '_blank'
|
|
827
831
|
link.rel = 'noopener noreferrer'
|
|
828
|
-
link.textContent =
|
|
829
|
-
'definition: ' +
|
|
830
|
-
source.definition.file +
|
|
831
|
-
':' +
|
|
832
|
-
source.definition.line +
|
|
833
|
-
':' +
|
|
834
|
-
source.definition.column
|
|
832
|
+
link.textContent = 'definition'
|
|
835
833
|
label.appendChild(link)
|
|
836
834
|
sourceWrap.appendChild(label)
|
|
837
835
|
}
|
|
@@ -847,16 +845,7 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
847
845
|
link.target = '_blank'
|
|
848
846
|
link.rel = 'noopener noreferrer'
|
|
849
847
|
link.textContent =
|
|
850
|
-
name +
|
|
851
|
-
': ' +
|
|
852
|
-
(schema.sourceName || schema.tag || '<anonymous>') +
|
|
853
|
-
' (' +
|
|
854
|
-
schema.file +
|
|
855
|
-
':' +
|
|
856
|
-
schema.line +
|
|
857
|
-
':' +
|
|
858
|
-
schema.column +
|
|
859
|
-
')'
|
|
848
|
+
name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
|
|
860
849
|
row.appendChild(link)
|
|
861
850
|
} else {
|
|
862
851
|
row.textContent = name + ': ' + (schema.sourceName || schema.tag || '<anonymous>')
|
|
@@ -890,6 +879,7 @@ var SCHEMA_KEYS = [
|
|
|
890
879
|
"queryExtensionSchema"
|
|
891
880
|
];
|
|
892
881
|
var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
882
|
+
var MAX_RECURSION_DEPTH = 120;
|
|
893
883
|
function toLocation(node) {
|
|
894
884
|
const sourceFile = node.getSourceFile();
|
|
895
885
|
const { line, character } = sourceFile.getLineAndCharacterOfPosition(
|
|
@@ -901,6 +891,10 @@ function toLocation(node) {
|
|
|
901
891
|
column: character + 1
|
|
902
892
|
};
|
|
903
893
|
}
|
|
894
|
+
function markFile(ctx, sourceFile) {
|
|
895
|
+
if (!sourceFile) return;
|
|
896
|
+
ctx.visitedFilePaths.add(import_node_path.default.resolve(sourceFile.fileName));
|
|
897
|
+
}
|
|
904
898
|
function trimPreview(text, max = 80) {
|
|
905
899
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
906
900
|
return normalized.length > max ? `${normalized.slice(0, max)}...` : normalized;
|
|
@@ -958,7 +952,7 @@ function findSourceFile(program, filePath) {
|
|
|
958
952
|
const normalizedWanted = import_node_path.default.normalize(wanted);
|
|
959
953
|
return program.getSourceFiles().find((file) => import_node_path.default.normalize(import_node_path.default.resolve(file.fileName)) === normalizedWanted);
|
|
960
954
|
}
|
|
961
|
-
function
|
|
955
|
+
function declarationToExpression(declaration) {
|
|
962
956
|
if (!declaration) return void 0;
|
|
963
957
|
if (import_typescript.default.isVariableDeclaration(declaration)) {
|
|
964
958
|
return declaration.initializer;
|
|
@@ -975,14 +969,95 @@ function expressionFromDeclaration(declaration) {
|
|
|
975
969
|
if (import_typescript.default.isBindingElement(declaration)) {
|
|
976
970
|
return declaration.initializer;
|
|
977
971
|
}
|
|
972
|
+
if (import_typescript.default.isEnumMember(declaration)) {
|
|
973
|
+
return declaration.initializer;
|
|
974
|
+
}
|
|
978
975
|
return void 0;
|
|
979
976
|
}
|
|
980
|
-
function
|
|
981
|
-
const
|
|
982
|
-
if (!
|
|
977
|
+
function symbolKey(symbol) {
|
|
978
|
+
const decl = symbol.declarations?.[0];
|
|
979
|
+
if (!decl) return `${symbol.getName()}#${symbol.flags}`;
|
|
980
|
+
const file = import_node_path.default.resolve(decl.getSourceFile().fileName);
|
|
981
|
+
return `${file}:${decl.getStart()}:${symbol.getName()}`;
|
|
982
|
+
}
|
|
983
|
+
function resolveSymbolFromNode(node, ctx) {
|
|
984
|
+
const symbol = ctx.checker.getSymbolAtLocation(node);
|
|
985
|
+
if (!symbol) {
|
|
986
|
+
ctx.unresolvedReferences += 1;
|
|
987
|
+
return void 0;
|
|
988
|
+
}
|
|
983
989
|
const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
|
|
984
|
-
const
|
|
985
|
-
|
|
990
|
+
const key = symbolKey(target);
|
|
991
|
+
ctx.visitedSymbolKeys.add(key);
|
|
992
|
+
return target;
|
|
993
|
+
}
|
|
994
|
+
function getExpressionFromSymbol(symbol, ctx, depth) {
|
|
995
|
+
const key = symbolKey(symbol);
|
|
996
|
+
if (ctx.activeSymbols.has(key)) return void 0;
|
|
997
|
+
if (depth > MAX_RECURSION_DEPTH) return void 0;
|
|
998
|
+
ctx.activeSymbols.add(key);
|
|
999
|
+
try {
|
|
1000
|
+
const declaration = symbol.declarations?.[0];
|
|
1001
|
+
markFile(ctx, declaration?.getSourceFile());
|
|
1002
|
+
const direct = declarationToExpression(declaration);
|
|
1003
|
+
if (direct) return direct;
|
|
1004
|
+
if (declaration && import_typescript.default.isImportSpecifier(declaration)) {
|
|
1005
|
+
const target = getAliasedSymbolIfNeeded(ctx.checker, symbol);
|
|
1006
|
+
if (target !== symbol) {
|
|
1007
|
+
return getExpressionFromSymbol(target, ctx, depth + 1);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return void 0;
|
|
1011
|
+
} finally {
|
|
1012
|
+
ctx.activeSymbols.delete(key);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function resolveIdentifierExpression(identifier, ctx, depth) {
|
|
1016
|
+
const symbol = resolveSymbolFromNode(identifier, ctx);
|
|
1017
|
+
if (!symbol) return void 0;
|
|
1018
|
+
return getExpressionFromSymbol(symbol, ctx, depth);
|
|
1019
|
+
}
|
|
1020
|
+
function resolvePropertyExpression(expression, ctx, depth) {
|
|
1021
|
+
if (depth > MAX_RECURSION_DEPTH) return void 0;
|
|
1022
|
+
if (import_typescript.default.isPropertyAccessExpression(expression)) {
|
|
1023
|
+
const symbol = resolveSymbolFromNode(expression.name, ctx);
|
|
1024
|
+
if (symbol) {
|
|
1025
|
+
const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
|
|
1026
|
+
if (fromSymbol) return fromSymbol;
|
|
1027
|
+
}
|
|
1028
|
+
} else if (expression.argumentExpression) {
|
|
1029
|
+
const symbol = resolveSymbolFromNode(expression.argumentExpression, ctx);
|
|
1030
|
+
if (symbol) {
|
|
1031
|
+
const fromSymbol = getExpressionFromSymbol(symbol, ctx, depth + 1);
|
|
1032
|
+
if (fromSymbol) return fromSymbol;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
const ownerExpr = evaluateExpressionReference(expression.expression, ctx, depth + 1);
|
|
1036
|
+
const owner = ownerExpr ? maybeObjectLiteral(ownerExpr, ctx, depth + 1) : void 0;
|
|
1037
|
+
if (!owner) return void 0;
|
|
1038
|
+
const propName = import_typescript.default.isPropertyAccessExpression(expression) ? expression.name.text : expression.argumentExpression && import_typescript.default.isStringLiteralLike(expression.argumentExpression) ? expression.argumentExpression.text : void 0;
|
|
1039
|
+
if (!propName) return void 0;
|
|
1040
|
+
for (const property of owner.properties) {
|
|
1041
|
+
if (import_typescript.default.isPropertyAssignment(property) && getTextName(property.name) === propName) {
|
|
1042
|
+
return property.initializer;
|
|
1043
|
+
}
|
|
1044
|
+
if (import_typescript.default.isShorthandPropertyAssignment(property) && property.name.text === propName) {
|
|
1045
|
+
return property.name;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return void 0;
|
|
1049
|
+
}
|
|
1050
|
+
function evaluateExpressionReference(expression, ctx, depth) {
|
|
1051
|
+
const resolved = unwrapExpression(expression);
|
|
1052
|
+
markFile(ctx, resolved.getSourceFile());
|
|
1053
|
+
if (depth > MAX_RECURSION_DEPTH) return void 0;
|
|
1054
|
+
if (import_typescript.default.isIdentifier(resolved)) {
|
|
1055
|
+
return resolveIdentifierExpression(resolved, ctx, depth + 1);
|
|
1056
|
+
}
|
|
1057
|
+
if (import_typescript.default.isPropertyAccessExpression(resolved) || import_typescript.default.isElementAccessExpression(resolved)) {
|
|
1058
|
+
return resolvePropertyExpression(resolved, ctx, depth + 1);
|
|
1059
|
+
}
|
|
1060
|
+
return void 0;
|
|
986
1061
|
}
|
|
987
1062
|
function resolveExportExpression(sourceFile, exportName, checker) {
|
|
988
1063
|
const moduleSymbol = getModuleSymbol(checker, sourceFile);
|
|
@@ -991,12 +1066,12 @@ function resolveExportExpression(sourceFile, exportName, checker) {
|
|
|
991
1066
|
const explicit = exports2.find((entry) => entry.getName() === exportName);
|
|
992
1067
|
if (explicit) {
|
|
993
1068
|
const declaration = getAliasedSymbolIfNeeded(checker, explicit).declarations?.[0];
|
|
994
|
-
return
|
|
1069
|
+
return declarationToExpression(declaration);
|
|
995
1070
|
}
|
|
996
1071
|
const defaultExport = exports2.find((entry) => entry.getName() === "default");
|
|
997
1072
|
if (!defaultExport) return void 0;
|
|
998
1073
|
const defaultDecl = getAliasedSymbolIfNeeded(checker, defaultExport).declarations?.[0];
|
|
999
|
-
const defaultExpr =
|
|
1074
|
+
const defaultExpr = declarationToExpression(defaultDecl);
|
|
1000
1075
|
if (!defaultExpr) return void 0;
|
|
1001
1076
|
const resolved = unwrapExpression(defaultExpr);
|
|
1002
1077
|
if (!import_typescript.default.isObjectLiteralExpression(resolved)) return void 0;
|
|
@@ -1008,24 +1083,25 @@ function resolveExportExpression(sourceFile, exportName, checker) {
|
|
|
1008
1083
|
}
|
|
1009
1084
|
return void 0;
|
|
1010
1085
|
}
|
|
1011
|
-
function maybeObjectLiteral(expression, ctx) {
|
|
1012
|
-
if (!expression) return void 0;
|
|
1086
|
+
function maybeObjectLiteral(expression, ctx, depth = 0) {
|
|
1087
|
+
if (!expression || depth > MAX_RECURSION_DEPTH) return void 0;
|
|
1013
1088
|
const resolved = unwrapExpression(expression);
|
|
1089
|
+
markFile(ctx, resolved.getSourceFile());
|
|
1014
1090
|
if (import_typescript.default.isObjectLiteralExpression(resolved)) return resolved;
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
return maybeObjectLiteral(target, ctx);
|
|
1019
|
-
}
|
|
1020
|
-
return void 0;
|
|
1091
|
+
const referenced = evaluateExpressionReference(resolved, ctx, depth + 1);
|
|
1092
|
+
if (!referenced) return void 0;
|
|
1093
|
+
return maybeObjectLiteral(referenced, ctx, depth + 1);
|
|
1021
1094
|
}
|
|
1022
|
-
function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
|
|
1095
|
+
function collectSchemaExpressionsFromObject(objectLiteral, ctx, depth) {
|
|
1023
1096
|
const schemas = {};
|
|
1024
1097
|
for (const property of objectLiteral.properties) {
|
|
1025
1098
|
if (import_typescript.default.isSpreadAssignment(property)) {
|
|
1026
|
-
const spreadObject = maybeObjectLiteral(property.expression, ctx);
|
|
1099
|
+
const spreadObject = maybeObjectLiteral(property.expression, ctx, depth + 1);
|
|
1027
1100
|
if (!spreadObject) continue;
|
|
1028
|
-
Object.assign(
|
|
1101
|
+
Object.assign(
|
|
1102
|
+
schemas,
|
|
1103
|
+
collectSchemaExpressionsFromObject(spreadObject, ctx, depth + 1)
|
|
1104
|
+
);
|
|
1029
1105
|
continue;
|
|
1030
1106
|
}
|
|
1031
1107
|
if (import_typescript.default.isPropertyAssignment(property)) {
|
|
@@ -1042,10 +1118,10 @@ function collectSchemaExpressionsFromObject(objectLiteral, ctx) {
|
|
|
1042
1118
|
}
|
|
1043
1119
|
return schemas;
|
|
1044
1120
|
}
|
|
1045
|
-
function extractSchemaExpressions(cfgExpression, ctx) {
|
|
1046
|
-
const objectLiteral = maybeObjectLiteral(cfgExpression, ctx);
|
|
1121
|
+
function extractSchemaExpressions(cfgExpression, ctx, depth) {
|
|
1122
|
+
const objectLiteral = maybeObjectLiteral(cfgExpression, ctx, depth);
|
|
1047
1123
|
if (!objectLiteral) return {};
|
|
1048
|
-
return collectSchemaExpressionsFromObject(objectLiteral, ctx);
|
|
1124
|
+
return collectSchemaExpressionsFromObject(objectLiteral, ctx, depth);
|
|
1049
1125
|
}
|
|
1050
1126
|
function getNearestVariableName(node) {
|
|
1051
1127
|
let current = node;
|
|
@@ -1057,31 +1133,45 @@ function getNearestVariableName(node) {
|
|
|
1057
1133
|
}
|
|
1058
1134
|
return void 0;
|
|
1059
1135
|
}
|
|
1060
|
-
function
|
|
1136
|
+
function isAllAccess(expression) {
|
|
1137
|
+
if (import_typescript.default.isPropertyAccessExpression(expression)) {
|
|
1138
|
+
return expression.name.text === "all";
|
|
1139
|
+
}
|
|
1140
|
+
return Boolean(
|
|
1141
|
+
expression.argumentExpression && import_typescript.default.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === "all"
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
function evaluateBranchExpression(expression, ctx, depth) {
|
|
1061
1145
|
const resolved = unwrapExpression(expression);
|
|
1146
|
+
markFile(ctx, resolved.getSourceFile());
|
|
1147
|
+
if (depth > MAX_RECURSION_DEPTH) return void 0;
|
|
1062
1148
|
if (import_typescript.default.isIdentifier(resolved)) {
|
|
1063
|
-
const valueExpr = resolveIdentifierExpression(resolved, ctx);
|
|
1149
|
+
const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
|
|
1064
1150
|
if (!valueExpr) return void 0;
|
|
1065
|
-
return evaluateBranchExpression(valueExpr, ctx);
|
|
1151
|
+
return evaluateBranchExpression(valueExpr, ctx, depth + 1);
|
|
1066
1152
|
}
|
|
1067
1153
|
if (!import_typescript.default.isCallExpression(resolved)) return void 0;
|
|
1068
1154
|
const call = resolved;
|
|
1069
1155
|
if (import_typescript.default.isIdentifier(call.expression) && call.expression.text === "resource") {
|
|
1070
1156
|
const firstArg = call.arguments[0];
|
|
1071
|
-
if (!firstArg || !import_typescript.default.isStringLiteralLike(firstArg))
|
|
1157
|
+
if (!firstArg || !import_typescript.default.isStringLiteralLike(firstArg)) {
|
|
1158
|
+
ctx.unsupportedShapeSeen = true;
|
|
1159
|
+
return void 0;
|
|
1160
|
+
}
|
|
1072
1161
|
return { base: normalizeResourceBase(firstArg.text), leaves: [] };
|
|
1073
1162
|
}
|
|
1074
|
-
if (!import_typescript.default.isPropertyAccessExpression(call.expression))
|
|
1163
|
+
if (!import_typescript.default.isPropertyAccessExpression(call.expression)) {
|
|
1164
|
+
ctx.unsupportedShapeSeen = true;
|
|
1165
|
+
return void 0;
|
|
1166
|
+
}
|
|
1075
1167
|
const owner = call.expression.expression;
|
|
1076
1168
|
const method = call.expression.name.text;
|
|
1077
|
-
const branch = evaluateBranchExpression(owner, ctx);
|
|
1169
|
+
const branch = evaluateBranchExpression(owner, ctx, depth + 1);
|
|
1078
1170
|
if (!branch) return void 0;
|
|
1079
|
-
if (method === "with")
|
|
1080
|
-
return branch;
|
|
1081
|
-
}
|
|
1171
|
+
if (method === "with") return branch;
|
|
1082
1172
|
if (HTTP_METHODS.has(method)) {
|
|
1083
1173
|
const cfgExpression = call.arguments[0];
|
|
1084
|
-
const schemas = extractSchemaExpressions(cfgExpression, ctx);
|
|
1174
|
+
const schemas = extractSchemaExpressions(cfgExpression, ctx, depth + 1);
|
|
1085
1175
|
const nextLeaf = {
|
|
1086
1176
|
method,
|
|
1087
1177
|
path: branch.base,
|
|
@@ -1095,7 +1185,7 @@ function evaluateBranchExpression(expression, ctx) {
|
|
|
1095
1185
|
}
|
|
1096
1186
|
if (method === "sub") {
|
|
1097
1187
|
const mountedLeaves = call.arguments.flatMap(
|
|
1098
|
-
(arg) => evaluateLeavesFromExpression(arg, ctx)
|
|
1188
|
+
(arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
|
|
1099
1189
|
);
|
|
1100
1190
|
const prefixed = mountedLeaves.map((leaf) => ({
|
|
1101
1191
|
...leaf,
|
|
@@ -1106,29 +1196,44 @@ function evaluateBranchExpression(expression, ctx) {
|
|
|
1106
1196
|
leaves: [...branch.leaves, ...prefixed]
|
|
1107
1197
|
};
|
|
1108
1198
|
}
|
|
1199
|
+
ctx.unsupportedShapeSeen = true;
|
|
1109
1200
|
return void 0;
|
|
1110
1201
|
}
|
|
1111
|
-
function
|
|
1202
|
+
function expressionKey(expression) {
|
|
1203
|
+
const source = expression.getSourceFile();
|
|
1204
|
+
return `${import_node_path.default.resolve(source.fileName)}:${expression.getStart(source)}:${expression.getEnd()}:${expression.kind}`;
|
|
1205
|
+
}
|
|
1206
|
+
function evaluateLeavesFromExpression(expression, ctx, depth = 0) {
|
|
1112
1207
|
const resolved = unwrapExpression(expression);
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
ctx.
|
|
1208
|
+
markFile(ctx, resolved.getSourceFile());
|
|
1209
|
+
const key = expressionKey(resolved);
|
|
1210
|
+
if (ctx.activeExpressionKeys.has(key)) return [];
|
|
1211
|
+
if (depth > MAX_RECURSION_DEPTH) return [];
|
|
1212
|
+
ctx.activeExpressionKeys.add(key);
|
|
1118
1213
|
try {
|
|
1119
1214
|
if (import_typescript.default.isIdentifier(resolved)) {
|
|
1120
|
-
const valueExpr = resolveIdentifierExpression(resolved, ctx);
|
|
1215
|
+
const valueExpr = resolveIdentifierExpression(resolved, ctx, depth + 1);
|
|
1121
1216
|
if (!valueExpr) return [];
|
|
1122
|
-
return evaluateLeavesFromExpression(valueExpr, ctx);
|
|
1217
|
+
return evaluateLeavesFromExpression(valueExpr, ctx, depth + 1);
|
|
1218
|
+
}
|
|
1219
|
+
if (import_typescript.default.isPropertyAccessExpression(resolved) || import_typescript.default.isElementAccessExpression(resolved)) {
|
|
1220
|
+
if (isAllAccess(resolved)) {
|
|
1221
|
+
return evaluateLeavesFromExpression(resolved.expression, ctx, depth + 1);
|
|
1222
|
+
}
|
|
1223
|
+
const refExpr = resolvePropertyExpression(resolved, ctx, depth + 1);
|
|
1224
|
+
if (refExpr) {
|
|
1225
|
+
return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
|
|
1226
|
+
}
|
|
1227
|
+
return [];
|
|
1123
1228
|
}
|
|
1124
1229
|
if (import_typescript.default.isArrayLiteralExpression(resolved)) {
|
|
1125
1230
|
const leaves = [];
|
|
1126
1231
|
for (const element of resolved.elements) {
|
|
1127
1232
|
if (import_typescript.default.isSpreadElement(element)) {
|
|
1128
|
-
leaves.push(...evaluateLeavesFromExpression(element.expression, ctx));
|
|
1233
|
+
leaves.push(...evaluateLeavesFromExpression(element.expression, ctx, depth + 1));
|
|
1129
1234
|
continue;
|
|
1130
1235
|
}
|
|
1131
|
-
leaves.push(...evaluateLeavesFromExpression(element, ctx));
|
|
1236
|
+
leaves.push(...evaluateLeavesFromExpression(element, ctx, depth + 1));
|
|
1132
1237
|
}
|
|
1133
1238
|
return leaves;
|
|
1134
1239
|
}
|
|
@@ -1138,34 +1243,43 @@ function evaluateLeavesFromExpression(expression, ctx) {
|
|
|
1138
1243
|
if (callName === "finalize") {
|
|
1139
1244
|
const arg = resolved.arguments[0];
|
|
1140
1245
|
if (!arg) return [];
|
|
1141
|
-
return evaluateLeavesFromExpression(arg, ctx);
|
|
1246
|
+
return evaluateLeavesFromExpression(arg, ctx, depth + 1);
|
|
1142
1247
|
}
|
|
1143
1248
|
if (callName === "mergeArrays") {
|
|
1144
1249
|
return resolved.arguments.flatMap(
|
|
1145
|
-
(arg) => evaluateLeavesFromExpression(arg, ctx)
|
|
1250
|
+
(arg) => evaluateLeavesFromExpression(arg, ctx, depth + 1)
|
|
1146
1251
|
);
|
|
1147
1252
|
}
|
|
1148
1253
|
}
|
|
1149
1254
|
if (import_typescript.default.isPropertyAccessExpression(resolved.expression)) {
|
|
1150
1255
|
const prop = resolved.expression.name.text;
|
|
1151
1256
|
if (prop === "done") {
|
|
1152
|
-
const branch2 = evaluateBranchExpression(
|
|
1257
|
+
const branch2 = evaluateBranchExpression(
|
|
1258
|
+
resolved.expression.expression,
|
|
1259
|
+
ctx,
|
|
1260
|
+
depth + 1
|
|
1261
|
+
);
|
|
1153
1262
|
return branch2?.leaves ?? [];
|
|
1154
1263
|
}
|
|
1155
1264
|
}
|
|
1265
|
+
const refExpr = evaluateExpressionReference(resolved, ctx, depth + 1);
|
|
1266
|
+
if (refExpr) return evaluateLeavesFromExpression(refExpr, ctx, depth + 1);
|
|
1267
|
+
ctx.unsupportedShapeSeen = true;
|
|
1268
|
+
return [];
|
|
1156
1269
|
}
|
|
1157
|
-
const branch = evaluateBranchExpression(resolved, ctx);
|
|
1158
|
-
return branch
|
|
1270
|
+
const branch = evaluateBranchExpression(resolved, ctx, depth + 1);
|
|
1271
|
+
if (branch) return branch.leaves;
|
|
1272
|
+
ctx.unsupportedShapeSeen = true;
|
|
1273
|
+
return [];
|
|
1159
1274
|
} finally {
|
|
1160
|
-
ctx.
|
|
1275
|
+
ctx.activeExpressionKeys.delete(key);
|
|
1161
1276
|
}
|
|
1162
1277
|
}
|
|
1163
1278
|
function resolveSchemaMetadata(expression, ctx) {
|
|
1164
1279
|
const resolved = unwrapExpression(expression);
|
|
1165
1280
|
if (import_typescript.default.isIdentifier(resolved)) {
|
|
1166
|
-
const symbol =
|
|
1167
|
-
const
|
|
1168
|
-
const declaration = target?.declarations?.[0];
|
|
1281
|
+
const symbol = resolveSymbolFromNode(resolved, ctx);
|
|
1282
|
+
const declaration = symbol?.declarations?.[0];
|
|
1169
1283
|
const locationNode = declaration ? import_typescript.default.isVariableDeclaration(declaration) ? declaration.name : declaration : resolved;
|
|
1170
1284
|
const sourceName = declaration && import_typescript.default.isVariableDeclaration(declaration) && import_typescript.default.isIdentifier(declaration.name) ? declaration.name.text : resolved.text;
|
|
1171
1285
|
return {
|
|
@@ -1203,13 +1317,25 @@ function parseTsConfig(cwd, tsconfigPath) {
|
|
|
1203
1317
|
void 0,
|
|
1204
1318
|
resolvedTsconfig
|
|
1205
1319
|
);
|
|
1206
|
-
|
|
1320
|
+
const nonEmptyInputErrors = parsed.errors.filter((entry) => entry.code !== 18003);
|
|
1321
|
+
if (nonEmptyInputErrors.length > 0) {
|
|
1207
1322
|
throw new Error(
|
|
1208
|
-
|
|
1323
|
+
nonEmptyInputErrors.map((entry) => import_typescript.default.flattenDiagnosticMessageText(entry.messageText, "\n")).join("\n")
|
|
1209
1324
|
);
|
|
1210
1325
|
}
|
|
1211
1326
|
return { resolvedTsconfig, parsed };
|
|
1212
1327
|
}
|
|
1328
|
+
function createProgramWithFallback(parsed, moduleFileAbs) {
|
|
1329
|
+
const base = import_typescript.default.createProgram({
|
|
1330
|
+
rootNames: parsed.fileNames,
|
|
1331
|
+
options: parsed.options
|
|
1332
|
+
});
|
|
1333
|
+
if (findSourceFile(base, moduleFileAbs)) {
|
|
1334
|
+
return base;
|
|
1335
|
+
}
|
|
1336
|
+
const rootNames = Array.from(/* @__PURE__ */ new Set([...parsed.fileNames, moduleFileAbs]));
|
|
1337
|
+
return import_typescript.default.createProgram({ rootNames, options: parsed.options });
|
|
1338
|
+
}
|
|
1213
1339
|
function extractLeafSourceByAst({
|
|
1214
1340
|
modulePath,
|
|
1215
1341
|
exportName,
|
|
@@ -1218,32 +1344,43 @@ function extractLeafSourceByAst({
|
|
|
1218
1344
|
}) {
|
|
1219
1345
|
const parsedConfig = parseTsConfig(cwd, tsconfigPath);
|
|
1220
1346
|
if (!parsedConfig) {
|
|
1221
|
-
return {
|
|
1347
|
+
return {
|
|
1348
|
+
sourceByLeaf: {},
|
|
1349
|
+
reason: "module_not_in_program",
|
|
1350
|
+
stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
|
|
1351
|
+
};
|
|
1222
1352
|
}
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
options: parsedConfig.parsed.options
|
|
1226
|
-
});
|
|
1353
|
+
const moduleAbs = import_node_path.default.resolve(cwd, modulePath);
|
|
1354
|
+
const program = createProgramWithFallback(parsedConfig.parsed, moduleAbs);
|
|
1227
1355
|
const checker = program.getTypeChecker();
|
|
1228
|
-
const moduleFile = findSourceFile(program,
|
|
1356
|
+
const moduleFile = findSourceFile(program, moduleAbs);
|
|
1229
1357
|
if (!moduleFile) {
|
|
1230
1358
|
return {
|
|
1231
1359
|
sourceByLeaf: {},
|
|
1232
|
-
tsconfigPath: parsedConfig.resolvedTsconfig
|
|
1360
|
+
tsconfigPath: parsedConfig.resolvedTsconfig,
|
|
1361
|
+
reason: "module_not_in_program",
|
|
1362
|
+
stats: { visitedSymbols: 0, visitedFiles: 0, unresolvedReferences: 0 }
|
|
1233
1363
|
};
|
|
1234
1364
|
}
|
|
1235
1365
|
const exportedExpression = resolveExportExpression(moduleFile, exportName, checker);
|
|
1236
1366
|
if (!exportedExpression) {
|
|
1237
1367
|
return {
|
|
1238
1368
|
sourceByLeaf: {},
|
|
1239
|
-
tsconfigPath: parsedConfig.resolvedTsconfig
|
|
1369
|
+
tsconfigPath: parsedConfig.resolvedTsconfig,
|
|
1370
|
+
reason: "export_not_found",
|
|
1371
|
+
stats: { visitedSymbols: 0, visitedFiles: 1, unresolvedReferences: 0 }
|
|
1240
1372
|
};
|
|
1241
1373
|
}
|
|
1242
1374
|
const ctx = {
|
|
1243
1375
|
checker,
|
|
1244
|
-
|
|
1376
|
+
activeExpressionKeys: /* @__PURE__ */ new Set(),
|
|
1377
|
+
activeSymbols: /* @__PURE__ */ new Set(),
|
|
1378
|
+
visitedSymbolKeys: /* @__PURE__ */ new Set(),
|
|
1379
|
+
visitedFilePaths: /* @__PURE__ */ new Set([import_node_path.default.resolve(moduleFile.fileName)]),
|
|
1380
|
+
unresolvedReferences: 0,
|
|
1381
|
+
unsupportedShapeSeen: false
|
|
1245
1382
|
};
|
|
1246
|
-
const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx);
|
|
1383
|
+
const evaluatedLeaves = evaluateLeavesFromExpression(exportedExpression, ctx, 0);
|
|
1247
1384
|
const sourceByLeaf = {};
|
|
1248
1385
|
for (const leaf of evaluatedLeaves) {
|
|
1249
1386
|
const key = buildLeafKey(leaf.method, leaf.path);
|
|
@@ -1251,17 +1388,27 @@ function extractLeafSourceByAst({
|
|
|
1251
1388
|
...toLocation(leaf.definitionNode),
|
|
1252
1389
|
symbolName: getNearestVariableName(leaf.definitionNode)
|
|
1253
1390
|
};
|
|
1391
|
+
ctx.visitedFilePaths.add(definition.file);
|
|
1254
1392
|
const schemas = {};
|
|
1255
1393
|
for (const schemaKey of SCHEMA_KEYS) {
|
|
1256
1394
|
const schemaExpression = leaf.schemas[schemaKey];
|
|
1257
1395
|
if (!schemaExpression) continue;
|
|
1258
|
-
|
|
1396
|
+
const schemaMeta = resolveSchemaMetadata(schemaExpression, ctx);
|
|
1397
|
+
ctx.visitedFilePaths.add(schemaMeta.file);
|
|
1398
|
+
schemas[schemaKey] = schemaMeta;
|
|
1259
1399
|
}
|
|
1260
1400
|
sourceByLeaf[key] = { definition, schemas };
|
|
1261
1401
|
}
|
|
1402
|
+
const reason = Object.keys(sourceByLeaf).length > 0 ? void 0 : ctx.unsupportedShapeSeen ? "unsupported_expression_shape" : "resolved_zero_leaves";
|
|
1262
1403
|
return {
|
|
1263
1404
|
sourceByLeaf,
|
|
1264
|
-
tsconfigPath: parsedConfig.resolvedTsconfig
|
|
1405
|
+
tsconfigPath: parsedConfig.resolvedTsconfig,
|
|
1406
|
+
reason,
|
|
1407
|
+
stats: {
|
|
1408
|
+
visitedSymbols: ctx.visitedSymbolKeys.size,
|
|
1409
|
+
visitedFiles: ctx.visitedFilePaths.size,
|
|
1410
|
+
unresolvedReferences: ctx.unresolvedReferences
|
|
1411
|
+
}
|
|
1265
1412
|
};
|
|
1266
1413
|
}
|
|
1267
1414
|
|
|
@@ -1444,7 +1591,9 @@ async function exportFinalizedLeaves(input, options = {}) {
|
|
|
1444
1591
|
modulePath: import_node_path2.default.resolve(modulePath),
|
|
1445
1592
|
exportName,
|
|
1446
1593
|
tsconfigPath: extracted.tsconfigPath,
|
|
1447
|
-
resolvedLeafCount: Object.keys(sourceByLeaf).length
|
|
1594
|
+
resolvedLeafCount: Object.keys(sourceByLeaf).length,
|
|
1595
|
+
reason: extracted.reason,
|
|
1596
|
+
stats: extracted.stats
|
|
1448
1597
|
};
|
|
1449
1598
|
} else {
|
|
1450
1599
|
sourceExtraction = {
|
|
@@ -1452,7 +1601,13 @@ async function exportFinalizedLeaves(input, options = {}) {
|
|
|
1452
1601
|
enabled: false,
|
|
1453
1602
|
exportName,
|
|
1454
1603
|
tsconfigPath: options.tsconfigPath ? import_node_path2.default.resolve(options.tsconfigPath) : void 0,
|
|
1455
|
-
resolvedLeafCount: 0
|
|
1604
|
+
resolvedLeafCount: 0,
|
|
1605
|
+
reason: "resolved_zero_leaves",
|
|
1606
|
+
stats: {
|
|
1607
|
+
visitedSymbols: 0,
|
|
1608
|
+
visitedFiles: 0,
|
|
1609
|
+
unresolvedReferences: 0
|
|
1610
|
+
}
|
|
1456
1611
|
};
|
|
1457
1612
|
}
|
|
1458
1613
|
}
|