@chatally/core
Advanced tools
+224
| import type { Level, Logger } from "@chatally/logger"; | ||
| import { EventEmitter } from "node:events"; | ||
| import type { Message } from "./messages.d.ts"; | ||
| /** | ||
| * ChatAlly Application Class | ||
| * | ||
| * Dispatches incoming requests to registered middleware. | ||
| */ | ||
| export declare class Application<D extends Object> extends EventEmitter< | ||
| ApplicationEvents<D> | ||
| > { | ||
| /** | ||
| * Create a Chatally application that dispatches incoming chat requests from | ||
| * all registered servers to all registered middleware. | ||
| */ | ||
| constructor(options?: ApplicationOptions<D>); | ||
| /** | ||
| * Register a Middleware or a Server | ||
| * | ||
| * Middlewares are executed in order of registration, but can `await next()` | ||
| * to wait for the following middlewares to finish. | ||
| * | ||
| * It is preferrable to use a named function over an arrow function, because | ||
| * the name is used to identify child loggers. Optionally, you can provide a | ||
| * name for the middleware. | ||
| * | ||
| * @param m Middleware or server. | ||
| * @param name [Optional] Name for the registered module. | ||
| * @returns `this` | ||
| */ | ||
| use(m: Middleware<D> | Server, name?: string): this; | ||
| /** | ||
| * Start all registered servers in parallel. | ||
| * | ||
| * @returns never | ||
| */ | ||
| listen(): void; | ||
| /** | ||
| * Asynchronous Method to Trigger Request Handling | ||
| * | ||
| * The application will pass request and response as context to each | ||
| * middleware. The application will handle all uncaught errors within a | ||
| * callback. The callback will resolve after the last middleware finished, | ||
| * but a server could send responses earlier, by registering the | ||
| * `on("finish")` event on the response. | ||
| * | ||
| * @returns a bound dispatch method | ||
| */ | ||
| get dispatch(): Dispatch; | ||
| /** | ||
| * Get a child logger. | ||
| * | ||
| * @param name Name of the child logger | ||
| * @param level [Optional] Log level other than parent level | ||
| * @returns A child logger | ||
| */ | ||
| getLogger(name: string, level?: Level): Logger; | ||
| } | ||
| type ApplicationEvents<D> = { | ||
| error: [Error & Record<string, unknown>, Omit<Context<D>, "next">]; | ||
| }; | ||
| export interface ApplicationOptions<D> { | ||
| /** | ||
| * [Optional] Arbitrary data to put into the context for each request | ||
| * [`default=undefined`] | ||
| */ | ||
| data?: D; | ||
| /** | ||
| * [Optional] Custom logger or flag if you want to use a default logger | ||
| * [`default=new BaseLogger()`] | ||
| */ | ||
| log?: Logger | boolean; | ||
| /** | ||
| * [Optional] Flag to run application in development mode | ||
| * [`default=false`] | ||
| */ | ||
| dev?: boolean; | ||
| } | ||
| /** | ||
| * Sync or async middleware. | ||
| */ | ||
| export type Middleware<D> = | ||
| | ((params: Context<D>) => void | unknown) | ||
| | ((params: Context<D>) => Promise<void | unknown>); | ||
| export interface Context<D> { | ||
| /** Request that triggered the handling */ | ||
| readonly req: IRequest; | ||
| /** Response for the request */ | ||
| readonly res: IResponse; | ||
| /** Trigger dispatching to the next middleware. */ | ||
| readonly next: () => Promise<void>; | ||
| /** Context-specific logger for the middleware */ | ||
| readonly log: Logger; | ||
| /** Arbitrary data storage */ | ||
| readonly data: D & Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Dispatch function provided by ChatAlly application. | ||
| */ | ||
| export type Dispatch = | ||
| | ((req: IRequest, res: IResponse) => Promise<void>) | ||
| | ((req: IRequest, res: IResponse) => void); | ||
| /** | ||
| * Chat server | ||
| * | ||
| * A chat server receives incoming chat messages and dispatches them to the | ||
| * application. | ||
| */ | ||
| export interface Server { | ||
| /** The server's name */ | ||
| name: string; | ||
| /** Registers the application as dispatcher */ | ||
| set dispatch(d: Dispatch); | ||
| /** | ||
| * The logger to use during runtime. Set this explicitly to false or NoLogger, | ||
| * if the registered server shall not log, otherwise the application will | ||
| * register a child logger. | ||
| */ | ||
| log: Logger | boolean | undefined; | ||
| /** Starts the server */ | ||
| listen: () => void; | ||
| } | ||
| /** | ||
| * Chat request with incoming message | ||
| */ | ||
| export declare class Request implements IRequest { | ||
| /** | ||
| * Create a chat request for an incoming message. | ||
| * | ||
| * @param message Fully typed message or a string that can optionally contain | ||
| * a "sender:" before a colon | ||
| */ | ||
| constructor(message: string | IncomingMessage); | ||
| get message(): IncomingMessage; | ||
| get text(): string; | ||
| } | ||
| /** | ||
| * Chat request with incoming message. | ||
| */ | ||
| export interface IRequest { | ||
| /** Incoming message */ | ||
| readonly message: IncomingMessage; | ||
| /** Textual content of incoming message */ | ||
| readonly text: string; | ||
| } | ||
| /** | ||
| * Incoming message. | ||
| */ | ||
| export type IncomingMessage = Message & { | ||
| /** Arrival time of message */ | ||
| timestamp: number; | ||
| /** Id of message */ | ||
| id: string; | ||
| /** Id of sender */ | ||
| from: string; | ||
| /** [Optional] id of message that this message is a reply to */ | ||
| replyTo?: string; | ||
| }; | ||
| /** | ||
| * Chat response. | ||
| */ | ||
| export declare class Response | ||
| extends EventEmitter<ResponseEvents> | ||
| implements IResponse | ||
| { | ||
| /** | ||
| * Create a new chat response. | ||
| * | ||
| * @param onFinished | ||
| * [Optional] Handler to be called, when response `end()` is called. | ||
| */ | ||
| constructor(onFinished?: (r: Response) => void); | ||
| messages: OutgoingMessage[]; | ||
| isWritable: Readonly<boolean>; | ||
| text: readonly string[]; | ||
| write: (msg: string | OutgoingMessage) => void; | ||
| end: (msg?: string | OutgoingMessage | undefined) => void; | ||
| } | ||
| type ResponseEvents = { | ||
| finished: [Response]; | ||
| write: [OutgoingMessage]; | ||
| }; | ||
| /** | ||
| * Chat response. | ||
| */ | ||
| export interface IResponse { | ||
| /** Messages to send as response. */ | ||
| messages: OutgoingMessage[]; | ||
| /** True if no middleware called end. */ | ||
| isWritable: Readonly<boolean>; | ||
| /** Textual representation of all messages. */ | ||
| text: Readonly<string[]>; | ||
| /** Write a message. */ | ||
| write: (msg: string | OutgoingMessage) => void; | ||
| /** End the response, optionally with a message. */ | ||
| end: (msg?: string | OutgoingMessage) => void; | ||
| } | ||
| /** | ||
| * Incoming message | ||
| */ | ||
| export type OutgoingMessage = Message & { | ||
| /** [Optional] Id of message that this message is a reply to. */ | ||
| replyTo?: string; | ||
| }; |
| /** | ||
| * @param {any} object | ||
| * @returns {object is import("./index.d.ts").Server} | ||
| */ | ||
| export function isServer(object) { | ||
| if (!object) return false; | ||
| if (typeof object.listen !== "function") return false; | ||
| const dispatch = | ||
| Object.getOwnPropertyDescriptor(object, "dispatch") || | ||
| Object.getOwnPropertyDescriptor(Object.getPrototypeOf(object), "dispatch"); | ||
| if (!dispatch) return false; | ||
| if (!dispatch.set && !dispatch.writable) return false; | ||
| return true; | ||
| } |
| > @chatally/core@0.0.6 lint | ||
| > @chatally/core@0.0.7 lint | ||
| > eslint . | ||
@@ -9,6 +9,12 @@ | ||
| /home/runner/work/chatally/chatally/packages/core/lib/index.d.ts | ||
| 10:44 warning Don't use `Object` as a type. The `Object` type actually means "any non-nullish value", so it is marginally better than `unknown`. | ||
| - If you want a type meaning "any object", you probably want `object` instead. | ||
| - If you want a type meaning "any value", you probably want `unknown` instead. | ||
| - If you really want a type meaning "any non-nullish value", you probably want `NonNullable<unknown>` instead @typescript-eslint/ban-types | ||
| /home/runner/work/chatally/chatally/packages/core/lib/messages.d.ts | ||
| 46:20 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any | ||
| ✖ 1 problem (0 errors, 1 warning) | ||
| ✖ 2 problems (0 errors, 2 warnings) | ||
| > @chatally/core@0.0.6 test | ||
| > @chatally/core@0.0.7 test | ||
| > vitest run | ||
@@ -8,8 +8,8 @@ | ||
| [32m✓[39m lib/application.test.js [2m ([22m[2m7 tests[22m[2m)[22m[90m 11[2mms[22m[39m | ||
| [32m✓[39m lib/application.test.js [2m ([22m[2m7 tests[22m[2m)[22m[90m 24[2mms[22m[39m | ||
| [2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m | ||
| [2m Tests [22m [1m[32m7 passed[39m[22m[90m (7)[39m | ||
| [2m Start at [22m 14:32:46 | ||
| [2m Duration [22m 971ms[2m (transform 218ms, setup 0ms, collect 221ms, tests 11ms, environment 0ms, prepare 214ms)[22m | ||
| [2m Start at [22m 12:30:08 | ||
| [2m Duration [22m 871ms[2m (transform 166ms, setup 1ms, collect 139ms, tests 24ms, environment 0ms, prepare 199ms)[22m | ||
+8
-0
| # @chatally/core | ||
| ## 0.0.7 | ||
| ### Patch Changes | ||
| - 795d1ac: Improved types for all packages | ||
| - Updated dependencies [795d1ac] | ||
| - @chatally/logger@0.0.5 | ||
| ## 0.0.6 | ||
@@ -4,0 +12,0 @@ |
+12
-31
| import { BaseLogger, NoLogger } from "@chatally/logger"; | ||
| import { EventEmitter } from "node:events"; | ||
| import { isServer } from "./server.js"; | ||
| import { isServer } from "./is-server.js"; | ||
@@ -9,4 +9,4 @@ /** | ||
| * | ||
| * @template {Object} D | ||
| * @extends {EventEmitter<{error: [Error & Record<string, unknown>, Omit<import("./middleware.d.ts").Context<D>, "next">]}>} | ||
| * @template {object} D | ||
| * @type {import("./index.d.ts").Application<D>} | ||
| */ | ||
@@ -21,3 +21,3 @@ export class Application extends EventEmitter { | ||
| * Middlewares in order of registration | ||
| * @type {import("./middleware.d.ts").Middleware<D>[]} | ||
| * @type {import("./index.d.ts").Middleware<D>[]} | ||
| */ | ||
@@ -28,3 +28,3 @@ #middlewares = []; | ||
| * Servers | ||
| * @type {import("./server.js").Server[]} | ||
| * @type {import("./index.d.ts").Server[]} | ||
| */ | ||
@@ -46,15 +46,6 @@ #servers = []; | ||
| /** | ||
| * Create an application that dispatches incoming chat requests from all | ||
| * registered servers to all registered middleware. | ||
| * | ||
| * @param {Object} [options={}] | ||
| * @param {D} [options.data] | ||
| * [Optional] Arbitrary data to put into the context for each request | ||
| * [`default=undefined`] | ||
| * @param {import("@chatally/logger").Logger | boolean} [options.log] | ||
| * [Optional] Custom logger or flag if you want to use a default logger | ||
| * [`default=new BaseLogger()`] | ||
| * @param {boolean} [options.dev] | ||
| * [Optional] Flag to run application in development mode | ||
| * [`default=false`] | ||
| */ | ||
@@ -77,12 +68,3 @@ constructor(options = {}) { | ||
| /** | ||
| * Register a middleware function or a server | ||
| * | ||
| * Middlewares are executed in order of registration, but can `await next()` | ||
| * to wait for the following middlewares to finish. | ||
| * | ||
| * It is preferrable to use a named function over an arrow function, because | ||
| * the name is used to identify child loggers. Optionally, you can provide a | ||
| * name for the middleware. | ||
| * | ||
| * @param {import("./middleware.d.ts").Middleware<D> | import("./server.js").Server} m | ||
| * @param {import("./index.d.ts").Middleware<D> | import("./index.d.ts").Server} m | ||
| * @param {String} [name] | ||
@@ -131,3 +113,3 @@ */ | ||
| * | ||
| * @type {import("./server.js").Dispatch} | ||
| * @type {import("./index.d.ts").Dispatch} | ||
| */ | ||
@@ -152,4 +134,4 @@ get dispatch() { | ||
| * | ||
| * @param {import("./request.js").IRequest} req | ||
| * @param {import("./response.js").IResponse} res | ||
| * @param {import("./index.d.ts").IRequest} req | ||
| * @param {import("./index.d.ts").IResponse} res | ||
| * @param {D & Record<string, unknown>} data | ||
@@ -184,3 +166,3 @@ * @param {import("@chatally/logger").Logger} log | ||
| * @param {unknown} err | ||
| * @param {Omit<import("./middleware.d.ts").Context<D>, "next">} context | ||
| * @param {Omit<import("./index.d.ts").Context<D>, "next">} context | ||
| */ | ||
@@ -190,3 +172,2 @@ #handleError(err, context) { | ||
| if (err instanceof Error) { | ||
| // @ts-expect-error For better DX, we pass down an error that behaves like "any" | ||
| if (!this.emit("error", err, context)) { | ||
@@ -229,6 +210,6 @@ context.log.error(err); | ||
| dev || process.env.NODE_ENV === "development" ? "debug" : "info"; | ||
| return BaseLogger.create({ level, name: "@chatally/core" }); | ||
| return new BaseLogger({ level, name: "@chatally/core" }); | ||
| } else { | ||
| return log || NoLogger.create(); | ||
| return log || new NoLogger(); | ||
| } | ||
| } |
| import { BaseLogger } from "@chatally/logger"; | ||
| import { StringWritable, XError } from "@internal/utils"; | ||
| import { StringWritable, TestError } from "@internal/test-utils"; | ||
| import { Application } from "./application.js"; | ||
@@ -8,3 +8,3 @@ import { Request } from "./request.js"; | ||
| /** | ||
| * @type {import("./middleware.js").Middleware<{}>} | ||
| * @type {import("./index.d.ts").Middleware<{}>} | ||
| */ | ||
@@ -33,3 +33,3 @@ const echo = ({ req, res }) => { | ||
| function getLogger(options) { | ||
| return BaseLogger.create(options); | ||
| return new BaseLogger(options); | ||
| } | ||
@@ -120,3 +120,3 @@ | ||
| .use(function throws() { | ||
| throw new XError("Boom", { expose: true }); | ||
| throw new TestError("Boom", { expose: true }); | ||
| }) | ||
@@ -138,3 +138,3 @@ .on("error", (err, { res }) => { | ||
| log.out = out; | ||
| log.timestamps = false; | ||
| log.timestamp = false; | ||
@@ -151,3 +151,3 @@ new Application({ log }) | ||
| log.out = out; | ||
| log.timestamps = false; | ||
| log.timestamp = false; | ||
@@ -154,0 +154,0 @@ const app = new Application({ log }) // |
@@ -14,3 +14,3 @@ export type Message = | ||
| export type Action = { | ||
| type Action = { | ||
| id: string; | ||
@@ -17,0 +17,0 @@ title: string; |
+6
-33
@@ -5,37 +5,9 @@ import { nanoid } from "nanoid"; | ||
| /** | ||
| * Chat request with incoming message | ||
| * | ||
| * @typedef IRequest | ||
| * @property {Readonly<IncomingMessage>} message Incoming message | ||
| * @property {Readonly<string>} text Textual content of incoming message | ||
| * @type {import("./index.d.ts").Request} | ||
| */ | ||
| /** | ||
| * Incoming message | ||
| * | ||
| * @typedef {(Incoming & import("./messages.d.ts").Message)} IncomingMessage | ||
| * | ||
| * @typedef {object} Incoming | ||
| * @property {number} IncomingMessage.timestamp Arrival time of message | ||
| * @property {string} IncomingMessage.id Id of message | ||
| * @property {string} IncomingMessage.from Id of sender | ||
| * @property {string} [IncomingMessage.replyTo] Id of message that this message | ||
| * is a reply to | ||
| */ | ||
| /** | ||
| * Chat request with incoming message | ||
| * | ||
| * @class | ||
| * @implements {IRequest} | ||
| */ | ||
| export class Request { | ||
| /** @type {IncomingMessage} */ | ||
| /** @type {import("./index.d.ts").IncomingMessage} */ | ||
| #message; | ||
| /** | ||
| * | ||
| * @param {IncomingMessage | string} message Fully typed message or a string | ||
| * that can optionally contain a "sender:" before a colon | ||
| */ | ||
| /** @param {import("./index.d.ts").IncomingMessage | string} message */ | ||
| constructor(message) { | ||
@@ -48,3 +20,3 @@ if (typeof message === "string") { | ||
| } | ||
| message = { | ||
| this.#message = { | ||
| type: "text", | ||
@@ -56,4 +28,5 @@ text, | ||
| }; | ||
| } else { | ||
| this.#message = message; | ||
| } | ||
| this.#message = message; | ||
| } | ||
@@ -60,0 +33,0 @@ |
+5
-42
| import { EventEmitter } from "node:events"; | ||
| import { text } from "./text.js"; | ||
| /** | ||
| * Chat response | ||
| * @typedef IResponse | ||
| * @property {OutgoingMessage[]} messages Messages to send as response | ||
| * @property {Readonly<boolean>} isWritable True if no middleware called end | ||
| * @property {Readonly<string[]>} text Textual representation of all messages | ||
| * @property {(msg: string | OutgoingMessage) => void} write Write a message | ||
| * @property {(msg?: string | OutgoingMessage) => void} end | ||
| * End the response, optionally with a message | ||
| * @template {keyof Events} E | ||
| * @property {(event: E, listener: Events[E]) => IResponse} on | ||
| * | ||
| * @typedef {object} Events | ||
| * @property {[Response]} finished | ||
| * @property {[OutgoingMessage]} write | ||
| */ | ||
| /** | ||
| * Outgoing message | ||
| * @typedef {(Outgoing & import("./messages.d.ts").Message)} OutgoingMessage | ||
| * | ||
| * @typedef {object} Outgoing | ||
| * @property {string} [OutgoingMessage.replyTo] | ||
| * id of message that this message is a reply to | ||
| */ | ||
| /** | ||
| * Chat response | ||
| * | ||
| * @class | ||
| * @extends {EventEmitter<Events>} | ||
| * @implements {IResponse} | ||
| */ | ||
| /** @type {import("./index.d.ts").Response} */ | ||
| export class Response extends EventEmitter { | ||
| /** @type {OutgoingMessage[]} */ | ||
| /** @type {import("./index.d.ts").OutgoingMessage[]} */ | ||
| #messages = []; | ||
| #finished = false; | ||
| /** | ||
| * Create a new response | ||
| * | ||
| * @param {(() => void)} [onFinished] | ||
| * optional handler to be called, when response `end()` is called | ||
| */ | ||
| /** @param {((r: Response) => void)} [onFinished] */ | ||
| constructor(onFinished) { | ||
@@ -67,3 +30,3 @@ super(); | ||
| /** @param {string | OutgoingMessage} [msg] */ | ||
| /** @param {string | import("./index.d.ts").OutgoingMessage} [msg] */ | ||
| end(msg) { | ||
@@ -75,3 +38,3 @@ this.write(msg); | ||
| /** @param {string | OutgoingMessage} [msg] */ | ||
| /** @param {string | import("./index.d.ts").OutgoingMessage} [msg] */ | ||
| write(msg) { | ||
@@ -78,0 +41,0 @@ if (!msg) return; |
+1
-1
| { | ||
| "name": "@chatally/core", | ||
| "version": "0.0.6", | ||
| "version": "0.0.7", | ||
| "description": "ChatAlly Core Modules", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
| import type { Logger } from "@chatally/logger"; | ||
| import type { IRequest } from "./request.js"; | ||
| import type { IResponse } from "./response.js"; | ||
| /** | ||
| * Sync or async middleware. | ||
| */ | ||
| export type Middleware<D> = | ||
| | ((params: Context<D>) => void | unknown) | ||
| | ((params: Context<D>) => Promise<void | unknown>); | ||
| export interface Context<D> { | ||
| /** Request that triggered the handling */ | ||
| readonly req: IRequest; | ||
| /** Response for the request */ | ||
| readonly res: IResponse; | ||
| /** Trigger dispatching to the next middleware. */ | ||
| readonly next: () => Promise<void>; | ||
| /** Context-specific logger for the middleware */ | ||
| readonly log: Logger; | ||
| /** Arbitrary data storage */ | ||
| readonly data: D & Record<string, unknown>; | ||
| } |
| /** | ||
| * Dispatch function provided by ChatAlly application. | ||
| * @typedef {((req: import("./request.js").IRequest, res: import("./response.js").IResponse) => Promise<void>) | ((req: import("./request.js").IRequest, res: import("./response.js").IResponse) => void)} Dispatch | ||
| */ | ||
| /** | ||
| * Chat server | ||
| * | ||
| * A chat server receives incoming chat messages and dispatches them to the | ||
| * application. | ||
| * | ||
| * @typedef {object} Server | ||
| * @property {string} name The server's name | ||
| * @property {Dispatch} dispatch Registers the application as dispatcher | ||
| * @property {import("@chatally/logger").Logger | boolean | undefined} log | ||
| * The logger to use during runtime. Set this explicitly to false or | ||
| * NoLogger, if the registered server shall not log, otherwise the | ||
| * application will register a child logger | ||
| * @property {() => void} listen Starts the server | ||
| */ | ||
| /** | ||
| * @param {any} object | ||
| * @returns {object is Server} | ||
| */ | ||
| export function isServer(object) { | ||
| if (!object) return false; | ||
| if (typeof object.listen !== "function") return false; | ||
| const prop = | ||
| Object.getOwnPropertyDescriptor(object, "dispatch") || | ||
| Object.getOwnPropertyDescriptor(Object.getPrototypeOf(object), "dispatch"); | ||
| if (!prop) return false; | ||
| if (!prop.set && !prop.writable) return false; | ||
| return true; | ||
| } |
| export * from "./index.js"; | ||
| export type * from "./messages.d.ts"; | ||
| export type { Context, Middleware } from "./middleware.js"; | ||
| export type { IncomingMessage } from "./request.js"; | ||
| export type { OutgoingMessage } from "./response.js"; | ||
| export type { Dispatch, Server } from "./server.js"; |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
25839
8.37%820
10.66%15
-6.25%