chatwoot-router 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export declare function isFreshTimestamp(timestamp: string | undefined, nowSeconds: number): boolean;
2
+ /** Constant-time check (WebCrypto HMAC verify) of a Chatwoot webhook signature. */
3
+ export declare function verifyChatwootSignature(secret: string, timestamp: string, body: Uint8Array, signatureHeader: string | undefined): Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ export declare const jsonRecord: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<unknown, string>>, z.ZodRecord<z.ZodString, z.ZodString>>;
3
+ export declare class ConfigError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ export declare function describe(error: z.ZodError): string;
7
+ export declare const reconcileSchema: z.ZodPrefault<z.ZodObject<{
8
+ lookbackSeconds: z.ZodDefault<z.ZodNumber>;
9
+ maxCatchUpSeconds: z.ZodDefault<z.ZodNumber>;
10
+ }, z.core.$strict>>;
@@ -0,0 +1,4 @@
1
+ /** A JSON object (not an array). */
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ /** The parsed value, or undefined when `text` is missing or not JSON. */
4
+ export declare function parseJson(text: string | undefined): unknown;
@@ -0,0 +1,9 @@
1
+ type Fields = Record<string, string | number | boolean | null | undefined>;
2
+ export declare const log: {
3
+ info: (message: string, fields?: Fields) => void;
4
+ warn: (message: string, fields?: Fields) => void;
5
+ error: (message: string, fields?: Fields) => void;
6
+ };
7
+ /** The error's class and message, safe to log: errors in this service never carry bodies. */
8
+ export declare function errorFields(error: unknown): Fields;
9
+ export {};
@@ -0,0 +1,6 @@
1
+ export interface ConfigEnv {
2
+ CONFIG?: unknown;
3
+ CONFIG_KEY?: string;
4
+ CONFIG_STORE?: KVNamespace;
5
+ }
6
+ export declare function settingsLoader<Env extends ConfigEnv, Settings>(parse: (config: unknown, env: Env) => Settings): (env: Env) => Promise<Settings>;
@@ -0,0 +1,37 @@
1
+ export declare function retryDelay(attempts: number): number;
2
+ export interface Job {
3
+ key: string;
4
+ payload: string;
5
+ version: number;
6
+ attempts: number;
7
+ /** When the job was first queued (ms since the epoch). */
8
+ createdAt: number;
9
+ }
10
+ export declare class QueueStore {
11
+ protected readonly sql: SqlStorage;
12
+ protected readonly now: () => number;
13
+ constructor(sql: SqlStorage, now?: () => number);
14
+ migrate(): void;
15
+ get(key: string): string | undefined;
16
+ set(key: string, value: string, ttlMs?: number): void;
17
+ delete(key: string): void;
18
+ list(start: string, end: string): string[];
19
+ /**
20
+ * Adds a job, or marks an existing one as needing another run (its version changes). A job
21
+ * that is backing off after failures keeps its schedule.
22
+ */
23
+ enqueue(key: string, priority: number, payload: string, notBefore?: number): void;
24
+ nextDueJob(): Job | undefined;
25
+ /** Earliest due time. */
26
+ nextWakeup(): number | undefined;
27
+ /** Removes a finished job unless it was enqueued again while running. */
28
+ completeJob(job: Job): void;
29
+ deleteJob(key: string): void;
30
+ retryJob(job: Job, delayMs: number): void;
31
+ /**
32
+ * Makes a job due after `delayMs` without counting an attempt: when an invocation runs out of
33
+ * budget, or Discord rate limits it.
34
+ */
35
+ deferJob(job: Job, delayMs?: number): void;
36
+ prune(): void;
37
+ }
@@ -0,0 +1,6 @@
1
+ import type { z } from "zod";
2
+ /** The configuration in `file` (JSON with comments), validated; throws when it is invalid. */
3
+ export declare function readStoredConfig(file: string | URL, configSchema: z.ZodType): {
4
+ key: string;
5
+ value: string;
6
+ };
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ import type { reconcileSchema } from "./config.ts";
3
+ import type { QueueStore } from "./store.ts";
4
+ declare const passSchema: z.ZodObject<{
5
+ cutoff: z.ZodNumber;
6
+ page: z.ZodNumber;
7
+ startedAt: z.ZodNumber;
8
+ }, z.core.$strip>;
9
+ export declare function readSweepPass(store: QueueStore, accountId: number, reconcile: z.infer<typeof reconcileSchema>): z.infer<typeof passSchema>;
10
+ export declare function saveSweepPass(store: QueueStore, accountId: number, pass: z.infer<typeof passSchema>, nextPage?: number): void;
11
+ export {};
@@ -0,0 +1,86 @@
1
+ export type MessageType = "incoming" | "outgoing" | "activity" | "template";
2
+ export interface RelayAssignee {
3
+ id?: number | undefined;
4
+ name?: string | null | undefined;
5
+ }
6
+ /** A Chatwoot agent linked to a Discord user (`agents[]`). */
7
+ export interface LinkedAgent {
8
+ discordUserId: string;
9
+ }
10
+ /** A file (its URL, and what it is when Chatwoot says), or a shared contact or location, which has no file. */
11
+ export type RelayAttachment = {
12
+ type: "file";
13
+ url: string;
14
+ label?: string;
15
+ } | {
16
+ type: "contact";
17
+ name: string;
18
+ phone: string;
19
+ } | {
20
+ type: "location";
21
+ title: string;
22
+ latitude: number;
23
+ longitude: number;
24
+ url: string;
25
+ };
26
+ export interface RelayItem {
27
+ title: string;
28
+ description: string;
29
+ url: string;
30
+ /** A card's link buttons. */
31
+ links: Array<{
32
+ text: string;
33
+ url: string;
34
+ }>;
35
+ }
36
+ export interface RelayConversation {
37
+ /** The conversation's display id (the number shown in Chatwoot). */
38
+ id: number;
39
+ /** open, pending, snoozed, or resolved. */
40
+ status?: string | undefined;
41
+ channel?: string | null;
42
+ /** urgent, high, medium, low, or null. */
43
+ priority?: string | null;
44
+ labels: string[];
45
+ contact: {
46
+ name?: string | null;
47
+ email?: string | null;
48
+ phone?: string | null;
49
+ blocked?: boolean;
50
+ avatarUrl?: string | null;
51
+ };
52
+ assignee?: RelayAssignee | null;
53
+ customAttributes: Record<string, unknown>;
54
+ }
55
+ export interface RelayMessage {
56
+ id: number;
57
+ /** Unix seconds. */
58
+ createdAt?: number | null;
59
+ messageType: MessageType;
60
+ private: boolean;
61
+ /** Deleted in Chatwoot (its content is replaced by a placeholder). */
62
+ deleted?: boolean;
63
+ content: string;
64
+ emailSubject?: string | null;
65
+ /** An automatic email reply (out of office, for example): relayed without notifications. */
66
+ autoReply?: boolean;
67
+ attachments: RelayAttachment[];
68
+ /** A bot's options to pick, cards, or articles, with a link each when they have one. */
69
+ items?: RelayItem[];
70
+ /** Chatwoot user id -> Discord user id of the linked agents a private note mentions. */
71
+ mentionedAgents?: ReadonlyMap<number, string>;
72
+ /** The Discord avatar of the linked agent who sent the message, if any. */
73
+ discordAvatarUrl?: string;
74
+ sender?: {
75
+ name?: string | null | undefined;
76
+ email?: string | null | undefined;
77
+ type?: string | null | undefined;
78
+ avatarUrl?: string | null | undefined;
79
+ } | undefined;
80
+ account: {
81
+ id: number;
82
+ name: string;
83
+ };
84
+ inboxName?: string | null;
85
+ conversation: RelayConversation;
86
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "chatwoot-router",
3
+ "version": "0.1.0",
4
+ "description": "Route Chatwoot conversations with TypeSafe Jev on Cloudflare Workers.",
5
+ "keywords": [
6
+ "chatwoot",
7
+ "cloudflare-workers",
8
+ "durable-objects",
9
+ "triage"
10
+ ],
11
+ "homepage": "https://github.com/Phala-Network/chatwoot-workers/tree/main/packages/chatwoot-router#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Phala-Network/chatwoot-workers/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/Phala-Network/chatwoot-workers.git",
18
+ "directory": "packages/chatwoot-router"
19
+ },
20
+ "license": "MIT",
21
+ "type": "module",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./lib/packages/chatwoot-router/src/index.d.ts",
25
+ "default": "./lib/packages/chatwoot-router/src/index.js"
26
+ },
27
+ "./stored-config": {
28
+ "types": "./lib/packages/chatwoot-router/scripts/stored-config.d.ts",
29
+ "default": "./lib/packages/chatwoot-router/scripts/stored-config.js"
30
+ }
31
+ },
32
+ "bin": {
33
+ "chatwoot-router-store-config": "lib/packages/chatwoot-router/scripts/store-config.js"
34
+ },
35
+ "files": [
36
+ "lib/packages/chatwoot-router/src",
37
+ "lib/packages/chatwoot-router/scripts",
38
+ "lib/shared",
39
+ "CHANGELOG.md",
40
+ "LICENSE"
41
+ ],
42
+ "packageManager": "npm@12.1.0",
43
+ "engines": {
44
+ "node": ">=24.15.0"
45
+ },
46
+ "scripts": {
47
+ "dev": "cf dev",
48
+ "lint": "biome check .",
49
+ "format": "biome check --write .",
50
+ "types": "cf workers types",
51
+ "typecheck": "cf workers types && tsc --noEmit && tsc --noEmit -p tsconfig.node.json",
52
+ "test": "vitest run",
53
+ "build": "cf build",
54
+ "deploy": "cf deploy",
55
+ "gen:chatwoot": "npx --yes --package=openapi-typescript@7.13.0 --package=typescript@5.9.3 -- openapi-typescript https://raw.githubusercontent.com/chatwoot/chatwoot/v4.18.0/swagger/swagger.json --empty-objects-unknown -o ../../shared/chatwoot/schema.d.ts",
56
+ "store-config": "node scripts/store-config.ts",
57
+ "build:package": "node ../../scripts/build-package.ts",
58
+ "prepack": "npm run build:package"
59
+ },
60
+ "devDependencies": {
61
+ "@cloudflare/vite-plugin": "2.0.0-beta.sha-805ec1ff3"
62
+ },
63
+ "dependencies": {
64
+ "hono": "^4.13.10",
65
+ "ip-regex": "^5.0.0",
66
+ "jsonc-parser": "^3.3.1",
67
+ "openapi-fetch": "^0.17.0",
68
+ "zod": "^4.6.5"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ }
73
+ }