@plures/praxis 1.4.0 → 1.4.4

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 (51) hide show
  1. package/dist/browser/{chunk-N63K4KWS.js → chunk-4IRUGWR3.js} +1 -1
  2. package/dist/browser/chunk-6SJ44Q64.js +473 -0
  3. package/dist/browser/chunk-BQOYZBWA.js +282 -0
  4. package/dist/browser/chunk-IG5BJ2MT.js +91 -0
  5. package/dist/browser/{chunk-MJK3IYTJ.js → chunk-JZDJU2DO.js} +4 -84
  6. package/dist/browser/chunk-ZEW4LJAJ.js +353 -0
  7. package/dist/browser/{engine-YIEGSX7U.js → engine-3B5WJPGT.js} +2 -1
  8. package/dist/browser/expectations/index.d.ts +180 -0
  9. package/dist/browser/expectations/index.js +14 -0
  10. package/dist/browser/factory/index.d.ts +149 -0
  11. package/dist/browser/factory/index.js +15 -0
  12. package/dist/browser/index.d.ts +274 -3
  13. package/dist/browser/index.js +407 -54
  14. package/dist/browser/integrations/svelte.d.ts +3 -2
  15. package/dist/browser/integrations/svelte.js +3 -2
  16. package/dist/browser/project/index.d.ts +176 -0
  17. package/dist/browser/project/index.js +19 -0
  18. package/dist/browser/reactive-engine.svelte-DgVTqHLc.d.ts +223 -0
  19. package/dist/browser/{reactive-engine.svelte-DjynI82A.d.ts → rules-i1LHpnGd.d.ts} +13 -221
  20. package/dist/node/chunk-AZLNISFI.js +1690 -0
  21. package/dist/node/chunk-IG5BJ2MT.js +91 -0
  22. package/dist/node/{chunk-KMJWAFZV.js → chunk-JZDJU2DO.js} +4 -89
  23. package/dist/node/{chunk-7M3HV4XR.js → chunk-ZO2LU4G4.js} +1 -1
  24. package/dist/node/cli/index.cjs +48 -0
  25. package/dist/node/cli/index.js +2 -2
  26. package/dist/node/{engine-FEN5IYZ5.js → engine-VFHCIEM4.js} +2 -1
  27. package/dist/node/index.cjs +1747 -0
  28. package/dist/node/index.d.cts +960 -278
  29. package/dist/node/index.d.ts +960 -278
  30. package/dist/node/index.js +559 -6
  31. package/dist/node/integrations/svelte.js +3 -2
  32. package/dist/node/{server-SYZPDULV.js → server-FKLVY57V.js} +4 -2
  33. package/dist/node/{validate-TQGVIG7G.js → validate-5PSWJTIC.js} +2 -1
  34. package/package.json +32 -11
  35. package/src/__tests__/chronos-project.test.ts +799 -0
  36. package/src/__tests__/decision-ledger.test.ts +857 -402
  37. package/src/chronos/diff.ts +336 -0
  38. package/src/chronos/hooks.ts +227 -0
  39. package/src/chronos/index.ts +83 -0
  40. package/src/chronos/project-chronicle.ts +198 -0
  41. package/src/chronos/timeline.ts +152 -0
  42. package/src/decision-ledger/analyzer-types.ts +280 -0
  43. package/src/decision-ledger/analyzer.ts +518 -0
  44. package/src/decision-ledger/contract-verification.ts +456 -0
  45. package/src/decision-ledger/derivation.ts +158 -0
  46. package/src/decision-ledger/index.ts +59 -0
  47. package/src/decision-ledger/report.ts +378 -0
  48. package/src/decision-ledger/suggestions.ts +287 -0
  49. package/src/index.browser.ts +83 -0
  50. package/src/index.ts +77 -0
  51. package/dist/node/chunk-FWOXU4MM.js +0 -487
@@ -2,12 +2,39 @@ import {
2
2
  PraxisRegistry,
3
3
  ReactiveLogicEngine,
4
4
  createReactiveEngine
5
- } from "./chunk-N63K4KWS.js";
5
+ } from "./chunk-4IRUGWR3.js";
6
+ import {
7
+ Expectation,
8
+ ExpectationSet,
9
+ expectBehavior,
10
+ formatVerificationReport,
11
+ verify
12
+ } from "./chunk-ZEW4LJAJ.js";
13
+ import {
14
+ dataRules,
15
+ formRules,
16
+ inputRules,
17
+ navigationRules,
18
+ toastRules
19
+ } from "./chunk-6SJ44Q64.js";
20
+ import {
21
+ branchRules,
22
+ commitFromState,
23
+ defineGate,
24
+ expectationGate,
25
+ formatGate,
26
+ lintGate,
27
+ semverContract
28
+ } from "./chunk-BQOYZBWA.js";
6
29
  import {
7
30
  LogicEngine,
8
31
  PRAXIS_PROTOCOL_VERSION,
9
32
  createPraxisEngine
10
- } from "./chunk-MJK3IYTJ.js";
33
+ } from "./chunk-JZDJU2DO.js";
34
+ import {
35
+ RuleResult,
36
+ fact
37
+ } from "./chunk-IG5BJ2MT.js";
11
38
  import {
12
39
  InMemoryPraxisDB,
13
40
  PluresDBPraxisAdapter,
@@ -563,8 +590,8 @@ function defineFact(tag) {
563
590
  create(payload) {
564
591
  return { tag, payload };
565
592
  },
566
- is(fact) {
567
- return fact.tag === tag;
593
+ is(fact2) {
594
+ return fact2.tag === tag;
568
595
  }
569
596
  };
570
597
  }
@@ -666,16 +693,16 @@ function validateSchema(schema) {
666
693
  if (schema.logic) {
667
694
  schema.logic.forEach((logic, logicIndex) => {
668
695
  if (logic.facts) {
669
- logic.facts.forEach((fact, factIndex) => {
670
- if (!fact.tag) {
696
+ logic.facts.forEach((fact2, factIndex) => {
697
+ if (!fact2.tag) {
671
698
  errors.push({
672
699
  path: `logic[${logicIndex}].facts[${factIndex}].tag`,
673
700
  message: "Fact tag is required"
674
701
  });
675
- } else if (!isValidIdentifier(fact.tag)) {
702
+ } else if (!isValidIdentifier(fact2.tag)) {
676
703
  errors.push({
677
704
  path: `logic[${logicIndex}].facts[${factIndex}].tag`,
678
- message: `Fact tag "${fact.tag}" is not a valid JavaScript identifier. Use only letters, numbers, underscores, and dollar signs, and do not start with a number.`
705
+ message: `Fact tag "${fact2.tag}" is not a valid JavaScript identifier. Use only letters, numbers, underscores, and dollar signs, and do not start with a number.`
679
706
  });
680
707
  }
681
708
  });
@@ -1002,37 +1029,37 @@ var PraxisDBStore = class {
1002
1029
  * @param fact The fact to store
1003
1030
  * @returns Promise that resolves when the fact is stored
1004
1031
  */
1005
- async storeFact(fact) {
1006
- const constraintResult = await this.checkConstraints([fact]);
1032
+ async storeFact(fact2) {
1033
+ const constraintResult = await this.checkConstraints([fact2]);
1007
1034
  if (!constraintResult.valid) {
1008
1035
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
1009
1036
  }
1010
1037
  let before;
1011
1038
  if (this.chronicle) {
1012
- const payload = fact.payload;
1039
+ const payload = fact2.payload;
1013
1040
  const id = payload?.id;
1014
1041
  if (id) {
1015
- before = await this.getFact(fact.tag, id);
1042
+ before = await this.getFact(fact2.tag, id);
1016
1043
  }
1017
1044
  }
1018
- await this.persistFact(fact);
1045
+ await this.persistFact(fact2);
1019
1046
  if (this.chronicle) {
1020
- const payload = fact.payload;
1047
+ const payload = fact2.payload;
1021
1048
  const id = payload?.id ?? "";
1022
1049
  const span = ChronicleContext.current;
1023
1050
  try {
1024
1051
  await this.chronicle.record({
1025
- path: getFactPath(fact.tag, id),
1052
+ path: getFactPath(fact2.tag, id),
1026
1053
  before,
1027
- after: fact,
1054
+ after: fact2,
1028
1055
  cause: span?.spanId,
1029
1056
  context: span?.contextId,
1030
- metadata: { factTag: fact.tag, operation: "storeFact" }
1057
+ metadata: { factTag: fact2.tag, operation: "storeFact" }
1031
1058
  });
1032
1059
  } catch {
1033
1060
  }
1034
1061
  }
1035
- await this.triggerRules([fact]);
1062
+ await this.triggerRules([fact2]);
1036
1063
  }
1037
1064
  /**
1038
1065
  * Store multiple facts in PluresDB
@@ -1044,28 +1071,28 @@ var PraxisDBStore = class {
1044
1071
  if (!constraintResult.valid) {
1045
1072
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
1046
1073
  }
1047
- for (const fact of facts) {
1074
+ for (const fact2 of facts) {
1048
1075
  let before;
1049
1076
  if (this.chronicle) {
1050
- const payload = fact.payload;
1077
+ const payload = fact2.payload;
1051
1078
  const id = payload?.id;
1052
1079
  if (id) {
1053
- before = await this.getFact(fact.tag, id);
1080
+ before = await this.getFact(fact2.tag, id);
1054
1081
  }
1055
1082
  }
1056
- await this.persistFact(fact);
1083
+ await this.persistFact(fact2);
1057
1084
  if (this.chronicle) {
1058
- const payload = fact.payload;
1085
+ const payload = fact2.payload;
1059
1086
  const id = payload?.id ?? "";
1060
1087
  const span = ChronicleContext.current;
1061
1088
  try {
1062
1089
  await this.chronicle.record({
1063
- path: getFactPath(fact.tag, id),
1090
+ path: getFactPath(fact2.tag, id),
1064
1091
  before,
1065
- after: fact,
1092
+ after: fact2,
1066
1093
  cause: span?.spanId,
1067
1094
  context: span?.contextId,
1068
- metadata: { factTag: fact.tag, operation: "storeFacts" }
1095
+ metadata: { factTag: fact2.tag, operation: "storeFacts" }
1069
1096
  });
1070
1097
  } catch {
1071
1098
  }
@@ -1077,11 +1104,11 @@ var PraxisDBStore = class {
1077
1104
  * Internal method to persist a fact without constraint checking
1078
1105
  * Used by both storeFact and derived fact storage
1079
1106
  */
1080
- async persistFact(fact) {
1081
- const payload = fact.payload;
1107
+ async persistFact(fact2) {
1108
+ const payload = fact2.payload;
1082
1109
  const id = payload?.id ?? generateId();
1083
- const path = getFactPath(fact.tag, id);
1084
- await this.db.set(path, fact);
1110
+ const path = getFactPath(fact2.tag, id);
1111
+ await this.db.set(path, fact2);
1085
1112
  }
1086
1113
  /**
1087
1114
  * Get a fact by tag and id
@@ -1218,8 +1245,8 @@ var PraxisDBStore = class {
1218
1245
  if (watchers) {
1219
1246
  watchers.add(callback);
1220
1247
  }
1221
- const unsubscribe = this.db.watch(path, (fact) => {
1222
- callback([fact]);
1248
+ const unsubscribe = this.db.watch(path, (fact2) => {
1249
+ callback([fact2]);
1223
1250
  });
1224
1251
  this.subscriptions.push(unsubscribe);
1225
1252
  return () => {
@@ -1296,19 +1323,19 @@ var PraxisDBStore = class {
1296
1323
  if (derivedFacts.length > 0) {
1297
1324
  const constraintResult = await this.checkConstraints(derivedFacts);
1298
1325
  if (constraintResult.valid) {
1299
- for (const fact of derivedFacts) {
1300
- await this.persistFact(fact);
1326
+ for (const fact2 of derivedFacts) {
1327
+ await this.persistFact(fact2);
1301
1328
  if (this.chronicle) {
1302
- const payload = fact.payload;
1329
+ const payload = fact2.payload;
1303
1330
  const id = payload?.id ?? "";
1304
1331
  const span = ChronicleContext.current;
1305
1332
  try {
1306
1333
  await this.chronicle.record({
1307
- path: getFactPath(fact.tag, id),
1308
- after: fact,
1334
+ path: getFactPath(fact2.tag, id),
1335
+ after: fact2,
1309
1336
  cause: span?.spanId,
1310
1337
  context: span?.contextId,
1311
- metadata: { factTag: fact.tag, operation: "derivedFact" }
1338
+ metadata: { factTag: fact2.tag, operation: "derivedFact" }
1312
1339
  });
1313
1340
  } catch {
1314
1341
  }
@@ -1768,7 +1795,7 @@ async function createUnumAdapter(config) {
1768
1795
  type: "event"
1769
1796
  });
1770
1797
  }
1771
- async function broadcastFact(channelId, fact) {
1798
+ async function broadcastFact(channelId, fact2) {
1772
1799
  const channel = channels.get(channelId);
1773
1800
  if (!channel) {
1774
1801
  throw new Error(`Not joined to channel ${channelId}`);
@@ -1776,7 +1803,7 @@ async function createUnumAdapter(config) {
1776
1803
  await channel.publish({
1777
1804
  id: generateId2(),
1778
1805
  sender: currentIdentity || { id: "anonymous", createdAt: Date.now() },
1779
- content: fact,
1806
+ content: fact2,
1780
1807
  type: "fact"
1781
1808
  });
1782
1809
  }
@@ -1919,17 +1946,17 @@ function schemaToCanvas(schema, _options = {}) {
1919
1946
  yOffset += schema.events.length * ySpacing + 30;
1920
1947
  }
1921
1948
  if (schema.facts) {
1922
- schema.facts.forEach((fact, index) => {
1923
- const pos = fact.position && (fact.position.x !== 0 || fact.position.y !== 0) ? fact.position : { x: 50 + xSpacing * 3, y: yOffset + index * ySpacing };
1949
+ schema.facts.forEach((fact2, index) => {
1950
+ const pos = fact2.position && (fact2.position.x !== 0 || fact2.position.y !== 0) ? fact2.position : { x: 50 + xSpacing * 3, y: yOffset + index * ySpacing };
1924
1951
  const node = {
1925
- id: fact.id || `fact-${nodeId++}`,
1952
+ id: fact2.id || `fact-${nodeId++}`,
1926
1953
  type: "fact",
1927
- label: fact.tag,
1954
+ label: fact2.tag,
1928
1955
  x: pos.x,
1929
1956
  y: pos.y,
1930
1957
  width: 150,
1931
1958
  height: 50,
1932
- data: fact,
1959
+ data: fact2,
1933
1960
  style: {
1934
1961
  backgroundColor: "#fce4ec",
1935
1962
  borderColor: "#c2185b"
@@ -2384,8 +2411,8 @@ var StateDocsGenerator = class {
2384
2411
  if (logic.facts && logic.facts.length > 0) {
2385
2412
  lines.push("**Facts:**");
2386
2413
  lines.push("");
2387
- for (const fact of logic.facts) {
2388
- lines.push(`- \`${fact.tag}\`: ${fact.description || ""}`);
2414
+ for (const fact2 of logic.facts) {
2415
+ lines.push(`- \`${fact2.tag}\`: ${fact2.description || ""}`);
2389
2416
  }
2390
2417
  lines.push("");
2391
2418
  }
@@ -2517,9 +2544,9 @@ var StateDocsGenerator = class {
2517
2544
  lines.push("");
2518
2545
  lines.push("| Fact | Description | Payload |");
2519
2546
  lines.push("|------|-------------|---------|");
2520
- for (const fact of logic.facts) {
2521
- const payload = fact.payload ? Object.entries(fact.payload).map(([k, v]) => `${k}: ${v}`).join(", ") : "-";
2522
- lines.push(`| \`${fact.tag}\` | ${fact.description || "-"} | ${payload} |`);
2547
+ for (const fact2 of logic.facts) {
2548
+ const payload = fact2.payload ? Object.entries(fact2.payload).map(([k, v]) => `${k}: ${v}`).join(", ") : "-";
2549
+ lines.push(`| \`${fact2.tag}\` | ${fact2.description || "-"} | ${payload} |`);
2523
2550
  }
2524
2551
  lines.push("");
2525
2552
  }
@@ -2571,8 +2598,8 @@ var StateDocsGenerator = class {
2571
2598
  for (const event of logic.events) {
2572
2599
  lines.push(` Processing --> ${event.tag.replace(/[^a-zA-Z0-9]/g, "")}: ${event.tag}`);
2573
2600
  }
2574
- for (const fact of logic.facts) {
2575
- lines.push(` ${fact.tag.replace(/[^a-zA-Z0-9]/g, "")} --> [*]`);
2601
+ for (const fact2 of logic.facts) {
2602
+ lines.push(` ${fact2.tag.replace(/[^a-zA-Z0-9]/g, "")} --> [*]`);
2576
2603
  }
2577
2604
  }
2578
2605
  return {
@@ -2894,7 +2921,7 @@ function generateTauriConfig(config) {
2894
2921
 
2895
2922
  // src/integrations/unified.ts
2896
2923
  async function createUnifiedApp(config) {
2897
- const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-YIEGSX7U.js");
2924
+ const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-3B5WJPGT.js");
2898
2925
  const { createInMemoryDB: createInMemoryDB2 } = await import("./adapter-CIMBGDC7.js");
2899
2926
  const db = config.db || createInMemoryDB2();
2900
2927
  const pluresdb = createPluresDBAdapter({
@@ -3023,8 +3050,302 @@ async function attachAllIntegrations(engine, registry, options = {}) {
3023
3050
  }
3024
3051
  };
3025
3052
  }
3053
+
3054
+ // src/core/ui-rules.ts
3055
+ var loadingGateRule = {
3056
+ id: "ui/loading-gate",
3057
+ description: "Signals when the app is in a loading state",
3058
+ eventTypes: ["ui.state-change", "app.init"],
3059
+ impl: (state) => {
3060
+ const ctx = state.context;
3061
+ if (ctx.loading) {
3062
+ return RuleResult.emit([fact("ui.loading-gate", { active: true })]);
3063
+ }
3064
+ return RuleResult.retract(["ui.loading-gate"], "Not loading");
3065
+ },
3066
+ contract: {
3067
+ ruleId: "RULE_ID_PLACEHOLDER",
3068
+ behavior: "Emits ui.loading-gate when context.loading is true, retracts when false",
3069
+ examples: [
3070
+ { given: "loading is true", when: "ui state changes", then: "ui.loading-gate emitted" },
3071
+ { given: "loading is false", when: "ui state changes", then: "ui.loading-gate retracted" }
3072
+ ],
3073
+ invariants: ["Loading gate must reflect context.loading exactly"]
3074
+ }
3075
+ };
3076
+ var errorDisplayRule = {
3077
+ id: "ui/error-display",
3078
+ description: "Signals when an error should be displayed to the user",
3079
+ eventTypes: ["ui.state-change", "app.error"],
3080
+ impl: (state) => {
3081
+ const ctx = state.context;
3082
+ if (ctx.error) {
3083
+ return RuleResult.emit([fact("ui.error-display", { message: ctx.error, severity: "error" })]);
3084
+ }
3085
+ return RuleResult.retract(["ui.error-display"], "Error cleared");
3086
+ },
3087
+ contract: {
3088
+ ruleId: "RULE_ID_PLACEHOLDER",
3089
+ behavior: "Emits ui.error-display when context.error is non-null, retracts when cleared",
3090
+ examples: [
3091
+ { given: "error is set", when: "ui state changes", then: "ui.error-display emitted with message" },
3092
+ { given: "error is null", when: "ui state changes", then: "ui.error-display retracted" }
3093
+ ],
3094
+ invariants: ["Error display must clear when error is null"]
3095
+ }
3096
+ };
3097
+ var offlineIndicatorRule = {
3098
+ id: "ui/offline-indicator",
3099
+ description: "Signals when the app is offline",
3100
+ eventTypes: ["ui.state-change", "network.change"],
3101
+ impl: (state) => {
3102
+ if (state.context.offline) {
3103
+ return RuleResult.emit([fact("ui.offline", { message: "You are offline. Changes will sync when reconnected." })]);
3104
+ }
3105
+ return RuleResult.retract(["ui.offline"], "Back online");
3106
+ },
3107
+ contract: {
3108
+ ruleId: "RULE_ID_PLACEHOLDER",
3109
+ behavior: "Emits ui.offline when context.offline is true, retracts when back online",
3110
+ examples: [
3111
+ { given: "offline is true", when: "network changes", then: "ui.offline emitted" },
3112
+ { given: "offline is false", when: "network changes", then: "ui.offline retracted" }
3113
+ ],
3114
+ invariants: ["Offline indicator must match actual connectivity"]
3115
+ }
3116
+ };
3117
+ var dirtyGuardRule = {
3118
+ id: "ui/dirty-guard",
3119
+ description: "Warns when there are unsaved changes",
3120
+ eventTypes: ["ui.state-change", "navigation.request"],
3121
+ impl: (state) => {
3122
+ if (state.context.dirty) {
3123
+ return RuleResult.emit([fact("ui.unsaved-warning", {
3124
+ message: "You have unsaved changes",
3125
+ blocking: true
3126
+ })]);
3127
+ }
3128
+ return RuleResult.retract(["ui.unsaved-warning"], "No unsaved changes");
3129
+ },
3130
+ contract: {
3131
+ ruleId: "RULE_ID_PLACEHOLDER",
3132
+ behavior: "Emits ui.unsaved-warning when context.dirty is true, retracts when saved",
3133
+ examples: [
3134
+ { given: "dirty is true", when: "ui state changes", then: "ui.unsaved-warning emitted with blocking=true" },
3135
+ { given: "dirty is false", when: "ui state changes", then: "ui.unsaved-warning retracted" }
3136
+ ],
3137
+ invariants: ["Dirty guard must clear after save"]
3138
+ }
3139
+ };
3140
+ var initGateRule = {
3141
+ id: "ui/init-gate",
3142
+ description: "Signals whether the app has completed initialization",
3143
+ eventTypes: ["ui.state-change", "app.init"],
3144
+ impl: (state) => {
3145
+ if (!state.context.initialized) {
3146
+ return RuleResult.emit([fact("ui.init-pending", {
3147
+ message: "App is initializing..."
3148
+ })]);
3149
+ }
3150
+ return RuleResult.retract(["ui.init-pending"], "App initialized");
3151
+ },
3152
+ contract: {
3153
+ ruleId: "RULE_ID_PLACEHOLDER",
3154
+ behavior: "Emits ui.init-pending until context.initialized is true",
3155
+ examples: [
3156
+ { given: "initialized is false", when: "app starts", then: "ui.init-pending emitted" },
3157
+ { given: "initialized is true", when: "init completes", then: "ui.init-pending retracted" }
3158
+ ],
3159
+ invariants: ["Init gate must clear exactly once, when initialization completes"]
3160
+ }
3161
+ };
3162
+ var viewportRule = {
3163
+ id: "ui/viewport-class",
3164
+ description: "Classifies viewport size for responsive layout decisions",
3165
+ eventTypes: ["ui.state-change", "ui.resize"],
3166
+ impl: (state) => {
3167
+ const vp = state.context.viewport;
3168
+ if (!vp) return RuleResult.skip("No viewport data");
3169
+ return RuleResult.emit([fact("ui.viewport-class", {
3170
+ viewport: vp,
3171
+ compact: vp === "mobile",
3172
+ showSidebar: vp !== "mobile"
3173
+ })]);
3174
+ },
3175
+ contract: {
3176
+ ruleId: "RULE_ID_PLACEHOLDER",
3177
+ behavior: "Classifies viewport into responsive layout hints",
3178
+ examples: [
3179
+ { given: "viewport is mobile", when: "resize event", then: "compact=true, showSidebar=false" },
3180
+ { given: "viewport is desktop", when: "resize event", then: "compact=false, showSidebar=true" }
3181
+ ],
3182
+ invariants: ["Viewport class must update on every resize event"]
3183
+ }
3184
+ };
3185
+ var noInteractionWhileLoadingConstraint = {
3186
+ id: "ui/no-interaction-while-loading",
3187
+ description: "Prevents data mutations while a load operation is in progress",
3188
+ impl: (state) => {
3189
+ if (state.context.loading) {
3190
+ return "Cannot perform action while data is loading";
3191
+ }
3192
+ return true;
3193
+ },
3194
+ contract: {
3195
+ ruleId: "RULE_ID_PLACEHOLDER",
3196
+ behavior: "Fails when context.loading is true",
3197
+ examples: [
3198
+ { given: "loading is true", when: "action attempted", then: "violation" },
3199
+ { given: "loading is false", when: "action attempted", then: "pass" }
3200
+ ],
3201
+ invariants: ["Must always fail during loading"]
3202
+ }
3203
+ };
3204
+ var mustBeInitializedConstraint = {
3205
+ id: "ui/must-be-initialized",
3206
+ description: "Requires app initialization before user interactions",
3207
+ impl: (state) => {
3208
+ if (!state.context.initialized) {
3209
+ return "App must be initialized before performing this action";
3210
+ }
3211
+ return true;
3212
+ },
3213
+ contract: {
3214
+ ruleId: "RULE_ID_PLACEHOLDER",
3215
+ behavior: "Fails when context.initialized is false",
3216
+ examples: [
3217
+ { given: "initialized is false", when: "action attempted", then: "violation" },
3218
+ { given: "initialized is true", when: "action attempted", then: "pass" }
3219
+ ],
3220
+ invariants: ["Must always fail before init completes"]
3221
+ }
3222
+ };
3223
+ var uiModule = {
3224
+ rules: [
3225
+ loadingGateRule,
3226
+ errorDisplayRule,
3227
+ offlineIndicatorRule,
3228
+ dirtyGuardRule,
3229
+ initGateRule,
3230
+ viewportRule
3231
+ ],
3232
+ constraints: [
3233
+ noInteractionWhileLoadingConstraint,
3234
+ mustBeInitializedConstraint
3235
+ ],
3236
+ meta: {
3237
+ name: "praxis-ui",
3238
+ version: "1.0.0",
3239
+ description: "Predefined UI rules and constraints \u2014 separate from business logic"
3240
+ }
3241
+ };
3242
+ function createUIModule(options) {
3243
+ const allRules = uiModule.rules;
3244
+ const allConstraints = uiModule.constraints;
3245
+ const selectedRules = options.rules ? allRules.filter((r) => options.rules.includes(r.id)) : allRules;
3246
+ const selectedConstraints = options.constraints ? allConstraints.filter((c) => options.constraints.includes(c.id)) : allConstraints;
3247
+ return {
3248
+ rules: [...selectedRules, ...options.extraRules ?? []],
3249
+ constraints: [...selectedConstraints, ...options.extraConstraints ?? []],
3250
+ meta: { ...uiModule.meta, customized: true }
3251
+ };
3252
+ }
3253
+ function uiStateChanged(changes) {
3254
+ return { tag: "ui.state-change", payload: changes ?? {} };
3255
+ }
3256
+ function navigationRequest(to) {
3257
+ return { tag: "navigation.request", payload: { to } };
3258
+ }
3259
+ function resizeEvent(width, height) {
3260
+ return { tag: "ui.resize", payload: { width, height } };
3261
+ }
3262
+
3263
+ // src/core/completeness.ts
3264
+ function auditCompleteness(manifest, registryRuleIds, registryConstraintIds, rulesWithContracts, config) {
3265
+ const threshold = config?.threshold ?? 90;
3266
+ const domainBranches = manifest.branches.filter((b) => b.kind === "domain");
3267
+ const coveredDomain = domainBranches.filter((b) => b.coveredBy && registryRuleIds.includes(b.coveredBy));
3268
+ const uncoveredDomain = domainBranches.filter((b) => !b.coveredBy || !registryRuleIds.includes(b.coveredBy));
3269
+ const invariantBranches = manifest.branches.filter((b) => b.kind === "invariant");
3270
+ const coveredInvariants = invariantBranches.filter((b) => b.coveredBy && registryConstraintIds.includes(b.coveredBy));
3271
+ const uncoveredInvariants = invariantBranches.filter((b) => !b.coveredBy || !registryConstraintIds.includes(b.coveredBy));
3272
+ const needContracts = manifest.rulesNeedingContracts;
3273
+ const haveContracts = needContracts.filter((id) => rulesWithContracts.includes(id));
3274
+ const missingContracts = needContracts.filter((id) => !rulesWithContracts.includes(id));
3275
+ const neededFields = manifest.stateFields.filter((f) => f.usedByRule);
3276
+ const coveredFields = neededFields.filter((f) => f.inContext);
3277
+ const missingFields = neededFields.filter((f) => !f.inContext);
3278
+ const coveredTransitions = manifest.transitions.filter((t) => t.eventTag);
3279
+ const missingTransitions = manifest.transitions.filter((t) => !t.eventTag);
3280
+ const ruleScore = domainBranches.length > 0 ? coveredDomain.length / domainBranches.length * 40 : 40;
3281
+ const constraintScore = invariantBranches.length > 0 ? coveredInvariants.length / invariantBranches.length * 20 : 20;
3282
+ const contractScore = needContracts.length > 0 ? haveContracts.length / needContracts.length * 15 : 15;
3283
+ const contextScore = neededFields.length > 0 ? coveredFields.length / neededFields.length * 15 : 15;
3284
+ const eventScore = manifest.transitions.length > 0 ? coveredTransitions.length / manifest.transitions.length * 10 : 10;
3285
+ const score = Math.round(ruleScore + constraintScore + contractScore + contextScore + eventScore);
3286
+ const rating = score >= 90 ? "complete" : score >= 70 ? "good" : score >= 50 ? "partial" : "incomplete";
3287
+ const report = {
3288
+ score,
3289
+ rating,
3290
+ rules: { total: domainBranches.length, covered: coveredDomain.length, uncovered: uncoveredDomain },
3291
+ constraints: { total: invariantBranches.length, covered: coveredInvariants.length, uncovered: uncoveredInvariants },
3292
+ contracts: { total: needContracts.length, withContracts: haveContracts.length, missing: missingContracts },
3293
+ context: { total: neededFields.length, covered: coveredFields.length, missing: missingFields },
3294
+ events: { total: manifest.transitions.length, covered: coveredTransitions.length, missing: missingTransitions }
3295
+ };
3296
+ if (config?.strict && score < threshold) {
3297
+ throw new Error(`Praxis completeness ${score}/100 (${rating}) \u2014 below threshold ${threshold}. ${uncoveredDomain.length} uncovered rules, ${uncoveredInvariants.length} uncovered invariants, ${missingContracts.length} missing contracts.`);
3298
+ }
3299
+ return report;
3300
+ }
3301
+ function formatReport(report) {
3302
+ const lines = [];
3303
+ const icon = report.rating === "complete" ? "\u2705" : report.rating === "good" ? "\u{1F7E2}" : report.rating === "partial" ? "\u{1F7E1}" : "\u{1F534}";
3304
+ lines.push(`${icon} Praxis Completeness: ${report.score}/100 (${report.rating})`);
3305
+ lines.push("");
3306
+ lines.push(`Rules: ${report.rules.covered}/${report.rules.total} domain branches covered (${pct(report.rules.covered, report.rules.total)})`);
3307
+ lines.push(`Constraints: ${report.constraints.covered}/${report.constraints.total} invariants covered (${pct(report.constraints.covered, report.constraints.total)})`);
3308
+ lines.push(`Contracts: ${report.contracts.withContracts}/${report.contracts.total} rules have contracts (${pct(report.contracts.withContracts, report.contracts.total)})`);
3309
+ lines.push(`Context: ${report.context.covered}/${report.context.total} state fields in context (${pct(report.context.covered, report.context.total)})`);
3310
+ lines.push(`Events: ${report.events.covered}/${report.events.total} transitions have events (${pct(report.events.covered, report.events.total)})`);
3311
+ if (report.rules.uncovered.length > 0) {
3312
+ lines.push("");
3313
+ lines.push("Uncovered domain logic:");
3314
+ for (const b of report.rules.uncovered) {
3315
+ lines.push(` \u274C ${b.location}: ${b.condition}${b.note ? ` \u2014 ${b.note}` : ""}`);
3316
+ }
3317
+ }
3318
+ if (report.constraints.uncovered.length > 0) {
3319
+ lines.push("");
3320
+ lines.push("Uncovered invariants:");
3321
+ for (const b of report.constraints.uncovered) {
3322
+ lines.push(` \u274C ${b.location}: ${b.condition}${b.note ? ` \u2014 ${b.note}` : ""}`);
3323
+ }
3324
+ }
3325
+ if (report.contracts.missing.length > 0) {
3326
+ lines.push("");
3327
+ lines.push("Rules missing contracts:");
3328
+ for (const id of report.contracts.missing) {
3329
+ lines.push(` \u{1F4DD} ${id}`);
3330
+ }
3331
+ }
3332
+ if (report.events.missing.length > 0) {
3333
+ lines.push("");
3334
+ lines.push("State transitions without events:");
3335
+ for (const t of report.events.missing) {
3336
+ lines.push(` \u26A1 ${t.location}: ${t.description}`);
3337
+ }
3338
+ }
3339
+ return lines.join("\n");
3340
+ }
3341
+ function pct(a, b) {
3342
+ if (b === 0) return "100%";
3343
+ return Math.round(a / b * 100) + "%";
3344
+ }
3026
3345
  export {
3027
3346
  ActorManager,
3347
+ Expectation,
3348
+ ExpectationSet,
3028
3349
  ReactiveLogicEngine2 as FrameworkAgnosticReactiveEngine,
3029
3350
  InMemoryPraxisDB,
3030
3351
  LogicEngine,
@@ -3037,14 +3358,18 @@ export {
3037
3358
  PraxisSchemaRegistry,
3038
3359
  ReactiveLogicEngine,
3039
3360
  RegistryIntrospector,
3361
+ RuleResult,
3040
3362
  StateDocsGenerator,
3041
3363
  attachAllIntegrations,
3042
3364
  attachTauriToEngine,
3043
3365
  attachToEngine,
3044
3366
  attachUnumToEngine,
3367
+ auditCompleteness,
3368
+ branchRules,
3045
3369
  canvasToMermaid,
3046
3370
  canvasToSchema,
3047
3371
  canvasToYaml,
3372
+ commitFromState,
3048
3373
  createCanvasEditor,
3049
3374
  createReactiveEngine2 as createFrameworkAgnosticReactiveEngine,
3050
3375
  createInMemoryDB,
@@ -3063,28 +3388,56 @@ export {
3063
3388
  createStateDocsGenerator,
3064
3389
  createTauriPraxisAdapter,
3065
3390
  createTimerActor,
3391
+ createUIModule,
3066
3392
  createUnifiedApp,
3067
3393
  createUnumAdapter,
3394
+ dataRules,
3068
3395
  defineConstraint,
3069
3396
  defineEvent,
3070
3397
  defineFact,
3398
+ defineGate,
3071
3399
  defineModule,
3072
3400
  defineRule,
3401
+ dirtyGuardRule,
3402
+ errorDisplayRule,
3403
+ expectBehavior,
3404
+ expectationGate,
3405
+ fact,
3073
3406
  filterEvents,
3074
3407
  filterFacts,
3075
3408
  findEvent,
3076
3409
  findFact,
3410
+ formRules,
3411
+ formatGate,
3412
+ formatReport,
3413
+ formatVerificationReport,
3077
3414
  generateDocs,
3078
3415
  generateId,
3079
3416
  generateTauriConfig,
3080
3417
  getEventPath,
3081
3418
  getFactPath,
3082
3419
  getSchemaPath,
3420
+ initGateRule,
3421
+ inputRules,
3422
+ lintGate,
3083
3423
  loadSchemaFromJson,
3084
3424
  loadSchemaFromYaml,
3425
+ loadingGateRule,
3426
+ mustBeInitializedConstraint,
3427
+ navigationRequest,
3428
+ navigationRules,
3429
+ noInteractionWhileLoadingConstraint,
3430
+ offlineIndicatorRule,
3085
3431
  registerSchema,
3432
+ resizeEvent,
3086
3433
  schemaToCanvas,
3434
+ semverContract,
3435
+ toastRules,
3436
+ uiModule,
3437
+ uiStateChanged,
3087
3438
  validateForGeneration,
3088
3439
  validateSchema,
3089
- validateWithGuardian
3440
+ validateWithGuardian,
3441
+ verify,
3442
+ viewportRule
3090
3443
  };
@@ -1,5 +1,6 @@
1
- import { P as PraxisState, a as PraxisEvent, L as LogicEngine } from '../reactive-engine.svelte-DjynI82A.js';
2
- export { o as ReactiveEngineOptions, p as ReactiveLogicEngine, s as createReactiveEngine } from '../reactive-engine.svelte-DjynI82A.js';
1
+ import { L as LogicEngine } from '../reactive-engine.svelte-DgVTqHLc.js';
2
+ export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-DgVTqHLc.js';
3
+ import { P as PraxisState, a as PraxisEvent } from '../rules-i1LHpnGd.js';
3
4
 
4
5
  /**
5
6
  * Svelte v5 Integration