@trillboards/api-client 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/dist/index.js ADDED
@@ -0,0 +1,416 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/error.ts
6
+ var TrillboardsError = class _TrillboardsError extends Error {
7
+ constructor(opts) {
8
+ super(opts.message);
9
+ this.name = "TrillboardsError";
10
+ this.type = opts.type;
11
+ this.code = opts.code;
12
+ this.statusCode = opts.statusCode;
13
+ this.param = opts.param ?? null;
14
+ this.requestId = opts.requestId ?? "";
15
+ this.docsUrl = opts.docsUrl ?? `https://api.trillboards.com/docs/errors#${opts.code}`;
16
+ this.details = opts.details ?? [];
17
+ this.help = opts.help ?? null;
18
+ this._retryAfterSeconds = opts.retryAfterSeconds ?? null;
19
+ }
20
+ /**
21
+ * Construct a TrillboardsError from an API error response body and fetch Response.
22
+ *
23
+ * The API wraps errors in { error: { ... } }. We unwrap and map to class fields.
24
+ */
25
+ static fromResponse(body, response) {
26
+ const errorObj = body?.error ?? body;
27
+ const errData = errorObj;
28
+ const retryAfterHeader = response.headers.get("retry-after");
29
+ let retryAfterSeconds = null;
30
+ if (retryAfterHeader) {
31
+ const parsed = parseInt(retryAfterHeader, 10);
32
+ if (!isNaN(parsed)) {
33
+ retryAfterSeconds = parsed;
34
+ }
35
+ }
36
+ return new _TrillboardsError({
37
+ type: errData.type ?? "api_error",
38
+ code: errData.code ?? "unknown_error",
39
+ message: errData.message ?? "An unknown error occurred.",
40
+ statusCode: response.status,
41
+ param: errData.param ?? null,
42
+ requestId: errData.request_id ?? "",
43
+ docsUrl: errData.doc_url ?? void 0,
44
+ details: errData.details ?? [],
45
+ help: errData.help ?? null,
46
+ retryAfterSeconds
47
+ });
48
+ }
49
+ /** True if this is a 429 rate limit response. */
50
+ get isRateLimited() {
51
+ return this.statusCode === 429;
52
+ }
53
+ /**
54
+ * Number of seconds to wait before retrying, from the Retry-After header.
55
+ * Returns null if no Retry-After header was present.
56
+ */
57
+ get retryAfter() {
58
+ return this._retryAfterSeconds;
59
+ }
60
+ /** True if this is an authentication error (401). */
61
+ get isAuthError() {
62
+ return this.statusCode === 401;
63
+ }
64
+ /** True if this is a permission/forbidden error (403). */
65
+ get isForbidden() {
66
+ return this.statusCode === 403;
67
+ }
68
+ /** True if this is a server-side error (5xx). */
69
+ get isServerError() {
70
+ return this.statusCode >= 500;
71
+ }
72
+ };
73
+
74
+ // src/version.ts
75
+ var VERSION = "0.1.0";
76
+
77
+ // src/http.ts
78
+ var HttpClient = class {
79
+ constructor(config) {
80
+ this.config = config;
81
+ }
82
+ async request(method, path, options) {
83
+ const url = this.buildUrl(path, options?.params);
84
+ const headers = {
85
+ "Authorization": `Bearer ${this.config.apiKey}`,
86
+ "Content-Type": "application/json",
87
+ "X-Trillboards-Client": `typescript-sdk/${VERSION}`,
88
+ "User-Agent": `trillboards-typescript/${VERSION}`,
89
+ ...options?.headers ?? {}
90
+ };
91
+ if (this.config.version) {
92
+ headers["Trillboards-Version"] = this.config.version;
93
+ }
94
+ const fetchOptions = {
95
+ method,
96
+ headers
97
+ };
98
+ if (options?.body !== void 0 && options.body !== null) {
99
+ fetchOptions.body = JSON.stringify(options.body);
100
+ }
101
+ const response = await fetch(url, fetchOptions);
102
+ if (response.status === 204) {
103
+ return void 0;
104
+ }
105
+ let json;
106
+ try {
107
+ json = await response.json();
108
+ } catch {
109
+ throw new TrillboardsError({
110
+ type: "api_error",
111
+ code: "invalid_response",
112
+ message: `Invalid JSON response from API (HTTP ${response.status}).`,
113
+ statusCode: response.status
114
+ });
115
+ }
116
+ if (!response.ok) {
117
+ throw TrillboardsError.fromResponse(json, response);
118
+ }
119
+ return json;
120
+ }
121
+ get(path, opts) {
122
+ return this.request("GET", path, { params: opts?.params });
123
+ }
124
+ post(path, body) {
125
+ return this.request("POST", path, { body });
126
+ }
127
+ put(path, body) {
128
+ return this.request("PUT", path, { body });
129
+ }
130
+ patch(path, body) {
131
+ return this.request("PATCH", path, { body });
132
+ }
133
+ delete(path) {
134
+ return this.request("DELETE", path);
135
+ }
136
+ buildUrl(path, params) {
137
+ const base = `${this.config.baseUrl}${path}`;
138
+ if (!params || Object.keys(params).length === 0) {
139
+ return base;
140
+ }
141
+ const searchParams = new URLSearchParams();
142
+ for (const [key, value] of Object.entries(params)) {
143
+ if (value !== void 0 && value !== null) {
144
+ searchParams.set(key, String(value));
145
+ }
146
+ }
147
+ return `${base}?${searchParams.toString()}`;
148
+ }
149
+ };
150
+
151
+ // src/resources/screens.ts
152
+ var ScreensClient = class {
153
+ constructor(http) {
154
+ this.http = http;
155
+ }
156
+ /** List all screens owned by this partner. */
157
+ async list(params) {
158
+ return this.http.get("/v1/partner/devices", { params });
159
+ }
160
+ /** Get a single screen by ID. */
161
+ async get(screenId) {
162
+ return this.http.get(`/v1/partner/device/${screenId}`);
163
+ }
164
+ /** Register a new device/screen. */
165
+ async create(body) {
166
+ return this.http.post("/v1/partner/device", body);
167
+ }
168
+ /** Delete a screen/device. */
169
+ async delete(screenId) {
170
+ return this.http.delete(`/v1/partner/device/${screenId}`);
171
+ }
172
+ };
173
+
174
+ // src/resources/devices.ts
175
+ var DevicesClient = class {
176
+ constructor(http) {
177
+ this.http = http;
178
+ }
179
+ /** List all devices for this partner. */
180
+ async list(params) {
181
+ return this.http.get("/v1/partner/devices", { params });
182
+ }
183
+ /** Get a single device by ID. */
184
+ async get(deviceId) {
185
+ return this.http.get(`/v1/partner/device/${deviceId}`);
186
+ }
187
+ /** Register a new device. */
188
+ async create(body) {
189
+ return this.http.post("/v1/partner/device", body);
190
+ }
191
+ /** Delete a device. */
192
+ async delete(deviceId) {
193
+ return this.http.delete(`/v1/partner/device/${deviceId}`);
194
+ }
195
+ /** Send heartbeat for a device. */
196
+ async heartbeat(deviceId, body) {
197
+ return this.http.post(`/v1/partner/device/${deviceId}/heartbeat`, body ?? {});
198
+ }
199
+ };
200
+
201
+ // src/resources/impressions.ts
202
+ var ImpressionsClient = class {
203
+ constructor(http) {
204
+ this.http = http;
205
+ }
206
+ /** Record a single impression. */
207
+ async create(body) {
208
+ return this.http.post("/v1/partner/impression", body);
209
+ }
210
+ /** Record a batch of impressions (offline sync). */
211
+ async createBatch(body) {
212
+ return this.http.post("/v1/partner/impressions/batch", body);
213
+ }
214
+ };
215
+
216
+ // src/resources/analytics.ts
217
+ var AnalyticsClient = class {
218
+ constructor(http) {
219
+ this.http = http;
220
+ }
221
+ /** Get partner analytics summary. */
222
+ async get(params) {
223
+ return this.http.get("/v1/partner/analytics", { params });
224
+ }
225
+ };
226
+
227
+ // src/resources/earnings.ts
228
+ var EarningsClient = class {
229
+ constructor(http) {
230
+ this.http = http;
231
+ }
232
+ /** Get earnings summary for the authenticated partner. */
233
+ async get(params) {
234
+ return this.http.get("/v1/partner/connect/earnings", { params });
235
+ }
236
+ };
237
+
238
+ // src/resources/errors.ts
239
+ var ErrorsClient = class {
240
+ constructor(http) {
241
+ this.http = http;
242
+ }
243
+ /** List recent errors for this partner. */
244
+ async list(params) {
245
+ return this.http.get("/v1/partner/usage", { params });
246
+ }
247
+ /** Get a single error by request_id. */
248
+ async get(requestId) {
249
+ return this.http.get(`/v1/partner/usage`, {
250
+ params: { request_id: requestId }
251
+ });
252
+ }
253
+ };
254
+
255
+ // src/resources/webhooks.ts
256
+ var WebhooksClient = class {
257
+ constructor(http) {
258
+ this.http = http;
259
+ }
260
+ /** List all webhook subscriptions. */
261
+ async list() {
262
+ return this.http.get("/v1/partner/webhooks");
263
+ }
264
+ /** Create a new webhook subscription. */
265
+ async create(body) {
266
+ return this.http.post("/v1/partner/webhooks", body);
267
+ }
268
+ /** Update a webhook subscription. */
269
+ async update(webhookId, body) {
270
+ return this.http.patch(`/v1/partner/webhooks/${webhookId}`, body);
271
+ }
272
+ /** Delete a webhook subscription. */
273
+ async delete(webhookId) {
274
+ return this.http.delete(`/v1/partner/webhooks/${webhookId}`);
275
+ }
276
+ /** Send a test event to a webhook. */
277
+ async test(webhookId) {
278
+ return this.http.post(`/v1/partner/webhooks/${webhookId}/test`);
279
+ }
280
+ };
281
+
282
+ // src/resources/fleet.ts
283
+ var FleetClient = class {
284
+ constructor(http) {
285
+ this.http = http;
286
+ }
287
+ /** Push a command to a set of screens. */
288
+ async pushCommand(body) {
289
+ return this.http.post("/v1/partner/fleet/command", body);
290
+ }
291
+ /** List recent fleet commands. */
292
+ async listCommands(params) {
293
+ return this.http.get("/v1/partner/fleet/commands", { params });
294
+ }
295
+ };
296
+
297
+ // src/resources/audience.ts
298
+ var AudienceClient = class {
299
+ constructor(http) {
300
+ this.http = http;
301
+ }
302
+ /** Get live audience data for a screen. */
303
+ async getLive(screenId) {
304
+ return this.http.get(`/v1/partner/audience/live/${screenId}`);
305
+ }
306
+ /** Get audience heatmap across screens. */
307
+ async getHeatmap(params) {
308
+ return this.http.get("/v1/partner/audience/heatmap", { params });
309
+ }
310
+ /** Get aggregate audience statistics. */
311
+ async getStats(params) {
312
+ return this.http.get("/v1/partner/audience/stats", { params });
313
+ }
314
+ };
315
+
316
+ // src/resources/data.ts
317
+ var DataClient = class {
318
+ constructor(http) {
319
+ this.http = http;
320
+ }
321
+ /** Get venue intelligence data for a screen. */
322
+ async getVenueIntelligence(screenId) {
323
+ return this.http.get(`/v1/partner/venue/${screenId}`);
324
+ }
325
+ /** Get API usage summary. */
326
+ async getUsage(params) {
327
+ return this.http.get("/v1/partner/usage", { params });
328
+ }
329
+ };
330
+
331
+ // src/resources/billing.ts
332
+ var BillingClient = class {
333
+ constructor(http) {
334
+ this.http = http;
335
+ }
336
+ /** Get current billing info (plan, credits, usage). */
337
+ async get() {
338
+ return this.http.get("/v1/partner/billing");
339
+ }
340
+ /** Get Stripe Connect account status. */
341
+ async getStripeStatus() {
342
+ return this.http.get("/v1/partner/stripe/status");
343
+ }
344
+ /** Request a payout transfer. */
345
+ async requestPayout(body) {
346
+ return this.http.post("/v1/partner/stripe/payout", body ?? {});
347
+ }
348
+ };
349
+
350
+ // src/client.ts
351
+ var DEFAULT_BASE_URL = "https://api.trillboards.com";
352
+ var TrillboardsClient = class {
353
+ constructor(options) {
354
+ if (!options.apiKey) {
355
+ throw new Error(
356
+ "TrillboardsClient requires an apiKey. Get one at https://trillboards.com/developers"
357
+ );
358
+ }
359
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
360
+ this._http = new HttpClient({
361
+ apiKey: options.apiKey,
362
+ baseUrl,
363
+ version: options.version
364
+ });
365
+ this.screens = new ScreensClient(this._http);
366
+ this.devices = new DevicesClient(this._http);
367
+ this.impressions = new ImpressionsClient(this._http);
368
+ this.analytics = new AnalyticsClient(this._http);
369
+ this.webhooks = new WebhooksClient(this._http);
370
+ this.earnings = new EarningsClient(this._http);
371
+ this.fleet = new FleetClient(this._http);
372
+ this.audience = new AudienceClient(this._http);
373
+ this.data = new DataClient(this._http);
374
+ this.errors = new ErrorsClient(this._http);
375
+ this.billing = new BillingClient(this._http);
376
+ }
377
+ };
378
+ function constructEvent(payload, signature, secret) {
379
+ const payloadStr = typeof payload === "string" ? payload : payload.toString("utf8");
380
+ const expectedSignature = crypto.createHmac("sha256", secret).update(payloadStr).digest("hex");
381
+ const sigBuffer = Buffer.from(signature, "utf8");
382
+ const expectedBuffer = Buffer.from(expectedSignature, "utf8");
383
+ if (sigBuffer.length !== expectedBuffer.length || !crypto.timingSafeEqual(sigBuffer, expectedBuffer)) {
384
+ throw new TrillboardsError({
385
+ type: "authentication_error",
386
+ code: "webhook_signature_invalid",
387
+ message: "Webhook signature verification failed. The payload may have been tampered with.",
388
+ statusCode: 401
389
+ });
390
+ }
391
+ return JSON.parse(payloadStr);
392
+ }
393
+ var Webhooks = { constructEvent };
394
+
395
+ // src/generated/index.ts
396
+ var generatedTypes = {};
397
+
398
+ exports.AnalyticsClient = AnalyticsClient;
399
+ exports.AudienceClient = AudienceClient;
400
+ exports.BillingClient = BillingClient;
401
+ exports.DataClient = DataClient;
402
+ exports.DevicesClient = DevicesClient;
403
+ exports.EarningsClient = EarningsClient;
404
+ exports.ErrorsClient = ErrorsClient;
405
+ exports.FleetClient = FleetClient;
406
+ exports.ImpressionsClient = ImpressionsClient;
407
+ exports.ScreensClient = ScreensClient;
408
+ exports.TrillboardsClient = TrillboardsClient;
409
+ exports.TrillboardsError = TrillboardsError;
410
+ exports.VERSION = VERSION;
411
+ exports.Webhooks = Webhooks;
412
+ exports.WebhooksClient = WebhooksClient;
413
+ exports.constructEvent = constructEvent;
414
+ exports.generatedTypes = generatedTypes;
415
+ //# sourceMappingURL=index.js.map
416
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/error.ts","../src/version.ts","../src/http.ts","../src/resources/screens.ts","../src/resources/devices.ts","../src/resources/impressions.ts","../src/resources/analytics.ts","../src/resources/earnings.ts","../src/resources/errors.ts","../src/resources/webhooks.ts","../src/resources/fleet.ts","../src/resources/audience.ts","../src/resources/data.ts","../src/resources/billing.ts","../src/client.ts","../src/webhooks.ts","../src/generated/index.ts"],"names":["createHmac","timingSafeEqual"],"mappings":";;;;;AAuBO,IAAM,gBAAA,GAAN,MAAM,iBAAA,SAAyB,KAAA,CAAM;AAAA,EAW1C,YAAY,IAAA,EAA+B;AACzC,IAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AACZ,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AACvB,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAK,KAAA,IAAS,IAAA;AAC3B,IAAA,IAAA,CAAK,SAAA,GAAY,KAAK,SAAA,IAAa,EAAA;AACnC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,CAAA,wCAAA,EAA2C,KAAK,IAAI,CAAA,CAAA;AACnF,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,IAAW,EAAC;AAChC,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,IAAA,IAAQ,IAAA;AACzB,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,iBAAA,IAAqB,IAAA;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,YAAA,CAAa,IAAA,EAAmD,QAAA,EAA+F;AACpK,IAAA,MAAM,QAAA,GAAY,MAA4B,KAAA,IAAS,IAAA;AACvD,IAAA,MAAM,OAAA,GAAU,QAAA;AAEhB,IAAA,MAAM,gBAAA,GAAmB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA;AAC3D,IAAA,IAAI,iBAAA,GAAmC,IAAA;AACvC,IAAA,IAAI,gBAAA,EAAkB;AACpB,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,gBAAA,EAAkB,EAAE,CAAA;AAC5C,MAAA,IAAI,CAAC,KAAA,CAAM,MAAM,CAAA,EAAG;AAClB,QAAA,iBAAA,GAAoB,MAAA;AAAA,MACtB;AAAA,IACF;AAEA,IAAA,OAAO,IAAI,iBAAA,CAAiB;AAAA,MAC1B,IAAA,EAAO,QAAQ,IAAA,IAAyB,WAAA;AAAA,MACxC,IAAA,EAAO,QAAQ,IAAA,IAAmB,eAAA;AAAA,MAClC,OAAA,EAAU,QAAQ,OAAA,IAAsB,4BAAA;AAAA,MACxC,YAAY,QAAA,CAAS,MAAA;AAAA,MACrB,KAAA,EAAQ,QAAQ,KAAA,IAA2B,IAAA;AAAA,MAC3C,SAAA,EAAY,QAAQ,UAAA,IAAyB,EAAA;AAAA,MAC7C,OAAA,EAAU,QAAQ,OAAA,IAAsB,MAAA;AAAA,MACxC,OAAA,EAAU,OAAA,CAAQ,OAAA,IAA6B,EAAC;AAAA,MAChD,IAAA,EAAO,QAAQ,IAAA,IAAoC,IAAA;AAAA,MACnD;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,IAAI,aAAA,GAAyB;AAC3B,IAAA,OAAO,KAAK,UAAA,KAAe,GAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,UAAA,GAA4B;AAC9B,IAAA,OAAO,IAAA,CAAK,kBAAA;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,UAAA,KAAe,GAAA;AAAA,EAC7B;AAAA;AAAA,EAGA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,UAAA,KAAe,GAAA;AAAA,EAC7B;AAAA;AAAA,EAGA,IAAI,aAAA,GAAyB;AAC3B,IAAA,OAAO,KAAK,UAAA,IAAc,GAAA;AAAA,EAC5B;AACF;;;ACvGO,IAAM,OAAA,GAAU;;;ACahB,IAAM,aAAN,MAAiB;AAAA,EAGtB,YAAY,MAAA,EAA0B;AACpC,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,MAAM,OAAA,CAAW,MAAA,EAAgB,IAAA,EAAc,OAAA,EAAsC;AACnF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,SAAS,MAAM,CAAA;AAE/C,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,eAAA,EAAiB,CAAA,OAAA,EAAU,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,CAAA;AAAA,MAC7C,cAAA,EAAgB,kBAAA;AAAA,MAChB,sBAAA,EAAwB,kBAAkB,OAAO,CAAA,CAAA;AAAA,MACjD,YAAA,EAAc,0BAA0B,OAAO,CAAA,CAAA;AAAA,MAC/C,GAAI,OAAA,EAAS,OAAA,IAAW;AAAC,KAC3B;AAEA,IAAA,IAAI,IAAA,CAAK,OAAO,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,qBAAqB,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,OAAA;AAAA,IAC/C;AAEA,IAAA,MAAM,YAAA,GAAuC;AAAA,MAC3C,MAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,OAAA,EAAS,IAAA,KAAS,MAAA,IAAa,OAAA,CAAQ,SAAS,IAAA,EAAM;AACxD,MAAA,YAAA,CAAa,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA;AAAA,IACjD;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK,YAAY,CAAA;AAG9C,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAM,SAAS,IAAA,EAAK;AAAA,IAC7B,CAAA,CAAA,MAAQ;AACN,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,IAAA,EAAM,WAAA;AAAA,QACN,IAAA,EAAM,kBAAA;AAAA,QACN,OAAA,EAAS,CAAA,qCAAA,EAAwC,QAAA,CAAS,MAAM,CAAA,EAAA,CAAA;AAAA,QAChE,YAAY,QAAA,CAAS;AAAA,OACtB,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,gBAAA,CAAiB,YAAA,CAAa,IAAA,EAA2B,QAAQ,CAAA;AAAA,IACzE;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,GAAA,CAAO,MAAc,IAAA,EAAyD;AAC5E,IAAA,OAAO,IAAA,CAAK,QAAW,KAAA,EAAO,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,QAAQ,CAAA;AAAA,EAC9D;AAAA,EAEA,IAAA,CAAQ,MAAc,IAAA,EAA4B;AAChD,IAAA,OAAO,KAAK,OAAA,CAAW,MAAA,EAAQ,IAAA,EAAM,EAAE,MAAM,CAAA;AAAA,EAC/C;AAAA,EAEA,GAAA,CAAO,MAAc,IAAA,EAA4B;AAC/C,IAAA,OAAO,KAAK,OAAA,CAAW,KAAA,EAAO,IAAA,EAAM,EAAE,MAAM,CAAA;AAAA,EAC9C;AAAA,EAEA,KAAA,CAAS,MAAc,IAAA,EAA4B;AACjD,IAAA,OAAO,KAAK,OAAA,CAAW,OAAA,EAAS,IAAA,EAAM,EAAE,MAAM,CAAA;AAAA,EAChD;AAAA,EAEA,OAAU,IAAA,EAA0B;AAClC,IAAA,OAAO,IAAA,CAAK,OAAA,CAAW,QAAA,EAAU,IAAI,CAAA;AAAA,EACvC;AAAA,EAEQ,QAAA,CAAS,MAAc,MAAA,EAA0C;AACvE,IAAA,MAAM,OAAO,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,OAAO,GAAG,IAAI,CAAA,CAAA;AAC1C,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,KAAK,MAAM,CAAA,CAAE,WAAW,CAAA,EAAG;AAC/C,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,YAAA,GAAe,IAAI,eAAA,EAAgB;AACzC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,MAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AACzC,QAAA,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MACrC;AAAA,IACF;AACA,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,YAAA,CAAa,UAAU,CAAA,CAAA;AAAA,EAC3C;AACF,CAAA;;;AClGO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,KAAK,MAAA,EAA6E;AACtF,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAA0B,qBAAA,EAAuB,EAAE,QAAQ,CAAA;AAAA,EAC9E;AAAA;AAAA,EAGA,MAAM,IAAI,QAAA,EAA+D;AACvE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAwC,CAAA,mBAAA,EAAsB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAC3F;AAAA;AAAA,EAGA,MAAM,OAAO,IAAA,EAQmC;AAC9C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAyC,oBAAA,EAAsB,IAAI,CAAA;AAAA,EACtF;AAAA;AAAA,EAGA,MAAM,OAAO,QAAA,EAAiD;AAC5D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,MAAA,CAA6B,CAAA,mBAAA,EAAsB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAChF;AACF;;;AC9BO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,KAAK,MAAA,EAA6E;AACtF,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAA0B,qBAAA,EAAuB,EAAE,QAAQ,CAAA;AAAA,EAC9E;AAAA;AAAA,EAGA,MAAM,IAAI,QAAA,EAA+D;AACvE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAwC,CAAA,mBAAA,EAAsB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAC3F;AAAA;AAAA,EAGA,MAAM,OAAO,IAAA,EAImC;AAC9C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAyC,oBAAA,EAAsB,IAAI,CAAA;AAAA,EACtF;AAAA;AAAA,EAGA,MAAM,OAAO,QAAA,EAAiD;AAC5D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,MAAA,CAA6B,CAAA,mBAAA,EAAsB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAChF;AAAA;AAAA,EAGA,MAAM,SAAA,CAAU,QAAA,EAAkB,IAAA,EAA+D;AAC/F,IAAA,OAAO,IAAA,CAAK,KAAK,IAAA,CAA2B,CAAA,mBAAA,EAAsB,QAAQ,CAAA,UAAA,CAAA,EAAc,IAAA,IAAQ,EAAE,CAAA;AAAA,EACpG;AACF;;;AC/BO,IAAM,oBAAN,MAAwB;AAAA,EAC7B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,OAAO,IAAA,EAMuC;AAClD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAA6C,wBAAA,EAA0B,IAAI,CAAA;AAAA,EAC9F;AAAA;AAAA,EAGA,MAAM,YAAY,IAAA,EAS+B;AAC/C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAA0C,+BAAA,EAAiC,IAAI,CAAA;AAAA,EAClG;AACF;;;AC3BO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,IAAI,MAAA,EAK6C;AACrD,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAA+C,uBAAA,EAAyB,EAAE,QAAQ,CAAA;AAAA,EACrG;AACF;;;ACZO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,IAAI,MAAA,EAI4C;AACpD,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAA8C,8BAAA,EAAgC,EAAE,QAAQ,CAAA;AAAA,EAC3G;AACF;;;ACXO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,KAAK,MAAA,EAK2B;AACpC,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAA8B,mBAAA,EAAqB,EAAE,QAAQ,CAAA;AAAA,EAChF;AAAA;AAAA,EAGA,MAAM,IAAI,SAAA,EAAoE;AAC5E,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAA4C,CAAA,iBAAA,CAAA,EAAqB;AAAA,MAChF,MAAA,EAAQ,EAAE,UAAA,EAAY,SAAA;AAAU,KACjC,CAAA;AAAA,EACH;AACF;;;ACnBO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,IAAA,GAAuC;AAC3C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAA2B,sBAAsB,CAAA;AAAA,EACpE;AAAA;AAAA,EAGA,MAAM,OAAO,IAAA,EAGoC;AAC/C,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAA0C,sBAAA,EAAwB,IAAI,CAAA;AAAA,EACzF;AAAA;AAAA,EAGA,MAAM,MAAA,CAAO,SAAA,EAAmB,IAAA,EAIiB;AAC/C,IAAA,OAAO,KAAK,IAAA,CAAK,KAAA,CAA2C,CAAA,qBAAA,EAAwB,SAAS,IAAI,IAAI,CAAA;AAAA,EACvG;AAAA;AAAA,EAGA,MAAM,OAAO,SAAA,EAAkD;AAC7D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,MAAA,CAA6B,CAAA,qBAAA,EAAwB,SAAS,CAAA,CAAE,CAAA;AAAA,EACnF;AAAA;AAAA,EAGA,MAAM,KAAK,SAAA,EAAkD;AAC3D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAA2B,CAAA,qBAAA,EAAwB,SAAS,CAAA,KAAA,CAAO,CAAA;AAAA,EACtF;AACF;;;AClCO,IAAM,cAAN,MAAkB;AAAA,EACvB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,YAAY,IAAA,EAMoC;AACpD,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAA+C,2BAAA,EAA6B,IAAI,CAAA;AAAA,EACnG;AAAA;AAAA,EAGA,MAAM,aAAa,MAAA,EAAmF;AACpG,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAAgC,4BAAA,EAA8B,EAAE,QAAQ,CAAA;AAAA,EAC3F;AACF;;;AClBO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,QAAQ,QAAA,EAAqE;AACjF,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAA8C,CAAA,0BAAA,EAA6B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACxG;AAAA;AAAA,EAGA,MAAM,WAAW,MAAA,EAIgD;AAC/D,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAAyD,8BAAA,EAAgC,EAAE,QAAQ,CAAA;AAAA,EACtH;AAAA;AAAA,EAGA,MAAM,SAAS,MAAA,EAEkD;AAC/D,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAAyD,4BAAA,EAA8B,EAAE,QAAQ,CAAA;AAAA,EACpH;AACF;;;ACxBO,IAAM,aAAN,MAAiB;AAAA,EACtB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,qBAAqB,QAAA,EAAgF;AACzG,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAyD,CAAA,kBAAA,EAAqB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAC3G;AAAA;AAAA,EAGA,MAAM,SAAS,MAAA,EAEkD;AAC/D,IAAA,OAAO,KAAK,IAAA,CAAK,GAAA,CAAyD,mBAAA,EAAqB,EAAE,QAAQ,CAAA;AAAA,EAC3G;AACF;;;ACbO,IAAM,gBAAN,MAAoB;AAAA,EACzB,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGhD,MAAM,GAAA,GAAwD;AAC5D,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAA6C,qBAAqB,CAAA;AAAA,EACrF;AAAA;AAAA,EAGA,MAAM,eAAA,GAAgF;AACpF,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAyD,2BAA2B,CAAA;AAAA,EACvG;AAAA;AAAA,EAGA,MAAM,cAAc,IAAA,EAE6C;AAC/D,IAAA,OAAO,KAAK,IAAA,CAAK,IAAA,CAA0D,2BAAA,EAA6B,IAAA,IAAQ,EAAE,CAAA;AAAA,EACpH;AACF;;;ACFA,IAAM,gBAAA,GAAmB,6BAAA;AAElB,IAAM,oBAAN,MAAwB;AAAA,EAgB7B,YAAY,OAAA,EAAwB;AAClC,IAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,OAAA,CAAQ,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAExE,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAI,UAAA,CAAW;AAAA,MAC1B,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,OAAA;AAAA,MACA,SAAS,OAAA,CAAQ;AAAA,KAClB,CAAA;AAED,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AAC3C,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AAC3C,IAAA,IAAA,CAAK,WAAA,GAAc,IAAI,iBAAA,CAAkB,IAAA,CAAK,KAAK,CAAA;AACnD,IAAA,IAAA,CAAK,SAAA,GAAY,IAAI,eAAA,CAAgB,IAAA,CAAK,KAAK,CAAA;AAC/C,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAI,WAAA,CAAY,IAAA,CAAK,KAAK,CAAA;AACvC,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA;AAC7C,IAAA,IAAA,CAAK,IAAA,GAAO,IAAI,UAAA,CAAW,IAAA,CAAK,KAAK,CAAA;AACrC,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,YAAA,CAAa,IAAA,CAAK,KAAK,CAAA;AACzC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AAAA,EAC7C;AACF;ACpCO,SAAS,cAAA,CACd,OAAA,EACA,SAAA,EACA,MAAA,EACc;AACd,EAAA,MAAM,aAAa,OAAO,OAAA,KAAY,WAAW,OAAA,GAAU,OAAA,CAAQ,SAAS,MAAM,CAAA;AAElF,EAAA,MAAM,iBAAA,GAAoBA,kBAAW,QAAA,EAAU,MAAM,EAClD,MAAA,CAAO,UAAU,CAAA,CACjB,MAAA,CAAO,KAAK,CAAA;AAEf,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,MAAM,CAAA;AAC/C,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,iBAAA,EAAmB,MAAM,CAAA;AAE5D,EAAA,IAAI,SAAA,CAAU,WAAW,cAAA,CAAe,MAAA,IAAU,CAACC,sBAAA,CAAgB,SAAA,EAAW,cAAc,CAAA,EAAG;AAC7F,IAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,MACzB,IAAA,EAAM,sBAAA;AAAA,MACN,IAAA,EAAM,2BAAA;AAAA,MACN,OAAA,EAAS,iFAAA;AAAA,MACT,UAAA,EAAY;AAAA,KACb,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA,CAAK,MAAM,UAAU,CAAA;AAC9B;AAEO,IAAM,QAAA,GAAW,EAAE,cAAA;;;ACvDnB,IAAM,iBAAiB","file":"index.js","sourcesContent":["/**\n * TrillboardsError — client-side error class that mirrors the server-side\n * canonical error envelope from utils/apiErrors.ts.\n *\n * Error shape from the API:\n * { error: { type, code, message, param, doc_url, request_id, details?, help? } }\n */\n\nimport type { ApiErrorType, ErrorDetail, ErrorResponseBody } from './types';\n\nexport interface TrillboardsErrorOptions {\n type: ApiErrorType;\n code: string;\n message: string;\n statusCode: number;\n param?: string | null;\n requestId?: string;\n docsUrl?: string;\n details?: ErrorDetail[];\n help?: Record<string, unknown>;\n retryAfterSeconds?: number | null;\n}\n\nexport class TrillboardsError extends Error {\n readonly type: ApiErrorType;\n readonly code: string;\n readonly param: string | null;\n readonly requestId: string;\n readonly docsUrl: string;\n readonly statusCode: number;\n readonly details: ErrorDetail[];\n readonly help: Record<string, unknown> | null;\n private readonly _retryAfterSeconds: number | null;\n\n constructor(opts: TrillboardsErrorOptions) {\n super(opts.message);\n this.name = 'TrillboardsError';\n this.type = opts.type;\n this.code = opts.code;\n this.statusCode = opts.statusCode;\n this.param = opts.param ?? null;\n this.requestId = opts.requestId ?? '';\n this.docsUrl = opts.docsUrl ?? `https://api.trillboards.com/docs/errors#${opts.code}`;\n this.details = opts.details ?? [];\n this.help = opts.help ?? null;\n this._retryAfterSeconds = opts.retryAfterSeconds ?? null;\n }\n\n /**\n * Construct a TrillboardsError from an API error response body and fetch Response.\n *\n * The API wraps errors in { error: { ... } }. We unwrap and map to class fields.\n */\n static fromResponse(body: ErrorResponseBody | Record<string, unknown>, response: { status: number; headers: { get(name: string): string | null } }): TrillboardsError {\n const errorObj = (body as ErrorResponseBody)?.error ?? body;\n const errData = errorObj as Record<string, unknown>;\n\n const retryAfterHeader = response.headers.get('retry-after');\n let retryAfterSeconds: number | null = null;\n if (retryAfterHeader) {\n const parsed = parseInt(retryAfterHeader, 10);\n if (!isNaN(parsed)) {\n retryAfterSeconds = parsed;\n }\n }\n\n return new TrillboardsError({\n type: (errData.type as ApiErrorType) ?? 'api_error',\n code: (errData.code as string) ?? 'unknown_error',\n message: (errData.message as string) ?? 'An unknown error occurred.',\n statusCode: response.status,\n param: (errData.param as string | null) ?? null,\n requestId: (errData.request_id as string) ?? '',\n docsUrl: (errData.doc_url as string) ?? undefined,\n details: (errData.details as ErrorDetail[]) ?? [],\n help: (errData.help as Record<string, unknown>) ?? null,\n retryAfterSeconds,\n });\n }\n\n /** True if this is a 429 rate limit response. */\n get isRateLimited(): boolean {\n return this.statusCode === 429;\n }\n\n /**\n * Number of seconds to wait before retrying, from the Retry-After header.\n * Returns null if no Retry-After header was present.\n */\n get retryAfter(): number | null {\n return this._retryAfterSeconds;\n }\n\n /** True if this is an authentication error (401). */\n get isAuthError(): boolean {\n return this.statusCode === 401;\n }\n\n /** True if this is a permission/forbidden error (403). */\n get isForbidden(): boolean {\n return this.statusCode === 403;\n }\n\n /** True if this is a server-side error (5xx). */\n get isServerError(): boolean {\n return this.statusCode >= 500;\n }\n}\n","/**\n * SDK version constant. Sent in User-Agent and X-Trillboards-Client headers.\n * Keep in sync with package.json version.\n */\nexport const VERSION = '0.1.0';\n","/**\n * Internal HTTP client for the Trillboards API.\n *\n * Uses native fetch (Node 18+). Zero runtime dependencies.\n * Sends standard headers: Authorization, Content-Type, User-Agent, X-Trillboards-Client.\n */\n\nimport { TrillboardsError } from './error';\nimport type { ErrorResponseBody, RequestOptions } from './types';\nimport { VERSION } from './version';\n\nexport interface HttpClientConfig {\n apiKey: string;\n baseUrl: string;\n version?: string;\n}\n\nexport class HttpClient {\n private readonly config: HttpClientConfig;\n\n constructor(config: HttpClientConfig) {\n this.config = config;\n }\n\n async request<T>(method: string, path: string, options?: RequestOptions): Promise<T> {\n const url = this.buildUrl(path, options?.params);\n\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n 'X-Trillboards-Client': `typescript-sdk/${VERSION}`,\n 'User-Agent': `trillboards-typescript/${VERSION}`,\n ...(options?.headers ?? {}),\n };\n\n if (this.config.version) {\n headers['Trillboards-Version'] = this.config.version;\n }\n\n const fetchOptions: globalThis.RequestInit = {\n method,\n headers,\n };\n\n if (options?.body !== undefined && options.body !== null) {\n fetchOptions.body = JSON.stringify(options.body);\n }\n\n const response = await fetch(url, fetchOptions);\n\n // Handle 204 No Content\n if (response.status === 204) {\n return undefined as T;\n }\n\n let json: unknown;\n try {\n json = await response.json();\n } catch {\n throw new TrillboardsError({\n type: 'api_error',\n code: 'invalid_response',\n message: `Invalid JSON response from API (HTTP ${response.status}).`,\n statusCode: response.status,\n });\n }\n\n if (!response.ok) {\n throw TrillboardsError.fromResponse(json as ErrorResponseBody, response);\n }\n\n return json as T;\n }\n\n get<T>(path: string, opts?: { params?: Record<string, unknown> }): Promise<T> {\n return this.request<T>('GET', path, { params: opts?.params });\n }\n\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, { body });\n }\n\n put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PUT', path, { body });\n }\n\n patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, { body });\n }\n\n delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', path);\n }\n\n private buildUrl(path: string, params?: Record<string, unknown>): string {\n const base = `${this.config.baseUrl}${path}`;\n if (!params || Object.keys(params).length === 0) {\n return base;\n }\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n searchParams.set(key, String(value));\n }\n }\n return `${base}?${searchParams.toString()}`;\n }\n}\n","/**\n * ScreensClient — Partner API screen management.\n *\n * Base path: /v1/partner (screens are mapped from devices + screen pairs)\n */\n\nimport type { HttpClient } from '../http';\nimport type { ListResponse, Screen } from '../types';\n\nexport class ScreensClient {\n constructor(private readonly http: HttpClient) {}\n\n /** List all screens owned by this partner. */\n async list(params?: { limit?: number; offset?: number }): Promise<ListResponse<Screen>> {\n return this.http.get<ListResponse<Screen>>('/v1/partner/devices', { params });\n }\n\n /** Get a single screen by ID. */\n async get(screenId: string): Promise<{ success: boolean; data: Screen }> {\n return this.http.get<{ success: boolean; data: Screen }>(`/v1/partner/device/${screenId}`);\n }\n\n /** Register a new device/screen. */\n async create(body: {\n device_name: string;\n device_type?: string;\n venue_type?: string;\n country?: string;\n city?: string;\n timezone?: string;\n resolution?: string;\n }): Promise<{ success: boolean; data: Screen }> {\n return this.http.post<{ success: boolean; data: Screen }>('/v1/partner/device', body);\n }\n\n /** Delete a screen/device. */\n async delete(screenId: string): Promise<{ success: boolean }> {\n return this.http.delete<{ success: boolean }>(`/v1/partner/device/${screenId}`);\n }\n}\n","/**\n * DevicesClient — device registration and heartbeat.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { Device, ListResponse } from '../types';\n\nexport class DevicesClient {\n constructor(private readonly http: HttpClient) {}\n\n /** List all devices for this partner. */\n async list(params?: { limit?: number; offset?: number }): Promise<ListResponse<Device>> {\n return this.http.get<ListResponse<Device>>('/v1/partner/devices', { params });\n }\n\n /** Get a single device by ID. */\n async get(deviceId: string): Promise<{ success: boolean; data: Device }> {\n return this.http.get<{ success: boolean; data: Device }>(`/v1/partner/device/${deviceId}`);\n }\n\n /** Register a new device. */\n async create(body: {\n device_name: string;\n device_type?: string;\n platform?: string;\n }): Promise<{ success: boolean; data: Device }> {\n return this.http.post<{ success: boolean; data: Device }>('/v1/partner/device', body);\n }\n\n /** Delete a device. */\n async delete(deviceId: string): Promise<{ success: boolean }> {\n return this.http.delete<{ success: boolean }>(`/v1/partner/device/${deviceId}`);\n }\n\n /** Send heartbeat for a device. */\n async heartbeat(deviceId: string, body?: Record<string, unknown>): Promise<{ success: boolean }> {\n return this.http.post<{ success: boolean }>(`/v1/partner/device/${deviceId}/heartbeat`, body ?? {});\n }\n}\n","/**\n * ImpressionsClient — impression tracking.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { Impression } from '../types';\n\nexport class ImpressionsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Record a single impression. */\n async create(body: {\n screen_id: string;\n device_id: string;\n ad_type?: string;\n duration_seconds?: number;\n metadata?: Record<string, unknown>;\n }): Promise<{ success: boolean; data: Impression }> {\n return this.http.post<{ success: boolean; data: Impression }>('/v1/partner/impression', body);\n }\n\n /** Record a batch of impressions (offline sync). */\n async createBatch(body: {\n impressions: Array<{\n screen_id: string;\n device_id: string;\n ad_type?: string;\n duration_seconds?: number;\n played_at?: string;\n metadata?: Record<string, unknown>;\n }>;\n }): Promise<{ success: boolean; count: number }> {\n return this.http.post<{ success: boolean; count: number }>('/v1/partner/impressions/batch', body);\n }\n}\n","/**\n * AnalyticsClient — partner analytics and reporting.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { AnalyticsData } from '../types';\n\nexport class AnalyticsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Get partner analytics summary. */\n async get(params?: {\n period?: 'day' | 'week' | 'month';\n start_date?: string;\n end_date?: string;\n screen_id?: string;\n }): Promise<{ success: boolean; data: AnalyticsData }> {\n return this.http.get<{ success: boolean; data: AnalyticsData }>('/v1/partner/analytics', { params });\n }\n}\n","/**\n * EarningsClient — earnings, payouts, and transaction history.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { EarningsData } from '../types';\n\nexport class EarningsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Get earnings summary for the authenticated partner. */\n async get(params?: {\n period?: 'day' | 'week' | 'month';\n start_date?: string;\n end_date?: string;\n }): Promise<{ success: boolean; data: EarningsData }> {\n return this.http.get<{ success: boolean; data: EarningsData }>('/v1/partner/connect/earnings', { params });\n }\n}\n","/**\n * ErrorsClient — query the partner error log.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { ErrorEntry, ListResponse } from '../types';\n\nexport class ErrorsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** List recent errors for this partner. */\n async list(params?: {\n limit?: number;\n offset?: number;\n code?: string;\n type?: string;\n }): Promise<ListResponse<ErrorEntry>> {\n return this.http.get<ListResponse<ErrorEntry>>('/v1/partner/usage', { params });\n }\n\n /** Get a single error by request_id. */\n async get(requestId: string): Promise<{ success: boolean; data: ErrorEntry }> {\n return this.http.get<{ success: boolean; data: ErrorEntry }>(`/v1/partner/usage`, {\n params: { request_id: requestId },\n });\n }\n}\n","/**\n * WebhooksClient — webhook subscription management.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { ListResponse, Webhook } from '../types';\n\nexport class WebhooksClient {\n constructor(private readonly http: HttpClient) {}\n\n /** List all webhook subscriptions. */\n async list(): Promise<ListResponse<Webhook>> {\n return this.http.get<ListResponse<Webhook>>('/v1/partner/webhooks');\n }\n\n /** Create a new webhook subscription. */\n async create(body: {\n url: string;\n events: string[];\n }): Promise<{ success: boolean; data: Webhook }> {\n return this.http.post<{ success: boolean; data: Webhook }>('/v1/partner/webhooks', body);\n }\n\n /** Update a webhook subscription. */\n async update(webhookId: string, body: {\n url?: string;\n events?: string[];\n status?: string;\n }): Promise<{ success: boolean; data: Webhook }> {\n return this.http.patch<{ success: boolean; data: Webhook }>(`/v1/partner/webhooks/${webhookId}`, body);\n }\n\n /** Delete a webhook subscription. */\n async delete(webhookId: string): Promise<{ success: boolean }> {\n return this.http.delete<{ success: boolean }>(`/v1/partner/webhooks/${webhookId}`);\n }\n\n /** Send a test event to a webhook. */\n async test(webhookId: string): Promise<{ success: boolean }> {\n return this.http.post<{ success: boolean }>(`/v1/partner/webhooks/${webhookId}/test`);\n }\n}\n","/**\n * FleetClient — fleet-wide device management.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { FleetCommand, ListResponse } from '../types';\n\nexport class FleetClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Push a command to a set of screens. */\n async pushCommand(body: {\n command: string;\n target_screens?: string[];\n venue_type?: string;\n country?: string;\n params?: Record<string, unknown>;\n }): Promise<{ success: boolean; data: FleetCommand }> {\n return this.http.post<{ success: boolean; data: FleetCommand }>('/v1/partner/fleet/command', body);\n }\n\n /** List recent fleet commands. */\n async listCommands(params?: { limit?: number; offset?: number }): Promise<ListResponse<FleetCommand>> {\n return this.http.get<ListResponse<FleetCommand>>('/v1/partner/fleet/commands', { params });\n }\n}\n","/**\n * AudienceClient — audience intelligence API.\n *\n * Base path: /v1/partner/audience\n */\n\nimport type { HttpClient } from '../http';\nimport type { AudienceData } from '../types';\n\nexport class AudienceClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Get live audience data for a screen. */\n async getLive(screenId: string): Promise<{ success: boolean; data: AudienceData }> {\n return this.http.get<{ success: boolean; data: AudienceData }>(`/v1/partner/audience/live/${screenId}`);\n }\n\n /** Get audience heatmap across screens. */\n async getHeatmap(params?: {\n start_date?: string;\n end_date?: string;\n screen_ids?: string[];\n }): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.get<{ success: boolean; data: Record<string, unknown> }>('/v1/partner/audience/heatmap', { params });\n }\n\n /** Get aggregate audience statistics. */\n async getStats(params?: {\n period?: 'day' | 'week' | 'month';\n }): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.get<{ success: boolean; data: Record<string, unknown> }>('/v1/partner/audience/stats', { params });\n }\n}\n","/**\n * DataClient — data export and venue intelligence.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\n\nexport class DataClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Get venue intelligence data for a screen. */\n async getVenueIntelligence(screenId: string): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.get<{ success: boolean; data: Record<string, unknown> }>(`/v1/partner/venue/${screenId}`);\n }\n\n /** Get API usage summary. */\n async getUsage(params?: {\n period?: 'day' | 'week' | 'month';\n }): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.get<{ success: boolean; data: Record<string, unknown> }>('/v1/partner/usage', { params });\n }\n}\n","/**\n * BillingClient — usage-based billing and credits.\n *\n * Base path: /v1/partner\n */\n\nimport type { HttpClient } from '../http';\nimport type { BillingInfo } from '../types';\n\nexport class BillingClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Get current billing info (plan, credits, usage). */\n async get(): Promise<{ success: boolean; data: BillingInfo }> {\n return this.http.get<{ success: boolean; data: BillingInfo }>('/v1/partner/billing');\n }\n\n /** Get Stripe Connect account status. */\n async getStripeStatus(): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.get<{ success: boolean; data: Record<string, unknown> }>('/v1/partner/stripe/status');\n }\n\n /** Request a payout transfer. */\n async requestPayout(body?: {\n amount?: number;\n }): Promise<{ success: boolean; data: Record<string, unknown> }> {\n return this.http.post<{ success: boolean; data: Record<string, unknown> }>('/v1/partner/stripe/payout', body ?? {});\n }\n}\n","/**\n * TrillboardsClient — main entry point for the Trillboards Partner API SDK.\n *\n * Usage:\n * import { TrillboardsClient } from '@trillboards/api-client';\n *\n * const client = new TrillboardsClient({ apiKey: 'trb_partner_xxxxx' });\n * const screens = await client.screens.list();\n */\n\nimport { HttpClient } from './http';\nimport type { ClientOptions } from './types';\nimport {\n ScreensClient,\n DevicesClient,\n ImpressionsClient,\n AnalyticsClient,\n EarningsClient,\n ErrorsClient,\n WebhooksClient,\n FleetClient,\n AudienceClient,\n DataClient,\n BillingClient,\n} from './resources';\n\nconst DEFAULT_BASE_URL = 'https://api.trillboards.com';\n\nexport class TrillboardsClient {\n readonly screens: ScreensClient;\n readonly devices: DevicesClient;\n readonly impressions: ImpressionsClient;\n readonly analytics: AnalyticsClient;\n readonly webhooks: WebhooksClient;\n readonly earnings: EarningsClient;\n readonly fleet: FleetClient;\n readonly audience: AudienceClient;\n readonly data: DataClient;\n readonly errors: ErrorsClient;\n readonly billing: BillingClient;\n\n /** The underlying HTTP client (exposed for advanced use cases). */\n readonly _http: HttpClient;\n\n constructor(options: ClientOptions) {\n if (!options.apiKey) {\n throw new Error(\n 'TrillboardsClient requires an apiKey. Get one at https://trillboards.com/developers'\n );\n }\n\n const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n\n this._http = new HttpClient({\n apiKey: options.apiKey,\n baseUrl,\n version: options.version,\n });\n\n this.screens = new ScreensClient(this._http);\n this.devices = new DevicesClient(this._http);\n this.impressions = new ImpressionsClient(this._http);\n this.analytics = new AnalyticsClient(this._http);\n this.webhooks = new WebhooksClient(this._http);\n this.earnings = new EarningsClient(this._http);\n this.fleet = new FleetClient(this._http);\n this.audience = new AudienceClient(this._http);\n this.data = new DataClient(this._http);\n this.errors = new ErrorsClient(this._http);\n this.billing = new BillingClient(this._http);\n }\n}\n","/**\n * Webhook signature verification for Trillboards Partner API webhooks.\n *\n * Uses HMAC-SHA256 with timing-safe comparison to prevent signature\n * forgery and timing attacks.\n */\n\nimport { createHmac, timingSafeEqual } from 'crypto';\nimport { TrillboardsError } from './error';\nimport type { WebhookEvent } from './types';\n\n/**\n * Verify a webhook payload signature and parse the event.\n *\n * @param payload - Raw request body as string or Buffer.\n * @param signature - The signature from the `X-Trillboards-Signature` header.\n * @param secret - The webhook secret from your webhook subscription.\n * @returns The parsed WebhookEvent if signature is valid.\n * @throws TrillboardsError if the signature does not match.\n *\n * @example\n * ```typescript\n * import { Webhooks } from '@trillboards/api-client';\n *\n * app.post('/webhooks', (req, res) => {\n * const event = Webhooks.constructEvent(\n * req.body,\n * req.headers['x-trillboards-signature'],\n * process.env.WEBHOOK_SECRET\n * );\n * console.log('Received event:', event.type);\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function constructEvent(\n payload: string | Buffer,\n signature: string,\n secret: string,\n): WebhookEvent {\n const payloadStr = typeof payload === 'string' ? payload : payload.toString('utf8');\n\n const expectedSignature = createHmac('sha256', secret)\n .update(payloadStr)\n .digest('hex');\n\n const sigBuffer = Buffer.from(signature, 'utf8');\n const expectedBuffer = Buffer.from(expectedSignature, 'utf8');\n\n if (sigBuffer.length !== expectedBuffer.length || !timingSafeEqual(sigBuffer, expectedBuffer)) {\n throw new TrillboardsError({\n type: 'authentication_error',\n code: 'webhook_signature_invalid',\n message: 'Webhook signature verification failed. The payload may have been tampered with.',\n statusCode: 401,\n });\n }\n\n return JSON.parse(payloadStr) as WebhookEvent;\n}\n\nexport const Webhooks = { constructEvent };\n","// Auto-generated from OpenAPI specs — run npm run generate to populate\n//\n// This directory will contain auto-generated TypeScript types derived from\n// trillboard-api/docs/openapi/partner-api.yaml and the generated fragments\n// at trillboard-api/docs/openapi/generated/partner-api-generated.json.\n\nexport const generatedTypes = {} as const;\n"]}