@ariadng/sheets 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/README.md +427 -300
- package/bin/sheets.js +3 -0
- package/dist/api/index.d.ts +31 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +87 -0
- package/dist/api/index.js.map +1 -0
- package/dist/auth/constants.d.ts +13 -0
- package/dist/auth/constants.d.ts.map +1 -0
- package/dist/auth/constants.js +21 -0
- package/dist/auth/constants.js.map +1 -0
- package/dist/auth/index.d.ts +13 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +22 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth.d.ts +11 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +14 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/service-account.d.ts +18 -0
- package/dist/auth/service-account.d.ts.map +1 -0
- package/dist/auth/service-account.js +92 -0
- package/dist/auth/service-account.js.map +1 -0
- package/dist/auth/user-auth.d.ts +24 -0
- package/dist/auth/user-auth.d.ts.map +1 -0
- package/dist/auth/user-auth.js +230 -0
- package/dist/auth/user-auth.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +318 -0
- package/dist/cli.js.map +1 -0
- package/dist/http/index.d.ts +19 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +68 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +133 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +58 -79
- package/dist/advanced/index.d.ts +0 -5
- package/dist/advanced/index.d.ts.map +0 -1
- package/dist/advanced/index.js +0 -1063
- package/dist/advanced/index.mjs +0 -1005
- package/dist/advanced/metrics.d.ts +0 -50
- package/dist/advanced/metrics.d.ts.map +0 -1
- package/dist/advanced/rate-limit.d.ts +0 -27
- package/dist/advanced/rate-limit.d.ts.map +0 -1
- package/dist/core/auth.d.ts +0 -33
- package/dist/core/auth.d.ts.map +0 -1
- package/dist/core/client.d.ts +0 -35
- package/dist/core/client.d.ts.map +0 -1
- package/dist/core/errors.d.ts +0 -11
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -315
- package/dist/core/index.mjs +0 -271
- package/dist/plus/batch.d.ts +0 -25
- package/dist/plus/batch.d.ts.map +0 -1
- package/dist/plus/cache.d.ts +0 -19
- package/dist/plus/cache.d.ts.map +0 -1
- package/dist/plus/index.d.ts +0 -7
- package/dist/plus/index.d.ts.map +0 -1
- package/dist/plus/index.js +0 -742
- package/dist/plus/index.mjs +0 -691
- package/dist/plus/types.d.ts +0 -39
- package/dist/plus/types.d.ts.map +0 -1
package/dist/advanced/index.js
DELETED
|
@@ -1,1063 +0,0 @@
|
|
|
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/advanced/index.ts
|
|
31
|
-
var advanced_exports = {};
|
|
32
|
-
__export(advanced_exports, {
|
|
33
|
-
A1: () => A1,
|
|
34
|
-
AdaptiveRateLimiter: () => AdaptiveRateLimiter,
|
|
35
|
-
BatchOperations: () => BatchOperations,
|
|
36
|
-
GoogleSheetsCore: () => GoogleSheetsCore,
|
|
37
|
-
GoogleSheetsError: () => GoogleSheetsError,
|
|
38
|
-
MetricsCollector: () => MetricsCollector,
|
|
39
|
-
Parsers: () => Parsers,
|
|
40
|
-
PerformanceMonitor: () => PerformanceMonitor,
|
|
41
|
-
Serializers: () => Serializers,
|
|
42
|
-
SimpleCache: () => SimpleCache,
|
|
43
|
-
TokenBucketRateLimiter: () => TokenBucketRateLimiter,
|
|
44
|
-
TypedSheets: () => TypedSheets,
|
|
45
|
-
createAuth: () => createAuth,
|
|
46
|
-
createOAuth2Client: () => createOAuth2Client,
|
|
47
|
-
createServiceAccountAuth: () => createServiceAccountAuth,
|
|
48
|
-
generateAuthUrl: () => generateAuthUrl,
|
|
49
|
-
getTokenFromCode: () => getTokenFromCode,
|
|
50
|
-
saveToken: () => saveToken,
|
|
51
|
-
withAdaptiveRateLimit: () => withAdaptiveRateLimit,
|
|
52
|
-
withCache: () => withCache,
|
|
53
|
-
withMetrics: () => withMetrics,
|
|
54
|
-
withTokenBucketRateLimit: () => withTokenBucketRateLimit
|
|
55
|
-
});
|
|
56
|
-
module.exports = __toCommonJS(advanced_exports);
|
|
57
|
-
|
|
58
|
-
// src/core/errors.ts
|
|
59
|
-
var GoogleSheetsError = class extends Error {
|
|
60
|
-
constructor(originalError) {
|
|
61
|
-
const message = originalError.response?.data?.error?.message || originalError.message || "Unknown error";
|
|
62
|
-
super(message);
|
|
63
|
-
this.name = "GoogleSheetsError";
|
|
64
|
-
this.code = originalError.response?.status || originalError.code;
|
|
65
|
-
this.originalError = originalError;
|
|
66
|
-
const retryableCodes = [429, 500, 502, 503, 504, "ECONNRESET", "ETIMEDOUT", "ENOTFOUND"];
|
|
67
|
-
this.isRetryable = retryableCodes.includes(this.code);
|
|
68
|
-
if (originalError.stack) {
|
|
69
|
-
this.stack = originalError.stack;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Check if error is a rate limit error
|
|
74
|
-
*/
|
|
75
|
-
isRateLimitError() {
|
|
76
|
-
return this.code === 429;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Check if error is a permission error
|
|
80
|
-
*/
|
|
81
|
-
isPermissionError() {
|
|
82
|
-
return this.code === 403;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Check if error is a not found error
|
|
86
|
-
*/
|
|
87
|
-
isNotFoundError() {
|
|
88
|
-
return this.code === 404;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Get a user-friendly error message
|
|
92
|
-
*/
|
|
93
|
-
getUserMessage() {
|
|
94
|
-
if (this.isPermissionError()) {
|
|
95
|
-
return "Permission denied. Please ensure the spreadsheet is shared with the service account or you have proper OAuth permissions.";
|
|
96
|
-
}
|
|
97
|
-
if (this.isRateLimitError()) {
|
|
98
|
-
return "Rate limit exceeded. Please wait before making more requests.";
|
|
99
|
-
}
|
|
100
|
-
if (this.isNotFoundError()) {
|
|
101
|
-
return "Spreadsheet or range not found. Please check the ID and range are correct.";
|
|
102
|
-
}
|
|
103
|
-
return this.message;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// src/advanced/rate-limit.ts
|
|
108
|
-
var AdaptiveRateLimiter = class {
|
|
109
|
-
constructor() {
|
|
110
|
-
this.successRate = 1;
|
|
111
|
-
this.baseDelay = 0;
|
|
112
|
-
this.requestTimes = [];
|
|
113
|
-
this.windowMs = 1e5;
|
|
114
|
-
// 100 seconds (Google's quota window)
|
|
115
|
-
this.maxRequestsPerWindow = 90;
|
|
116
|
-
}
|
|
117
|
-
// Leave buffer below 100 limit
|
|
118
|
-
async execute(fn) {
|
|
119
|
-
const now = Date.now();
|
|
120
|
-
this.requestTimes = this.requestTimes.filter(
|
|
121
|
-
(time) => time > now - this.windowMs
|
|
122
|
-
);
|
|
123
|
-
if (this.requestTimes.length >= this.maxRequestsPerWindow) {
|
|
124
|
-
const oldestRequest = this.requestTimes[0];
|
|
125
|
-
if (oldestRequest) {
|
|
126
|
-
const waitTime = oldestRequest + this.windowMs - now;
|
|
127
|
-
if (waitTime > 0) {
|
|
128
|
-
await new Promise((r) => setTimeout(r, waitTime + 100));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (this.baseDelay > 0) {
|
|
133
|
-
await new Promise((r) => setTimeout(r, this.baseDelay));
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
const result = await fn();
|
|
137
|
-
this.requestTimes.push(Date.now());
|
|
138
|
-
this.successRate = Math.min(1, this.successRate * 1.05);
|
|
139
|
-
this.baseDelay = Math.max(0, this.baseDelay - 10);
|
|
140
|
-
return result;
|
|
141
|
-
} catch (error) {
|
|
142
|
-
const isRateLimit = error.code === 429 || error.response?.status === 429 || error instanceof GoogleSheetsError && error.isRateLimitError();
|
|
143
|
-
if (isRateLimit) {
|
|
144
|
-
this.successRate *= 0.5;
|
|
145
|
-
this.baseDelay = Math.min(1e3, this.baseDelay + 200);
|
|
146
|
-
}
|
|
147
|
-
throw error;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Get current rate limiter stats
|
|
152
|
-
*/
|
|
153
|
-
getStats() {
|
|
154
|
-
const now = Date.now();
|
|
155
|
-
this.requestTimes = this.requestTimes.filter(
|
|
156
|
-
(time) => time > now - this.windowMs
|
|
157
|
-
);
|
|
158
|
-
return {
|
|
159
|
-
requestsInWindow: this.requestTimes.length,
|
|
160
|
-
successRate: this.successRate,
|
|
161
|
-
baseDelay: this.baseDelay
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Reset rate limiter state
|
|
166
|
-
*/
|
|
167
|
-
reset() {
|
|
168
|
-
this.successRate = 1;
|
|
169
|
-
this.baseDelay = 0;
|
|
170
|
-
this.requestTimes = [];
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
function withAdaptiveRateLimit(client) {
|
|
174
|
-
const limiter = new AdaptiveRateLimiter();
|
|
175
|
-
const wrappedClient = Object.create(client);
|
|
176
|
-
const methodsToWrap = [
|
|
177
|
-
"read",
|
|
178
|
-
"write",
|
|
179
|
-
"append",
|
|
180
|
-
"clear",
|
|
181
|
-
"batchRead",
|
|
182
|
-
"batchWrite",
|
|
183
|
-
"batchClear",
|
|
184
|
-
"getSpreadsheet"
|
|
185
|
-
];
|
|
186
|
-
for (const method of methodsToWrap) {
|
|
187
|
-
const original = client[method];
|
|
188
|
-
if (typeof original === "function") {
|
|
189
|
-
wrappedClient[method] = function(...args) {
|
|
190
|
-
return limiter.execute(() => original.apply(client, args));
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return wrappedClient;
|
|
195
|
-
}
|
|
196
|
-
var TokenBucketRateLimiter = class {
|
|
197
|
-
// tokens per second
|
|
198
|
-
constructor(maxTokens = 100, refillRate = 1) {
|
|
199
|
-
this.maxTokens = maxTokens;
|
|
200
|
-
this.tokens = maxTokens;
|
|
201
|
-
this.refillRate = refillRate;
|
|
202
|
-
this.lastRefill = Date.now();
|
|
203
|
-
}
|
|
204
|
-
async acquire(tokens = 1) {
|
|
205
|
-
const now = Date.now();
|
|
206
|
-
const timePassed = (now - this.lastRefill) / 1e3;
|
|
207
|
-
const tokensToAdd = timePassed * this.refillRate;
|
|
208
|
-
this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
209
|
-
this.lastRefill = now;
|
|
210
|
-
if (this.tokens < tokens) {
|
|
211
|
-
const waitTime = (tokens - this.tokens) / this.refillRate * 1e3;
|
|
212
|
-
await new Promise((r) => setTimeout(r, waitTime));
|
|
213
|
-
return this.acquire(tokens);
|
|
214
|
-
}
|
|
215
|
-
this.tokens -= tokens;
|
|
216
|
-
}
|
|
217
|
-
getAvailableTokens() {
|
|
218
|
-
const now = Date.now();
|
|
219
|
-
const timePassed = (now - this.lastRefill) / 1e3;
|
|
220
|
-
const tokensToAdd = timePassed * this.refillRate;
|
|
221
|
-
return Math.min(this.maxTokens, this.tokens + tokensToAdd);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
function withTokenBucketRateLimit(client, maxTokens = 100, refillRate = 1) {
|
|
225
|
-
const limiter = new TokenBucketRateLimiter(maxTokens, refillRate);
|
|
226
|
-
const wrappedClient = Object.create(client);
|
|
227
|
-
const methodsToWrap = [
|
|
228
|
-
"read",
|
|
229
|
-
"write",
|
|
230
|
-
"append",
|
|
231
|
-
"clear",
|
|
232
|
-
"batchRead",
|
|
233
|
-
"batchWrite",
|
|
234
|
-
"batchClear",
|
|
235
|
-
"getSpreadsheet"
|
|
236
|
-
];
|
|
237
|
-
for (const method of methodsToWrap) {
|
|
238
|
-
const original = client[method];
|
|
239
|
-
if (typeof original === "function") {
|
|
240
|
-
wrappedClient[method] = async function(...args) {
|
|
241
|
-
await limiter.acquire(1);
|
|
242
|
-
return original.apply(client, args);
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return wrappedClient;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/advanced/metrics.ts
|
|
250
|
-
var MetricsCollector = class {
|
|
251
|
-
constructor() {
|
|
252
|
-
this.metrics = {
|
|
253
|
-
totalRequests: 0,
|
|
254
|
-
successfulRequests: 0,
|
|
255
|
-
failedRequests: 0,
|
|
256
|
-
retryCount: 0,
|
|
257
|
-
averageLatency: 0,
|
|
258
|
-
rateLimitHits: 0,
|
|
259
|
-
errorsByCode: /* @__PURE__ */ new Map(),
|
|
260
|
-
requestsByMethod: /* @__PURE__ */ new Map()
|
|
261
|
-
};
|
|
262
|
-
this.latencies = [];
|
|
263
|
-
this.maxLatencySamples = 100;
|
|
264
|
-
this.startTime = Date.now();
|
|
265
|
-
}
|
|
266
|
-
recordRequest(method, duration, success, retries = 0, error) {
|
|
267
|
-
this.metrics.totalRequests++;
|
|
268
|
-
const currentCount = this.metrics.requestsByMethod.get(method) || 0;
|
|
269
|
-
this.metrics.requestsByMethod.set(method, currentCount + 1);
|
|
270
|
-
if (success) {
|
|
271
|
-
this.metrics.successfulRequests++;
|
|
272
|
-
} else {
|
|
273
|
-
this.metrics.failedRequests++;
|
|
274
|
-
if (error) {
|
|
275
|
-
const code = error.code || error.response?.status || (error instanceof GoogleSheetsError ? error.code : "unknown");
|
|
276
|
-
const errorCount = this.metrics.errorsByCode.get(code) || 0;
|
|
277
|
-
this.metrics.errorsByCode.set(code, errorCount + 1);
|
|
278
|
-
if (code === 429) {
|
|
279
|
-
this.metrics.rateLimitHits++;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
this.metrics.retryCount += retries;
|
|
284
|
-
this.latencies.push(duration);
|
|
285
|
-
if (this.latencies.length > this.maxLatencySamples) {
|
|
286
|
-
this.latencies.shift();
|
|
287
|
-
}
|
|
288
|
-
this.metrics.averageLatency = this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length;
|
|
289
|
-
}
|
|
290
|
-
recordRateLimitHit() {
|
|
291
|
-
this.metrics.rateLimitHits++;
|
|
292
|
-
}
|
|
293
|
-
getMetrics() {
|
|
294
|
-
return {
|
|
295
|
-
...this.metrics,
|
|
296
|
-
errorsByCode: new Map(this.metrics.errorsByCode),
|
|
297
|
-
requestsByMethod: new Map(this.metrics.requestsByMethod)
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
getSummary() {
|
|
301
|
-
const uptimeSeconds = (Date.now() - this.startTime) / 1e3;
|
|
302
|
-
const successRate = this.metrics.totalRequests > 0 ? this.metrics.successfulRequests / this.metrics.totalRequests : 0;
|
|
303
|
-
return {
|
|
304
|
-
totalRequests: this.metrics.totalRequests,
|
|
305
|
-
successRate,
|
|
306
|
-
averageLatency: this.metrics.averageLatency,
|
|
307
|
-
rateLimitHits: this.metrics.rateLimitHits,
|
|
308
|
-
uptimeSeconds,
|
|
309
|
-
requestsPerSecond: this.metrics.totalRequests / uptimeSeconds
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
reset() {
|
|
313
|
-
this.metrics = {
|
|
314
|
-
totalRequests: 0,
|
|
315
|
-
successfulRequests: 0,
|
|
316
|
-
failedRequests: 0,
|
|
317
|
-
retryCount: 0,
|
|
318
|
-
averageLatency: 0,
|
|
319
|
-
rateLimitHits: 0,
|
|
320
|
-
errorsByCode: /* @__PURE__ */ new Map(),
|
|
321
|
-
requestsByMethod: /* @__PURE__ */ new Map()
|
|
322
|
-
};
|
|
323
|
-
this.latencies = [];
|
|
324
|
-
this.startTime = Date.now();
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
function withMetrics(client) {
|
|
328
|
-
const metrics = new MetricsCollector();
|
|
329
|
-
const wrappedClient = Object.create(client);
|
|
330
|
-
const methodsToWrap = [
|
|
331
|
-
"read",
|
|
332
|
-
"write",
|
|
333
|
-
"append",
|
|
334
|
-
"clear",
|
|
335
|
-
"batchRead",
|
|
336
|
-
"batchWrite",
|
|
337
|
-
"batchClear",
|
|
338
|
-
"getSpreadsheet"
|
|
339
|
-
];
|
|
340
|
-
for (const method of methodsToWrap) {
|
|
341
|
-
const original = client[method];
|
|
342
|
-
if (typeof original === "function") {
|
|
343
|
-
wrappedClient[method] = async function(...args) {
|
|
344
|
-
const startTime = Date.now();
|
|
345
|
-
let retries = 0;
|
|
346
|
-
let lastError;
|
|
347
|
-
while (retries < 3) {
|
|
348
|
-
try {
|
|
349
|
-
const result = await original.apply(client, args);
|
|
350
|
-
const duration = Date.now() - startTime;
|
|
351
|
-
metrics.recordRequest(method, duration, true, retries);
|
|
352
|
-
return result;
|
|
353
|
-
} catch (error) {
|
|
354
|
-
lastError = error;
|
|
355
|
-
retries++;
|
|
356
|
-
const isRetryable = error instanceof GoogleSheetsError && error.isRetryable;
|
|
357
|
-
if (!isRetryable || retries >= 3) {
|
|
358
|
-
const duration = Date.now() - startTime;
|
|
359
|
-
metrics.recordRequest(method, duration, false, retries - 1, error);
|
|
360
|
-
throw error;
|
|
361
|
-
}
|
|
362
|
-
await new Promise((r) => setTimeout(r, 1e3 * retries));
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
throw lastError;
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
wrappedClient.metrics = metrics;
|
|
370
|
-
return wrappedClient;
|
|
371
|
-
}
|
|
372
|
-
var PerformanceMonitor = class {
|
|
373
|
-
constructor() {
|
|
374
|
-
this.operations = /* @__PURE__ */ new Map();
|
|
375
|
-
}
|
|
376
|
-
record(operation, duration) {
|
|
377
|
-
const current = this.operations.get(operation) || {
|
|
378
|
-
count: 0,
|
|
379
|
-
totalDuration: 0,
|
|
380
|
-
minDuration: Infinity,
|
|
381
|
-
maxDuration: 0
|
|
382
|
-
};
|
|
383
|
-
current.count++;
|
|
384
|
-
current.totalDuration += duration;
|
|
385
|
-
current.minDuration = Math.min(current.minDuration, duration);
|
|
386
|
-
current.maxDuration = Math.max(current.maxDuration, duration);
|
|
387
|
-
this.operations.set(operation, current);
|
|
388
|
-
}
|
|
389
|
-
getStats(operation) {
|
|
390
|
-
const stats = this.operations.get(operation);
|
|
391
|
-
if (!stats) return null;
|
|
392
|
-
return {
|
|
393
|
-
count: stats.count,
|
|
394
|
-
average: stats.totalDuration / stats.count,
|
|
395
|
-
min: stats.minDuration,
|
|
396
|
-
max: stats.maxDuration
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
getAllStats() {
|
|
400
|
-
const result = /* @__PURE__ */ new Map();
|
|
401
|
-
for (const [operation, stats] of this.operations) {
|
|
402
|
-
result.set(operation, {
|
|
403
|
-
count: stats.count,
|
|
404
|
-
average: stats.totalDuration / stats.count,
|
|
405
|
-
min: stats.minDuration,
|
|
406
|
-
max: stats.maxDuration
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
return result;
|
|
410
|
-
}
|
|
411
|
-
reset() {
|
|
412
|
-
this.operations.clear();
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
// src/plus/batch.ts
|
|
417
|
-
var BatchOperations = class {
|
|
418
|
-
constructor(client) {
|
|
419
|
-
this.client = client;
|
|
420
|
-
// Google Sheets allows up to 100 operations per batch
|
|
421
|
-
this.MAX_BATCH_SIZE = 100;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Execute multiple write operations efficiently
|
|
425
|
-
* Automatically splits into optimal batch sizes
|
|
426
|
-
*/
|
|
427
|
-
async batchWrite(spreadsheetId, operations) {
|
|
428
|
-
const batches = this.chunk(operations, this.MAX_BATCH_SIZE);
|
|
429
|
-
const results = [];
|
|
430
|
-
for (const batch of batches) {
|
|
431
|
-
const result = await this.client.batchWrite(spreadsheetId, batch);
|
|
432
|
-
results.push(result);
|
|
433
|
-
}
|
|
434
|
-
return results;
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Execute multiple clear operations efficiently
|
|
438
|
-
*/
|
|
439
|
-
async batchClear(spreadsheetId, ranges) {
|
|
440
|
-
const batches = this.chunk(ranges, this.MAX_BATCH_SIZE);
|
|
441
|
-
const results = [];
|
|
442
|
-
for (const batch of batches) {
|
|
443
|
-
const result = await this.client.batchClear(spreadsheetId, batch);
|
|
444
|
-
results.push(result);
|
|
445
|
-
}
|
|
446
|
-
return results;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Execute multiple read operations efficiently
|
|
450
|
-
*/
|
|
451
|
-
async batchRead(spreadsheetId, ranges) {
|
|
452
|
-
const batches = this.chunk(ranges, this.MAX_BATCH_SIZE);
|
|
453
|
-
const results = [];
|
|
454
|
-
for (const batch of batches) {
|
|
455
|
-
const batchResult = await this.client.batchRead(spreadsheetId, batch);
|
|
456
|
-
results.push(...batchResult);
|
|
457
|
-
}
|
|
458
|
-
return results;
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Execute a mixed batch of operations
|
|
462
|
-
*/
|
|
463
|
-
async executeBatch(spreadsheetId, operations) {
|
|
464
|
-
const results = {};
|
|
465
|
-
const promises = [];
|
|
466
|
-
if (operations.writes) {
|
|
467
|
-
promises.push(
|
|
468
|
-
this.batchWrite(spreadsheetId, operations.writes).then((r) => {
|
|
469
|
-
results.writeResults = r;
|
|
470
|
-
})
|
|
471
|
-
);
|
|
472
|
-
}
|
|
473
|
-
if (operations.clears) {
|
|
474
|
-
promises.push(
|
|
475
|
-
this.batchClear(spreadsheetId, operations.clears).then((r) => {
|
|
476
|
-
results.clearResults = r;
|
|
477
|
-
})
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
if (operations.reads) {
|
|
481
|
-
promises.push(
|
|
482
|
-
this.batchRead(spreadsheetId, operations.reads).then((r) => {
|
|
483
|
-
results.readResults = r;
|
|
484
|
-
})
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
await Promise.all(promises);
|
|
488
|
-
return results;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Helper to chunk arrays into smaller batches
|
|
492
|
-
*/
|
|
493
|
-
chunk(array, size) {
|
|
494
|
-
const chunks = [];
|
|
495
|
-
for (let i = 0; i < array.length; i += size) {
|
|
496
|
-
chunks.push(array.slice(i, i + size));
|
|
497
|
-
}
|
|
498
|
-
return chunks;
|
|
499
|
-
}
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// src/plus/cache.ts
|
|
503
|
-
var SimpleCache = class {
|
|
504
|
-
constructor(config) {
|
|
505
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
506
|
-
this.config = {
|
|
507
|
-
ttlSeconds: config?.ttlSeconds ?? 60,
|
|
508
|
-
maxEntries: config?.maxEntries ?? 100
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
get(key) {
|
|
512
|
-
const entry = this.cache.get(key);
|
|
513
|
-
if (!entry) return null;
|
|
514
|
-
if (Date.now() > entry.expiry) {
|
|
515
|
-
this.cache.delete(key);
|
|
516
|
-
return null;
|
|
517
|
-
}
|
|
518
|
-
return entry.value;
|
|
519
|
-
}
|
|
520
|
-
set(key, value, ttlOverride) {
|
|
521
|
-
if (this.cache.size >= this.config.maxEntries) {
|
|
522
|
-
const firstKey = this.cache.keys().next().value;
|
|
523
|
-
if (firstKey) {
|
|
524
|
-
this.cache.delete(firstKey);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
const ttl = ttlOverride ?? this.config.ttlSeconds;
|
|
528
|
-
this.cache.set(key, {
|
|
529
|
-
value,
|
|
530
|
-
expiry: Date.now() + ttl * 1e3
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
invalidate(pattern) {
|
|
534
|
-
if (!pattern) {
|
|
535
|
-
this.cache.clear();
|
|
536
|
-
return;
|
|
537
|
-
}
|
|
538
|
-
const regex = new RegExp(pattern.replace("*", ".*"));
|
|
539
|
-
for (const key of this.cache.keys()) {
|
|
540
|
-
if (regex.test(key)) {
|
|
541
|
-
this.cache.delete(key);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
size() {
|
|
546
|
-
return this.cache.size;
|
|
547
|
-
}
|
|
548
|
-
clear() {
|
|
549
|
-
this.cache.clear();
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
function withCache(client, config) {
|
|
553
|
-
const cache = new SimpleCache(config);
|
|
554
|
-
const wrappedClient = Object.create(client);
|
|
555
|
-
const originalRead = client.read.bind(client);
|
|
556
|
-
wrappedClient.read = async function(spreadsheetId, range) {
|
|
557
|
-
const cacheKey = `${spreadsheetId}:${range}`;
|
|
558
|
-
const cached = cache.get(cacheKey);
|
|
559
|
-
if (cached !== null) {
|
|
560
|
-
return cached;
|
|
561
|
-
}
|
|
562
|
-
const result = await originalRead(spreadsheetId, range);
|
|
563
|
-
cache.set(cacheKey, result);
|
|
564
|
-
return result;
|
|
565
|
-
};
|
|
566
|
-
const originalBatchRead = client.batchRead.bind(client);
|
|
567
|
-
wrappedClient.batchRead = async function(spreadsheetId, ranges) {
|
|
568
|
-
const uncachedRanges = [];
|
|
569
|
-
const cachedResults = /* @__PURE__ */ new Map();
|
|
570
|
-
for (const range of ranges) {
|
|
571
|
-
const cacheKey = `${spreadsheetId}:${range}`;
|
|
572
|
-
const cached = cache.get(cacheKey);
|
|
573
|
-
if (cached !== null) {
|
|
574
|
-
cachedResults.set(range, {
|
|
575
|
-
range,
|
|
576
|
-
values: cached
|
|
577
|
-
});
|
|
578
|
-
} else {
|
|
579
|
-
uncachedRanges.push(range);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
let freshResults = [];
|
|
583
|
-
if (uncachedRanges.length > 0) {
|
|
584
|
-
freshResults = await originalBatchRead(spreadsheetId, uncachedRanges);
|
|
585
|
-
for (const result of freshResults) {
|
|
586
|
-
if (result.range) {
|
|
587
|
-
const cacheKey = `${spreadsheetId}:${result.range}`;
|
|
588
|
-
cache.set(cacheKey, result.values || []);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
const results = [];
|
|
593
|
-
for (const range of ranges) {
|
|
594
|
-
const cached = cachedResults.get(range);
|
|
595
|
-
if (cached) {
|
|
596
|
-
results.push(cached);
|
|
597
|
-
} else {
|
|
598
|
-
const fresh = freshResults.find((r) => r.range === range);
|
|
599
|
-
if (fresh) {
|
|
600
|
-
results.push(fresh);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return results;
|
|
605
|
-
};
|
|
606
|
-
const originalWrite = client.write.bind(client);
|
|
607
|
-
wrappedClient.write = async function(spreadsheetId, range, values) {
|
|
608
|
-
const result = await originalWrite(spreadsheetId, range, values);
|
|
609
|
-
cache.invalidate(`${spreadsheetId}:${range}*`);
|
|
610
|
-
return result;
|
|
611
|
-
};
|
|
612
|
-
const originalAppend = client.append.bind(client);
|
|
613
|
-
wrappedClient.append = async function(spreadsheetId, range, values) {
|
|
614
|
-
const result = await originalAppend(spreadsheetId, range, values);
|
|
615
|
-
cache.invalidate(`${spreadsheetId}:*`);
|
|
616
|
-
return result;
|
|
617
|
-
};
|
|
618
|
-
const originalClear = client.clear.bind(client);
|
|
619
|
-
wrappedClient.clear = async function(spreadsheetId, range) {
|
|
620
|
-
const result = await originalClear(spreadsheetId, range);
|
|
621
|
-
cache.invalidate(`${spreadsheetId}:${range}*`);
|
|
622
|
-
return result;
|
|
623
|
-
};
|
|
624
|
-
wrappedClient.cache = cache;
|
|
625
|
-
return wrappedClient;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// src/plus/types.ts
|
|
629
|
-
var A1 = class _A1 {
|
|
630
|
-
/**
|
|
631
|
-
* Convert column letter to index (A=0, B=1, etc)
|
|
632
|
-
*/
|
|
633
|
-
static columnToIndex(column) {
|
|
634
|
-
let index = 0;
|
|
635
|
-
for (let i = 0; i < column.length; i++) {
|
|
636
|
-
index = index * 26 + (column.charCodeAt(i) - 64);
|
|
637
|
-
}
|
|
638
|
-
return index - 1;
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Convert index to column letter (0=A, 1=B, etc)
|
|
642
|
-
*/
|
|
643
|
-
static indexToColumn(index) {
|
|
644
|
-
let column = "";
|
|
645
|
-
index++;
|
|
646
|
-
while (index > 0) {
|
|
647
|
-
const remainder = (index - 1) % 26;
|
|
648
|
-
column = String.fromCharCode(65 + remainder) + column;
|
|
649
|
-
index = Math.floor((index - 1) / 26);
|
|
650
|
-
}
|
|
651
|
-
return column;
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Parse A1 notation to components
|
|
655
|
-
*/
|
|
656
|
-
static parse(notation) {
|
|
657
|
-
const match = notation.match(
|
|
658
|
-
/^(?:(?:'([^']+)'|([^!]+))!)?([A-Z]+)(\d+)(?::([A-Z]+)(\d+))?$/
|
|
659
|
-
);
|
|
660
|
-
if (!match) {
|
|
661
|
-
throw new Error(`Invalid A1 notation: ${notation}`);
|
|
662
|
-
}
|
|
663
|
-
const [, quotedSheet, unquotedSheet, startCol, startRow, endCol, endRow] = match;
|
|
664
|
-
return {
|
|
665
|
-
sheet: quotedSheet || unquotedSheet || void 0,
|
|
666
|
-
startCol,
|
|
667
|
-
startRow: parseInt(startRow, 10),
|
|
668
|
-
endCol: endCol || void 0,
|
|
669
|
-
endRow: endRow ? parseInt(endRow, 10) : void 0
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* Build A1 notation from components
|
|
674
|
-
*/
|
|
675
|
-
static build(sheet, startCol, startRow, endCol, endRow) {
|
|
676
|
-
let sheetPrefix = "";
|
|
677
|
-
if (sheet) {
|
|
678
|
-
sheetPrefix = /[^a-zA-Z0-9]/.test(sheet) ? `'${sheet}'!` : `${sheet}!`;
|
|
679
|
-
}
|
|
680
|
-
const start = `${startCol}${startRow}`;
|
|
681
|
-
if (endCol && endRow) {
|
|
682
|
-
return `${sheetPrefix}${start}:${endCol}${endRow}`;
|
|
683
|
-
}
|
|
684
|
-
return `${sheetPrefix}${start}`;
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Get range dimensions
|
|
688
|
-
*/
|
|
689
|
-
static getDimensions(notation) {
|
|
690
|
-
const parsed = _A1.parse(notation);
|
|
691
|
-
const rows = parsed.endRow ? parsed.endRow - parsed.startRow + 1 : 1;
|
|
692
|
-
const columns = parsed.endCol ? _A1.columnToIndex(parsed.endCol) - _A1.columnToIndex(parsed.startCol) + 1 : 1;
|
|
693
|
-
return { rows, columns };
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Offset a range by rows and columns
|
|
697
|
-
*/
|
|
698
|
-
static offset(notation, rowOffset, colOffset) {
|
|
699
|
-
const parsed = _A1.parse(notation);
|
|
700
|
-
const newStartCol = _A1.indexToColumn(
|
|
701
|
-
_A1.columnToIndex(parsed.startCol) + colOffset
|
|
702
|
-
);
|
|
703
|
-
const newStartRow = parsed.startRow + rowOffset;
|
|
704
|
-
if (newStartRow < 1) {
|
|
705
|
-
throw new Error("Row offset results in invalid range");
|
|
706
|
-
}
|
|
707
|
-
let newEndCol;
|
|
708
|
-
let newEndRow;
|
|
709
|
-
if (parsed.endCol && parsed.endRow) {
|
|
710
|
-
newEndCol = _A1.indexToColumn(
|
|
711
|
-
_A1.columnToIndex(parsed.endCol) + colOffset
|
|
712
|
-
);
|
|
713
|
-
newEndRow = parsed.endRow + rowOffset;
|
|
714
|
-
if (newEndRow < 1) {
|
|
715
|
-
throw new Error("Row offset results in invalid range");
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
return _A1.build(parsed.sheet, newStartCol, newStartRow, newEndCol, newEndRow);
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
var TypedSheets = class {
|
|
722
|
-
constructor(client) {
|
|
723
|
-
this.client = client;
|
|
724
|
-
}
|
|
725
|
-
async read(spreadsheetId, range, parser) {
|
|
726
|
-
const data = await this.client.read(spreadsheetId, range);
|
|
727
|
-
return parser ? parser(data) : data;
|
|
728
|
-
}
|
|
729
|
-
async write(spreadsheetId, range, data, serializer) {
|
|
730
|
-
const values = serializer ? serializer(data) : data;
|
|
731
|
-
await this.client.write(spreadsheetId, range, values);
|
|
732
|
-
}
|
|
733
|
-
async append(spreadsheetId, range, data, serializer) {
|
|
734
|
-
const values = serializer ? serializer(data) : data;
|
|
735
|
-
await this.client.append(spreadsheetId, range, values);
|
|
736
|
-
}
|
|
737
|
-
};
|
|
738
|
-
var Parsers = {
|
|
739
|
-
/**
|
|
740
|
-
* Parse rows as objects using first row as headers
|
|
741
|
-
*/
|
|
742
|
-
rowsToObjects(data) {
|
|
743
|
-
if (data.length < 2) return [];
|
|
744
|
-
const [headers, ...rows] = data;
|
|
745
|
-
return rows.map((row) => {
|
|
746
|
-
const obj = {};
|
|
747
|
-
headers?.forEach((header, i) => {
|
|
748
|
-
obj[header] = row[i];
|
|
749
|
-
});
|
|
750
|
-
return obj;
|
|
751
|
-
});
|
|
752
|
-
},
|
|
753
|
-
/**
|
|
754
|
-
* Parse as simple 2D array with type coercion to numbers
|
|
755
|
-
*/
|
|
756
|
-
asNumbers(data) {
|
|
757
|
-
return data.map((row) => row.map((cell) => parseFloat(cell) || 0));
|
|
758
|
-
},
|
|
759
|
-
/**
|
|
760
|
-
* Parse as strings, handling empty cells
|
|
761
|
-
*/
|
|
762
|
-
asStrings(data) {
|
|
763
|
-
return data.map((row) => row.map((cell) => String(cell || "")));
|
|
764
|
-
},
|
|
765
|
-
/**
|
|
766
|
-
* Parse as key-value pairs from two columns
|
|
767
|
-
*/
|
|
768
|
-
asMap(data) {
|
|
769
|
-
const map = /* @__PURE__ */ new Map();
|
|
770
|
-
for (const row of data) {
|
|
771
|
-
if (row.length >= 2) {
|
|
772
|
-
map.set(String(row[0]), row[1]);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
return map;
|
|
776
|
-
},
|
|
777
|
-
/**
|
|
778
|
-
* Parse single column as array
|
|
779
|
-
*/
|
|
780
|
-
column(data, columnIndex = 0) {
|
|
781
|
-
return data.map((row) => row[columnIndex]).filter((val) => val !== void 0);
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
var Serializers = {
|
|
785
|
-
/**
|
|
786
|
-
* Convert objects to rows with headers
|
|
787
|
-
*/
|
|
788
|
-
objectsToRows(objects, headers) {
|
|
789
|
-
if (objects.length === 0) return [];
|
|
790
|
-
const keys = headers || Object.keys(objects[0]);
|
|
791
|
-
const headerRow = keys.map(String);
|
|
792
|
-
const dataRows = objects.map((obj) => keys.map((key) => obj[key]));
|
|
793
|
-
return [headerRow, ...dataRows];
|
|
794
|
-
},
|
|
795
|
-
/**
|
|
796
|
-
* Convert Map to two-column format
|
|
797
|
-
*/
|
|
798
|
-
mapToRows(map) {
|
|
799
|
-
const rows = [];
|
|
800
|
-
for (const [key, value] of map.entries()) {
|
|
801
|
-
rows.push([key, value]);
|
|
802
|
-
}
|
|
803
|
-
return rows;
|
|
804
|
-
},
|
|
805
|
-
/**
|
|
806
|
-
* Convert array to single column
|
|
807
|
-
*/
|
|
808
|
-
arrayToColumn(array) {
|
|
809
|
-
return array.map((item) => [item]);
|
|
810
|
-
},
|
|
811
|
-
/**
|
|
812
|
-
* Transpose rows and columns
|
|
813
|
-
*/
|
|
814
|
-
transpose(data) {
|
|
815
|
-
if (data.length === 0) return [];
|
|
816
|
-
const maxLength = Math.max(...data.map((row) => row.length));
|
|
817
|
-
const result = [];
|
|
818
|
-
for (let col = 0; col < maxLength; col++) {
|
|
819
|
-
const newRow = [];
|
|
820
|
-
for (let row = 0; row < data.length; row++) {
|
|
821
|
-
newRow.push(data[row]?.[col] ?? "");
|
|
822
|
-
}
|
|
823
|
-
result.push(newRow);
|
|
824
|
-
}
|
|
825
|
-
return result;
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
|
|
829
|
-
// src/core/client.ts
|
|
830
|
-
var import_googleapis = require("googleapis");
|
|
831
|
-
var GoogleSheetsCore = class {
|
|
832
|
-
constructor(config) {
|
|
833
|
-
this.sheets = import_googleapis.google.sheets({
|
|
834
|
-
version: "v4",
|
|
835
|
-
auth: config.auth
|
|
836
|
-
});
|
|
837
|
-
this.retryConfig = {
|
|
838
|
-
maxAttempts: config.retryConfig?.maxAttempts ?? 3,
|
|
839
|
-
maxDelay: config.retryConfig?.maxDelay ?? 1e4,
|
|
840
|
-
initialDelay: config.retryConfig?.initialDelay ?? 1e3
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Read values from a spreadsheet
|
|
845
|
-
* @param spreadsheetId The spreadsheet ID
|
|
846
|
-
* @param range A1 notation range (e.g., 'Sheet1!A1:B10')
|
|
847
|
-
* @returns 2D array of values
|
|
848
|
-
*/
|
|
849
|
-
async read(spreadsheetId, range) {
|
|
850
|
-
return this.withRetry(async () => {
|
|
851
|
-
const response = await this.sheets.spreadsheets.values.get({
|
|
852
|
-
spreadsheetId,
|
|
853
|
-
range
|
|
854
|
-
});
|
|
855
|
-
return response.data.values || [];
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
/**
|
|
859
|
-
* Write values to a spreadsheet
|
|
860
|
-
* @param spreadsheetId The spreadsheet ID
|
|
861
|
-
* @param range A1 notation range
|
|
862
|
-
* @param values 2D array of values to write
|
|
863
|
-
*/
|
|
864
|
-
async write(spreadsheetId, range, values) {
|
|
865
|
-
return this.withRetry(async () => {
|
|
866
|
-
const response = await this.sheets.spreadsheets.values.update({
|
|
867
|
-
spreadsheetId,
|
|
868
|
-
range,
|
|
869
|
-
valueInputOption: "USER_ENTERED",
|
|
870
|
-
requestBody: { values }
|
|
871
|
-
});
|
|
872
|
-
return response.data;
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
/**
|
|
876
|
-
* Append values to a spreadsheet
|
|
877
|
-
*/
|
|
878
|
-
async append(spreadsheetId, range, values) {
|
|
879
|
-
return this.withRetry(async () => {
|
|
880
|
-
const response = await this.sheets.spreadsheets.values.append({
|
|
881
|
-
spreadsheetId,
|
|
882
|
-
range,
|
|
883
|
-
valueInputOption: "USER_ENTERED",
|
|
884
|
-
insertDataOption: "INSERT_ROWS",
|
|
885
|
-
requestBody: { values }
|
|
886
|
-
});
|
|
887
|
-
return response.data;
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Clear values in a range
|
|
892
|
-
*/
|
|
893
|
-
async clear(spreadsheetId, range) {
|
|
894
|
-
return this.withRetry(async () => {
|
|
895
|
-
const response = await this.sheets.spreadsheets.values.clear({
|
|
896
|
-
spreadsheetId,
|
|
897
|
-
range
|
|
898
|
-
});
|
|
899
|
-
return response.data;
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Batch read multiple ranges
|
|
904
|
-
*/
|
|
905
|
-
async batchRead(spreadsheetId, ranges) {
|
|
906
|
-
return this.withRetry(async () => {
|
|
907
|
-
const response = await this.sheets.spreadsheets.values.batchGet({
|
|
908
|
-
spreadsheetId,
|
|
909
|
-
ranges
|
|
910
|
-
});
|
|
911
|
-
return response.data.valueRanges || [];
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Batch update multiple ranges
|
|
916
|
-
*/
|
|
917
|
-
async batchWrite(spreadsheetId, data) {
|
|
918
|
-
return this.withRetry(async () => {
|
|
919
|
-
const response = await this.sheets.spreadsheets.values.batchUpdate({
|
|
920
|
-
spreadsheetId,
|
|
921
|
-
requestBody: {
|
|
922
|
-
data: data.map((item) => ({
|
|
923
|
-
range: item.range,
|
|
924
|
-
values: item.values
|
|
925
|
-
})),
|
|
926
|
-
valueInputOption: "USER_ENTERED"
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
return response.data;
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Batch clear multiple ranges
|
|
934
|
-
*/
|
|
935
|
-
async batchClear(spreadsheetId, ranges) {
|
|
936
|
-
return this.withRetry(async () => {
|
|
937
|
-
const response = await this.sheets.spreadsheets.values.batchClear({
|
|
938
|
-
spreadsheetId,
|
|
939
|
-
requestBody: { ranges }
|
|
940
|
-
});
|
|
941
|
-
return response.data;
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
|
-
/**
|
|
945
|
-
* Get spreadsheet metadata
|
|
946
|
-
*/
|
|
947
|
-
async getSpreadsheet(spreadsheetId) {
|
|
948
|
-
return this.withRetry(async () => {
|
|
949
|
-
const response = await this.sheets.spreadsheets.get({
|
|
950
|
-
spreadsheetId
|
|
951
|
-
});
|
|
952
|
-
return response.data;
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Get the underlying Sheets API instance for advanced usage
|
|
957
|
-
*/
|
|
958
|
-
getApi() {
|
|
959
|
-
return this.sheets;
|
|
960
|
-
}
|
|
961
|
-
/**
|
|
962
|
-
* Simple exponential backoff retry logic
|
|
963
|
-
*/
|
|
964
|
-
async withRetry(fn) {
|
|
965
|
-
let lastError;
|
|
966
|
-
for (let attempt = 0; attempt < this.retryConfig.maxAttempts; attempt++) {
|
|
967
|
-
try {
|
|
968
|
-
return await fn();
|
|
969
|
-
} catch (error) {
|
|
970
|
-
lastError = error;
|
|
971
|
-
if (!this.isRetryable(error) || attempt === this.retryConfig.maxAttempts - 1) {
|
|
972
|
-
throw new GoogleSheetsError(error);
|
|
973
|
-
}
|
|
974
|
-
const baseDelay = Math.min(
|
|
975
|
-
this.retryConfig.initialDelay * Math.pow(2, attempt),
|
|
976
|
-
this.retryConfig.maxDelay
|
|
977
|
-
);
|
|
978
|
-
const jitter = Math.random() * 1e3;
|
|
979
|
-
const delay = baseDelay + jitter;
|
|
980
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
throw new GoogleSheetsError(lastError);
|
|
984
|
-
}
|
|
985
|
-
isRetryable(error) {
|
|
986
|
-
const retryableCodes = [429, 500, 502, 503, 504];
|
|
987
|
-
const retryableErrors = ["ECONNRESET", "ETIMEDOUT", "ENOTFOUND"];
|
|
988
|
-
return retryableCodes.includes(error.code) || retryableCodes.includes(error.response?.status) || retryableErrors.includes(error.code);
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
|
|
992
|
-
// src/core/auth.ts
|
|
993
|
-
var import_google_auth_library = require("google-auth-library");
|
|
994
|
-
var fs = __toESM(require("fs/promises"));
|
|
995
|
-
async function createServiceAccountAuth(keyFile) {
|
|
996
|
-
const key = typeof keyFile === "string" ? JSON.parse(await fs.readFile(keyFile, "utf8")) : keyFile;
|
|
997
|
-
const jwt = new import_google_auth_library.JWT({
|
|
998
|
-
email: key.client_email,
|
|
999
|
-
key: key.private_key,
|
|
1000
|
-
scopes: ["https://www.googleapis.com/auth/spreadsheets"]
|
|
1001
|
-
});
|
|
1002
|
-
return jwt;
|
|
1003
|
-
}
|
|
1004
|
-
async function createOAuth2Client(credentials, tokenPath) {
|
|
1005
|
-
const client = new import_google_auth_library.OAuth2Client(
|
|
1006
|
-
credentials.client_id,
|
|
1007
|
-
credentials.client_secret,
|
|
1008
|
-
credentials.redirect_uris[0]
|
|
1009
|
-
);
|
|
1010
|
-
if (tokenPath) {
|
|
1011
|
-
try {
|
|
1012
|
-
const token = JSON.parse(await fs.readFile(tokenPath, "utf8"));
|
|
1013
|
-
client.setCredentials(token);
|
|
1014
|
-
} catch {
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
return client;
|
|
1018
|
-
}
|
|
1019
|
-
function generateAuthUrl(client, scopes = ["https://www.googleapis.com/auth/spreadsheets"]) {
|
|
1020
|
-
return client.generateAuthUrl({
|
|
1021
|
-
access_type: "offline",
|
|
1022
|
-
scope: scopes
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
async function getTokenFromCode(client, code) {
|
|
1026
|
-
const { tokens } = await client.getToken(code);
|
|
1027
|
-
client.setCredentials(tokens);
|
|
1028
|
-
return tokens;
|
|
1029
|
-
}
|
|
1030
|
-
async function saveToken(tokens, path) {
|
|
1031
|
-
await fs.writeFile(path, JSON.stringify(tokens, null, 2));
|
|
1032
|
-
}
|
|
1033
|
-
function createAuth(auth) {
|
|
1034
|
-
if (auth instanceof import_google_auth_library.GoogleAuth || auth instanceof import_google_auth_library.OAuth2Client || auth instanceof import_google_auth_library.JWT) {
|
|
1035
|
-
return auth;
|
|
1036
|
-
}
|
|
1037
|
-
return createServiceAccountAuth(auth);
|
|
1038
|
-
}
|
|
1039
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1040
|
-
0 && (module.exports = {
|
|
1041
|
-
A1,
|
|
1042
|
-
AdaptiveRateLimiter,
|
|
1043
|
-
BatchOperations,
|
|
1044
|
-
GoogleSheetsCore,
|
|
1045
|
-
GoogleSheetsError,
|
|
1046
|
-
MetricsCollector,
|
|
1047
|
-
Parsers,
|
|
1048
|
-
PerformanceMonitor,
|
|
1049
|
-
Serializers,
|
|
1050
|
-
SimpleCache,
|
|
1051
|
-
TokenBucketRateLimiter,
|
|
1052
|
-
TypedSheets,
|
|
1053
|
-
createAuth,
|
|
1054
|
-
createOAuth2Client,
|
|
1055
|
-
createServiceAccountAuth,
|
|
1056
|
-
generateAuthUrl,
|
|
1057
|
-
getTokenFromCode,
|
|
1058
|
-
saveToken,
|
|
1059
|
-
withAdaptiveRateLimit,
|
|
1060
|
-
withCache,
|
|
1061
|
-
withMetrics,
|
|
1062
|
-
withTokenBucketRateLimit
|
|
1063
|
-
});
|