@plures/praxis 1.4.4 → 2.0.3

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 (59) hide show
  1. package/README.md +164 -1067
  2. package/dist/browser/chunk-IUEKGHQN.js +373 -0
  3. package/dist/browser/factory/index.d.ts +2 -1
  4. package/dist/browser/index.d.ts +7 -4
  5. package/dist/browser/index.js +18 -6
  6. package/dist/browser/integrations/svelte.d.ts +4 -3
  7. package/dist/browser/project/index.d.ts +2 -1
  8. package/dist/browser/{reactive-engine.svelte-DgVTqHLc.d.ts → reactive-engine.svelte-BwWadvAW.d.ts} +2 -1
  9. package/dist/browser/rule-result-DcXWe9tn.d.ts +206 -0
  10. package/dist/browser/{rules-i1LHpnGd.d.ts → rules-BaWMqxuG.d.ts} +2 -205
  11. package/dist/browser/unified/index.d.ts +239 -0
  12. package/dist/browser/unified/index.js +20 -0
  13. package/dist/node/chunk-IUEKGHQN.js +373 -0
  14. package/dist/node/cli/index.js +1 -1
  15. package/dist/node/index.cjs +377 -0
  16. package/dist/node/index.d.cts +4 -2
  17. package/dist/node/index.d.ts +4 -2
  18. package/dist/node/index.js +19 -7
  19. package/dist/node/integrations/svelte.d.cts +3 -2
  20. package/dist/node/integrations/svelte.d.ts +3 -2
  21. package/dist/node/integrations/svelte.js +2 -2
  22. package/dist/node/{reactive-engine.svelte-DekxqFu0.d.ts → reactive-engine.svelte-BBZLMzus.d.ts} +3 -79
  23. package/dist/node/{reactive-engine.svelte-Cg0Yc2Hs.d.cts → reactive-engine.svelte-Cbq_V20o.d.cts} +3 -79
  24. package/dist/node/rule-result-B9GMivAn.d.cts +80 -0
  25. package/dist/node/rule-result-Bo3sFMmN.d.ts +80 -0
  26. package/dist/node/unified/index.cjs +494 -0
  27. package/dist/node/unified/index.d.cts +240 -0
  28. package/dist/node/unified/index.d.ts +240 -0
  29. package/dist/node/unified/index.js +21 -0
  30. package/docs/README.md +58 -102
  31. package/docs/archive/1.x/CONVERSATIONS_IMPLEMENTATION.md +207 -0
  32. package/docs/archive/1.x/DECISION_LEDGER_IMPLEMENTATION.md +109 -0
  33. package/docs/archive/1.x/DECISION_LEDGER_SUMMARY.md +424 -0
  34. package/docs/archive/1.x/ELEVATION_SUMMARY.md +249 -0
  35. package/docs/archive/1.x/FEATURE_SUMMARY.md +238 -0
  36. package/docs/archive/1.x/GOLDEN_PATH_IMPLEMENTATION.md +280 -0
  37. package/docs/archive/1.x/IMPLEMENTATION.md +166 -0
  38. package/docs/archive/1.x/IMPLEMENTATION_COMPLETE.md +389 -0
  39. package/docs/archive/1.x/IMPLEMENTATION_SUMMARY.md +59 -0
  40. package/docs/archive/1.x/INTEGRATION_ENHANCEMENT_SUMMARY.md +238 -0
  41. package/docs/archive/1.x/KNO_ENG_REFACTORING_SUMMARY.md +198 -0
  42. package/docs/archive/1.x/MONOREPO_SUMMARY.md +158 -0
  43. package/docs/archive/1.x/README.md +28 -0
  44. package/docs/archive/1.x/SVELTE_INTEGRATION_SUMMARY.md +415 -0
  45. package/docs/archive/1.x/TASK_1_COMPLETE.md +235 -0
  46. package/docs/archive/1.x/TASK_1_SUMMARY.md +281 -0
  47. package/docs/archive/1.x/VERSION_0.2.0_RELEASE_NOTES.md +288 -0
  48. package/docs/archive/1.x/ValidationChecklist.md +7 -0
  49. package/package.json +13 -1
  50. package/src/index.browser.ts +20 -0
  51. package/src/index.ts +21 -0
  52. package/src/unified/__tests__/unified-qa.test.ts +761 -0
  53. package/src/unified/__tests__/unified.test.ts +396 -0
  54. package/src/unified/core.ts +534 -0
  55. package/src/unified/index.ts +32 -0
  56. package/src/unified/rules.ts +66 -0
  57. package/src/unified/types.ts +148 -0
  58. package/dist/node/{chunk-ZO2LU4G4.js → chunk-WFRHXZBP.js} +3 -3
  59. package/dist/node/{validate-5PSWJTIC.js → validate-BY7JNY7H.js} +1 -1
@@ -0,0 +1,494 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/unified/index.ts
21
+ var unified_exports = {};
22
+ __export(unified_exports, {
23
+ RuleResult: () => RuleResult,
24
+ createApp: () => createApp,
25
+ defineConstraint: () => defineConstraint,
26
+ defineModule: () => defineModule,
27
+ definePath: () => definePath,
28
+ defineRule: () => defineRule,
29
+ fact: () => fact
30
+ });
31
+ module.exports = __toCommonJS(unified_exports);
32
+
33
+ // src/unified/core.ts
34
+ var _idCounter = 0;
35
+ function nextId() {
36
+ return `px:${Date.now()}-${++_idCounter}`;
37
+ }
38
+ function createApp(config) {
39
+ const paths = /* @__PURE__ */ new Map();
40
+ for (const schema of config.schema) {
41
+ paths.set(schema.path, {
42
+ schema,
43
+ value: schema.initial,
44
+ subscribers: /* @__PURE__ */ new Set(),
45
+ lastUpdated: 0,
46
+ updateCount: 0
47
+ });
48
+ }
49
+ let facts = [];
50
+ const factMap = /* @__PURE__ */ new Map();
51
+ const timeline = [];
52
+ const maxTimeline = 1e4;
53
+ function recordTimeline(path, kind, data) {
54
+ const entry = {
55
+ id: nextId(),
56
+ timestamp: Date.now(),
57
+ path,
58
+ kind,
59
+ data
60
+ };
61
+ timeline.push(entry);
62
+ if (timeline.length > maxTimeline) {
63
+ timeline.splice(0, timeline.length - maxTimeline);
64
+ }
65
+ }
66
+ const ruleStates = (config.rules ?? []).map((rule) => ({
67
+ rule,
68
+ lastResult: null,
69
+ emittedTags: /* @__PURE__ */ new Set()
70
+ }));
71
+ const constraints = config.constraints ?? [];
72
+ const livenessConfig = config.liveness;
73
+ const initTime = Date.now();
74
+ let livenessTimer = null;
75
+ if (livenessConfig) {
76
+ const timeout = livenessConfig.timeoutMs ?? 5e3;
77
+ livenessTimer = setTimeout(() => {
78
+ for (const expectedPath of livenessConfig.expect) {
79
+ const state = paths.get(expectedPath);
80
+ if (!state || state.updateCount === 0) {
81
+ const elapsed = Date.now() - initTime;
82
+ recordTimeline(expectedPath, "liveness", {
83
+ stale: true,
84
+ elapsed,
85
+ message: `Path "${expectedPath}" never updated after ${elapsed}ms`
86
+ });
87
+ livenessConfig.onStale?.(expectedPath, elapsed);
88
+ }
89
+ }
90
+ }, timeout);
91
+ }
92
+ function getPathValues(watchPaths) {
93
+ const values = {};
94
+ for (const p of watchPaths) {
95
+ const state = paths.get(p);
96
+ values[p] = state ? state.value : void 0;
97
+ }
98
+ return values;
99
+ }
100
+ function notify(path, value) {
101
+ const state = paths.get(path);
102
+ if (!state) return;
103
+ for (const cb of state.subscribers) {
104
+ try {
105
+ cb(value);
106
+ } catch (err) {
107
+ console.error(`[praxis] Subscriber error for "${path}":`, err);
108
+ }
109
+ }
110
+ }
111
+ function checkConstraints(path, value) {
112
+ const violations = [];
113
+ for (const c of constraints) {
114
+ if (!c.watch.includes(path)) continue;
115
+ const values = getPathValues(c.watch);
116
+ values[path] = value;
117
+ try {
118
+ const result = c.validate(values);
119
+ if (result !== true) {
120
+ violations.push({
121
+ kind: "constraint-violation",
122
+ message: result,
123
+ data: { constraintId: c.id, path, description: c.description }
124
+ });
125
+ recordTimeline(path, "constraint-check", {
126
+ constraintId: c.id,
127
+ violated: true,
128
+ message: result
129
+ });
130
+ }
131
+ } catch (err) {
132
+ violations.push({
133
+ kind: "constraint-violation",
134
+ message: `Constraint "${c.id}" threw: ${err instanceof Error ? err.message : String(err)}`,
135
+ data: { constraintId: c.id, error: err }
136
+ });
137
+ }
138
+ }
139
+ return violations;
140
+ }
141
+ function evaluateRules() {
142
+ const newFacts = [];
143
+ const retractions = [];
144
+ for (const rs of ruleStates) {
145
+ const values = getPathValues(rs.rule.watch);
146
+ try {
147
+ const result = rs.rule.evaluate(values, [...facts]);
148
+ rs.lastResult = result;
149
+ recordTimeline(rs.rule.watch[0] ?? "*", "rule-eval", {
150
+ ruleId: rs.rule.id,
151
+ kind: result.kind,
152
+ reason: result.reason
153
+ });
154
+ switch (result.kind) {
155
+ case "emit":
156
+ newFacts.push(...result.facts);
157
+ for (const f of result.facts) {
158
+ rs.emittedTags.add(f.tag);
159
+ }
160
+ break;
161
+ case "retract":
162
+ retractions.push(...result.retractTags);
163
+ for (const tag of result.retractTags) {
164
+ rs.emittedTags.delete(tag);
165
+ }
166
+ break;
167
+ case "noop":
168
+ case "skip":
169
+ if (rs.emittedTags.size > 0) {
170
+ retractions.push(...rs.emittedTags);
171
+ rs.emittedTags.clear();
172
+ }
173
+ break;
174
+ }
175
+ } catch (err) {
176
+ console.error(`[praxis] Rule "${rs.rule.id}" error:`, err);
177
+ }
178
+ }
179
+ if (retractions.length > 0) {
180
+ const retractSet = new Set(retractions);
181
+ for (const tag of retractSet) {
182
+ factMap.delete(tag);
183
+ }
184
+ }
185
+ for (const f of newFacts) {
186
+ factMap.set(f.tag, f);
187
+ }
188
+ facts = Array.from(factMap.values());
189
+ }
190
+ function query(path, opts) {
191
+ let state = paths.get(path);
192
+ if (!state) {
193
+ state = {
194
+ schema: { path, initial: void 0 },
195
+ value: void 0,
196
+ subscribers: /* @__PURE__ */ new Set(),
197
+ lastUpdated: 0,
198
+ updateCount: 0
199
+ };
200
+ paths.set(path, state);
201
+ }
202
+ const ref = {
203
+ get current() {
204
+ const s = paths.get(path);
205
+ return applyQueryOpts(s?.value ?? state.schema.initial, opts);
206
+ },
207
+ subscribe(cb) {
208
+ const wrappedCb = (rawValue) => {
209
+ const processed = applyQueryOpts(rawValue, opts);
210
+ cb(processed);
211
+ };
212
+ const s = paths.get(path);
213
+ s.subscribers.add(wrappedCb);
214
+ try {
215
+ cb(ref.current);
216
+ } catch (err) {
217
+ console.error(`[praxis] query("${path}") subscriber init error:`, err);
218
+ }
219
+ return () => {
220
+ s.subscribers.delete(wrappedCb);
221
+ };
222
+ }
223
+ };
224
+ return ref;
225
+ }
226
+ function applyQueryOpts(value, opts) {
227
+ if (!opts || !Array.isArray(value)) return value;
228
+ let result = [...value];
229
+ if (opts.where) result = result.filter(opts.where);
230
+ if (opts.sort) result.sort(opts.sort);
231
+ if (opts.select) result = result.map(opts.select);
232
+ if (opts.limit) result = result.slice(0, opts.limit);
233
+ return result;
234
+ }
235
+ function mutateInternal(path, value) {
236
+ const violations = checkConstraints(path, value);
237
+ if (violations.length > 0) {
238
+ return { violations, emittedFacts: [] };
239
+ }
240
+ const state = paths.get(path);
241
+ if (!state) {
242
+ paths.set(path, {
243
+ schema: { path, initial: value },
244
+ value,
245
+ subscribers: /* @__PURE__ */ new Set(),
246
+ lastUpdated: Date.now(),
247
+ updateCount: 1
248
+ });
249
+ } else {
250
+ const before = state.value;
251
+ state.value = value;
252
+ state.lastUpdated = Date.now();
253
+ state.updateCount++;
254
+ recordTimeline(path, "mutation", {
255
+ before: summarize(before),
256
+ after: summarize(value)
257
+ });
258
+ }
259
+ notify(path, value);
260
+ const factsBefore = facts.length;
261
+ evaluateRules();
262
+ const emittedFacts = facts.slice(factsBefore);
263
+ return { violations: [], emittedFacts };
264
+ }
265
+ function mutate(path, value) {
266
+ const { violations, emittedFacts } = mutateInternal(path, value);
267
+ return {
268
+ accepted: violations.length === 0,
269
+ violations,
270
+ facts: emittedFacts
271
+ };
272
+ }
273
+ function batchMutate(fn) {
274
+ const allViolations = [];
275
+ const pendingWrites = [];
276
+ fn((path, value) => {
277
+ const violations = checkConstraints(path, value);
278
+ if (violations.length > 0) {
279
+ allViolations.push(...violations);
280
+ } else {
281
+ pendingWrites.push({ path, value });
282
+ }
283
+ });
284
+ if (allViolations.length > 0) {
285
+ return { accepted: false, violations: allViolations, facts: [] };
286
+ }
287
+ for (const { path, value } of pendingWrites) {
288
+ const state = paths.get(path);
289
+ if (state) {
290
+ const before = state.value;
291
+ state.value = value;
292
+ state.lastUpdated = Date.now();
293
+ state.updateCount++;
294
+ recordTimeline(path, "mutation", {
295
+ before: summarize(before),
296
+ after: summarize(value)
297
+ });
298
+ } else {
299
+ paths.set(path, {
300
+ schema: { path, initial: value },
301
+ value,
302
+ subscribers: /* @__PURE__ */ new Set(),
303
+ lastUpdated: Date.now(),
304
+ updateCount: 1
305
+ });
306
+ }
307
+ }
308
+ for (const { path, value } of pendingWrites) {
309
+ notify(path, value);
310
+ }
311
+ const factsBefore = facts.length;
312
+ evaluateRules();
313
+ return {
314
+ accepted: true,
315
+ violations: [],
316
+ facts: facts.slice(factsBefore)
317
+ };
318
+ }
319
+ function getLiveness() {
320
+ const result = {};
321
+ const now = Date.now();
322
+ const watchPaths = livenessConfig?.expect ?? [];
323
+ for (const p of watchPaths) {
324
+ const state = paths.get(p);
325
+ const lastUpdated = state?.lastUpdated ?? 0;
326
+ const elapsed = lastUpdated > 0 ? now - lastUpdated : now - initTime;
327
+ result[p] = {
328
+ stale: !state || state.updateCount === 0,
329
+ lastUpdated,
330
+ elapsed
331
+ };
332
+ }
333
+ return result;
334
+ }
335
+ function destroy() {
336
+ if (livenessTimer) clearTimeout(livenessTimer);
337
+ for (const state of paths.values()) {
338
+ state.subscribers.clear();
339
+ }
340
+ paths.clear();
341
+ facts = [];
342
+ factMap.clear();
343
+ timeline.length = 0;
344
+ }
345
+ return {
346
+ query,
347
+ mutate,
348
+ batch: batchMutate,
349
+ facts: () => [...facts],
350
+ violations: () => {
351
+ const allViolations = [];
352
+ for (const c of constraints) {
353
+ const values = getPathValues(c.watch);
354
+ try {
355
+ const result = c.validate(values);
356
+ if (result !== true) {
357
+ allViolations.push({
358
+ kind: "constraint-violation",
359
+ message: result,
360
+ data: { constraintId: c.id }
361
+ });
362
+ }
363
+ } catch {
364
+ }
365
+ }
366
+ return allViolations;
367
+ },
368
+ timeline: () => [...timeline],
369
+ evaluate: evaluateRules,
370
+ destroy,
371
+ liveness: getLiveness
372
+ };
373
+ }
374
+ function summarize(value) {
375
+ if (value === null || value === void 0) return value;
376
+ if (typeof value !== "object") return value;
377
+ if (Array.isArray(value)) return `[Array(${value.length})]`;
378
+ const keys = Object.keys(value);
379
+ if (keys.length > 10) return `{Object(${keys.length} keys)}`;
380
+ return value;
381
+ }
382
+
383
+ // src/unified/types.ts
384
+ function definePath(path, initial, opts) {
385
+ return { path, initial, ...opts };
386
+ }
387
+
388
+ // src/core/rule-result.ts
389
+ var RuleResult = class _RuleResult {
390
+ /** The kind of result */
391
+ kind;
392
+ /** Facts produced (only for 'emit') */
393
+ facts;
394
+ /** Fact tags to retract (only for 'retract') */
395
+ retractTags;
396
+ /** Optional reason (for noop/skip/retract — useful for debugging) */
397
+ reason;
398
+ /** The rule ID that produced this result (set by engine) */
399
+ ruleId;
400
+ constructor(kind, facts, retractTags, reason) {
401
+ this.kind = kind;
402
+ this.facts = facts;
403
+ this.retractTags = retractTags;
404
+ this.reason = reason;
405
+ }
406
+ /**
407
+ * Rule produced facts.
408
+ *
409
+ * @example
410
+ * return RuleResult.emit([
411
+ * { tag: 'sprint.behind', payload: { deficit: 5 } }
412
+ * ]);
413
+ */
414
+ static emit(facts) {
415
+ if (facts.length === 0) {
416
+ throw new Error(
417
+ "RuleResult.emit() requires at least one fact. Use RuleResult.noop() or RuleResult.skip() when a rule has nothing to say."
418
+ );
419
+ }
420
+ return new _RuleResult("emit", facts, []);
421
+ }
422
+ /**
423
+ * Rule evaluated but had nothing to report.
424
+ * Unlike returning [], this is explicit and traceable.
425
+ *
426
+ * @example
427
+ * if (ctx.completedHours >= expectedHours) {
428
+ * return RuleResult.noop('Sprint is on pace');
429
+ * }
430
+ */
431
+ static noop(reason) {
432
+ return new _RuleResult("noop", [], [], reason);
433
+ }
434
+ /**
435
+ * Rule decided to skip because preconditions were not met.
436
+ * Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
437
+ *
438
+ * @example
439
+ * if (!ctx.sprintName) {
440
+ * return RuleResult.skip('No active sprint');
441
+ * }
442
+ */
443
+ static skip(reason) {
444
+ return new _RuleResult("skip", [], [], reason);
445
+ }
446
+ /**
447
+ * Rule retracts previously emitted facts by tag.
448
+ * Used when a condition that previously produced facts is no longer true.
449
+ *
450
+ * @example
451
+ * // Sprint was behind, but caught up
452
+ * if (ctx.completedHours >= expectedHours) {
453
+ * return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
454
+ * }
455
+ */
456
+ static retract(tags, reason) {
457
+ if (tags.length === 0) {
458
+ throw new Error("RuleResult.retract() requires at least one tag.");
459
+ }
460
+ return new _RuleResult("retract", [], tags, reason);
461
+ }
462
+ /** Whether this result produced facts */
463
+ get hasFacts() {
464
+ return this.facts.length > 0;
465
+ }
466
+ /** Whether this result retracts facts */
467
+ get hasRetractions() {
468
+ return this.retractTags.length > 0;
469
+ }
470
+ };
471
+ function fact(tag, payload) {
472
+ return { tag, payload };
473
+ }
474
+
475
+ // src/unified/rules.ts
476
+ function defineRule(rule) {
477
+ return rule;
478
+ }
479
+ function defineConstraint(constraint) {
480
+ return constraint;
481
+ }
482
+ function defineModule(name, rules) {
483
+ return { name, rules };
484
+ }
485
+ // Annotate the CommonJS export names for ESM import in node:
486
+ 0 && (module.exports = {
487
+ RuleResult,
488
+ createApp,
489
+ defineConstraint,
490
+ defineModule,
491
+ definePath,
492
+ defineRule,
493
+ fact
494
+ });