@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yelle Software
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,188 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @yellesoftware/meridian-ads
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Serving SDK for **Meridian Ads** by **Yelle Software**. ESM and CommonJS for Node.js 18+. Typed callers can render in the browser.
|
|
4
|
+
|
|
5
|
+
Request a fill with `serve(placement, userKey, context)`, record an impression when the creative is shown, and send clicks through the adserver 302 URL. Do not open the advertiser landing page directly.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Node.js 18+. The package is on the public npm registry. Maintainers publish with `just publish-sdk-typescript` from the monorepo root after bumping `package.json` and merging.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @yellesoftware/meridian-ads
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
pnpm: `pnpm add @yellesoftware/meridian-ads`. Yarn: `yarn add @yellesoftware/meridian-ads`.
|
|
16
|
+
|
|
17
|
+
Until that version is on npm, install from the monorepo path:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @yellesoftware/meridian-ads@file:../path/to/meridian-ads/sdks/typescript
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
Create a production app key in the Meridian Ads console. The public prefix is `appKey` (`mk_live_...`). The HMAC secret is shown once.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { MeridianAds } from "@yellesoftware/meridian-ads"
|
|
29
|
+
|
|
30
|
+
const ads = new MeridianAds({
|
|
31
|
+
appKey: process.env.MERIDIAN_ADS_APP_KEY!,
|
|
32
|
+
secret: process.env.MERIDIAN_ADS_SECRET!,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const decision = await ads.serve("home", "user-123", {
|
|
36
|
+
plan: "pro",
|
|
37
|
+
country: "US",
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
if (!decision) {
|
|
41
|
+
// HTTP 204: no paid or house creative eligible
|
|
42
|
+
} else {
|
|
43
|
+
await ads.recordImpression(decision)
|
|
44
|
+
// Clicks: decision.creative.click_url (GET, 302 to landing)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Browser render
|
|
49
|
+
|
|
50
|
+
`render` mounts the banner or native unit and fires the impression once it is in the DOM. The link href is always `/v1/click/{token}`. `user_key` is never placed on the click URL.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const slot = document.getElementById("ad-slot")
|
|
54
|
+
const decision = await ads.serve("sidebar", visitorId, { locale: "en" })
|
|
55
|
+
if (decision && slot) {
|
|
56
|
+
await ads.render(slot, decision)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## JavaScript
|
|
61
|
+
|
|
62
|
+
Plain JavaScript calls the same package from a Node server. Keep `MERIDIAN_ADS_SECRET` in that process. The browser only receives the creative and `click_url`.
|
|
63
|
+
|
|
64
|
+
`server.mjs` (or any file in a package with `"type": "module"`):
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import http from "node:http"
|
|
68
|
+
import { MeridianAds } from "@yellesoftware/meridian-ads"
|
|
69
|
+
|
|
70
|
+
const ads = new MeridianAds({
|
|
71
|
+
appKey: process.env.MERIDIAN_ADS_APP_KEY,
|
|
72
|
+
secret: process.env.MERIDIAN_ADS_SECRET,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const server = http.createServer(async (req, res) => {
|
|
76
|
+
if (req.method !== "GET" || req.url !== "/api/ad") {
|
|
77
|
+
res.writeHead(404)
|
|
78
|
+
res.end()
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const decision = await ads.serve("home", "user-123", {
|
|
83
|
+
plan: "pro",
|
|
84
|
+
country: "US",
|
|
85
|
+
})
|
|
86
|
+
if (!decision) {
|
|
87
|
+
res.writeHead(204)
|
|
88
|
+
res.end()
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await ads.recordImpression(decision)
|
|
93
|
+
const creative = decision.creative
|
|
94
|
+
res.writeHead(200, { "Content-Type": "application/json" })
|
|
95
|
+
res.end(JSON.stringify({
|
|
96
|
+
house: decision.house,
|
|
97
|
+
campaign_id: decision.campaign_id,
|
|
98
|
+
format: creative.format,
|
|
99
|
+
image: creative.image ?? null,
|
|
100
|
+
native: creative.native ?? null,
|
|
101
|
+
click_url: creative.click_url,
|
|
102
|
+
}))
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
server.listen(3000)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
CommonJS (`require`, the Node default when `"type"` is omitted):
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
const http = require("node:http")
|
|
112
|
+
const { MeridianAds } = require("@yellesoftware/meridian-ads")
|
|
113
|
+
|
|
114
|
+
const ads = new MeridianAds({
|
|
115
|
+
appKey: process.env.MERIDIAN_ADS_APP_KEY,
|
|
116
|
+
secret: process.env.MERIDIAN_ADS_SECRET,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const server = http.createServer(async (req, res) => {
|
|
120
|
+
if (req.method !== "GET" || req.url !== "/api/ad") {
|
|
121
|
+
res.writeHead(404)
|
|
122
|
+
res.end()
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const decision = await ads.serve("home", "user-123", {
|
|
127
|
+
plan: "pro",
|
|
128
|
+
country: "US",
|
|
129
|
+
})
|
|
130
|
+
if (!decision) {
|
|
131
|
+
res.writeHead(204)
|
|
132
|
+
res.end()
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await ads.recordImpression(decision)
|
|
137
|
+
const creative = decision.creative
|
|
138
|
+
res.writeHead(200, { "Content-Type": "application/json" })
|
|
139
|
+
res.end(JSON.stringify({
|
|
140
|
+
house: decision.house,
|
|
141
|
+
campaign_id: decision.campaign_id,
|
|
142
|
+
format: creative.format,
|
|
143
|
+
image: creative.image ?? null,
|
|
144
|
+
native: creative.native ?? null,
|
|
145
|
+
click_url: creative.click_url,
|
|
146
|
+
}))
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
server.listen(3000)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The page fetches `/api/ad` and uses `click_url` as the link. It does not import the SDK and it does not see the secret. `user_key` stays on the server. The impression is counted when the server handles `/api/ad`, before the page paints.
|
|
153
|
+
|
|
154
|
+
## Auth
|
|
155
|
+
|
|
156
|
+
`POST /v1/decision` and `POST /v1/events` require HMAC-SHA256:
|
|
157
|
+
|
|
158
|
+
- Canonical string: `{unix_timestamp}.{METHOD}.{path}.{raw_body}`
|
|
159
|
+
- Headers: `X-App-Key`, `X-Timestamp` (unix seconds), `X-Signature` (hex digest)
|
|
160
|
+
|
|
161
|
+
`GET /v1/click/{token}` has no HMAC. The token is bound to a prior impression.
|
|
162
|
+
|
|
163
|
+
## Serve contract
|
|
164
|
+
|
|
165
|
+
| Call | HTTP | Notes |
|
|
166
|
+
|------|------|--------|
|
|
167
|
+
| `serve(placement, userKey, context?)` | `POST /v1/decision` | Fill or `null` (204). Does not count an impression. |
|
|
168
|
+
| `recordImpression(decision)` | `POST /v1/events` | Fire on render, not on decision. Not IAB viewability. |
|
|
169
|
+
| `render(element, decision)` | events + DOM | Impression after mount. Click uses `click_url`. |
|
|
170
|
+
|
|
171
|
+
House ads (`decision.house === true`) are first-party Yelle Software / Meridian Ads fallbacks. They do not spend advertiser credits.
|
|
172
|
+
|
|
173
|
+
Rate limit: 100 decisions per second per app key (HTTP 429, `RateLimitError`).
|
|
174
|
+
|
|
175
|
+
Context JSON max 8KB. Missing traits fail closed on the server.
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
cd sdks/typescript
|
|
181
|
+
pnpm install
|
|
182
|
+
pnpm test
|
|
183
|
+
pnpm typecheck
|
|
184
|
+
pnpm build
|
|
185
|
+
pnpm test:entries
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
License: MIT.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MeridianAds = void 0;
|
|
4
|
+
const render_js_1 = require("./render.js");
|
|
5
|
+
const errors_js_1 = require("./errors.js");
|
|
6
|
+
const hmac_js_1 = require("./hmac.js");
|
|
7
|
+
const types_js_1 = require("./types.js");
|
|
8
|
+
class MeridianAds {
|
|
9
|
+
appKey;
|
|
10
|
+
baseUrl;
|
|
11
|
+
#secret;
|
|
12
|
+
#fetch;
|
|
13
|
+
#now;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
const appKey = options.appKey.trim();
|
|
16
|
+
const secret = options.secret.trim();
|
|
17
|
+
if (!appKey) {
|
|
18
|
+
throw new errors_js_1.InvalidRequestError("appKey is required");
|
|
19
|
+
}
|
|
20
|
+
if (!secret) {
|
|
21
|
+
throw new errors_js_1.InvalidRequestError("secret is required");
|
|
22
|
+
}
|
|
23
|
+
this.appKey = appKey;
|
|
24
|
+
this.baseUrl = trimSlash(options.baseUrl ?? types_js_1.DEFAULT_SERVE_URL);
|
|
25
|
+
this.#secret = (0, hmac_js_1.secretBytes)(secret);
|
|
26
|
+
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
27
|
+
this.#now = options.now ?? Date.now;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Request a fill for a placement. Returns null on HTTP 204 (no paid or house creative).
|
|
31
|
+
* Does not record an impression; call {@link render} or {@link recordImpression} when shown.
|
|
32
|
+
*/
|
|
33
|
+
async serve(placement, userKey, context, options) {
|
|
34
|
+
const place = placement.trim();
|
|
35
|
+
const user = userKey.trim();
|
|
36
|
+
if (!place || !user) {
|
|
37
|
+
throw new errors_js_1.InvalidRequestError("placement and userKey are required");
|
|
38
|
+
}
|
|
39
|
+
const payload = {
|
|
40
|
+
placement: place,
|
|
41
|
+
user_key: user,
|
|
42
|
+
};
|
|
43
|
+
if (context !== undefined) {
|
|
44
|
+
assertContextSize(context);
|
|
45
|
+
payload.context = context;
|
|
46
|
+
}
|
|
47
|
+
const body = JSON.stringify(payload);
|
|
48
|
+
const res = await this.#signedPost(types_js_1.DECISION_PATH, body, options);
|
|
49
|
+
if (res.status === 204) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
await throwIfNotOk(res);
|
|
53
|
+
return (await res.json());
|
|
54
|
+
}
|
|
55
|
+
/** Fire POST /v1/events with the signed impression token. Call this when the creative is shown. */
|
|
56
|
+
async recordImpression(decision, options) {
|
|
57
|
+
const token = typeof decision === "string"
|
|
58
|
+
? decision.trim()
|
|
59
|
+
: decision.creative.impression_token.trim();
|
|
60
|
+
if (!token) {
|
|
61
|
+
throw new errors_js_1.InvalidRequestError("impression token is required");
|
|
62
|
+
}
|
|
63
|
+
const body = JSON.stringify({ token, event: "impression" });
|
|
64
|
+
const res = await this.#signedPost(types_js_1.EVENTS_PATH, body, options);
|
|
65
|
+
if (res.status === 204) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
await throwIfNotOk(res);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Mount the creative into `target` and fire the impression once it is in the DOM.
|
|
72
|
+
* The click target is the decision click_url (adserver 302). Never puts user_key on the link.
|
|
73
|
+
*/
|
|
74
|
+
async render(target, decision, options) {
|
|
75
|
+
const root = (0, render_js_1.mountCreative)(target, decision);
|
|
76
|
+
await this.recordImpression(decision, options);
|
|
77
|
+
return root;
|
|
78
|
+
}
|
|
79
|
+
async #signedPost(path, body, options) {
|
|
80
|
+
const ts = (0, hmac_js_1.unixSeconds)(this.#now());
|
|
81
|
+
const signature = await (0, hmac_js_1.signRequest)(this.#secret, ts, "POST", path, body);
|
|
82
|
+
const headers = {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"X-App-Key": this.appKey,
|
|
85
|
+
"X-Timestamp": ts,
|
|
86
|
+
"X-Signature": signature,
|
|
87
|
+
};
|
|
88
|
+
const init = {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers,
|
|
91
|
+
body,
|
|
92
|
+
};
|
|
93
|
+
if (options?.signal) {
|
|
94
|
+
init.signal = options.signal;
|
|
95
|
+
}
|
|
96
|
+
return this.#fetch(`${this.baseUrl}${path}`, init);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.MeridianAds = MeridianAds;
|
|
100
|
+
function trimSlash(url) {
|
|
101
|
+
return url.replace(/\/+$/, "");
|
|
102
|
+
}
|
|
103
|
+
function assertContextSize(context) {
|
|
104
|
+
let encoded;
|
|
105
|
+
try {
|
|
106
|
+
encoded = JSON.stringify(context);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
throw new errors_js_1.InvalidRequestError(`context is not JSON-serializable: ${err instanceof Error ? err.message : String(err)}`);
|
|
110
|
+
}
|
|
111
|
+
if (new TextEncoder().encode(encoded).length > types_js_1.MAX_CONTEXT_BYTES) {
|
|
112
|
+
throw new errors_js_1.InvalidRequestError(`context exceeds ${types_js_1.MAX_CONTEXT_BYTES} bytes`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function throwIfNotOk(res) {
|
|
116
|
+
if (res.ok) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const body = await res.text();
|
|
120
|
+
const message = errorMessage(body) || res.statusText || `HTTP ${res.status}`;
|
|
121
|
+
if (res.status === 401) {
|
|
122
|
+
throw new errors_js_1.AuthError(message, 401, body);
|
|
123
|
+
}
|
|
124
|
+
if (res.status === 429) {
|
|
125
|
+
const retry = retryAfterSeconds(res);
|
|
126
|
+
throw new errors_js_1.RateLimitError(message, retry, body);
|
|
127
|
+
}
|
|
128
|
+
if (res.status === 400) {
|
|
129
|
+
throw new errors_js_1.InvalidRequestError(message, 400, body);
|
|
130
|
+
}
|
|
131
|
+
throw new errors_js_1.ServeError(message, res.status, body);
|
|
132
|
+
}
|
|
133
|
+
function errorMessage(body) {
|
|
134
|
+
if (!body) {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(body);
|
|
139
|
+
if (typeof parsed.error === "string") {
|
|
140
|
+
return parsed.error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// not JSON
|
|
145
|
+
}
|
|
146
|
+
return body;
|
|
147
|
+
}
|
|
148
|
+
function retryAfterSeconds(res) {
|
|
149
|
+
const raw = res.headers.get("Retry-After");
|
|
150
|
+
if (!raw) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const n = Number.parseInt(raw, 10);
|
|
154
|
+
return Number.isFinite(n) ? n : undefined;
|
|
155
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clickUrl = clickUrl;
|
|
4
|
+
exports.impressionToken = impressionToken;
|
|
5
|
+
exports.describeCreative = describeCreative;
|
|
6
|
+
/** Click-through URL from a decision. Always the adserver 302 path, never the landing URL. */
|
|
7
|
+
function clickUrl(decision) {
|
|
8
|
+
return decision.creative.click_url;
|
|
9
|
+
}
|
|
10
|
+
function impressionToken(decision) {
|
|
11
|
+
return decision.creative.impression_token;
|
|
12
|
+
}
|
|
13
|
+
/** Pure description used by the browser renderer. Clicks stay on click_url. */
|
|
14
|
+
function describeCreative(decision) {
|
|
15
|
+
const creative = decision.creative;
|
|
16
|
+
const out = {
|
|
17
|
+
href: creative.click_url,
|
|
18
|
+
format: creative.format,
|
|
19
|
+
house: decision.house,
|
|
20
|
+
campaignId: decision.campaign_id,
|
|
21
|
+
};
|
|
22
|
+
if (creative.format === "image" && creative.image) {
|
|
23
|
+
const image = {
|
|
24
|
+
src: creative.image.cdn_url ?? "",
|
|
25
|
+
alt: creative.image.alt,
|
|
26
|
+
};
|
|
27
|
+
if (creative.image.width !== undefined)
|
|
28
|
+
image.width = creative.image.width;
|
|
29
|
+
if (creative.image.height !== undefined)
|
|
30
|
+
image.height = creative.image.height;
|
|
31
|
+
out.image = image;
|
|
32
|
+
}
|
|
33
|
+
if (creative.format === "native_text" && creative.native) {
|
|
34
|
+
const native = {
|
|
35
|
+
headline: creative.native.headline,
|
|
36
|
+
};
|
|
37
|
+
if (creative.native.body !== undefined)
|
|
38
|
+
native.body = creative.native.body;
|
|
39
|
+
if (creative.native.sponsor !== undefined)
|
|
40
|
+
native.sponsor = creative.native.sponsor;
|
|
41
|
+
if (creative.native.cta !== undefined)
|
|
42
|
+
native.cta = creative.native.cta;
|
|
43
|
+
out.native = native;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
@@ -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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidRequestError = exports.RateLimitError = exports.AuthError = exports.ServeError = void 0;
|
|
4
|
+
class ServeError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
body;
|
|
7
|
+
constructor(message, status, body = "") {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "ServeError";
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.body = body;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.ServeError = ServeError;
|
|
15
|
+
class AuthError extends ServeError {
|
|
16
|
+
constructor(message = "unauthorized", status = 401, body = "") {
|
|
17
|
+
super(message, status, body);
|
|
18
|
+
this.name = "AuthError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.AuthError = AuthError;
|
|
22
|
+
class RateLimitError extends ServeError {
|
|
23
|
+
retryAfterSeconds;
|
|
24
|
+
constructor(message = "rate limited", retryAfterSeconds, body = "") {
|
|
25
|
+
super(message, 429, body);
|
|
26
|
+
this.name = "RateLimitError";
|
|
27
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.RateLimitError = RateLimitError;
|
|
31
|
+
class InvalidRequestError extends ServeError {
|
|
32
|
+
constructor(message, status = 400, body = "") {
|
|
33
|
+
super(message, status, body);
|
|
34
|
+
this.name = "InvalidRequestError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.InvalidRequestError = InvalidRequestError;
|
|
@@ -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/cjs/hmac.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.secretBytes = secretBytes;
|
|
4
|
+
exports.canonicalHMACString = canonicalHMACString;
|
|
5
|
+
exports.signRequest = signRequest;
|
|
6
|
+
exports.unixSeconds = unixSeconds;
|
|
7
|
+
const HEX = /^[0-9a-fA-F]+$/;
|
|
8
|
+
/** Decode a console hex secret, or UTF-8 bytes for test / raw secrets. */
|
|
9
|
+
function secretBytes(secret) {
|
|
10
|
+
const trimmed = secret.trim();
|
|
11
|
+
if (trimmed.length >= 2 && trimmed.length % 2 === 0 && HEX.test(trimmed)) {
|
|
12
|
+
const out = new Uint8Array(trimmed.length / 2);
|
|
13
|
+
for (let i = 0; i < out.length; i++) {
|
|
14
|
+
out[i] = Number.parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
return new TextEncoder().encode(trimmed);
|
|
19
|
+
}
|
|
20
|
+
/** OpenAPI canonical string: `{unix_timestamp}.{METHOD}.{path}.{raw_body}` */
|
|
21
|
+
function canonicalHMACString(unixTS, method, path, body) {
|
|
22
|
+
return `${unixTS}.${method}.${path}.${body}`;
|
|
23
|
+
}
|
|
24
|
+
function bytesToHex(bytes) {
|
|
25
|
+
let hex = "";
|
|
26
|
+
for (const b of bytes) {
|
|
27
|
+
hex += b.toString(16).padStart(2, "0");
|
|
28
|
+
}
|
|
29
|
+
return hex;
|
|
30
|
+
}
|
|
31
|
+
function requireSubtle() {
|
|
32
|
+
const subtle = globalThis.crypto?.subtle;
|
|
33
|
+
if (!subtle) {
|
|
34
|
+
throw new Error("Web Crypto is required (Node.js 18+ or a modern browser)");
|
|
35
|
+
}
|
|
36
|
+
return subtle;
|
|
37
|
+
}
|
|
38
|
+
/** HMAC-SHA256 hex digest of the canonical serve string. */
|
|
39
|
+
async function signRequest(secret, unixTS, method, path, body) {
|
|
40
|
+
const keyBytes = typeof secret === "string" ? secretBytes(secret) : secret;
|
|
41
|
+
const subtle = requireSubtle();
|
|
42
|
+
const key = await subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
43
|
+
const sig = await subtle.sign("HMAC", key, new TextEncoder().encode(canonicalHMACString(unixTS, method, path, body)));
|
|
44
|
+
return bytesToHex(new Uint8Array(sig));
|
|
45
|
+
}
|
|
46
|
+
function unixSeconds(nowMs) {
|
|
47
|
+
return String(Math.floor(nowMs / 1000));
|
|
48
|
+
}
|
|
@@ -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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_CONTEXT_BYTES = exports.EVENTS_PATH = exports.DECISION_PATH = exports.DEFAULT_SERVE_URL = exports.mountCreative = exports.signRequest = exports.secretBytes = exports.canonicalHMACString = exports.ServeError = exports.RateLimitError = exports.InvalidRequestError = exports.AuthError = exports.impressionToken = exports.describeCreative = exports.clickUrl = exports.MeridianAds = void 0;
|
|
4
|
+
var client_js_1 = require("./client.js");
|
|
5
|
+
Object.defineProperty(exports, "MeridianAds", { enumerable: true, get: function () { return client_js_1.MeridianAds; } });
|
|
6
|
+
var creative_js_1 = require("./creative.js");
|
|
7
|
+
Object.defineProperty(exports, "clickUrl", { enumerable: true, get: function () { return creative_js_1.clickUrl; } });
|
|
8
|
+
Object.defineProperty(exports, "describeCreative", { enumerable: true, get: function () { return creative_js_1.describeCreative; } });
|
|
9
|
+
Object.defineProperty(exports, "impressionToken", { enumerable: true, get: function () { return creative_js_1.impressionToken; } });
|
|
10
|
+
var errors_js_1 = require("./errors.js");
|
|
11
|
+
Object.defineProperty(exports, "AuthError", { enumerable: true, get: function () { return errors_js_1.AuthError; } });
|
|
12
|
+
Object.defineProperty(exports, "InvalidRequestError", { enumerable: true, get: function () { return errors_js_1.InvalidRequestError; } });
|
|
13
|
+
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return errors_js_1.RateLimitError; } });
|
|
14
|
+
Object.defineProperty(exports, "ServeError", { enumerable: true, get: function () { return errors_js_1.ServeError; } });
|
|
15
|
+
var hmac_js_1 = require("./hmac.js");
|
|
16
|
+
Object.defineProperty(exports, "canonicalHMACString", { enumerable: true, get: function () { return hmac_js_1.canonicalHMACString; } });
|
|
17
|
+
Object.defineProperty(exports, "secretBytes", { enumerable: true, get: function () { return hmac_js_1.secretBytes; } });
|
|
18
|
+
Object.defineProperty(exports, "signRequest", { enumerable: true, get: function () { return hmac_js_1.signRequest; } });
|
|
19
|
+
var render_js_1 = require("./render.js");
|
|
20
|
+
Object.defineProperty(exports, "mountCreative", { enumerable: true, get: function () { return render_js_1.mountCreative; } });
|
|
21
|
+
var types_js_1 = require("./types.js");
|
|
22
|
+
Object.defineProperty(exports, "DEFAULT_SERVE_URL", { enumerable: true, get: function () { return types_js_1.DEFAULT_SERVE_URL; } });
|
|
23
|
+
Object.defineProperty(exports, "DECISION_PATH", { enumerable: true, get: function () { return types_js_1.DECISION_PATH; } });
|
|
24
|
+
Object.defineProperty(exports, "EVENTS_PATH", { enumerable: true, get: function () { return types_js_1.EVENTS_PATH; } });
|
|
25
|
+
Object.defineProperty(exports, "MAX_CONTEXT_BYTES", { enumerable: true, get: function () { return types_js_1.MAX_CONTEXT_BYTES; } });
|
|
@@ -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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mountCreative = mountCreative;
|
|
4
|
+
const creative_js_1 = require("./creative.js");
|
|
5
|
+
const ROOT_CLASS = "meridian-ads";
|
|
6
|
+
/**
|
|
7
|
+
* Insert a banner or native unit into `target`.
|
|
8
|
+
* Clicks use the decision click_url so the adserver can 302 to the landing page.
|
|
9
|
+
*/
|
|
10
|
+
function mountCreative(target, decision) {
|
|
11
|
+
if (typeof document === "undefined") {
|
|
12
|
+
throw new Error("render() requires a browser document");
|
|
13
|
+
}
|
|
14
|
+
const spec = (0, creative_js_1.describeCreative)(decision);
|
|
15
|
+
const anchor = document.createElement("a");
|
|
16
|
+
anchor.className = ROOT_CLASS;
|
|
17
|
+
anchor.href = spec.href;
|
|
18
|
+
anchor.rel = "noopener noreferrer";
|
|
19
|
+
anchor.target = "_blank";
|
|
20
|
+
anchor.dataset.format = spec.format;
|
|
21
|
+
anchor.dataset.house = spec.house ? "true" : "false";
|
|
22
|
+
anchor.dataset.campaignId = spec.campaignId;
|
|
23
|
+
if (spec.image) {
|
|
24
|
+
const img = document.createElement("img");
|
|
25
|
+
img.className = `${ROOT_CLASS}__image`;
|
|
26
|
+
img.src = spec.image.src;
|
|
27
|
+
img.alt = spec.image.alt;
|
|
28
|
+
if (spec.image.width) {
|
|
29
|
+
img.width = spec.image.width;
|
|
30
|
+
}
|
|
31
|
+
if (spec.image.height) {
|
|
32
|
+
img.height = spec.image.height;
|
|
33
|
+
}
|
|
34
|
+
anchor.append(img);
|
|
35
|
+
}
|
|
36
|
+
else if (spec.native) {
|
|
37
|
+
const unit = document.createElement("span");
|
|
38
|
+
unit.className = `${ROOT_CLASS}__native`;
|
|
39
|
+
if (spec.native.sponsor) {
|
|
40
|
+
const sponsor = document.createElement("span");
|
|
41
|
+
sponsor.className = `${ROOT_CLASS}__sponsor`;
|
|
42
|
+
sponsor.textContent = spec.native.sponsor;
|
|
43
|
+
unit.append(sponsor);
|
|
44
|
+
}
|
|
45
|
+
const headline = document.createElement("span");
|
|
46
|
+
headline.className = `${ROOT_CLASS}__headline`;
|
|
47
|
+
headline.textContent = spec.native.headline;
|
|
48
|
+
unit.append(headline);
|
|
49
|
+
if (spec.native.body) {
|
|
50
|
+
const body = document.createElement("span");
|
|
51
|
+
body.className = `${ROOT_CLASS}__body`;
|
|
52
|
+
body.textContent = spec.native.body;
|
|
53
|
+
unit.append(body);
|
|
54
|
+
}
|
|
55
|
+
if (spec.native.cta) {
|
|
56
|
+
const cta = document.createElement("span");
|
|
57
|
+
cta.className = `${ROOT_CLASS}__cta`;
|
|
58
|
+
cta.textContent = spec.native.cta;
|
|
59
|
+
unit.append(cta);
|
|
60
|
+
}
|
|
61
|
+
anchor.append(unit);
|
|
62
|
+
}
|
|
63
|
+
target.replaceChildren(anchor);
|
|
64
|
+
return anchor;
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EVENTS_PATH = exports.DECISION_PATH = exports.MAX_CONTEXT_BYTES = exports.DEFAULT_SERVE_URL = void 0;
|
|
4
|
+
exports.DEFAULT_SERVE_URL = "https://serve.meridianads.tech";
|
|
5
|
+
exports.MAX_CONTEXT_BYTES = 8 * 1024;
|
|
6
|
+
exports.DECISION_PATH = "/v1/decision";
|
|
7
|
+
exports.EVENTS_PATH = "/v1/events";
|
|
@@ -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"}
|