@ariadng/sheets 0.4.0 → 0.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.
Files changed (47) hide show
  1. package/dist/cli.cjs +1610 -0
  2. package/dist/cli.cjs.map +1 -0
  3. package/dist/cli.d.cts +1 -0
  4. package/dist/cli.d.ts +0 -6
  5. package/dist/cli.js +1318 -595
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.cjs +858 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.cts +356 -0
  10. package/dist/index.d.ts +355 -10
  11. package/dist/index.js +810 -11
  12. package/dist/index.js.map +1 -1
  13. package/package.json +5 -3
  14. package/dist/api/index.d.ts +0 -60
  15. package/dist/api/index.d.ts.map +0 -1
  16. package/dist/api/index.js +0 -347
  17. package/dist/api/index.js.map +0 -1
  18. package/dist/auth/constants.d.ts +0 -13
  19. package/dist/auth/constants.d.ts.map +0 -1
  20. package/dist/auth/constants.js +0 -21
  21. package/dist/auth/constants.js.map +0 -1
  22. package/dist/auth/index.d.ts +0 -13
  23. package/dist/auth/index.d.ts.map +0 -1
  24. package/dist/auth/index.js +0 -22
  25. package/dist/auth/index.js.map +0 -1
  26. package/dist/auth/oauth.d.ts +0 -32
  27. package/dist/auth/oauth.d.ts.map +0 -1
  28. package/dist/auth/oauth.js +0 -80
  29. package/dist/auth/oauth.js.map +0 -1
  30. package/dist/auth/service-account.d.ts +0 -18
  31. package/dist/auth/service-account.d.ts.map +0 -1
  32. package/dist/auth/service-account.js +0 -92
  33. package/dist/auth/service-account.js.map +0 -1
  34. package/dist/auth/user-auth.d.ts +0 -24
  35. package/dist/auth/user-auth.d.ts.map +0 -1
  36. package/dist/auth/user-auth.js +0 -230
  37. package/dist/auth/user-auth.js.map +0 -1
  38. package/dist/cli.d.ts.map +0 -1
  39. package/dist/http/index.d.ts +0 -19
  40. package/dist/http/index.d.ts.map +0 -1
  41. package/dist/http/index.js +0 -68
  42. package/dist/http/index.js.map +0 -1
  43. package/dist/index.d.ts.map +0 -1
  44. package/dist/types/index.d.ts +0 -200
  45. package/dist/types/index.d.ts.map +0 -1
  46. package/dist/types/index.js +0 -16
  47. package/dist/types/index.js.map +0 -1
package/dist/index.cjs ADDED
@@ -0,0 +1,858 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ HttpClient: () => HttpClient,
34
+ OAuthAuth: () => OAuthAuth,
35
+ ServiceAccountAuth: () => ServiceAccountAuth,
36
+ SheetsClient: () => SheetsClient,
37
+ SheetsError: () => SheetsError,
38
+ UserAuth: () => UserAuth,
39
+ createAuthProvider: () => createAuthProvider,
40
+ createClient: () => createClient,
41
+ deleteTokens: () => deleteTokens,
42
+ loadStoredTokens: () => loadStoredTokens,
43
+ login: () => login
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+
47
+ // src/types/index.ts
48
+ var SheetsError = class extends Error {
49
+ code;
50
+ status;
51
+ details;
52
+ constructor(error) {
53
+ super(error.message);
54
+ this.name = "SheetsError";
55
+ this.code = error.code;
56
+ this.status = error.status;
57
+ this.details = error.details;
58
+ }
59
+ };
60
+
61
+ // src/http/index.ts
62
+ var BASE_URL = "https://sheets.googleapis.com/v4";
63
+ var MAX_RETRIES = 3;
64
+ var INITIAL_BACKOFF_MS = 1e3;
65
+ var HttpClient = class {
66
+ getAccessToken;
67
+ constructor(options) {
68
+ this.getAccessToken = options.getAccessToken;
69
+ }
70
+ async request(path2, options = {}) {
71
+ const { method = "GET", body, params } = options;
72
+ let url = `${BASE_URL}${path2}`;
73
+ if (params) {
74
+ const searchParams = new URLSearchParams(params);
75
+ url += `?${searchParams.toString()}`;
76
+ }
77
+ let lastError = null;
78
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
79
+ try {
80
+ const accessToken = await this.getAccessToken();
81
+ const response = await fetch(url, {
82
+ method,
83
+ headers: {
84
+ "Authorization": `Bearer ${accessToken}`,
85
+ "Content-Type": "application/json"
86
+ },
87
+ body: body ? JSON.stringify(body) : void 0
88
+ });
89
+ if (response.status === 429) {
90
+ const backoffMs = INITIAL_BACKOFF_MS * Math.pow(2, attempt);
91
+ await this.sleep(backoffMs);
92
+ continue;
93
+ }
94
+ const data = await response.json();
95
+ if (!response.ok) {
96
+ if (data.error) {
97
+ throw new SheetsError({
98
+ code: data.error.code,
99
+ message: data.error.message,
100
+ status: data.error.status
101
+ });
102
+ }
103
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
104
+ }
105
+ return data;
106
+ } catch (error) {
107
+ lastError = error;
108
+ if (error instanceof SheetsError && error.code !== 429 && error.code !== 500 && error.code !== 503) {
109
+ throw error;
110
+ }
111
+ if (attempt < MAX_RETRIES - 1) {
112
+ const backoffMs = INITIAL_BACKOFF_MS * Math.pow(2, attempt);
113
+ await this.sleep(backoffMs);
114
+ }
115
+ }
116
+ }
117
+ throw lastError || new Error("Request failed after retries");
118
+ }
119
+ sleep(ms) {
120
+ return new Promise((resolve) => setTimeout(resolve, ms));
121
+ }
122
+ };
123
+
124
+ // src/auth/constants.ts
125
+ var OAUTH_CLIENT_ID = "344941894490-jmdvo5ghomqi7vuisfrf80hfassk1ma5.apps.googleusercontent.com";
126
+ var OAUTH_CLIENT_SECRET = "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";
127
+ var OAUTH_REDIRECT_URI = "http://localhost:8085/callback";
128
+ var OAUTH_CALLBACK_PORT = 8085;
129
+ var OAUTH_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
130
+ var OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
131
+ var OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
132
+ var OAUTH_SCOPES = [
133
+ "https://www.googleapis.com/auth/spreadsheets",
134
+ "https://www.googleapis.com/auth/userinfo.email"
135
+ ];
136
+
137
+ // src/auth/oauth.ts
138
+ var OAuthAuth = class {
139
+ config;
140
+ cachedToken;
141
+ expiresAt;
142
+ constructor(config) {
143
+ this.config = config;
144
+ this.cachedToken = config.accessToken;
145
+ this.expiresAt = config.expiresAt || 0;
146
+ }
147
+ async getAccessToken() {
148
+ if (!this.canRefresh()) {
149
+ return this.cachedToken;
150
+ }
151
+ if (this.isExpired()) {
152
+ await this.refreshToken();
153
+ }
154
+ return this.cachedToken;
155
+ }
156
+ canRefresh() {
157
+ return !!(this.config.refreshToken && this.config.clientId && this.config.clientSecret && this.expiresAt > 0);
158
+ }
159
+ isExpired() {
160
+ return Date.now() >= this.expiresAt - 6e4;
161
+ }
162
+ async refreshToken() {
163
+ if (!this.config.refreshToken || !this.config.clientId || !this.config.clientSecret) {
164
+ throw new Error("Token expired and missing refresh credentials (refreshToken, clientId, clientSecret)");
165
+ }
166
+ const response = await fetch(OAUTH_TOKEN_URL, {
167
+ method: "POST",
168
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
169
+ body: new URLSearchParams({
170
+ client_id: this.config.clientId,
171
+ client_secret: this.config.clientSecret,
172
+ refresh_token: this.config.refreshToken,
173
+ grant_type: "refresh_token"
174
+ })
175
+ });
176
+ if (!response.ok) {
177
+ const error = await response.text();
178
+ throw new Error(`Token refresh failed: ${error}`);
179
+ }
180
+ const tokenResponse = await response.json();
181
+ this.cachedToken = tokenResponse.access_token;
182
+ this.expiresAt = Date.now() + tokenResponse.expires_in * 1e3;
183
+ if (tokenResponse.refresh_token) {
184
+ this.config.refreshToken = tokenResponse.refresh_token;
185
+ }
186
+ }
187
+ /**
188
+ * Get current token state for persistence in automation tools
189
+ * Returns updated tokens after any refresh operations
190
+ */
191
+ getTokenState() {
192
+ return {
193
+ accessToken: this.cachedToken,
194
+ refreshToken: this.config.refreshToken,
195
+ expiresAt: this.expiresAt
196
+ };
197
+ }
198
+ };
199
+
200
+ // src/auth/service-account.ts
201
+ var crypto = __toESM(require("crypto"), 1);
202
+ var fs = __toESM(require("fs/promises"), 1);
203
+ var TOKEN_URI = "https://oauth2.googleapis.com/token";
204
+ var SCOPE = "https://www.googleapis.com/auth/spreadsheets";
205
+ var TOKEN_LIFETIME_SECONDS = 3600;
206
+ var ServiceAccountAuth = class {
207
+ config;
208
+ credentials = null;
209
+ cachedToken = null;
210
+ tokenExpiresAt = 0;
211
+ constructor(config) {
212
+ this.config = config;
213
+ }
214
+ async getAccessToken() {
215
+ if (this.cachedToken && Date.now() < this.tokenExpiresAt - 6e4) {
216
+ return this.cachedToken;
217
+ }
218
+ await this.loadCredentials();
219
+ const jwt = this.createJwt();
220
+ const token = await this.exchangeJwtForToken(jwt);
221
+ this.cachedToken = token.access_token;
222
+ this.tokenExpiresAt = Date.now() + token.expires_in * 1e3;
223
+ return this.cachedToken;
224
+ }
225
+ async loadCredentials() {
226
+ if (this.credentials) return;
227
+ if (this.config.credentials) {
228
+ this.credentials = this.config.credentials;
229
+ return;
230
+ }
231
+ if (!this.config.credentialsPath) {
232
+ throw new Error("Service account requires credentialsPath or credentials");
233
+ }
234
+ const content = await fs.readFile(this.config.credentialsPath, "utf-8");
235
+ this.credentials = JSON.parse(content);
236
+ }
237
+ createJwt() {
238
+ if (!this.credentials) {
239
+ throw new Error("Credentials not loaded");
240
+ }
241
+ const now = Math.floor(Date.now() / 1e3);
242
+ const header = {
243
+ alg: "RS256",
244
+ typ: "JWT"
245
+ };
246
+ const payload = {
247
+ iss: this.credentials.client_email,
248
+ scope: SCOPE,
249
+ aud: TOKEN_URI,
250
+ iat: now,
251
+ exp: now + TOKEN_LIFETIME_SECONDS
252
+ };
253
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
254
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
255
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
256
+ const sign = crypto.createSign("RSA-SHA256");
257
+ sign.update(signatureInput);
258
+ const signature = sign.sign(this.credentials.private_key);
259
+ const encodedSignature = this.base64UrlEncode(signature);
260
+ return `${signatureInput}.${encodedSignature}`;
261
+ }
262
+ base64UrlEncode(input) {
263
+ const buffer = typeof input === "string" ? Buffer.from(input) : input;
264
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
265
+ }
266
+ async exchangeJwtForToken(jwt) {
267
+ const response = await fetch(TOKEN_URI, {
268
+ method: "POST",
269
+ headers: {
270
+ "Content-Type": "application/x-www-form-urlencoded"
271
+ },
272
+ body: new URLSearchParams({
273
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
274
+ assertion: jwt
275
+ })
276
+ });
277
+ if (!response.ok) {
278
+ const error = await response.text();
279
+ throw new Error(`Token exchange failed: ${error}`);
280
+ }
281
+ return await response.json();
282
+ }
283
+ };
284
+
285
+ // src/auth/user-auth.ts
286
+ var crypto2 = __toESM(require("crypto"), 1);
287
+ var fs2 = __toESM(require("fs/promises"), 1);
288
+ var http = __toESM(require("http"), 1);
289
+ var os = __toESM(require("os"), 1);
290
+ var path = __toESM(require("path"), 1);
291
+ var import_child_process = require("child_process");
292
+ var CONFIG_DIR = path.join(os.homedir(), ".sheets");
293
+ var TOKENS_FILE = path.join(CONFIG_DIR, "tokens.json");
294
+ function generatePKCE() {
295
+ const codeVerifier = crypto2.randomBytes(32).toString("base64url");
296
+ const codeChallenge = crypto2.createHash("sha256").update(codeVerifier).digest("base64url");
297
+ return { codeVerifier, codeChallenge };
298
+ }
299
+ function openBrowser(url) {
300
+ return new Promise((resolve, reject) => {
301
+ const platform = process.platform;
302
+ let command;
303
+ if (platform === "darwin") {
304
+ command = `open "${url}"`;
305
+ } else if (platform === "win32") {
306
+ command = `start "" "${url}"`;
307
+ } else {
308
+ command = `xdg-open "${url}"`;
309
+ }
310
+ (0, import_child_process.exec)(command, (error) => {
311
+ if (error) {
312
+ reject(new Error(`Failed to open browser: ${error.message}`));
313
+ } else {
314
+ resolve();
315
+ }
316
+ });
317
+ });
318
+ }
319
+ function startCallbackServer() {
320
+ return new Promise((resolve, reject) => {
321
+ let timeoutId;
322
+ const server = http.createServer((req, res) => {
323
+ const url = new URL(req.url || "", `http://localhost:${OAUTH_CALLBACK_PORT}`);
324
+ if (url.pathname === "/callback") {
325
+ const code = url.searchParams.get("code");
326
+ const error = url.searchParams.get("error");
327
+ if (error) {
328
+ res.writeHead(400, { "Content-Type": "text/html" });
329
+ res.end("<html><body><h1>Authorization Failed</h1><p>You can close this window.</p></body></html>");
330
+ clearTimeout(timeoutId);
331
+ server.close();
332
+ reject(new Error(`Authorization error: ${error}`));
333
+ return;
334
+ }
335
+ if (code) {
336
+ res.writeHead(200, { "Content-Type": "text/html" });
337
+ res.end("<html><body><h1>Authorization Successful</h1><p>You can close this window.</p></body></html>");
338
+ clearTimeout(timeoutId);
339
+ server.close();
340
+ resolve(code);
341
+ return;
342
+ }
343
+ res.writeHead(400, { "Content-Type": "text/html" });
344
+ res.end("<html><body><h1>Missing Code</h1></body></html>");
345
+ } else {
346
+ res.writeHead(404);
347
+ res.end();
348
+ }
349
+ });
350
+ server.on("error", (err) => {
351
+ clearTimeout(timeoutId);
352
+ reject(new Error(`Callback server error: ${err.message}`));
353
+ });
354
+ server.listen(OAUTH_CALLBACK_PORT, () => {
355
+ });
356
+ timeoutId = setTimeout(() => {
357
+ server.close();
358
+ reject(new Error("Authorization timeout"));
359
+ }, 5 * 60 * 1e3);
360
+ });
361
+ }
362
+ function getAuthorizationUrl(codeChallenge, clientId) {
363
+ const params = new URLSearchParams({
364
+ client_id: clientId || OAUTH_CLIENT_ID,
365
+ redirect_uri: OAUTH_REDIRECT_URI,
366
+ response_type: "code",
367
+ scope: OAUTH_SCOPES.join(" "),
368
+ code_challenge: codeChallenge,
369
+ code_challenge_method: "S256",
370
+ access_type: "offline",
371
+ prompt: "consent"
372
+ });
373
+ return `${OAUTH_AUTH_URL}?${params.toString()}`;
374
+ }
375
+ async function exchangeCodeForTokens(code, codeVerifier, credentials) {
376
+ const response = await fetch(OAUTH_TOKEN_URL, {
377
+ method: "POST",
378
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
379
+ body: new URLSearchParams({
380
+ client_id: credentials?.clientId || OAUTH_CLIENT_ID,
381
+ client_secret: credentials?.clientSecret || OAUTH_CLIENT_SECRET,
382
+ code,
383
+ code_verifier: codeVerifier,
384
+ grant_type: "authorization_code",
385
+ redirect_uri: OAUTH_REDIRECT_URI
386
+ })
387
+ });
388
+ if (!response.ok) {
389
+ const error = await response.text();
390
+ throw new Error(`Token exchange failed: ${error}`);
391
+ }
392
+ return await response.json();
393
+ }
394
+ async function refreshAccessToken(refreshToken) {
395
+ const response = await fetch(OAUTH_TOKEN_URL, {
396
+ method: "POST",
397
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
398
+ body: new URLSearchParams({
399
+ client_id: OAUTH_CLIENT_ID,
400
+ client_secret: OAUTH_CLIENT_SECRET,
401
+ refresh_token: refreshToken,
402
+ grant_type: "refresh_token"
403
+ })
404
+ });
405
+ if (!response.ok) {
406
+ const error = await response.text();
407
+ throw new Error(`Token refresh failed: ${error}`);
408
+ }
409
+ return await response.json();
410
+ }
411
+ async function getUserInfo(accessToken) {
412
+ const response = await fetch(OAUTH_USERINFO_URL, {
413
+ headers: { Authorization: `Bearer ${accessToken}` }
414
+ });
415
+ if (!response.ok) {
416
+ throw new Error("Failed to get user info");
417
+ }
418
+ return await response.json();
419
+ }
420
+ async function ensureConfigDir() {
421
+ try {
422
+ await fs2.mkdir(CONFIG_DIR, { recursive: true });
423
+ } catch {
424
+ }
425
+ }
426
+ async function loadStoredTokens() {
427
+ try {
428
+ const content = await fs2.readFile(TOKENS_FILE, "utf-8");
429
+ return JSON.parse(content);
430
+ } catch {
431
+ return null;
432
+ }
433
+ }
434
+ async function saveTokens(tokens) {
435
+ await ensureConfigDir();
436
+ await fs2.writeFile(TOKENS_FILE, JSON.stringify(tokens, null, 2));
437
+ }
438
+ async function deleteTokens() {
439
+ try {
440
+ await fs2.unlink(TOKENS_FILE);
441
+ } catch {
442
+ }
443
+ }
444
+ async function login(credentials) {
445
+ const { codeVerifier, codeChallenge } = generatePKCE();
446
+ const authUrl = getAuthorizationUrl(codeChallenge, credentials?.clientId);
447
+ console.log("Opening browser for Google login...");
448
+ const codePromise = startCallbackServer();
449
+ await openBrowser(authUrl);
450
+ console.log("Waiting for authorization...");
451
+ const code = await codePromise;
452
+ console.log("Exchanging code for tokens...");
453
+ const tokenResponse = await exchangeCodeForTokens(code, codeVerifier, credentials);
454
+ const userInfo = await getUserInfo(tokenResponse.access_token);
455
+ const tokens = {
456
+ accessToken: tokenResponse.access_token,
457
+ refreshToken: tokenResponse.refresh_token || "",
458
+ expiresAt: Date.now() + tokenResponse.expires_in * 1e3,
459
+ email: userInfo.email
460
+ };
461
+ await saveTokens(tokens);
462
+ return tokens;
463
+ }
464
+ var UserAuth = class {
465
+ tokens = null;
466
+ async getAccessToken() {
467
+ if (!this.tokens) {
468
+ this.tokens = await loadStoredTokens();
469
+ }
470
+ if (!this.tokens) {
471
+ throw new Error('Not logged in. Run "sheets login" first.');
472
+ }
473
+ if (Date.now() >= this.tokens.expiresAt - 6e4) {
474
+ if (!this.tokens.refreshToken) {
475
+ throw new Error('Token expired and no refresh token. Run "sheets login" again.');
476
+ }
477
+ const tokenResponse = await refreshAccessToken(this.tokens.refreshToken);
478
+ this.tokens.accessToken = tokenResponse.access_token;
479
+ this.tokens.expiresAt = Date.now() + tokenResponse.expires_in * 1e3;
480
+ if (tokenResponse.refresh_token) {
481
+ this.tokens.refreshToken = tokenResponse.refresh_token;
482
+ }
483
+ await saveTokens(this.tokens);
484
+ }
485
+ return this.tokens.accessToken;
486
+ }
487
+ };
488
+
489
+ // src/auth/index.ts
490
+ function createAuthProvider(config) {
491
+ switch (config.type) {
492
+ case "oauth":
493
+ return new OAuthAuth(config);
494
+ case "service-account":
495
+ return new ServiceAccountAuth(config);
496
+ case "user":
497
+ return new UserAuth();
498
+ default:
499
+ throw new Error(`Unknown auth type: ${config.type}`);
500
+ }
501
+ }
502
+
503
+ // src/api/index.ts
504
+ function columnLetterToNumber(letters) {
505
+ let result = 0;
506
+ for (let i = 0; i < letters.length; i++) {
507
+ result = result * 26 + (letters.charCodeAt(i) - 64);
508
+ }
509
+ return result;
510
+ }
511
+ function columnNumberToLetter(num) {
512
+ let result = "";
513
+ while (num > 0) {
514
+ const remainder = (num - 1) % 26;
515
+ result = String.fromCharCode(65 + remainder) + result;
516
+ num = Math.floor((num - 1) / 26);
517
+ }
518
+ return result;
519
+ }
520
+ function parseA1Range(range) {
521
+ const cellRef = range.includes("!") ? range.split("!")[1] : range;
522
+ const firstCell = cellRef.split(":")[0];
523
+ const match = firstCell.match(/^([A-Z]+)(\d+)$/i);
524
+ if (!match) {
525
+ return { startRow: 1, startCol: 1 };
526
+ }
527
+ return {
528
+ startCol: columnLetterToNumber(match[1].toUpperCase()),
529
+ startRow: parseInt(match[2], 10)
530
+ };
531
+ }
532
+ var SheetsClient = class {
533
+ http;
534
+ constructor(options) {
535
+ const authProvider = createAuthProvider(options.auth);
536
+ this.http = new HttpClient({
537
+ getAccessToken: () => authProvider.getAccessToken()
538
+ });
539
+ }
540
+ /**
541
+ * Get a spreadsheet by ID
542
+ */
543
+ async getSpreadsheet(spreadsheetId) {
544
+ return this.http.request(`/spreadsheets/${spreadsheetId}`);
545
+ }
546
+ /**
547
+ * Get list of sheets in a spreadsheet
548
+ */
549
+ async getSheets(spreadsheetId) {
550
+ const spreadsheet = await this.getSpreadsheet(spreadsheetId);
551
+ return spreadsheet.sheets.map((sheet) => sheet.properties);
552
+ }
553
+ /**
554
+ * Read cell values from a range
555
+ */
556
+ async getValues(spreadsheetId, range, options) {
557
+ const params = {};
558
+ if (options?.valueRenderOption) {
559
+ params.valueRenderOption = options.valueRenderOption;
560
+ }
561
+ if (options?.dateTimeRenderOption) {
562
+ params.dateTimeRenderOption = options.dateTimeRenderOption;
563
+ }
564
+ if (options?.majorDimension) {
565
+ params.majorDimension = options.majorDimension;
566
+ }
567
+ const encodedRange = encodeURIComponent(range);
568
+ const response = await this.http.request(
569
+ `/spreadsheets/${spreadsheetId}/values/${encodedRange}`,
570
+ { params }
571
+ );
572
+ return this.normalizeValueRange(response);
573
+ }
574
+ /**
575
+ * Read cell formulas from a range
576
+ */
577
+ async getFormulas(spreadsheetId, range) {
578
+ return this.getValues(spreadsheetId, range, { valueRenderOption: "FORMULA" });
579
+ }
580
+ /**
581
+ * Read multiple ranges at once
582
+ */
583
+ async batchGetValues(spreadsheetId, ranges, options) {
584
+ const params = {
585
+ ranges: ranges.join(",")
586
+ };
587
+ if (options?.valueRenderOption) {
588
+ params.valueRenderOption = options.valueRenderOption;
589
+ }
590
+ if (options?.dateTimeRenderOption) {
591
+ params.dateTimeRenderOption = options.dateTimeRenderOption;
592
+ }
593
+ if (options?.majorDimension) {
594
+ params.majorDimension = options.majorDimension;
595
+ }
596
+ const response = await this.http.request(
597
+ `/spreadsheets/${spreadsheetId}/values:batchGet`,
598
+ { params }
599
+ );
600
+ return {
601
+ spreadsheetId: response.spreadsheetId,
602
+ valueRanges: (response.valueRanges || []).map((vr) => this.normalizeValueRange(vr))
603
+ };
604
+ }
605
+ /**
606
+ * Clear values from a single range
607
+ */
608
+ async clearValues(spreadsheetId, range) {
609
+ const encodedRange = encodeURIComponent(range);
610
+ return this.http.request(
611
+ `/spreadsheets/${spreadsheetId}/values/${encodedRange}:clear`,
612
+ { method: "POST" }
613
+ );
614
+ }
615
+ /**
616
+ * Clear values from multiple ranges
617
+ */
618
+ async batchClearValues(spreadsheetId, ranges) {
619
+ return this.http.request(
620
+ `/spreadsheets/${spreadsheetId}/values:batchClear`,
621
+ { method: "POST", body: { ranges } }
622
+ );
623
+ }
624
+ /**
625
+ * Write values to a range (or starting cell)
626
+ * Range can be "A1" or "A1:D10" - data array determines actual extent
627
+ */
628
+ async updateValues(spreadsheetId, range, values, options) {
629
+ const params = {
630
+ valueInputOption: options?.valueInputOption || "USER_ENTERED"
631
+ };
632
+ if (options?.includeValuesInResponse) {
633
+ params.includeValuesInResponse = "true";
634
+ }
635
+ if (options?.responseValueRenderOption) {
636
+ params.responseValueRenderOption = options.responseValueRenderOption;
637
+ }
638
+ if (options?.responseDateTimeRenderOption) {
639
+ params.responseDateTimeRenderOption = options.responseDateTimeRenderOption;
640
+ }
641
+ const encodedRange = encodeURIComponent(range);
642
+ return this.http.request(
643
+ `/spreadsheets/${spreadsheetId}/values/${encodedRange}`,
644
+ {
645
+ method: "PUT",
646
+ params,
647
+ body: {
648
+ majorDimension: options?.majorDimension || "ROWS",
649
+ values
650
+ }
651
+ }
652
+ );
653
+ }
654
+ /**
655
+ * Write to multiple ranges in one request
656
+ */
657
+ async batchUpdateValues(spreadsheetId, data, options) {
658
+ return this.http.request(
659
+ `/spreadsheets/${spreadsheetId}/values:batchUpdate`,
660
+ {
661
+ method: "POST",
662
+ body: {
663
+ valueInputOption: options?.valueInputOption || "USER_ENTERED",
664
+ data: data.map((d) => ({
665
+ range: d.range,
666
+ majorDimension: options?.majorDimension || "ROWS",
667
+ values: d.values
668
+ })),
669
+ includeValuesInResponse: options?.includeValuesInResponse || false,
670
+ responseValueRenderOption: options?.responseValueRenderOption,
671
+ responseDateTimeRenderOption: options?.responseDateTimeRenderOption
672
+ }
673
+ }
674
+ );
675
+ }
676
+ /**
677
+ * Append rows after the last row of detected table
678
+ */
679
+ async appendValues(spreadsheetId, range, values, options) {
680
+ const params = {
681
+ valueInputOption: options?.valueInputOption || "USER_ENTERED"
682
+ };
683
+ if (options?.insertDataOption) {
684
+ params.insertDataOption = options.insertDataOption;
685
+ }
686
+ if (options?.includeValuesInResponse) {
687
+ params.includeValuesInResponse = "true";
688
+ }
689
+ if (options?.responseValueRenderOption) {
690
+ params.responseValueRenderOption = options.responseValueRenderOption;
691
+ }
692
+ if (options?.responseDateTimeRenderOption) {
693
+ params.responseDateTimeRenderOption = options.responseDateTimeRenderOption;
694
+ }
695
+ const encodedRange = encodeURIComponent(range);
696
+ return this.http.request(
697
+ `/spreadsheets/${spreadsheetId}/values/${encodedRange}:append`,
698
+ {
699
+ method: "POST",
700
+ params,
701
+ body: {
702
+ majorDimension: options?.majorDimension || "ROWS",
703
+ values
704
+ }
705
+ }
706
+ );
707
+ }
708
+ /**
709
+ * Search for values matching a query across sheets
710
+ */
711
+ async searchValues(spreadsheetId, query, options) {
712
+ const caseSensitive = options?.caseSensitive ?? false;
713
+ const exactMatch = options?.exactMatch ?? false;
714
+ const useRegex = options?.regex ?? false;
715
+ const limit = options?.limit;
716
+ const matchType = useRegex ? "regex" : exactMatch ? "exact" : "contains";
717
+ let matcher;
718
+ if (useRegex) {
719
+ const flags = caseSensitive ? "" : "i";
720
+ const regex = new RegExp(query, flags);
721
+ matcher = (cellValue) => regex.test(cellValue);
722
+ } else if (exactMatch) {
723
+ if (caseSensitive) {
724
+ matcher = (cellValue) => cellValue === query;
725
+ } else {
726
+ const lowerQuery = query.toLowerCase();
727
+ matcher = (cellValue) => cellValue.toLowerCase() === lowerQuery;
728
+ }
729
+ } else {
730
+ if (caseSensitive) {
731
+ matcher = (cellValue) => cellValue.includes(query);
732
+ } else {
733
+ const lowerQuery = query.toLowerCase();
734
+ matcher = (cellValue) => cellValue.toLowerCase().includes(lowerQuery);
735
+ }
736
+ }
737
+ const matches = [];
738
+ const allSheets = await this.getSheets(spreadsheetId);
739
+ let sheetsToSearch;
740
+ if (options?.sheetIndex !== void 0) {
741
+ const sheet = allSheets.find((s) => s.index === options.sheetIndex);
742
+ if (!sheet) {
743
+ throw new Error(`Sheet index ${options.sheetIndex} not found.`);
744
+ }
745
+ sheetsToSearch = [sheet];
746
+ } else if (options?.gid !== void 0) {
747
+ const sheet = allSheets.find((s) => s.sheetId === options.gid);
748
+ if (!sheet) {
749
+ throw new Error(`Sheet with gid ${options.gid} not found.`);
750
+ }
751
+ sheetsToSearch = [sheet];
752
+ } else if (options?.range && options.range.includes("!")) {
753
+ sheetsToSearch = [];
754
+ } else {
755
+ sheetsToSearch = allSheets.filter((s) => !s.hidden);
756
+ }
757
+ if (options?.range && options.range.includes("!")) {
758
+ const valueRange = await this.getValues(spreadsheetId, options.range);
759
+ const sheetName = options.range.split("!")[0].replace(/^'|'$/g, "").replace(/''/g, "'");
760
+ const sheet = allSheets.find((s) => s.title === sheetName);
761
+ const { startRow, startCol } = parseA1Range(options.range);
762
+ this.collectMatches(
763
+ valueRange,
764
+ sheetName,
765
+ sheet?.sheetId ?? 0,
766
+ startRow,
767
+ startCol,
768
+ matcher,
769
+ matches,
770
+ limit
771
+ );
772
+ } else {
773
+ for (const sheet of sheetsToSearch) {
774
+ if (limit && matches.length >= limit) break;
775
+ const escapedTitle = sheet.title.replace(/'/g, "''");
776
+ const range = options?.range ? `'${escapedTitle}'!${options.range}` : `'${escapedTitle}'`;
777
+ try {
778
+ const valueRange = await this.getValues(spreadsheetId, range);
779
+ const { startRow, startCol } = parseA1Range(valueRange.range);
780
+ this.collectMatches(
781
+ valueRange,
782
+ sheet.title,
783
+ sheet.sheetId,
784
+ startRow,
785
+ startCol,
786
+ matcher,
787
+ matches,
788
+ limit
789
+ );
790
+ } catch {
791
+ continue;
792
+ }
793
+ }
794
+ }
795
+ return {
796
+ query,
797
+ matchType,
798
+ caseSensitive,
799
+ totalMatches: matches.length,
800
+ matches
801
+ };
802
+ }
803
+ collectMatches(valueRange, sheetName, sheetId, startRow, startCol, matcher, matches, limit) {
804
+ for (let rowIndex = 0; rowIndex < valueRange.values.length; rowIndex++) {
805
+ if (limit && matches.length >= limit) return;
806
+ const row = valueRange.values[rowIndex];
807
+ for (let colIndex = 0; colIndex < row.length; colIndex++) {
808
+ if (limit && matches.length >= limit) return;
809
+ const cell = row[colIndex];
810
+ const cellValue = cell.value;
811
+ if (cellValue == null) continue;
812
+ const stringValue = String(cellValue);
813
+ if (matcher(stringValue)) {
814
+ const actualRow = startRow + rowIndex;
815
+ const actualCol = startCol + colIndex;
816
+ matches.push({
817
+ sheet: sheetName,
818
+ sheetId,
819
+ address: columnNumberToLetter(actualCol) + actualRow,
820
+ row: actualRow,
821
+ column: actualCol,
822
+ value: cellValue
823
+ });
824
+ }
825
+ }
826
+ }
827
+ }
828
+ normalizeValueRange(raw) {
829
+ const values = (raw.values || []).map(
830
+ (row) => row.map((cell) => ({
831
+ value: cell
832
+ }))
833
+ );
834
+ return {
835
+ range: raw.range,
836
+ majorDimension: raw.majorDimension || "ROWS",
837
+ values
838
+ };
839
+ }
840
+ };
841
+ function createClient(options) {
842
+ return new SheetsClient(options);
843
+ }
844
+ // Annotate the CommonJS export names for ESM import in node:
845
+ 0 && (module.exports = {
846
+ HttpClient,
847
+ OAuthAuth,
848
+ ServiceAccountAuth,
849
+ SheetsClient,
850
+ SheetsError,
851
+ UserAuth,
852
+ createAuthProvider,
853
+ createClient,
854
+ deleteTokens,
855
+ loadStoredTokens,
856
+ login
857
+ });
858
+ //# sourceMappingURL=index.cjs.map