@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.
@@ -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
- return !!(fieldConfig && typeof fieldConfig === "object" && ("values" in fieldConfig) && fieldConfig.values);
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 && typeof fieldConfig === "object" && "values" in fieldConfig && fieldConfig.values) {
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=E5DA6950FD1E0C7764756E2164756E21
7592
+ //# debugId=AA5557533E02826364756E2164756E21