@fayz-ai/plugin-conversations 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,11 +9,13 @@
9
9
 
10
10
  Customers reach out everywhere — a WhatsApp here, an Instagram DM there, an email, a text. Replying across five tabs is how businesses drop conversations. `plugin-conversations` is the GoHighLevel-style unified inbox for the Fayz engine: every channel collapses into one threaded view, so a salon, a restaurant, or an agency answers everyone from a single place.
11
11
 
12
- This is foundational today. The plugin ships a complete inbox UI and a data-provider seam: it runs on a rich mock out of the box, and reads/writes the `conversations` + `conversation_messages` tables (tenant-scoped) when a Supabase client is registered. Real channel connectors — Twilio, WhatsApp Cloud, Meta, IMAP are the next milestone, but the inbox, the store, the events, and the AI tools are already wired so the rest snaps in behind them.
12
+ This is foundational today. The plugin ships a complete inbox UI and a data-provider seam: it runs on a rich mock out of the box, and reads/writes the `conversations` + `conversation_messages` tables (tenant-scoped) when a Supabase client is registered. WhatsApp is the first real channel, through the **Tyxter** connector; Instagram, email and SMS still arrive out-of-band.
13
13
 
14
14
  ## What's inside
15
15
  - A full-bleed `/conversations` inbox view backed by a Zustand store
16
16
  - A `ConversationsProvider` seam: mock provider, or a Supabase provider (tenant-scoped via the active org) when a client is registered
17
+ - The **Tyxter** connector (`messaging` shelf): WhatsApp on a number the platform already owns — no key to paste — with an optional dedicated number per tenant
18
+ - `plg_conversations_channels`: which number a message leaves by (product fallback vs tenant dedicated), read by members, written only by the service role
17
19
  - Domain events: `conversations.message.received`, `conversations.message.sent`
18
20
  - AI tools: `listConversations` (filter by `sms` / `whatsapp` / `instagram` / `email` / `webchat`) and `sendMessage`
19
21
  - Configurable navigation and full i18n
@@ -0,0 +1,70 @@
1
+ import type { Channel } from '../types';
2
+ /** Whose number it is. `fallback` is the product's, shared by every tenant. */
3
+ export type MessagingChannelKind = 'fallback' | 'dedicated';
4
+ /** The provider's own lifecycle, kept verbatim. Only `active` may carry a send. */
5
+ export type MessagingChannelStatus = 'requested' | 'provisioning' | 'provisioned' | 'verifying' | 'active' | 'failed' | 'released' | 'disconnected';
6
+ export interface MessagingChannelAccount {
7
+ id: string;
8
+ /** `null` on the product fallback row — it belongs to the software, not a tenant. */
9
+ tenantId: string | null;
10
+ channel: Channel;
11
+ provider: string;
12
+ providerNumberId: string;
13
+ phoneE164: string | null;
14
+ kind: MessagingChannelKind;
15
+ status: MessagingChannelStatus;
16
+ displayName: string | null;
17
+ metadata: Record<string, unknown>;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }
21
+ export interface ListMessagingChannelsOptions {
22
+ /** Narrow to one provider ('tyxter'). Omitted: every provider in the pool. */
23
+ provider?: string;
24
+ /** Narrow to one channel ('whatsapp'). */
25
+ channel?: Channel;
26
+ /** Supabase client override; defaults to the globally registered one. */
27
+ supabaseClient?: unknown;
28
+ }
29
+ /**
30
+ * Every number this tenant can send by — its own, plus the product fallback.
31
+ *
32
+ * No tenant filter is applied, and that is deliberate: RLS already returns the
33
+ * tenant's rows and the fallback row, and the fallback has no tenant to filter
34
+ * on. Filtering here would hide exactly the row the panel most needs to show.
35
+ */
36
+ export declare function listMessagingChannels(options?: ListMessagingChannelsOptions): Promise<MessagingChannelAccount[]>;
37
+ /**
38
+ * The happy path, in order. Observed live against the provider's sandbox on
39
+ * 2026-08-24: a provision answered `requested` and the number then walked
40
+ * `provisioned` → `verifying` → `active` in about a second. `provisioning` is in
41
+ * the provider's enum and was not seen at 150ms polling — kept here because the
42
+ * enum has it and a step missing from a timeline reads as a step that failed.
43
+ */
44
+ export declare const CHANNEL_CLAIM_TIMELINE: MessagingChannelStatus[];
45
+ /**
46
+ * The states in which the tenant already has a claim and must not start another.
47
+ *
48
+ * Mirrors `LIVE_CHANNEL_STATUSES` in functions/_shared/tyxter-runtime.js, which
49
+ * is where the edge function enforces it. Two copies because an edge function
50
+ * may not import from src/ — the vitest suite asserts they agree, so a change to
51
+ * one that is not made to the other fails a test rather than letting the panel
52
+ * offer a button the server will refuse.
53
+ */
54
+ export declare const LIVE_CHANNEL_STATUSES: MessagingChannelStatus[];
55
+ /** Terminal, and not a reason to stop the tenant trying again. */
56
+ export declare function isDeadChannelStatus(status: MessagingChannelStatus): boolean;
57
+ /** On its way, but not carrying messages yet — the state the panel polls in. */
58
+ export declare function isPendingChannelStatus(status: MessagingChannelStatus): boolean;
59
+ /** The tenant's own number, live or on its way. Null when they have none. */
60
+ export declare function findDedicatedChannel(channels: MessagingChannelAccount[], tenantId?: string | null): MessagingChannelAccount | null;
61
+ /** The product's shared number, which every tenant sends by until it has its own. */
62
+ export declare function findFallbackChannel(channels: MessagingChannelAccount[]): MessagingChannelAccount | null;
63
+ /**
64
+ * Can this tenant ask for a number right now?
65
+ *
66
+ * The same rule the edge function answers 409 on, asked before the round trip
67
+ * so the button is disabled rather than the click refused.
68
+ */
69
+ export declare function canClaimDedicated(channels: MessagingChannelAccount[], tenantId?: string | null): boolean;
70
+ //# sourceMappingURL=channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../src/data/channels.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAEvC,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,WAAW,CAAA;AAE3D,mFAAmF;AACnF,MAAM,MAAM,sBAAsB,GAC9B,WAAW,GACX,cAAc,GACd,aAAa,GACb,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,cAAc,CAAA;AAElB,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAA;IACV,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,IAAI,EAAE,oBAAoB,CAAA;IAC1B,MAAM,EAAE,sBAAsB,CAAA;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAqBD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,4BAAiC,GACzC,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAoBpC;AAUD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,sBAAsB,EAM1D,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,sBAAsB,EAMzD,CAAA;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAE3E;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAE9E;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,uBAAuB,EAAE,EACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,uBAAuB,GAAG,IAAI,CAMhC;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,uBAAuB,EAAE,GAClC,uBAAuB,GAAG,IAAI,CAEhC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,uBAAuB,EAAE,EACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GACvB,OAAO,CAET"}
@@ -0,0 +1,42 @@
1
+ /** The edge function that holds both questions. */
2
+ export declare const PAYMENT_PREFLIGHT_FUNCTION = "tyxter-payment-preflight";
3
+ /**
4
+ * Which rail is actually behind the payments channel.
5
+ *
6
+ * The distinction that matters and is easy to collapse: `sandbox_default` is
7
+ * READY — charges open, settle and fire webhooks — but the money is not real,
8
+ * and the transparent-Pix provider options are refused. A green badge over it
9
+ * is how a salon finds out on the day of its first real charge, so the panel
10
+ * says "sandbox" beside the tick instead.
11
+ */
12
+ export type PaymentActiveMode = 'sandbox_default' | 'iniciador' | 'abacate_pay';
13
+ export interface PaymentReadiness {
14
+ /** The provider's own verdict. True for the sandbox fixture too — see `sandbox`. */
15
+ ready: boolean;
16
+ status: string;
17
+ mode: PaymentActiveMode | null;
18
+ /** `mode === 'sandbox_default'`: ready, but not with real money. */
19
+ sandbox: boolean;
20
+ environment: string | null;
21
+ /** Why not, when the provider says why. */
22
+ reason: string | null;
23
+ }
24
+ /** The browser handoff where the merchant enters credentials this app never sees. */
25
+ export interface PaymentSetupSession {
26
+ setupUrl: string | null;
27
+ requestId: string | null;
28
+ expiresAt: string | null;
29
+ environment: string | null;
30
+ }
31
+ export declare function readPaymentReadiness(tenantId?: string): Promise<PaymentReadiness>;
32
+ export declare function openPaymentSetupSession(tenantId?: string): Promise<PaymentSetupSession>;
33
+ /**
34
+ * Is the `pay` action safe to offer in a message template?
35
+ *
36
+ * The sandbox fixture counts, deliberately: a build being dogfooded on sandbox
37
+ * credentials must be able to exercise the whole flow, and the panel is where
38
+ * the "sandbox" qualifier is shown. What is refused is a channel with nothing
39
+ * selected behind it, where a Pix button would produce a charge that never opens.
40
+ */
41
+ export declare function canOfferPayment(readiness: PaymentReadiness | null): boolean;
42
+ //# sourceMappingURL=payment-preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-preflight.d.ts","sourceRoot":"","sources":["../../src/data/payment-preflight.ts"],"names":[],"mappings":"AAWA,mDAAmD;AACnD,eAAO,MAAM,0BAA0B,6BAA6B,CAAA;AAEpE;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,GAAG,WAAW,GAAG,aAAa,CAAA;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC9B,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,qFAAqF;AACrF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAqCD,wBAAsB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAGvF;AAED,wBAAsB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAE7F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAG3E"}
@@ -1,5 +1,6 @@
1
1
  export declare const T: {
2
2
  readonly conversations: "plg_conversations";
3
3
  readonly messages: "plg_conversation_messages";
4
+ readonly channels: "plg_conversations_channels";
4
5
  };
5
6
  //# sourceMappingURL=tables.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/data/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,CAAC;;;CAGJ,CAAA"}
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/data/tables.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,CAAC;;;;CAIJ,CAAA"}
package/dist/index.d.ts CHANGED
@@ -27,4 +27,7 @@ export type { ConversationsProvider } from './data/types';
27
27
  export type { Conversation, Message, Channel, CreateConversationInput } from './types';
28
28
  export { createMockConversationsProvider, type MockConversationsConfig } from './data/mock';
29
29
  export { createSupabaseConversationsProvider, type SupabaseConversationsConfig, } from './data/supabase';
30
+ export { readPaymentReadiness, openPaymentSetupSession, canOfferPayment, PAYMENT_PREFLIGHT_FUNCTION, type PaymentReadiness, type PaymentActiveMode, type PaymentSetupSession, } from './data/payment-preflight';
31
+ export { listMessagingChannels, type MessagingChannelAccount, type MessagingChannelKind, type MessagingChannelStatus, type ListMessagingChannelsOptions, } from './data/channels';
32
+ export { tyxterConnectorDef, TYXTER_CONNECTOR_ID, TYXTER_LATENCY_BUDGET_MS, } from './integrations/tyxter/connectorDef';
30
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAEvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAGlD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAkBzD,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IAEpC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAA;CAC7B;AA2BD,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,cAAc,CA8G9F;AAED,YAAY,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AACtF,OAAO,EAAE,+BAA+B,EAAE,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAC3F,OAAO,EACL,mCAAmC,EACnC,KAAK,2BAA2B,GACjC,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAEvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAGlD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AA8BzD,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IAEpC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAA;CAC7B;AA2BD,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,cAAc,CAoP9F;AAED,YAAY,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AACtF,OAAO,EAAE,+BAA+B,EAAE,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAC3F,OAAO,EACL,mCAAmC,EACnC,KAAK,2BAA2B,GACjC,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,4BAA4B,GAClC,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,oCAAoC,CAAA"}