@ecosy/core 0.4.0 → 0.5.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/CHANGELOG.md +163 -0
- package/dist/cache/disk.d.ts +32 -0
- package/dist/cache/disk.js +1 -0
- package/dist/cache/disk.mjs +1 -0
- package/dist/cache/index.d.ts +17 -0
- package/dist/cache/index.js +1 -0
- package/dist/cache/index.mjs +1 -0
- package/dist/cache/memory.d.ts +19 -0
- package/dist/cache/memory.js +1 -0
- package/dist/cache/memory.mjs +1 -0
- package/dist/cache/redis.d.ts +63 -0
- package/dist/cache/redis.js +1 -0
- package/dist/cache/redis.mjs +1 -0
- package/dist/cache/types.d.ts +35 -0
- package/dist/cache/types.js +1 -0
- package/dist/cache/types.mjs +1 -0
- package/dist/ecosy-core.umd.js +1 -1
- package/dist/ecosy-core.umd.js.map +1 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/serialize/index.d.ts +62 -0
- package/dist/serialize/index.js +1 -0
- package/dist/serialize/index.mjs +1 -0
- package/dist/serialize/interpolate.d.ts +10 -0
- package/dist/serialize/interpolate.js +1 -0
- package/dist/serialize/interpolate.mjs +1 -0
- package/dist/serialize/json.d.ts +3 -0
- package/dist/serialize/json.js +1 -0
- package/dist/serialize/json.mjs +1 -0
- package/dist/serialize/primitive.d.ts +3 -0
- package/dist/serialize/primitive.js +1 -0
- package/dist/serialize/primitive.mjs +1 -0
- package/dist/serialize/query-string.d.ts +3 -0
- package/dist/serialize/query-string.js +1 -0
- package/dist/serialize/query-string.mjs +1 -0
- package/dist/serialize/types.d.ts +42 -0
- package/dist/serialize/url.d.ts +3 -0
- package/dist/serialize/url.js +1 -0
- package/dist/serialize/url.mjs +1 -0
- package/dist/syhemo/collectors.d.ts +25 -0
- package/dist/syhemo/collectors.js +1 -0
- package/dist/syhemo/collectors.mjs +1 -0
- package/dist/syhemo/exposition.d.ts +15 -0
- package/dist/syhemo/exposition.js +1 -0
- package/dist/syhemo/exposition.mjs +1 -0
- package/dist/syhemo/index.d.ts +50 -0
- package/dist/syhemo/index.js +1 -0
- package/dist/syhemo/index.mjs +1 -0
- package/dist/{logger.d.ts → syhemo/logger.d.ts} +23 -11
- package/dist/syhemo/logger.js +1 -0
- package/dist/{logger.mjs → syhemo/logger.mjs} +1 -1
- package/dist/syhemo/types.d.ts +114 -0
- package/dist/utilities/index.d.ts +2 -0
- package/dist/utilities/index.js +1 -1
- package/dist/utilities/index.mjs +1 -1
- package/dist/{searchify.d.ts → utilities/searchify.d.ts} +1 -1
- package/dist/utilities/searchify.js +1 -0
- package/dist/utilities/searchify.mjs +1 -0
- package/package.json +35 -37
- package/agents/skills/ecosy-core-fetcher/SKILL.md +0 -93
- package/dist/env.d.ts +0 -26
- package/dist/env.js +0 -1
- package/dist/env.mjs +0 -1
- package/dist/http/array.d.ts +0 -2
- package/dist/http/array.js +0 -1
- package/dist/http/array.mjs +0 -1
- package/dist/http/client.d.ts +0 -38
- package/dist/http/client.js +0 -1
- package/dist/http/client.mjs +0 -1
- package/dist/http/endpoint.d.ts +0 -8
- package/dist/http/endpoint.js +0 -1
- package/dist/http/endpoint.mjs +0 -1
- package/dist/http/fetcher.d.ts +0 -84
- package/dist/http/fetcher.js +0 -1
- package/dist/http/fetcher.mjs +0 -1
- package/dist/http/http-core.d.ts +0 -5
- package/dist/http/http-core.js +0 -1
- package/dist/http/http-core.mjs +0 -1
- package/dist/http/http-static.d.ts +0 -20
- package/dist/http/http-static.js +0 -1
- package/dist/http/http-static.mjs +0 -1
- package/dist/http/http.d.ts +0 -56
- package/dist/http/http.js +0 -1
- package/dist/http/http.mjs +0 -1
- package/dist/http/index.d.ts +0 -14
- package/dist/http/index.js +0 -1
- package/dist/http/index.mjs +0 -1
- package/dist/http/init.d.ts +0 -83
- package/dist/http/init.js +0 -1
- package/dist/http/init.mjs +0 -1
- package/dist/http/method.d.ts +0 -15
- package/dist/http/method.js +0 -1
- package/dist/http/method.mjs +0 -1
- package/dist/http/plugins/cache.d.ts +0 -13
- package/dist/http/plugins/cache.js +0 -1
- package/dist/http/plugins/cache.mjs +0 -1
- package/dist/http/plugins/dedupe.d.ts +0 -7
- package/dist/http/plugins/dedupe.js +0 -1
- package/dist/http/plugins/dedupe.mjs +0 -1
- package/dist/http/plugins/index.d.ts +0 -3
- package/dist/http/plugins/index.js +0 -1
- package/dist/http/plugins/index.mjs +0 -1
- package/dist/http/plugins/logger.d.ts +0 -24
- package/dist/http/plugins/logger.js +0 -1
- package/dist/http/plugins/logger.mjs +0 -1
- package/dist/http/request.d.ts +0 -26
- package/dist/http/response.d.ts +0 -9
- package/dist/http/utils.d.ts +0 -21
- package/dist/http/utils.js +0 -1
- package/dist/http/utils.mjs +0 -1
- package/dist/http/xml.d.ts +0 -24
- package/dist/http/xml.js +0 -1
- package/dist/http/xml.mjs +0 -1
- package/dist/logger.js +0 -1
- package/dist/node_modules/tslib/tslib.es6.js +0 -1
- package/dist/node_modules/tslib/tslib.es6.mjs +0 -1
- package/dist/searchify.js +0 -1
- package/dist/searchify.mjs +0 -1
- package/dist/serialize.d.ts +0 -80
- package/dist/serialize.js +0 -1
- package/dist/serialize.mjs +0 -1
- package/dist/syhemo.d.ts +0 -102
- package/dist/syhemo.js +0 -1
- package/dist/syhemo.mjs +0 -1
- /package/dist/{http/request.js → serialize/types.js} +0 -0
- /package/dist/{http/request.mjs → serialize/types.mjs} +0 -0
- /package/dist/{http/response.js → syhemo/types.js} +0 -0
- /package/dist/{http/response.mjs → syhemo/types.mjs} +0 -0
- /package/dist/{slugify.d.ts → utilities/slugify.d.ts} +0 -0
- /package/dist/{slugify.js → utilities/slugify.js} +0 -0
- /package/dist/{slugify.mjs → utilities/slugify.mjs} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
export * from "./env";
|
|
2
|
-
export * from "./http";
|
|
3
|
-
export * from "./logger";
|
|
4
|
-
export * from "./searchify";
|
|
5
1
|
export * from "./serialize";
|
|
6
|
-
export * from "./slugify";
|
|
7
2
|
export * from "./subscriber";
|
|
8
3
|
export * from "./utilities";
|
|
9
|
-
export type * from "./http";
|
|
10
|
-
export type * from "./logger";
|
|
11
|
-
export type * from "./searchify";
|
|
12
4
|
export type * from "./serialize";
|
|
13
|
-
export type * from "./slugify";
|
|
14
5
|
export type * from "./subscriber";
|
|
15
6
|
export type * from "./utilities";
|
|
16
7
|
export type * from "./types";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./
|
|
1
|
+
"use strict";var e=require("./serialize/index.js"),i=require("./subscriber.js"),r=require("./utilities/clone.js"),s=require("./utilities/defer.js"),t=require("./utilities/filelist.js"),o=require("./utilities/flatten.js"),u=require("./utilities/freeze.js"),l=require("./utilities/get.js"),a=require("./utilities/is-equal.js"),p=require("./utilities/is-function.js"),x=require("./utilities/object.js"),j=require("./utilities/merge.js"),n=require("./utilities/formdata.js"),c=require("./utilities/sanitize-mime.js"),q=require("./utilities/searchify.js"),f=require("./utilities/slugify.js"),b=require("./utilities/string.js"),g=require("./serialize/json.js"),m=require("./serialize/primitive.js"),y=require("./serialize/url.js"),z=require("./serialize/interpolate.js"),E=require("./serialize/query-string.js");exports.Serialize=e.Serialize,exports.Subscriber=i.Subscriber,exports.clone=r.clone,exports.defer=s.defer,exports.deferAsync=s.deferAsync,exports.isFileList=t.isFileList,exports.escapeRegexKey=o.escapeRegexKey,exports.flatten=o.flatten,exports.flattenToArray=o.flattenToArray,exports.freeze=u.freeze,exports.get=l.get,exports.isEqual=a.isEqual,exports.isFunction=p.isFunction,exports.hasOwnProperty=x.hasOwnProperty,exports.isComplexObject=x.isComplexObject,exports.isLiteralObject=x.isLiteralObject,exports.isObject=x.isObject,exports.isObjectable=x.isObjectable,exports.merge=j.merge,exports.isFormData=n.isFormData,exports.objectToFormData=n.objectToFormData,exports.MIME_REGEX=c.MIME_REGEX,exports.sanitizeMime=c.sanitizeMime,exports.searchify=q.searchify,exports.DEFAULT_TRANSFORMER=f.DEFAULT_TRANSFORMER,exports.slugify=f.slugify,exports.pascalToKebab=b.pascalToKebab,exports.toString=b.toString,exports.ucfirst=b.ucfirst,exports.Json=g.Json,exports.Primitive=m.Primitive,exports.Url=y.Url,exports.interpolate=z.interpolate,exports.queryString=E.queryString;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{Serialize}from"./serialize/index.mjs";export{Subscriber}from"./subscriber.mjs";export{clone}from"./utilities/clone.mjs";export{defer,deferAsync}from"./utilities/defer.mjs";export{isFileList}from"./utilities/filelist.mjs";export{escapeRegexKey,flatten,flattenToArray}from"./utilities/flatten.mjs";export{freeze}from"./utilities/freeze.mjs";export{get}from"./utilities/get.mjs";export{isEqual}from"./utilities/is-equal.mjs";export{isFunction}from"./utilities/is-function.mjs";export{hasOwnProperty,isComplexObject,isLiteralObject,isObject,isObjectable}from"./utilities/object.mjs";export{merge}from"./utilities/merge.mjs";export{isFormData,objectToFormData}from"./utilities/formdata.mjs";export{MIME_REGEX,sanitizeMime}from"./utilities/sanitize-mime.mjs";export{searchify}from"./utilities/searchify.mjs";export{DEFAULT_TRANSFORMER,slugify}from"./utilities/slugify.mjs";export{pascalToKebab,toString,ucfirst}from"./utilities/string.mjs";export{Json}from"./serialize/json.mjs";export{Primitive}from"./serialize/primitive.mjs";export{Url}from"./serialize/url.mjs";export{interpolate}from"./serialize/interpolate.mjs";export{queryString}from"./serialize/query-string.mjs";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { interpolate } from "./interpolate";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
export { interpolate } from "./interpolate";
|
|
4
|
+
export { Json } from "./json";
|
|
5
|
+
export { Primitive } from "./primitive";
|
|
6
|
+
export { queryString } from "./query-string";
|
|
7
|
+
export { Url } from "./url";
|
|
8
|
+
/**
|
|
9
|
+
* Centralized serialization engine and type-guard toolkit.
|
|
10
|
+
* Addresses common pitfalls of `JSON.stringify`/`JSON.parse` — BigInt safety,
|
|
11
|
+
* Date preservation, undefined stripping, and safe URL encoding/decoding.
|
|
12
|
+
*
|
|
13
|
+
* This is the convenience surface: it gathers every part under one name.
|
|
14
|
+
* Referencing it keeps all of them, because a class is a single binding and a
|
|
15
|
+
* bundler cannot drop half of one. Code that cares about what it ships should
|
|
16
|
+
* import the part it uses instead — each lives in its own module, so nothing
|
|
17
|
+
* else comes with it:
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { Primitive } from "@ecosy/core/serialize/primitive";
|
|
21
|
+
* import { queryString } from "@ecosy/core/serialize/query-string";
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The two are the same objects, so mixing them is free.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* Serialize.JSON.stringify({ date: new Date(), big: 123n });
|
|
29
|
+
* Serialize.JSON.parse<User>(jsonText);
|
|
30
|
+
* Serialize.URL.encode("hello world");
|
|
31
|
+
* Serialize.queryString.stringify({ page: 1, tags: ["a", "b"] });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class Serialize {
|
|
35
|
+
static readonly interpolate: typeof interpolate;
|
|
36
|
+
/** Type guards and deep normalization utilities. */
|
|
37
|
+
static readonly Primitive: {
|
|
38
|
+
readonly isString: (value: unknown) => value is string;
|
|
39
|
+
readonly isNumber: (value: unknown) => value is number;
|
|
40
|
+
readonly isBoolean: (value: unknown) => value is boolean;
|
|
41
|
+
readonly isPrimitive: (value: unknown) => value is import("./types").PrimitiveValue;
|
|
42
|
+
readonly isDate: (value: unknown) => value is Date;
|
|
43
|
+
readonly isPlainObject: (value: unknown) => value is import("..").LiteralObject;
|
|
44
|
+
readonly normalize: <T, R = unknown>(data: T) => R;
|
|
45
|
+
};
|
|
46
|
+
/** Safe JSON stringify/parse that never throws. */
|
|
47
|
+
static readonly JSON: {
|
|
48
|
+
readonly stringify: (value: unknown, space?: number) => string;
|
|
49
|
+
readonly parse: <T = unknown>(text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => T | null;
|
|
50
|
+
};
|
|
51
|
+
/** URL encoding/decoding with malformed-character recovery. */
|
|
52
|
+
static readonly URL: {
|
|
53
|
+
readonly encode: (value: string, component?: boolean | ((value: string) => string)) => string;
|
|
54
|
+
readonly decode: (value: string, component?: boolean | ((value: string) => string)) => string;
|
|
55
|
+
readonly build: (uri: string, params?: Record<string, import("./types").PrimitiveValue> | null) => string;
|
|
56
|
+
};
|
|
57
|
+
/** Query string parse/stringify with configurable array formats. */
|
|
58
|
+
static readonly queryString: {
|
|
59
|
+
readonly parse: (query: string) => Record<string, string>;
|
|
60
|
+
readonly stringify: (params: Record<string, unknown>, options?: import("./types").SerializeQueryOptions) => string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var r=require("./interpolate.js"),e=require("./json.js"),i=require("./primitive.js"),t=require("./query-string.js"),s=require("./url.js");class o{}o.interpolate=r.interpolate,o.Primitive=i.Primitive,o.JSON=e.Json,o.URL=s.Url,o.queryString=t.queryString,exports.interpolate=r.interpolate,exports.Json=e.Json,exports.Primitive=i.Primitive,exports.queryString=t.queryString,exports.Url=s.Url,exports.Serialize=o;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{interpolate as r}from"./interpolate.mjs";import{Json as m}from"./json.mjs";import{Primitive as i}from"./primitive.mjs";import{queryString as o}from"./query-string.mjs";import{Url as t}from"./url.mjs";class e{}e.interpolate=r,e.Primitive=i,e.JSON=m,e.URL=t,e.queryString=o;export{m as Json,i as Primitive,e as Serialize,t as Url,r as interpolate,o as queryString};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpolates `{key}` placeholders in a string using deep path resolution via {@link get}.
|
|
3
|
+
* Objects are silently replaced with empty strings to avoid `[object Object]`.
|
|
4
|
+
*
|
|
5
|
+
* @param pattern - Template string with `{key}` or `{path.to.key}` placeholders.
|
|
6
|
+
* @param params - Data object or array to resolve values from.
|
|
7
|
+
* @returns The interpolated string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function interpolate(pattern: string, params?: Record<string, unknown> | Array<unknown>): string;
|
|
10
|
+
/** Type guards and deep normalization utilities. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t=require("../utilities/get.js");exports.interpolate=function(e,r={}){return e&&"string"==typeof e&&e.trim().length&&e.includes("{")&&e.includes("}")?e.replace(/\{([a-zA-Z0-9_.-]+)\}/g,(e,i)=>{const n=t.get(r,i);return null==n||"object"==typeof n?"":String(n)}):e};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{get as t}from"../utilities/get.mjs";function e(e,n={}){return e&&"string"==typeof e&&e.trim().length&&e.includes("{")&&e.includes("}")?e.replace(/\{([a-zA-Z0-9_.-]+)\}/g,(e,r)=>{const i=t(n,r);return null==i||"object"==typeof i?"":String(i)}):e}export{e as interpolate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var r=require("../utilities/freeze.js"),e=require("./primitive.js");const t=r.freeze({stringify:(r,t)=>{var i;try{const n=e.Primitive.normalize(r);return null!==(i=JSON.stringify(n,null,t))&&void 0!==i?i:""}catch(r){return""}},parse:(r,e)=>{if(!r)return null;try{return JSON.parse(r,e)}catch(r){return null}}});exports.Json=t;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{freeze as r}from"../utilities/freeze.mjs";import{Primitive as t}from"./primitive.mjs";const i=r({stringify:(r,i)=>{var e;try{const n=t.normalize(r);return null!==(e=JSON.stringify(n,null,i))&&void 0!==e?e:""}catch(r){return""}},parse:(r,t)=>{if(!r)return null;try{return JSON.parse(r,t)}catch(r){return null}}});export{i as Json};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var i=require("../utilities/freeze.js"),e=require("../utilities/object.js"),t=require("../utilities/is-function.js");const r=i.freeze({isString:i=>"string"==typeof i,isNumber:i=>"number"==typeof i&&Number.isFinite(i),isBoolean:i=>"boolean"==typeof i,isPrimitive:i=>!e.isObjectable(i),isDate:i=>i instanceof Date&&!Number.isNaN(i.getTime()),isPlainObject:e.isLiteralObject,normalize(i){const s=r;if(s.isPrimitive(i))return"bigint"==typeof i?i.toString():i;if(s.isDate(i))return i.toISOString();if(Array.isArray(i))return i.map(i=>s.normalize(i));if(e.isLiteralObject(i)){const t={};for(const r in i)if(e.hasOwnProperty(i,r)){const e=i[r];void 0!==e&&(t[r]=s.normalize(e))}return t}return i&&e.hasOwnProperty(i,"toJSON")&&t.isFunction(i.toJSON)?i.toJSON():{}}});exports.Primitive=r;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{freeze as i}from"../utilities/freeze.mjs";import{isLiteralObject as t,hasOwnProperty as e,isObjectable as r}from"../utilities/object.mjs";import{isFunction as o}from"../utilities/is-function.mjs";const n=i({isString:i=>"string"==typeof i,isNumber:i=>"number"==typeof i&&Number.isFinite(i),isBoolean:i=>"boolean"==typeof i,isPrimitive:i=>!r(i),isDate:i=>i instanceof Date&&!Number.isNaN(i.getTime()),isPlainObject:t,normalize(i){const r=n;if(r.isPrimitive(i))return"bigint"==typeof i?i.toString():i;if(r.isDate(i))return i.toISOString();if(Array.isArray(i))return i.map(i=>r.normalize(i));if(t(i)){const t={};for(const o in i)if(e(i,o)){const e=i[o];void 0!==e&&(t[o]=r.normalize(e))}return t}return i&&e(i,"toJSON")&&o(i.toJSON)?i.toJSON():{}}});export{n as Primitive};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var r=require("../utilities/freeze.js"),e=require("./primitive.js"),t=require("./url.js");const i=r.freeze({parse(r){if(!r)return{};const e=r.startsWith("?")?r.slice(1):r,i={};return e.split("&").forEach(r=>{if(!r)return;const[e,s]=r.split("=");e&&(i[t.Url.decode(e)]=s?t.Url.decode(s):"")}),i},stringify(r,i={}){if(null===r||"object"!=typeof r)return"";const{arrayFormat:s="none",arrayFormatSeparator:o=",",skipNull:n=!1,skipEmptyString:l=!1,encode:a=!0,strict:u=!0,sort:c=!1}=i,p=r=>r.length>0&&/^[a-zA-Z0-9_\-.[\]]+$/.test(r),f=r=>a?t.Url.encode(r,a):r,$=[],y=(r,t)=>{if(Array.isArray(t)){if("comma"===s||"separator"===s){const e=t.filter(r=>null!=r&&""!==r);return void(e.length>0&&$.push(`${f(r)}=${f(e.map(String).join(o))}`))}t.forEach((e,t)=>{let i=r;"bracket"===s?i=`${r}[]`:"index"===s&&(i=`${r}[${t}]`),y(i,e)})}else if(e.Primitive.isPlainObject(t))for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&y(`${r}[${e}]`,t[e]);else null!=t?""!==t?"boolean"!=typeof t?e.Primitive.isDate(t)?$.push(`${f(r)}=${f(t.toISOString())}`):$.push(`${f(r)}=${f(String(t))}`):$.push(`${f(r)}=${t?"true":"false"}`):l||$.push(`${f(r)}=`):n||$.push(`${f(r)}=`)};let h=Object.keys(r);c&&(h="function"==typeof c?h.sort(c):h.sort());for(const e of h)u&&!p(e)||y(e,r[e]);return $.join("&")}});exports.queryString=i;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{freeze as t}from"../utilities/freeze.mjs";import{Primitive as r}from"./primitive.mjs";import{Url as e}from"./url.mjs";const o=t({parse(t){if(!t)return{};const r=t.startsWith("?")?t.slice(1):t,o={};return r.split("&").forEach(t=>{if(!t)return;const[r,s]=t.split("=");r&&(o[e.decode(r)]=s?e.decode(s):"")}),o},stringify(t,o={}){if(null===t||"object"!=typeof t)return"";const{arrayFormat:s="none",arrayFormatSeparator:i=",",skipNull:n=!1,skipEmptyString:a=!1,encode:p=!0,strict:l=!0,sort:c=!1}=o,f=t=>t.length>0&&/^[a-zA-Z0-9_\-.[\]]+$/.test(t),u=t=>p?e.encode(t,p):t,m=[],$=(t,e)=>{if(Array.isArray(e)){if("comma"===s||"separator"===s){const r=e.filter(t=>null!=t&&""!==t);return void(r.length>0&&m.push(`${u(t)}=${u(r.map(String).join(i))}`))}e.forEach((r,e)=>{let o=t;"bracket"===s?o=`${t}[]`:"index"===s&&(o=`${t}[${e}]`),$(o,r)})}else if(r.isPlainObject(e))for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&$(`${t}[${r}]`,e[r]);else null!=e?""!==e?"boolean"!=typeof e?r.isDate(e)?m.push(`${u(t)}=${u(e.toISOString())}`):m.push(`${u(t)}=${u(String(e))}`):m.push(`${u(t)}=${e?"true":"false"}`):a||m.push(`${u(t)}=`):n||m.push(`${u(t)}=`)};let h=Object.keys(t);c&&(h="function"==typeof c?h.sort(c):h.sort());for(const r of h)l&&!f(r)||$(r,t[r]);return m.join("&")}});export{o as queryString};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Freezable, LiteralObject } from "../types/built-in";
|
|
2
|
+
/** Anything JSON can hold as a leaf, plus the JS primitives that need special care. */
|
|
3
|
+
export type PrimitiveValue = string | number | boolean | null | undefined | symbol | bigint;
|
|
4
|
+
/** Options for {@link queryString.stringify}. */
|
|
5
|
+
export interface SerializeQueryOptions {
|
|
6
|
+
/** Array serialization format. Defaults to `"none"`. */
|
|
7
|
+
arrayFormat?: "bracket" | "index" | "comma" | "separator" | "none";
|
|
8
|
+
/** Separator character when `arrayFormat` is `"separator"`. Defaults to `","`. */
|
|
9
|
+
arrayFormatSeparator?: string;
|
|
10
|
+
/** Skip keys whose value is `null` or `undefined`. */
|
|
11
|
+
skipNull?: boolean;
|
|
12
|
+
/** Skip keys whose value is an empty string. */
|
|
13
|
+
skipEmptyString?: boolean;
|
|
14
|
+
/** Whether to URL-encode keys and values, or a custom encoder function. Defaults to `true`. */
|
|
15
|
+
encode?: boolean | ((value: string) => string);
|
|
16
|
+
/** Reject keys with non-standard characters when `true`. Defaults to `true`. */
|
|
17
|
+
strict?: boolean;
|
|
18
|
+
/** Sort keys alphabetically, or provide a custom comparator. */
|
|
19
|
+
sort?: boolean | ((a: string, b: string) => number);
|
|
20
|
+
}
|
|
21
|
+
export type SerializePrimitive = Freezable<{
|
|
22
|
+
isString(value: unknown): value is string;
|
|
23
|
+
isNumber(value: unknown): value is number;
|
|
24
|
+
isBoolean(value: unknown): value is boolean;
|
|
25
|
+
isPrimitive(value: unknown): value is PrimitiveValue;
|
|
26
|
+
isDate(value: unknown): value is Date;
|
|
27
|
+
isPlainObject(value: unknown): value is LiteralObject;
|
|
28
|
+
normalize<T, R = unknown>(data: T): R;
|
|
29
|
+
}>;
|
|
30
|
+
export type SerializeJSON = Freezable<{
|
|
31
|
+
stringify: (value: unknown, space?: number) => string;
|
|
32
|
+
parse: <T = unknown>(text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => T | null;
|
|
33
|
+
}>;
|
|
34
|
+
export type SerializeURL = Freezable<{
|
|
35
|
+
encode(value: string, component?: boolean | ((value: string) => string)): string;
|
|
36
|
+
decode(value: string, component?: boolean | ((value: string) => string)): string;
|
|
37
|
+
build(uri: string, params?: Record<string, PrimitiveValue> | null): string;
|
|
38
|
+
}>;
|
|
39
|
+
export type SerializeQueryString = Freezable<{
|
|
40
|
+
parse(query: string): Record<string, string>;
|
|
41
|
+
stringify(params: Record<string, unknown>, options?: SerializeQueryOptions): string;
|
|
42
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("../utilities/freeze.js").freeze({encode(e,r=!0){if(!e)return"";if("function"==typeof r)return r(e);try{return r?encodeURIComponent(e):encodeURI(e)}catch(t){const n=e.replace(/[\uD800-\uDFFF]/g,"");return r?encodeURIComponent(n):encodeURI(n)}},decode:(e,r=!0)=>{if(!e)return"";if("function"==typeof r)return r(e);const t=r?decodeURIComponent:decodeURI;return(r?e.replace(/\+/g,"%20"):e).replace(/(%[0-9A-F]{2})+/gi,e=>{try{return t(e)}catch(r){return e}})},build:(r,t)=>r?t&&"object"==typeof t?r.replace(/:([a-zA-Z\d_]+)/g,(r,n)=>{const o=t[n];return null==o?r:e.encode(String(o),!0)}):r:""});exports.Url=e;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{freeze as e}from"../utilities/freeze.mjs";const n=e({encode(e,n=!0){if(!e)return"";if("function"==typeof n)return n(e);try{return n?encodeURIComponent(e):encodeURI(e)}catch(t){const r=e.replace(/[\uD800-\uDFFF]/g,"");return n?encodeURIComponent(r):encodeURI(r)}},decode:(e,n=!0)=>{if(!e)return"";if("function"==typeof n)return n(e);const t=n?decodeURIComponent:decodeURI;return(n?e.replace(/\+/g,"%20"):e).replace(/(%[0-9A-F]{2})+/gi,e=>{try{return t(e)}catch(n){return e}})},build:(e,t)=>e?t&&"object"==typeof t?e.replace(/:([a-zA-Z\d_]+)/g,(e,r)=>{const o=t[r];return null==o?e:n.encode(String(o),!0)}):e:""});export{n as Url};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where every number comes from.
|
|
3
|
+
*
|
|
4
|
+
* All of it is Node stdlib — perf_hooks, v8, os, process — plus two things the
|
|
5
|
+
* host feeds in: `recordHttpRequest` for traffic, and the `db` callback given
|
|
6
|
+
* to `Syhemo.start()`. Nothing here knows what framework is running above it.
|
|
7
|
+
*
|
|
8
|
+
* Two rules hold throughout, both from the Prometheus model:
|
|
9
|
+
*
|
|
10
|
+
* - Values are in base units and are not rounded.
|
|
11
|
+
* - Anything typed `counter` only ever grows. Collection runs on Syhemo's own
|
|
12
|
+
* interval, so a scrape never mutates state and two scrapers never steal
|
|
13
|
+
* each other's numbers.
|
|
14
|
+
*/
|
|
15
|
+
import type { Metric, SyhemoLogSource } from "./types";
|
|
16
|
+
/**
|
|
17
|
+
* Records one finished request.
|
|
18
|
+
*
|
|
19
|
+
* @param latencyMs Duration in milliseconds — converted to seconds here, since
|
|
20
|
+
* callers time requests in ms and Prometheus wants seconds.
|
|
21
|
+
*/
|
|
22
|
+
export declare function recordHttpRequest(latencyMs: number): void;
|
|
23
|
+
/** Registers the connectivity probe `Syhemo.start({ db })` was given. */
|
|
24
|
+
export declare function setDbChecker(fn: (() => boolean) | null): void;
|
|
25
|
+
export declare function collectMetrics(source: SyhemoLogSource): Metric[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var node_perf_hooks=require("node:perf_hooks"),v8=require("v8"),os=require("os");function _interopNamespace(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach(function(o){if("default"!==o){var a=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,a.get?a:{enumerable:!0,get:function(){return e[o]}})}}),t.default=e,Object.freeze(t)}var v8__namespace=_interopNamespace(v8),os__namespace=_interopNamespace(os);const NS_PER_SECOND=1e9,MS_PER_SECOND=1e3;function collectMemory(){const e=process.memoryUsage();return[{name:"process_resident_memory_bytes",type:"gauge",help:"Resident set size in bytes.",value:e.rss},{name:"nodejs_external_memory_bytes",type:"gauge",help:"Memory used by C++ objects bound to JavaScript, in bytes.",value:e.external},{name:"nodejs_array_buffers_bytes",type:"gauge",help:"Memory allocated for ArrayBuffers and SharedArrayBuffers, in bytes.",value:e.arrayBuffers}]}let prevCpuUsage=null;function collectCpu(){var e,t;const o=os__namespace.cpus();let a=0;if(prevCpuUsage&&prevCpuUsage.length===o.length){let e=0,t=0;for(let a=0;a<o.length;a++){const l=prevCpuUsage[a].times,s=o[a].times,n=l.user+l.nice+l.sys+l.idle+l.irq;e+=s.user+s.nice+s.sys+s.idle+s.irq-n,t+=s.idle-l.idle}a=e>0?1-t/e:0}return prevCpuUsage=o,[{name:"process_cpu_usage_ratio",type:"gauge",help:"Share of CPU time spent non-idle since the previous collection, 0 to 1.",value:a},{name:"nodejs_cpu_count",type:"gauge",help:"Number of logical CPUs.",value:o.length},{name:"nodejs_cpu_info",type:"gauge",help:"CPU model, always 1.",labels:{model:null!==(t=null===(e=o[0])||void 0===e?void 0:e.model)&&void 0!==t?t:"unknown"},value:1}]}function collectHeap(){const e=v8__namespace.getHeapStatistics();return[{name:"nodejs_heap_size_total_bytes",type:"gauge",help:"Total V8 heap size in bytes.",value:e.total_heap_size},{name:"nodejs_heap_size_used_bytes",type:"gauge",help:"Used V8 heap size in bytes.",value:e.used_heap_size},{name:"nodejs_heap_size_limit_bytes",type:"gauge",help:"Ceiling V8 will grow the heap to, in bytes.",value:e.heap_size_limit},{name:"nodejs_malloced_memory_bytes",type:"gauge",help:"Memory malloc'd by V8, in bytes.",value:e.malloced_memory},{name:"nodejs_native_contexts",type:"gauge",help:"Live top-level contexts. A number that only grows indicates a leak.",value:e.number_of_native_contexts},{name:"nodejs_detached_contexts",type:"gauge",help:"Contexts detached but not yet garbage collected.",value:e.number_of_detached_contexts}]}function collectHandles(){var e,t,o,a,l,s;const n=null!==(t=null===(e=process._getActiveHandles)||void 0===e?void 0:e.call(process))&&void 0!==t?t:[],r=null!==(a=null===(o=process._getActiveRequests)||void 0===o?void 0:o.call(process))&&void 0!==a?a:[];let c=0,u=0;for(const e of n){const t=null!==(s=null===(l=null==e?void 0:e.constructor)||void 0===l?void 0:l.name)&&void 0!==s?s:"";"Timeout"===t||"Timer"===t||"Immediate"===t?c++:"Socket"!==t&&"TCP"!==t&&"TLSSocket"!==t||u++}return[{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"timer"},value:c},{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"socket"},value:u},{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"other"},value:n.length-c-u},{name:"nodejs_active_requests_total",type:"gauge",help:"Active libuv requests.",value:r.length}]}const TOP_MODULE_GROUPS=20;function collectModules(){var _a;try{const cache=eval("typeof require !== 'undefined' && require.cache")||{},keys=Object.keys(cache),groups=new Map;for(const e of keys){const t=e.match(/node_modules\/([^/]+)/),o=t?`node_modules/${t[1]}`:e.replace(process.cwd(),".");groups.set(o,(null!==(_a=groups.get(o))&&void 0!==_a?_a:0)+1)}const top=Array.from(groups.entries()).sort((e,t)=>t[1]-e[1]).slice(0,TOP_MODULE_GROUPS);return[{name:"nodejs_module_cache_entries",type:"gauge",help:"Entries in the CommonJS module cache.",value:keys.length},...top.map(([e,t])=>({name:"nodejs_module_cache_entries_by_group",type:"gauge",help:`Module cache entries per group, top ${TOP_MODULE_GROUPS}.`,labels:{group:e},value:t}))]}catch(e){return[{name:"nodejs_module_cache_entries",type:"gauge",help:"Entries in the CommonJS module cache.",value:0}]}}const histogram=node_perf_hooks.monitorEventLoopDelay({resolution:10});function collectEventLoop(){const e=0===histogram.count,t=t=>e||!Number.isFinite(t)?0:t/NS_PER_SECOND,o=[{name:"nodejs_eventloop_lag_mean_seconds",type:"gauge",help:"Mean event loop lag over the last interval.",value:t(histogram.mean)},{name:"nodejs_eventloop_lag_min_seconds",type:"gauge",help:"Minimum event loop lag over the last interval.",value:t(histogram.min)},{name:"nodejs_eventloop_lag_max_seconds",type:"gauge",help:"Maximum event loop lag over the last interval.",value:t(histogram.max)},{name:"nodejs_eventloop_lag_p99_seconds",type:"gauge",help:"99th percentile event loop lag over the last interval.",value:t(histogram.percentile(99))}];return histogram.reset(),o}histogram.enable();const LATENCY_BUCKETS=[.005,.01,.025,.05,.1,.25,.5,1,2.5,5,10];let httpTotal=0,httpDurationSum=0;const httpBuckets=new Array(LATENCY_BUCKETS.length+1).fill(0);function recordHttpRequest(e){const t=e/MS_PER_SECOND;httpTotal++,httpDurationSum+=t;for(let e=0;e<LATENCY_BUCKETS.length;e++)t<=LATENCY_BUCKETS[e]&&httpBuckets[e]++;httpBuckets[LATENCY_BUCKETS.length]++}function collectHttp(){const e=LATENCY_BUCKETS.map((e,t)=>({le:e,count:httpBuckets[t]}));return e.push({le:1/0,count:httpBuckets[LATENCY_BUCKETS.length]}),[{name:"http_requests_total",type:"counter",help:"Requests recorded since the process started.",value:httpTotal},{name:"http_request_duration_seconds",type:"histogram",help:"Request duration in seconds.",buckets:e,sum:httpDurationSum,count:httpTotal}]}let dbChecker=null;function setDbChecker(e){dbChecker=e}function collectDbPool(){return[{name:"db_pool_connected",type:"gauge",help:"1 when the database pool reports a connection, 0 otherwise.",value:dbChecker&&dbChecker()?1:0}]}function collectLogs(e){const t=e.counts(),o="Log entries written since the process started, by level.";return[{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"error"},value:t.errors},{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"warn"},value:t.warns},{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"other"},value:Math.max(0,t.total-t.errors-t.warns)}]}function collectSystem(){const[e,t,o]=os__namespace.loadavg(),a="System load average.";return[{name:"nodejs_version_info",type:"gauge",help:"Node.js version and platform, always 1.",labels:{version:process.version,platform:os__namespace.platform(),arch:os__namespace.arch()},value:1},{name:"system_memory_total_bytes",type:"gauge",help:"Total system memory in bytes.",value:os__namespace.totalmem()},{name:"system_memory_free_bytes",type:"gauge",help:"Free system memory in bytes.",value:os__namespace.freemem()},{name:"process_uptime_seconds",type:"gauge",help:"Seconds since the process started.",value:process.uptime()},{name:"system_load_average",type:"gauge",help:a,labels:{period:"1m"},value:e},{name:"system_load_average",type:"gauge",help:a,labels:{period:"5m"},value:t},{name:"system_load_average",type:"gauge",help:a,labels:{period:"15m"},value:o}]}function collectMetrics(e){return[...collectMemory(),...collectCpu(),...collectHeap(),...collectHandles(),...collectModules(),...collectEventLoop(),...collectHttp(),...collectDbPool(),...collectLogs(e),...collectSystem()]}exports.collectMetrics=collectMetrics,exports.recordHttpRequest=recordHttpRequest,exports.setDbChecker=setDbChecker;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{monitorEventLoopDelay}from"node:perf_hooks";import*as v8 from"v8";import*as os from"os";const NS_PER_SECOND=1e9,MS_PER_SECOND=1e3;function collectMemory(){const e=process.memoryUsage();return[{name:"process_resident_memory_bytes",type:"gauge",help:"Resident set size in bytes.",value:e.rss},{name:"nodejs_external_memory_bytes",type:"gauge",help:"Memory used by C++ objects bound to JavaScript, in bytes.",value:e.external},{name:"nodejs_array_buffers_bytes",type:"gauge",help:"Memory allocated for ArrayBuffers and SharedArrayBuffers, in bytes.",value:e.arrayBuffers}]}let prevCpuUsage=null;function collectCpu(){var e,t;const o=os.cpus();let l=0;if(prevCpuUsage&&prevCpuUsage.length===o.length){let e=0,t=0;for(let l=0;l<o.length;l++){const s=prevCpuUsage[l].times,a=o[l].times,n=s.user+s.nice+s.sys+s.idle+s.irq;e+=a.user+a.nice+a.sys+a.idle+a.irq-n,t+=a.idle-s.idle}l=e>0?1-t/e:0}return prevCpuUsage=o,[{name:"process_cpu_usage_ratio",type:"gauge",help:"Share of CPU time spent non-idle since the previous collection, 0 to 1.",value:l},{name:"nodejs_cpu_count",type:"gauge",help:"Number of logical CPUs.",value:o.length},{name:"nodejs_cpu_info",type:"gauge",help:"CPU model, always 1.",labels:{model:null!==(t=null===(e=o[0])||void 0===e?void 0:e.model)&&void 0!==t?t:"unknown"},value:1}]}function collectHeap(){const e=v8.getHeapStatistics();return[{name:"nodejs_heap_size_total_bytes",type:"gauge",help:"Total V8 heap size in bytes.",value:e.total_heap_size},{name:"nodejs_heap_size_used_bytes",type:"gauge",help:"Used V8 heap size in bytes.",value:e.used_heap_size},{name:"nodejs_heap_size_limit_bytes",type:"gauge",help:"Ceiling V8 will grow the heap to, in bytes.",value:e.heap_size_limit},{name:"nodejs_malloced_memory_bytes",type:"gauge",help:"Memory malloc'd by V8, in bytes.",value:e.malloced_memory},{name:"nodejs_native_contexts",type:"gauge",help:"Live top-level contexts. A number that only grows indicates a leak.",value:e.number_of_native_contexts},{name:"nodejs_detached_contexts",type:"gauge",help:"Contexts detached but not yet garbage collected.",value:e.number_of_detached_contexts}]}function collectHandles(){var e,t,o,l,s,a;const n=null!==(t=null===(e=process._getActiveHandles)||void 0===e?void 0:e.call(process))&&void 0!==t?t:[],r=null!==(l=null===(o=process._getActiveRequests)||void 0===o?void 0:o.call(process))&&void 0!==l?l:[];let c=0,u=0;for(const e of n){const t=null!==(a=null===(s=null==e?void 0:e.constructor)||void 0===s?void 0:s.name)&&void 0!==a?a:"";"Timeout"===t||"Timer"===t||"Immediate"===t?c++:"Socket"!==t&&"TCP"!==t&&"TLSSocket"!==t||u++}return[{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"timer"},value:c},{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"socket"},value:u},{name:"nodejs_active_handles",type:"gauge",help:"Active libuv handles by kind.",labels:{kind:"other"},value:n.length-c-u},{name:"nodejs_active_requests_total",type:"gauge",help:"Active libuv requests.",value:r.length}]}const TOP_MODULE_GROUPS=20;function collectModules(){var _a;try{const cache=eval("typeof require !== 'undefined' && require.cache")||{},keys=Object.keys(cache),groups=new Map;for(const e of keys){const t=e.match(/node_modules\/([^/]+)/),o=t?`node_modules/${t[1]}`:e.replace(process.cwd(),".");groups.set(o,(null!==(_a=groups.get(o))&&void 0!==_a?_a:0)+1)}const top=Array.from(groups.entries()).sort((e,t)=>t[1]-e[1]).slice(0,TOP_MODULE_GROUPS);return[{name:"nodejs_module_cache_entries",type:"gauge",help:"Entries in the CommonJS module cache.",value:keys.length},...top.map(([e,t])=>({name:"nodejs_module_cache_entries_by_group",type:"gauge",help:`Module cache entries per group, top ${TOP_MODULE_GROUPS}.`,labels:{group:e},value:t}))]}catch(e){return[{name:"nodejs_module_cache_entries",type:"gauge",help:"Entries in the CommonJS module cache.",value:0}]}}const histogram=monitorEventLoopDelay({resolution:10});function collectEventLoop(){const e=0===histogram.count,t=t=>e||!Number.isFinite(t)?0:t/NS_PER_SECOND,o=[{name:"nodejs_eventloop_lag_mean_seconds",type:"gauge",help:"Mean event loop lag over the last interval.",value:t(histogram.mean)},{name:"nodejs_eventloop_lag_min_seconds",type:"gauge",help:"Minimum event loop lag over the last interval.",value:t(histogram.min)},{name:"nodejs_eventloop_lag_max_seconds",type:"gauge",help:"Maximum event loop lag over the last interval.",value:t(histogram.max)},{name:"nodejs_eventloop_lag_p99_seconds",type:"gauge",help:"99th percentile event loop lag over the last interval.",value:t(histogram.percentile(99))}];return histogram.reset(),o}histogram.enable();const LATENCY_BUCKETS=[.005,.01,.025,.05,.1,.25,.5,1,2.5,5,10];let httpTotal=0,httpDurationSum=0;const httpBuckets=new Array(LATENCY_BUCKETS.length+1).fill(0);function recordHttpRequest(e){const t=e/MS_PER_SECOND;httpTotal++,httpDurationSum+=t;for(let e=0;e<LATENCY_BUCKETS.length;e++)t<=LATENCY_BUCKETS[e]&&httpBuckets[e]++;httpBuckets[LATENCY_BUCKETS.length]++}function collectHttp(){const e=LATENCY_BUCKETS.map((e,t)=>({le:e,count:httpBuckets[t]}));return e.push({le:1/0,count:httpBuckets[LATENCY_BUCKETS.length]}),[{name:"http_requests_total",type:"counter",help:"Requests recorded since the process started.",value:httpTotal},{name:"http_request_duration_seconds",type:"histogram",help:"Request duration in seconds.",buckets:e,sum:httpDurationSum,count:httpTotal}]}let dbChecker=null;function setDbChecker(e){dbChecker=e}function collectDbPool(){return[{name:"db_pool_connected",type:"gauge",help:"1 when the database pool reports a connection, 0 otherwise.",value:dbChecker&&dbChecker()?1:0}]}function collectLogs(e){const t=e.counts(),o="Log entries written since the process started, by level.";return[{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"error"},value:t.errors},{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"warn"},value:t.warns},{name:"nodejs_log_entries_total",type:"counter",help:o,labels:{level:"other"},value:Math.max(0,t.total-t.errors-t.warns)}]}function collectSystem(){const[e,t,o]=os.loadavg(),l="System load average.";return[{name:"nodejs_version_info",type:"gauge",help:"Node.js version and platform, always 1.",labels:{version:process.version,platform:os.platform(),arch:os.arch()},value:1},{name:"system_memory_total_bytes",type:"gauge",help:"Total system memory in bytes.",value:os.totalmem()},{name:"system_memory_free_bytes",type:"gauge",help:"Free system memory in bytes.",value:os.freemem()},{name:"process_uptime_seconds",type:"gauge",help:"Seconds since the process started.",value:process.uptime()},{name:"system_load_average",type:"gauge",help:l,labels:{period:"1m"},value:e},{name:"system_load_average",type:"gauge",help:l,labels:{period:"5m"},value:t},{name:"system_load_average",type:"gauge",help:l,labels:{period:"15m"},value:o}]}function collectMetrics(e){return[...collectMemory(),...collectCpu(),...collectHeap(),...collectHandles(),...collectModules(),...collectEventLoop(),...collectHttp(),...collectDbPool(),...collectLogs(e),...collectSystem()]}export{collectMetrics,recordHttpRequest,setDbChecker};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Metric } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Renders metrics as the Prometheus text exposition format.
|
|
4
|
+
*
|
|
5
|
+
* Serve the result verbatim with `CONTENT_TYPE`; nothing else is required to
|
|
6
|
+
* be scrapeable.
|
|
7
|
+
*/
|
|
8
|
+
/** What a `/metrics` response must declare. */
|
|
9
|
+
export declare const CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8";
|
|
10
|
+
/**
|
|
11
|
+
* `# HELP` and `# TYPE` are declared once per metric name, even when several
|
|
12
|
+
* series share it under different labels — repeating them makes the scrape
|
|
13
|
+
* fail with a duplicate-metadata error.
|
|
14
|
+
*/
|
|
15
|
+
export declare function render(metrics: Metric[]): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function e(e,n){const t=Object.assign(Object.assign({},e),n),r=Object.keys(t);if(0===r.length)return"";return`{${r.map(e=>{return`${e}="${n=t[e],n.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n")}"`;var n}).join(",")}}`}function n(e){return e===1/0?"+Inf":e===-1/0?"-Inf":Number.isNaN(e)?"NaN":String(e)}function t(t){return[`${t.name}${e(t.labels)} ${n(t.value)}`]}function r(t){const r=t.buckets.map(r=>`${t.name}_bucket${e(t.labels,{le:n(r.le)})} ${n(r.count)}`);return r.push(`${t.name}_sum${e(t.labels)} ${n(t.sum)}`),r.push(`${t.name}_count${e(t.labels)} ${n(t.count)}`),r}exports.CONTENT_TYPE="text/plain; version=0.0.4; charset=utf-8",exports.render=function(e){const n=[],s=new Set;for(const a of e)s.has(a.name)||(s.add(a.name),n.push(`# HELP ${a.name} ${a.help.replace(/\n/g," ")}`),n.push(`# TYPE ${a.name} ${a.type}`)),n.push(..."histogram"===a.type?r(a):t(a));return n.length?`${n.join("\n")}\n`:""};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const n="text/plain; version=0.0.4; charset=utf-8";function e(n,e){const t=Object.assign(Object.assign({},n),e),a=Object.keys(t);if(0===a.length)return"";return`{${a.map(n=>{return`${n}="${e=t[n],e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n")}"`;var e}).join(",")}}`}function t(n){return n===1/0?"+Inf":n===-1/0?"-Inf":Number.isNaN(n)?"NaN":String(n)}function a(n){return[`${n.name}${e(n.labels)} ${t(n.value)}`]}function s(n){const a=n.buckets.map(a=>`${n.name}_bucket${e(n.labels,{le:t(a.le)})} ${t(a.count)}`);return a.push(`${n.name}_sum${e(n.labels)} ${t(n.sum)}`),a.push(`${n.name}_count${e(n.labels)} ${t(n.count)}`),a}function u(n){const e=[],t=new Set;for(const u of n)t.has(u.name)||(t.add(u.name),e.push(`# HELP ${u.name} ${u.help.replace(/\n/g," ")}`),e.push(`# TYPE ${u.name} ${u.type}`)),e.push(..."histogram"===u.type?s(u):a(u));return e.length?`${e.join("\n")}\n`:""}export{n as CONTENT_TYPE,u as render};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syhemo: in-process runtime monitoring.
|
|
3
|
+
*
|
|
4
|
+
* Node-only, and not re-exported from the package index — reach it by
|
|
5
|
+
* subpath: `import { Syhemo } from "@ecosy/core/syhemo"`.
|
|
6
|
+
*
|
|
7
|
+
* Metrics follow the Prometheus model, so exposing them is a two-line route
|
|
8
|
+
* with no client library involved:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* export function GET() {
|
|
12
|
+
* return new Response(monitor.metrics(), {
|
|
13
|
+
* headers: { "Content-Type": CONTENT_TYPE },
|
|
14
|
+
* });
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Collection runs on this class's own timer, never on a scrape, which is what
|
|
19
|
+
* makes that route safe to call from more than one scraper: reading never
|
|
20
|
+
* mutates a counter.
|
|
21
|
+
*/
|
|
22
|
+
import { Subscriber } from "../subscriber";
|
|
23
|
+
import type { Metric, SyhemoDeps, SyhemoOptions, SyhemoState } from "./types";
|
|
24
|
+
export * from "./types";
|
|
25
|
+
export { recordHttpRequest } from "./collectors";
|
|
26
|
+
export { render, CONTENT_TYPE } from "./exposition";
|
|
27
|
+
declare const syhemoEvents: {
|
|
28
|
+
readonly metrics: {
|
|
29
|
+
readonly snapshot: "syhemo:metrics:snapshot";
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
type SyhemoEvents = typeof syhemoEvents;
|
|
33
|
+
export declare class Syhemo extends Subscriber<SyhemoState, SyhemoEvents> {
|
|
34
|
+
private timer;
|
|
35
|
+
private readonly logger;
|
|
36
|
+
private readonly source;
|
|
37
|
+
constructor(deps?: SyhemoDeps);
|
|
38
|
+
start(options?: SyhemoOptions): void;
|
|
39
|
+
stop(): void;
|
|
40
|
+
/** The most recent collection, or an empty list before the first one. */
|
|
41
|
+
getMetrics(): Metric[];
|
|
42
|
+
/**
|
|
43
|
+
* The most recent collection in Prometheus text exposition format.
|
|
44
|
+
*
|
|
45
|
+
* Serve it with `CONTENT_TYPE`. Returns an empty string before the first
|
|
46
|
+
* collection, which a scraper reads as "no metrics" rather than an error.
|
|
47
|
+
*/
|
|
48
|
+
metrics(): string;
|
|
49
|
+
private collect;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("../subscriber.js"),t=require("../utilities/freeze.js"),s=require("./logger.js"),r=require("./collectors.js"),i=require("./exposition.js");const o=t.freeze({metrics:{snapshot:"syhemo:metrics:snapshot"}});class l extends e.Subscriber{constructor(e={}){var t,r;super({current:null,snapshots:[],logs:[],started:!1},o),this.timer=null,this.logger=null!==(t=e.logger)&&void 0!==t?t:new s.BufferedLogger("Syhemo"),this.source=null!==(r=e.source)&&void 0!==r?r:s.BufferedLogger}start(e={}){if(this.getState().started)return;const{interval:t=5e3,db:s}=e;s&&r.setDbChecker(s),this.setState({started:!0}),this.collect(),this.timer=setInterval(()=>this.collect(),t),this.logger.log(`Started (interval: ${t}ms)`)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null),this.setState({started:!1}),this.logger.log("Stopped")}getMetrics(){var e,t;return null!==(t=null===(e=this.getState().current)||void 0===e?void 0:e.metrics)&&void 0!==t?t:[]}metrics(){return i.render(this.getMetrics())}collect(){try{const e={timestamp:Date.now(),metrics:r.collectMetrics(this.source)},t=this.getState().snapshots,s=t.length>=60?[...t.slice(1),e]:[...t,e];this.setState({current:e,snapshots:s,logs:this.source.getLogs()}),this.dispatch(o.metrics.snapshot,e)}catch(e){this.logger.error(`Collection failed: ${e}`)}}}exports.recordHttpRequest=r.recordHttpRequest,exports.CONTENT_TYPE=i.CONTENT_TYPE,exports.render=i.render,exports.Syhemo=l;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Subscriber as t}from"../subscriber.mjs";import{freeze as s}from"../utilities/freeze.mjs";import{BufferedLogger as e}from"./logger.mjs";import{collectMetrics as r,setDbChecker as o}from"./collectors.mjs";export{recordHttpRequest}from"./collectors.mjs";import{render as i}from"./exposition.mjs";export{CONTENT_TYPE}from"./exposition.mjs";const l=s({metrics:{snapshot:"syhemo:metrics:snapshot"}});class c extends t{constructor(t={}){var s,r;super({current:null,snapshots:[],logs:[],started:!1},l),this.timer=null,this.logger=null!==(s=t.logger)&&void 0!==s?s:new e("Syhemo"),this.source=null!==(r=t.source)&&void 0!==r?r:e}start(t={}){if(this.getState().started)return;const{interval:s=5e3,db:e}=t;e&&o(e),this.setState({started:!0}),this.collect(),this.timer=setInterval(()=>this.collect(),s),this.logger.log(`Started (interval: ${s}ms)`)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null),this.setState({started:!1}),this.logger.log("Stopped")}getMetrics(){var t,s;return null!==(s=null===(t=this.getState().current)||void 0===t?void 0:t.metrics)&&void 0!==s?s:[]}metrics(){return i(this.getMetrics())}collect(){try{const t={timestamp:Date.now(),metrics:r(this.source)},s=this.getState().snapshots,e=s.length>=60?[...s.slice(1),t]:[...s,t];this.setState({current:t,snapshots:e,logs:this.source.getLogs()}),this.dispatch(l.metrics.snapshot,t)}catch(t){this.logger.error(`Collection failed: ${t}`)}}}export{c as Syhemo,i as render};
|
|
@@ -12,24 +12,32 @@ export interface LogEntry {
|
|
|
12
12
|
message: string;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Syhemo's built-in logging stack: colour-coded output plus the shared buffer
|
|
16
|
+
* and counters the monitor reads.
|
|
16
17
|
*
|
|
17
|
-
* -
|
|
18
|
-
*
|
|
18
|
+
* This is NOT a general-purpose logger, and it is not re-exported from the
|
|
19
|
+
* package index — reach for `@ecosy/logger` for formatters, deliveries and
|
|
20
|
+
* telemetry standards. What lives here exists so that {@link Syhemo} works out
|
|
21
|
+
* of the box: it is one concrete implementation of the `SyhemoLogger` and
|
|
22
|
+
* `SyhemoLogSource` ports, and Syhemo accepts any other.
|
|
19
23
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
24
|
+
* An app that wants its own logs to appear in monitoring snapshots has to
|
|
25
|
+
* write them through something that fills the same buffer — either this class,
|
|
26
|
+
* or its own implementation of `SyhemoLogSource` handed to Syhemo.
|
|
27
|
+
*
|
|
28
|
+
* - **Server (Node.js)**: ANSI escape codes for terminal colour.
|
|
29
|
+
* - **Browser**: `%c` CSS formatting for DevTools.
|
|
22
30
|
*
|
|
23
31
|
* @example
|
|
24
32
|
* ```ts
|
|
25
|
-
* const logger = new
|
|
33
|
+
* const logger = new BufferedLogger("MyModule");
|
|
26
34
|
* logger.log("Server started on port 3000");
|
|
27
35
|
* logger.warn("Deprecated API used");
|
|
28
36
|
* logger.error("Connection failed", error);
|
|
29
37
|
* logger.debug("Payload:", data);
|
|
30
38
|
* ```
|
|
31
39
|
*/
|
|
32
|
-
export declare class
|
|
40
|
+
export declare class BufferedLogger {
|
|
33
41
|
private readonly context;
|
|
34
42
|
/**
|
|
35
43
|
* @param context - Label shown in log output, e.g. `[MyModule]`.
|
|
@@ -51,14 +59,18 @@ export declare class Logger {
|
|
|
51
59
|
verbose(message: string, ...args: unknown[]): void;
|
|
52
60
|
/**
|
|
53
61
|
* Returns a shallow copy of all buffered log entries (non-destructive).
|
|
54
|
-
*
|
|
62
|
+
* Satisfies the `getLogs` half of Syhemo's log source port.
|
|
55
63
|
*/
|
|
56
64
|
static getLogs(): LogEntry[];
|
|
57
65
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
66
|
+
* Running totals since the process started.
|
|
67
|
+
*
|
|
68
|
+
* Non-destructive on purpose: these become Prometheus counters, and a
|
|
69
|
+
* counter that resets when it is read cannot be shared by two consumers and
|
|
70
|
+
* cannot produce a correct rate. The scraper takes the difference between
|
|
71
|
+
* two reads; that is its job, not this one's.
|
|
60
72
|
*/
|
|
61
|
-
static
|
|
73
|
+
static counts(): {
|
|
62
74
|
errors: number;
|
|
63
75
|
warns: number;
|
|
64
76
|
total: number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const o=()=>"undefined"==typeof window,t="[0m",r="[1m",e="[33m",n="[37m",s={LOG:"color:#22c55e;font-weight:bold",WARN:"color:#eab308;font-weight:bold",ERROR:"color:#ef4444;font-weight:bold",DEBUG:"color:#a855f7;font-weight:bold",VERBOSE:"color:#06b6d4;font-weight:bold"},c="color:inherit;font-weight:normal",i={LOG:"color:#22c55e",WARN:"color:#eab308",ERROR:"color:#ef4444",DEBUG:"color:#a855f7",VERBOSE:"color:#06b6d4"};function a(){return(new Date).toLocaleString("en-US",{month:"2-digit",day:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!0})}const f={LOG:"[32m",WARN:e,ERROR:"[31m",DEBUG:"[35m",VERBOSE:"[36m"},l=Symbol.for("@ecosy:logger"),m=globalThis;m[l]||(m[l]={buffer:[],errors:0,warns:0,total:0});const R=m[l];exports.BufferedLogger=class{constructor(o="@ecosy"){this.context=o}format(t,r,...e){const n="ERROR"===t?console.error:"WARN"===t?console.warn:console.log;R.buffer.push({timestamp:Date.now(),level:t,context:this.context,message:r}),R.buffer.length>200&&R.buffer.shift(),R.total++,"ERROR"===t&&R.errors++,"WARN"===t&&R.warns++,o()?this.formatServer(n,t,r,...e):this.formatBrowser(n,t,r,...e)}formatServer(s,c,i,...l){const m=f[c],R=`${n}${a()}${t}`;s(`${`${m}${r}${c.padEnd(7)}${t}`} ${`${e}${o()&&"undefined"!=typeof process?process.pid:0}${t}`} - ${R} ${`${e}[${this.context}]${t}`} ${`${m}${i}${t}`}`,...l)}formatBrowser(o,t,r,...e){o(`%c${t.padEnd(7)}%c - %c${a()}%c %c[${this.context}]%c %c${r}`,s[t],c,"color:#9ca3af;font-weight:normal",c,"color:#eab308;font-weight:normal",c,i[t],...e)}log(o,...t){this.format("LOG",o,...t)}warn(o,...t){this.format("WARN",o,...t)}error(o,...t){this.format("ERROR",o,...t)}debug(o,...t){this.format("DEBUG",o,...t)}verbose(o,...t){this.format("VERBOSE",o,...t)}static getLogs(){return[...R.buffer]}static counts(){return{errors:R.errors,warns:R.warns,total:R.total}}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const o=()=>"undefined"==typeof window,t="[0m",r="[1m",e="[33m",n="[37m",
|
|
1
|
+
const o=()=>"undefined"==typeof window,t="[0m",r="[1m",e="[33m",n="[37m",c={LOG:"color:#22c55e;font-weight:bold",WARN:"color:#eab308;font-weight:bold",ERROR:"color:#ef4444;font-weight:bold",DEBUG:"color:#a855f7;font-weight:bold",VERBOSE:"color:#06b6d4;font-weight:bold"},s="color:inherit;font-weight:normal",i={LOG:"color:#22c55e",WARN:"color:#eab308",ERROR:"color:#ef4444",DEBUG:"color:#a855f7",VERBOSE:"color:#06b6d4"};function a(){return(new Date).toLocaleString("en-US",{month:"2-digit",day:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!0})}const f={LOG:"[32m",WARN:e,ERROR:"[31m",DEBUG:"[35m",VERBOSE:"[36m"},l=Symbol.for("@ecosy:logger"),m=globalThis;m[l]||(m[l]={buffer:[],errors:0,warns:0,total:0});const R=m[l];class h{constructor(o="@ecosy"){this.context=o}format(t,r,...e){const n="ERROR"===t?console.error:"WARN"===t?console.warn:console.log;R.buffer.push({timestamp:Date.now(),level:t,context:this.context,message:r}),R.buffer.length>200&&R.buffer.shift(),R.total++,"ERROR"===t&&R.errors++,"WARN"===t&&R.warns++,o()?this.formatServer(n,t,r,...e):this.formatBrowser(n,t,r,...e)}formatServer(c,s,i,...l){const m=f[s],R=`${n}${a()}${t}`;c(`${`${m}${r}${s.padEnd(7)}${t}`} ${`${e}${o()&&"undefined"!=typeof process?process.pid:0}${t}`} - ${R} ${`${e}[${this.context}]${t}`} ${`${m}${i}${t}`}`,...l)}formatBrowser(o,t,r,...e){o(`%c${t.padEnd(7)}%c - %c${a()}%c %c[${this.context}]%c %c${r}`,c[t],s,"color:#9ca3af;font-weight:normal",s,"color:#eab308;font-weight:normal",s,i[t],...e)}log(o,...t){this.format("LOG",o,...t)}warn(o,...t){this.format("WARN",o,...t)}error(o,...t){this.format("ERROR",o,...t)}debug(o,...t){this.format("DEBUG",o,...t)}verbose(o,...t){this.format("VERBOSE",o,...t)}static getLogs(){return[...R.buffer]}static counts(){return{errors:R.errors,warns:R.warns,total:R.total}}}export{h as BufferedLogger};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every shape Syhemo produces or consumes.
|
|
3
|
+
*
|
|
4
|
+
* The metric model below follows Prometheus, so a project that wants a
|
|
5
|
+
* `/metrics` endpoint only has to return what Syhemo already produces — no
|
|
6
|
+
* client library, no registry, no push gateway. That constrains three things
|
|
7
|
+
* which are easy to get wrong and expensive to fix later:
|
|
8
|
+
*
|
|
9
|
+
* - **Base units.** Bytes, seconds, and ratios between 0 and 1. Never MB, ms
|
|
10
|
+
* or percentages: a query that has to know which unit a metric used is a
|
|
11
|
+
* query that will eventually be wrong.
|
|
12
|
+
* - **Counters are monotonic.** `counter` *means* never-decreasing; a scraper
|
|
13
|
+
* computes rates from the difference between two scrapes. A value that
|
|
14
|
+
* resets itself is a gauge wearing a counter's name, and the dashboards it
|
|
15
|
+
* produces look plausible while being wrong.
|
|
16
|
+
* - **Full precision.** Values are not rounded here. Rounding is a display
|
|
17
|
+
* decision, and doing it at the source cannot be undone.
|
|
18
|
+
*/
|
|
19
|
+
import type { LogEntry, LogLevel } from "./logger";
|
|
20
|
+
export type { LogEntry, LogLevel };
|
|
21
|
+
/**
|
|
22
|
+
* `counter` only ever grows. `gauge` moves in both directions. `histogram`
|
|
23
|
+
* carries cumulative buckets plus a sum and a count.
|
|
24
|
+
*
|
|
25
|
+
* Summaries are deliberately absent: their quantiles cannot be aggregated
|
|
26
|
+
* across instances, which is the whole reason histograms exist.
|
|
27
|
+
*/
|
|
28
|
+
export type MetricType = "counter" | "gauge" | "histogram";
|
|
29
|
+
export type MetricLabels = Record<string, string>;
|
|
30
|
+
/** One cumulative bucket: how many observations were `<= le`. */
|
|
31
|
+
export interface HistogramBucket {
|
|
32
|
+
/** Upper bound, inclusive. `Infinity` renders as `+Inf`. */
|
|
33
|
+
le: number;
|
|
34
|
+
count: number;
|
|
35
|
+
}
|
|
36
|
+
interface MetricBase {
|
|
37
|
+
/** `<namespace>_<subsystem>_<name>_<unit>`, e.g. `process_resident_memory_bytes`. */
|
|
38
|
+
name: string;
|
|
39
|
+
help: string;
|
|
40
|
+
labels?: MetricLabels;
|
|
41
|
+
}
|
|
42
|
+
export interface ScalarMetric extends MetricBase {
|
|
43
|
+
type: "counter" | "gauge";
|
|
44
|
+
value: number;
|
|
45
|
+
}
|
|
46
|
+
export interface HistogramMetric extends MetricBase {
|
|
47
|
+
type: "histogram";
|
|
48
|
+
/** Cumulative: each bucket counts everything at or below its bound. */
|
|
49
|
+
buckets: HistogramBucket[];
|
|
50
|
+
sum: number;
|
|
51
|
+
count: number;
|
|
52
|
+
}
|
|
53
|
+
export type Metric = ScalarMetric | HistogramMetric;
|
|
54
|
+
/** One collection pass. */
|
|
55
|
+
export interface MetricsSnapshot {
|
|
56
|
+
/** Unix milliseconds. */
|
|
57
|
+
timestamp: number;
|
|
58
|
+
metrics: Metric[];
|
|
59
|
+
}
|
|
60
|
+
export interface SyhemoState {
|
|
61
|
+
current: MetricsSnapshot | null;
|
|
62
|
+
snapshots: MetricsSnapshot[];
|
|
63
|
+
logs: LogEntry[];
|
|
64
|
+
started: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** Monotonic log totals since the process started. */
|
|
67
|
+
export interface LogCounts {
|
|
68
|
+
errors: number;
|
|
69
|
+
warns: number;
|
|
70
|
+
total: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Where Syhemo writes its own diagnostics.
|
|
74
|
+
*
|
|
75
|
+
* Structurally an `ILogger`, so `@ecosy/logger` — or a bare `console` — is
|
|
76
|
+
* already one. Declared here rather than imported because it is the port the
|
|
77
|
+
* monitor needs, which is what keeps this package free of any dependency on a
|
|
78
|
+
* logging library.
|
|
79
|
+
*/
|
|
80
|
+
export interface SyhemoLogger {
|
|
81
|
+
log(message: string, ...args: unknown[]): void;
|
|
82
|
+
warn(message: string, ...args: unknown[]): void;
|
|
83
|
+
error(message: string, ...args: unknown[]): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Where Syhemo reads log activity from.
|
|
87
|
+
*
|
|
88
|
+
* A logging library does not normally provide this — writing logs and being
|
|
89
|
+
* able to count them are different jobs — which is why it is a separate port.
|
|
90
|
+
* `BufferedLogger` implements both; pairing `@ecosy/logger` with monitoring
|
|
91
|
+
* means giving it a delivery that fills something shaped like this.
|
|
92
|
+
*/
|
|
93
|
+
export interface SyhemoLogSource {
|
|
94
|
+
/** Recent entries. Non-destructive. */
|
|
95
|
+
getLogs(): LogEntry[];
|
|
96
|
+
/**
|
|
97
|
+
* Running totals since the process started.
|
|
98
|
+
*
|
|
99
|
+
* Must be monotonic and non-destructive: these become counters, and a
|
|
100
|
+
* counter that resets when it is read cannot be shared by two consumers and
|
|
101
|
+
* cannot produce a correct rate.
|
|
102
|
+
*/
|
|
103
|
+
counts(): LogCounts;
|
|
104
|
+
}
|
|
105
|
+
export interface SyhemoOptions {
|
|
106
|
+
interval?: number;
|
|
107
|
+
db?: () => boolean;
|
|
108
|
+
}
|
|
109
|
+
export interface SyhemoDeps {
|
|
110
|
+
/** Defaults to the built-in `BufferedLogger`. */
|
|
111
|
+
logger?: SyhemoLogger;
|
|
112
|
+
/** Defaults to the built-in buffer behind `BufferedLogger`. */
|
|
113
|
+
source?: SyhemoLogSource;
|
|
114
|
+
}
|
|
@@ -10,4 +10,6 @@ export { isLiteralObject, isComplexObject, isObject, isObjectable, hasOwnPropert
|
|
|
10
10
|
export { merge } from "./merge";
|
|
11
11
|
export { isFormData, objectToFormData } from "./formdata";
|
|
12
12
|
export { MIME_REGEX, sanitizeMime } from "./sanitize-mime";
|
|
13
|
+
export { searchify, type SearchPosition, type SearchResult } from "./searchify";
|
|
14
|
+
export { slugify, DEFAULT_TRANSFORMER, type SlugifyOptions } from "./slugify";
|
|
13
15
|
export { toString, ucfirst, pascalToKebab } from "./string";
|