rulit 1.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.
- package/LICENSE.md +21 -0
- package/README.md +545 -0
- package/dist/cli/ui-template.hbs +313 -0
- package/dist/cli/ui.d.mts +9 -0
- package/dist/cli/ui.d.ts +9 -0
- package/dist/cli/ui.js +311 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli/ui.mjs +277 -0
- package/dist/cli/ui.mjs.map +1 -0
- package/dist/index.d.mts +745 -0
- package/dist/index.d.ts +745 -0
- package/dist/index.js +1357 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1322 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/ui.ts","../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","../../src/registry.ts"],"sourcesContent":["#!/usr/bin/env node\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport Handlebars from \"handlebars\";\nimport { registry } from \"../registry.js\";\n\ntype CliOptions = {\n port: number;\n load: string[];\n};\n\nexport async function startServer(options: CliOptions) {\n await loadModules(options.load, Date.now());\n\n const server = http.createServer((req, res) => {\n if (!req.url || req.url === \"/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(buildHtml());\n return;\n }\n\n res.writeHead(404);\n res.end(\"Not found\");\n });\n\n server.listen(options.port, () => {\n // eslint-disable-next-line no-console\n console.log(`Rulit UI running at http://localhost:${options.port}`);\n });\n}\n\nexport function buildHtml() {\n const entries = registry.list();\n const template = Handlebars.compile(loadTemplate());\n const view = {\n entries: entries.map((entry) => {\n const mermaid = registry.getMermaid(entry.id) ?? \"flowchart TD\\n empty\";\n const graph = registry.getGraph(entry.id) ?? { nodes: [], edges: [] };\n return {\n id: entry.id,\n name: entry.name ?? entry.id,\n createdAt: new Date(entry.createdAt).toLocaleString(),\n mermaid,\n mermaidEncoded: encodeURIComponent(mermaid),\n traces: registry.listTraces(entry.id).map((trace) => ({\n id: trace.id,\n createdAt: new Date(trace.createdAt).toLocaleString(),\n firedCount: trace.fired.length,\n matchedCount: trace.trace.filter((rule) => rule.matched).length,\n traceEncoded: encodeURIComponent(JSON.stringify(trace)),\n })),\n json: JSON.stringify(graph, null, 2),\n };\n }),\n };\n\n return template(view);\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n port: 5173,\n load: [],\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const value = args[i];\n if (!value) {\n continue;\n }\n if (value === \"--port\") {\n const next = args[i + 1];\n if (next) {\n options.port = Number(next);\n i += 1;\n }\n continue;\n }\n if (value === \"--load\") {\n const next = args[i + 1];\n if (next) {\n options.load.push(next);\n i += 1;\n }\n continue;\n }\n }\n\n const envLoad = process.env.RULIT_UI_LOAD;\n if (envLoad) {\n options.load.push(\n ...envLoad\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean),\n );\n }\n\n const envPort = process.env.RULIT_UI_PORT;\n if (envPort) {\n options.port = Number(envPort);\n }\n\n return options;\n}\n\nasync function loadModules(paths: string[], cacheBust: number) {\n for (const modulePath of paths) {\n const fullPath = path.resolve(process.cwd(), modulePath);\n await import(`${pathToFileURL(fullPath).href}?t=${cacheBust}`);\n }\n}\n\nconst isMain =\n typeof import.meta.url === \"string\" &&\n import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href;\nif (isMain) {\n const options = parseArgs(process.argv.slice(2));\n void startServer(options);\n}\n\nfunction loadTemplate() {\n const templatePath = resolveTemplatePath();\n if (!templatePath) {\n throw new Error(\"UI template not found. Expected ui-template.hbs near cli ui.\");\n }\n return fs.readFileSync(templatePath, \"utf8\");\n}\n\nfunction resolveTemplatePath(): string | null {\n const candidates: (string | URL)[] = [];\n\n if (typeof import.meta.url === \"string\") {\n candidates.push(\n new URL(\"./ui-template.hbs\", import.meta.url),\n new URL(\"../../src/cli/ui-template.hbs\", import.meta.url),\n );\n } else {\n // Fallback for CJS if necessary, though we prefer ESM for CLI\n candidates.push(path.join(__dirname, \"ui-template.hbs\"));\n }\n\n for (const candidate of candidates) {\n const filePath = typeof candidate === \"string\" ? candidate : fileURLToPath(candidate);\n if (fs.existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import type { RuleTrace, RulesetGraph, TraceRun } from \"./types\";\n\ntype GraphSource = {\n graph: () => RulesetGraph;\n toMermaid: () => string;\n};\n\ntype RegistryEntry = {\n id: string;\n name?: string;\n createdAt: number;\n source: GraphSource;\n traces: TraceRun[];\n};\n\ntype RegistryListItem = {\n id: string;\n name?: string;\n createdAt: number;\n};\n\nconst entries = new Map<string, RegistryEntry>();\nconst nameIndex = new Map<string, string>();\nlet counter = 0;\nlet traceCounter = 0;\nconst traceLimit = 25;\n\nfunction makeId() {\n return `ruleset-${counter++}`;\n}\n\n/**\n * In-memory registry of rulesets created in this process.\n */\nexport const registry = {\n /**\n * Register a ruleset in the global registry.\n *\n * @example\n * ```ts\n * const rs = Rules.ruleset<Facts, Effects>(\"eligibility\");\n * Rules.registry.list();\n * ```\n */\n register(source: GraphSource, name?: string): string {\n const id = makeId();\n entries.set(id, { id, name, createdAt: Date.now(), source, traces: [] });\n if (name) {\n nameIndex.set(name, id);\n }\n return id;\n },\n\n /**\n * List all registered rulesets.\n *\n * @example\n * ```ts\n * const list = Rules.registry.list();\n * ```\n */\n list(): RegistryListItem[] {\n return Array.from(entries.values()).map(({ id, name, createdAt }) => ({\n id,\n name,\n createdAt,\n }));\n },\n\n /**\n * Get a graph by id or ruleset name.\n *\n * @example\n * ```ts\n * const graph = Rules.registry.getGraph(\"eligibility\");\n * ```\n */\n getGraph(idOrName: string): RulesetGraph | undefined {\n const entry = getEntry(idOrName);\n return entry?.source.graph();\n },\n\n /**\n * Get Mermaid output by id or ruleset name.\n *\n * @example\n * ```ts\n * const mermaid = Rules.registry.getMermaid(\"eligibility\");\n * ```\n */\n getMermaid(idOrName: string): string | undefined {\n const entry = getEntry(idOrName);\n return entry?.source.toMermaid();\n },\n\n /**\n * Record a trace run for a ruleset. Keeps a rolling window of traces.\n *\n * @example\n * ```ts\n * Rules.registry.recordTrace(\"eligibility\", trace, fired);\n * ```\n */\n recordTrace(\n idOrName: string,\n trace: RuleTrace[],\n fired: string[],\n facts: unknown,\n ): TraceRun | undefined {\n const entry = getEntry(idOrName);\n if (!entry) {\n return undefined;\n }\n const run: TraceRun = {\n id: `trace-${traceCounter++}`,\n createdAt: Date.now(),\n facts,\n fired,\n trace,\n };\n entry.traces.push(run);\n if (entry.traces.length > traceLimit) {\n entry.traces.splice(0, entry.traces.length - traceLimit);\n }\n return run;\n },\n\n /**\n * List trace runs for a ruleset.\n *\n * @example\n * ```ts\n * const traces = Rules.registry.listTraces(\"eligibility\");\n * ```\n */\n listTraces(idOrName: string): TraceRun[] {\n const entry = getEntry(idOrName);\n return entry ? [...entry.traces] : [];\n },\n\n /**\n * Get a trace by id for a ruleset.\n *\n * @example\n * ```ts\n * const trace = Rules.registry.getTrace(\"eligibility\", \"trace-0\");\n * ```\n */\n getTrace(idOrName: string, traceId: string): TraceRun | undefined {\n const entry = getEntry(idOrName);\n return entry?.traces.find((run) => run.id === traceId);\n },\n\n /**\n * Clear the registry (useful in tests).\n *\n * @example\n * ```ts\n * Rules.registry.clear();\n * ```\n */\n clear(): void {\n entries.clear();\n nameIndex.clear();\n traceCounter = 0;\n },\n};\n\nfunction getEntry(idOrName: string): RegistryEntry | undefined {\n const byId = entries.get(idOrName);\n if (byId) {\n return byId;\n }\n const id = nameIndex.get(idOrName);\n return id ? entries.get(id) : undefined;\n}\n"],"mappings":"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","names":["http","entries","Handlebars","path","fs"]}
|
package/dist/cli/ui.mjs
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
7
|
+
var getDirname = () => path.dirname(getFilename());
|
|
8
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
9
|
+
|
|
10
|
+
// src/cli/ui.ts
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import http from "http";
|
|
13
|
+
import path2 from "path";
|
|
14
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
15
|
+
import Handlebars from "handlebars";
|
|
16
|
+
|
|
17
|
+
// src/registry.ts
|
|
18
|
+
var entries = /* @__PURE__ */ new Map();
|
|
19
|
+
var nameIndex = /* @__PURE__ */ new Map();
|
|
20
|
+
var counter = 0;
|
|
21
|
+
var traceCounter = 0;
|
|
22
|
+
var traceLimit = 25;
|
|
23
|
+
function makeId() {
|
|
24
|
+
return `ruleset-${counter++}`;
|
|
25
|
+
}
|
|
26
|
+
var registry = {
|
|
27
|
+
/**
|
|
28
|
+
* Register a ruleset in the global registry.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const rs = Rules.ruleset<Facts, Effects>("eligibility");
|
|
33
|
+
* Rules.registry.list();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
register(source, name) {
|
|
37
|
+
const id = makeId();
|
|
38
|
+
entries.set(id, { id, name, createdAt: Date.now(), source, traces: [] });
|
|
39
|
+
if (name) {
|
|
40
|
+
nameIndex.set(name, id);
|
|
41
|
+
}
|
|
42
|
+
return id;
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* List all registered rulesets.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const list = Rules.registry.list();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
list() {
|
|
53
|
+
return Array.from(entries.values()).map(({ id, name, createdAt }) => ({
|
|
54
|
+
id,
|
|
55
|
+
name,
|
|
56
|
+
createdAt
|
|
57
|
+
}));
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* Get a graph by id or ruleset name.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const graph = Rules.registry.getGraph("eligibility");
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
getGraph(idOrName) {
|
|
68
|
+
const entry = getEntry(idOrName);
|
|
69
|
+
return entry?.source.graph();
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Get Mermaid output by id or ruleset name.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const mermaid = Rules.registry.getMermaid("eligibility");
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
getMermaid(idOrName) {
|
|
80
|
+
const entry = getEntry(idOrName);
|
|
81
|
+
return entry?.source.toMermaid();
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Record a trace run for a ruleset. Keeps a rolling window of traces.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* Rules.registry.recordTrace("eligibility", trace, fired);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
recordTrace(idOrName, trace, fired, facts) {
|
|
92
|
+
const entry = getEntry(idOrName);
|
|
93
|
+
if (!entry) {
|
|
94
|
+
return void 0;
|
|
95
|
+
}
|
|
96
|
+
const run = {
|
|
97
|
+
id: `trace-${traceCounter++}`,
|
|
98
|
+
createdAt: Date.now(),
|
|
99
|
+
facts,
|
|
100
|
+
fired,
|
|
101
|
+
trace
|
|
102
|
+
};
|
|
103
|
+
entry.traces.push(run);
|
|
104
|
+
if (entry.traces.length > traceLimit) {
|
|
105
|
+
entry.traces.splice(0, entry.traces.length - traceLimit);
|
|
106
|
+
}
|
|
107
|
+
return run;
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
* List trace runs for a ruleset.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* const traces = Rules.registry.listTraces("eligibility");
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
listTraces(idOrName) {
|
|
118
|
+
const entry = getEntry(idOrName);
|
|
119
|
+
return entry ? [...entry.traces] : [];
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Get a trace by id for a ruleset.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const trace = Rules.registry.getTrace("eligibility", "trace-0");
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
getTrace(idOrName, traceId) {
|
|
130
|
+
const entry = getEntry(idOrName);
|
|
131
|
+
return entry?.traces.find((run) => run.id === traceId);
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Clear the registry (useful in tests).
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* Rules.registry.clear();
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
clear() {
|
|
142
|
+
entries.clear();
|
|
143
|
+
nameIndex.clear();
|
|
144
|
+
traceCounter = 0;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
function getEntry(idOrName) {
|
|
148
|
+
const byId = entries.get(idOrName);
|
|
149
|
+
if (byId) {
|
|
150
|
+
return byId;
|
|
151
|
+
}
|
|
152
|
+
const id = nameIndex.get(idOrName);
|
|
153
|
+
return id ? entries.get(id) : void 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/cli/ui.ts
|
|
157
|
+
async function startServer(options) {
|
|
158
|
+
await loadModules(options.load, Date.now());
|
|
159
|
+
const server = http.createServer((req, res) => {
|
|
160
|
+
if (!req.url || req.url === "/") {
|
|
161
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
162
|
+
res.end(buildHtml());
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
res.writeHead(404);
|
|
166
|
+
res.end("Not found");
|
|
167
|
+
});
|
|
168
|
+
server.listen(options.port, () => {
|
|
169
|
+
console.log(`Rulit UI running at http://localhost:${options.port}`);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function buildHtml() {
|
|
173
|
+
const entries2 = registry.list();
|
|
174
|
+
const template = Handlebars.compile(loadTemplate());
|
|
175
|
+
const view = {
|
|
176
|
+
entries: entries2.map((entry) => {
|
|
177
|
+
const mermaid = registry.getMermaid(entry.id) ?? "flowchart TD\n empty";
|
|
178
|
+
const graph = registry.getGraph(entry.id) ?? { nodes: [], edges: [] };
|
|
179
|
+
return {
|
|
180
|
+
id: entry.id,
|
|
181
|
+
name: entry.name ?? entry.id,
|
|
182
|
+
createdAt: new Date(entry.createdAt).toLocaleString(),
|
|
183
|
+
mermaid,
|
|
184
|
+
mermaidEncoded: encodeURIComponent(mermaid),
|
|
185
|
+
traces: registry.listTraces(entry.id).map((trace) => ({
|
|
186
|
+
id: trace.id,
|
|
187
|
+
createdAt: new Date(trace.createdAt).toLocaleString(),
|
|
188
|
+
firedCount: trace.fired.length,
|
|
189
|
+
matchedCount: trace.trace.filter((rule) => rule.matched).length,
|
|
190
|
+
traceEncoded: encodeURIComponent(JSON.stringify(trace))
|
|
191
|
+
})),
|
|
192
|
+
json: JSON.stringify(graph, null, 2)
|
|
193
|
+
};
|
|
194
|
+
})
|
|
195
|
+
};
|
|
196
|
+
return template(view);
|
|
197
|
+
}
|
|
198
|
+
function parseArgs(args) {
|
|
199
|
+
const options = {
|
|
200
|
+
port: 5173,
|
|
201
|
+
load: []
|
|
202
|
+
};
|
|
203
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
204
|
+
const value = args[i];
|
|
205
|
+
if (!value) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (value === "--port") {
|
|
209
|
+
const next = args[i + 1];
|
|
210
|
+
if (next) {
|
|
211
|
+
options.port = Number(next);
|
|
212
|
+
i += 1;
|
|
213
|
+
}
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (value === "--load") {
|
|
217
|
+
const next = args[i + 1];
|
|
218
|
+
if (next) {
|
|
219
|
+
options.load.push(next);
|
|
220
|
+
i += 1;
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const envLoad = process.env.RULIT_UI_LOAD;
|
|
226
|
+
if (envLoad) {
|
|
227
|
+
options.load.push(
|
|
228
|
+
...envLoad.split(",").map((value) => value.trim()).filter(Boolean)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const envPort = process.env.RULIT_UI_PORT;
|
|
232
|
+
if (envPort) {
|
|
233
|
+
options.port = Number(envPort);
|
|
234
|
+
}
|
|
235
|
+
return options;
|
|
236
|
+
}
|
|
237
|
+
async function loadModules(paths, cacheBust) {
|
|
238
|
+
for (const modulePath of paths) {
|
|
239
|
+
const fullPath = path2.resolve(process.cwd(), modulePath);
|
|
240
|
+
await import(`${pathToFileURL(fullPath).href}?t=${cacheBust}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
var isMain = typeof import.meta.url === "string" && import.meta.url === pathToFileURL(process.argv[1] ?? "").href;
|
|
244
|
+
if (isMain) {
|
|
245
|
+
const options = parseArgs(process.argv.slice(2));
|
|
246
|
+
void startServer(options);
|
|
247
|
+
}
|
|
248
|
+
function loadTemplate() {
|
|
249
|
+
const templatePath = resolveTemplatePath();
|
|
250
|
+
if (!templatePath) {
|
|
251
|
+
throw new Error("UI template not found. Expected ui-template.hbs near cli ui.");
|
|
252
|
+
}
|
|
253
|
+
return fs.readFileSync(templatePath, "utf8");
|
|
254
|
+
}
|
|
255
|
+
function resolveTemplatePath() {
|
|
256
|
+
const candidates = [];
|
|
257
|
+
if (typeof import.meta.url === "string") {
|
|
258
|
+
candidates.push(
|
|
259
|
+
new URL("./ui-template.hbs", import.meta.url),
|
|
260
|
+
new URL("../../src/cli/ui-template.hbs", import.meta.url)
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
candidates.push(path2.join(__dirname, "ui-template.hbs"));
|
|
264
|
+
}
|
|
265
|
+
for (const candidate of candidates) {
|
|
266
|
+
const filePath = typeof candidate === "string" ? candidate : fileURLToPath2(candidate);
|
|
267
|
+
if (fs.existsSync(filePath)) {
|
|
268
|
+
return filePath;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
buildHtml,
|
|
275
|
+
startServer
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=ui.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js","../../src/cli/ui.ts","../../src/registry.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","#!/usr/bin/env node\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport Handlebars from \"handlebars\";\nimport { registry } from \"../registry.js\";\n\ntype CliOptions = {\n port: number;\n load: string[];\n};\n\nexport async function startServer(options: CliOptions) {\n await loadModules(options.load, Date.now());\n\n const server = http.createServer((req, res) => {\n if (!req.url || req.url === \"/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(buildHtml());\n return;\n }\n\n res.writeHead(404);\n res.end(\"Not found\");\n });\n\n server.listen(options.port, () => {\n // eslint-disable-next-line no-console\n console.log(`Rulit UI running at http://localhost:${options.port}`);\n });\n}\n\nexport function buildHtml() {\n const entries = registry.list();\n const template = Handlebars.compile(loadTemplate());\n const view = {\n entries: entries.map((entry) => {\n const mermaid = registry.getMermaid(entry.id) ?? \"flowchart TD\\n empty\";\n const graph = registry.getGraph(entry.id) ?? { nodes: [], edges: [] };\n return {\n id: entry.id,\n name: entry.name ?? entry.id,\n createdAt: new Date(entry.createdAt).toLocaleString(),\n mermaid,\n mermaidEncoded: encodeURIComponent(mermaid),\n traces: registry.listTraces(entry.id).map((trace) => ({\n id: trace.id,\n createdAt: new Date(trace.createdAt).toLocaleString(),\n firedCount: trace.fired.length,\n matchedCount: trace.trace.filter((rule) => rule.matched).length,\n traceEncoded: encodeURIComponent(JSON.stringify(trace)),\n })),\n json: JSON.stringify(graph, null, 2),\n };\n }),\n };\n\n return template(view);\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n port: 5173,\n load: [],\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const value = args[i];\n if (!value) {\n continue;\n }\n if (value === \"--port\") {\n const next = args[i + 1];\n if (next) {\n options.port = Number(next);\n i += 1;\n }\n continue;\n }\n if (value === \"--load\") {\n const next = args[i + 1];\n if (next) {\n options.load.push(next);\n i += 1;\n }\n continue;\n }\n }\n\n const envLoad = process.env.RULIT_UI_LOAD;\n if (envLoad) {\n options.load.push(\n ...envLoad\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean),\n );\n }\n\n const envPort = process.env.RULIT_UI_PORT;\n if (envPort) {\n options.port = Number(envPort);\n }\n\n return options;\n}\n\nasync function loadModules(paths: string[], cacheBust: number) {\n for (const modulePath of paths) {\n const fullPath = path.resolve(process.cwd(), modulePath);\n await import(`${pathToFileURL(fullPath).href}?t=${cacheBust}`);\n }\n}\n\nconst isMain =\n typeof import.meta.url === \"string\" &&\n import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href;\nif (isMain) {\n const options = parseArgs(process.argv.slice(2));\n void startServer(options);\n}\n\nfunction loadTemplate() {\n const templatePath = resolveTemplatePath();\n if (!templatePath) {\n throw new Error(\"UI template not found. Expected ui-template.hbs near cli ui.\");\n }\n return fs.readFileSync(templatePath, \"utf8\");\n}\n\nfunction resolveTemplatePath(): string | null {\n const candidates: (string | URL)[] = [];\n\n if (typeof import.meta.url === \"string\") {\n candidates.push(\n new URL(\"./ui-template.hbs\", import.meta.url),\n new URL(\"../../src/cli/ui-template.hbs\", import.meta.url),\n );\n } else {\n // Fallback for CJS if necessary, though we prefer ESM for CLI\n candidates.push(path.join(__dirname, \"ui-template.hbs\"));\n }\n\n for (const candidate of candidates) {\n const filePath = typeof candidate === \"string\" ? candidate : fileURLToPath(candidate);\n if (fs.existsSync(filePath)) {\n return filePath;\n }\n }\n\n return null;\n}\n","import type { RuleTrace, RulesetGraph, TraceRun } from \"./types\";\n\ntype GraphSource = {\n graph: () => RulesetGraph;\n toMermaid: () => string;\n};\n\ntype RegistryEntry = {\n id: string;\n name?: string;\n createdAt: number;\n source: GraphSource;\n traces: TraceRun[];\n};\n\ntype RegistryListItem = {\n id: string;\n name?: string;\n createdAt: number;\n};\n\nconst entries = new Map<string, RegistryEntry>();\nconst nameIndex = new Map<string, string>();\nlet counter = 0;\nlet traceCounter = 0;\nconst traceLimit = 25;\n\nfunction makeId() {\n return `ruleset-${counter++}`;\n}\n\n/**\n * In-memory registry of rulesets created in this process.\n */\nexport const registry = {\n /**\n * Register a ruleset in the global registry.\n *\n * @example\n * ```ts\n * const rs = Rules.ruleset<Facts, Effects>(\"eligibility\");\n * Rules.registry.list();\n * ```\n */\n register(source: GraphSource, name?: string): string {\n const id = makeId();\n entries.set(id, { id, name, createdAt: Date.now(), source, traces: [] });\n if (name) {\n nameIndex.set(name, id);\n }\n return id;\n },\n\n /**\n * List all registered rulesets.\n *\n * @example\n * ```ts\n * const list = Rules.registry.list();\n * ```\n */\n list(): RegistryListItem[] {\n return Array.from(entries.values()).map(({ id, name, createdAt }) => ({\n id,\n name,\n createdAt,\n }));\n },\n\n /**\n * Get a graph by id or ruleset name.\n *\n * @example\n * ```ts\n * const graph = Rules.registry.getGraph(\"eligibility\");\n * ```\n */\n getGraph(idOrName: string): RulesetGraph | undefined {\n const entry = getEntry(idOrName);\n return entry?.source.graph();\n },\n\n /**\n * Get Mermaid output by id or ruleset name.\n *\n * @example\n * ```ts\n * const mermaid = Rules.registry.getMermaid(\"eligibility\");\n * ```\n */\n getMermaid(idOrName: string): string | undefined {\n const entry = getEntry(idOrName);\n return entry?.source.toMermaid();\n },\n\n /**\n * Record a trace run for a ruleset. Keeps a rolling window of traces.\n *\n * @example\n * ```ts\n * Rules.registry.recordTrace(\"eligibility\", trace, fired);\n * ```\n */\n recordTrace(\n idOrName: string,\n trace: RuleTrace[],\n fired: string[],\n facts: unknown,\n ): TraceRun | undefined {\n const entry = getEntry(idOrName);\n if (!entry) {\n return undefined;\n }\n const run: TraceRun = {\n id: `trace-${traceCounter++}`,\n createdAt: Date.now(),\n facts,\n fired,\n trace,\n };\n entry.traces.push(run);\n if (entry.traces.length > traceLimit) {\n entry.traces.splice(0, entry.traces.length - traceLimit);\n }\n return run;\n },\n\n /**\n * List trace runs for a ruleset.\n *\n * @example\n * ```ts\n * const traces = Rules.registry.listTraces(\"eligibility\");\n * ```\n */\n listTraces(idOrName: string): TraceRun[] {\n const entry = getEntry(idOrName);\n return entry ? [...entry.traces] : [];\n },\n\n /**\n * Get a trace by id for a ruleset.\n *\n * @example\n * ```ts\n * const trace = Rules.registry.getTrace(\"eligibility\", \"trace-0\");\n * ```\n */\n getTrace(idOrName: string, traceId: string): TraceRun | undefined {\n const entry = getEntry(idOrName);\n return entry?.traces.find((run) => run.id === traceId);\n },\n\n /**\n * Clear the registry (useful in tests).\n *\n * @example\n * ```ts\n * Rules.registry.clear();\n * ```\n */\n clear(): void {\n entries.clear();\n nameIndex.clear();\n traceCounter = 0;\n },\n};\n\nfunction getEntry(idOrName: string): RegistryEntry | undefined {\n const byId = entries.get(idOrName);\n if (byId) {\n return byId;\n }\n const id = nameIndex.get(idOrName);\n return id ? entries.get(id) : undefined;\n}\n"],"mappings":"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","names":["path","fileURLToPath","entries","path","fileURLToPath"]}
|