@mereb/app-messaging 0.0.4 → 0.0.5

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/gql.d.ts CHANGED
@@ -1,3 +1 @@
1
- export declare const CONVERSATIONS: import("@apollo/client").DocumentNode;
2
- export declare const MESSAGES: import("@apollo/client").DocumentNode;
3
- export declare const SEND_MESSAGE: import("@apollo/client").DocumentNode;
1
+ export { ConversationsDocument as CONVERSATIONS, MessagesDocument as MESSAGES, SendMessageDocument as SEND_MESSAGE } from './headless.js';
package/dist/gql.js CHANGED
@@ -1,50 +1 @@
1
- import { gql } from '@apollo/client';
2
- export const CONVERSATIONS = gql `
3
- query Conversations {
4
- conversations {
5
- id
6
- title
7
- participantIds
8
- unreadCount
9
- updatedAt
10
- lastMessage {
11
- id
12
- senderName
13
- body
14
- sentAt
15
- conversationId
16
- }
17
- }
18
- }
19
- `;
20
- export const MESSAGES = gql `
21
- query ConversationMessages($conversationId: ID!, $after: String) {
22
- messages(conversationId: $conversationId, after: $after, limit: 50) {
23
- edges {
24
- cursor
25
- node {
26
- id
27
- conversationId
28
- senderName
29
- body
30
- sentAt
31
- }
32
- }
33
- pageInfo {
34
- endCursor
35
- hasNextPage
36
- }
37
- }
38
- }
39
- `;
40
- export const SEND_MESSAGE = gql `
41
- mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
42
- sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
43
- id
44
- conversationId
45
- senderName
46
- body
47
- sentAt
48
- }
49
- }
50
- `;
1
+ export { ConversationsDocument as CONVERSATIONS, MessagesDocument as MESSAGES, SendMessageDocument as SEND_MESSAGE } from './headless.js';
@@ -0,0 +1,38 @@
1
+ export type MessagingMessage = {
2
+ id: string;
3
+ conversationId: string;
4
+ senderId: string;
5
+ senderName?: string | null;
6
+ body: string;
7
+ sentAt: string;
8
+ };
9
+ export type MessagingConversation = {
10
+ id: string;
11
+ title: string;
12
+ participantIds: string[];
13
+ lastMessage?: MessagingMessage | null;
14
+ unreadCount: number;
15
+ updatedAt: string;
16
+ };
17
+ export type MessagingMessagesConnection = {
18
+ edges: Array<{
19
+ cursor: string;
20
+ node: MessagingMessage;
21
+ }>;
22
+ pageInfo: {
23
+ endCursor: string | null;
24
+ hasNextPage: boolean;
25
+ };
26
+ };
27
+ export type ConversationsQueryResult = {
28
+ conversations: MessagingConversation[];
29
+ };
30
+ export type ConversationMessagesQueryResult = {
31
+ messages: MessagingMessagesConnection;
32
+ };
33
+ export type SendMessageMutationResult = {
34
+ sendMessage: MessagingMessage;
35
+ };
36
+ export declare const ConversationsDocument: import("@apollo/client").DocumentNode;
37
+ export declare const MessagesDocument: import("@apollo/client").DocumentNode;
38
+ export declare const SendMessageDocument: import("@apollo/client").DocumentNode;
@@ -0,0 +1,53 @@
1
+ import { gql } from '@apollo/client';
2
+ export const ConversationsDocument = gql `
3
+ query Conversations {
4
+ conversations {
5
+ id
6
+ title
7
+ participantIds
8
+ unreadCount
9
+ updatedAt
10
+ lastMessage {
11
+ id
12
+ senderId
13
+ senderName
14
+ body
15
+ sentAt
16
+ conversationId
17
+ }
18
+ }
19
+ }
20
+ `;
21
+ export const MessagesDocument = gql `
22
+ query ConversationMessages($conversationId: ID!, $after: String) {
23
+ messages(conversationId: $conversationId, after: $after, limit: 50) {
24
+ edges {
25
+ cursor
26
+ node {
27
+ id
28
+ conversationId
29
+ senderId
30
+ senderName
31
+ body
32
+ sentAt
33
+ }
34
+ }
35
+ pageInfo {
36
+ endCursor
37
+ hasNextPage
38
+ }
39
+ }
40
+ }
41
+ `;
42
+ export const SendMessageDocument = gql `
43
+ mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
44
+ sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
45
+ id
46
+ conversationId
47
+ senderId
48
+ senderName
49
+ body
50
+ sentAt
51
+ }
52
+ }
53
+ `;
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export * from './store';
1
+ export * from './headless.js';
2
+ export * from './gql.js';
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
- export * from './store';
1
+ export * from './headless.js';
2
+ export * from './gql.js';
package/dist/native.d.ts CHANGED
@@ -1 +1 @@
1
- export declare function MessagesScreen(): import("react").JSX.Element;
1
+ export declare function MessagesScreen(): import("react/jsx-runtime.js").JSX.Element;
package/dist/native.js CHANGED
@@ -1,35 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
2
  import { useMemo, useState } from 'react';
2
3
  import { Text, View } from 'react-native';
3
- import { createInMemoryMessagingStore } from './store';
4
+ import { createInMemoryMessagingStore } from './store.js';
4
5
  export function MessagesScreen() {
5
6
  const store = useMemo(() => createInMemoryMessagingStore(), []);
6
7
  const [version, setVersion] = useState(0);
7
8
  const conversations = store.getConversations();
8
- return (<View style={{ flex: 1, padding: 24, gap: 16 }}>
9
- <Text style={{ fontWeight: '700', fontSize: 18, marginBottom: 8 }}>Messaging</Text>
10
- {conversations.map((conversation) => (<View key={conversation.id} style={{
11
- padding: 12,
12
- borderRadius: 12,
13
- borderWidth: 1,
14
- borderColor: '#E2E8F0',
15
- backgroundColor: '#FFFFFF',
16
- gap: 4
17
- }}>
18
- <Text style={{ fontWeight: '600', fontSize: 16 }}>{conversation.title}</Text>
19
- <Text style={{ color: '#475569' }}>{conversation.lastMessage?.body ?? 'No messages yet'}</Text>
20
- <Text style={{ color: '#94A3B8', fontSize: 12 }}>Unread: {conversation.unreadCount}</Text>
21
- </View>))}
22
- <Text onPress={() => {
23
- store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
24
- id: 'mobile-user',
25
- name: 'You'
26
- });
27
- setVersion((v) => v + 1);
28
- }} style={{ color: '#2563EB', fontWeight: '600' }}>
29
- Send a quick test message
30
- </Text>
31
- <Text style={{ color: '#94A3B8', fontSize: 12 }}>
32
- Version {version} • This view uses the shared in-memory store so it stays in sync with the web shell preview.
33
- </Text>
34
- </View>);
9
+ return (_jsxs(View, { style: { flex: 1, padding: 24, gap: 16 }, children: [_jsx(Text, { style: { fontWeight: '700', fontSize: 18, marginBottom: 8 }, children: "Messaging" }), conversations.map((conversation) => (_jsxs(View, { style: {
10
+ padding: 12,
11
+ borderRadius: 12,
12
+ borderWidth: 1,
13
+ borderColor: '#E2E8F0',
14
+ backgroundColor: '#FFFFFF',
15
+ gap: 4
16
+ }, children: [_jsx(Text, { style: { fontWeight: '600', fontSize: 16 }, children: conversation.title }), _jsx(Text, { style: { color: '#475569' }, children: conversation.lastMessage?.body ?? 'No messages yet' }), _jsxs(Text, { style: { color: '#94A3B8', fontSize: 12 }, children: ["Unread: ", conversation.unreadCount] })] }, conversation.id))), _jsx(Text, { onPress: () => {
17
+ store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
18
+ id: 'mobile-user',
19
+ name: 'You'
20
+ });
21
+ setVersion((v) => v + 1);
22
+ }, style: { color: '#2563EB', fontWeight: '600' }, children: "Send a quick test message" }), _jsxs(Text, { style: { color: '#94A3B8', fontSize: 12 }, children: ["Version ", version, " \u2022 This preview uses the package's local in-memory store. Web now uses the real messaging service."] })] }));
35
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mereb/app-messaging",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Messaging experience primitives for Mereb applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,6 +16,11 @@
16
16
  "import": "./dist/store.js",
17
17
  "default": "./dist/store.js"
18
18
  },
19
+ "./headless": {
20
+ "types": "./dist/headless.d.ts",
21
+ "import": "./dist/headless.js",
22
+ "default": "./dist/headless.js"
23
+ },
19
24
  "./native": {
20
25
  "types": "./dist/native.d.ts",
21
26
  "import": "./dist/native.js",
@@ -28,7 +33,6 @@
28
33
  }
29
34
  },
30
35
  "files": [
31
- "src",
32
36
  "dist",
33
37
  "package.json"
34
38
  ],
@@ -43,10 +47,16 @@
43
47
  "@apollo/client": "^3.12.5",
44
48
  "@types/react": "~18.2.79",
45
49
  "graphql": "^16.11.0",
46
- "typescript": ">=5.3.3"
50
+ "husky": "^9.1.7",
51
+ "typescript": ">=5.3.3",
52
+ "vitest": "^3.2.4"
47
53
  },
48
54
  "scripts": {
55
+ "clean": "node -e \"const fs=require('node:fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
49
56
  "typecheck": "tsc --noEmit --project tsconfig.json",
50
- "build": "tsc --project tsconfig.json"
57
+ "test": "pnpm exec vitest run --config vitest.config.ts",
58
+ "test:watch": "pnpm exec vitest --config vitest.config.ts",
59
+ "build": "pnpm run clean && tsc --project tsconfig.json",
60
+ "version:bump": "node ./scripts/bump-version.mjs"
51
61
  }
52
62
  }
package/src/gql.ts DELETED
@@ -1,53 +0,0 @@
1
- import {gql} from '@apollo/client'
2
-
3
- export const CONVERSATIONS = gql`
4
- query Conversations {
5
- conversations {
6
- id
7
- title
8
- participantIds
9
- unreadCount
10
- updatedAt
11
- lastMessage {
12
- id
13
- senderName
14
- body
15
- sentAt
16
- conversationId
17
- }
18
- }
19
- }
20
- `
21
-
22
- export const MESSAGES = gql`
23
- query ConversationMessages($conversationId: ID!, $after: String) {
24
- messages(conversationId: $conversationId, after: $after, limit: 50) {
25
- edges {
26
- cursor
27
- node {
28
- id
29
- conversationId
30
- senderName
31
- body
32
- sentAt
33
- }
34
- }
35
- pageInfo {
36
- endCursor
37
- hasNextPage
38
- }
39
- }
40
- }
41
- `
42
-
43
- export const SEND_MESSAGE = gql`
44
- mutation SendMessage($conversationId: ID, $toUserId: ID, $body: String!) {
45
- sendMessage(conversationId: $conversationId, toUserId: $toUserId, body: $body) {
46
- id
47
- conversationId
48
- senderName
49
- body
50
- sentAt
51
- }
52
- }
53
- `
package/src/index.tsx DELETED
@@ -1 +0,0 @@
1
- export * from './store'
package/src/native.tsx DELETED
@@ -1,47 +0,0 @@
1
- import { useMemo, useState } from 'react'
2
- import { Text, View } from 'react-native'
3
- import { createInMemoryMessagingStore } from './store'
4
-
5
- export function MessagesScreen() {
6
- const store = useMemo(() => createInMemoryMessagingStore(), [])
7
- const [version, setVersion] = useState(0)
8
- const conversations = store.getConversations()
9
-
10
- return (
11
- <View style={{ flex: 1, padding: 24, gap: 16 }}>
12
- <Text style={{ fontWeight: '700', fontSize: 18, marginBottom: 8 }}>Messaging</Text>
13
- {conversations.map((conversation) => (
14
- <View
15
- key={conversation.id}
16
- style={{
17
- padding: 12,
18
- borderRadius: 12,
19
- borderWidth: 1,
20
- borderColor: '#E2E8F0',
21
- backgroundColor: '#FFFFFF',
22
- gap: 4
23
- }}
24
- >
25
- <Text style={{ fontWeight: '600', fontSize: 16 }}>{conversation.title}</Text>
26
- <Text style={{ color: '#475569' }}>{conversation.lastMessage?.body ?? 'No messages yet'}</Text>
27
- <Text style={{ color: '#94A3B8', fontSize: 12 }}>Unread: {conversation.unreadCount}</Text>
28
- </View>
29
- ))}
30
- <Text
31
- onPress={() => {
32
- store.sendMessage(conversations[0]?.id ?? 'conv-1', 'Sent from mobile preview', {
33
- id: 'mobile-user',
34
- name: 'You'
35
- })
36
- setVersion((v) => v + 1)
37
- }}
38
- style={{ color: '#2563EB', fontWeight: '600' }}
39
- >
40
- Send a quick test message
41
- </Text>
42
- <Text style={{ color: '#94A3B8', fontSize: 12 }}>
43
- Version {version} • This view uses the shared in-memory store so it stays in sync with the web shell preview.
44
- </Text>
45
- </View>
46
- )
47
- }
package/src/store.ts DELETED
@@ -1,111 +0,0 @@
1
- export type MessagingMessage = {
2
- id: string
3
- conversationId: string
4
- senderId: string
5
- senderName?: string
6
- body: string
7
- sentAt: string
8
- }
9
-
10
- export type MessagingConversation = {
11
- id: string
12
- title: string
13
- participantIds: string[]
14
- lastMessage?: MessagingMessage
15
- unreadCount: number
16
- updatedAt: string
17
- }
18
-
19
- export type MessagingStore = {
20
- getConversations: () => MessagingConversation[]
21
- getConversation: (id: string) => MessagingConversation | undefined
22
- getMessages: (conversationId: string) => MessagingMessage[]
23
- sendMessage: (
24
- conversationId: string,
25
- body: string,
26
- sender?: { id: string; name?: string }
27
- ) => MessagingMessage
28
- }
29
-
30
- const seedConversations: MessagingConversation[] = [
31
- {
32
- id: 'conv-1',
33
- title: 'Platform Ops',
34
- participantIds: ['u-platform', 'u-release', 'u-ops'],
35
- unreadCount: 2,
36
- updatedAt: new Date().toISOString(),
37
- lastMessage: {
38
- id: 'msg-1',
39
- conversationId: 'conv-1',
40
- senderId: 'u-release',
41
- senderName: 'Release Manager',
42
- body: 'Copy that. Will queue prod promotion after smoke.',
43
- sentAt: new Date().toISOString()
44
- }
45
- },
46
- {
47
- id: 'conv-2',
48
- title: 'Support triage',
49
- participantIds: ['u-support', 'u-oncall', 'u-platform'],
50
- unreadCount: 0,
51
- updatedAt: new Date().toISOString(),
52
- lastMessage: {
53
- id: 'msg-2',
54
- conversationId: 'conv-2',
55
- senderId: 'u-oncall',
56
- senderName: 'On-call',
57
- body: 'Acknowledged. Looking at the router dashboards now.',
58
- sentAt: new Date().toISOString()
59
- }
60
- }
61
- ]
62
-
63
- export function createInMemoryMessagingStore(seed: MessagingConversation[] = seedConversations): MessagingStore {
64
- let conversations: MessagingConversation[] = seed.map((conversation) => ({
65
- ...conversation,
66
- lastMessage: conversation.lastMessage
67
- ? { ...conversation.lastMessage, sentAt: conversation.lastMessage.sentAt ?? new Date().toISOString() }
68
- : undefined
69
- }))
70
- const messageLookup: Record<string, MessagingMessage[]> = {}
71
-
72
- conversations.forEach((conversation) => {
73
- messageLookup[conversation.id] = conversation.lastMessage ? [conversation.lastMessage] : []
74
- })
75
-
76
- const refreshConversation = (conversationId: string, message: MessagingMessage) => {
77
- const idx = conversations.findIndex((c) => c.id === conversationId)
78
- if (idx >= 0) {
79
- conversations[idx] = {
80
- ...conversations[idx],
81
- lastMessage: message,
82
- updatedAt: message.sentAt,
83
- unreadCount: Math.max(0, conversations[idx].unreadCount - 1)
84
- }
85
- }
86
- }
87
-
88
- return {
89
- getConversations: () => [...conversations].sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)),
90
- getConversation: (id: string) => conversations.find((conversation) => conversation.id === id),
91
- getMessages: (conversationId: string) => [...(messageLookup[conversationId] ?? [])],
92
- sendMessage: (conversationId: string, body: string, sender?: { id: string; name?: string }) => {
93
- const conversationExists = conversations.some((conversation) => conversation.id === conversationId)
94
- if (!conversationExists) {
95
- throw new Error('Conversation not found')
96
- }
97
- const message: MessagingMessage = {
98
- id: `msg-${Math.random().toString(36).slice(2, 8)}`,
99
- conversationId,
100
- senderId: sender?.id ?? 'anonymous',
101
- senderName: sender?.name ?? 'You',
102
- body,
103
- sentAt: new Date().toISOString()
104
- }
105
-
106
- messageLookup[conversationId] = [...(messageLookup[conversationId] ?? []), message]
107
- refreshConversation(conversationId, message)
108
- return message
109
- }
110
- }
111
- }