UNPKG

iopa

Version:

API-first, Internet of Things (IoT) stack for Typescript, official implementation of the Internet Open Protocols Alliance (IOPA) reference pattern

1,571 lines (1,551 loc) 42.4 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // src/util/shallow.ts var shallow_exports = {}; __export(shallow_exports, { assign: () => assign, clone: () => clone, cloneDoubleLayer: () => cloneDoubleLayer, cloneFilter: () => cloneFilter, cloneTripleLayer: () => cloneTripleLayer, copy: () => copy, merge: () => merge, mergeContext: () => mergeContext }); function merge(target, defaults, replace) { if (!target) { throw new Error("target must not be empty"); } if (!defaults) { defaults = {}; } if (replace) { for (var key in defaults) { if (defaults.hasOwnProperty(key)) { target[key] = defaults[key]; } } } else { for (var key in defaults) { if (defaults.hasOwnProperty(key) && !target.hasOwnProperty(key)) { target[key] = defaults[key]; } } } } function assign(target) { if (!target) { throw new Error("target must not be empty"); } target = Object(target); for (let index = 1; index < arguments.length; index++) { const source = arguments[index]; if (source != null) { for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } } return target; } function copy(source, target) { if (!source) { source = {}; } if (!target) { target = Object.create(Object.getPrototypeOf(source)); } for (const key in source) { if (source.hasOwnProperty(key)) { target[key] = source[key]; } } return target; } function clone(source) { const clone2 = Object.create(Object.getPrototypeOf(source)); for (const key in source) { if (source.hasOwnProperty(key)) { clone2[key] = source[key]; } } return clone2; } function cloneDoubleLayer(source) { const clone2 = Object.create(Object.getPrototypeOf(source)); for (const key1 in source) { if (source.hasOwnProperty(key1)) { const item = source[key1]; if (typeof item === "object") { const targetItem = Object.create(Object.getPrototypeOf(item)); for (const key2 in item) { if (item.hasOwnProperty(key2)) { targetItem[key2] = item[key2]; } } clone2[key1] = targetItem; } else { clone2[key1] = item; } } } return clone2; } function cloneTripleLayer(source) { const clone2 = Object.create(Object.getPrototypeOf(source)); for (const key1 in source) { if (source.hasOwnProperty(key1)) { const item = source[key1]; if (typeof item === "object") { const targetItem = Object.create(Object.getPrototypeOf(item)); for (const key2 in item) { if (item.hasOwnProperty(key2)) { const item2 = item[key2]; if (typeof item2 === "object") { const targetItem2 = Object.create(Object.getPrototypeOf(item2)); for (const key3 in item2) { if (item2.hasOwnProperty(key3)) { targetItem2[key3] = item[key3]; } } targetItem[key2] = targetItem2; } else { targetItem[key2] = item2; } } } clone2[key1] = targetItem; } else { clone2[key1] = item; } } } return clone2; } function cloneFilter(source, blacklist) { const clone2 = Object.create(Object.getPrototypeOf(source)); for (const key in source) { if (source.hasOwnProperty(key) && blacklist.indexOf(key) == -1) { clone2[key] = source[key]; } } return clone2; } function mergeContext(target, defaults) { if (!target) { throw new Error("target must not be empty"); } if (!defaults) { return; } Object.keys(defaults).forEach((key) => { if (key !== "iopa.Headers") { target[key] = defaults[key]; } else { const targetHeaders = target[key]; defaults[key].forEach((value, key2) => { targetHeaders.set(key2, value); }); } }); } // src/util/url.ts var url_exports = {}; __export(url_exports, { getPathFromURL: () => getPathFromURL, getPattern: () => getPattern, isAbsoluteURL: () => isAbsoluteURL, mergePath: () => mergePath, splitPath: () => splitPath }); var URL_REGEXP = /^https?:\/\/[a-zA-Z0-9\-\.:]+(\/?[^?#]*)/; var splitPath = (path) => { const paths = path.split(/\//); if (paths[0] === "") { paths.shift(); } return paths; }; var patternCache = {}; var getPattern = (label) => { if (label === "*") { return "*"; } const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); if (match) { if (!patternCache[label]) { if (match[2]) { patternCache[label] = [ label, match[1], new RegExp("^" + match[2] + "$") ]; } else { patternCache[label] = [label, match[1], true]; } } return patternCache[label]; } return null; }; var getPathFromURL = (url, params = { strict: true }) => { if (params.strict === false && url.endsWith("/")) { url = url.slice(0, -1); } const match = url.match(URL_REGEXP); if (match) { return match[1]; } return ""; }; var isAbsoluteURL = (url) => { const match = url.match(URL_REGEXP); if (match) { return true; } return false; }; var mergePath = (...paths) => { let p = ""; let endsWithSlash = false; for (let path of paths) { if (p.endsWith("/")) { p = p.slice(0, -1); endsWithSlash = true; } if (!path.startsWith("/")) { path = `/${path}`; } if (path === "/" && endsWithSlash) { p = `${p}/`; } else if (path !== "/") { p = `${p}${path}`; } if (path === "/" && p === "") { p = "/"; } } return p; }; // src/util/status.ts var status_exports = {}; __export(status_exports, { getStatusText: () => getStatusText }); var statuses = { 100: "Continue", 101: "Switching Protocols", 102: "Processing", 103: "Early Hints", 200: "OK", 201: "Created", 202: "Accepted", 204: "No Content", 206: "Partial Content", 301: "Moved Permanently", 302: "Moved Temporarily", 303: "See Other", 304: "Not Modified", 307: "Temporary Redirect", 308: "Permanent Redirect", 400: "Bad Request", 401: "Unauthorized", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Not Allowed", 406: "Not Acceptable", 408: "Request Time-out", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Request Entity Too Large", 414: "Request-URI Too Large", 415: "Unsupported Media Type", 416: "Requested Range Not Satisfiable", 421: "Misdirected Request", 429: "Too Many Requests", 500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Temporarily Unavailable", 504: "Gateway Time-out", 505: "HTTP Version Not Supported", 507: "Insufficient Storage" }; var getStatusText = (statusCode) => { const text = statuses[statusCode]; return text; }; // src/util/prototype.ts function cloneKeyBehaviors(targetObjectPrototype, sourceObjectprototype, iopaContextKey, response) { Object.getOwnPropertyNames(sourceObjectprototype).forEach((srcProp) => { if (typeof sourceObjectprototype[srcProp] === "function") { targetObjectPrototype[srcProp] = (...rest) => { let item; if (response) { item = targetObjectPrototype[iopaContextKey]; } else { item = targetObjectPrototype.response[iopaContextKey]; } return item[srcProp](...rest); }; } else { Object.defineProperty(targetObjectPrototype, srcProp, { get() { let item; if (response) { item = this[iopaContextKey]; } else { item = this.response[iopaContextKey]; } return item[srcProp]; }, set(val) { let item; if (response) { item = this[iopaContextKey]; } else { item = this.response[iopaContextKey]; } item[srcProp] = val; } }); } }); } // src/util/disposable.ts var Disposable = class { _disposer; constructor(disposer) { this._disposer = disposer; } dispose() { this._disposer.apply(this._disposer); } }; // src/iopa/constants.ts var VERSION = "4.0"; var URN_APP = "urn:io.iopa:app"; var AppRef = { id: URN_APP, description: "Iopa App Capability common to every Iopa App, includes host name and version" }; // src/util/guid.ts function guid() { return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; } function s4() { return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1); } // src/util/freelist.ts var FreeList = class { name; max; _constructor; list; constructor(name, max, factory) { this.name = name; this._constructor = factory; this.max = max; this.list = []; } alloc(...args) { return this.list.length ? this.list.shift() : this._constructor(...args); } free(obj) { if (this.list.length < this.max) { this.list.push(obj); return true; } return false; } }; // src/util/events.ts var ExtendableEventTarget = class extends EventTarget { constructor() { super(); } dispatchEvent(event) { if (!(event instanceof Event)) { const { type, ...rest } = event; const eventInstance = new Event(type); Object.keys(rest).forEach((key) => { eventInstance[key] = rest[key]; }); event = eventInstance; } return super.dispatchEvent(event); } async dispatchEventExtendable(event) { const promises = []; const extendableEvent = { ...event, waitUntil: (p) => { promises.push(p); } }; const result = this.dispatchEvent(extendableEvent); await Promise.all(promises); return result; } }; var TraceEvent = class extends Event { label; get type() { return super.type; } constructor(type, eventInitDict) { const { label, ...options } = eventInitDict || {}; super(type, options); this.label = label; } }; // src/iopa/logging.ts function noop() { } var logger = { flush: noop, fatal: noop, error: noop, warn: noop, info: noop, debug: noop, trace: noop, child: () => logger, level: "info", silent: noop, isLevelEnabled: function(level) { throw new Error("Function not implemented."); }, bindings: function() { throw new Error("Function not implemented."); }, setBindings: function(bindings) { throw new Error("Function not implemented."); } }; var consoleLogger = { flush: noop, fatal: console.error, error: console.error, warn: console.warn, info: console.info, debug: console.debug, trace: console.trace, child: () => consoleLogger, level: "info", silent: noop, isLevelEnabled: function(level) { throw new Error("Function not implemented."); }, bindings: function() { throw new Error("Function not implemented."); }, setBindings: function(bindings) { throw new Error("Function not implemented."); } }; var logging_default = logger; // src/iopa/map.ts var _IopaMap = class { constructor(data, prevData) { if (prevData) { this._loadEntries(prevData.entries()); } if (data) { if (Array.isArray(data)) { this._loadEntries(data); } else if ("entries" in data) { this._loadEntries(data.entries()); } else { this._loadEntries(Object.entries(data)); } } } get(key) { return this[key]; } set(data, value) { if (value || typeof data !== "object") { this[data] = value; return; } if (Array.isArray(data)) { this._loadEntries(data); } else if ("entries" in data) { this._loadEntries(data.entries()); } else { this._loadEntries(Object.entries(data)); } } _loadEntries(entries) { for (const entry of entries) { this.set(entry[0], entry[1]); } } getRef(iRef) { return this[iRef.id]; } has(key) { return key in this; } addRef(iRef, value) { this[iRef.id] = value; return value; } delete(key) { if (key in this) { delete this[key]; return true; } return false; } default(key, valueFn) { if (key in this) { } else if (typeof valueFn === "function") { this.set(key, valueFn()); } else { this.set(key, valueFn); } return this.get(key); } entries() { return Object.entries(this); } toString() { return jsonSerialize(this.toJSON()); } toJSON() { const jsonObj = {}; for (const key of Object.getOwnPropertyNames(this).filter((key2) => !key2.startsWith("_") && !_IopaMap._BLACK_LIST_STRINGIFY.includes(key2) && this[key2] != null)) { if (typeof this[key] === "object" && this[key].constructor.name.toString() === "URL") { jsonObj[key] = this[key].href; break; } jsonObj[key] = this[key]; } const proto1 = Object.getPrototypeOf(this); const proto2 = Object.getPrototypeOf(proto1); [proto1, proto2].forEach((proto) => { for (const key of Object.getOwnPropertyNames(proto).filter((key2) => !(key2 in jsonObj) && !key2.startsWith("_") && !_IopaMap._BLACK_LIST_STRINGIFY.includes(key2) && this[key2] != null)) { const desc = Object.getOwnPropertyDescriptor(proto, key); const hasGetter = desc && typeof desc.get === "function"; if (hasGetter) { const value = desc.get.call(this); if (typeof value === "object" && value.constructor.name.toString() === "Headers") { jsonObj[key] = Object.fromEntries(value.entries()); break; } jsonObj[key] = value; } } }); if (this["iopa.Headers"]) { jsonObj["iopa.Headers"] = Object.fromEntries(this["iopa.Headers"].entries()); } return jsonObj; } }; var IopaMap = _IopaMap; __publicField(IopaMap, "_BLACK_LIST_STRINGIFY", [ "app", "body", "bodyUsed", "capability", "create", "createChild", "delete", "dispose", "get", "headers", "iopa.Body", "iopa.Events", "iopa.Headers", "iopa.IsFinalized", "iopa.LogStream", "iopa.RawRequest", "iopa.RawRequestClone", "iopa.RawResponse", "iopa.RawResponseClone", "iopa.Url", "log", "method", "ok", "redirected", "response", "server.AbortController", "server.AbortSignal", "server.Capabilities", "server.CurrentMiddleware", "server.Environment", "server.Events", "server.PassThroughOnException", "server.Trace", "server.WaitUntil", "set", "setCapability", "signal", "toJSON", "url" ]); function getCircularReplacer() { const seen = /* @__PURE__ */ new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return void 0; } seen.add(value); if ("toJSON" in value) { return value.toJSON(); } } return value; }; } function jsonSerialize(data) { return JSON.stringify(data, getCircularReplacer(), 2); } // src/iopa/context-core.ts var ContextCore = class extends IopaMap { "iopa.Version"; dispose; createChild; log; app; init() { const abortController = new AbortController(); this.set("server.Timestamp", Date.now()); this.set("iopa.Events", new ExtendableEventTarget()); this.set("iopa.Version", VERSION); this.set("server.AbortController", abortController); this.set("server.AbortSignal", abortController.signal); this.set("server.CurrentMiddleware", ""); this.set("log", logging_default); return this; } dispatchEventExtendable(event) { return this.get("iopa.Events").dispatchEventExtendable(event); } addEventListener(type, callback, options) { return this.get("iopa.Events").addEventListener(type, callback, options); } dispatchEvent(event) { return this.get("iopa.Events").dispatchEvent(event); } removeEventListener(type, callback, options) { return this.get("iopa.Events").removeEventListener(type, callback, options); } using(appFuncPromiseOrValue) { if (typeof appFuncPromiseOrValue === "function") { return _using({ context: this, p: appFuncPromiseOrValue(this) }); } return _using({ context: this, p: appFuncPromiseOrValue }); } capability(keyOrRef) { if (typeof keyOrRef === "string") { return this._capability(keyOrRef); } else { return this._capability(keyOrRef.id); } } _capability(keyOrRef) { return this.get("server.Capabilities")[keyOrRef]; } setCapability(keyOrRef, value) { if (typeof keyOrRef === "string") { return this._setCapability(keyOrRef, value); } else { return this._setCapability(keyOrRef.id, value); } } _setCapability(keyOrRef, value) { this.get("server.Capabilities")[keyOrRef] = value; } respondWith(res, status) { if (res || status) { throw new Error("responses Not Implemented in Core"); } return this; } }; function _using({ context, p }) { return new Promise((resolve, reject) => { if (typeof p === "undefined") { p = null; } resolve(p); }).then((value) => { return Promise.resolve((() => { setTimeout(() => { if (context.dispose) { context.dispose(); } }, 0); return value; })()); }, (err) => { console.error(err); setTimeout(() => { if (context.dispose) { context.dispose(); } }, 0); throw err; }); } // src/iopa/factory-core.ts var Factory = class { _factory; constructor(name = "IopaContext", size = 100, factory = () => new ContextCore()) { this._factory = new FreeList(name, size, factory); } createContext(urlOrRequest, options) { const optionsValid = _validOptions(options); const context = this._factory.alloc().init(); context.dispose = this._dispose.bind(this, context); context.set("iopa.Id", `#${_nextSequence()}`); context.set("server.Timestamp", Date.now()); if (urlOrRequest) { if (typeof urlOrRequest === "string") { const url = urlOrRequest; context.set("iopa.OriginalUrl", url); context.set("iopa.Url", new URL(url)); } else { const url = urlOrRequest.url; context.set("iopa.OriginalUrl", url); context.set("iopa.Url", new URL(url)); context.set("iopa.RawRequest", urlOrRequest); context.set("iopa.RawRequestClone", urlOrRequest.clone()); } } context.createChild = this.createChildContext.bind(this, context); mergeContext(context, optionsValid); return context; } createChildContext(parentContext, urlPath, defaults) { const defaultsValid = _validOptions(defaults); const context = this.createContext(); context.set("server.Events", parentContext.get("server.Events")); context.set("server.Capabilities", parentContext.get("server.Capabilities")); context.app = parentContext.app; const parentUrl = parentContext.get("iopa.Url") || new URL("https://schema.iopa.io"); const childUrl = `${parentUrl.protocol}//${parentUrl.host}/${urlPath ? urlPath.replace(/^\//, "") : parentUrl.pathname.replace(/^\//, "")}`; context.set("iopa.OriginalUrl", childUrl); context.set("iopa.Url", new URL(childUrl)); if (parentContext.get("iopa.RawRequest")) { context.set("iopa.RawRequest", new Request(childUrl, { headers: defaultsValid["iopa.Headers"] || parentContext.get("iopa.Headers"), method: defaultsValid["iopa.Method"] || parentContext.get("iopa.Method"), body: null })); context.set("iopa.RawRequestClone", new Request(childUrl, { headers: defaultsValid["iopa.Headers"] || parentContext.get("iopa.Headers"), method: defaultsValid["iopa.Method"] || parentContext.get("iopa.Method"), body: null })); } mergeContext(context, defaultsValid); return context; } _dispose(context) { if (context === null || context.get("server.AbortController") === null) { return; } Object.keys(context).forEach((prop) => { context[prop] = null; }); this._factory.free(context); } }; function _validOptions(options) { if (typeof options === "string" || options instanceof String) { const result = {}; result["iopa.Method"] = options; return result; } return options || {}; } var maxSequence = 2 ** 16; var _lastSequence = Math.floor(Math.random() * (maxSequence - 1)); function _nextSequence() { _lastSequence += 1; if (_lastSequence === maxSequence) { _lastSequence = 1; } return _lastSequence.toString(); } // src/iopa/context-edge-cookies.ts var mins = ["m", "min", "mins", "minute", "minutes"]; var hrs = ["h", "hr", "hrs", "hour", "hours"]; var days = ["d", "day", "days"]; var weeks = ["w", "wk", "wks", "week", "weeks"]; var timeUnits = [mins, hrs, days, weeks]; function getSeconds(timeString) { const [val, unit] = timeString.split(" "); const time = Number.parseInt(val, 10); if (Number.isNaN(time)) { return 0; } const multipliers = [60, 3600, 86400, 604800]; for (let i = 0; i < timeUnits.length; i++) { if (timeUnits[i].includes(unit)) { return time * multipliers[i]; } } return 0; } function getMaxAge(age) { return `Max-Age=${getSeconds(age)}`; } function getExpires(date) { if (typeof date === "number") { return `Expires=${new Date(date).toUTCString()}`; } return `Expires=${new Date(Date.now() + getSeconds(date) * 1e3).toUTCString()}`; } function mapOptionsToString({ options }) { const { expires, maxAge, domain, path, secure, httpOnly, sameSite } = options; let opt = ""; if (expires) { opt += ` ${getExpires(expires)};`; } if (maxAge) { opt += ` ${getMaxAge(maxAge)};`; } if (domain) { opt += ` Domain=${domain};`; } if (path) { opt += ` Path=${path};`; } if (secure) { opt += " Secure;"; } if (httpOnly) { opt += " HttpOnly;"; } if (sameSite) { opt += ` SameSite=${sameSite};`; } return opt; } var context_edge_cookies_default = { set: (headers, name, value, options) => { let cookie = `${name}=${value};`; if (options) { cookie += mapOptionsToString({ options }); } headers.append("set-cookie", cookie); }, clear: (headers, name, options) => { let cookie = `${name}=; ${getExpires(0)};`; if (options) { cookie += mapOptionsToString({ options }); } headers.append("set-cookie", cookie); } }; // src/iopa/context-edge.ts var kRequest = Symbol("request"); var kRequestClone = Symbol("requestClone"); var kHeaders = Symbol("headers"); var kIsFinalized = Symbol("is_finalized"); var kResponse = Symbol("response"); var kStatusCode = Symbol("statusCode"); var ContextEdge = class extends ContextCore { [kRequest]; [kHeaders]; notFound; response; cookies; signedCookies; secret; init() { super.init(); this.set("iopa.Headers", new Headers()); return this; } get body() { return this[kRequest].body; } get headers() { return this[kRequest] ? this[kRequest].headers : this[kHeaders]; } get url() { return this["iopa.OriginalUrl"]; } get signal() { return this[kRequest] ? this[kRequest].signal : this.get("server.AbortSignal"); } get bodyUsed() { return this[kRequest].bodyUsed; } get method() { return this[kRequest].method; } get "iopa.Method"() { return this[kRequest].method; } set "iopa.Method"(value) { } get "iopa.Headers"() { return this[kRequest] ? this[kRequest].headers : this[kHeaders]; } set "iopa.Headers"(headers) { this[kHeaders] = headers; } get "iopa.RawRequest"() { return this[kRequest]; } set "iopa.RawRequest"(request) { this[kRequest] = request; } get "iopa.RawRequestClone"() { return this[kRequestClone]; } set "iopa.RawRequestClone"(request) { this[kRequestClone] = request; } param(key) { const params = this.get("iopa.Params") || {}; if (params) { if (key) { return params[key]; } else { return params; } } return void 0; } query(key) { const params = Object.fromEntries(this.get("iopa.Url").searchParams.entries()); if (params) { if (key) { return params[key]; } else { return params; } } return void 0; } async blob() { return this[kRequest].blob(); } async arrayBuffer() { return this[kRequest].arrayBuffer(); } async text() { return this[kRequest].text(); } async json() { return this[kRequest].json(); } async formData() { return this[kRequest].formData(); } redirect(location, code) { if (typeof location !== "string") { throw new TypeError("location must be a string!"); } if (!isAbsoluteURL(location)) { const url = this.get("iopa.Url"); url.pathname = location; location = url.toString(); } this.response.status(code || 302).headers.set("location", location); return this; } respondWith(res, options) { if (res instanceof Error || !this.response[kIsFinalized]) { this.response.respondWith(res, options); } return this; } }; var ContextReply = class extends IopaMap { [kHeaders]; [kStatusCode]; "iopa.Version"; [kResponse]; [kIsFinalized] = false; get "iopa.StatusCode"() { return this[kStatusCode] || 200; } set "iopa.StatusCode"(value) { this[kStatusCode] = value; } get headers() { return this[kHeaders]; } get redirected() { const status = this.get("iopa.StatusCode"); return status === 302 || status === 301; } get ok() { const status = this.get("iopa.StatusCode"); return status < 300; } get statusText() { return this.get("iopa.StatusText"); } get "iopa.Headers"() { return this[kHeaders]; } set "iopa.Headers"(headers) { this[kHeaders] = headers; } get "iopa.IsFinalized"() { return this[kIsFinalized]; } get "iopa.RawResponse"() { if (this[kIsFinalized]) { return this[kResponse]; } else { return new Response(this.get("iopa.Body"), { headers: this.get("iopa.Headers"), status: this.get("iopa.StatusCode"), statusText: this.get("iopa.StatusText") || getStatusText(this.get("iopa.StatusCode")) || void 0 }); } } get "iopa.RawResponseClone"() { if (this[kIsFinalized]) { return Promise.resolve(this[kResponse]).then((x) => x.clone()); } else { return new Response(this.get("iopa.Body"), { headers: this.get("iopa.Headers"), status: this.get("iopa.StatusCode"), statusText: this.get("iopa.StatusText") || void 0 }); } } init() { this.set("iopa.Version", VERSION); this[kHeaders] = new Headers(); this[kIsFinalized] = false; return this; } status(code) { this.set("iopa.StatusCode", code); return this; } header(field, value) { this.get("iopa.Headers").set(field, value); return this; } cookie(name, value, options) { context_edge_cookies_default.set(this.get("iopa.Headers"), name, value, options); return this; } clearCookie(name, options) { context_edge_cookies_default.clear(this.get("iopa.Headers"), name, options); return this; } arrayBuffer(data) { this.set("iopa.Body", data); return this; } formData(data) { this.set("iopa.Body", data); return this; } blob(data) { this.set("iopa.Body", data); return this; } json(data) { this.header("Content-Type", "application/json; charset=UTF-8"); this.set("iopa.Body", JSON.stringify(data)); return this; } text(data) { this.header("Content-Type", "text/html; charset=UTF-8"); this.set("iopa.Body", data); return this; } html(data) { this.header("Content-Type", "text/plain; charset=UTF-8"); this.set("iopa.Body", data); return this; } respondWith(res, options) { if (this[kIsFinalized]) { return this; } let status; if (options || options === 0) { if (typeof options === "number") { status = options; } else { status = options.status; } } if (options && options.headers) { Object.entries(options.headers).forEach(([key, value]) => this[kHeaders].set(key, value)); } if (status) { this.status(status); } if (res === void 0 || res === null) { this[kResponse] = this.get("iopa.RawResponse"); this[kIsFinalized] = true; } else if (res instanceof Response) { this[kResponse] = res; this[kIsFinalized] = true; } else if (res instanceof Error) { this.status(res.statusCode || status || 500).text(`${res.statusCode || status || 500} ERROR ${res.name} ${res.message}`); } else { if (typeof res === "number" && !status) { this.status(res); } else if (typeof res === "string" || res instanceof String) { this.text(res).status(status || 200); } else if (typeof res === "boolean") { if (res) { this.text(`true`).status(status || 200); } else { this.text(`false`).status(status || 200); } } else if ("iopa.Version" in res) { } else { this.json(res).status(status || 200); } this[kResponse] = this.get("iopa.RawResponse"); this[kIsFinalized] = true; } return this; } }; // src/iopa/factory-edge.ts var FactoryEdge = class extends Factory { _factoryResponse; constructor(name = "IopaContext", size = 100, factory = () => new ContextEdge()) { super(name, size, factory); this._factoryResponse = new FreeList("IopaReply", size, () => new ContextReply()); } createContext(urlOrRequest, options) { const context = super.createContext(urlOrRequest, options); const response = this._factoryResponse.alloc().init(); context.response = response; return context; } _dispose(context) { if (context === null || context.get("server.AbortController") === null) { return; } if (context.response) { const { response } = context; Object.keys(response).forEach((prop) => { response[prop] = null; }); this._factoryResponse.free(response); } Object.keys(context).forEach((prop) => { context[prop] = null; }); this._factory.free(context); } }; // src/appBuilder/middleware.ts function Middleware(app, middleware, action = "invoke") { let args; if (typeof middleware === "function") { switch (middleware.length) { case 0: middleware.call(app); return (context, next) => { return middleware.invoke.call(context, next); }; break; case 1: args = _getParamNames(middleware); if (arrayEqual(args, ["next"])) { return (context, next) => { return middleware.call(context, next); }; } const mw = new middleware(app); return mw.invoke.bind(mw); case 2: args = _getParamNames(middleware); if (arrayEqual(args, ["req", "res"])) { throw new Error("must require 'iopa-connect' to use Connect/Express style middleware"); } else { return middleware; } case 3: throw new Error("must require 'iopa-connect' to use Connect/Express style middleware"); case 4: throw new Error("must require 'iopa-connect' to use Connect/Express style middleware"); default: throw new Error("unknown middleware"); } } else { console.error("middleware must be a function"); throw new Error("middleware must be called on a function"); } } var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; function _getParamNames(func) { const fnStr = func.toString().replace(STRIP_COMMENTS, ""); let result = fnStr.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")).match(/([^\s,]+)/g); if (result === null) { result = []; } return result; } function arrayEqual(array1, array2) { if (!array2) { return false; } if (array1.length !== array2.length) { return false; } for (let i = 0, l = array1.length; i < l; i++) { if (array1[i] instanceof Array && array2[i] instanceof Array) { if (!array1[i].compare(array2[i])) { return false; } } else if (array1[i] !== array2[i]) { return false; } } return true; } // src/appBuilder/appBuilder.ts var AppBuilder = class { properties; _middleware; middlewareProxy = Middleware; _factory; _abortController; constructor(appProperties, appOptions = { "server.Version": VERSION, "server.Base": "core" }) { this._abortController = new AbortController(); process.on("exit", this._abort); process.on("SIGINT", this._abort); process.on("uncaughtException", this._exception); this._abortController.signal.addEventListener("abort", this._dispose); const defaults = { "iopa.Id": guid(), "server.Capabilities": { [URN_APP]: appOptions }, "server.Events": new ExtendableEventTarget(), "server.Logger": logging_default, "server.NotFound": DefaultApp, "server.ErrorHandler": ErrorHandler, "server.IsBuilt": false, "server.Pipeline": void 0, "server.AbortController": this._abortController, "server.AbortSignal": this._abortController.signal }; if (typeof appProperties === "string") { appProperties = { "iopa.Id": appProperties }; } if (appProperties) { Object.entries(appProperties).forEach(([key, value]) => { if (key === "server.Capabilities") { const capabilities = defaults["server.Capabilities"]; Object.entries(value).forEach(([key2, value2]) => { capabilities[key2] = value2; }); } else { defaults[key] = value; } }); } this._factory = defaults["server.Factory"] ?? appOptions["server.Base"] === "request" ? new FactoryEdge() : new Factory(); this.properties = new IopaMap(defaults); this._middleware = { invoke: [], dispatch: [] }; } _dispose = () => { this._abortController.signal.removeEventListener("abort", this._dispose); process.off("exit", this._abort); process.off("SIGINT", this._abort); process.off("uncaughtException", this._exception); }; _abort = () => { this._abortController.abort(); }; _exception = (e) => { console.log("Uncaught Exception..."); console.log(e.stack); this._abortController.abort(); process.exit(99); }; capability(keyOrRef) { if (typeof keyOrRef === "string") { return this._capability(keyOrRef); } else { return this._capability(keyOrRef.id); } } _capability(keyOrRef) { return this.properties.get("server.Capabilities")[keyOrRef]; } setCapability(keyOrRef, value) { if (typeof keyOrRef === "string") { return this._setCapability(keyOrRef, value); } else { return this._setCapability(keyOrRef.id, value); } } _setCapability(keyOrRef, value) { this.properties.get("server.Capabilities")[keyOrRef] = value; } createContext(urlOrRequest, options) { return this._factory.createContext(urlOrRequest, options); } fork(when) { const subApp = new AppBuilder(this.properties.toJSON()); this.use(async function forkFunction(context, next) { if (!subApp.properties.get("server.IsBuilt")) { subApp.build(); } if (when(context)) { await subApp.invoke(context); return Promise.resolve(null); } return next(); }, "forkFuncton"); return subApp; } use(arg0, arg1, options) { let id, mw, method; if (typeof arg0 === "object" && "default" in arg0) { arg0 = arg0["default"]; } else if (typeof arg1 === "object" && "default" in arg1) { arg1 = arg1["default"]; } if (typeof arg0 === "function" && typeof arg1 === "string") { id = arg1; mw = arg0; method = "invoke"; } else if (typeof arg0 === "function" && !arg1) { id = arg0.name || arg0.toString().split(/\r?\n/)[0]; mw = arg0; method = "invoke"; } else if (arg0 === void 0) { throw new Error(`app.use called with undefined / empty middleware for ${mw}`); } else { id = arg1.name || arg1.toString().split(/\r?\n/)[0]; method = arg0; mw = arg1; } if (!this._middleware[method]) { throw new Error(`Unknown AppBuilder Category ${JSON.stringify(method, null, 2)} for ${id}`); } const params = _getParams(mw); if (params === "app" || mw.length === 1) { const Mw = mw; let mwInstance; try { mwInstance = new Mw(this, options); } catch (ex) { mwInstance = Mw(this, options) || {}; } if (typeof mwInstance.invoke === "function") { const fn = mwInstance.invoke.bind(mwInstance); fn.id = id; this._middleware.invoke.push(fn); } if (typeof mwInstance.dispatch === "function") { const fn = mwInstance.dispatch.bind(mwInstance); fn.id = id; this._middleware.dispatch.push(fn); } } else { if (options) { throw new Error(`Cannot instantiate AppFunc ${id} with options, use a class constructor instead`); } const fn = this.middlewareProxy(this, mw); fn.id = id; this._middleware[method].push(fn); } return this; } dispose() { } build() { const middleware = this._middleware.invoke.concat(this.properties.get("server.NotFound")); const pipeline = this._compose(middleware); if (this._middleware.dispatch.length > 0) { pipeline.dispatch = this._compose(this._middleware.dispatch.reverse()); } else { pipeline.dispatch = (context) => { return Promise.resolve(); }; } pipeline.properties = this.properties; this.properties.set("server.IsBuilt", true); this.properties.set("server.Pipeline", pipeline); return pipeline; } _buildDynamic(handler) { const middleware = this._middleware.invoke.concat(handler); return this._compose(middleware); } dispatch(context) { return this.properties.get("server.Pipeline").dispatch.call(this, context); } invoke(context) { context.log = this.properties.get("server.Logger").child({ tid: context.get("iopa.Id") }); if (!this.properties.get("server.IsBuilt")) { this.build(); } return this.properties.get("server.Pipeline").call(this, context); } childInvoke(context, handler, id) { handler.id = id || "childInvoke"; const pipeline = this._buildDynamic(handler); context.log = this.properties.get("server.Logger").child({ tid: context.get("iopa.Id") }); return pipeline.call(this, context); } _compose(middleware) { let i; let next; let curr; i = middleware.length; next = () => { return Promise.resolve(); }; while (i--) { curr = middleware[i]; if (!curr) { console.error(middleware, i); throw new Error("Missing middleware"); } next = ((fn, prev, context) => { context.set("server.CurrentMiddleware", fn.id); const _next = () => { context.dispatchEvent(new TraceEvent("trace-next", { label: fn.id })); return prev.call(this, context).then(() => { context.dispatchEvent(new TraceEvent("trace-next-resume", { label: fn.id })); }); }; _next.invoke = prev; context.dispatchEvent(new TraceEvent("trace-start", { label: fn.id })); const _oldLogger = context.log; context.log = context.log.child({ middleware: fn.id }); return Promise.resolve(fn.call(this, context, _next)).then((res) => { context.log = _oldLogger; if (res || res === false || res === 0 || res === "") { ; context.respondWith(res); } context.dispatchEvent(new TraceEvent("trace-end", { label: fn.id })); }); }).bind(this, curr, next); } const rootPipeline = next; return (context) => { const capabilities = this.properties.get("server.Capabilities"); context.set("server.Capabilities", capabilities); context.set("server.Events", this.properties.get("server.Events")); context.app = this; return rootPipeline.call(this, context); }; } usePlugin(plugin) { throw new Error("Not Implemented"); } }; var RouterApp = class extends AppBuilder { head; post; get; options; put; delete; patch; all; config; constructor(appProperties = void 0, appOptions = {}) { super(appProperties, { "server.Base": "request", ...appOptions }); } registerFeatureFlag(flagOptions) { throw new Error("Method not implemented."); } }; function DefaultApp(context) { return Promise.resolve(); } DefaultApp.id = "DefaultApp"; function ErrorHandler(ex, context) { throw ex; } ErrorHandler.id = "ErrorHandler"; var STRIP_COMMENTS2 = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; function _getParams(func) { const fnStr = func.toString().replace(STRIP_COMMENTS2, ""); let result = fnStr.slice(fnStr.indexOf("(") + 1, fnStr.indexOf(")")).match(/([^\s,]+)/g); if (result === null) { result = []; } return result.join(); } // src/index.ts var iopaPrototype = { cloneKeyBehaviors }; var util = { shallow: shallow_exports, url: url_exports, status: status_exports, prototype: iopaPrototype, Disposable }; export { AppBuilder as App, AppRef, ContextCore, ContextEdge, ContextReply, IopaMap, RouterApp, TraceEvent, URN_APP, VERSION, consoleLogger, iopaPrototype, util };