@ariadng/sheets 0.1.0 → 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.
Files changed (72) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +427 -300
  3. package/bin/sheets.js +3 -0
  4. package/dist/api/index.d.ts +31 -0
  5. package/dist/api/index.d.ts.map +1 -0
  6. package/dist/api/index.js +87 -0
  7. package/dist/api/index.js.map +1 -0
  8. package/dist/auth/constants.d.ts +13 -0
  9. package/dist/auth/constants.d.ts.map +1 -0
  10. package/dist/auth/constants.js +21 -0
  11. package/dist/auth/constants.js.map +1 -0
  12. package/dist/auth/index.d.ts +13 -0
  13. package/dist/auth/index.d.ts.map +1 -0
  14. package/dist/auth/index.js +22 -0
  15. package/dist/auth/index.js.map +1 -0
  16. package/dist/auth/oauth.d.ts +11 -0
  17. package/dist/auth/oauth.d.ts.map +1 -0
  18. package/dist/auth/oauth.js +14 -0
  19. package/dist/auth/oauth.js.map +1 -0
  20. package/dist/auth/service-account.d.ts +18 -0
  21. package/dist/auth/service-account.d.ts.map +1 -0
  22. package/dist/auth/service-account.js +92 -0
  23. package/dist/auth/service-account.js.map +1 -0
  24. package/dist/auth/user-auth.d.ts +24 -0
  25. package/dist/auth/user-auth.d.ts.map +1 -0
  26. package/dist/auth/user-auth.js +230 -0
  27. package/dist/auth/user-auth.js.map +1 -0
  28. package/dist/cli.d.ts +7 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +318 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/http/index.d.ts +19 -0
  33. package/dist/http/index.d.ts.map +1 -0
  34. package/dist/http/index.js +68 -0
  35. package/dist/http/index.js.map +1 -0
  36. package/dist/index.d.ts +11 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +12 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/types/index.d.ts +133 -0
  41. package/dist/types/index.d.ts.map +1 -0
  42. package/dist/types/index.js +16 -0
  43. package/dist/types/index.js.map +1 -0
  44. package/package.json +58 -79
  45. package/dist/advanced/index.d.ts +0 -5
  46. package/dist/advanced/index.d.ts.map +0 -1
  47. package/dist/advanced/index.js +0 -1063
  48. package/dist/advanced/index.mjs +0 -1005
  49. package/dist/advanced/metrics.d.ts +0 -50
  50. package/dist/advanced/metrics.d.ts.map +0 -1
  51. package/dist/advanced/rate-limit.d.ts +0 -27
  52. package/dist/advanced/rate-limit.d.ts.map +0 -1
  53. package/dist/core/auth.d.ts +0 -32
  54. package/dist/core/auth.d.ts.map +0 -1
  55. package/dist/core/client.d.ts +0 -35
  56. package/dist/core/client.d.ts.map +0 -1
  57. package/dist/core/errors.d.ts +0 -11
  58. package/dist/core/errors.d.ts.map +0 -1
  59. package/dist/core/index.d.ts +0 -6
  60. package/dist/core/index.d.ts.map +0 -1
  61. package/dist/core/index.js +0 -315
  62. package/dist/core/index.mjs +0 -271
  63. package/dist/plus/batch.d.ts +0 -25
  64. package/dist/plus/batch.d.ts.map +0 -1
  65. package/dist/plus/cache.d.ts +0 -19
  66. package/dist/plus/cache.d.ts.map +0 -1
  67. package/dist/plus/index.d.ts +0 -7
  68. package/dist/plus/index.d.ts.map +0 -1
  69. package/dist/plus/index.js +0 -742
  70. package/dist/plus/index.mjs +0 -691
  71. package/dist/plus/types.d.ts +0 -39
  72. package/dist/plus/types.d.ts.map +0 -1
package/bin/sheets.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import('../dist/cli.js');
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Google Sheets API Client
3
+ */
4
+ import type { Spreadsheet, SheetProperties, ValueRange, GetValuesOptions, SheetsClientOptions, BatchGetValuesResponse } from '../types/index.js';
5
+ export declare class SheetsClient {
6
+ private http;
7
+ constructor(options: SheetsClientOptions);
8
+ /**
9
+ * Get a spreadsheet by ID
10
+ */
11
+ getSpreadsheet(spreadsheetId: string): Promise<Spreadsheet>;
12
+ /**
13
+ * Get list of sheets in a spreadsheet
14
+ */
15
+ getSheets(spreadsheetId: string): Promise<SheetProperties[]>;
16
+ /**
17
+ * Read cell values from a range
18
+ */
19
+ getValues(spreadsheetId: string, range: string, options?: GetValuesOptions): Promise<ValueRange>;
20
+ /**
21
+ * Read cell formulas from a range
22
+ */
23
+ getFormulas(spreadsheetId: string, range: string): Promise<ValueRange>;
24
+ /**
25
+ * Read multiple ranges at once
26
+ */
27
+ batchGetValues(spreadsheetId: string, ranges: string[], options?: GetValuesOptions): Promise<BatchGetValuesResponse>;
28
+ private normalizeValueRange;
29
+ }
30
+ export declare function createClient(options: SheetsClientOptions): SheetsClient;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACR,WAAW,EACX,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACzB,MAAM,mBAAmB,CAAC;AAe3B,qBAAa,YAAY;IACrB,OAAO,CAAC,IAAI,CAAa;gBAEb,OAAO,EAAE,mBAAmB;IAOxC;;OAEG;IACG,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIjE;;OAEG;IACG,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAKlE;;OAEG;IACG,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAsBtG;;OAEG;IACG,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI5E;;OAEG;IACG,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA0B1H,OAAO,CAAC,mBAAmB;CAa9B;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAEvE"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Google Sheets API Client
3
+ */
4
+ import { HttpClient } from '../http/index.js';
5
+ import { createAuthProvider } from '../auth/index.js';
6
+ export class SheetsClient {
7
+ http;
8
+ constructor(options) {
9
+ const authProvider = createAuthProvider(options.auth);
10
+ this.http = new HttpClient({
11
+ getAccessToken: () => authProvider.getAccessToken(),
12
+ });
13
+ }
14
+ /**
15
+ * Get a spreadsheet by ID
16
+ */
17
+ async getSpreadsheet(spreadsheetId) {
18
+ return this.http.request(`/spreadsheets/${spreadsheetId}`);
19
+ }
20
+ /**
21
+ * Get list of sheets in a spreadsheet
22
+ */
23
+ async getSheets(spreadsheetId) {
24
+ const spreadsheet = await this.getSpreadsheet(spreadsheetId);
25
+ return spreadsheet.sheets.map(sheet => sheet.properties);
26
+ }
27
+ /**
28
+ * Read cell values from a range
29
+ */
30
+ async getValues(spreadsheetId, range, options) {
31
+ const params = {};
32
+ if (options?.valueRenderOption) {
33
+ params.valueRenderOption = options.valueRenderOption;
34
+ }
35
+ if (options?.dateTimeRenderOption) {
36
+ params.dateTimeRenderOption = options.dateTimeRenderOption;
37
+ }
38
+ if (options?.majorDimension) {
39
+ params.majorDimension = options.majorDimension;
40
+ }
41
+ const encodedRange = encodeURIComponent(range);
42
+ const response = await this.http.request(`/spreadsheets/${spreadsheetId}/values/${encodedRange}`, { params });
43
+ return this.normalizeValueRange(response);
44
+ }
45
+ /**
46
+ * Read cell formulas from a range
47
+ */
48
+ async getFormulas(spreadsheetId, range) {
49
+ return this.getValues(spreadsheetId, range, { valueRenderOption: 'FORMULA' });
50
+ }
51
+ /**
52
+ * Read multiple ranges at once
53
+ */
54
+ async batchGetValues(spreadsheetId, ranges, options) {
55
+ const params = {
56
+ ranges: ranges.join(','),
57
+ };
58
+ if (options?.valueRenderOption) {
59
+ params.valueRenderOption = options.valueRenderOption;
60
+ }
61
+ if (options?.dateTimeRenderOption) {
62
+ params.dateTimeRenderOption = options.dateTimeRenderOption;
63
+ }
64
+ if (options?.majorDimension) {
65
+ params.majorDimension = options.majorDimension;
66
+ }
67
+ const response = await this.http.request(`/spreadsheets/${spreadsheetId}/values:batchGet`, { params });
68
+ return {
69
+ spreadsheetId: response.spreadsheetId,
70
+ valueRanges: (response.valueRanges || []).map(vr => this.normalizeValueRange(vr)),
71
+ };
72
+ }
73
+ normalizeValueRange(raw) {
74
+ const values = (raw.values || []).map(row => row.map(cell => ({
75
+ value: cell,
76
+ })));
77
+ return {
78
+ range: raw.range,
79
+ majorDimension: raw.majorDimension || 'ROWS',
80
+ values,
81
+ };
82
+ }
83
+ }
84
+ export function createClient(options) {
85
+ return new SheetsClient(options);
86
+ }
87
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * OAuth Client Constants
3
+ * Replace with your Google Cloud OAuth client credentials
4
+ */
5
+ export declare const OAUTH_CLIENT_ID = "344941894490-jmdvo5ghomqi7vuisfrf80hfassk1ma5.apps.googleusercontent.com";
6
+ export declare const OAUTH_CLIENT_SECRET = "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";
7
+ export declare const OAUTH_REDIRECT_URI = "http://localhost:8085/callback";
8
+ export declare const OAUTH_CALLBACK_PORT = 8085;
9
+ export declare const OAUTH_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
10
+ export declare const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
11
+ export declare const OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
12
+ export declare const OAUTH_SCOPES: string[];
13
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/auth/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,eAAe,6EAA6E,CAAC;AAC1G,eAAO,MAAM,mBAAmB,wCAAwC,CAAC;AAGzE,eAAO,MAAM,kBAAkB,mCAAmC,CAAC;AACnE,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAGxC,eAAO,MAAM,cAAc,iDAAiD,CAAC;AAC7E,eAAO,MAAM,eAAe,wCAAwC,CAAC;AACrE,eAAO,MAAM,kBAAkB,kDAAkD,CAAC;AAGlF,eAAO,MAAM,YAAY,UAGxB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * OAuth Client Constants
3
+ * Replace with your Google Cloud OAuth client credentials
4
+ */
5
+ // Built-in OAuth client (Desktop app type)
6
+ // Project: ariadng-sheets
7
+ export const OAUTH_CLIENT_ID = '344941894490-jmdvo5ghomqi7vuisfrf80hfassk1ma5.apps.googleusercontent.com';
8
+ export const OAUTH_CLIENT_SECRET = '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';
9
+ // Redirect URI for local callback server
10
+ export const OAUTH_REDIRECT_URI = 'http://localhost:8085/callback';
11
+ export const OAUTH_CALLBACK_PORT = 8085;
12
+ // OAuth endpoints
13
+ export const OAUTH_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
14
+ export const OAUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token';
15
+ export const OAUTH_USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo';
16
+ // Scopes
17
+ export const OAUTH_SCOPES = [
18
+ 'https://www.googleapis.com/auth/spreadsheets.readonly',
19
+ 'https://www.googleapis.com/auth/userinfo.email',
20
+ ];
21
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/auth/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2CAA2C;AAC3C,0BAA0B;AAC1B,MAAM,CAAC,MAAM,eAAe,GAAG,0EAA0E,CAAC;AAC1G,MAAM,CAAC,MAAM,mBAAmB,GAAG,qCAAqC,CAAC;AAEzE,yCAAyC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,gCAAgC,CAAC;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAExC,kBAAkB;AAClB,MAAM,CAAC,MAAM,cAAc,GAAG,8CAA8C,CAAC;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,qCAAqC,CAAC;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,+CAA+C,CAAC;AAElF,SAAS;AACT,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,uDAAuD;IACvD,gDAAgD;CACnD,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Authentication Module Exports
3
+ */
4
+ import type { AuthConfig } from '../types/index.js';
5
+ export { OAuthAuth } from './oauth.js';
6
+ export { ServiceAccountAuth } from './service-account.js';
7
+ export { UserAuth, login, loadStoredTokens, deleteTokens } from './user-auth.js';
8
+ export type { OAuthClientCredentials } from './user-auth.js';
9
+ export interface AuthProvider {
10
+ getAccessToken(): Promise<string>;
11
+ }
12
+ export declare function createAuthProvider(config: AuthConfig): AuthProvider;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKpD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D,MAAM,WAAW,YAAY;IACzB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAWnE"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Authentication Module Exports
3
+ */
4
+ import { OAuthAuth } from './oauth.js';
5
+ import { ServiceAccountAuth } from './service-account.js';
6
+ import { UserAuth } from './user-auth.js';
7
+ export { OAuthAuth } from './oauth.js';
8
+ export { ServiceAccountAuth } from './service-account.js';
9
+ export { UserAuth, login, loadStoredTokens, deleteTokens } from './user-auth.js';
10
+ export function createAuthProvider(config) {
11
+ switch (config.type) {
12
+ case 'oauth':
13
+ return new OAuthAuth(config);
14
+ case 'service-account':
15
+ return new ServiceAccountAuth(config);
16
+ case 'user':
17
+ return new UserAuth();
18
+ default:
19
+ throw new Error(`Unknown auth type: ${config.type}`);
20
+ }
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAOjF,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACjD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO;YACR,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;QACjC,KAAK,iBAAiB;YAClB,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC1C,KAAK,MAAM;YACP,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC1B;YACI,MAAM,IAAI,KAAK,CAAC,sBAAuB,MAA2B,CAAC,IAAI,EAAE,CAAC,CAAC;IACnF,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OAuth 2.0 Authentication
3
+ * Simple token-based auth (accepts pre-obtained access token)
4
+ */
5
+ import type { OAuthConfig } from '../types/index.js';
6
+ export declare class OAuthAuth {
7
+ private config;
8
+ constructor(config: OAuthConfig);
9
+ getAccessToken(): Promise<string>;
10
+ }
11
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,qBAAa,SAAS;IAClB,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,WAAW;IAIzB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;CAG1C"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * OAuth 2.0 Authentication
3
+ * Simple token-based auth (accepts pre-obtained access token)
4
+ */
5
+ export class OAuthAuth {
6
+ config;
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ async getAccessToken() {
11
+ return this.config.accessToken;
12
+ }
13
+ }
14
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/auth/oauth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,OAAO,SAAS;IACV,MAAM,CAAc;IAE5B,YAAY,MAAmB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,cAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;IACnC,CAAC;CACJ"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Service Account JWT Authentication
3
+ * Uses RS256 signing to exchange JWT for access token
4
+ */
5
+ import type { ServiceAccountConfig } from '../types/index.js';
6
+ export declare class ServiceAccountAuth {
7
+ private config;
8
+ private credentials;
9
+ private cachedToken;
10
+ private tokenExpiresAt;
11
+ constructor(config: ServiceAccountConfig);
12
+ getAccessToken(): Promise<string>;
13
+ private loadCredentials;
14
+ private createJwt;
15
+ private base64UrlEncode;
16
+ private exchangeJwtForToken;
17
+ }
18
+ //# sourceMappingURL=service-account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-account.d.ts","sourceRoot":"","sources":["../../src/auth/service-account.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAA6B,MAAM,mBAAmB,CAAC;AAYzF,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAA0C;IAC7D,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE,oBAAoB;IAIlC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;YAezB,eAAe;IAgB7B,OAAO,CAAC,SAAS;IAgCjB,OAAO,CAAC,eAAe;YAQT,mBAAmB;CAmBpC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Service Account JWT Authentication
3
+ * Uses RS256 signing to exchange JWT for access token
4
+ */
5
+ import * as crypto from 'crypto';
6
+ import * as fs from 'fs/promises';
7
+ const TOKEN_URI = 'https://oauth2.googleapis.com/token';
8
+ const SCOPE = 'https://www.googleapis.com/auth/spreadsheets.readonly';
9
+ const TOKEN_LIFETIME_SECONDS = 3600;
10
+ export class ServiceAccountAuth {
11
+ config;
12
+ credentials = null;
13
+ cachedToken = null;
14
+ tokenExpiresAt = 0;
15
+ constructor(config) {
16
+ this.config = config;
17
+ }
18
+ async getAccessToken() {
19
+ if (this.cachedToken && Date.now() < this.tokenExpiresAt - 60000) {
20
+ return this.cachedToken;
21
+ }
22
+ await this.loadCredentials();
23
+ const jwt = this.createJwt();
24
+ const token = await this.exchangeJwtForToken(jwt);
25
+ this.cachedToken = token.access_token;
26
+ this.tokenExpiresAt = Date.now() + (token.expires_in * 1000);
27
+ return this.cachedToken;
28
+ }
29
+ async loadCredentials() {
30
+ if (this.credentials)
31
+ return;
32
+ if (this.config.credentials) {
33
+ this.credentials = this.config.credentials;
34
+ return;
35
+ }
36
+ if (!this.config.credentialsPath) {
37
+ throw new Error('Service account requires credentialsPath or credentials');
38
+ }
39
+ const content = await fs.readFile(this.config.credentialsPath, 'utf-8');
40
+ this.credentials = JSON.parse(content);
41
+ }
42
+ createJwt() {
43
+ if (!this.credentials) {
44
+ throw new Error('Credentials not loaded');
45
+ }
46
+ const now = Math.floor(Date.now() / 1000);
47
+ const header = {
48
+ alg: 'RS256',
49
+ typ: 'JWT',
50
+ };
51
+ const payload = {
52
+ iss: this.credentials.client_email,
53
+ scope: SCOPE,
54
+ aud: TOKEN_URI,
55
+ iat: now,
56
+ exp: now + TOKEN_LIFETIME_SECONDS,
57
+ };
58
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
59
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
60
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
61
+ const sign = crypto.createSign('RSA-SHA256');
62
+ sign.update(signatureInput);
63
+ const signature = sign.sign(this.credentials.private_key);
64
+ const encodedSignature = this.base64UrlEncode(signature);
65
+ return `${signatureInput}.${encodedSignature}`;
66
+ }
67
+ base64UrlEncode(input) {
68
+ const buffer = typeof input === 'string' ? Buffer.from(input) : input;
69
+ return buffer.toString('base64')
70
+ .replace(/\+/g, '-')
71
+ .replace(/\//g, '_')
72
+ .replace(/=+$/, '');
73
+ }
74
+ async exchangeJwtForToken(jwt) {
75
+ const response = await fetch(TOKEN_URI, {
76
+ method: 'POST',
77
+ headers: {
78
+ 'Content-Type': 'application/x-www-form-urlencoded',
79
+ },
80
+ body: new URLSearchParams({
81
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
82
+ assertion: jwt,
83
+ }),
84
+ });
85
+ if (!response.ok) {
86
+ const error = await response.text();
87
+ throw new Error(`Token exchange failed: ${error}`);
88
+ }
89
+ return await response.json();
90
+ }
91
+ }
92
+ //# sourceMappingURL=service-account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-account.js","sourceRoot":"","sources":["../../src/auth/service-account.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAGlC,MAAM,SAAS,GAAG,qCAAqC,CAAC;AACxD,MAAM,KAAK,GAAG,uDAAuD,CAAC;AACtE,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAQpC,MAAM,OAAO,kBAAkB;IACnB,MAAM,CAAuB;IAC7B,WAAW,GAAqC,IAAI,CAAC;IACrD,WAAW,GAAkB,IAAI,CAAC;IAClC,cAAc,GAAW,CAAC,CAAC;IAEnC,YAAY,MAA4B;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,cAAc;QAChB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,GAAG,KAAK,EAAE,CAAC;YAC/D,OAAO,IAAI,CAAC,WAAW,CAAC;QAC5B,CAAC;QAED,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAElD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC;QACtC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;QAE7D,OAAO,IAAI,CAAC,WAAW,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,eAAe;QACzB,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAE7B,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;QACxE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA8B,CAAC;IACxE,CAAC;IAEO,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC9C,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAE1C,MAAM,MAAM,GAAG;YACX,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,KAAK;SACb,CAAC;QAEF,MAAM,OAAO,GAAG;YACZ,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,YAAY;YAClC,KAAK,EAAE,KAAK;YACZ,GAAG,EAAE,SAAS;YACd,GAAG,EAAE,GAAG;YACR,GAAG,EAAE,GAAG,GAAG,sBAAsB;SACpC,CAAC;QAEF,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACrE,MAAM,cAAc,GAAG,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC;QAE5D,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAEzD,OAAO,GAAG,cAAc,IAAI,gBAAgB,EAAE,CAAC;IACnD,CAAC;IAEO,eAAe,CAAC,KAAsB;QAC1C,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACtE,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;aAC3B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,GAAW;QACzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACpC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,mCAAmC;aACtD;YACD,IAAI,EAAE,IAAI,eAAe,CAAC;gBACtB,UAAU,EAAE,6CAA6C;gBACzD,SAAS,EAAE,GAAG;aACjB,CAAC;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAmB,CAAC;IAClD,CAAC;CACJ"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * User OAuth Authentication
3
+ * OAuth 2.0 Authorization Code flow with PKCE for personal Google accounts
4
+ */
5
+ import type { StoredTokens } from '../types/index.js';
6
+ export interface OAuthClientCredentials {
7
+ clientId: string;
8
+ clientSecret: string;
9
+ }
10
+ interface PKCEPair {
11
+ codeVerifier: string;
12
+ codeChallenge: string;
13
+ }
14
+ export declare function generatePKCE(): PKCEPair;
15
+ export declare function getAuthorizationUrl(codeChallenge: string, clientId?: string): string;
16
+ export declare function loadStoredTokens(): Promise<StoredTokens | null>;
17
+ export declare function deleteTokens(): Promise<void>;
18
+ export declare function login(credentials?: OAuthClientCredentials): Promise<StoredTokens>;
19
+ export declare class UserAuth {
20
+ private tokens;
21
+ getAccessToken(): Promise<string>;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=user-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-auth.d.ts","sourceRoot":"","sources":["../../src/auth/user-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAYtD,MAAM,WAAW,sBAAsB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACxB;AAOD,UAAU,QAAQ;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,YAAY,IAAI,QAAQ,CAOvC;AAqFD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAapF;AAkFD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAOrE;AAOD,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAMlD;AAID,wBAAsB,KAAK,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2BvF;AAID,qBAAa,QAAQ;IACjB,OAAO,CAAC,MAAM,CAA6B;IAErC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;CA4B1C"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * User OAuth Authentication
3
+ * OAuth 2.0 Authorization Code flow with PKCE for personal Google accounts
4
+ */
5
+ import * as crypto from 'crypto';
6
+ import * as fs from 'fs/promises';
7
+ import * as http from 'http';
8
+ import * as os from 'os';
9
+ import * as path from 'path';
10
+ import { exec } from 'child_process';
11
+ import { OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_REDIRECT_URI, OAUTH_CALLBACK_PORT, OAUTH_AUTH_URL, OAUTH_TOKEN_URL, OAUTH_USERINFO_URL, OAUTH_SCOPES, } from './constants.js';
12
+ const CONFIG_DIR = path.join(os.homedir(), '.sheets');
13
+ const TOKENS_FILE = path.join(CONFIG_DIR, 'tokens.json');
14
+ export function generatePKCE() {
15
+ const codeVerifier = crypto.randomBytes(32).toString('base64url');
16
+ const codeChallenge = crypto
17
+ .createHash('sha256')
18
+ .update(codeVerifier)
19
+ .digest('base64url');
20
+ return { codeVerifier, codeChallenge };
21
+ }
22
+ // === Browser ===
23
+ function openBrowser(url) {
24
+ return new Promise((resolve, reject) => {
25
+ const platform = process.platform;
26
+ let command;
27
+ if (platform === 'darwin') {
28
+ command = `open "${url}"`;
29
+ }
30
+ else if (platform === 'win32') {
31
+ command = `start "" "${url}"`;
32
+ }
33
+ else {
34
+ command = `xdg-open "${url}"`;
35
+ }
36
+ exec(command, (error) => {
37
+ if (error) {
38
+ reject(new Error(`Failed to open browser: ${error.message}`));
39
+ }
40
+ else {
41
+ resolve();
42
+ }
43
+ });
44
+ });
45
+ }
46
+ // === Callback Server ===
47
+ function startCallbackServer() {
48
+ return new Promise((resolve, reject) => {
49
+ let timeoutId;
50
+ const server = http.createServer((req, res) => {
51
+ const url = new URL(req.url || '', `http://localhost:${OAUTH_CALLBACK_PORT}`);
52
+ if (url.pathname === '/callback') {
53
+ const code = url.searchParams.get('code');
54
+ const error = url.searchParams.get('error');
55
+ if (error) {
56
+ res.writeHead(400, { 'Content-Type': 'text/html' });
57
+ res.end('<html><body><h1>Authorization Failed</h1><p>You can close this window.</p></body></html>');
58
+ clearTimeout(timeoutId);
59
+ server.close();
60
+ reject(new Error(`Authorization error: ${error}`));
61
+ return;
62
+ }
63
+ if (code) {
64
+ res.writeHead(200, { 'Content-Type': 'text/html' });
65
+ res.end('<html><body><h1>Authorization Successful</h1><p>You can close this window.</p></body></html>');
66
+ clearTimeout(timeoutId);
67
+ server.close();
68
+ resolve(code);
69
+ return;
70
+ }
71
+ res.writeHead(400, { 'Content-Type': 'text/html' });
72
+ res.end('<html><body><h1>Missing Code</h1></body></html>');
73
+ }
74
+ else {
75
+ res.writeHead(404);
76
+ res.end();
77
+ }
78
+ });
79
+ server.on('error', (err) => {
80
+ clearTimeout(timeoutId);
81
+ reject(new Error(`Callback server error: ${err.message}`));
82
+ });
83
+ server.listen(OAUTH_CALLBACK_PORT, () => {
84
+ // Server started, waiting for callback
85
+ });
86
+ // Timeout after 5 minutes
87
+ timeoutId = setTimeout(() => {
88
+ server.close();
89
+ reject(new Error('Authorization timeout'));
90
+ }, 5 * 60 * 1000);
91
+ });
92
+ }
93
+ // === Authorization URL ===
94
+ export function getAuthorizationUrl(codeChallenge, clientId) {
95
+ const params = new URLSearchParams({
96
+ client_id: clientId || OAUTH_CLIENT_ID,
97
+ redirect_uri: OAUTH_REDIRECT_URI,
98
+ response_type: 'code',
99
+ scope: OAUTH_SCOPES.join(' '),
100
+ code_challenge: codeChallenge,
101
+ code_challenge_method: 'S256',
102
+ access_type: 'offline',
103
+ prompt: 'consent',
104
+ });
105
+ return `${OAUTH_AUTH_URL}?${params.toString()}`;
106
+ }
107
+ async function exchangeCodeForTokens(code, codeVerifier, credentials) {
108
+ const response = await fetch(OAUTH_TOKEN_URL, {
109
+ method: 'POST',
110
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
111
+ body: new URLSearchParams({
112
+ client_id: credentials?.clientId || OAUTH_CLIENT_ID,
113
+ client_secret: credentials?.clientSecret || OAUTH_CLIENT_SECRET,
114
+ code,
115
+ code_verifier: codeVerifier,
116
+ grant_type: 'authorization_code',
117
+ redirect_uri: OAUTH_REDIRECT_URI,
118
+ }),
119
+ });
120
+ if (!response.ok) {
121
+ const error = await response.text();
122
+ throw new Error(`Token exchange failed: ${error}`);
123
+ }
124
+ return await response.json();
125
+ }
126
+ async function refreshAccessToken(refreshToken) {
127
+ const response = await fetch(OAUTH_TOKEN_URL, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
130
+ body: new URLSearchParams({
131
+ client_id: OAUTH_CLIENT_ID,
132
+ client_secret: OAUTH_CLIENT_SECRET,
133
+ refresh_token: refreshToken,
134
+ grant_type: 'refresh_token',
135
+ }),
136
+ });
137
+ if (!response.ok) {
138
+ const error = await response.text();
139
+ throw new Error(`Token refresh failed: ${error}`);
140
+ }
141
+ return await response.json();
142
+ }
143
+ async function getUserInfo(accessToken) {
144
+ const response = await fetch(OAUTH_USERINFO_URL, {
145
+ headers: { Authorization: `Bearer ${accessToken}` },
146
+ });
147
+ if (!response.ok) {
148
+ throw new Error('Failed to get user info');
149
+ }
150
+ return await response.json();
151
+ }
152
+ // === Token Storage ===
153
+ async function ensureConfigDir() {
154
+ try {
155
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
156
+ }
157
+ catch {
158
+ // Directory already exists
159
+ }
160
+ }
161
+ export async function loadStoredTokens() {
162
+ try {
163
+ const content = await fs.readFile(TOKENS_FILE, 'utf-8');
164
+ return JSON.parse(content);
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ }
170
+ async function saveTokens(tokens) {
171
+ await ensureConfigDir();
172
+ await fs.writeFile(TOKENS_FILE, JSON.stringify(tokens, null, 2));
173
+ }
174
+ export async function deleteTokens() {
175
+ try {
176
+ await fs.unlink(TOKENS_FILE);
177
+ }
178
+ catch {
179
+ // File doesn't exist
180
+ }
181
+ }
182
+ // === Login Flow ===
183
+ export async function login(credentials) {
184
+ const { codeVerifier, codeChallenge } = generatePKCE();
185
+ const authUrl = getAuthorizationUrl(codeChallenge, credentials?.clientId);
186
+ console.log('Opening browser for Google login...');
187
+ // Start server first, then open browser
188
+ const codePromise = startCallbackServer();
189
+ await openBrowser(authUrl);
190
+ console.log('Waiting for authorization...');
191
+ const code = await codePromise;
192
+ console.log('Exchanging code for tokens...');
193
+ const tokenResponse = await exchangeCodeForTokens(code, codeVerifier, credentials);
194
+ const userInfo = await getUserInfo(tokenResponse.access_token);
195
+ const tokens = {
196
+ accessToken: tokenResponse.access_token,
197
+ refreshToken: tokenResponse.refresh_token || '',
198
+ expiresAt: Date.now() + (tokenResponse.expires_in * 1000),
199
+ email: userInfo.email,
200
+ };
201
+ await saveTokens(tokens);
202
+ return tokens;
203
+ }
204
+ // === Auth Provider ===
205
+ export class UserAuth {
206
+ tokens = null;
207
+ async getAccessToken() {
208
+ if (!this.tokens) {
209
+ this.tokens = await loadStoredTokens();
210
+ }
211
+ if (!this.tokens) {
212
+ throw new Error('Not logged in. Run "sheets login" first.');
213
+ }
214
+ // Refresh if expired (with 1 minute buffer)
215
+ if (Date.now() >= this.tokens.expiresAt - 60000) {
216
+ if (!this.tokens.refreshToken) {
217
+ throw new Error('Token expired and no refresh token. Run "sheets login" again.');
218
+ }
219
+ const tokenResponse = await refreshAccessToken(this.tokens.refreshToken);
220
+ this.tokens.accessToken = tokenResponse.access_token;
221
+ this.tokens.expiresAt = Date.now() + (tokenResponse.expires_in * 1000);
222
+ if (tokenResponse.refresh_token) {
223
+ this.tokens.refreshToken = tokenResponse.refresh_token;
224
+ }
225
+ await saveTokens(this.tokens);
226
+ }
227
+ return this.tokens.accessToken;
228
+ }
229
+ }
230
+ //# sourceMappingURL=user-auth.js.map