@fairfox/polly 0.18.0 → 0.20.0
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 +125 -948
- package/dist/cli/polly.js +25 -4
- package/dist/cli/polly.js.map +3 -3
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +46 -3
- package/dist/src/index.js.map +6 -5
- package/dist/src/shared/lib/resource.d.ts +54 -0
- package/dist/src/shared/lib/resource.js +583 -0
- package/dist/src/shared/lib/resource.js.map +13 -0
- package/dist/src/shared/lib/state.d.ts +1 -0
- package/dist/src/shared/lib/state.js +2 -1
- package/dist/src/shared/lib/state.js.map +3 -3
- package/dist/src/shared/state/app-state.js.map +2 -2
- package/dist/tools/init/templates/pwa/package.json.template +1 -2
- package/dist/tools/verify/specs/docker-compose.yml +1 -1
- package/dist/tools/verify/src/cli.js +269 -23
- package/dist/tools/verify/src/cli.js.map +6 -6
- package/dist/tools/visualize/src/cli.js +164 -8
- package/dist/tools/visualize/src/cli.js.map +3 -3
- package/package.json +5 -5
- package/dist/src/elysia/tla-generator.d.ts +0 -16
- package/dist/tools/verify/specs/verification.config.ts +0 -64
|
@@ -429,13 +429,22 @@ async function generateSubsystemTLA(_subsystemName, subsystem, config, analysis)
|
|
|
429
429
|
temporalConstraints: filteredConfig.tier2.temporalConstraints.filter((tc) => handlerNames.has(tc.before) && handlerNames.has(tc.after))
|
|
430
430
|
};
|
|
431
431
|
}
|
|
432
|
+
const subsystemFieldPatterns = subsystem.state.map((field) => {
|
|
433
|
+
const dotIdx = field.indexOf(".");
|
|
434
|
+
if (dotIdx >= 0) {
|
|
435
|
+
return `${field.substring(0, dotIdx)}.value.${field.substring(dotIdx + 1)}`;
|
|
436
|
+
}
|
|
437
|
+
return `${field}.value`;
|
|
438
|
+
});
|
|
439
|
+
const filteredGlobalStateConstraints = (analysis.globalStateConstraints ?? []).filter((constraint) => subsystemFieldPatterns.some((pattern) => constraint.expression.includes(pattern)));
|
|
432
440
|
const filteredAnalysis = {
|
|
433
441
|
...analysis,
|
|
434
442
|
messageTypes: analysis.messageTypes.filter((mt) => handlerNames.has(mt)),
|
|
435
|
-
handlers: analysis.handlers.filter((h) => handlerNames.has(h.messageType))
|
|
443
|
+
handlers: analysis.handlers.filter((h) => handlerNames.has(h.messageType)),
|
|
444
|
+
globalStateConstraints: filteredGlobalStateConstraints
|
|
436
445
|
};
|
|
437
446
|
const generator = new TLAGenerator;
|
|
438
|
-
return await generator.generate(filteredConfig, filteredAnalysis);
|
|
447
|
+
return await generator.generate(filteredConfig, filteredAnalysis, `UserApp_${_subsystemName}`);
|
|
439
448
|
}
|
|
440
449
|
var TLAValidationError, TLAGenerator;
|
|
441
450
|
var init_tla = __esm(() => {
|
|
@@ -458,6 +467,7 @@ var init_tla = __esm(() => {
|
|
|
458
467
|
resolvedActionNames = new Map;
|
|
459
468
|
tabSymmetryEnabled = false;
|
|
460
469
|
tabCount = 0;
|
|
470
|
+
moduleName = "UserApp";
|
|
461
471
|
constructor(options) {
|
|
462
472
|
this.options = options;
|
|
463
473
|
}
|
|
@@ -467,7 +477,10 @@ var init_tla = __esm(() => {
|
|
|
467
477
|
}
|
|
468
478
|
return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s);
|
|
469
479
|
}
|
|
470
|
-
async generate(config, analysis) {
|
|
480
|
+
async generate(config, analysis, moduleName) {
|
|
481
|
+
if (moduleName) {
|
|
482
|
+
this.moduleName = moduleName;
|
|
483
|
+
}
|
|
471
484
|
this.validateInputs(config, analysis);
|
|
472
485
|
this.extractInvariantsIfEnabled();
|
|
473
486
|
this.generateTemporalPropertiesIfEnabled(analysis);
|
|
@@ -757,7 +770,7 @@ var init_tla = __esm(() => {
|
|
|
757
770
|
}
|
|
758
771
|
}
|
|
759
772
|
addHeader() {
|
|
760
|
-
this.line(
|
|
773
|
+
this.line(`------------------------- MODULE ${this.moduleName} -------------------------`);
|
|
761
774
|
this.line("(*");
|
|
762
775
|
this.line(" Auto-generated TLA+ specification for web extension");
|
|
763
776
|
this.line(" ");
|
|
@@ -1349,32 +1362,86 @@ var init_tla = __esm(() => {
|
|
|
1349
1362
|
if (assignment.value !== null)
|
|
1350
1363
|
return true;
|
|
1351
1364
|
const fieldConfig = state[assignment.field];
|
|
1352
|
-
|
|
1365
|
+
if (!fieldConfig || typeof fieldConfig !== "object")
|
|
1366
|
+
return false;
|
|
1367
|
+
if ("abstract" in fieldConfig && fieldConfig.abstract)
|
|
1368
|
+
return true;
|
|
1369
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable)
|
|
1370
|
+
return true;
|
|
1371
|
+
return !!(("values" in fieldConfig) && fieldConfig.values);
|
|
1353
1372
|
}
|
|
1354
1373
|
mapNullAssignment(assignment, state) {
|
|
1355
1374
|
if (assignment.value !== null)
|
|
1356
1375
|
return assignment;
|
|
1357
1376
|
const fieldConfig = state[assignment.field];
|
|
1358
|
-
if (fieldConfig
|
|
1377
|
+
if (!fieldConfig || typeof fieldConfig !== "object")
|
|
1378
|
+
return assignment;
|
|
1379
|
+
if ("abstract" in fieldConfig && fieldConfig.abstract)
|
|
1380
|
+
return assignment;
|
|
1381
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable)
|
|
1382
|
+
return assignment;
|
|
1383
|
+
if ("values" in fieldConfig && fieldConfig.values) {
|
|
1359
1384
|
const nullValue = fieldConfig.values[fieldConfig.values.length - 1];
|
|
1360
1385
|
return { ...assignment, value: nullValue };
|
|
1361
1386
|
}
|
|
1362
1387
|
return assignment;
|
|
1363
1388
|
}
|
|
1364
1389
|
emitStateUpdates(validAssignments, preconditions) {
|
|
1365
|
-
if (validAssignments.length
|
|
1366
|
-
|
|
1390
|
+
if (validAssignments.length === 0) {
|
|
1391
|
+
if (preconditions.length === 0) {
|
|
1392
|
+
this.line("\\* No state changes in handler");
|
|
1393
|
+
}
|
|
1394
|
+
this.line("/\\ UNCHANGED contextStates");
|
|
1395
|
+
return true;
|
|
1396
|
+
}
|
|
1397
|
+
const ndetAssignments = [];
|
|
1398
|
+
const detAssignments = [];
|
|
1399
|
+
for (const a of validAssignments) {
|
|
1400
|
+
if (typeof a.value === "string" && a.value.startsWith("NDET:")) {
|
|
1401
|
+
ndetAssignments.push({ field: a.field, value: a.value });
|
|
1402
|
+
} else {
|
|
1403
|
+
detAssignments.push(a);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (ndetAssignments.length === 0) {
|
|
1407
|
+
const exceptClauses = detAssignments.map((a) => {
|
|
1367
1408
|
const tlaValue = this.assignmentValueToTLA(a.value);
|
|
1368
1409
|
return `![ctx].${this.sanitizeFieldName(a.field)} = ${tlaValue}`;
|
|
1369
1410
|
});
|
|
1370
1411
|
this.line(`/\\ contextStates' = [contextStates EXCEPT ${exceptClauses.join(", ")}]`);
|
|
1371
1412
|
return false;
|
|
1372
1413
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1414
|
+
this.emitNDETStateUpdates(ndetAssignments, detAssignments);
|
|
1415
|
+
return false;
|
|
1416
|
+
}
|
|
1417
|
+
emitNDETStateUpdates(ndetAssignments, detAssignments) {
|
|
1418
|
+
const detExceptClauses = detAssignments.map((a) => {
|
|
1419
|
+
const tlaValue = this.assignmentValueToTLA(a.value);
|
|
1420
|
+
return `![ctx].${this.sanitizeFieldName(a.field)} = ${tlaValue}`;
|
|
1421
|
+
});
|
|
1422
|
+
const ndetExceptClauses = [];
|
|
1423
|
+
const quantifierOpeners = [];
|
|
1424
|
+
for (const a of ndetAssignments) {
|
|
1425
|
+
const fieldName = this.sanitizeFieldName(a.field);
|
|
1426
|
+
const fieldRef = `contextStates[ctx].${fieldName}`;
|
|
1427
|
+
if (a.value === "NDET:FILTER") {
|
|
1428
|
+
quantifierOpeners.push(`/\\ \\E newLen_${fieldName} \\in 0..Len(${fieldRef}) :`);
|
|
1429
|
+
ndetExceptClauses.push(`![ctx].${fieldName} = SubSeq(@, 1, newLen_${fieldName})`);
|
|
1430
|
+
} else if (a.value === "NDET:MAP") {
|
|
1431
|
+
quantifierOpeners.push(`/\\ \\E mapIdx_${fieldName} \\in 1..Len(${fieldRef}) :`);
|
|
1432
|
+
quantifierOpeners.push(`\\E mapVal_${fieldName} \\in Value :`);
|
|
1433
|
+
ndetExceptClauses.push(`![ctx].${fieldName} = [@ EXCEPT ![mapIdx_${fieldName}] = mapVal_${fieldName}]`);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
for (const opener of quantifierOpeners) {
|
|
1437
|
+
this.line(opener);
|
|
1438
|
+
this.indent++;
|
|
1439
|
+
}
|
|
1440
|
+
const allExceptClauses = [...detExceptClauses, ...ndetExceptClauses];
|
|
1441
|
+
this.line(`/\\ contextStates' = [contextStates EXCEPT ${allExceptClauses.join(", ")}]`);
|
|
1442
|
+
for (const _opener of quantifierOpeners) {
|
|
1443
|
+
this.indent--;
|
|
1375
1444
|
}
|
|
1376
|
-
this.line("/\\ UNCHANGED contextStates");
|
|
1377
|
-
return true;
|
|
1378
1445
|
}
|
|
1379
1446
|
tsExpressionToTLA(expr, isPrimed = false) {
|
|
1380
1447
|
if (!expr || typeof expr !== "string") {
|
|
@@ -1787,6 +1854,9 @@ var init_tla = __esm(() => {
|
|
|
1787
1854
|
return "NULL";
|
|
1788
1855
|
}
|
|
1789
1856
|
if (typeof value === "string") {
|
|
1857
|
+
if (value.startsWith("NDET:")) {
|
|
1858
|
+
throw new Error(`NDET marker "${value}" reached assignmentValueToTLA — should have been partitioned in emitStateUpdates`);
|
|
1859
|
+
}
|
|
1790
1860
|
if (value.startsWith("param:")) {
|
|
1791
1861
|
const paramName = value.substring(6);
|
|
1792
1862
|
return `payload.${this.sanitizeFieldName(paramName)}`;
|
|
@@ -2011,6 +2081,9 @@ var init_tla = __esm(() => {
|
|
|
2011
2081
|
}
|
|
2012
2082
|
tryAbstractType(fieldConfig) {
|
|
2013
2083
|
if ("abstract" in fieldConfig && fieldConfig.abstract === true) {
|
|
2084
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable === true) {
|
|
2085
|
+
return "Value \\union {NULL}";
|
|
2086
|
+
}
|
|
2014
2087
|
return "Value";
|
|
2015
2088
|
}
|
|
2016
2089
|
return null;
|
|
@@ -2178,6 +2251,9 @@ var init_tla = __esm(() => {
|
|
|
2178
2251
|
}
|
|
2179
2252
|
getInitialValue(fieldConfig) {
|
|
2180
2253
|
if ("abstract" in fieldConfig && fieldConfig.abstract === true) {
|
|
2254
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable === true) {
|
|
2255
|
+
return "NULL";
|
|
2256
|
+
}
|
|
2181
2257
|
return '"v1"';
|
|
2182
2258
|
}
|
|
2183
2259
|
if (Array.isArray(fieldConfig)) {
|
|
@@ -2869,7 +2945,7 @@ class ConfigGenerator {
|
|
|
2869
2945
|
this.addHeader();
|
|
2870
2946
|
this.addImports();
|
|
2871
2947
|
this.addExport();
|
|
2872
|
-
this.addStateConfig(analysis.fields);
|
|
2948
|
+
this.addStateConfig(analysis.fields, analysis.resources);
|
|
2873
2949
|
this.addMessagesConfig();
|
|
2874
2950
|
this.addBehaviorConfig();
|
|
2875
2951
|
this.closeExport();
|
|
@@ -2935,7 +3011,7 @@ class ConfigGenerator {
|
|
|
2935
3011
|
this.indent--;
|
|
2936
3012
|
this.line("})");
|
|
2937
3013
|
}
|
|
2938
|
-
addStateConfig(fields) {
|
|
3014
|
+
addStateConfig(fields, resources) {
|
|
2939
3015
|
this.line("state: {");
|
|
2940
3016
|
this.indent++;
|
|
2941
3017
|
for (let i = 0;i < fields.length; i++) {
|
|
@@ -2947,6 +3023,18 @@ class ConfigGenerator {
|
|
|
2947
3023
|
}
|
|
2948
3024
|
this.addFieldConfig(field);
|
|
2949
3025
|
}
|
|
3026
|
+
if (resources && resources.length > 0) {
|
|
3027
|
+
if (fields.length > 0) {
|
|
3028
|
+
this.line("");
|
|
3029
|
+
}
|
|
3030
|
+
this.line("// ─── $resource async lifecycle fields (auto-generated) ───");
|
|
3031
|
+
for (const resource of resources) {
|
|
3032
|
+
this.line("");
|
|
3033
|
+
this.line(`// ${resource.name}: fetch lifecycle status`);
|
|
3034
|
+
this.line(`"${resource.name}_status": { type: "enum", values: ["idle", "loading", "success", "error"] },`);
|
|
3035
|
+
this.line(`"${resource.name}_error": { type: "boolean" },`);
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
2950
3038
|
this.indent--;
|
|
2951
3039
|
this.line("},");
|
|
2952
3040
|
this.line("");
|
|
@@ -4277,12 +4365,13 @@ class HandlerExtractor {
|
|
|
4277
4365
|
const stateConstraints = [];
|
|
4278
4366
|
const globalStateConstraints = [];
|
|
4279
4367
|
const verifiedStates = [];
|
|
4368
|
+
const resources = [];
|
|
4280
4369
|
this.warnings = [];
|
|
4281
4370
|
const allSourceFiles = this.project.getSourceFiles();
|
|
4282
4371
|
const entryPoints = allSourceFiles.filter((f) => this.isWithinPackage(f.getFilePath()));
|
|
4283
4372
|
this.debugLogSourceFiles(allSourceFiles, entryPoints);
|
|
4284
4373
|
for (const entryPoint of entryPoints) {
|
|
4285
|
-
this.analyzeFileAndImports(entryPoint, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates);
|
|
4374
|
+
this.analyzeFileAndImports(entryPoint, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources);
|
|
4286
4375
|
}
|
|
4287
4376
|
if (verifiedStates.length > 0) {
|
|
4288
4377
|
if (process.env["POLLY_DEBUG"]) {
|
|
@@ -4314,10 +4403,11 @@ class HandlerExtractor {
|
|
|
4314
4403
|
stateConstraints,
|
|
4315
4404
|
globalStateConstraints,
|
|
4316
4405
|
verifiedStates,
|
|
4406
|
+
resources,
|
|
4317
4407
|
warnings: this.warnings
|
|
4318
4408
|
};
|
|
4319
4409
|
}
|
|
4320
|
-
analyzeFileAndImports(sourceFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates) {
|
|
4410
|
+
analyzeFileAndImports(sourceFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources) {
|
|
4321
4411
|
const filePath = sourceFile.getFilePath();
|
|
4322
4412
|
if (this.analyzedFiles.has(filePath)) {
|
|
4323
4413
|
return;
|
|
@@ -4335,6 +4425,20 @@ class HandlerExtractor {
|
|
|
4335
4425
|
globalStateConstraints.push(...fileGlobalConstraints);
|
|
4336
4426
|
const fileVerifiedStates = this.extractVerifiedStatesFromFile(sourceFile);
|
|
4337
4427
|
verifiedStates.push(...fileVerifiedStates);
|
|
4428
|
+
const fileResources = this.extractResourcesFromFile(sourceFile, filePath);
|
|
4429
|
+
for (const resource of fileResources) {
|
|
4430
|
+
resources.push(resource);
|
|
4431
|
+
const context = this.inferContext(filePath);
|
|
4432
|
+
const syntheticHandlers = this.createResourceHandlers(resource, context);
|
|
4433
|
+
for (const handler of syntheticHandlers) {
|
|
4434
|
+
handlers.push(handler);
|
|
4435
|
+
if (this.isValidTLAIdentifier(handler.messageType)) {
|
|
4436
|
+
messageTypes.add(handler.messageType);
|
|
4437
|
+
} else {
|
|
4438
|
+
invalidMessageTypes.add(handler.messageType);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4338
4442
|
const importDeclarations = sourceFile.getImportDeclarations();
|
|
4339
4443
|
for (const importDecl of importDeclarations) {
|
|
4340
4444
|
const importedFile = importDecl.getModuleSpecifierSourceFile();
|
|
@@ -4346,7 +4450,7 @@ class HandlerExtractor {
|
|
|
4346
4450
|
}
|
|
4347
4451
|
continue;
|
|
4348
4452
|
}
|
|
4349
|
-
this.analyzeFileAndImports(importedFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates);
|
|
4453
|
+
this.analyzeFileAndImports(importedFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources);
|
|
4350
4454
|
} else if (process.env["POLLY_DEBUG"]) {
|
|
4351
4455
|
const specifier = importDecl.getModuleSpecifierValue();
|
|
4352
4456
|
if (!specifier.startsWith("node:") && !this.isNodeModuleImport(specifier)) {
|
|
@@ -4705,7 +4809,9 @@ class HandlerExtractor {
|
|
|
4705
4809
|
return;
|
|
4706
4810
|
if (this.tryExtractSetConstructorPattern(fieldPath, right, assignments))
|
|
4707
4811
|
return;
|
|
4708
|
-
this.tryExtractMapConstructorPattern(fieldPath, right, assignments)
|
|
4812
|
+
if (this.tryExtractMapConstructorPattern(fieldPath, right, assignments))
|
|
4813
|
+
return;
|
|
4814
|
+
this.tryExtractSignalDirectValuePattern(fieldPath, right, assignments);
|
|
4709
4815
|
}
|
|
4710
4816
|
tryExtractStateFieldPattern(fieldPath, right, assignments) {
|
|
4711
4817
|
if (!fieldPath.startsWith("state."))
|
|
@@ -4824,6 +4930,25 @@ class HandlerExtractor {
|
|
|
4824
4930
|
}
|
|
4825
4931
|
return true;
|
|
4826
4932
|
}
|
|
4933
|
+
tryExtractSignalDirectValuePattern(fieldPath, right, assignments) {
|
|
4934
|
+
if (!fieldPath.endsWith(".value"))
|
|
4935
|
+
return false;
|
|
4936
|
+
const signalName = fieldPath.slice(0, -6);
|
|
4937
|
+
const literalValue = this.extractValue(right);
|
|
4938
|
+
if (literalValue !== undefined) {
|
|
4939
|
+
assignments.push({ field: signalName, value: literalValue });
|
|
4940
|
+
return true;
|
|
4941
|
+
}
|
|
4942
|
+
if (Node2.isPropertyAccessExpression(right)) {
|
|
4943
|
+
const rightPath = this.getPropertyPath(right);
|
|
4944
|
+
const parts = rightPath.split(".");
|
|
4945
|
+
if (parts.length === 2 && parts[0] !== undefined && parts[1] !== undefined && this.currentFunctionParams.includes(parts[0])) {
|
|
4946
|
+
assignments.push({ field: signalName, value: `param:${parts[1]}` });
|
|
4947
|
+
return true;
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
return false;
|
|
4951
|
+
}
|
|
4827
4952
|
extractSetOperation(newExpr, fieldPath, signalName) {
|
|
4828
4953
|
const args = newExpr.getArguments();
|
|
4829
4954
|
if (args.length === 0) {
|
|
@@ -4940,11 +5065,11 @@ class HandlerExtractor {
|
|
|
4940
5065
|
return null;
|
|
4941
5066
|
switch (methodName) {
|
|
4942
5067
|
case "filter":
|
|
4943
|
-
return { field: signalName, value: "
|
|
5068
|
+
return { field: signalName, value: "NDET:FILTER" };
|
|
4944
5069
|
case "map":
|
|
4945
|
-
return { field: signalName, value: "
|
|
5070
|
+
return { field: signalName, value: "NDET:MAP" };
|
|
4946
5071
|
case "slice":
|
|
4947
|
-
return { field: signalName, value: "
|
|
5072
|
+
return { field: signalName, value: "NDET:FILTER" };
|
|
4948
5073
|
case "concat":
|
|
4949
5074
|
return { field: signalName, value: "@ \\o <<payload>>" };
|
|
4950
5075
|
case "reverse":
|
|
@@ -6215,6 +6340,125 @@ class HandlerExtractor {
|
|
|
6215
6340
|
}
|
|
6216
6341
|
return name || funcName;
|
|
6217
6342
|
}
|
|
6343
|
+
extractResourcesFromFile(sourceFile, filePath) {
|
|
6344
|
+
const resources = [];
|
|
6345
|
+
sourceFile.forEachDescendant((node) => {
|
|
6346
|
+
if (!Node2.isCallExpression(node))
|
|
6347
|
+
return;
|
|
6348
|
+
const resource = this.extractResourcePattern(node, filePath);
|
|
6349
|
+
if (resource) {
|
|
6350
|
+
resources.push(resource);
|
|
6351
|
+
}
|
|
6352
|
+
});
|
|
6353
|
+
return resources;
|
|
6354
|
+
}
|
|
6355
|
+
extractResourcePattern(node, filePath) {
|
|
6356
|
+
const expression = node.getExpression();
|
|
6357
|
+
if (!Node2.isIdentifier(expression))
|
|
6358
|
+
return null;
|
|
6359
|
+
if (expression.getText() !== "$resource")
|
|
6360
|
+
return null;
|
|
6361
|
+
const args = node.getArguments();
|
|
6362
|
+
if (args.length < 2)
|
|
6363
|
+
return null;
|
|
6364
|
+
const nameArg = args[0];
|
|
6365
|
+
if (!nameArg || !Node2.isStringLiteral(nameArg))
|
|
6366
|
+
return null;
|
|
6367
|
+
const name = nameArg.getLiteralValue();
|
|
6368
|
+
const optionsArg = args[1];
|
|
6369
|
+
if (!optionsArg || !Node2.isObjectLiteralExpression(optionsArg))
|
|
6370
|
+
return null;
|
|
6371
|
+
const sourceSignals = this.extractResourceSourceReads(optionsArg);
|
|
6372
|
+
const variableName = this.getVariableNameFromParent(node) || name;
|
|
6373
|
+
if (process.env["POLLY_DEBUG"]) {
|
|
6374
|
+
console.log(`[DEBUG] Found $resource: ${variableName} (name: "${name}") with source signals: [${sourceSignals.join(", ")}]`);
|
|
6375
|
+
}
|
|
6376
|
+
return {
|
|
6377
|
+
name,
|
|
6378
|
+
variableName,
|
|
6379
|
+
filePath,
|
|
6380
|
+
line: node.getStartLineNumber(),
|
|
6381
|
+
sourceSignals
|
|
6382
|
+
};
|
|
6383
|
+
}
|
|
6384
|
+
extractResourceSourceReads(optionsObj) {
|
|
6385
|
+
const signals = [];
|
|
6386
|
+
const sourceProp = optionsObj.getProperty("source");
|
|
6387
|
+
if (!sourceProp || !Node2.isPropertyAssignment(sourceProp))
|
|
6388
|
+
return signals;
|
|
6389
|
+
const sourceInit = sourceProp.getInitializer();
|
|
6390
|
+
if (!sourceInit)
|
|
6391
|
+
return signals;
|
|
6392
|
+
sourceInit.forEachDescendant((node) => {
|
|
6393
|
+
if (!Node2.isPropertyAccessExpression(node))
|
|
6394
|
+
return;
|
|
6395
|
+
const text = node.getText();
|
|
6396
|
+
const match = text.match(/^(\w+)\.value(?:\.(\w+))?$/);
|
|
6397
|
+
if (match) {
|
|
6398
|
+
const signalName = match[1];
|
|
6399
|
+
const fieldName = match[2];
|
|
6400
|
+
if (fieldName) {
|
|
6401
|
+
signals.push(`${signalName}_${fieldName}`);
|
|
6402
|
+
} else {
|
|
6403
|
+
signals.push(signalName);
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
});
|
|
6407
|
+
return signals;
|
|
6408
|
+
}
|
|
6409
|
+
createResourceHandlers(resource, context) {
|
|
6410
|
+
const { name, filePath, line } = resource;
|
|
6411
|
+
const location = { file: filePath, line };
|
|
6412
|
+
const fetchStart = {
|
|
6413
|
+
messageType: `${name}_FetchStart`,
|
|
6414
|
+
node: context,
|
|
6415
|
+
assignments: [
|
|
6416
|
+
{ field: `${name}_status`, value: "loading" },
|
|
6417
|
+
{ field: `${name}_error`, value: false }
|
|
6418
|
+
],
|
|
6419
|
+
preconditions: [
|
|
6420
|
+
{
|
|
6421
|
+
expression: `${name}_status !== "loading"`,
|
|
6422
|
+
location: { line, column: 0 }
|
|
6423
|
+
}
|
|
6424
|
+
],
|
|
6425
|
+
postconditions: [],
|
|
6426
|
+
location
|
|
6427
|
+
};
|
|
6428
|
+
const fetchSuccess = {
|
|
6429
|
+
messageType: `${name}_FetchSuccess`,
|
|
6430
|
+
node: context,
|
|
6431
|
+
assignments: [
|
|
6432
|
+
{ field: `${name}_status`, value: "success" },
|
|
6433
|
+
{ field: `${name}_error`, value: false }
|
|
6434
|
+
],
|
|
6435
|
+
preconditions: [
|
|
6436
|
+
{
|
|
6437
|
+
expression: `${name}_status === "loading"`,
|
|
6438
|
+
location: { line, column: 0 }
|
|
6439
|
+
}
|
|
6440
|
+
],
|
|
6441
|
+
postconditions: [],
|
|
6442
|
+
location
|
|
6443
|
+
};
|
|
6444
|
+
const fetchError = {
|
|
6445
|
+
messageType: `${name}_FetchError`,
|
|
6446
|
+
node: context,
|
|
6447
|
+
assignments: [
|
|
6448
|
+
{ field: `${name}_status`, value: "error" },
|
|
6449
|
+
{ field: `${name}_error`, value: true }
|
|
6450
|
+
],
|
|
6451
|
+
preconditions: [
|
|
6452
|
+
{
|
|
6453
|
+
expression: `${name}_status === "loading"`,
|
|
6454
|
+
location: { line, column: 0 }
|
|
6455
|
+
}
|
|
6456
|
+
],
|
|
6457
|
+
postconditions: [],
|
|
6458
|
+
location
|
|
6459
|
+
};
|
|
6460
|
+
return [fetchStart, fetchSuccess, fetchError];
|
|
6461
|
+
}
|
|
6218
6462
|
}
|
|
6219
6463
|
|
|
6220
6464
|
// tools/analysis/src/extract/types.ts
|
|
@@ -6248,7 +6492,9 @@ class TypeExtractor {
|
|
|
6248
6492
|
fields,
|
|
6249
6493
|
handlers: completeHandlers,
|
|
6250
6494
|
stateConstraints: handlerAnalysis.stateConstraints,
|
|
6251
|
-
globalStateConstraints: handlerAnalysis.globalStateConstraints
|
|
6495
|
+
globalStateConstraints: handlerAnalysis.globalStateConstraints,
|
|
6496
|
+
verifiedStates: handlerAnalysis.verifiedStates,
|
|
6497
|
+
resources: handlerAnalysis.resources
|
|
6252
6498
|
};
|
|
6253
6499
|
}
|
|
6254
6500
|
extractHandlerAnalysis() {
|
|
@@ -7343,4 +7589,4 @@ main().catch((error) => {
|
|
|
7343
7589
|
process.exit(1);
|
|
7344
7590
|
});
|
|
7345
7591
|
|
|
7346
|
-
//# debugId=
|
|
7592
|
+
//# debugId=AA5557533E02826364756E2164756E21
|