@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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +187 -2
  3. package/dist/cjs/client.js +155 -0
  4. package/dist/cjs/client.js.map +1 -0
  5. package/dist/cjs/creative.js +46 -0
  6. package/dist/cjs/creative.js.map +1 -0
  7. package/dist/cjs/errors.js +37 -0
  8. package/dist/cjs/errors.js.map +1 -0
  9. package/dist/cjs/hmac.js +48 -0
  10. package/dist/cjs/hmac.js.map +1 -0
  11. package/dist/cjs/index.js +25 -0
  12. package/dist/cjs/index.js.map +1 -0
  13. package/dist/cjs/package.json +1 -0
  14. package/dist/cjs/render.js +65 -0
  15. package/dist/cjs/render.js.map +1 -0
  16. package/dist/cjs/types.js +7 -0
  17. package/dist/cjs/types.js.map +1 -0
  18. package/dist/client.d.ts +20 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +152 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/creative.d.ts +25 -0
  23. package/dist/creative.d.ts.map +1 -0
  24. package/dist/creative.js +42 -0
  25. package/dist/creative.js.map +1 -0
  26. package/dist/errors.d.ts +16 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +31 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/hmac.d.ts +8 -0
  31. package/dist/hmac.d.ts.map +1 -0
  32. package/dist/hmac.js +43 -0
  33. package/dist/hmac.js.map +1 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +7 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/render.d.ts +7 -0
  39. package/dist/render.d.ts.map +1 -0
  40. package/dist/render.js +63 -0
  41. package/dist/render.js.map +1 -0
  42. package/dist/types.d.ts +55 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +5 -0
  45. package/dist/types.js.map +1 -0
  46. 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
- # Temporary Holding Version
1
+ # @yellesoftware/meridian-ads
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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"}
@@ -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":";AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAGhD,MAAM,UAAU,GAAG,cAAc,CAAA;AAEjC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,QAAkB;IACnE,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;IACzD,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;IAC1C,MAAM,CAAC,SAAS,GAAG,UAAU,CAAA;IAC7B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IACvB,MAAM,CAAC,GAAG,GAAG,qBAAqB,CAAA;IAClC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAA;IACxB,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;IACnC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;IACpD,MAAM,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;IAE3C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QACzC,GAAG,CAAC,SAAS,GAAG,GAAG,UAAU,SAAS,CAAA;QACtC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAA;QACxB,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAA;QACxB,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAA;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACtB,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAA;QAChC,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACpB,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QAC3C,IAAI,CAAC,SAAS,GAAG,GAAG,UAAU,UAAU,CAAA;QACxC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9C,OAAO,CAAC,SAAS,GAAG,GAAG,UAAU,WAAW,CAAA;YAC5C,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA;YACzC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACtB,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;QAC/C,QAAQ,CAAC,SAAS,GAAG,GAAG,UAAU,YAAY,CAAA;QAC9C,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAA;QAC3C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACrB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;YAC3C,IAAI,CAAC,SAAS,GAAG,GAAG,UAAU,QAAQ,CAAA;YACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAA;YACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACnB,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACpB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;YAC1C,GAAG,CAAC,SAAS,GAAG,GAAG,UAAU,OAAO,CAAA;YACpC,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAA;YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAClB,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;IAC9B,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -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"}