novapsis 1.0.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 +9 -0
- package/README.md +211 -0
- package/dist/index.cjs +354 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +331 -0
- package/dist/index.d.ts +333 -0
- package/dist/index.mjs +339 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var NovapsisAPIError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
status;
|
|
7
|
+
details;
|
|
8
|
+
constructor(message, code = "API_ERROR", status = 500, details) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "NovapsisAPIError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.details = details;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var HttpClient = class {
|
|
17
|
+
apiKey;
|
|
18
|
+
baseUrl;
|
|
19
|
+
timeoutMs;
|
|
20
|
+
maxRetries;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.apiKey = options.apiKey;
|
|
23
|
+
this.baseUrl = (options.baseUrl || "https://app.novapsis.com").replace(/\/+$/, "");
|
|
24
|
+
this.timeoutMs = options.timeoutMs || 15e3;
|
|
25
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
26
|
+
}
|
|
27
|
+
async request(method, path, body, query) {
|
|
28
|
+
let url = `${this.baseUrl}/api/v1${path.startsWith("/") ? path : `/${path}`}`;
|
|
29
|
+
if (query && Object.keys(query).length > 0) {
|
|
30
|
+
const qs = new URLSearchParams(query).toString();
|
|
31
|
+
url += `?${qs}`;
|
|
32
|
+
}
|
|
33
|
+
let lastError = null;
|
|
34
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(url, {
|
|
39
|
+
method,
|
|
40
|
+
headers: {
|
|
41
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"User-Agent": "Novapsis-SDK-Node/1.0.0"
|
|
44
|
+
},
|
|
45
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
46
|
+
signal: controller.signal
|
|
47
|
+
});
|
|
48
|
+
clearTimeout(timeoutId);
|
|
49
|
+
const data = await response.json().catch(() => ({
|
|
50
|
+
success: false,
|
|
51
|
+
error: { code: "INVALID_JSON_RESPONSE", message: `HTTP ${response.status} Sin cuerpo JSON v\xE1lido.` }
|
|
52
|
+
}));
|
|
53
|
+
if (!response.ok || !data.success) {
|
|
54
|
+
throw new NovapsisAPIError(
|
|
55
|
+
data.error?.message || `Error en petici\xF3n HTTP ${response.status}`,
|
|
56
|
+
data.error?.code || `HTTP_${response.status}`,
|
|
57
|
+
response.status,
|
|
58
|
+
data.error?.details
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return data.data;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
clearTimeout(timeoutId);
|
|
64
|
+
lastError = err;
|
|
65
|
+
if (err instanceof NovapsisAPIError && err.status >= 400 && err.status < 500) {
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
if (attempt === this.maxRetries) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 300 * Math.pow(3, attempt)));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw lastError || new NovapsisAPIError("Error de red al comunicarse con Novapsis API", "NETWORK_ERROR", 0);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var WhatsAppService = class {
|
|
78
|
+
constructor(http) {
|
|
79
|
+
this.http = http;
|
|
80
|
+
}
|
|
81
|
+
http;
|
|
82
|
+
/** Envía un mensaje de texto plano */
|
|
83
|
+
async sendText(options) {
|
|
84
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
85
|
+
recipient: options.to,
|
|
86
|
+
connection_id: options.connectionId,
|
|
87
|
+
type: "text",
|
|
88
|
+
text: options.text
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** Envía una imagen o fotografía */
|
|
92
|
+
async sendImage(options) {
|
|
93
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
94
|
+
recipient: options.to,
|
|
95
|
+
connection_id: options.connectionId,
|
|
96
|
+
type: "image",
|
|
97
|
+
media_url: options.imageUrl,
|
|
98
|
+
caption: options.caption
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** Envía un archivo de audio o nota de voz */
|
|
102
|
+
async sendAudio(options) {
|
|
103
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
104
|
+
recipient: options.to,
|
|
105
|
+
connection_id: options.connectionId,
|
|
106
|
+
type: "audio",
|
|
107
|
+
media_url: options.audioUrl
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/** Envía un documento (PDF, Word, Excel, etc.) con nombre de archivo personalizado */
|
|
111
|
+
async sendDocument(options) {
|
|
112
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
113
|
+
recipient: options.to,
|
|
114
|
+
connection_id: options.connectionId,
|
|
115
|
+
type: "document",
|
|
116
|
+
media_url: options.documentUrl,
|
|
117
|
+
filename: options.filename,
|
|
118
|
+
caption: options.caption
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** Envía un video */
|
|
122
|
+
async sendVideo(options) {
|
|
123
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
124
|
+
recipient: options.to,
|
|
125
|
+
connection_id: options.connectionId,
|
|
126
|
+
type: "video",
|
|
127
|
+
media_url: options.videoUrl,
|
|
128
|
+
caption: options.caption
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Envía una ubicación con coordenadas y dirección en el mapa */
|
|
132
|
+
async sendLocation(options) {
|
|
133
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
134
|
+
recipient: options.to,
|
|
135
|
+
connection_id: options.connectionId,
|
|
136
|
+
type: "location",
|
|
137
|
+
latitude: options.latitude,
|
|
138
|
+
longitude: options.longitude,
|
|
139
|
+
location_name: options.name,
|
|
140
|
+
location_address: options.address
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/** Envía una tarjeta de contacto (vCard) interactiva */
|
|
144
|
+
async sendContact(options) {
|
|
145
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
146
|
+
recipient: options.to,
|
|
147
|
+
connection_id: options.connectionId,
|
|
148
|
+
type: "contacts",
|
|
149
|
+
contact_name: options.name,
|
|
150
|
+
contact_phone: options.phone
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/** Envía una plantilla oficial aprobada por Meta con variables dinámicas */
|
|
154
|
+
async sendTemplate(options) {
|
|
155
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
156
|
+
recipient: options.to,
|
|
157
|
+
connection_id: options.connectionId,
|
|
158
|
+
type: "template",
|
|
159
|
+
template_name: options.templateName,
|
|
160
|
+
language_code: options.languageCode || "es",
|
|
161
|
+
components: options.components || []
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** Marca un mensaje o chat como leído */
|
|
165
|
+
async markAsRead(options) {
|
|
166
|
+
return this.http.request("POST", "/whatsapp/messages", {
|
|
167
|
+
recipient: options.to,
|
|
168
|
+
connection_id: options.connectionId,
|
|
169
|
+
type: "mark_read",
|
|
170
|
+
message_id: options.messageId
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** Obtiene la lista de líneas y números de WhatsApp conectados en la organización */
|
|
174
|
+
async listConnections() {
|
|
175
|
+
const res = await this.http.request("GET", "/whatsapp/connections");
|
|
176
|
+
return res.connections;
|
|
177
|
+
}
|
|
178
|
+
/** Obtiene las plantillas oficiales de Meta aprobadas */
|
|
179
|
+
async getTemplates() {
|
|
180
|
+
const res = await this.http.request("GET", "/whatsapp/templates");
|
|
181
|
+
return res.templates;
|
|
182
|
+
}
|
|
183
|
+
/** Crea una nueva plantilla para enviar a revisión a Meta */
|
|
184
|
+
async createTemplate(options) {
|
|
185
|
+
return this.http.request("POST", "/whatsapp/templates", {
|
|
186
|
+
name: options.name,
|
|
187
|
+
category: options.category,
|
|
188
|
+
components: [{ type: "BODY", text: options.bodyText }]
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/** Lista las campañas masivas de WhatsApp configuradas */
|
|
192
|
+
async listCampaigns() {
|
|
193
|
+
const res = await this.http.request("GET", "/whatsapp/campaigns");
|
|
194
|
+
return res.campaigns;
|
|
195
|
+
}
|
|
196
|
+
/** Ejecuta una campaña masiva */
|
|
197
|
+
async executeCampaign(campaignId) {
|
|
198
|
+
return this.http.request("POST", `/whatsapp/campaigns/${campaignId}/execute`);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var OnboardingService = class {
|
|
202
|
+
constructor(http) {
|
|
203
|
+
this.http = http;
|
|
204
|
+
}
|
|
205
|
+
http;
|
|
206
|
+
/**
|
|
207
|
+
* Crea una sesión de conexión hosted para que un cliente o sede vincule su WhatsApp.
|
|
208
|
+
* Devuelve `session_url` que puedes abrir en un modal o popup en tu CRM.
|
|
209
|
+
*/
|
|
210
|
+
async createSession(options = {}) {
|
|
211
|
+
return this.http.request("POST", "/onboarding/sessions", {
|
|
212
|
+
client_name: options.clientName,
|
|
213
|
+
redirect_url: options.redirectUrl,
|
|
214
|
+
logo_url: options.logoUrl,
|
|
215
|
+
brand_color: options.brandColor,
|
|
216
|
+
metadata: options.metadata
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Consulta el estado de una sesión de onboarding por su ID */
|
|
220
|
+
async getSessionStatus(sessionId) {
|
|
221
|
+
return this.http.request("GET", `/onboarding/sessions/${sessionId}`);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
var WebhookService = class {
|
|
225
|
+
constructor(http) {
|
|
226
|
+
this.http = http;
|
|
227
|
+
}
|
|
228
|
+
http;
|
|
229
|
+
/** Suscribe una URL de tu backend para recibir eventos en tiempo real */
|
|
230
|
+
async subscribe(options) {
|
|
231
|
+
return this.http.request("POST", "/webhooks/subscriptions", {
|
|
232
|
+
name: options.name,
|
|
233
|
+
url: options.url,
|
|
234
|
+
events: options.events,
|
|
235
|
+
secret: options.secret,
|
|
236
|
+
is_active: true
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/** Lista todas las suscripciones de webhooks activas */
|
|
240
|
+
async list() {
|
|
241
|
+
const res = await this.http.request("GET", "/webhooks/subscriptions");
|
|
242
|
+
return res.subscriptions;
|
|
243
|
+
}
|
|
244
|
+
/** Elimina una suscripción de webhook */
|
|
245
|
+
async delete(webhookId) {
|
|
246
|
+
const res = await this.http.request("DELETE", `/webhooks/subscriptions/${webhookId}`);
|
|
247
|
+
return res.success;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Valida la firma criptográfica HMAC SHA-256 de un webhook recibido de Novapsis SM.
|
|
251
|
+
* Evita ataques de suplantación garantizando que la petición proviene de Novapsis.
|
|
252
|
+
*
|
|
253
|
+
* @param rawBody - El cuerpo crudo recibido en el request (string sin parsear)
|
|
254
|
+
* @param signatureHeader - El header 'X-Novapsis-Signature'
|
|
255
|
+
* @param secret - Tu secreto de webhook de Novapsis
|
|
256
|
+
*/
|
|
257
|
+
verifySignature(rawBody, signatureHeader, secret) {
|
|
258
|
+
if (!signatureHeader || !secret || !rawBody) return false;
|
|
259
|
+
try {
|
|
260
|
+
const computed = crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
|
|
261
|
+
return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(signatureHeader));
|
|
262
|
+
} catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var ContactsService = class {
|
|
268
|
+
constructor(http) {
|
|
269
|
+
this.http = http;
|
|
270
|
+
}
|
|
271
|
+
http;
|
|
272
|
+
/** Lista contactos del CRM */
|
|
273
|
+
async list() {
|
|
274
|
+
const res = await this.http.request("GET", "/contacts");
|
|
275
|
+
return res.contacts;
|
|
276
|
+
}
|
|
277
|
+
/** Crea o actualiza un contacto por número de teléfono */
|
|
278
|
+
async upsert(options) {
|
|
279
|
+
return this.http.request("POST", "/contacts", options);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var AIService = class {
|
|
283
|
+
constructor(http) {
|
|
284
|
+
this.http = http;
|
|
285
|
+
}
|
|
286
|
+
http;
|
|
287
|
+
/** Lista los agentes de IA configurados con sus modelos */
|
|
288
|
+
async listAgents() {
|
|
289
|
+
const res = await this.http.request("GET", "/ai/agents");
|
|
290
|
+
return res.agents;
|
|
291
|
+
}
|
|
292
|
+
/** Ejecuta una consulta contra un agente de IA con su base de conocimiento RAG */
|
|
293
|
+
async execute(options) {
|
|
294
|
+
return this.http.request("POST", `/ai/agents/${options.agentId}/execute`, {
|
|
295
|
+
message: options.message
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
var Novapsis = class {
|
|
300
|
+
http;
|
|
301
|
+
/** Métodos para envíos y operaciones de WhatsApp */
|
|
302
|
+
whatsapp;
|
|
303
|
+
/** Métodos para onboarding hosted y vinculación B2B */
|
|
304
|
+
onboarding;
|
|
305
|
+
/** Métodos para suscripción y validación de Webhooks */
|
|
306
|
+
webhooks;
|
|
307
|
+
/** Métodos para contactos del CRM */
|
|
308
|
+
contacts;
|
|
309
|
+
/** Métodos para Agentes de Inteligencia Artificial */
|
|
310
|
+
ai;
|
|
311
|
+
constructor(options) {
|
|
312
|
+
if (!options.apiKey) {
|
|
313
|
+
throw new NovapsisAPIError("Se requiere una apiKey v\xE1lida para inicializar el cliente de Novapsis.", "CONFIG_ERROR", 400);
|
|
314
|
+
}
|
|
315
|
+
this.http = new HttpClient(options);
|
|
316
|
+
this.whatsapp = new WhatsAppService(this.http);
|
|
317
|
+
this.onboarding = new OnboardingService(this.http);
|
|
318
|
+
this.webhooks = new WebhookService(this.http);
|
|
319
|
+
this.contacts = new ContactsService(this.http);
|
|
320
|
+
this.ai = new AIService(this.http);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var index_default = Novapsis;
|
|
324
|
+
/**
|
|
325
|
+
* novapsis - Official TypeScript / JavaScript SDK
|
|
326
|
+
* ══════════════════════════════════════════════════════════════════════════════
|
|
327
|
+
* Official SDK for Novapsis SM.
|
|
328
|
+
* Universal typed client for WhatsApp Business Cloud API, Hosted Onboarding,
|
|
329
|
+
* CRM, AI Agents and HMAC Webhook Verification.
|
|
330
|
+
*
|
|
331
|
+
* @version 1.0.0
|
|
332
|
+
* @author Novapsis Technologies
|
|
333
|
+
* @license MIT
|
|
334
|
+
* ══════════════════════════════════════════════════════════════════════════════
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
export { AIService, ContactsService, Novapsis, NovapsisAPIError, OnboardingService, WebhookService, WhatsAppService, index_default as default };
|
|
338
|
+
//# sourceMappingURL=index.mjs.map
|
|
339
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * novapsis - Official TypeScript / JavaScript SDK\n * ══════════════════════════════════════════════════════════════════════════════\n * Official SDK for Novapsis SM.\n * Universal typed client for WhatsApp Business Cloud API, Hosted Onboarding,\n * CRM, AI Agents and HMAC Webhook Verification.\n *\n * @version 1.0.0\n * @author Novapsis Technologies\n * @license MIT\n * ══════════════════════════════════════════════════════════════════════════════\n */\n\nimport crypto from 'crypto';\n\n// ── TYPES & DEFINITIONS ──────────────────────────────────────────────────────\n\nexport interface NovapsisClientOptions {\n /** Clave de API de Novapsis (empieza por nvs_live_ o nvs_test_) */\n apiKey: string;\n /** URL base de la API (por defecto: https://app.novapsis.com) */\n baseUrl?: string;\n /** Timeout en milisegundos para peticiones HTTP (por defecto: 15000) */\n timeoutMs?: number;\n /** Número máximo de reintentos automáticos ante fallos de red transitorios (por defecto: 2) */\n maxRetries?: number;\n}\n\nexport interface APIResponse<T = any> {\n success: boolean;\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: any;\n };\n}\n\nexport class NovapsisAPIError extends Error {\n public readonly code: string;\n public readonly status: number;\n public readonly details?: any;\n\n constructor(message: string, code = 'API_ERROR', status = 500, details?: any) {\n super(message);\n this.name = 'NovapsisAPIError';\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\n// ── WHATSAPP TYPES ───────────────────────────────────────────────────────────\n\nexport interface SendBaseOptions {\n /** Número destinatario con código de país, sin '+' ni espacios (ej: '34600112233') */\n to: string;\n /** UUID de la conexión o Phone Number ID de Meta desde el cual enviar. Si se omite, usa la línea principal. */\n connectionId?: string;\n}\n\nexport interface SendTextOptions extends SendBaseOptions {\n /** Cuerpo del mensaje de texto */\n text: string;\n /** Si es true, genera vista previa de los enlaces web incluidos en el texto */\n previewUrl?: boolean;\n}\n\nexport interface SendImageOptions extends SendBaseOptions {\n /** URL HTTPS pública de la imagen (JPG, PNG, WebP) */\n imageUrl: string;\n /** Pie de foto opcional */\n caption?: string;\n}\n\nexport interface SendAudioOptions extends SendBaseOptions {\n /** URL HTTPS pública del archivo de audio (MP3, AAC, OGG, M4A) */\n audioUrl: string;\n}\n\nexport interface SendDocumentOptions extends SendBaseOptions {\n /** URL HTTPS pública del documento o PDF */\n documentUrl: string;\n /** Nombre visible del archivo (ej: 'factura_2026.pdf') */\n filename?: string;\n /** Descripción o pie opcional */\n caption?: string;\n}\n\nexport interface SendVideoOptions extends SendBaseOptions {\n /** URL HTTPS pública del video (MP4, 3GP) */\n videoUrl: string;\n /** Pie de video opcional */\n caption?: string;\n}\n\nexport interface SendLocationOptions extends SendBaseOptions {\n /** Coordenada de latitud decimal (ej: 40.416775) */\n latitude: number | string;\n /** Coordenada de longitud decimal (ej: -3.703790) */\n longitude: number | string;\n /** Nombre del lugar o establecimiento (ej: 'Sede Central Madrid') */\n name?: string;\n /** Dirección postal completa */\n address?: string;\n}\n\nexport interface SendContactCardOptions extends SendBaseOptions {\n /** Nombre completo del contacto a compartir */\n name: string;\n /** Número de teléfono del contacto compartido */\n phone: string;\n}\n\nexport interface SendTemplateOptions extends SendBaseOptions {\n /** Nombre exacto de la plantilla aprobada en Meta */\n templateName: string;\n /** Código de idioma (por defecto: 'es') */\n languageCode?: string;\n /** Componentes y variables dinámicas de la plantilla */\n components?: Array<{\n type: 'header' | 'body' | 'button';\n sub_type?: string;\n index?: string | number;\n parameters?: Array<{\n type: 'text' | 'image' | 'document' | 'video' | 'currency' | 'date_time';\n text?: string;\n [key: string]: any;\n }>;\n }>;\n}\n\nexport interface SendMessageResult {\n message_id: string;\n recipient: string;\n type: string;\n status: string;\n conversation_id: string | null;\n}\n\nexport interface WhatsAppConnection {\n id: string;\n display_name: string;\n phone_number: string | null;\n phone_number_id: string;\n verified_name: string | null;\n status: string;\n webhook_subscribed: boolean;\n}\n\nexport interface WhatsAppTemplate {\n id: string;\n name: string;\n category: string;\n language: string;\n status: string;\n components: any[];\n}\n\n// ── ONBOARDING TYPES ─────────────────────────────────────────────────────────\n\nexport interface CreateOnboardingSessionOptions {\n /** Nombre de la empresa, cliente o sede a mostrar en la pantalla de conexión */\n clientName?: string;\n /** URL de retorno a la que redirigir al usuario cuando vincule su WhatsApp */\n redirectUrl?: string;\n /** URL del logotipo de la empresa cliente para marca blanca */\n logoUrl?: string;\n /** Color hexadecimal corporativo para la interfaz de conexión (ej: '#0ea5e9') */\n brandColor?: string;\n /** Metadata personalizada para tracking en tu CRM */\n metadata?: Record<string, any>;\n}\n\nexport interface OnboardingSession {\n id: string;\n session_id: string;\n session_url: string;\n status: 'started' | 'completed' | 'expired' | 'failed';\n expires_at: string;\n created_at: string;\n}\n\n// ── WEBHOOK TYPES ────────────────────────────────────────────────────────────\n\nexport type NovapsisWebhookEvent =\n | 'whatsapp.message_received'\n | 'whatsapp.message_sent'\n | 'whatsapp.connected'\n | 'conversation.started'\n | 'lead.completed'\n | 'call.completed'\n | 'review.received'\n | '*';\n\nexport interface CreateWebhookSubscriptionOptions {\n /** Nombre descriptivo de la suscripción (ej: 'CRM Sincronizador') */\n name: string;\n /** URL HTTPS de tu backend que recibirá los eventos POST */\n url: string;\n /** Lista de eventos a escuchar */\n events: NovapsisWebhookEvent[];\n /** Secreto opcional para firma HMAC. Si se omite, Novapsis generará uno automáticamente. */\n secret?: string;\n}\n\nexport interface WebhookSubscription {\n id: string;\n name: string;\n url: string;\n events: string[];\n secret: string;\n is_active: boolean;\n created_at: string;\n}\n\nexport interface InboundWebhookPayload<T = any> {\n event: NovapsisWebhookEvent;\n timestamp: string;\n organization_id: string;\n data: T;\n}\n\n// ── CRM & CONTACTS TYPES ─────────────────────────────────────────────────────\n\nexport interface UpsertContactOptions {\n phone: string;\n name?: string;\n email?: string;\n tags?: string[];\n metadata?: Record<string, any>;\n}\n\nexport interface Contact {\n id: string;\n phone: string;\n name: string | null;\n email: string | null;\n tags: string[];\n created_at: string;\n}\n\n// ── INTERNAL HTTP CLIENT ────────────────────────────────────────────────────\n\nclass HttpClient {\n private apiKey: string;\n private baseUrl: string;\n private timeoutMs: number;\n private maxRetries: number;\n\n constructor(options: NovapsisClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl || 'https://app.novapsis.com').replace(/\\/+$/, '');\n this.timeoutMs = options.timeoutMs || 15000;\n this.maxRetries = options.maxRetries ?? 2;\n }\n\n async request<T>(method: string, path: string, body?: any, query?: Record<string, string>): Promise<T> {\n let url = `${this.baseUrl}/api/v1${path.startsWith('/') ? path : `/${path}`}`;\n if (query && Object.keys(query).length > 0) {\n const qs = new URLSearchParams(query).toString();\n url += `?${qs}`;\n }\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Authorization': `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'Novapsis-SDK-Node/1.0.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n const data: APIResponse<T> = await response.json().catch(() => ({\n success: false,\n error: { code: 'INVALID_JSON_RESPONSE', message: `HTTP ${response.status} Sin cuerpo JSON válido.` },\n }));\n\n if (!response.ok || !data.success) {\n throw new NovapsisAPIError(\n data.error?.message || `Error en petición HTTP ${response.status}`,\n data.error?.code || `HTTP_${response.status}`,\n response.status,\n data.error?.details\n );\n }\n\n return data.data as T;\n } catch (err: any) {\n clearTimeout(timeoutId);\n lastError = err;\n\n // No reintentar errores de validación de cliente 4xx\n if (err instanceof NovapsisAPIError && err.status >= 400 && err.status < 500) {\n throw err;\n }\n\n // Si es el último intento, lanzar el error\n if (attempt === this.maxRetries) {\n break;\n }\n\n // Backoff exponencial: 300ms, 900ms...\n await new Promise((resolve) => setTimeout(resolve, 300 * Math.pow(3, attempt)));\n }\n }\n\n throw lastError || new NovapsisAPIError('Error de red al comunicarse con Novapsis API', 'NETWORK_ERROR', 0);\n }\n}\n\n// ── SDK SERVICES ────────────────────────────────────────────────────────────\n\n/** Servicio para envíos y gestión de WhatsApp Cloud API */\nexport class WhatsAppService {\n constructor(private http: HttpClient) {}\n\n /** Envía un mensaje de texto plano */\n async sendText(options: SendTextOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'text',\n text: options.text,\n });\n }\n\n /** Envía una imagen o fotografía */\n async sendImage(options: SendImageOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'image',\n media_url: options.imageUrl,\n caption: options.caption,\n });\n }\n\n /** Envía un archivo de audio o nota de voz */\n async sendAudio(options: SendAudioOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'audio',\n media_url: options.audioUrl,\n });\n }\n\n /** Envía un documento (PDF, Word, Excel, etc.) con nombre de archivo personalizado */\n async sendDocument(options: SendDocumentOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'document',\n media_url: options.documentUrl,\n filename: options.filename,\n caption: options.caption,\n });\n }\n\n /** Envía un video */\n async sendVideo(options: SendVideoOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'video',\n media_url: options.videoUrl,\n caption: options.caption,\n });\n }\n\n /** Envía una ubicación con coordenadas y dirección en el mapa */\n async sendLocation(options: SendLocationOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'location',\n latitude: options.latitude,\n longitude: options.longitude,\n location_name: options.name,\n location_address: options.address,\n });\n }\n\n /** Envía una tarjeta de contacto (vCard) interactiva */\n async sendContact(options: SendContactCardOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'contacts',\n contact_name: options.name,\n contact_phone: options.phone,\n });\n }\n\n /** Envía una plantilla oficial aprobada por Meta con variables dinámicas */\n async sendTemplate(options: SendTemplateOptions): Promise<SendMessageResult> {\n return this.http.request<SendMessageResult>('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'template',\n template_name: options.templateName,\n language_code: options.languageCode || 'es',\n components: options.components || [],\n });\n }\n\n /** Marca un mensaje o chat como leído */\n async markAsRead(options: { to: string; connectionId?: string; messageId?: string }): Promise<any> {\n return this.http.request('POST', '/whatsapp/messages', {\n recipient: options.to,\n connection_id: options.connectionId,\n type: 'mark_read',\n message_id: options.messageId,\n });\n }\n\n /** Obtiene la lista de líneas y números de WhatsApp conectados en la organización */\n async listConnections(): Promise<WhatsAppConnection[]> {\n const res = await this.http.request<{ connections: WhatsAppConnection[] }>('GET', '/whatsapp/connections');\n return res.connections;\n }\n\n /** Obtiene las plantillas oficiales de Meta aprobadas */\n async getTemplates(): Promise<WhatsAppTemplate[]> {\n const res = await this.http.request<{ templates: WhatsAppTemplate[] }>('GET', '/whatsapp/templates');\n return res.templates;\n }\n\n /** Crea una nueva plantilla para enviar a revisión a Meta */\n async createTemplate(options: {\n name: string;\n category: 'MARKETING' | 'UTILITY' | 'AUTHENTICATION';\n bodyText: string;\n }): Promise<any> {\n return this.http.request('POST', '/whatsapp/templates', {\n name: options.name,\n category: options.category,\n components: [{ type: 'BODY', text: options.bodyText }],\n });\n }\n\n /** Lista las campañas masivas de WhatsApp configuradas */\n async listCampaigns(): Promise<any[]> {\n const res = await this.http.request<{ campaigns: any[] }>('GET', '/whatsapp/campaigns');\n return res.campaigns;\n }\n\n /** Ejecuta una campaña masiva */\n async executeCampaign(campaignId: string): Promise<any> {\n return this.http.request('POST', `/whatsapp/campaigns/${campaignId}/execute`);\n }\n}\n\n/** Servicio para onboarding y conexión de WhatsApp de clientes B2B (Marca Blanca) */\nexport class OnboardingService {\n constructor(private http: HttpClient) {}\n\n /**\n * Crea una sesión de conexión hosted para que un cliente o sede vincule su WhatsApp.\n * Devuelve `session_url` que puedes abrir en un modal o popup en tu CRM.\n */\n async createSession(options: CreateOnboardingSessionOptions = {}): Promise<OnboardingSession> {\n return this.http.request<OnboardingSession>('POST', '/onboarding/sessions', {\n client_name: options.clientName,\n redirect_url: options.redirectUrl,\n logo_url: options.logoUrl,\n brand_color: options.brandColor,\n metadata: options.metadata,\n });\n }\n\n /** Consulta el estado de una sesión de onboarding por su ID */\n async getSessionStatus(sessionId: string): Promise<OnboardingSession> {\n return this.http.request<OnboardingSession>('GET', `/onboarding/sessions/${sessionId}`);\n }\n}\n\n/** Servicio para gestionar Webhooks salientes en tiempo real */\nexport class WebhookService {\n constructor(private http: HttpClient) {}\n\n /** Suscribe una URL de tu backend para recibir eventos en tiempo real */\n async subscribe(options: CreateWebhookSubscriptionOptions): Promise<WebhookSubscription> {\n return this.http.request<WebhookSubscription>('POST', '/webhooks/subscriptions', {\n name: options.name,\n url: options.url,\n events: options.events,\n secret: options.secret,\n is_active: true,\n });\n }\n\n /** Lista todas las suscripciones de webhooks activas */\n async list(): Promise<WebhookSubscription[]> {\n const res = await this.http.request<{ subscriptions: WebhookSubscription[] }>('GET', '/webhooks/subscriptions');\n return res.subscriptions;\n }\n\n /** Elimina una suscripción de webhook */\n async delete(webhookId: string): Promise<boolean> {\n const res = await this.http.request<{ success: boolean }>('DELETE', `/webhooks/subscriptions/${webhookId}`);\n return res.success;\n }\n\n /**\n * Valida la firma criptográfica HMAC SHA-256 de un webhook recibido de Novapsis SM.\n * Evita ataques de suplantación garantizando que la petición proviene de Novapsis.\n *\n * @param rawBody - El cuerpo crudo recibido en el request (string sin parsear)\n * @param signatureHeader - El header 'X-Novapsis-Signature'\n * @param secret - Tu secreto de webhook de Novapsis\n */\n verifySignature(rawBody: string, signatureHeader: string | null | undefined, secret: string): boolean {\n if (!signatureHeader || !secret || !rawBody) return false;\n try {\n const computed = crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');\n return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(signatureHeader));\n } catch {\n return false;\n }\n }\n}\n\n/** Servicio para gestionar Contactos del CRM */\nexport class ContactsService {\n constructor(private http: HttpClient) {}\n\n /** Lista contactos del CRM */\n async list(): Promise<Contact[]> {\n const res = await this.http.request<{ contacts: Contact[] }>('GET', '/contacts');\n return res.contacts;\n }\n\n /** Crea o actualiza un contacto por número de teléfono */\n async upsert(options: UpsertContactOptions): Promise<Contact> {\n return this.http.request<Contact>('POST', '/contacts', options);\n }\n}\n\n/** Servicio para interactuar con Agentes de Inteligencia Artificial */\nexport class AIService {\n constructor(private http: HttpClient) {}\n\n /** Lista los agentes de IA configurados con sus modelos */\n async listAgents(): Promise<any[]> {\n const res = await this.http.request<{ agents: any[] }>('GET', '/ai/agents');\n return res.agents;\n }\n\n /** Ejecuta una consulta contra un agente de IA con su base de conocimiento RAG */\n async execute(options: { agentId: string; message: string }): Promise<{ response: string; tokens_used?: number }> {\n return this.http.request('POST', `/ai/agents/${options.agentId}/execute`, {\n message: options.message,\n });\n }\n}\n\n// ── MAIN CLIENT ─────────────────────────────────────────────────────────────\n\n/**\n * Cliente oficial de Novapsis SM.\n * Punto de entrada principal para interactuar con todos los servicios.\n *\n * @example\n * ```typescript\n * import { Novapsis } from 'novapsis';\n *\n * const client = new Novapsis({\n * apiKey: 'nvs_live_OfnAAb6sEGztGcdbR5IKnhFpPrDJSTq7',\n * });\n *\n * // Enviar mensaje de texto\n * await client.whatsapp.sendText({\n * to: '34600112233',\n * text: '¡Hola desde Novapsis SDK!'\n * });\n * ```\n */\nexport class Novapsis {\n private http: HttpClient;\n\n /** Métodos para envíos y operaciones de WhatsApp */\n public readonly whatsapp: WhatsAppService;\n /** Métodos para onboarding hosted y vinculación B2B */\n public readonly onboarding: OnboardingService;\n /** Métodos para suscripción y validación de Webhooks */\n public readonly webhooks: WebhookService;\n /** Métodos para contactos del CRM */\n public readonly contacts: ContactsService;\n /** Métodos para Agentes de Inteligencia Artificial */\n public readonly ai: AIService;\n\n constructor(options: NovapsisClientOptions) {\n if (!options.apiKey) {\n throw new NovapsisAPIError('Se requiere una apiKey válida para inicializar el cliente de Novapsis.', 'CONFIG_ERROR', 400);\n }\n this.http = new HttpClient(options);\n this.whatsapp = new WhatsAppService(this.http);\n this.onboarding = new OnboardingService(this.http);\n this.webhooks = new WebhookService(this.http);\n this.contacts = new ContactsService(this.http);\n this.ai = new AIService(this.http);\n }\n}\n\n// Default export\nexport default Novapsis;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "novapsis",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "SDK oficial de Novapsis SM para Node.js, Next.js y TypeScript. WhatsApp Cloud API, Onboarding B2B Marca Blanca, CRM y Webhooks HMAC.",
|
|
5
|
+
"author": "Novapsis Technologies <soporte@novapsis.com> (https://app.novapsis.com)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"novapsis",
|
|
30
|
+
"whatsapp",
|
|
31
|
+
"whatsapp-cloud-api",
|
|
32
|
+
"whatsapp-business",
|
|
33
|
+
"meta",
|
|
34
|
+
"crm",
|
|
35
|
+
"onboarding",
|
|
36
|
+
"white-label",
|
|
37
|
+
"webhooks",
|
|
38
|
+
"ai-agent",
|
|
39
|
+
"sdk"
|
|
40
|
+
],
|
|
41
|
+
"homepage": "https://app.novapsis.com",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/novapsis/novapsis-sm.git",
|
|
45
|
+
"directory": "packages/novapsis-sdk"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.17.19",
|
|
49
|
+
"tsup": "^8.3.6",
|
|
50
|
+
"typescript": "^5.7.3"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|