@convosite/client 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +201 -2
- package/dist/index.cjs +325 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +224 -0
- package/dist/index.d.ts +224 -0
- package/dist/index.js +297 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Convosite
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,202 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @convosite/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-agnostic runtime client for Convosite. It has no runtime dependencies and uses the platform `fetch` API, so the same client works in browsers, Node 18+, React, Next.js, Vue, Nuxt, Svelte, SvelteKit, Astro, Bun and other modern JavaScript runtimes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @convosite/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createConvosite } from '@convosite/client';
|
|
15
|
+
|
|
16
|
+
const convosite = createConvosite({
|
|
17
|
+
site: 'YOUR_SITE_ULID',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const { page } = await convosite.pages.get('/about');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Production defaults to `https://app.convosite.io/api`. No API token belongs in frontend code: the runtime API is public and Convosite actions resolve protected site variables on the server.
|
|
24
|
+
|
|
25
|
+
## Site
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const site = await convosite.site.get();
|
|
29
|
+
console.log(site.global);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Pages
|
|
33
|
+
|
|
34
|
+
Get by path:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const { site, page } = await convosite.pages.get('/news/my-article');
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Get by page ID:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const { page } = await convosite.pages.find('PAGE_ULID');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
List full, resolved pages with pagination:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const result = await convosite.pages.list({
|
|
50
|
+
parentId: 'NEWS_PAGE_ULID',
|
|
51
|
+
sort: 'created_at',
|
|
52
|
+
direction: 'desc',
|
|
53
|
+
page: 1,
|
|
54
|
+
perPage: 12,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
for (const page of result.pages) {
|
|
58
|
+
console.log(page.blocks, page.meta);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Supported list sorts are `name`, `slug`, `title`, `created_at`, and `updated_at`. `perPage` can be 1–100.
|
|
63
|
+
|
|
64
|
+
## Menus
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const { menu } = await convosite.menus.get('MENU_ULID');
|
|
68
|
+
console.log(menu.items);
|
|
69
|
+
console.log(menu.structured);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Search
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const { pages } = await convosite.search('pricing', { limit: 10 });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Search returns full resolved pages, including their relevance score.
|
|
79
|
+
|
|
80
|
+
## Actions
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
await convosite.actions.run('ENDPOINT_ULID', {
|
|
84
|
+
name: 'Joe',
|
|
85
|
+
email: 'joe@example.com',
|
|
86
|
+
message: 'Hello',
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Only public input is sent by the client. Site variables such as API tokens remain server-side and are resolved by Convosite when the endpoint action runs.
|
|
91
|
+
|
|
92
|
+
## Conversations
|
|
93
|
+
|
|
94
|
+
Conversations are streamed from the Convosite Cloudflare Worker as NDJSON. The SDK parses the stream and manages the `X-Conversation-Session` header automatically.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const conversation = convosite.conversations.create();
|
|
98
|
+
|
|
99
|
+
for await (const event of conversation.send('What services do you offer?')) {
|
|
100
|
+
if (event.type === 'field_delta') {
|
|
101
|
+
console.log(event.path, event.delta);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (event.type === 'field_complete') {
|
|
105
|
+
console.log(event.path, event.value);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Continues the same server-side conversation.
|
|
110
|
+
for await (const event of conversation.send('Tell me more.')) {
|
|
111
|
+
console.log(event);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await conversation.end();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
You can also use the stateless surface when you want to manage session IDs yourself:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
for await (const event of convosite.conversations.send('Hello', {
|
|
121
|
+
sessionId: 'SESSION_UUID',
|
|
122
|
+
})) {
|
|
123
|
+
console.log(event);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await convosite.conversations.end('SESSION_UUID');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `conversation.sessionId` to inspect the current session and `conversation.reset()` to forget it locally without ending it on the server.
|
|
130
|
+
|
|
131
|
+
## Cancellation
|
|
132
|
+
|
|
133
|
+
All methods accept an `AbortSignal`:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const controller = new AbortController();
|
|
137
|
+
|
|
138
|
+
const request = convosite.pages.get('/about', {
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
controller.abort();
|
|
143
|
+
await request;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Conversation streams support the same pattern.
|
|
147
|
+
|
|
148
|
+
## SSR and custom fetch
|
|
149
|
+
|
|
150
|
+
A custom `fetch` can be supplied for framework runtimes, instrumentation or tests:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const convosite = createConvosite({
|
|
154
|
+
site: 'YOUR_SITE_ULID',
|
|
155
|
+
fetch: customFetch,
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Errors
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { ConvositeError } from '@convosite/client';
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
await convosite.pages.get('/missing');
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error instanceof ConvositeError) {
|
|
168
|
+
console.log(error.status);
|
|
169
|
+
console.log(error.code);
|
|
170
|
+
console.log(error.message);
|
|
171
|
+
console.log(error.details);
|
|
172
|
+
console.log(error.requestId);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Validation errors are available through `details`. Worker error codes such as `conversation_ended` and `conversation_limit_reached` are exposed through `code`.
|
|
178
|
+
|
|
179
|
+
## Raw request escape hatch
|
|
180
|
+
|
|
181
|
+
For new runtime endpoints that are not yet wrapped by the SDK:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const data = await convosite.request('/some/runtime/endpoint');
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The path is relative to `baseUrl` and uses the same error handling and configured headers.
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const convosite = createConvosite({
|
|
193
|
+
site: 'YOUR_SITE_ULID',
|
|
194
|
+
baseUrl: 'https://app.convosite.io/api',
|
|
195
|
+
conversationUrl: 'https://app.convosite.io/api',
|
|
196
|
+
timeout: 30_000,
|
|
197
|
+
headers: {},
|
|
198
|
+
fetch: globalThis.fetch,
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The timeout applies to normal JSON requests. Conversation streams are intentionally not given a fixed SDK timeout; use an `AbortSignal` when you need one.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ConvositeError: () => ConvositeError,
|
|
24
|
+
createConvosite: () => createConvosite
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/errors.ts
|
|
29
|
+
var ConvositeError = class extends Error {
|
|
30
|
+
status;
|
|
31
|
+
code;
|
|
32
|
+
details;
|
|
33
|
+
requestId;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
super(options.message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
36
|
+
this.name = "ConvositeError";
|
|
37
|
+
this.status = options.status ?? 0;
|
|
38
|
+
this.code = options.code ?? null;
|
|
39
|
+
this.details = options.details ?? null;
|
|
40
|
+
this.requestId = options.requestId ?? null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/http.ts
|
|
45
|
+
function normalizeBaseUrl(value) {
|
|
46
|
+
return value.replace(/\/+$/, "");
|
|
47
|
+
}
|
|
48
|
+
function joinUrl(base, path) {
|
|
49
|
+
if (/^https?:\/\//i.test(path)) return path;
|
|
50
|
+
return `${normalizeBaseUrl(base)}/${path.replace(/^\/+/, "")}`;
|
|
51
|
+
}
|
|
52
|
+
function encodeSegment(value) {
|
|
53
|
+
return encodeURIComponent(value);
|
|
54
|
+
}
|
|
55
|
+
function queryString(values) {
|
|
56
|
+
const params = new URLSearchParams();
|
|
57
|
+
for (const [key, value] of Object.entries(values)) {
|
|
58
|
+
if (value !== void 0 && value !== null) params.set(key, String(value));
|
|
59
|
+
}
|
|
60
|
+
const query = params.toString();
|
|
61
|
+
return query ? `?${query}` : "";
|
|
62
|
+
}
|
|
63
|
+
function timeoutSignal(signal, timeout) {
|
|
64
|
+
if (timeout <= 0) return signal ? { signal, cleanup: () => void 0 } : { cleanup: () => void 0 };
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
const timer = setTimeout(() => controller.abort(new DOMException("Request timed out.", "TimeoutError")), timeout);
|
|
67
|
+
const abort = () => controller.abort(signal?.reason);
|
|
68
|
+
if (signal) {
|
|
69
|
+
if (signal.aborted) abort();
|
|
70
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
signal: controller.signal,
|
|
74
|
+
cleanup: () => {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
signal?.removeEventListener("abort", abort);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function responseBody(response) {
|
|
81
|
+
const text = await response.text();
|
|
82
|
+
if (!text) return null;
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(text);
|
|
85
|
+
} catch {
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function errorFrom(response, body) {
|
|
90
|
+
const object = body && typeof body === "object" ? body : {};
|
|
91
|
+
const message = typeof object.message === "string" ? object.message : `Convosite request failed with status ${response.status}.`;
|
|
92
|
+
const code = typeof object.error === "string" ? object.error : null;
|
|
93
|
+
const details = object.errors ?? body;
|
|
94
|
+
return new ConvositeError({
|
|
95
|
+
message,
|
|
96
|
+
status: response.status,
|
|
97
|
+
code,
|
|
98
|
+
details,
|
|
99
|
+
requestId: response.headers.get("X-Request-ID")
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
var HttpClient = class {
|
|
103
|
+
constructor(fetcher, baseUrl, headers, timeout) {
|
|
104
|
+
this.fetcher = fetcher;
|
|
105
|
+
this.baseUrl = baseUrl;
|
|
106
|
+
this.headers = headers;
|
|
107
|
+
this.timeout = timeout;
|
|
108
|
+
}
|
|
109
|
+
fetcher;
|
|
110
|
+
baseUrl;
|
|
111
|
+
headers;
|
|
112
|
+
timeout;
|
|
113
|
+
async api(path, init = {}) {
|
|
114
|
+
const response = await this.fetch(path, init, true);
|
|
115
|
+
const body = await responseBody(response);
|
|
116
|
+
if (!response.ok) throw errorFrom(response, body);
|
|
117
|
+
if (!body || typeof body !== "object" || !("success" in body)) {
|
|
118
|
+
throw new ConvositeError({ message: "Convosite returned an invalid API response.", status: response.status, details: body });
|
|
119
|
+
}
|
|
120
|
+
const envelope = body;
|
|
121
|
+
if (envelope.success !== true) {
|
|
122
|
+
throw new ConvositeError({ message: envelope.message || "Convosite request failed.", status: response.status, details: envelope.errors });
|
|
123
|
+
}
|
|
124
|
+
return envelope.data;
|
|
125
|
+
}
|
|
126
|
+
async raw(path, options = {}) {
|
|
127
|
+
const { parseAs = "json", ...init } = options;
|
|
128
|
+
const response = await this.fetch(path, init, true);
|
|
129
|
+
if (!response.ok) throw errorFrom(response, await responseBody(response));
|
|
130
|
+
if (parseAs === "response") return response;
|
|
131
|
+
if (parseAs === "text") return await response.text();
|
|
132
|
+
if (response.status === 204) return void 0;
|
|
133
|
+
return await response.json();
|
|
134
|
+
}
|
|
135
|
+
async fetch(path, init = {}, useTimeout = false) {
|
|
136
|
+
const headers = new Headers(this.headers);
|
|
137
|
+
new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
138
|
+
const timed = useTimeout ? timeoutSignal(init.signal ?? void 0, this.timeout) : timeoutSignal(init.signal ?? void 0, 0);
|
|
139
|
+
try {
|
|
140
|
+
const requestInit = { ...init, headers };
|
|
141
|
+
if (timed.signal) requestInit.signal = timed.signal;
|
|
142
|
+
return await this.fetcher(joinUrl(this.baseUrl, path), requestInit);
|
|
143
|
+
} catch (cause) {
|
|
144
|
+
if (cause instanceof ConvositeError) throw cause;
|
|
145
|
+
const aborted = timed.signal?.aborted;
|
|
146
|
+
throw new ConvositeError({
|
|
147
|
+
message: aborted ? "Convosite request was aborted." : "Unable to connect to Convosite.",
|
|
148
|
+
code: aborted ? "request_aborted" : "network_error",
|
|
149
|
+
cause
|
|
150
|
+
});
|
|
151
|
+
} finally {
|
|
152
|
+
timed.cleanup();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
async function workerError(response) {
|
|
157
|
+
return errorFrom(response, await responseBody(response));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/conversations.ts
|
|
161
|
+
async function* parseNdjson(response) {
|
|
162
|
+
if (!response.body) throw new ConvositeError({ message: "Conversation response did not contain a stream.", status: response.status });
|
|
163
|
+
const reader = response.body.getReader();
|
|
164
|
+
const decoder = new TextDecoder();
|
|
165
|
+
let buffer = "";
|
|
166
|
+
try {
|
|
167
|
+
while (true) {
|
|
168
|
+
const { value, done } = await reader.read();
|
|
169
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
170
|
+
let newline;
|
|
171
|
+
while ((newline = buffer.indexOf("\n")) !== -1) {
|
|
172
|
+
const line = buffer.slice(0, newline).trim();
|
|
173
|
+
buffer = buffer.slice(newline + 1);
|
|
174
|
+
if (line) yield parseEvent(line);
|
|
175
|
+
}
|
|
176
|
+
if (done) break;
|
|
177
|
+
}
|
|
178
|
+
const tail = buffer.trim();
|
|
179
|
+
if (tail) yield parseEvent(tail);
|
|
180
|
+
} finally {
|
|
181
|
+
reader.releaseLock();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function parseEvent(line) {
|
|
185
|
+
try {
|
|
186
|
+
const event = JSON.parse(line);
|
|
187
|
+
if (!event || typeof event !== "object" || typeof event.type !== "string") {
|
|
188
|
+
throw new Error("Event has no type.");
|
|
189
|
+
}
|
|
190
|
+
return event;
|
|
191
|
+
} catch (cause) {
|
|
192
|
+
throw new ConvositeError({ message: "Conversation stream contained an invalid event.", code: "invalid_stream", details: line, cause });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
var ConversationsClient = class {
|
|
196
|
+
constructor(fetcher, baseUrl, siteId) {
|
|
197
|
+
this.fetcher = fetcher;
|
|
198
|
+
this.baseUrl = baseUrl;
|
|
199
|
+
this.siteId = siteId;
|
|
200
|
+
}
|
|
201
|
+
fetcher;
|
|
202
|
+
baseUrl;
|
|
203
|
+
siteId;
|
|
204
|
+
create(sessionId) {
|
|
205
|
+
let currentSession = sessionId?.trim() || null;
|
|
206
|
+
const client = this;
|
|
207
|
+
return {
|
|
208
|
+
get sessionId() {
|
|
209
|
+
return currentSession;
|
|
210
|
+
},
|
|
211
|
+
async *send(query, options = {}) {
|
|
212
|
+
const stream = client.send(query, { ...options, ...currentSession ? { sessionId: currentSession } : {} }, (id) => {
|
|
213
|
+
currentSession = id;
|
|
214
|
+
});
|
|
215
|
+
yield* stream;
|
|
216
|
+
},
|
|
217
|
+
async end(options = {}) {
|
|
218
|
+
if (!currentSession) return { ended: false };
|
|
219
|
+
const result = await client.end(currentSession, options);
|
|
220
|
+
if (result.ended) currentSession = null;
|
|
221
|
+
return result;
|
|
222
|
+
},
|
|
223
|
+
reset() {
|
|
224
|
+
currentSession = null;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async *send(query, options = {}, onSession) {
|
|
229
|
+
const trimmed = query.trim();
|
|
230
|
+
if (!trimmed) throw new ConvositeError({ message: "Conversation query is required.", code: "validation_error" });
|
|
231
|
+
const headers = new Headers({ "Content-Type": "application/json", "Accept": "application/x-ndjson" });
|
|
232
|
+
if (options.sessionId) headers.set("X-Conversation-Session", options.sessionId);
|
|
233
|
+
let response;
|
|
234
|
+
try {
|
|
235
|
+
const init = { method: "POST", headers, body: JSON.stringify({ query: trimmed }) };
|
|
236
|
+
if (options.signal) init.signal = options.signal;
|
|
237
|
+
response = await this.fetcher(joinUrl(this.baseUrl, `/conversations/${encodeSegment(this.siteId)}`), init);
|
|
238
|
+
} catch (cause) {
|
|
239
|
+
throw new ConvositeError({ message: options.signal?.aborted ? "Conversation request was aborted." : "Unable to connect to Convosite conversations.", code: options.signal?.aborted ? "request_aborted" : "network_error", cause });
|
|
240
|
+
}
|
|
241
|
+
if (!response.ok) throw await workerError(response);
|
|
242
|
+
const session = response.headers.get("X-Conversation-Session");
|
|
243
|
+
if (session) onSession?.(session);
|
|
244
|
+
yield* parseNdjson(response);
|
|
245
|
+
}
|
|
246
|
+
async end(sessionId, options = {}) {
|
|
247
|
+
const headers = new Headers({ "Accept": "application/json", "X-Conversation-Session": sessionId });
|
|
248
|
+
let response;
|
|
249
|
+
try {
|
|
250
|
+
const init = { method: "POST", headers };
|
|
251
|
+
if (options.signal) init.signal = options.signal;
|
|
252
|
+
response = await this.fetcher(joinUrl(this.baseUrl, `/conversations/${encodeSegment(this.siteId)}/end`), init);
|
|
253
|
+
} catch (cause) {
|
|
254
|
+
throw new ConvositeError({ message: options.signal?.aborted ? "Conversation request was aborted." : "Unable to connect to Convosite conversations.", code: options.signal?.aborted ? "request_aborted" : "network_error", cause });
|
|
255
|
+
}
|
|
256
|
+
if (!response.ok) throw await workerError(response);
|
|
257
|
+
return await response.json();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/client.ts
|
|
262
|
+
var DEFAULT_BASE_URL = "https://app.convosite.io/api";
|
|
263
|
+
function createConvosite(config) {
|
|
264
|
+
if (!config || typeof config.site !== "string" || !config.site.trim()) {
|
|
265
|
+
throw new ConvositeError({ message: "A Convosite site ID is required.", code: "invalid_config" });
|
|
266
|
+
}
|
|
267
|
+
const fetcher = config.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
268
|
+
if (!fetcher) throw new ConvositeError({ message: "No fetch implementation is available.", code: "invalid_config" });
|
|
269
|
+
const siteId = config.site.trim();
|
|
270
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl ?? DEFAULT_BASE_URL);
|
|
271
|
+
const conversationUrl = normalizeBaseUrl(config.conversationUrl ?? baseUrl);
|
|
272
|
+
const http = new HttpClient(fetcher, baseUrl, config.headers ?? {}, config.timeout ?? 3e4);
|
|
273
|
+
const conversations = new ConversationsClient(fetcher, conversationUrl, siteId);
|
|
274
|
+
const site = encodeSegment(siteId);
|
|
275
|
+
const requestOptions = (options) => options?.signal ? { signal: options.signal } : {};
|
|
276
|
+
return {
|
|
277
|
+
site: {
|
|
278
|
+
get: (options) => http.api(`/${site}`, requestOptions(options))
|
|
279
|
+
},
|
|
280
|
+
pages: {
|
|
281
|
+
get: (path, options) => {
|
|
282
|
+
if (typeof path !== "string") throw new ConvositeError({ message: "Page path must be a string.", code: "validation_error" });
|
|
283
|
+
return http.api(`/content/${site}/pages${queryString({ path })}`, requestOptions(options));
|
|
284
|
+
},
|
|
285
|
+
find: (id, options) => http.api(`/content/${site}/pages/${encodeSegment(id)}`, requestOptions(options)),
|
|
286
|
+
list: (options = {}) => {
|
|
287
|
+
const { parentId, sort, direction, page, perPage, signal } = options;
|
|
288
|
+
return http.api(`/content/${site}/pages${queryString({
|
|
289
|
+
parent_id: parentId,
|
|
290
|
+
sort,
|
|
291
|
+
direction,
|
|
292
|
+
page,
|
|
293
|
+
per_page: perPage
|
|
294
|
+
})}`, requestOptions(signal ? { signal } : void 0));
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
menus: {
|
|
298
|
+
get: (id, options) => http.api(`/content/${site}/menus/${encodeSegment(id)}`, requestOptions(options))
|
|
299
|
+
},
|
|
300
|
+
search: (query, options = {}) => {
|
|
301
|
+
const { limit, signal } = options;
|
|
302
|
+
return http.api(`/search/${site}${queryString({ query, limit })}`, requestOptions(signal ? { signal } : void 0));
|
|
303
|
+
},
|
|
304
|
+
actions: {
|
|
305
|
+
run: (endpointId, input, options) => http.api(`/actions/${site}/${encodeSegment(endpointId)}`, {
|
|
306
|
+
method: "POST",
|
|
307
|
+
headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
|
308
|
+
body: JSON.stringify(input),
|
|
309
|
+
...requestOptions(options)
|
|
310
|
+
})
|
|
311
|
+
},
|
|
312
|
+
conversations: {
|
|
313
|
+
create: (sessionId) => conversations.create(sessionId),
|
|
314
|
+
send: (query, options) => conversations.send(query, options),
|
|
315
|
+
end: (sessionId, options) => conversations.end(sessionId, options)
|
|
316
|
+
},
|
|
317
|
+
request: (path, options = {}) => http.raw(path, options)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
321
|
+
0 && (module.exports = {
|
|
322
|
+
ConvositeError,
|
|
323
|
+
createConvosite
|
|
324
|
+
});
|
|
325
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/conversations.ts","../src/client.ts"],"sourcesContent":["export { createConvosite } from './client';\nexport { ConvositeError } from './errors';\nexport type {\n ActionResponse,\n Conversation,\n ConversationCompleteEvent,\n ConversationEndResponse,\n ConversationErrorEvent,\n ConversationEvent,\n ConversationFieldEvent,\n ConversationSendOptions,\n ConversationUsageEvent,\n ConvositeClient,\n ConvositeConfig,\n Fetch,\n JsonPrimitive,\n JsonValue,\n Media,\n Menu,\n MenuItem,\n MenuResponse,\n Page,\n PageBlock,\n PageListOptions,\n PageListResponse,\n PageMetaValue,\n PageResponse,\n PageSort,\n Pagination,\n RawRequestOptions,\n RequestOptions,\n SearchOptions,\n SearchResponse,\n Site,\n SiteMetaValue,\n SortDirection,\n StructuredMenuItem,\n} from './types';\n","export class ConvositeError extends Error {\n readonly status: number;\n readonly code: string | null;\n readonly details: unknown;\n readonly requestId: string | null;\n\n constructor(options: {\n message: string;\n status?: number;\n code?: string | null;\n details?: unknown;\n requestId?: string | null;\n cause?: unknown;\n }) {\n super(options.message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'ConvositeError';\n this.status = options.status ?? 0;\n this.code = options.code ?? null;\n this.details = options.details ?? null;\n this.requestId = options.requestId ?? null;\n }\n}\n","import { ConvositeError } from './errors';\nimport type { Fetch, RawRequestOptions } from './types';\n\ninterface ApiEnvelope<T> {\n success: boolean;\n message: string;\n data: T;\n errors: unknown;\n}\n\nexport function normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, '');\n}\n\nexport function joinUrl(base: string, path: string): string {\n if (/^https?:\\/\\//i.test(path)) return path;\n return `${normalizeBaseUrl(base)}/${path.replace(/^\\/+/, '')}`;\n}\n\nexport function encodeSegment(value: string): string {\n return encodeURIComponent(value);\n}\n\nexport function queryString(values: Record<string, string | number | null | undefined>): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(values)) {\n if (value !== undefined && value !== null) params.set(key, String(value));\n }\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n\nfunction timeoutSignal(signal: AbortSignal | undefined, timeout: number): { signal?: AbortSignal; cleanup: () => void } {\n if (timeout <= 0) return signal ? { signal, cleanup: () => undefined } : { cleanup: () => undefined };\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(new DOMException('Request timed out.', 'TimeoutError')), timeout);\n const abort = () => controller.abort(signal?.reason);\n if (signal) {\n if (signal.aborted) abort();\n else signal.addEventListener('abort', abort, { once: true });\n }\n return {\n signal: controller.signal,\n cleanup: () => {\n clearTimeout(timer);\n signal?.removeEventListener('abort', abort);\n },\n };\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) return null;\n try { return JSON.parse(text); } catch { return text; }\n}\n\nfunction errorFrom(response: Response, body: unknown): ConvositeError {\n const object = body && typeof body === 'object' ? body as Record<string, unknown> : {};\n const message = typeof object.message === 'string' ? object.message : `Convosite request failed with status ${response.status}.`;\n const code = typeof object.error === 'string' ? object.error : null;\n const details = object.errors ?? body;\n return new ConvositeError({\n message,\n status: response.status,\n code,\n details,\n requestId: response.headers.get('X-Request-ID'),\n });\n}\n\nexport class HttpClient {\n constructor(\n private readonly fetcher: Fetch,\n private readonly baseUrl: string,\n private readonly headers: HeadersInit,\n private readonly timeout: number,\n ) {}\n\n async api<T>(path: string, init: RequestInit = {}): Promise<T> {\n const response = await this.fetch(path, init, true);\n const body = await responseBody(response);\n if (!response.ok) throw errorFrom(response, body);\n if (!body || typeof body !== 'object' || !('success' in body)) {\n throw new ConvositeError({ message: 'Convosite returned an invalid API response.', status: response.status, details: body });\n }\n const envelope = body as ApiEnvelope<T>;\n if (envelope.success !== true) {\n throw new ConvositeError({ message: envelope.message || 'Convosite request failed.', status: response.status, details: envelope.errors });\n }\n return envelope.data;\n }\n\n async raw<T>(path: string, options: RawRequestOptions = {}): Promise<T> {\n const { parseAs = 'json', ...init } = options;\n const response = await this.fetch(path, init, true);\n if (!response.ok) throw errorFrom(response, await responseBody(response));\n if (parseAs === 'response') return response as T;\n if (parseAs === 'text') return await response.text() as T;\n if (response.status === 204) return undefined as T;\n return await response.json() as T;\n }\n\n async fetch(path: string, init: RequestInit = {}, useTimeout = false): Promise<Response> {\n const headers = new Headers(this.headers);\n new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n const timed = useTimeout ? timeoutSignal(init.signal ?? undefined, this.timeout) : timeoutSignal(init.signal ?? undefined, 0);\n try {\n const requestInit: RequestInit = { ...init, headers };\n if (timed.signal) requestInit.signal = timed.signal;\n return await this.fetcher(joinUrl(this.baseUrl, path), requestInit);\n } catch (cause) {\n if (cause instanceof ConvositeError) throw cause;\n const aborted = timed.signal?.aborted;\n throw new ConvositeError({\n message: aborted ? 'Convosite request was aborted.' : 'Unable to connect to Convosite.',\n code: aborted ? 'request_aborted' : 'network_error',\n cause,\n });\n } finally {\n timed.cleanup();\n }\n }\n}\n\nexport async function workerError(response: Response): Promise<ConvositeError> {\n return errorFrom(response, await responseBody(response));\n}\n","import { ConvositeError } from './errors';\nimport { encodeSegment, joinUrl, workerError } from './http';\nimport type {\n Conversation,\n ConversationEndResponse,\n ConversationEvent,\n ConversationSendOptions,\n Fetch,\n RequestOptions,\n} from './types';\n\nasync function* parseNdjson(response: Response): AsyncGenerator<ConversationEvent> {\n if (!response.body) throw new ConvositeError({ message: 'Conversation response did not contain a stream.', status: response.status });\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { value, done } = await reader.read();\n buffer += decoder.decode(value, { stream: !done });\n let newline: number;\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const line = buffer.slice(0, newline).trim();\n buffer = buffer.slice(newline + 1);\n if (line) yield parseEvent(line);\n }\n if (done) break;\n }\n const tail = buffer.trim();\n if (tail) yield parseEvent(tail);\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction parseEvent(line: string): ConversationEvent {\n try {\n const event = JSON.parse(line) as unknown;\n if (!event || typeof event !== 'object' || typeof (event as { type?: unknown }).type !== 'string') {\n throw new Error('Event has no type.');\n }\n return event as ConversationEvent;\n } catch (cause) {\n throw new ConvositeError({ message: 'Conversation stream contained an invalid event.', code: 'invalid_stream', details: line, cause });\n }\n}\n\nexport class ConversationsClient {\n constructor(\n private readonly fetcher: Fetch,\n private readonly baseUrl: string,\n private readonly siteId: string,\n ) {}\n\n create(sessionId?: string): Conversation {\n let currentSession = sessionId?.trim() || null;\n const client = this;\n return {\n get sessionId() { return currentSession; },\n async *send(query: string, options: RequestOptions = {}) {\n const stream = client.send(query, { ...options, ...(currentSession ? { sessionId: currentSession } : {}) }, (id) => { currentSession = id; });\n yield* stream;\n },\n async end(options: RequestOptions = {}) {\n if (!currentSession) return { ended: false };\n const result = await client.end(currentSession, options);\n if (result.ended) currentSession = null;\n return result;\n },\n reset() { currentSession = null; },\n };\n }\n\n async *send(\n query: string,\n options: ConversationSendOptions = {},\n onSession?: (sessionId: string) => void,\n ): AsyncGenerator<ConversationEvent> {\n const trimmed = query.trim();\n if (!trimmed) throw new ConvositeError({ message: 'Conversation query is required.', code: 'validation_error' });\n const headers = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/x-ndjson' });\n if (options.sessionId) headers.set('X-Conversation-Session', options.sessionId);\n let response: Response;\n try {\n const init: RequestInit = { method: 'POST', headers, body: JSON.stringify({ query: trimmed }) };\n if (options.signal) init.signal = options.signal;\n response = await this.fetcher(joinUrl(this.baseUrl, `/conversations/${encodeSegment(this.siteId)}`), init);\n } catch (cause) {\n throw new ConvositeError({ message: options.signal?.aborted ? 'Conversation request was aborted.' : 'Unable to connect to Convosite conversations.', code: options.signal?.aborted ? 'request_aborted' : 'network_error', cause });\n }\n if (!response.ok) throw await workerError(response);\n const session = response.headers.get('X-Conversation-Session');\n if (session) onSession?.(session);\n yield* parseNdjson(response);\n }\n\n async end(sessionId: string, options: RequestOptions = {}): Promise<ConversationEndResponse> {\n const headers = new Headers({ 'Accept': 'application/json', 'X-Conversation-Session': sessionId });\n let response: Response;\n try {\n const init: RequestInit = { method: 'POST', headers };\n if (options.signal) init.signal = options.signal;\n response = await this.fetcher(joinUrl(this.baseUrl, `/conversations/${encodeSegment(this.siteId)}/end`), init);\n } catch (cause) {\n throw new ConvositeError({ message: options.signal?.aborted ? 'Conversation request was aborted.' : 'Unable to connect to Convosite conversations.', code: options.signal?.aborted ? 'request_aborted' : 'network_error', cause });\n }\n if (!response.ok) throw await workerError(response);\n return await response.json() as ConversationEndResponse;\n }\n}\n","import { ConvositeError } from './errors';\nimport { ConversationsClient } from './conversations';\nimport { encodeSegment, HttpClient, normalizeBaseUrl, queryString } from './http';\nimport type {\n ActionResponse,\n ConvositeClient,\n ConvositeConfig,\n MenuResponse,\n PageListOptions,\n PageListResponse,\n PageResponse,\n RawRequestOptions,\n RequestOptions,\n SearchOptions,\n SearchResponse,\n Site,\n} from './types';\n\nconst DEFAULT_BASE_URL = 'https://app.convosite.io/api';\n\nexport function createConvosite(config: ConvositeConfig): ConvositeClient {\n if (!config || typeof config.site !== 'string' || !config.site.trim()) {\n throw new ConvositeError({ message: 'A Convosite site ID is required.', code: 'invalid_config' });\n }\n const fetcher = config.fetch ?? globalThis.fetch?.bind(globalThis);\n if (!fetcher) throw new ConvositeError({ message: 'No fetch implementation is available.', code: 'invalid_config' });\n const siteId = config.site.trim();\n const baseUrl = normalizeBaseUrl(config.baseUrl ?? DEFAULT_BASE_URL);\n const conversationUrl = normalizeBaseUrl(config.conversationUrl ?? baseUrl);\n const http = new HttpClient(fetcher, baseUrl, config.headers ?? {}, config.timeout ?? 30_000);\n const conversations = new ConversationsClient(fetcher, conversationUrl, siteId);\n const site = encodeSegment(siteId);\n\n const requestOptions = (options?: RequestOptions): RequestInit => options?.signal ? { signal: options.signal } : {};\n\n return {\n site: {\n get: (options) => http.api<Site>(`/${site}`, requestOptions(options)),\n },\n pages: {\n get: (path, options) => {\n if (typeof path !== 'string') throw new ConvositeError({ message: 'Page path must be a string.', code: 'validation_error' });\n return http.api<PageResponse>(`/content/${site}/pages${queryString({ path })}`, requestOptions(options));\n },\n find: (id, options) => http.api<PageResponse>(`/content/${site}/pages/${encodeSegment(id)}`, requestOptions(options)),\n list: (options: PageListOptions = {}) => {\n const { parentId, sort, direction, page, perPage, signal } = options;\n return http.api<PageListResponse>(`/content/${site}/pages${queryString({\n parent_id: parentId,\n sort,\n direction,\n page,\n per_page: perPage,\n })}`, requestOptions(signal ? { signal } : undefined));\n },\n },\n menus: {\n get: (id, options) => http.api<MenuResponse>(`/content/${site}/menus/${encodeSegment(id)}`, requestOptions(options)),\n },\n search: (query: string, options: SearchOptions = {}) => {\n const { limit, signal } = options;\n return http.api<SearchResponse>(`/search/${site}${queryString({ query, limit })}`, requestOptions(signal ? { signal } : undefined));\n },\n actions: {\n run: (endpointId, input, options) => http.api<ActionResponse>(`/actions/${site}/${encodeSegment(endpointId)}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\n body: JSON.stringify(input),\n ...requestOptions(options),\n }),\n },\n conversations: {\n create: (sessionId) => conversations.create(sessionId),\n send: (query, options) => conversations.send(query, options),\n end: (sessionId, options) => conversations.end(sessionId, options),\n },\n request: <T = unknown>(path: string, options: RawRequestOptions = {}) => http.raw<T>(path, options),\n };\n}\n"],"mappings":"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","names":[]}
|