@openclaw/crabline 0.1.9 → 0.1.11

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.
Files changed (189) hide show
  1. package/README.md +47 -22
  2. package/assets/crabline-banner.svg +20 -0
  3. package/dist/src/bin/crabline.js.map +1 -1
  4. package/dist/src/cli/program.d.ts +25 -1
  5. package/dist/src/cli/program.js +579 -82
  6. package/dist/src/cli/program.js.map +1 -1
  7. package/dist/src/config/load.js +59 -6
  8. package/dist/src/config/load.js.map +1 -1
  9. package/dist/src/config/schema.d.ts +116 -92
  10. package/dist/src/config/schema.js +179 -73
  11. package/dist/src/config/schema.js.map +1 -1
  12. package/dist/src/core/errors.js +9 -2
  13. package/dist/src/core/errors.js.map +1 -1
  14. package/dist/src/core/exit-codes.js.map +1 -1
  15. package/dist/src/core/matcher.js +7 -6
  16. package/dist/src/core/matcher.js.map +1 -1
  17. package/dist/src/core/message-template.js.map +1 -1
  18. package/dist/src/core/nonces.d.ts +1 -0
  19. package/dist/src/core/nonces.js +4 -2
  20. package/dist/src/core/nonces.js.map +1 -1
  21. package/dist/src/core/reporters.d.ts +1 -0
  22. package/dist/src/core/reporters.js +42 -4
  23. package/dist/src/core/reporters.js.map +1 -1
  24. package/dist/src/core/run.d.ts +3 -0
  25. package/dist/src/core/run.js +415 -107
  26. package/dist/src/core/run.js.map +1 -1
  27. package/dist/src/core/safe-regex.d.ts +3 -0
  28. package/dist/src/core/safe-regex.js +21 -0
  29. package/dist/src/core/safe-regex.js.map +1 -0
  30. package/dist/src/index.d.ts +2 -2
  31. package/dist/src/index.js +1 -1
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/openclaw/artifact-generation.d.ts +43 -0
  34. package/dist/src/openclaw/artifact-generation.js +363 -0
  35. package/dist/src/openclaw/artifact-generation.js.map +1 -0
  36. package/dist/src/openclaw/bridges/matrix.js +97 -12
  37. package/dist/src/openclaw/bridges/matrix.js.map +1 -1
  38. package/dist/src/openclaw/bridges/mattermost.js +32 -10
  39. package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
  40. package/dist/src/openclaw/bridges/signal.js +55 -18
  41. package/dist/src/openclaw/bridges/signal.js.map +1 -1
  42. package/dist/src/openclaw/bridges/slack.js +85 -7
  43. package/dist/src/openclaw/bridges/slack.js.map +1 -1
  44. package/dist/src/openclaw/bridges/telegram.js +124 -30
  45. package/dist/src/openclaw/bridges/telegram.js.map +1 -1
  46. package/dist/src/openclaw/bridges/whatsapp.js +78 -17
  47. package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
  48. package/dist/src/openclaw/bridges/zalo.js +14 -5
  49. package/dist/src/openclaw/bridges/zalo.js.map +1 -1
  50. package/dist/src/openclaw/outbound-contract.d.ts +5 -0
  51. package/dist/src/openclaw/outbound-contract.js +40 -0
  52. package/dist/src/openclaw/outbound-contract.js.map +1 -0
  53. package/dist/src/openclaw/private-file.d.ts +28 -0
  54. package/dist/src/openclaw/private-file.js +465 -0
  55. package/dist/src/openclaw/private-file.js.map +1 -0
  56. package/dist/src/openclaw/shared.d.ts +21 -2
  57. package/dist/src/openclaw/shared.js +115 -18
  58. package/dist/src/openclaw/shared.js.map +1 -1
  59. package/dist/src/openclaw/smoke-lock.d.ts +63 -0
  60. package/dist/src/openclaw/smoke-lock.js +1004 -0
  61. package/dist/src/openclaw/smoke-lock.js.map +1 -0
  62. package/dist/src/openclaw.d.ts +16 -8
  63. package/dist/src/openclaw.js +334 -52
  64. package/dist/src/openclaw.js.map +1 -1
  65. package/dist/src/providers/builtin/discord.d.ts +10 -1
  66. package/dist/src/providers/builtin/discord.js +144 -18
  67. package/dist/src/providers/builtin/discord.js.map +1 -1
  68. package/dist/src/providers/builtin/external-webhook-auth.d.ts +11 -0
  69. package/dist/src/providers/builtin/external-webhook-auth.js +10 -0
  70. package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -0
  71. package/dist/src/providers/builtin/feishu.d.ts +19 -2
  72. package/dist/src/providers/builtin/feishu.js +307 -25
  73. package/dist/src/providers/builtin/feishu.js.map +1 -1
  74. package/dist/src/providers/builtin/googlechat.d.ts +20 -1
  75. package/dist/src/providers/builtin/googlechat.js +178 -19
  76. package/dist/src/providers/builtin/googlechat.js.map +1 -1
  77. package/dist/src/providers/builtin/imessage.d.ts +4 -0
  78. package/dist/src/providers/builtin/imessage.js +26 -11
  79. package/dist/src/providers/builtin/imessage.js.map +1 -1
  80. package/dist/src/providers/builtin/loopback.js +92 -30
  81. package/dist/src/providers/builtin/loopback.js.map +1 -1
  82. package/dist/src/providers/builtin/matrix.d.ts +11 -1
  83. package/dist/src/providers/builtin/matrix.js +53 -29
  84. package/dist/src/providers/builtin/matrix.js.map +1 -1
  85. package/dist/src/providers/builtin/mattermost.d.ts +10 -0
  86. package/dist/src/providers/builtin/mattermost.js +38 -15
  87. package/dist/src/providers/builtin/mattermost.js.map +1 -1
  88. package/dist/src/providers/builtin/msteams.d.ts +17 -1
  89. package/dist/src/providers/builtin/msteams.js +113 -14
  90. package/dist/src/providers/builtin/msteams.js.map +1 -1
  91. package/dist/src/providers/builtin/native-local-mock.d.ts +2 -9
  92. package/dist/src/providers/builtin/native-local-mock.js +12 -35
  93. package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
  94. package/dist/src/providers/builtin/script.d.ts +11 -15
  95. package/dist/src/providers/builtin/script.js +566 -104
  96. package/dist/src/providers/builtin/script.js.map +1 -1
  97. package/dist/src/providers/builtin/slack.d.ts +6 -0
  98. package/dist/src/providers/builtin/slack.js +99 -40
  99. package/dist/src/providers/builtin/slack.js.map +1 -1
  100. package/dist/src/providers/builtin/telegram.d.ts +6 -0
  101. package/dist/src/providers/builtin/telegram.js +64 -43
  102. package/dist/src/providers/builtin/telegram.js.map +1 -1
  103. package/dist/src/providers/builtin/whatsapp.d.ts +28 -8
  104. package/dist/src/providers/builtin/whatsapp.js +567 -30
  105. package/dist/src/providers/builtin/whatsapp.js.map +1 -1
  106. package/dist/src/providers/builtin/zalo.js +30 -19
  107. package/dist/src/providers/builtin/zalo.js.map +1 -1
  108. package/dist/src/providers/catalog.d.ts +3 -3
  109. package/dist/src/providers/catalog.js +3 -3
  110. package/dist/src/providers/catalog.js.map +1 -1
  111. package/dist/src/providers/local-mock.d.ts +11 -2
  112. package/dist/src/providers/local-mock.js +322 -96
  113. package/dist/src/providers/local-mock.js.map +1 -1
  114. package/dist/src/providers/native-ids.d.ts +1 -0
  115. package/dist/src/providers/native-ids.js +6 -1
  116. package/dist/src/providers/native-ids.js.map +1 -1
  117. package/dist/src/providers/recorder.d.ts +30 -2
  118. package/dist/src/providers/recorder.js +549 -70
  119. package/dist/src/providers/recorder.js.map +1 -1
  120. package/dist/src/providers/registry.d.ts +25 -1
  121. package/dist/src/providers/registry.js +280 -40
  122. package/dist/src/providers/registry.js.map +1 -1
  123. package/dist/src/providers/signed-jwt.d.ts +29 -0
  124. package/dist/src/providers/signed-jwt.js +202 -0
  125. package/dist/src/providers/signed-jwt.js.map +1 -0
  126. package/dist/src/providers/slack-ids.js.map +1 -1
  127. package/dist/src/providers/target-normalizers.d.ts +26 -0
  128. package/dist/src/providers/target-normalizers.js +276 -0
  129. package/dist/src/providers/target-normalizers.js.map +1 -0
  130. package/dist/src/providers/types.d.ts +5 -0
  131. package/dist/src/providers/types.js.map +1 -1
  132. package/dist/src/providers/webhook-server.d.ts +2 -0
  133. package/dist/src/providers/webhook-server.js +72 -25
  134. package/dist/src/providers/webhook-server.js.map +1 -1
  135. package/dist/src/servers/http.d.ts +23 -3
  136. package/dist/src/servers/http.js +213 -20
  137. package/dist/src/servers/http.js.map +1 -1
  138. package/dist/src/servers/index.js +20 -21
  139. package/dist/src/servers/index.js.map +1 -1
  140. package/dist/src/servers/matrix.js +465 -51
  141. package/dist/src/servers/matrix.js.map +1 -1
  142. package/dist/src/servers/mattermost.d.ts +5 -0
  143. package/dist/src/servers/mattermost.js +306 -58
  144. package/dist/src/servers/mattermost.js.map +1 -1
  145. package/dist/src/servers/pending-events.d.ts +2 -0
  146. package/dist/src/servers/pending-events.js +11 -0
  147. package/dist/src/servers/pending-events.js.map +1 -0
  148. package/dist/src/servers/recorder.d.ts +5 -0
  149. package/dist/src/servers/recorder.js +262 -3
  150. package/dist/src/servers/recorder.js.map +1 -1
  151. package/dist/src/servers/signal.d.ts +2 -0
  152. package/dist/src/servers/signal.js +444 -56
  153. package/dist/src/servers/signal.js.map +1 -1
  154. package/dist/src/servers/slack.d.ts +5 -0
  155. package/dist/src/servers/slack.js +515 -49
  156. package/dist/src/servers/slack.js.map +1 -1
  157. package/dist/src/servers/telegram.d.ts +111 -0
  158. package/dist/src/servers/telegram.js +830 -158
  159. package/dist/src/servers/telegram.js.map +1 -1
  160. package/dist/src/servers/webhook-target.d.ts +40 -0
  161. package/dist/src/servers/webhook-target.js +349 -0
  162. package/dist/src/servers/webhook-target.js.map +1 -0
  163. package/dist/src/servers/websocket.d.ts +2 -0
  164. package/dist/src/servers/websocket.js +29 -0
  165. package/dist/src/servers/websocket.js.map +1 -0
  166. package/dist/src/servers/whatsapp-baileys-websocket.d.ts +65 -1
  167. package/dist/src/servers/whatsapp-baileys-websocket.js +723 -99
  168. package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
  169. package/dist/src/servers/whatsapp-jid.d.ts +5 -0
  170. package/dist/src/servers/whatsapp-jid.js +32 -0
  171. package/dist/src/servers/whatsapp-jid.js.map +1 -0
  172. package/dist/src/servers/whatsapp-wire/binary-node.d.ts +5 -0
  173. package/dist/src/servers/whatsapp-wire/binary-node.js +87 -20
  174. package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
  175. package/dist/src/servers/whatsapp-wire/crypto.d.ts +6 -1
  176. package/dist/src/servers/whatsapp-wire/crypto.js +99 -45
  177. package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
  178. package/dist/src/servers/whatsapp-wire/handshake.js +39 -4
  179. package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
  180. package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
  181. package/dist/src/servers/whatsapp.d.ts +12 -7
  182. package/dist/src/servers/whatsapp.js +269 -107
  183. package/dist/src/servers/whatsapp.js.map +1 -1
  184. package/dist/src/servers/zalo.d.ts +14 -0
  185. package/dist/src/servers/zalo.js +521 -88
  186. package/dist/src/servers/zalo.js.map +1 -1
  187. package/docs/channel-setup.md +197 -19
  188. package/fixtures/examples/crabline.example.yaml +10 -0
  189. package/package.json +20 -15
@@ -1,38 +1,52 @@
1
1
  import path from "node:path";
2
2
  import { CrablineError, ensureErrorMessage } from "../core/errors.js";
3
- import { appendRecordedInbound, waitForRecordedInbound, watchRecordedInbound } from "./recorder.js";
3
+ import { appendRecordedInbound, cloneRecordedInboundCursor, createRecordedInboundCursor, waitForRecordedInbound, watchRecordedInbound, } from "./recorder.js";
4
4
  import { startWebhookServer } from "./webhook-server.js";
5
- export function createGenericLocalMockTargetCodec(platform) {
6
- const prefix = `${platform}:`;
7
- const encode = (value) => (value.startsWith(prefix) ? value : `${prefix}${value}`);
8
- return {
9
- normalize(target) {
10
- const normalized = {
11
- id: target.id,
12
- metadata: target.metadata,
13
- };
14
- if (target.channelId) {
15
- normalized.channelId = encode(target.channelId);
16
- }
17
- else if (!target.threadId) {
18
- normalized.channelId = encode(target.id);
19
- }
20
- if (target.threadId) {
21
- normalized.channelId ??= encode(target.id);
22
- normalized.threadId = target.threadId.startsWith(prefix)
23
- ? target.threadId
24
- : `${normalized.channelId}:${target.threadId}`;
25
- }
26
- return normalized;
27
- },
28
- resolveThreadId(target) {
29
- const normalized = this.normalize(target);
30
- return normalized.threadId ?? normalized.channelId ?? encode(normalized.id);
31
- },
32
- };
5
+ export { createGenericLocalMockTargetCodec } from "./target-normalizers.js";
6
+ const MAX_WAIT_CURSORS = 64;
7
+ function cursorHasAdvanced(candidate, current) {
8
+ if (!current) {
9
+ return true;
10
+ }
11
+ if (candidate.readState.generation !== current.readState.generation) {
12
+ return candidate.readState.generation > current.readState.generation;
13
+ }
14
+ if (candidate.readState.offset !== current.readState.offset) {
15
+ return candidate.readState.offset > current.readState.offset;
16
+ }
17
+ return candidate.buffered.length < current.buffered.length;
33
18
  }
34
- function sleep(ms) {
35
- return new Promise((resolve) => setTimeout(resolve, ms));
19
+ function pruneInactiveWaitCursors(cursors) {
20
+ for (const [key, state] of cursors) {
21
+ if (cursors.size <= MAX_WAIT_CURSORS) {
22
+ return;
23
+ }
24
+ if (state.active === 0) {
25
+ cursors.delete(key);
26
+ }
27
+ }
28
+ }
29
+ function sleep(ms, signal) {
30
+ return new Promise((resolve, reject) => {
31
+ let timer;
32
+ const cleanup = () => signal?.removeEventListener("abort", abort);
33
+ const abort = () => {
34
+ if (timer) {
35
+ clearTimeout(timer);
36
+ }
37
+ cleanup();
38
+ reject(signal?.reason ?? new Error("Provider operation aborted."));
39
+ };
40
+ timer = setTimeout(() => {
41
+ cleanup();
42
+ resolve();
43
+ }, ms);
44
+ if (signal?.aborted) {
45
+ abort();
46
+ return;
47
+ }
48
+ signal?.addEventListener("abort", abort, { once: true });
49
+ });
36
50
  }
37
51
  function isAddressInChannel(threadId, channelId) {
38
52
  if (!channelId) {
@@ -56,9 +70,44 @@ function authorFromPayload(payload) {
56
70
  return (payload.message?.authorIsBot ?? payload.authorIsBot ?? true) ? "assistant" : "user";
57
71
  }
58
72
  function normalizeWebhookPayload(payload) {
59
- if (!payload || typeof payload !== "object") {
73
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
60
74
  throw new CrablineError("mock webhook payload must be an object", { kind: "inbound" });
61
75
  }
76
+ const record = payload;
77
+ const messageValue = record.message;
78
+ if (messageValue !== undefined &&
79
+ (!messageValue || typeof messageValue !== "object" || Array.isArray(messageValue))) {
80
+ throw new CrablineError("mock webhook payload message must be an object", {
81
+ kind: "inbound",
82
+ });
83
+ }
84
+ for (const [label, envelope] of [
85
+ ["payload", record],
86
+ ["payload.message", messageValue],
87
+ ]) {
88
+ if (!envelope) {
89
+ continue;
90
+ }
91
+ if (envelope.author !== undefined &&
92
+ envelope.author !== "assistant" &&
93
+ envelope.author !== "system" &&
94
+ envelope.author !== "user") {
95
+ throw new CrablineError(`${label}.author must be assistant, system, or user`, {
96
+ kind: "inbound",
97
+ });
98
+ }
99
+ if (envelope.authorIsBot !== undefined && typeof envelope.authorIsBot !== "boolean") {
100
+ throw new CrablineError(`${label}.authorIsBot must be a boolean`, { kind: "inbound" });
101
+ }
102
+ for (const field of ["id", "text", "threadId"]) {
103
+ const value = envelope[field];
104
+ if (value !== undefined && (typeof value !== "string" || value.length === 0)) {
105
+ throw new CrablineError(`${label}.${field} must be a non-empty string`, {
106
+ kind: "inbound",
107
+ });
108
+ }
109
+ }
110
+ }
62
111
  return payload;
63
112
  }
64
113
  function mockReplyText(params) {
@@ -68,33 +117,45 @@ export class LocalMockProviderAdapter {
68
117
  id;
69
118
  platform;
70
119
  status = "ready";
71
- supports = ["probe", "send", "roundtrip", "agent"];
120
+ supports;
72
121
  #codec;
73
122
  #config;
74
123
  #options;
124
+ #publicUrl;
75
125
  #recorderPath;
126
+ #activeSends = new Set();
127
+ #cleanupController = new AbortController();
128
+ #waitCursors = new Map();
129
+ #cleanupBegun = false;
130
+ #cleanupPromise = null;
76
131
  #server = null;
132
+ #serverStarting = null;
77
133
  constructor(params) {
78
134
  this.id = params.id;
79
135
  this.platform = params.options.platform;
136
+ this.supports = [...params.config.capabilities];
80
137
  this.#codec = params.codec;
81
138
  this.#config = params.config;
82
139
  this.#options = params.options;
140
+ this.#publicUrl = params.options.publicUrl ?? params.options.webhook?.publicUrl;
83
141
  this.#recorderPath = toRecorderPath(params.id, params.options.recorderPath);
84
142
  }
85
143
  normalizeTarget(target) {
144
+ this.#assertActive();
86
145
  return this.#codec.normalize(target);
87
146
  }
88
147
  async probe(context) {
89
- const server = await this.#ensureWebhookServer(true);
148
+ context.signal?.throwIfAborted();
149
+ const server = await this.#ensureWebhookServer();
150
+ context.signal?.throwIfAborted();
90
151
  const target = this.normalizeTarget(context.fixture.target);
91
152
  const details = [
92
153
  `${this.platform} local mock ready`,
93
154
  `recorder path ${this.#recorderPath}`,
94
155
  `${this.#options.endpointLabel} ${server.endpointUrl}`,
95
156
  ];
96
- if (this.#options.publicUrl) {
97
- details.push(`public webhook ${this.#options.publicUrl}`);
157
+ if (this.#publicUrl) {
158
+ details.push(`public webhook ${this.#publicUrl}`);
98
159
  }
99
160
  if (target.threadId) {
100
161
  details.push(`thread reachable ${target.threadId}`);
@@ -107,9 +168,23 @@ export class LocalMockProviderAdapter {
107
168
  }
108
169
  return { details, healthy: true };
109
170
  }
110
- async send(context) {
171
+ send(context) {
172
+ try {
173
+ this.#assertActive();
174
+ }
175
+ catch (error) {
176
+ return Promise.reject(error);
177
+ }
178
+ const sending = this.#send(context);
179
+ this.#activeSends.add(sending);
180
+ void sending.then(() => this.#activeSends.delete(sending), () => this.#activeSends.delete(sending));
181
+ return sending;
182
+ }
183
+ async #send(context) {
184
+ const { signal } = context;
111
185
  const threadId = this.#codec.resolveThreadId(context.fixture.target);
112
186
  const messageId = createMessageId(this.platform);
187
+ signal?.throwIfAborted();
113
188
  await appendRecordedInbound(this.#recorderPath, {
114
189
  author: "user",
115
190
  id: messageId,
@@ -119,12 +194,14 @@ export class LocalMockProviderAdapter {
119
194
  mode: context.mode,
120
195
  platform: this.platform,
121
196
  },
197
+ recordedDirection: "outbound",
122
198
  sentAt: new Date().toISOString(),
123
199
  text: context.text,
124
200
  threadId,
125
201
  });
126
202
  if (context.mode !== "send" && context.fixture.target.behavior !== "sink") {
127
- await sleep(this.#config.loopback?.delayMs ?? 25);
203
+ await sleep(this.#config.loopback?.delayMs ?? 25, signal);
204
+ signal?.throwIfAborted();
128
205
  await appendRecordedInbound(this.#recorderPath, {
129
206
  author: "assistant",
130
207
  id: `${messageId}-reply`,
@@ -134,6 +211,7 @@ export class LocalMockProviderAdapter {
134
211
  mode: context.mode,
135
212
  platform: this.platform,
136
213
  },
214
+ recordedDirection: "inbound",
137
215
  sentAt: new Date().toISOString(),
138
216
  text: mockReplyText({ platform: this.platform, text: context.text }),
139
217
  threadId,
@@ -146,98 +224,246 @@ export class LocalMockProviderAdapter {
146
224
  };
147
225
  }
148
226
  async waitForInbound(context) {
149
- await this.#ensureWebhookServer(true);
227
+ this.#assertActive();
228
+ const signal = this.#signalFor(context.signal);
229
+ await this.#ensureWebhookServer();
150
230
  const target = this.normalizeTarget(context.fixture.target);
151
231
  const expectedAuthor = context.fixture.inboundMatch.author;
152
- return await waitForRecordedInbound({
153
- filePath: this.#recorderPath,
154
- matches: (event) => event.provider === this.id &&
155
- (expectedAuthor === "any" || event.author === expectedAuthor) &&
156
- isAddressInChannel(event.threadId, context.threadId ?? target.threadId ?? target.channelId),
157
- since: context.since,
158
- timeoutMs: context.timeoutMs,
159
- });
232
+ const channelId = context.threadId ?? target.threadId ?? target.channelId;
233
+ const excludedIds = new Set(context.excludeIds ?? []);
234
+ const cursorKey = JSON.stringify([context.nonce, context.since, channelId, expectedAuthor]);
235
+ const cursorState = this.#waitCursors.get(cursorKey) ?? { active: 0 };
236
+ const cursor = cursorState.cursor
237
+ ? cloneRecordedInboundCursor(cursorState.cursor)
238
+ : createRecordedInboundCursor();
239
+ cursorState.active++;
240
+ this.#waitCursors.delete(cursorKey);
241
+ this.#waitCursors.set(cursorKey, cursorState);
242
+ pruneInactiveWaitCursors(this.#waitCursors);
243
+ try {
244
+ const event = await waitForRecordedInbound({
245
+ cursor,
246
+ filePath: this.#recorderPath,
247
+ matches: (candidate) => candidate.provider === this.id &&
248
+ !excludedIds.has(candidate.id) &&
249
+ (expectedAuthor === "any" || candidate.author === expectedAuthor) &&
250
+ (this.#options.matchesThread ?? isAddressInChannel)(candidate.threadId, channelId, target, candidate.raw),
251
+ recordedDirection: "inbound",
252
+ since: context.since,
253
+ signal,
254
+ timeoutMs: context.timeoutMs,
255
+ });
256
+ if (this.#waitCursors.get(cursorKey) === cursorState &&
257
+ cursorHasAdvanced(cursor, cursorState.cursor)) {
258
+ cursorState.cursor = cursor;
259
+ }
260
+ return event;
261
+ }
262
+ finally {
263
+ cursorState.active--;
264
+ if (cursorState.active === 0 &&
265
+ !cursorState.cursor &&
266
+ this.#waitCursors.get(cursorKey) === cursorState) {
267
+ this.#waitCursors.delete(cursorKey);
268
+ }
269
+ }
160
270
  }
161
271
  async *watch(context) {
162
- await this.#ensureWebhookServer(false);
272
+ this.#assertActive();
273
+ const signal = this.#signalFor(context.signal);
274
+ await this.#ensureWebhookServer();
163
275
  const target = this.normalizeTarget(context.fixture.target);
164
276
  for await (const event of watchRecordedInbound({
165
277
  filePath: this.#recorderPath,
166
278
  matches: (entry) => entry.provider === this.id &&
167
- isAddressInChannel(entry.threadId, target.threadId ?? target.channelId),
279
+ (this.#options.matchesThread ?? isAddressInChannel)(entry.threadId, target.threadId ?? target.channelId, target, entry.raw),
280
+ recordedDirection: "inbound",
281
+ signal,
168
282
  since: context.since,
169
283
  })) {
170
284
  yield event;
171
285
  }
172
286
  }
173
- async cleanup() {
174
- if (!this.#server) {
287
+ #beginCleanup() {
288
+ if (this.#cleanupBegun) {
175
289
  return;
176
290
  }
177
- await this.#server.close();
178
- this.#server = null;
291
+ this.#cleanupBegun = true;
292
+ this.#cleanupController.abort(this.#cleanedUpError());
293
+ this.#waitCursors.clear();
294
+ }
295
+ async cleanup() {
296
+ this.#beginCleanup();
297
+ this.#cleanupPromise ??= (async () => {
298
+ const sends = [...this.#activeSends];
299
+ const [serverResult] = await Promise.allSettled([this.#closeWebhookServer(), ...sends]);
300
+ if (serverResult?.status === "rejected") {
301
+ throw serverResult.reason;
302
+ }
303
+ })();
304
+ await this.#cleanupPromise;
179
305
  }
180
306
  async #handleWebhook(request) {
181
- if (!request.headers.get("content-type")?.includes("application/json")) {
182
- return new Response("expected application/json", { status: 415 });
307
+ if (this.#cleanupBegun) {
308
+ return new Response("provider is shutting down", { status: 503 });
309
+ }
310
+ const rawBody = await request.text();
311
+ const authenticationFailure = await this.#options.authenticateWebhookRequest?.(request, rawBody);
312
+ if (authenticationFailure) {
313
+ return authenticationFailure;
314
+ }
315
+ const mediaType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
316
+ let rawPayload;
317
+ if (mediaType === "application/json") {
318
+ try {
319
+ rawPayload = JSON.parse(rawBody);
320
+ }
321
+ catch {
322
+ this.#options.settleWebhookRequest?.({
323
+ accepted: false,
324
+ payload: undefined,
325
+ rawBody,
326
+ });
327
+ return new Response("invalid JSON", { status: 400 });
328
+ }
329
+ }
330
+ else {
331
+ rawPayload = rawBody;
183
332
  }
184
- let payload;
333
+ let settled = false;
334
+ const settle = (accepted) => {
335
+ if (settled) {
336
+ return;
337
+ }
338
+ settled = true;
339
+ this.#options.settleWebhookRequest?.({ accepted, payload: rawPayload, rawBody });
340
+ };
341
+ const respond = (response) => {
342
+ settle(response.ok);
343
+ return response;
344
+ };
185
345
  try {
186
- const rawPayload = await request.json();
187
- payload = normalizeWebhookPayload(this.#options.normalizeWebhookPayload
188
- ? this.#options.normalizeWebhookPayload(rawPayload)
189
- : rawPayload);
346
+ const directResponse = await this.#options.handleWebhookPayload?.(rawPayload, request, rawBody);
347
+ if (directResponse) {
348
+ return respond(directResponse);
349
+ }
350
+ if (mediaType !== "application/json") {
351
+ return respond(new Response("expected application/json", { status: 415 }));
352
+ }
353
+ let payload;
354
+ try {
355
+ payload = normalizeWebhookPayload(this.#options.normalizeWebhookPayload
356
+ ? this.#options.normalizeWebhookPayload(rawPayload)
357
+ : rawPayload);
358
+ }
359
+ catch (error) {
360
+ if (error instanceof CrablineError && error.kind === "inbound") {
361
+ return respond(new Response(ensureErrorMessage(error), { status: 400 }));
362
+ }
363
+ throw error;
364
+ }
365
+ const id = payload.message?.id ?? payload.id ?? createMessageId(this.platform);
366
+ const threadId = payload.message?.threadId ?? payload.threadId;
367
+ const text = payload.message?.text ?? payload.text;
368
+ if (!threadId || !text) {
369
+ return respond(new Response("payload requires message.threadId and message.text", { status: 400 }));
370
+ }
371
+ if (this.#cleanupBegun) {
372
+ return respond(new Response("provider is shutting down", { status: 503 }));
373
+ }
374
+ await appendRecordedInbound(this.#recorderPath, {
375
+ author: authorFromPayload(payload),
376
+ id,
377
+ provider: this.id,
378
+ raw: payload.message?.raw ?? payload.raw ?? payload,
379
+ recordedDirection: "inbound",
380
+ sentAt: new Date().toISOString(),
381
+ text,
382
+ threadId,
383
+ });
384
+ return respond((await this.#options.createWebhookSuccessResponse?.(rawPayload, id)) ??
385
+ new Response(JSON.stringify({ ok: true, id }), {
386
+ headers: { "content-type": "application/json" },
387
+ status: 200,
388
+ }));
190
389
  }
191
390
  catch (error) {
192
- return new Response(ensureErrorMessage(error), { status: 400 });
391
+ settle(false);
392
+ throw error;
193
393
  }
194
- const id = payload.message?.id ?? payload.id ?? createMessageId(this.platform);
195
- const threadId = payload.message?.threadId ?? payload.threadId;
196
- const text = payload.message?.text ?? payload.text;
197
- if (!threadId || !text) {
198
- return new Response("payload requires message.threadId and message.text", { status: 400 });
199
- }
200
- await appendRecordedInbound(this.#recorderPath, {
201
- author: authorFromPayload(payload),
202
- id,
203
- provider: this.id,
204
- raw: payload.message?.raw ?? payload.raw ?? payload,
205
- sentAt: new Date().toISOString(),
206
- text,
207
- threadId,
208
- });
209
- return new Response(JSON.stringify({ ok: true, id }), {
210
- headers: { "content-type": "application/json" },
211
- status: 200,
212
- });
213
394
  }
214
- async #ensureWebhookServer(allowExisting) {
395
+ async #ensureWebhookServer() {
396
+ if (this.#cleanupBegun) {
397
+ throw this.#cleanedUpError();
398
+ }
215
399
  if (this.#server) {
216
400
  return this.#server;
217
401
  }
402
+ if (this.#serverStarting) {
403
+ return await this.#serverStarting;
404
+ }
218
405
  const webhook = this.#options.webhook;
219
406
  const host = webhook?.host ?? this.#options.defaultWebhook.host;
220
407
  const port = webhook?.port ?? this.#options.defaultWebhook.port;
221
408
  const webhookPath = webhook?.path ?? this.#options.defaultWebhook.path;
409
+ const starting = (async () => {
410
+ try {
411
+ return await startWebhookServer({
412
+ handle: (request) => this.#handleWebhook(request),
413
+ host,
414
+ path: webhookPath,
415
+ port,
416
+ });
417
+ }
418
+ catch (error) {
419
+ throw new CrablineError(`${this.platform} local mock webhook server failed: ${ensureErrorMessage(error)}`, { cause: error, kind: "connectivity" });
420
+ }
421
+ })();
422
+ this.#serverStarting = starting;
222
423
  try {
223
- this.#server = await startWebhookServer({
224
- handle: (request) => this.#handleWebhook(request),
225
- host,
226
- path: webhookPath,
227
- port,
228
- });
229
- return this.#server;
424
+ const server = await starting;
425
+ if (this.#cleanupBegun) {
426
+ throw this.#cleanedUpError();
427
+ }
428
+ this.#server = server;
429
+ return server;
230
430
  }
231
- catch (error) {
232
- const code = error.code;
233
- if (allowExisting && code === "EADDRINUSE") {
234
- return {
235
- async close() { },
236
- endpointUrl: `http://${host}:${port}${webhookPath}`,
237
- };
431
+ finally {
432
+ if (this.#serverStarting === starting) {
433
+ this.#serverStarting = null;
238
434
  }
239
- throw new CrablineError(`${this.platform} local mock webhook server failed: ${ensureErrorMessage(error)}`, { cause: error, kind: "connectivity" });
240
435
  }
241
436
  }
437
+ async #closeWebhookServer() {
438
+ let server = this.#server;
439
+ if (!server && this.#serverStarting) {
440
+ try {
441
+ server = await this.#serverStarting;
442
+ }
443
+ catch {
444
+ return;
445
+ }
446
+ }
447
+ if (!server) {
448
+ return;
449
+ }
450
+ if (this.#server === server) {
451
+ this.#server = null;
452
+ }
453
+ await server.close();
454
+ }
455
+ #cleanedUpError() {
456
+ return new CrablineError(`Provider "${this.id}" has been cleaned up.`, { kind: "config" });
457
+ }
458
+ #assertActive() {
459
+ if (this.#cleanupBegun) {
460
+ throw this.#cleanedUpError();
461
+ }
462
+ }
463
+ #signalFor(signal) {
464
+ return signal
465
+ ? AbortSignal.any([signal, this.#cleanupController.signal])
466
+ : this.#cleanupController.signal;
467
+ }
242
468
  }
243
469
  //# sourceMappingURL=local-mock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-mock.js","sourceRoot":"","sources":["../../../src/providers/local-mock.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-mock.js","sourceRoot":"","sources":["../../../src/providers/local-mock.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport { CrablineError, ensureErrorMessage } from \"../core/errors.js\";\nimport type { ProviderConfig, ProviderPlatform } from \"../config/schema.js\";\nimport {\n appendRecordedInbound,\n cloneRecordedInboundCursor,\n createRecordedInboundCursor,\n waitForRecordedInbound,\n watchRecordedInbound,\n type RecordedInboundCursor,\n} from \"./recorder.js\";\nimport type {\n InboundEnvelope,\n NormalizedTarget,\n ProbeResult,\n ProviderAdapter,\n ProviderContext,\n SendContext,\n SendResult,\n WaitContext,\n WatchContext,\n} from \"./types.js\";\nimport { startWebhookServer, type StartedWebhookServer } from \"./webhook-server.js\";\n\nexport { createGenericLocalMockTargetCodec } from \"./target-normalizers.js\";\n\nexport type LocalMockWebhookConfig = {\n host?: string;\n path?: string;\n port?: number;\n publicUrl?: string | undefined;\n};\n\nexport type LocalMockAdapterOptions = {\n authenticateWebhookRequest?: (\n request: Request,\n rawBody: string,\n ) => Promise<Response | undefined> | Response | undefined;\n createWebhookSuccessResponse?: (payload: unknown, id: string) => Promise<Response> | Response;\n defaultWebhook: Required<Pick<LocalMockWebhookConfig, \"host\" | \"path\" | \"port\">>;\n endpointLabel: string;\n matchesThread?: (\n candidateThreadId: string,\n expectedThreadId: string | undefined,\n target: NormalizedTarget,\n raw?: unknown,\n ) => boolean;\n handleWebhookPayload?: (\n payload: unknown,\n request: Request,\n rawBody: string,\n ) => Promise<Response | undefined> | Response | undefined;\n normalizeWebhookPayload?: (payload: unknown) => unknown;\n platform: ProviderPlatform;\n publicUrl?: string | undefined;\n recorderPath?: string | undefined;\n settleWebhookRequest?: (params: { accepted: boolean; payload: unknown; rawBody: string }) => void;\n webhook?: LocalMockWebhookConfig | undefined;\n};\n\nconst MAX_WAIT_CURSORS = 64;\n\ntype WaitCursorState = {\n active: number;\n cursor?: RecordedInboundCursor | undefined;\n};\n\nfunction cursorHasAdvanced(\n candidate: RecordedInboundCursor,\n current: RecordedInboundCursor | undefined,\n): boolean {\n if (!current) {\n return true;\n }\n if (candidate.readState.generation !== current.readState.generation) {\n return candidate.readState.generation > current.readState.generation;\n }\n if (candidate.readState.offset !== current.readState.offset) {\n return candidate.readState.offset > current.readState.offset;\n }\n return candidate.buffered.length < current.buffered.length;\n}\n\nfunction pruneInactiveWaitCursors(cursors: Map<string, WaitCursorState>): void {\n for (const [key, state] of cursors) {\n if (cursors.size <= MAX_WAIT_CURSORS) {\n return;\n }\n if (state.active === 0) {\n cursors.delete(key);\n }\n }\n}\n\nexport type LocalMockTargetCodec = {\n normalize(target: ProviderContext[\"fixture\"][\"target\"]): NormalizedTarget;\n resolveThreadId(target: ProviderContext[\"fixture\"][\"target\"]): string;\n};\n\ntype MockWebhookPayload = {\n author?: \"assistant\" | \"system\" | \"user\";\n authorIsBot?: boolean;\n id?: string;\n message?: {\n author?: \"assistant\" | \"system\" | \"user\";\n authorIsBot?: boolean;\n id?: string;\n raw?: unknown;\n text?: string;\n threadId?: string;\n };\n raw?: unknown;\n text?: string;\n threadId?: string;\n};\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const cleanup = () => signal?.removeEventListener(\"abort\", abort);\n const abort = () => {\n if (timer) {\n clearTimeout(timer);\n }\n cleanup();\n reject(signal?.reason ?? new Error(\"Provider operation aborted.\"));\n };\n timer = setTimeout(() => {\n cleanup();\n resolve();\n }, ms);\n if (signal?.aborted) {\n abort();\n return;\n }\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n\nfunction isAddressInChannel(threadId: string, channelId?: string): boolean {\n if (!channelId) {\n return true;\n }\n return threadId === channelId || threadId.startsWith(`${channelId}:`);\n}\n\nfunction createMessageId(platform: ProviderPlatform) {\n return `${platform}-mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction toRecorderPath(providerId: string, configuredPath?: string): string {\n return configuredPath\n ? path.resolve(configuredPath)\n : path.resolve(\".crabline\", \"recorders\", `${providerId}.jsonl`);\n}\n\nfunction authorFromPayload(payload: MockWebhookPayload): InboundEnvelope[\"author\"] {\n const explicit = payload.message?.author ?? payload.author;\n if (explicit) {\n return explicit;\n }\n return (payload.message?.authorIsBot ?? payload.authorIsBot ?? true) ? \"assistant\" : \"user\";\n}\n\nfunction normalizeWebhookPayload(payload: unknown): MockWebhookPayload {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new CrablineError(\"mock webhook payload must be an object\", { kind: \"inbound\" });\n }\n\n const record = payload as Record<string, unknown>;\n const messageValue = record.message;\n if (\n messageValue !== undefined &&\n (!messageValue || typeof messageValue !== \"object\" || Array.isArray(messageValue))\n ) {\n throw new CrablineError(\"mock webhook payload message must be an object\", {\n kind: \"inbound\",\n });\n }\n\n for (const [label, envelope] of [\n [\"payload\", record],\n [\"payload.message\", messageValue as Record<string, unknown> | undefined],\n ] as const) {\n if (!envelope) {\n continue;\n }\n if (\n envelope.author !== undefined &&\n envelope.author !== \"assistant\" &&\n envelope.author !== \"system\" &&\n envelope.author !== \"user\"\n ) {\n throw new CrablineError(`${label}.author must be assistant, system, or user`, {\n kind: \"inbound\",\n });\n }\n if (envelope.authorIsBot !== undefined && typeof envelope.authorIsBot !== \"boolean\") {\n throw new CrablineError(`${label}.authorIsBot must be a boolean`, { kind: \"inbound\" });\n }\n for (const field of [\"id\", \"text\", \"threadId\"] as const) {\n const value = envelope[field];\n if (value !== undefined && (typeof value !== \"string\" || value.length === 0)) {\n throw new CrablineError(`${label}.${field} must be a non-empty string`, {\n kind: \"inbound\",\n });\n }\n }\n }\n\n return payload as MockWebhookPayload;\n}\n\nfunction mockReplyText(params: { platform: ProviderPlatform; text: string }) {\n return `[${params.platform} mock] ${params.text}`;\n}\n\nexport class LocalMockProviderAdapter implements ProviderAdapter {\n readonly id;\n readonly platform;\n readonly status = \"ready\" as const;\n readonly supports: ProviderAdapter[\"supports\"];\n\n readonly #codec: LocalMockTargetCodec;\n readonly #config: ProviderConfig;\n readonly #options: LocalMockAdapterOptions;\n readonly #publicUrl: string | undefined;\n readonly #recorderPath: string;\n readonly #activeSends = new Set<Promise<SendResult>>();\n readonly #cleanupController = new AbortController();\n readonly #waitCursors = new Map<string, WaitCursorState>();\n #cleanupBegun = false;\n #cleanupPromise: Promise<void> | null = null;\n #server: StartedWebhookServer | null = null;\n #serverStarting: Promise<StartedWebhookServer> | null = null;\n\n constructor(params: {\n codec: LocalMockTargetCodec;\n config: ProviderConfig;\n id: string;\n options: LocalMockAdapterOptions;\n }) {\n this.id = params.id;\n this.platform = params.options.platform;\n this.supports = [...params.config.capabilities];\n this.#codec = params.codec;\n this.#config = params.config;\n this.#options = params.options;\n this.#publicUrl = params.options.publicUrl ?? params.options.webhook?.publicUrl;\n this.#recorderPath = toRecorderPath(params.id, params.options.recorderPath);\n }\n\n normalizeTarget(target: ProviderContext[\"fixture\"][\"target\"]): NormalizedTarget {\n this.#assertActive();\n return this.#codec.normalize(target);\n }\n\n async probe(context: ProviderContext): Promise<ProbeResult> {\n context.signal?.throwIfAborted();\n const server = await this.#ensureWebhookServer();\n context.signal?.throwIfAborted();\n const target = this.normalizeTarget(context.fixture.target);\n const details = [\n `${this.platform} local mock ready`,\n `recorder path ${this.#recorderPath}`,\n `${this.#options.endpointLabel} ${server.endpointUrl}`,\n ];\n if (this.#publicUrl) {\n details.push(`public webhook ${this.#publicUrl}`);\n }\n if (target.threadId) {\n details.push(`thread reachable ${target.threadId}`);\n } else if (target.channelId) {\n details.push(`channel reachable ${target.channelId}`);\n } else {\n details.push(`dm reachable ${this.#codec.resolveThreadId(context.fixture.target)}`);\n }\n return { details, healthy: true };\n }\n\n send(context: SendContext): Promise<SendResult> {\n try {\n this.#assertActive();\n } catch (error) {\n return Promise.reject(error);\n }\n const sending = this.#send(context);\n this.#activeSends.add(sending);\n void sending.then(\n () => this.#activeSends.delete(sending),\n () => this.#activeSends.delete(sending),\n );\n return sending;\n }\n\n async #send(context: SendContext): Promise<SendResult> {\n const { signal } = context;\n const threadId = this.#codec.resolveThreadId(context.fixture.target);\n const messageId = createMessageId(this.platform);\n signal?.throwIfAborted();\n await appendRecordedInbound(this.#recorderPath, {\n author: \"user\",\n id: messageId,\n provider: this.id,\n raw: {\n direction: \"outbound\",\n mode: context.mode,\n platform: this.platform,\n },\n recordedDirection: \"outbound\",\n sentAt: new Date().toISOString(),\n text: context.text,\n threadId,\n });\n\n if (context.mode !== \"send\" && context.fixture.target.behavior !== \"sink\") {\n await sleep(this.#config.loopback?.delayMs ?? 25, signal);\n signal?.throwIfAborted();\n await appendRecordedInbound(this.#recorderPath, {\n author: \"assistant\",\n id: `${messageId}-reply`,\n provider: this.id,\n raw: {\n direction: \"mock-reply\",\n mode: context.mode,\n platform: this.platform,\n },\n recordedDirection: \"inbound\",\n sentAt: new Date().toISOString(),\n text: mockReplyText({ platform: this.platform, text: context.text }),\n threadId,\n });\n }\n\n return {\n accepted: true,\n messageId,\n threadId,\n };\n }\n\n async waitForInbound(context: WaitContext): Promise<InboundEnvelope | null> {\n this.#assertActive();\n const signal = this.#signalFor(context.signal);\n await this.#ensureWebhookServer();\n const target = this.normalizeTarget(context.fixture.target);\n const expectedAuthor = context.fixture.inboundMatch.author;\n const channelId = context.threadId ?? target.threadId ?? target.channelId;\n const excludedIds = new Set(context.excludeIds ?? []);\n const cursorKey = JSON.stringify([context.nonce, context.since, channelId, expectedAuthor]);\n const cursorState = this.#waitCursors.get(cursorKey) ?? { active: 0 };\n const cursor = cursorState.cursor\n ? cloneRecordedInboundCursor(cursorState.cursor)\n : createRecordedInboundCursor();\n cursorState.active++;\n this.#waitCursors.delete(cursorKey);\n this.#waitCursors.set(cursorKey, cursorState);\n pruneInactiveWaitCursors(this.#waitCursors);\n\n try {\n const event = await waitForRecordedInbound({\n cursor,\n filePath: this.#recorderPath,\n matches: (candidate) =>\n candidate.provider === this.id &&\n !excludedIds.has(candidate.id) &&\n (expectedAuthor === \"any\" || candidate.author === expectedAuthor) &&\n (this.#options.matchesThread ?? isAddressInChannel)(\n candidate.threadId,\n channelId,\n target,\n candidate.raw,\n ),\n recordedDirection: \"inbound\",\n since: context.since,\n signal,\n timeoutMs: context.timeoutMs,\n });\n if (\n this.#waitCursors.get(cursorKey) === cursorState &&\n cursorHasAdvanced(cursor, cursorState.cursor)\n ) {\n cursorState.cursor = cursor;\n }\n return event;\n } finally {\n cursorState.active--;\n if (\n cursorState.active === 0 &&\n !cursorState.cursor &&\n this.#waitCursors.get(cursorKey) === cursorState\n ) {\n this.#waitCursors.delete(cursorKey);\n }\n }\n }\n\n async *watch(context: WatchContext): AsyncIterable<InboundEnvelope> {\n this.#assertActive();\n const signal = this.#signalFor(context.signal);\n await this.#ensureWebhookServer();\n const target = this.normalizeTarget(context.fixture.target);\n for await (const event of watchRecordedInbound({\n filePath: this.#recorderPath,\n matches: (entry) =>\n entry.provider === this.id &&\n (this.#options.matchesThread ?? isAddressInChannel)(\n entry.threadId,\n target.threadId ?? target.channelId,\n target,\n entry.raw,\n ),\n recordedDirection: \"inbound\",\n signal,\n since: context.since,\n })) {\n yield event;\n }\n }\n\n #beginCleanup(): void {\n if (this.#cleanupBegun) {\n return;\n }\n this.#cleanupBegun = true;\n this.#cleanupController.abort(this.#cleanedUpError());\n this.#waitCursors.clear();\n }\n\n async cleanup(): Promise<void> {\n this.#beginCleanup();\n this.#cleanupPromise ??= (async () => {\n const sends = [...this.#activeSends];\n const [serverResult] = await Promise.allSettled([this.#closeWebhookServer(), ...sends]);\n if (serverResult?.status === \"rejected\") {\n throw serverResult.reason;\n }\n })();\n await this.#cleanupPromise;\n }\n\n async #handleWebhook(request: Request): Promise<Response> {\n if (this.#cleanupBegun) {\n return new Response(\"provider is shutting down\", { status: 503 });\n }\n const rawBody = await request.text();\n const authenticationFailure = await this.#options.authenticateWebhookRequest?.(\n request,\n rawBody,\n );\n if (authenticationFailure) {\n return authenticationFailure;\n }\n const mediaType = request.headers.get(\"content-type\")?.split(\";\", 1)[0]?.trim().toLowerCase();\n let rawPayload: unknown;\n if (mediaType === \"application/json\") {\n try {\n rawPayload = JSON.parse(rawBody) as unknown;\n } catch {\n this.#options.settleWebhookRequest?.({\n accepted: false,\n payload: undefined,\n rawBody,\n });\n return new Response(\"invalid JSON\", { status: 400 });\n }\n } else {\n rawPayload = rawBody;\n }\n let settled = false;\n const settle = (accepted: boolean) => {\n if (settled) {\n return;\n }\n settled = true;\n this.#options.settleWebhookRequest?.({ accepted, payload: rawPayload, rawBody });\n };\n const respond = (response: Response) => {\n settle(response.ok);\n return response;\n };\n try {\n const directResponse = await this.#options.handleWebhookPayload?.(\n rawPayload,\n request,\n rawBody,\n );\n if (directResponse) {\n return respond(directResponse);\n }\n if (mediaType !== \"application/json\") {\n return respond(new Response(\"expected application/json\", { status: 415 }));\n }\n let payload: MockWebhookPayload;\n try {\n payload = normalizeWebhookPayload(\n this.#options.normalizeWebhookPayload\n ? this.#options.normalizeWebhookPayload(rawPayload)\n : rawPayload,\n );\n } catch (error) {\n if (error instanceof CrablineError && error.kind === \"inbound\") {\n return respond(new Response(ensureErrorMessage(error), { status: 400 }));\n }\n throw error;\n }\n\n const id = payload.message?.id ?? payload.id ?? createMessageId(this.platform);\n const threadId = payload.message?.threadId ?? payload.threadId;\n const text = payload.message?.text ?? payload.text;\n if (!threadId || !text) {\n return respond(\n new Response(\"payload requires message.threadId and message.text\", { status: 400 }),\n );\n }\n if (this.#cleanupBegun) {\n return respond(new Response(\"provider is shutting down\", { status: 503 }));\n }\n\n await appendRecordedInbound(this.#recorderPath, {\n author: authorFromPayload(payload),\n id,\n provider: this.id,\n raw: payload.message?.raw ?? payload.raw ?? payload,\n recordedDirection: \"inbound\",\n sentAt: new Date().toISOString(),\n text,\n threadId,\n });\n return respond(\n (await this.#options.createWebhookSuccessResponse?.(rawPayload, id)) ??\n new Response(JSON.stringify({ ok: true, id }), {\n headers: { \"content-type\": \"application/json\" },\n status: 200,\n }),\n );\n } catch (error) {\n settle(false);\n throw error;\n }\n }\n\n async #ensureWebhookServer(): Promise<StartedWebhookServer> {\n if (this.#cleanupBegun) {\n throw this.#cleanedUpError();\n }\n if (this.#server) {\n return this.#server;\n }\n if (this.#serverStarting) {\n return await this.#serverStarting;\n }\n\n const webhook = this.#options.webhook;\n const host = webhook?.host ?? this.#options.defaultWebhook.host;\n const port = webhook?.port ?? this.#options.defaultWebhook.port;\n const webhookPath = webhook?.path ?? this.#options.defaultWebhook.path;\n const starting = (async () => {\n try {\n return await startWebhookServer({\n handle: (request) => this.#handleWebhook(request),\n host,\n path: webhookPath,\n port,\n });\n } catch (error) {\n throw new CrablineError(\n `${this.platform} local mock webhook server failed: ${ensureErrorMessage(error)}`,\n { cause: error, kind: \"connectivity\" },\n );\n }\n })();\n this.#serverStarting = starting;\n\n try {\n const server = await starting;\n if (this.#cleanupBegun) {\n throw this.#cleanedUpError();\n }\n this.#server = server;\n return server;\n } finally {\n if (this.#serverStarting === starting) {\n this.#serverStarting = null;\n }\n }\n }\n\n async #closeWebhookServer(): Promise<void> {\n let server = this.#server;\n if (!server && this.#serverStarting) {\n try {\n server = await this.#serverStarting;\n } catch {\n return;\n }\n }\n if (!server) {\n return;\n }\n\n if (this.#server === server) {\n this.#server = null;\n }\n await server.close();\n }\n\n #cleanedUpError(): CrablineError {\n return new CrablineError(`Provider \"${this.id}\" has been cleaned up.`, { kind: \"config\" });\n }\n\n #assertActive(): void {\n if (this.#cleanupBegun) {\n throw this.#cleanedUpError();\n }\n }\n\n #signalFor(signal?: AbortSignal): AbortSignal {\n return signal\n ? AbortSignal.any([signal, this.#cleanupController.signal])\n : this.#cleanupController.signal;\n }\n}\n"]}
@@ -3,3 +3,4 @@ export type NativeIdRule = {
3
3
  name: string;
4
4
  pattern: RegExp;
5
5
  };
6
+ export declare function numericNativeId(value: number | bigint): string | undefined;
@@ -1,2 +1,7 @@
1
- export {};
1
+ export function numericNativeId(value) {
2
+ if (typeof value === "number" && !Number.isSafeInteger(value)) {
3
+ return undefined;
4
+ }
5
+ return value.toString();
6
+ }
2
7
  //# sourceMappingURL=native-ids.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-ids.js","sourceRoot":"","sources":["../../../src/providers/native-ids.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"native-ids.js","sourceRoot":"","sources":["../../../src/providers/native-ids.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC","sourcesContent":["export type NativeIdRule = {\n example: string;\n name: string;\n pattern: RegExp;\n};\n\nexport function numericNativeId(value: number | bigint): string | undefined {\n if (typeof value === \"number\" && !Number.isSafeInteger(value)) {\n return undefined;\n }\n return value.toString();\n}\n"]}