@fayz-ai/plugin-conversations 0.23.0 → 0.23.2
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.d.ts.map +1 -1
- package/dist/index.js +62 -4
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -13,6 +13,7 @@ export declare const MIGRATION_011_O_ANUNCIO_E_UMA_CAMPAINHA_NAO_UM_RECADO = "--
|
|
|
13
13
|
export declare const MIGRATION_012_A_RICH_MESSAGE_KEEPS_ITS_SHAPE = "-- ---------------------------------------------------------------------------\n-- 012_a_rich_message_keeps_its_shape.sql\n--\n-- \u2500\u2500 what was lost, and how \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- `plg_conversation_messages` has exactly one content column: `body text`. A\n-- WhatsApp message that is not text therefore arrives flattened to whatever\n-- string the ingest could make of it \u2014 and when the ingest could make none, to\n-- the empty string, which renders as an empty bubble.\n--\n-- On 2026-09-22 a lead sent his finance director's contact card. It stored as\n-- `body = ''`. He then sent a screenshot of the same card; that stored as\n-- `body = ''` too. The fact the conversation existed to obtain \u2014 a name and a\n-- phone number \u2014 was in the payload both times and in the row neither time.\n--\n-- Recovering it meant going back to the Evolution instance and reading the\n-- provider's own copy. That worked, but it is not a recovery path: Evolution\n-- prunes, the instance is local-only, and a row that needs a second system to\n-- be legible is not a record.\n--\n-- \u2500\u2500 the two columns \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\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-- `message_type` names what the provider sent \u2014 text, contact, image, audio,\n-- document, sticker, location, reaction, unknown. The inbox needs it to choose\n-- a bubble, and `unknown` is the one that makes the NEXT gap visible in a query\n-- instead of in a complaint.\n--\n-- `payload` keeps the part that does not survive flattening: the vCard's parsed\n-- name and numbers, a media mimetype and size, a location's coordinates. Not\n-- the whole provider envelope \u2014 that carries decryption keys and a base64\n-- thumbnail, neither of which belongs in a row that every member of the tenant\n-- can read.\n--\n-- Deliberately NOT stored here: delivery status. It changes after the fact \u2014\n-- sent, delivered, read \u2014 and `conversations-provider`'s `message_detail` reads\n-- it live for that reason. `payload` is the shape of the message, which does\n-- not change once it arrives.\n--\n-- \u2500\u2500 why both are nullable, and nothing is backfilled \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Every row written before this migration has neither, and no default can\n-- invent them: `message_type` would have to guess, and guessing 'text' for the\n-- three empty bubbles already in the table would assert something false about\n-- exactly the rows this exists to fix. A reader that treats NULL as \"written\n-- before 012\" is correct; a reader that treats '' as text is the bug.\n--\n-- Table-level `GRANT ALL ... TO authenticated` already covers new columns, and\n-- the four tenant policies are row-scoped, so neither needs restating.\n--\n-- Idempotent and replay-safe.\n-- ---------------------------------------------------------------------------\n\nALTER TABLE public.plg_conversation_messages\n ADD COLUMN IF NOT EXISTS message_type text,\n ADD COLUMN IF NOT EXISTS payload jsonb;\n\nCOMMENT ON COLUMN public.plg_conversation_messages.message_type IS\n 'What the provider sent: text, contact, image, video, audio, document, sticker, location, reaction, unknown. NULL means the row predates 012 (#504).';\n\nCOMMENT ON COLUMN public.plg_conversation_messages.payload IS\n 'The structured remainder that `body` cannot hold \u2014 parsed vCards, media metadata, coordinates. Never credentials, thumbnails or decryption keys; never delivery status, which is read live (#504).';\n";
|
|
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
|
+
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";
|
|
16
17
|
export declare const MIGRATIONS: Array<{
|
|
17
18
|
id: string;
|
|
18
19
|
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,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,
|
|
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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"status": "preview",
|
|
5
5
|
"dependencies": []
|
|
6
6
|
},
|
|
7
|
-
"version": "0.23.
|
|
7
|
+
"version": "0.23.2",
|
|
8
8
|
"description": "[experimental] Fayz SDK — unified conversations / omni-channel inbox plugin",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"sideEffects": false,
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"lucide-react": ">=0.400.0 <1.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@fayz-ai/core": "^0.
|
|
31
|
-
"@fayz-ai/ui": "^0.
|
|
32
|
-
"@fayz-ai/admin": "^0.
|
|
30
|
+
"@fayz-ai/core": "^0.50.0",
|
|
31
|
+
"@fayz-ai/ui": "^0.50.0",
|
|
32
|
+
"@fayz-ai/admin": "^0.50.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/react": "^18.3.0",
|