@fairfox/polly 0.19.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 +176 -9
- package/dist/tools/verify/src/cli.js.map +6 -6
- package/dist/tools/visualize/src/cli.js +139 -4
- 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
|
@@ -1362,13 +1362,25 @@ var init_tla = __esm(() => {
|
|
|
1362
1362
|
if (assignment.value !== null)
|
|
1363
1363
|
return true;
|
|
1364
1364
|
const fieldConfig = state[assignment.field];
|
|
1365
|
-
|
|
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);
|
|
1366
1372
|
}
|
|
1367
1373
|
mapNullAssignment(assignment, state) {
|
|
1368
1374
|
if (assignment.value !== null)
|
|
1369
1375
|
return assignment;
|
|
1370
1376
|
const fieldConfig = state[assignment.field];
|
|
1371
|
-
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) {
|
|
1372
1384
|
const nullValue = fieldConfig.values[fieldConfig.values.length - 1];
|
|
1373
1385
|
return { ...assignment, value: nullValue };
|
|
1374
1386
|
}
|
|
@@ -2069,6 +2081,9 @@ var init_tla = __esm(() => {
|
|
|
2069
2081
|
}
|
|
2070
2082
|
tryAbstractType(fieldConfig) {
|
|
2071
2083
|
if ("abstract" in fieldConfig && fieldConfig.abstract === true) {
|
|
2084
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable === true) {
|
|
2085
|
+
return "Value \\union {NULL}";
|
|
2086
|
+
}
|
|
2072
2087
|
return "Value";
|
|
2073
2088
|
}
|
|
2074
2089
|
return null;
|
|
@@ -2236,6 +2251,9 @@ var init_tla = __esm(() => {
|
|
|
2236
2251
|
}
|
|
2237
2252
|
getInitialValue(fieldConfig) {
|
|
2238
2253
|
if ("abstract" in fieldConfig && fieldConfig.abstract === true) {
|
|
2254
|
+
if ("nullable" in fieldConfig && fieldConfig.nullable === true) {
|
|
2255
|
+
return "NULL";
|
|
2256
|
+
}
|
|
2239
2257
|
return '"v1"';
|
|
2240
2258
|
}
|
|
2241
2259
|
if (Array.isArray(fieldConfig)) {
|
|
@@ -2927,7 +2945,7 @@ class ConfigGenerator {
|
|
|
2927
2945
|
this.addHeader();
|
|
2928
2946
|
this.addImports();
|
|
2929
2947
|
this.addExport();
|
|
2930
|
-
this.addStateConfig(analysis.fields);
|
|
2948
|
+
this.addStateConfig(analysis.fields, analysis.resources);
|
|
2931
2949
|
this.addMessagesConfig();
|
|
2932
2950
|
this.addBehaviorConfig();
|
|
2933
2951
|
this.closeExport();
|
|
@@ -2993,7 +3011,7 @@ class ConfigGenerator {
|
|
|
2993
3011
|
this.indent--;
|
|
2994
3012
|
this.line("})");
|
|
2995
3013
|
}
|
|
2996
|
-
addStateConfig(fields) {
|
|
3014
|
+
addStateConfig(fields, resources) {
|
|
2997
3015
|
this.line("state: {");
|
|
2998
3016
|
this.indent++;
|
|
2999
3017
|
for (let i = 0;i < fields.length; i++) {
|
|
@@ -3005,6 +3023,18 @@ class ConfigGenerator {
|
|
|
3005
3023
|
}
|
|
3006
3024
|
this.addFieldConfig(field);
|
|
3007
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
|
+
}
|
|
3008
3038
|
this.indent--;
|
|
3009
3039
|
this.line("},");
|
|
3010
3040
|
this.line("");
|
|
@@ -4335,12 +4365,13 @@ class HandlerExtractor {
|
|
|
4335
4365
|
const stateConstraints = [];
|
|
4336
4366
|
const globalStateConstraints = [];
|
|
4337
4367
|
const verifiedStates = [];
|
|
4368
|
+
const resources = [];
|
|
4338
4369
|
this.warnings = [];
|
|
4339
4370
|
const allSourceFiles = this.project.getSourceFiles();
|
|
4340
4371
|
const entryPoints = allSourceFiles.filter((f) => this.isWithinPackage(f.getFilePath()));
|
|
4341
4372
|
this.debugLogSourceFiles(allSourceFiles, entryPoints);
|
|
4342
4373
|
for (const entryPoint of entryPoints) {
|
|
4343
|
-
this.analyzeFileAndImports(entryPoint, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates);
|
|
4374
|
+
this.analyzeFileAndImports(entryPoint, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources);
|
|
4344
4375
|
}
|
|
4345
4376
|
if (verifiedStates.length > 0) {
|
|
4346
4377
|
if (process.env["POLLY_DEBUG"]) {
|
|
@@ -4372,10 +4403,11 @@ class HandlerExtractor {
|
|
|
4372
4403
|
stateConstraints,
|
|
4373
4404
|
globalStateConstraints,
|
|
4374
4405
|
verifiedStates,
|
|
4406
|
+
resources,
|
|
4375
4407
|
warnings: this.warnings
|
|
4376
4408
|
};
|
|
4377
4409
|
}
|
|
4378
|
-
analyzeFileAndImports(sourceFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates) {
|
|
4410
|
+
analyzeFileAndImports(sourceFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources) {
|
|
4379
4411
|
const filePath = sourceFile.getFilePath();
|
|
4380
4412
|
if (this.analyzedFiles.has(filePath)) {
|
|
4381
4413
|
return;
|
|
@@ -4393,6 +4425,20 @@ class HandlerExtractor {
|
|
|
4393
4425
|
globalStateConstraints.push(...fileGlobalConstraints);
|
|
4394
4426
|
const fileVerifiedStates = this.extractVerifiedStatesFromFile(sourceFile);
|
|
4395
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
|
+
}
|
|
4396
4442
|
const importDeclarations = sourceFile.getImportDeclarations();
|
|
4397
4443
|
for (const importDecl of importDeclarations) {
|
|
4398
4444
|
const importedFile = importDecl.getModuleSpecifierSourceFile();
|
|
@@ -4404,7 +4450,7 @@ class HandlerExtractor {
|
|
|
4404
4450
|
}
|
|
4405
4451
|
continue;
|
|
4406
4452
|
}
|
|
4407
|
-
this.analyzeFileAndImports(importedFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates);
|
|
4453
|
+
this.analyzeFileAndImports(importedFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources);
|
|
4408
4454
|
} else if (process.env["POLLY_DEBUG"]) {
|
|
4409
4455
|
const specifier = importDecl.getModuleSpecifierValue();
|
|
4410
4456
|
if (!specifier.startsWith("node:") && !this.isNodeModuleImport(specifier)) {
|
|
@@ -6294,6 +6340,125 @@ class HandlerExtractor {
|
|
|
6294
6340
|
}
|
|
6295
6341
|
return name || funcName;
|
|
6296
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
|
+
}
|
|
6297
6462
|
}
|
|
6298
6463
|
|
|
6299
6464
|
// tools/analysis/src/extract/types.ts
|
|
@@ -6327,7 +6492,9 @@ class TypeExtractor {
|
|
|
6327
6492
|
fields,
|
|
6328
6493
|
handlers: completeHandlers,
|
|
6329
6494
|
stateConstraints: handlerAnalysis.stateConstraints,
|
|
6330
|
-
globalStateConstraints: handlerAnalysis.globalStateConstraints
|
|
6495
|
+
globalStateConstraints: handlerAnalysis.globalStateConstraints,
|
|
6496
|
+
verifiedStates: handlerAnalysis.verifiedStates,
|
|
6497
|
+
resources: handlerAnalysis.resources
|
|
6331
6498
|
};
|
|
6332
6499
|
}
|
|
6333
6500
|
extractHandlerAnalysis() {
|
|
@@ -7422,4 +7589,4 @@ main().catch((error) => {
|
|
|
7422
7589
|
process.exit(1);
|
|
7423
7590
|
});
|
|
7424
7591
|
|
|
7425
|
-
//# debugId=
|
|
7592
|
+
//# debugId=AA5557533E02826364756E2164756E21
|