@advertising-chat/sdk 0.2.1
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 +41 -0
- package/bin/client.d.ts +72 -0
- package/bin/client.js +179 -0
- package/bin/client.js.map +1 -0
- package/bin/index.d.ts +4 -0
- package/bin/index.js +26 -0
- package/bin/index.js.map +1 -0
- package/bin/render/webchat.d.ts +40 -0
- package/bin/render/webchat.js +34 -0
- package/bin/render/webchat.js.map +1 -0
- package/bin/render/whatsapp.d.ts +34 -0
- package/bin/render/whatsapp.js +68 -0
- package/bin/render/whatsapp.js.map +1 -0
- package/bin/types.d.ts +176 -0
- package/bin/types.js +40 -0
- package/bin/types.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @advertising-chat/sdk
|
|
2
|
+
|
|
3
|
+
Typed client for the [Advertising.chat](https://advertising.chat) ads API —
|
|
4
|
+
fetch ads, track clicks and conversions, and render payloads for your
|
|
5
|
+
channel. Zero runtime dependencies (uses native `fetch`, Node >= 18).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @advertising-chat/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { AdChat, renderWebchat, renderWhatsApp, UniversalAdPayload } from '@advertising-chat/sdk';
|
|
13
|
+
|
|
14
|
+
const adchat = new AdChat({ apiKey: process.env.ADCHAT_API_KEY! });
|
|
15
|
+
|
|
16
|
+
// Fetch an ad, passing conversation context for relevance.
|
|
17
|
+
const ad = await adchat.fetchAd<UniversalAdPayload>({
|
|
18
|
+
adUnit: 'my-ad-unit',
|
|
19
|
+
subscriber: sessionId, // anonymous id — enables frequency capping
|
|
20
|
+
context: [
|
|
21
|
+
{ role: 'user', text: 'Best hotels in Rome?' },
|
|
22
|
+
{ role: 'assistant', text: 'Here are three options...' },
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (ad) {
|
|
27
|
+
// null means no-fill: continue without an ad.
|
|
28
|
+
const msg = renderWebchat(ad.payload); // { text, imageUrl, buttons, disclosure }
|
|
29
|
+
const wa = renderWhatsApp(ad.payload); // WhatsApp Cloud API message object
|
|
30
|
+
|
|
31
|
+
// Button URLs are click-tracking URLs (302 to the advertiser) — use verbatim.
|
|
32
|
+
await adchat.recordEvent({ messageId: ad.messageId, type: 'send' });
|
|
33
|
+
await adchat.recordConversion({ messageId: ad.messageId, name: 'purchase', value: 19.99, currency: 'USD' });
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Always present ads clearly labeled as **Sponsored**.
|
|
38
|
+
|
|
39
|
+
Full quickstarts: [docs.advertising.chat](https://docs.advertising.chat).
|
|
40
|
+
Part of [AdKit](https://github.com/advertising-chat/adkit) — see also
|
|
41
|
+
`@advertising-chat/mcp` (MCP server) and the Ad Skill.
|
package/bin/client.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Ad, AdContext, ClickUrlParams, FetchAdParams, RecordConversionParams, RecordEventParams } from './types';
|
|
2
|
+
export interface AdChatOptions {
|
|
3
|
+
/** Org API key (from the Advertising.chat console). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/**
|
|
6
|
+
* Base URL of the ads runtime API.
|
|
7
|
+
* Defaults to `https://ads.advertising.chat/v1`.
|
|
8
|
+
*/
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/** Override fetch implementation (mainly for tests). */
|
|
11
|
+
fetchFn?: typeof fetch;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Client for the Advertising.chat ads runtime API.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* const adchat = new AdChat({ apiKey: process.env.ADCHAT_API_KEY! });
|
|
18
|
+
* const ad = await adchat.fetchAd({ adUnit: 'my-ad-unit', context: summary });
|
|
19
|
+
* if (ad) {
|
|
20
|
+
* // render ad.payload, then:
|
|
21
|
+
* await adchat.recordEvent({ messageId: ad.messageId, type: 'send' });
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class AdChat {
|
|
26
|
+
private readonly apiKey;
|
|
27
|
+
private readonly baseUrl;
|
|
28
|
+
private readonly fetchFn;
|
|
29
|
+
constructor(options: AdChatOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Fetch an ad for a placement.
|
|
32
|
+
*
|
|
33
|
+
* Resolves with the served ad, or `null` when no ad is available for
|
|
34
|
+
* this request (no-fill: frequency caps, targeting, empty ad unit...).
|
|
35
|
+
* A no-fill is a normal outcome — simply continue the conversation
|
|
36
|
+
* without an ad.
|
|
37
|
+
*
|
|
38
|
+
* Throws {@link AdChatApiError} for request errors (unknown ad unit,
|
|
39
|
+
* unsupported format, bad API key...).
|
|
40
|
+
*/
|
|
41
|
+
fetchAd<P = unknown>(params?: FetchAdParams): Promise<Ad<P> | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Record a lifecycle or engagement event ('send', 'deliver', 'read',
|
|
44
|
+
* 'click', 'conversion') for a previously fetched ad.
|
|
45
|
+
*/
|
|
46
|
+
recordEvent(params: RecordEventParams): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Record a conversion for a previously fetched ad, optionally with a
|
|
49
|
+
* monetary value.
|
|
50
|
+
*/
|
|
51
|
+
recordConversion(params: RecordConversionParams): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Build a click-tracking URL that records the click and 302-redirects
|
|
54
|
+
* to `targetUrl`.
|
|
55
|
+
*
|
|
56
|
+
* NOTE: Navigate actions in fetched payloads already contain wrapped
|
|
57
|
+
* click URLs — use those directly. This helper is for surfaces where
|
|
58
|
+
* you construct links yourself.
|
|
59
|
+
*/
|
|
60
|
+
clickUrl(params: ClickUrlParams): string;
|
|
61
|
+
private headers;
|
|
62
|
+
private toApiError;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Derive the fetch API's structured `conversation` field from structured
|
|
66
|
+
* context turns. String context has no turn structure, so it is sent only
|
|
67
|
+
* in the `context` field.
|
|
68
|
+
*/
|
|
69
|
+
export declare function conversationFromContext(context: AdContext, subscriber?: string): {
|
|
70
|
+
conversationId: string;
|
|
71
|
+
messages: object[];
|
|
72
|
+
} | undefined;
|
package/bin/client.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AdChat = void 0;
|
|
4
|
+
exports.conversationFromContext = conversationFromContext;
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const DEFAULT_BASE_URL = 'https://ads.advertising.chat/v1';
|
|
7
|
+
/**
|
|
8
|
+
* Client for the Advertising.chat ads runtime API.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const adchat = new AdChat({ apiKey: process.env.ADCHAT_API_KEY! });
|
|
12
|
+
* const ad = await adchat.fetchAd({ adUnit: 'my-ad-unit', context: summary });
|
|
13
|
+
* if (ad) {
|
|
14
|
+
* // render ad.payload, then:
|
|
15
|
+
* await adchat.recordEvent({ messageId: ad.messageId, type: 'send' });
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
class AdChat {
|
|
20
|
+
apiKey;
|
|
21
|
+
baseUrl;
|
|
22
|
+
fetchFn;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
if (!options?.apiKey) {
|
|
25
|
+
throw new Error('AdChat: apiKey is required');
|
|
26
|
+
}
|
|
27
|
+
this.apiKey = options.apiKey;
|
|
28
|
+
this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
29
|
+
// Bind to globalThis so `fetch` keeps its expected receiver.
|
|
30
|
+
this.fetchFn = options.fetchFn || fetch.bind(globalThis);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fetch an ad for a placement.
|
|
34
|
+
*
|
|
35
|
+
* Resolves with the served ad, or `null` when no ad is available for
|
|
36
|
+
* this request (no-fill: frequency caps, targeting, empty ad unit...).
|
|
37
|
+
* A no-fill is a normal outcome — simply continue the conversation
|
|
38
|
+
* without an ad.
|
|
39
|
+
*
|
|
40
|
+
* Throws {@link AdChatApiError} for request errors (unknown ad unit,
|
|
41
|
+
* unsupported format, bad API key...).
|
|
42
|
+
*/
|
|
43
|
+
async fetchAd(params = {}) {
|
|
44
|
+
const body = {};
|
|
45
|
+
if (params.adUnit)
|
|
46
|
+
body.adUnit = params.adUnit;
|
|
47
|
+
if (params.format)
|
|
48
|
+
body.format = params.format;
|
|
49
|
+
if (params.subscriber)
|
|
50
|
+
body.subscriber = params.subscriber;
|
|
51
|
+
if (params.attributes)
|
|
52
|
+
body.attributes = params.attributes;
|
|
53
|
+
if (params.test)
|
|
54
|
+
body.test = true;
|
|
55
|
+
if (params.context !== undefined) {
|
|
56
|
+
body.context = params.context;
|
|
57
|
+
// Also populate the fetch API's structured `conversation` field so
|
|
58
|
+
// context-aware demand sources can use turn-level context today.
|
|
59
|
+
const conversation = conversationFromContext(params.context, params.subscriber);
|
|
60
|
+
if (conversation)
|
|
61
|
+
body.conversation = conversation;
|
|
62
|
+
}
|
|
63
|
+
const res = await this.fetchFn(`${this.baseUrl}/fetch/v2`, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: this.headers(),
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
});
|
|
68
|
+
if (res.status === 204) {
|
|
69
|
+
return null; // no fill
|
|
70
|
+
}
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw await this.toApiError(res);
|
|
73
|
+
}
|
|
74
|
+
const ad = (await res.json());
|
|
75
|
+
// Defensive: some no-fill paths respond 200 with an empty body/payload.
|
|
76
|
+
if (!ad || !ad.payload)
|
|
77
|
+
return null;
|
|
78
|
+
return ad;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Record a lifecycle or engagement event ('send', 'deliver', 'read',
|
|
82
|
+
* 'click', 'conversion') for a previously fetched ad.
|
|
83
|
+
*/
|
|
84
|
+
async recordEvent(params) {
|
|
85
|
+
const res = await this.fetchFn(`${this.baseUrl}/event`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: this.headers(),
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
version: 2,
|
|
90
|
+
eventType: params.type,
|
|
91
|
+
fetchEventId: params.messageId,
|
|
92
|
+
...(params.data ? { eventData: params.data } : {}),
|
|
93
|
+
...(params.subscriber ? { subscriber: params.subscriber } : {}),
|
|
94
|
+
...(params.timestamp ? { timestamp: params.timestamp } : {}),
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
if (res.status !== 201) {
|
|
98
|
+
throw await this.toApiError(res);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Record a conversion for a previously fetched ad, optionally with a
|
|
103
|
+
* monetary value.
|
|
104
|
+
*/
|
|
105
|
+
async recordConversion(params) {
|
|
106
|
+
return this.recordEvent({
|
|
107
|
+
messageId: params.messageId,
|
|
108
|
+
type: 'conversion',
|
|
109
|
+
subscriber: params.subscriber,
|
|
110
|
+
data: {
|
|
111
|
+
...(params.name ? { conversionName: params.name } : {}),
|
|
112
|
+
...(params.value !== undefined ? { value: params.value } : {}),
|
|
113
|
+
...(params.currency ? { cur: params.currency } : {}),
|
|
114
|
+
...(params.data || {}),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build a click-tracking URL that records the click and 302-redirects
|
|
120
|
+
* to `targetUrl`.
|
|
121
|
+
*
|
|
122
|
+
* NOTE: Navigate actions in fetched payloads already contain wrapped
|
|
123
|
+
* click URLs — use those directly. This helper is for surfaces where
|
|
124
|
+
* you construct links yourself.
|
|
125
|
+
*/
|
|
126
|
+
clickUrl(params) {
|
|
127
|
+
const q = new URLSearchParams({
|
|
128
|
+
key: this.apiKey,
|
|
129
|
+
postbackData: `${params.messageId}_${params.actionId}`,
|
|
130
|
+
target: params.targetUrl,
|
|
131
|
+
});
|
|
132
|
+
return `${this.baseUrl}/click?${q.toString()}`;
|
|
133
|
+
}
|
|
134
|
+
headers() {
|
|
135
|
+
return {
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
138
|
+
'User-Agent': '@advertising-chat/sdk',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async toApiError(res) {
|
|
142
|
+
let reason;
|
|
143
|
+
let description;
|
|
144
|
+
try {
|
|
145
|
+
const body = (await res.json());
|
|
146
|
+
reason = body?.reason;
|
|
147
|
+
description = body?.description || body?.title;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// non-JSON error body
|
|
151
|
+
}
|
|
152
|
+
return new types_1.AdChatApiError(res.status, reason, description
|
|
153
|
+
? `Advertising.chat API error ${res.status}: ${description}`
|
|
154
|
+
: undefined);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
exports.AdChat = AdChat;
|
|
158
|
+
/**
|
|
159
|
+
* Derive the fetch API's structured `conversation` field from structured
|
|
160
|
+
* context turns. String context has no turn structure, so it is sent only
|
|
161
|
+
* in the `context` field.
|
|
162
|
+
*/
|
|
163
|
+
function conversationFromContext(context, subscriber) {
|
|
164
|
+
if (typeof context === 'string' || !Array.isArray(context))
|
|
165
|
+
return undefined;
|
|
166
|
+
if (context.length === 0)
|
|
167
|
+
return undefined;
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
return {
|
|
170
|
+
conversationId: `adkit-${now.toString(36)}`,
|
|
171
|
+
messages: context.map((turn, i) => ({
|
|
172
|
+
messageId: `adkit-${now.toString(36)}-${i}`,
|
|
173
|
+
authorId: turn.role === 'assistant' ? 'bot' : subscriber || 'user',
|
|
174
|
+
text: turn.text,
|
|
175
|
+
timestamp: new Date(now).toISOString(),
|
|
176
|
+
})),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/bin/index.d.ts
ADDED
package/bin/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.renderWhatsApp = exports.renderWebchat = exports.conversationFromContext = exports.AdChat = void 0;
|
|
18
|
+
var client_1 = require("./client");
|
|
19
|
+
Object.defineProperty(exports, "AdChat", { enumerable: true, get: function () { return client_1.AdChat; } });
|
|
20
|
+
Object.defineProperty(exports, "conversationFromContext", { enumerable: true, get: function () { return client_1.conversationFromContext; } });
|
|
21
|
+
__exportStar(require("./types"), exports);
|
|
22
|
+
var webchat_1 = require("./render/webchat");
|
|
23
|
+
Object.defineProperty(exports, "renderWebchat", { enumerable: true, get: function () { return webchat_1.renderWebchat; } });
|
|
24
|
+
var whatsapp_1 = require("./render/whatsapp");
|
|
25
|
+
Object.defineProperty(exports, "renderWhatsApp", { enumerable: true, get: function () { return whatsapp_1.renderWhatsApp; } });
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
package/bin/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mCAA0E;AAAjE,gGAAA,MAAM,OAAA;AAAiB,iHAAA,uBAAuB,OAAA;AACvD,0CAAwB;AACxB,4CAAgF;AAAvE,wGAAA,aAAa,OAAA;AACtB,8CAAoE;AAA3D,0GAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { UniversalAdPayload } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* A webchat-ready message: plain text plus tappable buttons. Maps 1:1
|
|
4
|
+
* onto typical webchat widget APIs (text + quick replies / buttons).
|
|
5
|
+
*/
|
|
6
|
+
export interface WebchatMessage {
|
|
7
|
+
/** Message text: title and subtitle on separate lines. */
|
|
8
|
+
text: string;
|
|
9
|
+
/** Card image, if the ad has one. */
|
|
10
|
+
imageUrl?: string;
|
|
11
|
+
/** Tappable buttons. `url` buttons already point at click-tracking URLs. */
|
|
12
|
+
buttons: WebchatButton[];
|
|
13
|
+
/**
|
|
14
|
+
* Disclosure label. Always render this visibly with the ad
|
|
15
|
+
* (e.g. as a badge or prefix) — sponsored content must be labeled.
|
|
16
|
+
*/
|
|
17
|
+
disclosure: string;
|
|
18
|
+
}
|
|
19
|
+
export interface WebchatButton {
|
|
20
|
+
/** Button label. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** Click-tracking URL to open (Navigate actions). */
|
|
23
|
+
url?: string;
|
|
24
|
+
/** Phone number to dial (Call actions). */
|
|
25
|
+
phoneNumber?: string;
|
|
26
|
+
/** Stable id for the button (the ad action id). */
|
|
27
|
+
id: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Render a universal ('direqt' format) ad payload as a webchat message.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const ad = await adchat.fetchAd<UniversalAdPayload>({ adUnit: 'chat-footer' });
|
|
34
|
+
* if (ad) {
|
|
35
|
+
* const msg = renderWebchat(ad.payload);
|
|
36
|
+
* widget.send({ text: `${msg.disclosure}: ${msg.text}`, buttons: msg.buttons });
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function renderWebchat(payload: UniversalAdPayload): WebchatMessage;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderWebchat = renderWebchat;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
/**
|
|
6
|
+
* Render a universal ('direqt' format) ad payload as a webchat message.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const ad = await adchat.fetchAd<UniversalAdPayload>({ adUnit: 'chat-footer' });
|
|
10
|
+
* if (ad) {
|
|
11
|
+
* const msg = renderWebchat(ad.payload);
|
|
12
|
+
* widget.send({ text: `${msg.disclosure}: ${msg.text}`, buttons: msg.buttons });
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function renderWebchat(payload) {
|
|
17
|
+
const lines = [payload.title, payload.subtitle].filter(Boolean);
|
|
18
|
+
return {
|
|
19
|
+
text: lines.join('\n') || payload.altText || '',
|
|
20
|
+
...(payload.imageUrl ? { imageUrl: payload.imageUrl } : {}),
|
|
21
|
+
buttons: (payload.actions || []).map(action => ({
|
|
22
|
+
label: action.text,
|
|
23
|
+
...(action.type === types_1.AdActionType.Navigate && action.navigate
|
|
24
|
+
? { url: action.navigate.url }
|
|
25
|
+
: {}),
|
|
26
|
+
...(action.type === types_1.AdActionType.Call && action.call
|
|
27
|
+
? { phoneNumber: action.call.phoneNumber }
|
|
28
|
+
: {}),
|
|
29
|
+
id: action.actionId,
|
|
30
|
+
})),
|
|
31
|
+
disclosure: 'Sponsored',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=webchat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webchat.js","sourceRoot":"","sources":["../../src/render/webchat.ts"],"names":[],"mappings":";;AA0CA,sCAiBC;AA3DD,oCAA4D;AA+B5D;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAAC,OAA2B;IACvD,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE;QAC/C,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9C,KAAK,EAAE,MAAM,CAAC,IAAI;YAClB,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,oBAAY,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;gBAC1D,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE;gBAC9B,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,oBAAY,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI;gBAClD,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACP,EAAE,EAAE,MAAM,CAAC,QAAQ;SACpB,CAAC,CAAC;QACH,UAAU,EAAE,WAAW;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { UniversalAdPayload } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* A WhatsApp Cloud API message object (`POST /<phone>/messages` body),
|
|
4
|
+
* minus the `to` field — spread in the recipient before sending:
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* await waClient.post(`/${phoneId}/messages`, { ...renderWhatsApp(ad.payload), to });
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export type WhatsAppMessage = {
|
|
11
|
+
messaging_product: 'whatsapp';
|
|
12
|
+
recipient_type: 'individual';
|
|
13
|
+
type: 'interactive' | 'image' | 'text';
|
|
14
|
+
interactive?: object;
|
|
15
|
+
image?: {
|
|
16
|
+
link: string;
|
|
17
|
+
caption?: string;
|
|
18
|
+
};
|
|
19
|
+
text?: {
|
|
20
|
+
body: string;
|
|
21
|
+
preview_url?: boolean;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Render a universal ('direqt' format) ad payload as a WhatsApp Cloud API
|
|
26
|
+
* message object.
|
|
27
|
+
*
|
|
28
|
+
* Mapping (faithful to the platform's channel renderers):
|
|
29
|
+
* - One Navigate action → interactive `cta_url` message: image header,
|
|
30
|
+
* title/subtitle body, "Sponsored" footer, URL button (click-tracked).
|
|
31
|
+
* - Otherwise → image with caption (or plain text if no image); Navigate
|
|
32
|
+
* URLs are appended to the text, SMS-style.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderWhatsApp(payload: UniversalAdPayload): WhatsAppMessage;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderWhatsApp = renderWhatsApp;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
/**
|
|
6
|
+
* Render a universal ('direqt' format) ad payload as a WhatsApp Cloud API
|
|
7
|
+
* message object.
|
|
8
|
+
*
|
|
9
|
+
* Mapping (faithful to the platform's channel renderers):
|
|
10
|
+
* - One Navigate action → interactive `cta_url` message: image header,
|
|
11
|
+
* title/subtitle body, "Sponsored" footer, URL button (click-tracked).
|
|
12
|
+
* - Otherwise → image with caption (or plain text if no image); Navigate
|
|
13
|
+
* URLs are appended to the text, SMS-style.
|
|
14
|
+
*/
|
|
15
|
+
function renderWhatsApp(payload) {
|
|
16
|
+
const bodyText = [payload.title, payload.subtitle].filter(Boolean).join('\n') ||
|
|
17
|
+
payload.altText ||
|
|
18
|
+
'';
|
|
19
|
+
const navigateActions = (payload.actions || []).filter(a => a.type === types_1.AdActionType.Navigate && a.navigate?.url);
|
|
20
|
+
if (navigateActions.length === 1) {
|
|
21
|
+
const action = navigateActions[0];
|
|
22
|
+
return {
|
|
23
|
+
messaging_product: 'whatsapp',
|
|
24
|
+
recipient_type: 'individual',
|
|
25
|
+
type: 'interactive',
|
|
26
|
+
interactive: {
|
|
27
|
+
type: 'cta_url',
|
|
28
|
+
...(payload.imageUrl
|
|
29
|
+
? { header: { type: 'image', image: { link: payload.imageUrl } } }
|
|
30
|
+
: {}),
|
|
31
|
+
body: { text: bodyText },
|
|
32
|
+
footer: { text: 'Sponsored' },
|
|
33
|
+
action: {
|
|
34
|
+
name: 'cta_url',
|
|
35
|
+
parameters: {
|
|
36
|
+
display_text: truncate(action.text, 20) || 'Learn more',
|
|
37
|
+
// Already a click-tracking URL that 302s to the advertiser.
|
|
38
|
+
url: action.navigate.url,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// Fallback: degrade like the SMS renderer — text plus URL(s).
|
|
45
|
+
const urls = navigateActions.map(a => a.navigate.url);
|
|
46
|
+
const caption = ['Sponsored', bodyText, ...urls].filter(Boolean).join('\n');
|
|
47
|
+
if (payload.imageUrl) {
|
|
48
|
+
return {
|
|
49
|
+
messaging_product: 'whatsapp',
|
|
50
|
+
recipient_type: 'individual',
|
|
51
|
+
type: 'image',
|
|
52
|
+
image: { link: payload.imageUrl, caption },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
messaging_product: 'whatsapp',
|
|
57
|
+
recipient_type: 'individual',
|
|
58
|
+
type: 'text',
|
|
59
|
+
text: { body: caption, preview_url: true },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** WhatsApp cta_url display_text is limited to 20 characters. */
|
|
63
|
+
function truncate(s, max) {
|
|
64
|
+
if (!s)
|
|
65
|
+
return s;
|
|
66
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}…`;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=whatsapp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../src/render/whatsapp.ts"],"names":[],"mappings":";;AA6BA,wCAqDC;AAlFD,oCAA4D;AAmB5D;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,OAA2B;IACxD,MAAM,QAAQ,GACZ,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5D,OAAO,CAAC,OAAO;QACf,EAAE,CAAC;IAEL,MAAM,eAAe,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CACpD,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,oBAAY,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,EAAE,GAAG,CACzD,CAAC;IAEF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;QAClC,OAAO;YACL,iBAAiB,EAAE,UAAU;YAC7B,cAAc,EAAE,YAAY;YAC5B,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE;gBACX,IAAI,EAAE,SAAS;gBACf,GAAG,CAAC,OAAO,CAAC,QAAQ;oBAClB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE;oBAClE,CAAC,CAAC,EAAE,CAAC;gBACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACxB,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE;gBAC7B,MAAM,EAAE;oBACN,IAAI,EAAE,SAAS;oBACf,UAAU,EAAE;wBACV,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,YAAY;wBACvD,4DAA4D;wBAC5D,GAAG,EAAE,MAAM,CAAC,QAAS,CAAC,GAAG;qBAC1B;iBACF;aACF;SACF,CAAC;IACJ,CAAC;IAED,8DAA8D;IAC9D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAS,CAAC,GAAG,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE5E,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO;YACL,iBAAiB,EAAE,UAAU;YAC7B,cAAc,EAAE,YAAY;YAC5B,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE;SAC3C,CAAC;IACJ,CAAC;IACD,OAAO;QACL,iBAAiB,EAAE,UAAU;QAC7B,cAAc,EAAE,YAAY;QAC5B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE;KAC3C,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,IAAI,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjB,OAAO,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;AACzD,CAAC"}
|
package/bin/types.d.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the Advertising.chat ads runtime API (`ads.advertising.chat/v1`).
|
|
3
|
+
*
|
|
4
|
+
* These mirror the server's fetch v2 / event v2 contracts. Ground truth:
|
|
5
|
+
* the `ads` service (`src/ads-api/v1`) and `ads/ads-api/src/v1/fetch.ts`.
|
|
6
|
+
*/
|
|
7
|
+
/** Render formats supported by fetch v2 (`format` parameter). */
|
|
8
|
+
export type RenderFormat = 'direqt' | 'sms' | 'fbm' | 'html' | 'rcs-google' | 'rcs-gsma' | 'slack' | 'dialogflow';
|
|
9
|
+
/**
|
|
10
|
+
* One turn of conversation context.
|
|
11
|
+
*
|
|
12
|
+
* `role` is 'assistant' for messages authored by the bot/agent and 'user'
|
|
13
|
+
* for messages authored by the human subscriber.
|
|
14
|
+
*/
|
|
15
|
+
export interface ConversationTurn {
|
|
16
|
+
role: 'user' | 'assistant';
|
|
17
|
+
text: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Conversation context for semantic ad selection: either a free-text
|
|
21
|
+
* summary of the conversation so far, or the most recent turns.
|
|
22
|
+
*/
|
|
23
|
+
export type AdContext = string | ConversationTurn[];
|
|
24
|
+
export interface FetchAdParams {
|
|
25
|
+
/**
|
|
26
|
+
* Ad unit code identifying the placement. If omitted, the org's default
|
|
27
|
+
* ad unit is used.
|
|
28
|
+
*/
|
|
29
|
+
adUnit?: string;
|
|
30
|
+
/** Render format for the payload. Defaults to 'direqt' (universal JSON). */
|
|
31
|
+
format?: RenderFormat;
|
|
32
|
+
/**
|
|
33
|
+
* Opaque, org-specific subscriber identifier. Used for frequency caps
|
|
34
|
+
* and reporting. Never send PII — an anonymized/hashed id is ideal.
|
|
35
|
+
*/
|
|
36
|
+
subscriber?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Conversation context (summary string or recent turns). Passed to the
|
|
39
|
+
* server for context-aware ad selection.
|
|
40
|
+
*/
|
|
41
|
+
context?: AdContext;
|
|
42
|
+
/** Targeting attributes (key/value pairs) for rule targeting. */
|
|
43
|
+
attributes?: Record<string, string>;
|
|
44
|
+
/**
|
|
45
|
+
* Marks the fetch as a test: excluded from normal reporting and
|
|
46
|
+
* performance statistics.
|
|
47
|
+
*/
|
|
48
|
+
test?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** Action types on a universal ad payload. */
|
|
51
|
+
export declare enum AdActionType {
|
|
52
|
+
Navigate = "Navigate",
|
|
53
|
+
Call = "Call"
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A tappable action on an ad (button / suggested reply).
|
|
57
|
+
*
|
|
58
|
+
* For Navigate actions, `navigate.url` is already a wrapped click-tracking
|
|
59
|
+
* URL — send the user there directly and the click is recorded before the
|
|
60
|
+
* redirect to the advertiser.
|
|
61
|
+
*/
|
|
62
|
+
export interface AdAction {
|
|
63
|
+
type: AdActionType | string;
|
|
64
|
+
text: string;
|
|
65
|
+
navigate?: {
|
|
66
|
+
url: string;
|
|
67
|
+
};
|
|
68
|
+
call?: {
|
|
69
|
+
phoneNumber: string;
|
|
70
|
+
};
|
|
71
|
+
/** Postback data: `<messageId>_<actionId>`. */
|
|
72
|
+
context: string;
|
|
73
|
+
actionId: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The universal ('direqt' format) ad payload — a card with title,
|
|
77
|
+
* subtitle, image, and actions.
|
|
78
|
+
*/
|
|
79
|
+
export interface UniversalAdPayload {
|
|
80
|
+
title: string;
|
|
81
|
+
subtitle: string;
|
|
82
|
+
imageUrl: string;
|
|
83
|
+
actions: AdAction[];
|
|
84
|
+
/** Plain-text rendering of the ad (used for text-only channels). */
|
|
85
|
+
altText?: string;
|
|
86
|
+
displayUrl?: string;
|
|
87
|
+
}
|
|
88
|
+
/** The 'sms' format payload. */
|
|
89
|
+
export interface SmsAdPayload {
|
|
90
|
+
text: string;
|
|
91
|
+
}
|
|
92
|
+
export interface AdMedia {
|
|
93
|
+
file_url: string;
|
|
94
|
+
thumbnail_url: string;
|
|
95
|
+
content_description: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A served ad, as returned by fetch v2.
|
|
99
|
+
*/
|
|
100
|
+
export interface Ad<P = unknown> {
|
|
101
|
+
/**
|
|
102
|
+
* Fetch id for this served ad (e.g. `direqt-<uuid>`). Pass it to
|
|
103
|
+
* recordEvent()/recordConversion() to attribute events to this ad.
|
|
104
|
+
*/
|
|
105
|
+
messageId: string;
|
|
106
|
+
/** Render format of `payload`. */
|
|
107
|
+
format?: string;
|
|
108
|
+
/**
|
|
109
|
+
* The rendered ad. Shape depends on the requested format:
|
|
110
|
+
* UniversalAdPayload for 'direqt', SmsAdPayload for 'sms', channel-native
|
|
111
|
+
* JSON for the gateway formats.
|
|
112
|
+
*/
|
|
113
|
+
payload: P;
|
|
114
|
+
properties?: Record<string, string>;
|
|
115
|
+
/** Media files that must be uploaded to the gateway (RBM/FBM only). */
|
|
116
|
+
media?: AdMedia[];
|
|
117
|
+
isTest?: boolean;
|
|
118
|
+
}
|
|
119
|
+
/** Event types accepted by the v2 event API. */
|
|
120
|
+
export type AdEventType = 'send' | 'deliver' | 'read' | 'click' | 'conversion';
|
|
121
|
+
export interface RecordEventParams {
|
|
122
|
+
/** The `messageId` from the fetch response this event belongs to. */
|
|
123
|
+
messageId: string;
|
|
124
|
+
/** Lifecycle or engagement event type. */
|
|
125
|
+
type: AdEventType;
|
|
126
|
+
/** Arbitrary JSON to associate with the event. */
|
|
127
|
+
data?: object;
|
|
128
|
+
/** Subscriber id (only needed if not sent with the fetch). */
|
|
129
|
+
subscriber?: string;
|
|
130
|
+
/** Event time (unix ms). Defaults to now, server-side. */
|
|
131
|
+
timestamp?: number;
|
|
132
|
+
}
|
|
133
|
+
export interface RecordConversionParams {
|
|
134
|
+
/** The `messageId` from the fetch response this conversion belongs to. */
|
|
135
|
+
messageId: string;
|
|
136
|
+
/** Name of the conversion (e.g. 'purchase', 'signup'). */
|
|
137
|
+
name?: string;
|
|
138
|
+
/** Monetary value of the conversion. */
|
|
139
|
+
value?: number;
|
|
140
|
+
/** ISO-4217 currency for `value` (e.g. 'USD'). */
|
|
141
|
+
currency?: string;
|
|
142
|
+
/** Additional JSON to associate with the conversion. */
|
|
143
|
+
data?: object;
|
|
144
|
+
subscriber?: string;
|
|
145
|
+
}
|
|
146
|
+
export interface ClickUrlParams {
|
|
147
|
+
/** The `messageId` from the fetch response. */
|
|
148
|
+
messageId: string;
|
|
149
|
+
/** The action id of the tapped action (from `AdAction.actionId`). */
|
|
150
|
+
actionId: string;
|
|
151
|
+
/** Destination URL the user should land on after click tracking. */
|
|
152
|
+
targetUrl: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Error thrown for non-2xx API responses.
|
|
156
|
+
*
|
|
157
|
+
* Note that "no ad available" is NOT an error: fetchAd() returns null for
|
|
158
|
+
* the server's 204 no-fill responses.
|
|
159
|
+
*/
|
|
160
|
+
export declare class AdChatApiError extends Error {
|
|
161
|
+
/** HTTP status returned by the API. */
|
|
162
|
+
readonly status: number;
|
|
163
|
+
/**
|
|
164
|
+
* Machine-readable reason from the API, when provided
|
|
165
|
+
* (e.g. 'UnknownAdUnit', 'UnsupportedFormat').
|
|
166
|
+
*/
|
|
167
|
+
readonly reason?: string | undefined;
|
|
168
|
+
constructor(
|
|
169
|
+
/** HTTP status returned by the API. */
|
|
170
|
+
status: number,
|
|
171
|
+
/**
|
|
172
|
+
* Machine-readable reason from the API, when provided
|
|
173
|
+
* (e.g. 'UnknownAdUnit', 'UnsupportedFormat').
|
|
174
|
+
*/
|
|
175
|
+
reason?: string | undefined, message?: string);
|
|
176
|
+
}
|
package/bin/types.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types for the Advertising.chat ads runtime API (`ads.advertising.chat/v1`).
|
|
4
|
+
*
|
|
5
|
+
* These mirror the server's fetch v2 / event v2 contracts. Ground truth:
|
|
6
|
+
* the `ads` service (`src/ads-api/v1`) and `ads/ads-api/src/v1/fetch.ts`.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AdChatApiError = exports.AdActionType = void 0;
|
|
10
|
+
/** Action types on a universal ad payload. */
|
|
11
|
+
var AdActionType;
|
|
12
|
+
(function (AdActionType) {
|
|
13
|
+
AdActionType["Navigate"] = "Navigate";
|
|
14
|
+
AdActionType["Call"] = "Call";
|
|
15
|
+
})(AdActionType || (exports.AdActionType = AdActionType = {}));
|
|
16
|
+
/**
|
|
17
|
+
* Error thrown for non-2xx API responses.
|
|
18
|
+
*
|
|
19
|
+
* Note that "no ad available" is NOT an error: fetchAd() returns null for
|
|
20
|
+
* the server's 204 no-fill responses.
|
|
21
|
+
*/
|
|
22
|
+
class AdChatApiError extends Error {
|
|
23
|
+
status;
|
|
24
|
+
reason;
|
|
25
|
+
constructor(
|
|
26
|
+
/** HTTP status returned by the API. */
|
|
27
|
+
status,
|
|
28
|
+
/**
|
|
29
|
+
* Machine-readable reason from the API, when provided
|
|
30
|
+
* (e.g. 'UnknownAdUnit', 'UnsupportedFormat').
|
|
31
|
+
*/
|
|
32
|
+
reason, message) {
|
|
33
|
+
super(message || `Advertising.chat API error ${status} ${reason || ''}`);
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.reason = reason;
|
|
36
|
+
this.name = 'AdChatApiError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.AdChatApiError = AdChatApiError;
|
|
40
|
+
//# sourceMappingURL=types.js.map
|
package/bin/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAyDH,8CAA8C;AAC9C,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,qCAAqB,CAAA;IACrB,6BAAa,CAAA;AACf,CAAC,EAHW,YAAY,4BAAZ,YAAY,QAGvB;AA0GD;;;;;GAKG;AACH,MAAa,cAAe,SAAQ,KAAK;IAGrB;IAKA;IAPlB;IACE,uCAAuC;IACvB,MAAc;IAC9B;;;OAGG;IACa,MAAe,EAC/B,OAAgB;QAEhB,KAAK,CAAC,OAAO,IAAI,8BAA8B,MAAM,IAAI,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;QARzD,WAAM,GAAN,MAAM,CAAQ;QAKd,WAAM,GAAN,MAAM,CAAS;QAI/B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAdD,wCAcC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@advertising-chat/sdk",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Typed client for the Advertising.chat ads API — fetch, click, and event tracking plus per-channel render helpers",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Advertising.chat",
|
|
7
|
+
"homepage": "https://docs.advertising.chat/",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/advertising-chat/adkit.git",
|
|
11
|
+
"directory": "packages/sdk"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"advertising",
|
|
15
|
+
"ads",
|
|
16
|
+
"chatbot",
|
|
17
|
+
"llm",
|
|
18
|
+
"monetization",
|
|
19
|
+
"conversational-ai"
|
|
20
|
+
],
|
|
21
|
+
"main": "bin/index.js",
|
|
22
|
+
"types": "bin/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p .",
|
|
29
|
+
"clean": "rm -rf ./bin",
|
|
30
|
+
"prepack": "npm run clean && npm run build",
|
|
31
|
+
"test": "mocha"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
}
|
|
36
|
+
}
|