saasfunnels 0.3.2 → 0.3.4
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 +10 -0
- package/dist/library.js +211555 -11
- package/dist/saasfunnels.js +935 -398
- package/dist/types/feature-setup.d.ts +2 -0
- package/dist/types/identity.d.ts +1 -1
- package/dist/types/mcp.d.ts +1 -1
- package/dist/types/setup-pricing.d.ts +21 -0
- package/dist/types/source-analysis.d.ts +15 -0
- package/dist/types/source-scope.d.ts +1 -0
- package/package.json +1 -1
package/dist/saasfunnels.js
CHANGED
|
@@ -43,7 +43,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
43
43
|
// node_modules/typescript/lib/typescript.js
|
|
44
44
|
var require_typescript = __commonJS({
|
|
45
45
|
"node_modules/typescript/lib/typescript.js"(exports, module) {
|
|
46
|
-
var
|
|
46
|
+
var ts6 = {};
|
|
47
47
|
((module2) => {
|
|
48
48
|
"use strict";
|
|
49
49
|
var __defProp2 = Object.defineProperty;
|
|
@@ -8649,7 +8649,7 @@ ${lanes.join("\n")}
|
|
|
8649
8649
|
writeOutputIsTTY() {
|
|
8650
8650
|
return process.stdout.isTTY;
|
|
8651
8651
|
},
|
|
8652
|
-
readFile:
|
|
8652
|
+
readFile: readFile8,
|
|
8653
8653
|
writeFile: writeFile22,
|
|
8654
8654
|
watchFile: watchFile2,
|
|
8655
8655
|
watchDirectory,
|
|
@@ -8855,7 +8855,7 @@ ${lanes.join("\n")}
|
|
|
8855
8855
|
callback
|
|
8856
8856
|
);
|
|
8857
8857
|
}
|
|
8858
|
-
function
|
|
8858
|
+
function readFile8(fileName, _encoding) {
|
|
8859
8859
|
let buffer;
|
|
8860
8860
|
try {
|
|
8861
8861
|
buffer = _fs.readFileSync(fileName);
|
|
@@ -30472,10 +30472,10 @@ ${lanes.join("\n")}
|
|
|
30472
30472
|
return expressions.length > 10 ? createCommaListExpression(expressions) : reduceLeft(expressions, factory2.createComma);
|
|
30473
30473
|
}
|
|
30474
30474
|
function getName(node, allowComments, allowSourceMaps, emitFlags = 0, ignoreAssignedName) {
|
|
30475
|
-
const
|
|
30476
|
-
if (
|
|
30477
|
-
const name = setParent(setTextRange(cloneNode(
|
|
30478
|
-
emitFlags |= getEmitFlags(
|
|
30475
|
+
const nodeName2 = ignoreAssignedName ? node && getNonAssignedNameOfDeclaration(node) : getNameOfDeclaration(node);
|
|
30476
|
+
if (nodeName2 && isIdentifier(nodeName2) && !isGeneratedIdentifier(nodeName2)) {
|
|
30477
|
+
const name = setParent(setTextRange(cloneNode(nodeName2), nodeName2), nodeName2.parent);
|
|
30478
|
+
emitFlags |= getEmitFlags(nodeName2);
|
|
30479
30479
|
if (!allowSourceMaps) emitFlags |= 96;
|
|
30480
30480
|
if (!allowComments) emitFlags |= 3072;
|
|
30481
30481
|
if (emitFlags) setEmitFlags(name, emitFlags);
|
|
@@ -30936,7 +30936,7 @@ ${lanes.join("\n")}
|
|
|
30936
30936
|
startsOnNewLine,
|
|
30937
30937
|
snippetElement,
|
|
30938
30938
|
classThis,
|
|
30939
|
-
assignedName
|
|
30939
|
+
assignedName
|
|
30940
30940
|
} = sourceEmitNode;
|
|
30941
30941
|
if (!destEmitNode) destEmitNode = {};
|
|
30942
30942
|
if (flags) {
|
|
@@ -30977,8 +30977,8 @@ ${lanes.join("\n")}
|
|
|
30977
30977
|
if (classThis) {
|
|
30978
30978
|
destEmitNode.classThis = classThis;
|
|
30979
30979
|
}
|
|
30980
|
-
if (
|
|
30981
|
-
destEmitNode.assignedName =
|
|
30980
|
+
if (assignedName) {
|
|
30981
|
+
destEmitNode.assignedName = assignedName;
|
|
30982
30982
|
}
|
|
30983
30983
|
return destEmitNode;
|
|
30984
30984
|
}
|
|
@@ -45538,7 +45538,7 @@ ${lanes.join("\n")}
|
|
|
45538
45538
|
const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName);
|
|
45539
45539
|
return possibleOption ? createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption);
|
|
45540
45540
|
}
|
|
45541
|
-
function parseCommandLineWorker(diagnostics, commandLine,
|
|
45541
|
+
function parseCommandLineWorker(diagnostics, commandLine, readFile8) {
|
|
45542
45542
|
const options = {};
|
|
45543
45543
|
let watchOptions;
|
|
45544
45544
|
const fileNames = [];
|
|
@@ -45586,7 +45586,7 @@ ${lanes.join("\n")}
|
|
|
45586
45586
|
}
|
|
45587
45587
|
}
|
|
45588
45588
|
function parseResponseFile(fileName) {
|
|
45589
|
-
const text = tryReadFile(fileName,
|
|
45589
|
+
const text = tryReadFile(fileName, readFile8 || ((fileName2) => sys.readFile(fileName2)));
|
|
45590
45590
|
if (!isString(text)) {
|
|
45591
45591
|
errors.push(text);
|
|
45592
45592
|
return;
|
|
@@ -45689,8 +45689,8 @@ ${lanes.join("\n")}
|
|
|
45689
45689
|
unknownDidYouMeanDiagnostic: Diagnostics.Unknown_compiler_option_0_Did_you_mean_1,
|
|
45690
45690
|
optionTypeMismatchDiagnostic: Diagnostics.Compiler_option_0_expects_an_argument
|
|
45691
45691
|
};
|
|
45692
|
-
function parseCommandLine(commandLine,
|
|
45693
|
-
return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine,
|
|
45692
|
+
function parseCommandLine(commandLine, readFile8) {
|
|
45693
|
+
return parseCommandLineWorker(compilerOptionsDidYouMeanDiagnostics, commandLine, readFile8);
|
|
45694
45694
|
}
|
|
45695
45695
|
function getOptionFromName(optionName, allowShort) {
|
|
45696
45696
|
return getOptionDeclarationFromName(getOptionsNameMap, optionName, allowShort);
|
|
@@ -45772,8 +45772,8 @@ ${lanes.join("\n")}
|
|
|
45772
45772
|
watchOptionsToExtend
|
|
45773
45773
|
);
|
|
45774
45774
|
}
|
|
45775
|
-
function readConfigFile(fileName,
|
|
45776
|
-
const textOrDiagnostic = tryReadFile(fileName,
|
|
45775
|
+
function readConfigFile(fileName, readFile8) {
|
|
45776
|
+
const textOrDiagnostic = tryReadFile(fileName, readFile8);
|
|
45777
45777
|
return isString(textOrDiagnostic) ? parseConfigFileTextToJson(fileName, textOrDiagnostic) : { config: {}, error: textOrDiagnostic };
|
|
45778
45778
|
}
|
|
45779
45779
|
function parseConfigFileTextToJson(fileName, jsonText) {
|
|
@@ -45788,14 +45788,14 @@ ${lanes.join("\n")}
|
|
|
45788
45788
|
error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : void 0
|
|
45789
45789
|
};
|
|
45790
45790
|
}
|
|
45791
|
-
function readJsonConfigFile(fileName,
|
|
45792
|
-
const textOrDiagnostic = tryReadFile(fileName,
|
|
45791
|
+
function readJsonConfigFile(fileName, readFile8) {
|
|
45792
|
+
const textOrDiagnostic = tryReadFile(fileName, readFile8);
|
|
45793
45793
|
return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { fileName, parseDiagnostics: [textOrDiagnostic] };
|
|
45794
45794
|
}
|
|
45795
|
-
function tryReadFile(fileName,
|
|
45795
|
+
function tryReadFile(fileName, readFile8) {
|
|
45796
45796
|
let text;
|
|
45797
45797
|
try {
|
|
45798
|
-
text =
|
|
45798
|
+
text = readFile8(fileName);
|
|
45799
45799
|
} catch (e) {
|
|
45800
45800
|
return createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, e.message);
|
|
45801
45801
|
}
|
|
@@ -62210,10 +62210,10 @@ ${lanes.join("\n")}
|
|
|
62210
62210
|
}
|
|
62211
62211
|
let expression;
|
|
62212
62212
|
if (isSingleOrDoubleQuote(firstChar) && !(symbol22.flags & 8)) {
|
|
62213
|
-
const
|
|
62214
|
-
context.approximateLength +=
|
|
62213
|
+
const literalText2 = stripQuotes(symbolName2).replace(/\\./g, (s) => s.substring(1));
|
|
62214
|
+
context.approximateLength += literalText2.length + 2;
|
|
62215
62215
|
expression = factory.createStringLiteral(
|
|
62216
|
-
|
|
62216
|
+
literalText2,
|
|
62217
62217
|
firstChar === 39
|
|
62218
62218
|
/* singleQuote */
|
|
62219
62219
|
);
|
|
@@ -105852,27 +105852,27 @@ ${lanes.join("\n")}
|
|
|
105852
105852
|
function getAssignedNameOfPropertyName(context, name, assignedNameText) {
|
|
105853
105853
|
const { factory: factory2 } = context;
|
|
105854
105854
|
if (assignedNameText !== void 0) {
|
|
105855
|
-
const
|
|
105856
|
-
return { assignedName:
|
|
105855
|
+
const assignedName2 = factory2.createStringLiteral(assignedNameText);
|
|
105856
|
+
return { assignedName: assignedName2, name };
|
|
105857
105857
|
}
|
|
105858
105858
|
if (isPropertyNameLiteral(name) || isPrivateIdentifier(name)) {
|
|
105859
|
-
const
|
|
105860
|
-
return { assignedName:
|
|
105859
|
+
const assignedName2 = factory2.createStringLiteralFromNode(name);
|
|
105860
|
+
return { assignedName: assignedName2, name };
|
|
105861
105861
|
}
|
|
105862
105862
|
if (isPropertyNameLiteral(name.expression) && !isIdentifier(name.expression)) {
|
|
105863
|
-
const
|
|
105864
|
-
return { assignedName:
|
|
105863
|
+
const assignedName2 = factory2.createStringLiteralFromNode(name.expression);
|
|
105864
|
+
return { assignedName: assignedName2, name };
|
|
105865
105865
|
}
|
|
105866
|
-
const
|
|
105867
|
-
context.hoistVariableDeclaration(
|
|
105866
|
+
const assignedName = factory2.getGeneratedNameForNode(name);
|
|
105867
|
+
context.hoistVariableDeclaration(assignedName);
|
|
105868
105868
|
const key2 = context.getEmitHelperFactory().createPropKeyHelper(name.expression);
|
|
105869
|
-
const assignment = factory2.createAssignment(
|
|
105869
|
+
const assignment = factory2.createAssignment(assignedName, key2);
|
|
105870
105870
|
const updatedName = factory2.updateComputedPropertyName(name, assignment);
|
|
105871
|
-
return { assignedName
|
|
105871
|
+
return { assignedName, name: updatedName };
|
|
105872
105872
|
}
|
|
105873
|
-
function createClassNamedEvaluationHelperBlock(context,
|
|
105873
|
+
function createClassNamedEvaluationHelperBlock(context, assignedName, thisExpression = context.factory.createThis()) {
|
|
105874
105874
|
const { factory: factory2 } = context;
|
|
105875
|
-
const expression = context.getEmitHelperFactory().createSetFunctionNameHelper(thisExpression,
|
|
105875
|
+
const expression = context.getEmitHelperFactory().createSetFunctionNameHelper(thisExpression, assignedName);
|
|
105876
105876
|
const statement = factory2.createExpressionStatement(expression);
|
|
105877
105877
|
const body = factory2.createBlock(
|
|
105878
105878
|
[statement],
|
|
@@ -105880,7 +105880,7 @@ ${lanes.join("\n")}
|
|
|
105880
105880
|
false
|
|
105881
105881
|
);
|
|
105882
105882
|
const block = factory2.createClassStaticBlockDeclaration(body);
|
|
105883
|
-
getOrCreateEmitNode(block).assignedName =
|
|
105883
|
+
getOrCreateEmitNode(block).assignedName = assignedName;
|
|
105884
105884
|
return block;
|
|
105885
105885
|
}
|
|
105886
105886
|
function isClassNamedEvaluationHelperBlock(node) {
|
|
@@ -105898,12 +105898,12 @@ ${lanes.join("\n")}
|
|
|
105898
105898
|
function classHasDeclaredOrExplicitlyAssignedName(node) {
|
|
105899
105899
|
return !!node.name || classHasExplicitlyAssignedName(node);
|
|
105900
105900
|
}
|
|
105901
|
-
function injectClassNamedEvaluationHelperBlockIfMissing(context, node,
|
|
105901
|
+
function injectClassNamedEvaluationHelperBlockIfMissing(context, node, assignedName, thisExpression) {
|
|
105902
105902
|
if (classHasExplicitlyAssignedName(node)) {
|
|
105903
105903
|
return node;
|
|
105904
105904
|
}
|
|
105905
105905
|
const { factory: factory2 } = context;
|
|
105906
|
-
const namedEvaluationBlock = createClassNamedEvaluationHelperBlock(context,
|
|
105906
|
+
const namedEvaluationBlock = createClassNamedEvaluationHelperBlock(context, assignedName, thisExpression);
|
|
105907
105907
|
if (node.name) {
|
|
105908
105908
|
setSourceMapRange(namedEvaluationBlock.body.statements[0], node.name);
|
|
105909
105909
|
}
|
|
@@ -105927,22 +105927,22 @@ ${lanes.join("\n")}
|
|
|
105927
105927
|
node.heritageClauses,
|
|
105928
105928
|
members
|
|
105929
105929
|
);
|
|
105930
|
-
getOrCreateEmitNode(node).assignedName =
|
|
105930
|
+
getOrCreateEmitNode(node).assignedName = assignedName;
|
|
105931
105931
|
return node;
|
|
105932
105932
|
}
|
|
105933
|
-
function finishTransformNamedEvaluation(context, expression,
|
|
105934
|
-
if (ignoreEmptyStringLiteral && isStringLiteral(
|
|
105933
|
+
function finishTransformNamedEvaluation(context, expression, assignedName, ignoreEmptyStringLiteral) {
|
|
105934
|
+
if (ignoreEmptyStringLiteral && isStringLiteral(assignedName) && isEmptyStringLiteral(assignedName)) {
|
|
105935
105935
|
return expression;
|
|
105936
105936
|
}
|
|
105937
105937
|
const { factory: factory2 } = context;
|
|
105938
105938
|
const innerExpression = skipOuterExpressions(expression);
|
|
105939
|
-
const updatedExpression = isClassExpression(innerExpression) ? cast(injectClassNamedEvaluationHelperBlockIfMissing(context, innerExpression,
|
|
105939
|
+
const updatedExpression = isClassExpression(innerExpression) ? cast(injectClassNamedEvaluationHelperBlockIfMissing(context, innerExpression, assignedName), isClassExpression) : context.getEmitHelperFactory().createSetFunctionNameHelper(innerExpression, assignedName);
|
|
105940
105940
|
return factory2.restoreOuterExpressions(expression, updatedExpression);
|
|
105941
105941
|
}
|
|
105942
105942
|
function transformNamedEvaluationOfPropertyAssignment(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
105943
105943
|
const { factory: factory2 } = context;
|
|
105944
|
-
const { assignedName
|
|
105945
|
-
const initializer3 = finishTransformNamedEvaluation(context, node.initializer,
|
|
105944
|
+
const { assignedName, name } = getAssignedNameOfPropertyName(context, node.name, assignedNameText);
|
|
105945
|
+
const initializer3 = finishTransformNamedEvaluation(context, node.initializer, assignedName, ignoreEmptyStringLiteral);
|
|
105946
105946
|
return factory2.updatePropertyAssignment(
|
|
105947
105947
|
node,
|
|
105948
105948
|
name,
|
|
@@ -105951,8 +105951,8 @@ ${lanes.join("\n")}
|
|
|
105951
105951
|
}
|
|
105952
105952
|
function transformNamedEvaluationOfShorthandAssignmentProperty(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
105953
105953
|
const { factory: factory2 } = context;
|
|
105954
|
-
const
|
|
105955
|
-
const objectAssignmentInitializer = finishTransformNamedEvaluation(context, node.objectAssignmentInitializer,
|
|
105954
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : getAssignedNameOfIdentifier(factory2, node.name, node.objectAssignmentInitializer);
|
|
105955
|
+
const objectAssignmentInitializer = finishTransformNamedEvaluation(context, node.objectAssignmentInitializer, assignedName, ignoreEmptyStringLiteral);
|
|
105956
105956
|
return factory2.updateShorthandPropertyAssignment(
|
|
105957
105957
|
node,
|
|
105958
105958
|
node.name,
|
|
@@ -105961,8 +105961,8 @@ ${lanes.join("\n")}
|
|
|
105961
105961
|
}
|
|
105962
105962
|
function transformNamedEvaluationOfVariableDeclaration(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
105963
105963
|
const { factory: factory2 } = context;
|
|
105964
|
-
const
|
|
105965
|
-
const initializer3 = finishTransformNamedEvaluation(context, node.initializer,
|
|
105964
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : getAssignedNameOfIdentifier(factory2, node.name, node.initializer);
|
|
105965
|
+
const initializer3 = finishTransformNamedEvaluation(context, node.initializer, assignedName, ignoreEmptyStringLiteral);
|
|
105966
105966
|
return factory2.updateVariableDeclaration(
|
|
105967
105967
|
node,
|
|
105968
105968
|
node.name,
|
|
@@ -105973,8 +105973,8 @@ ${lanes.join("\n")}
|
|
|
105973
105973
|
}
|
|
105974
105974
|
function transformNamedEvaluationOfParameterDeclaration(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
105975
105975
|
const { factory: factory2 } = context;
|
|
105976
|
-
const
|
|
105977
|
-
const initializer3 = finishTransformNamedEvaluation(context, node.initializer,
|
|
105976
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : getAssignedNameOfIdentifier(factory2, node.name, node.initializer);
|
|
105977
|
+
const initializer3 = finishTransformNamedEvaluation(context, node.initializer, assignedName, ignoreEmptyStringLiteral);
|
|
105978
105978
|
return factory2.updateParameterDeclaration(
|
|
105979
105979
|
node,
|
|
105980
105980
|
node.modifiers,
|
|
@@ -105987,8 +105987,8 @@ ${lanes.join("\n")}
|
|
|
105987
105987
|
}
|
|
105988
105988
|
function transformNamedEvaluationOfBindingElement(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
105989
105989
|
const { factory: factory2 } = context;
|
|
105990
|
-
const
|
|
105991
|
-
const initializer3 = finishTransformNamedEvaluation(context, node.initializer,
|
|
105990
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : getAssignedNameOfIdentifier(factory2, node.name, node.initializer);
|
|
105991
|
+
const initializer3 = finishTransformNamedEvaluation(context, node.initializer, assignedName, ignoreEmptyStringLiteral);
|
|
105992
105992
|
return factory2.updateBindingElement(
|
|
105993
105993
|
node,
|
|
105994
105994
|
node.dotDotDotToken,
|
|
@@ -105999,8 +105999,8 @@ ${lanes.join("\n")}
|
|
|
105999
105999
|
}
|
|
106000
106000
|
function transformNamedEvaluationOfPropertyDeclaration(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
106001
106001
|
const { factory: factory2 } = context;
|
|
106002
|
-
const { assignedName
|
|
106003
|
-
const initializer3 = finishTransformNamedEvaluation(context, node.initializer,
|
|
106002
|
+
const { assignedName, name } = getAssignedNameOfPropertyName(context, node.name, assignedNameText);
|
|
106003
|
+
const initializer3 = finishTransformNamedEvaluation(context, node.initializer, assignedName, ignoreEmptyStringLiteral);
|
|
106004
106004
|
return factory2.updatePropertyDeclaration(
|
|
106005
106005
|
node,
|
|
106006
106006
|
node.modifiers,
|
|
@@ -106012,8 +106012,8 @@ ${lanes.join("\n")}
|
|
|
106012
106012
|
}
|
|
106013
106013
|
function transformNamedEvaluationOfAssignmentExpression(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
106014
106014
|
const { factory: factory2 } = context;
|
|
106015
|
-
const
|
|
106016
|
-
const right = finishTransformNamedEvaluation(context, node.right,
|
|
106015
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : getAssignedNameOfIdentifier(factory2, node.left, node.right);
|
|
106016
|
+
const right = finishTransformNamedEvaluation(context, node.right, assignedName, ignoreEmptyStringLiteral);
|
|
106017
106017
|
return factory2.updateBinaryExpression(
|
|
106018
106018
|
node,
|
|
106019
106019
|
node.left,
|
|
@@ -106023,32 +106023,32 @@ ${lanes.join("\n")}
|
|
|
106023
106023
|
}
|
|
106024
106024
|
function transformNamedEvaluationOfExportAssignment(context, node, ignoreEmptyStringLiteral, assignedNameText) {
|
|
106025
106025
|
const { factory: factory2 } = context;
|
|
106026
|
-
const
|
|
106027
|
-
const expression = finishTransformNamedEvaluation(context, node.expression,
|
|
106026
|
+
const assignedName = assignedNameText !== void 0 ? factory2.createStringLiteral(assignedNameText) : factory2.createStringLiteral(node.isExportEquals ? "" : "default");
|
|
106027
|
+
const expression = finishTransformNamedEvaluation(context, node.expression, assignedName, ignoreEmptyStringLiteral);
|
|
106028
106028
|
return factory2.updateExportAssignment(
|
|
106029
106029
|
node,
|
|
106030
106030
|
node.modifiers,
|
|
106031
106031
|
expression
|
|
106032
106032
|
);
|
|
106033
106033
|
}
|
|
106034
|
-
function transformNamedEvaluation(context, node, ignoreEmptyStringLiteral,
|
|
106034
|
+
function transformNamedEvaluation(context, node, ignoreEmptyStringLiteral, assignedName) {
|
|
106035
106035
|
switch (node.kind) {
|
|
106036
106036
|
case 304:
|
|
106037
|
-
return transformNamedEvaluationOfPropertyAssignment(context, node, ignoreEmptyStringLiteral,
|
|
106037
|
+
return transformNamedEvaluationOfPropertyAssignment(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106038
106038
|
case 305:
|
|
106039
|
-
return transformNamedEvaluationOfShorthandAssignmentProperty(context, node, ignoreEmptyStringLiteral,
|
|
106039
|
+
return transformNamedEvaluationOfShorthandAssignmentProperty(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106040
106040
|
case 261:
|
|
106041
|
-
return transformNamedEvaluationOfVariableDeclaration(context, node, ignoreEmptyStringLiteral,
|
|
106041
|
+
return transformNamedEvaluationOfVariableDeclaration(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106042
106042
|
case 170:
|
|
106043
|
-
return transformNamedEvaluationOfParameterDeclaration(context, node, ignoreEmptyStringLiteral,
|
|
106043
|
+
return transformNamedEvaluationOfParameterDeclaration(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106044
106044
|
case 209:
|
|
106045
|
-
return transformNamedEvaluationOfBindingElement(context, node, ignoreEmptyStringLiteral,
|
|
106045
|
+
return transformNamedEvaluationOfBindingElement(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106046
106046
|
case 173:
|
|
106047
|
-
return transformNamedEvaluationOfPropertyDeclaration(context, node, ignoreEmptyStringLiteral,
|
|
106047
|
+
return transformNamedEvaluationOfPropertyDeclaration(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106048
106048
|
case 227:
|
|
106049
|
-
return transformNamedEvaluationOfAssignmentExpression(context, node, ignoreEmptyStringLiteral,
|
|
106049
|
+
return transformNamedEvaluationOfAssignmentExpression(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106050
106050
|
case 278:
|
|
106051
|
-
return transformNamedEvaluationOfExportAssignment(context, node, ignoreEmptyStringLiteral,
|
|
106051
|
+
return transformNamedEvaluationOfExportAssignment(context, node, ignoreEmptyStringLiteral, assignedName);
|
|
106052
106052
|
}
|
|
106053
106053
|
}
|
|
106054
106054
|
var ProcessLevel = /* @__PURE__ */ ((ProcessLevel2) => {
|
|
@@ -135899,12 +135899,12 @@ ${lanes.join("\n")}
|
|
|
135899
135899
|
function createCompilerHost(options, setParentNodes) {
|
|
135900
135900
|
return createCompilerHostWorker(options, setParentNodes);
|
|
135901
135901
|
}
|
|
135902
|
-
function createGetSourceFile(
|
|
135902
|
+
function createGetSourceFile(readFile8, setParentNodes) {
|
|
135903
135903
|
return (fileName, languageVersionOrOptions, onError) => {
|
|
135904
135904
|
let text;
|
|
135905
135905
|
try {
|
|
135906
135906
|
mark("beforeIORead");
|
|
135907
|
-
text =
|
|
135907
|
+
text = readFile8(fileName);
|
|
135908
135908
|
mark("afterIORead");
|
|
135909
135909
|
measure("I/O Read", "beforeIORead", "afterIORead");
|
|
135910
135910
|
} catch (e) {
|
|
@@ -136808,7 +136808,7 @@ ${lanes.join("\n")}
|
|
|
136808
136808
|
getRedirectFromOutput,
|
|
136809
136809
|
forEachResolvedProjectReference: forEachResolvedProjectReference2
|
|
136810
136810
|
});
|
|
136811
|
-
const
|
|
136811
|
+
const readFile8 = host.readFile.bind(host);
|
|
136812
136812
|
(_e = tracing) == null ? void 0 : _e.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
|
|
136813
136813
|
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
|
|
136814
136814
|
(_f = tracing) == null ? void 0 : _f.pop();
|
|
@@ -137029,7 +137029,7 @@ ${lanes.join("\n")}
|
|
|
137029
137029
|
shouldTransformImportCall,
|
|
137030
137030
|
emitBuildInfo,
|
|
137031
137031
|
fileExists,
|
|
137032
|
-
readFile:
|
|
137032
|
+
readFile: readFile8,
|
|
137033
137033
|
directoryExists,
|
|
137034
137034
|
getSymlinkCache,
|
|
137035
137035
|
realpath: (_o = host.realpath) == null ? void 0 : _o.bind(host),
|
|
@@ -211562,9 +211562,9 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
211562
211562
|
};
|
|
211563
211563
|
}
|
|
211564
211564
|
})({ get exports() {
|
|
211565
|
-
return
|
|
211565
|
+
return ts6;
|
|
211566
211566
|
}, set exports(v) {
|
|
211567
|
-
|
|
211567
|
+
ts6 = v;
|
|
211568
211568
|
if (typeof module !== "undefined" && module.exports) {
|
|
211569
211569
|
module.exports = v;
|
|
211570
211570
|
}
|
|
@@ -216136,9 +216136,9 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
216136
216136
|
default: {
|
|
216137
216137
|
const message = `Unexpected token in block scalar header: ${token.type}`;
|
|
216138
216138
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
216139
|
-
const
|
|
216140
|
-
if (
|
|
216141
|
-
length +=
|
|
216139
|
+
const ts6 = token.source;
|
|
216140
|
+
if (ts6 && typeof ts6 === "string")
|
|
216141
|
+
length += ts6.length;
|
|
216142
216142
|
}
|
|
216143
216143
|
}
|
|
216144
216144
|
}
|
|
@@ -216447,9 +216447,9 @@ var require_compose_scalar = __commonJS({
|
|
|
216447
216447
|
if (schema.compat) {
|
|
216448
216448
|
const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR];
|
|
216449
216449
|
if (tag.tag !== compat.tag) {
|
|
216450
|
-
const
|
|
216450
|
+
const ts6 = directives.tagString(tag.tag);
|
|
216451
216451
|
const cs = directives.tagString(compat.tag);
|
|
216452
|
-
const msg = `Value may be parsed as either ${
|
|
216452
|
+
const msg = `Value may be parsed as either ${ts6} or ${cs}`;
|
|
216453
216453
|
onError(token, "TAG_RESOLVE_FAILED", msg, true);
|
|
216454
216454
|
}
|
|
216455
216455
|
}
|
|
@@ -218943,10 +218943,178 @@ async function resolveSetupAccess(options) {
|
|
|
218943
218943
|
}
|
|
218944
218944
|
|
|
218945
218945
|
// src/cli.ts
|
|
218946
|
-
import { dirname as dirname3, join as
|
|
218946
|
+
import { dirname as dirname3, join as join6, resolve as resolve5 } from "node:path";
|
|
218947
218947
|
|
|
218948
|
-
// src/
|
|
218948
|
+
// src/source-analysis.ts
|
|
218949
218949
|
var import_typescript = __toESM(require_typescript(), 1);
|
|
218950
|
+
var nonProductName = /(?:^|[_-])(?:test|tests|fixture|fixtures|mock|mocks|demo|sample|e2e|storybook)(?:$|[_-])|(?:^|[a-z])(?:TestFixture|Fixture|Mock|Demo|E2e|E2E|Storybook)(?:[A-Z]|$)|^(?:test|mock|demo|fixture|sample|e2e)[A-Z]/;
|
|
218951
|
+
var nonProductPath = /(?:^|\/)(?:__tests__|__mocks__|tests?|fixtures?|mocks?|stories|examples?|qa)(?:\/|$)|(?:^|[\/._-])(?:fixture|fixtures|e2e|mock|stories)(?:[._-]|$)|\.(?:test|spec|stories|d)\.[cm]?[jt]sx?$/i;
|
|
218952
|
+
function parseApplicationSource(path, source) {
|
|
218953
|
+
return import_typescript.default.createSourceFile(
|
|
218954
|
+
path,
|
|
218955
|
+
source,
|
|
218956
|
+
import_typescript.default.ScriptTarget.Latest,
|
|
218957
|
+
true,
|
|
218958
|
+
/\.[jt]sx$/.test(path) ? import_typescript.default.ScriptKind.TSX : import_typescript.default.ScriptKind.TS
|
|
218959
|
+
);
|
|
218960
|
+
}
|
|
218961
|
+
function nodeName(node) {
|
|
218962
|
+
if ((import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isClassDeclaration(node) || import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isVariableDeclaration(node)) && node.name)
|
|
218963
|
+
return node.name.getText();
|
|
218964
|
+
return null;
|
|
218965
|
+
}
|
|
218966
|
+
function demoCondition(expression) {
|
|
218967
|
+
if (import_typescript.default.isParenthesizedExpression(expression))
|
|
218968
|
+
return demoCondition(expression.expression);
|
|
218969
|
+
if (import_typescript.default.isPrefixUnaryExpression(expression) && expression.operator === import_typescript.default.SyntaxKind.ExclamationToken) {
|
|
218970
|
+
const value = demoCondition(expression.operand);
|
|
218971
|
+
return value === null ? null : !value;
|
|
218972
|
+
}
|
|
218973
|
+
const text = expression.getText();
|
|
218974
|
+
if (/^(?:isDemo|demoMode|isTest|[A-Z_]*(?:E2E|FIXTURES|MOCKS)[A-Z_]*)$/.test(
|
|
218975
|
+
text
|
|
218976
|
+
) || /^(?:process\.env|import\.meta\.env)\.[A-Z_]*(?:E2E|FIXTURES|MOCKS)[A-Z_]*$/.test(
|
|
218977
|
+
text
|
|
218978
|
+
))
|
|
218979
|
+
return true;
|
|
218980
|
+
if (import_typescript.default.isBinaryExpression(expression)) {
|
|
218981
|
+
const op = expression.operatorToken.kind;
|
|
218982
|
+
const equality = [
|
|
218983
|
+
import_typescript.default.SyntaxKind.EqualsEqualsToken,
|
|
218984
|
+
import_typescript.default.SyntaxKind.EqualsEqualsEqualsToken
|
|
218985
|
+
].includes(op);
|
|
218986
|
+
const inequality = [
|
|
218987
|
+
import_typescript.default.SyntaxKind.ExclamationEqualsToken,
|
|
218988
|
+
import_typescript.default.SyntaxKind.ExclamationEqualsEqualsToken
|
|
218989
|
+
].includes(op);
|
|
218990
|
+
if (!equality && !inequality) return null;
|
|
218991
|
+
const right = expression.right;
|
|
218992
|
+
const value = import_typescript.default.isStringLiteral(right) ? right.text : right.kind === import_typescript.default.SyntaxKind.TrueKeyword ? "1" : right.kind === import_typescript.default.SyntaxKind.FalseKeyword ? "0" : null;
|
|
218993
|
+
const nodeEnv = /(?:^|\.)NODE_ENV$/.test(expression.left.getText());
|
|
218994
|
+
const positive = nodeEnv && value === "test" ? true : demoCondition(expression.left);
|
|
218995
|
+
if (positive === null || nodeEnv && value !== "test") return null;
|
|
218996
|
+
const enabled = nodeEnv || value === "1" || value === "true";
|
|
218997
|
+
if (!enabled && value !== "0" && value !== "false") return null;
|
|
218998
|
+
return equality ? enabled === positive : enabled !== positive;
|
|
218999
|
+
}
|
|
219000
|
+
return null;
|
|
219001
|
+
}
|
|
219002
|
+
function isProductNode(node) {
|
|
219003
|
+
for (let current = node; current; current = current.parent) {
|
|
219004
|
+
const name = nodeName(current);
|
|
219005
|
+
if (name && nonProductName.test(name)) return false;
|
|
219006
|
+
if (import_typescript.default.isIfStatement(current) || import_typescript.default.isConditionalExpression(current)) {
|
|
219007
|
+
const value = demoCondition(
|
|
219008
|
+
import_typescript.default.isIfStatement(current) ? current.expression : current.condition
|
|
219009
|
+
);
|
|
219010
|
+
if (value === null) continue;
|
|
219011
|
+
let child = node;
|
|
219012
|
+
while (child.parent && child.parent !== current) child = child.parent;
|
|
219013
|
+
const excluded = import_typescript.default.isIfStatement(current) ? value ? current.thenStatement : current.elseStatement : value ? current.whenTrue : current.whenFalse;
|
|
219014
|
+
if (child === excluded) return false;
|
|
219015
|
+
}
|
|
219016
|
+
}
|
|
219017
|
+
return true;
|
|
219018
|
+
}
|
|
219019
|
+
function enclosingSymbol(node) {
|
|
219020
|
+
for (let current = node; current; current = current.parent) {
|
|
219021
|
+
const name = nodeName(current);
|
|
219022
|
+
if (name) return name.slice(0, 120);
|
|
219023
|
+
}
|
|
219024
|
+
return "module";
|
|
219025
|
+
}
|
|
219026
|
+
function literalText(node) {
|
|
219027
|
+
return node && (import_typescript.default.isStringLiteral(node) || import_typescript.default.isNoSubstitutionTemplateLiteral(node)) ? node.text : null;
|
|
219028
|
+
}
|
|
219029
|
+
function namedFeatureEvidence(path, source) {
|
|
219030
|
+
if (nonProductPath.test(path)) return [];
|
|
219031
|
+
const file2 = parseApplicationSource(path, source);
|
|
219032
|
+
const findings = [];
|
|
219033
|
+
const add = (node, key2, model, kind, reason) => {
|
|
219034
|
+
if (!key2 || !/^[a-z][a-z0-9_.-]{0,79}$/.test(key2)) return;
|
|
219035
|
+
findings.push({
|
|
219036
|
+
key: key2,
|
|
219037
|
+
model,
|
|
219038
|
+
kind,
|
|
219039
|
+
reason,
|
|
219040
|
+
line: file2.getLineAndCharacterOfPosition(node.getStart(file2)).line + 1,
|
|
219041
|
+
symbol: enclosingSymbol(node)
|
|
219042
|
+
});
|
|
219043
|
+
};
|
|
219044
|
+
const visit = (node) => {
|
|
219045
|
+
if (!isProductNode(node)) return;
|
|
219046
|
+
if (import_typescript.default.isCallExpression(node)) {
|
|
219047
|
+
const name = import_typescript.default.isIdentifier(node.expression) ? node.expression.text : import_typescript.default.isPropertyAccessExpression(node.expression) ? node.expression.name.text : "";
|
|
219048
|
+
const featureCall = /^(?:has|check|require|assert|canUse|enforce|authorize|evaluate|is).*?(?:Feature|Entitlement)(?:Enabled|Access)?$/i.test(
|
|
219049
|
+
name
|
|
219050
|
+
) || /^(?:has|check|require|assert|canUse|enforce|authorize|evaluate|is).*Capability(?:Enabled|Access)?$/i.test(
|
|
219051
|
+
name
|
|
219052
|
+
) && /billing|plan|subscription|commercial|product/i.test(name);
|
|
219053
|
+
const limitCall = /^(?:check|require|assert|enforce|report|track|consume).*?(?:Quota|Usage|Limit)$/i.test(
|
|
219054
|
+
name
|
|
219055
|
+
);
|
|
219056
|
+
if (featureCall || limitCall) {
|
|
219057
|
+
const literals = node.arguments.map(literalText).filter((v) => v !== null);
|
|
219058
|
+
let key2 = literals.length === 1 ? literals[0] : null;
|
|
219059
|
+
for (const arg of node.arguments)
|
|
219060
|
+
if (import_typescript.default.isObjectLiteralExpression(arg)) {
|
|
219061
|
+
for (const p of arg.properties)
|
|
219062
|
+
if (import_typescript.default.isPropertyAssignment(p) && /^(featureKey|feature|capability|entitlement|quotaKey|limitKey|meterKey)$/.test(
|
|
219063
|
+
p.name.getText(file2).replace(/["']/g, "")
|
|
219064
|
+
))
|
|
219065
|
+
key2 = literalText(p.initializer);
|
|
219066
|
+
}
|
|
219067
|
+
add(
|
|
219068
|
+
node,
|
|
219069
|
+
key2,
|
|
219070
|
+
limitCall ? "limit" : "boolean",
|
|
219071
|
+
limitCall ? "usage_reporter" : "server_enforcement",
|
|
219072
|
+
limitCall ? "Application usage or limit check" : "Application access check"
|
|
219073
|
+
);
|
|
219074
|
+
}
|
|
219075
|
+
}
|
|
219076
|
+
if (import_typescript.default.isJsxOpeningElement(node) || import_typescript.default.isJsxSelfClosingElement(node)) {
|
|
219077
|
+
if (/^(FeatureGate|EntitlementGate|CapabilityGate)$/.test(
|
|
219078
|
+
node.tagName.getText(file2)
|
|
219079
|
+
)) {
|
|
219080
|
+
for (const prop of node.attributes.properties)
|
|
219081
|
+
if (import_typescript.default.isJsxAttribute(prop) && /^(featureKey|feature|capability)$/.test(prop.name.getText(file2)))
|
|
219082
|
+
add(
|
|
219083
|
+
prop,
|
|
219084
|
+
prop.initializer && import_typescript.default.isJsxExpression(prop.initializer) ? literalText(prop.initializer.expression) : literalText(prop.initializer),
|
|
219085
|
+
"boolean",
|
|
219086
|
+
"browser_presentation",
|
|
219087
|
+
"Application feature visibility gate"
|
|
219088
|
+
);
|
|
219089
|
+
}
|
|
219090
|
+
}
|
|
219091
|
+
if (import_typescript.default.isPropertyAssignment(node)) {
|
|
219092
|
+
const key2 = node.name.getText(file2).replace(/["']/g, "");
|
|
219093
|
+
if (/^(usageKey|limitKey|meterKey|featureLimit)$/.test(key2))
|
|
219094
|
+
add(
|
|
219095
|
+
node,
|
|
219096
|
+
literalText(node.initializer),
|
|
219097
|
+
"limit",
|
|
219098
|
+
"usage_reporter",
|
|
219099
|
+
"Application usage or limit declaration"
|
|
219100
|
+
);
|
|
219101
|
+
if (/^(deniedFeatureKey|featureDenied|onFeatureDenied)$/.test(key2))
|
|
219102
|
+
add(
|
|
219103
|
+
node,
|
|
219104
|
+
literalText(node.initializer),
|
|
219105
|
+
"boolean",
|
|
219106
|
+
"funnel_trigger",
|
|
219107
|
+
"Application denied-access trigger"
|
|
219108
|
+
);
|
|
219109
|
+
}
|
|
219110
|
+
import_typescript.default.forEachChild(node, visit);
|
|
219111
|
+
};
|
|
219112
|
+
visit(file2);
|
|
219113
|
+
return findings;
|
|
219114
|
+
}
|
|
219115
|
+
|
|
219116
|
+
// src/setup-pricing.ts
|
|
219117
|
+
var import_typescript2 = __toESM(require_typescript(), 1);
|
|
218950
219118
|
var import_yaml = __toESM(require_dist(), 1);
|
|
218951
219119
|
var PricingExtractionError = class extends Error {
|
|
218952
219120
|
constructor(code, message) {
|
|
@@ -218962,29 +219130,29 @@ var unsupported = () => new PricingExtractionError(
|
|
|
218962
219130
|
var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
218963
219131
|
var safeKey = (v) => typeof v === "string" && /^[a-z][a-z0-9_.-]{0,79}$/.test(v);
|
|
218964
219132
|
var safeProperty = (key2) => !["__proto__", "constructor", "prototype"].includes(key2);
|
|
218965
|
-
function declarations(source) {
|
|
218966
|
-
const file2 =
|
|
219133
|
+
function declarations(source, featuresOnly = false) {
|
|
219134
|
+
const file2 = import_typescript2.default.createSourceFile(
|
|
218967
219135
|
"pricing.ts",
|
|
218968
219136
|
source,
|
|
218969
|
-
|
|
219137
|
+
import_typescript2.default.ScriptTarget.Latest,
|
|
218970
219138
|
true,
|
|
218971
|
-
|
|
219139
|
+
import_typescript2.default.ScriptKind.TSX
|
|
218972
219140
|
);
|
|
218973
219141
|
const values = /* @__PURE__ */ new Map();
|
|
218974
219142
|
const roots = [];
|
|
218975
219143
|
const planName = /^(plans?|pricing|tiers?|billingCatalog|planCatalog|subscriptionPlans)$/i;
|
|
218976
219144
|
const containsAccess = (node, fields = /^(features|capabilities|quotas)$/) => {
|
|
218977
|
-
if (
|
|
219145
|
+
if (import_typescript2.default.isPropertyAssignment(node) && fields.test(node.name.getText(file2).replace(/["']/g, "")))
|
|
218978
219146
|
return true;
|
|
218979
|
-
return
|
|
219147
|
+
return import_typescript2.default.forEachChild(
|
|
218980
219148
|
node,
|
|
218981
219149
|
(child) => containsAccess(child, fields) ? true : void 0
|
|
218982
219150
|
) ?? false;
|
|
218983
219151
|
};
|
|
218984
219152
|
for (const statement of file2.statements) {
|
|
218985
|
-
if (
|
|
219153
|
+
if (import_typescript2.default.isVariableStatement(statement))
|
|
218986
219154
|
for (const declaration of statement.declarationList.declarations) {
|
|
218987
|
-
if (
|
|
219155
|
+
if (import_typescript2.default.isIdentifier(declaration.name) && declaration.initializer && isProductNode(declaration)) {
|
|
218988
219156
|
values.set(declaration.name.text, declaration.initializer);
|
|
218989
219157
|
if (planName.test(declaration.name.text) || containsAccess(declaration.initializer) && containsAccess(
|
|
218990
219158
|
declaration.initializer,
|
|
@@ -218993,50 +219161,54 @@ function declarations(source) {
|
|
|
218993
219161
|
roots.push(declaration.initializer);
|
|
218994
219162
|
}
|
|
218995
219163
|
}
|
|
218996
|
-
if (
|
|
219164
|
+
if (import_typescript2.default.isExportAssignment(statement) && containsAccess(statement.expression))
|
|
218997
219165
|
roots.push(statement.expression);
|
|
218998
219166
|
}
|
|
218999
219167
|
function read(node, depth = 0, locals = /* @__PURE__ */ new Map()) {
|
|
219000
219168
|
if (depth > 30) throw unsupported();
|
|
219001
219169
|
const next = (value) => read(value, depth + 1, locals);
|
|
219002
|
-
if (
|
|
219170
|
+
if (import_typescript2.default.isAsExpression(node) || import_typescript2.default.isSatisfiesExpression(node) || import_typescript2.default.isParenthesizedExpression(node))
|
|
219003
219171
|
return next(node.expression);
|
|
219004
|
-
if (
|
|
219172
|
+
if (import_typescript2.default.isStringLiteral(node) || import_typescript2.default.isNoSubstitutionTemplateLiteral(node))
|
|
219005
219173
|
return node.text;
|
|
219006
|
-
if (
|
|
219007
|
-
if (node.kind ===
|
|
219008
|
-
if (node.kind ===
|
|
219009
|
-
if (
|
|
219174
|
+
if (import_typescript2.default.isNumericLiteral(node)) return Number(node.text.replaceAll("_", ""));
|
|
219175
|
+
if (node.kind === import_typescript2.default.SyntaxKind.TrueKeyword) return true;
|
|
219176
|
+
if (node.kind === import_typescript2.default.SyntaxKind.FalseKeyword) return false;
|
|
219177
|
+
if (import_typescript2.default.isIdentifier(node)) {
|
|
219010
219178
|
if (locals.has(node.text)) return locals.get(node.text);
|
|
219011
219179
|
if (values.has(node.text)) return next(values.get(node.text));
|
|
219012
219180
|
}
|
|
219013
|
-
if (
|
|
219181
|
+
if (import_typescript2.default.isArrayLiteralExpression(node))
|
|
219014
219182
|
return node.elements.flatMap((n) => {
|
|
219015
|
-
if (!
|
|
219183
|
+
if (!import_typescript2.default.isSpreadElement(n)) return [next(n)];
|
|
219016
219184
|
const spread = next(n.expression);
|
|
219017
219185
|
if (!Array.isArray(spread)) throw unsupported();
|
|
219018
219186
|
return spread;
|
|
219019
219187
|
});
|
|
219020
|
-
if (
|
|
219188
|
+
if (import_typescript2.default.isObjectLiteralExpression(node)) {
|
|
219021
219189
|
const result3 = /* @__PURE__ */ Object.create(null);
|
|
219022
219190
|
for (const property of node.properties) {
|
|
219023
|
-
if (
|
|
219191
|
+
if (import_typescript2.default.isSpreadAssignment(property)) {
|
|
219024
219192
|
const spread = object(next(property.expression));
|
|
219025
219193
|
if (!spread) throw unsupported();
|
|
219026
219194
|
Object.assign(result3, spread);
|
|
219027
219195
|
continue;
|
|
219028
219196
|
}
|
|
219029
|
-
if (!
|
|
219197
|
+
if (!import_typescript2.default.isPropertyAssignment(property) || import_typescript2.default.isComputedPropertyName(property.name))
|
|
219030
219198
|
throw unsupported();
|
|
219031
219199
|
const key2 = property.name.getText(file2).replace(/^["']|["']$/g, "");
|
|
219032
219200
|
if (!safeProperty(key2)) throw unsupported();
|
|
219201
|
+
if (featuresOnly && /^(prices|stripePriceId|priceId|stripeProductId|productKey)$/.test(
|
|
219202
|
+
key2
|
|
219203
|
+
))
|
|
219204
|
+
continue;
|
|
219033
219205
|
result3[key2] = next(property.initializer);
|
|
219034
219206
|
}
|
|
219035
219207
|
return result3;
|
|
219036
219208
|
}
|
|
219037
|
-
if (
|
|
219209
|
+
if (import_typescript2.default.isCallExpression(node) && import_typescript2.default.isPropertyAccessExpression(node.expression)) {
|
|
219038
219210
|
const callee = node.expression;
|
|
219039
|
-
if (
|
|
219211
|
+
if (import_typescript2.default.isIdentifier(callee.expression) && callee.expression.text === "Object" && !values.has("Object") && callee.name.text === "fromEntries" && node.arguments.length === 1) {
|
|
219040
219212
|
const entries = next(node.arguments[0]);
|
|
219041
219213
|
if (!Array.isArray(entries) || entries.length > 1e4)
|
|
219042
219214
|
throw unsupported();
|
|
@@ -219051,7 +219223,7 @@ function declarations(source) {
|
|
|
219051
219223
|
if (callee.name.text === "map" && node.arguments.length === 1) {
|
|
219052
219224
|
const array2 = next(callee.expression);
|
|
219053
219225
|
const callback = node.arguments[0];
|
|
219054
|
-
if (!Array.isArray(array2) || array2.length > 1e4 || !
|
|
219226
|
+
if (!Array.isArray(array2) || array2.length > 1e4 || !import_typescript2.default.isArrowFunction(callback) || callback.parameters.length !== 1 || !import_typescript2.default.isIdentifier(callback.parameters[0].name) || import_typescript2.default.isBlock(callback.body))
|
|
219055
219227
|
throw unsupported();
|
|
219056
219228
|
const parameter = callback.parameters[0].name.text;
|
|
219057
219229
|
return array2.map(
|
|
@@ -219067,13 +219239,13 @@ function declarations(source) {
|
|
|
219067
219239
|
}
|
|
219068
219240
|
return roots.map((node) => read(node));
|
|
219069
219241
|
}
|
|
219070
|
-
function extractSetupPricing(source, kind, catalogPrices = []) {
|
|
219242
|
+
function extractSetupPricing(source, kind, catalogPrices = [], options = {}) {
|
|
219071
219243
|
if (source.length > 2e6)
|
|
219072
219244
|
throw new PricingExtractionError(
|
|
219073
219245
|
"needs_review",
|
|
219074
219246
|
"Pricing file exceeds the 2 MB per-file limit."
|
|
219075
219247
|
);
|
|
219076
|
-
const documents = kind === "typescript" ? declarations(source) : kind === "json" ? [JSON.parse(source)] : [
|
|
219248
|
+
const documents = kind === "typescript" ? declarations(source, options.featuresOnly) : kind === "json" ? [JSON.parse(source)] : [
|
|
219077
219249
|
(0, import_yaml.parseDocument)(source, { uniqueKeys: true }).toJS({
|
|
219078
219250
|
maxAliasCount: 10
|
|
219079
219251
|
})
|
|
@@ -219108,13 +219280,19 @@ function extractSetupPricing(source, kind, catalogPrices = []) {
|
|
|
219108
219280
|
for (const [feature, access2] of entries) {
|
|
219109
219281
|
if (!safeKey(feature) || !safeProperty(feature) || !(typeof access2 === "boolean" || access2 === "unlimited" || typeof access2 === "number" && Number.isFinite(access2) && access2 >= 0))
|
|
219110
219282
|
throw unsupported();
|
|
219111
|
-
|
|
219112
|
-
|
|
219113
|
-
|
|
219283
|
+
const previous = features[feature];
|
|
219284
|
+
if (previous !== void 0 && previous !== access2) {
|
|
219285
|
+
if (previous === false || access2 === false || previous !== true && access2 !== true)
|
|
219286
|
+
throw new PricingExtractionError(
|
|
219287
|
+
"needs_review",
|
|
219288
|
+
"Conflicting access values for the same feature."
|
|
219289
|
+
);
|
|
219290
|
+
}
|
|
219291
|
+
features[feature] = access2 === true && previous !== void 0 ? previous : access2;
|
|
219114
219292
|
}
|
|
219115
219293
|
}
|
|
219116
219294
|
const rawPrices = Array.isArray(row.prices) ? row.prices : object(row.prices) ? Object.values(row.prices) : row.stripePriceId ?? row.priceId ? [{ key: row.stripePriceId ?? row.priceId }] : [];
|
|
219117
|
-
const prices = rawPrices.map((value2) => {
|
|
219295
|
+
const prices = (options.featuresOnly ? [] : rawPrices).map((value2) => {
|
|
219118
219296
|
const price = typeof value2 === "string" ? value2 : object(value2)?.key ?? object(value2)?.stripePriceId;
|
|
219119
219297
|
if (typeof price === "string" && /^price_[A-Za-z0-9]+$/.test(price))
|
|
219120
219298
|
return { key: price };
|
|
@@ -219145,6 +219323,18 @@ function extractSetupPricing(source, kind, catalogPrices = []) {
|
|
|
219145
219323
|
plans.set(key2, plan);
|
|
219146
219324
|
}
|
|
219147
219325
|
}
|
|
219326
|
+
const featureModels = /* @__PURE__ */ new Map();
|
|
219327
|
+
for (const plan of plans.values())
|
|
219328
|
+
for (const [key2, value] of Object.entries(plan.features)) {
|
|
219329
|
+
const models = featureModels.get(key2) ?? /* @__PURE__ */ new Set();
|
|
219330
|
+
models.add(typeof value === "boolean" ? "boolean" : "limit");
|
|
219331
|
+
featureModels.set(key2, models);
|
|
219332
|
+
}
|
|
219333
|
+
if ([...featureModels.values()].some((models) => models.size > 1))
|
|
219334
|
+
throw new PricingExtractionError(
|
|
219335
|
+
"needs_review",
|
|
219336
|
+
"A feature mixes capability and quota values across plans. Review its access model; no conversion was guessed."
|
|
219337
|
+
);
|
|
219148
219338
|
if (!plans.size)
|
|
219149
219339
|
throw new PricingExtractionError(
|
|
219150
219340
|
"no_declarations",
|
|
@@ -219158,12 +219348,374 @@ function extractSetupPricing(source, kind, catalogPrices = []) {
|
|
|
219158
219348
|
return [...plans.values()];
|
|
219159
219349
|
}
|
|
219160
219350
|
|
|
219161
|
-
// src/
|
|
219162
|
-
|
|
219351
|
+
// src/plan-analysis.ts
|
|
219352
|
+
var import_typescript3 = __toESM(require_typescript(), 1);
|
|
219353
|
+
function effect(node) {
|
|
219354
|
+
let result3 = node.kind === import_typescript3.default.SyntaxKind.FalseKeyword ? "deny" : null;
|
|
219355
|
+
const visit = (child) => {
|
|
219356
|
+
if (child !== node && (import_typescript3.default.isFunctionLike(child) || !isProductNode(child)))
|
|
219357
|
+
return;
|
|
219358
|
+
if (import_typescript3.default.isReturnStatement(child)) {
|
|
219359
|
+
if (child.expression?.kind === import_typescript3.default.SyntaxKind.FalseKeyword || child.expression?.kind === import_typescript3.default.SyntaxKind.NullKeyword)
|
|
219360
|
+
result3 = "deny";
|
|
219361
|
+
}
|
|
219362
|
+
if (import_typescript3.default.isThrowStatement(child)) {
|
|
219363
|
+
if (/upgrade|paywall|forbidden|entitlement|feature.+(?:denied|unavailable)|plan.+(?:required|does not|cannot)|quota|limit exceeded/i.test(
|
|
219364
|
+
child.expression.getText()
|
|
219365
|
+
))
|
|
219366
|
+
result3 = "deny";
|
|
219367
|
+
}
|
|
219368
|
+
if (import_typescript3.default.isCallExpression(child)) {
|
|
219369
|
+
const name = import_typescript3.default.isIdentifier(child.expression) ? child.expression.text : import_typescript3.default.isPropertyAccessExpression(child.expression) ? child.expression.name.text : "";
|
|
219370
|
+
if (/^(forbidden|denyAccess|requireUpgrade|showPaywall|denyFeatureAccess)$/i.test(
|
|
219371
|
+
name
|
|
219372
|
+
))
|
|
219373
|
+
result3 = "deny";
|
|
219374
|
+
if (/^(redirect|navigate|push|replace)$/.test(name) && child.arguments.some(
|
|
219375
|
+
(a) => /upgrade|paywall/.test(literalText(a) ?? "")
|
|
219376
|
+
))
|
|
219377
|
+
result3 = "deny";
|
|
219378
|
+
if (/^(status|sendStatus)$/.test(name) && child.arguments.some((a) => import_typescript3.default.isNumericLiteral(a) && a.text === "403"))
|
|
219379
|
+
result3 = "deny";
|
|
219380
|
+
}
|
|
219381
|
+
if (import_typescript3.default.isPropertyAssignment(child) && child.name.getText().replace(/["']/g, "") === "status" && import_typescript3.default.isNumericLiteral(child.initializer) && child.initializer.text === "403")
|
|
219382
|
+
result3 = "deny";
|
|
219383
|
+
import_typescript3.default.forEachChild(child, visit);
|
|
219384
|
+
};
|
|
219385
|
+
visit(node);
|
|
219386
|
+
return result3;
|
|
219387
|
+
}
|
|
219388
|
+
function conditionalEffect(node) {
|
|
219389
|
+
let conditional = false;
|
|
219390
|
+
const visit = (child) => {
|
|
219391
|
+
if (import_typescript3.default.isFunctionLike(child)) return;
|
|
219392
|
+
if (import_typescript3.default.isIfStatement(child) || import_typescript3.default.isConditionalExpression(child) || import_typescript3.default.isSwitchStatement(child) || import_typescript3.default.isIterationStatement(child, false))
|
|
219393
|
+
conditional = true;
|
|
219394
|
+
import_typescript3.default.forEachChild(child, visit);
|
|
219395
|
+
};
|
|
219396
|
+
visit(node);
|
|
219397
|
+
return conditional;
|
|
219398
|
+
}
|
|
219399
|
+
function planRestrictionEvidence(path, source) {
|
|
219400
|
+
if (nonProductPath.test(path)) return [];
|
|
219401
|
+
const file2 = parseApplicationSource(path, source);
|
|
219402
|
+
const results = [];
|
|
219403
|
+
function consequence(node) {
|
|
219404
|
+
const conditionPolarity = (condition, polarity) => {
|
|
219405
|
+
let cursor = condition;
|
|
219406
|
+
let inverted = false;
|
|
219407
|
+
while (import_typescript3.default.isParenthesizedExpression(cursor) || import_typescript3.default.isPrefixUnaryExpression(cursor) && cursor.operator === import_typescript3.default.SyntaxKind.ExclamationToken) {
|
|
219408
|
+
if (import_typescript3.default.isParenthesizedExpression(cursor)) cursor = cursor.expression;
|
|
219409
|
+
else {
|
|
219410
|
+
inverted = !inverted;
|
|
219411
|
+
cursor = cursor.operand;
|
|
219412
|
+
}
|
|
219413
|
+
}
|
|
219414
|
+
if (cursor !== node) return "unclear";
|
|
219415
|
+
return inverted ? polarity === "deny" ? "grant" : polarity === "grant" ? "deny" : "unclear" : polarity;
|
|
219416
|
+
};
|
|
219417
|
+
let child = node;
|
|
219418
|
+
for (let parent = node.parent; parent; child = parent, parent = parent.parent) {
|
|
219419
|
+
if (import_typescript3.default.isIfStatement(parent) && child === parent.expression) {
|
|
219420
|
+
if (effect(parent.thenStatement) === "deny")
|
|
219421
|
+
return {
|
|
219422
|
+
shape: "boolean",
|
|
219423
|
+
polarity: conditionalEffect(parent.thenStatement) ? "unclear" : conditionPolarity(parent.expression, "deny")
|
|
219424
|
+
};
|
|
219425
|
+
if (parent.elseStatement && effect(parent.elseStatement) === "deny")
|
|
219426
|
+
return {
|
|
219427
|
+
shape: "boolean",
|
|
219428
|
+
polarity: conditionalEffect(parent.elseStatement) ? "unclear" : conditionPolarity(parent.expression, "grant")
|
|
219429
|
+
};
|
|
219430
|
+
return null;
|
|
219431
|
+
}
|
|
219432
|
+
if (import_typescript3.default.isConditionalExpression(parent) && child === parent.condition) {
|
|
219433
|
+
const symbol2 = enclosingSymbol(parent);
|
|
219434
|
+
if (/limit|quota|capacity|max(?:imum)?|allowance/i.test(symbol2) && import_typescript3.default.isNumericLiteral(parent.whenTrue) && import_typescript3.default.isNumericLiteral(parent.whenFalse))
|
|
219435
|
+
return { shape: "limit", polarity: "unclear" };
|
|
219436
|
+
if (import_typescript3.default.isJsxElement(parent.whenTrue) || import_typescript3.default.isJsxSelfClosingElement(parent.whenTrue) || import_typescript3.default.isJsxElement(parent.whenFalse) || import_typescript3.default.isJsxSelfClosingElement(parent.whenFalse))
|
|
219437
|
+
return { shape: "presentation", polarity: "unclear" };
|
|
219438
|
+
if (effect(parent.whenTrue) === "deny")
|
|
219439
|
+
return {
|
|
219440
|
+
shape: "boolean",
|
|
219441
|
+
polarity: conditionPolarity(parent.condition, "deny")
|
|
219442
|
+
};
|
|
219443
|
+
if (effect(parent.whenFalse) === "deny")
|
|
219444
|
+
return {
|
|
219445
|
+
shape: "boolean",
|
|
219446
|
+
polarity: conditionPolarity(parent.condition, "grant")
|
|
219447
|
+
};
|
|
219448
|
+
return null;
|
|
219449
|
+
}
|
|
219450
|
+
if (import_typescript3.default.isVariableDeclaration(parent) && child === parent.initializer && import_typescript3.default.isIdentifier(parent.name)) {
|
|
219451
|
+
const scope = parent.parent.parent.parent;
|
|
219452
|
+
if (!scope) return null;
|
|
219453
|
+
let found = null;
|
|
219454
|
+
const search = (candidate) => {
|
|
219455
|
+
if (candidate === parent || candidate !== scope && import_typescript3.default.isFunctionLike(candidate))
|
|
219456
|
+
return;
|
|
219457
|
+
if (import_typescript3.default.isIdentifier(candidate) && candidate.text === parent.name.getText() && candidate.parent !== parent) {
|
|
219458
|
+
let ancestor = candidate.parent;
|
|
219459
|
+
while (ancestor && !import_typescript3.default.isIfStatement(ancestor) && !import_typescript3.default.isStatement(ancestor))
|
|
219460
|
+
ancestor = ancestor.parent;
|
|
219461
|
+
if (ancestor && import_typescript3.default.isIfStatement(ancestor) && effect(ancestor.thenStatement) === "deny")
|
|
219462
|
+
found = { shape: "boolean", polarity: "unclear" };
|
|
219463
|
+
}
|
|
219464
|
+
import_typescript3.default.forEachChild(candidate, search);
|
|
219465
|
+
};
|
|
219466
|
+
search(scope);
|
|
219467
|
+
return found;
|
|
219468
|
+
}
|
|
219469
|
+
if (import_typescript3.default.isStatement(parent) || import_typescript3.default.isFunctionLike(parent)) return null;
|
|
219470
|
+
}
|
|
219471
|
+
return null;
|
|
219472
|
+
}
|
|
219473
|
+
const visit = (node) => {
|
|
219474
|
+
if (!isProductNode(node)) return;
|
|
219475
|
+
if (import_typescript3.default.isBinaryExpression(node) && [
|
|
219476
|
+
import_typescript3.default.SyntaxKind.EqualsEqualsEqualsToken,
|
|
219477
|
+
import_typescript3.default.SyntaxKind.EqualsEqualsToken,
|
|
219478
|
+
import_typescript3.default.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
219479
|
+
import_typescript3.default.SyntaxKind.ExclamationEqualsToken
|
|
219480
|
+
].includes(node.operatorToken.kind)) {
|
|
219481
|
+
const left = node.left.getText(file2);
|
|
219482
|
+
if (/plan|tier|subscription|package/i.test(left)) {
|
|
219483
|
+
const right = node.right;
|
|
219484
|
+
const literal2 = literalText(right);
|
|
219485
|
+
const reference = import_typescript3.default.isIdentifier(right) || import_typescript3.default.isPropertyAccessExpression(right) ? right.getText(file2) : null;
|
|
219486
|
+
const outcome = consequence(node);
|
|
219487
|
+
if (outcome && (literal2 || reference)) {
|
|
219488
|
+
const negated = node.operatorToken.kind === import_typescript3.default.SyntaxKind.ExclamationEqualsEqualsToken || node.operatorToken.kind === import_typescript3.default.SyntaxKind.ExclamationEqualsToken;
|
|
219489
|
+
results.push({
|
|
219490
|
+
...outcome,
|
|
219491
|
+
polarity: negated ? outcome.polarity === "deny" ? "grant" : outcome.polarity === "grant" ? "deny" : "unclear" : outcome.polarity,
|
|
219492
|
+
literal: literal2,
|
|
219493
|
+
reference,
|
|
219494
|
+
negated: node.operatorToken.kind === import_typescript3.default.SyntaxKind.ExclamationEqualsEqualsToken || node.operatorToken.kind === import_typescript3.default.SyntaxKind.ExclamationEqualsToken,
|
|
219495
|
+
line: file2.getLineAndCharacterOfPosition(node.getStart(file2)).line + 1,
|
|
219496
|
+
symbol: enclosingSymbol(node)
|
|
219497
|
+
});
|
|
219498
|
+
}
|
|
219499
|
+
}
|
|
219500
|
+
}
|
|
219501
|
+
import_typescript3.default.forEachChild(node, visit);
|
|
219502
|
+
};
|
|
219503
|
+
visit(file2);
|
|
219504
|
+
return results;
|
|
219505
|
+
}
|
|
219506
|
+
|
|
219507
|
+
// src/plan-constants.ts
|
|
219508
|
+
var import_typescript4 = __toESM(require_typescript(), 1);
|
|
219509
|
+
import { posix } from "node:path";
|
|
219510
|
+
function planConstantResolver(sources) {
|
|
219511
|
+
const modules = /* @__PURE__ */ new Map();
|
|
219512
|
+
for (const [path, source] of sources) {
|
|
219513
|
+
const module = {
|
|
219514
|
+
values: /* @__PURE__ */ new Map(),
|
|
219515
|
+
imports: /* @__PURE__ */ new Map(),
|
|
219516
|
+
exports: /* @__PURE__ */ new Set()
|
|
219517
|
+
};
|
|
219518
|
+
modules.set(path, module);
|
|
219519
|
+
const file2 = parseApplicationSource(path, source);
|
|
219520
|
+
const add = (name, expression, exported) => {
|
|
219521
|
+
module.values.set(name, expression);
|
|
219522
|
+
if (exported) module.exports.add(name.split(".")[0]);
|
|
219523
|
+
};
|
|
219524
|
+
for (const statement of file2.statements) {
|
|
219525
|
+
if (!isProductNode(statement)) continue;
|
|
219526
|
+
const exported = import_typescript4.default.canHaveModifiers(statement) && Boolean(
|
|
219527
|
+
import_typescript4.default.getModifiers(statement)?.some((m) => m.kind === import_typescript4.default.SyntaxKind.ExportKeyword)
|
|
219528
|
+
);
|
|
219529
|
+
if (import_typescript4.default.isImportDeclaration(statement) && import_typescript4.default.isStringLiteral(statement.moduleSpecifier) && !statement.importClause?.isTypeOnly) {
|
|
219530
|
+
const clause = statement.importClause;
|
|
219531
|
+
const source2 = statement.moduleSpecifier.text;
|
|
219532
|
+
if (clause?.name)
|
|
219533
|
+
module.imports.set(clause.name.text, { source: source2, name: "default" });
|
|
219534
|
+
const bindings = clause?.namedBindings;
|
|
219535
|
+
if (bindings && import_typescript4.default.isNamedImports(bindings))
|
|
219536
|
+
for (const element of bindings.elements) {
|
|
219537
|
+
if (!element.isTypeOnly)
|
|
219538
|
+
module.imports.set(element.name.text, {
|
|
219539
|
+
source: source2,
|
|
219540
|
+
name: element.propertyName?.text ?? element.name.text
|
|
219541
|
+
});
|
|
219542
|
+
}
|
|
219543
|
+
else if (bindings && import_typescript4.default.isNamespaceImport(bindings))
|
|
219544
|
+
module.imports.set(bindings.name.text, { source: source2, name: "" });
|
|
219545
|
+
}
|
|
219546
|
+
if (import_typescript4.default.isVariableStatement(statement))
|
|
219547
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
219548
|
+
if (!import_typescript4.default.isIdentifier(declaration.name) || !declaration.initializer || !isProductNode(declaration))
|
|
219549
|
+
continue;
|
|
219550
|
+
let expression = declaration.initializer;
|
|
219551
|
+
while (import_typescript4.default.isAsExpression(expression) || import_typescript4.default.isSatisfiesExpression(expression) || import_typescript4.default.isParenthesizedExpression(expression))
|
|
219552
|
+
expression = expression.expression;
|
|
219553
|
+
const name = declaration.name.text;
|
|
219554
|
+
add(name, expression, exported);
|
|
219555
|
+
if (import_typescript4.default.isObjectLiteralExpression(expression))
|
|
219556
|
+
for (const property of expression.properties) {
|
|
219557
|
+
if (import_typescript4.default.isPropertyAssignment(property) && !import_typescript4.default.isComputedPropertyName(property.name))
|
|
219558
|
+
add(
|
|
219559
|
+
`${name}.${property.name.getText(file2).replace(/["']/g, "")}`,
|
|
219560
|
+
property.initializer,
|
|
219561
|
+
exported
|
|
219562
|
+
);
|
|
219563
|
+
}
|
|
219564
|
+
}
|
|
219565
|
+
if (import_typescript4.default.isEnumDeclaration(statement))
|
|
219566
|
+
for (const member of statement.members) {
|
|
219567
|
+
if (member.initializer && !import_typescript4.default.isComputedPropertyName(member.name))
|
|
219568
|
+
add(
|
|
219569
|
+
`${statement.name.text}.${member.name.getText(file2).replace(/["']/g, "")}`,
|
|
219570
|
+
member.initializer,
|
|
219571
|
+
exported
|
|
219572
|
+
);
|
|
219573
|
+
}
|
|
219574
|
+
if (import_typescript4.default.isExportAssignment(statement))
|
|
219575
|
+
add("default", statement.expression, true);
|
|
219576
|
+
}
|
|
219577
|
+
}
|
|
219578
|
+
const resolve6 = (path, reference, depth = 0) => {
|
|
219579
|
+
if (depth > 12) return null;
|
|
219580
|
+
const module = modules.get(path);
|
|
219581
|
+
if (!module) return null;
|
|
219582
|
+
const value = module.values.get(reference);
|
|
219583
|
+
if (value) {
|
|
219584
|
+
const literal2 = literalText(value);
|
|
219585
|
+
if (literal2 !== null) return literal2.toLowerCase();
|
|
219586
|
+
if (import_typescript4.default.isIdentifier(value) || import_typescript4.default.isPropertyAccessExpression(value))
|
|
219587
|
+
return resolve6(path, value.getText(), depth + 1);
|
|
219588
|
+
return null;
|
|
219589
|
+
}
|
|
219590
|
+
const [first, ...tail] = reference.split(".");
|
|
219591
|
+
const imported = module.imports.get(first);
|
|
219592
|
+
if (!imported?.source.startsWith(".")) return null;
|
|
219593
|
+
const base = posix.normalize(
|
|
219594
|
+
posix.join(posix.dirname(path), imported.source)
|
|
219595
|
+
);
|
|
219596
|
+
if (base.startsWith("../")) return null;
|
|
219597
|
+
const stem = base.replace(/\.[cm]?jsx?$/, "");
|
|
219598
|
+
const target = [
|
|
219599
|
+
base,
|
|
219600
|
+
...[
|
|
219601
|
+
".ts",
|
|
219602
|
+
".tsx",
|
|
219603
|
+
".js",
|
|
219604
|
+
".jsx",
|
|
219605
|
+
".mjs",
|
|
219606
|
+
".cjs",
|
|
219607
|
+
"/index.ts",
|
|
219608
|
+
"/index.js"
|
|
219609
|
+
].map((ext) => stem + ext)
|
|
219610
|
+
].find((candidate) => modules.has(candidate));
|
|
219611
|
+
if (!target) return null;
|
|
219612
|
+
const member = [imported.name, ...tail].filter(Boolean).join(".");
|
|
219613
|
+
if (!modules.get(target).exports.has(member.split(".")[0])) return null;
|
|
219614
|
+
return resolve6(target, member, depth + 1);
|
|
219615
|
+
};
|
|
219616
|
+
return (path, reference, line) => {
|
|
219617
|
+
if (line !== void 0) {
|
|
219618
|
+
const file2 = parseApplicationSource(path, sources.get(path) ?? "");
|
|
219619
|
+
const root = reference.split(".")[0];
|
|
219620
|
+
let shadowed = false;
|
|
219621
|
+
const binds = (name) => import_typescript4.default.isIdentifier(name) ? name.text === root : name.elements.some(
|
|
219622
|
+
(element) => import_typescript4.default.isBindingElement(element) && binds(element.name)
|
|
219623
|
+
);
|
|
219624
|
+
const containsLine = (node) => file2.getLineAndCharacterOfPosition(node.getStart(file2)).line + 1 <= line && file2.getLineAndCharacterOfPosition(node.end).line + 1 >= line;
|
|
219625
|
+
const visit = (node) => {
|
|
219626
|
+
if (!containsLine(node)) return;
|
|
219627
|
+
if (import_typescript4.default.isFunctionLike(node) && node.parameters.some((p) => binds(p.name)))
|
|
219628
|
+
shadowed = true;
|
|
219629
|
+
if (import_typescript4.default.isCatchClause(node) && node.variableDeclaration && binds(node.variableDeclaration.name))
|
|
219630
|
+
shadowed = true;
|
|
219631
|
+
if (import_typescript4.default.isBlock(node)) {
|
|
219632
|
+
for (const statement of node.statements) {
|
|
219633
|
+
if (import_typescript4.default.isVariableStatement(statement) && statement.declarationList.declarations.some((d) => binds(d.name)))
|
|
219634
|
+
shadowed = true;
|
|
219635
|
+
if ((import_typescript4.default.isFunctionDeclaration(statement) || import_typescript4.default.isClassDeclaration(statement)) && statement.name?.text === root)
|
|
219636
|
+
shadowed = true;
|
|
219637
|
+
}
|
|
219638
|
+
}
|
|
219639
|
+
import_typescript4.default.forEachChild(node, visit);
|
|
219640
|
+
};
|
|
219641
|
+
visit(file2);
|
|
219642
|
+
if (shadowed) return null;
|
|
219643
|
+
}
|
|
219644
|
+
return resolve6(path, reference);
|
|
219645
|
+
};
|
|
219646
|
+
}
|
|
219647
|
+
|
|
219648
|
+
// src/discovery-coverage.ts
|
|
219649
|
+
var import_typescript5 = __toESM(require_typescript(), 1);
|
|
219650
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
219651
|
+
import { promisify as promisify3 } from "node:util";
|
|
219652
|
+
import { lstat as lstat2, readFile as readFile3 } from "node:fs/promises";
|
|
219653
|
+
import { join as join2 } from "node:path";
|
|
219654
|
+
|
|
219655
|
+
// src/source-scope.ts
|
|
219163
219656
|
import { execFile as execFile2 } from "node:child_process";
|
|
219164
219657
|
import { promisify as promisify2 } from "node:util";
|
|
219165
|
-
|
|
219166
|
-
|
|
219658
|
+
var exec2 = promisify2(execFile2);
|
|
219659
|
+
async function repositorySourceScope(cwd2) {
|
|
219660
|
+
try {
|
|
219661
|
+
await exec2("git", ["rev-parse", "--is-inside-work-tree"], { cwd: cwd2 });
|
|
219662
|
+
} catch (error51) {
|
|
219663
|
+
const failure = error51;
|
|
219664
|
+
if (failure.code === "ENOENT" || failure.stderr?.includes("not a git repository"))
|
|
219665
|
+
return (_path) => true;
|
|
219666
|
+
throw new Error(
|
|
219667
|
+
"Cannot read repository source scope. Check Git access and retry."
|
|
219668
|
+
);
|
|
219669
|
+
}
|
|
219670
|
+
let stdout;
|
|
219671
|
+
try {
|
|
219672
|
+
({ stdout } = await exec2(
|
|
219673
|
+
"git",
|
|
219674
|
+
[
|
|
219675
|
+
"ls-files",
|
|
219676
|
+
"--cached",
|
|
219677
|
+
"--others",
|
|
219678
|
+
"--exclude-standard",
|
|
219679
|
+
"-z",
|
|
219680
|
+
"--",
|
|
219681
|
+
"."
|
|
219682
|
+
],
|
|
219683
|
+
{
|
|
219684
|
+
cwd: cwd2,
|
|
219685
|
+
maxBuffer: 32 * 1024 * 1024
|
|
219686
|
+
}
|
|
219687
|
+
));
|
|
219688
|
+
} catch {
|
|
219689
|
+
throw new Error(
|
|
219690
|
+
"Cannot list repository sources. Check Git access and retry."
|
|
219691
|
+
);
|
|
219692
|
+
}
|
|
219693
|
+
const included = /* @__PURE__ */ new Set([""]);
|
|
219694
|
+
for (const path of stdout.split("\0").filter(Boolean)) {
|
|
219695
|
+
included.add(path);
|
|
219696
|
+
let end = path.lastIndexOf("/");
|
|
219697
|
+
while (end > 0) {
|
|
219698
|
+
included.add(path.slice(0, end));
|
|
219699
|
+
end = path.lastIndexOf("/", end - 1);
|
|
219700
|
+
}
|
|
219701
|
+
}
|
|
219702
|
+
return (path) => included.has(path);
|
|
219703
|
+
}
|
|
219704
|
+
|
|
219705
|
+
// src/feature-setup.ts
|
|
219706
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
219707
|
+
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
219708
|
+
import {
|
|
219709
|
+
access,
|
|
219710
|
+
mkdir,
|
|
219711
|
+
readFile as readFile2,
|
|
219712
|
+
readdir,
|
|
219713
|
+
rename,
|
|
219714
|
+
rm,
|
|
219715
|
+
lstat,
|
|
219716
|
+
writeFile
|
|
219717
|
+
} from "node:fs/promises";
|
|
219718
|
+
import { dirname, extname, join, relative, resolve as resolve2 } from "node:path";
|
|
219167
219719
|
|
|
219168
219720
|
// node_modules/zod/v4/classic/external.js
|
|
219169
219721
|
var external_exports = {};
|
|
@@ -233679,21 +234231,6 @@ function date4(params) {
|
|
|
233679
234231
|
// node_modules/zod/v4/classic/external.js
|
|
233680
234232
|
config(en_default());
|
|
233681
234233
|
|
|
233682
|
-
// src/feature-setup.ts
|
|
233683
|
-
var import_yaml2 = __toESM(require_dist(), 1);
|
|
233684
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
233685
|
-
import {
|
|
233686
|
-
access,
|
|
233687
|
-
mkdir,
|
|
233688
|
-
readFile as readFile2,
|
|
233689
|
-
readdir,
|
|
233690
|
-
rename,
|
|
233691
|
-
rm,
|
|
233692
|
-
lstat,
|
|
233693
|
-
writeFile
|
|
233694
|
-
} from "node:fs/promises";
|
|
233695
|
-
import { dirname, extname, join, relative, resolve as resolve2 } from "node:path";
|
|
233696
|
-
|
|
233697
234234
|
// src/runtime-contracts.js
|
|
233698
234235
|
import { createHash } from "node:crypto";
|
|
233699
234236
|
var DIRECT_API_AGENT_HANDOFF_VERSION = "2026-06-28.1";
|
|
@@ -234379,9 +234916,9 @@ function boundedTraitValue(value) {
|
|
|
234379
234916
|
}
|
|
234380
234917
|
function pickAccountTraits(records, keys = accountTraitKeys) {
|
|
234381
234918
|
const traits = {};
|
|
234382
|
-
for (const
|
|
234919
|
+
for (const record3 of records) {
|
|
234383
234920
|
for (const key2 of keys) {
|
|
234384
|
-
const value = boundedTraitValue(
|
|
234921
|
+
const value = boundedTraitValue(record3[key2]);
|
|
234385
234922
|
if (value !== void 0) traits[key2] = value;
|
|
234386
234923
|
}
|
|
234387
234924
|
}
|
|
@@ -235114,8 +235651,8 @@ function collectSemanticCandidates(payload, source) {
|
|
|
235114
235651
|
records.push({ path: "event.properties", record: objectValue2(wrappedEvent.properties) });
|
|
235115
235652
|
}
|
|
235116
235653
|
return records.flatMap(
|
|
235117
|
-
({ path, record:
|
|
235118
|
-
const value = nonEmptyString(
|
|
235654
|
+
({ path, record: record3 }) => ["semantic_type", "event_type"].flatMap((field) => {
|
|
235655
|
+
const value = nonEmptyString(record3[field]);
|
|
235119
235656
|
return value ? [{ path: path ? `${path}.${field}` : field, value }] : [];
|
|
235120
235657
|
})
|
|
235121
235658
|
);
|
|
@@ -235840,6 +236377,7 @@ var featureBindingKinds = [
|
|
|
235840
236377
|
"discovery"
|
|
235841
236378
|
];
|
|
235842
236379
|
var defaultRoots = [
|
|
236380
|
+
".",
|
|
235843
236381
|
"app",
|
|
235844
236382
|
"src",
|
|
235845
236383
|
"lib",
|
|
@@ -235848,10 +236386,13 @@ var defaultRoots = [
|
|
|
235848
236386
|
"apps",
|
|
235849
236387
|
"packages",
|
|
235850
236388
|
"server",
|
|
235851
|
-
"client"
|
|
236389
|
+
"client",
|
|
236390
|
+
"config"
|
|
235852
236391
|
];
|
|
235853
236392
|
var ignoredSegments = /* @__PURE__ */ new Set([
|
|
235854
236393
|
".claude",
|
|
236394
|
+
".agents",
|
|
236395
|
+
".codex",
|
|
235855
236396
|
".git",
|
|
235856
236397
|
".next",
|
|
235857
236398
|
".output",
|
|
@@ -235961,7 +236502,8 @@ async function exists(path) {
|
|
|
235961
236502
|
}
|
|
235962
236503
|
function allowedPath(path, excludes) {
|
|
235963
236504
|
const normalized = path.replaceAll("\\", "/");
|
|
235964
|
-
if (sensitivePathPattern.test(normalized)
|
|
236505
|
+
if (sensitivePathPattern.test(normalized) || nonProductPath.test(normalized))
|
|
236506
|
+
return false;
|
|
235965
236507
|
const segments = normalized.split("/");
|
|
235966
236508
|
if (segments.some(
|
|
235967
236509
|
(segment) => ignoredSegments.has(segment) || /^\.next(?:[-.]|$)/.test(segment)
|
|
@@ -235973,6 +236515,7 @@ function allowedPath(path, excludes) {
|
|
|
235973
236515
|
);
|
|
235974
236516
|
}
|
|
235975
236517
|
async function walkFiles(input) {
|
|
236518
|
+
const inRepository = await repositorySourceScope(input.cwd);
|
|
235976
236519
|
const files = [];
|
|
235977
236520
|
const queue = input.roots.map((root) => resolve2(input.cwd, root));
|
|
235978
236521
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -235982,7 +236525,8 @@ async function walkFiles(input) {
|
|
|
235982
236525
|
if (seen.has(current)) continue;
|
|
235983
236526
|
seen.add(current);
|
|
235984
236527
|
const relativePath = safeRelative(input.cwd, current);
|
|
235985
|
-
if (!relativePath || !allowedPath(relativePath, input.excludes))
|
|
236528
|
+
if (relativePath === null && current !== resolve2(input.cwd) || !inRepository(relativePath ?? "") || !allowedPath(relativePath ?? "", input.excludes))
|
|
236529
|
+
continue;
|
|
235986
236530
|
let currentStat;
|
|
235987
236531
|
try {
|
|
235988
236532
|
currentStat = await lstat(current);
|
|
@@ -235994,7 +236538,9 @@ async function walkFiles(input) {
|
|
|
235994
236538
|
entries.sort((left, right) => left.name.localeCompare(right.name)).forEach((entry) => queue.push(join(current, entry.name)));
|
|
235995
236539
|
continue;
|
|
235996
236540
|
}
|
|
235997
|
-
if (currentStat.isFile() && supportedExtensions.has(extname(current)))
|
|
236541
|
+
if (currentStat.isFile() && (supportedExtensions.has(extname(current)) || /\.(json|ya?ml)$/.test(current) && /(?:^|[\/._-])(plans?|pricing|entitlements?|catalog|tiers?|features|quotas)(?:[._-]|$)/i.test(
|
|
236542
|
+
relativePath ?? ""
|
|
236543
|
+
))) {
|
|
235998
236544
|
if (currentStat.size > 2e6) {
|
|
235999
236545
|
oversizedFileCount++;
|
|
236000
236546
|
continue;
|
|
@@ -236004,102 +236550,80 @@ async function walkFiles(input) {
|
|
|
236004
236550
|
}
|
|
236005
236551
|
return { files: files.sort(), oversizedFileCount };
|
|
236006
236552
|
}
|
|
236007
|
-
function nearestSymbol(lines, lineIndex) {
|
|
236008
|
-
for (let index = lineIndex; index >= Math.max(0, lineIndex - 20); index -= 1) {
|
|
236009
|
-
const match = lines[index]?.match(
|
|
236010
|
-
/(?:export\s+)?(?:async\s+)?(?:function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/
|
|
236011
|
-
);
|
|
236012
|
-
if (match?.[1]) return match[1];
|
|
236013
|
-
}
|
|
236014
|
-
return "module";
|
|
236015
|
-
}
|
|
236016
236553
|
function candidatesFromFile(path, source) {
|
|
236017
|
-
const
|
|
236018
|
-
|
|
236019
|
-
|
|
236020
|
-
|
|
236021
|
-
|
|
236022
|
-
|
|
236023
|
-
|
|
236024
|
-
|
|
236025
|
-
|
|
236026
|
-
|
|
236027
|
-
|
|
236028
|
-
|
|
236029
|
-
|
|
236030
|
-
|
|
236031
|
-
|
|
236032
|
-
|
|
236033
|
-
|
|
236034
|
-
|
|
236035
|
-
|
|
236036
|
-
|
|
236037
|
-
|
|
236038
|
-
|
|
236039
|
-
|
|
236040
|
-
|
|
236041
|
-
|
|
236042
|
-
|
|
236043
|
-
|
|
236044
|
-
|
|
236045
|
-
accessModel: "boolean",
|
|
236046
|
-
bindingKind: "funnel_trigger",
|
|
236047
|
-
confidenceBasisPoints: 8900,
|
|
236048
|
-
pattern: /(?:deniedFeatureKey|featureDenied|onFeatureDenied)\s*[:=(]\s*["'`]([a-z][a-z0-9_.-]{1,79})["'`]/g,
|
|
236049
|
-
rationale: "Denied-access Funnel trigger",
|
|
236050
|
-
unit: null
|
|
236051
|
-
},
|
|
236052
|
-
{
|
|
236053
|
-
accessModel: "boolean",
|
|
236054
|
-
bindingKind: "discovery",
|
|
236055
|
-
confidenceBasisPoints: 7600,
|
|
236056
|
-
pattern: /(?:featureKey\s*[:=]\s*|(?:FEATURES?|ENTITLEMENTS?)\s*(?:=|:)\s*(?:\{|\[)[^\n]*?)["'`]([a-z][a-z0-9_.-]{1,79})["'`]/g,
|
|
236057
|
-
rationale: "Feature or entitlement constant",
|
|
236058
|
-
unit: null
|
|
236554
|
+
const named = namedFeatureEvidence(path, source).map(
|
|
236555
|
+
(evidence) => {
|
|
236556
|
+
const fingerprint3 = sha256(
|
|
236557
|
+
JSON.stringify([
|
|
236558
|
+
path,
|
|
236559
|
+
evidence.key,
|
|
236560
|
+
evidence.model,
|
|
236561
|
+
evidence.line,
|
|
236562
|
+
evidence.symbol
|
|
236563
|
+
])
|
|
236564
|
+
);
|
|
236565
|
+
return {
|
|
236566
|
+
accessModel: evidence.model,
|
|
236567
|
+
bindingIdentityFingerprint: sha256(
|
|
236568
|
+
JSON.stringify([evidence.kind, evidence.key, evidence.symbol])
|
|
236569
|
+
),
|
|
236570
|
+
bindingKind: evidence.kind,
|
|
236571
|
+
confidenceBasisPoints: evidence.kind === "server_enforcement" ? 9800 : 9e3,
|
|
236572
|
+
file: path,
|
|
236573
|
+
fingerprint: fingerprint3,
|
|
236574
|
+
id: fingerprint3.slice(0, 12),
|
|
236575
|
+
line: evidence.line,
|
|
236576
|
+
name: displayName(evidence.key),
|
|
236577
|
+
rationale: evidence.reason,
|
|
236578
|
+
suggestedKey: evidence.key,
|
|
236579
|
+
symbol: evidence.symbol,
|
|
236580
|
+
unit: null
|
|
236581
|
+
};
|
|
236059
236582
|
}
|
|
236060
|
-
|
|
236061
|
-
|
|
236062
|
-
|
|
236063
|
-
|
|
236064
|
-
|
|
236065
|
-
|
|
236066
|
-
|
|
236067
|
-
|
|
236068
|
-
|
|
236069
|
-
|
|
236070
|
-
|
|
236071
|
-
|
|
236072
|
-
|
|
236073
|
-
|
|
236074
|
-
|
|
236075
|
-
|
|
236076
|
-
|
|
236077
|
-
|
|
236078
|
-
|
|
236079
|
-
|
|
236080
|
-
|
|
236081
|
-
|
|
236082
|
-
|
|
236083
|
-
|
|
236084
|
-
|
|
236085
|
-
|
|
236086
|
-
|
|
236087
|
-
|
|
236088
|
-
|
|
236089
|
-
|
|
236090
|
-
|
|
236091
|
-
|
|
236092
|
-
|
|
236093
|
-
|
|
236094
|
-
|
|
236095
|
-
|
|
236096
|
-
|
|
236097
|
-
|
|
236098
|
-
|
|
236099
|
-
|
|
236100
|
-
|
|
236101
|
-
|
|
236102
|
-
|
|
236583
|
+
);
|
|
236584
|
+
if (nonProductPath.test(path)) return named;
|
|
236585
|
+
try {
|
|
236586
|
+
const plans = extractSetupPricing(
|
|
236587
|
+
source,
|
|
236588
|
+
/\.json$/.test(path) ? "json" : /\.ya?ml$/.test(path) ? "yaml" : "typescript",
|
|
236589
|
+
[],
|
|
236590
|
+
{ featuresOnly: true }
|
|
236591
|
+
);
|
|
236592
|
+
const features = /* @__PURE__ */ new Map();
|
|
236593
|
+
for (const plan of plans)
|
|
236594
|
+
for (const [key2, value] of Object.entries(plan.features)) {
|
|
236595
|
+
const model = typeof value === "boolean" ? "boolean" : "limit";
|
|
236596
|
+
if (features.get(key2) === "limit" && model === "boolean") continue;
|
|
236597
|
+
features.set(key2, model);
|
|
236598
|
+
}
|
|
236599
|
+
for (const [key2, model] of features) {
|
|
236600
|
+
const line = Math.max(
|
|
236601
|
+
1,
|
|
236602
|
+
source.slice(0, Math.max(0, source.indexOf(key2))).split(/\r?\n/).length
|
|
236603
|
+
);
|
|
236604
|
+
const fingerprint3 = sha256(
|
|
236605
|
+
JSON.stringify([path, key2, model, "plan_catalog"])
|
|
236606
|
+
);
|
|
236607
|
+
named.push({
|
|
236608
|
+
accessModel: model,
|
|
236609
|
+
bindingIdentityFingerprint: fingerprint3,
|
|
236610
|
+
bindingKind: "discovery",
|
|
236611
|
+
confidenceBasisPoints: 9900,
|
|
236612
|
+
file: path,
|
|
236613
|
+
fingerprint: fingerprint3,
|
|
236614
|
+
id: fingerprint3.slice(0, 12),
|
|
236615
|
+
line,
|
|
236616
|
+
name: displayName(key2),
|
|
236617
|
+
rationale: model === "limit" ? "Usage limit declared in application plans" : "Feature access declared in application plans",
|
|
236618
|
+
suggestedKey: key2,
|
|
236619
|
+
symbol: "plan_catalog",
|
|
236620
|
+
unit: null
|
|
236621
|
+
});
|
|
236622
|
+
}
|
|
236623
|
+
} catch (error51) {
|
|
236624
|
+
if (!(error51 instanceof PricingExtractionError)) throw error51;
|
|
236625
|
+
}
|
|
236626
|
+
return named;
|
|
236103
236627
|
}
|
|
236104
236628
|
async function detectFramework(cwd2, requestedRoots) {
|
|
236105
236629
|
let packageJson = {};
|
|
@@ -236394,7 +236918,7 @@ async function runFeatureSetup(input) {
|
|
|
236394
236918
|
const existingManifestSource = await readable(
|
|
236395
236919
|
resolve2(input.cwd, manifestPath)
|
|
236396
236920
|
);
|
|
236397
|
-
const existingManifestValidation = existingManifestSource ? parseFeatureManifestSource(existingManifestSource) : null;
|
|
236921
|
+
const existingManifestValidation = existingManifestSource && !input.freshDiscovery ? parseFeatureManifestSource(existingManifestSource) : null;
|
|
236398
236922
|
if (existingManifestValidation && !existingManifestValidation.ok) {
|
|
236399
236923
|
throw new Error(
|
|
236400
236924
|
`The existing ${manifestPath} is invalid. Run \`saasfunnels catalog validate\` and repair it before setup.`
|
|
@@ -236441,7 +236965,7 @@ async function runFeatureSetup(input) {
|
|
|
236441
236965
|
candidates,
|
|
236442
236966
|
accepted,
|
|
236443
236967
|
input.mappings ?? {},
|
|
236444
|
-
existingManifestValidation?.manifest
|
|
236968
|
+
input.freshDiscovery ? void 0 : existingManifestValidation?.manifest
|
|
236445
236969
|
);
|
|
236446
236970
|
const validation = validateFeatureCatalogManifest(manifest);
|
|
236447
236971
|
const manifestContents = (0, import_yaml2.stringify)(manifest, {
|
|
@@ -236496,7 +237020,7 @@ async function runFeatureSetup(input) {
|
|
|
236496
237020
|
(edit) => changedEdits.some((changed) => changed.id === edit.id)
|
|
236497
237021
|
);
|
|
236498
237022
|
changeRecordPath = ".saasfunnels/feature-installation.json";
|
|
236499
|
-
const
|
|
237023
|
+
const record3 = {
|
|
236500
237024
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
236501
237025
|
environment: input.environment,
|
|
236502
237026
|
files: changedEdits.map((edit) => ({
|
|
@@ -236513,7 +237037,7 @@ async function runFeatureSetup(input) {
|
|
|
236513
237037
|
path: change.path
|
|
236514
237038
|
})),
|
|
236515
237039
|
{
|
|
236516
|
-
contents: `${JSON.stringify(
|
|
237040
|
+
contents: `${JSON.stringify(record3, null, 2)}
|
|
236517
237041
|
`,
|
|
236518
237042
|
path: changeRecordPath
|
|
236519
237043
|
}
|
|
@@ -236651,9 +237175,98 @@ Applied reviewed edits. Change record: ${result3.changeRecordPath}` : result3.id
|
|
|
236651
237175
|
].join("\n");
|
|
236652
237176
|
}
|
|
236653
237177
|
|
|
237178
|
+
// src/discovery-coverage.ts
|
|
237179
|
+
var exec3 = promisify3(execFile3);
|
|
237180
|
+
async function discoverCoverageLimitations(cwd2) {
|
|
237181
|
+
const { stdout } = await exec3(
|
|
237182
|
+
"git",
|
|
237183
|
+
["ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", "."],
|
|
237184
|
+
{ cwd: cwd2, maxBuffer: 32 * 1024 * 1024 }
|
|
237185
|
+
);
|
|
237186
|
+
const files = [...new Set(stdout.split("\0").filter(Boolean))].sort();
|
|
237187
|
+
if (files.length > 5e4)
|
|
237188
|
+
throw new Error(
|
|
237189
|
+
"Feature discovery needs attention: more than 50,000 repository files. Narrow the application repository before scanning."
|
|
237190
|
+
);
|
|
237191
|
+
const sources = /* @__PURE__ */ new Map();
|
|
237192
|
+
const results = /* @__PURE__ */ new Map();
|
|
237193
|
+
const record3 = (code, file2) => {
|
|
237194
|
+
const paths = results.get(code) ?? /* @__PURE__ */ new Set();
|
|
237195
|
+
if (paths.size < 12) paths.add(file2);
|
|
237196
|
+
results.set(code, paths);
|
|
237197
|
+
};
|
|
237198
|
+
for (const path of files) {
|
|
237199
|
+
if (path.length > 200 || path.startsWith("/") || path.includes("..") || /[\x00-\x1f]/.test(path) || sensitivePathPattern.test(path) || /secret|credential/i.test(path) || nonProductPath.test(path) || path.split("/").some(
|
|
237200
|
+
(part) => ignoredSegments.has(part) || /^\.next(?:[-.]|$)/.test(part)
|
|
237201
|
+
))
|
|
237202
|
+
continue;
|
|
237203
|
+
const stat = await lstat2(join2(cwd2, path)).catch(() => null);
|
|
237204
|
+
if (!stat?.isFile() || stat.isSymbolicLink()) continue;
|
|
237205
|
+
if (/\.(?:py|rb|go|rs|java|kt|cs|php|ex|exs|scala|swift|vue|svelte)$/.test(
|
|
237206
|
+
path
|
|
237207
|
+
)) {
|
|
237208
|
+
record3("unsupported_language", path);
|
|
237209
|
+
continue;
|
|
237210
|
+
}
|
|
237211
|
+
if (!/\.[cm]?[jt]sx?$/.test(path) || /\.d\.ts$/.test(path) || stat.size > 2e6)
|
|
237212
|
+
continue;
|
|
237213
|
+
const source = await readFile3(join2(cwd2, path), "utf8");
|
|
237214
|
+
sources.set(path, source);
|
|
237215
|
+
const file2 = parseApplicationSource(path, source);
|
|
237216
|
+
const visit = (node) => {
|
|
237217
|
+
if (!isProductNode(node)) return;
|
|
237218
|
+
if ((import_typescript5.default.isVariableDeclaration(node) || import_typescript5.default.isPropertyAssignment(node)) && node.initializer) {
|
|
237219
|
+
const name = node.name.getText(file2).replace(/["'_-]/g, "");
|
|
237220
|
+
let initializer3 = node.initializer;
|
|
237221
|
+
while (import_typescript5.default.isAsExpression(initializer3) || import_typescript5.default.isSatisfiesExpression(initializer3) || import_typescript5.default.isParenthesizedExpression(initializer3))
|
|
237222
|
+
initializer3 = initializer3.expression;
|
|
237223
|
+
const commercialFields = (node2) => {
|
|
237224
|
+
if (import_typescript5.default.isPropertyAssignment(node2) && /^(?:prices?|quotas?|features?|entitlements?|.*Limit|.*Allowance|lookupKey|amount|unit_amount)$/i.test(
|
|
237225
|
+
node2.name.getText(file2).replace(/["']/g, "")
|
|
237226
|
+
))
|
|
237227
|
+
return true;
|
|
237228
|
+
return import_typescript5.default.forEachChild(
|
|
237229
|
+
node2,
|
|
237230
|
+
(child) => commercialFields(child) ? true : void 0
|
|
237231
|
+
) ?? false;
|
|
237232
|
+
};
|
|
237233
|
+
const entitlementObject = /trialfeatures|trialentitlements|externalentitlements/i.test(name) && import_typescript5.default.isObjectLiteralExpression(initializer3) && initializer3.properties.length > 0;
|
|
237234
|
+
if (/(?:addons?|capacitybands?|usagerates?|meteredpricing|trialfeatures|trialentitlements|externalentitlements|seatpricing)/i.test(
|
|
237235
|
+
name
|
|
237236
|
+
) && (import_typescript5.default.isArrayLiteralExpression(initializer3) || import_typescript5.default.isObjectLiteralExpression(initializer3)) && (entitlementObject || commercialFields(initializer3)))
|
|
237237
|
+
record3("commercial_extensions", path);
|
|
237238
|
+
}
|
|
237239
|
+
if (import_typescript5.default.isCallExpression(node)) {
|
|
237240
|
+
const name = import_typescript5.default.isIdentifier(node.expression) ? node.expression.text : import_typescript5.default.isPropertyAccessExpression(node.expression) ? node.expression.name.text : "";
|
|
237241
|
+
if (/^(?:has|check|require|assert|enforce|evaluate|canUse|is)(?:Feature|Entitlement|Quota|UsageLimit)/i.test(
|
|
237242
|
+
name
|
|
237243
|
+
) && !namedFeatureEvidence(path, node.getText(file2)).length)
|
|
237244
|
+
record3("dynamic_access", path);
|
|
237245
|
+
}
|
|
237246
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
237247
|
+
};
|
|
237248
|
+
visit(file2);
|
|
237249
|
+
}
|
|
237250
|
+
const resolvePlanConstant = planConstantResolver(sources);
|
|
237251
|
+
for (const [path, source] of sources) {
|
|
237252
|
+
if (planRestrictionEvidence(path, source).some(
|
|
237253
|
+
(finding) => finding.reference && !resolvePlanConstant(path, finding.reference, finding.line)
|
|
237254
|
+
))
|
|
237255
|
+
record3("dynamic_access", path);
|
|
237256
|
+
}
|
|
237257
|
+
return [...results].map(([code, paths]) => ({ code, files: [...paths] }));
|
|
237258
|
+
}
|
|
237259
|
+
|
|
237260
|
+
// src/setup-run.ts
|
|
237261
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
237262
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
237263
|
+
import { promisify as promisify4 } from "node:util";
|
|
237264
|
+
import { readFile as readFile6, writeFile as writeFile3, mkdir as mkdir3 } from "node:fs/promises";
|
|
237265
|
+
import { join as join5 } from "node:path";
|
|
237266
|
+
|
|
236654
237267
|
// src/plan-sources.ts
|
|
236655
|
-
import { readFile as
|
|
236656
|
-
import { dirname as dirname2, extname as extname2, join as
|
|
237268
|
+
import { readFile as readFile4, readdir as readdir2, lstat as lstat3, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
237269
|
+
import { dirname as dirname2, extname as extname2, join as join3, relative as relative2, resolve as resolve3, sep } from "node:path";
|
|
236657
237270
|
var planSourceApprovalPath = ".saasfunnels/plan-sources.json";
|
|
236658
237271
|
var maxPlanSourceFiles = 12;
|
|
236659
237272
|
var maxPlanSourceCharacters = 512e3;
|
|
@@ -236709,16 +237322,18 @@ async function discoverPlanSourceCandidates(input) {
|
|
|
236709
237322
|
const roots = input.roots ?? [".", "config", ...defaultRoots];
|
|
236710
237323
|
const seen = /* @__PURE__ */ new Set();
|
|
236711
237324
|
const candidates = [];
|
|
237325
|
+
const inRepository = await repositorySourceScope(input.cwd);
|
|
236712
237326
|
const queue = roots.map((root) => resolve3(input.cwd, root));
|
|
236713
237327
|
while (queue.length) {
|
|
236714
237328
|
const current = queue.shift();
|
|
236715
237329
|
if (seen.has(current)) continue;
|
|
236716
237330
|
seen.add(current);
|
|
236717
237331
|
const relativePath = normalize(relative2(input.cwd, current));
|
|
236718
|
-
if (relativePath && !allowed(relativePath, excludes))
|
|
237332
|
+
if (!inRepository(relativePath) || relativePath && !allowed(relativePath, excludes))
|
|
237333
|
+
continue;
|
|
236719
237334
|
let entryStat;
|
|
236720
237335
|
try {
|
|
236721
|
-
entryStat = await
|
|
237336
|
+
entryStat = await lstat3(current);
|
|
236722
237337
|
} catch {
|
|
236723
237338
|
continue;
|
|
236724
237339
|
}
|
|
@@ -236728,7 +237343,7 @@ async function discoverPlanSourceCandidates(input) {
|
|
|
236728
237343
|
for (const entry of entries.sort(
|
|
236729
237344
|
(a, b) => a.name.localeCompare(b.name)
|
|
236730
237345
|
)) {
|
|
236731
|
-
queue.push(
|
|
237346
|
+
queue.push(join3(current, entry.name));
|
|
236732
237347
|
}
|
|
236733
237348
|
continue;
|
|
236734
237349
|
}
|
|
@@ -236742,7 +237357,7 @@ async function discoverPlanSourceCandidates(input) {
|
|
|
236742
237357
|
);
|
|
236743
237358
|
continue;
|
|
236744
237359
|
}
|
|
236745
|
-
const contents = await
|
|
237360
|
+
const contents = await readFile4(current, "utf8");
|
|
236746
237361
|
const hasStripePriceId = stripePriceIdPattern.test(contents);
|
|
236747
237362
|
const named = looksLikePlanSource(relativePath);
|
|
236748
237363
|
const looksPriced = billingIntervalPattern.test(contents) && chargeAmountPattern.test(contents);
|
|
@@ -236765,7 +237380,7 @@ async function discoverPlanSourceCandidates(input) {
|
|
|
236765
237380
|
}
|
|
236766
237381
|
async function readApprovedPlanSources(cwd2) {
|
|
236767
237382
|
try {
|
|
236768
|
-
const raw = await
|
|
237383
|
+
const raw = await readFile4(resolve3(cwd2, planSourceApprovalPath), "utf8");
|
|
236769
237384
|
const parsed = JSON.parse(raw);
|
|
236770
237385
|
return Array.isArray(parsed.files) ? parsed.files.filter(
|
|
236771
237386
|
(value) => typeof value === "string"
|
|
@@ -236812,7 +237427,7 @@ async function buildPlanMappingHandoff(input) {
|
|
|
236812
237427
|
if (!kind) {
|
|
236813
237428
|
throw new Error(`${relativePath} is not a supported plan source type.`);
|
|
236814
237429
|
}
|
|
236815
|
-
const content = await
|
|
237430
|
+
const content = await readFile4(resolve3(input.cwd, relativePath), "utf8");
|
|
236816
237431
|
if (content.length > maxPlanSourceCharacters) {
|
|
236817
237432
|
throw new Error(
|
|
236818
237433
|
`${relativePath} is ${content.length} characters; the limit is ${maxPlanSourceCharacters}.`
|
|
@@ -236828,68 +237443,12 @@ function planMappingRequestKey(input) {
|
|
|
236828
237443
|
}
|
|
236829
237444
|
|
|
236830
237445
|
// src/plan-branches.ts
|
|
236831
|
-
import {
|
|
236832
|
-
import {
|
|
237446
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
237447
|
+
import { readdir as readdir3, readFile as readFile5, lstat as lstat4 } from "node:fs/promises";
|
|
237448
|
+
import { extname as extname3, join as join4, relative as relative3, resolve as resolve4, sep as sep2 } from "node:path";
|
|
236833
237449
|
var maxPlanBranchFileCharacters = 2e6;
|
|
236834
237450
|
var codeExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
236835
237451
|
var nonDefinitionPattern2 = /(^|\/)(__tests__|__mocks__|tests?|specs?|fixtures?|testing|mocks?|mock-data|stories|__stories__)(\/)|\.(test|spec|d|stories)\.[cm]?[jt]sx?$/i;
|
|
236836
|
-
var comparisonPattern = /\b([A-Za-z_$][\w$]*(?:\.[\w$]+)*)\s*(===|==|!==|!=)\s*(?:["'`]([A-Za-z][A-Za-z0-9_ -]{0,39})["'`]|([A-Za-z_$][\w$]*(?:\.[\w$]+){0,2}))/g;
|
|
236837
|
-
var planIdentifierPattern = /(plan|tier|level|subscription|package)/i;
|
|
236838
|
-
var enumOpenPattern = /\b(?:enum|const)\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?\{/;
|
|
236839
|
-
var memberPattern = /^\s*([A-Za-z_$][\w$]*)\s*[:=]\s*["'`]([A-Za-z][A-Za-z0-9_ -]{0,39})["'`]/;
|
|
236840
|
-
var blockClosePattern = /^\s*\}/;
|
|
236841
|
-
var simpleConstPattern = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[\w<>[\]|\s]+)?=\s*["'`]([A-Za-z][A-Za-z0-9_ -]{0,39})["'`]/;
|
|
236842
|
-
var denialPattern = /\b(?:throw|redirect|notFound|forbidden)\b|\breturn\s+(?:null|false|undefined)\b|upgrade|paywall|locked|disabled|403/i;
|
|
236843
|
-
var restrictionNamePattern = /\b(?:needs?|requires?|must)[A-Z_]|\b(?:is|has)?(?:Locked|Blocked|Restricted|Gated|Disabled)|upgrade|paywall/i;
|
|
236844
|
-
var numericBranchPattern = /\?\s*[\d_]+\s*:\s*[\d_]+|:\s*[\d_]+\s*[,;)]/;
|
|
236845
|
-
var renderPattern = /<[A-Z][\w.]*|className=|return\s*\(/;
|
|
236846
|
-
var marketingPattern = /\b(?:upgrade|get started|contact sales|choose|current plan|most popular|per month|per year|\/mo\b|\/yr\b)|\$\{?\d/i;
|
|
236847
|
-
var escapePattern = /\breturn\s+(?:null|false|undefined)\b|\bthrow\b|\bredirect\b|\bnotFound\b/;
|
|
236848
|
-
function collectConstants(lines, into) {
|
|
236849
|
-
let container = null;
|
|
236850
|
-
for (const line of lines) {
|
|
236851
|
-
if (container && blockClosePattern.test(line)) {
|
|
236852
|
-
container = null;
|
|
236853
|
-
continue;
|
|
236854
|
-
}
|
|
236855
|
-
if (!container) {
|
|
236856
|
-
const open = line.match(enumOpenPattern);
|
|
236857
|
-
if (open?.[1]) {
|
|
236858
|
-
container = open[1];
|
|
236859
|
-
continue;
|
|
236860
|
-
}
|
|
236861
|
-
const simple = line.match(simpleConstPattern);
|
|
236862
|
-
if (simple?.[1] && simple[2]) record3(into, simple[1], simple[2]);
|
|
236863
|
-
continue;
|
|
236864
|
-
}
|
|
236865
|
-
const member = line.match(memberPattern);
|
|
236866
|
-
if (member?.[1] && member[2]) {
|
|
236867
|
-
record3(into, `${container}.${member[1]}`, member[2]);
|
|
236868
|
-
record3(into, member[1], member[2]);
|
|
236869
|
-
}
|
|
236870
|
-
}
|
|
236871
|
-
}
|
|
236872
|
-
function record3(into, name, value) {
|
|
236873
|
-
const normalized = value.toLowerCase();
|
|
236874
|
-
if (!into.has(name)) {
|
|
236875
|
-
into.set(name, normalized);
|
|
236876
|
-
return;
|
|
236877
|
-
}
|
|
236878
|
-
if (into.get(name) !== normalized) into.set(name, null);
|
|
236879
|
-
}
|
|
236880
|
-
function resolveConstant(index, reference) {
|
|
236881
|
-
const direct = index.get(reference);
|
|
236882
|
-
if (direct !== void 0) return direct;
|
|
236883
|
-
const segments = reference.split(".");
|
|
236884
|
-
if (segments.length > 1) {
|
|
236885
|
-
const tail = segments.slice(-2).join(".");
|
|
236886
|
-
const viaTail = index.get(tail);
|
|
236887
|
-
if (viaTail !== void 0) return viaTail;
|
|
236888
|
-
const viaMember = index.get(segments[segments.length - 1]);
|
|
236889
|
-
if (viaMember !== void 0) return viaMember;
|
|
236890
|
-
}
|
|
236891
|
-
return void 0;
|
|
236892
|
-
}
|
|
236893
237452
|
function normalize2(path) {
|
|
236894
237453
|
return path.split(sep2).join("/");
|
|
236895
237454
|
}
|
|
@@ -236902,51 +237461,11 @@ function allowed2(relativePath, excludes) {
|
|
|
236902
237461
|
if (nonDefinitionPattern2.test(relativePath)) return false;
|
|
236903
237462
|
return !excludes.some((exclude) => relativePath.startsWith(exclude));
|
|
236904
237463
|
}
|
|
236905
|
-
function nearestSymbol2(lines, lineIndex) {
|
|
236906
|
-
for (let index = lineIndex; index >= Math.max(0, lineIndex - 20); index -= 1) {
|
|
236907
|
-
const match = lines[index]?.match(
|
|
236908
|
-
/(?:export\s+)?(?:async\s+)?(?:function|class|const|let|var)\s+([A-Za-z_$][\w$]*)/
|
|
236909
|
-
);
|
|
236910
|
-
if (match?.[1]) return match[1];
|
|
236911
|
-
}
|
|
236912
|
-
return "module";
|
|
236913
|
-
}
|
|
236914
|
-
function assignedName(line) {
|
|
236915
|
-
return line.match(/(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/)?.[1] ?? null;
|
|
236916
|
-
}
|
|
236917
|
-
function bindingUsages(lines, lineIndex, name) {
|
|
236918
|
-
const reference = new RegExp(`\\b${name}\\b`);
|
|
236919
|
-
const usages = [];
|
|
236920
|
-
lines.forEach((line, index) => {
|
|
236921
|
-
if (index === lineIndex || !reference.test(line)) return;
|
|
236922
|
-
usages.push(line);
|
|
236923
|
-
});
|
|
236924
|
-
return usages.join("\n");
|
|
236925
|
-
}
|
|
236926
|
-
function shapeAt(lines, lineIndex) {
|
|
236927
|
-
const line = lines[lineIndex] ?? "";
|
|
236928
|
-
const window = lines.slice(lineIndex, lineIndex + 4).join("\n");
|
|
236929
|
-
if (numericBranchPattern.test(line)) return "limit";
|
|
236930
|
-
if (renderPattern.test(window) && marketingPattern.test(window) && !escapePattern.test(window)) {
|
|
236931
|
-
return "presentation";
|
|
236932
|
-
}
|
|
236933
|
-
return "boolean";
|
|
236934
|
-
}
|
|
236935
|
-
function polarityAt(lines, lineIndex, negated) {
|
|
236936
|
-
const window = lines.slice(lineIndex, lineIndex + 3).join("\n");
|
|
236937
|
-
const bound = assignedName(lines[lineIndex] ?? "");
|
|
236938
|
-
const named = bound ? restrictionNamePattern.test(bound) : false;
|
|
236939
|
-
const usages = bound ? bindingUsages(lines, lineIndex, bound) : "";
|
|
236940
|
-
if (!named && !denialPattern.test(window) && !denialPattern.test(usages)) {
|
|
236941
|
-
return "unclear";
|
|
236942
|
-
}
|
|
236943
|
-
return negated ? "unclear" : "deny";
|
|
236944
|
-
}
|
|
236945
237464
|
function clusterPlanBranches(branches) {
|
|
236946
237465
|
const clusters = /* @__PURE__ */ new Map();
|
|
236947
237466
|
for (const branch of branches) {
|
|
236948
|
-
const
|
|
236949
|
-
const location =
|
|
237467
|
+
const fullLocation = `${branch.repositoryPath}::${branch.symbol}`;
|
|
237468
|
+
const location = fullLocation.length <= 400 ? fullLocation : `${fullLocation.slice(0, 367)}~${createHash3("sha256").update(fullLocation).digest("hex").slice(0, 32)}`;
|
|
236950
237469
|
const key2 = `${branch.shape}|${location}`;
|
|
236951
237470
|
const existing = clusters.get(key2);
|
|
236952
237471
|
if (existing) existing.push(branch);
|
|
@@ -236977,18 +237496,20 @@ async function discoverPlanBranches(input) {
|
|
|
236977
237496
|
const roots = input.roots ?? [".", ...defaultRoots];
|
|
236978
237497
|
const seen = /* @__PURE__ */ new Set();
|
|
236979
237498
|
const branches = [];
|
|
236980
|
-
const
|
|
237499
|
+
const sources = /* @__PURE__ */ new Map();
|
|
236981
237500
|
const pending = [];
|
|
237501
|
+
const inRepository = await repositorySourceScope(input.cwd);
|
|
236982
237502
|
const queue = roots.map((root) => resolve4(input.cwd, root));
|
|
236983
237503
|
while (queue.length) {
|
|
236984
237504
|
const current = queue.shift();
|
|
236985
237505
|
if (seen.has(current)) continue;
|
|
236986
237506
|
seen.add(current);
|
|
236987
237507
|
const relativePath = normalize2(relative3(input.cwd, current));
|
|
236988
|
-
if (relativePath && !allowed2(relativePath, excludes))
|
|
237508
|
+
if (!inRepository(relativePath) || relativePath && !allowed2(relativePath, excludes))
|
|
237509
|
+
continue;
|
|
236989
237510
|
let entryStat;
|
|
236990
237511
|
try {
|
|
236991
|
-
entryStat = await
|
|
237512
|
+
entryStat = await lstat4(current);
|
|
236992
237513
|
} catch {
|
|
236993
237514
|
continue;
|
|
236994
237515
|
}
|
|
@@ -236998,7 +237519,7 @@ async function discoverPlanBranches(input) {
|
|
|
236998
237519
|
for (const entry of entries.sort(
|
|
236999
237520
|
(a, b) => a.name.localeCompare(b.name)
|
|
237000
237521
|
)) {
|
|
237001
|
-
queue.push(
|
|
237522
|
+
queue.push(join4(current, entry.name));
|
|
237002
237523
|
}
|
|
237003
237524
|
continue;
|
|
237004
237525
|
}
|
|
@@ -237008,30 +237529,15 @@ async function discoverPlanBranches(input) {
|
|
|
237008
237529
|
throw new Error(
|
|
237009
237530
|
"Plan discovery found a source file above the 2 MB per-file limit. Split large source files before retrying."
|
|
237010
237531
|
);
|
|
237011
|
-
const
|
|
237012
|
-
|
|
237013
|
-
|
|
237014
|
-
|
|
237015
|
-
|
|
237016
|
-
while (match = comparisonPattern.exec(line)) {
|
|
237017
|
-
const [, identifier, operator, literal2, reference] = match;
|
|
237018
|
-
if (!planIdentifierPattern.test(identifier ?? "")) continue;
|
|
237019
|
-
if (!literal2 && !reference) continue;
|
|
237020
|
-
pending.push({
|
|
237021
|
-
line: lineIndex + 1,
|
|
237022
|
-
literal: literal2 ?? null,
|
|
237023
|
-
negated: operator.startsWith("!"),
|
|
237024
|
-
polarity: polarityAt(lines, lineIndex, operator.startsWith("!")),
|
|
237025
|
-
reference: reference ?? null,
|
|
237026
|
-
repositoryPath: relativePath,
|
|
237027
|
-
shape: shapeAt(lines, lineIndex),
|
|
237028
|
-
symbol: nearestSymbol2(lines, lineIndex)
|
|
237029
|
-
});
|
|
237030
|
-
}
|
|
237031
|
-
});
|
|
237532
|
+
const source = await readFile5(current, "utf8");
|
|
237533
|
+
sources.set(relativePath, source);
|
|
237534
|
+
for (const evidence of planRestrictionEvidence(relativePath, source)) {
|
|
237535
|
+
pending.push({ ...evidence, repositoryPath: relativePath });
|
|
237536
|
+
}
|
|
237032
237537
|
}
|
|
237538
|
+
const resolveConstant = planConstantResolver(sources);
|
|
237033
237539
|
for (const item of pending) {
|
|
237034
|
-
const value = item.literal ? item.literal.toLowerCase() : resolveConstant(
|
|
237540
|
+
const value = item.literal ? item.literal.toLowerCase() : resolveConstant(item.repositoryPath, item.reference, item.line);
|
|
237035
237541
|
if (!value || !planValues.has(value)) continue;
|
|
237036
237542
|
branches.push({
|
|
237037
237543
|
line: item.line,
|
|
@@ -237048,8 +237554,8 @@ async function discoverPlanBranches(input) {
|
|
|
237048
237554
|
}
|
|
237049
237555
|
|
|
237050
237556
|
// src/setup-run.ts
|
|
237051
|
-
var
|
|
237052
|
-
var hash2 = (text) =>
|
|
237557
|
+
var exec4 = promisify4(execFile4);
|
|
237558
|
+
var hash2 = (text) => createHash4("sha256").update(text).digest("hex");
|
|
237053
237559
|
var contextSchema = external_exports.object({
|
|
237054
237560
|
workspaceId: external_exports.string(),
|
|
237055
237561
|
generation: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
@@ -237121,15 +237627,15 @@ async function runGuidedSetup(options) {
|
|
|
237121
237627
|
"Connect Stripe and create app credentials in Setup first."
|
|
237122
237628
|
);
|
|
237123
237629
|
try {
|
|
237124
|
-
await
|
|
237125
|
-
await
|
|
237630
|
+
await exec4("git", ["rev-parse", "HEAD"], { cwd: options.cwd });
|
|
237631
|
+
await exec4("git", ["remote", "get-url", "origin"], { cwd: options.cwd });
|
|
237126
237632
|
} catch {
|
|
237127
237633
|
throw new Error(
|
|
237128
237634
|
"Repository setup needed: run this command inside your app\u2019s Git repository with an initial commit and an origin remote. No push or production deploy is required for discovery."
|
|
237129
237635
|
);
|
|
237130
237636
|
}
|
|
237131
|
-
const head = (await
|
|
237132
|
-
const diff = (await
|
|
237637
|
+
const head = (await exec4("git", ["rev-parse", "HEAD"], { cwd: options.cwd })).stdout.trim();
|
|
237638
|
+
const diff = (await exec4(
|
|
237133
237639
|
"git",
|
|
237134
237640
|
[
|
|
237135
237641
|
"diff",
|
|
@@ -237142,7 +237648,7 @@ async function runGuidedSetup(options) {
|
|
|
237142
237648
|
],
|
|
237143
237649
|
{ cwd: options.cwd, maxBuffer: 8e6 }
|
|
237144
237650
|
)).stdout;
|
|
237145
|
-
const untracked = (await
|
|
237651
|
+
const untracked = (await exec4("git", ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
237146
237652
|
cwd: options.cwd
|
|
237147
237653
|
})).stdout.split("\0").filter(
|
|
237148
237654
|
(path) => path && !path.startsWith(".saasfunnels/") && !/(^|\/)(\.env|node_modules|dist|\.next)(\/|$|\.)/i.test(path)
|
|
@@ -237154,15 +237660,15 @@ async function runGuidedSetup(options) {
|
|
|
237154
237660
|
const sourceHashes = await Promise.all(
|
|
237155
237661
|
untracked.map(async (path) => [
|
|
237156
237662
|
path,
|
|
237157
|
-
hash2(await
|
|
237663
|
+
hash2(await readFile6(join5(options.cwd, path), "utf8"))
|
|
237158
237664
|
])
|
|
237159
237665
|
);
|
|
237160
|
-
const revision = `${head}:${hash2(JSON.stringify([diff, sourceHashes]))}`;
|
|
237161
|
-
const remote = (await
|
|
237666
|
+
const revision = `${head}:${hash2(JSON.stringify(["discovery-quality-v2", diff, sourceHashes]))}`;
|
|
237667
|
+
const remote = (await exec4("git", ["remote", "get-url", "origin"], { cwd: options.cwd })).stdout.trim();
|
|
237162
237668
|
const repositoryKey = safeRepositoryKey(remote);
|
|
237163
|
-
const statePath =
|
|
237669
|
+
const statePath = join5(options.cwd, ".saasfunnels", "setup-run.json");
|
|
237164
237670
|
if (options.resume) {
|
|
237165
|
-
const saved = JSON.parse(await
|
|
237671
|
+
const saved = JSON.parse(await readFile6(statePath, "utf8"));
|
|
237166
237672
|
if (saved.generation !== context.generation || saved.repositoryKey !== repositoryKey || saved.revision !== revision)
|
|
237167
237673
|
throw new Error(
|
|
237168
237674
|
"The app connection or revision changed. Run setup again without --resume."
|
|
@@ -237185,7 +237691,7 @@ async function runGuidedSetup(options) {
|
|
|
237185
237691
|
repositoryRevisionHash: hash2(revision)
|
|
237186
237692
|
})).run;
|
|
237187
237693
|
if (!run) throw new Error("Setup did not return a run.");
|
|
237188
|
-
await mkdir3(
|
|
237694
|
+
await mkdir3(join5(options.cwd, ".saasfunnels"), { recursive: true });
|
|
237189
237695
|
await writeFile3(
|
|
237190
237696
|
statePath,
|
|
237191
237697
|
JSON.stringify(
|
|
@@ -237220,9 +237726,14 @@ async function runGuidedSetup(options) {
|
|
|
237220
237726
|
});
|
|
237221
237727
|
return heartbeat;
|
|
237222
237728
|
};
|
|
237223
|
-
const send = (evidence) => {
|
|
237729
|
+
const send = (evidence, proposals) => {
|
|
237224
237730
|
const currentStage = stage;
|
|
237225
|
-
if (JSON.stringify({
|
|
237731
|
+
if (JSON.stringify({
|
|
237732
|
+
runId: run.id,
|
|
237733
|
+
stage: currentStage,
|
|
237734
|
+
evidence,
|
|
237735
|
+
...proposals ? { proposals } : {}
|
|
237736
|
+
}).length > 1e6)
|
|
237226
237737
|
throw new Error(
|
|
237227
237738
|
"Feature discovery evidence exceeds the upload size limit. Review the source scope before retrying."
|
|
237228
237739
|
);
|
|
@@ -237230,7 +237741,8 @@ async function runGuidedSetup(options) {
|
|
|
237230
237741
|
const result3 = await request("/api/developer-tools/setup/evidence", {
|
|
237231
237742
|
runId: run.id,
|
|
237232
237743
|
stage: currentStage,
|
|
237233
|
-
evidence
|
|
237744
|
+
evidence,
|
|
237745
|
+
...proposals ? { proposals } : {}
|
|
237234
237746
|
});
|
|
237235
237747
|
if (typeof result3.sequence === "number")
|
|
237236
237748
|
run.sequence = result3.sequence;
|
|
@@ -237250,6 +237762,11 @@ async function runGuidedSetup(options) {
|
|
|
237250
237762
|
void update(progressState, progressFailure).catch(() => {
|
|
237251
237763
|
});
|
|
237252
237764
|
}, 3e4);
|
|
237765
|
+
const limitations = await discoverCoverageLimitations(options.cwd);
|
|
237766
|
+
if (limitations.length)
|
|
237767
|
+
options.progress?.(
|
|
237768
|
+
"Some app behavior needs manual review. Coverage details will appear with your findings in Setup."
|
|
237769
|
+
);
|
|
237253
237770
|
options.progress?.(
|
|
237254
237771
|
run.receipts.features ? "2/4 Features already submitted." : "2/4 Discovering features\u2026"
|
|
237255
237772
|
);
|
|
@@ -237261,9 +237778,11 @@ async function runGuidedSetup(options) {
|
|
|
237261
237778
|
environment: context.environment,
|
|
237262
237779
|
apply: false,
|
|
237263
237780
|
manifestOnly: true,
|
|
237781
|
+
freshDiscovery: true,
|
|
237264
237782
|
accept: ["all"]
|
|
237265
237783
|
});
|
|
237266
|
-
|
|
237784
|
+
const unsupportedOnly = scan.coverage.scannedFiles === 0 && !scan.coverage.byteLimitReached && limitations.some((l) => l.code === "unsupported_language");
|
|
237785
|
+
if (!scan.coverage.complete && !unsupportedOnly) {
|
|
237267
237786
|
const reason = scan.coverage.byteLimitReached ? `${scan.coverage.oversizedFileCount} source file(s) exceed the 2 MB per-file limit. Split large source files before retrying.` : !scan.coverage.scannedFiles ? "No JavaScript or TypeScript app files were found in the source roots. Run from the application folder." : "More than 10,000 feature bindings were found. Review the source scope before retrying.";
|
|
237268
237787
|
throw new Error(
|
|
237269
237788
|
`Feature discovery needs attention: ${reason} Scanned ${scan.coverage.scannedFiles} of ${scan.coverage.matchedFiles} files. Nothing was submitted for this stage.`
|
|
@@ -237272,6 +237791,10 @@ async function runGuidedSetup(options) {
|
|
|
237272
237791
|
options.progress?.(
|
|
237273
237792
|
`Scanned ${scan.scannedFileCount} files; found ${scan.manifest.features.length} feature proposals.`
|
|
237274
237793
|
);
|
|
237794
|
+
if (scan.manifest.features.length > 500)
|
|
237795
|
+
throw new Error(
|
|
237796
|
+
"Feature discovery needs attention: more than 500 features. Narrow the application scope before retrying; nothing was submitted."
|
|
237797
|
+
);
|
|
237275
237798
|
if (!scan.ok)
|
|
237276
237799
|
throw new Error(
|
|
237277
237800
|
"Feature discovery needs attention. Run features setup to review the local findings."
|
|
@@ -237282,7 +237805,17 @@ async function runGuidedSetup(options) {
|
|
|
237282
237805
|
repositoryKey,
|
|
237283
237806
|
repositoryRevision: revision,
|
|
237284
237807
|
discoveryRoots: scan.framework.roots
|
|
237285
|
-
})
|
|
237808
|
+
}),
|
|
237809
|
+
scan.manifest.features.map((feature) => ({
|
|
237810
|
+
featureKey: feature.key,
|
|
237811
|
+
name: feature.name,
|
|
237812
|
+
accessModel: feature.accessModel,
|
|
237813
|
+
reason: feature.evidence.some((e) => e.symbol === "plan_catalog") ? "plan_catalog" : feature.accessModel === "limit" ? "usage_check" : feature.evidence.some(
|
|
237814
|
+
(e) => e.bindingKind === "server_enforcement"
|
|
237815
|
+
) ? "access_check" : feature.evidence.some(
|
|
237816
|
+
(e) => e.bindingKind === "browser_presentation"
|
|
237817
|
+
) ? "visibility_gate" : "denied_access"
|
|
237818
|
+
}))
|
|
237286
237819
|
);
|
|
237287
237820
|
}
|
|
237288
237821
|
stage = "plans";
|
|
@@ -237317,8 +237850,8 @@ async function runGuidedSetup(options) {
|
|
|
237317
237850
|
});
|
|
237318
237851
|
let manual = null;
|
|
237319
237852
|
try {
|
|
237320
|
-
manual = await
|
|
237321
|
-
|
|
237853
|
+
manual = await readFile6(
|
|
237854
|
+
join5(options.cwd, ".saasfunnels/setup-pricing.json"),
|
|
237322
237855
|
"utf8"
|
|
237323
237856
|
);
|
|
237324
237857
|
} catch (error51) {
|
|
@@ -237331,7 +237864,7 @@ async function runGuidedSetup(options) {
|
|
|
237331
237864
|
for (const candidate of sources) {
|
|
237332
237865
|
try {
|
|
237333
237866
|
const plans = extractSetupPricing(
|
|
237334
|
-
manual ?? await
|
|
237867
|
+
manual ?? await readFile6(join5(options.cwd, candidate.path), "utf8"),
|
|
237335
237868
|
candidate.kind,
|
|
237336
237869
|
context.catalogPrices
|
|
237337
237870
|
);
|
|
@@ -237360,7 +237893,7 @@ async function runGuidedSetup(options) {
|
|
|
237360
237893
|
});
|
|
237361
237894
|
if (issues.length) {
|
|
237362
237895
|
await writeFile3(
|
|
237363
|
-
|
|
237896
|
+
join5(options.cwd, ".saasfunnels/setup-review.json"),
|
|
237364
237897
|
JSON.stringify({ stage: "plans", issues }, null, 2) + "\n",
|
|
237365
237898
|
{ mode: 384 }
|
|
237366
237899
|
);
|
|
@@ -237375,7 +237908,11 @@ async function runGuidedSetup(options) {
|
|
|
237375
237908
|
options.progress?.(
|
|
237376
237909
|
`Found ${byKey.size} plan proposals in ${reviewedFiles.length} pricing files.`
|
|
237377
237910
|
);
|
|
237378
|
-
await send({
|
|
237911
|
+
await send({
|
|
237912
|
+
plans: [...byKey.values()],
|
|
237913
|
+
reviewedFiles,
|
|
237914
|
+
...limitations.length ? { limitations } : {}
|
|
237915
|
+
});
|
|
237379
237916
|
}
|
|
237380
237917
|
stage = "branches";
|
|
237381
237918
|
const planValues = [
|
|
@@ -237473,11 +238010,11 @@ async function submitApprovedSetupPlans(input) {
|
|
|
237473
238010
|
}
|
|
237474
238011
|
|
|
237475
238012
|
// src/cli.ts
|
|
237476
|
-
import { mkdir as mkdir4, readFile as
|
|
238013
|
+
import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
|
|
237477
238014
|
|
|
237478
238015
|
// src/identity.ts
|
|
237479
238016
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
237480
|
-
var SAASFUNNELS_CLI_VERSION = "0.3.
|
|
238017
|
+
var SAASFUNNELS_CLI_VERSION = "0.3.4";
|
|
237481
238018
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
237482
238019
|
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.ai";
|
|
237483
238020
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
@@ -237975,7 +238512,7 @@ function featureCatalogMarkdown() {
|
|
|
237975
238512
|
return [
|
|
237976
238513
|
"# SaaSFunnels Feature Catalog Manifest",
|
|
237977
238514
|
"",
|
|
237978
|
-
"The
|
|
238515
|
+
"The local features setup command maintains `.saasfunnels/catalog.yaml`; applying generated changes is Test-only. Guided setup submits structured proposals in the connection's environment after explicit approval, without raw source. Coverage tasks and quota semantics require human review in the current Plans & pricing editor.",
|
|
237979
238516
|
"",
|
|
237980
238517
|
"Required top-level fields:",
|
|
237981
238518
|
"- `schemaVersion: 1`",
|
|
@@ -239303,7 +239840,7 @@ function filesystem(options) {
|
|
|
239303
239840
|
mkdir: async (path, mkdirOptions) => {
|
|
239304
239841
|
await mkdir4(path, mkdirOptions);
|
|
239305
239842
|
},
|
|
239306
|
-
readFile:
|
|
239843
|
+
readFile: readFile7,
|
|
239307
239844
|
writeFile: writeFile4
|
|
239308
239845
|
};
|
|
239309
239846
|
}
|
|
@@ -239341,7 +239878,7 @@ async function readJsonFile(filePath, options) {
|
|
|
239341
239878
|
}
|
|
239342
239879
|
async function commandInit(flags, options) {
|
|
239343
239880
|
const fs = filesystem(options);
|
|
239344
|
-
const configPath =
|
|
239881
|
+
const configPath = join6(cwd(options), ".saasfunnels", "config.json");
|
|
239345
239882
|
const config2 = {
|
|
239346
239883
|
api_base_url: apiBaseUrl2(options, flags),
|
|
239347
239884
|
schema_version: cliSchemaVersion
|
|
@@ -239381,7 +239918,7 @@ async function commandAgentInstall(args, flags, options) {
|
|
|
239381
239918
|
const artifacts = getDeveloperToolAgentHandoffArtifacts({ endpoint });
|
|
239382
239919
|
const artifact = artifacts[target];
|
|
239383
239920
|
const targetPath = flagString(flags, "path") ?? saasFunnelsPublicText(artifact.target_path);
|
|
239384
|
-
const absoluteTarget =
|
|
239921
|
+
const absoluteTarget = join6(cwd(options), targetPath);
|
|
239385
239922
|
const fs = filesystem(options);
|
|
239386
239923
|
await fs.mkdir(dirname3(absoluteTarget), { recursive: true });
|
|
239387
239924
|
await fs.writeFile(
|