@plures/praxis 1.4.0 → 2.0.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.
Files changed (72) hide show
  1. package/dist/browser/{chunk-N63K4KWS.js → chunk-4IRUGWR3.js} +1 -1
  2. package/dist/browser/chunk-6MVRT7CK.js +363 -0
  3. package/dist/browser/chunk-6SJ44Q64.js +473 -0
  4. package/dist/browser/chunk-BQOYZBWA.js +282 -0
  5. package/dist/browser/chunk-IG5BJ2MT.js +91 -0
  6. package/dist/browser/{chunk-MJK3IYTJ.js → chunk-JZDJU2DO.js} +4 -84
  7. package/dist/browser/chunk-ZEW4LJAJ.js +353 -0
  8. package/dist/browser/{engine-YIEGSX7U.js → engine-3B5WJPGT.js} +2 -1
  9. package/dist/browser/expectations/index.d.ts +180 -0
  10. package/dist/browser/expectations/index.js +14 -0
  11. package/dist/browser/factory/index.d.ts +150 -0
  12. package/dist/browser/factory/index.js +15 -0
  13. package/dist/browser/index.d.ts +277 -3
  14. package/dist/browser/index.js +425 -60
  15. package/dist/browser/integrations/svelte.d.ts +4 -2
  16. package/dist/browser/integrations/svelte.js +3 -2
  17. package/dist/browser/project/index.d.ts +177 -0
  18. package/dist/browser/project/index.js +19 -0
  19. package/dist/browser/reactive-engine.svelte-BwWadvAW.d.ts +224 -0
  20. package/dist/browser/rule-result-DcXWe9tn.d.ts +206 -0
  21. package/dist/browser/rules-BaWMqxuG.d.ts +277 -0
  22. package/dist/browser/unified/index.d.ts +239 -0
  23. package/dist/browser/unified/index.js +20 -0
  24. package/dist/node/chunk-6MVRT7CK.js +363 -0
  25. package/dist/node/chunk-AZLNISFI.js +1690 -0
  26. package/dist/node/chunk-IG5BJ2MT.js +91 -0
  27. package/dist/node/{chunk-KMJWAFZV.js → chunk-JZDJU2DO.js} +4 -89
  28. package/dist/node/{chunk-7M3HV4XR.js → chunk-WFRHXZBP.js} +3 -3
  29. package/dist/node/cli/index.cjs +48 -0
  30. package/dist/node/cli/index.js +2 -2
  31. package/dist/node/{engine-FEN5IYZ5.js → engine-VFHCIEM4.js} +2 -1
  32. package/dist/node/index.cjs +2114 -0
  33. package/dist/node/index.d.cts +964 -280
  34. package/dist/node/index.d.ts +964 -280
  35. package/dist/node/index.js +575 -10
  36. package/dist/node/integrations/svelte.d.cts +3 -2
  37. package/dist/node/integrations/svelte.d.ts +3 -2
  38. package/dist/node/integrations/svelte.js +3 -2
  39. package/dist/node/{reactive-engine.svelte-DekxqFu0.d.ts → reactive-engine.svelte-BBZLMzus.d.ts} +3 -79
  40. package/dist/node/{reactive-engine.svelte-Cg0Yc2Hs.d.cts → reactive-engine.svelte-Cbq_V20o.d.cts} +3 -79
  41. package/dist/node/rule-result-B9GMivAn.d.cts +80 -0
  42. package/dist/node/rule-result-Bo3sFMmN.d.ts +80 -0
  43. package/dist/node/{server-SYZPDULV.js → server-FKLVY57V.js} +4 -2
  44. package/dist/node/unified/index.cjs +484 -0
  45. package/dist/node/unified/index.d.cts +240 -0
  46. package/dist/node/unified/index.d.ts +240 -0
  47. package/dist/node/unified/index.js +21 -0
  48. package/dist/node/{validate-TQGVIG7G.js → validate-BY7JNY7H.js} +2 -1
  49. package/package.json +38 -11
  50. package/src/__tests__/chronos-project.test.ts +799 -0
  51. package/src/__tests__/decision-ledger.test.ts +857 -402
  52. package/src/chronos/diff.ts +336 -0
  53. package/src/chronos/hooks.ts +227 -0
  54. package/src/chronos/index.ts +83 -0
  55. package/src/chronos/project-chronicle.ts +198 -0
  56. package/src/chronos/timeline.ts +152 -0
  57. package/src/decision-ledger/analyzer-types.ts +280 -0
  58. package/src/decision-ledger/analyzer.ts +518 -0
  59. package/src/decision-ledger/contract-verification.ts +456 -0
  60. package/src/decision-ledger/derivation.ts +158 -0
  61. package/src/decision-ledger/index.ts +59 -0
  62. package/src/decision-ledger/report.ts +378 -0
  63. package/src/decision-ledger/suggestions.ts +287 -0
  64. package/src/index.browser.ts +103 -0
  65. package/src/index.ts +98 -0
  66. package/src/unified/__tests__/unified.test.ts +396 -0
  67. package/src/unified/core.ts +517 -0
  68. package/src/unified/index.ts +32 -0
  69. package/src/unified/rules.ts +66 -0
  70. package/src/unified/types.ts +148 -0
  71. package/dist/browser/reactive-engine.svelte-DjynI82A.d.ts +0 -688
  72. package/dist/node/chunk-FWOXU4MM.js +0 -487
@@ -0,0 +1,353 @@
1
+ // src/expectations/expectations.ts
2
+ var Expectation = class {
3
+ name;
4
+ _conditions = [];
5
+ constructor(name) {
6
+ this.name = name;
7
+ }
8
+ /**
9
+ * Declare that this behavior should ONLY occur when a condition is true.
10
+ * If the condition is false, the behavior should NOT occur.
11
+ */
12
+ onlyWhen(condition) {
13
+ this._conditions.push({ description: condition, type: "onlyWhen" });
14
+ return this;
15
+ }
16
+ /**
17
+ * Declare that this behavior should NEVER occur under a given condition.
18
+ */
19
+ never(condition) {
20
+ this._conditions.push({ description: condition, type: "never" });
21
+ return this;
22
+ }
23
+ /**
24
+ * Declare that this behavior should ALWAYS have a certain property.
25
+ */
26
+ always(condition) {
27
+ this._conditions.push({ description: condition, type: "always" });
28
+ return this;
29
+ }
30
+ /** Get all declared conditions. */
31
+ get conditions() {
32
+ return this._conditions;
33
+ }
34
+ };
35
+ var ExpectationSet = class {
36
+ name;
37
+ description;
38
+ _expectations = [];
39
+ constructor(options) {
40
+ this.name = options.name;
41
+ this.description = options.description ?? "";
42
+ }
43
+ /** Add an expectation to the set. */
44
+ add(expectation) {
45
+ this._expectations.push(expectation);
46
+ return this;
47
+ }
48
+ /** Get all expectations in this set. */
49
+ get expectations() {
50
+ return this._expectations;
51
+ }
52
+ /** Number of expectations. */
53
+ get size() {
54
+ return this._expectations.length;
55
+ }
56
+ };
57
+ function expectBehavior(name) {
58
+ return new Expectation(name);
59
+ }
60
+ function verify(registry, expectations) {
61
+ const rules = registry.getAllRules();
62
+ const constraints = registry.getAllConstraints();
63
+ const ruleIds = registry.getRuleIds();
64
+ const constraintIds = registry.getConstraintIds();
65
+ const allDescriptors = [...rules, ...constraints];
66
+ const expectationResults = [];
67
+ for (const exp of expectations.expectations) {
68
+ const result = verifyExpectation(exp, allDescriptors, ruleIds, constraintIds);
69
+ expectationResults.push(result);
70
+ }
71
+ const satisfied = expectationResults.filter((r) => r.status === "satisfied").length;
72
+ const violated = expectationResults.filter((r) => r.status === "violated").length;
73
+ const partial = expectationResults.filter((r) => r.status === "partial").length;
74
+ const allEdgeCases = expectationResults.flatMap((r) => r.edgeCases);
75
+ const allMitigations = expectationResults.flatMap((r) => r.mitigations);
76
+ return {
77
+ setName: expectations.name,
78
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
79
+ status: violated > 0 ? "violated" : partial > 0 ? "partial" : "satisfied",
80
+ expectations: expectationResults,
81
+ summary: {
82
+ total: expectationResults.length,
83
+ satisfied,
84
+ violated,
85
+ partial
86
+ },
87
+ allEdgeCases,
88
+ allMitigations
89
+ };
90
+ }
91
+ function verifyExpectation(expectation, descriptors, ruleIds, _constraintIds) {
92
+ const conditionResults = [];
93
+ const edgeCases = [];
94
+ const mitigations = [];
95
+ const related = findRelatedDescriptors(expectation.name, descriptors);
96
+ for (const condition of expectation.conditions) {
97
+ const result = verifyCondition(condition, expectation.name, related, ruleIds);
98
+ conditionResults.push(result);
99
+ if (result.status === "unverifiable") {
100
+ edgeCases.push(`Cannot verify "${condition.description}" for "${expectation.name}" \u2014 no covering rule/contract found`);
101
+ mitigations.push(`Add a rule or constraint that explicitly covers: ${condition.description}`);
102
+ } else if (result.status === "violated") {
103
+ edgeCases.push(`"${expectation.name}" may fire incorrectly: ${condition.description}`);
104
+ mitigations.push(`Review rule logic for "${expectation.name}" regarding: ${condition.description}`);
105
+ }
106
+ }
107
+ const satisfiedCount = conditionResults.filter((r) => r.status === "satisfied").length;
108
+ const violatedCount = conditionResults.filter((r) => r.status === "violated").length;
109
+ const total = conditionResults.length;
110
+ let status;
111
+ if (total === 0) {
112
+ status = "satisfied";
113
+ } else if (violatedCount > 0) {
114
+ status = "violated";
115
+ } else if (satisfiedCount === total) {
116
+ status = "satisfied";
117
+ } else {
118
+ status = "partial";
119
+ }
120
+ return {
121
+ name: expectation.name,
122
+ status,
123
+ conditions: conditionResults,
124
+ edgeCases,
125
+ mitigations
126
+ };
127
+ }
128
+ function textOverlaps(a, b) {
129
+ const stopWords = /* @__PURE__ */ new Set([
130
+ "the",
131
+ "a",
132
+ "an",
133
+ "is",
134
+ "are",
135
+ "was",
136
+ "were",
137
+ "be",
138
+ "been",
139
+ "being",
140
+ "have",
141
+ "has",
142
+ "had",
143
+ "do",
144
+ "does",
145
+ "did",
146
+ "will",
147
+ "would",
148
+ "could",
149
+ "should",
150
+ "may",
151
+ "might",
152
+ "shall",
153
+ "can",
154
+ "to",
155
+ "of",
156
+ "in",
157
+ "for",
158
+ "on",
159
+ "with",
160
+ "at",
161
+ "by",
162
+ "from",
163
+ "as",
164
+ "into",
165
+ "through",
166
+ "during",
167
+ "before",
168
+ "after",
169
+ "when",
170
+ "that",
171
+ "this",
172
+ "it",
173
+ "its",
174
+ "and",
175
+ "or",
176
+ "but",
177
+ "not",
178
+ "no",
179
+ "if",
180
+ "then",
181
+ "than",
182
+ "so",
183
+ "up",
184
+ "out",
185
+ "about",
186
+ "just",
187
+ "must"
188
+ ]);
189
+ const wordsA = a.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
190
+ const wordsB = b.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
191
+ if (wordsA.length === 0 || wordsB.length === 0) return false;
192
+ const matches = wordsA.filter(
193
+ (wa) => wordsB.some((wb) => wa.startsWith(wb.slice(0, 4)) || wb.startsWith(wa.slice(0, 4)))
194
+ );
195
+ const minWords = Math.min(wordsA.length, wordsB.length);
196
+ return matches.length >= Math.max(1, Math.ceil(minWords * 0.5));
197
+ }
198
+ function verifyCondition(condition, expectationName, relatedDescriptors, _ruleIds) {
199
+ if (relatedDescriptors.length === 0) {
200
+ return {
201
+ condition,
202
+ status: "unverifiable",
203
+ explanation: `No rules or constraints found related to "${expectationName}"`,
204
+ relatedRules: []
205
+ };
206
+ }
207
+ const relatedIds = relatedDescriptors.map((d) => d.id);
208
+ const condLower = condition.description.toLowerCase();
209
+ const matches = (target) => target.toLowerCase().includes(condLower) || condLower.includes(target.toLowerCase()) || textOverlaps(condLower, target);
210
+ switch (condition.type) {
211
+ case "onlyWhen": {
212
+ const coveringRule = relatedDescriptors.find(
213
+ (d) => d.contract?.examples.some(
214
+ (ex) => matches(ex.given) || matches(ex.when)
215
+ ) || d.contract?.invariants.some((inv) => matches(inv))
216
+ );
217
+ if (coveringRule) {
218
+ return {
219
+ condition,
220
+ status: "satisfied",
221
+ explanation: `Rule "${coveringRule.id}" contract covers precondition: ${condition.description}`,
222
+ relatedRules: relatedIds
223
+ };
224
+ }
225
+ const descMatch = relatedDescriptors.find((d) => matches(d.description));
226
+ if (descMatch) {
227
+ return {
228
+ condition,
229
+ status: "satisfied",
230
+ explanation: `Rule "${descMatch.id}" description addresses: ${condition.description}`,
231
+ relatedRules: relatedIds
232
+ };
233
+ }
234
+ return {
235
+ condition,
236
+ status: "unverifiable",
237
+ explanation: `No rule contract explicitly covers the precondition: ${condition.description}`,
238
+ relatedRules: relatedIds
239
+ };
240
+ }
241
+ case "never": {
242
+ const preventingDescriptor = relatedDescriptors.find(
243
+ (d) => d.contract?.invariants.some((inv) => matches(inv)) || d.contract?.examples.some(
244
+ (ex) => (matches(ex.given) || matches(ex.when)) && (ex.then.toLowerCase().includes("retract") || ex.then.toLowerCase().includes("fail") || ex.then.toLowerCase().includes("violation") || ex.then.toLowerCase().includes("skip") || ex.then.toLowerCase().includes("block") || ex.then.toLowerCase().includes("no "))
245
+ ) || d.contract?.behavior.toLowerCase().includes("block") || d.contract?.behavior.toLowerCase().includes("prevent")
246
+ );
247
+ if (preventingDescriptor) {
248
+ return {
249
+ condition,
250
+ status: "satisfied",
251
+ explanation: `Constraint/rule "${preventingDescriptor.id}" prevents: ${condition.description}`,
252
+ relatedRules: relatedIds
253
+ };
254
+ }
255
+ return {
256
+ condition,
257
+ status: "unverifiable",
258
+ explanation: `No rule or constraint explicitly prevents: ${condition.description}`,
259
+ relatedRules: relatedIds
260
+ };
261
+ }
262
+ case "always": {
263
+ const guaranteeing = relatedDescriptors.find(
264
+ (d) => d.contract?.invariants.some((inv) => matches(inv)) || d.contract?.behavior && matches(d.contract.behavior)
265
+ );
266
+ if (guaranteeing) {
267
+ return {
268
+ condition,
269
+ status: "satisfied",
270
+ explanation: `Rule "${guaranteeing.id}" guarantees: ${condition.description}`,
271
+ relatedRules: relatedIds
272
+ };
273
+ }
274
+ const exampleMatch = relatedDescriptors.find(
275
+ (d) => d.contract?.examples.some((ex) => matches(ex.then))
276
+ );
277
+ if (exampleMatch) {
278
+ return {
279
+ condition,
280
+ status: "satisfied",
281
+ explanation: `Rule "${exampleMatch.id}" example demonstrates: ${condition.description}`,
282
+ relatedRules: relatedIds
283
+ };
284
+ }
285
+ return {
286
+ condition,
287
+ status: "unverifiable",
288
+ explanation: `No rule contract guarantees: ${condition.description}`,
289
+ relatedRules: relatedIds
290
+ };
291
+ }
292
+ }
293
+ }
294
+ function findRelatedDescriptors(expectationName, descriptors) {
295
+ const nameLower = expectationName.toLowerCase();
296
+ const nameParts = nameLower.split(/[-_./\s]+/);
297
+ return descriptors.filter((d) => {
298
+ const idLower = d.id.toLowerCase();
299
+ const descLower = d.description.toLowerCase();
300
+ const behaviorLower = d.contract?.behavior?.toLowerCase() ?? "";
301
+ if (idLower.includes(nameLower) || nameLower.includes(idLower)) return true;
302
+ if (descLower.includes(nameLower)) return true;
303
+ if (behaviorLower.includes(nameLower)) return true;
304
+ const minParts = Math.min(2, nameParts.length);
305
+ const matchingParts = nameParts.filter(
306
+ (part) => part.length > 2 && (idLower.includes(part) || descLower.includes(part) || behaviorLower.includes(part))
307
+ );
308
+ if (matchingParts.length >= minParts) return true;
309
+ if (d.eventTypes) {
310
+ const eventStr = Array.isArray(d.eventTypes) ? d.eventTypes.join(" ") : d.eventTypes;
311
+ if (eventStr.toLowerCase().includes(nameLower)) return true;
312
+ }
313
+ return false;
314
+ });
315
+ }
316
+ function formatVerificationReport(report) {
317
+ const lines = [];
318
+ const icon = report.status === "satisfied" ? "\u2705" : report.status === "partial" ? "\u{1F7E1}" : "\u{1F534}";
319
+ lines.push(`${icon} Expectations: ${report.setName} \u2014 ${report.status.toUpperCase()}`);
320
+ lines.push(` ${report.summary.satisfied}/${report.summary.total} satisfied, ${report.summary.violated} violated, ${report.summary.partial} partial`);
321
+ lines.push("");
322
+ for (const exp of report.expectations) {
323
+ const expIcon = exp.status === "satisfied" ? "\u2705" : exp.status === "partial" ? "\u{1F7E1}" : "\u{1F534}";
324
+ lines.push(`${expIcon} ${exp.name}`);
325
+ for (const cond of exp.conditions) {
326
+ const condIcon = cond.status === "satisfied" ? " \u2713" : cond.status === "violated" ? " \u2717" : " ?";
327
+ lines.push(`${condIcon} [${cond.condition.type}] ${cond.condition.description}`);
328
+ lines.push(` ${cond.explanation}`);
329
+ }
330
+ if (exp.edgeCases.length > 0) {
331
+ lines.push(" Edge cases:");
332
+ for (const ec of exp.edgeCases) {
333
+ lines.push(` \u26A0\uFE0F ${ec}`);
334
+ }
335
+ }
336
+ lines.push("");
337
+ }
338
+ if (report.allMitigations.length > 0) {
339
+ lines.push("Suggested mitigations:");
340
+ for (const m of report.allMitigations) {
341
+ lines.push(` \u{1F4A1} ${m}`);
342
+ }
343
+ }
344
+ return lines.join("\n");
345
+ }
346
+
347
+ export {
348
+ Expectation,
349
+ ExpectationSet,
350
+ expectBehavior,
351
+ verify,
352
+ formatVerificationReport
353
+ };
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  LogicEngine,
3
3
  createPraxisEngine
4
- } from "./chunk-MJK3IYTJ.js";
4
+ } from "./chunk-JZDJU2DO.js";
5
+ import "./chunk-IG5BJ2MT.js";
5
6
  export {
6
7
  LogicEngine,
7
8
  createPraxisEngine
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Expectations DSL — Types
3
+ *
4
+ * Types for declaring behavioral expectations about rules.
5
+ * Expectations replace traditional tests with behavioral declarations.
6
+ */
7
+ /** A condition under which a behavior should or should not occur. */
8
+ interface ExpectationCondition {
9
+ /** Human-readable condition description */
10
+ description: string;
11
+ /** Type of expectation */
12
+ type: 'onlyWhen' | 'never' | 'always';
13
+ }
14
+ /** Verification status for a single expectation condition. */
15
+ type ConditionStatus = 'satisfied' | 'violated' | 'unverifiable';
16
+ /** Detailed result for a single condition check. */
17
+ interface ConditionResult {
18
+ condition: ExpectationCondition;
19
+ status: ConditionStatus;
20
+ /** Explanation of how the condition was verified or why it couldn't be */
21
+ explanation: string;
22
+ /** Related rule IDs that informed this check */
23
+ relatedRules: string[];
24
+ }
25
+ /** Verification result for a single Expectation. */
26
+ interface ExpectationResult {
27
+ /** The expectation name/ID */
28
+ name: string;
29
+ /** Overall status: satisfied if ALL conditions pass */
30
+ status: 'satisfied' | 'violated' | 'partial';
31
+ /** Per-condition results */
32
+ conditions: ConditionResult[];
33
+ /** Edge cases discovered */
34
+ edgeCases: string[];
35
+ /** Suggested mitigations for violated/partial expectations */
36
+ mitigations: string[];
37
+ }
38
+ /** Full verification report for an ExpectationSet. */
39
+ interface VerificationReport {
40
+ /** Set name */
41
+ setName: string;
42
+ /** Timestamp of verification */
43
+ timestamp: string;
44
+ /** Overall status: satisfied if ALL expectations are satisfied */
45
+ status: 'satisfied' | 'violated' | 'partial';
46
+ /** Per-expectation results */
47
+ expectations: ExpectationResult[];
48
+ /** Summary stats */
49
+ summary: {
50
+ total: number;
51
+ satisfied: number;
52
+ violated: number;
53
+ partial: number;
54
+ };
55
+ /** All edge cases found across all expectations */
56
+ allEdgeCases: string[];
57
+ /** All mitigations suggested */
58
+ allMitigations: string[];
59
+ }
60
+ /** Options for creating an ExpectationSet */
61
+ interface ExpectationSetOptions {
62
+ /** Name/domain for this set of expectations */
63
+ name: string;
64
+ /** Optional description */
65
+ description?: string;
66
+ }
67
+ /** Interface describing a rule or constraint for verification */
68
+ interface VerifiableDescriptor {
69
+ id: string;
70
+ description: string;
71
+ eventTypes?: string | string[];
72
+ contract?: {
73
+ behavior: string;
74
+ examples: Array<{
75
+ given: string;
76
+ when: string;
77
+ then: string;
78
+ }>;
79
+ invariants: string[];
80
+ ruleId: string;
81
+ };
82
+ }
83
+ /** Registry-like interface for verification */
84
+ interface VerifiableRegistry {
85
+ getAllRules(): VerifiableDescriptor[];
86
+ getAllConstraints(): VerifiableDescriptor[];
87
+ getRuleIds(): string[];
88
+ getConstraintIds(): string[];
89
+ }
90
+
91
+ /**
92
+ * Expectations DSL — Core
93
+ *
94
+ * Behavioral declarations for Praxis rules. Instead of writing test
95
+ * assertions, you declare what behaviors you expect from your system.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { expectBehavior, ExpectationSet, verify } from '@plures/praxis/expectations';
100
+ *
101
+ * const expectations = new ExpectationSet({ name: 'settings' });
102
+ *
103
+ * expectations.add(
104
+ * expectBehavior('settings-saved-toast')
105
+ * .onlyWhen('settings.diff is non-empty')
106
+ * .never('when settings panel opens without changes')
107
+ * .never('when save fails')
108
+ * .always('includes which settings changed')
109
+ * );
110
+ *
111
+ * const report = verify(registry, expectations);
112
+ * ```
113
+ */
114
+
115
+ /**
116
+ * A behavioral expectation declaration.
117
+ *
118
+ * Chainable API for declaring conditions under which a behavior
119
+ * should or should not occur.
120
+ */
121
+ declare class Expectation {
122
+ readonly name: string;
123
+ private _conditions;
124
+ constructor(name: string);
125
+ /**
126
+ * Declare that this behavior should ONLY occur when a condition is true.
127
+ * If the condition is false, the behavior should NOT occur.
128
+ */
129
+ onlyWhen(condition: string): this;
130
+ /**
131
+ * Declare that this behavior should NEVER occur under a given condition.
132
+ */
133
+ never(condition: string): this;
134
+ /**
135
+ * Declare that this behavior should ALWAYS have a certain property.
136
+ */
137
+ always(condition: string): this;
138
+ /** Get all declared conditions. */
139
+ get conditions(): ReadonlyArray<ExpectationCondition>;
140
+ }
141
+ /**
142
+ * A collection of expectations for a specific domain.
143
+ */
144
+ declare class ExpectationSet {
145
+ readonly name: string;
146
+ readonly description: string;
147
+ private _expectations;
148
+ constructor(options: ExpectationSetOptions);
149
+ /** Add an expectation to the set. */
150
+ add(expectation: Expectation): this;
151
+ /** Get all expectations in this set. */
152
+ get expectations(): ReadonlyArray<Expectation>;
153
+ /** Number of expectations. */
154
+ get size(): number;
155
+ }
156
+ /**
157
+ * Create a new behavioral expectation.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * expectBehavior('settings-saved-toast')
162
+ * .onlyWhen('settings.diff is non-empty')
163
+ * .never('when save fails')
164
+ * .always('includes which settings changed');
165
+ * ```
166
+ */
167
+ declare function expectBehavior(name: string): Expectation;
168
+ /**
169
+ * Verify expectations against a rule registry.
170
+ *
171
+ * Walks the rule graph to determine if expectations are satisfied,
172
+ * violated, or unverifiable given the registered rules and contracts.
173
+ */
174
+ declare function verify(registry: VerifiableRegistry, expectations: ExpectationSet): VerificationReport;
175
+ /**
176
+ * Format a verification report as human-readable text.
177
+ */
178
+ declare function formatVerificationReport(report: VerificationReport): string;
179
+
180
+ export { type ConditionResult, type ConditionStatus, Expectation, type ExpectationCondition, type ExpectationResult, ExpectationSet, type ExpectationSetOptions, type VerifiableDescriptor, type VerifiableRegistry, type VerificationReport, expectBehavior, formatVerificationReport, verify };
@@ -0,0 +1,14 @@
1
+ import {
2
+ Expectation,
3
+ ExpectationSet,
4
+ expectBehavior,
5
+ formatVerificationReport,
6
+ verify
7
+ } from "../chunk-ZEW4LJAJ.js";
8
+ export {
9
+ Expectation,
10
+ ExpectationSet,
11
+ expectBehavior,
12
+ formatVerificationReport,
13
+ verify
14
+ };
@@ -0,0 +1,150 @@
1
+ import { d as PraxisModule } from '../rules-BaWMqxuG.js';
2
+ import '../rule-result-DcXWe9tn.js';
3
+
4
+ /**
5
+ * Praxis Rules Factory — Types
6
+ *
7
+ * Configuration types for predefined rule module factories.
8
+ */
9
+ type SanitizationType = 'sql-injection' | 'xss' | 'path-traversal' | 'command-injection';
10
+ interface InputRulesConfig {
11
+ /** Sanitization checks to apply */
12
+ sanitize?: SanitizationType[];
13
+ /** Maximum input length (0 = unlimited) */
14
+ maxLength?: number;
15
+ /** Whether the input is required (non-empty) */
16
+ required?: boolean;
17
+ /** Custom field name for facts/events (default: 'input') */
18
+ fieldName?: string;
19
+ }
20
+ interface ToastRulesConfig {
21
+ /** Only show toast if there's a meaningful diff */
22
+ requireDiff?: boolean;
23
+ /** Auto-dismiss after N milliseconds (0 = no auto-dismiss) */
24
+ autoDismissMs?: number;
25
+ /** Prevent duplicate toasts with same message */
26
+ deduplicate?: boolean;
27
+ }
28
+ interface FormRulesConfig {
29
+ /** Validate fields on blur */
30
+ validateOnBlur?: boolean;
31
+ /** Gate form submission on validation passing */
32
+ submitGate?: boolean;
33
+ /** Custom form name for namespacing facts */
34
+ formName?: string;
35
+ }
36
+ interface NavigationRulesConfig {
37
+ /** Warn/block navigation when form has unsaved changes */
38
+ dirtyGuard?: boolean;
39
+ /** Require authentication for navigation */
40
+ authRequired?: boolean;
41
+ }
42
+ interface DataRulesConfig {
43
+ /** Enable optimistic UI updates */
44
+ optimisticUpdate?: boolean;
45
+ /** Rollback optimistic updates on error */
46
+ rollbackOnError?: boolean;
47
+ /** Invalidate relevant caches on data change */
48
+ cacheInvalidation?: boolean;
49
+ /** Custom entity name for facts */
50
+ entityName?: string;
51
+ }
52
+
53
+ /**
54
+ * Praxis Rules Factory
55
+ *
56
+ * Predefined rule modules for common application patterns.
57
+ * Each factory returns a PraxisModule with rules, constraints, and contracts.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { inputRules, toastRules, formRules } from '@plures/praxis/factory';
62
+ *
63
+ * const registry = new PraxisRegistry();
64
+ * registry.registerModule(inputRules({ sanitize: ['xss', 'sql-injection'], required: true }));
65
+ * registry.registerModule(toastRules({ requireDiff: true, deduplicate: true }));
66
+ * registry.registerModule(formRules({ validateOnBlur: true, submitGate: true }));
67
+ * ```
68
+ */
69
+
70
+ /** Context type for input rules. */
71
+ interface InputContext {
72
+ input?: {
73
+ value?: string;
74
+ field?: string;
75
+ };
76
+ }
77
+ /**
78
+ * Create input validation rules module.
79
+ *
80
+ * Generates rules for sanitizing user input, enforcing length limits,
81
+ * and requiring non-empty values.
82
+ */
83
+ declare function inputRules(config?: InputRulesConfig): PraxisModule<InputContext>;
84
+ /** Context type for toast rules. */
85
+ interface ToastContext {
86
+ diff?: Record<string, unknown> | null;
87
+ toasts?: Array<{
88
+ message: string;
89
+ id: string;
90
+ }>;
91
+ }
92
+ /**
93
+ * Create truthful toast notification rules.
94
+ *
95
+ * Generates rules that ensure toasts only appear with meaningful content,
96
+ * auto-dismiss after a timeout, and avoid duplicates.
97
+ */
98
+ declare function toastRules(config?: ToastRulesConfig): PraxisModule<ToastContext>;
99
+ /** Context type for form rules. */
100
+ interface FormContext {
101
+ form?: {
102
+ fields?: Record<string, {
103
+ value: unknown;
104
+ error?: string;
105
+ touched?: boolean;
106
+ }>;
107
+ valid?: boolean;
108
+ dirty?: boolean;
109
+ submitting?: boolean;
110
+ };
111
+ }
112
+ /**
113
+ * Create form lifecycle rules.
114
+ *
115
+ * Generates rules for field validation on blur, submit gating,
116
+ * and form state management.
117
+ */
118
+ declare function formRules(config?: FormRulesConfig): PraxisModule<FormContext>;
119
+ /** Context type for navigation rules. */
120
+ interface NavigationContext {
121
+ dirty?: boolean;
122
+ authenticated?: boolean;
123
+ route?: string;
124
+ }
125
+ /**
126
+ * Create route protection rules.
127
+ *
128
+ * Generates rules for dirty-data navigation guards and
129
+ * authentication-required route protection.
130
+ */
131
+ declare function navigationRules(config?: NavigationRulesConfig): PraxisModule<NavigationContext>;
132
+ /** Context type for data rules. */
133
+ interface DataContext {
134
+ pending?: Record<string, {
135
+ original: unknown;
136
+ optimistic: unknown;
137
+ }>;
138
+ cache?: Record<string, {
139
+ data: unknown;
140
+ timestamp: number;
141
+ }>;
142
+ }
143
+ /**
144
+ * Create data lifecycle rules.
145
+ *
146
+ * Generates rules for optimistic updates, error rollback, and cache invalidation.
147
+ */
148
+ declare function dataRules(config?: DataRulesConfig): PraxisModule<DataContext>;
149
+
150
+ export { type DataRulesConfig, type FormRulesConfig, type InputRulesConfig, type NavigationRulesConfig, type SanitizationType, type ToastRulesConfig, dataRules, formRules, inputRules, navigationRules, toastRules };