@run-iq/server 0.1.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 +21 -0
- package/README.md +108 -0
- package/dist/index.cjs +251 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +212 -0
- package/dist/index.js.map +1 -0
- package/dist/standalone.cjs +330 -0
- package/dist/standalone.cjs.map +1 -0
- package/dist/standalone.d.cts +20 -0
- package/dist/standalone.d.ts +20 -0
- package/dist/standalone.js +290 -0
- package/dist/standalone.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// src/app.ts
|
|
2
|
+
import Fastify from "fastify";
|
|
3
|
+
import { PPEError, ValidationError, RuleConflictError, SnapshotFailureError } from "@run-iq/core";
|
|
4
|
+
|
|
5
|
+
// src/config.ts
|
|
6
|
+
var DEFAULT_PORT = 3e3;
|
|
7
|
+
var DEFAULT_HOST = "0.0.0.0";
|
|
8
|
+
var DEFAULT_LOG_LEVEL = "info";
|
|
9
|
+
function resolveConfig(partial) {
|
|
10
|
+
return {
|
|
11
|
+
port: partial.port ?? DEFAULT_PORT,
|
|
12
|
+
host: partial.host ?? DEFAULT_HOST,
|
|
13
|
+
plugins: partial.plugins,
|
|
14
|
+
dsls: partial.dsls,
|
|
15
|
+
snapshot: partial.snapshot,
|
|
16
|
+
strict: partial.strict ?? true,
|
|
17
|
+
timeout: partial.timeout,
|
|
18
|
+
logLevel: partial.logLevel ?? DEFAULT_LOG_LEVEL
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/routes/health.ts
|
|
23
|
+
var ENGINE_VERSION = "0.1.2";
|
|
24
|
+
async function healthRoute(app) {
|
|
25
|
+
app.get("/health", async () => {
|
|
26
|
+
return { status: "ok", engine: ENGINE_VERSION };
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/routes/evaluate.ts
|
|
31
|
+
import { PPEEngine, hydrateRules } from "@run-iq/core";
|
|
32
|
+
|
|
33
|
+
// src/schemas/evaluate.ts
|
|
34
|
+
var evaluateRequestSchema = {
|
|
35
|
+
type: "object",
|
|
36
|
+
required: ["rules", "input"],
|
|
37
|
+
properties: {
|
|
38
|
+
rules: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: {
|
|
41
|
+
type: "object",
|
|
42
|
+
required: [
|
|
43
|
+
"id",
|
|
44
|
+
"version",
|
|
45
|
+
"model",
|
|
46
|
+
"params",
|
|
47
|
+
"priority",
|
|
48
|
+
"effectiveFrom",
|
|
49
|
+
"tags",
|
|
50
|
+
"checksum"
|
|
51
|
+
],
|
|
52
|
+
properties: {
|
|
53
|
+
id: { type: "string" },
|
|
54
|
+
version: { type: "number" },
|
|
55
|
+
model: { type: "string" },
|
|
56
|
+
params: {},
|
|
57
|
+
condition: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
dsl: { type: "string" },
|
|
61
|
+
value: {}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
priority: { type: "number" },
|
|
65
|
+
effectiveFrom: { type: "string" },
|
|
66
|
+
effectiveUntil: { type: ["string", "null"] },
|
|
67
|
+
tags: { type: "array", items: { type: "string" } },
|
|
68
|
+
checksum: { type: "string" },
|
|
69
|
+
schemaVersion: { type: "string" }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
input: {
|
|
74
|
+
type: "object",
|
|
75
|
+
required: ["requestId", "data", "meta"],
|
|
76
|
+
properties: {
|
|
77
|
+
requestId: { type: "string" },
|
|
78
|
+
data: { type: "object" },
|
|
79
|
+
meta: {
|
|
80
|
+
type: "object",
|
|
81
|
+
required: ["tenantId"],
|
|
82
|
+
properties: {
|
|
83
|
+
tenantId: { type: "string" },
|
|
84
|
+
userId: { type: "string" },
|
|
85
|
+
tags: { type: "array", items: { type: "string" } },
|
|
86
|
+
context: { type: "object" },
|
|
87
|
+
effectiveDate: { type: "string" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/routes/evaluate.ts
|
|
96
|
+
function createEvaluateRoute(config) {
|
|
97
|
+
return async function evaluateRoute(app) {
|
|
98
|
+
const engineConfig = {
|
|
99
|
+
plugins: config.plugins,
|
|
100
|
+
dsls: config.dsls,
|
|
101
|
+
snapshot: config.snapshot,
|
|
102
|
+
strict: config.strict,
|
|
103
|
+
timeout: config.timeout,
|
|
104
|
+
dryRun: config.snapshot == null
|
|
105
|
+
};
|
|
106
|
+
const engine = new PPEEngine(engineConfig);
|
|
107
|
+
app.post(
|
|
108
|
+
"/evaluate",
|
|
109
|
+
{
|
|
110
|
+
schema: {
|
|
111
|
+
body: evaluateRequestSchema
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
async (request) => {
|
|
115
|
+
const { rules: rawRules, input: rawInput } = request.body;
|
|
116
|
+
const rules = hydrateRules(rawRules);
|
|
117
|
+
const input = {
|
|
118
|
+
requestId: rawInput.requestId,
|
|
119
|
+
data: rawInput.data,
|
|
120
|
+
meta: {
|
|
121
|
+
tenantId: rawInput.meta.tenantId,
|
|
122
|
+
userId: rawInput.meta.userId,
|
|
123
|
+
tags: rawInput.meta.tags,
|
|
124
|
+
context: rawInput.meta.context,
|
|
125
|
+
effectiveDate: rawInput.meta.effectiveDate ? new Date(rawInput.meta.effectiveDate) : void 0
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const result = await engine.evaluate(rules, input);
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/app.ts
|
|
136
|
+
function createApp(partial) {
|
|
137
|
+
const config = resolveConfig(partial);
|
|
138
|
+
const app = Fastify({
|
|
139
|
+
logger: config.logLevel !== "silent" ? { level: config.logLevel ?? "info" } : false
|
|
140
|
+
});
|
|
141
|
+
app.register(healthRoute);
|
|
142
|
+
app.register(createEvaluateRoute(config));
|
|
143
|
+
app.setErrorHandler((error, _request, reply) => {
|
|
144
|
+
if ("validation" in error && error.validation) {
|
|
145
|
+
return reply.status(400).send({
|
|
146
|
+
error: "Validation Error",
|
|
147
|
+
message: error.message,
|
|
148
|
+
statusCode: 400
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (error instanceof ValidationError) {
|
|
152
|
+
return reply.status(400).send({
|
|
153
|
+
error: "Validation Error",
|
|
154
|
+
message: error.message,
|
|
155
|
+
code: error.code,
|
|
156
|
+
reasons: error.reasons,
|
|
157
|
+
statusCode: 400
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
if (error instanceof RuleConflictError) {
|
|
161
|
+
return reply.status(409).send({
|
|
162
|
+
error: "Rule Conflict",
|
|
163
|
+
message: error.message,
|
|
164
|
+
code: error.code,
|
|
165
|
+
ruleIds: error.ruleIds,
|
|
166
|
+
statusCode: 409
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (error instanceof SnapshotFailureError) {
|
|
170
|
+
return reply.status(503).send({
|
|
171
|
+
error: "Snapshot Failure",
|
|
172
|
+
message: error.message,
|
|
173
|
+
code: error.code,
|
|
174
|
+
statusCode: 503
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (error instanceof PPEError) {
|
|
178
|
+
return reply.status(422).send({
|
|
179
|
+
error: "Processing Error",
|
|
180
|
+
message: error.message,
|
|
181
|
+
code: error.code,
|
|
182
|
+
statusCode: 422
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return reply.status(500).send({
|
|
186
|
+
error: "Internal Server Error",
|
|
187
|
+
message: "An unexpected error occurred",
|
|
188
|
+
statusCode: 500
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
return app;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/server.ts
|
|
195
|
+
async function start(partial) {
|
|
196
|
+
const config = resolveConfig(partial);
|
|
197
|
+
const app = createApp(partial);
|
|
198
|
+
const shutdown = async (signal) => {
|
|
199
|
+
app.log.info(`Received ${signal}, shutting down gracefully\u2026`);
|
|
200
|
+
await app.close();
|
|
201
|
+
process.exit(0);
|
|
202
|
+
};
|
|
203
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
204
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
205
|
+
await app.listen({ port: config.port, host: config.host });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/standalone.ts
|
|
209
|
+
var PLUGIN_MAP = {
|
|
210
|
+
fiscal: "@run-iq/plugin-fiscal"
|
|
211
|
+
};
|
|
212
|
+
var DSL_MAP = {
|
|
213
|
+
jsonlogic: "@run-iq/dsl-jsonlogic"
|
|
214
|
+
};
|
|
215
|
+
function parseList(value) {
|
|
216
|
+
if (!value) return [];
|
|
217
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
218
|
+
}
|
|
219
|
+
function parseLogLevel(value) {
|
|
220
|
+
const valid = ["info", "warn", "error", "silent"];
|
|
221
|
+
if (value && valid.includes(value)) {
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
return "info";
|
|
225
|
+
}
|
|
226
|
+
async function loadPlugins(names) {
|
|
227
|
+
const plugins = [];
|
|
228
|
+
for (const name of names) {
|
|
229
|
+
const pkg = PLUGIN_MAP[name];
|
|
230
|
+
if (!pkg) {
|
|
231
|
+
console.warn(`Unknown plugin "${name}", skipping.`);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const mod = await import(pkg);
|
|
236
|
+
const PluginClass = mod["default"] ?? mod[Object.keys(mod)[0]];
|
|
237
|
+
if (typeof PluginClass === "function") {
|
|
238
|
+
plugins.push(new PluginClass());
|
|
239
|
+
}
|
|
240
|
+
} catch {
|
|
241
|
+
console.warn(`Plugin "${name}" (${pkg}) not installed, skipping.`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return plugins;
|
|
245
|
+
}
|
|
246
|
+
async function loadDSLs(names) {
|
|
247
|
+
const dsls = [];
|
|
248
|
+
for (const name of names) {
|
|
249
|
+
const pkg = DSL_MAP[name];
|
|
250
|
+
if (!pkg) {
|
|
251
|
+
console.warn(`Unknown DSL "${name}", skipping.`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const mod = await import(pkg);
|
|
256
|
+
const EvaluatorClass = mod["default"] ?? mod[Object.keys(mod)[0]];
|
|
257
|
+
if (typeof EvaluatorClass === "function") {
|
|
258
|
+
dsls.push(new EvaluatorClass());
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
console.warn(`DSL "${name}" (${pkg}) not installed, skipping.`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return dsls;
|
|
265
|
+
}
|
|
266
|
+
async function main() {
|
|
267
|
+
const port = Number(process.env["PORT"]) || 3e3;
|
|
268
|
+
const host = process.env["HOST"] ?? "0.0.0.0";
|
|
269
|
+
const logLevel = parseLogLevel(process.env["LOG_LEVEL"]);
|
|
270
|
+
const strict = process.env["STRICT"] !== "false";
|
|
271
|
+
const pluginNames = parseList(process.env["PLUGINS"]);
|
|
272
|
+
const dslNames = parseList(process.env["DSLS"]);
|
|
273
|
+
const plugins = await loadPlugins(pluginNames);
|
|
274
|
+
const dsls = await loadDSLs(dslNames);
|
|
275
|
+
console.log(
|
|
276
|
+
`Starting @run-iq/server \u2014 port=${String(port)} host=${host} plugins=[${plugins.map((p) => p.name).join(",")}] dsls=[${dsls.map((d) => d.dsl).join(",")}]`
|
|
277
|
+
);
|
|
278
|
+
await start({ port, host, logLevel, strict, plugins, dsls });
|
|
279
|
+
}
|
|
280
|
+
main().catch((err) => {
|
|
281
|
+
console.error("Failed to start @run-iq/server:", err);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
});
|
|
284
|
+
export {
|
|
285
|
+
loadDSLs,
|
|
286
|
+
loadPlugins,
|
|
287
|
+
parseList,
|
|
288
|
+
parseLogLevel
|
|
289
|
+
};
|
|
290
|
+
//# sourceMappingURL=standalone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app.ts","../src/config.ts","../src/routes/health.ts","../src/routes/evaluate.ts","../src/schemas/evaluate.ts","../src/server.ts","../src/standalone.ts"],"sourcesContent":["import Fastify from 'fastify';\nimport type { FastifyInstance, FastifyError } from 'fastify';\nimport { PPEError, ValidationError, RuleConflictError, SnapshotFailureError } from '@run-iq/core';\nimport { type ServerConfig, resolveConfig } from './config.js';\nimport { healthRoute } from './routes/health.js';\nimport { createEvaluateRoute } from './routes/evaluate.js';\n\nexport function createApp(\n partial: Partial<ServerConfig> & Pick<ServerConfig, 'plugins' | 'dsls'>,\n): FastifyInstance {\n const config = resolveConfig(partial);\n\n const app = Fastify({\n logger: config.logLevel !== 'silent' ? { level: config.logLevel ?? 'info' } : false,\n });\n\n // Register routes\n app.register(healthRoute);\n app.register(createEvaluateRoute(config));\n\n // Global error handler\n app.setErrorHandler((error: FastifyError | Error, _request, reply) => {\n if ('validation' in error && (error as FastifyError).validation) {\n return reply.status(400).send({\n error: 'Validation Error',\n message: error.message,\n statusCode: 400,\n });\n }\n\n if (error instanceof ValidationError) {\n return reply.status(400).send({\n error: 'Validation Error',\n message: error.message,\n code: error.code,\n reasons: error.reasons,\n statusCode: 400,\n });\n }\n\n if (error instanceof RuleConflictError) {\n return reply.status(409).send({\n error: 'Rule Conflict',\n message: error.message,\n code: error.code,\n ruleIds: error.ruleIds,\n statusCode: 409,\n });\n }\n\n if (error instanceof SnapshotFailureError) {\n return reply.status(503).send({\n error: 'Snapshot Failure',\n message: error.message,\n code: error.code,\n statusCode: 503,\n });\n }\n\n if (error instanceof PPEError) {\n return reply.status(422).send({\n error: 'Processing Error',\n message: error.message,\n code: error.code,\n statusCode: 422,\n });\n }\n\n return reply.status(500).send({\n error: 'Internal Server Error',\n message: 'An unexpected error occurred',\n statusCode: 500,\n });\n });\n\n return app;\n}\n","import type { PPEPlugin, DSLEvaluator, ISnapshotAdapter } from '@run-iq/core';\n\nexport interface ServerConfig {\n readonly port: number;\n readonly host: string;\n readonly plugins: PPEPlugin[];\n readonly dsls: DSLEvaluator[];\n readonly snapshot?: ISnapshotAdapter | undefined;\n readonly strict?: boolean | undefined;\n readonly timeout?:\n | {\n readonly dsl?: number | undefined;\n readonly hook?: number | undefined;\n readonly pipeline?: number | undefined;\n }\n | undefined;\n readonly logLevel?: 'info' | 'warn' | 'error' | 'silent' | undefined;\n}\n\nconst DEFAULT_PORT = 3000;\nconst DEFAULT_HOST = '0.0.0.0';\nconst DEFAULT_LOG_LEVEL = 'info';\n\nexport function resolveConfig(\n partial: Partial<ServerConfig> & Pick<ServerConfig, 'plugins' | 'dsls'>,\n): ServerConfig {\n return {\n port: partial.port ?? DEFAULT_PORT,\n host: partial.host ?? DEFAULT_HOST,\n plugins: partial.plugins,\n dsls: partial.dsls,\n snapshot: partial.snapshot,\n strict: partial.strict ?? true,\n timeout: partial.timeout,\n logLevel: partial.logLevel ?? DEFAULT_LOG_LEVEL,\n };\n}\n","import type { FastifyInstance } from 'fastify';\n\nconst ENGINE_VERSION = '0.1.2';\n\nexport async function healthRoute(app: FastifyInstance): Promise<void> {\n app.get('/health', async () => {\n return { status: 'ok', engine: ENGINE_VERSION };\n });\n}\n","import type { FastifyInstance, FastifyPluginAsync } from 'fastify';\nimport { PPEEngine, hydrateRules } from '@run-iq/core';\nimport type { PPEEngineConfig, EvaluationInput } from '@run-iq/core';\nimport type { ServerConfig } from '../config.js';\nimport { evaluateRequestSchema } from '../schemas/evaluate.js';\n\ninterface EvaluateBody {\n rules: Record<string, unknown>[];\n input: {\n requestId: string;\n data: Record<string, unknown>;\n meta: {\n tenantId: string;\n userId?: string;\n tags?: string[];\n context?: Record<string, unknown>;\n effectiveDate?: string;\n };\n };\n}\n\nexport function createEvaluateRoute(config: ServerConfig): FastifyPluginAsync {\n return async function evaluateRoute(app: FastifyInstance): Promise<void> {\n const engineConfig: PPEEngineConfig = {\n plugins: config.plugins,\n dsls: config.dsls,\n snapshot: config.snapshot,\n strict: config.strict,\n timeout: config.timeout,\n dryRun: config.snapshot == null,\n };\n\n const engine = new PPEEngine(engineConfig);\n\n app.post<{ Body: EvaluateBody }>(\n '/evaluate',\n {\n schema: {\n body: evaluateRequestSchema,\n },\n },\n async (request) => {\n const { rules: rawRules, input: rawInput } = request.body;\n\n const rules = hydrateRules(rawRules);\n\n const input: EvaluationInput = {\n requestId: rawInput.requestId,\n data: rawInput.data,\n meta: {\n tenantId: rawInput.meta.tenantId,\n userId: rawInput.meta.userId,\n tags: rawInput.meta.tags,\n context: rawInput.meta.context,\n effectiveDate: rawInput.meta.effectiveDate\n ? new Date(rawInput.meta.effectiveDate)\n : undefined,\n },\n };\n\n const result = await engine.evaluate(rules, input);\n\n return result;\n },\n );\n };\n}\n","export const evaluateRequestSchema = {\n type: 'object',\n required: ['rules', 'input'],\n properties: {\n rules: {\n type: 'array',\n items: {\n type: 'object',\n required: [\n 'id',\n 'version',\n 'model',\n 'params',\n 'priority',\n 'effectiveFrom',\n 'tags',\n 'checksum',\n ],\n properties: {\n id: { type: 'string' },\n version: { type: 'number' },\n model: { type: 'string' },\n params: {},\n condition: {\n type: 'object',\n properties: {\n dsl: { type: 'string' },\n value: {},\n },\n },\n priority: { type: 'number' },\n effectiveFrom: { type: 'string' },\n effectiveUntil: { type: ['string', 'null'] },\n tags: { type: 'array', items: { type: 'string' } },\n checksum: { type: 'string' },\n schemaVersion: { type: 'string' },\n },\n },\n },\n input: {\n type: 'object',\n required: ['requestId', 'data', 'meta'],\n properties: {\n requestId: { type: 'string' },\n data: { type: 'object' },\n meta: {\n type: 'object',\n required: ['tenantId'],\n properties: {\n tenantId: { type: 'string' },\n userId: { type: 'string' },\n tags: { type: 'array', items: { type: 'string' } },\n context: { type: 'object' },\n effectiveDate: { type: 'string' },\n },\n },\n },\n },\n },\n} as const;\n\nexport const evaluateResponseSchema = {\n type: 'object',\n properties: {\n requestId: { type: 'string' },\n value: {},\n breakdown: { type: 'array' },\n appliedRules: { type: 'array' },\n skippedRules: { type: 'array' },\n trace: { type: 'object' },\n snapshotId: { type: 'string' },\n engineVersion: { type: 'string' },\n pluginVersions: { type: 'object' },\n dslVersions: { type: 'object' },\n timestamp: { type: 'string' },\n meta: { type: 'object' },\n },\n} as const;\n","import type { ServerConfig } from './config.js';\nimport { createApp } from './app.js';\nimport { resolveConfig } from './config.js';\n\nexport async function start(\n partial: Partial<ServerConfig> & Pick<ServerConfig, 'plugins' | 'dsls'>,\n): Promise<void> {\n const config = resolveConfig(partial);\n const app = createApp(partial);\n\n const shutdown = async (signal: string): Promise<void> => {\n app.log.info(`Received ${signal}, shutting down gracefully…`);\n await app.close();\n process.exit(0);\n };\n\n process.on('SIGINT', () => void shutdown('SIGINT'));\n process.on('SIGTERM', () => void shutdown('SIGTERM'));\n\n await app.listen({ port: config.port, host: config.host });\n}\n","/* eslint-disable no-console */\n/**\n * Standalone entry point for Docker / direct `node dist/standalone.js` usage.\n *\n * Reads configuration from environment variables:\n * PORT – HTTP port (default 3000)\n * HOST – Bind address (default 0.0.0.0)\n * LOG_LEVEL – info|warn|error|silent (default info)\n * STRICT – Snapshot strict mode (default true)\n * PLUGINS – Comma-separated list (e.g. \"fiscal\")\n * DSLS – Comma-separated list (e.g. \"jsonlogic\")\n */\n\nimport type { PPEPlugin, DSLEvaluator } from '@run-iq/core';\nimport { start } from './server.js';\n\nconst PLUGIN_MAP: Record<string, string> = {\n fiscal: '@run-iq/plugin-fiscal',\n};\n\nconst DSL_MAP: Record<string, string> = {\n jsonlogic: '@run-iq/dsl-jsonlogic',\n};\n\nexport function parseList(value: string | undefined): string[] {\n if (!value) return [];\n return value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nexport function parseLogLevel(value: string | undefined): 'info' | 'warn' | 'error' | 'silent' {\n const valid = ['info', 'warn', 'error', 'silent'] as const;\n if (value && (valid as readonly string[]).includes(value)) {\n return value as (typeof valid)[number];\n }\n return 'info';\n}\n\nexport async function loadPlugins(names: string[]): Promise<PPEPlugin[]> {\n const plugins: PPEPlugin[] = [];\n for (const name of names) {\n const pkg = PLUGIN_MAP[name];\n if (!pkg) {\n console.warn(`Unknown plugin \"${name}\", skipping.`);\n continue;\n }\n try {\n const mod: Record<string, unknown> = await import(pkg);\n const PluginClass = mod['default'] ?? mod[Object.keys(mod)[0]!];\n if (typeof PluginClass === 'function') {\n plugins.push(new (PluginClass as new () => PPEPlugin)());\n }\n } catch {\n console.warn(`Plugin \"${name}\" (${pkg}) not installed, skipping.`);\n }\n }\n return plugins;\n}\n\nexport async function loadDSLs(names: string[]): Promise<DSLEvaluator[]> {\n const dsls: DSLEvaluator[] = [];\n for (const name of names) {\n const pkg = DSL_MAP[name];\n if (!pkg) {\n console.warn(`Unknown DSL \"${name}\", skipping.`);\n continue;\n }\n try {\n const mod: Record<string, unknown> = await import(pkg);\n const EvaluatorClass = mod['default'] ?? mod[Object.keys(mod)[0]!];\n if (typeof EvaluatorClass === 'function') {\n dsls.push(new (EvaluatorClass as new () => DSLEvaluator)());\n }\n } catch {\n console.warn(`DSL \"${name}\" (${pkg}) not installed, skipping.`);\n }\n }\n return dsls;\n}\n\nasync function main(): Promise<void> {\n const port = Number(process.env['PORT']) || 3000;\n const host = process.env['HOST'] ?? '0.0.0.0';\n const logLevel = parseLogLevel(process.env['LOG_LEVEL']);\n const strict = process.env['STRICT'] !== 'false';\n\n const pluginNames = parseList(process.env['PLUGINS']);\n const dslNames = parseList(process.env['DSLS']);\n\n const plugins = await loadPlugins(pluginNames);\n const dsls = await loadDSLs(dslNames);\n\n console.log(\n `Starting @run-iq/server — port=${String(port)} host=${host} plugins=[${plugins.map((p) => p.name).join(',')}] dsls=[${dsls.map((d) => d.dsl).join(',')}]`,\n );\n\n await start({ port, host, logLevel, strict, plugins, dsls });\n}\n\nmain().catch((err: unknown) => {\n console.error('Failed to start @run-iq/server:', err);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@run-iq/server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "PPE — HTTP REST server for the Parametric Policy Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": ["dist"],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "eslint src/ tests/ && prettier --check src/ tests/",
|
|
27
|
+
"lint:fix": "eslint src/ tests/ --fix && prettier --write src/ tests/",
|
|
28
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm test && npm run lint"
|
|
29
|
+
},
|
|
30
|
+
"keywords": ["ppe", "policy-engine", "rules-engine", "parametric", "server", "rest"],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Run-IQ/server.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Run-IQ/server#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/Run-IQ/server/issues"
|
|
38
|
+
},
|
|
39
|
+
"author": "Abdou-Raouf ATARMLA",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@run-iq/core": "^0.1.2",
|
|
43
|
+
"fastify": "^5.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.11.0",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
48
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
49
|
+
"eslint": "^8.57.0",
|
|
50
|
+
"prettier": "^3.2.0",
|
|
51
|
+
"tsup": "^8.0.0",
|
|
52
|
+
"typescript": "^5.4.0",
|
|
53
|
+
"vitest": "^1.3.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|