@adatechnology/meta-whatsapp-contracts 0.2.0-rc.7 → 0.2.0-rc.9

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/dist/index.cjs CHANGED
@@ -21,21 +21,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ AudioNotIngestedError: () => AudioNotIngestedError,
24
25
  CROSS_FLOW_PREFIX: () => CROSS_FLOW_PREFIX,
25
26
  ConfigMissingError: () => ConfigMissingError,
26
27
  DuplicateWebhookDeliveryError: () => DuplicateWebhookDeliveryError,
27
28
  FLOW_ACTION_KIND: () => FLOW_ACTION_KIND,
28
29
  InvalidWebhookSignatureError: () => InvalidWebhookSignatureError,
29
30
  META_WHATSAPP_ERROR_CODES: () => META_WHATSAPP_ERROR_CODES,
31
+ MessageNotAudioError: () => MessageNotAudioError,
30
32
  MetaWhatsAppError: () => MetaWhatsAppError,
33
+ PREVIEW_MEDIA_ID_PREFIX: () => PREVIEW_MEDIA_ID_PREFIX,
31
34
  SessionNotFoundError: () => SessionNotFoundError,
32
35
  TemplateNotConfiguredError: () => TemplateNotConfiguredError,
36
+ TranscriptionDisabledError: () => TranscriptionDisabledError,
33
37
  WindowExpiredError: () => WindowExpiredError,
34
38
  crossFlowKey: () => crossFlowKey,
35
39
  flowGraphNodesSchema: () => flowGraphNodesSchema,
36
40
  flowNodeDataSchema: () => flowNodeDataSchema,
37
41
  flowNodeNextSchema: () => flowNodeNextSchema,
38
42
  isCrossFlowTarget: () => isCrossFlowTarget,
43
+ resolvePreviewUploadId: () => resolvePreviewUploadId,
44
+ toPreviewMediaId: () => toPreviewMediaId,
39
45
  whatsAppInteractiveSchema: () => whatsAppInteractiveSchema,
40
46
  whatsAppMediaSchema: () => whatsAppMediaSchema,
41
47
  whatsAppMessageEchoSchema: () => whatsAppMessageEchoSchema,
@@ -223,7 +229,10 @@ var META_WHATSAPP_ERROR_CODES = {
223
229
  DUPLICATE_DELIVERY: "META_WHATSAPP_DUPLICATE_DELIVERY",
224
230
  CONFIG_MISSING: "META_WHATSAPP_CONFIG_MISSING",
225
231
  TEMPLATE_NOT_CONFIGURED: "META_WHATSAPP_TEMPLATE_NOT_CONFIGURED",
226
- SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND"
232
+ SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND",
233
+ AUDIO_NOT_INGESTED: "META_WHATSAPP_AUDIO_NOT_INGESTED",
234
+ MESSAGE_NOT_AUDIO: "META_WHATSAPP_MESSAGE_NOT_AUDIO",
235
+ TRANSCRIPTION_DISABLED: "META_WHATSAPP_TRANSCRIPTION_DISABLED"
227
236
  };
228
237
  var WindowExpiredError = class extends MetaWhatsAppError {
229
238
  static {
@@ -282,23 +291,70 @@ var SessionNotFoundError = class extends MetaWhatsAppError {
282
291
  });
283
292
  }
284
293
  };
294
+ var AudioNotIngestedError = class extends MetaWhatsAppError {
295
+ static {
296
+ __name(this, "AudioNotIngestedError");
297
+ }
298
+ constructor(messageId) {
299
+ super("\xC1udio ainda est\xE1 sendo copiado \u2014 tente novamente em alguns segundos.", 409, META_WHATSAPP_ERROR_CODES.AUDIO_NOT_INGESTED, {
300
+ messageId
301
+ });
302
+ }
303
+ };
304
+ var TranscriptionDisabledError = class extends MetaWhatsAppError {
305
+ static {
306
+ __name(this, "TranscriptionDisabledError");
307
+ }
308
+ constructor() {
309
+ super("Transcri\xE7\xE3o de \xE1udio est\xE1 desligada para esta empresa.", 409, META_WHATSAPP_ERROR_CODES.TRANSCRIPTION_DISABLED);
310
+ }
311
+ };
312
+ var MessageNotAudioError = class extends MetaWhatsAppError {
313
+ static {
314
+ __name(this, "MessageNotAudioError");
315
+ }
316
+ constructor(messageId, type) {
317
+ super(`Mensagem ${messageId} \xE9 do tipo "${type}" \u2014 s\xF3 \xE1udio \xE9 transcrito.`, 422, META_WHATSAPP_ERROR_CODES.MESSAGE_NOT_AUDIO, {
318
+ messageId,
319
+ type
320
+ });
321
+ }
322
+ };
323
+
324
+ // src/previewMedia.types.ts
325
+ var PREVIEW_MEDIA_ID_PREFIX = "preview-upload:";
326
+ function toPreviewMediaId(uploadId) {
327
+ return `${PREVIEW_MEDIA_ID_PREFIX}${uploadId}`;
328
+ }
329
+ __name(toPreviewMediaId, "toPreviewMediaId");
330
+ function resolvePreviewUploadId(mediaId) {
331
+ if (!mediaId.startsWith(PREVIEW_MEDIA_ID_PREFIX)) return void 0;
332
+ return mediaId.slice(PREVIEW_MEDIA_ID_PREFIX.length) || void 0;
333
+ }
334
+ __name(resolvePreviewUploadId, "resolvePreviewUploadId");
285
335
  // Annotate the CommonJS export names for ESM import in node:
286
336
  0 && (module.exports = {
337
+ AudioNotIngestedError,
287
338
  CROSS_FLOW_PREFIX,
288
339
  ConfigMissingError,
289
340
  DuplicateWebhookDeliveryError,
290
341
  FLOW_ACTION_KIND,
291
342
  InvalidWebhookSignatureError,
292
343
  META_WHATSAPP_ERROR_CODES,
344
+ MessageNotAudioError,
293
345
  MetaWhatsAppError,
346
+ PREVIEW_MEDIA_ID_PREFIX,
294
347
  SessionNotFoundError,
295
348
  TemplateNotConfiguredError,
349
+ TranscriptionDisabledError,
296
350
  WindowExpiredError,
297
351
  crossFlowKey,
298
352
  flowGraphNodesSchema,
299
353
  flowNodeDataSchema,
300
354
  flowNodeNextSchema,
301
355
  isCrossFlowTarget,
356
+ resolvePreviewUploadId,
357
+ toPreviewMediaId,
302
358
  whatsAppInteractiveSchema,
303
359
  whatsAppMediaSchema,
304
360
  whatsAppMessageEchoSchema,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/webhook.types.ts","../src/flow.types.ts","../src/errors.ts"],"sourcesContent":["export {\n whatsAppMediaSchema,\n whatsAppInteractiveSchema,\n whatsAppOrderSchema,\n whatsAppMessageSchema,\n whatsAppMessageEchoSchema,\n whatsAppMessageStatusSchema,\n whatsAppStatusSchema,\n whatsAppWebhookPayloadSchema,\n} from './webhook.types'\nexport type {\n WhatsAppMedia,\n WhatsAppInteractive,\n WhatsAppOrder,\n WhatsAppMessage,\n WhatsAppMessageEcho,\n WhatsAppMessageStatusValue,\n WhatsAppStatus,\n WhatsAppWebhookPayload,\n} from './webhook.types'\n\nexport type {\n SessionState,\n SessionMode,\n MessageDirection,\n MessageSender,\n MessageStatus,\n ConversationSession,\n ConversationMessage,\n ConversationSummary,\n ListConversationsParams,\n ListMessagesParams,\n} from './conversation.types'\n\nexport {\n CROSS_FLOW_PREFIX,\n FLOW_ACTION_KIND,\n isCrossFlowTarget,\n crossFlowKey,\n flowNodeNextSchema,\n flowNodeDataSchema,\n flowGraphNodesSchema,\n} from './flow.types'\nexport type {\n FlowNodeType,\n FlowQuestionType,\n FlowActionKind,\n FlowConditionOperator,\n FlowNodeNext,\n FlowNodeData,\n FlowGraphData,\n FlowGraphSummary,\n LiveFlowPosition,\n} from './flow.types'\n\nexport type {\n WhatsAppSettings,\n TemplateVariablesMap,\n TemplateConfig,\n WhatsAppTemplateSummary,\n CreateTemplateResult,\n} from './settings.types'\n\nexport type { MetaWhatsAppHooks, MessageHookOutcome, InboundMediaDescriptor } from './events'\n\nexport type {\n ChannelAdapterInterface,\n SubjectResolverInterface,\n CatalogProduct,\n CatalogPort,\n ObjectStorageInterface,\n RealtimeNotifierInterface,\n FlowActionHandler,\n FlowActionResult,\n FlowActionRegistry,\n} from './providers'\n\nexport {\n MetaWhatsAppError,\n META_WHATSAPP_ERROR_CODES,\n WindowExpiredError,\n InvalidWebhookSignatureError,\n DuplicateWebhookDeliveryError,\n ConfigMissingError,\n TemplateNotConfiguredError,\n SessionNotFoundError,\n} from './errors'\n","import { z } from 'zod'\n\n// Extraído de financiamento-imobiliario-bot/apps/api/src/modules/webhook/application/use-cases/\n// ReceiveWhatsAppWebhook.use-case.ts:39-113 — shape real do payload da Cloud API da Meta.\n\nexport const whatsAppMediaSchema = z.object({\n id: z.string(),\n mime_type: z.string(),\n sha256: z.string().optional(),\n caption: z.string().optional(),\n filename: z.string().optional(),\n})\nexport type WhatsAppMedia = z.infer<typeof whatsAppMediaSchema>\n\nexport const whatsAppInteractiveSchema = z.object({\n type: z.string(),\n button_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n list_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n})\nexport type WhatsAppInteractive = z.infer<typeof whatsAppInteractiveSchema>\n\nexport const whatsAppOrderSchema = z.object({\n catalog_id: z.string(),\n text: z.string().optional(),\n product_items: z.array(\n z.object({\n product_retailer_id: z.string(),\n quantity: z.number(),\n item_price: z.number(),\n currency: z.string(),\n }),\n ),\n})\nexport type WhatsAppOrder = z.infer<typeof whatsAppOrderSchema>\n\nexport const whatsAppMessageSchema = z.object({\n id: z.string(),\n from: z.string(),\n type: z.string(),\n text: z.object({ body: z.string() }).optional(),\n image: whatsAppMediaSchema.optional(),\n audio: whatsAppMediaSchema.optional(),\n video: whatsAppMediaSchema.optional(),\n document: whatsAppMediaSchema.optional(),\n sticker: whatsAppMediaSchema.optional(),\n interactive: whatsAppInteractiveSchema.optional(),\n // Pedido enviado pelo carrinho do catálogo (WhatsApp Commerce)\n order: whatsAppOrderSchema.optional(),\n // Presente quando o cliente abre um item do catálogo e manda mensagem pela página do produto\n context: z\n .object({\n from: z.string().optional(),\n id: z.string().optional(),\n referred_product: z.object({ catalog_id: z.string(), product_retailer_id: z.string() }).optional(),\n })\n .optional(),\n timestamp: z.string(),\n})\nexport type WhatsAppMessage = z.infer<typeof whatsAppMessageSchema>\n\nexport const whatsAppMessageEchoSchema = z.object({\n id: z.string(),\n from: z.string(),\n timestamp: z.string(),\n type: z.string(),\n})\nexport type WhatsAppMessageEcho = z.infer<typeof whatsAppMessageEchoSchema>\n\nexport const whatsAppMessageStatusSchema = z.enum(['sent', 'delivered', 'read', 'failed'])\nexport type WhatsAppMessageStatusValue = z.infer<typeof whatsAppMessageStatusSchema>\n\nexport const whatsAppStatusSchema = z.object({\n id: z.string(),\n status: whatsAppMessageStatusSchema,\n timestamp: z.string(),\n recipient_id: z.string().optional(),\n})\nexport type WhatsAppStatus = z.infer<typeof whatsAppStatusSchema>\n\nexport const whatsAppWebhookPayloadSchema = z.object({\n object: z.string(),\n entry: z.array(\n z.object({\n id: z.string(),\n changes: z.array(\n z.object({\n value: z.object({\n messaging_product: z.string(),\n messages: z.array(whatsAppMessageSchema).optional(),\n message_echoes: z.array(whatsAppMessageEchoSchema).optional(),\n statuses: z.array(whatsAppStatusSchema).optional(),\n metadata: z.object({ display_phone_number: z.string(), phone_number_id: z.string() }).optional(),\n }),\n }),\n ),\n }),\n ),\n})\nexport type WhatsAppWebhookPayload = z.infer<typeof whatsAppWebhookPayloadSchema>\n","// Mesmo shape de dados do editor visual (@adatechnology/conversations-ui/flows) — o backend\n// interpreta/persiste o grafo, o frontend edita; os dois lados precisam concordar no formato.\n// `FlowActionKind` é string aberta (não union fechada): o host registra os próprios actions\n// (ex.: 'trigger_simulation' no bot) via registerFlowAction() no módulo (T4.3) — o pacote nunca\n// assume nenhum caso de negócio específico.\nimport { z } from 'zod'\n\nexport type FlowNodeType = 'question' | 'entrada_choice' | 'action' | 'menu' | 'condition'\nexport type FlowQuestionType = 'text' | 'money' | 'date' | 'int' | 'cpf' | 'choice'\nexport type FlowActionKind = string\n\n// Os únicos `actionKind` que o próprio pacote implementa — todo o resto é do host. Ficam aqui, e\n// não no editor, porque o mesmo literal é usado nos dois lados (o editor oferece na paleta, o\n// módulo registra o handler) e duas cópias divergem sem nada acusar.\nexport const FLOW_ACTION_KIND = {\n HANDOFF: 'handoff',\n RATE_LIMITED_HANDOFF: 'rate_limited_handoff',\n SEND_PRODUCT_LIST: 'send_product_list',\n SEND_MEDIA: 'send_media',\n} as const\nexport type FlowConditionOperator = '>' | '>=' | '<' | '<=' | '==' | '!=' | 'contains'\nexport type FlowNodeNext = string | { byAnswer: Record<string, string>; default: string }\n\n// Destinos \"flow:<key>\" são saltos para outro fluxo — mesma convenção usada pelo editor visual\n// (conversations-ui/flows/flowGraph.ts); backend e frontend precisam concordar no formato.\nexport const CROSS_FLOW_PREFIX = 'flow:'\nexport const isCrossFlowTarget = (target: string): boolean => target.startsWith(CROSS_FLOW_PREFIX)\nexport const crossFlowKey = (target: string): string => target.slice(CROSS_FLOW_PREFIX.length)\n\nexport type FlowNodeData = {\n id: string\n type: FlowNodeType\n contextKey?: string\n questionType?: FlowQuestionType\n question?: string\n options?: [string, string][]\n actionKind?: FlowActionKind\n // Parâmetros arbitrários que o nó carrega para o handler registrado em `actionKind`. O pacote\n // nunca os interpreta — é o que permite um nó de ação ser configurado pelo editor sem que o\n // vocabulário do produto (o antigo `simulationTemplate`) vaze para dentro do contrato.\n actionParams?: Record<string, unknown>\n directMessage?: string\n fallbackMessage?: string\n conditionContextKey?: string\n conditionOperator?: FlowConditionOperator\n conditionValue?: string\n position?: { x: number; y: number }\n next?: FlowNodeNext\n}\n\nexport interface FlowGraphData {\n key: string\n label: string\n startNodeId: string\n version: number\n nodes: Record<string, FlowNodeData>\n}\n\nexport interface FlowGraphSummary {\n key: string\n label: string\n nodeCount: number\n showInMenu: boolean\n menuOptionLabel?: string\n updatedAt: string\n}\n\n// Retorno de GetLiveFlowPositions (T4.2) — quantas sessões ativas estão em cada nó agora,\n// usado pelo editor para desenhar o \"liveCount\" nos cards (FlowNodeCardData.liveCount).\nexport interface LiveFlowPosition {\n flowKey: string\n nodeId: string\n count: number\n}\n\n// Validação de runtime do grafo. O `nodes` é um jsonb que entrou pelo editor (dado de origem\n// cliente) e sai do banco como `unknown` — sem parse, um grafo malformado só se manifestaria\n// como comportamento estranho lá dentro do interpretador, longe da causa.\n//\n// Permissivo de propósito em dois pontos: `actionKind` é string aberta (o host registra os\n// seus) e `.passthrough()` deixa passar campos extras — um grafo salvo por uma versão mais nova\n// do editor não pode ficar irrecuperável numa versão mais antiga do módulo.\nexport const flowNodeNextSchema = z.union([\n z.string(),\n z.object({ byAnswer: z.record(z.string(), z.string()), default: z.string() }),\n])\n\nexport const flowNodeDataSchema = z\n .object({\n id: z.string(),\n type: z.enum(['question', 'entrada_choice', 'action', 'menu', 'condition']),\n contextKey: z.string().optional(),\n questionType: z.enum(['text', 'money', 'date', 'int', 'cpf', 'choice']).optional(),\n question: z.string().optional(),\n options: z.array(z.tuple([z.string(), z.string()])).optional(),\n actionKind: z.string().optional(),\n actionParams: z.record(z.string(), z.unknown()).optional(),\n directMessage: z.string().optional(),\n fallbackMessage: z.string().optional(),\n conditionContextKey: z.string().optional(),\n conditionOperator: z.enum(['>', '>=', '<', '<=', '==', '!=', 'contains']).optional(),\n conditionValue: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n next: flowNodeNextSchema.optional(),\n })\n .passthrough()\n\nexport const flowGraphNodesSchema = z.record(z.string(), flowNodeDataSchema)\n","// Hierarquia de erros do módulo — não depende do framework de erros do host (nenhum DomainError\n// importado); o host mapeia estes para o seu próprio formato de resposta no exception filter.\nexport class MetaWhatsAppError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'MetaWhatsAppError'\n }\n}\n\nexport const META_WHATSAPP_ERROR_CODES = {\n WINDOW_EXPIRED: 'META_WHATSAPP_WINDOW_EXPIRED',\n INVALID_SIGNATURE: 'META_WHATSAPP_INVALID_SIGNATURE',\n DUPLICATE_DELIVERY: 'META_WHATSAPP_DUPLICATE_DELIVERY',\n CONFIG_MISSING: 'META_WHATSAPP_CONFIG_MISSING',\n TEMPLATE_NOT_CONFIGURED: 'META_WHATSAPP_TEMPLATE_NOT_CONFIGURED',\n SESSION_NOT_FOUND: 'META_WHATSAPP_SESSION_NOT_FOUND',\n} as const\n\nexport class WindowExpiredError extends MetaWhatsAppError {\n constructor(public readonly hoursSinceLastMessage?: number) {\n super(\n 'Janela de 24h expirada — não é possível enviar mensagem para este contato fora de template.',\n 400,\n META_WHATSAPP_ERROR_CODES.WINDOW_EXPIRED,\n { hoursSinceLastMessage },\n )\n }\n}\n\n// Assinatura HMAC do webhook não confere — Meta ou um terceiro tentando forjar uma entrega.\nexport class InvalidWebhookSignatureError extends MetaWhatsAppError {\n constructor() {\n super('Assinatura do webhook inválida.', 401, META_WHATSAPP_ERROR_CODES.INVALID_SIGNATURE)\n }\n}\n\n// Meta reenviou uma entrega dentro da janela de replay (nonce já visto) — não é erro do\n// cliente, é comportamento esperado da Cloud API; o host deve responder 200 e ignorar.\nexport class DuplicateWebhookDeliveryError extends MetaWhatsAppError {\n constructor(nonce: string) {\n super('Entrega de webhook duplicada.', 409, META_WHATSAPP_ERROR_CODES.DUPLICATE_DELIVERY, { nonce })\n }\n}\n\nexport class ConfigMissingError extends MetaWhatsAppError {\n constructor(missingField: string) {\n super(`Configuração do WhatsApp ausente: ${missingField}.`, 503, META_WHATSAPP_ERROR_CODES.CONFIG_MISSING, {\n missingField,\n })\n }\n}\n\nexport class TemplateNotConfiguredError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Nenhum template de WhatsApp configurado para reabertura de janela.',\n 503,\n META_WHATSAPP_ERROR_CODES.TEMPLATE_NOT_CONFIGURED,\n )\n }\n}\n\nexport class SessionNotFoundError extends MetaWhatsAppError {\n constructor(whatsappNumber: string) {\n super(\n `Sessão de conversa não encontrada para ${whatsappNumber}.`,\n 404,\n META_WHATSAPP_ERROR_CODES.SESSION_NOT_FOUND,\n { whatsappNumber },\n )\n }\n}\n"],"mappings":"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","names":["whatsAppMediaSchema","z","object","id","string","mime_type","sha256","optional","caption","filename","whatsAppInteractiveSchema","type","button_reply","title","list_reply","whatsAppOrderSchema","catalog_id","text","product_items","array","product_retailer_id","quantity","number","item_price","currency","whatsAppMessageSchema","from","body","image","audio","video","document","sticker","interactive","order","context","referred_product","timestamp","whatsAppMessageEchoSchema","whatsAppMessageStatusSchema","enum","whatsAppStatusSchema","status","recipient_id","whatsAppWebhookPayloadSchema","entry","changes","value","messaging_product","messages","message_echoes","statuses","metadata","display_phone_number","phone_number_id","import_zod","FLOW_ACTION_KIND","HANDOFF","RATE_LIMITED_HANDOFF","SEND_PRODUCT_LIST","SEND_MEDIA","CROSS_FLOW_PREFIX","isCrossFlowTarget","target","startsWith","crossFlowKey","slice","length","flowNodeNextSchema","z","union","string","object","byAnswer","record","default","flowNodeDataSchema","id","type","enum","contextKey","optional","questionType","question","options","array","tuple","actionKind","actionParams","unknown","directMessage","fallbackMessage","conditionContextKey","conditionOperator","conditionValue","position","x","number","y","next","passthrough","flowGraphNodesSchema","MetaWhatsAppError","Error","message","statusCode","code","details","name","META_WHATSAPP_ERROR_CODES","WINDOW_EXPIRED","INVALID_SIGNATURE","DUPLICATE_DELIVERY","CONFIG_MISSING","TEMPLATE_NOT_CONFIGURED","SESSION_NOT_FOUND","WindowExpiredError","hoursSinceLastMessage","InvalidWebhookSignatureError","DuplicateWebhookDeliveryError","nonce","ConfigMissingError","missingField","TemplateNotConfiguredError","SessionNotFoundError","whatsappNumber"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/webhook.types.ts","../src/flow.types.ts","../src/errors.ts","../src/previewMedia.types.ts"],"sourcesContent":["export {\n whatsAppMediaSchema,\n whatsAppInteractiveSchema,\n whatsAppOrderSchema,\n whatsAppMessageSchema,\n whatsAppMessageEchoSchema,\n whatsAppMessageStatusSchema,\n whatsAppStatusSchema,\n whatsAppWebhookPayloadSchema,\n} from './webhook.types'\nexport type {\n WhatsAppMedia,\n WhatsAppInteractive,\n WhatsAppOrder,\n WhatsAppMessage,\n WhatsAppMessageEcho,\n WhatsAppMessageStatusValue,\n WhatsAppStatus,\n WhatsAppWebhookPayload,\n} from './webhook.types'\n\nexport type {\n SessionState,\n SessionMode,\n MessageDirection,\n MessageSender,\n MessageStatus,\n ConversationSession,\n ConversationMessage,\n ConversationSummary,\n ListConversationsParams,\n ListMessagesParams,\n} from './conversation.types'\n\nexport {\n CROSS_FLOW_PREFIX,\n FLOW_ACTION_KIND,\n isCrossFlowTarget,\n crossFlowKey,\n flowNodeNextSchema,\n flowNodeDataSchema,\n flowGraphNodesSchema,\n} from './flow.types'\nexport type {\n FlowNodeType,\n FlowQuestionType,\n FlowActionKind,\n FlowConditionOperator,\n FlowNodeNext,\n FlowNodeData,\n FlowGraphData,\n FlowGraphSummary,\n LiveFlowPosition,\n} from './flow.types'\n\nexport type {\n WhatsAppSettings,\n TranscriptionMode,\n TemplateVariablesMap,\n TemplateConfig,\n WhatsAppTemplateSummary,\n CreateTemplateResult,\n} from './settings.types'\n\nexport type {\n MetaWhatsAppHooks,\n MessageHookOutcome,\n InboundMediaDescriptor,\n TranscriptionDeferredDescriptor,\n} from './events'\n\nexport type {\n ChannelAdapterInterface,\n SubjectResolverInterface,\n CatalogProduct,\n CatalogPort,\n ObjectStorageInterface,\n RealtimeNotifierInterface,\n CacheInterface,\n FlowActionHandler,\n FlowActionResult,\n FlowActionRegistry,\n} from './providers'\n\nexport {\n MetaWhatsAppError,\n META_WHATSAPP_ERROR_CODES,\n WindowExpiredError,\n InvalidWebhookSignatureError,\n DuplicateWebhookDeliveryError,\n ConfigMissingError,\n TemplateNotConfiguredError,\n SessionNotFoundError,\n AudioNotIngestedError,\n MessageNotAudioError,\n TranscriptionDisabledError,\n} from './errors'\n\nexport { PREVIEW_MEDIA_ID_PREFIX, toPreviewMediaId, resolvePreviewUploadId } from './previewMedia.types'\n","import { z } from 'zod'\n\n// Extraído de financiamento-imobiliario-bot/apps/api/src/modules/webhook/application/use-cases/\n// ReceiveWhatsAppWebhook.use-case.ts:39-113 — shape real do payload da Cloud API da Meta.\n\nexport const whatsAppMediaSchema = z.object({\n id: z.string(),\n mime_type: z.string(),\n sha256: z.string().optional(),\n caption: z.string().optional(),\n filename: z.string().optional(),\n})\nexport type WhatsAppMedia = z.infer<typeof whatsAppMediaSchema>\n\nexport const whatsAppInteractiveSchema = z.object({\n type: z.string(),\n button_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n list_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n})\nexport type WhatsAppInteractive = z.infer<typeof whatsAppInteractiveSchema>\n\nexport const whatsAppOrderSchema = z.object({\n catalog_id: z.string(),\n text: z.string().optional(),\n product_items: z.array(\n z.object({\n product_retailer_id: z.string(),\n quantity: z.number(),\n item_price: z.number(),\n currency: z.string(),\n }),\n ),\n})\nexport type WhatsAppOrder = z.infer<typeof whatsAppOrderSchema>\n\nexport const whatsAppMessageSchema = z.object({\n id: z.string(),\n from: z.string(),\n type: z.string(),\n text: z.object({ body: z.string() }).optional(),\n image: whatsAppMediaSchema.optional(),\n audio: whatsAppMediaSchema.optional(),\n video: whatsAppMediaSchema.optional(),\n document: whatsAppMediaSchema.optional(),\n sticker: whatsAppMediaSchema.optional(),\n interactive: whatsAppInteractiveSchema.optional(),\n // Pedido enviado pelo carrinho do catálogo (WhatsApp Commerce)\n order: whatsAppOrderSchema.optional(),\n // Presente quando o cliente abre um item do catálogo e manda mensagem pela página do produto\n context: z\n .object({\n from: z.string().optional(),\n id: z.string().optional(),\n referred_product: z.object({ catalog_id: z.string(), product_retailer_id: z.string() }).optional(),\n })\n .optional(),\n timestamp: z.string(),\n})\nexport type WhatsAppMessage = z.infer<typeof whatsAppMessageSchema>\n\nexport const whatsAppMessageEchoSchema = z.object({\n id: z.string(),\n from: z.string(),\n timestamp: z.string(),\n type: z.string(),\n})\nexport type WhatsAppMessageEcho = z.infer<typeof whatsAppMessageEchoSchema>\n\nexport const whatsAppMessageStatusSchema = z.enum(['sent', 'delivered', 'read', 'failed'])\nexport type WhatsAppMessageStatusValue = z.infer<typeof whatsAppMessageStatusSchema>\n\nexport const whatsAppStatusSchema = z.object({\n id: z.string(),\n status: whatsAppMessageStatusSchema,\n timestamp: z.string(),\n recipient_id: z.string().optional(),\n})\nexport type WhatsAppStatus = z.infer<typeof whatsAppStatusSchema>\n\nexport const whatsAppWebhookPayloadSchema = z.object({\n object: z.string(),\n entry: z.array(\n z.object({\n id: z.string(),\n changes: z.array(\n z.object({\n value: z.object({\n messaging_product: z.string(),\n messages: z.array(whatsAppMessageSchema).optional(),\n message_echoes: z.array(whatsAppMessageEchoSchema).optional(),\n statuses: z.array(whatsAppStatusSchema).optional(),\n metadata: z.object({ display_phone_number: z.string(), phone_number_id: z.string() }).optional(),\n }),\n }),\n ),\n }),\n ),\n})\nexport type WhatsAppWebhookPayload = z.infer<typeof whatsAppWebhookPayloadSchema>\n","// Mesmo shape de dados do editor visual (@adatechnology/conversations-ui/flows) — o backend\n// interpreta/persiste o grafo, o frontend edita; os dois lados precisam concordar no formato.\n// `FlowActionKind` é string aberta (não union fechada): o host registra os próprios actions\n// (ex.: 'trigger_simulation' no bot) via registerFlowAction() no módulo (T4.3) — o pacote nunca\n// assume nenhum caso de negócio específico.\nimport { z } from 'zod'\n\nexport type FlowNodeType = 'question' | 'entrada_choice' | 'action' | 'menu' | 'condition'\nexport type FlowQuestionType = 'text' | 'money' | 'date' | 'int' | 'cpf' | 'choice'\nexport type FlowActionKind = string\n\n// Os únicos `actionKind` que o próprio pacote implementa — todo o resto é do host. Ficam aqui, e\n// não no editor, porque o mesmo literal é usado nos dois lados (o editor oferece na paleta, o\n// módulo registra o handler) e duas cópias divergem sem nada acusar.\nexport const FLOW_ACTION_KIND = {\n HANDOFF: 'handoff',\n RATE_LIMITED_HANDOFF: 'rate_limited_handoff',\n SEND_PRODUCT_LIST: 'send_product_list',\n SEND_MEDIA: 'send_media',\n} as const\nexport type FlowConditionOperator = '>' | '>=' | '<' | '<=' | '==' | '!=' | 'contains'\nexport type FlowNodeNext = string | { byAnswer: Record<string, string>; default: string }\n\n// Destinos \"flow:<key>\" são saltos para outro fluxo — mesma convenção usada pelo editor visual\n// (conversations-ui/flows/flowGraph.ts); backend e frontend precisam concordar no formato.\nexport const CROSS_FLOW_PREFIX = 'flow:'\nexport const isCrossFlowTarget = (target: string): boolean => target.startsWith(CROSS_FLOW_PREFIX)\nexport const crossFlowKey = (target: string): string => target.slice(CROSS_FLOW_PREFIX.length)\n\nexport type FlowNodeData = {\n id: string\n type: FlowNodeType\n contextKey?: string\n questionType?: FlowQuestionType\n question?: string\n options?: [string, string][]\n actionKind?: FlowActionKind\n // Parâmetros arbitrários que o nó carrega para o handler registrado em `actionKind`. O pacote\n // nunca os interpreta — é o que permite um nó de ação ser configurado pelo editor sem que o\n // vocabulário do produto (o antigo `simulationTemplate`) vaze para dentro do contrato.\n actionParams?: Record<string, unknown>\n directMessage?: string\n fallbackMessage?: string\n conditionContextKey?: string\n conditionOperator?: FlowConditionOperator\n conditionValue?: string\n position?: { x: number; y: number }\n next?: FlowNodeNext\n}\n\nexport interface FlowGraphData {\n key: string\n label: string\n startNodeId: string\n version: number\n nodes: Record<string, FlowNodeData>\n}\n\nexport interface FlowGraphSummary {\n key: string\n label: string\n nodeCount: number\n showInMenu: boolean\n menuOptionLabel?: string\n updatedAt: string\n}\n\n// Retorno de GetLiveFlowPositions (T4.2) — quantas sessões ativas estão em cada nó agora,\n// usado pelo editor para desenhar o \"liveCount\" nos cards (FlowNodeCardData.liveCount).\nexport interface LiveFlowPosition {\n flowKey: string\n nodeId: string\n count: number\n}\n\n// Validação de runtime do grafo. O `nodes` é um jsonb que entrou pelo editor (dado de origem\n// cliente) e sai do banco como `unknown` — sem parse, um grafo malformado só se manifestaria\n// como comportamento estranho lá dentro do interpretador, longe da causa.\n//\n// Permissivo de propósito em dois pontos: `actionKind` é string aberta (o host registra os\n// seus) e `.passthrough()` deixa passar campos extras — um grafo salvo por uma versão mais nova\n// do editor não pode ficar irrecuperável numa versão mais antiga do módulo.\nexport const flowNodeNextSchema = z.union([\n z.string(),\n z.object({ byAnswer: z.record(z.string(), z.string()), default: z.string() }),\n])\n\nexport const flowNodeDataSchema = z\n .object({\n id: z.string(),\n type: z.enum(['question', 'entrada_choice', 'action', 'menu', 'condition']),\n contextKey: z.string().optional(),\n questionType: z.enum(['text', 'money', 'date', 'int', 'cpf', 'choice']).optional(),\n question: z.string().optional(),\n options: z.array(z.tuple([z.string(), z.string()])).optional(),\n actionKind: z.string().optional(),\n actionParams: z.record(z.string(), z.unknown()).optional(),\n directMessage: z.string().optional(),\n fallbackMessage: z.string().optional(),\n conditionContextKey: z.string().optional(),\n conditionOperator: z.enum(['>', '>=', '<', '<=', '==', '!=', 'contains']).optional(),\n conditionValue: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n next: flowNodeNextSchema.optional(),\n })\n .passthrough()\n\nexport const flowGraphNodesSchema = z.record(z.string(), flowNodeDataSchema)\n","// Hierarquia de erros do módulo — não depende do framework de erros do host (nenhum DomainError\n// importado); o host mapeia estes para o seu próprio formato de resposta no exception filter.\nexport class MetaWhatsAppError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'MetaWhatsAppError'\n }\n}\n\nexport const META_WHATSAPP_ERROR_CODES = {\n WINDOW_EXPIRED: 'META_WHATSAPP_WINDOW_EXPIRED',\n INVALID_SIGNATURE: 'META_WHATSAPP_INVALID_SIGNATURE',\n DUPLICATE_DELIVERY: 'META_WHATSAPP_DUPLICATE_DELIVERY',\n CONFIG_MISSING: 'META_WHATSAPP_CONFIG_MISSING',\n TEMPLATE_NOT_CONFIGURED: 'META_WHATSAPP_TEMPLATE_NOT_CONFIGURED',\n SESSION_NOT_FOUND: 'META_WHATSAPP_SESSION_NOT_FOUND',\n AUDIO_NOT_INGESTED: 'META_WHATSAPP_AUDIO_NOT_INGESTED',\n MESSAGE_NOT_AUDIO: 'META_WHATSAPP_MESSAGE_NOT_AUDIO',\n TRANSCRIPTION_DISABLED: 'META_WHATSAPP_TRANSCRIPTION_DISABLED',\n} as const\n\nexport class WindowExpiredError extends MetaWhatsAppError {\n constructor(public readonly hoursSinceLastMessage?: number) {\n super(\n 'Janela de 24h expirada — não é possível enviar mensagem para este contato fora de template.',\n 400,\n META_WHATSAPP_ERROR_CODES.WINDOW_EXPIRED,\n { hoursSinceLastMessage },\n )\n }\n}\n\n// Assinatura HMAC do webhook não confere — Meta ou um terceiro tentando forjar uma entrega.\nexport class InvalidWebhookSignatureError extends MetaWhatsAppError {\n constructor() {\n super('Assinatura do webhook inválida.', 401, META_WHATSAPP_ERROR_CODES.INVALID_SIGNATURE)\n }\n}\n\n// Meta reenviou uma entrega dentro da janela de replay (nonce já visto) — não é erro do\n// cliente, é comportamento esperado da Cloud API; o host deve responder 200 e ignorar.\nexport class DuplicateWebhookDeliveryError extends MetaWhatsAppError {\n constructor(nonce: string) {\n super('Entrega de webhook duplicada.', 409, META_WHATSAPP_ERROR_CODES.DUPLICATE_DELIVERY, { nonce })\n }\n}\n\nexport class ConfigMissingError extends MetaWhatsAppError {\n constructor(missingField: string) {\n super(`Configuração do WhatsApp ausente: ${missingField}.`, 503, META_WHATSAPP_ERROR_CODES.CONFIG_MISSING, {\n missingField,\n })\n }\n}\n\nexport class TemplateNotConfiguredError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Nenhum template de WhatsApp configurado para reabertura de janela.',\n 503,\n META_WHATSAPP_ERROR_CODES.TEMPLATE_NOT_CONFIGURED,\n )\n }\n}\n\nexport class SessionNotFoundError extends MetaWhatsAppError {\n constructor(whatsappNumber: string) {\n super(\n `Sessão de conversa não encontrada para ${whatsappNumber}.`,\n 404,\n META_WHATSAPP_ERROR_CODES.SESSION_NOT_FOUND,\n { whatsappNumber },\n )\n }\n}\n\n/**\n * Pediram transcrição de um áudio que ainda não foi copiado da Meta para o storage.\n *\n * 409 e não 404: a mensagem existe e o áudio vai chegar — a ingestão é assíncrona e o atendente\n * simplesmente clicou antes de ela terminar. É o único erro de transcrição em que \"tente de novo em\n * alguns segundos\" é a orientação correta para a interface.\n */\nexport class AudioNotIngestedError extends MetaWhatsAppError {\n constructor(messageId: string) {\n super(\n 'Áudio ainda está sendo copiado — tente novamente em alguns segundos.',\n 409,\n META_WHATSAPP_ERROR_CODES.AUDIO_NOT_INGESTED,\n { messageId },\n )\n }\n}\n\n/**\n * Transcrição está desligada para esta empresa.\n *\n * Guarda de última linha, não o caminho normal: o painel lê as configurações e nem desenha o botão\n * quando está desligado. Existe para o caso de a tela estar com dado velho, ou de alguém chamar a\n * rota direto — e é `409` (estado atual conflita com a operação), não `403`: ninguém está sem\n * permissão, o recurso está desligado por escolha e liga a qualquer momento.\n */\nexport class TranscriptionDisabledError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Transcrição de áudio está desligada para esta empresa.',\n 409,\n META_WHATSAPP_ERROR_CODES.TRANSCRIPTION_DISABLED,\n )\n }\n}\n\n/** Pediram transcrição de mensagem que não é áudio. Nunca melhora com retentativa. */\nexport class MessageNotAudioError extends MetaWhatsAppError {\n constructor(messageId: string, type: string) {\n super(\n `Mensagem ${messageId} é do tipo \"${type}\" — só áudio é transcrito.`,\n 422,\n META_WHATSAPP_ERROR_CODES.MESSAGE_NOT_AUDIO,\n { messageId, type },\n )\n }\n}\n","/**\n * Mídia que veio do simulador de conversa, não da Meta.\n *\n * Vive no `contracts` porque é literalmente um contrato entre as duas pontas: o front gera o id, o\n * backend o resolve. Nasceu duplicado num produto (duas constantes iguais, cada uma com um\n * comentário pedindo \"precisa bater com a outra\") e por isso o segundo produto não herdou nada —\n * este pacote é o único lugar em que os dois lados já se encontram.\n *\n * O simulador manda **webhook**, e webhook da Meta carrega referência de mídia (um id), nunca o\n * binário. Então o arquivo gravado no navegador precisa existir em algum lugar que o servidor\n * alcance, e o id precisa dizer onde — é essa a convenção deste arquivo.\n *\n * Existe no pacote, e não em cada host, porque a convenção tem DOIS lados que precisam concordar\n * (quem gera o id no front, quem o resolve no backend). Onde ela ficou no host, o resultado foi\n * exatamente o previsível: duas constantes iguais em pacotes diferentes, cada uma com um comentário\n * pedindo \"precisa bater com a outra\" — e o segundo produto não herdou nada e ficou sem o recurso.\n */\n\n/**\n * Prefixo que marca o id como local.\n *\n * Dois-pontos no fim de propósito: id da Meta é alfanumérico com `_` e `-`, então o separador torna\n * a colisão impossível em vez de improvável.\n */\nexport const PREVIEW_MEDIA_ID_PREFIX = 'preview-upload:'\n\n/** Monta o id que o simulador manda no webhook. */\nexport function toPreviewMediaId(uploadId: string): string {\n return `${PREVIEW_MEDIA_ID_PREFIX}${uploadId}`\n}\n\n/**\n * `uploadId` embutido no id, ou `undefined` quando a mídia veio mesmo da Meta.\n *\n * Devolver `undefined` para id sem prefixo é o que mantém o caminho normal intacto: o adaptador só\n * desvia para o storage quando o prefixo está lá.\n */\nexport function resolvePreviewUploadId(mediaId: string): string | undefined {\n if (!mediaId.startsWith(PREVIEW_MEDIA_ID_PREFIX)) return undefined\n return mediaId.slice(PREVIEW_MEDIA_ID_PREFIX.length) || undefined\n}\n"],"mappings":"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","names":["whatsAppMediaSchema","z","object","id","string","mime_type","sha256","optional","caption","filename","whatsAppInteractiveSchema","type","button_reply","title","list_reply","whatsAppOrderSchema","catalog_id","text","product_items","array","product_retailer_id","quantity","number","item_price","currency","whatsAppMessageSchema","from","body","image","audio","video","document","sticker","interactive","order","context","referred_product","timestamp","whatsAppMessageEchoSchema","whatsAppMessageStatusSchema","enum","whatsAppStatusSchema","status","recipient_id","whatsAppWebhookPayloadSchema","entry","changes","value","messaging_product","messages","message_echoes","statuses","metadata","display_phone_number","phone_number_id","import_zod","FLOW_ACTION_KIND","HANDOFF","RATE_LIMITED_HANDOFF","SEND_PRODUCT_LIST","SEND_MEDIA","CROSS_FLOW_PREFIX","isCrossFlowTarget","target","startsWith","crossFlowKey","slice","length","flowNodeNextSchema","z","union","string","object","byAnswer","record","default","flowNodeDataSchema","id","type","enum","contextKey","optional","questionType","question","options","array","tuple","actionKind","actionParams","unknown","directMessage","fallbackMessage","conditionContextKey","conditionOperator","conditionValue","position","x","number","y","next","passthrough","flowGraphNodesSchema","MetaWhatsAppError","Error","message","statusCode","code","details","name","META_WHATSAPP_ERROR_CODES","WINDOW_EXPIRED","INVALID_SIGNATURE","DUPLICATE_DELIVERY","CONFIG_MISSING","TEMPLATE_NOT_CONFIGURED","SESSION_NOT_FOUND","AUDIO_NOT_INGESTED","MESSAGE_NOT_AUDIO","TRANSCRIPTION_DISABLED","WindowExpiredError","hoursSinceLastMessage","InvalidWebhookSignatureError","DuplicateWebhookDeliveryError","nonce","ConfigMissingError","missingField","TemplateNotConfiguredError","SessionNotFoundError","whatsappNumber","AudioNotIngestedError","messageId","TranscriptionDisabledError","MessageNotAudioError","type","PREVIEW_MEDIA_ID_PREFIX","toPreviewMediaId","uploadId","resolvePreviewUploadId","mediaId","startsWith","undefined","slice","length"]}
package/dist/index.d.cts CHANGED
@@ -338,12 +338,33 @@ declare const flowGraphNodesSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
338
338
  }>]>>;
339
339
  }, z.ZodTypeAny, "passthrough">>>;
340
340
 
341
+ /**
342
+ * Quando transcrever nota de voz.
343
+ *
344
+ * Vive no contrato, e não só no módulo, porque atravessa três fronteiras: o painel escolhe, a API
345
+ * transporta e o módulo obedece. Duas definições concorrentes divergiriam no primeiro valor novo.
346
+ */
347
+ type TranscriptionMode = 'auto' | 'onDemand';
341
348
  interface WhatsAppSettings {
342
349
  templateName: string;
343
350
  templateLanguage: string;
344
351
  templateVariables: string[];
345
352
  welcomeMessage: string;
346
353
  farewellMessage: string;
354
+ /**
355
+ * Transcrição de áudio ligada para ESTA empresa.
356
+ *
357
+ * Tri-state de propósito: `null` significa "não decidido pelo painel", e aí vale o padrão que o
358
+ * host injetou (tipicamente ambiente). Se fosse `boolean` com padrão `false`, atualizar o módulo
359
+ * desligaria a transcrição de quem já a tinha ligada por variável de ambiente — regressão
360
+ * silenciosa num recurso que estava funcionando.
361
+ *
362
+ * Ligar aqui não cria capacidade: sem engine e chave injetados no host, não há o que transcrever.
363
+ * Ambiente decide se É POSSÍVEL; isto decide se é para FAZER.
364
+ */
365
+ transcriptionEnabled: boolean | null;
366
+ /** `null` = herda o modo injetado pelo host. */
367
+ transcriptionMode: TranscriptionMode | null;
347
368
  }
348
369
  interface TemplateVariablesMap {
349
370
  [index: number]: string;
@@ -407,7 +428,31 @@ interface MetaWhatsAppHooks {
407
428
  nodeId: string;
408
429
  uploadId: string;
409
430
  }) => void;
431
+ /**
432
+ * Transcrição de áudio falhou de forma **retriável** (cota estourada, rede, 5xx) — o áudio segue
433
+ * transcritível e alguém precisa tentar de novo.
434
+ *
435
+ * É hook, e não exceção propagada, porque a mídia JÁ foi copiada para o storage quando isto
436
+ * acontece: deixar o erro subir marcaria a ingestão inteira como falha e o job reprocessaria o
437
+ * download de um binário que está salvo. E é hook, e não retentativa interna, porque o módulo não
438
+ * tem fila — quem sabe reenfileirar com atraso é o host, que já tem uma.
439
+ *
440
+ * Sem implementar, o áudio fica com `transcription_status = 'pending'` e nada o retoma: a
441
+ * transcrição não se perde nem mente, mas só sai se pedirem sob demanda.
442
+ */
443
+ onTranscriptionDeferred?: (details: TranscriptionDeferredDescriptor) => Promise<void> | void;
410
444
  }
445
+ type TranscriptionDeferredDescriptor = {
446
+ readonly companyId: string;
447
+ readonly messageId: string;
448
+ readonly whatsappNumber: string;
449
+ /** Onde o áudio já está salvo — quem retomar lê daqui, não da Meta (cuja URL expira). */
450
+ readonly uploadId: string;
451
+ /** Do `Retry-After` do engine, quando informado. É o intervalo mínimo a respeitar. */
452
+ readonly retryAfterSeconds?: number;
453
+ readonly reason: 'rate-limited' | 'transient-failure';
454
+ readonly error: unknown;
455
+ };
411
456
  type InboundMediaDescriptor = {
412
457
  readonly companyId: string;
413
458
  readonly messageId: string;
@@ -516,6 +561,22 @@ interface ObjectStorageInterface {
516
561
  interface RealtimeNotifierInterface {
517
562
  emit(channel: string, event: string, payload: Record<string, unknown>): void;
518
563
  }
564
+ /**
565
+ * Cache chave-valor do host (Redis, Memcached, o que for). Injetado como todo o resto de
566
+ * ambiente: o módulo não abre conexão própria nem decide onde o dado mora.
567
+ *
568
+ * Precisa ser COMPARTILHADO entre instâncias. Um cache em memória por processo faria cada
569
+ * instância servir uma versão diferente do mesmo fluxo depois de uma publicação, e a invalidação
570
+ * de uma não alcançaria as outras — o cliente cairia no grafo velho ou no novo conforme o
571
+ * balanceador.
572
+ *
573
+ * Valores são texto: serializar é responsabilidade de quem usa, para o contrato não impor formato.
574
+ */
575
+ interface CacheInterface {
576
+ get(key: string): Promise<string | null>;
577
+ set(key: string, value: string, ttlSeconds?: number): Promise<void>;
578
+ delete(key: string): Promise<void>;
579
+ }
519
580
  type FlowActionResult = {
520
581
  next?: string;
521
582
  context?: Record<string, unknown>;
@@ -543,6 +604,9 @@ declare const META_WHATSAPP_ERROR_CODES: {
543
604
  readonly CONFIG_MISSING: "META_WHATSAPP_CONFIG_MISSING";
544
605
  readonly TEMPLATE_NOT_CONFIGURED: "META_WHATSAPP_TEMPLATE_NOT_CONFIGURED";
545
606
  readonly SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND";
607
+ readonly AUDIO_NOT_INGESTED: "META_WHATSAPP_AUDIO_NOT_INGESTED";
608
+ readonly MESSAGE_NOT_AUDIO: "META_WHATSAPP_MESSAGE_NOT_AUDIO";
609
+ readonly TRANSCRIPTION_DISABLED: "META_WHATSAPP_TRANSCRIPTION_DISABLED";
546
610
  };
547
611
  declare class WindowExpiredError extends MetaWhatsAppError {
548
612
  readonly hoursSinceLastMessage?: number | undefined;
@@ -563,5 +627,64 @@ declare class TemplateNotConfiguredError extends MetaWhatsAppError {
563
627
  declare class SessionNotFoundError extends MetaWhatsAppError {
564
628
  constructor(whatsappNumber: string);
565
629
  }
630
+ /**
631
+ * Pediram transcrição de um áudio que ainda não foi copiado da Meta para o storage.
632
+ *
633
+ * 409 e não 404: a mensagem existe e o áudio vai chegar — a ingestão é assíncrona e o atendente
634
+ * simplesmente clicou antes de ela terminar. É o único erro de transcrição em que "tente de novo em
635
+ * alguns segundos" é a orientação correta para a interface.
636
+ */
637
+ declare class AudioNotIngestedError extends MetaWhatsAppError {
638
+ constructor(messageId: string);
639
+ }
640
+ /**
641
+ * Transcrição está desligada para esta empresa.
642
+ *
643
+ * Guarda de última linha, não o caminho normal: o painel lê as configurações e nem desenha o botão
644
+ * quando está desligado. Existe para o caso de a tela estar com dado velho, ou de alguém chamar a
645
+ * rota direto — e é `409` (estado atual conflita com a operação), não `403`: ninguém está sem
646
+ * permissão, o recurso está desligado por escolha e liga a qualquer momento.
647
+ */
648
+ declare class TranscriptionDisabledError extends MetaWhatsAppError {
649
+ constructor();
650
+ }
651
+ /** Pediram transcrição de mensagem que não é áudio. Nunca melhora com retentativa. */
652
+ declare class MessageNotAudioError extends MetaWhatsAppError {
653
+ constructor(messageId: string, type: string);
654
+ }
655
+
656
+ /**
657
+ * Mídia que veio do simulador de conversa, não da Meta.
658
+ *
659
+ * Vive no `contracts` porque é literalmente um contrato entre as duas pontas: o front gera o id, o
660
+ * backend o resolve. Nasceu duplicado num produto (duas constantes iguais, cada uma com um
661
+ * comentário pedindo "precisa bater com a outra") e por isso o segundo produto não herdou nada —
662
+ * este pacote é o único lugar em que os dois lados já se encontram.
663
+ *
664
+ * O simulador manda **webhook**, e webhook da Meta carrega referência de mídia (um id), nunca o
665
+ * binário. Então o arquivo gravado no navegador precisa existir em algum lugar que o servidor
666
+ * alcance, e o id precisa dizer onde — é essa a convenção deste arquivo.
667
+ *
668
+ * Existe no pacote, e não em cada host, porque a convenção tem DOIS lados que precisam concordar
669
+ * (quem gera o id no front, quem o resolve no backend). Onde ela ficou no host, o resultado foi
670
+ * exatamente o previsível: duas constantes iguais em pacotes diferentes, cada uma com um comentário
671
+ * pedindo "precisa bater com a outra" — e o segundo produto não herdou nada e ficou sem o recurso.
672
+ */
673
+ /**
674
+ * Prefixo que marca o id como local.
675
+ *
676
+ * Dois-pontos no fim de propósito: id da Meta é alfanumérico com `_` e `-`, então o separador torna
677
+ * a colisão impossível em vez de improvável.
678
+ */
679
+ declare const PREVIEW_MEDIA_ID_PREFIX = "preview-upload:";
680
+ /** Monta o id que o simulador manda no webhook. */
681
+ declare function toPreviewMediaId(uploadId: string): string;
682
+ /**
683
+ * `uploadId` embutido no id, ou `undefined` quando a mídia veio mesmo da Meta.
684
+ *
685
+ * Devolver `undefined` para id sem prefixo é o que mantém o caminho normal intacto: o adaptador só
686
+ * desvia para o storage quando o prefixo está lá.
687
+ */
688
+ declare function resolvePreviewUploadId(mediaId: string): string | undefined;
566
689
 
567
- export { CROSS_FLOW_PREFIX, type CatalogPort, type CatalogProduct, type ChannelAdapterInterface, ConfigMissingError, type ConversationMessage, type ConversationSession, type ConversationSummary, type CreateTemplateResult, DuplicateWebhookDeliveryError, FLOW_ACTION_KIND, type FlowActionHandler, type FlowActionKind, type FlowActionRegistry, type FlowActionResult, type FlowConditionOperator, type FlowGraphData, type FlowGraphSummary, type FlowNodeData, type FlowNodeNext, type FlowNodeType, type FlowQuestionType, type InboundMediaDescriptor, InvalidWebhookSignatureError, type ListConversationsParams, type ListMessagesParams, type LiveFlowPosition, META_WHATSAPP_ERROR_CODES, type MessageDirection, type MessageHookOutcome, type MessageSender, type MessageStatus, MetaWhatsAppError, type MetaWhatsAppHooks, type ObjectStorageInterface, type RealtimeNotifierInterface, type SessionMode, SessionNotFoundError, type SessionState, type SubjectResolverInterface, type TemplateConfig, TemplateNotConfiguredError, type TemplateVariablesMap, WhatsAppMessage, type WhatsAppSettings, WhatsAppStatus, type WhatsAppTemplateSummary, WindowExpiredError, crossFlowKey, flowGraphNodesSchema, flowNodeDataSchema, flowNodeNextSchema, isCrossFlowTarget };
690
+ export { AudioNotIngestedError, CROSS_FLOW_PREFIX, type CacheInterface, type CatalogPort, type CatalogProduct, type ChannelAdapterInterface, ConfigMissingError, type ConversationMessage, type ConversationSession, type ConversationSummary, type CreateTemplateResult, DuplicateWebhookDeliveryError, FLOW_ACTION_KIND, type FlowActionHandler, type FlowActionKind, type FlowActionRegistry, type FlowActionResult, type FlowConditionOperator, type FlowGraphData, type FlowGraphSummary, type FlowNodeData, type FlowNodeNext, type FlowNodeType, type FlowQuestionType, type InboundMediaDescriptor, InvalidWebhookSignatureError, type ListConversationsParams, type ListMessagesParams, type LiveFlowPosition, META_WHATSAPP_ERROR_CODES, type MessageDirection, type MessageHookOutcome, MessageNotAudioError, type MessageSender, type MessageStatus, MetaWhatsAppError, type MetaWhatsAppHooks, type ObjectStorageInterface, PREVIEW_MEDIA_ID_PREFIX, type RealtimeNotifierInterface, type SessionMode, SessionNotFoundError, type SessionState, type SubjectResolverInterface, type TemplateConfig, TemplateNotConfiguredError, type TemplateVariablesMap, type TranscriptionDeferredDescriptor, TranscriptionDisabledError, type TranscriptionMode, WhatsAppMessage, type WhatsAppSettings, WhatsAppStatus, type WhatsAppTemplateSummary, WindowExpiredError, crossFlowKey, flowGraphNodesSchema, flowNodeDataSchema, flowNodeNextSchema, isCrossFlowTarget, resolvePreviewUploadId, toPreviewMediaId };
package/dist/index.d.ts CHANGED
@@ -338,12 +338,33 @@ declare const flowGraphNodesSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
338
338
  }>]>>;
339
339
  }, z.ZodTypeAny, "passthrough">>>;
340
340
 
341
+ /**
342
+ * Quando transcrever nota de voz.
343
+ *
344
+ * Vive no contrato, e não só no módulo, porque atravessa três fronteiras: o painel escolhe, a API
345
+ * transporta e o módulo obedece. Duas definições concorrentes divergiriam no primeiro valor novo.
346
+ */
347
+ type TranscriptionMode = 'auto' | 'onDemand';
341
348
  interface WhatsAppSettings {
342
349
  templateName: string;
343
350
  templateLanguage: string;
344
351
  templateVariables: string[];
345
352
  welcomeMessage: string;
346
353
  farewellMessage: string;
354
+ /**
355
+ * Transcrição de áudio ligada para ESTA empresa.
356
+ *
357
+ * Tri-state de propósito: `null` significa "não decidido pelo painel", e aí vale o padrão que o
358
+ * host injetou (tipicamente ambiente). Se fosse `boolean` com padrão `false`, atualizar o módulo
359
+ * desligaria a transcrição de quem já a tinha ligada por variável de ambiente — regressão
360
+ * silenciosa num recurso que estava funcionando.
361
+ *
362
+ * Ligar aqui não cria capacidade: sem engine e chave injetados no host, não há o que transcrever.
363
+ * Ambiente decide se É POSSÍVEL; isto decide se é para FAZER.
364
+ */
365
+ transcriptionEnabled: boolean | null;
366
+ /** `null` = herda o modo injetado pelo host. */
367
+ transcriptionMode: TranscriptionMode | null;
347
368
  }
348
369
  interface TemplateVariablesMap {
349
370
  [index: number]: string;
@@ -407,7 +428,31 @@ interface MetaWhatsAppHooks {
407
428
  nodeId: string;
408
429
  uploadId: string;
409
430
  }) => void;
431
+ /**
432
+ * Transcrição de áudio falhou de forma **retriável** (cota estourada, rede, 5xx) — o áudio segue
433
+ * transcritível e alguém precisa tentar de novo.
434
+ *
435
+ * É hook, e não exceção propagada, porque a mídia JÁ foi copiada para o storage quando isto
436
+ * acontece: deixar o erro subir marcaria a ingestão inteira como falha e o job reprocessaria o
437
+ * download de um binário que está salvo. E é hook, e não retentativa interna, porque o módulo não
438
+ * tem fila — quem sabe reenfileirar com atraso é o host, que já tem uma.
439
+ *
440
+ * Sem implementar, o áudio fica com `transcription_status = 'pending'` e nada o retoma: a
441
+ * transcrição não se perde nem mente, mas só sai se pedirem sob demanda.
442
+ */
443
+ onTranscriptionDeferred?: (details: TranscriptionDeferredDescriptor) => Promise<void> | void;
410
444
  }
445
+ type TranscriptionDeferredDescriptor = {
446
+ readonly companyId: string;
447
+ readonly messageId: string;
448
+ readonly whatsappNumber: string;
449
+ /** Onde o áudio já está salvo — quem retomar lê daqui, não da Meta (cuja URL expira). */
450
+ readonly uploadId: string;
451
+ /** Do `Retry-After` do engine, quando informado. É o intervalo mínimo a respeitar. */
452
+ readonly retryAfterSeconds?: number;
453
+ readonly reason: 'rate-limited' | 'transient-failure';
454
+ readonly error: unknown;
455
+ };
411
456
  type InboundMediaDescriptor = {
412
457
  readonly companyId: string;
413
458
  readonly messageId: string;
@@ -516,6 +561,22 @@ interface ObjectStorageInterface {
516
561
  interface RealtimeNotifierInterface {
517
562
  emit(channel: string, event: string, payload: Record<string, unknown>): void;
518
563
  }
564
+ /**
565
+ * Cache chave-valor do host (Redis, Memcached, o que for). Injetado como todo o resto de
566
+ * ambiente: o módulo não abre conexão própria nem decide onde o dado mora.
567
+ *
568
+ * Precisa ser COMPARTILHADO entre instâncias. Um cache em memória por processo faria cada
569
+ * instância servir uma versão diferente do mesmo fluxo depois de uma publicação, e a invalidação
570
+ * de uma não alcançaria as outras — o cliente cairia no grafo velho ou no novo conforme o
571
+ * balanceador.
572
+ *
573
+ * Valores são texto: serializar é responsabilidade de quem usa, para o contrato não impor formato.
574
+ */
575
+ interface CacheInterface {
576
+ get(key: string): Promise<string | null>;
577
+ set(key: string, value: string, ttlSeconds?: number): Promise<void>;
578
+ delete(key: string): Promise<void>;
579
+ }
519
580
  type FlowActionResult = {
520
581
  next?: string;
521
582
  context?: Record<string, unknown>;
@@ -543,6 +604,9 @@ declare const META_WHATSAPP_ERROR_CODES: {
543
604
  readonly CONFIG_MISSING: "META_WHATSAPP_CONFIG_MISSING";
544
605
  readonly TEMPLATE_NOT_CONFIGURED: "META_WHATSAPP_TEMPLATE_NOT_CONFIGURED";
545
606
  readonly SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND";
607
+ readonly AUDIO_NOT_INGESTED: "META_WHATSAPP_AUDIO_NOT_INGESTED";
608
+ readonly MESSAGE_NOT_AUDIO: "META_WHATSAPP_MESSAGE_NOT_AUDIO";
609
+ readonly TRANSCRIPTION_DISABLED: "META_WHATSAPP_TRANSCRIPTION_DISABLED";
546
610
  };
547
611
  declare class WindowExpiredError extends MetaWhatsAppError {
548
612
  readonly hoursSinceLastMessage?: number | undefined;
@@ -563,5 +627,64 @@ declare class TemplateNotConfiguredError extends MetaWhatsAppError {
563
627
  declare class SessionNotFoundError extends MetaWhatsAppError {
564
628
  constructor(whatsappNumber: string);
565
629
  }
630
+ /**
631
+ * Pediram transcrição de um áudio que ainda não foi copiado da Meta para o storage.
632
+ *
633
+ * 409 e não 404: a mensagem existe e o áudio vai chegar — a ingestão é assíncrona e o atendente
634
+ * simplesmente clicou antes de ela terminar. É o único erro de transcrição em que "tente de novo em
635
+ * alguns segundos" é a orientação correta para a interface.
636
+ */
637
+ declare class AudioNotIngestedError extends MetaWhatsAppError {
638
+ constructor(messageId: string);
639
+ }
640
+ /**
641
+ * Transcrição está desligada para esta empresa.
642
+ *
643
+ * Guarda de última linha, não o caminho normal: o painel lê as configurações e nem desenha o botão
644
+ * quando está desligado. Existe para o caso de a tela estar com dado velho, ou de alguém chamar a
645
+ * rota direto — e é `409` (estado atual conflita com a operação), não `403`: ninguém está sem
646
+ * permissão, o recurso está desligado por escolha e liga a qualquer momento.
647
+ */
648
+ declare class TranscriptionDisabledError extends MetaWhatsAppError {
649
+ constructor();
650
+ }
651
+ /** Pediram transcrição de mensagem que não é áudio. Nunca melhora com retentativa. */
652
+ declare class MessageNotAudioError extends MetaWhatsAppError {
653
+ constructor(messageId: string, type: string);
654
+ }
655
+
656
+ /**
657
+ * Mídia que veio do simulador de conversa, não da Meta.
658
+ *
659
+ * Vive no `contracts` porque é literalmente um contrato entre as duas pontas: o front gera o id, o
660
+ * backend o resolve. Nasceu duplicado num produto (duas constantes iguais, cada uma com um
661
+ * comentário pedindo "precisa bater com a outra") e por isso o segundo produto não herdou nada —
662
+ * este pacote é o único lugar em que os dois lados já se encontram.
663
+ *
664
+ * O simulador manda **webhook**, e webhook da Meta carrega referência de mídia (um id), nunca o
665
+ * binário. Então o arquivo gravado no navegador precisa existir em algum lugar que o servidor
666
+ * alcance, e o id precisa dizer onde — é essa a convenção deste arquivo.
667
+ *
668
+ * Existe no pacote, e não em cada host, porque a convenção tem DOIS lados que precisam concordar
669
+ * (quem gera o id no front, quem o resolve no backend). Onde ela ficou no host, o resultado foi
670
+ * exatamente o previsível: duas constantes iguais em pacotes diferentes, cada uma com um comentário
671
+ * pedindo "precisa bater com a outra" — e o segundo produto não herdou nada e ficou sem o recurso.
672
+ */
673
+ /**
674
+ * Prefixo que marca o id como local.
675
+ *
676
+ * Dois-pontos no fim de propósito: id da Meta é alfanumérico com `_` e `-`, então o separador torna
677
+ * a colisão impossível em vez de improvável.
678
+ */
679
+ declare const PREVIEW_MEDIA_ID_PREFIX = "preview-upload:";
680
+ /** Monta o id que o simulador manda no webhook. */
681
+ declare function toPreviewMediaId(uploadId: string): string;
682
+ /**
683
+ * `uploadId` embutido no id, ou `undefined` quando a mídia veio mesmo da Meta.
684
+ *
685
+ * Devolver `undefined` para id sem prefixo é o que mantém o caminho normal intacto: o adaptador só
686
+ * desvia para o storage quando o prefixo está lá.
687
+ */
688
+ declare function resolvePreviewUploadId(mediaId: string): string | undefined;
566
689
 
567
- export { CROSS_FLOW_PREFIX, type CatalogPort, type CatalogProduct, type ChannelAdapterInterface, ConfigMissingError, type ConversationMessage, type ConversationSession, type ConversationSummary, type CreateTemplateResult, DuplicateWebhookDeliveryError, FLOW_ACTION_KIND, type FlowActionHandler, type FlowActionKind, type FlowActionRegistry, type FlowActionResult, type FlowConditionOperator, type FlowGraphData, type FlowGraphSummary, type FlowNodeData, type FlowNodeNext, type FlowNodeType, type FlowQuestionType, type InboundMediaDescriptor, InvalidWebhookSignatureError, type ListConversationsParams, type ListMessagesParams, type LiveFlowPosition, META_WHATSAPP_ERROR_CODES, type MessageDirection, type MessageHookOutcome, type MessageSender, type MessageStatus, MetaWhatsAppError, type MetaWhatsAppHooks, type ObjectStorageInterface, type RealtimeNotifierInterface, type SessionMode, SessionNotFoundError, type SessionState, type SubjectResolverInterface, type TemplateConfig, TemplateNotConfiguredError, type TemplateVariablesMap, WhatsAppMessage, type WhatsAppSettings, WhatsAppStatus, type WhatsAppTemplateSummary, WindowExpiredError, crossFlowKey, flowGraphNodesSchema, flowNodeDataSchema, flowNodeNextSchema, isCrossFlowTarget };
690
+ export { AudioNotIngestedError, CROSS_FLOW_PREFIX, type CacheInterface, type CatalogPort, type CatalogProduct, type ChannelAdapterInterface, ConfigMissingError, type ConversationMessage, type ConversationSession, type ConversationSummary, type CreateTemplateResult, DuplicateWebhookDeliveryError, FLOW_ACTION_KIND, type FlowActionHandler, type FlowActionKind, type FlowActionRegistry, type FlowActionResult, type FlowConditionOperator, type FlowGraphData, type FlowGraphSummary, type FlowNodeData, type FlowNodeNext, type FlowNodeType, type FlowQuestionType, type InboundMediaDescriptor, InvalidWebhookSignatureError, type ListConversationsParams, type ListMessagesParams, type LiveFlowPosition, META_WHATSAPP_ERROR_CODES, type MessageDirection, type MessageHookOutcome, MessageNotAudioError, type MessageSender, type MessageStatus, MetaWhatsAppError, type MetaWhatsAppHooks, type ObjectStorageInterface, PREVIEW_MEDIA_ID_PREFIX, type RealtimeNotifierInterface, type SessionMode, SessionNotFoundError, type SessionState, type SubjectResolverInterface, type TemplateConfig, TemplateNotConfiguredError, type TemplateVariablesMap, type TranscriptionDeferredDescriptor, TranscriptionDisabledError, type TranscriptionMode, WhatsAppMessage, type WhatsAppSettings, WhatsAppStatus, type WhatsAppTemplateSummary, WindowExpiredError, crossFlowKey, flowGraphNodesSchema, flowNodeDataSchema, flowNodeNextSchema, isCrossFlowTarget, resolvePreviewUploadId, toPreviewMediaId };
package/dist/index.js CHANGED
@@ -177,7 +177,10 @@ var META_WHATSAPP_ERROR_CODES = {
177
177
  DUPLICATE_DELIVERY: "META_WHATSAPP_DUPLICATE_DELIVERY",
178
178
  CONFIG_MISSING: "META_WHATSAPP_CONFIG_MISSING",
179
179
  TEMPLATE_NOT_CONFIGURED: "META_WHATSAPP_TEMPLATE_NOT_CONFIGURED",
180
- SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND"
180
+ SESSION_NOT_FOUND: "META_WHATSAPP_SESSION_NOT_FOUND",
181
+ AUDIO_NOT_INGESTED: "META_WHATSAPP_AUDIO_NOT_INGESTED",
182
+ MESSAGE_NOT_AUDIO: "META_WHATSAPP_MESSAGE_NOT_AUDIO",
183
+ TRANSCRIPTION_DISABLED: "META_WHATSAPP_TRANSCRIPTION_DISABLED"
181
184
  };
182
185
  var WindowExpiredError = class extends MetaWhatsAppError {
183
186
  static {
@@ -236,22 +239,69 @@ var SessionNotFoundError = class extends MetaWhatsAppError {
236
239
  });
237
240
  }
238
241
  };
242
+ var AudioNotIngestedError = class extends MetaWhatsAppError {
243
+ static {
244
+ __name(this, "AudioNotIngestedError");
245
+ }
246
+ constructor(messageId) {
247
+ super("\xC1udio ainda est\xE1 sendo copiado \u2014 tente novamente em alguns segundos.", 409, META_WHATSAPP_ERROR_CODES.AUDIO_NOT_INGESTED, {
248
+ messageId
249
+ });
250
+ }
251
+ };
252
+ var TranscriptionDisabledError = class extends MetaWhatsAppError {
253
+ static {
254
+ __name(this, "TranscriptionDisabledError");
255
+ }
256
+ constructor() {
257
+ super("Transcri\xE7\xE3o de \xE1udio est\xE1 desligada para esta empresa.", 409, META_WHATSAPP_ERROR_CODES.TRANSCRIPTION_DISABLED);
258
+ }
259
+ };
260
+ var MessageNotAudioError = class extends MetaWhatsAppError {
261
+ static {
262
+ __name(this, "MessageNotAudioError");
263
+ }
264
+ constructor(messageId, type) {
265
+ super(`Mensagem ${messageId} \xE9 do tipo "${type}" \u2014 s\xF3 \xE1udio \xE9 transcrito.`, 422, META_WHATSAPP_ERROR_CODES.MESSAGE_NOT_AUDIO, {
266
+ messageId,
267
+ type
268
+ });
269
+ }
270
+ };
271
+
272
+ // src/previewMedia.types.ts
273
+ var PREVIEW_MEDIA_ID_PREFIX = "preview-upload:";
274
+ function toPreviewMediaId(uploadId) {
275
+ return `${PREVIEW_MEDIA_ID_PREFIX}${uploadId}`;
276
+ }
277
+ __name(toPreviewMediaId, "toPreviewMediaId");
278
+ function resolvePreviewUploadId(mediaId) {
279
+ if (!mediaId.startsWith(PREVIEW_MEDIA_ID_PREFIX)) return void 0;
280
+ return mediaId.slice(PREVIEW_MEDIA_ID_PREFIX.length) || void 0;
281
+ }
282
+ __name(resolvePreviewUploadId, "resolvePreviewUploadId");
239
283
  export {
284
+ AudioNotIngestedError,
240
285
  CROSS_FLOW_PREFIX,
241
286
  ConfigMissingError,
242
287
  DuplicateWebhookDeliveryError,
243
288
  FLOW_ACTION_KIND,
244
289
  InvalidWebhookSignatureError,
245
290
  META_WHATSAPP_ERROR_CODES,
291
+ MessageNotAudioError,
246
292
  MetaWhatsAppError,
293
+ PREVIEW_MEDIA_ID_PREFIX,
247
294
  SessionNotFoundError,
248
295
  TemplateNotConfiguredError,
296
+ TranscriptionDisabledError,
249
297
  WindowExpiredError,
250
298
  crossFlowKey,
251
299
  flowGraphNodesSchema,
252
300
  flowNodeDataSchema,
253
301
  flowNodeNextSchema,
254
302
  isCrossFlowTarget,
303
+ resolvePreviewUploadId,
304
+ toPreviewMediaId,
255
305
  whatsAppInteractiveSchema,
256
306
  whatsAppMediaSchema,
257
307
  whatsAppMessageEchoSchema,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhook.types.ts","../src/flow.types.ts","../src/errors.ts"],"sourcesContent":["import { z } from 'zod'\n\n// Extraído de financiamento-imobiliario-bot/apps/api/src/modules/webhook/application/use-cases/\n// ReceiveWhatsAppWebhook.use-case.ts:39-113 — shape real do payload da Cloud API da Meta.\n\nexport const whatsAppMediaSchema = z.object({\n id: z.string(),\n mime_type: z.string(),\n sha256: z.string().optional(),\n caption: z.string().optional(),\n filename: z.string().optional(),\n})\nexport type WhatsAppMedia = z.infer<typeof whatsAppMediaSchema>\n\nexport const whatsAppInteractiveSchema = z.object({\n type: z.string(),\n button_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n list_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n})\nexport type WhatsAppInteractive = z.infer<typeof whatsAppInteractiveSchema>\n\nexport const whatsAppOrderSchema = z.object({\n catalog_id: z.string(),\n text: z.string().optional(),\n product_items: z.array(\n z.object({\n product_retailer_id: z.string(),\n quantity: z.number(),\n item_price: z.number(),\n currency: z.string(),\n }),\n ),\n})\nexport type WhatsAppOrder = z.infer<typeof whatsAppOrderSchema>\n\nexport const whatsAppMessageSchema = z.object({\n id: z.string(),\n from: z.string(),\n type: z.string(),\n text: z.object({ body: z.string() }).optional(),\n image: whatsAppMediaSchema.optional(),\n audio: whatsAppMediaSchema.optional(),\n video: whatsAppMediaSchema.optional(),\n document: whatsAppMediaSchema.optional(),\n sticker: whatsAppMediaSchema.optional(),\n interactive: whatsAppInteractiveSchema.optional(),\n // Pedido enviado pelo carrinho do catálogo (WhatsApp Commerce)\n order: whatsAppOrderSchema.optional(),\n // Presente quando o cliente abre um item do catálogo e manda mensagem pela página do produto\n context: z\n .object({\n from: z.string().optional(),\n id: z.string().optional(),\n referred_product: z.object({ catalog_id: z.string(), product_retailer_id: z.string() }).optional(),\n })\n .optional(),\n timestamp: z.string(),\n})\nexport type WhatsAppMessage = z.infer<typeof whatsAppMessageSchema>\n\nexport const whatsAppMessageEchoSchema = z.object({\n id: z.string(),\n from: z.string(),\n timestamp: z.string(),\n type: z.string(),\n})\nexport type WhatsAppMessageEcho = z.infer<typeof whatsAppMessageEchoSchema>\n\nexport const whatsAppMessageStatusSchema = z.enum(['sent', 'delivered', 'read', 'failed'])\nexport type WhatsAppMessageStatusValue = z.infer<typeof whatsAppMessageStatusSchema>\n\nexport const whatsAppStatusSchema = z.object({\n id: z.string(),\n status: whatsAppMessageStatusSchema,\n timestamp: z.string(),\n recipient_id: z.string().optional(),\n})\nexport type WhatsAppStatus = z.infer<typeof whatsAppStatusSchema>\n\nexport const whatsAppWebhookPayloadSchema = z.object({\n object: z.string(),\n entry: z.array(\n z.object({\n id: z.string(),\n changes: z.array(\n z.object({\n value: z.object({\n messaging_product: z.string(),\n messages: z.array(whatsAppMessageSchema).optional(),\n message_echoes: z.array(whatsAppMessageEchoSchema).optional(),\n statuses: z.array(whatsAppStatusSchema).optional(),\n metadata: z.object({ display_phone_number: z.string(), phone_number_id: z.string() }).optional(),\n }),\n }),\n ),\n }),\n ),\n})\nexport type WhatsAppWebhookPayload = z.infer<typeof whatsAppWebhookPayloadSchema>\n","// Mesmo shape de dados do editor visual (@adatechnology/conversations-ui/flows) — o backend\n// interpreta/persiste o grafo, o frontend edita; os dois lados precisam concordar no formato.\n// `FlowActionKind` é string aberta (não union fechada): o host registra os próprios actions\n// (ex.: 'trigger_simulation' no bot) via registerFlowAction() no módulo (T4.3) — o pacote nunca\n// assume nenhum caso de negócio específico.\nimport { z } from 'zod'\n\nexport type FlowNodeType = 'question' | 'entrada_choice' | 'action' | 'menu' | 'condition'\nexport type FlowQuestionType = 'text' | 'money' | 'date' | 'int' | 'cpf' | 'choice'\nexport type FlowActionKind = string\n\n// Os únicos `actionKind` que o próprio pacote implementa — todo o resto é do host. Ficam aqui, e\n// não no editor, porque o mesmo literal é usado nos dois lados (o editor oferece na paleta, o\n// módulo registra o handler) e duas cópias divergem sem nada acusar.\nexport const FLOW_ACTION_KIND = {\n HANDOFF: 'handoff',\n RATE_LIMITED_HANDOFF: 'rate_limited_handoff',\n SEND_PRODUCT_LIST: 'send_product_list',\n SEND_MEDIA: 'send_media',\n} as const\nexport type FlowConditionOperator = '>' | '>=' | '<' | '<=' | '==' | '!=' | 'contains'\nexport type FlowNodeNext = string | { byAnswer: Record<string, string>; default: string }\n\n// Destinos \"flow:<key>\" são saltos para outro fluxo — mesma convenção usada pelo editor visual\n// (conversations-ui/flows/flowGraph.ts); backend e frontend precisam concordar no formato.\nexport const CROSS_FLOW_PREFIX = 'flow:'\nexport const isCrossFlowTarget = (target: string): boolean => target.startsWith(CROSS_FLOW_PREFIX)\nexport const crossFlowKey = (target: string): string => target.slice(CROSS_FLOW_PREFIX.length)\n\nexport type FlowNodeData = {\n id: string\n type: FlowNodeType\n contextKey?: string\n questionType?: FlowQuestionType\n question?: string\n options?: [string, string][]\n actionKind?: FlowActionKind\n // Parâmetros arbitrários que o nó carrega para o handler registrado em `actionKind`. O pacote\n // nunca os interpreta — é o que permite um nó de ação ser configurado pelo editor sem que o\n // vocabulário do produto (o antigo `simulationTemplate`) vaze para dentro do contrato.\n actionParams?: Record<string, unknown>\n directMessage?: string\n fallbackMessage?: string\n conditionContextKey?: string\n conditionOperator?: FlowConditionOperator\n conditionValue?: string\n position?: { x: number; y: number }\n next?: FlowNodeNext\n}\n\nexport interface FlowGraphData {\n key: string\n label: string\n startNodeId: string\n version: number\n nodes: Record<string, FlowNodeData>\n}\n\nexport interface FlowGraphSummary {\n key: string\n label: string\n nodeCount: number\n showInMenu: boolean\n menuOptionLabel?: string\n updatedAt: string\n}\n\n// Retorno de GetLiveFlowPositions (T4.2) — quantas sessões ativas estão em cada nó agora,\n// usado pelo editor para desenhar o \"liveCount\" nos cards (FlowNodeCardData.liveCount).\nexport interface LiveFlowPosition {\n flowKey: string\n nodeId: string\n count: number\n}\n\n// Validação de runtime do grafo. O `nodes` é um jsonb que entrou pelo editor (dado de origem\n// cliente) e sai do banco como `unknown` — sem parse, um grafo malformado só se manifestaria\n// como comportamento estranho lá dentro do interpretador, longe da causa.\n//\n// Permissivo de propósito em dois pontos: `actionKind` é string aberta (o host registra os\n// seus) e `.passthrough()` deixa passar campos extras — um grafo salvo por uma versão mais nova\n// do editor não pode ficar irrecuperável numa versão mais antiga do módulo.\nexport const flowNodeNextSchema = z.union([\n z.string(),\n z.object({ byAnswer: z.record(z.string(), z.string()), default: z.string() }),\n])\n\nexport const flowNodeDataSchema = z\n .object({\n id: z.string(),\n type: z.enum(['question', 'entrada_choice', 'action', 'menu', 'condition']),\n contextKey: z.string().optional(),\n questionType: z.enum(['text', 'money', 'date', 'int', 'cpf', 'choice']).optional(),\n question: z.string().optional(),\n options: z.array(z.tuple([z.string(), z.string()])).optional(),\n actionKind: z.string().optional(),\n actionParams: z.record(z.string(), z.unknown()).optional(),\n directMessage: z.string().optional(),\n fallbackMessage: z.string().optional(),\n conditionContextKey: z.string().optional(),\n conditionOperator: z.enum(['>', '>=', '<', '<=', '==', '!=', 'contains']).optional(),\n conditionValue: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n next: flowNodeNextSchema.optional(),\n })\n .passthrough()\n\nexport const flowGraphNodesSchema = z.record(z.string(), flowNodeDataSchema)\n","// Hierarquia de erros do módulo — não depende do framework de erros do host (nenhum DomainError\n// importado); o host mapeia estes para o seu próprio formato de resposta no exception filter.\nexport class MetaWhatsAppError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'MetaWhatsAppError'\n }\n}\n\nexport const META_WHATSAPP_ERROR_CODES = {\n WINDOW_EXPIRED: 'META_WHATSAPP_WINDOW_EXPIRED',\n INVALID_SIGNATURE: 'META_WHATSAPP_INVALID_SIGNATURE',\n DUPLICATE_DELIVERY: 'META_WHATSAPP_DUPLICATE_DELIVERY',\n CONFIG_MISSING: 'META_WHATSAPP_CONFIG_MISSING',\n TEMPLATE_NOT_CONFIGURED: 'META_WHATSAPP_TEMPLATE_NOT_CONFIGURED',\n SESSION_NOT_FOUND: 'META_WHATSAPP_SESSION_NOT_FOUND',\n} as const\n\nexport class WindowExpiredError extends MetaWhatsAppError {\n constructor(public readonly hoursSinceLastMessage?: number) {\n super(\n 'Janela de 24h expirada — não é possível enviar mensagem para este contato fora de template.',\n 400,\n META_WHATSAPP_ERROR_CODES.WINDOW_EXPIRED,\n { hoursSinceLastMessage },\n )\n }\n}\n\n// Assinatura HMAC do webhook não confere — Meta ou um terceiro tentando forjar uma entrega.\nexport class InvalidWebhookSignatureError extends MetaWhatsAppError {\n constructor() {\n super('Assinatura do webhook inválida.', 401, META_WHATSAPP_ERROR_CODES.INVALID_SIGNATURE)\n }\n}\n\n// Meta reenviou uma entrega dentro da janela de replay (nonce já visto) — não é erro do\n// cliente, é comportamento esperado da Cloud API; o host deve responder 200 e ignorar.\nexport class DuplicateWebhookDeliveryError extends MetaWhatsAppError {\n constructor(nonce: string) {\n super('Entrega de webhook duplicada.', 409, META_WHATSAPP_ERROR_CODES.DUPLICATE_DELIVERY, { nonce })\n }\n}\n\nexport class ConfigMissingError extends MetaWhatsAppError {\n constructor(missingField: string) {\n super(`Configuração do WhatsApp ausente: ${missingField}.`, 503, META_WHATSAPP_ERROR_CODES.CONFIG_MISSING, {\n missingField,\n })\n }\n}\n\nexport class TemplateNotConfiguredError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Nenhum template de WhatsApp configurado para reabertura de janela.',\n 503,\n META_WHATSAPP_ERROR_CODES.TEMPLATE_NOT_CONFIGURED,\n )\n }\n}\n\nexport class SessionNotFoundError extends MetaWhatsAppError {\n constructor(whatsappNumber: string) {\n super(\n `Sessão de conversa não encontrada para ${whatsappNumber}.`,\n 404,\n META_WHATSAPP_ERROR_CODES.SESSION_NOT_FOUND,\n { whatsappNumber },\n )\n }\n}\n"],"mappings":"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","names":["z","whatsAppMediaSchema","object","id","string","mime_type","sha256","optional","caption","filename","whatsAppInteractiveSchema","type","button_reply","title","list_reply","whatsAppOrderSchema","catalog_id","text","product_items","array","product_retailer_id","quantity","number","item_price","currency","whatsAppMessageSchema","from","body","image","audio","video","document","sticker","interactive","order","context","referred_product","timestamp","whatsAppMessageEchoSchema","whatsAppMessageStatusSchema","enum","whatsAppStatusSchema","status","recipient_id","whatsAppWebhookPayloadSchema","entry","changes","value","messaging_product","messages","message_echoes","statuses","metadata","display_phone_number","phone_number_id","z","FLOW_ACTION_KIND","HANDOFF","RATE_LIMITED_HANDOFF","SEND_PRODUCT_LIST","SEND_MEDIA","CROSS_FLOW_PREFIX","isCrossFlowTarget","target","startsWith","crossFlowKey","slice","length","flowNodeNextSchema","z","union","string","object","byAnswer","record","default","flowNodeDataSchema","id","type","enum","contextKey","optional","questionType","question","options","array","tuple","actionKind","actionParams","unknown","directMessage","fallbackMessage","conditionContextKey","conditionOperator","conditionValue","position","x","number","y","next","passthrough","flowGraphNodesSchema","MetaWhatsAppError","Error","message","statusCode","code","details","name","META_WHATSAPP_ERROR_CODES","WINDOW_EXPIRED","INVALID_SIGNATURE","DUPLICATE_DELIVERY","CONFIG_MISSING","TEMPLATE_NOT_CONFIGURED","SESSION_NOT_FOUND","WindowExpiredError","hoursSinceLastMessage","InvalidWebhookSignatureError","DuplicateWebhookDeliveryError","nonce","ConfigMissingError","missingField","TemplateNotConfiguredError","SessionNotFoundError","whatsappNumber"]}
1
+ {"version":3,"sources":["../src/webhook.types.ts","../src/flow.types.ts","../src/errors.ts","../src/previewMedia.types.ts"],"sourcesContent":["import { z } from 'zod'\n\n// Extraído de financiamento-imobiliario-bot/apps/api/src/modules/webhook/application/use-cases/\n// ReceiveWhatsAppWebhook.use-case.ts:39-113 — shape real do payload da Cloud API da Meta.\n\nexport const whatsAppMediaSchema = z.object({\n id: z.string(),\n mime_type: z.string(),\n sha256: z.string().optional(),\n caption: z.string().optional(),\n filename: z.string().optional(),\n})\nexport type WhatsAppMedia = z.infer<typeof whatsAppMediaSchema>\n\nexport const whatsAppInteractiveSchema = z.object({\n type: z.string(),\n button_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n list_reply: z.object({ id: z.string(), title: z.string() }).optional(),\n})\nexport type WhatsAppInteractive = z.infer<typeof whatsAppInteractiveSchema>\n\nexport const whatsAppOrderSchema = z.object({\n catalog_id: z.string(),\n text: z.string().optional(),\n product_items: z.array(\n z.object({\n product_retailer_id: z.string(),\n quantity: z.number(),\n item_price: z.number(),\n currency: z.string(),\n }),\n ),\n})\nexport type WhatsAppOrder = z.infer<typeof whatsAppOrderSchema>\n\nexport const whatsAppMessageSchema = z.object({\n id: z.string(),\n from: z.string(),\n type: z.string(),\n text: z.object({ body: z.string() }).optional(),\n image: whatsAppMediaSchema.optional(),\n audio: whatsAppMediaSchema.optional(),\n video: whatsAppMediaSchema.optional(),\n document: whatsAppMediaSchema.optional(),\n sticker: whatsAppMediaSchema.optional(),\n interactive: whatsAppInteractiveSchema.optional(),\n // Pedido enviado pelo carrinho do catálogo (WhatsApp Commerce)\n order: whatsAppOrderSchema.optional(),\n // Presente quando o cliente abre um item do catálogo e manda mensagem pela página do produto\n context: z\n .object({\n from: z.string().optional(),\n id: z.string().optional(),\n referred_product: z.object({ catalog_id: z.string(), product_retailer_id: z.string() }).optional(),\n })\n .optional(),\n timestamp: z.string(),\n})\nexport type WhatsAppMessage = z.infer<typeof whatsAppMessageSchema>\n\nexport const whatsAppMessageEchoSchema = z.object({\n id: z.string(),\n from: z.string(),\n timestamp: z.string(),\n type: z.string(),\n})\nexport type WhatsAppMessageEcho = z.infer<typeof whatsAppMessageEchoSchema>\n\nexport const whatsAppMessageStatusSchema = z.enum(['sent', 'delivered', 'read', 'failed'])\nexport type WhatsAppMessageStatusValue = z.infer<typeof whatsAppMessageStatusSchema>\n\nexport const whatsAppStatusSchema = z.object({\n id: z.string(),\n status: whatsAppMessageStatusSchema,\n timestamp: z.string(),\n recipient_id: z.string().optional(),\n})\nexport type WhatsAppStatus = z.infer<typeof whatsAppStatusSchema>\n\nexport const whatsAppWebhookPayloadSchema = z.object({\n object: z.string(),\n entry: z.array(\n z.object({\n id: z.string(),\n changes: z.array(\n z.object({\n value: z.object({\n messaging_product: z.string(),\n messages: z.array(whatsAppMessageSchema).optional(),\n message_echoes: z.array(whatsAppMessageEchoSchema).optional(),\n statuses: z.array(whatsAppStatusSchema).optional(),\n metadata: z.object({ display_phone_number: z.string(), phone_number_id: z.string() }).optional(),\n }),\n }),\n ),\n }),\n ),\n})\nexport type WhatsAppWebhookPayload = z.infer<typeof whatsAppWebhookPayloadSchema>\n","// Mesmo shape de dados do editor visual (@adatechnology/conversations-ui/flows) — o backend\n// interpreta/persiste o grafo, o frontend edita; os dois lados precisam concordar no formato.\n// `FlowActionKind` é string aberta (não union fechada): o host registra os próprios actions\n// (ex.: 'trigger_simulation' no bot) via registerFlowAction() no módulo (T4.3) — o pacote nunca\n// assume nenhum caso de negócio específico.\nimport { z } from 'zod'\n\nexport type FlowNodeType = 'question' | 'entrada_choice' | 'action' | 'menu' | 'condition'\nexport type FlowQuestionType = 'text' | 'money' | 'date' | 'int' | 'cpf' | 'choice'\nexport type FlowActionKind = string\n\n// Os únicos `actionKind` que o próprio pacote implementa — todo o resto é do host. Ficam aqui, e\n// não no editor, porque o mesmo literal é usado nos dois lados (o editor oferece na paleta, o\n// módulo registra o handler) e duas cópias divergem sem nada acusar.\nexport const FLOW_ACTION_KIND = {\n HANDOFF: 'handoff',\n RATE_LIMITED_HANDOFF: 'rate_limited_handoff',\n SEND_PRODUCT_LIST: 'send_product_list',\n SEND_MEDIA: 'send_media',\n} as const\nexport type FlowConditionOperator = '>' | '>=' | '<' | '<=' | '==' | '!=' | 'contains'\nexport type FlowNodeNext = string | { byAnswer: Record<string, string>; default: string }\n\n// Destinos \"flow:<key>\" são saltos para outro fluxo — mesma convenção usada pelo editor visual\n// (conversations-ui/flows/flowGraph.ts); backend e frontend precisam concordar no formato.\nexport const CROSS_FLOW_PREFIX = 'flow:'\nexport const isCrossFlowTarget = (target: string): boolean => target.startsWith(CROSS_FLOW_PREFIX)\nexport const crossFlowKey = (target: string): string => target.slice(CROSS_FLOW_PREFIX.length)\n\nexport type FlowNodeData = {\n id: string\n type: FlowNodeType\n contextKey?: string\n questionType?: FlowQuestionType\n question?: string\n options?: [string, string][]\n actionKind?: FlowActionKind\n // Parâmetros arbitrários que o nó carrega para o handler registrado em `actionKind`. O pacote\n // nunca os interpreta — é o que permite um nó de ação ser configurado pelo editor sem que o\n // vocabulário do produto (o antigo `simulationTemplate`) vaze para dentro do contrato.\n actionParams?: Record<string, unknown>\n directMessage?: string\n fallbackMessage?: string\n conditionContextKey?: string\n conditionOperator?: FlowConditionOperator\n conditionValue?: string\n position?: { x: number; y: number }\n next?: FlowNodeNext\n}\n\nexport interface FlowGraphData {\n key: string\n label: string\n startNodeId: string\n version: number\n nodes: Record<string, FlowNodeData>\n}\n\nexport interface FlowGraphSummary {\n key: string\n label: string\n nodeCount: number\n showInMenu: boolean\n menuOptionLabel?: string\n updatedAt: string\n}\n\n// Retorno de GetLiveFlowPositions (T4.2) — quantas sessões ativas estão em cada nó agora,\n// usado pelo editor para desenhar o \"liveCount\" nos cards (FlowNodeCardData.liveCount).\nexport interface LiveFlowPosition {\n flowKey: string\n nodeId: string\n count: number\n}\n\n// Validação de runtime do grafo. O `nodes` é um jsonb que entrou pelo editor (dado de origem\n// cliente) e sai do banco como `unknown` — sem parse, um grafo malformado só se manifestaria\n// como comportamento estranho lá dentro do interpretador, longe da causa.\n//\n// Permissivo de propósito em dois pontos: `actionKind` é string aberta (o host registra os\n// seus) e `.passthrough()` deixa passar campos extras — um grafo salvo por uma versão mais nova\n// do editor não pode ficar irrecuperável numa versão mais antiga do módulo.\nexport const flowNodeNextSchema = z.union([\n z.string(),\n z.object({ byAnswer: z.record(z.string(), z.string()), default: z.string() }),\n])\n\nexport const flowNodeDataSchema = z\n .object({\n id: z.string(),\n type: z.enum(['question', 'entrada_choice', 'action', 'menu', 'condition']),\n contextKey: z.string().optional(),\n questionType: z.enum(['text', 'money', 'date', 'int', 'cpf', 'choice']).optional(),\n question: z.string().optional(),\n options: z.array(z.tuple([z.string(), z.string()])).optional(),\n actionKind: z.string().optional(),\n actionParams: z.record(z.string(), z.unknown()).optional(),\n directMessage: z.string().optional(),\n fallbackMessage: z.string().optional(),\n conditionContextKey: z.string().optional(),\n conditionOperator: z.enum(['>', '>=', '<', '<=', '==', '!=', 'contains']).optional(),\n conditionValue: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n next: flowNodeNextSchema.optional(),\n })\n .passthrough()\n\nexport const flowGraphNodesSchema = z.record(z.string(), flowNodeDataSchema)\n","// Hierarquia de erros do módulo — não depende do framework de erros do host (nenhum DomainError\n// importado); o host mapeia estes para o seu próprio formato de resposta no exception filter.\nexport class MetaWhatsAppError extends Error {\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'MetaWhatsAppError'\n }\n}\n\nexport const META_WHATSAPP_ERROR_CODES = {\n WINDOW_EXPIRED: 'META_WHATSAPP_WINDOW_EXPIRED',\n INVALID_SIGNATURE: 'META_WHATSAPP_INVALID_SIGNATURE',\n DUPLICATE_DELIVERY: 'META_WHATSAPP_DUPLICATE_DELIVERY',\n CONFIG_MISSING: 'META_WHATSAPP_CONFIG_MISSING',\n TEMPLATE_NOT_CONFIGURED: 'META_WHATSAPP_TEMPLATE_NOT_CONFIGURED',\n SESSION_NOT_FOUND: 'META_WHATSAPP_SESSION_NOT_FOUND',\n AUDIO_NOT_INGESTED: 'META_WHATSAPP_AUDIO_NOT_INGESTED',\n MESSAGE_NOT_AUDIO: 'META_WHATSAPP_MESSAGE_NOT_AUDIO',\n TRANSCRIPTION_DISABLED: 'META_WHATSAPP_TRANSCRIPTION_DISABLED',\n} as const\n\nexport class WindowExpiredError extends MetaWhatsAppError {\n constructor(public readonly hoursSinceLastMessage?: number) {\n super(\n 'Janela de 24h expirada — não é possível enviar mensagem para este contato fora de template.',\n 400,\n META_WHATSAPP_ERROR_CODES.WINDOW_EXPIRED,\n { hoursSinceLastMessage },\n )\n }\n}\n\n// Assinatura HMAC do webhook não confere — Meta ou um terceiro tentando forjar uma entrega.\nexport class InvalidWebhookSignatureError extends MetaWhatsAppError {\n constructor() {\n super('Assinatura do webhook inválida.', 401, META_WHATSAPP_ERROR_CODES.INVALID_SIGNATURE)\n }\n}\n\n// Meta reenviou uma entrega dentro da janela de replay (nonce já visto) — não é erro do\n// cliente, é comportamento esperado da Cloud API; o host deve responder 200 e ignorar.\nexport class DuplicateWebhookDeliveryError extends MetaWhatsAppError {\n constructor(nonce: string) {\n super('Entrega de webhook duplicada.', 409, META_WHATSAPP_ERROR_CODES.DUPLICATE_DELIVERY, { nonce })\n }\n}\n\nexport class ConfigMissingError extends MetaWhatsAppError {\n constructor(missingField: string) {\n super(`Configuração do WhatsApp ausente: ${missingField}.`, 503, META_WHATSAPP_ERROR_CODES.CONFIG_MISSING, {\n missingField,\n })\n }\n}\n\nexport class TemplateNotConfiguredError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Nenhum template de WhatsApp configurado para reabertura de janela.',\n 503,\n META_WHATSAPP_ERROR_CODES.TEMPLATE_NOT_CONFIGURED,\n )\n }\n}\n\nexport class SessionNotFoundError extends MetaWhatsAppError {\n constructor(whatsappNumber: string) {\n super(\n `Sessão de conversa não encontrada para ${whatsappNumber}.`,\n 404,\n META_WHATSAPP_ERROR_CODES.SESSION_NOT_FOUND,\n { whatsappNumber },\n )\n }\n}\n\n/**\n * Pediram transcrição de um áudio que ainda não foi copiado da Meta para o storage.\n *\n * 409 e não 404: a mensagem existe e o áudio vai chegar — a ingestão é assíncrona e o atendente\n * simplesmente clicou antes de ela terminar. É o único erro de transcrição em que \"tente de novo em\n * alguns segundos\" é a orientação correta para a interface.\n */\nexport class AudioNotIngestedError extends MetaWhatsAppError {\n constructor(messageId: string) {\n super(\n 'Áudio ainda está sendo copiado — tente novamente em alguns segundos.',\n 409,\n META_WHATSAPP_ERROR_CODES.AUDIO_NOT_INGESTED,\n { messageId },\n )\n }\n}\n\n/**\n * Transcrição está desligada para esta empresa.\n *\n * Guarda de última linha, não o caminho normal: o painel lê as configurações e nem desenha o botão\n * quando está desligado. Existe para o caso de a tela estar com dado velho, ou de alguém chamar a\n * rota direto — e é `409` (estado atual conflita com a operação), não `403`: ninguém está sem\n * permissão, o recurso está desligado por escolha e liga a qualquer momento.\n */\nexport class TranscriptionDisabledError extends MetaWhatsAppError {\n constructor() {\n super(\n 'Transcrição de áudio está desligada para esta empresa.',\n 409,\n META_WHATSAPP_ERROR_CODES.TRANSCRIPTION_DISABLED,\n )\n }\n}\n\n/** Pediram transcrição de mensagem que não é áudio. Nunca melhora com retentativa. */\nexport class MessageNotAudioError extends MetaWhatsAppError {\n constructor(messageId: string, type: string) {\n super(\n `Mensagem ${messageId} é do tipo \"${type}\" — só áudio é transcrito.`,\n 422,\n META_WHATSAPP_ERROR_CODES.MESSAGE_NOT_AUDIO,\n { messageId, type },\n )\n }\n}\n","/**\n * Mídia que veio do simulador de conversa, não da Meta.\n *\n * Vive no `contracts` porque é literalmente um contrato entre as duas pontas: o front gera o id, o\n * backend o resolve. Nasceu duplicado num produto (duas constantes iguais, cada uma com um\n * comentário pedindo \"precisa bater com a outra\") e por isso o segundo produto não herdou nada —\n * este pacote é o único lugar em que os dois lados já se encontram.\n *\n * O simulador manda **webhook**, e webhook da Meta carrega referência de mídia (um id), nunca o\n * binário. Então o arquivo gravado no navegador precisa existir em algum lugar que o servidor\n * alcance, e o id precisa dizer onde — é essa a convenção deste arquivo.\n *\n * Existe no pacote, e não em cada host, porque a convenção tem DOIS lados que precisam concordar\n * (quem gera o id no front, quem o resolve no backend). Onde ela ficou no host, o resultado foi\n * exatamente o previsível: duas constantes iguais em pacotes diferentes, cada uma com um comentário\n * pedindo \"precisa bater com a outra\" — e o segundo produto não herdou nada e ficou sem o recurso.\n */\n\n/**\n * Prefixo que marca o id como local.\n *\n * Dois-pontos no fim de propósito: id da Meta é alfanumérico com `_` e `-`, então o separador torna\n * a colisão impossível em vez de improvável.\n */\nexport const PREVIEW_MEDIA_ID_PREFIX = 'preview-upload:'\n\n/** Monta o id que o simulador manda no webhook. */\nexport function toPreviewMediaId(uploadId: string): string {\n return `${PREVIEW_MEDIA_ID_PREFIX}${uploadId}`\n}\n\n/**\n * `uploadId` embutido no id, ou `undefined` quando a mídia veio mesmo da Meta.\n *\n * Devolver `undefined` para id sem prefixo é o que mantém o caminho normal intacto: o adaptador só\n * desvia para o storage quando o prefixo está lá.\n */\nexport function resolvePreviewUploadId(mediaId: string): string | undefined {\n if (!mediaId.startsWith(PREVIEW_MEDIA_ID_PREFIX)) return undefined\n return mediaId.slice(PREVIEW_MEDIA_ID_PREFIX.length) || undefined\n}\n"],"mappings":"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","names":["z","whatsAppMediaSchema","object","id","string","mime_type","sha256","optional","caption","filename","whatsAppInteractiveSchema","type","button_reply","title","list_reply","whatsAppOrderSchema","catalog_id","text","product_items","array","product_retailer_id","quantity","number","item_price","currency","whatsAppMessageSchema","from","body","image","audio","video","document","sticker","interactive","order","context","referred_product","timestamp","whatsAppMessageEchoSchema","whatsAppMessageStatusSchema","enum","whatsAppStatusSchema","status","recipient_id","whatsAppWebhookPayloadSchema","entry","changes","value","messaging_product","messages","message_echoes","statuses","metadata","display_phone_number","phone_number_id","z","FLOW_ACTION_KIND","HANDOFF","RATE_LIMITED_HANDOFF","SEND_PRODUCT_LIST","SEND_MEDIA","CROSS_FLOW_PREFIX","isCrossFlowTarget","target","startsWith","crossFlowKey","slice","length","flowNodeNextSchema","z","union","string","object","byAnswer","record","default","flowNodeDataSchema","id","type","enum","contextKey","optional","questionType","question","options","array","tuple","actionKind","actionParams","unknown","directMessage","fallbackMessage","conditionContextKey","conditionOperator","conditionValue","position","x","number","y","next","passthrough","flowGraphNodesSchema","MetaWhatsAppError","Error","message","statusCode","code","details","name","META_WHATSAPP_ERROR_CODES","WINDOW_EXPIRED","INVALID_SIGNATURE","DUPLICATE_DELIVERY","CONFIG_MISSING","TEMPLATE_NOT_CONFIGURED","SESSION_NOT_FOUND","AUDIO_NOT_INGESTED","MESSAGE_NOT_AUDIO","TRANSCRIPTION_DISABLED","WindowExpiredError","hoursSinceLastMessage","InvalidWebhookSignatureError","DuplicateWebhookDeliveryError","nonce","ConfigMissingError","missingField","TemplateNotConfiguredError","SessionNotFoundError","whatsappNumber","AudioNotIngestedError","messageId","TranscriptionDisabledError","MessageNotAudioError","type","PREVIEW_MEDIA_ID_PREFIX","toPreviewMediaId","uploadId","resolvePreviewUploadId","mediaId","startsWith","undefined","slice","length"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/meta-whatsapp-contracts",
3
- "version": "0.2.0-rc.7",
3
+ "version": "0.2.0-rc.9",
4
4
  "description": "Shared types, zod schemas, and port interfaces for the meta-whatsapp trio (contracts only — no runtime behavior)",
5
5
  "publishConfig": {
6
6
  "access": "public"