@fairfox/polly 0.10.0 → 0.10.1
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.
|
@@ -48,6 +48,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
48
48
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
// tools/verify/src/primitives/index.ts
|
|
52
|
+
function $constraints(_stateField, _constraints) {}
|
|
53
|
+
|
|
51
54
|
// tools/verify/src/config.ts
|
|
52
55
|
function defineVerification(config) {
|
|
53
56
|
if ("adapter" in config) {
|
|
@@ -70,7 +73,8 @@ function defineVerification(config) {
|
|
|
70
73
|
return config;
|
|
71
74
|
}
|
|
72
75
|
export {
|
|
73
|
-
defineVerification
|
|
76
|
+
defineVerification,
|
|
77
|
+
$constraints
|
|
74
78
|
};
|
|
75
79
|
|
|
76
|
-
//# debugId=
|
|
80
|
+
//# debugId=06CC0A95E020BF9F64756E2164756E21
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../tools/verify/src/config.ts"],
|
|
3
|
+
"sources": ["../tools/verify/src/primitives/index.ts", "../tools/verify/src/config.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"//
|
|
5
|
+
"// Verification primitives for formal verification\n// These are runtime no-ops but extracted during verification\n\n/**\n * Assert a precondition that must be true when the handler executes.\n *\n * In production: No-op (compiled away)\n * In verification: Translated to TLA+ assertion\n *\n * @example\n * messageBus.on(\"USER_LOGIN\", (payload) => {\n * requires(state.user.loggedIn === false, \"User must not be logged in\")\n * state.user.loggedIn = true\n * })\n */\nexport function requires(condition: boolean, message?: string): void {\n // Runtime no-op - only used during verification\n // Condition and message are checked during static analysis only\n void condition;\n void message;\n}\n\n/**\n * Assert a postcondition that must be true after the handler completes.\n *\n * In production: No-op (compiled away)\n * In verification: Translated to TLA+ assertion\n *\n * @example\n * messageBus.on(\"USER_LOGIN\", (payload) => {\n * state.user.loggedIn = true\n * ensures(state.user.loggedIn === true, \"User must be logged in\")\n * })\n */\nexport function ensures(condition: boolean, message?: string): void {\n // Runtime no-op - only used during verification\n // Condition and message are checked during static analysis only\n void condition;\n void message;\n}\n\n/**\n * Define a global invariant that must always hold.\n *\n * In production: No-op (compiled away)\n * In verification: Translated to TLA+ invariant\n *\n * @example\n * invariant(\"UserIdConsistent\", () =>\n * state.user.loggedIn === false || state.user.id !== null\n * )\n */\nexport function invariant(_name: string, condition: () => boolean): void {\n // Runtime no-op - only used during verification\n // Name and condition are checked during static analysis only\n void condition;\n}\n\n/**\n * Assert that a value is within a valid range.\n *\n * @example\n * requires(inRange(todoCount, 0, 100), \"Todo count must be 0-100\")\n */\nexport function inRange(value: number, min: number, max: number): boolean {\n return value >= min && value <= max;\n}\n\n/**\n * Assert that a value is one of the allowed values.\n *\n * @example\n * requires(oneOf(state.user.role, [\"admin\", \"user\"]), \"Role must be admin or user\")\n */\nexport function oneOf<T>(value: T, allowed: T[]): boolean {\n return allowed.includes(value);\n}\n\n/**\n * Assert that an array has a specific length constraint.\n *\n * @example\n * requires(hasLength(state.todos, { max: 10 }), \"Too many todos\")\n */\nexport function hasLength(array: unknown[], constraint: { min?: number; max?: number }): boolean {\n if (constraint.min !== undefined && array.length < constraint.min) return false;\n if (constraint.max !== undefined && array.length > constraint.max) return false;\n return true;\n}\n\n/**\n * Declare state-level constraints for verification.\n * Maps message types to preconditions on state fields.\n *\n * The parser automatically wires these constraints to handlers.\n *\n * @example\n * const state = { loggedIn: false };\n *\n * $constraints(\"loggedIn\", {\n * USER_LOGOUT: { requires: \"loggedIn === true\", message: \"Must be logged in\" },\n * BOOKMARK_ADD: { requires: \"loggedIn === true\", message: \"Must be logged in\" },\n * });\n */\nexport function $constraints(\n _stateField: string,\n _constraints: Record<\n string,\n {\n requires?: string;\n ensures?: string;\n message?: string;\n }\n >\n): void {\n // Runtime no-op - only used during verification\n // Parser extracts these and wires them to TLA+ handlers\n}\n\n// Re-export for convenience\nexport const verify = {\n requires,\n ensures,\n invariant,\n inRange,\n oneOf,\n hasLength,\n $constraints,\n};\n",
|
|
6
|
+
"// ═══════════════════════════════════════════════════════════════\n// Configuration Helper for @fairfox/polly/verify\n// ═══════════════════════════════════════════════════════════════\n//\n// Lightweight entry point for user configuration files.\n// Does NOT include heavy dependencies (ts-morph, analysis, etc.)\n// which are only needed by the CLI tool.\n\n// ─────────────────────────────────────────────────────────────────\n// Configuration Types (inlined to avoid heavy dependencies)\n// ─────────────────────────────────────────────────────────────────\n\n// Legacy verification configuration\ninterface LegacyVerificationConfig {\n state: Record<string, unknown>;\n messages: {\n // Basic bounds\n maxInFlight?: number;\n maxTabs?: number;\n maxClients?: number;\n maxRenderers?: number;\n maxWorkers?: number;\n maxContexts?: number;\n\n // Tier 1 Optimizations (no precision loss)\n include?: string[]; // Only verify these message types\n exclude?: string[]; // Exclude these message types (mutually exclusive with include)\n symmetry?: string[][]; // Groups of symmetric message types [[type1, type2], [type3, type4]]\n perMessageBounds?: Record<string, number>; // Different maxInFlight per message type\n };\n onBuild?: \"warn\" | \"error\" | \"off\";\n onRelease?: \"warn\" | \"error\" | \"off\";\n preset?: \"quick\" | \"balanced\" | \"thorough\";\n\n // Verification engine options\n verification?: {\n timeout?: number; // Timeout in seconds (0 = no timeout)\n workers?: number; // Number of TLC workers\n };\n\n // Tier 2 Optimizations (controlled approximations)\n tier2?: {\n // Temporal constraints: ordering requirements between messages\n temporalConstraints?: Array<{\n before: string; // Message type that must occur first\n after: string; // Message type that must occur after\n description?: string; // Human-readable description\n }>;\n\n // Bounded exploration: limit depth for specific scenarios\n boundedExploration?: {\n maxDepth?: number; // Maximum state depth to explore\n criticalPaths?: string[][]; // Sequences of message types that must be fully explored\n };\n };\n}\n\n// Adapter-based configuration (for future use)\ninterface AdapterVerificationConfig {\n adapter: unknown; // Adapter interface not exported to avoid heavy deps\n state: Record<string, unknown>;\n bounds?: {\n maxInFlight?: number;\n [key: string]: unknown;\n };\n onBuild?: \"warn\" | \"error\" | \"off\";\n}\n\n// Union type for both config formats\ntype UnifiedVerificationConfig = LegacyVerificationConfig | AdapterVerificationConfig;\n\n/**\n * Define verification configuration with type checking\n *\n * Used in generated verification.config.ts files.\n *\n * @example\n * ```typescript\n * import { defineVerification } from '@fairfox/polly/verify'\n *\n * export default defineVerification({\n * state: {\n * \"user.role\": { type: \"enum\", values: [\"admin\", \"user\", \"guest\"] },\n * },\n * messages: {\n * maxInFlight: 6,\n * maxTabs: 2,\n * },\n * })\n * ```\n */\nexport function defineVerification<T extends UnifiedVerificationConfig>(config: T): T {\n // Validate configuration structure\n if (\"adapter\" in config) {\n // New adapter-based format\n if (!config.adapter) {\n throw new Error(\"Configuration must include an adapter\");\n }\n if (!config.state) {\n throw new Error(\"Configuration must include state bounds\");\n }\n } else if (\"messages\" in config) {\n // Legacy format\n if (!config.state) {\n throw new Error(\"Configuration must include state bounds\");\n }\n if (!config.messages) {\n throw new Error(\"Legacy configuration must include messages bounds\");\n }\n } else {\n throw new Error(\n \"Invalid configuration format. Must include either 'adapter' (new format) or 'messages' (legacy format)\"\n );\n }\n\n return config;\n}\n\n// Re-export $constraints from primitives for user code\nexport { $constraints } from \"./primitives/index.js\";\n"
|
|
6
7
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
8
|
-
"debugId": "
|
|
8
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwGO,SAAS,YAAY,CAC1B,aACA,cAQM;;;ACvBD,SAAS,kBAAuD,CAAC,QAAc;AAAA,EAEpF,IAAI,aAAa,QAAQ;AAAA,IAEvB,IAAI,CAAC,OAAO,SAAS;AAAA,MACnB,MAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAAA,IACA,IAAI,CAAC,OAAO,OAAO;AAAA,MACjB,MAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,EACF,EAAO,SAAI,cAAc,QAAQ;AAAA,IAE/B,IAAI,CAAC,OAAO,OAAO;AAAA,MACjB,MAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAAA,IACA,IAAI,CAAC,OAAO,UAAU;AAAA,MACpB,MAAM,IAAI,MAAM,mDAAmD;AAAA,IACrE;AAAA,EACF,EAAO;AAAA,IACL,MAAM,IAAI,MACR,wGACF;AAAA;AAAA,EAGF,OAAO;AAAA;",
|
|
9
|
+
"debugId": "06CC0A95E020BF9F64756E2164756E21",
|
|
9
10
|
"names": []
|
|
10
11
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assert a precondition that must be true when the handler executes.
|
|
3
|
+
*
|
|
4
|
+
* In production: No-op (compiled away)
|
|
5
|
+
* In verification: Translated to TLA+ assertion
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* messageBus.on("USER_LOGIN", (payload) => {
|
|
9
|
+
* requires(state.user.loggedIn === false, "User must not be logged in")
|
|
10
|
+
* state.user.loggedIn = true
|
|
11
|
+
* })
|
|
12
|
+
*/
|
|
13
|
+
export declare function requires(condition: boolean, message?: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Assert a postcondition that must be true after the handler completes.
|
|
16
|
+
*
|
|
17
|
+
* In production: No-op (compiled away)
|
|
18
|
+
* In verification: Translated to TLA+ assertion
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* messageBus.on("USER_LOGIN", (payload) => {
|
|
22
|
+
* state.user.loggedIn = true
|
|
23
|
+
* ensures(state.user.loggedIn === true, "User must be logged in")
|
|
24
|
+
* })
|
|
25
|
+
*/
|
|
26
|
+
export declare function ensures(condition: boolean, message?: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Define a global invariant that must always hold.
|
|
29
|
+
*
|
|
30
|
+
* In production: No-op (compiled away)
|
|
31
|
+
* In verification: Translated to TLA+ invariant
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* invariant("UserIdConsistent", () =>
|
|
35
|
+
* state.user.loggedIn === false || state.user.id !== null
|
|
36
|
+
* )
|
|
37
|
+
*/
|
|
38
|
+
export declare function invariant(_name: string, condition: () => boolean): void;
|
|
39
|
+
/**
|
|
40
|
+
* Assert that a value is within a valid range.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* requires(inRange(todoCount, 0, 100), "Todo count must be 0-100")
|
|
44
|
+
*/
|
|
45
|
+
export declare function inRange(value: number, min: number, max: number): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Assert that a value is one of the allowed values.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* requires(oneOf(state.user.role, ["admin", "user"]), "Role must be admin or user")
|
|
51
|
+
*/
|
|
52
|
+
export declare function oneOf<T>(value: T, allowed: T[]): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Assert that an array has a specific length constraint.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* requires(hasLength(state.todos, { max: 10 }), "Too many todos")
|
|
58
|
+
*/
|
|
59
|
+
export declare function hasLength(array: unknown[], constraint: {
|
|
60
|
+
min?: number;
|
|
61
|
+
max?: number;
|
|
62
|
+
}): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Declare state-level constraints for verification.
|
|
65
|
+
* Maps message types to preconditions on state fields.
|
|
66
|
+
*
|
|
67
|
+
* The parser automatically wires these constraints to handlers.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const state = { loggedIn: false };
|
|
71
|
+
*
|
|
72
|
+
* $constraints("loggedIn", {
|
|
73
|
+
* USER_LOGOUT: { requires: "loggedIn === true", message: "Must be logged in" },
|
|
74
|
+
* BOOKMARK_ADD: { requires: "loggedIn === true", message: "Must be logged in" },
|
|
75
|
+
* });
|
|
76
|
+
*/
|
|
77
|
+
export declare function $constraints(_stateField: string, _constraints: Record<string, {
|
|
78
|
+
requires?: string;
|
|
79
|
+
ensures?: string;
|
|
80
|
+
message?: string;
|
|
81
|
+
}>): void;
|
|
82
|
+
export declare const verify: {
|
|
83
|
+
requires: typeof requires;
|
|
84
|
+
ensures: typeof ensures;
|
|
85
|
+
invariant: typeof invariant;
|
|
86
|
+
inRange: typeof inRange;
|
|
87
|
+
oneOf: typeof oneOf;
|
|
88
|
+
hasLength: typeof hasLength;
|
|
89
|
+
$constraints: typeof $constraints;
|
|
90
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairfox/polly",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",
|