@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.
- package/README.md +164 -1067
- package/dist/browser/chunk-IUEKGHQN.js +373 -0
- package/dist/browser/factory/index.d.ts +2 -1
- package/dist/browser/index.d.ts +7 -4
- package/dist/browser/index.js +18 -6
- package/dist/browser/integrations/svelte.d.ts +4 -3
- package/dist/browser/project/index.d.ts +2 -1
- package/dist/browser/{reactive-engine.svelte-DgVTqHLc.d.ts → reactive-engine.svelte-BwWadvAW.d.ts} +2 -1
- package/dist/browser/rule-result-DcXWe9tn.d.ts +206 -0
- package/dist/browser/{rules-i1LHpnGd.d.ts → rules-BaWMqxuG.d.ts} +2 -205
- package/dist/browser/unified/index.d.ts +239 -0
- package/dist/browser/unified/index.js +20 -0
- package/dist/node/chunk-IUEKGHQN.js +373 -0
- package/dist/node/cli/index.js +1 -1
- package/dist/node/index.cjs +377 -0
- package/dist/node/index.d.cts +4 -2
- package/dist/node/index.d.ts +4 -2
- package/dist/node/index.js +19 -7
- package/dist/node/integrations/svelte.d.cts +3 -2
- package/dist/node/integrations/svelte.d.ts +3 -2
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{reactive-engine.svelte-DekxqFu0.d.ts → reactive-engine.svelte-BBZLMzus.d.ts} +3 -79
- package/dist/node/{reactive-engine.svelte-Cg0Yc2Hs.d.cts → reactive-engine.svelte-Cbq_V20o.d.cts} +3 -79
- package/dist/node/rule-result-B9GMivAn.d.cts +80 -0
- package/dist/node/rule-result-Bo3sFMmN.d.ts +80 -0
- package/dist/node/unified/index.cjs +494 -0
- package/dist/node/unified/index.d.cts +240 -0
- package/dist/node/unified/index.d.ts +240 -0
- package/dist/node/unified/index.js +21 -0
- package/docs/README.md +58 -102
- package/docs/archive/1.x/CONVERSATIONS_IMPLEMENTATION.md +207 -0
- package/docs/archive/1.x/DECISION_LEDGER_IMPLEMENTATION.md +109 -0
- package/docs/archive/1.x/DECISION_LEDGER_SUMMARY.md +424 -0
- package/docs/archive/1.x/ELEVATION_SUMMARY.md +249 -0
- package/docs/archive/1.x/FEATURE_SUMMARY.md +238 -0
- package/docs/archive/1.x/GOLDEN_PATH_IMPLEMENTATION.md +280 -0
- package/docs/archive/1.x/IMPLEMENTATION.md +166 -0
- package/docs/archive/1.x/IMPLEMENTATION_COMPLETE.md +389 -0
- package/docs/archive/1.x/IMPLEMENTATION_SUMMARY.md +59 -0
- package/docs/archive/1.x/INTEGRATION_ENHANCEMENT_SUMMARY.md +238 -0
- package/docs/archive/1.x/KNO_ENG_REFACTORING_SUMMARY.md +198 -0
- package/docs/archive/1.x/MONOREPO_SUMMARY.md +158 -0
- package/docs/archive/1.x/README.md +28 -0
- package/docs/archive/1.x/SVELTE_INTEGRATION_SUMMARY.md +415 -0
- package/docs/archive/1.x/TASK_1_COMPLETE.md +235 -0
- package/docs/archive/1.x/TASK_1_SUMMARY.md +281 -0
- package/docs/archive/1.x/VERSION_0.2.0_RELEASE_NOTES.md +288 -0
- package/docs/archive/1.x/ValidationChecklist.md +7 -0
- package/package.json +13 -1
- package/src/index.browser.ts +20 -0
- package/src/index.ts +21 -0
- package/src/unified/__tests__/unified-qa.test.ts +761 -0
- package/src/unified/__tests__/unified.test.ts +396 -0
- package/src/unified/core.ts +534 -0
- package/src/unified/index.ts +32 -0
- package/src/unified/rules.ts +66 -0
- package/src/unified/types.ts +148 -0
- package/dist/node/{chunk-ZO2LU4G4.js → chunk-WFRHXZBP.js} +3 -3
- 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
|
+
});
|