chatwoot-router 0.3.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.
@@ -2,4 +2,4 @@ import { type ChatwootClient, type ChatwootMessage } from "../../../shared/chatw
2
2
  /** A Chatwoot creation receipt, not a channel delivery receipt. */
3
3
  export declare function confirmedReply(message: ChatwootMessage | undefined, botId: number | undefined): boolean;
4
4
  /** Read past turn boundaries, including replies made by the former relay's brand bot. */
5
- export declare function replyHistory(chatwoot: ChatwootClient, accountId: number, conversationId: number, botId: number | undefined, freshPages?: readonly ChatwootMessage[][]): Promise<"found" | "complete-none" | "unknown">;
5
+ export declare function replyHistory(chatwoot: ChatwootClient, accountId: number, conversationId: number, botId: number | undefined): Promise<"found" | "complete-none" | "unknown">;
@@ -1,11 +1,15 @@
1
1
  import { DurableObject } from "cloudflare:workers";
2
2
  import type { Env } from "./env.ts";
3
3
  import type { Transition } from "./webhook.ts";
4
- export declare function conversationName(accountId: number, conversationId: number): string;
4
+ export declare function sweeper(env: Env): DurableObjectStub<Router>;
5
+ export declare function conversationRouter(env: Env, accountId: number, conversationId: number): DurableObjectStub<Router>;
5
6
  export declare class Router extends DurableObject<Env> {
6
7
  private readonly store;
7
8
  constructor(context: DurableObjectState, env: Env);
8
9
  enqueueConversation(accountId: number, conversationId: number, transition?: Transition): Promise<void>;
10
+ requestSweep(): Promise<void>;
9
11
  alarm(): Promise<void>;
12
+ private sweep;
10
13
  private enqueue;
14
+ private schedule;
11
15
  }
@@ -9,58 +9,6 @@ export declare function requestHandoff(store: RoutingStore, accountId: number, c
9
9
  export declare function recordFailure(store: RoutingStore, accountId: number, conversationId: number): number;
10
10
  export declare function clearFailures(store: RoutingStore, accountId: number, conversationId: number): void;
11
11
  export declare function readTurn(chatwoot: ChatwootClient, store: RoutingStore, accountId: number, conversationId: number): Promise<{
12
- pages: {
13
- id: number;
14
- conversation_id?: number | undefined;
15
- content?: string | null | undefined;
16
- message_type: number;
17
- content_type?: string | null | undefined;
18
- status?: string | null | undefined;
19
- created_at?: number | null | undefined;
20
- private?: boolean | null | undefined;
21
- content_attributes?: {
22
- email?: {
23
- subject?: string | null | undefined;
24
- auto_reply?: boolean | null | undefined;
25
- text_content?: {
26
- quoted?: string | null | undefined;
27
- } | null | undefined;
28
- html_content?: {
29
- quoted?: string | null | undefined;
30
- } | null | undefined;
31
- } | null | undefined;
32
- deleted?: boolean | null | undefined;
33
- external_error?: string | null | undefined;
34
- activity?: {
35
- type?: string | null | undefined;
36
- status?: string | null | undefined;
37
- } | null | undefined;
38
- submitted_values?: unknown;
39
- submitted_email?: unknown;
40
- items?: unknown;
41
- } | null | undefined;
42
- sender?: {
43
- id?: number | null | undefined;
44
- name?: string | null | undefined;
45
- email?: string | null | undefined;
46
- type?: string | null | undefined;
47
- thumbnail?: string | null | undefined;
48
- avatar_url?: string | null | undefined;
49
- } | null | undefined;
50
- attachments?: {
51
- file_type?: string | null | undefined;
52
- data_url?: string | null | undefined;
53
- fallback_title?: string | null | undefined;
54
- coordinates_lat?: number | null | undefined;
55
- coordinates_long?: number | null | undefined;
56
- meta?: {
57
- firstName?: string | null | undefined;
58
- lastName?: string | null | undefined;
59
- first_name?: string | null | undefined;
60
- last_name?: string | null | undefined;
61
- } | null | undefined;
62
- }[] | null | undefined;
63
- }[][];
64
12
  messages: {
65
13
  id: number;
66
14
  conversation_id?: number | undefined;
@@ -2,25 +2,14 @@ import type { Fetch } from "./chatwoot/api.ts";
2
2
  export declare class BudgetExhaustedError extends Error {
3
3
  constructor();
4
4
  }
5
- export { JobDeadlineError } from "./deadline.ts";
6
- export declare const METADATA_TIMEOUT_MS = 1500;
7
- export declare const TRANSFER_TIMEOUT_MS = 8000;
8
- export declare const JOB_SLICE_MS = 10000;
9
5
  export declare class Budget {
10
6
  readonly limit: number;
11
7
  private readonly fetchImpl;
8
+ private readonly timeoutMs;
12
9
  private used;
13
- private slice;
14
- private sliceEnds;
15
- constructor(limit: number, fetchImpl?: Fetch);
10
+ constructor(limit: number, fetchImpl?: Fetch, timeoutMs?: number);
16
11
  get remaining(): number;
17
- startSlice(ms?: number): void;
18
- requireTime(ms: number): void;
19
- checkpoint(): void;
12
+ /** Counts a request that is not a fetch, such as a call to another Durable Object. */
20
13
  consume(): void;
21
- require(requests: number): void;
22
- controlSignal(ms: number): AbortSignal;
23
- fetchWith(timeoutMs: number): Fetch;
24
14
  readonly fetch: Fetch;
25
- private request;
26
15
  }
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
- import type { RateLimitStore } from "../rate-limit.ts";
3
2
  import type { RelayConversation, RelayMessage } from "../types.ts";
4
3
  import type { components, operations } from "./schema.ts";
5
4
  export type Fetch = (input: Request) => Promise<Response>;
@@ -7,18 +6,20 @@ export type Fetch = (input: Request) => Promise<Response>;
7
6
  export declare class ChatwootError extends Error {
8
7
  readonly status: number;
9
8
  readonly conversationMissing: boolean;
10
- readonly retryAfterMs: number | undefined;
11
- constructor(status: number, operation: string, conversationMissing?: boolean, retryAfterMs?: number);
9
+ constructor(status: number, operation: string, conversationMissing?: boolean);
12
10
  }
13
11
  /**
14
12
  * A conversation as returned by GET conversations/{id} and in the conversation list. The spec's
15
13
  * `status` enum omits `snoozed`, which the API does return, so the relay treats it as a string.
16
14
  */
17
- export type ChatwootConversation = components["schemas"]["conversation_show"] & {
15
+ export type ChatwootConversation = Omit<components["schemas"]["conversation_show"], "snoozed_until"> & {
18
16
  meta?: {
19
17
  assignee_type?: string | null;
20
18
  };
19
+ snoozed_until?: string | number | null;
21
20
  };
21
+ /** Unix seconds when a snoozed conversation reopens; null unless it is snoozed until a time. */
22
+ export declare function snoozedUntil(conversation: ChatwootConversation): number | null;
22
23
  declare const messageSchema: z.ZodObject<{
23
24
  id: z.ZodNumber;
24
25
  conversation_id: z.ZodOptional<z.ZodNumber>;
@@ -99,7 +100,7 @@ export type ChatwootClient = ReturnType<typeof chatwootClient>;
99
100
  type MessageQuery = NonNullable<operations["list-all-messages"]["parameters"]["query"]> & {
100
101
  filter_internal_messages?: true;
101
102
  };
102
- export declare function chatwootClient(baseUrl: string, token: string, fetch: Fetch, limits?: RateLimitStore): {
103
+ export declare function chatwootClient(baseUrl: string, token: string, fetch: Fetch): {
103
104
  /** The conversation, or undefined when it does not exist (it was deleted). */
104
105
  getConversation(accountId: number, conversationId: number): Promise<ChatwootConversation | undefined>;
105
106
  /** Messages with an id above `after`, oldest first; without `after`, the latest page. */
@@ -1,18 +1,16 @@
1
1
  export declare function retryDelay(attempts: number): number;
2
- export type Job = {
2
+ export interface Job {
3
3
  key: string;
4
4
  payload: string;
5
5
  version: number;
6
6
  attempts: number;
7
7
  /** When the job was first queued (ms since the epoch). */
8
8
  createdAt: number;
9
- };
9
+ }
10
10
  export declare class QueueStore {
11
11
  protected readonly sql: SqlStorage;
12
12
  protected readonly now: () => number;
13
- private readonly atomic;
14
- constructor(sql: SqlStorage, now?: () => number, atomic?: <T>(write: () => T) => T);
15
- transaction<T>(write: () => T): T;
13
+ constructor(sql: SqlStorage, now?: () => number);
16
14
  migrate(): void;
17
15
  get(key: string): string | undefined;
18
16
  set(key: string, value: string, ttlMs?: number): void;
@@ -23,8 +21,6 @@ export declare class QueueStore {
23
21
  */
24
22
  enqueue(key: string, priority: number, payload: string, notBefore?: number): void;
25
23
  nextDueJob(): Job | undefined;
26
- /** A bounded due batch; independent scan and delivery work can select their own keys. */
27
- nextDueJobs(limit: number, prefix?: string): Job[];
28
24
  /** Earliest due time. */
29
25
  nextWakeup(): number | undefined;
30
26
  /** Removes a finished job unless it was enqueued again while running. */
@@ -36,9 +32,10 @@ export declare class QueueStore {
36
32
  * budget, or Discord rate limits it.
37
33
  */
38
34
  deferJob(job: Job, delayMs?: number): void;
39
- /** One short re-read for a racing status webhook, then wait for an event or a sweep. */
40
- holdJob(job: Job): void;
41
- /** Suspended conversation jobs need reconciliation even outside the ordinary activity window. */
42
- wakeHeldJobs(): void;
35
+ /**
36
+ * Makes a job due again after `delayMs`, unless it was queued again while it ran (it is due
37
+ * then), clearing its failures.
38
+ */
39
+ postponeJob(job: Job, delayMs: number): void;
43
40
  prune(): void;
44
41
  }
@@ -51,6 +51,10 @@ export interface RelayConversation {
51
51
  };
52
52
  /** Person only; agent bots are represented as unassigned. */
53
53
  assignee?: RelayAssignee | null;
54
+ /** Unix seconds since the customer has waited for a reply (Chatwoot's `waiting_since`); null when they do not. */
55
+ waitingSince?: number | null;
56
+ /** Unix seconds when a snoozed conversation reopens; null when not snoozed or snoozed until the next reply. */
57
+ snoozedUntil?: number | null;
54
58
  assigneeType?: string | null;
55
59
  customAttributes: Record<string, unknown>;
56
60
  }
@@ -66,8 +70,8 @@ export interface RelayMessage {
66
70
  emailSubject?: string | null;
67
71
  /** An automatic email reply (out of office, for example): relayed without notifications. */
68
72
  autoReply?: boolean;
69
- /** A qualifying public reply follows this message in Chatwoot. */
70
- answered?: boolean;
73
+ /** A public reply that answers the customer (see isAnsweringReply). */
74
+ answers?: boolean;
71
75
  attachments: RelayAttachment[];
72
76
  /** A bot's options to pick, cards, or articles, with a link each when they have one. */
73
77
  items?: RelayItem[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatwoot-router",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Route Chatwoot conversations with TypeSafe Jev on Cloudflare Workers.",
5
5
  "keywords": [
6
6
  "chatwoot",
@@ -1,10 +0,0 @@
1
- import { DurableObject } from "cloudflare:workers";
2
- import type { Env } from "./env.ts";
3
- export declare const COORDINATOR_NAME = "global";
4
- /** Lists one page per alarm. Conversation state belongs exclusively to the conversation's Router. */
5
- export declare class Coordinator extends DurableObject<Env> {
6
- private readonly store;
7
- constructor(ctx: DurableObjectState, env: Env);
8
- requestSweep(): Promise<void>;
9
- alarm(): Promise<void>;
10
- }
@@ -1,2 +0,0 @@
1
- /** Set the one Durable Object alarm for the earliest durable wake-up. */
2
- export declare function scheduleAlarm(state: DurableObjectState, next: number | undefined): Promise<void>;
@@ -1,4 +0,0 @@
1
- /** Finish or cancel every independent read before the caller can yield to another job. */
2
- export declare function parallel<T extends readonly unknown[]>(...reads: {
3
- [K in keyof T]: Promise<T[K]>;
4
- }): Promise<T>;
@@ -1,6 +0,0 @@
1
- /** Bounds non-fetch dependencies too, including an RPC whose enqueue outcome can be unknown. */
2
- export declare function within<T>(pending: Promise<T>, signal: AbortSignal): Promise<T>;
3
- export declare class JobDeadlineError extends Error {
4
- readonly requestStarted: boolean;
5
- constructor(requestStarted?: boolean);
6
- }
@@ -1,5 +0,0 @@
1
- /** Cache-aside state shared by upstream clients' durable cooldown stores. */
2
- export interface RateLimitStore {
3
- get(key: string): string | undefined;
4
- set(key: string, value: string, ttlMs?: number): void;
5
- }