@fayz-ai/plugin-conversations 0.23.2 → 0.23.3

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.
@@ -14,6 +14,7 @@ export declare const MIGRATION_012_A_RICH_MESSAGE_KEEPS_ITS_SHAPE = "-- --------
14
14
  export declare const MIGRATION_013_A_MIDIA_FICA_CONOSCO = "-- ---------------------------------------------------------------------------\n-- 013_a_midia_fica_conosco.sql\n--\n-- \u2500\u2500 o que a 012 resolveu, e o que ela n\u00E3o resolveu \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A 012 fez a mensagem que n\u00E3o \u00E9 texto parar de chegar como bolha vazia:\n-- `message_type` diz que \u00E9 imagem, `payload` guarda mimetype, tamanho e\n-- dura\u00E7\u00E3o. Funcionou \u2014 um \u00E1udio de 23 segundos entrou classificado como\n-- `audio`, `ptt: true`, `audio/ogg; codecs=opus`, 55.825 bytes.\n--\n-- E a tela mostra `[\u00E1udio]`. Porque saber QUE existe um arquivo n\u00E3o \u00E9 ter o\n-- arquivo. Nada no banco aponta para bytes nenhuns: o WhatsApp entrega m\u00EDdia\n-- cifrada num CDN pr\u00F3prio, e quem decifra \u00E9 o provedor.\n--\n-- \u2500\u2500 por que uma coluna, e n\u00E3o um campo no payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A 012 escreveu que `payload` \u00E9 \"a forma da mensagem, que n\u00E3o muda depois que\n-- ela chega\". Onde o arquivo foi parar \u00E9 decidido DEPOIS \u2014 no upload, que pode\n-- falhar, ser refeito, ou ainda n\u00E3o ter acontecido. Enfiar isso no `payload`\n-- contradiz a \u00FAnica frase que diz quando `payload` pode ser lido sem medo.\n--\n-- Em coluna, a pergunta que importa vira consulta:\n--\n-- SELECT count(*) FROM plg_conversation_messages\n-- WHERE message_type IN ('image','video','audio','document','sticker')\n-- AND media_path IS NULL;\n--\n-- \"quanta m\u00EDdia eu recebi e n\u00E3o guardei\" \u2014 que num campo de JSON seria uma\n-- varredura e aqui \u00E9 um \u00EDndice.\n--\n-- \u2500\u2500 por que guardar, em vez de buscar no provedor quando algu\u00E9m abrir \u2500\u2500\u2500\n--\n-- A Evolution decifra sob demanda, e seria mais barato n\u00E3o copiar nada. Mas ela\n-- \u00E9 local-only, poda o que \u00E9 antigo, e a inst\u00E2ncia roda numa m\u00E1quina que nem\n-- sempre est\u00E1 de p\u00E9. Essa era exatamente a obje\u00E7\u00E3o da 012 contra depender dela\n-- para ler um vCard: uma linha que precisa de um segundo sistema para ser\n-- leg\u00EDvel n\u00E3o \u00E9 registro. Vale igual para os bytes.\n--\n-- O destino \u00E9 o balde `attachments` \u2014 privado, 25 MB, j\u00E1 existente e j\u00E1 em uso.\n-- Uma foto que um lead mandou no WhatsApp n\u00E3o pode ser p\u00FAblica, e um balde novo\n-- seria mais uma lista de mimetypes para algu\u00E9m manter desatualizada.\n--\n-- \u2500\u2500 nulo \u00E9 um estado leg\u00EDtimo, n\u00E3o uma falha \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `media_path` \u00E9 NULL em tr\u00EAs situa\u00E7\u00F5es diferentes e todas normais: a mensagem\n-- \u00E9 texto; a linha \u00E9 anterior a esta migra\u00E7\u00E3o; ou o upload ainda n\u00E3o aconteceu.\n-- Quem l\u00EA distingue pela combina\u00E7\u00E3o com `message_type`, que \u00E9 o que torna a\n-- consulta acima poss\u00EDvel. Um default inventaria um caminho para arquivo que\n-- n\u00E3o existe.\n--\n-- Idempotente e replay-safe.\n-- ---------------------------------------------------------------------------\n\nALTER TABLE public.plg_conversation_messages\n ADD COLUMN IF NOT EXISTS media_path text;\n\nCOMMENT ON COLUMN public.plg_conversation_messages.media_path IS\n 'Onde os bytes ficaram no balde `attachments`, ou NULL: texto, linha anterior \u00E0 013, ou upload que ainda n\u00E3o aconteceu. Distingue-se pela combina\u00E7\u00E3o com `message_type` (013).';\n\n-- Parcial: s\u00F3 a m\u00EDdia sem arquivo interessa, e \u00E9 a minoria. O \u00EDndice fica do\n-- tamanho do problema em vez do tamanho da tabela.\nCREATE INDEX IF NOT EXISTS plg_conversation_messages_midia_sem_arquivo\n ON public.plg_conversation_messages (tenant_id, at DESC)\n WHERE media_path IS NULL\n AND message_type IN ('image', 'video', 'audio', 'document', 'sticker');\n";
15
15
  export declare const MIGRATION_014_CLAIMING_TARGETS_IS_SERVER_ONLY = "-- 014_claiming_targets_is_server_only.sql\n--\n-- 003 revoked `conversations_claim_targets` from PUBLIC and anon but not from\n-- `authenticated`, which the pool's default privileges grant on every new\n-- function in public. Live, any signed-in user of any tenant could claim (and\n-- so burn) another tenant's campaign targets by uuid \u2014 SECURITY DEFINER, no\n-- caller check. The only caller is the conversations-provider edge function,\n-- which uses the service-role key.\n\nREVOKE ALL ON FUNCTION public.conversations_claim_targets(uuid, integer) FROM PUBLIC, anon, authenticated;\nGRANT EXECUTE ON FUNCTION public.conversations_claim_targets(uuid, integer) TO service_role;\n";
16
16
  export declare const MIGRATION_015_THE_SERVICE_DOOR_FOR_MESSAGING = "-- ============================================================================\n-- 015_the_service_door_for_messaging \u2014 the WhatsApp edge functions move to the\n-- integration service (ADR 0003 phase 2, wave 2).\n--\n-- Two kinds of table, two kinds of access:\n--\n-- * tenant tables a member already writes (conversations, messages,\n-- campaigns, templates): the service writes them AS the store's owner or\n-- as the verified caller (core migrations 210/212), under RLS. Nothing\n-- here.\n-- * service tables only a machine writes, which service_role wrote until\n-- now: provider credentials, channels, opt-outs, webhook events, provider\n-- templates, payment requests. fayz_integration gets exactly that, table\n-- by table, with an explicit policy. Panels keep their SELECT policies.\n--\n-- And the three server-only functions the webhooks call get EXECUTE for the\n-- service role (they were service_role only, 014).\n--\n-- Guarded: the role is an operator step. Replay-safe.\n-- ============================================================================\n\nDO $$\nDECLARE\n t text;\n f text;\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN\n RETURN;\n END IF;\n\n FOREACH t IN ARRAY ARRAY[\n 'plg_conversations_providers',\n 'plg_conversations_channels',\n 'plg_conversations_optouts',\n 'plg_conversations_webhook_events',\n 'plg_automations_provider_templates',\n 'plg_conversations_payment_requests'\n ] LOOP\n IF to_regclass('public.' || t) IS NULL THEN CONTINUE; END IF;\n EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON public.%I TO fayz_integration', t);\n EXECUTE format('DROP POLICY IF EXISTS %I ON public.%I', t || '_service', t);\n EXECUTE format('CREATE POLICY %I ON public.%I FOR ALL TO fayz_integration USING (true) WITH CHECK (true)', t || '_service', t);\n END LOOP;\n\n FOREACH f IN ARRAY ARRAY[\n 'public.conversations_claim_targets(uuid, integer)',\n 'public.plg_booking_apply_channel_reply(uuid, text, uuid)',\n 'public.plg_conversations_match_by_phone(text, text, text, uuid)'\n ] LOOP\n IF to_regprocedure(f) IS NOT NULL THEN\n EXECUTE format('GRANT EXECUTE ON FUNCTION %s TO fayz_integration', f);\n END IF;\n END LOOP;\nEND $$;\n";
17
+ export declare const MIGRATION_016_A_DELIVERY_RECEIPT_FINDS_ITS_TENANT = "-- ============================================================================\n-- 016_a_delivery_receipt_finds_its_tenant \u2014 the Tyxter webhook moves to the\n-- integration service (ADR 0003 phase 2).\n--\n-- A delivery receipt names only the provider's message id. The service writes\n-- the inbox AS the store's owner (core 212), so it must learn the store first:\n-- this answers the tenant of the message that id was sent as, and nothing\n-- else. SECURITY DEFINER, service role only.\n--\n-- Replay-safe: CREATE OR REPLACE; guarded grants.\n-- ============================================================================\n\nCREATE OR REPLACE FUNCTION public.conversations_message_tenant(p_provider_message_id text) RETURNS uuid\nLANGUAGE sql STABLE SECURITY DEFINER SET search_path TO ''\nAS $function$\n SELECT m.tenant_id\n FROM public.plg_conversation_messages m\n WHERE m.provider_message_id = p_provider_message_id\n ORDER BY m.at DESC NULLS LAST\n LIMIT 1;\n$function$;\n\nDO $$\nBEGIN\n REVOKE ALL ON FUNCTION public.conversations_message_tenant(text) FROM PUBLIC;\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN REVOKE ALL ON FUNCTION public.conversations_message_tenant(text) FROM anon; END IF;\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN REVOKE ALL ON FUNCTION public.conversations_message_tenant(text) FROM authenticated; END IF;\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'service_role') THEN GRANT EXECUTE ON FUNCTION public.conversations_message_tenant(text) TO service_role; END IF;\n IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'fayz_integration') THEN GRANT EXECUTE ON FUNCTION public.conversations_message_tenant(text) TO fayz_integration; END IF;\nEND $$;\n";
17
18
  export declare const MIGRATIONS: Array<{
18
19
  id: string;
19
20
  sql: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2rhCAkgBlC,CAAA;AAED,eAAO,MAAM,gDAAgD,i0LAuG5D,CAAA;AAED,eAAO,MAAM,oDAAoD,m5bAuOhE,CAAA;AAED,eAAO,MAAM,0DAA0D,81rBAwWtE,CAAA;AAED,eAAO,MAAM,gEAAgE,+lJA2E5E,CAAA;AAED,eAAO,MAAM,yDAAyD,w1RAmKrE,CAAA;AAED,eAAO,MAAM,sCAAsC,mtOA6HlD,CAAA;AAED,eAAO,MAAM,6CAA6C,qmIA6DzD,CAAA;AAED,eAAO,MAAM,yCAAyC,ywGA8CrD,CAAA;AAED,eAAO,MAAM,qDAAqD,40FAiDjE,CAAA;AAED,eAAO,MAAM,6CAA6C,6oJA0FzD,CAAA;AAED,eAAO,MAAM,qDAAqD,itGA8DjE,CAAA;AAED,eAAO,MAAM,4CAA4C,6gIA6DxD,CAAA;AAED,eAAO,MAAM,kCAAkC,qlIAiE9C,CAAA;AAED,eAAO,MAAM,6CAA6C,orBAWzD,CAAA;AAED,eAAO,MAAM,4CAA4C,8wEAsDxD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAiBzD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,2rhCAkgBlC,CAAA;AAED,eAAO,MAAM,gDAAgD,i0LAuG5D,CAAA;AAED,eAAO,MAAM,oDAAoD,m5bAuOhE,CAAA;AAED,eAAO,MAAM,0DAA0D,81rBAwWtE,CAAA;AAED,eAAO,MAAM,gEAAgE,+lJA2E5E,CAAA;AAED,eAAO,MAAM,yDAAyD,w1RAmKrE,CAAA;AAED,eAAO,MAAM,sCAAsC,mtOA6HlD,CAAA;AAED,eAAO,MAAM,6CAA6C,qmIA6DzD,CAAA;AAED,eAAO,MAAM,yCAAyC,ywGA8CrD,CAAA;AAED,eAAO,MAAM,qDAAqD,40FAiDjE,CAAA;AAED,eAAO,MAAM,6CAA6C,6oJA0FzD,CAAA;AAED,eAAO,MAAM,qDAAqD,itGA8DjE,CAAA;AAED,eAAO,MAAM,4CAA4C,6gIA6DxD,CAAA;AAED,eAAO,MAAM,kCAAkC,qlIAiE9C,CAAA;AAED,eAAO,MAAM,6CAA6C,orBAWzD,CAAA;AAED,eAAO,MAAM,4CAA4C,8wEAsDxD,CAAA;AAED,eAAO,MAAM,iDAAiD,yuDA8B7D,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAkBzD,CAAA"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "status": "preview",
5
5
  "dependencies": []
6
6
  },
7
- "version": "0.23.2",
7
+ "version": "0.23.3",
8
8
  "description": "[experimental] Fayz SDK — unified conversations / omni-channel inbox plugin",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@fayz-ai/core": "^0.50.0",
31
31
  "@fayz-ai/ui": "^0.50.0",
32
- "@fayz-ai/admin": "^0.50.0"
32
+ "@fayz-ai/admin": "^0.51.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/react": "^18.3.0",