@contentgrowth/llm-service 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import OpenAI from 'openai';
2
2
  import Anthropic from '@anthropic-ai/sdk';
3
3
  import { GoogleGenAI } from '@google/genai';
4
4
  export { Modality } from '@google/genai';
5
+ import { GoogleTokenManager } from './utils/google-auth.cjs';
5
6
 
6
7
  declare namespace FINISH_REASONS {
7
8
  let COMPLETED: string;
@@ -807,98 +808,6 @@ declare function handleApiError(error: Error, operation?: string, context?: stri
807
808
  */
808
809
  declare function sanitizeError(error: Error, context?: string): Error;
809
810
 
810
- /**
811
- * Google OAuth2 Token Manager
812
- *
813
- * Handles token generation, caching, and automatic refresh for
814
- * Google Cloud service accounts. Tokens are cached and automatically
815
- * refreshed when they expire (with a 5-minute safety buffer).
816
- *
817
- * @example
818
- * const sa = parseGoogleCredentials(credentials);
819
- * const tokenManager = new GoogleTokenManager(sa);
820
- *
821
- * // Get token (cached or fresh)
822
- * const token = await tokenManager.getToken();
823
- *
824
- * // Force refresh
825
- * tokenManager.invalidate();
826
- * const newToken = await tokenManager.getToken();
827
- */
828
- declare class GoogleTokenManager {
829
- /**
830
- * @param {object} serviceAccount - Parsed service account with client_email and private_key
831
- * @param {object} options - Optional configuration
832
- * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
833
- * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
834
- */
835
- constructor(serviceAccount: object, options?: {
836
- scope: string;
837
- tokenLifetime: number;
838
- });
839
- clientEmail: any;
840
- privateKey: any;
841
- projectId: any;
842
- scope: string;
843
- tokenLifetime: number;
844
- _tokenCache: string;
845
- _expiresAt: number;
846
- /**
847
- * Get a valid access token (cached or fresh)
848
- *
849
- * @returns {Promise<string>} OAuth2 access token
850
- */
851
- getToken(): Promise<string>;
852
- /**
853
- * Clear cached token (force refresh on next getToken call)
854
- */
855
- invalidate(): void;
856
- /**
857
- * Check if cached token is still valid
858
- *
859
- * @returns {boolean} True if token is valid
860
- */
861
- isValid(): boolean;
862
- /**
863
- * Get time until token expires (in seconds)
864
- *
865
- * @returns {number} Seconds until expiration (0 if no token or expired)
866
- */
867
- getTimeToExpiry(): number;
868
- /**
869
- * Generate OAuth2 token via JWT flow
870
- *
871
- * @private
872
- * @returns {Promise<string>} Access token
873
- */
874
- private _generateToken;
875
- /**
876
- * Sign JWT using service account private key
877
- *
878
- * @private
879
- * @param {object} header - JWT header
880
- * @param {object} payload - JWT payload
881
- * @returns {Promise<string>} Signed JWT
882
- */
883
- private _signJWT;
884
- /**
885
- * Import PEM private key for signing
886
- *
887
- * @private
888
- * @param {string} pemKey - PEM formatted private key
889
- * @returns {Promise<CryptoKey>} Imported key
890
- */
891
- private _importPrivateKey;
892
- /**
893
- * Base64 URL encode (without padding)
894
- *
895
- * @private
896
- * @param {string|ArrayBuffer} input - Data to encode
897
- * @returns {string} Base64 URL encoded string
898
- */
899
- private _base64UrlEncode;
900
- }
901
-
902
811
  /**
903
812
  * Transcription Service - Separate from LLM Service
904
813
  * Handles audio-to-text transcription using various providers.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import OpenAI from 'openai';
2
2
  import Anthropic from '@anthropic-ai/sdk';
3
3
  import { GoogleGenAI } from '@google/genai';
4
4
  export { Modality } from '@google/genai';
5
+ import { GoogleTokenManager } from './utils/google-auth.js';
5
6
 
6
7
  declare namespace FINISH_REASONS {
7
8
  let COMPLETED: string;
@@ -807,98 +808,6 @@ declare function handleApiError(error: Error, operation?: string, context?: stri
807
808
  */
808
809
  declare function sanitizeError(error: Error, context?: string): Error;
809
810
 
810
- /**
811
- * Google OAuth2 Token Manager
812
- *
813
- * Handles token generation, caching, and automatic refresh for
814
- * Google Cloud service accounts. Tokens are cached and automatically
815
- * refreshed when they expire (with a 5-minute safety buffer).
816
- *
817
- * @example
818
- * const sa = parseGoogleCredentials(credentials);
819
- * const tokenManager = new GoogleTokenManager(sa);
820
- *
821
- * // Get token (cached or fresh)
822
- * const token = await tokenManager.getToken();
823
- *
824
- * // Force refresh
825
- * tokenManager.invalidate();
826
- * const newToken = await tokenManager.getToken();
827
- */
828
- declare class GoogleTokenManager {
829
- /**
830
- * @param {object} serviceAccount - Parsed service account with client_email and private_key
831
- * @param {object} options - Optional configuration
832
- * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
833
- * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
834
- */
835
- constructor(serviceAccount: object, options?: {
836
- scope: string;
837
- tokenLifetime: number;
838
- });
839
- clientEmail: any;
840
- privateKey: any;
841
- projectId: any;
842
- scope: string;
843
- tokenLifetime: number;
844
- _tokenCache: string;
845
- _expiresAt: number;
846
- /**
847
- * Get a valid access token (cached or fresh)
848
- *
849
- * @returns {Promise<string>} OAuth2 access token
850
- */
851
- getToken(): Promise<string>;
852
- /**
853
- * Clear cached token (force refresh on next getToken call)
854
- */
855
- invalidate(): void;
856
- /**
857
- * Check if cached token is still valid
858
- *
859
- * @returns {boolean} True if token is valid
860
- */
861
- isValid(): boolean;
862
- /**
863
- * Get time until token expires (in seconds)
864
- *
865
- * @returns {number} Seconds until expiration (0 if no token or expired)
866
- */
867
- getTimeToExpiry(): number;
868
- /**
869
- * Generate OAuth2 token via JWT flow
870
- *
871
- * @private
872
- * @returns {Promise<string>} Access token
873
- */
874
- private _generateToken;
875
- /**
876
- * Sign JWT using service account private key
877
- *
878
- * @private
879
- * @param {object} header - JWT header
880
- * @param {object} payload - JWT payload
881
- * @returns {Promise<string>} Signed JWT
882
- */
883
- private _signJWT;
884
- /**
885
- * Import PEM private key for signing
886
- *
887
- * @private
888
- * @param {string} pemKey - PEM formatted private key
889
- * @returns {Promise<CryptoKey>} Imported key
890
- */
891
- private _importPrivateKey;
892
- /**
893
- * Base64 URL encode (without padding)
894
- *
895
- * @private
896
- * @param {string|ArrayBuffer} input - Data to encode
897
- * @returns {string} Base64 URL encoded string
898
- */
899
- private _base64UrlEncode;
900
- }
901
-
902
811
  /**
903
812
  * Transcription Service - Separate from LLM Service
904
813
  * Handles audio-to-text transcription using various providers.
@@ -0,0 +1,257 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/utils/google-auth.js
20
+ var google_auth_exports = {};
21
+ __export(google_auth_exports, {
22
+ GoogleTokenManager: () => GoogleTokenManager,
23
+ getGoogleOAuth2Token: () => getGoogleOAuth2Token,
24
+ parseGoogleCredentials: () => parseGoogleCredentials,
25
+ validateServiceAccount: () => validateServiceAccount
26
+ });
27
+ module.exports = __toCommonJS(google_auth_exports);
28
+ var import_node_buffer = require("buffer");
29
+ function parseGoogleCredentials(input) {
30
+ if (!input) {
31
+ throw new Error("Google Cloud credentials not provided");
32
+ }
33
+ if (typeof input === "object") {
34
+ const { client_email, private_key } = input;
35
+ if (!client_email || !private_key) {
36
+ throw new Error("Google Cloud Service Account object missing client_email or private_key");
37
+ }
38
+ return input;
39
+ }
40
+ if (typeof input === "string") {
41
+ const trimmed = input.trim();
42
+ if (trimmed.startsWith("ya29.")) {
43
+ return trimmed;
44
+ }
45
+ if (trimmed.startsWith("AIzaSy")) {
46
+ throw new Error(
47
+ "Google Cloud Service APIs (Text-to-Speech, Speech-to-Text) do not support API keys (AIzaSy...). Please provide a Service Account JSON key from GCP Console > IAM & Admin > Service Accounts."
48
+ );
49
+ }
50
+ let jsonString = trimmed;
51
+ if (!jsonString.startsWith("{")) {
52
+ try {
53
+ jsonString = import_node_buffer.Buffer.from(jsonString, "base64").toString("utf-8");
54
+ } catch (err) {
55
+ throw new Error(`Failed to decode base64 service account: ${err.message}`);
56
+ }
57
+ }
58
+ try {
59
+ const parsed = JSON.parse(jsonString);
60
+ if (!parsed.client_email || !parsed.private_key) {
61
+ throw new Error("Service account missing required fields: client_email, private_key");
62
+ }
63
+ return parsed;
64
+ } catch (err) {
65
+ if (err.message.includes("missing required fields")) {
66
+ throw err;
67
+ }
68
+ throw new Error(`Failed to parse service account JSON: ${err.message}`);
69
+ }
70
+ }
71
+ throw new Error(
72
+ "Invalid Google Cloud credentials. Expected a Service Account JSON (with client_email & private_key) or OAuth2 Bearer token."
73
+ );
74
+ }
75
+ function validateServiceAccount(sa) {
76
+ if (!sa || typeof sa !== "object") {
77
+ throw new Error("Service account must be an object");
78
+ }
79
+ if (!sa.client_email) {
80
+ throw new Error("Service account missing client_email");
81
+ }
82
+ if (!sa.private_key) {
83
+ throw new Error("Service account missing private_key");
84
+ }
85
+ if (!sa.project_id) {
86
+ console.warn("[GoogleAuth] Service account missing project_id - may need to specify project explicitly");
87
+ }
88
+ return true;
89
+ }
90
+ var GoogleTokenManager = class {
91
+ /**
92
+ * @param {object} serviceAccount - Parsed service account with client_email and private_key
93
+ * @param {object} options - Optional configuration
94
+ * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
95
+ * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
96
+ */
97
+ constructor(serviceAccount, options = {}) {
98
+ validateServiceAccount(serviceAccount);
99
+ this.clientEmail = serviceAccount.client_email;
100
+ this.privateKey = serviceAccount.private_key;
101
+ this.projectId = serviceAccount.project_id;
102
+ this.scope = options.scope || "https://www.googleapis.com/auth/cloud-platform";
103
+ this.tokenLifetime = options.tokenLifetime || 3600;
104
+ this._tokenCache = null;
105
+ this._expiresAt = 0;
106
+ }
107
+ /**
108
+ * Get a valid access token (cached or fresh)
109
+ *
110
+ * @returns {Promise<string>} OAuth2 access token
111
+ */
112
+ async getToken() {
113
+ const now = Math.floor(Date.now() / 1e3);
114
+ if (this._tokenCache && now < this._expiresAt - 300) {
115
+ return this._tokenCache;
116
+ }
117
+ this._tokenCache = await this._generateToken();
118
+ this._expiresAt = now + this.tokenLifetime;
119
+ return this._tokenCache;
120
+ }
121
+ /**
122
+ * Clear cached token (force refresh on next getToken call)
123
+ */
124
+ invalidate() {
125
+ this._tokenCache = null;
126
+ this._expiresAt = 0;
127
+ }
128
+ /**
129
+ * Check if cached token is still valid
130
+ *
131
+ * @returns {boolean} True if token is valid
132
+ */
133
+ isValid() {
134
+ const now = Math.floor(Date.now() / 1e3);
135
+ return this._tokenCache && now < this._expiresAt - 300;
136
+ }
137
+ /**
138
+ * Get time until token expires (in seconds)
139
+ *
140
+ * @returns {number} Seconds until expiration (0 if no token or expired)
141
+ */
142
+ getTimeToExpiry() {
143
+ if (!this._tokenCache) return 0;
144
+ const now = Math.floor(Date.now() / 1e3);
145
+ return Math.max(0, this._expiresAt - now);
146
+ }
147
+ /**
148
+ * Generate OAuth2 token via JWT flow
149
+ *
150
+ * @private
151
+ * @returns {Promise<string>} Access token
152
+ */
153
+ async _generateToken() {
154
+ const now = Math.floor(Date.now() / 1e3);
155
+ const header = {
156
+ alg: "RS256",
157
+ typ: "JWT"
158
+ };
159
+ const payload = {
160
+ iss: this.clientEmail,
161
+ scope: this.scope,
162
+ aud: "https://oauth2.googleapis.com/token",
163
+ exp: now + this.tokenLifetime,
164
+ iat: now
165
+ };
166
+ const jwt = await this._signJWT(header, payload);
167
+ const response = await fetch("https://oauth2.googleapis.com/token", {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/x-www-form-urlencoded"
171
+ },
172
+ body: new URLSearchParams({
173
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
174
+ assertion: jwt
175
+ })
176
+ });
177
+ if (!response.ok) {
178
+ const error = await response.text();
179
+ throw new Error(`Failed to get OAuth2 token: ${response.status} - ${error}`);
180
+ }
181
+ const data = await response.json();
182
+ if (!data.access_token) {
183
+ throw new Error("No access_token in OAuth2 response");
184
+ }
185
+ return data.access_token;
186
+ }
187
+ /**
188
+ * Sign JWT using service account private key
189
+ *
190
+ * @private
191
+ * @param {object} header - JWT header
192
+ * @param {object} payload - JWT payload
193
+ * @returns {Promise<string>} Signed JWT
194
+ */
195
+ async _signJWT(header, payload) {
196
+ const encoder = new TextEncoder();
197
+ const headerB64 = this._base64UrlEncode(JSON.stringify(header));
198
+ const payloadB64 = this._base64UrlEncode(JSON.stringify(payload));
199
+ const unsignedToken = `${headerB64}.${payloadB64}`;
200
+ const privateKey = await this._importPrivateKey(this.privateKey);
201
+ const signature = await crypto.subtle.sign(
202
+ { name: "RSASSA-PKCS1-v1_5" },
203
+ privateKey,
204
+ encoder.encode(unsignedToken)
205
+ );
206
+ const signatureB64 = this._base64UrlEncode(signature);
207
+ return `${unsignedToken}.${signatureB64}`;
208
+ }
209
+ /**
210
+ * Import PEM private key for signing
211
+ *
212
+ * @private
213
+ * @param {string} pemKey - PEM formatted private key
214
+ * @returns {Promise<CryptoKey>} Imported key
215
+ */
216
+ async _importPrivateKey(pemKey) {
217
+ const pemContents = pemKey.replace(/-----BEGIN PRIVATE KEY-----/, "").replace(/-----END PRIVATE KEY-----/, "").replace(/\\n/g, "").replace(/\n/g, "").trim();
218
+ const binaryKey = import_node_buffer.Buffer.from(pemContents, "base64");
219
+ return crypto.subtle.importKey(
220
+ "pkcs8",
221
+ binaryKey,
222
+ {
223
+ name: "RSASSA-PKCS1-v1_5",
224
+ hash: "SHA-256"
225
+ },
226
+ false,
227
+ ["sign"]
228
+ );
229
+ }
230
+ /**
231
+ * Base64 URL encode (without padding)
232
+ *
233
+ * @private
234
+ * @param {string|ArrayBuffer} input - Data to encode
235
+ * @returns {string} Base64 URL encoded string
236
+ */
237
+ _base64UrlEncode(input) {
238
+ const buffer = typeof input === "string" ? import_node_buffer.Buffer.from(input, "utf-8") : import_node_buffer.Buffer.from(input);
239
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
240
+ }
241
+ };
242
+ async function getGoogleOAuth2Token(credentialInput, options = {}) {
243
+ const parsed = parseGoogleCredentials(credentialInput);
244
+ if (typeof parsed === "string") {
245
+ return parsed;
246
+ }
247
+ const manager = new GoogleTokenManager(parsed, options);
248
+ return manager.getToken();
249
+ }
250
+ // Annotate the CommonJS export names for ESM import in node:
251
+ 0 && (module.exports = {
252
+ GoogleTokenManager,
253
+ getGoogleOAuth2Token,
254
+ parseGoogleCredentials,
255
+ validateServiceAccount
256
+ });
257
+ //# sourceMappingURL=google-auth.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/google-auth.js"],"sourcesContent":["/**\n * Google Cloud Authentication Utilities\n * \n * Provides service account parsing, OAuth2 token generation,\n * and token lifecycle management for Google Cloud services.\n * \n * These utilities are designed for edge runtime environments\n * (Cloudflare Workers, Vercel Edge, etc.) where traditional\n * ADC (Application Default Credentials) is not available.\n */\n\nimport { Buffer } from 'node:buffer';\n\n/**\n * Parses and normalizes Google Service Account credentials.\n * Accepts:\n * - Service account JSON string\n * - Base64-encoded service account JSON string\n * - JavaScript object { client_email, private_key }\n * - Direct OAuth2 token string (starts with ya29.)\n * \n * @param {string|object} input - Service account as JSON string, base64, object, or OAuth2 token\n * @returns {string|object} OAuth2 token string (if input is token) or service account object\n * @throws {Error} If input is invalid or missing required fields\n * \n * @example\n * // From JSON string\n * const sa = parseGoogleCredentials('{\"client_email\":\"...\",\"private_key\":\"...\"}');\n * // Returns: { client_email, private_key, ... }\n * \n * // From base64\n * const sa = parseGoogleCredentials('eyJjbGllbnRfZW1haWwiOi4uLn0=');\n * // Returns: { client_email, private_key, ... }\n * \n * // From object\n * const sa = parseGoogleCredentials({ client_email: '...', private_key: '...' });\n * // Returns: { client_email, private_key, ... }\n * \n * // From OAuth2 token (pass-through)\n * const token = parseGoogleCredentials('ya29.a0...');\n * // Returns: 'ya29.a0...'\n */\nexport function parseGoogleCredentials(input) {\n if (!input) {\n throw new Error('Google Cloud credentials not provided');\n }\n \n // Already an object with client_email and private_key\n if (typeof input === 'object') {\n const { client_email, private_key } = input;\n if (!client_email || !private_key) {\n throw new Error('Google Cloud Service Account object missing client_email or private_key');\n }\n return input;\n }\n \n if (typeof input === 'string') {\n const trimmed = input.trim();\n \n // Already a Google OAuth2 access token (starts with ya29.)\n if (trimmed.startsWith('ya29.')) {\n return trimmed;\n }\n \n // Detect API key and provide clear error message\n if (trimmed.startsWith('AIzaSy')) {\n throw new Error(\n 'Google Cloud Service APIs (Text-to-Speech, Speech-to-Text) do not support API keys (AIzaSy...). ' +\n 'Please provide a Service Account JSON key from GCP Console > IAM & Admin > Service Accounts.'\n );\n }\n \n let jsonString = trimmed;\n \n // Try base64 decode if doesn't look like JSON\n if (!jsonString.startsWith('{')) {\n try {\n jsonString = Buffer.from(jsonString, 'base64').toString('utf-8');\n } catch (err) {\n throw new Error(`Failed to decode base64 service account: ${err.message}`);\n }\n }\n \n // Parse JSON\n try {\n const parsed = JSON.parse(jsonString);\n \n if (!parsed.client_email || !parsed.private_key) {\n throw new Error('Service account missing required fields: client_email, private_key');\n }\n \n return parsed;\n } catch (err) {\n if (err.message.includes('missing required fields')) {\n throw err;\n }\n throw new Error(`Failed to parse service account JSON: ${err.message}`);\n }\n }\n \n throw new Error(\n 'Invalid Google Cloud credentials. Expected a Service Account JSON (with client_email & private_key) or OAuth2 Bearer token.'\n );\n}\n\n/**\n * Validate service account has required fields\n * \n * @param {object} sa - Service account object\n * @returns {boolean} True if valid\n * @throws {Error} If invalid\n */\nexport function validateServiceAccount(sa) {\n if (!sa || typeof sa !== 'object') {\n throw new Error('Service account must be an object');\n }\n \n if (!sa.client_email) {\n throw new Error('Service account missing client_email');\n }\n \n if (!sa.private_key) {\n throw new Error('Service account missing private_key');\n }\n \n if (!sa.project_id) {\n console.warn('[GoogleAuth] Service account missing project_id - may need to specify project explicitly');\n }\n \n return true;\n}\n\n/**\n * Google OAuth2 Token Manager\n * \n * Handles token generation, caching, and automatic refresh for\n * Google Cloud service accounts. Tokens are cached and automatically\n * refreshed when they expire (with a 5-minute safety buffer).\n * \n * @example\n * const sa = parseGoogleCredentials(credentials);\n * const tokenManager = new GoogleTokenManager(sa);\n * \n * // Get token (cached or fresh)\n * const token = await tokenManager.getToken();\n * \n * // Force refresh\n * tokenManager.invalidate();\n * const newToken = await tokenManager.getToken();\n */\nexport class GoogleTokenManager {\n /**\n * @param {object} serviceAccount - Parsed service account with client_email and private_key\n * @param {object} options - Optional configuration\n * @param {string} options.scope - OAuth2 scope (default: cloud-platform)\n * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)\n */\n constructor(serviceAccount, options = {}) {\n validateServiceAccount(serviceAccount);\n \n this.clientEmail = serviceAccount.client_email;\n this.privateKey = serviceAccount.private_key;\n this.projectId = serviceAccount.project_id;\n \n this.scope = options.scope || 'https://www.googleapis.com/auth/cloud-platform';\n this.tokenLifetime = options.tokenLifetime || 3600;\n \n this._tokenCache = null;\n this._expiresAt = 0;\n }\n \n /**\n * Get a valid access token (cached or fresh)\n * \n * @returns {Promise<string>} OAuth2 access token\n */\n async getToken() {\n const now = Math.floor(Date.now() / 1000);\n \n // Return cached token if still valid (with 5 min buffer)\n if (this._tokenCache && now < this._expiresAt - 300) {\n return this._tokenCache;\n }\n \n // Generate new token\n this._tokenCache = await this._generateToken();\n this._expiresAt = now + this.tokenLifetime;\n \n return this._tokenCache;\n }\n \n /**\n * Clear cached token (force refresh on next getToken call)\n */\n invalidate() {\n this._tokenCache = null;\n this._expiresAt = 0;\n }\n \n /**\n * Check if cached token is still valid\n * \n * @returns {boolean} True if token is valid\n */\n isValid() {\n const now = Math.floor(Date.now() / 1000);\n return this._tokenCache && now < this._expiresAt - 300;\n }\n \n /**\n * Get time until token expires (in seconds)\n * \n * @returns {number} Seconds until expiration (0 if no token or expired)\n */\n getTimeToExpiry() {\n if (!this._tokenCache) return 0;\n const now = Math.floor(Date.now() / 1000);\n return Math.max(0, this._expiresAt - now);\n }\n \n /**\n * Generate OAuth2 token via JWT flow\n * \n * @private\n * @returns {Promise<string>} Access token\n */\n async _generateToken() {\n const now = Math.floor(Date.now() / 1000);\n \n // Create JWT header\n const header = {\n alg: 'RS256',\n typ: 'JWT'\n };\n \n // Create JWT payload\n const payload = {\n iss: this.clientEmail,\n scope: this.scope,\n aud: 'https://oauth2.googleapis.com/token',\n exp: now + this.tokenLifetime,\n iat: now\n };\n \n // Sign JWT\n const jwt = await this._signJWT(header, payload);\n \n // Exchange JWT for access token\n const response = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded'\n },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n assertion: jwt\n })\n });\n \n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to get OAuth2 token: ${response.status} - ${error}`);\n }\n \n const data = await response.json();\n \n if (!data.access_token) {\n throw new Error('No access_token in OAuth2 response');\n }\n \n return data.access_token;\n }\n \n /**\n * Sign JWT using service account private key\n * \n * @private\n * @param {object} header - JWT header\n * @param {object} payload - JWT payload\n * @returns {Promise<string>} Signed JWT\n */\n async _signJWT(header, payload) {\n const encoder = new TextEncoder();\n \n // Encode header and payload\n const headerB64 = this._base64UrlEncode(JSON.stringify(header));\n const payloadB64 = this._base64UrlEncode(JSON.stringify(payload));\n const unsignedToken = `${headerB64}.${payloadB64}`;\n \n // Import private key\n const privateKey = await this._importPrivateKey(this.privateKey);\n \n // Sign\n const signature = await crypto.subtle.sign(\n { name: 'RSASSA-PKCS1-v1_5' },\n privateKey,\n encoder.encode(unsignedToken)\n );\n \n // Encode signature\n const signatureB64 = this._base64UrlEncode(signature);\n \n return `${unsignedToken}.${signatureB64}`;\n }\n \n /**\n * Import PEM private key for signing\n * \n * @private\n * @param {string} pemKey - PEM formatted private key\n * @returns {Promise<CryptoKey>} Imported key\n */\n async _importPrivateKey(pemKey) {\n // Remove PEM headers and newlines\n const pemContents = pemKey\n .replace(/-----BEGIN PRIVATE KEY-----/, '')\n .replace(/-----END PRIVATE KEY-----/, '')\n .replace(/\\\\n/g, '')\n .replace(/\\n/g, '')\n .trim();\n \n // Decode base64\n const binaryKey = Buffer.from(pemContents, 'base64');\n \n // Import key\n return crypto.subtle.importKey(\n 'pkcs8',\n binaryKey,\n {\n name: 'RSASSA-PKCS1-v1_5',\n hash: 'SHA-256'\n },\n false,\n ['sign']\n );\n }\n \n /**\n * Base64 URL encode (without padding)\n * \n * @private\n * @param {string|ArrayBuffer} input - Data to encode\n * @returns {string} Base64 URL encoded string\n */\n _base64UrlEncode(input) {\n const buffer = typeof input === 'string' \n ? Buffer.from(input, 'utf-8')\n : Buffer.from(input);\n \n return buffer\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n }\n}\n\n/**\n * Convenience function to get a token directly (no caching)\n * Handles both service accounts and pre-existing OAuth2 tokens\n * \n * @param {string|object} credentialInput - Service account or OAuth2 token\n * @param {object} options - Optional configuration\n * @param {string} options.scope - OAuth2 scope (default: cloud-platform)\n * @returns {Promise<string>} Access token\n * \n * @example\n * // From service account\n * const sa = { client_email: '...', private_key: '...' };\n * const token = await getGoogleOAuth2Token(sa);\n * \n * // From OAuth2 token (pass-through)\n * const token = await getGoogleOAuth2Token('ya29.a0...');\n */\nexport async function getGoogleOAuth2Token(credentialInput, options = {}) {\n const parsed = parseGoogleCredentials(credentialInput);\n \n // If already a token (string), return it\n if (typeof parsed === 'string') {\n return parsed;\n }\n \n // Generate token from service account\n const manager = new GoogleTokenManager(parsed, options);\n return manager.getToken();\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Parses and normalizes Google Service Account credentials.
3
+ * Accepts:
4
+ * - Service account JSON string
5
+ * - Base64-encoded service account JSON string
6
+ * - JavaScript object { client_email, private_key }
7
+ * - Direct OAuth2 token string (starts with ya29.)
8
+ *
9
+ * @param {string|object} input - Service account as JSON string, base64, object, or OAuth2 token
10
+ * @returns {string|object} OAuth2 token string (if input is token) or service account object
11
+ * @throws {Error} If input is invalid or missing required fields
12
+ *
13
+ * @example
14
+ * // From JSON string
15
+ * const sa = parseGoogleCredentials('{"client_email":"...","private_key":"..."}');
16
+ * // Returns: { client_email, private_key, ... }
17
+ *
18
+ * // From base64
19
+ * const sa = parseGoogleCredentials('eyJjbGllbnRfZW1haWwiOi4uLn0=');
20
+ * // Returns: { client_email, private_key, ... }
21
+ *
22
+ * // From object
23
+ * const sa = parseGoogleCredentials({ client_email: '...', private_key: '...' });
24
+ * // Returns: { client_email, private_key, ... }
25
+ *
26
+ * // From OAuth2 token (pass-through)
27
+ * const token = parseGoogleCredentials('ya29.a0...');
28
+ * // Returns: 'ya29.a0...'
29
+ */
30
+ declare function parseGoogleCredentials(input: string | object): string | object;
31
+ /**
32
+ * Validate service account has required fields
33
+ *
34
+ * @param {object} sa - Service account object
35
+ * @returns {boolean} True if valid
36
+ * @throws {Error} If invalid
37
+ */
38
+ declare function validateServiceAccount(sa: object): boolean;
39
+ /**
40
+ * Convenience function to get a token directly (no caching)
41
+ * Handles both service accounts and pre-existing OAuth2 tokens
42
+ *
43
+ * @param {string|object} credentialInput - Service account or OAuth2 token
44
+ * @param {object} options - Optional configuration
45
+ * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
46
+ * @returns {Promise<string>} Access token
47
+ *
48
+ * @example
49
+ * // From service account
50
+ * const sa = { client_email: '...', private_key: '...' };
51
+ * const token = await getGoogleOAuth2Token(sa);
52
+ *
53
+ * // From OAuth2 token (pass-through)
54
+ * const token = await getGoogleOAuth2Token('ya29.a0...');
55
+ */
56
+ declare function getGoogleOAuth2Token(credentialInput: string | object, options?: {
57
+ scope: string;
58
+ }): Promise<string>;
59
+ /**
60
+ * Google OAuth2 Token Manager
61
+ *
62
+ * Handles token generation, caching, and automatic refresh for
63
+ * Google Cloud service accounts. Tokens are cached and automatically
64
+ * refreshed when they expire (with a 5-minute safety buffer).
65
+ *
66
+ * @example
67
+ * const sa = parseGoogleCredentials(credentials);
68
+ * const tokenManager = new GoogleTokenManager(sa);
69
+ *
70
+ * // Get token (cached or fresh)
71
+ * const token = await tokenManager.getToken();
72
+ *
73
+ * // Force refresh
74
+ * tokenManager.invalidate();
75
+ * const newToken = await tokenManager.getToken();
76
+ */
77
+ declare class GoogleTokenManager {
78
+ /**
79
+ * @param {object} serviceAccount - Parsed service account with client_email and private_key
80
+ * @param {object} options - Optional configuration
81
+ * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
82
+ * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
83
+ */
84
+ constructor(serviceAccount: object, options?: {
85
+ scope: string;
86
+ tokenLifetime: number;
87
+ });
88
+ clientEmail: any;
89
+ privateKey: any;
90
+ projectId: any;
91
+ scope: string;
92
+ tokenLifetime: number;
93
+ _tokenCache: string;
94
+ _expiresAt: number;
95
+ /**
96
+ * Get a valid access token (cached or fresh)
97
+ *
98
+ * @returns {Promise<string>} OAuth2 access token
99
+ */
100
+ getToken(): Promise<string>;
101
+ /**
102
+ * Clear cached token (force refresh on next getToken call)
103
+ */
104
+ invalidate(): void;
105
+ /**
106
+ * Check if cached token is still valid
107
+ *
108
+ * @returns {boolean} True if token is valid
109
+ */
110
+ isValid(): boolean;
111
+ /**
112
+ * Get time until token expires (in seconds)
113
+ *
114
+ * @returns {number} Seconds until expiration (0 if no token or expired)
115
+ */
116
+ getTimeToExpiry(): number;
117
+ /**
118
+ * Generate OAuth2 token via JWT flow
119
+ *
120
+ * @private
121
+ * @returns {Promise<string>} Access token
122
+ */
123
+ private _generateToken;
124
+ /**
125
+ * Sign JWT using service account private key
126
+ *
127
+ * @private
128
+ * @param {object} header - JWT header
129
+ * @param {object} payload - JWT payload
130
+ * @returns {Promise<string>} Signed JWT
131
+ */
132
+ private _signJWT;
133
+ /**
134
+ * Import PEM private key for signing
135
+ *
136
+ * @private
137
+ * @param {string} pemKey - PEM formatted private key
138
+ * @returns {Promise<CryptoKey>} Imported key
139
+ */
140
+ private _importPrivateKey;
141
+ /**
142
+ * Base64 URL encode (without padding)
143
+ *
144
+ * @private
145
+ * @param {string|ArrayBuffer} input - Data to encode
146
+ * @returns {string} Base64 URL encoded string
147
+ */
148
+ private _base64UrlEncode;
149
+ }
150
+
151
+ export { GoogleTokenManager, getGoogleOAuth2Token, parseGoogleCredentials, validateServiceAccount };