@akira-io/billing-js 0.3.2 → 0.3.4

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/client.cjs CHANGED
@@ -136,6 +136,12 @@ var BillingClient = class {
136
136
  async entitlements() {
137
137
  return this.signed("GET", "/api/me/entitlements");
138
138
  }
139
+ async customerFeatures(product) {
140
+ return this.signed(
141
+ "GET",
142
+ `/api/me/features?product=${encodeURIComponent(product)}`
143
+ );
144
+ }
139
145
  async billingPortal(returnUrl) {
140
146
  const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;
141
147
  return this.signed("GET", path);
@@ -1 +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 LicenseSyncUsagePayload,\n LicenseSyncUsageResponse,\n GithubAppInfo,\n GithubInstallationTokenPayload,\n GithubInstallationTokenResponse,\n GithubUserInstallationsResponse,\n OauthExchangePayload,\n OauthExchangeResponse,\n OauthProvidersResponse,\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 getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getProductSlug(): string {\n return this.productSlug;\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 licenseSyncUsage(payload: LicenseSyncUsagePayload): Promise<LicenseSyncUsageResponse> {\n return this.signed<LicenseSyncUsageResponse>('POST', '/api/licenses/sync-usage', payload);\n }\n\n async listOauthProviders(product: string): Promise<OauthProvidersResponse> {\n return this.signed<OauthProvidersResponse>('GET', `/api/v1/products/${encodeURIComponent(product)}/auth/providers`);\n }\n\n async exchangeOauthCode(payload: OauthExchangePayload): Promise<OauthExchangeResponse> {\n const res = await this.signed<OauthExchangeResponse>('POST', '/api/auth/oauth/exchange', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async githubInstallationToken(\n payload: GithubInstallationTokenPayload = {},\n ): Promise<GithubInstallationTokenResponse> {\n return this.signed<GithubInstallationTokenResponse>(\n 'POST',\n '/api/me/github/installation-token',\n payload,\n );\n }\n\n async meGithubInstallations(): Promise<GithubUserInstallationsResponse> {\n return this.signed<GithubUserInstallationsResponse>('GET', '/api/me/github/installations');\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 trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/v1/usage/anonymous', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n async githubAppInfo(): Promise<GithubAppInfo> {\n return this.unsigned<GithubAppInfo>('GET', '/api/v1/github/app');\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: string;\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"]}
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 LicenseSyncUsagePayload,\n LicenseSyncUsageResponse,\n GithubAppInfo,\n GithubInstallationTokenPayload,\n GithubInstallationTokenResponse,\n GithubUserInstallationsResponse,\n OauthExchangePayload,\n OauthExchangeResponse,\n OauthProvidersResponse,\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 getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getProductSlug(): string {\n return this.productSlug;\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 licenseSyncUsage(payload: LicenseSyncUsagePayload): Promise<LicenseSyncUsageResponse> {\n return this.signed<LicenseSyncUsageResponse>('POST', '/api/licenses/sync-usage', payload);\n }\n\n async listOauthProviders(product: string): Promise<OauthProvidersResponse> {\n return this.signed<OauthProvidersResponse>('GET', `/api/v1/products/${encodeURIComponent(product)}/auth/providers`);\n }\n\n async exchangeOauthCode(payload: OauthExchangePayload): Promise<OauthExchangeResponse> {\n const res = await this.signed<OauthExchangeResponse>('POST', '/api/auth/oauth/exchange', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async githubInstallationToken(\n payload: GithubInstallationTokenPayload = {},\n ): Promise<GithubInstallationTokenResponse> {\n return this.signed<GithubInstallationTokenResponse>(\n 'POST',\n '/api/me/github/installation-token',\n payload,\n );\n }\n\n async meGithubInstallations(): Promise<GithubUserInstallationsResponse> {\n return this.signed<GithubUserInstallationsResponse>('GET', '/api/me/github/installations');\n }\n\n async entitlements(): Promise<EntitlementsResponse> {\n return this.signed<EntitlementsResponse>('GET', '/api/me/entitlements');\n }\n\n async customerFeatures(product: string): Promise<{ product: string; features: string[] }> {\n return this.signed<{ product: string; features: string[] }>(\n 'GET',\n `/api/me/features?product=${encodeURIComponent(product)}`,\n );\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 trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/v1/usage/anonymous', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n async githubAppInfo(): Promise<GithubAppInfo> {\n return this.unsigned<GithubAppInfo>('GET', '/api/v1/github/app');\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: string;\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"]}
package/dist/client.d.cts CHANGED
@@ -34,6 +34,10 @@ declare class BillingClient {
34
34
  githubInstallationToken(payload?: GithubInstallationTokenPayload): Promise<GithubInstallationTokenResponse>;
35
35
  meGithubInstallations(): Promise<GithubUserInstallationsResponse>;
36
36
  entitlements(): Promise<EntitlementsResponse>;
37
+ customerFeatures(product: string): Promise<{
38
+ product: string;
39
+ features: string[];
40
+ }>;
37
41
  billingPortal(returnUrl: string): Promise<BillingPortalResponse>;
38
42
  trackUsage(payload: UsagePayload): Promise<UsageResponse>;
39
43
  trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse>;
package/dist/client.d.ts CHANGED
@@ -34,6 +34,10 @@ declare class BillingClient {
34
34
  githubInstallationToken(payload?: GithubInstallationTokenPayload): Promise<GithubInstallationTokenResponse>;
35
35
  meGithubInstallations(): Promise<GithubUserInstallationsResponse>;
36
36
  entitlements(): Promise<EntitlementsResponse>;
37
+ customerFeatures(product: string): Promise<{
38
+ product: string;
39
+ features: string[];
40
+ }>;
37
41
  billingPortal(returnUrl: string): Promise<BillingPortalResponse>;
38
42
  trackUsage(payload: UsagePayload): Promise<UsageResponse>;
39
43
  trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse>;
package/dist/client.js CHANGED
@@ -134,6 +134,12 @@ var BillingClient = class {
134
134
  async entitlements() {
135
135
  return this.signed("GET", "/api/me/entitlements");
136
136
  }
137
+ async customerFeatures(product) {
138
+ return this.signed(
139
+ "GET",
140
+ `/api/me/features?product=${encodeURIComponent(product)}`
141
+ );
142
+ }
137
143
  async billingPortal(returnUrl) {
138
144
  const path = `/api/billing/portal?return_url=${encodeURIComponent(returnUrl)}`;
139
145
  return this.signed("GET", path);
@@ -1 +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 LicenseSyncUsagePayload,\n LicenseSyncUsageResponse,\n GithubAppInfo,\n GithubInstallationTokenPayload,\n GithubInstallationTokenResponse,\n GithubUserInstallationsResponse,\n OauthExchangePayload,\n OauthExchangeResponse,\n OauthProvidersResponse,\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 getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getProductSlug(): string {\n return this.productSlug;\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 licenseSyncUsage(payload: LicenseSyncUsagePayload): Promise<LicenseSyncUsageResponse> {\n return this.signed<LicenseSyncUsageResponse>('POST', '/api/licenses/sync-usage', payload);\n }\n\n async listOauthProviders(product: string): Promise<OauthProvidersResponse> {\n return this.signed<OauthProvidersResponse>('GET', `/api/v1/products/${encodeURIComponent(product)}/auth/providers`);\n }\n\n async exchangeOauthCode(payload: OauthExchangePayload): Promise<OauthExchangeResponse> {\n const res = await this.signed<OauthExchangeResponse>('POST', '/api/auth/oauth/exchange', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async githubInstallationToken(\n payload: GithubInstallationTokenPayload = {},\n ): Promise<GithubInstallationTokenResponse> {\n return this.signed<GithubInstallationTokenResponse>(\n 'POST',\n '/api/me/github/installation-token',\n payload,\n );\n }\n\n async meGithubInstallations(): Promise<GithubUserInstallationsResponse> {\n return this.signed<GithubUserInstallationsResponse>('GET', '/api/me/github/installations');\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 trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/v1/usage/anonymous', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n async githubAppInfo(): Promise<GithubAppInfo> {\n return this.unsigned<GithubAppInfo>('GET', '/api/v1/github/app');\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: string;\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"]}
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 LicenseSyncUsagePayload,\n LicenseSyncUsageResponse,\n GithubAppInfo,\n GithubInstallationTokenPayload,\n GithubInstallationTokenResponse,\n GithubUserInstallationsResponse,\n OauthExchangePayload,\n OauthExchangeResponse,\n OauthProvidersResponse,\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 getBaseUrl(): string {\n return this.baseUrl;\n }\n\n getProductSlug(): string {\n return this.productSlug;\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 licenseSyncUsage(payload: LicenseSyncUsagePayload): Promise<LicenseSyncUsageResponse> {\n return this.signed<LicenseSyncUsageResponse>('POST', '/api/licenses/sync-usage', payload);\n }\n\n async listOauthProviders(product: string): Promise<OauthProvidersResponse> {\n return this.signed<OauthProvidersResponse>('GET', `/api/v1/products/${encodeURIComponent(product)}/auth/providers`);\n }\n\n async exchangeOauthCode(payload: OauthExchangePayload): Promise<OauthExchangeResponse> {\n const res = await this.signed<OauthExchangeResponse>('POST', '/api/auth/oauth/exchange', payload);\n this.setCustomerToken(res.access_token);\n return res;\n }\n\n async githubInstallationToken(\n payload: GithubInstallationTokenPayload = {},\n ): Promise<GithubInstallationTokenResponse> {\n return this.signed<GithubInstallationTokenResponse>(\n 'POST',\n '/api/me/github/installation-token',\n payload,\n );\n }\n\n async meGithubInstallations(): Promise<GithubUserInstallationsResponse> {\n return this.signed<GithubUserInstallationsResponse>('GET', '/api/me/github/installations');\n }\n\n async entitlements(): Promise<EntitlementsResponse> {\n return this.signed<EntitlementsResponse>('GET', '/api/me/entitlements');\n }\n\n async customerFeatures(product: string): Promise<{ product: string; features: string[] }> {\n return this.signed<{ product: string; features: string[] }>(\n 'GET',\n `/api/me/features?product=${encodeURIComponent(product)}`,\n );\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 trackAnonymousUsage(payload: UsagePayload): Promise<UsageResponse> {\n return this.signed<UsageResponse>('POST', '/api/v1/usage/anonymous', payload);\n }\n\n async publicLicenseKeys(): Promise<LicensePublicKeysResponse> {\n return this.unsigned<LicensePublicKeysResponse>('GET', '/api/v1/license-keys/public');\n }\n\n async githubAppInfo(): Promise<GithubAppInfo> {\n return this.unsigned<GithubAppInfo>('GET', '/api/v1/github/app');\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: string;\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"]}
package/dist/desktop.cjs CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  var child_process = require('child_process');
4
4
  var crypto = require('crypto');
5
+ var promises = require('fs/promises');
6
+ var path = require('path');
5
7
  var os = require('os');
6
8
 
7
9
  // src/desktop/checkout.ts
@@ -31,6 +33,71 @@ function openBrowser(url) {
31
33
  resolve();
32
34
  });
33
35
  }
36
+ var NONCE_LEN = 12;
37
+ var TokenCipher = class {
38
+ key;
39
+ constructor(key) {
40
+ if (key.length !== 32) throw new Error("TokenCipher: key must be 32 bytes");
41
+ this.key = crypto.webcrypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt", "decrypt"]);
42
+ }
43
+ async encrypt(plaintext) {
44
+ const key = await this.key;
45
+ const nonce = crypto.webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));
46
+ const ct = new Uint8Array(await crypto.webcrypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(plaintext)));
47
+ const combined = new Uint8Array(NONCE_LEN + ct.length);
48
+ combined.set(nonce, 0);
49
+ combined.set(ct, NONCE_LEN);
50
+ return Buffer.from(combined).toString("base64");
51
+ }
52
+ async decrypt(encoded) {
53
+ const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
54
+ if (bytes.length <= NONCE_LEN) throw new Error("ciphertext too short");
55
+ const nonce = bytes.slice(0, NONCE_LEN);
56
+ const ct = bytes.slice(NONCE_LEN);
57
+ const key = await this.key;
58
+ const pt = new Uint8Array(await crypto.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, key, ct));
59
+ return new TextDecoder().decode(pt);
60
+ }
61
+ };
62
+ function generateKey() {
63
+ return crypto.webcrypto.getRandomValues(new Uint8Array(32));
64
+ }
65
+ var KeyStore = class {
66
+ constructor(opts) {
67
+ this.opts = opts;
68
+ }
69
+ opts;
70
+ async loadOrCreate() {
71
+ if (this.opts.debugFilePath) {
72
+ try {
73
+ const encoded2 = (await promises.readFile(this.opts.debugFilePath, "utf8")).trim();
74
+ return decodeKey(encoded2);
75
+ } catch {
76
+ }
77
+ }
78
+ const encoded = await this.opts.keyring.get().catch(() => null);
79
+ if (encoded) {
80
+ const key2 = decodeKey(encoded);
81
+ await this.writeDebugFile(encoded);
82
+ return key2;
83
+ }
84
+ const key = generateKey();
85
+ const encodedNew = Buffer.from(key).toString("base64");
86
+ await this.opts.keyring.set(encodedNew).catch(() => void 0);
87
+ await this.writeDebugFile(encodedNew);
88
+ return key;
89
+ }
90
+ async writeDebugFile(encoded) {
91
+ if (!this.opts.debugFilePath) return;
92
+ await promises.mkdir(path.dirname(this.opts.debugFilePath), { recursive: true });
93
+ await promises.writeFile(this.opts.debugFilePath, encoded, "utf8");
94
+ }
95
+ };
96
+ function decodeKey(encoded) {
97
+ const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
98
+ if (bytes.length !== 32) throw new Error("key has unexpected length");
99
+ return bytes;
100
+ }
34
101
  async function machineId() {
35
102
  const dyn = new Function("m", "return import(m)");
36
103
  try {
@@ -189,10 +256,13 @@ var AuthController = class {
189
256
  };
190
257
 
191
258
  exports.AuthController = AuthController;
259
+ exports.KeyStore = KeyStore;
192
260
  exports.SessionStore = SessionStore;
261
+ exports.TokenCipher = TokenCipher;
193
262
  exports.TokenKeyring = TokenKeyring;
194
263
  exports.checkoutUrl = checkoutUrl;
195
264
  exports.deviceFingerprint = deviceFingerprint;
265
+ exports.generateKey = generateKey;
196
266
  exports.openBrowser = openBrowser;
197
267
  //# sourceMappingURL=desktop.cjs.map
198
268
  //# sourceMappingURL=desktop.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":["spawn","hostname","platform","createHash"],"mappings":"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","file":"desktop.cjs","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/desktop/checkout.ts","../src/desktop/browser.ts","../src/desktop/cipher.ts","../src/desktop/keystore.ts","../src/desktop/fingerprint.ts","../src/desktop/keyring.ts","../src/desktop/session.ts","../src/desktop/use-auth.ts"],"names":["spawn","webcrypto","encoded","readFile","key","mkdir","dirname","writeFile","hostname","platform","createHash"],"mappings":"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","file":"desktop.cjs","sourcesContent":["/** Build the public checkout URL for a product on the billing site. */\nexport function checkoutUrl(baseUrl: string, product: string): string {\n return `${baseUrl.replace(/\\/$/, '')}/plans?product=${encodeURIComponent(product)}`;\n}\n","import { spawn } from 'node:child_process';\n\n/** Launch the system default browser. Node-only. */\nexport function openBrowser(url: string): Promise<void> {\n return new Promise((resolve, reject) => {\n let cmd: string;\n let args: string[];\n switch (process.platform) {\n case 'darwin':\n cmd = 'open';\n args = [url];\n break;\n case 'win32':\n cmd = 'rundll32';\n args = ['url.dll,FileProtocolHandler', url];\n break;\n default:\n cmd = 'xdg-open';\n args = [url];\n }\n const child = spawn(cmd, args, { stdio: 'ignore', detached: true });\n child.once('error', reject);\n child.unref();\n resolve();\n });\n}\n","import { webcrypto } from 'node:crypto';\n\nconst NONCE_LEN = 12;\n\n/** AES-256-GCM string encryption keyed by a 32-byte secret. */\nexport class TokenCipher {\n private readonly key: CryptoKey | Promise<CryptoKey>;\n\n constructor(key: Uint8Array) {\n if (key.length !== 32) throw new Error('TokenCipher: key must be 32 bytes');\n this.key = webcrypto.subtle.importKey('raw', key as unknown as ArrayBuffer, 'AES-GCM', false, ['encrypt', 'decrypt']);\n }\n\n async encrypt(plaintext: string): Promise<string> {\n const key = await this.key;\n const nonce = webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));\n const ct = new Uint8Array(await webcrypto.subtle.encrypt({ name: 'AES-GCM', iv: nonce }, key, new TextEncoder().encode(plaintext)));\n const combined = new Uint8Array(NONCE_LEN + ct.length);\n combined.set(nonce, 0);\n combined.set(ct, NONCE_LEN);\n return Buffer.from(combined).toString('base64');\n }\n\n async decrypt(encoded: string): Promise<string> {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length <= NONCE_LEN) throw new Error('ciphertext too short');\n const nonce = bytes.slice(0, NONCE_LEN);\n const ct = bytes.slice(NONCE_LEN);\n const key = await this.key;\n const pt = new Uint8Array(await webcrypto.subtle.decrypt({ name: 'AES-GCM', iv: nonce }, key, ct as unknown as ArrayBuffer));\n return new TextDecoder().decode(pt);\n }\n}\n\nexport function generateKey(): Uint8Array {\n return webcrypto.getRandomValues(new Uint8Array(32));\n}\n","import { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nimport { generateKey } from './cipher';\nimport { TokenKeyring } from './keyring';\n\nexport interface KeyStoreOptions {\n keyring: TokenKeyring;\n /** Optional debug fallback path. When set, the key is mirrored to disk so\n * unsigned dev rebuilds can still decrypt previously persisted data. */\n debugFilePath?: string;\n}\n\n/** Loads or generates a 32-byte AES key, persisted in the OS keychain. */\nexport class KeyStore {\n constructor(private readonly opts: KeyStoreOptions) {}\n\n async loadOrCreate(): Promise<Uint8Array> {\n if (this.opts.debugFilePath) {\n try {\n const encoded = (await readFile(this.opts.debugFilePath, 'utf8')).trim();\n return decodeKey(encoded);\n } catch { /* fall through */ }\n }\n\n const encoded = await this.opts.keyring.get().catch(() => null);\n if (encoded) {\n const key = decodeKey(encoded);\n await this.writeDebugFile(encoded);\n return key;\n }\n\n const key = generateKey();\n const encodedNew = Buffer.from(key).toString('base64');\n await this.opts.keyring.set(encodedNew).catch(() => undefined);\n await this.writeDebugFile(encodedNew);\n return key;\n }\n\n private async writeDebugFile(encoded: string): Promise<void> {\n if (!this.opts.debugFilePath) return;\n await mkdir(dirname(this.opts.debugFilePath), { recursive: true });\n await writeFile(this.opts.debugFilePath, encoded, 'utf8');\n }\n}\n\nfunction decodeKey(encoded: string): Uint8Array {\n const bytes = new Uint8Array(Buffer.from(encoded, 'base64'));\n if (bytes.length !== 32) throw new Error('key has unexpected length');\n return bytes;\n}\n","import { createHash } from 'node:crypto';\nimport { hostname, platform } from 'node:os';\n\nexport interface DeviceFingerprint {\n fingerprint: string;\n platform: string;\n app_version: string;\n}\n\nasync function machineId(): Promise<string> {\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n try {\n const mod = (await dyn('node-machine-id').catch(() => null)) as { machineIdSync?: (orig?: boolean) => string } | null;\n if (mod?.machineIdSync) return mod.machineIdSync(true);\n } catch {\n // fallthrough\n }\n return hostname();\n}\n\nexport async function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint> {\n const id = await machineId();\n const plat = platform();\n const hash = createHash('sha256');\n hash.update(id);\n hash.update('::');\n hash.update(plat);\n hash.update('::');\n hash.update(appVersion);\n return {\n fingerprint: hash.digest('hex'),\n platform: plat,\n app_version: appVersion,\n };\n}\n","/**\n * Thin wrapper around `keytar` for desktop apps. Consumers install `keytar` as\n * an optional dependency; this module dynamically imports it so server-side\n * bundles never see it.\n */\nexport interface TokenKeyringOptions {\n service: string;\n account: string;\n}\n\ntype Keytar = {\n getPassword: (service: string, account: string) => Promise<string | null>;\n setPassword: (service: string, account: string, password: string) => Promise<void>;\n deletePassword: (service: string, account: string) => Promise<boolean>;\n};\n\nasync function loadKeytar(): Promise<Keytar> {\n // Use Function constructor so tsup/typescript don't try to resolve the\n // optional module at build time. Consumers add `keytar` as a peer/optional\n // dependency.\n const dyn = new Function('m', 'return import(m)') as (m: string) => Promise<unknown>;\n const mod = (await dyn('keytar').catch(() => null)) as { default?: Keytar } | null;\n if (!mod) throw new Error('TokenKeyring: install `keytar` to use the desktop keyring helper.');\n return mod.default ?? (mod as unknown as Keytar);\n}\n\nexport class TokenKeyring {\n private readonly service: string;\n private readonly account: string;\n\n constructor(opts: TokenKeyringOptions) {\n this.service = opts.service;\n this.account = opts.account;\n }\n\n async get(): Promise<string | null> {\n const k = await loadKeytar();\n return k.getPassword(this.service, this.account);\n }\n\n async set(value: string): Promise<void> {\n const k = await loadKeytar();\n await k.setPassword(this.service, this.account, value);\n }\n\n async delete(): Promise<void> {\n const k = await loadKeytar();\n await k.deletePassword(this.service, this.account);\n }\n}\n","import { BillingClient } from '../client';\nimport { TokenKeyring } from './keyring';\n\nexport class SessionStore {\n constructor(private readonly keyring: TokenKeyring) {}\n\n async hydrate(client: BillingClient): Promise<boolean> {\n const token = await this.keyring.get();\n if (!token) return false;\n client.setCustomerToken(token);\n return true;\n }\n\n async persist(client: BillingClient, token: string): Promise<void> {\n await this.keyring.set(token);\n client.setCustomerToken(token);\n }\n\n async clear(client: BillingClient): Promise<void> {\n await this.keyring.delete();\n client.setCustomerToken('');\n }\n\n async hasToken(): Promise<boolean> {\n const v = await this.keyring.get();\n return !!v;\n }\n}\n","import type { BillingClient } from '../client';\nimport type { Customer, OauthExchangeResponse } from '../client-types';\nimport type { LoopbackOutcome } from '../loopback';\nimport type { SessionStore } from './session';\n\nexport type AuthStatusState =\n | { state: 'loading' }\n | { state: 'guest' }\n | { state: 'authenticated'; customer: Customer; licensed: boolean };\n\nexport interface AuthControllerOptions {\n client: BillingClient;\n session: SessionStore;\n product: string;\n feature?: string;\n /** Loopback flow used by `oauthLogin`. */\n loopbackLogin: (provider: string) => Promise<LoopbackOutcome>;\n /** Override the runtime license check (e.g. cache, custom feature key). */\n checkLicense?: (client: BillingClient) => Promise<boolean>;\n}\n\nexport class AuthController {\n private listeners = new Set<(s: AuthStatusState) => void>();\n private current: AuthStatusState = { state: 'loading' };\n\n constructor(private readonly opts: AuthControllerOptions) {}\n\n subscribe(listener: (s: AuthStatusState) => void): () => void {\n this.listeners.add(listener);\n listener(this.current);\n return () => this.listeners.delete(listener);\n }\n\n snapshot(): AuthStatusState {\n return this.current;\n }\n\n private emit(next: AuthStatusState): void {\n this.current = next;\n this.listeners.forEach((l) => l(next));\n }\n\n async bootstrap(): Promise<void> {\n await this.opts.session.hydrate(this.opts.client);\n await this.refresh();\n }\n\n async refresh(): Promise<void> {\n if (!(await this.opts.session.hasToken())) {\n this.emit({ state: 'guest' });\n return;\n }\n try {\n const customer = await this.opts.client.customerMe();\n const licensed = this.opts.checkLicense\n ? await this.opts.checkLicense(this.opts.client)\n : await this.runDefaultLicenseCheck();\n this.emit({ state: 'authenticated', customer, licensed });\n } catch (e) {\n if ((e as { status?: number }).status === 401) {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n return;\n }\n throw e;\n }\n }\n\n private async runDefaultLicenseCheck(): Promise<boolean> {\n try {\n const resp = await this.opts.client.licenseCheck({\n product: this.opts.product,\n feature: this.opts.feature ?? 'general',\n });\n return resp.allowed;\n } catch {\n return false;\n }\n }\n\n async requestOtp(email: string, deviceFp?: string): Promise<void> {\n await this.opts.client.requestOtp({ email, device_fp: deviceFp ?? '' });\n }\n\n async verifyOtp(email: string, code: string, deviceFp?: string): Promise<Customer> {\n const resp = await this.opts.client.verifyOtp({\n email,\n code,\n device_fp: deviceFp ?? '',\n });\n await this.opts.session.persist(this.opts.client, resp.access_token);\n await this.refresh();\n return (await this.opts.client.customerMe());\n }\n\n async oauthLogin(provider: string): Promise<OauthExchangeResponse> {\n const outcome = await this.opts.loopbackLogin(provider);\n await this.opts.session.persist(this.opts.client, outcome.exchange.access_token);\n await this.refresh();\n return outcome.exchange;\n }\n\n async logout(): Promise<void> {\n await this.opts.session.clear(this.opts.client);\n this.emit({ state: 'guest' });\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
- export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, T as TokenKeyring, c as TokenKeyringOptions } from './use-auth-Dbol539E.cjs';
1
+ import { T as TokenKeyring } from './use-auth-Dbol539E.cjs';
2
+ export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, c as TokenKeyringOptions } from './use-auth-Dbol539E.cjs';
2
3
  import './client.cjs';
3
4
  import './client-types-BRHIoFm6.cjs';
4
5
  import './loopback.cjs';
@@ -9,6 +10,29 @@ declare function checkoutUrl(baseUrl: string, product: string): string;
9
10
  /** Launch the system default browser. Node-only. */
10
11
  declare function openBrowser(url: string): Promise<void>;
11
12
 
13
+ /** AES-256-GCM string encryption keyed by a 32-byte secret. */
14
+ declare class TokenCipher {
15
+ private readonly key;
16
+ constructor(key: Uint8Array);
17
+ encrypt(plaintext: string): Promise<string>;
18
+ decrypt(encoded: string): Promise<string>;
19
+ }
20
+ declare function generateKey(): Uint8Array;
21
+
22
+ interface KeyStoreOptions {
23
+ keyring: TokenKeyring;
24
+ /** Optional debug fallback path. When set, the key is mirrored to disk so
25
+ * unsigned dev rebuilds can still decrypt previously persisted data. */
26
+ debugFilePath?: string;
27
+ }
28
+ /** Loads or generates a 32-byte AES key, persisted in the OS keychain. */
29
+ declare class KeyStore {
30
+ private readonly opts;
31
+ constructor(opts: KeyStoreOptions);
32
+ loadOrCreate(): Promise<Uint8Array>;
33
+ private writeDebugFile;
34
+ }
35
+
12
36
  interface DeviceFingerprint {
13
37
  fingerprint: string;
14
38
  platform: string;
@@ -16,4 +40,4 @@ interface DeviceFingerprint {
16
40
  }
17
41
  declare function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint>;
18
42
 
19
- export { type DeviceFingerprint, checkoutUrl, deviceFingerprint, openBrowser };
43
+ export { type DeviceFingerprint, KeyStore, type KeyStoreOptions, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
package/dist/desktop.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, T as TokenKeyring, c as TokenKeyringOptions } from './use-auth-BaAeVSZO.js';
1
+ import { T as TokenKeyring } from './use-auth-BaAeVSZO.js';
2
+ export { A as AuthController, a as AuthControllerOptions, b as AuthStatusState, S as SessionStore, c as TokenKeyringOptions } from './use-auth-BaAeVSZO.js';
2
3
  import './client.js';
3
4
  import './client-types-BRHIoFm6.js';
4
5
  import './loopback.js';
@@ -9,6 +10,29 @@ declare function checkoutUrl(baseUrl: string, product: string): string;
9
10
  /** Launch the system default browser. Node-only. */
10
11
  declare function openBrowser(url: string): Promise<void>;
11
12
 
13
+ /** AES-256-GCM string encryption keyed by a 32-byte secret. */
14
+ declare class TokenCipher {
15
+ private readonly key;
16
+ constructor(key: Uint8Array);
17
+ encrypt(plaintext: string): Promise<string>;
18
+ decrypt(encoded: string): Promise<string>;
19
+ }
20
+ declare function generateKey(): Uint8Array;
21
+
22
+ interface KeyStoreOptions {
23
+ keyring: TokenKeyring;
24
+ /** Optional debug fallback path. When set, the key is mirrored to disk so
25
+ * unsigned dev rebuilds can still decrypt previously persisted data. */
26
+ debugFilePath?: string;
27
+ }
28
+ /** Loads or generates a 32-byte AES key, persisted in the OS keychain. */
29
+ declare class KeyStore {
30
+ private readonly opts;
31
+ constructor(opts: KeyStoreOptions);
32
+ loadOrCreate(): Promise<Uint8Array>;
33
+ private writeDebugFile;
34
+ }
35
+
12
36
  interface DeviceFingerprint {
13
37
  fingerprint: string;
14
38
  platform: string;
@@ -16,4 +40,4 @@ interface DeviceFingerprint {
16
40
  }
17
41
  declare function deviceFingerprint(appVersion: string): Promise<DeviceFingerprint>;
18
42
 
19
- export { type DeviceFingerprint, checkoutUrl, deviceFingerprint, openBrowser };
43
+ export { type DeviceFingerprint, KeyStore, type KeyStoreOptions, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
package/dist/desktop.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { spawn } from 'child_process';
2
- import { createHash } from 'crypto';
2
+ import { webcrypto, createHash } from 'crypto';
3
+ import { readFile, mkdir, writeFile } from 'fs/promises';
4
+ import { dirname } from 'path';
3
5
  import { platform, hostname } from 'os';
4
6
 
5
7
  // src/desktop/checkout.ts
@@ -29,6 +31,71 @@ function openBrowser(url) {
29
31
  resolve();
30
32
  });
31
33
  }
34
+ var NONCE_LEN = 12;
35
+ var TokenCipher = class {
36
+ key;
37
+ constructor(key) {
38
+ if (key.length !== 32) throw new Error("TokenCipher: key must be 32 bytes");
39
+ this.key = webcrypto.subtle.importKey("raw", key, "AES-GCM", false, ["encrypt", "decrypt"]);
40
+ }
41
+ async encrypt(plaintext) {
42
+ const key = await this.key;
43
+ const nonce = webcrypto.getRandomValues(new Uint8Array(NONCE_LEN));
44
+ const ct = new Uint8Array(await webcrypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, key, new TextEncoder().encode(plaintext)));
45
+ const combined = new Uint8Array(NONCE_LEN + ct.length);
46
+ combined.set(nonce, 0);
47
+ combined.set(ct, NONCE_LEN);
48
+ return Buffer.from(combined).toString("base64");
49
+ }
50
+ async decrypt(encoded) {
51
+ const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
52
+ if (bytes.length <= NONCE_LEN) throw new Error("ciphertext too short");
53
+ const nonce = bytes.slice(0, NONCE_LEN);
54
+ const ct = bytes.slice(NONCE_LEN);
55
+ const key = await this.key;
56
+ const pt = new Uint8Array(await webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, key, ct));
57
+ return new TextDecoder().decode(pt);
58
+ }
59
+ };
60
+ function generateKey() {
61
+ return webcrypto.getRandomValues(new Uint8Array(32));
62
+ }
63
+ var KeyStore = class {
64
+ constructor(opts) {
65
+ this.opts = opts;
66
+ }
67
+ opts;
68
+ async loadOrCreate() {
69
+ if (this.opts.debugFilePath) {
70
+ try {
71
+ const encoded2 = (await readFile(this.opts.debugFilePath, "utf8")).trim();
72
+ return decodeKey(encoded2);
73
+ } catch {
74
+ }
75
+ }
76
+ const encoded = await this.opts.keyring.get().catch(() => null);
77
+ if (encoded) {
78
+ const key2 = decodeKey(encoded);
79
+ await this.writeDebugFile(encoded);
80
+ return key2;
81
+ }
82
+ const key = generateKey();
83
+ const encodedNew = Buffer.from(key).toString("base64");
84
+ await this.opts.keyring.set(encodedNew).catch(() => void 0);
85
+ await this.writeDebugFile(encodedNew);
86
+ return key;
87
+ }
88
+ async writeDebugFile(encoded) {
89
+ if (!this.opts.debugFilePath) return;
90
+ await mkdir(dirname(this.opts.debugFilePath), { recursive: true });
91
+ await writeFile(this.opts.debugFilePath, encoded, "utf8");
92
+ }
93
+ };
94
+ function decodeKey(encoded) {
95
+ const bytes = new Uint8Array(Buffer.from(encoded, "base64"));
96
+ if (bytes.length !== 32) throw new Error("key has unexpected length");
97
+ return bytes;
98
+ }
32
99
  async function machineId() {
33
100
  const dyn = new Function("m", "return import(m)");
34
101
  try {
@@ -186,6 +253,6 @@ var AuthController = class {
186
253
  }
187
254
  };
188
255
 
189
- export { AuthController, SessionStore, TokenKeyring, checkoutUrl, deviceFingerprint, openBrowser };
256
+ export { AuthController, KeyStore, SessionStore, TokenCipher, TokenKeyring, checkoutUrl, deviceFingerprint, generateKey, openBrowser };
190
257
  //# sourceMappingURL=desktop.js.map
191
258
  //# sourceMappingURL=desktop.js.map