@contentgrowth/llm-service 1.4.0 → 1.4.2

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.
@@ -0,0 +1,230 @@
1
+ // src/utils/google-auth.js
2
+ import { Buffer } from "buffer";
3
+ function parseGoogleCredentials(input) {
4
+ if (!input) {
5
+ throw new Error("Google Cloud credentials not provided");
6
+ }
7
+ if (typeof input === "object") {
8
+ const { client_email, private_key } = input;
9
+ if (!client_email || !private_key) {
10
+ throw new Error("Google Cloud Service Account object missing client_email or private_key");
11
+ }
12
+ return input;
13
+ }
14
+ if (typeof input === "string") {
15
+ const trimmed = input.trim();
16
+ if (trimmed.startsWith("ya29.")) {
17
+ return trimmed;
18
+ }
19
+ if (trimmed.startsWith("AIzaSy")) {
20
+ throw new Error(
21
+ "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."
22
+ );
23
+ }
24
+ let jsonString = trimmed;
25
+ if (!jsonString.startsWith("{")) {
26
+ try {
27
+ jsonString = Buffer.from(jsonString, "base64").toString("utf-8");
28
+ } catch (err) {
29
+ throw new Error(`Failed to decode base64 service account: ${err.message}`);
30
+ }
31
+ }
32
+ try {
33
+ const parsed = JSON.parse(jsonString);
34
+ if (!parsed.client_email || !parsed.private_key) {
35
+ throw new Error("Service account missing required fields: client_email, private_key");
36
+ }
37
+ return parsed;
38
+ } catch (err) {
39
+ if (err.message.includes("missing required fields")) {
40
+ throw err;
41
+ }
42
+ throw new Error(`Failed to parse service account JSON: ${err.message}`);
43
+ }
44
+ }
45
+ throw new Error(
46
+ "Invalid Google Cloud credentials. Expected a Service Account JSON (with client_email & private_key) or OAuth2 Bearer token."
47
+ );
48
+ }
49
+ function validateServiceAccount(sa) {
50
+ if (!sa || typeof sa !== "object") {
51
+ throw new Error("Service account must be an object");
52
+ }
53
+ if (!sa.client_email) {
54
+ throw new Error("Service account missing client_email");
55
+ }
56
+ if (!sa.private_key) {
57
+ throw new Error("Service account missing private_key");
58
+ }
59
+ if (!sa.project_id) {
60
+ console.warn("[GoogleAuth] Service account missing project_id - may need to specify project explicitly");
61
+ }
62
+ return true;
63
+ }
64
+ var GoogleTokenManager = class {
65
+ /**
66
+ * @param {object} serviceAccount - Parsed service account with client_email and private_key
67
+ * @param {object} options - Optional configuration
68
+ * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
69
+ * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
70
+ */
71
+ constructor(serviceAccount, options = {}) {
72
+ validateServiceAccount(serviceAccount);
73
+ this.clientEmail = serviceAccount.client_email;
74
+ this.privateKey = serviceAccount.private_key;
75
+ this.projectId = serviceAccount.project_id;
76
+ this.scope = options.scope || "https://www.googleapis.com/auth/cloud-platform";
77
+ this.tokenLifetime = options.tokenLifetime || 3600;
78
+ this._tokenCache = null;
79
+ this._expiresAt = 0;
80
+ }
81
+ /**
82
+ * Get a valid access token (cached or fresh)
83
+ *
84
+ * @returns {Promise<string>} OAuth2 access token
85
+ */
86
+ async getToken() {
87
+ const now = Math.floor(Date.now() / 1e3);
88
+ if (this._tokenCache && now < this._expiresAt - 300) {
89
+ return this._tokenCache;
90
+ }
91
+ this._tokenCache = await this._generateToken();
92
+ this._expiresAt = now + this.tokenLifetime;
93
+ return this._tokenCache;
94
+ }
95
+ /**
96
+ * Clear cached token (force refresh on next getToken call)
97
+ */
98
+ invalidate() {
99
+ this._tokenCache = null;
100
+ this._expiresAt = 0;
101
+ }
102
+ /**
103
+ * Check if cached token is still valid
104
+ *
105
+ * @returns {boolean} True if token is valid
106
+ */
107
+ isValid() {
108
+ const now = Math.floor(Date.now() / 1e3);
109
+ return this._tokenCache && now < this._expiresAt - 300;
110
+ }
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() {
117
+ if (!this._tokenCache) return 0;
118
+ const now = Math.floor(Date.now() / 1e3);
119
+ return Math.max(0, this._expiresAt - now);
120
+ }
121
+ /**
122
+ * Generate OAuth2 token via JWT flow
123
+ *
124
+ * @private
125
+ * @returns {Promise<string>} Access token
126
+ */
127
+ async _generateToken() {
128
+ const now = Math.floor(Date.now() / 1e3);
129
+ const header = {
130
+ alg: "RS256",
131
+ typ: "JWT"
132
+ };
133
+ const payload = {
134
+ iss: this.clientEmail,
135
+ scope: this.scope,
136
+ aud: "https://oauth2.googleapis.com/token",
137
+ exp: now + this.tokenLifetime,
138
+ iat: now
139
+ };
140
+ const jwt = await this._signJWT(header, payload);
141
+ const response = await fetch("https://oauth2.googleapis.com/token", {
142
+ method: "POST",
143
+ headers: {
144
+ "Content-Type": "application/x-www-form-urlencoded"
145
+ },
146
+ body: new URLSearchParams({
147
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
148
+ assertion: jwt
149
+ })
150
+ });
151
+ if (!response.ok) {
152
+ const error = await response.text();
153
+ throw new Error(`Failed to get OAuth2 token: ${response.status} - ${error}`);
154
+ }
155
+ const data = await response.json();
156
+ if (!data.access_token) {
157
+ throw new Error("No access_token in OAuth2 response");
158
+ }
159
+ return data.access_token;
160
+ }
161
+ /**
162
+ * Sign JWT using service account private key
163
+ *
164
+ * @private
165
+ * @param {object} header - JWT header
166
+ * @param {object} payload - JWT payload
167
+ * @returns {Promise<string>} Signed JWT
168
+ */
169
+ async _signJWT(header, payload) {
170
+ const encoder = new TextEncoder();
171
+ const headerB64 = this._base64UrlEncode(JSON.stringify(header));
172
+ const payloadB64 = this._base64UrlEncode(JSON.stringify(payload));
173
+ const unsignedToken = `${headerB64}.${payloadB64}`;
174
+ const privateKey = await this._importPrivateKey(this.privateKey);
175
+ const signature = await crypto.subtle.sign(
176
+ { name: "RSASSA-PKCS1-v1_5" },
177
+ privateKey,
178
+ encoder.encode(unsignedToken)
179
+ );
180
+ const signatureB64 = this._base64UrlEncode(signature);
181
+ return `${unsignedToken}.${signatureB64}`;
182
+ }
183
+ /**
184
+ * Import PEM private key for signing
185
+ *
186
+ * @private
187
+ * @param {string} pemKey - PEM formatted private key
188
+ * @returns {Promise<CryptoKey>} Imported key
189
+ */
190
+ async _importPrivateKey(pemKey) {
191
+ const pemContents = pemKey.replace(/-----BEGIN PRIVATE KEY-----/, "").replace(/-----END PRIVATE KEY-----/, "").replace(/\\n/g, "").replace(/\n/g, "").trim();
192
+ const binaryKey = Buffer.from(pemContents, "base64");
193
+ return crypto.subtle.importKey(
194
+ "pkcs8",
195
+ binaryKey,
196
+ {
197
+ name: "RSASSA-PKCS1-v1_5",
198
+ hash: "SHA-256"
199
+ },
200
+ false,
201
+ ["sign"]
202
+ );
203
+ }
204
+ /**
205
+ * Base64 URL encode (without padding)
206
+ *
207
+ * @private
208
+ * @param {string|ArrayBuffer} input - Data to encode
209
+ * @returns {string} Base64 URL encoded string
210
+ */
211
+ _base64UrlEncode(input) {
212
+ const buffer = typeof input === "string" ? Buffer.from(input, "utf-8") : Buffer.from(input);
213
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
214
+ }
215
+ };
216
+ async function getGoogleOAuth2Token(credentialInput, options = {}) {
217
+ const parsed = parseGoogleCredentials(credentialInput);
218
+ if (typeof parsed === "string") {
219
+ return parsed;
220
+ }
221
+ const manager = new GoogleTokenManager(parsed, options);
222
+ return manager.getToken();
223
+ }
224
+ export {
225
+ GoogleTokenManager,
226
+ getGoogleOAuth2Token,
227
+ parseGoogleCredentials,
228
+ validateServiceAccount
229
+ };
230
+ //# sourceMappingURL=google-auth.js.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,259 @@
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/index.js
20
+ var utils_exports = {};
21
+ __export(utils_exports, {
22
+ GoogleTokenManager: () => GoogleTokenManager,
23
+ getGoogleOAuth2Token: () => getGoogleOAuth2Token,
24
+ parseGoogleCredentials: () => parseGoogleCredentials,
25
+ validateServiceAccount: () => validateServiceAccount
26
+ });
27
+ module.exports = __toCommonJS(utils_exports);
28
+
29
+ // src/utils/google-auth.js
30
+ var import_node_buffer = require("buffer");
31
+ function parseGoogleCredentials(input) {
32
+ if (!input) {
33
+ throw new Error("Google Cloud credentials not provided");
34
+ }
35
+ if (typeof input === "object") {
36
+ const { client_email, private_key } = input;
37
+ if (!client_email || !private_key) {
38
+ throw new Error("Google Cloud Service Account object missing client_email or private_key");
39
+ }
40
+ return input;
41
+ }
42
+ if (typeof input === "string") {
43
+ const trimmed = input.trim();
44
+ if (trimmed.startsWith("ya29.")) {
45
+ return trimmed;
46
+ }
47
+ if (trimmed.startsWith("AIzaSy")) {
48
+ throw new Error(
49
+ "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."
50
+ );
51
+ }
52
+ let jsonString = trimmed;
53
+ if (!jsonString.startsWith("{")) {
54
+ try {
55
+ jsonString = import_node_buffer.Buffer.from(jsonString, "base64").toString("utf-8");
56
+ } catch (err) {
57
+ throw new Error(`Failed to decode base64 service account: ${err.message}`);
58
+ }
59
+ }
60
+ try {
61
+ const parsed = JSON.parse(jsonString);
62
+ if (!parsed.client_email || !parsed.private_key) {
63
+ throw new Error("Service account missing required fields: client_email, private_key");
64
+ }
65
+ return parsed;
66
+ } catch (err) {
67
+ if (err.message.includes("missing required fields")) {
68
+ throw err;
69
+ }
70
+ throw new Error(`Failed to parse service account JSON: ${err.message}`);
71
+ }
72
+ }
73
+ throw new Error(
74
+ "Invalid Google Cloud credentials. Expected a Service Account JSON (with client_email & private_key) or OAuth2 Bearer token."
75
+ );
76
+ }
77
+ function validateServiceAccount(sa) {
78
+ if (!sa || typeof sa !== "object") {
79
+ throw new Error("Service account must be an object");
80
+ }
81
+ if (!sa.client_email) {
82
+ throw new Error("Service account missing client_email");
83
+ }
84
+ if (!sa.private_key) {
85
+ throw new Error("Service account missing private_key");
86
+ }
87
+ if (!sa.project_id) {
88
+ console.warn("[GoogleAuth] Service account missing project_id - may need to specify project explicitly");
89
+ }
90
+ return true;
91
+ }
92
+ var GoogleTokenManager = class {
93
+ /**
94
+ * @param {object} serviceAccount - Parsed service account with client_email and private_key
95
+ * @param {object} options - Optional configuration
96
+ * @param {string} options.scope - OAuth2 scope (default: cloud-platform)
97
+ * @param {number} options.tokenLifetime - Token lifetime in seconds (default: 3600)
98
+ */
99
+ constructor(serviceAccount, options = {}) {
100
+ validateServiceAccount(serviceAccount);
101
+ this.clientEmail = serviceAccount.client_email;
102
+ this.privateKey = serviceAccount.private_key;
103
+ this.projectId = serviceAccount.project_id;
104
+ this.scope = options.scope || "https://www.googleapis.com/auth/cloud-platform";
105
+ this.tokenLifetime = options.tokenLifetime || 3600;
106
+ this._tokenCache = null;
107
+ this._expiresAt = 0;
108
+ }
109
+ /**
110
+ * Get a valid access token (cached or fresh)
111
+ *
112
+ * @returns {Promise<string>} OAuth2 access token
113
+ */
114
+ async getToken() {
115
+ const now = Math.floor(Date.now() / 1e3);
116
+ if (this._tokenCache && now < this._expiresAt - 300) {
117
+ return this._tokenCache;
118
+ }
119
+ this._tokenCache = await this._generateToken();
120
+ this._expiresAt = now + this.tokenLifetime;
121
+ return this._tokenCache;
122
+ }
123
+ /**
124
+ * Clear cached token (force refresh on next getToken call)
125
+ */
126
+ invalidate() {
127
+ this._tokenCache = null;
128
+ this._expiresAt = 0;
129
+ }
130
+ /**
131
+ * Check if cached token is still valid
132
+ *
133
+ * @returns {boolean} True if token is valid
134
+ */
135
+ isValid() {
136
+ const now = Math.floor(Date.now() / 1e3);
137
+ return this._tokenCache && now < this._expiresAt - 300;
138
+ }
139
+ /**
140
+ * Get time until token expires (in seconds)
141
+ *
142
+ * @returns {number} Seconds until expiration (0 if no token or expired)
143
+ */
144
+ getTimeToExpiry() {
145
+ if (!this._tokenCache) return 0;
146
+ const now = Math.floor(Date.now() / 1e3);
147
+ return Math.max(0, this._expiresAt - now);
148
+ }
149
+ /**
150
+ * Generate OAuth2 token via JWT flow
151
+ *
152
+ * @private
153
+ * @returns {Promise<string>} Access token
154
+ */
155
+ async _generateToken() {
156
+ const now = Math.floor(Date.now() / 1e3);
157
+ const header = {
158
+ alg: "RS256",
159
+ typ: "JWT"
160
+ };
161
+ const payload = {
162
+ iss: this.clientEmail,
163
+ scope: this.scope,
164
+ aud: "https://oauth2.googleapis.com/token",
165
+ exp: now + this.tokenLifetime,
166
+ iat: now
167
+ };
168
+ const jwt = await this._signJWT(header, payload);
169
+ const response = await fetch("https://oauth2.googleapis.com/token", {
170
+ method: "POST",
171
+ headers: {
172
+ "Content-Type": "application/x-www-form-urlencoded"
173
+ },
174
+ body: new URLSearchParams({
175
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
176
+ assertion: jwt
177
+ })
178
+ });
179
+ if (!response.ok) {
180
+ const error = await response.text();
181
+ throw new Error(`Failed to get OAuth2 token: ${response.status} - ${error}`);
182
+ }
183
+ const data = await response.json();
184
+ if (!data.access_token) {
185
+ throw new Error("No access_token in OAuth2 response");
186
+ }
187
+ return data.access_token;
188
+ }
189
+ /**
190
+ * Sign JWT using service account private key
191
+ *
192
+ * @private
193
+ * @param {object} header - JWT header
194
+ * @param {object} payload - JWT payload
195
+ * @returns {Promise<string>} Signed JWT
196
+ */
197
+ async _signJWT(header, payload) {
198
+ const encoder = new TextEncoder();
199
+ const headerB64 = this._base64UrlEncode(JSON.stringify(header));
200
+ const payloadB64 = this._base64UrlEncode(JSON.stringify(payload));
201
+ const unsignedToken = `${headerB64}.${payloadB64}`;
202
+ const privateKey = await this._importPrivateKey(this.privateKey);
203
+ const signature = await crypto.subtle.sign(
204
+ { name: "RSASSA-PKCS1-v1_5" },
205
+ privateKey,
206
+ encoder.encode(unsignedToken)
207
+ );
208
+ const signatureB64 = this._base64UrlEncode(signature);
209
+ return `${unsignedToken}.${signatureB64}`;
210
+ }
211
+ /**
212
+ * Import PEM private key for signing
213
+ *
214
+ * @private
215
+ * @param {string} pemKey - PEM formatted private key
216
+ * @returns {Promise<CryptoKey>} Imported key
217
+ */
218
+ async _importPrivateKey(pemKey) {
219
+ const pemContents = pemKey.replace(/-----BEGIN PRIVATE KEY-----/, "").replace(/-----END PRIVATE KEY-----/, "").replace(/\\n/g, "").replace(/\n/g, "").trim();
220
+ const binaryKey = import_node_buffer.Buffer.from(pemContents, "base64");
221
+ return crypto.subtle.importKey(
222
+ "pkcs8",
223
+ binaryKey,
224
+ {
225
+ name: "RSASSA-PKCS1-v1_5",
226
+ hash: "SHA-256"
227
+ },
228
+ false,
229
+ ["sign"]
230
+ );
231
+ }
232
+ /**
233
+ * Base64 URL encode (without padding)
234
+ *
235
+ * @private
236
+ * @param {string|ArrayBuffer} input - Data to encode
237
+ * @returns {string} Base64 URL encoded string
238
+ */
239
+ _base64UrlEncode(input) {
240
+ const buffer = typeof input === "string" ? import_node_buffer.Buffer.from(input, "utf-8") : import_node_buffer.Buffer.from(input);
241
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
242
+ }
243
+ };
244
+ async function getGoogleOAuth2Token(credentialInput, options = {}) {
245
+ const parsed = parseGoogleCredentials(credentialInput);
246
+ if (typeof parsed === "string") {
247
+ return parsed;
248
+ }
249
+ const manager = new GoogleTokenManager(parsed, options);
250
+ return manager.getToken();
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ GoogleTokenManager,
255
+ getGoogleOAuth2Token,
256
+ parseGoogleCredentials,
257
+ validateServiceAccount
258
+ });
259
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/index.js","../../src/utils/google-auth.js"],"sourcesContent":["/**\n * LLM Service Utilities\n * \n * Shared utilities for working with LLM providers,\n * authentication, and configuration management.\n */\n\nexport { \n parseGoogleCredentials, \n validateServiceAccount,\n GoogleTokenManager,\n getGoogleOAuth2Token\n} from './google-auth.js';\n","/**\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 @@
1
+ export { GoogleTokenManager, getGoogleOAuth2Token, parseGoogleCredentials, validateServiceAccount } from './google-auth.cjs';
@@ -0,0 +1 @@
1
+ export { GoogleTokenManager, getGoogleOAuth2Token, parseGoogleCredentials, validateServiceAccount } from './google-auth.js';