@ownichat/sdk 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 +21 -0
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/chunk-2OWJTVJ3.js +29 -0
- package/dist/index.cjs +318 -0
- package/dist/index.d.cts +213 -0
- package/dist/index.d.ts +213 -0
- package/dist/index.js +286 -0
- package/dist/webhooks.cjs +60 -0
- package/dist/webhooks.d.cts +51 -0
- package/dist/webhooks.d.ts +51 -0
- package/dist/webhooks.js +53 -0
- package/dist/widget.cjs +102 -0
- package/dist/widget.d.cts +82 -0
- package/dist/widget.d.ts +82 -0
- package/dist/widget.js +81 -0
- package/package.json +92 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
// src/webhooks.ts
|
|
6
|
+
var SIGNATURE_HEADER = "x-owni-signature";
|
|
7
|
+
var TIMESTAMP_HEADER = "x-owni-timestamp";
|
|
8
|
+
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
9
|
+
var computeWebhookSignature = (secret, timestamp, body) => `sha256=${crypto.createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex")}`;
|
|
10
|
+
var safeEquals = (a, b) => {
|
|
11
|
+
const bufferA = Buffer.from(a);
|
|
12
|
+
const bufferB = Buffer.from(b);
|
|
13
|
+
if (bufferA.length !== bufferB.length) return false;
|
|
14
|
+
return crypto.timingSafeEqual(bufferA, bufferB);
|
|
15
|
+
};
|
|
16
|
+
var verifyWebhookSignature = (input) => {
|
|
17
|
+
if (!input.signature || !input.timestamp) {
|
|
18
|
+
return { valid: false, reason: "missing_headers" };
|
|
19
|
+
}
|
|
20
|
+
const timestampSeconds = Number(input.timestamp);
|
|
21
|
+
if (!Number.isFinite(timestampSeconds)) {
|
|
22
|
+
return { valid: false, reason: "stale_timestamp" };
|
|
23
|
+
}
|
|
24
|
+
const now = input.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
25
|
+
const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
26
|
+
if (Math.abs(now - timestampSeconds) > tolerance) {
|
|
27
|
+
return { valid: false, reason: "stale_timestamp" };
|
|
28
|
+
}
|
|
29
|
+
const expected = computeWebhookSignature(input.secret, input.timestamp, input.body);
|
|
30
|
+
if (!safeEquals(expected, input.signature)) {
|
|
31
|
+
return { valid: false, reason: "bad_signature" };
|
|
32
|
+
}
|
|
33
|
+
return { valid: true };
|
|
34
|
+
};
|
|
35
|
+
var readHeader = (headers, name) => {
|
|
36
|
+
if (typeof headers.get === "function") {
|
|
37
|
+
return headers.get(name);
|
|
38
|
+
}
|
|
39
|
+
const record = headers;
|
|
40
|
+
const value = record[name] ?? record[name.toLowerCase()];
|
|
41
|
+
return Array.isArray(value) ? value[0] ?? null : value ?? null;
|
|
42
|
+
};
|
|
43
|
+
var parseWebhookRequest = (input) => {
|
|
44
|
+
const result = verifyWebhookSignature({
|
|
45
|
+
body: input.body,
|
|
46
|
+
signature: readHeader(input.headers, SIGNATURE_HEADER),
|
|
47
|
+
timestamp: readHeader(input.headers, TIMESTAMP_HEADER),
|
|
48
|
+
secret: input.secret,
|
|
49
|
+
toleranceSeconds: input.toleranceSeconds
|
|
50
|
+
});
|
|
51
|
+
if (!result.valid) return result;
|
|
52
|
+
return { valid: true, event: JSON.parse(input.body) };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
exports.DEFAULT_TOLERANCE_SECONDS = DEFAULT_TOLERANCE_SECONDS;
|
|
56
|
+
exports.SIGNATURE_HEADER = SIGNATURE_HEADER;
|
|
57
|
+
exports.TIMESTAMP_HEADER = TIMESTAMP_HEADER;
|
|
58
|
+
exports.computeWebhookSignature = computeWebhookSignature;
|
|
59
|
+
exports.parseWebhookRequest = parseWebhookRequest;
|
|
60
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const SIGNATURE_HEADER = "x-owni-signature";
|
|
2
|
+
declare const TIMESTAMP_HEADER = "x-owni-timestamp";
|
|
3
|
+
/** Deliveries older than this are rejected, so a captured request can't be replayed. */
|
|
4
|
+
declare const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
5
|
+
type OwniWebhookEvent<TData = Record<string, unknown>> = {
|
|
6
|
+
event: string;
|
|
7
|
+
data: TData;
|
|
8
|
+
sent_at: string;
|
|
9
|
+
};
|
|
10
|
+
type LeadCapturedEvent = OwniWebhookEvent<{
|
|
11
|
+
conversation_id?: string;
|
|
12
|
+
project_id?: string;
|
|
13
|
+
fields?: Record<string, unknown>;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}>;
|
|
16
|
+
type VerifyWebhookInput = {
|
|
17
|
+
/** The raw request body, exactly as received. Re-serialized JSON will not verify. */
|
|
18
|
+
body: string;
|
|
19
|
+
signature: string | null | undefined;
|
|
20
|
+
timestamp: string | null | undefined;
|
|
21
|
+
secret: string;
|
|
22
|
+
toleranceSeconds?: number;
|
|
23
|
+
/** Injectable for tests; defaults to the current time. */
|
|
24
|
+
nowSeconds?: number;
|
|
25
|
+
};
|
|
26
|
+
type VerifyWebhookResult = {
|
|
27
|
+
valid: true;
|
|
28
|
+
} | {
|
|
29
|
+
valid: false;
|
|
30
|
+
reason: 'missing_headers' | 'bad_signature' | 'stale_timestamp';
|
|
31
|
+
};
|
|
32
|
+
declare const computeWebhookSignature: (secret: string, timestamp: string, body: string) => string;
|
|
33
|
+
declare const verifyWebhookSignature: (input: VerifyWebhookInput) => VerifyWebhookResult;
|
|
34
|
+
type HeaderSource = Headers | Record<string, string | string[] | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Verifies and parses in one step. Works with both a `Headers` object (fetch/Next.js
|
|
37
|
+
* route handlers) and a plain header record (Express, Node http).
|
|
38
|
+
*/
|
|
39
|
+
declare const parseWebhookRequest: <TData = Record<string, unknown>>(input: {
|
|
40
|
+
body: string;
|
|
41
|
+
headers: HeaderSource;
|
|
42
|
+
secret: string;
|
|
43
|
+
toleranceSeconds?: number;
|
|
44
|
+
}) => {
|
|
45
|
+
valid: true;
|
|
46
|
+
event: OwniWebhookEvent<TData>;
|
|
47
|
+
} | (VerifyWebhookResult & {
|
|
48
|
+
valid: false;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export { DEFAULT_TOLERANCE_SECONDS, type HeaderSource, type LeadCapturedEvent, type OwniWebhookEvent, SIGNATURE_HEADER, TIMESTAMP_HEADER, type VerifyWebhookInput, type VerifyWebhookResult, computeWebhookSignature, parseWebhookRequest, verifyWebhookSignature };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const SIGNATURE_HEADER = "x-owni-signature";
|
|
2
|
+
declare const TIMESTAMP_HEADER = "x-owni-timestamp";
|
|
3
|
+
/** Deliveries older than this are rejected, so a captured request can't be replayed. */
|
|
4
|
+
declare const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
5
|
+
type OwniWebhookEvent<TData = Record<string, unknown>> = {
|
|
6
|
+
event: string;
|
|
7
|
+
data: TData;
|
|
8
|
+
sent_at: string;
|
|
9
|
+
};
|
|
10
|
+
type LeadCapturedEvent = OwniWebhookEvent<{
|
|
11
|
+
conversation_id?: string;
|
|
12
|
+
project_id?: string;
|
|
13
|
+
fields?: Record<string, unknown>;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}>;
|
|
16
|
+
type VerifyWebhookInput = {
|
|
17
|
+
/** The raw request body, exactly as received. Re-serialized JSON will not verify. */
|
|
18
|
+
body: string;
|
|
19
|
+
signature: string | null | undefined;
|
|
20
|
+
timestamp: string | null | undefined;
|
|
21
|
+
secret: string;
|
|
22
|
+
toleranceSeconds?: number;
|
|
23
|
+
/** Injectable for tests; defaults to the current time. */
|
|
24
|
+
nowSeconds?: number;
|
|
25
|
+
};
|
|
26
|
+
type VerifyWebhookResult = {
|
|
27
|
+
valid: true;
|
|
28
|
+
} | {
|
|
29
|
+
valid: false;
|
|
30
|
+
reason: 'missing_headers' | 'bad_signature' | 'stale_timestamp';
|
|
31
|
+
};
|
|
32
|
+
declare const computeWebhookSignature: (secret: string, timestamp: string, body: string) => string;
|
|
33
|
+
declare const verifyWebhookSignature: (input: VerifyWebhookInput) => VerifyWebhookResult;
|
|
34
|
+
type HeaderSource = Headers | Record<string, string | string[] | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Verifies and parses in one step. Works with both a `Headers` object (fetch/Next.js
|
|
37
|
+
* route handlers) and a plain header record (Express, Node http).
|
|
38
|
+
*/
|
|
39
|
+
declare const parseWebhookRequest: <TData = Record<string, unknown>>(input: {
|
|
40
|
+
body: string;
|
|
41
|
+
headers: HeaderSource;
|
|
42
|
+
secret: string;
|
|
43
|
+
toleranceSeconds?: number;
|
|
44
|
+
}) => {
|
|
45
|
+
valid: true;
|
|
46
|
+
event: OwniWebhookEvent<TData>;
|
|
47
|
+
} | (VerifyWebhookResult & {
|
|
48
|
+
valid: false;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export { DEFAULT_TOLERANCE_SECONDS, type HeaderSource, type LeadCapturedEvent, type OwniWebhookEvent, SIGNATURE_HEADER, TIMESTAMP_HEADER, type VerifyWebhookInput, type VerifyWebhookResult, computeWebhookSignature, parseWebhookRequest, verifyWebhookSignature };
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
|
|
3
|
+
// src/webhooks.ts
|
|
4
|
+
var SIGNATURE_HEADER = "x-owni-signature";
|
|
5
|
+
var TIMESTAMP_HEADER = "x-owni-timestamp";
|
|
6
|
+
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
7
|
+
var computeWebhookSignature = (secret, timestamp, body) => `sha256=${createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex")}`;
|
|
8
|
+
var safeEquals = (a, b) => {
|
|
9
|
+
const bufferA = Buffer.from(a);
|
|
10
|
+
const bufferB = Buffer.from(b);
|
|
11
|
+
if (bufferA.length !== bufferB.length) return false;
|
|
12
|
+
return timingSafeEqual(bufferA, bufferB);
|
|
13
|
+
};
|
|
14
|
+
var verifyWebhookSignature = (input) => {
|
|
15
|
+
if (!input.signature || !input.timestamp) {
|
|
16
|
+
return { valid: false, reason: "missing_headers" };
|
|
17
|
+
}
|
|
18
|
+
const timestampSeconds = Number(input.timestamp);
|
|
19
|
+
if (!Number.isFinite(timestampSeconds)) {
|
|
20
|
+
return { valid: false, reason: "stale_timestamp" };
|
|
21
|
+
}
|
|
22
|
+
const now = input.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
23
|
+
const tolerance = input.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
24
|
+
if (Math.abs(now - timestampSeconds) > tolerance) {
|
|
25
|
+
return { valid: false, reason: "stale_timestamp" };
|
|
26
|
+
}
|
|
27
|
+
const expected = computeWebhookSignature(input.secret, input.timestamp, input.body);
|
|
28
|
+
if (!safeEquals(expected, input.signature)) {
|
|
29
|
+
return { valid: false, reason: "bad_signature" };
|
|
30
|
+
}
|
|
31
|
+
return { valid: true };
|
|
32
|
+
};
|
|
33
|
+
var readHeader = (headers, name) => {
|
|
34
|
+
if (typeof headers.get === "function") {
|
|
35
|
+
return headers.get(name);
|
|
36
|
+
}
|
|
37
|
+
const record = headers;
|
|
38
|
+
const value = record[name] ?? record[name.toLowerCase()];
|
|
39
|
+
return Array.isArray(value) ? value[0] ?? null : value ?? null;
|
|
40
|
+
};
|
|
41
|
+
var parseWebhookRequest = (input) => {
|
|
42
|
+
const result = verifyWebhookSignature({
|
|
43
|
+
body: input.body,
|
|
44
|
+
signature: readHeader(input.headers, SIGNATURE_HEADER),
|
|
45
|
+
timestamp: readHeader(input.headers, TIMESTAMP_HEADER),
|
|
46
|
+
secret: input.secret,
|
|
47
|
+
toleranceSeconds: input.toleranceSeconds
|
|
48
|
+
});
|
|
49
|
+
if (!result.valid) return result;
|
|
50
|
+
return { valid: true, event: JSON.parse(input.body) };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export { DEFAULT_TOLERANCE_SECONDS, SIGNATURE_HEADER, TIMESTAMP_HEADER, computeWebhookSignature, parseWebhookRequest, verifyWebhookSignature };
|
package/dist/widget.cjs
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var OwniApiError = class extends Error {
|
|
5
|
+
status;
|
|
6
|
+
body;
|
|
7
|
+
constructor(status, message, body) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "OwniApiError";
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.body = body;
|
|
12
|
+
}
|
|
13
|
+
/** 401/403 — a wrong, revoked, expired or under-scoped API key. Retrying never helps. */
|
|
14
|
+
get isAuthError() {
|
|
15
|
+
return this.status === 401 || this.status === 403;
|
|
16
|
+
}
|
|
17
|
+
/** 429 — the external API allows 60 requests per minute per key. */
|
|
18
|
+
get isRateLimited() {
|
|
19
|
+
return this.status === 429;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/widget.ts
|
|
24
|
+
var OwniWidgetClient = class {
|
|
25
|
+
baseUrl;
|
|
26
|
+
projectKey;
|
|
27
|
+
fetchImpl;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.baseUrl = (options.baseUrl ?? "https://app.owni.chat").replace(/\/+$/, "");
|
|
30
|
+
this.projectKey = options.projectKey;
|
|
31
|
+
const resolved = options.fetch ?? globalThis.fetch;
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
throw new Error("No fetch implementation found. Pass `fetch` in the client options.");
|
|
34
|
+
}
|
|
35
|
+
this.fetchImpl = resolved;
|
|
36
|
+
}
|
|
37
|
+
async request(path, init) {
|
|
38
|
+
const url = `${this.baseUrl}/api/widget/${encodeURIComponent(this.projectKey)}${path}`;
|
|
39
|
+
const response = await this.fetchImpl(url, {
|
|
40
|
+
...init,
|
|
41
|
+
headers: { "Content-Type": "application/json", ...init?.headers ?? {} },
|
|
42
|
+
credentials: "omit",
|
|
43
|
+
mode: "cors"
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
let message = `Widget API responded with status ${response.status}`;
|
|
47
|
+
let body = null;
|
|
48
|
+
try {
|
|
49
|
+
body = await response.json();
|
|
50
|
+
if (body && typeof body === "object" && "message" in body && typeof body.message === "string") {
|
|
51
|
+
message = body.message;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
throw new OwniApiError(response.status, message, body);
|
|
56
|
+
}
|
|
57
|
+
return await response.json();
|
|
58
|
+
}
|
|
59
|
+
getConfig() {
|
|
60
|
+
return this.request("/config");
|
|
61
|
+
}
|
|
62
|
+
createSession(input = {}) {
|
|
63
|
+
return this.request("/session", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: JSON.stringify(input)
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
listMessages(input) {
|
|
69
|
+
const query = new URLSearchParams({
|
|
70
|
+
visitor_id: input.visitorId,
|
|
71
|
+
visitor_token: input.visitorToken
|
|
72
|
+
});
|
|
73
|
+
return this.request(
|
|
74
|
+
`/conversations/${encodeURIComponent(input.conversationId)}/messages?${query.toString()}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
sendMessage(input) {
|
|
78
|
+
return this.request(`/conversations/${encodeURIComponent(input.conversationId)}/messages`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
visitor_id: input.visitorId,
|
|
82
|
+
visitor_token: input.visitorToken,
|
|
83
|
+
content: input.content
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
lookupOrder(input) {
|
|
88
|
+
return this.request("/order-lookup", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: JSON.stringify({
|
|
91
|
+
order_number: input.orderNumber,
|
|
92
|
+
email: input.email,
|
|
93
|
+
visitor_id: input.visitorId,
|
|
94
|
+
visitor_token: input.visitorToken
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var createWidgetClient = (options) => new OwniWidgetClient(options);
|
|
100
|
+
|
|
101
|
+
exports.OwniWidgetClient = OwniWidgetClient;
|
|
102
|
+
exports.createWidgetClient = createWidgetClient;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type WidgetProductCard = {
|
|
2
|
+
type: 'product';
|
|
3
|
+
product_id?: string | null;
|
|
4
|
+
external_id?: string | null;
|
|
5
|
+
title: string;
|
|
6
|
+
price?: string | number | null;
|
|
7
|
+
old_price?: string | number | null;
|
|
8
|
+
currency?: string | null;
|
|
9
|
+
image_url?: string | null;
|
|
10
|
+
url: string;
|
|
11
|
+
attributes?: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
type WidgetSessionInput = {
|
|
17
|
+
visitor_id?: string | null;
|
|
18
|
+
source_page_url?: string | null;
|
|
19
|
+
timezone?: string | null;
|
|
20
|
+
referrer?: string | null;
|
|
21
|
+
language?: string | null;
|
|
22
|
+
};
|
|
23
|
+
type WidgetSession = {
|
|
24
|
+
visitor_id: string;
|
|
25
|
+
visitor_token: string;
|
|
26
|
+
conversation_id?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
type WidgetMessage = {
|
|
30
|
+
id: string;
|
|
31
|
+
conversation_id: string;
|
|
32
|
+
sender_type: string;
|
|
33
|
+
content: string;
|
|
34
|
+
created_at: string;
|
|
35
|
+
cards?: WidgetProductCard[];
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
type OrderLookupResult = {
|
|
39
|
+
connected: boolean;
|
|
40
|
+
provider: string | null;
|
|
41
|
+
found: boolean;
|
|
42
|
+
email_matches: boolean;
|
|
43
|
+
order: Record<string, unknown> | null;
|
|
44
|
+
};
|
|
45
|
+
type WidgetClientOptions = {
|
|
46
|
+
projectKey: string;
|
|
47
|
+
baseUrl?: string;
|
|
48
|
+
fetch?: typeof fetch;
|
|
49
|
+
};
|
|
50
|
+
declare class OwniWidgetClient {
|
|
51
|
+
private readonly baseUrl;
|
|
52
|
+
private readonly projectKey;
|
|
53
|
+
private readonly fetchImpl;
|
|
54
|
+
constructor(options: WidgetClientOptions);
|
|
55
|
+
private request;
|
|
56
|
+
getConfig<T = Record<string, unknown>>(): Promise<T>;
|
|
57
|
+
createSession(input?: WidgetSessionInput): Promise<WidgetSession>;
|
|
58
|
+
listMessages(input: {
|
|
59
|
+
conversationId: string;
|
|
60
|
+
visitorId: string;
|
|
61
|
+
visitorToken: string;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
messages: WidgetMessage[];
|
|
64
|
+
}>;
|
|
65
|
+
sendMessage(input: {
|
|
66
|
+
conversationId: string;
|
|
67
|
+
visitorId: string;
|
|
68
|
+
visitorToken: string;
|
|
69
|
+
content: string;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
message: WidgetMessage;
|
|
72
|
+
}>;
|
|
73
|
+
lookupOrder(input: {
|
|
74
|
+
orderNumber: string;
|
|
75
|
+
email: string;
|
|
76
|
+
visitorId: string;
|
|
77
|
+
visitorToken: string;
|
|
78
|
+
}): Promise<OrderLookupResult>;
|
|
79
|
+
}
|
|
80
|
+
declare const createWidgetClient: (options: WidgetClientOptions) => OwniWidgetClient;
|
|
81
|
+
|
|
82
|
+
export { type OrderLookupResult, OwniWidgetClient, type WidgetClientOptions, type WidgetMessage, type WidgetProductCard, type WidgetSession, type WidgetSessionInput, createWidgetClient };
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
type WidgetProductCard = {
|
|
2
|
+
type: 'product';
|
|
3
|
+
product_id?: string | null;
|
|
4
|
+
external_id?: string | null;
|
|
5
|
+
title: string;
|
|
6
|
+
price?: string | number | null;
|
|
7
|
+
old_price?: string | number | null;
|
|
8
|
+
currency?: string | null;
|
|
9
|
+
image_url?: string | null;
|
|
10
|
+
url: string;
|
|
11
|
+
attributes?: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
type WidgetSessionInput = {
|
|
17
|
+
visitor_id?: string | null;
|
|
18
|
+
source_page_url?: string | null;
|
|
19
|
+
timezone?: string | null;
|
|
20
|
+
referrer?: string | null;
|
|
21
|
+
language?: string | null;
|
|
22
|
+
};
|
|
23
|
+
type WidgetSession = {
|
|
24
|
+
visitor_id: string;
|
|
25
|
+
visitor_token: string;
|
|
26
|
+
conversation_id?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
type WidgetMessage = {
|
|
30
|
+
id: string;
|
|
31
|
+
conversation_id: string;
|
|
32
|
+
sender_type: string;
|
|
33
|
+
content: string;
|
|
34
|
+
created_at: string;
|
|
35
|
+
cards?: WidgetProductCard[];
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
type OrderLookupResult = {
|
|
39
|
+
connected: boolean;
|
|
40
|
+
provider: string | null;
|
|
41
|
+
found: boolean;
|
|
42
|
+
email_matches: boolean;
|
|
43
|
+
order: Record<string, unknown> | null;
|
|
44
|
+
};
|
|
45
|
+
type WidgetClientOptions = {
|
|
46
|
+
projectKey: string;
|
|
47
|
+
baseUrl?: string;
|
|
48
|
+
fetch?: typeof fetch;
|
|
49
|
+
};
|
|
50
|
+
declare class OwniWidgetClient {
|
|
51
|
+
private readonly baseUrl;
|
|
52
|
+
private readonly projectKey;
|
|
53
|
+
private readonly fetchImpl;
|
|
54
|
+
constructor(options: WidgetClientOptions);
|
|
55
|
+
private request;
|
|
56
|
+
getConfig<T = Record<string, unknown>>(): Promise<T>;
|
|
57
|
+
createSession(input?: WidgetSessionInput): Promise<WidgetSession>;
|
|
58
|
+
listMessages(input: {
|
|
59
|
+
conversationId: string;
|
|
60
|
+
visitorId: string;
|
|
61
|
+
visitorToken: string;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
messages: WidgetMessage[];
|
|
64
|
+
}>;
|
|
65
|
+
sendMessage(input: {
|
|
66
|
+
conversationId: string;
|
|
67
|
+
visitorId: string;
|
|
68
|
+
visitorToken: string;
|
|
69
|
+
content: string;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
message: WidgetMessage;
|
|
72
|
+
}>;
|
|
73
|
+
lookupOrder(input: {
|
|
74
|
+
orderNumber: string;
|
|
75
|
+
email: string;
|
|
76
|
+
visitorId: string;
|
|
77
|
+
visitorToken: string;
|
|
78
|
+
}): Promise<OrderLookupResult>;
|
|
79
|
+
}
|
|
80
|
+
declare const createWidgetClient: (options: WidgetClientOptions) => OwniWidgetClient;
|
|
81
|
+
|
|
82
|
+
export { type OrderLookupResult, OwniWidgetClient, type WidgetClientOptions, type WidgetMessage, type WidgetProductCard, type WidgetSession, type WidgetSessionInput, createWidgetClient };
|
package/dist/widget.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { OwniApiError } from './chunk-2OWJTVJ3.js';
|
|
2
|
+
|
|
3
|
+
// src/widget.ts
|
|
4
|
+
var OwniWidgetClient = class {
|
|
5
|
+
baseUrl;
|
|
6
|
+
projectKey;
|
|
7
|
+
fetchImpl;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.baseUrl = (options.baseUrl ?? "https://app.owni.chat").replace(/\/+$/, "");
|
|
10
|
+
this.projectKey = options.projectKey;
|
|
11
|
+
const resolved = options.fetch ?? globalThis.fetch;
|
|
12
|
+
if (!resolved) {
|
|
13
|
+
throw new Error("No fetch implementation found. Pass `fetch` in the client options.");
|
|
14
|
+
}
|
|
15
|
+
this.fetchImpl = resolved;
|
|
16
|
+
}
|
|
17
|
+
async request(path, init) {
|
|
18
|
+
const url = `${this.baseUrl}/api/widget/${encodeURIComponent(this.projectKey)}${path}`;
|
|
19
|
+
const response = await this.fetchImpl(url, {
|
|
20
|
+
...init,
|
|
21
|
+
headers: { "Content-Type": "application/json", ...init?.headers ?? {} },
|
|
22
|
+
credentials: "omit",
|
|
23
|
+
mode: "cors"
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
let message = `Widget API responded with status ${response.status}`;
|
|
27
|
+
let body = null;
|
|
28
|
+
try {
|
|
29
|
+
body = await response.json();
|
|
30
|
+
if (body && typeof body === "object" && "message" in body && typeof body.message === "string") {
|
|
31
|
+
message = body.message;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
throw new OwniApiError(response.status, message, body);
|
|
36
|
+
}
|
|
37
|
+
return await response.json();
|
|
38
|
+
}
|
|
39
|
+
getConfig() {
|
|
40
|
+
return this.request("/config");
|
|
41
|
+
}
|
|
42
|
+
createSession(input = {}) {
|
|
43
|
+
return this.request("/session", {
|
|
44
|
+
method: "POST",
|
|
45
|
+
body: JSON.stringify(input)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
listMessages(input) {
|
|
49
|
+
const query = new URLSearchParams({
|
|
50
|
+
visitor_id: input.visitorId,
|
|
51
|
+
visitor_token: input.visitorToken
|
|
52
|
+
});
|
|
53
|
+
return this.request(
|
|
54
|
+
`/conversations/${encodeURIComponent(input.conversationId)}/messages?${query.toString()}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
sendMessage(input) {
|
|
58
|
+
return this.request(`/conversations/${encodeURIComponent(input.conversationId)}/messages`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
body: JSON.stringify({
|
|
61
|
+
visitor_id: input.visitorId,
|
|
62
|
+
visitor_token: input.visitorToken,
|
|
63
|
+
content: input.content
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
lookupOrder(input) {
|
|
68
|
+
return this.request("/order-lookup", {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
order_number: input.orderNumber,
|
|
72
|
+
email: input.email,
|
|
73
|
+
visitor_id: input.visitorId,
|
|
74
|
+
visitor_token: input.visitorToken
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var createWidgetClient = (options) => new OwniWidgetClient(options);
|
|
80
|
+
|
|
81
|
+
export { OwniWidgetClient, createWidgetClient };
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ownichat/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official owni.chat API client: product catalog sync, knowledge sources, allowed domains, webhook verification and the public widget API.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://owni.chat",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/UpMon-app/chats.git",
|
|
10
|
+
"directory": "integrations/javascript/packages/sdk"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"owni",
|
|
14
|
+
"ownichat",
|
|
15
|
+
"live chat",
|
|
16
|
+
"ai chat widget",
|
|
17
|
+
"ecommerce",
|
|
18
|
+
"product catalog"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/index.d.cts",
|
|
30
|
+
"default": "./dist/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"./webhooks": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/webhooks.d.ts",
|
|
36
|
+
"default": "./dist/webhooks.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/webhooks.d.cts",
|
|
40
|
+
"default": "./dist/webhooks.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"./widget": {
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./dist/widget.d.ts",
|
|
46
|
+
"default": "./dist/widget.js"
|
|
47
|
+
},
|
|
48
|
+
"require": {
|
|
49
|
+
"types": "./dist/widget.d.cts",
|
|
50
|
+
"default": "./dist/widget.cjs"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"./package.json": "./package.json"
|
|
54
|
+
},
|
|
55
|
+
"main": "./dist/index.cjs",
|
|
56
|
+
"module": "./dist/index.js",
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"files": [
|
|
59
|
+
"dist",
|
|
60
|
+
"README.md",
|
|
61
|
+
"CHANGELOG.md",
|
|
62
|
+
"LICENSE"
|
|
63
|
+
],
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsup",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"typecheck": "tsc --noEmit"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=18"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@types/node": "^22.20.1"
|
|
77
|
+
},
|
|
78
|
+
"typesVersions": {
|
|
79
|
+
"*": {
|
|
80
|
+
"webhooks": [
|
|
81
|
+
"./dist/webhooks.d.ts"
|
|
82
|
+
],
|
|
83
|
+
"widget": [
|
|
84
|
+
"./dist/widget.d.ts"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"bugs": {
|
|
89
|
+
"url": "https://github.com/UpMon-app/chats/issues"
|
|
90
|
+
},
|
|
91
|
+
"author": "owni.chat"
|
|
92
|
+
}
|