@akira-io/billing-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/checkout.cjs +10 -0
- package/dist/checkout.cjs.map +1 -0
- package/dist/checkout.d.cts +3 -0
- package/dist/checkout.d.ts +3 -0
- package/dist/checkout.js +8 -0
- package/dist/checkout.js.map +1 -0
- package/dist/client-DpOXhuxx.d.cts +141 -0
- package/dist/client-DpOXhuxx.d.ts +141 -0
- package/dist/client.cjs +179 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +1 -0
- package/dist/client.d.ts +1 -0
- package/dist/client.js +176 -0
- package/dist/client.js.map +1 -0
- package/dist/downloads.cjs +51 -0
- package/dist/downloads.cjs.map +1 -0
- package/dist/downloads.d.cts +34 -0
- package/dist/downloads.d.ts +34 -0
- package/dist/downloads.js +46 -0
- package/dist/downloads.js.map +1 -0
- package/dist/helpers.cjs +116 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +55 -0
- package/dist/helpers.d.ts +55 -0
- package/dist/helpers.js +109 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.cjs +439 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +416 -0
- package/dist/index.js.map +1 -0
- package/dist/pricing.cjs +104 -0
- package/dist/pricing.cjs.map +1 -0
- package/dist/pricing.d.cts +15 -0
- package/dist/pricing.d.ts +15 -0
- package/dist/pricing.js +101 -0
- package/dist/pricing.js.map +1 -0
- package/dist/react.cjs +219 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +39 -0
- package/dist/react.d.ts +39 -0
- package/dist/react.js +215 -0
- package/dist/react.js.map +1 -0
- package/dist/types-CH4Vkivj.d.cts +59 -0
- package/dist/types-CH4Vkivj.d.ts +59 -0
- package/dist/vue.cjs +218 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +32 -0
- package/dist/vue.d.ts +32 -0
- package/dist/vue.js +214 -0
- package/dist/vue.js.map +1 -0
- package/package.json +113 -0
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/signature.ts
|
|
4
|
+
var HEADER_PRODUCT = "X-Akira-Product";
|
|
5
|
+
var HEADER_TIMESTAMP = "X-Akira-Timestamp";
|
|
6
|
+
var HEADER_NONCE = "X-Akira-Nonce";
|
|
7
|
+
var HEADER_SIGNATURE = "X-Akira-Signature";
|
|
8
|
+
async function sha256Hex(bytes) {
|
|
9
|
+
const digest = await getSubtle().digest("SHA-256", bytes);
|
|
10
|
+
return bufferToHex(new Uint8Array(digest));
|
|
11
|
+
}
|
|
12
|
+
function getSubtle() {
|
|
13
|
+
const subtle = globalThis.crypto?.subtle;
|
|
14
|
+
if (!subtle) {
|
|
15
|
+
throw new Error("Web Crypto SubtleCrypto API is not available in this runtime.");
|
|
16
|
+
}
|
|
17
|
+
return subtle;
|
|
18
|
+
}
|
|
19
|
+
function bufferToHex(buffer) {
|
|
20
|
+
let out = "";
|
|
21
|
+
for (const byte of buffer) {
|
|
22
|
+
out += byte.toString(16).padStart(2, "0");
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
function newNonce() {
|
|
27
|
+
const buf = new Uint8Array(16);
|
|
28
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
29
|
+
globalThis.crypto.getRandomValues(buf);
|
|
30
|
+
} else {
|
|
31
|
+
for (let i = 0; i < buf.length; i += 1) {
|
|
32
|
+
buf[i] = Math.floor(Math.random() * 256);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return bufferToHex(buf);
|
|
36
|
+
}
|
|
37
|
+
async function canonical(productSlug, timestamp, nonce, method, path, body) {
|
|
38
|
+
const bodyHash = await sha256Hex(body);
|
|
39
|
+
return `${productSlug}
|
|
40
|
+
${timestamp}
|
|
41
|
+
${nonce}
|
|
42
|
+
${method.toUpperCase()}
|
|
43
|
+
${path}
|
|
44
|
+
${bodyHash}`;
|
|
45
|
+
}
|
|
46
|
+
async function sign(productSecret, canonicalString) {
|
|
47
|
+
const subtle = getSubtle();
|
|
48
|
+
const keyData = new TextEncoder().encode(productSecret);
|
|
49
|
+
const key = await subtle.importKey(
|
|
50
|
+
"raw",
|
|
51
|
+
keyData,
|
|
52
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
53
|
+
false,
|
|
54
|
+
["sign"]
|
|
55
|
+
);
|
|
56
|
+
const sig = await subtle.sign("HMAC", key, new TextEncoder().encode(canonicalString));
|
|
57
|
+
return bufferToHex(new Uint8Array(sig));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/client.ts
|
|
61
|
+
var BillingApiError = class extends Error {
|
|
62
|
+
status;
|
|
63
|
+
code;
|
|
64
|
+
constructor(status, code) {
|
|
65
|
+
super(`billing api ${status}: ${code}`);
|
|
66
|
+
this.status = status;
|
|
67
|
+
this.code = code;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var BillingClient = class {
|
|
71
|
+
baseUrl;
|
|
72
|
+
productSlug;
|
|
73
|
+
productSecret;
|
|
74
|
+
customerToken;
|
|
75
|
+
fetcher;
|
|
76
|
+
constructor(config) {
|
|
77
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
78
|
+
this.productSlug = config.productSlug;
|
|
79
|
+
this.productSecret = config.productSecret;
|
|
80
|
+
this.customerToken = config.customerToken;
|
|
81
|
+
this.fetcher = config.fetcher ?? globalThis.fetch;
|
|
82
|
+
if (!this.fetcher) {
|
|
83
|
+
throw new Error("No fetch implementation available. Pass `fetcher` or use a runtime with global fetch.");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
setCustomerToken(token) {
|
|
87
|
+
this.customerToken = token;
|
|
88
|
+
}
|
|
89
|
+
async requestOtp(payload) {
|
|
90
|
+
await this.signed("POST", "/api/auth/customer/otp/request", payload);
|
|
91
|
+
}
|
|
92
|
+
async verifyOtp(payload) {
|
|
93
|
+
const res = await this.signed("POST", "/api/auth/customer/otp/verify", payload);
|
|
94
|
+
this.setCustomerToken(res.access_token);
|
|
95
|
+
return res;
|
|
96
|
+
}
|
|
97
|
+
async customerMe() {
|
|
98
|
+
return this.signed("GET", "/api/me");
|
|
99
|
+
}
|
|
100
|
+
async licenseCheck(payload) {
|
|
101
|
+
return this.signed("POST", "/api/licenses/check", payload);
|
|
102
|
+
}
|
|
103
|
+
async licenseActivate(payload) {
|
|
104
|
+
return this.signed("POST", "/api/licenses/activate", payload);
|
|
105
|
+
}
|
|
106
|
+
async licenseRefresh(payload) {
|
|
107
|
+
return this.signed("POST", "/api/licenses/refresh", payload);
|
|
108
|
+
}
|
|
109
|
+
async entitlements() {
|
|
110
|
+
return this.signed("GET", "/api/me/entitlements");
|
|
111
|
+
}
|
|
112
|
+
async billingPortal(returnUrl) {
|
|
113
|
+
const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;
|
|
114
|
+
return this.signed("GET", path);
|
|
115
|
+
}
|
|
116
|
+
async trackUsage(payload) {
|
|
117
|
+
return this.signed("POST", "/api/me/usage", payload);
|
|
118
|
+
}
|
|
119
|
+
async publicLicenseKeys() {
|
|
120
|
+
return this.unsigned("GET", "/api/v1/license-keys/public");
|
|
121
|
+
}
|
|
122
|
+
async signed(method, path, body) {
|
|
123
|
+
const bodyBytes = body === void 0 ? new Uint8Array() : new TextEncoder().encode(JSON.stringify(body));
|
|
124
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
125
|
+
const nonce = newNonce();
|
|
126
|
+
const canonicalString = await canonical(this.productSlug, timestamp, nonce, method, path, bodyBytes);
|
|
127
|
+
const signature = await sign(this.productSecret, canonicalString);
|
|
128
|
+
const headers = {
|
|
129
|
+
Accept: "application/json",
|
|
130
|
+
[HEADER_PRODUCT]: this.productSlug,
|
|
131
|
+
[HEADER_TIMESTAMP]: String(timestamp),
|
|
132
|
+
[HEADER_NONCE]: nonce,
|
|
133
|
+
[HEADER_SIGNATURE]: signature
|
|
134
|
+
};
|
|
135
|
+
if (bodyBytes.length > 0) headers["Content-Type"] = "application/json";
|
|
136
|
+
if (this.customerToken) headers.Authorization = `Bearer ${this.customerToken}`;
|
|
137
|
+
const res = await this.fetcher(`${this.baseUrl}${path}`, {
|
|
138
|
+
method,
|
|
139
|
+
headers,
|
|
140
|
+
body: bodyBytes.length > 0 ? bodyBytes : void 0
|
|
141
|
+
});
|
|
142
|
+
return this.parseResponse(res);
|
|
143
|
+
}
|
|
144
|
+
async unsigned(method, path, body) {
|
|
145
|
+
const headers = { Accept: "application/json" };
|
|
146
|
+
let bodyInit;
|
|
147
|
+
if (body !== void 0) {
|
|
148
|
+
headers["Content-Type"] = "application/json";
|
|
149
|
+
bodyInit = JSON.stringify(body);
|
|
150
|
+
}
|
|
151
|
+
const res = await this.fetcher(`${this.baseUrl}${path}`, { method, headers, body: bodyInit });
|
|
152
|
+
return this.parseResponse(res);
|
|
153
|
+
}
|
|
154
|
+
async parseResponse(res) {
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
let code = "";
|
|
157
|
+
try {
|
|
158
|
+
const parsed = await res.json();
|
|
159
|
+
code = parsed?.error ?? "";
|
|
160
|
+
} catch {
|
|
161
|
+
try {
|
|
162
|
+
code = await res.text();
|
|
163
|
+
} catch {
|
|
164
|
+
code = "";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new BillingApiError(res.status, code);
|
|
168
|
+
}
|
|
169
|
+
if (res.status === 204) {
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
return await res.json();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
exports.BillingApiError = BillingApiError;
|
|
177
|
+
exports.BillingClient = BillingClient;
|
|
178
|
+
//# sourceMappingURL=client.cjs.map
|
|
179
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/signature.ts","../src/client.ts"],"names":[],"mappings":"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","file":"client.cjs","sourcesContent":["export const HEADER_PRODUCT = 'X-Akira-Product';\nexport const HEADER_TIMESTAMP = 'X-Akira-Timestamp';\nexport const HEADER_NONCE = 'X-Akira-Nonce';\nexport const HEADER_SIGNATURE = 'X-Akira-Signature';\n\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n const digest = await getSubtle().digest('SHA-256', bytes as BufferSource);\n return bufferToHex(new Uint8Array(digest));\n}\n\nfunction getSubtle(): SubtleCrypto {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new Error('Web Crypto SubtleCrypto API is not available in this runtime.');\n }\n return subtle;\n}\n\nfunction bufferToHex(buffer: Uint8Array): string {\n let out = '';\n for (const byte of buffer) {\n out += byte.toString(16).padStart(2, '0');\n }\n return out;\n}\n\nexport function newNonce(): string {\n const buf = new Uint8Array(16);\n if (globalThis.crypto?.getRandomValues) {\n globalThis.crypto.getRandomValues(buf);\n } else {\n for (let i = 0; i < buf.length; i += 1) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n }\n return bufferToHex(buf);\n}\n\nexport async function canonical(\n productSlug: string,\n timestamp: number,\n nonce: string,\n method: string,\n path: string,\n body: Uint8Array,\n): Promise<string> {\n const bodyHash = await sha256Hex(body);\n return `${productSlug}\\n${timestamp}\\n${nonce}\\n${method.toUpperCase()}\\n${path}\\n${bodyHash}`;\n}\n\nexport async function sign(productSecret: string, canonicalString: string): Promise<string> {\n const subtle = getSubtle();\n const keyData = new TextEncoder().encode(productSecret) as BufferSource;\n const key = await subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const sig = await subtle.sign('HMAC', key, new TextEncoder().encode(canonicalString) as BufferSource);\n return bufferToHex(new Uint8Array(sig));\n}\n","import {\n HEADER_NONCE,\n HEADER_PRODUCT,\n HEADER_SIGNATURE,\n HEADER_TIMESTAMP,\n canonical,\n newNonce,\n sign,\n} from './signature';\nimport type {\n BillingPortalResponse,\n Customer,\n EntitlementsResponse,\n LicenseActivatePayload,\n LicenseActivateResponse,\n LicenseCheckPayload,\n LicenseCheckResponse,\n LicensePublicKeysResponse,\n LicenseRefreshPayload,\n OtpRequestPayload,\n OtpVerifyPayload,\n OtpVerifyResponse,\n UsagePayload,\n UsageResponse,\n} from './client-types';\n\nexport interface BillingClientConfig {\n baseUrl: string;\n productSlug: string;\n productSecret: string;\n customerToken?: string;\n fetcher?: typeof fetch;\n}\n\nexport class BillingApiError extends Error {\n status: number;\n code: string;\n constructor(status: number, code: string) {\n super(`billing api ${status}: ${code}`);\n this.status = status;\n this.code = code;\n }\n}\n\nexport class BillingClient {\n private readonly baseUrl: string;\n private readonly productSlug: string;\n private readonly productSecret: string;\n private customerToken: string | undefined;\n private readonly fetcher: typeof fetch;\n\n constructor(config: BillingClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.productSlug = config.productSlug;\n this.productSecret = config.productSecret;\n this.customerToken = config.customerToken;\n this.fetcher = config.fetcher ?? globalThis.fetch;\n if (!this.fetcher) {\n throw new Error('No fetch implementation available. Pass `fetcher` or use a runtime with global fetch.');\n }\n }\n\n setCustomerToken(token: string): void {\n this.customerToken = token;\n }\n\n async requestOtp(payload: OtpRequestPayload): Promise<void> {\n await this.signed('POST', '/api/auth/customer/otp/request', payload);\n }\n\n async verifyOtp(payload: OtpVerifyPayload): Promise<OtpVerifyResponse> {\n const res = await this.signed<OtpVerifyResponse>('POST', '/api/auth/customer/otp/verify', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async customerMe(): Promise<Customer> {\n return this.signed<Customer>('GET', '/api/me');\n }\n\n async licenseCheck(payload: LicenseCheckPayload): Promise<LicenseCheckResponse> {\n return this.signed<LicenseCheckResponse>('POST', '/api/licenses/check', payload);\n }\n\n async licenseActivate(payload: LicenseActivatePayload): Promise<LicenseActivateResponse> {\n return this.signed<LicenseActivateResponse>('POST', '/api/licenses/activate', payload);\n }\n\n async licenseRefresh(payload: LicenseRefreshPayload): Promise<LicenseActivateResponse> {\n return this.signed<LicenseActivateResponse>('POST', '/api/licenses/refresh', payload);\n }\n\n async entitlements(): Promise<EntitlementsResponse> {\n return this.signed<EntitlementsResponse>('GET', '/api/me/entitlements');\n }\n\n async billingPortal(returnUrl: string): Promise<BillingPortalResponse> {\n const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;\n return this.signed<BillingPortalResponse>('GET', path);\n }\n\n async trackUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/me/usage', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n private async signed<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const bodyBytes = body === undefined ? new Uint8Array() : new TextEncoder().encode(JSON.stringify(body));\n const timestamp = Math.floor(Date.now() / 1000);\n const nonce = newNonce();\n const canonicalString = await canonical(this.productSlug, timestamp, nonce, method, path, bodyBytes);\n const signature = await sign(this.productSecret, canonicalString);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n [HEADER_PRODUCT]: this.productSlug,\n [HEADER_TIMESTAMP]: String(timestamp),\n [HEADER_NONCE]: nonce,\n [HEADER_SIGNATURE]: signature,\n };\n if (bodyBytes.length > 0) headers['Content-Type'] = 'application/json';\n if (this.customerToken) headers.Authorization = `Bearer ${this.customerToken}`;\n\n const res = await this.fetcher(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: bodyBytes.length > 0 ? bodyBytes : undefined,\n });\n\n return this.parseResponse<T>(res);\n }\n\n private async unsigned<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const headers: Record<string, string> = { Accept: 'application/json' };\n let bodyInit: BodyInit | undefined;\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n bodyInit = JSON.stringify(body);\n }\n\n const res = await this.fetcher(`${this.baseUrl}${path}`, { method, headers, body: bodyInit });\n return this.parseResponse<T>(res);\n }\n\n private async parseResponse<T>(res: Response): Promise<T> {\n if (!res.ok) {\n let code = '';\n try {\n const parsed = (await res.json()) as { error?: string };\n code = parsed?.error ?? '';\n } catch {\n try {\n code = await res.text();\n } catch {\n code = '';\n }\n }\n throw new BillingApiError(res.status, code);\n }\n\n if (res.status === 204) {\n return undefined as T;\n }\n\n return (await res.json()) as T;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { B as BillingApiError, a as BillingClient, b as BillingClientConfig } from './client-DpOXhuxx.cjs';
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { B as BillingApiError, a as BillingClient, b as BillingClientConfig } from './client-DpOXhuxx.js';
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// src/signature.ts
|
|
2
|
+
var HEADER_PRODUCT = "X-Akira-Product";
|
|
3
|
+
var HEADER_TIMESTAMP = "X-Akira-Timestamp";
|
|
4
|
+
var HEADER_NONCE = "X-Akira-Nonce";
|
|
5
|
+
var HEADER_SIGNATURE = "X-Akira-Signature";
|
|
6
|
+
async function sha256Hex(bytes) {
|
|
7
|
+
const digest = await getSubtle().digest("SHA-256", bytes);
|
|
8
|
+
return bufferToHex(new Uint8Array(digest));
|
|
9
|
+
}
|
|
10
|
+
function getSubtle() {
|
|
11
|
+
const subtle = globalThis.crypto?.subtle;
|
|
12
|
+
if (!subtle) {
|
|
13
|
+
throw new Error("Web Crypto SubtleCrypto API is not available in this runtime.");
|
|
14
|
+
}
|
|
15
|
+
return subtle;
|
|
16
|
+
}
|
|
17
|
+
function bufferToHex(buffer) {
|
|
18
|
+
let out = "";
|
|
19
|
+
for (const byte of buffer) {
|
|
20
|
+
out += byte.toString(16).padStart(2, "0");
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
function newNonce() {
|
|
25
|
+
const buf = new Uint8Array(16);
|
|
26
|
+
if (globalThis.crypto?.getRandomValues) {
|
|
27
|
+
globalThis.crypto.getRandomValues(buf);
|
|
28
|
+
} else {
|
|
29
|
+
for (let i = 0; i < buf.length; i += 1) {
|
|
30
|
+
buf[i] = Math.floor(Math.random() * 256);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return bufferToHex(buf);
|
|
34
|
+
}
|
|
35
|
+
async function canonical(productSlug, timestamp, nonce, method, path, body) {
|
|
36
|
+
const bodyHash = await sha256Hex(body);
|
|
37
|
+
return `${productSlug}
|
|
38
|
+
${timestamp}
|
|
39
|
+
${nonce}
|
|
40
|
+
${method.toUpperCase()}
|
|
41
|
+
${path}
|
|
42
|
+
${bodyHash}`;
|
|
43
|
+
}
|
|
44
|
+
async function sign(productSecret, canonicalString) {
|
|
45
|
+
const subtle = getSubtle();
|
|
46
|
+
const keyData = new TextEncoder().encode(productSecret);
|
|
47
|
+
const key = await subtle.importKey(
|
|
48
|
+
"raw",
|
|
49
|
+
keyData,
|
|
50
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
51
|
+
false,
|
|
52
|
+
["sign"]
|
|
53
|
+
);
|
|
54
|
+
const sig = await subtle.sign("HMAC", key, new TextEncoder().encode(canonicalString));
|
|
55
|
+
return bufferToHex(new Uint8Array(sig));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/client.ts
|
|
59
|
+
var BillingApiError = class extends Error {
|
|
60
|
+
status;
|
|
61
|
+
code;
|
|
62
|
+
constructor(status, code) {
|
|
63
|
+
super(`billing api ${status}: ${code}`);
|
|
64
|
+
this.status = status;
|
|
65
|
+
this.code = code;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var BillingClient = class {
|
|
69
|
+
baseUrl;
|
|
70
|
+
productSlug;
|
|
71
|
+
productSecret;
|
|
72
|
+
customerToken;
|
|
73
|
+
fetcher;
|
|
74
|
+
constructor(config) {
|
|
75
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
76
|
+
this.productSlug = config.productSlug;
|
|
77
|
+
this.productSecret = config.productSecret;
|
|
78
|
+
this.customerToken = config.customerToken;
|
|
79
|
+
this.fetcher = config.fetcher ?? globalThis.fetch;
|
|
80
|
+
if (!this.fetcher) {
|
|
81
|
+
throw new Error("No fetch implementation available. Pass `fetcher` or use a runtime with global fetch.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
setCustomerToken(token) {
|
|
85
|
+
this.customerToken = token;
|
|
86
|
+
}
|
|
87
|
+
async requestOtp(payload) {
|
|
88
|
+
await this.signed("POST", "/api/auth/customer/otp/request", payload);
|
|
89
|
+
}
|
|
90
|
+
async verifyOtp(payload) {
|
|
91
|
+
const res = await this.signed("POST", "/api/auth/customer/otp/verify", payload);
|
|
92
|
+
this.setCustomerToken(res.access_token);
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
async customerMe() {
|
|
96
|
+
return this.signed("GET", "/api/me");
|
|
97
|
+
}
|
|
98
|
+
async licenseCheck(payload) {
|
|
99
|
+
return this.signed("POST", "/api/licenses/check", payload);
|
|
100
|
+
}
|
|
101
|
+
async licenseActivate(payload) {
|
|
102
|
+
return this.signed("POST", "/api/licenses/activate", payload);
|
|
103
|
+
}
|
|
104
|
+
async licenseRefresh(payload) {
|
|
105
|
+
return this.signed("POST", "/api/licenses/refresh", payload);
|
|
106
|
+
}
|
|
107
|
+
async entitlements() {
|
|
108
|
+
return this.signed("GET", "/api/me/entitlements");
|
|
109
|
+
}
|
|
110
|
+
async billingPortal(returnUrl) {
|
|
111
|
+
const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;
|
|
112
|
+
return this.signed("GET", path);
|
|
113
|
+
}
|
|
114
|
+
async trackUsage(payload) {
|
|
115
|
+
return this.signed("POST", "/api/me/usage", payload);
|
|
116
|
+
}
|
|
117
|
+
async publicLicenseKeys() {
|
|
118
|
+
return this.unsigned("GET", "/api/v1/license-keys/public");
|
|
119
|
+
}
|
|
120
|
+
async signed(method, path, body) {
|
|
121
|
+
const bodyBytes = body === void 0 ? new Uint8Array() : new TextEncoder().encode(JSON.stringify(body));
|
|
122
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
123
|
+
const nonce = newNonce();
|
|
124
|
+
const canonicalString = await canonical(this.productSlug, timestamp, nonce, method, path, bodyBytes);
|
|
125
|
+
const signature = await sign(this.productSecret, canonicalString);
|
|
126
|
+
const headers = {
|
|
127
|
+
Accept: "application/json",
|
|
128
|
+
[HEADER_PRODUCT]: this.productSlug,
|
|
129
|
+
[HEADER_TIMESTAMP]: String(timestamp),
|
|
130
|
+
[HEADER_NONCE]: nonce,
|
|
131
|
+
[HEADER_SIGNATURE]: signature
|
|
132
|
+
};
|
|
133
|
+
if (bodyBytes.length > 0) headers["Content-Type"] = "application/json";
|
|
134
|
+
if (this.customerToken) headers.Authorization = `Bearer ${this.customerToken}`;
|
|
135
|
+
const res = await this.fetcher(`${this.baseUrl}${path}`, {
|
|
136
|
+
method,
|
|
137
|
+
headers,
|
|
138
|
+
body: bodyBytes.length > 0 ? bodyBytes : void 0
|
|
139
|
+
});
|
|
140
|
+
return this.parseResponse(res);
|
|
141
|
+
}
|
|
142
|
+
async unsigned(method, path, body) {
|
|
143
|
+
const headers = { Accept: "application/json" };
|
|
144
|
+
let bodyInit;
|
|
145
|
+
if (body !== void 0) {
|
|
146
|
+
headers["Content-Type"] = "application/json";
|
|
147
|
+
bodyInit = JSON.stringify(body);
|
|
148
|
+
}
|
|
149
|
+
const res = await this.fetcher(`${this.baseUrl}${path}`, { method, headers, body: bodyInit });
|
|
150
|
+
return this.parseResponse(res);
|
|
151
|
+
}
|
|
152
|
+
async parseResponse(res) {
|
|
153
|
+
if (!res.ok) {
|
|
154
|
+
let code = "";
|
|
155
|
+
try {
|
|
156
|
+
const parsed = await res.json();
|
|
157
|
+
code = parsed?.error ?? "";
|
|
158
|
+
} catch {
|
|
159
|
+
try {
|
|
160
|
+
code = await res.text();
|
|
161
|
+
} catch {
|
|
162
|
+
code = "";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
throw new BillingApiError(res.status, code);
|
|
166
|
+
}
|
|
167
|
+
if (res.status === 204) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
return await res.json();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export { BillingApiError, BillingClient };
|
|
175
|
+
//# sourceMappingURL=client.js.map
|
|
176
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/signature.ts","../src/client.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["export const HEADER_PRODUCT = 'X-Akira-Product';\nexport const HEADER_TIMESTAMP = 'X-Akira-Timestamp';\nexport const HEADER_NONCE = 'X-Akira-Nonce';\nexport const HEADER_SIGNATURE = 'X-Akira-Signature';\n\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n const digest = await getSubtle().digest('SHA-256', bytes as BufferSource);\n return bufferToHex(new Uint8Array(digest));\n}\n\nfunction getSubtle(): SubtleCrypto {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new Error('Web Crypto SubtleCrypto API is not available in this runtime.');\n }\n return subtle;\n}\n\nfunction bufferToHex(buffer: Uint8Array): string {\n let out = '';\n for (const byte of buffer) {\n out += byte.toString(16).padStart(2, '0');\n }\n return out;\n}\n\nexport function newNonce(): string {\n const buf = new Uint8Array(16);\n if (globalThis.crypto?.getRandomValues) {\n globalThis.crypto.getRandomValues(buf);\n } else {\n for (let i = 0; i < buf.length; i += 1) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n }\n return bufferToHex(buf);\n}\n\nexport async function canonical(\n productSlug: string,\n timestamp: number,\n nonce: string,\n method: string,\n path: string,\n body: Uint8Array,\n): Promise<string> {\n const bodyHash = await sha256Hex(body);\n return `${productSlug}\\n${timestamp}\\n${nonce}\\n${method.toUpperCase()}\\n${path}\\n${bodyHash}`;\n}\n\nexport async function sign(productSecret: string, canonicalString: string): Promise<string> {\n const subtle = getSubtle();\n const keyData = new TextEncoder().encode(productSecret) as BufferSource;\n const key = await subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const sig = await subtle.sign('HMAC', key, new TextEncoder().encode(canonicalString) as BufferSource);\n return bufferToHex(new Uint8Array(sig));\n}\n","import {\n HEADER_NONCE,\n HEADER_PRODUCT,\n HEADER_SIGNATURE,\n HEADER_TIMESTAMP,\n canonical,\n newNonce,\n sign,\n} from './signature';\nimport type {\n BillingPortalResponse,\n Customer,\n EntitlementsResponse,\n LicenseActivatePayload,\n LicenseActivateResponse,\n LicenseCheckPayload,\n LicenseCheckResponse,\n LicensePublicKeysResponse,\n LicenseRefreshPayload,\n OtpRequestPayload,\n OtpVerifyPayload,\n OtpVerifyResponse,\n UsagePayload,\n UsageResponse,\n} from './client-types';\n\nexport interface BillingClientConfig {\n baseUrl: string;\n productSlug: string;\n productSecret: string;\n customerToken?: string;\n fetcher?: typeof fetch;\n}\n\nexport class BillingApiError extends Error {\n status: number;\n code: string;\n constructor(status: number, code: string) {\n super(`billing api ${status}: ${code}`);\n this.status = status;\n this.code = code;\n }\n}\n\nexport class BillingClient {\n private readonly baseUrl: string;\n private readonly productSlug: string;\n private readonly productSecret: string;\n private customerToken: string | undefined;\n private readonly fetcher: typeof fetch;\n\n constructor(config: BillingClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.productSlug = config.productSlug;\n this.productSecret = config.productSecret;\n this.customerToken = config.customerToken;\n this.fetcher = config.fetcher ?? globalThis.fetch;\n if (!this.fetcher) {\n throw new Error('No fetch implementation available. Pass `fetcher` or use a runtime with global fetch.');\n }\n }\n\n setCustomerToken(token: string): void {\n this.customerToken = token;\n }\n\n async requestOtp(payload: OtpRequestPayload): Promise<void> {\n await this.signed('POST', '/api/auth/customer/otp/request', payload);\n }\n\n async verifyOtp(payload: OtpVerifyPayload): Promise<OtpVerifyResponse> {\n const res = await this.signed<OtpVerifyResponse>('POST', '/api/auth/customer/otp/verify', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async customerMe(): Promise<Customer> {\n return this.signed<Customer>('GET', '/api/me');\n }\n\n async licenseCheck(payload: LicenseCheckPayload): Promise<LicenseCheckResponse> {\n return this.signed<LicenseCheckResponse>('POST', '/api/licenses/check', payload);\n }\n\n async licenseActivate(payload: LicenseActivatePayload): Promise<LicenseActivateResponse> {\n return this.signed<LicenseActivateResponse>('POST', '/api/licenses/activate', payload);\n }\n\n async licenseRefresh(payload: LicenseRefreshPayload): Promise<LicenseActivateResponse> {\n return this.signed<LicenseActivateResponse>('POST', '/api/licenses/refresh', payload);\n }\n\n async entitlements(): Promise<EntitlementsResponse> {\n return this.signed<EntitlementsResponse>('GET', '/api/me/entitlements');\n }\n\n async billingPortal(returnUrl: string): Promise<BillingPortalResponse> {\n const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;\n return this.signed<BillingPortalResponse>('GET', path);\n }\n\n async trackUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/me/usage', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n private async signed<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const bodyBytes = body === undefined ? new Uint8Array() : new TextEncoder().encode(JSON.stringify(body));\n const timestamp = Math.floor(Date.now() / 1000);\n const nonce = newNonce();\n const canonicalString = await canonical(this.productSlug, timestamp, nonce, method, path, bodyBytes);\n const signature = await sign(this.productSecret, canonicalString);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n [HEADER_PRODUCT]: this.productSlug,\n [HEADER_TIMESTAMP]: String(timestamp),\n [HEADER_NONCE]: nonce,\n [HEADER_SIGNATURE]: signature,\n };\n if (bodyBytes.length > 0) headers['Content-Type'] = 'application/json';\n if (this.customerToken) headers.Authorization = `Bearer ${this.customerToken}`;\n\n const res = await this.fetcher(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: bodyBytes.length > 0 ? bodyBytes : undefined,\n });\n\n return this.parseResponse<T>(res);\n }\n\n private async unsigned<T = unknown>(method: string, path: string, body?: unknown): Promise<T> {\n const headers: Record<string, string> = { Accept: 'application/json' };\n let bodyInit: BodyInit | undefined;\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n bodyInit = JSON.stringify(body);\n }\n\n const res = await this.fetcher(`${this.baseUrl}${path}`, { method, headers, body: bodyInit });\n return this.parseResponse<T>(res);\n }\n\n private async parseResponse<T>(res: Response): Promise<T> {\n if (!res.ok) {\n let code = '';\n try {\n const parsed = (await res.json()) as { error?: string };\n code = parsed?.error ?? '';\n } catch {\n try {\n code = await res.text();\n } catch {\n code = '';\n }\n }\n throw new BillingApiError(res.status, code);\n }\n\n if (res.status === 204) {\n return undefined as T;\n }\n\n return (await res.json()) as T;\n }\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/downloads.ts
|
|
4
|
+
function downloadUrl(config) {
|
|
5
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
6
|
+
const path = `/api/v1/downloads/${config.product}/${config.channel}/${config.platform}`;
|
|
7
|
+
const params = new URLSearchParams();
|
|
8
|
+
for (const [k, v] of Object.entries(config.query ?? {})) {
|
|
9
|
+
if (v !== void 0 && v !== "") params.set(k, v);
|
|
10
|
+
}
|
|
11
|
+
const qs = params.toString();
|
|
12
|
+
return qs ? `${base}${path}?${qs}` : `${base}${path}`;
|
|
13
|
+
}
|
|
14
|
+
async function issueDownload(config) {
|
|
15
|
+
const f = config.fetcher ?? globalThis.fetch;
|
|
16
|
+
if (!f) {
|
|
17
|
+
throw new Error("No fetch implementation available. Pass a fetcher in config or use Node 18+.");
|
|
18
|
+
}
|
|
19
|
+
const url = downloadUrl(config);
|
|
20
|
+
const res = await f(url, { headers: { Accept: "application/json" } });
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
throw new Error(`download issue failed: HTTP ${res.status}`);
|
|
23
|
+
}
|
|
24
|
+
return await res.json();
|
|
25
|
+
}
|
|
26
|
+
function sendCompletionBeacon(beaconUrl) {
|
|
27
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
28
|
+
navigator.sendBeacon(beaconUrl);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (typeof fetch !== "undefined") {
|
|
32
|
+
void fetch(beaconUrl, { method: "POST", keepalive: true }).catch(() => {
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function triggerDownload(config) {
|
|
37
|
+
const issued = await issueDownload(config);
|
|
38
|
+
const delay = config.beaconDelayMs ?? 1500;
|
|
39
|
+
if (typeof window !== "undefined") {
|
|
40
|
+
window.location.href = issued.signedUrl;
|
|
41
|
+
setTimeout(() => sendCompletionBeacon(issued.beaconUrl), delay);
|
|
42
|
+
}
|
|
43
|
+
return issued;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
exports.downloadUrl = downloadUrl;
|
|
47
|
+
exports.issueDownload = issueDownload;
|
|
48
|
+
exports.sendCompletionBeacon = sendCompletionBeacon;
|
|
49
|
+
exports.triggerDownload = triggerDownload;
|
|
50
|
+
//# sourceMappingURL=downloads.cjs.map
|
|
51
|
+
//# sourceMappingURL=downloads.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/downloads.ts"],"names":[],"mappings":";;;AAcO,SAAS,YAAY,MAAA,EAAgG;AACxH,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,qBAAqB,MAAA,CAAO,OAAO,IAAI,MAAA,CAAO,OAAO,CAAA,CAAA,EAAI,MAAA,CAAO,QAAQ,CAAA,CAAA;AACrF,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,MAAA,CAAO,QAAQ,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA,EAAG;AACrD,IAAA,IAAI,MAAM,MAAA,IAAa,CAAA,KAAM,IAAI,MAAA,CAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,EACpD;AACA,EAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,EAAA,OAAO,EAAA,GAAK,CAAA,EAAG,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,CAAA,EAAG,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA;AACvD;AAOA,eAAsB,cAAc,MAAA,EAAiD;AACjF,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,OAAA,IAAW,UAAA,CAAW,KAAA;AACvC,EAAA,IAAI,CAAC,CAAA,EAAG;AACJ,IAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,EAClG;AAEA,EAAA,MAAM,GAAA,GAAM,YAAY,MAAM,CAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,MAAM,CAAA,CAAE,GAAA,EAAK,EAAE,SAAS,EAAE,MAAA,EAAQ,kBAAA,EAAmB,EAAG,CAAA;AAEpE,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACT,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,EAC/D;AAEA,EAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAC3B;AAOO,SAAS,qBAAqB,SAAA,EAAyB;AAC1D,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,OAAO,SAAA,CAAU,eAAe,UAAA,EAAY;AAChF,IAAA,SAAA,CAAU,WAAW,SAAS,CAAA;AAC9B,IAAA;AAAA,EACJ;AAEA,EAAA,IAAI,OAAO,UAAU,WAAA,EAAa;AAC9B,IAAA,KAAK,KAAA,CAAM,SAAA,EAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,WAAW,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAC7E;AACJ;AAOA,eAAsB,gBAAgB,MAAA,EAAiD;AACnF,EAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,MAAM,CAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,OAAO,aAAA,IAAiB,IAAA;AAEtC,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAA,CAAO,QAAA,CAAS,OAAO,MAAA,CAAO,SAAA;AAC9B,IAAA,UAAA,CAAW,MAAM,oBAAA,CAAqB,MAAA,CAAO,SAAS,GAAG,KAAK,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,MAAA;AACX","file":"downloads.cjs","sourcesContent":["import type { AssetPlatform, IssuedDownload, ReleaseChannel } from './types';\n\nexport type DownloadConfig = {\n baseUrl: string;\n product: string;\n channel: ReleaseChannel;\n platform: AssetPlatform;\n /** Optional UTM + landing tracking. */\n query?: Record<string, string | undefined>;\n /** Delay before firing the completion beacon, ms. Default 1500. */\n beaconDelayMs?: number;\n fetcher?: typeof fetch;\n};\n\nexport function downloadUrl(config: Pick<DownloadConfig, 'baseUrl' | 'product' | 'channel' | 'platform' | 'query'>): string {\n const base = config.baseUrl.replace(/\\/$/, '');\n const path = `/api/v1/downloads/${config.product}/${config.channel}/${config.platform}`;\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(config.query ?? {})) {\n if (v !== undefined && v !== '') params.set(k, v);\n }\n const qs = params.toString();\n return qs ? `${base}${path}?${qs}` : `${base}${path}`;\n}\n\n/**\n * Issues a download via the billing API and returns the signed asset URL +\n * beacon URL without redirecting. Useful when you want full control of the\n * UX (e.g. fetch then trigger your own `<a download>` flow).\n */\nexport async function issueDownload(config: DownloadConfig): Promise<IssuedDownload> {\n const f = config.fetcher ?? globalThis.fetch;\n if (!f) {\n throw new Error('No fetch implementation available. Pass a fetcher in config or use Node 18+.');\n }\n\n const url = downloadUrl(config);\n const res = await f(url, { headers: { Accept: 'application/json' } });\n\n if (!res.ok) {\n throw new Error(`download issue failed: HTTP ${res.status}`);\n }\n\n return (await res.json()) as IssuedDownload;\n}\n\n/**\n * Fires the completion beacon for an issued download. Uses\n * `navigator.sendBeacon` when available (survives page navigation), falls\n * back to `fetch` with `keepalive: true`. Safe to call at unload time.\n */\nexport function sendCompletionBeacon(beaconUrl: string): void {\n if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n navigator.sendBeacon(beaconUrl);\n return;\n }\n\n if (typeof fetch !== 'undefined') {\n void fetch(beaconUrl, { method: 'POST', keepalive: true }).catch(() => {});\n }\n}\n\n/**\n * One-shot helper for landing-page download CTAs: fetches a signed URL,\n * navigates the current tab to the asset, then schedules the completion\n * beacon. The function resolves once the navigation has been triggered.\n */\nexport async function triggerDownload(config: DownloadConfig): Promise<IssuedDownload> {\n const issued = await issueDownload(config);\n const delay = config.beaconDelayMs ?? 1500;\n\n if (typeof window !== 'undefined') {\n window.location.href = issued.signedUrl;\n setTimeout(() => sendCompletionBeacon(issued.beaconUrl), delay);\n }\n\n return issued;\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { R as ReleaseChannel, A as AssetPlatform, I as IssuedDownload } from './types-CH4Vkivj.cjs';
|
|
2
|
+
|
|
3
|
+
type DownloadConfig = {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
product: string;
|
|
6
|
+
channel: ReleaseChannel;
|
|
7
|
+
platform: AssetPlatform;
|
|
8
|
+
/** Optional UTM + landing tracking. */
|
|
9
|
+
query?: Record<string, string | undefined>;
|
|
10
|
+
/** Delay before firing the completion beacon, ms. Default 1500. */
|
|
11
|
+
beaconDelayMs?: number;
|
|
12
|
+
fetcher?: typeof fetch;
|
|
13
|
+
};
|
|
14
|
+
declare function downloadUrl(config: Pick<DownloadConfig, 'baseUrl' | 'product' | 'channel' | 'platform' | 'query'>): string;
|
|
15
|
+
/**
|
|
16
|
+
* Issues a download via the billing API and returns the signed asset URL +
|
|
17
|
+
* beacon URL without redirecting. Useful when you want full control of the
|
|
18
|
+
* UX (e.g. fetch then trigger your own `<a download>` flow).
|
|
19
|
+
*/
|
|
20
|
+
declare function issueDownload(config: DownloadConfig): Promise<IssuedDownload>;
|
|
21
|
+
/**
|
|
22
|
+
* Fires the completion beacon for an issued download. Uses
|
|
23
|
+
* `navigator.sendBeacon` when available (survives page navigation), falls
|
|
24
|
+
* back to `fetch` with `keepalive: true`. Safe to call at unload time.
|
|
25
|
+
*/
|
|
26
|
+
declare function sendCompletionBeacon(beaconUrl: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* One-shot helper for landing-page download CTAs: fetches a signed URL,
|
|
29
|
+
* navigates the current tab to the asset, then schedules the completion
|
|
30
|
+
* beacon. The function resolves once the navigation has been triggered.
|
|
31
|
+
*/
|
|
32
|
+
declare function triggerDownload(config: DownloadConfig): Promise<IssuedDownload>;
|
|
33
|
+
|
|
34
|
+
export { type DownloadConfig, downloadUrl, issueDownload, sendCompletionBeacon, triggerDownload };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { R as ReleaseChannel, A as AssetPlatform, I as IssuedDownload } from './types-CH4Vkivj.js';
|
|
2
|
+
|
|
3
|
+
type DownloadConfig = {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
product: string;
|
|
6
|
+
channel: ReleaseChannel;
|
|
7
|
+
platform: AssetPlatform;
|
|
8
|
+
/** Optional UTM + landing tracking. */
|
|
9
|
+
query?: Record<string, string | undefined>;
|
|
10
|
+
/** Delay before firing the completion beacon, ms. Default 1500. */
|
|
11
|
+
beaconDelayMs?: number;
|
|
12
|
+
fetcher?: typeof fetch;
|
|
13
|
+
};
|
|
14
|
+
declare function downloadUrl(config: Pick<DownloadConfig, 'baseUrl' | 'product' | 'channel' | 'platform' | 'query'>): string;
|
|
15
|
+
/**
|
|
16
|
+
* Issues a download via the billing API and returns the signed asset URL +
|
|
17
|
+
* beacon URL without redirecting. Useful when you want full control of the
|
|
18
|
+
* UX (e.g. fetch then trigger your own `<a download>` flow).
|
|
19
|
+
*/
|
|
20
|
+
declare function issueDownload(config: DownloadConfig): Promise<IssuedDownload>;
|
|
21
|
+
/**
|
|
22
|
+
* Fires the completion beacon for an issued download. Uses
|
|
23
|
+
* `navigator.sendBeacon` when available (survives page navigation), falls
|
|
24
|
+
* back to `fetch` with `keepalive: true`. Safe to call at unload time.
|
|
25
|
+
*/
|
|
26
|
+
declare function sendCompletionBeacon(beaconUrl: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* One-shot helper for landing-page download CTAs: fetches a signed URL,
|
|
29
|
+
* navigates the current tab to the asset, then schedules the completion
|
|
30
|
+
* beacon. The function resolves once the navigation has been triggered.
|
|
31
|
+
*/
|
|
32
|
+
declare function triggerDownload(config: DownloadConfig): Promise<IssuedDownload>;
|
|
33
|
+
|
|
34
|
+
export { type DownloadConfig, downloadUrl, issueDownload, sendCompletionBeacon, triggerDownload };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/downloads.ts
|
|
2
|
+
function downloadUrl(config) {
|
|
3
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
4
|
+
const path = `/api/v1/downloads/${config.product}/${config.channel}/${config.platform}`;
|
|
5
|
+
const params = new URLSearchParams();
|
|
6
|
+
for (const [k, v] of Object.entries(config.query ?? {})) {
|
|
7
|
+
if (v !== void 0 && v !== "") params.set(k, v);
|
|
8
|
+
}
|
|
9
|
+
const qs = params.toString();
|
|
10
|
+
return qs ? `${base}${path}?${qs}` : `${base}${path}`;
|
|
11
|
+
}
|
|
12
|
+
async function issueDownload(config) {
|
|
13
|
+
const f = config.fetcher ?? globalThis.fetch;
|
|
14
|
+
if (!f) {
|
|
15
|
+
throw new Error("No fetch implementation available. Pass a fetcher in config or use Node 18+.");
|
|
16
|
+
}
|
|
17
|
+
const url = downloadUrl(config);
|
|
18
|
+
const res = await f(url, { headers: { Accept: "application/json" } });
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`download issue failed: HTTP ${res.status}`);
|
|
21
|
+
}
|
|
22
|
+
return await res.json();
|
|
23
|
+
}
|
|
24
|
+
function sendCompletionBeacon(beaconUrl) {
|
|
25
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
26
|
+
navigator.sendBeacon(beaconUrl);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (typeof fetch !== "undefined") {
|
|
30
|
+
void fetch(beaconUrl, { method: "POST", keepalive: true }).catch(() => {
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function triggerDownload(config) {
|
|
35
|
+
const issued = await issueDownload(config);
|
|
36
|
+
const delay = config.beaconDelayMs ?? 1500;
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
window.location.href = issued.signedUrl;
|
|
39
|
+
setTimeout(() => sendCompletionBeacon(issued.beaconUrl), delay);
|
|
40
|
+
}
|
|
41
|
+
return issued;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { downloadUrl, issueDownload, sendCompletionBeacon, triggerDownload };
|
|
45
|
+
//# sourceMappingURL=downloads.js.map
|
|
46
|
+
//# sourceMappingURL=downloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/downloads.ts"],"names":[],"mappings":";AAcO,SAAS,YAAY,MAAA,EAAgG;AACxH,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,qBAAqB,MAAA,CAAO,OAAO,IAAI,MAAA,CAAO,OAAO,CAAA,CAAA,EAAI,MAAA,CAAO,QAAQ,CAAA,CAAA;AACrF,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,MAAA,CAAO,QAAQ,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA,EAAG;AACrD,IAAA,IAAI,MAAM,MAAA,IAAa,CAAA,KAAM,IAAI,MAAA,CAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,EACpD;AACA,EAAA,MAAM,EAAA,GAAK,OAAO,QAAA,EAAS;AAC3B,EAAA,OAAO,EAAA,GAAK,CAAA,EAAG,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,GAAK,CAAA,EAAG,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA;AACvD;AAOA,eAAsB,cAAc,MAAA,EAAiD;AACjF,EAAA,MAAM,CAAA,GAAI,MAAA,CAAO,OAAA,IAAW,UAAA,CAAW,KAAA;AACvC,EAAA,IAAI,CAAC,CAAA,EAAG;AACJ,IAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,EAClG;AAEA,EAAA,MAAM,GAAA,GAAM,YAAY,MAAM,CAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,MAAM,CAAA,CAAE,GAAA,EAAK,EAAE,SAAS,EAAE,MAAA,EAAQ,kBAAA,EAAmB,EAAG,CAAA;AAEpE,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACT,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,EAC/D;AAEA,EAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAC3B;AAOO,SAAS,qBAAqB,SAAA,EAAyB;AAC1D,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,OAAO,SAAA,CAAU,eAAe,UAAA,EAAY;AAChF,IAAA,SAAA,CAAU,WAAW,SAAS,CAAA;AAC9B,IAAA;AAAA,EACJ;AAEA,EAAA,IAAI,OAAO,UAAU,WAAA,EAAa;AAC9B,IAAA,KAAK,KAAA,CAAM,SAAA,EAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,WAAW,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAC7E;AACJ;AAOA,eAAsB,gBAAgB,MAAA,EAAiD;AACnF,EAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,MAAM,CAAA;AACzC,EAAA,MAAM,KAAA,GAAQ,OAAO,aAAA,IAAiB,IAAA;AAEtC,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAA,CAAO,QAAA,CAAS,OAAO,MAAA,CAAO,SAAA;AAC9B,IAAA,UAAA,CAAW,MAAM,oBAAA,CAAqB,MAAA,CAAO,SAAS,GAAG,KAAK,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,MAAA;AACX","file":"downloads.js","sourcesContent":["import type { AssetPlatform, IssuedDownload, ReleaseChannel } from './types';\n\nexport type DownloadConfig = {\n baseUrl: string;\n product: string;\n channel: ReleaseChannel;\n platform: AssetPlatform;\n /** Optional UTM + landing tracking. */\n query?: Record<string, string | undefined>;\n /** Delay before firing the completion beacon, ms. Default 1500. */\n beaconDelayMs?: number;\n fetcher?: typeof fetch;\n};\n\nexport function downloadUrl(config: Pick<DownloadConfig, 'baseUrl' | 'product' | 'channel' | 'platform' | 'query'>): string {\n const base = config.baseUrl.replace(/\\/$/, '');\n const path = `/api/v1/downloads/${config.product}/${config.channel}/${config.platform}`;\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(config.query ?? {})) {\n if (v !== undefined && v !== '') params.set(k, v);\n }\n const qs = params.toString();\n return qs ? `${base}${path}?${qs}` : `${base}${path}`;\n}\n\n/**\n * Issues a download via the billing API and returns the signed asset URL +\n * beacon URL without redirecting. Useful when you want full control of the\n * UX (e.g. fetch then trigger your own `<a download>` flow).\n */\nexport async function issueDownload(config: DownloadConfig): Promise<IssuedDownload> {\n const f = config.fetcher ?? globalThis.fetch;\n if (!f) {\n throw new Error('No fetch implementation available. Pass a fetcher in config or use Node 18+.');\n }\n\n const url = downloadUrl(config);\n const res = await f(url, { headers: { Accept: 'application/json' } });\n\n if (!res.ok) {\n throw new Error(`download issue failed: HTTP ${res.status}`);\n }\n\n return (await res.json()) as IssuedDownload;\n}\n\n/**\n * Fires the completion beacon for an issued download. Uses\n * `navigator.sendBeacon` when available (survives page navigation), falls\n * back to `fetch` with `keepalive: true`. Safe to call at unload time.\n */\nexport function sendCompletionBeacon(beaconUrl: string): void {\n if (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n navigator.sendBeacon(beaconUrl);\n return;\n }\n\n if (typeof fetch !== 'undefined') {\n void fetch(beaconUrl, { method: 'POST', keepalive: true }).catch(() => {});\n }\n}\n\n/**\n * One-shot helper for landing-page download CTAs: fetches a signed URL,\n * navigates the current tab to the asset, then schedules the completion\n * beacon. The function resolves once the navigation has been triggered.\n */\nexport async function triggerDownload(config: DownloadConfig): Promise<IssuedDownload> {\n const issued = await issueDownload(config);\n const delay = config.beaconDelayMs ?? 1500;\n\n if (typeof window !== 'undefined') {\n window.location.href = issued.signedUrl;\n setTimeout(() => sendCompletionBeacon(issued.beaconUrl), delay);\n }\n\n return issued;\n}\n"]}
|