@stapel/chat-react 0.1.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/CHANGELOG.md +1 -0
- package/MODULE.md +118 -0
- package/README.md +126 -0
- package/dist/api/chatApi.d.ts +49 -0
- package/dist/api/chatApi.d.ts.map +1 -0
- package/dist/api/chatApi.js +46 -0
- package/dist/api/chatApi.js.map +1 -0
- package/dist/api/extensions.d.ts +19 -0
- package/dist/api/extensions.d.ts.map +1 -0
- package/dist/api/extensions.js +19 -0
- package/dist/api/extensions.js.map +1 -0
- package/dist/api/generated/schema.d.ts +547 -0
- package/dist/api/generated/schema.d.ts.map +1 -0
- package/dist/api/generated/schema.js +2 -0
- package/dist/api/generated/schema.js.map +1 -0
- package/dist/api/types.d.ts +75 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/default/ConversationListPanel.d.ts +22 -0
- package/dist/default/ConversationListPanel.d.ts.map +1 -0
- package/dist/default/ConversationListPanel.js +49 -0
- package/dist/default/ConversationListPanel.js.map +1 -0
- package/dist/default/ConversationThreadPanel.d.ts +21 -0
- package/dist/default/ConversationThreadPanel.d.ts.map +1 -0
- package/dist/default/ConversationThreadPanel.js +51 -0
- package/dist/default/ConversationThreadPanel.js.map +1 -0
- package/dist/default/ErrorAlert.d.ts +8 -0
- package/dist/default/ErrorAlert.d.ts.map +1 -0
- package/dist/default/ErrorAlert.js +26 -0
- package/dist/default/ErrorAlert.js.map +1 -0
- package/dist/default/StartChatButton.d.ts +17 -0
- package/dist/default/StartChatButton.d.ts.map +1 -0
- package/dist/default/StartChatButton.js +18 -0
- package/dist/default/StartChatButton.js.map +1 -0
- package/dist/default/index.d.ts +21 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +18 -0
- package/dist/default/index.js.map +1 -0
- package/dist/flows/errors.d.ts +12 -0
- package/dist/flows/errors.d.ts.map +1 -0
- package/dist/flows/errors.js +13 -0
- package/dist/flows/errors.js.map +1 -0
- package/dist/flows/freshness.d.ts +69 -0
- package/dist/flows/freshness.d.ts.map +1 -0
- package/dist/flows/freshness.js +227 -0
- package/dist/flows/freshness.js.map +1 -0
- package/dist/flows/registry.d.ts +26 -0
- package/dist/flows/registry.d.ts.map +1 -0
- package/dist/flows/registry.js +24 -0
- package/dist/flows/registry.js.map +1 -0
- package/dist/headless/ChatProvider.d.ts +22 -0
- package/dist/headless/ChatProvider.d.ts.map +1 -0
- package/dist/headless/ChatProvider.js +18 -0
- package/dist/headless/ChatProvider.js.map +1 -0
- package/dist/headless/ConversationList.d.ts +51 -0
- package/dist/headless/ConversationList.d.ts.map +1 -0
- package/dist/headless/ConversationList.js +47 -0
- package/dist/headless/ConversationList.js.map +1 -0
- package/dist/headless/ConversationThread.d.ts +50 -0
- package/dist/headless/ConversationThread.d.ts.map +1 -0
- package/dist/headless/ConversationThread.js +64 -0
- package/dist/headless/ConversationThread.js.map +1 -0
- package/dist/headless/MessageComposer.d.ts +39 -0
- package/dist/headless/MessageComposer.d.ts.map +1 -0
- package/dist/headless/MessageComposer.js +45 -0
- package/dist/headless/MessageComposer.js.map +1 -0
- package/dist/headless/StartDirectChat.d.ts +40 -0
- package/dist/headless/StartDirectChat.d.ts.map +1 -0
- package/dist/headless/StartDirectChat.js +46 -0
- package/dist/headless/StartDirectChat.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +10 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +20 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +23 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +75 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +58 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +359 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +183 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +58 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +78 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +130 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +28 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +80 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +11 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +16 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/limits.d.ts +12 -0
- package/dist/model/limits.d.ts.map +1 -0
- package/dist/model/limits.js +12 -0
- package/dist/model/limits.js.map +1 -0
- package/dist/model/mutations.d.ts +66 -0
- package/dist/model/mutations.d.ts.map +1 -0
- package/dist/model/mutations.js +156 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/queries.d.ts +33 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +93 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +8 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +26 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/readMarker.d.ts +25 -0
- package/dist/model/readMarker.d.ts.map +1 -0
- package/dist/model/readMarker.js +31 -0
- package/dist/model/readMarker.js.map +1 -0
- package/dist/model/runtime.d.ts +50 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +27 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/model/threadWindow.d.ts +95 -0
- package/dist/model/threadWindow.d.ts.map +1 -0
- package/dist/model/threadWindow.js +100 -0
- package/dist/model/threadWindow.js.map +1 -0
- package/dist/nav/manifest.d.ts +23 -0
- package/dist/nav/manifest.d.ts.map +1 -0
- package/dist/nav/manifest.js +15 -0
- package/dist/nav/manifest.js.map +1 -0
- package/dist/realtime/chatSocket.d.ts +93 -0
- package/dist/realtime/chatSocket.d.ts.map +1 -0
- package/dist/realtime/chatSocket.js +199 -0
- package/dist/realtime/chatSocket.js.map +1 -0
- package/dist/realtime/frames.d.ts +123 -0
- package/dist/realtime/frames.d.ts.map +1 -0
- package/dist/realtime/frames.js +123 -0
- package/dist/realtime/frames.js.map +1 -0
- package/dist/realtime/streams.d.ts +49 -0
- package/dist/realtime/streams.d.ts.map +1 -0
- package/dist/realtime/streams.js +52 -0
- package/dist/realtime/streams.js.map +1 -0
- package/llms.txt +89 -0
- package/manifest.json +733 -0
- package/nav-manifest.json +25 -0
- package/package.json +114 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/chatApi.ts +142 -0
- package/src/api/extensions.ts +18 -0
- package/src/api/generated/schema.ts +549 -0
- package/src/api/types.ts +87 -0
- package/src/default/ConversationListPanel.tsx +163 -0
- package/src/default/ConversationThreadPanel.tsx +245 -0
- package/src/default/ErrorAlert.tsx +43 -0
- package/src/default/StartChatButton.tsx +78 -0
- package/src/default/index.ts +20 -0
- package/src/flows/errors.ts +16 -0
- package/src/flows/freshness.ts +315 -0
- package/src/flows/registry.ts +36 -0
- package/src/headless/ChatProvider.tsx +23 -0
- package/src/headless/ConversationList.tsx +92 -0
- package/src/headless/ConversationThread.tsx +107 -0
- package/src/headless/MessageComposer.tsx +86 -0
- package/src/headless/StartDirectChat.tsx +75 -0
- package/src/i18n/errorsMap.ts +31 -0
- package/src/i18n/es.ts +95 -0
- package/src/i18n/generated/errors.es.gen.ts +64 -0
- package/src/i18n/generated/errors.gen.ts +214 -0
- package/src/i18n/generated/errors.json +408 -0
- package/src/i18n/generated/errors.ru.gen.ts +64 -0
- package/src/i18n/keys.ts +152 -0
- package/src/i18n/ru.ts +98 -0
- package/src/index.ts +189 -0
- package/src/model/context.tsx +25 -0
- package/src/model/limits.ts +11 -0
- package/src/model/mutations.ts +208 -0
- package/src/model/queries.ts +120 -0
- package/src/model/queryKeys.ts +32 -0
- package/src/model/readMarker.ts +32 -0
- package/src/model/runtime.ts +90 -0
- package/src/model/threadWindow.ts +173 -0
- package/src/nav/manifest.ts +36 -0
- package/src/realtime/chatSocket.ts +304 -0
- package/src/realtime/frames.ts +229 -0
- package/src/realtime/streams.ts +85 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "@stapel/chat-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"id": "chat.conversations",
|
|
7
|
+
"labelKey": "chat.nav.conversations",
|
|
8
|
+
"icon": "MessageOutlined",
|
|
9
|
+
"route": {
|
|
10
|
+
"path": "chat"
|
|
11
|
+
},
|
|
12
|
+
"component": {
|
|
13
|
+
"export": "ConversationListPanel",
|
|
14
|
+
"subpath": "default"
|
|
15
|
+
},
|
|
16
|
+
"placement": {
|
|
17
|
+
"level": "top"
|
|
18
|
+
},
|
|
19
|
+
"menuVisibleDefault": true,
|
|
20
|
+
"requiresAuth": true,
|
|
21
|
+
"surface": "member",
|
|
22
|
+
"order": 25
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stapel/chat-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Headless React pair for stapel-chat: typed API client, TanStack Query hooks, a seq-ordered thread store, and ONE transport seam behind which the module's own resumable WebSocket protocol (hello/welcome/replay/replay_done/message/error) and seq-anchored polling are interchangeable — the UI never knows which is live. Conversation list with unread badges, thread replay + live tail, REST send, direct-thread idempotency for \"message the seller\", monotonic read markers. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/usestapel/stapel-react.git",
|
|
9
|
+
"directory": "packages/chat-react"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./default": {
|
|
21
|
+
"types": "./dist/default/index.d.ts",
|
|
22
|
+
"default": "./dist/default/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./i18n/ru": {
|
|
25
|
+
"types": "./dist/i18n/ru.d.ts",
|
|
26
|
+
"default": "./dist/i18n/ru.js"
|
|
27
|
+
},
|
|
28
|
+
"./i18n/es": {
|
|
29
|
+
"types": "./dist/i18n/es.d.ts",
|
|
30
|
+
"default": "./dist/i18n/es.js"
|
|
31
|
+
},
|
|
32
|
+
"./manifest": "./manifest.json",
|
|
33
|
+
"./manifest.json": "./manifest.json",
|
|
34
|
+
"./nav-manifest": "./nav-manifest.json",
|
|
35
|
+
"./nav-manifest.json": "./nav-manifest.json",
|
|
36
|
+
"./llms.txt": "./llms.txt",
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"src",
|
|
42
|
+
"tsconfig.json",
|
|
43
|
+
"README.md",
|
|
44
|
+
"MODULE.md",
|
|
45
|
+
"CHANGELOG.md",
|
|
46
|
+
"manifest.json",
|
|
47
|
+
"nav-manifest.json",
|
|
48
|
+
"llms.txt"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc -p tsconfig.json",
|
|
52
|
+
"test": "tsc -p tsconfig.demo.json && vitest run --exclude test/prodBundlePurity.test.ts",
|
|
53
|
+
"test:pack": "vitest run test/prodBundlePurity.test.ts",
|
|
54
|
+
"lint": "eslint .",
|
|
55
|
+
"size": "size-limit"
|
|
56
|
+
},
|
|
57
|
+
"size-limit": [
|
|
58
|
+
{
|
|
59
|
+
"name": "index — headless pair (client + hooks + thread store + transport seam)",
|
|
60
|
+
"path": "dist/index.js",
|
|
61
|
+
"limit": "9 KB"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "default — the antd chat skin must stay out of the main bundle",
|
|
65
|
+
"path": "dist/default/index.js",
|
|
66
|
+
"limit": "10 KB"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "i18n/ru — the Russian bundle is opt-in, not carried by the main entry",
|
|
70
|
+
"path": "dist/i18n/ru.js",
|
|
71
|
+
"limit": "4 KB"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "i18n/es — the Spanish bundle is opt-in, not carried by the main entry",
|
|
75
|
+
"path": "dist/i18n/es.js",
|
|
76
|
+
"limit": "3.5 KB"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"peerDependencies": {
|
|
80
|
+
"@stapel/core": ">=0.14.0 <1.0.0",
|
|
81
|
+
"@tanstack/react-query": "^5.0.0",
|
|
82
|
+
"antd": ">=5.20.0 <7",
|
|
83
|
+
"react": ">=19"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"antd": {
|
|
87
|
+
"optional": true
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@size-limit/preset-small-lib": "^11.2.0",
|
|
92
|
+
"@stapel/core": "workspace:^",
|
|
93
|
+
"@stapel/showcase": "workspace:^",
|
|
94
|
+
"@stapel/tokens": "workspace:^",
|
|
95
|
+
"@tanstack/react-query": "^5.81.0",
|
|
96
|
+
"@testing-library/react": "^16.3.0",
|
|
97
|
+
"@types/react": "^19.1.0",
|
|
98
|
+
"@types/react-dom": "^19.1.0",
|
|
99
|
+
"antd": "^6.5.0",
|
|
100
|
+
"jsdom": "^26.1.0",
|
|
101
|
+
"msw": "^2.10.2",
|
|
102
|
+
"react": "^19.1.0",
|
|
103
|
+
"react-dom": "^19.1.0",
|
|
104
|
+
"size-limit": "^11.2.0",
|
|
105
|
+
"typescript": "^5.8.3",
|
|
106
|
+
"vitest": "^3.2.4"
|
|
107
|
+
},
|
|
108
|
+
"engines": {
|
|
109
|
+
"node": ">=22"
|
|
110
|
+
},
|
|
111
|
+
"publishConfig": {
|
|
112
|
+
"access": "public"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { StapelClient, StapelRequestOptions } from "@stapel/core";
|
|
2
|
+
import type {
|
|
3
|
+
ChatMessage,
|
|
4
|
+
Conversation,
|
|
5
|
+
ConversationKind,
|
|
6
|
+
ConversationListParams,
|
|
7
|
+
ConversationPage,
|
|
8
|
+
MessageHistoryParams,
|
|
9
|
+
MessagePage,
|
|
10
|
+
SendMessageRequest,
|
|
11
|
+
} from "./types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* CSRF rule for cookie-authenticated browser clients (mirrors auth-react): the
|
|
15
|
+
* simplest SPA rule is to always send `X-Requested-With: XMLHttpRequest` on
|
|
16
|
+
* mutating requests. Header-token clients ignore it; it is harmless there, so
|
|
17
|
+
* every mutation carries it.
|
|
18
|
+
*/
|
|
19
|
+
const CSRF_HEADERS: Record<string, string> = {
|
|
20
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function mutating(
|
|
24
|
+
options?: Omit<StapelRequestOptions, "method" | "body">
|
|
25
|
+
): Omit<StapelRequestOptions, "method" | "body"> {
|
|
26
|
+
return {
|
|
27
|
+
...options,
|
|
28
|
+
headers: { ...CSRF_HEADERS, ...options?.headers },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Anchor/direction/limit → a query object, omitting what was not asked for. */
|
|
33
|
+
function pageQuery(
|
|
34
|
+
params: ConversationListParams | MessageHistoryParams | undefined
|
|
35
|
+
): Record<string, string | number> {
|
|
36
|
+
const query: Record<string, string | number> = {};
|
|
37
|
+
if (params?.anchor !== undefined) query.anchor = params.anchor;
|
|
38
|
+
if (params?.direction !== undefined) query.direction = params.direction;
|
|
39
|
+
if (params?.limit !== undefined) query.limit = params.limit;
|
|
40
|
+
return query;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The pair's typed operation surface — one method per stapel-chat endpoint a
|
|
45
|
+
* buyer/seller client may call, bound to the injected {@link StapelClient}
|
|
46
|
+
* (the per-module override seam of frontend-standard §7.2). Paths are relative
|
|
47
|
+
* to the runtime's `baseUrl` (`/chat/api/v1`).
|
|
48
|
+
*
|
|
49
|
+
* **WRITES NEVER GO OVER THE SOCKET.** stapel-chat's `ChatConsumer` accepts a
|
|
50
|
+
* `send` frame, and this pair deliberately does not use it: a message is a
|
|
51
|
+
* durable row with a locked `seq` allocation, and the REST path is the one
|
|
52
|
+
* that answers with the persisted row (and with a real error envelope when it
|
|
53
|
+
* refuses). The socket is a delivery convenience over that journal — see
|
|
54
|
+
* `src/realtime/`.
|
|
55
|
+
*
|
|
56
|
+
* The support-operator half of the module (`/support/queue`, assign / resolve
|
|
57
|
+
* / reopen) is intentionally absent: it is an operator console, not the
|
|
58
|
+
* buyer-and-seller surface this pair ships (spec §4.5 "Not in MVP").
|
|
59
|
+
*
|
|
60
|
+
* These operations will be GENERATED from schema.json operationIds by gen-api
|
|
61
|
+
* v2; until then they are hand-authored here (the ONE legal home of path
|
|
62
|
+
* strings — `stapel/no-string-paths` §2.3 carve-out).
|
|
63
|
+
*/
|
|
64
|
+
export interface ChatApi {
|
|
65
|
+
readonly client: StapelClient;
|
|
66
|
+
|
|
67
|
+
/** A page of the caller's conversations, newest activity first. */
|
|
68
|
+
conversations(params?: ConversationListParams): Promise<ConversationPage>;
|
|
69
|
+
/** One conversation (participant-only; 403 otherwise). */
|
|
70
|
+
conversation(conversationId: string): Promise<Conversation>;
|
|
71
|
+
/**
|
|
72
|
+
* Get-or-create a conversation. For `direct`, `participantIds` must name
|
|
73
|
+
* exactly one OTHER user: the thread is keyed by the (order-independent)
|
|
74
|
+
* participant pair, so a second call returns the same row — the idempotency
|
|
75
|
+
* "message the seller" is built on.
|
|
76
|
+
*/
|
|
77
|
+
createConversation(
|
|
78
|
+
kind: ConversationKind,
|
|
79
|
+
participantIds?: readonly string[]
|
|
80
|
+
): Promise<Conversation>;
|
|
81
|
+
/** A page of message history, anchored on `seq` (see {@link MessageHistoryParams}). */
|
|
82
|
+
messages(
|
|
83
|
+
conversationId: string,
|
|
84
|
+
params?: MessageHistoryParams
|
|
85
|
+
): Promise<MessagePage>;
|
|
86
|
+
/** Append a message. Resolves with the persisted row, carrying its `seq`. */
|
|
87
|
+
sendMessage(
|
|
88
|
+
conversationId: string,
|
|
89
|
+
body: SendMessageRequest
|
|
90
|
+
): Promise<ChatMessage>;
|
|
91
|
+
/**
|
|
92
|
+
* Advance the caller's read marker. The server never moves it backwards
|
|
93
|
+
* (`services.mark_read`), and neither does this pair — see
|
|
94
|
+
* `model/readMarker.ts`.
|
|
95
|
+
*/
|
|
96
|
+
markRead(conversationId: string, uptoSeq: number): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function createChatApi(client: StapelClient): ChatApi {
|
|
100
|
+
const conversationPath = (id: string): string =>
|
|
101
|
+
`/conversations/${encodeURIComponent(id)}`;
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
client,
|
|
105
|
+
|
|
106
|
+
conversations: (params) =>
|
|
107
|
+
client.get("/conversations", { query: pageQuery(params) }),
|
|
108
|
+
|
|
109
|
+
conversation: (conversationId) => client.get(conversationPath(conversationId)),
|
|
110
|
+
|
|
111
|
+
createConversation: (kind, participantIds) =>
|
|
112
|
+
client.post(
|
|
113
|
+
"/conversations",
|
|
114
|
+
{
|
|
115
|
+
kind,
|
|
116
|
+
...(participantIds !== undefined
|
|
117
|
+
? { participant_ids: [...participantIds] }
|
|
118
|
+
: {}),
|
|
119
|
+
},
|
|
120
|
+
mutating()
|
|
121
|
+
),
|
|
122
|
+
|
|
123
|
+
messages: (conversationId, params) =>
|
|
124
|
+
client.get(`${conversationPath(conversationId)}/messages`, {
|
|
125
|
+
query: pageQuery(params),
|
|
126
|
+
}),
|
|
127
|
+
|
|
128
|
+
sendMessage: (conversationId, body) =>
|
|
129
|
+
client.post(
|
|
130
|
+
`${conversationPath(conversationId)}/messages`,
|
|
131
|
+
body,
|
|
132
|
+
mutating()
|
|
133
|
+
),
|
|
134
|
+
|
|
135
|
+
markRead: (conversationId, uptoSeq) =>
|
|
136
|
+
client.post(
|
|
137
|
+
`${conversationPath(conversationId)}/read`,
|
|
138
|
+
{ upto_seq: uptoSeq },
|
|
139
|
+
mutating()
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-authored API surface the codegen does not (yet) cover. Everything that
|
|
3
|
+
* CAN be derived from schema.json belongs in the generated operations
|
|
4
|
+
* (`api/chatApi.ts`), not here.
|
|
5
|
+
*
|
|
6
|
+
* ── One field the operation surface deliberately does NOT expose ────────────
|
|
7
|
+
*
|
|
8
|
+
* `CreateConversationRequest.scope_key` exists in the schema and its own
|
|
9
|
+
* description says the rest: *"Ignored — the scope is resolved server-side
|
|
10
|
+
* from the request"* (`views.ConversationListCreateView.post` calls
|
|
11
|
+
* `get_scope_provider().resolve(request)` and never reads the field). A client
|
|
12
|
+
* that sends a listing id there would believe it had scoped the thread to a
|
|
13
|
+
* listing; the server would key the direct thread by the participant PAIR, as
|
|
14
|
+
* it always does. Rather than ship a parameter that silently does nothing,
|
|
15
|
+
* `createConversation` has no `scopeKey` argument at all — the listing context
|
|
16
|
+
* belongs in the first MESSAGE, which is durable, visible, and actually read.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|