@yellesoftware/meridian-ads 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 +187 -2
- package/dist/cjs/client.js +155 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/creative.js +46 -0
- package/dist/cjs/creative.js.map +1 -0
- package/dist/cjs/errors.js +37 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/hmac.js +48 -0
- package/dist/cjs/hmac.js.map +1 -0
- package/dist/cjs/index.js +25 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/render.js +65 -0
- package/dist/cjs/render.js.map +1 -0
- package/dist/cjs/types.js +7 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +152 -0
- package/dist/client.js.map +1 -0
- package/dist/creative.d.ts +25 -0
- package/dist/creative.d.ts.map +1 -0
- package/dist/creative.js +42 -0
- package/dist/creative.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -0
- package/dist/hmac.d.ts +8 -0
- package/dist/hmac.d.ts.map +1 -0
- package/dist/hmac.js +43 -0
- package/dist/hmac.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/render.d.ts +7 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +63 -0
- package/dist/render.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -4
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Decision, MeridianAdsOptions, RequestOptions, TraitMap } from "./types.js";
|
|
2
|
+
export declare class MeridianAds {
|
|
3
|
+
#private;
|
|
4
|
+
readonly appKey: string;
|
|
5
|
+
readonly baseUrl: string;
|
|
6
|
+
constructor(options: MeridianAdsOptions);
|
|
7
|
+
/**
|
|
8
|
+
* Request a fill for a placement. Returns null on HTTP 204 (no paid or house creative).
|
|
9
|
+
* Does not record an impression; call {@link render} or {@link recordImpression} when shown.
|
|
10
|
+
*/
|
|
11
|
+
serve(placement: string, userKey: string, context?: TraitMap, options?: RequestOptions): Promise<Decision | null>;
|
|
12
|
+
/** Fire POST /v1/events with the signed impression token. Call this when the creative is shown. */
|
|
13
|
+
recordImpression(decision: Decision | string, options?: RequestOptions): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Mount the creative into `target` and fire the impression once it is in the DOM.
|
|
16
|
+
* The click target is the decision click_url (adserver 302). Never puts user_key on the link.
|
|
17
|
+
*/
|
|
18
|
+
render(target: HTMLElement, decision: Decision, options?: RequestOptions): Promise<HTMLElement>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACT,MAAM,YAAY,CAAA;AAQnB,qBAAa,WAAW;;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;gBAMZ,OAAO,EAAE,kBAAkB;IAgBvC;;;OAGG;IACG,KAAK,CACT,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAuB3B,mGAAmG;IAC7F,gBAAgB,CACpB,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAC3B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IAgBhB;;;OAGG;IACG,MAAM,CACV,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,WAAW,CAAC;CA6BxB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { mountCreative } from "./render.js";
|
|
2
|
+
import { AuthError, InvalidRequestError, RateLimitError, ServeError, } from "./errors.js";
|
|
3
|
+
import { secretBytes, signRequest, unixSeconds } from "./hmac.js";
|
|
4
|
+
import { DEFAULT_SERVE_URL, DECISION_PATH, EVENTS_PATH, MAX_CONTEXT_BYTES, } from "./types.js";
|
|
5
|
+
export class MeridianAds {
|
|
6
|
+
appKey;
|
|
7
|
+
baseUrl;
|
|
8
|
+
#secret;
|
|
9
|
+
#fetch;
|
|
10
|
+
#now;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
const appKey = options.appKey.trim();
|
|
13
|
+
const secret = options.secret.trim();
|
|
14
|
+
if (!appKey) {
|
|
15
|
+
throw new InvalidRequestError("appKey is required");
|
|
16
|
+
}
|
|
17
|
+
if (!secret) {
|
|
18
|
+
throw new InvalidRequestError("secret is required");
|
|
19
|
+
}
|
|
20
|
+
this.appKey = appKey;
|
|
21
|
+
this.baseUrl = trimSlash(options.baseUrl ?? DEFAULT_SERVE_URL);
|
|
22
|
+
this.#secret = secretBytes(secret);
|
|
23
|
+
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
24
|
+
this.#now = options.now ?? Date.now;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Request a fill for a placement. Returns null on HTTP 204 (no paid or house creative).
|
|
28
|
+
* Does not record an impression; call {@link render} or {@link recordImpression} when shown.
|
|
29
|
+
*/
|
|
30
|
+
async serve(placement, userKey, context, options) {
|
|
31
|
+
const place = placement.trim();
|
|
32
|
+
const user = userKey.trim();
|
|
33
|
+
if (!place || !user) {
|
|
34
|
+
throw new InvalidRequestError("placement and userKey are required");
|
|
35
|
+
}
|
|
36
|
+
const payload = {
|
|
37
|
+
placement: place,
|
|
38
|
+
user_key: user,
|
|
39
|
+
};
|
|
40
|
+
if (context !== undefined) {
|
|
41
|
+
assertContextSize(context);
|
|
42
|
+
payload.context = context;
|
|
43
|
+
}
|
|
44
|
+
const body = JSON.stringify(payload);
|
|
45
|
+
const res = await this.#signedPost(DECISION_PATH, body, options);
|
|
46
|
+
if (res.status === 204) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
await throwIfNotOk(res);
|
|
50
|
+
return (await res.json());
|
|
51
|
+
}
|
|
52
|
+
/** Fire POST /v1/events with the signed impression token. Call this when the creative is shown. */
|
|
53
|
+
async recordImpression(decision, options) {
|
|
54
|
+
const token = typeof decision === "string"
|
|
55
|
+
? decision.trim()
|
|
56
|
+
: decision.creative.impression_token.trim();
|
|
57
|
+
if (!token) {
|
|
58
|
+
throw new InvalidRequestError("impression token is required");
|
|
59
|
+
}
|
|
60
|
+
const body = JSON.stringify({ token, event: "impression" });
|
|
61
|
+
const res = await this.#signedPost(EVENTS_PATH, body, options);
|
|
62
|
+
if (res.status === 204) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await throwIfNotOk(res);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Mount the creative into `target` and fire the impression once it is in the DOM.
|
|
69
|
+
* The click target is the decision click_url (adserver 302). Never puts user_key on the link.
|
|
70
|
+
*/
|
|
71
|
+
async render(target, decision, options) {
|
|
72
|
+
const root = mountCreative(target, decision);
|
|
73
|
+
await this.recordImpression(decision, options);
|
|
74
|
+
return root;
|
|
75
|
+
}
|
|
76
|
+
async #signedPost(path, body, options) {
|
|
77
|
+
const ts = unixSeconds(this.#now());
|
|
78
|
+
const signature = await signRequest(this.#secret, ts, "POST", path, body);
|
|
79
|
+
const headers = {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"X-App-Key": this.appKey,
|
|
82
|
+
"X-Timestamp": ts,
|
|
83
|
+
"X-Signature": signature,
|
|
84
|
+
};
|
|
85
|
+
const init = {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers,
|
|
88
|
+
body,
|
|
89
|
+
};
|
|
90
|
+
if (options?.signal) {
|
|
91
|
+
init.signal = options.signal;
|
|
92
|
+
}
|
|
93
|
+
return this.#fetch(`${this.baseUrl}${path}`, init);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function trimSlash(url) {
|
|
97
|
+
return url.replace(/\/+$/, "");
|
|
98
|
+
}
|
|
99
|
+
function assertContextSize(context) {
|
|
100
|
+
let encoded;
|
|
101
|
+
try {
|
|
102
|
+
encoded = JSON.stringify(context);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
throw new InvalidRequestError(`context is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`);
|
|
106
|
+
}
|
|
107
|
+
if (new TextEncoder().encode(encoded).length > MAX_CONTEXT_BYTES) {
|
|
108
|
+
throw new InvalidRequestError(`context exceeds ${MAX_CONTEXT_BYTES} bytes`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function throwIfNotOk(res) {
|
|
112
|
+
if (res.ok) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const body = await res.text();
|
|
116
|
+
const message = errorMessage(body) || res.statusText || `HTTP ${res.status}`;
|
|
117
|
+
if (res.status === 401) {
|
|
118
|
+
throw new AuthError(message, 401, body);
|
|
119
|
+
}
|
|
120
|
+
if (res.status === 429) {
|
|
121
|
+
const retry = retryAfterSeconds(res);
|
|
122
|
+
throw new RateLimitError(message, retry, body);
|
|
123
|
+
}
|
|
124
|
+
if (res.status === 400) {
|
|
125
|
+
throw new InvalidRequestError(message, 400, body);
|
|
126
|
+
}
|
|
127
|
+
throw new ServeError(message, res.status, body);
|
|
128
|
+
}
|
|
129
|
+
function errorMessage(body) {
|
|
130
|
+
if (!body) {
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(body);
|
|
135
|
+
if (typeof parsed.error === "string") {
|
|
136
|
+
return parsed.error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// not JSON
|
|
141
|
+
}
|
|
142
|
+
return body;
|
|
143
|
+
}
|
|
144
|
+
function retryAfterSeconds(res) {
|
|
145
|
+
const raw = res.headers.get("Retry-After");
|
|
146
|
+
if (!raw) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
const n = Number.parseInt(raw, 10);
|
|
150
|
+
return Number.isFinite(n) ? n : undefined;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Decision, DecisionCreative } from "./types.js";
|
|
2
|
+
/** Click-through URL from a decision. Always the adserver 302 path, never the landing URL. */
|
|
3
|
+
export declare function clickUrl(decision: Decision): string;
|
|
4
|
+
export declare function impressionToken(decision: Decision): string;
|
|
5
|
+
export interface RenderedCreative {
|
|
6
|
+
href: string;
|
|
7
|
+
format: DecisionCreative["format"];
|
|
8
|
+
house: boolean;
|
|
9
|
+
campaignId: string;
|
|
10
|
+
image?: {
|
|
11
|
+
src: string;
|
|
12
|
+
alt: string;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
};
|
|
16
|
+
native?: {
|
|
17
|
+
headline: string;
|
|
18
|
+
body?: string;
|
|
19
|
+
sponsor?: string;
|
|
20
|
+
cta?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Pure description used by the browser renderer. Clicks stay on click_url. */
|
|
24
|
+
export declare function describeCreative(decision: Decision): RenderedCreative;
|
|
25
|
+
//# sourceMappingURL=creative.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative.d.ts","sourceRoot":"","sources":["../src/creative.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE5D,8FAA8F;AAC9F,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEnD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAClC,KAAK,EAAE,OAAO,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,MAAM,CAAC,EAAE;QACP,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,GAAG,CAAC,EAAE,MAAM,CAAA;KACb,CAAA;CACF;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,gBAAgB,CA2BrE"}
|
package/dist/creative.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Click-through URL from a decision. Always the adserver 302 path, never the landing URL. */
|
|
2
|
+
export function clickUrl(decision) {
|
|
3
|
+
return decision.creative.click_url;
|
|
4
|
+
}
|
|
5
|
+
export function impressionToken(decision) {
|
|
6
|
+
return decision.creative.impression_token;
|
|
7
|
+
}
|
|
8
|
+
/** Pure description used by the browser renderer. Clicks stay on click_url. */
|
|
9
|
+
export function describeCreative(decision) {
|
|
10
|
+
const creative = decision.creative;
|
|
11
|
+
const out = {
|
|
12
|
+
href: creative.click_url,
|
|
13
|
+
format: creative.format,
|
|
14
|
+
house: decision.house,
|
|
15
|
+
campaignId: decision.campaign_id,
|
|
16
|
+
};
|
|
17
|
+
if (creative.format === "image" && creative.image) {
|
|
18
|
+
const image = {
|
|
19
|
+
src: creative.image.cdn_url ?? "",
|
|
20
|
+
alt: creative.image.alt,
|
|
21
|
+
};
|
|
22
|
+
if (creative.image.width !== undefined)
|
|
23
|
+
image.width = creative.image.width;
|
|
24
|
+
if (creative.image.height !== undefined)
|
|
25
|
+
image.height = creative.image.height;
|
|
26
|
+
out.image = image;
|
|
27
|
+
}
|
|
28
|
+
if (creative.format === "native_text" && creative.native) {
|
|
29
|
+
const native = {
|
|
30
|
+
headline: creative.native.headline,
|
|
31
|
+
};
|
|
32
|
+
if (creative.native.body !== undefined)
|
|
33
|
+
native.body = creative.native.body;
|
|
34
|
+
if (creative.native.sponsor !== undefined)
|
|
35
|
+
native.sponsor = creative.native.sponsor;
|
|
36
|
+
if (creative.native.cta !== undefined)
|
|
37
|
+
native.cta = creative.native.cta;
|
|
38
|
+
out.native = native;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=creative.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative.js","sourceRoot":"","sources":["../src/creative.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,UAAU,QAAQ,CAAC,QAAkB;IACzC,OAAO,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAkB;IAChD,OAAO,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAA;AAC3C,CAAC;AAgBD,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAA;IAClC,MAAM,GAAG,GAAqB;QAC5B,IAAI,EAAE,QAAQ,CAAC,SAAS;QACxB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,UAAU,EAAE,QAAQ,CAAC,WAAW;KACjC,CAAA;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClD,MAAM,KAAK,GAA2C;YACpD,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE;YACjC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG;SACxB,CAAA;QACD,IAAI,QAAQ,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAA;QAC1E,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAA;QAC7E,GAAG,CAAC,KAAK,GAAG,KAAK,CAAA;IACnB,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACzD,MAAM,MAAM,GAA4C;YACtD,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ;SACnC,CAAA;QACD,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAA;QAC1E,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAA;QACnF,IAAI,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAA;QACvE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAA;IACrB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class ServeError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly body: string;
|
|
4
|
+
constructor(message: string, status: number, body?: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class AuthError extends ServeError {
|
|
7
|
+
constructor(message?: string, status?: number, body?: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class RateLimitError extends ServeError {
|
|
10
|
+
readonly retryAfterSeconds: number | undefined;
|
|
11
|
+
constructor(message?: string, retryAfterSeconds?: number, body?: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class InvalidRequestError extends ServeError {
|
|
14
|
+
constructor(message: string, status?: number, body?: string);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAET,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,SAAK;CAMvD;AAED,qBAAa,SAAU,SAAQ,UAAU;gBAC3B,OAAO,SAAiB,EAAE,MAAM,SAAM,EAAE,IAAI,SAAK;CAI9D;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC5C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAA;gBAElC,OAAO,SAAiB,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,SAAK;CAK5E;AAED,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAM,EAAE,IAAI,SAAK;CAIrD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class ServeError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
body;
|
|
4
|
+
constructor(message, status, body = "") {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "ServeError";
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.body = body;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class AuthError extends ServeError {
|
|
12
|
+
constructor(message = "unauthorized", status = 401, body = "") {
|
|
13
|
+
super(message, status, body);
|
|
14
|
+
this.name = "AuthError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class RateLimitError extends ServeError {
|
|
18
|
+
retryAfterSeconds;
|
|
19
|
+
constructor(message = "rate limited", retryAfterSeconds, body = "") {
|
|
20
|
+
super(message, 429, body);
|
|
21
|
+
this.name = "RateLimitError";
|
|
22
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class InvalidRequestError extends ServeError {
|
|
26
|
+
constructor(message, status = 400, body = "") {
|
|
27
|
+
super(message, status, body);
|
|
28
|
+
this.name = "InvalidRequestError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,MAAM,CAAQ;IACd,IAAI,CAAQ;IAErB,YAAY,OAAe,EAAE,MAAc,EAAE,IAAI,GAAG,EAAE;QACpD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,YAAY,CAAA;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF;AAED,MAAM,OAAO,SAAU,SAAQ,UAAU;IACvC,YAAY,OAAO,GAAG,cAAc,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE;QAC3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAA;IACzB,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,UAAU;IACnC,iBAAiB,CAAoB;IAE9C,YAAY,OAAO,GAAG,cAAc,EAAE,iBAA0B,EAAE,IAAI,GAAG,EAAE;QACzE,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QACzB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;QAC5B,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAA;IAC5C,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,UAAU;IACjD,YAAY,OAAe,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE;QAClD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;QAC5B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF"}
|
package/dist/hmac.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Decode a console hex secret, or UTF-8 bytes for test / raw secrets. */
|
|
2
|
+
export declare function secretBytes(secret: string): Uint8Array;
|
|
3
|
+
/** OpenAPI canonical string: `{unix_timestamp}.{METHOD}.{path}.{raw_body}` */
|
|
4
|
+
export declare function canonicalHMACString(unixTS: string, method: string, path: string, body: string): string;
|
|
5
|
+
/** HMAC-SHA256 hex digest of the canonical serve string. */
|
|
6
|
+
export declare function signRequest(secret: Uint8Array | string, unixTS: string, method: string, path: string, body: string): Promise<string>;
|
|
7
|
+
export declare function unixSeconds(nowMs: number): string;
|
|
8
|
+
//# sourceMappingURL=hmac.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmac.d.ts","sourceRoot":"","sources":["../src/hmac.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUtD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAER;AAkBD,4DAA4D;AAC5D,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
package/dist/hmac.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const HEX = /^[0-9a-fA-F]+$/;
|
|
2
|
+
/** Decode a console hex secret, or UTF-8 bytes for test / raw secrets. */
|
|
3
|
+
export function secretBytes(secret) {
|
|
4
|
+
const trimmed = secret.trim();
|
|
5
|
+
if (trimmed.length >= 2 && trimmed.length % 2 === 0 && HEX.test(trimmed)) {
|
|
6
|
+
const out = new Uint8Array(trimmed.length / 2);
|
|
7
|
+
for (let i = 0; i < out.length; i++) {
|
|
8
|
+
out[i] = Number.parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
return new TextEncoder().encode(trimmed);
|
|
13
|
+
}
|
|
14
|
+
/** OpenAPI canonical string: `{unix_timestamp}.{METHOD}.{path}.{raw_body}` */
|
|
15
|
+
export function canonicalHMACString(unixTS, method, path, body) {
|
|
16
|
+
return `${unixTS}.${method}.${path}.${body}`;
|
|
17
|
+
}
|
|
18
|
+
function bytesToHex(bytes) {
|
|
19
|
+
let hex = "";
|
|
20
|
+
for (const b of bytes) {
|
|
21
|
+
hex += b.toString(16).padStart(2, "0");
|
|
22
|
+
}
|
|
23
|
+
return hex;
|
|
24
|
+
}
|
|
25
|
+
function requireSubtle() {
|
|
26
|
+
const subtle = globalThis.crypto?.subtle;
|
|
27
|
+
if (!subtle) {
|
|
28
|
+
throw new Error("Web Crypto is required (Node.js 18+ or a modern browser)");
|
|
29
|
+
}
|
|
30
|
+
return subtle;
|
|
31
|
+
}
|
|
32
|
+
/** HMAC-SHA256 hex digest of the canonical serve string. */
|
|
33
|
+
export async function signRequest(secret, unixTS, method, path, body) {
|
|
34
|
+
const keyBytes = typeof secret === "string" ? secretBytes(secret) : secret;
|
|
35
|
+
const subtle = requireSubtle();
|
|
36
|
+
const key = await subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
37
|
+
const sig = await subtle.sign("HMAC", key, new TextEncoder().encode(canonicalHMACString(unixTS, method, path, body)));
|
|
38
|
+
return bytesToHex(new Uint8Array(sig));
|
|
39
|
+
}
|
|
40
|
+
export function unixSeconds(nowMs) {
|
|
41
|
+
return String(Math.floor(nowMs / 1000));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=hmac.js.map
|
package/dist/hmac.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmac.js","sourceRoot":"","sources":["../src/hmac.ts"],"names":[],"mappings":"AAAA,MAAM,GAAG,GAAG,gBAAgB,CAAA;AAE5B,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;IAC7B,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACzE,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QAC/D,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;AAC1C,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB,CACjC,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,OAAO,GAAG,MAAM,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,EAAE,CAAA;AAC9C,CAAC;AAED,SAAS,UAAU,CAAC,KAAiB;IACnC,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAA2B,EAC3B,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC1E,MAAM,MAAM,GAAG,aAAa,EAAE,CAAA;IAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,SAAS,CAChC,KAAK,EACL,QAAwB,EACxB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAA;IACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAC3B,MAAM,EACN,GAAG,EACH,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAC1E,CAAA;IACD,OAAO,UAAU,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAA;AACzC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { MeridianAds } from "./client.js";
|
|
2
|
+
export { clickUrl, describeCreative, impressionToken, } from "./creative.js";
|
|
3
|
+
export { AuthError, InvalidRequestError, RateLimitError, ServeError, } from "./errors.js";
|
|
4
|
+
export { canonicalHMACString, secretBytes, signRequest } from "./hmac.js";
|
|
5
|
+
export { mountCreative } from "./render.js";
|
|
6
|
+
export type { CreativeFormat, Decision, DecisionCreative, ImageAsset, MeridianAdsOptions, NativeCopy, RequestOptions, TraitMap, } from "./types.js";
|
|
7
|
+
export type { RenderedCreative } from "./creative.js";
|
|
8
|
+
export { DEFAULT_SERVE_URL, DECISION_PATH, EVENTS_PATH, MAX_CONTEXT_BYTES, } from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,cAAc,EACd,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EACV,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,QAAQ,GACT,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { MeridianAds } from "./client.js";
|
|
2
|
+
export { clickUrl, describeCreative, impressionToken, } from "./creative.js";
|
|
3
|
+
export { AuthError, InvalidRequestError, RateLimitError, ServeError, } from "./errors.js";
|
|
4
|
+
export { canonicalHMACString, secretBytes, signRequest } from "./hmac.js";
|
|
5
|
+
export { mountCreative } from "./render.js";
|
|
6
|
+
export { DEFAULT_SERVE_URL, DECISION_PATH, EVENTS_PATH, MAX_CONTEXT_BYTES, } from "./types.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,cAAc,EACd,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAY3C,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Decision } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Insert a banner or native unit into `target`.
|
|
4
|
+
* Clicks use the decision click_url so the adserver can 302 to the landing page.
|
|
5
|
+
*/
|
|
6
|
+
export declare function mountCreative(target: HTMLElement, decision: Decision): HTMLElement;
|
|
7
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI1C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,GAAG,WAAW,CAwDlF"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describeCreative } from "./creative.js";
|
|
2
|
+
const ROOT_CLASS = "meridian-ads";
|
|
3
|
+
/**
|
|
4
|
+
* Insert a banner or native unit into `target`.
|
|
5
|
+
* Clicks use the decision click_url so the adserver can 302 to the landing page.
|
|
6
|
+
*/
|
|
7
|
+
export function mountCreative(target, decision) {
|
|
8
|
+
if (typeof document === "undefined") {
|
|
9
|
+
throw new Error("render() requires a browser document");
|
|
10
|
+
}
|
|
11
|
+
const spec = describeCreative(decision);
|
|
12
|
+
const anchor = document.createElement("a");
|
|
13
|
+
anchor.className = ROOT_CLASS;
|
|
14
|
+
anchor.href = spec.href;
|
|
15
|
+
anchor.rel = "noopener noreferrer";
|
|
16
|
+
anchor.target = "_blank";
|
|
17
|
+
anchor.dataset.format = spec.format;
|
|
18
|
+
anchor.dataset.house = spec.house ? "true" : "false";
|
|
19
|
+
anchor.dataset.campaignId = spec.campaignId;
|
|
20
|
+
if (spec.image) {
|
|
21
|
+
const img = document.createElement("img");
|
|
22
|
+
img.className = `${ROOT_CLASS}__image`;
|
|
23
|
+
img.src = spec.image.src;
|
|
24
|
+
img.alt = spec.image.alt;
|
|
25
|
+
if (spec.image.width) {
|
|
26
|
+
img.width = spec.image.width;
|
|
27
|
+
}
|
|
28
|
+
if (spec.image.height) {
|
|
29
|
+
img.height = spec.image.height;
|
|
30
|
+
}
|
|
31
|
+
anchor.append(img);
|
|
32
|
+
}
|
|
33
|
+
else if (spec.native) {
|
|
34
|
+
const unit = document.createElement("span");
|
|
35
|
+
unit.className = `${ROOT_CLASS}__native`;
|
|
36
|
+
if (spec.native.sponsor) {
|
|
37
|
+
const sponsor = document.createElement("span");
|
|
38
|
+
sponsor.className = `${ROOT_CLASS}__sponsor`;
|
|
39
|
+
sponsor.textContent = spec.native.sponsor;
|
|
40
|
+
unit.append(sponsor);
|
|
41
|
+
}
|
|
42
|
+
const headline = document.createElement("span");
|
|
43
|
+
headline.className = `${ROOT_CLASS}__headline`;
|
|
44
|
+
headline.textContent = spec.native.headline;
|
|
45
|
+
unit.append(headline);
|
|
46
|
+
if (spec.native.body) {
|
|
47
|
+
const body = document.createElement("span");
|
|
48
|
+
body.className = `${ROOT_CLASS}__body`;
|
|
49
|
+
body.textContent = spec.native.body;
|
|
50
|
+
unit.append(body);
|
|
51
|
+
}
|
|
52
|
+
if (spec.native.cta) {
|
|
53
|
+
const cta = document.createElement("span");
|
|
54
|
+
cta.className = `${ROOT_CLASS}__cta`;
|
|
55
|
+
cta.textContent = spec.native.cta;
|
|
56
|
+
unit.append(cta);
|
|
57
|
+
}
|
|
58
|
+
anchor.append(unit);
|
|
59
|
+
}
|
|
60
|
+
target.replaceChildren(anchor);
|
|
61
|
+
return anchor;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Trait map merged into the user profile (last-write-wins per key). Max 8KB JSON. */
|
|
2
|
+
export type TraitMap = Record<string, unknown>;
|
|
3
|
+
export type CreativeFormat = "image" | "native_text";
|
|
4
|
+
export interface ImageAsset {
|
|
5
|
+
object_key: string;
|
|
6
|
+
cdn_url?: string;
|
|
7
|
+
alt: string;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
mime: "image/jpeg" | "image/png" | "image/webp" | string;
|
|
11
|
+
bytes?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface NativeCopy {
|
|
14
|
+
headline: string;
|
|
15
|
+
body?: string;
|
|
16
|
+
sponsor?: string;
|
|
17
|
+
cta?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DecisionCreative {
|
|
20
|
+
id: string;
|
|
21
|
+
format: CreativeFormat;
|
|
22
|
+
image?: ImageAsset;
|
|
23
|
+
native?: NativeCopy;
|
|
24
|
+
/** GET this URL; the adserver records the click and 302s to the landing page. */
|
|
25
|
+
click_url: string;
|
|
26
|
+
impression_token: string;
|
|
27
|
+
}
|
|
28
|
+
/** Filled serve result. HTTP 204 maps to null in the SDK. */
|
|
29
|
+
export interface Decision {
|
|
30
|
+
campaign_id: string;
|
|
31
|
+
creative: DecisionCreative;
|
|
32
|
+
house: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RequestOptions {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
export interface MeridianAdsOptions {
|
|
38
|
+
/** Public app key prefix sent as X-App-Key (for example mk_live_...). */
|
|
39
|
+
appKey: string;
|
|
40
|
+
/**
|
|
41
|
+
* HMAC secret shown once when the key is created.
|
|
42
|
+
* Console values are hex; other strings are signed as UTF-8 bytes.
|
|
43
|
+
*/
|
|
44
|
+
secret: string;
|
|
45
|
+
/** Serve origin. Defaults to https://serve.meridianads.tech. */
|
|
46
|
+
baseUrl?: string;
|
|
47
|
+
fetch?: typeof fetch;
|
|
48
|
+
/** Unix-ms clock. Injected in tests. */
|
|
49
|
+
now?: () => number;
|
|
50
|
+
}
|
|
51
|
+
export declare const DEFAULT_SERVE_URL = "https://serve.meridianads.tech";
|
|
52
|
+
export declare const MAX_CONTEXT_BYTES: number;
|
|
53
|
+
export declare const DECISION_PATH = "/v1/decision";
|
|
54
|
+
export declare const EVENTS_PATH = "/v1/events";
|
|
55
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE9C,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,aAAa,CAAA;AAEpD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,MAAM,CAAA;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,iBAAiB,mCAAmC,CAAA;AACjE,eAAO,MAAM,iBAAiB,QAAW,CAAA;AACzC,eAAO,MAAM,aAAa,iBAAiB,CAAA;AAC3C,eAAO,MAAM,WAAW,eAAe,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0DA,MAAM,CAAC,MAAM,iBAAiB,GAAG,gCAAgC,CAAA;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,CAAA;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAA;AAC3C,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yellesoftware/meridian-ads",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Meridian Ads serving SDK for Node.js and browsers. ESM and CommonJS. Product of Yelle Software.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/cjs/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/cjs/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json && node ./scripts/emit-cjs.mjs",
|
|
27
|
+
"test:entries": "node --test test/package-entry.test.mjs",
|
|
28
|
+
"test": "tsx --test test/hmac.test.ts test/client.test.ts test/creative.test.ts",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"meridian-ads",
|
|
33
|
+
"ads",
|
|
34
|
+
"sdk",
|
|
35
|
+
"yelle-software"
|
|
36
|
+
],
|
|
37
|
+
"author": "Yelle Software",
|
|
38
|
+
"homepage": "https://meridianads.tech",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://git.yelle.software/yelle/saas/meridian-ads.git",
|
|
42
|
+
"directory": "sdks/typescript"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24.3.0",
|
|
50
|
+
"tsx": "^4.20.5",
|
|
51
|
+
"typescript": "^5.9.3"
|
|
52
|
+
}
|
|
53
|
+
}
|