UNPKG

standard-rule-engine

Version:

A simple rule engine that uses Standard Schema to validate facts

205 lines (201 loc) 5.87 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { Engine: () => Engine }); module.exports = __toCommonJS(index_exports); // src/utils.ts var isNotEmpty = (obj) => { if (!obj) return false; for (const x in obj) return true; return false; }; var isClass = (v) => typeof v === "function" && /^\s*class\s+/.test(v.toString()) || // Handle Object.create(null) v.toString && // Handle import * as Sentry from '@sentry/bun' // This also handle [object Date], [object Array] // and FFI value like [object Prisma] v.toString().startsWith("[object ") && v.toString() !== "[object Object]" || // If object prototype is not pure, then probably a class-like object isNotEmpty(Object.getPrototypeOf(v)); var isObject = (item) => item && typeof item === "object" && !Array.isArray(item); var mergeDeep = (target, source, { skipKeys, override = true } = {}) => { if (!isObject(target) || !isObject(source)) return target; for (const [key, value] of Object.entries(source)) { if (skipKeys?.includes(key)) continue; if (!isObject(value) || !(key in target) || isClass(value)) { if (override || !(key in target)) target[key] = value; continue; } target[key] = mergeDeep( target[key], value, { skipKeys, override } ); } return target; }; function standardValidate(schema, input) { let result = schema["~standard"].validate(input); if (result instanceof Promise) { throw new Error("Facts input must be synchronous"); } if (result.issues) { return { success: false, issues: result.issues, data: null }; } return { success: true, issues: null, data: result.value }; } // src/index.ts var import_clone = __toESM(require("clone"), 1); var Session = class { constructor(context, rules, helpers = {}) { this.context = context; this.rules = rules; this.wrappedHelpers = Object.entries(helpers).reduce( (acc, [key, fn]) => { acc[key] = (...args) => { return fn(context, ...args); }; return acc; }, {} ); } insertedFacts = []; wrappedHelpers; insert(facts) { this.insertedFacts.push(facts); return this; } insertMany(facts) { this.insertedFacts.push(...facts); return this; } fire() { for (const facts of this.insertedFacts) { Object.freeze(facts); for (const rule of this.rules) { if (!rule.schema) { rule.handler(facts, { context: this.context, helpers: this.wrappedHelpers }); continue; } const validationResult = standardValidate(rule.schema, facts); if (!validationResult.success) { continue; } rule.handler(validationResult.data, { context: this.context, helpers: this.wrappedHelpers }); } } return this; } }; var Engine = class { "~types" = { Singleton: {} }; initialContext = {}; rules = []; globalSchema; helpers = {}; schema(schema) { this.globalSchema = schema; return this; } context(nameOrContext, value) { if (value === void 0 && typeof nameOrContext === "object") { this.initialContext = mergeDeep(this.initialContext, nameOrContext); } else if (typeof nameOrContext === "string") { this.initialContext = mergeDeep(this.initialContext, { [nameOrContext]: value }); } return this; } helper(name, fn) { this.helpers = { ...this.helpers, [name]: fn }; return this; } rule(name, handler, meta) { const newRule = { name, handler, priority: meta?.priority ?? 1, schema: meta?.schema ?? this.globalSchema }; this.rules.push(newRule); this.rules.sort((a, b) => { if (a.priority !== b.priority) { return a.priority - b.priority; } return a.name.localeCompare(b.name); }); return this; } use(instance) { this.rules = [...this.rules, ...instance.rules]; this.initialContext = mergeDeep( this.initialContext, instance.initialContext ); this.helpers = mergeDeep(this.helpers, instance.helpers); return this; } createSession() { return new Session( (0, import_clone.default)(this.initialContext), this.rules, this.helpers ); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Engine });