@imessage-sdk/blooio 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +586 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 imessage-sdk contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @imessage-sdk/blooio
|
|
2
|
+
|
|
3
|
+
Blooio v2 provider for [`imessage-sdk`](https://www.npmjs.com/package/imessage-sdk).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add imessage-sdk @imessage-sdk/blooio
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { blooio } from '@imessage-sdk/blooio';
|
|
15
|
+
import { createIMessageClient } from 'imessage-sdk';
|
|
16
|
+
|
|
17
|
+
const client = createIMessageClient({
|
|
18
|
+
provider: blooio(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await client.messages.send({
|
|
22
|
+
to: { kind: 'phone', value: '+15551234567' },
|
|
23
|
+
text: 'Hello from Blooio',
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`blooio()` reads `BLOOIO_API_KEY`, `BLOOIO_FROM_NUMBER`, and
|
|
28
|
+
`BLOOIO_WEBHOOK_SECRET`. Explicit options override environment values.
|
|
29
|
+
|
|
30
|
+
Run the opt-in live test from the repository root:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm --filter @imessage-sdk/blooio test:integration
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
See the [repository README](https://github.com/jmisilo/imessage-sdk#readme) for
|
|
37
|
+
capabilities, attachments, webhooks, and required live-test variables.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ProviderConversations, ProviderConversation, IMessageStatus, IMessageService, ProviderMessages, MessageLocator, ProviderMessage, IMessageAddress, IMessageProvider, ProviderReactions, ProviderTyping, ProviderWebhooks } from 'imessage-sdk';
|
|
2
|
+
|
|
3
|
+
declare const BLOOIO_CAPABILITIES: {
|
|
4
|
+
readonly messages: {
|
|
5
|
+
readonly text: true;
|
|
6
|
+
readonly attachments: true;
|
|
7
|
+
readonly replies: true;
|
|
8
|
+
readonly get: true;
|
|
9
|
+
readonly edit: false;
|
|
10
|
+
readonly delete: false;
|
|
11
|
+
};
|
|
12
|
+
readonly conversations: {
|
|
13
|
+
readonly direct: true;
|
|
14
|
+
readonly groups: false;
|
|
15
|
+
readonly get: true;
|
|
16
|
+
readonly markRead: true;
|
|
17
|
+
};
|
|
18
|
+
readonly interactions: {
|
|
19
|
+
readonly reactions: true;
|
|
20
|
+
readonly typingStart: true;
|
|
21
|
+
readonly typingStop: true;
|
|
22
|
+
readonly readReceipts: true;
|
|
23
|
+
};
|
|
24
|
+
readonly events: {
|
|
25
|
+
readonly webhooks: true;
|
|
26
|
+
readonly stream: false;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
interface BlooioOptions {
|
|
30
|
+
readonly apiKey?: string;
|
|
31
|
+
readonly sender?: IMessageAddress;
|
|
32
|
+
readonly webhookSecret?: string;
|
|
33
|
+
readonly baseUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
interface BlooioMessageStatus {
|
|
36
|
+
readonly messageId: string;
|
|
37
|
+
readonly conversationId: string;
|
|
38
|
+
readonly status: IMessageStatus;
|
|
39
|
+
readonly providerStatus?: string;
|
|
40
|
+
readonly service: IMessageService;
|
|
41
|
+
readonly sentAt?: Date;
|
|
42
|
+
readonly deliveredAt?: Date;
|
|
43
|
+
readonly error?: string;
|
|
44
|
+
readonly raw: unknown;
|
|
45
|
+
}
|
|
46
|
+
type BlooioPlanKind = 'shared' | 'dedicated' | 'inbound' | 'trial' | '2fa';
|
|
47
|
+
interface BlooioNumber {
|
|
48
|
+
readonly phoneNumber: string;
|
|
49
|
+
readonly active: boolean;
|
|
50
|
+
readonly lastActive?: Date;
|
|
51
|
+
readonly planKind?: BlooioPlanKind;
|
|
52
|
+
readonly raw: unknown;
|
|
53
|
+
}
|
|
54
|
+
interface BlooioNumbers {
|
|
55
|
+
list(): Promise<readonly BlooioNumber[]>;
|
|
56
|
+
}
|
|
57
|
+
interface BlooioMessages extends ProviderMessages {
|
|
58
|
+
get(message: MessageLocator): Promise<ProviderMessage | null>;
|
|
59
|
+
getStatus(message: MessageLocator): Promise<BlooioMessageStatus | null>;
|
|
60
|
+
}
|
|
61
|
+
interface BlooioConversations extends ProviderConversations {
|
|
62
|
+
get(conversationId: string): Promise<ProviderConversation | null>;
|
|
63
|
+
markRead(conversationId: string): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
interface BlooioProvider extends IMessageProvider<'blooio', typeof BLOOIO_CAPABILITIES> {
|
|
66
|
+
readonly messages: BlooioMessages;
|
|
67
|
+
readonly conversations: BlooioConversations;
|
|
68
|
+
readonly reactions: ProviderReactions;
|
|
69
|
+
readonly typing: Required<ProviderTyping>;
|
|
70
|
+
readonly webhooks: ProviderWebhooks;
|
|
71
|
+
readonly numbers: BlooioNumbers;
|
|
72
|
+
}
|
|
73
|
+
/** Creates a Blooio v2 provider. No initialization call is required. */
|
|
74
|
+
declare function blooio(options?: BlooioOptions): BlooioProvider;
|
|
75
|
+
|
|
76
|
+
export { BLOOIO_CAPABILITIES, type BlooioConversations, type BlooioMessageStatus, type BlooioMessages, type BlooioNumber, type BlooioNumbers, type BlooioOptions, type BlooioPlanKind, type BlooioProvider, blooio };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineProvider, AuthenticationError, AmbiguousDeliveryError, ProviderUnavailableError, NotFoundError, ConflictError, RateLimitError, IMessageSDKError, ValidationError } from 'imessage-sdk';
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var DEFAULT_BASE_URL = "https://api.blooio.com/v2/api";
|
|
6
|
+
var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
7
|
+
var BLOOIO_CAPABILITIES = {
|
|
8
|
+
messages: {
|
|
9
|
+
text: true,
|
|
10
|
+
attachments: true,
|
|
11
|
+
replies: true,
|
|
12
|
+
get: true,
|
|
13
|
+
edit: false,
|
|
14
|
+
delete: false
|
|
15
|
+
},
|
|
16
|
+
conversations: {
|
|
17
|
+
direct: true,
|
|
18
|
+
groups: false,
|
|
19
|
+
get: true,
|
|
20
|
+
markRead: true
|
|
21
|
+
},
|
|
22
|
+
interactions: {
|
|
23
|
+
reactions: true,
|
|
24
|
+
typingStart: true,
|
|
25
|
+
typingStop: true,
|
|
26
|
+
readReceipts: true
|
|
27
|
+
},
|
|
28
|
+
events: {
|
|
29
|
+
webhooks: true,
|
|
30
|
+
stream: false
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var JsonObjectSchema = z.record(z.string(), z.unknown());
|
|
34
|
+
var OptionalNonEmptyStringSchema = z.string().min(1).optional().catch(void 0);
|
|
35
|
+
var OptionalNumberSchema = z.number().optional().catch(void 0);
|
|
36
|
+
var ReactionSchema = z.enum(["love", "like", "dislike", "laugh", "emphasize", "question"]);
|
|
37
|
+
var PlanKindSchema = z.enum(["shared", "dedicated", "inbound", "trial", "2fa"]);
|
|
38
|
+
function dateValue(value) {
|
|
39
|
+
const number = OptionalNumberSchema.parse(value);
|
|
40
|
+
if (number !== void 0) return new Date(number);
|
|
41
|
+
const string = OptionalNonEmptyStringSchema.parse(value);
|
|
42
|
+
if (string === void 0) return void 0;
|
|
43
|
+
const date = new Date(string);
|
|
44
|
+
return Number.isNaN(date.valueOf()) ? void 0 : date;
|
|
45
|
+
}
|
|
46
|
+
function address(value) {
|
|
47
|
+
return {
|
|
48
|
+
kind: value.includes("@") ? "email" : "phone",
|
|
49
|
+
value
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function requireAddress(value, fallback = "unknown") {
|
|
53
|
+
return address(OptionalNonEmptyStringSchema.parse(value) ?? fallback);
|
|
54
|
+
}
|
|
55
|
+
function mapStatus(value) {
|
|
56
|
+
switch (typeof value === "string" ? value.toLowerCase() : "") {
|
|
57
|
+
case "queued":
|
|
58
|
+
case "pending":
|
|
59
|
+
return "pending";
|
|
60
|
+
case "accepted":
|
|
61
|
+
return "accepted";
|
|
62
|
+
case "sent":
|
|
63
|
+
return "sent";
|
|
64
|
+
case "delivered":
|
|
65
|
+
return "delivered";
|
|
66
|
+
case "read":
|
|
67
|
+
return "read";
|
|
68
|
+
case "failed":
|
|
69
|
+
case "cancelled":
|
|
70
|
+
case "cancellation_requested":
|
|
71
|
+
case "error":
|
|
72
|
+
return "failed";
|
|
73
|
+
default:
|
|
74
|
+
return "pending";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function mapService(value) {
|
|
78
|
+
switch (typeof value === "string" ? value.toLowerCase() : "") {
|
|
79
|
+
case "imessage":
|
|
80
|
+
return "imessage";
|
|
81
|
+
case "sms":
|
|
82
|
+
return "sms";
|
|
83
|
+
case "rcs":
|
|
84
|
+
return "rcs";
|
|
85
|
+
default:
|
|
86
|
+
return "unknown";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function attachmentKind(contentType) {
|
|
90
|
+
if (contentType?.startsWith("image/")) return "image";
|
|
91
|
+
if (contentType?.startsWith("video/")) return "video";
|
|
92
|
+
return "file";
|
|
93
|
+
}
|
|
94
|
+
function mapAttachment(value) {
|
|
95
|
+
if (typeof value === "string") {
|
|
96
|
+
return { kind: "file", url: value, raw: value };
|
|
97
|
+
}
|
|
98
|
+
const parsedItem = JsonObjectSchema.safeParse(value);
|
|
99
|
+
const item = parsedItem.success ? parsedItem.data : {};
|
|
100
|
+
const contentType = OptionalNonEmptyStringSchema.parse(item["content_type"]) ?? OptionalNonEmptyStringSchema.parse(item["contentType"]) ?? OptionalNonEmptyStringSchema.parse(item["mime_type"]);
|
|
101
|
+
const id = OptionalNonEmptyStringSchema.parse(item["id"]);
|
|
102
|
+
const url = OptionalNonEmptyStringSchema.parse(item["url"]);
|
|
103
|
+
const filename = OptionalNonEmptyStringSchema.parse(item["name"]);
|
|
104
|
+
const size = OptionalNumberSchema.parse(item["size"]);
|
|
105
|
+
const result = {
|
|
106
|
+
kind: attachmentKind(contentType),
|
|
107
|
+
raw: value,
|
|
108
|
+
...id === void 0 ? {} : { id },
|
|
109
|
+
...url === void 0 ? {} : { url },
|
|
110
|
+
...filename === void 0 ? {} : { filename },
|
|
111
|
+
...contentType === void 0 ? {} : { contentType },
|
|
112
|
+
...size === void 0 ? {} : { size }
|
|
113
|
+
};
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
function participantValues(value) {
|
|
117
|
+
if (!Array.isArray(value)) return [];
|
|
118
|
+
return value.flatMap((participant) => {
|
|
119
|
+
if (typeof participant === "string") return [participant];
|
|
120
|
+
const parsedParticipant = JsonObjectSchema.safeParse(participant);
|
|
121
|
+
if (!parsedParticipant.success) return [];
|
|
122
|
+
const identifier = OptionalNonEmptyStringSchema.parse(parsedParticipant.data["identifier"]);
|
|
123
|
+
return identifier === void 0 ? [] : [identifier];
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function mapProviderMessage(raw, fallbackConversationId, configuredSender) {
|
|
127
|
+
const direction = raw["direction"] === "inbound" ? "inbound" : "outbound";
|
|
128
|
+
const conversationId2 = OptionalNonEmptyStringSchema.parse(raw["chat_id"]) ?? OptionalNonEmptyStringSchema.parse(raw["group_id"]) ?? OptionalNonEmptyStringSchema.parse(raw["external_id"]) ?? fallbackConversationId;
|
|
129
|
+
const internal = OptionalNonEmptyStringSchema.parse(raw["internal_id"]);
|
|
130
|
+
const parsedContact = JsonObjectSchema.safeParse(raw["contact"]);
|
|
131
|
+
const external = OptionalNonEmptyStringSchema.parse(raw["sender"]) ?? (parsedContact.success ? OptionalNonEmptyStringSchema.parse(parsedContact.data["identifier"]) : void 0) ?? OptionalNonEmptyStringSchema.parse(raw["external_id"]) ?? conversationId2;
|
|
132
|
+
const participants = participantValues(raw["participants"]);
|
|
133
|
+
const sender = direction === "inbound" ? address(external) : internal === void 0 ? configuredSender ?? address("unknown") : address(internal);
|
|
134
|
+
const recipients = direction === "inbound" ? [internal === void 0 ? configuredSender ?? address("unknown") : address(internal)] : (participants.length > 0 ? participants : [external]).map(address);
|
|
135
|
+
const providerStatus = OptionalNonEmptyStringSchema.parse(raw["status"]);
|
|
136
|
+
const parsedReply = JsonObjectSchema.safeParse(raw["reply_to"]);
|
|
137
|
+
const reply = parsedReply.success ? parsedReply.data : void 0;
|
|
138
|
+
const replyMessageId = reply === void 0 ? void 0 : OptionalNonEmptyStringSchema.parse(reply["message_id"]) ?? OptionalNonEmptyStringSchema.parse(reply["guid"]);
|
|
139
|
+
const sentAt = dateValue(raw["time_sent"] ?? raw["sent_at"]);
|
|
140
|
+
const deliveredAt = dateValue(raw["time_delivered"] ?? raw["delivered_at"]);
|
|
141
|
+
const readAt = dateValue(raw["read_at"]);
|
|
142
|
+
const partIndex = OptionalNumberSchema.parse(reply?.["part_index"]);
|
|
143
|
+
return {
|
|
144
|
+
providerMessageId: OptionalNonEmptyStringSchema.parse(raw["message_id"]) ?? OptionalNonEmptyStringSchema.parse(raw["id"]) ?? "unknown",
|
|
145
|
+
conversationId: conversationId2,
|
|
146
|
+
direction,
|
|
147
|
+
sender,
|
|
148
|
+
recipients,
|
|
149
|
+
text: typeof raw["text"] === "string" ? raw["text"] : "",
|
|
150
|
+
attachments: Array.isArray(raw["attachments"]) ? raw["attachments"].map(mapAttachment) : [],
|
|
151
|
+
...replyMessageId === void 0 ? {} : {
|
|
152
|
+
replyTo: {
|
|
153
|
+
messageId: replyMessageId,
|
|
154
|
+
...partIndex === void 0 ? {} : { partIndex }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
service: mapService(raw["protocol"]),
|
|
158
|
+
status: mapStatus(providerStatus),
|
|
159
|
+
...providerStatus === void 0 ? {} : { providerStatus },
|
|
160
|
+
createdAt: sentAt ?? dateValue(raw["received_at"]) ?? dateValue(raw["timestamp"]) ?? /* @__PURE__ */ new Date(),
|
|
161
|
+
...sentAt === void 0 ? {} : { sentAt },
|
|
162
|
+
...deliveredAt === void 0 ? {} : { deliveredAt },
|
|
163
|
+
...readAt === void 0 ? {} : { readAt },
|
|
164
|
+
raw
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function encodePath(value) {
|
|
168
|
+
return encodeURIComponent(value);
|
|
169
|
+
}
|
|
170
|
+
function conversationId(input) {
|
|
171
|
+
return input.participants.map((participant) => participant.value).join(",");
|
|
172
|
+
}
|
|
173
|
+
function parseSignature(header) {
|
|
174
|
+
let timestamp;
|
|
175
|
+
const signatures = [];
|
|
176
|
+
for (const component of header.split(",")) {
|
|
177
|
+
const separator = component.indexOf("=");
|
|
178
|
+
if (separator < 0) continue;
|
|
179
|
+
const key = component.slice(0, separator).trim();
|
|
180
|
+
const value = component.slice(separator + 1).trim();
|
|
181
|
+
if (key === "t") timestamp = value;
|
|
182
|
+
if (key === "v1") signatures.push(value);
|
|
183
|
+
}
|
|
184
|
+
return timestamp === void 0 || signatures.length === 0 ? void 0 : { timestamp, signatures };
|
|
185
|
+
}
|
|
186
|
+
function constantTimeEqual(left, right) {
|
|
187
|
+
if (left.length !== right.length) return false;
|
|
188
|
+
let result = 0;
|
|
189
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
190
|
+
result |= left.charCodeAt(index) ^ right.charCodeAt(index);
|
|
191
|
+
}
|
|
192
|
+
return result === 0;
|
|
193
|
+
}
|
|
194
|
+
async function hmacSha256(secret, value) {
|
|
195
|
+
const encoder = new TextEncoder();
|
|
196
|
+
const key = await crypto.subtle.importKey(
|
|
197
|
+
"raw",
|
|
198
|
+
encoder.encode(secret),
|
|
199
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
200
|
+
false,
|
|
201
|
+
["sign"]
|
|
202
|
+
);
|
|
203
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(value));
|
|
204
|
+
return Array.from(new Uint8Array(signature), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
205
|
+
""
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
function eventStatus(event, rawStatus) {
|
|
209
|
+
switch (event) {
|
|
210
|
+
case "message.received":
|
|
211
|
+
return "delivered";
|
|
212
|
+
case "message.sent":
|
|
213
|
+
return "sent";
|
|
214
|
+
case "message.delivered":
|
|
215
|
+
return "delivered";
|
|
216
|
+
case "message.read":
|
|
217
|
+
return "read";
|
|
218
|
+
case "message.failed":
|
|
219
|
+
return "failed";
|
|
220
|
+
default:
|
|
221
|
+
return mapStatus(rawStatus);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function mapWebhookEvent(raw) {
|
|
225
|
+
const event = OptionalNonEmptyStringSchema.parse(raw["event"]);
|
|
226
|
+
if (event === void 0) return void 0;
|
|
227
|
+
const timestamp = dateValue(raw["timestamp"]) ?? /* @__PURE__ */ new Date();
|
|
228
|
+
const messageId = OptionalNonEmptyStringSchema.parse(raw["message_id"]) ?? "unknown";
|
|
229
|
+
const conversation = OptionalNonEmptyStringSchema.parse(raw["group_id"]) ?? OptionalNonEmptyStringSchema.parse(raw["external_id"]) ?? "unknown";
|
|
230
|
+
const id = `${event}:${messageId}:${timestamp.valueOf()}`;
|
|
231
|
+
if (event === "message.reaction") {
|
|
232
|
+
const parsedReaction = ReactionSchema.safeParse(raw["reaction"]);
|
|
233
|
+
if (!parsedReaction.success) return void 0;
|
|
234
|
+
const reaction = parsedReaction.data;
|
|
235
|
+
const partIndex = OptionalNumberSchema.parse(raw["part_index"]);
|
|
236
|
+
return {
|
|
237
|
+
id,
|
|
238
|
+
type: raw["action"] === "remove" ? "reaction.removed" : "reaction.added",
|
|
239
|
+
timestamp,
|
|
240
|
+
conversationId: conversation,
|
|
241
|
+
messageId,
|
|
242
|
+
actor: requireAddress(raw["sender"] ?? raw["external_id"]),
|
|
243
|
+
reaction,
|
|
244
|
+
...partIndex === void 0 ? {} : { partIndex },
|
|
245
|
+
raw
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (event === "typing.started" || event === "typing.stopped") {
|
|
249
|
+
const actorValue = OptionalNonEmptyStringSchema.parse(raw["sender"]);
|
|
250
|
+
return {
|
|
251
|
+
id,
|
|
252
|
+
type: event,
|
|
253
|
+
timestamp,
|
|
254
|
+
conversationId: conversation,
|
|
255
|
+
...actorValue === void 0 ? {} : { actor: address(actorValue) },
|
|
256
|
+
raw
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (event !== "message.received" && event !== "message.sent" && event !== "message.delivered" && event !== "message.read" && event !== "message.failed") {
|
|
260
|
+
return void 0;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
id,
|
|
264
|
+
type: event,
|
|
265
|
+
timestamp,
|
|
266
|
+
message: mapProviderMessage(
|
|
267
|
+
{
|
|
268
|
+
...raw,
|
|
269
|
+
direction: event === "message.received" ? "inbound" : "outbound",
|
|
270
|
+
status: eventStatus(event, raw["status"])
|
|
271
|
+
},
|
|
272
|
+
conversation
|
|
273
|
+
),
|
|
274
|
+
raw
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function blooio(options = {}) {
|
|
278
|
+
const apiKey = options.apiKey ?? process.env["BLOOIO_API_KEY"];
|
|
279
|
+
const webhookSecret = options.webhookSecret ?? process.env["BLOOIO_WEBHOOK_SECRET"];
|
|
280
|
+
const configuredSender = process.env["BLOOIO_FROM_NUMBER"];
|
|
281
|
+
const sender = options.sender ?? (configuredSender === void 0 ? void 0 : address(configuredSender));
|
|
282
|
+
const config = {
|
|
283
|
+
...options,
|
|
284
|
+
...apiKey === void 0 ? {} : { apiKey },
|
|
285
|
+
...webhookSecret === void 0 ? {} : { webhookSecret },
|
|
286
|
+
...sender === void 0 ? {} : { sender }
|
|
287
|
+
};
|
|
288
|
+
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
289
|
+
const request = async (path, init = {}, requestOptions = {}) => {
|
|
290
|
+
if (config.apiKey === void 0 || config.apiKey.length === 0) {
|
|
291
|
+
throw new AuthenticationError("A Blooio API key is required.", {
|
|
292
|
+
provider: "blooio",
|
|
293
|
+
code: "missing_api_key"
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
let response;
|
|
297
|
+
try {
|
|
298
|
+
response = await fetch(`${baseUrl}${path}`, {
|
|
299
|
+
...init,
|
|
300
|
+
headers: {
|
|
301
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
302
|
+
accept: "application/json",
|
|
303
|
+
...init.body === void 0 ? {} : { "content-type": "application/json" },
|
|
304
|
+
...init.headers
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
} catch (cause) {
|
|
308
|
+
if (requestOptions.send === true) {
|
|
309
|
+
throw new AmbiguousDeliveryError(
|
|
310
|
+
"The Blooio send result is unknown; retry only with the same idempotency key.",
|
|
311
|
+
{ provider: "blooio", code: "ambiguous_delivery", retryable: true, raw: cause }
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
throw new ProviderUnavailableError("Could not reach Blooio.", {
|
|
315
|
+
provider: "blooio",
|
|
316
|
+
code: "provider_unavailable",
|
|
317
|
+
retryable: true,
|
|
318
|
+
raw: cause
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
const rawText = await response.text();
|
|
322
|
+
let raw;
|
|
323
|
+
try {
|
|
324
|
+
raw = rawText.length === 0 ? void 0 : JSON.parse(rawText);
|
|
325
|
+
} catch {
|
|
326
|
+
raw = rawText;
|
|
327
|
+
}
|
|
328
|
+
if (response.ok) return raw ?? {};
|
|
329
|
+
if (response.status === 404 && requestOptions.notFoundNull === true) return null;
|
|
330
|
+
const parsedBody = JsonObjectSchema.safeParse(raw);
|
|
331
|
+
const body = parsedBody.success ? parsedBody.data : {};
|
|
332
|
+
const message = OptionalNonEmptyStringSchema.parse(body["message"]) ?? OptionalNonEmptyStringSchema.parse(body["error"]) ?? `Blooio request failed with HTTP ${response.status}.`;
|
|
333
|
+
const code = OptionalNonEmptyStringSchema.parse(body["code"]) ?? `http_${response.status}`;
|
|
334
|
+
const common = {
|
|
335
|
+
provider: "blooio",
|
|
336
|
+
code,
|
|
337
|
+
statusCode: response.status,
|
|
338
|
+
raw
|
|
339
|
+
};
|
|
340
|
+
if (response.status === 401 || response.status === 403) {
|
|
341
|
+
throw new AuthenticationError(message, common);
|
|
342
|
+
}
|
|
343
|
+
if (response.status === 404) throw new NotFoundError(message, common);
|
|
344
|
+
if (response.status === 409) throw new ConflictError(message, common);
|
|
345
|
+
if (response.status === 429) {
|
|
346
|
+
const retryAfterHeader = response.headers.get("retry-after");
|
|
347
|
+
const retryAfter = retryAfterHeader === null ? void 0 : Number(retryAfterHeader);
|
|
348
|
+
throw new RateLimitError(message, {
|
|
349
|
+
...common,
|
|
350
|
+
retryable: true,
|
|
351
|
+
...retryAfter !== void 0 && Number.isFinite(retryAfter) ? { retryAfter } : {}
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
if (response.status >= 500) {
|
|
355
|
+
throw new ProviderUnavailableError(message, {
|
|
356
|
+
...common,
|
|
357
|
+
retryable: true
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
throw new IMessageSDKError(message, common);
|
|
361
|
+
};
|
|
362
|
+
const messages = {
|
|
363
|
+
async send(input) {
|
|
364
|
+
const destinations = input.to === void 0 ? [] : "value" in input.to ? [input.to] : input.to;
|
|
365
|
+
const chatId = input.conversationId ?? destinations.map((recipient) => recipient.value).join(",");
|
|
366
|
+
const attachments = input.attachments?.map((attachment) => {
|
|
367
|
+
if (attachment.source.type !== "url") {
|
|
368
|
+
throw new ValidationError(
|
|
369
|
+
"Blooio v2 requires attachments to use a publicly accessible URL.",
|
|
370
|
+
{ provider: "blooio", code: "attachment_url_required" }
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return attachment.filename === void 0 ? attachment.source.url : { url: attachment.source.url, name: attachment.filename };
|
|
374
|
+
});
|
|
375
|
+
const body = {
|
|
376
|
+
...input.text === void 0 ? {} : { text: input.text },
|
|
377
|
+
...attachments === void 0 || attachments.length === 0 ? {} : { attachments },
|
|
378
|
+
...config.sender === void 0 ? {} : { from_number: config.sender.value },
|
|
379
|
+
...input.replyTo === void 0 ? {} : {
|
|
380
|
+
reply_to: {
|
|
381
|
+
message_id: input.replyTo.messageId,
|
|
382
|
+
...input.replyTo.partIndex === void 0 ? {} : { part_index: input.replyTo.partIndex }
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
const raw = await request(
|
|
387
|
+
`/chats/${encodePath(chatId)}/messages`,
|
|
388
|
+
{
|
|
389
|
+
method: "POST",
|
|
390
|
+
...input.idempotencyKey === void 0 ? {} : { headers: { "idempotency-key": input.idempotencyKey } },
|
|
391
|
+
body: JSON.stringify(body)
|
|
392
|
+
},
|
|
393
|
+
{ send: true }
|
|
394
|
+
);
|
|
395
|
+
const response = raw ?? {};
|
|
396
|
+
const providerMessageId = OptionalNonEmptyStringSchema.parse(response["message_id"]);
|
|
397
|
+
if (providerMessageId === void 0) {
|
|
398
|
+
throw new IMessageSDKError("Blooio did not return a message ID.", {
|
|
399
|
+
provider: "blooio",
|
|
400
|
+
code: "invalid_provider_response",
|
|
401
|
+
raw: response
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
const actualConversationId = OptionalNonEmptyStringSchema.parse(response["group_id"]) ?? chatId;
|
|
405
|
+
const providerStatus = OptionalNonEmptyStringSchema.parse(response["status"]);
|
|
406
|
+
const recipients = input.to === void 0 ? participantValues(response["participants"]).map(address) : destinations;
|
|
407
|
+
return {
|
|
408
|
+
providerMessageId,
|
|
409
|
+
conversationId: actualConversationId,
|
|
410
|
+
direction: "outbound",
|
|
411
|
+
sender: config.sender ?? address("unknown"),
|
|
412
|
+
recipients,
|
|
413
|
+
text: input.text ?? "",
|
|
414
|
+
attachments: input.attachments?.map((attachment) => {
|
|
415
|
+
const url = attachment.source.type === "url" ? attachment.source.url : void 0;
|
|
416
|
+
return {
|
|
417
|
+
kind: attachment.kind,
|
|
418
|
+
...url === void 0 ? {} : { url },
|
|
419
|
+
...attachment.filename === void 0 ? {} : { filename: attachment.filename },
|
|
420
|
+
...attachment.contentType === void 0 ? {} : { contentType: attachment.contentType },
|
|
421
|
+
raw: attachment
|
|
422
|
+
};
|
|
423
|
+
}) ?? [],
|
|
424
|
+
...input.replyTo === void 0 ? {} : { replyTo: input.replyTo },
|
|
425
|
+
service: "imessage",
|
|
426
|
+
status: mapStatus(response["status"]),
|
|
427
|
+
...providerStatus === void 0 ? {} : { providerStatus },
|
|
428
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
429
|
+
raw: response
|
|
430
|
+
};
|
|
431
|
+
},
|
|
432
|
+
async get(message) {
|
|
433
|
+
const raw = await request(
|
|
434
|
+
`/chats/${encodePath(message.conversationId)}/messages/${encodePath(message.messageId)}`,
|
|
435
|
+
{},
|
|
436
|
+
{ notFoundNull: true }
|
|
437
|
+
);
|
|
438
|
+
return raw === null ? null : mapProviderMessage(raw, message.conversationId, config.sender);
|
|
439
|
+
},
|
|
440
|
+
async getStatus(message) {
|
|
441
|
+
const raw = await request(
|
|
442
|
+
`/chats/${encodePath(message.conversationId)}/messages/${encodePath(message.messageId)}/status`,
|
|
443
|
+
{},
|
|
444
|
+
{ notFoundNull: true }
|
|
445
|
+
);
|
|
446
|
+
if (raw === null) return null;
|
|
447
|
+
const providerStatus = OptionalNonEmptyStringSchema.parse(raw["status"]);
|
|
448
|
+
const sentAt = dateValue(raw["time_sent"]);
|
|
449
|
+
const deliveredAt = dateValue(raw["time_delivered"]);
|
|
450
|
+
const error = OptionalNonEmptyStringSchema.parse(raw["error"]);
|
|
451
|
+
return {
|
|
452
|
+
messageId: OptionalNonEmptyStringSchema.parse(raw["message_id"]) ?? message.messageId,
|
|
453
|
+
conversationId: OptionalNonEmptyStringSchema.parse(raw["chat_id"]) ?? message.conversationId,
|
|
454
|
+
status: mapStatus(providerStatus),
|
|
455
|
+
...providerStatus === void 0 ? {} : { providerStatus },
|
|
456
|
+
service: mapService(raw["protocol"]),
|
|
457
|
+
...sentAt === void 0 ? {} : { sentAt },
|
|
458
|
+
...deliveredAt === void 0 ? {} : { deliveredAt },
|
|
459
|
+
...error === void 0 ? {} : { error },
|
|
460
|
+
raw
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
const conversations = {
|
|
465
|
+
async open(input) {
|
|
466
|
+
const id = conversationId(input);
|
|
467
|
+
return {
|
|
468
|
+
providerConversationId: id,
|
|
469
|
+
participants: input.participants,
|
|
470
|
+
raw: { chat_id: id, resolved: false }
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
async get(id) {
|
|
474
|
+
const raw = await request(`/chats/${encodePath(id)}`, {}, { notFoundNull: true });
|
|
475
|
+
if (raw === null) return null;
|
|
476
|
+
const participantIdentifiers = participantValues(raw["participants"]);
|
|
477
|
+
const groupMembers = raw["is_group"] === true || id.startsWith("grp_") ? await request(
|
|
478
|
+
`/groups/${encodePath(id)}/members?limit=100`,
|
|
479
|
+
{},
|
|
480
|
+
{ notFoundNull: true }
|
|
481
|
+
) : null;
|
|
482
|
+
const groupMemberIdentifiers = groupMembers === null ? [] : participantValues(groupMembers["members"]);
|
|
483
|
+
const parsedContact = JsonObjectSchema.safeParse(raw["contact"]);
|
|
484
|
+
const contact = parsedContact.success ? parsedContact.data : void 0;
|
|
485
|
+
const contactIdentifier = contact === void 0 ? void 0 : OptionalNonEmptyStringSchema.parse(contact["identifier"]);
|
|
486
|
+
const participants = groupMemberIdentifiers.length > 0 ? groupMemberIdentifiers.map(address) : participantIdentifiers.length > 0 ? participantIdentifiers.map(address) : contactIdentifier === void 0 ? [address(OptionalNonEmptyStringSchema.parse(raw["id"]) ?? id)] : [address(contactIdentifier)];
|
|
487
|
+
const createdAt = dateValue(raw["first_message_time"]);
|
|
488
|
+
return {
|
|
489
|
+
providerConversationId: OptionalNonEmptyStringSchema.parse(raw["id"]) ?? id,
|
|
490
|
+
participants,
|
|
491
|
+
...createdAt === void 0 ? {} : { createdAt },
|
|
492
|
+
raw
|
|
493
|
+
};
|
|
494
|
+
},
|
|
495
|
+
async markRead(id) {
|
|
496
|
+
await request(`/chats/${encodePath(id)}/read`, { method: "POST" });
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
const react = async (action, input) => {
|
|
500
|
+
await request(
|
|
501
|
+
`/chats/${encodePath(input.conversationId)}/messages/${encodePath(input.messageId)}/reactions`,
|
|
502
|
+
{
|
|
503
|
+
method: "POST",
|
|
504
|
+
body: JSON.stringify({ reaction: `${action}${input.reaction}` })
|
|
505
|
+
}
|
|
506
|
+
);
|
|
507
|
+
};
|
|
508
|
+
return defineProvider({
|
|
509
|
+
name: "blooio",
|
|
510
|
+
capabilities: BLOOIO_CAPABILITIES,
|
|
511
|
+
messages,
|
|
512
|
+
conversations,
|
|
513
|
+
reactions: {
|
|
514
|
+
async add(input) {
|
|
515
|
+
await react("+", input);
|
|
516
|
+
},
|
|
517
|
+
async remove(input) {
|
|
518
|
+
await react("-", input);
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
typing: {
|
|
522
|
+
async start(id) {
|
|
523
|
+
await request(`/chats/${encodePath(id)}/typing`, { method: "POST" });
|
|
524
|
+
},
|
|
525
|
+
async stop(id) {
|
|
526
|
+
await request(`/chats/${encodePath(id)}/typing`, { method: "DELETE" });
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
webhooks: {
|
|
530
|
+
async verify(webhookRequest) {
|
|
531
|
+
if (config.webhookSecret === void 0) return false;
|
|
532
|
+
const header = webhookRequest.headers.get("x-blooio-signature");
|
|
533
|
+
if (header === null) return false;
|
|
534
|
+
const parsed = parseSignature(header);
|
|
535
|
+
if (parsed === void 0) return false;
|
|
536
|
+
const timestamp = Number(parsed.timestamp);
|
|
537
|
+
if (!Number.isFinite(timestamp)) return false;
|
|
538
|
+
const age = Math.abs(Math.floor(Date.now() / 1e3) - timestamp);
|
|
539
|
+
if (age > DEFAULT_WEBHOOK_TOLERANCE_SECONDS) return false;
|
|
540
|
+
const rawBody = await webhookRequest.text();
|
|
541
|
+
const expected = await hmacSha256(config.webhookSecret, `${parsed.timestamp}.${rawBody}`);
|
|
542
|
+
return parsed.signatures.some(
|
|
543
|
+
(signature) => constantTimeEqual(expected, signature.toLowerCase())
|
|
544
|
+
);
|
|
545
|
+
},
|
|
546
|
+
async parse(webhookRequest) {
|
|
547
|
+
const body = await webhookRequest.json();
|
|
548
|
+
const values = Array.isArray(body) ? body : [body];
|
|
549
|
+
return values.flatMap((value) => {
|
|
550
|
+
const parsedValue = JsonObjectSchema.safeParse(value);
|
|
551
|
+
if (!parsedValue.success) return [];
|
|
552
|
+
const event = mapWebhookEvent(parsedValue.data);
|
|
553
|
+
return event === void 0 ? [] : [event];
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
numbers: {
|
|
558
|
+
async list() {
|
|
559
|
+
const response = await request("/me/numbers");
|
|
560
|
+
const values = Array.isArray(response?.["numbers"]) ? response["numbers"] : [];
|
|
561
|
+
return values.flatMap((value) => {
|
|
562
|
+
const parsedValue = JsonObjectSchema.safeParse(value);
|
|
563
|
+
if (!parsedValue.success) return [];
|
|
564
|
+
const item = parsedValue.data;
|
|
565
|
+
const phoneNumber = OptionalNonEmptyStringSchema.parse(item["phone_number"]);
|
|
566
|
+
if (phoneNumber === void 0) return [];
|
|
567
|
+
const lastActive = dateValue(item["last_active"]);
|
|
568
|
+
const parsedPlanKind = PlanKindSchema.safeParse(item["plan_kind"]);
|
|
569
|
+
return [
|
|
570
|
+
{
|
|
571
|
+
phoneNumber,
|
|
572
|
+
active: item["is_active"] === true,
|
|
573
|
+
...lastActive === void 0 ? {} : { lastActive },
|
|
574
|
+
...parsedPlanKind.success ? { planKind: parsedPlanKind.data } : {},
|
|
575
|
+
raw: item
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export { BLOOIO_CAPABILITIES, blooio };
|
|
585
|
+
//# sourceMappingURL=index.js.map
|
|
586
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["conversationId"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\nimport type {\n IMessageAddress,\n IMessageAttachment,\n IMessageProvider,\n IMessageReaction,\n IMessageService,\n IMessageStatus,\n MessageLocator,\n OpenConversationInput,\n ProviderConversation,\n ProviderConversations,\n ProviderEvent,\n ProviderMessage,\n ProviderMessages,\n ProviderReactions,\n ProviderSentMessage,\n ProviderTyping,\n ProviderWebhooks,\n} from 'imessage-sdk';\nimport {\n AmbiguousDeliveryError,\n AuthenticationError,\n ConflictError,\n defineProvider,\n IMessageSDKError,\n NotFoundError,\n ProviderUnavailableError,\n RateLimitError,\n ValidationError,\n} from 'imessage-sdk';\n\nconst DEFAULT_BASE_URL = 'https://api.blooio.com/v2/api';\nconst DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;\n\nexport const BLOOIO_CAPABILITIES = {\n messages: {\n text: true,\n attachments: true,\n replies: true,\n get: true,\n edit: false,\n delete: false,\n },\n conversations: {\n direct: true,\n groups: false,\n get: true,\n markRead: true,\n },\n interactions: {\n reactions: true,\n typingStart: true,\n typingStop: true,\n readReceipts: true,\n },\n events: {\n webhooks: true,\n stream: false,\n },\n} as const;\n\nexport interface BlooioOptions {\n readonly apiKey?: string;\n readonly sender?: IMessageAddress;\n readonly webhookSecret?: string;\n readonly baseUrl?: string;\n}\n\nexport interface BlooioMessageStatus {\n readonly messageId: string;\n readonly conversationId: string;\n readonly status: IMessageStatus;\n readonly providerStatus?: string;\n readonly service: IMessageService;\n readonly sentAt?: Date;\n readonly deliveredAt?: Date;\n readonly error?: string;\n readonly raw: unknown;\n}\n\nexport type BlooioPlanKind = 'shared' | 'dedicated' | 'inbound' | 'trial' | '2fa';\n\nexport interface BlooioNumber {\n readonly phoneNumber: string;\n readonly active: boolean;\n readonly lastActive?: Date;\n readonly planKind?: BlooioPlanKind;\n readonly raw: unknown;\n}\n\nexport interface BlooioNumbers {\n list(): Promise<readonly BlooioNumber[]>;\n}\n\nexport interface BlooioMessages extends ProviderMessages {\n get(message: MessageLocator): Promise<ProviderMessage | null>;\n getStatus(message: MessageLocator): Promise<BlooioMessageStatus | null>;\n}\n\nexport interface BlooioConversations extends ProviderConversations {\n get(conversationId: string): Promise<ProviderConversation | null>;\n markRead(conversationId: string): Promise<void>;\n}\n\nexport interface BlooioProvider extends IMessageProvider<'blooio', typeof BLOOIO_CAPABILITIES> {\n readonly messages: BlooioMessages;\n readonly conversations: BlooioConversations;\n readonly reactions: ProviderReactions;\n readonly typing: Required<ProviderTyping>;\n readonly webhooks: ProviderWebhooks;\n readonly numbers: BlooioNumbers;\n}\n\nconst JsonObjectSchema = z.record(z.string(), z.unknown());\ntype JsonObject = z.infer<typeof JsonObjectSchema>;\nconst OptionalNonEmptyStringSchema = z.string().min(1).optional().catch(undefined);\nconst OptionalNumberSchema = z.number().optional().catch(undefined);\nconst ReactionSchema = z.enum(['love', 'like', 'dislike', 'laugh', 'emphasize', 'question']);\nconst PlanKindSchema = z.enum(['shared', 'dedicated', 'inbound', 'trial', '2fa']);\n\nfunction dateValue(value: unknown): Date | undefined {\n const number = OptionalNumberSchema.parse(value);\n if (number !== undefined) return new Date(number);\n const string = OptionalNonEmptyStringSchema.parse(value);\n if (string === undefined) return undefined;\n const date = new Date(string);\n return Number.isNaN(date.valueOf()) ? undefined : date;\n}\n\nfunction address(value: string): IMessageAddress {\n return {\n kind: value.includes('@') ? 'email' : 'phone',\n value,\n };\n}\n\nfunction requireAddress(value: unknown, fallback = 'unknown'): IMessageAddress {\n return address(OptionalNonEmptyStringSchema.parse(value) ?? fallback);\n}\n\nfunction mapStatus(value: unknown): IMessageStatus {\n switch (typeof value === 'string' ? value.toLowerCase() : '') {\n case 'queued':\n case 'pending':\n return 'pending';\n case 'accepted':\n return 'accepted';\n case 'sent':\n return 'sent';\n case 'delivered':\n return 'delivered';\n case 'read':\n return 'read';\n case 'failed':\n case 'cancelled':\n case 'cancellation_requested':\n case 'error':\n return 'failed';\n default:\n return 'pending';\n }\n}\n\nfunction mapService(value: unknown): IMessageService {\n switch (typeof value === 'string' ? value.toLowerCase() : '') {\n case 'imessage':\n return 'imessage';\n case 'sms':\n return 'sms';\n case 'rcs':\n return 'rcs';\n default:\n return 'unknown';\n }\n}\n\nfunction attachmentKind(contentType: string | undefined): IMessageAttachment['kind'] {\n if (contentType?.startsWith('image/')) return 'image';\n if (contentType?.startsWith('video/')) return 'video';\n return 'file';\n}\n\nfunction mapAttachment(value: unknown): IMessageAttachment {\n if (typeof value === 'string') {\n return { kind: 'file', url: value, raw: value };\n }\n\n const parsedItem = JsonObjectSchema.safeParse(value);\n const item = parsedItem.success ? parsedItem.data : {};\n const contentType =\n OptionalNonEmptyStringSchema.parse(item['content_type']) ??\n OptionalNonEmptyStringSchema.parse(item['contentType']) ??\n OptionalNonEmptyStringSchema.parse(item['mime_type']);\n const id = OptionalNonEmptyStringSchema.parse(item['id']);\n const url = OptionalNonEmptyStringSchema.parse(item['url']);\n const filename = OptionalNonEmptyStringSchema.parse(item['name']);\n const size = OptionalNumberSchema.parse(item['size']);\n const result: IMessageAttachment = {\n kind: attachmentKind(contentType),\n raw: value,\n ...(id === undefined ? {} : { id }),\n ...(url === undefined ? {} : { url }),\n ...(filename === undefined ? {} : { filename }),\n ...(contentType === undefined ? {} : { contentType }),\n ...(size === undefined ? {} : { size }),\n };\n return result;\n}\n\nfunction participantValues(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.flatMap((participant) => {\n if (typeof participant === 'string') return [participant];\n const parsedParticipant = JsonObjectSchema.safeParse(participant);\n if (!parsedParticipant.success) return [];\n const identifier = OptionalNonEmptyStringSchema.parse(parsedParticipant.data['identifier']);\n return identifier === undefined ? [] : [identifier];\n });\n}\n\nfunction mapProviderMessage(\n raw: JsonObject,\n fallbackConversationId: string,\n configuredSender?: IMessageAddress,\n): ProviderMessage {\n const direction = raw['direction'] === 'inbound' ? 'inbound' : 'outbound';\n const conversationId =\n OptionalNonEmptyStringSchema.parse(raw['chat_id']) ??\n OptionalNonEmptyStringSchema.parse(raw['group_id']) ??\n OptionalNonEmptyStringSchema.parse(raw['external_id']) ??\n fallbackConversationId;\n const internal = OptionalNonEmptyStringSchema.parse(raw['internal_id']);\n const parsedContact = JsonObjectSchema.safeParse(raw['contact']);\n const external =\n OptionalNonEmptyStringSchema.parse(raw['sender']) ??\n (parsedContact.success\n ? OptionalNonEmptyStringSchema.parse(parsedContact.data['identifier'])\n : undefined) ??\n OptionalNonEmptyStringSchema.parse(raw['external_id']) ??\n conversationId;\n const participants = participantValues(raw['participants']);\n const sender =\n direction === 'inbound'\n ? address(external)\n : internal === undefined\n ? (configuredSender ?? address('unknown'))\n : address(internal);\n const recipients =\n direction === 'inbound'\n ? [internal === undefined ? (configuredSender ?? address('unknown')) : address(internal)]\n : (participants.length > 0 ? participants : [external]).map(address);\n const providerStatus = OptionalNonEmptyStringSchema.parse(raw['status']);\n const parsedReply = JsonObjectSchema.safeParse(raw['reply_to']);\n const reply = parsedReply.success ? parsedReply.data : undefined;\n const replyMessageId =\n reply === undefined\n ? undefined\n : (OptionalNonEmptyStringSchema.parse(reply['message_id']) ??\n OptionalNonEmptyStringSchema.parse(reply['guid']));\n const sentAt = dateValue(raw['time_sent'] ?? raw['sent_at']);\n const deliveredAt = dateValue(raw['time_delivered'] ?? raw['delivered_at']);\n const readAt = dateValue(raw['read_at']);\n const partIndex = OptionalNumberSchema.parse(reply?.['part_index']);\n\n return {\n providerMessageId:\n OptionalNonEmptyStringSchema.parse(raw['message_id']) ??\n OptionalNonEmptyStringSchema.parse(raw['id']) ??\n 'unknown',\n conversationId,\n direction,\n sender,\n recipients,\n text: typeof raw['text'] === 'string' ? raw['text'] : '',\n attachments: Array.isArray(raw['attachments']) ? raw['attachments'].map(mapAttachment) : [],\n ...(replyMessageId === undefined\n ? {}\n : {\n replyTo: {\n messageId: replyMessageId,\n ...(partIndex === undefined ? {} : { partIndex }),\n },\n }),\n service: mapService(raw['protocol']),\n status: mapStatus(providerStatus),\n ...(providerStatus === undefined ? {} : { providerStatus }),\n createdAt: sentAt ?? dateValue(raw['received_at']) ?? dateValue(raw['timestamp']) ?? new Date(),\n ...(sentAt === undefined ? {} : { sentAt }),\n ...(deliveredAt === undefined ? {} : { deliveredAt }),\n ...(readAt === undefined ? {} : { readAt }),\n raw,\n };\n}\n\nfunction encodePath(value: string): string {\n return encodeURIComponent(value);\n}\n\nfunction conversationId(input: OpenConversationInput): string {\n return input.participants.map((participant) => participant.value).join(',');\n}\n\nfunction parseSignature(\n header: string,\n): { readonly timestamp: string; readonly signatures: readonly string[] } | undefined {\n let timestamp: string | undefined;\n const signatures: string[] = [];\n for (const component of header.split(',')) {\n const separator = component.indexOf('=');\n if (separator < 0) continue;\n const key = component.slice(0, separator).trim();\n const value = component.slice(separator + 1).trim();\n if (key === 't') timestamp = value;\n if (key === 'v1') signatures.push(value);\n }\n return timestamp === undefined || signatures.length === 0 ? undefined : { timestamp, signatures };\n}\n\nfunction constantTimeEqual(left: string, right: string): boolean {\n if (left.length !== right.length) return false;\n let result = 0;\n for (let index = 0; index < left.length; index += 1) {\n result |= left.charCodeAt(index) ^ right.charCodeAt(index);\n }\n return result === 0;\n}\n\nasync function hmacSha256(secret: string, value: string): Promise<string> {\n const encoder = new TextEncoder();\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(value));\n return Array.from(new Uint8Array(signature), (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n}\n\nfunction eventStatus(event: string, rawStatus: unknown): IMessageStatus {\n switch (event) {\n case 'message.received':\n return 'delivered';\n case 'message.sent':\n return 'sent';\n case 'message.delivered':\n return 'delivered';\n case 'message.read':\n return 'read';\n case 'message.failed':\n return 'failed';\n default:\n return mapStatus(rawStatus);\n }\n}\n\nfunction mapWebhookEvent(raw: JsonObject): ProviderEvent | undefined {\n const event = OptionalNonEmptyStringSchema.parse(raw['event']);\n if (event === undefined) return undefined;\n const timestamp = dateValue(raw['timestamp']) ?? new Date();\n const messageId = OptionalNonEmptyStringSchema.parse(raw['message_id']) ?? 'unknown';\n const conversation =\n OptionalNonEmptyStringSchema.parse(raw['group_id']) ??\n OptionalNonEmptyStringSchema.parse(raw['external_id']) ??\n 'unknown';\n const id = `${event}:${messageId}:${timestamp.valueOf()}`;\n\n if (event === 'message.reaction') {\n const parsedReaction = ReactionSchema.safeParse(raw['reaction']);\n if (!parsedReaction.success) return undefined;\n const reaction = parsedReaction.data;\n const partIndex = OptionalNumberSchema.parse(raw['part_index']);\n return {\n id,\n type: raw['action'] === 'remove' ? 'reaction.removed' : 'reaction.added',\n timestamp,\n conversationId: conversation,\n messageId,\n actor: requireAddress(raw['sender'] ?? raw['external_id']),\n reaction,\n ...(partIndex === undefined ? {} : { partIndex }),\n raw,\n };\n }\n\n if (event === 'typing.started' || event === 'typing.stopped') {\n const actorValue = OptionalNonEmptyStringSchema.parse(raw['sender']);\n return {\n id,\n type: event,\n timestamp,\n conversationId: conversation,\n ...(actorValue === undefined ? {} : { actor: address(actorValue) }),\n raw,\n };\n }\n\n if (\n event !== 'message.received' &&\n event !== 'message.sent' &&\n event !== 'message.delivered' &&\n event !== 'message.read' &&\n event !== 'message.failed'\n ) {\n return undefined;\n }\n\n return {\n id,\n type: event,\n timestamp,\n message: mapProviderMessage(\n {\n ...raw,\n direction: event === 'message.received' ? 'inbound' : 'outbound',\n status: eventStatus(event, raw['status']),\n },\n conversation,\n ),\n raw,\n };\n}\n\n/** Creates a Blooio v2 provider. No initialization call is required. */\nexport function blooio(options: BlooioOptions = {}): BlooioProvider {\n const apiKey = options.apiKey ?? process.env['BLOOIO_API_KEY'];\n const webhookSecret = options.webhookSecret ?? process.env['BLOOIO_WEBHOOK_SECRET'];\n const configuredSender = process.env['BLOOIO_FROM_NUMBER'];\n const sender =\n options.sender ?? (configuredSender === undefined ? undefined : address(configuredSender));\n const config: BlooioOptions = {\n ...options,\n ...(apiKey === undefined ? {} : { apiKey }),\n ...(webhookSecret === undefined ? {} : { webhookSecret }),\n ...(sender === undefined ? {} : { sender }),\n };\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n\n const request = async <T>(\n path: string,\n init: RequestInit = {},\n requestOptions: { readonly send?: boolean; readonly notFoundNull?: boolean } = {},\n ): Promise<T | null> => {\n if (config.apiKey === undefined || config.apiKey.length === 0) {\n throw new AuthenticationError('A Blooio API key is required.', {\n provider: 'blooio',\n code: 'missing_api_key',\n });\n }\n\n let response: Response;\n try {\n response = await fetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n authorization: `Bearer ${config.apiKey}`,\n accept: 'application/json',\n ...(init.body === undefined ? {} : { 'content-type': 'application/json' }),\n ...init.headers,\n },\n });\n } catch (cause) {\n if (requestOptions.send === true) {\n throw new AmbiguousDeliveryError(\n 'The Blooio send result is unknown; retry only with the same idempotency key.',\n { provider: 'blooio', code: 'ambiguous_delivery', retryable: true, raw: cause },\n );\n }\n throw new ProviderUnavailableError('Could not reach Blooio.', {\n provider: 'blooio',\n code: 'provider_unavailable',\n retryable: true,\n raw: cause,\n });\n }\n\n const rawText = await response.text();\n let raw: unknown;\n try {\n raw = rawText.length === 0 ? undefined : JSON.parse(rawText);\n } catch {\n raw = rawText;\n }\n\n if (response.ok) return (raw ?? {}) as T;\n if (response.status === 404 && requestOptions.notFoundNull === true) return null;\n\n const parsedBody = JsonObjectSchema.safeParse(raw);\n const body = parsedBody.success ? parsedBody.data : {};\n const message =\n OptionalNonEmptyStringSchema.parse(body['message']) ??\n OptionalNonEmptyStringSchema.parse(body['error']) ??\n `Blooio request failed with HTTP ${response.status}.`;\n const code = OptionalNonEmptyStringSchema.parse(body['code']) ?? `http_${response.status}`;\n const common = {\n provider: 'blooio',\n code,\n statusCode: response.status,\n raw,\n } as const;\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError(message, common);\n }\n if (response.status === 404) throw new NotFoundError(message, common);\n if (response.status === 409) throw new ConflictError(message, common);\n if (response.status === 429) {\n const retryAfterHeader = response.headers.get('retry-after');\n const retryAfter = retryAfterHeader === null ? undefined : Number(retryAfterHeader);\n throw new RateLimitError(message, {\n ...common,\n retryable: true,\n ...(retryAfter !== undefined && Number.isFinite(retryAfter) ? { retryAfter } : {}),\n });\n }\n if (response.status >= 500) {\n throw new ProviderUnavailableError(message, {\n ...common,\n retryable: true,\n });\n }\n throw new IMessageSDKError(message, common);\n };\n\n const messages: BlooioMessages = {\n async send(input): Promise<ProviderSentMessage> {\n const destinations: readonly IMessageAddress[] =\n input.to === undefined ? [] : 'value' in input.to ? [input.to] : input.to;\n const chatId =\n input.conversationId ?? destinations.map((recipient) => recipient.value).join(',');\n const attachments = input.attachments?.map((attachment) => {\n if (attachment.source.type !== 'url') {\n throw new ValidationError(\n 'Blooio v2 requires attachments to use a publicly accessible URL.',\n { provider: 'blooio', code: 'attachment_url_required' },\n );\n }\n return attachment.filename === undefined\n ? attachment.source.url\n : { url: attachment.source.url, name: attachment.filename };\n });\n const body = {\n ...(input.text === undefined ? {} : { text: input.text }),\n ...(attachments === undefined || attachments.length === 0 ? {} : { attachments }),\n ...(config.sender === undefined ? {} : { from_number: config.sender.value }),\n ...(input.replyTo === undefined\n ? {}\n : {\n reply_to: {\n message_id: input.replyTo.messageId,\n ...(input.replyTo.partIndex === undefined\n ? {}\n : { part_index: input.replyTo.partIndex }),\n },\n }),\n };\n const raw = await request<JsonObject>(\n `/chats/${encodePath(chatId)}/messages`,\n {\n method: 'POST',\n ...(input.idempotencyKey === undefined\n ? {}\n : { headers: { 'idempotency-key': input.idempotencyKey } }),\n body: JSON.stringify(body),\n },\n { send: true },\n );\n const response = raw ?? {};\n const providerMessageId = OptionalNonEmptyStringSchema.parse(response['message_id']);\n if (providerMessageId === undefined) {\n throw new IMessageSDKError('Blooio did not return a message ID.', {\n provider: 'blooio',\n code: 'invalid_provider_response',\n raw: response,\n });\n }\n const actualConversationId =\n OptionalNonEmptyStringSchema.parse(response['group_id']) ?? chatId;\n const providerStatus = OptionalNonEmptyStringSchema.parse(response['status']);\n const recipients =\n input.to === undefined\n ? participantValues(response['participants']).map(address)\n : destinations;\n return {\n providerMessageId,\n conversationId: actualConversationId,\n direction: 'outbound',\n sender: config.sender ?? address('unknown'),\n recipients,\n text: input.text ?? '',\n attachments:\n input.attachments?.map((attachment): IMessageAttachment => {\n const url = attachment.source.type === 'url' ? attachment.source.url : undefined;\n return {\n kind: attachment.kind,\n ...(url === undefined ? {} : { url }),\n ...(attachment.filename === undefined ? {} : { filename: attachment.filename }),\n ...(attachment.contentType === undefined\n ? {}\n : { contentType: attachment.contentType }),\n raw: attachment,\n };\n }) ?? [],\n ...(input.replyTo === undefined ? {} : { replyTo: input.replyTo }),\n service: 'imessage',\n status: mapStatus(response['status']),\n ...(providerStatus === undefined ? {} : { providerStatus }),\n createdAt: new Date(),\n raw: response,\n };\n },\n\n async get(message) {\n const raw = await request<JsonObject>(\n `/chats/${encodePath(message.conversationId)}/messages/${encodePath(message.messageId)}`,\n {},\n { notFoundNull: true },\n );\n return raw === null ? null : mapProviderMessage(raw, message.conversationId, config.sender);\n },\n\n async getStatus(message) {\n const raw = await request<JsonObject>(\n `/chats/${encodePath(message.conversationId)}/messages/${encodePath(message.messageId)}/status`,\n {},\n { notFoundNull: true },\n );\n if (raw === null) return null;\n const providerStatus = OptionalNonEmptyStringSchema.parse(raw['status']);\n const sentAt = dateValue(raw['time_sent']);\n const deliveredAt = dateValue(raw['time_delivered']);\n const error = OptionalNonEmptyStringSchema.parse(raw['error']);\n return {\n messageId: OptionalNonEmptyStringSchema.parse(raw['message_id']) ?? message.messageId,\n conversationId:\n OptionalNonEmptyStringSchema.parse(raw['chat_id']) ?? message.conversationId,\n status: mapStatus(providerStatus),\n ...(providerStatus === undefined ? {} : { providerStatus }),\n service: mapService(raw['protocol']),\n ...(sentAt === undefined ? {} : { sentAt }),\n ...(deliveredAt === undefined ? {} : { deliveredAt }),\n ...(error === undefined ? {} : { error }),\n raw,\n };\n },\n };\n\n const conversations: BlooioConversations = {\n async open(input) {\n const id = conversationId(input);\n return {\n providerConversationId: id,\n participants: input.participants,\n raw: { chat_id: id, resolved: false },\n };\n },\n\n async get(id) {\n const raw = await request<JsonObject>(`/chats/${encodePath(id)}`, {}, { notFoundNull: true });\n if (raw === null) return null;\n const participantIdentifiers = participantValues(raw['participants']);\n const groupMembers =\n raw['is_group'] === true || id.startsWith('grp_')\n ? await request<JsonObject>(\n `/groups/${encodePath(id)}/members?limit=100`,\n {},\n { notFoundNull: true },\n )\n : null;\n const groupMemberIdentifiers =\n groupMembers === null ? [] : participantValues(groupMembers['members']);\n const parsedContact = JsonObjectSchema.safeParse(raw['contact']);\n const contact = parsedContact.success ? parsedContact.data : undefined;\n const contactIdentifier =\n contact === undefined\n ? undefined\n : OptionalNonEmptyStringSchema.parse(contact['identifier']);\n const participants =\n groupMemberIdentifiers.length > 0\n ? groupMemberIdentifiers.map(address)\n : participantIdentifiers.length > 0\n ? participantIdentifiers.map(address)\n : contactIdentifier === undefined\n ? [address(OptionalNonEmptyStringSchema.parse(raw['id']) ?? id)]\n : [address(contactIdentifier)];\n const createdAt = dateValue(raw['first_message_time']);\n return {\n providerConversationId: OptionalNonEmptyStringSchema.parse(raw['id']) ?? id,\n participants,\n ...(createdAt === undefined ? {} : { createdAt }),\n raw,\n };\n },\n\n async markRead(id) {\n await request(`/chats/${encodePath(id)}/read`, { method: 'POST' });\n },\n };\n\n const react = async (\n action: '+' | '-',\n input: {\n readonly conversationId: string;\n readonly messageId: string;\n readonly reaction: IMessageReaction;\n },\n ): Promise<void> => {\n await request(\n `/chats/${encodePath(input.conversationId)}/messages/${encodePath(input.messageId)}/reactions`,\n {\n method: 'POST',\n body: JSON.stringify({ reaction: `${action}${input.reaction}` }),\n },\n );\n };\n\n return defineProvider({\n name: 'blooio',\n capabilities: BLOOIO_CAPABILITIES,\n messages,\n conversations,\n reactions: {\n async add(input) {\n await react('+', input);\n },\n async remove(input) {\n await react('-', input);\n },\n },\n typing: {\n async start(id) {\n await request(`/chats/${encodePath(id)}/typing`, { method: 'POST' });\n },\n async stop(id) {\n await request(`/chats/${encodePath(id)}/typing`, { method: 'DELETE' });\n },\n },\n webhooks: {\n async verify(webhookRequest) {\n if (config.webhookSecret === undefined) return false;\n const header = webhookRequest.headers.get('x-blooio-signature');\n if (header === null) return false;\n const parsed = parseSignature(header);\n if (parsed === undefined) return false;\n const timestamp = Number(parsed.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const age = Math.abs(Math.floor(Date.now() / 1000) - timestamp);\n if (age > DEFAULT_WEBHOOK_TOLERANCE_SECONDS) return false;\n const rawBody = await webhookRequest.text();\n const expected = await hmacSha256(config.webhookSecret, `${parsed.timestamp}.${rawBody}`);\n return parsed.signatures.some((signature) =>\n constantTimeEqual(expected, signature.toLowerCase()),\n );\n },\n async parse(webhookRequest) {\n const body: unknown = await webhookRequest.json();\n const values = Array.isArray(body) ? body : [body];\n return values.flatMap((value) => {\n const parsedValue = JsonObjectSchema.safeParse(value);\n if (!parsedValue.success) return [];\n const event = mapWebhookEvent(parsedValue.data);\n return event === undefined ? [] : [event];\n });\n },\n },\n numbers: {\n async list() {\n const response = await request<JsonObject>('/me/numbers');\n const values = Array.isArray(response?.['numbers']) ? response['numbers'] : [];\n return values.flatMap((value): BlooioNumber[] => {\n const parsedValue = JsonObjectSchema.safeParse(value);\n if (!parsedValue.success) return [];\n const item = parsedValue.data;\n const phoneNumber = OptionalNonEmptyStringSchema.parse(item['phone_number']);\n if (phoneNumber === undefined) return [];\n const lastActive = dateValue(item['last_active']);\n const parsedPlanKind = PlanKindSchema.safeParse(item['plan_kind']);\n return [\n {\n phoneNumber,\n active: item['is_active'] === true,\n ...(lastActive === undefined ? {} : { lastActive }),\n ...(parsedPlanKind.success ? { planKind: parsedPlanKind.data } : {}),\n raw: item,\n },\n ];\n });\n },\n },\n });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imessage-sdk/blooio",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Blooio provider for imessage-sdk",
|
|
5
|
+
"author": "jmisilo",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"imessage",
|
|
8
|
+
"blooio",
|
|
9
|
+
"messaging",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/jmisilo/imessage-sdk.git",
|
|
15
|
+
"directory": "packages/providers/blooio"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/jmisilo/imessage-sdk/tree/main/packages/providers/blooio#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/jmisilo/imessage-sdk/issues"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.js",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"provenance": true
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"zod": "^4.4.3",
|
|
48
|
+
"imessage-sdk": "^0.1.0-beta.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"dev": "tsup --watch",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:integration": "BLOOIO_LIVE_TEST=1 vitest run test/blooio.integration.test.ts",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --project tsconfig.json --noEmit"
|
|
57
|
+
}
|
|
58
|
+
}
|