@getuserfeedback/chat 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/README.md +87 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +10 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @getuserfeedback/chat
|
|
2
|
+
|
|
3
|
+
Headless TypeScript client for the getuserfeedback Chat API.
|
|
4
|
+
|
|
5
|
+
Use this package when you want to build your own user-facing conversation UI while getuserfeedback owns the team inbox, delivery tracking, and conversation backend.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @getuserfeedback/chat
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Create a client
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createChatClient } from "@getuserfeedback/chat";
|
|
17
|
+
|
|
18
|
+
export const chat = createChatClient({
|
|
19
|
+
token: process.env.GETUSERFEEDBACK_CHAT_API_KEY,
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
API keys are bearer credentials. Keep them on your server unless you are using a separately scoped browser-safe credential model.
|
|
24
|
+
|
|
25
|
+
## List conversations
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const { items } = await chat.inbox.list({
|
|
29
|
+
userId: user.id,
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## List messages
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const { messages } = await chat.messages.list({
|
|
37
|
+
conversationId: "conv_123",
|
|
38
|
+
userId: user.id,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Send a user message
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const { message } = await chat.messages.send({
|
|
46
|
+
clientMessageId: crypto.randomUUID(),
|
|
47
|
+
conversationId: "conv_123",
|
|
48
|
+
text: "Hello from my app.",
|
|
49
|
+
userId: user.id,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`clientMessageId` is optional but recommended. It makes retries idempotent within the authenticated Conversation API integration.
|
|
54
|
+
|
|
55
|
+
## Submit delivery events
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
await chat.receipts.submit({
|
|
59
|
+
receipts: [
|
|
60
|
+
{
|
|
61
|
+
data: {
|
|
62
|
+
deliveryId: "del_123",
|
|
63
|
+
sendAttemptId: "attempt_123",
|
|
64
|
+
},
|
|
65
|
+
id: "evt_123",
|
|
66
|
+
occurredAt: new Date().toISOString(),
|
|
67
|
+
type: "delivery.delivered",
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Errors
|
|
74
|
+
|
|
75
|
+
Failed API responses throw `ChatApiError`.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { ChatApiError } from "@getuserfeedback/chat";
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await chat.inbox.list({ userId: user.id });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof ChatApiError) {
|
|
84
|
+
console.error(error.status, error.code, error.message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
declare const CHAT_API_VERSION = "conversation-api@1";
|
|
2
|
+
declare const DEFAULT_CHAT_API_BASE_URL = "https://api.getuserfeedback.com";
|
|
3
|
+
type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
4
|
+
type ChatMessageAuthor = "system" | "team" | "unknown" | "user";
|
|
5
|
+
type ChatMessageContent = {
|
|
6
|
+
text: string;
|
|
7
|
+
type: "text";
|
|
8
|
+
} | {
|
|
9
|
+
responseId: string;
|
|
10
|
+
type: "flow_response";
|
|
11
|
+
} | {
|
|
12
|
+
type: "unsupported";
|
|
13
|
+
};
|
|
14
|
+
interface ChatMessage {
|
|
15
|
+
author: ChatMessageAuthor;
|
|
16
|
+
content: ChatMessageContent;
|
|
17
|
+
conversationId: string;
|
|
18
|
+
id: string;
|
|
19
|
+
receivedAt: string;
|
|
20
|
+
sentAt: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
}
|
|
23
|
+
interface ChatConversation {
|
|
24
|
+
createdAt: string;
|
|
25
|
+
id: string;
|
|
26
|
+
latestMessage: Pick<ChatMessage, "author" | "content" | "id" | "timestamp"> | null;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
29
|
+
interface ChatInboxListResponse {
|
|
30
|
+
items: ChatConversation[];
|
|
31
|
+
limit: number;
|
|
32
|
+
}
|
|
33
|
+
interface ChatMessageListResponse {
|
|
34
|
+
conversationId: string;
|
|
35
|
+
limit: number;
|
|
36
|
+
messages: ChatMessage[];
|
|
37
|
+
}
|
|
38
|
+
interface ChatMessageSendResponse {
|
|
39
|
+
message: ChatMessage;
|
|
40
|
+
}
|
|
41
|
+
type ChatReceiptType = "delivery.accepted" | "delivery.delivered" | "delivery.failed" | "delivery.read" | "delivery.rejected";
|
|
42
|
+
type ChatReceipt = {
|
|
43
|
+
data: ChatReceiptData;
|
|
44
|
+
id: string;
|
|
45
|
+
occurredAt: string;
|
|
46
|
+
type: Exclude<ChatReceiptType, "delivery.failed">;
|
|
47
|
+
} | {
|
|
48
|
+
data: ChatFailedReceiptData;
|
|
49
|
+
id: string;
|
|
50
|
+
occurredAt: string;
|
|
51
|
+
type: "delivery.failed";
|
|
52
|
+
};
|
|
53
|
+
interface ChatReceiptData {
|
|
54
|
+
deliveryId: string;
|
|
55
|
+
reason?: string;
|
|
56
|
+
sendAttemptId: string;
|
|
57
|
+
}
|
|
58
|
+
interface ChatFailedReceiptData extends ChatReceiptData {
|
|
59
|
+
retryable?: boolean;
|
|
60
|
+
}
|
|
61
|
+
interface ChatReceiptSubmitResponse {
|
|
62
|
+
processed: number;
|
|
63
|
+
status: "accepted";
|
|
64
|
+
}
|
|
65
|
+
interface ChatClientOptions {
|
|
66
|
+
baseUrl?: string;
|
|
67
|
+
fetch?: FetchLike;
|
|
68
|
+
token: string;
|
|
69
|
+
}
|
|
70
|
+
interface ChatRequestOptions {
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}
|
|
73
|
+
interface ChatInboxListInput extends ChatRequestOptions {
|
|
74
|
+
limit?: number;
|
|
75
|
+
userId: string;
|
|
76
|
+
}
|
|
77
|
+
interface ChatMessageListInput extends ChatRequestOptions {
|
|
78
|
+
conversationId: string;
|
|
79
|
+
limit?: number;
|
|
80
|
+
userId: string;
|
|
81
|
+
}
|
|
82
|
+
interface ChatMessageSendInput extends ChatRequestOptions {
|
|
83
|
+
clientMessageId?: string;
|
|
84
|
+
conversationId: string;
|
|
85
|
+
text: string;
|
|
86
|
+
userId: string;
|
|
87
|
+
}
|
|
88
|
+
interface ChatReceiptSubmitInput extends ChatRequestOptions {
|
|
89
|
+
receipts: ChatReceipt[];
|
|
90
|
+
}
|
|
91
|
+
interface ChatClient {
|
|
92
|
+
inbox: {
|
|
93
|
+
list: (input: ChatInboxListInput) => Promise<ChatInboxListResponse>;
|
|
94
|
+
};
|
|
95
|
+
messages: {
|
|
96
|
+
list: (input: ChatMessageListInput) => Promise<ChatMessageListResponse>;
|
|
97
|
+
send: (input: ChatMessageSendInput) => Promise<ChatMessageSendResponse>;
|
|
98
|
+
};
|
|
99
|
+
receipts: {
|
|
100
|
+
submit: (input: ChatReceiptSubmitInput) => Promise<ChatReceiptSubmitResponse>;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
interface ChatApiErrorDetails {
|
|
104
|
+
code: string;
|
|
105
|
+
responseBody: unknown;
|
|
106
|
+
status: number;
|
|
107
|
+
url: string;
|
|
108
|
+
}
|
|
109
|
+
declare class ChatApiError extends Error {
|
|
110
|
+
code: string;
|
|
111
|
+
responseBody: unknown;
|
|
112
|
+
status: number;
|
|
113
|
+
url: string;
|
|
114
|
+
constructor(message: string, details: ChatApiErrorDetails);
|
|
115
|
+
}
|
|
116
|
+
declare function createChatClient(options: ChatClientOptions): ChatClient;
|
|
117
|
+
|
|
118
|
+
export { CHAT_API_VERSION, ChatApiError, DEFAULT_CHAT_API_BASE_URL, createChatClient };
|
|
119
|
+
export type { ChatApiErrorDetails, ChatClient, ChatClientOptions, ChatConversation, ChatFailedReceiptData, ChatInboxListInput, ChatInboxListResponse, ChatMessage, ChatMessageAuthor, ChatMessageContent, ChatMessageListInput, ChatMessageListResponse, ChatMessageSendInput, ChatMessageSendResponse, ChatReceipt, ChatReceiptData, ChatReceiptSubmitInput, ChatReceiptSubmitResponse, ChatReceiptType, ChatRequestOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var b="conversation-api@1",v="https://api.getuserfeedback.com";class g extends Error{code;responseBody;status;url;constructor(e,t){super(e);this.name="ChatApiError",this.code=t.code,this.responseBody=t.responseBody,this.status=t.status,this.url=t.url}}function w(e){let t=o(e.token,"token"),a=I(e.fetch),c=f(e.baseUrl??"https://api.getuserfeedback.com"),d=async(s,r={})=>{let i=new URL(`${c.pathname}${s}`,c.origin),n=new Headers(r.headers);if(n.set("Authorization",`Bearer ${t}`),r.body!==void 0&&!n.has("Content-Type"))n.set("Content-Type","application/json");let p=await a(i,{...r,headers:n});return await m(p,i)};return{inbox:{list:async(s)=>{let r=o(s.userId,"userId"),i=u({limit:s.limit,userId:r}),n=await d(`/conversations?${i.toString()}`,{method:"GET",signal:s.signal});return{items:n.conversations,limit:n.limit}}},messages:{list:async(s)=>{let r=o(s.userId,"userId"),i=o(s.conversationId,"conversationId"),n=u({limit:s.limit,userId:r});return await d(`/conversations/${encodeURIComponent(i)}/messages?${n.toString()}`,{method:"GET",signal:s.signal})},send:async(s)=>{let r=o(s.userId,"userId"),i=o(s.conversationId,"conversationId"),n=o(s.text,"text"),p=u({userId:r}),h={text:n};if(s.clientMessageId!==void 0)h.clientMessageId=o(s.clientMessageId,"clientMessageId");return await d(`/conversations/${encodeURIComponent(i)}/messages?${p.toString()}`,{body:JSON.stringify(h),method:"POST",signal:s.signal})}},receipts:{submit:async(s)=>{if(s.receipts.length===0)throw TypeError("receipts must include at least one receipt.");return await d("/conversations/events",{body:JSON.stringify({events:s.receipts,version:"conversation-api@1"}),method:"POST",signal:s.signal})}}}}async function m(e,t){let a=await C(e);if(e.ok)return a;let c=R(a);throw new g(x(a,c),{code:c,responseBody:a,status:e.status,url:t.toString()})}async function C(e){let t=await e.text();if(t.trim().length===0)return null;try{return JSON.parse(t)}catch{throw new g("Chat API returned invalid JSON.",{code:"invalid_response",responseBody:t,status:e.status,url:e.url})}}function f(e){let t=new URL(e);return t.search="",t.hash="",t.pathname=y(t.pathname),t}function y(e){let t=e.replace(/\/+$/,"");if(t==="")return"/v1";if(t.endsWith("/v1"))return t;return`${t}/v1`}function I(e){if(e!==void 0)return e;if(typeof globalThis.fetch==="function")return globalThis.fetch.bind(globalThis);throw TypeError("Chat API client requires a fetch implementation.")}function o(e,t){if(typeof e!=="string"||e.trim().length===0)throw TypeError(`${t} must be a non-empty string.`);return e}function u(e){let t=new URLSearchParams({identity:e.userId});if(e.limit!==void 0){if(!Number.isInteger(e.limit)||e.limit<1)throw TypeError("limit must be a positive integer.");t.set("limit",String(e.limit))}return t}function R(e){return l(e,"code")??"request_failed"}function x(e,t){return l(e,"error")??`Chat API request failed with code ${t}.`}function l(e,t){if(typeof e==="object"&&e!==null&&t in e&&typeof e[t]==="string")return e[t];return null}export{w as createChatClient,v as DEFAULT_CHAT_API_BASE_URL,g as ChatApiError,b as CHAT_API_VERSION};
|
|
2
|
+
|
|
3
|
+
//# debugId=7913DB41F613110B64756E2164756E21
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export const CHAT_API_VERSION = \"conversation-api@1\";\nexport const DEFAULT_CHAT_API_BASE_URL = \"https://api.getuserfeedback.com\";\n\ntype FetchLike = (\n\tinput: string | URL | Request,\n\tinit?: RequestInit,\n) => Promise<Response>;\n\nexport type ChatMessageAuthor = \"system\" | \"team\" | \"unknown\" | \"user\";\n\nexport type ChatMessageContent =\n\t| {\n\t\t\ttext: string;\n\t\t\ttype: \"text\";\n\t }\n\t| {\n\t\t\tresponseId: string;\n\t\t\ttype: \"flow_response\";\n\t }\n\t| {\n\t\t\ttype: \"unsupported\";\n\t };\n\nexport interface ChatMessage {\n\tauthor: ChatMessageAuthor;\n\tcontent: ChatMessageContent;\n\tconversationId: string;\n\tid: string;\n\treceivedAt: string;\n\tsentAt: string;\n\ttimestamp: string;\n}\n\nexport interface ChatConversation {\n\tcreatedAt: string;\n\tid: string;\n\tlatestMessage: Pick<\n\t\tChatMessage,\n\t\t\"author\" | \"content\" | \"id\" | \"timestamp\"\n\t> | null;\n\tupdatedAt: string;\n}\n\nexport interface ChatInboxListResponse {\n\titems: ChatConversation[];\n\tlimit: number;\n}\n\nexport interface ChatMessageListResponse {\n\tconversationId: string;\n\tlimit: number;\n\tmessages: ChatMessage[];\n}\n\nexport interface ChatMessageSendResponse {\n\tmessage: ChatMessage;\n}\n\nexport type ChatReceiptType =\n\t| \"delivery.accepted\"\n\t| \"delivery.delivered\"\n\t| \"delivery.failed\"\n\t| \"delivery.read\"\n\t| \"delivery.rejected\";\n\nexport type ChatReceipt =\n\t| {\n\t\t\tdata: ChatReceiptData;\n\t\t\tid: string;\n\t\t\toccurredAt: string;\n\t\t\ttype: Exclude<ChatReceiptType, \"delivery.failed\">;\n\t }\n\t| {\n\t\t\tdata: ChatFailedReceiptData;\n\t\t\tid: string;\n\t\t\toccurredAt: string;\n\t\t\ttype: \"delivery.failed\";\n\t };\n\nexport interface ChatReceiptData {\n\tdeliveryId: string;\n\treason?: string;\n\tsendAttemptId: string;\n}\n\nexport interface ChatFailedReceiptData extends ChatReceiptData {\n\tretryable?: boolean;\n}\n\nexport interface ChatReceiptSubmitResponse {\n\tprocessed: number;\n\tstatus: \"accepted\";\n}\n\nexport interface ChatClientOptions {\n\tbaseUrl?: string;\n\tfetch?: FetchLike;\n\ttoken: string;\n}\n\nexport interface ChatRequestOptions {\n\tsignal?: AbortSignal;\n}\n\nexport interface ChatInboxListInput extends ChatRequestOptions {\n\tlimit?: number;\n\tuserId: string;\n}\n\nexport interface ChatMessageListInput extends ChatRequestOptions {\n\tconversationId: string;\n\tlimit?: number;\n\tuserId: string;\n}\n\nexport interface ChatMessageSendInput extends ChatRequestOptions {\n\tclientMessageId?: string;\n\tconversationId: string;\n\ttext: string;\n\tuserId: string;\n}\n\nexport interface ChatReceiptSubmitInput extends ChatRequestOptions {\n\treceipts: ChatReceipt[];\n}\n\nexport interface ChatClient {\n\tinbox: {\n\t\tlist: (input: ChatInboxListInput) => Promise<ChatInboxListResponse>;\n\t};\n\tmessages: {\n\t\tlist: (input: ChatMessageListInput) => Promise<ChatMessageListResponse>;\n\t\tsend: (input: ChatMessageSendInput) => Promise<ChatMessageSendResponse>;\n\t};\n\treceipts: {\n\t\tsubmit: (\n\t\t\tinput: ChatReceiptSubmitInput,\n\t\t) => Promise<ChatReceiptSubmitResponse>;\n\t};\n}\n\nexport interface ChatApiErrorDetails {\n\tcode: string;\n\tresponseBody: unknown;\n\tstatus: number;\n\turl: string;\n}\n\nexport class ChatApiError extends Error {\n\tcode: string;\n\tresponseBody: unknown;\n\tstatus: number;\n\turl: string;\n\n\tconstructor(message: string, details: ChatApiErrorDetails) {\n\t\tsuper(message);\n\t\tthis.name = \"ChatApiError\";\n\t\tthis.code = details.code;\n\t\tthis.responseBody = details.responseBody;\n\t\tthis.status = details.status;\n\t\tthis.url = details.url;\n\t}\n}\n\nexport function createChatClient(options: ChatClientOptions): ChatClient {\n\tconst token = requireNonEmptyString(options.token, \"token\");\n\tconst fetchFn = resolveFetch(options.fetch);\n\tconst baseUrl = resolveChatApiBaseUrl(\n\t\toptions.baseUrl ?? DEFAULT_CHAT_API_BASE_URL,\n\t);\n\n\tconst request = async <ResponseBody>(\n\t\tpath: string,\n\t\tinit: RequestInit & { signal?: AbortSignal } = {},\n\t): Promise<ResponseBody> => {\n\t\tconst url = new URL(`${baseUrl.pathname}${path}`, baseUrl.origin);\n\t\tconst headers = new Headers(init.headers);\n\t\theaders.set(\"Authorization\", `Bearer ${token}`);\n\t\tif (init.body !== undefined && !headers.has(\"Content-Type\")) {\n\t\t\theaders.set(\"Content-Type\", \"application/json\");\n\t\t}\n\n\t\tconst response = await fetchFn(url, {\n\t\t\t...init,\n\t\t\theaders,\n\t\t});\n\n\t\treturn await readChatApiResponse<ResponseBody>(response, url);\n\t};\n\n\treturn {\n\t\tinbox: {\n\t\t\tlist: async (input) => {\n\t\t\t\tconst userId = requireNonEmptyString(input.userId, \"userId\");\n\t\t\t\tconst query = toUserLimitSearchParams({\n\t\t\t\t\tlimit: input.limit,\n\t\t\t\t\tuserId,\n\t\t\t\t});\n\t\t\t\tconst response = await request<{\n\t\t\t\t\tconversations: ChatConversation[];\n\t\t\t\t\tlimit: number;\n\t\t\t\t}>(`/conversations?${query.toString()}`, {\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: input.signal,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\titems: response.conversations,\n\t\t\t\t\tlimit: response.limit,\n\t\t\t\t};\n\t\t\t},\n\t\t},\n\t\tmessages: {\n\t\t\tlist: async (input) => {\n\t\t\t\tconst userId = requireNonEmptyString(input.userId, \"userId\");\n\t\t\t\tconst conversationId = requireNonEmptyString(\n\t\t\t\t\tinput.conversationId,\n\t\t\t\t\t\"conversationId\",\n\t\t\t\t);\n\t\t\t\tconst query = toUserLimitSearchParams({\n\t\t\t\t\tlimit: input.limit,\n\t\t\t\t\tuserId,\n\t\t\t\t});\n\t\t\t\treturn await request<ChatMessageListResponse>(\n\t\t\t\t\t`/conversations/${encodeURIComponent(conversationId)}/messages?${query.toString()}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\tsignal: input.signal,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t\tsend: async (input) => {\n\t\t\t\tconst userId = requireNonEmptyString(input.userId, \"userId\");\n\t\t\t\tconst conversationId = requireNonEmptyString(\n\t\t\t\t\tinput.conversationId,\n\t\t\t\t\t\"conversationId\",\n\t\t\t\t);\n\t\t\t\tconst text = requireNonEmptyString(input.text, \"text\");\n\t\t\t\tconst query = toUserLimitSearchParams({ userId });\n\t\t\t\tconst body: {\n\t\t\t\t\tclientMessageId?: string;\n\t\t\t\t\ttext: string;\n\t\t\t\t} = { text };\n\t\t\t\tif (input.clientMessageId !== undefined) {\n\t\t\t\t\tbody.clientMessageId = requireNonEmptyString(\n\t\t\t\t\t\tinput.clientMessageId,\n\t\t\t\t\t\t\"clientMessageId\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn await request<ChatMessageSendResponse>(\n\t\t\t\t\t`/conversations/${encodeURIComponent(conversationId)}/messages?${query.toString()}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\tsignal: input.signal,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\treceipts: {\n\t\t\tsubmit: async (input) => {\n\t\t\t\tif (input.receipts.length === 0) {\n\t\t\t\t\tthrow new TypeError(\"receipts must include at least one receipt.\");\n\t\t\t\t}\n\t\t\t\treturn await request<ChatReceiptSubmitResponse>(\n\t\t\t\t\t\"/conversations/events\",\n\t\t\t\t\t{\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tevents: input.receipts,\n\t\t\t\t\t\t\tversion: CHAT_API_VERSION,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\tsignal: input.signal,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t};\n}\n\nasync function readChatApiResponse<ResponseBody>(\n\tresponse: Response,\n\turl: URL,\n): Promise<ResponseBody> {\n\tconst responseBody = await readJsonResponse(response);\n\tif (response.ok) {\n\t\treturn responseBody as ResponseBody;\n\t}\n\n\tconst code = toErrorCode(responseBody);\n\tthrow new ChatApiError(toErrorMessage(responseBody, code), {\n\t\tcode,\n\t\tresponseBody,\n\t\tstatus: response.status,\n\t\turl: url.toString(),\n\t});\n}\n\nasync function readJsonResponse(response: Response): Promise<unknown> {\n\tconst text = await response.text();\n\tif (text.trim().length === 0) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\tthrow new ChatApiError(\"Chat API returned invalid JSON.\", {\n\t\t\tcode: \"invalid_response\",\n\t\t\tresponseBody: text,\n\t\t\tstatus: response.status,\n\t\t\turl: response.url,\n\t\t});\n\t}\n}\n\nfunction resolveChatApiBaseUrl(baseUrl: string): URL {\n\tconst url = new URL(baseUrl);\n\turl.search = \"\";\n\turl.hash = \"\";\n\turl.pathname = resolveChatApiBasePath(url.pathname);\n\treturn url;\n}\n\nfunction resolveChatApiBasePath(pathname: string): string {\n\tconst trimmedPathname = pathname.replace(/\\/+$/, \"\");\n\tif (trimmedPathname === \"\") {\n\t\treturn \"/v1\";\n\t}\n\tif (trimmedPathname.endsWith(\"/v1\")) {\n\t\treturn trimmedPathname;\n\t}\n\treturn `${trimmedPathname}/v1`;\n}\n\nfunction resolveFetch(fetchFn: FetchLike | undefined): FetchLike {\n\tif (fetchFn !== undefined) {\n\t\treturn fetchFn;\n\t}\n\tif (typeof globalThis.fetch === \"function\") {\n\t\treturn globalThis.fetch.bind(globalThis);\n\t}\n\tthrow new TypeError(\"Chat API client requires a fetch implementation.\");\n}\n\nfunction requireNonEmptyString(value: string, name: string): string {\n\tif (typeof value !== \"string\" || value.trim().length === 0) {\n\t\tthrow new TypeError(`${name} must be a non-empty string.`);\n\t}\n\treturn value;\n}\n\nfunction toUserLimitSearchParams(input: {\n\tlimit?: number;\n\tuserId: string;\n}): URLSearchParams {\n\tconst params = new URLSearchParams({ identity: input.userId });\n\tif (input.limit !== undefined) {\n\t\tif (!Number.isInteger(input.limit) || input.limit < 1) {\n\t\t\tthrow new TypeError(\"limit must be a positive integer.\");\n\t\t}\n\t\tparams.set(\"limit\", String(input.limit));\n\t}\n\treturn params;\n}\n\nfunction toErrorCode(responseBody: unknown): string {\n\treturn toResponseStringProperty(responseBody, \"code\") ?? \"request_failed\";\n}\n\nfunction toErrorMessage(responseBody: unknown, code: string): string {\n\treturn (\n\t\ttoResponseStringProperty(responseBody, \"error\") ??\n\t\t`Chat API request failed with code ${code}.`\n\t);\n}\n\nfunction toResponseStringProperty(\n\tresponseBody: unknown,\n\tkey: string,\n): string | null {\n\tif (\n\t\ttypeof responseBody === \"object\" &&\n\t\tresponseBody !== null &&\n\t\tkey in responseBody &&\n\t\ttypeof responseBody[key as keyof typeof responseBody] === \"string\"\n\t) {\n\t\treturn responseBody[key as keyof typeof responseBody];\n\t}\n\treturn null;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7913DB41F613110B64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getuserfeedback/chat",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Headless getuserfeedback Chat API client",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"getuserfeedback",
|
|
7
|
+
"conversations",
|
|
8
|
+
"chat",
|
|
9
|
+
"sdk"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/getuserfeedback/getuserfeedback.git"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "bun ../../scripts/clean-build-output.ts dist tsconfig.tsbuildinfo && bun run scripts/build.ts && bun x rollup -c rollup.dts.config.mjs",
|
|
35
|
+
"lint": "ultracite check .",
|
|
36
|
+
"pack:verify": "node ../../scripts/pack-and-verify.cjs",
|
|
37
|
+
"postpack": "node scripts/postpack.cjs",
|
|
38
|
+
"prepack": "node scripts/prepack.cjs",
|
|
39
|
+
"publish:dry-run": "node ../../scripts/publish-package.cjs . -- --dry-run",
|
|
40
|
+
"publish:npm": "node ../../scripts/publish-package.cjs .",
|
|
41
|
+
"test": "bun test --dots",
|
|
42
|
+
"test:changed": "bun test --changed=${TEST_CHANGED_BASE:-HEAD} --pass-with-no-tests --dots",
|
|
43
|
+
"typecheck": "tsc -b tsconfig.json"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"rollup": "^4.57.1",
|
|
47
|
+
"rollup-plugin-dts": "^6.3.0",
|
|
48
|
+
"typescript": "^5.8.3"
|
|
49
|
+
}
|
|
50
|
+
}
|