@trillboards/api-client 0.4.1 → 1.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/README.md +1 -1
- package/dist/chunk-FJ7QMMH4.mjs +18 -0
- package/dist/chunk-FJ7QMMH4.mjs.map +1 -0
- package/dist/generated.d.mts +6643 -0
- package/dist/generated.d.ts +6643 -0
- package/dist/generated.js +13 -0
- package/dist/generated.js.map +1 -0
- package/dist/generated.mjs +3 -0
- package/dist/generated.mjs.map +1 -0
- package/dist/index.d.mts +110 -624
- package/dist/index.d.ts +110 -624
- package/dist/index.js +184 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { __require } from './chunk-FJ7QMMH4.mjs';
|
|
2
|
+
export { generatedTypes } from './chunk-FJ7QMMH4.mjs';
|
|
1
3
|
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
4
|
|
|
3
5
|
// src/error.ts
|
|
@@ -70,7 +72,7 @@ var TrillboardsError = class _TrillboardsError extends Error {
|
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
// src/version.ts
|
|
73
|
-
var VERSION = "
|
|
75
|
+
var VERSION = "1.1.0";
|
|
74
76
|
|
|
75
77
|
// src/http.ts
|
|
76
78
|
var PUBLIC_PATHS = ["/v1/errors", "/v1/changelog", "/api/discover", "/v1/health"];
|
|
@@ -353,7 +355,14 @@ var WebhooksClient = class {
|
|
|
353
355
|
async list() {
|
|
354
356
|
return this.http.get("/v1/partner/webhooks");
|
|
355
357
|
}
|
|
356
|
-
/**
|
|
358
|
+
/**
|
|
359
|
+
* Create a new webhook subscription.
|
|
360
|
+
*
|
|
361
|
+
* The created `Webhook` carries its signing `secret` (`whsec_…`) — this is the
|
|
362
|
+
* ONLY time it is returned. Persist it securely and pass it to
|
|
363
|
+
* `Webhooks.constructEvent()` to verify the compound `X-Trillboards-Signature`
|
|
364
|
+
* header over the raw delivery body.
|
|
365
|
+
*/
|
|
357
366
|
async create(body) {
|
|
358
367
|
return this.http.post("/v1/partner/webhooks", body);
|
|
359
368
|
}
|
|
@@ -621,20 +630,174 @@ function constructEventLegacy(payload, signature, secret) {
|
|
|
621
630
|
}
|
|
622
631
|
return JSON.parse(payloadStr);
|
|
623
632
|
}
|
|
633
|
+
var ADCP_WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
|
|
634
|
+
var ADCP_WEBHOOK_COMPONENTS = [
|
|
635
|
+
"@method",
|
|
636
|
+
"@target-uri",
|
|
637
|
+
"@authority",
|
|
638
|
+
"content-type",
|
|
639
|
+
"content-digest"
|
|
640
|
+
];
|
|
641
|
+
var ADCP_MAX_WINDOW_SECONDS = 300;
|
|
642
|
+
function splitDictionary(headerValue) {
|
|
643
|
+
const members = [];
|
|
644
|
+
let depth = 0;
|
|
645
|
+
let quoted = false;
|
|
646
|
+
let start = 0;
|
|
647
|
+
for (let i = 0; i < headerValue.length; i++) {
|
|
648
|
+
const ch = headerValue[i];
|
|
649
|
+
if (quoted) {
|
|
650
|
+
if (ch === "\\") i++;
|
|
651
|
+
else if (ch === '"') quoted = false;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (ch === '"') quoted = true;
|
|
655
|
+
else if (ch === "(") depth++;
|
|
656
|
+
else if (ch === ")") depth--;
|
|
657
|
+
else if (ch === "," && depth === 0) {
|
|
658
|
+
members.push(headerValue.slice(start, i).trim());
|
|
659
|
+
start = i + 1;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
members.push(headerValue.slice(start).trim());
|
|
663
|
+
return members.filter((member) => member.length > 0);
|
|
664
|
+
}
|
|
665
|
+
function dictionaryMember(headerValue, label) {
|
|
666
|
+
return splitDictionary(headerValue).find((member) => member.startsWith(`${label}=`));
|
|
667
|
+
}
|
|
668
|
+
var ADCP_SIGNATURE_LABEL = "sig1";
|
|
669
|
+
function selectAdcpSignatureInput(signatureInput) {
|
|
670
|
+
const members = splitDictionary(signatureInput);
|
|
671
|
+
const named = members.find((member) => member.startsWith(`${ADCP_SIGNATURE_LABEL}=`));
|
|
672
|
+
if (named) return { label: ADCP_SIGNATURE_LABEL, member: named };
|
|
673
|
+
const tagged = members.find((member) => member.includes(`tag="${ADCP_WEBHOOK_SIGNING_TAG}"`));
|
|
674
|
+
if (tagged) return { label: tagged.slice(0, tagged.indexOf("=")), member: tagged };
|
|
675
|
+
throw authError(
|
|
676
|
+
`AdCP webhook has no "${ADCP_SIGNATURE_LABEL}" signature member and no member tagged "${ADCP_WEBHOOK_SIGNING_TAG}".`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
function canonicalTargetUri(rawUrl) {
|
|
680
|
+
const parsed = new URL(rawUrl);
|
|
681
|
+
const beforeFragment = rawUrl.split("#", 1)[0];
|
|
682
|
+
const search = parsed.search || (beforeFragment.endsWith("?") ? "?" : "");
|
|
683
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname}${search}`.replace(
|
|
684
|
+
/%([0-9a-fA-F]{2})/g,
|
|
685
|
+
(match, hex) => {
|
|
686
|
+
const code = parseInt(hex, 16);
|
|
687
|
+
const unreserved = code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 45 || code === 46 || code === 95 || code === 126;
|
|
688
|
+
return unreserved ? String.fromCharCode(code) : `%${hex.toUpperCase()}`;
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
function verifyAdcpWebhook(opts) {
|
|
693
|
+
const { createHash, createPublicKey, verify: cryptoVerify } = __require("crypto");
|
|
694
|
+
const signatureInput = getHeader(opts.headers, "Signature-Input");
|
|
695
|
+
const signature = getHeader(opts.headers, "Signature");
|
|
696
|
+
if (!signatureInput || !signature) {
|
|
697
|
+
throw authError("AdCP webhook is missing the Signature / Signature-Input header pair.");
|
|
698
|
+
}
|
|
699
|
+
const { label, member: inputMember } = selectAdcpSignatureInput(signatureInput);
|
|
700
|
+
const signatureMember = dictionaryMember(signature, label);
|
|
701
|
+
if (!signatureMember) {
|
|
702
|
+
throw authError(`AdCP webhook Signature has no "${label}" member to match Signature-Input.`);
|
|
703
|
+
}
|
|
704
|
+
const paramsStart = inputMember.indexOf("(");
|
|
705
|
+
const paramsEnd = inputMember.indexOf(")");
|
|
706
|
+
if (paramsStart < 0 || paramsEnd < 0) {
|
|
707
|
+
throw authError("AdCP webhook Signature-Input is malformed.");
|
|
708
|
+
}
|
|
709
|
+
const components = inputMember.slice(paramsStart + 1, paramsEnd).split(" ").map((component) => component.replace(/^"|"$/g, "")).filter((component) => component.length > 0);
|
|
710
|
+
const readParam = (name) => {
|
|
711
|
+
const match = new RegExp(`;${name}=("?)([^";]+)\\1`).exec(inputMember);
|
|
712
|
+
return match ? match[2] : void 0;
|
|
713
|
+
};
|
|
714
|
+
const created = Number(readParam("created"));
|
|
715
|
+
const expires = Number(readParam("expires"));
|
|
716
|
+
const nonce = readParam("nonce");
|
|
717
|
+
const keyid = readParam("keyid");
|
|
718
|
+
const alg = readParam("alg");
|
|
719
|
+
const tag = readParam("tag");
|
|
720
|
+
if (!nonce || !keyid || !alg || !Number.isFinite(created) || !Number.isFinite(expires)) {
|
|
721
|
+
throw authError("AdCP webhook signature is missing a required parameter.");
|
|
722
|
+
}
|
|
723
|
+
if (tag !== ADCP_WEBHOOK_SIGNING_TAG) {
|
|
724
|
+
throw authError(`AdCP webhook signature has tag "${tag}", expected "${ADCP_WEBHOOK_SIGNING_TAG}".`);
|
|
725
|
+
}
|
|
726
|
+
if (alg !== "ed25519") {
|
|
727
|
+
throw authError(`AdCP webhook signature algorithm "${alg}" is not allowed.`);
|
|
728
|
+
}
|
|
729
|
+
for (const required of ADCP_WEBHOOK_COMPONENTS) {
|
|
730
|
+
if (!components.includes(required)) {
|
|
731
|
+
throw authError(`AdCP webhook signature does not cover "${required}".`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (expires <= created || expires - created > ADCP_MAX_WINDOW_SECONDS) {
|
|
735
|
+
throw authError("AdCP webhook signature window is invalid.");
|
|
736
|
+
}
|
|
737
|
+
const now = opts.nowSec ?? Math.floor(Date.now() / 1e3);
|
|
738
|
+
if (now > expires) {
|
|
739
|
+
throw authError("AdCP webhook signature has expired. Possible replay attack.");
|
|
740
|
+
}
|
|
741
|
+
const jwk = opts.keys.find((key) => key.kid === keyid);
|
|
742
|
+
if (!jwk) {
|
|
743
|
+
throw authError(`AdCP webhook signed with unknown key "${keyid}". Refresh the JWKS.`);
|
|
744
|
+
}
|
|
745
|
+
if (jwk.key_ops && !jwk.key_ops.includes("verify")) {
|
|
746
|
+
throw authError(`AdCP webhook key "${keyid}" is not a verification key.`);
|
|
747
|
+
}
|
|
748
|
+
if (jwk.adcp_use && jwk.adcp_use !== "request-signing" && jwk.adcp_use !== "webhook-signing") {
|
|
749
|
+
throw authError(`AdCP webhook key "${keyid}" has adcp_use "${jwk.adcp_use}".`);
|
|
750
|
+
}
|
|
751
|
+
const payloadBuf = typeof opts.payload === "string" ? Buffer.from(opts.payload, "utf8") : opts.payload;
|
|
752
|
+
const contentDigest = getHeader(opts.headers, "Content-Digest");
|
|
753
|
+
const expectedDigest = `sha-256=:${createHash("sha256").update(payloadBuf).digest("base64")}:`;
|
|
754
|
+
if (!contentDigest || contentDigest !== expectedDigest) {
|
|
755
|
+
throw authError("AdCP webhook Content-Digest does not match the body.");
|
|
756
|
+
}
|
|
757
|
+
const method = (opts.method ?? "POST").toUpperCase();
|
|
758
|
+
const target = new URL(opts.url);
|
|
759
|
+
const baseLines = components.map((component) => {
|
|
760
|
+
switch (component) {
|
|
761
|
+
case "@method":
|
|
762
|
+
return `"@method": ${method}`;
|
|
763
|
+
case "@target-uri":
|
|
764
|
+
return `"@target-uri": ${canonicalTargetUri(opts.url)}`;
|
|
765
|
+
case "@authority":
|
|
766
|
+
return `"@authority": ${target.host.toLowerCase()}`;
|
|
767
|
+
default: {
|
|
768
|
+
const value = getHeader(opts.headers, component);
|
|
769
|
+
if (value === void 0) {
|
|
770
|
+
throw authError(`AdCP webhook covers "${component}" but the header is absent.`);
|
|
771
|
+
}
|
|
772
|
+
return `"${component}": ${value.trim()}`;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
baseLines.push(`"@signature-params": ${inputMember.slice(label.length + 1)}`);
|
|
777
|
+
const signatureBytes = Buffer.from(
|
|
778
|
+
signatureMember.slice(label.length + 1).replace(/^:|:$/g, ""),
|
|
779
|
+
"base64url"
|
|
780
|
+
);
|
|
781
|
+
const publicKey = createPublicKey({ key: jwk, format: "jwk" });
|
|
782
|
+
if (!cryptoVerify(null, Buffer.from(baseLines.join("\n"), "utf8"), publicKey, signatureBytes)) {
|
|
783
|
+
throw authError(
|
|
784
|
+
"AdCP webhook signature verification failed. The delivery may have been tampered with."
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
return {
|
|
788
|
+
event: JSON.parse(payloadBuf.toString("utf8")),
|
|
789
|
+
keyid,
|
|
790
|
+
nonce,
|
|
791
|
+
expires
|
|
792
|
+
};
|
|
793
|
+
}
|
|
624
794
|
var Webhooks = {
|
|
625
795
|
constructEvent,
|
|
626
796
|
constructEventLegacy,
|
|
627
|
-
parseSignatureHeader
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
// src/generated/index.ts
|
|
631
|
-
var generatedTypes = {
|
|
632
|
-
schemaCount: 99,
|
|
633
|
-
operationCount: 210,
|
|
634
|
-
schemas: ["Ad", "AdSettings", "AudienceAnalyticsResponse", "AudienceAttentionPrediction", "AudienceExportResponse", "AudienceLoyaltyFeaturesRow", "AudienceMetricsResponse", "AudienceSegment", "AudienceSocialAttention", "BatchResult", "BleScanResultData", "ChangelogEntry", "ChangelogResponse", "ContentFitSettings", "ContentPreferencesInput", "ContentPreferencesResponse", "ContentPreset", "CreatePartnerSensingRouteRequest", "CreativeClassification", "DashboardResponse", "DataExportSummaryResponse", "DemographicsResponse", "Device", "DeviceCapabilityPayload", "DeviceSummary", "DisplaySettingsInput", "DisplaySettingsResponse", "EarningsBreakdownResponse", "EarningsResponse", "Error", "FleetCommand", "FleetCommandEvent", "HeaderBiddingSettings", "HttpProbeResult", "MdnsNetworkDevice", "Pagination", "Partner", "PartnerAdDeliveryProfile", "PartnerDefaultStream", "PartnerDefaultStreamCreate", "PartnerDefaultStreamUpdate", "PartnerDeviceHeartbeatBody", "PartnerHeartbeatCommand", "PartnerHeartbeatData", "PartnerHeartbeatMeasurementMetadata", "PartnerHeartbeatQueryData", "PartnerHeartbeatQueryResponse", "PartnerHeartbeatRequest", "PartnerHeartbeatResponse", "PartnerHeartbeatTelemetry", "PartnerLBarContent", "PartnerLBarDisplayPreferences", "PartnerLBarSettings", "PartnerMonetizationRequirements", "PartnerNetworkConfig", "PartnerNetworkConfigUpdate", "PartnerPlay", "PartnerRegistrationDisplaySettings", "PartnerRequirement", "PartnerSafeHeartbeatTelemetry", "PartnerSelfPromoAd", "PartnerSelfPromoCreate", "PartnerSelfPromoPlacement", "PartnerStats", "PayoutResponse", "PlaygroundResult", "PlaygroundValidationError", "ProgrammaticSettingsInput", "ProgrammaticSettingsResponse", "RequestMetadataResponse", "ResolvedContentPolicy", "ResolvedSensingConfig", "RolePermissions", "SDKConfig", "ScreenAnalyticsResponse", "ScreenGroup", "ScreenGroupContentPolicy", "ScreenGroupContentPolicyInput", "ScreenGroupEvent", "ScreenGroupSensingConfig", "ScreenGroupSensingConfigInput", "SensingPerformanceMode", "SensingRoute", "SensingRouteAction", "SensingRouteCondition", "SensingSettingsResponse", "SensingThresholdCrossedEvent", "SsdpDeviceInfo", "TeamInviteResponse", "TeamMember", "Transaction", "UpdatePartnerSensingRouteRequest", "VastWaterfall", "VastWaterfallSource", "Webhook", "WebhookDelivery", "WebhookEvent", "WifiEnvironmentSnapshot", "WifiScanResult"],
|
|
635
|
-
operations: ["listPartnerSelfPromoAds", "createPartnerSelfPromoAd", "listPartnerSelfPromoPlacements", "schedulePartnerSelfPromo", "createPartnerSelfPromoUpload", "disableSelfPromoAd", "registerAgent", "getAnalytics", "getAudienceContinuity", "getAudienceProfile", "getBleSummary", "getContentPerformance", "searchCreativeCatalog", "getCreativeScenes", "getCrossScreenFlow", "getPartnerAnalyticsDashboard", "getDeviceSuiteSignature", "exportPartnerAnalytics", "exportPartnerAdsByDay", "getFleetHourly", "getHaloFunnel", "getLoyaltyClock", "getNetworkSummary", "getNetworkTrend", "getProximitySummary", "getRevenueAttentionCorrelation", "getSceneFeed", "getSceneIntelligence", "rotateApiKey", "getAudienceAnalytics", "exportAudienceData", "getAudienceHeatmap", "getLiveAudience", "getLookalikeScreens", "predictScreenAudience", "getResolvedHouseholdReach", "listAudienceSegments", "createAudienceSegment", "deleteAudienceSegment", "getAudienceSegment", "updateAudienceSegment", "matchAudienceSegment", "estimateSegmentReach", "generateSegmentVastTag", "getAudienceStats", "cancelBillingSubscription", "purchaseCredits", "createCreditCheckout", "listBillingPlans", "setupBilling", "createBillingSubscription", "getBillingSubscription", "getBillingUsage", "getUsageSummary", "stripeWebhookCallback", "getConnectDashboard", "getConnectEarningsChart", "getConnectScreenHealth", "listContentPresets", "getContentPreset", "submitPartnerCreative", "getPartnerCreative", "getDashboard", "exportProximityGraphEdges", "getDataExportSummary", "exportDataset", "getGeohashAudiencePanel", "getOutcomeAttribution", "registerDevice", "deleteDevice", "getDevice", "updateDevice", "getAdsForDevice", "ackDeviceCommand", "getDeviceHeartbeat", "heartbeat", "recordProgrammaticEvent", "listDevices", "registerDeviceV2", "registerDevicesBatch", "batchUpdateDeviceOrientation", "reconcileDevices", "partnerDocsBrowser", "getEarnings", "getEarningsBreakdown", "requestPayout", "getTransactions", "getActiveSSEConnections", "subscribeToEvents", "getFleetCommand", "getFleetAnalytics", "pushFleetCommand", "getFleetOverview", "updateFleetSettings", "recordImpressionGet", "recordImpressionPost", "recordImpressionsBatch", "getPartnerInfo", "listIntentCategories", "getIntentCategory", "getIntentPricing", "getMeasurementMethodology", "getNetworkConfig", "patchNetworkConfig", "updateNetworkConfig", "getNetworkTrends", "partnerPing", "partnerPingAuth", "recordPartnerPlay", "recordPartnerPlaysBatch", "getPricing", "recordProgrammaticEventLegacyAlias", "quickStart", "registerPartner", "generateSandboxEvent", "resetSandbox", "createSandboxScreen", "getSandboxStatus", "listScreenGroups", "createScreenGroup", "deleteScreenGroup", "getScreenGroup", "updateScreenGroup", "getScreenGroupAnalytics", "createScreenGroupCommand", "getScreenGroupContentPolicy", "updateScreenGroupContentPolicy", "removeScreenGroupMembers", "listScreenGroupMembers", "addScreenGroupMembers", "getScreenGroupSensingConfig", "updateScreenGroupSensingConfig", "getScreenAudienceSegments", "batchUpdateContentPreferences", "batchUpdateDisplaySettings", "batchUpdateProgrammaticSettings", "resolvePartnerScreen", "getScreenAnalytics", "getPartnerScreenAtmosphere", "getPartnerScreenIntelligence", "getPartnerScreenTasteProfile", "getPartnerScreenTraffic", "getPartnerScreenVAS", "applyContentPreset", "getAudienceMetrics", "getContentPreferences", "patchContentPreferences", "updateContentPreferences", "listPartnerDefaultStreams", "createPartnerDefaultStream", "createPartnerDefaultStreamImages", "reorderPartnerDefaultStreams", "createPartnerDefaultStreamUpload", "deletePartnerDefaultStream", "updatePartnerDefaultStream", "getScreenDemographics", "getDisplaySettings", "patchDisplaySettings", "updateDisplaySettings", "getPartnerLBarSettings", "updatePartnerLBarSettings", "updatePartnerLBarLiveUpdates", "getProgrammaticSettings", "patchProgrammaticSettings", "updateProgrammaticSettings", "getResolvedContentPolicy", "getResolvedSensingConfig", "getScreenSensingChips", "interpretScreenSensingIntent", "deployScreenSensingProfile", "getScreenSensingSettings", "updateScreenSensingSettings", "listPartnerSensingRoutes", "createPartnerSensingRoute", "deletePartnerSensingRoute", "getPartnerSensingRoute", "updatePartnerSensingRoute", "createStripeConnectAccount", "getStripeLoginLink", "getStripeOnboardingLink", "requestStripePayout", "getStripePayoutHistory", "getStripeAccountStatus", "handleStripeWebhook", "listTeamMembers", "acceptInvitation", "inviteTeamMember", "getInvitationByToken", "resendInvitation", "removeTeamMember", "changeTeamMemberRole", "getTrackingPublicKey", "recordTrackingBatch", "getUsageSummary__get__usage", "getUsageDashboard", "getRateLimitStatus", "queryChangelog", "playgroundDryRun", "getRequestMetadata", "getVastConfig", "getVenuesNearby", "getVenueAtmosphere", "getVenueTraffic", "getVenueIntelligence", "listWebhooks", "createWebhook", "deleteWebhook", "updateWebhook", "getWebhookDeliveries", "testWebhook"]
|
|
797
|
+
parseSignatureHeader,
|
|
798
|
+
verifyAdcpWebhook
|
|
636
799
|
};
|
|
637
800
|
|
|
638
|
-
export { AnalyticsClient, AudienceClient, BillingClient, DataClient, DevicesClient, EarningsClient, ErrorsClient, FleetClient, ImpressionsClient, RoutesClient, ScreensClient, TrillboardsClient, TrillboardsError, VERSION, Webhooks, WebhooksClient, constructEvent, constructEventLegacy,
|
|
801
|
+
export { ADCP_WEBHOOK_SIGNING_TAG, AnalyticsClient, AudienceClient, BillingClient, DataClient, DevicesClient, EarningsClient, ErrorsClient, FleetClient, ImpressionsClient, RoutesClient, ScreensClient, TrillboardsClient, TrillboardsError, VERSION, Webhooks, WebhooksClient, constructEvent, constructEventLegacy, parseSignatureHeader, retryWithBackoff, verifyAdcpWebhook };
|
|
639
802
|
//# sourceMappingURL=index.mjs.map
|
|
640
803
|
//# sourceMappingURL=index.mjs.map
|