@ianmaleney/sheets-api-helper 1.0.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.
@@ -0,0 +1,109 @@
1
+ # Google Sheets API Authentication Flow - Implementation Summary
2
+
3
+ ## ✅ What's Been Created
4
+
5
+ ### 1. **Production-Ready Authentication System** ([index.js](index.js))
6
+
7
+ #### Core Functions:
8
+
9
+ **`initializeAuth()`**
10
+ - Initializes the Google Sheets API client using service account credentials
11
+ - Loads credentials from `service_key.json`
12
+ - Implements caching to avoid re-authentication
13
+ - Error handling with clear error messages
14
+
15
+ **`getSheetData(spreadsheetId, range)`**
16
+ - Fetches data from a single spreadsheet range
17
+ - Automatically initializes auth if needed
18
+ - Returns rows as an array
19
+ - Returns empty array if no data found
20
+ - Includes error handling
21
+
22
+ **`getMultipleRanges(spreadsheetId, ranges)`**
23
+ - Efficiently fetches multiple ranges in one API call
24
+ - Returns object with ranges as keys
25
+ - Better performance for bulk reads
26
+
27
+ ### 2. **Setup Guide** ([SETUP.md](SETUP.md))
28
+ Comprehensive documentation including:
29
+ - Prerequisites
30
+ - Step-by-step setup instructions
31
+ - How to create a service account
32
+ - How to share spreadsheets with the service account
33
+ - Usage examples
34
+ - Troubleshooting guide
35
+
36
+ ### 3. **Verification Script** ([setup-check.js](setup-check.js))
37
+ Automated setup checker that verifies:
38
+ - ✓ service_key.json exists and is valid
39
+ - ✓ Environment variables are set
40
+ - ✓ Dependencies are installed
41
+ - ✓ .gitignore is properly configured
42
+
43
+ ### 4. **Tests** ([index.test.js](index.test.js))
44
+ - Runs authentication before tests
45
+ - Integration test that verifies API connectivity
46
+ - Uses Bun's test framework
47
+
48
+ ### 5. **Security** ([.gitignore](.gitignore))
49
+ - Added `service_key.json` to .gitignore
50
+ - Added credential files to ignore list
51
+ - Prevents accidentally committing sensitive data
52
+
53
+ ## 🚀 How to Use
54
+
55
+ ### Initial Setup
56
+ ```bash
57
+ # 1. Add your service account key
58
+ cp your-service-account-key.json service_key.json
59
+
60
+ # 2. Update environment variables
61
+ echo "TEST_SHEET_ID=your_spreadsheet_id" >> .env
62
+
63
+ # 3. Share the spreadsheet with the service account email
64
+ # (Email is in service_key.json under "client_email")
65
+
66
+ # 4. Verify setup
67
+ node setup-check.js
68
+
69
+ # 5. Run tests
70
+ bun test
71
+ ```
72
+
73
+ ### In Your Code
74
+ ```javascript
75
+ import { initializeAuth, getSheetData } from './index.js';
76
+
77
+ // Initialize once at app startup
78
+ await initializeAuth();
79
+
80
+ // Then fetch data anytime
81
+ const rows = await getSheetData('spreadsheet-id', 'Sheet1!A1:D10');
82
+ ```
83
+
84
+ ## 🔑 Key Features
85
+
86
+ - **Service Account Authentication**: Uses service accounts for secure, non-interactive access
87
+ - **Automatic Initialization**: Auth is automatically initialized on first data fetch
88
+ - **Caching**: Client is cached to avoid re-authentication
89
+ - **Error Handling**: Clear error messages for debugging
90
+ - **Batch Fetching**: Support for multiple ranges in one call
91
+ - **Type Safety**: JSDoc comments for all functions
92
+
93
+ ## ⚠️ Common Issues & Solutions
94
+
95
+ **"The caller does not have permission"**
96
+ - Share the spreadsheet with the service account email (found in service_key.json)
97
+
98
+ **"Service key not found"**
99
+ - Ensure service_key.json is in the project root
100
+
101
+ **Slow Initial Load**
102
+ - First authentication takes a few seconds - this is normal
103
+
104
+ ## 📝 Next Steps
105
+
106
+ 1. Follow the [SETUP.md](SETUP.md) guide to complete configuration
107
+ 2. Run `node setup-check.js` to verify everything is set up correctly
108
+ 3. Run `bun test` to test the connection
109
+ 4. Start using `getSheetData()` in your application
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # Google Sheets API - Complete Authentication Flow
2
+
3
+ A production-ready authentication system for accessing Google Sheets data using service account credentials.
4
+
5
+ ## 🎯 What This Provides
6
+
7
+ ✅ **Service Account Authentication** - Secure, non-interactive API access
8
+ ✅ **Automatic Initialization** - Auth is set up automatically on first use
9
+ ✅ **Cached Client** - Avoids re-authentication for every request
10
+ ✅ **Error Handling** - Clear error messages for debugging
11
+ ✅ **Batch Operations** - Fetch multiple ranges efficiently
12
+ ✅ **Integration Tests** - Verify API connectivity
13
+ ✅ **Setup Verification** - Script to check your configuration
14
+
15
+ ## 📁 Project Structure
16
+
17
+ ```
18
+ .
19
+ ├── index.js # Main API implementation
20
+ ├── index.test.js # Integration tests
21
+ ├── example.js # Usage examples
22
+ ├── setup-check.js # Configuration verification script
23
+ ├── SETUP.md # Detailed setup guide
24
+ ├── AUTH_IMPLEMENTATION.md # Implementation details
25
+ ├── README.md # This file
26
+ ├── .env # Environment variables (contains TEST_SHEET_ID)
27
+ ├── .gitignore # Git ignore (keeps credentials safe)
28
+ ├── service_key.json # Service account key (DO NOT COMMIT)
29
+ └── package.json # Project dependencies
30
+ ```
31
+
32
+ ## 🚀 Quick Start
33
+
34
+ ### 1. Verify Setup
35
+ ```bash
36
+ node setup-check.js
37
+ ```
38
+
39
+ This will check:
40
+ - ✓ service_key.json exists and is valid
41
+ - ✓ TEST_SHEET_ID is set in .env
42
+ - ✓ Dependencies are installed
43
+ - ✓ Credentials are not committed to git
44
+
45
+ ### 2. Share Your Spreadsheet
46
+
47
+ 1. Get the service account email from the output above
48
+ 2. Open your Google Sheet
49
+ 3. Click **Share**
50
+ 4. Paste the service account email
51
+ 5. Give it **Viewer** access (or **Editor** if needed)
52
+
53
+ ### 3. Run Example
54
+ ```bash
55
+ bun run example.js
56
+ ```
57
+
58
+ ### 4. Run Tests
59
+ ```bash
60
+ bun test
61
+ ```
62
+
63
+ ## 📖 API Reference
64
+
65
+ ### `initializeAuth()`
66
+ Initialize the Google Sheets API client. Call this once at app startup.
67
+
68
+ ```javascript
69
+ import { initializeAuth } from './index.js';
70
+
71
+ await initializeAuth();
72
+ ```
73
+
74
+ ### `getSheetData(spreadsheetId, range)`
75
+ Fetch data from a single spreadsheet range.
76
+
77
+ ```javascript
78
+ const rows = await getSheetData(
79
+ '1ZEs6v0-izIyVNFdBb7B4PXOGXsdsjuTq0tAXJBg_kDU',
80
+ 'Sheet1!A1:D10'
81
+ );
82
+
83
+ console.log(rows); // Array of rows
84
+ ```
85
+
86
+ ### `getMultipleRanges(spreadsheetId, ranges)`
87
+ Fetch multiple ranges efficiently in one API call.
88
+
89
+ ```javascript
90
+ const data = await getMultipleRanges(
91
+ '1ZEs6v0-izIyVNFdBb7B4PXOGXsdsjuTq0tAXJBg_kDU',
92
+ ['Sheet1!A1:D10', 'Sheet2!A1:B5']
93
+ );
94
+
95
+ console.log(data['Sheet1!A1:D10']); // Array of rows
96
+ console.log(data['Sheet2!A1:B5']); // Array of rows
97
+ ```
98
+
99
+ ## 🔧 Configuration
100
+
101
+ ### Environment Variables (.env)
102
+ ```env
103
+ TEST_SHEET_ID=your_spreadsheet_id
104
+ ```
105
+
106
+ Get your spreadsheet ID from the URL:
107
+ ```
108
+ https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/edit
109
+ ```
110
+
111
+ ### Service Key (service_key.json)
112
+ Your Google Cloud service account JSON key. Already in `.gitignore` to prevent accidental commits.
113
+
114
+ ## 🐛 Troubleshooting
115
+
116
+ ### "The caller does not have permission"
117
+ **Cause:** Spreadsheet not shared with service account
118
+
119
+ **Solution:**
120
+ 1. Get service account email from `service_key.json` → `client_email`
121
+ 2. Share the spreadsheet with this email
122
+ 3. Grant at least **Viewer** access
123
+
124
+ ### "Service key not found"
125
+ **Cause:** `service_key.json` not in project root
126
+
127
+ **Solution:**
128
+ 1. Download your service account key from Google Cloud Console
129
+ 2. Save it as `service_key.json` in the project root
130
+ 3. Never commit it (already in `.gitignore`)
131
+
132
+ ### Authentication Timeout
133
+ **Cause:** Normal for first request
134
+
135
+ **Solution:** This is expected. Subsequent requests are faster due to caching.
136
+
137
+ ### No data returned
138
+ **Cause:** Empty sheet or invalid range
139
+
140
+ **Solution:**
141
+ - Check that the range contains data
142
+ - Verify spreadsheet ID is correct
143
+ - Try a different range like `Sheet1!A1:Z100`
144
+
145
+ ## 📚 More Information
146
+
147
+ - **Setup Instructions**: See [SETUP.md](SETUP.md)
148
+ - **Implementation Details**: See [AUTH_IMPLEMENTATION.md](AUTH_IMPLEMENTATION.md)
149
+ - **Code Examples**: See [example.js](example.js)
150
+ - **Google Sheets API Docs**: [https://developers.google.com/sheets/api](https://developers.google.com/sheets/api)
151
+
152
+ ## 🔐 Security
153
+
154
+ - ✓ `service_key.json` is in `.gitignore`
155
+ - ✓ Credentials never logged in production
156
+ - ✓ Uses Google Cloud's official libraries
157
+ - ✓ Service account is recommended over user authentication
158
+ - ✓ Use environment variables for sensitive data
159
+
160
+ ## 🔄 Workflow Summary
161
+
162
+ ```javascript
163
+ // 1. Import once in your app
164
+ import { initializeAuth, getSheetData } from './index.js';
165
+
166
+ // 2. Initialize once at startup
167
+ await initializeAuth();
168
+
169
+ // 3. Use anytime, anywhere
170
+ const rows = await getSheetData('spreadsheet-id', 'Sheet1!A1:D10');
171
+ ```
172
+
173
+ ## 💡 Tips
174
+
175
+ - **Batch Reads**: Use `getMultipleRanges()` instead of multiple `getSheetData()` calls
176
+ - **Range Format**: Use A1 notation like `Sheet1!A1:D10` or `'Sheet1'!A1:D10`
177
+ - **Large Datasets**: For very large sheets, consider using pagination
178
+ - **Caching**: The auth client is cached automatically - no need to reinitialize
179
+
180
+ ## 🤝 Need Help?
181
+
182
+ 1. Run `node setup-check.js` to verify configuration
183
+ 2. Check [SETUP.md](SETUP.md) for detailed instructions
184
+ 3. See [example.js](example.js) for working code
185
+ 4. Review error messages - they indicate the exact issue
package/SETUP.md ADDED
@@ -0,0 +1,114 @@
1
+ # Google Sheets API Setup Guide
2
+
3
+ ## Overview
4
+ This project provides a working authentication flow for the Google Sheets API using service account credentials.
5
+
6
+ ## Prerequisites
7
+ - A Google Cloud Project with the Sheets API enabled
8
+ - Service account key (JSON file)
9
+
10
+ ## Setup Steps
11
+
12
+ ### 1. Create a Service Account (if you haven't already)
13
+
14
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com)
15
+ 2. Create a new project or select an existing one
16
+ 3. Navigate to **Service Accounts** (Search for "Service Accounts")
17
+ 4. Click **Create Service Account**
18
+ 5. Fill in the service account details
19
+ 6. Click **Create and Continue**
20
+ 7. Grant the service account "Editor" role (or minimal "Sheets Editor" role)
21
+ 8. Click **Create Key** and select **JSON** format
22
+ 9. Save the JSON file as `service_key.json` in the root of this project
23
+
24
+ ### 2. Share Your Spreadsheet with the Service Account
25
+
26
+ 1. Open the spreadsheet you want to access
27
+ 2. Get your spreadsheet ID from the URL: `https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/edit`
28
+ 3. Click **Share** on the spreadsheet
29
+ 4. In the service account JSON file, find the `client_email` field
30
+ 5. Share the spreadsheet with this email address
31
+ 6. Grant it **Viewer** access (or **Editor** if you need write permissions)
32
+
33
+ ### 3. Set Environment Variables
34
+
35
+ Create or update `.env` file:
36
+
37
+ ```
38
+ TEST_SHEET_ID=your_spreadsheet_id_here
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### Basic Usage
44
+
45
+ ```javascript
46
+ import { initializeAuth, getSheetData } from './index.js';
47
+
48
+ // Initialize authentication (call once at app startup)
49
+ await initializeAuth();
50
+
51
+ // Fetch data from a specific range
52
+ const data = await getSheetData('your-spreadsheet-id', 'Sheet1!A1:D10');
53
+ console.log(data);
54
+ ```
55
+
56
+ ### Get Multiple Ranges
57
+
58
+ ```javascript
59
+ import { initializeAuth, getMultipleRanges } from './index.js';
60
+
61
+ await initializeAuth();
62
+
63
+ const data = await getMultipleRanges('your-spreadsheet-id', [
64
+ 'Sheet1!A1:D10',
65
+ 'Sheet2!A1:B5'
66
+ ]);
67
+
68
+ console.log(data['Sheet1!A1:D10']);
69
+ console.log(data['Sheet2!A1:B5']);
70
+ ```
71
+
72
+ ## Running Tests
73
+
74
+ ```bash
75
+ bun test
76
+ ```
77
+
78
+ ## Troubleshooting
79
+
80
+ ### "The caller does not have permission" Error
81
+ - Ensure the spreadsheet is shared with the service account email
82
+ - Check that the service account has at least **Viewer** access
83
+
84
+ ### "Service key not found" Error
85
+ - Verify `service_key.json` exists in the project root
86
+ - Check file permissions
87
+
88
+ ### Authentication Timeout
89
+ - The first authentication may take a few seconds
90
+ - Ensure your internet connection is stable
91
+
92
+ ## Architecture
93
+
94
+ ### `initializeAuth()`
95
+ - Initializes the Google Sheets API client once
96
+ - Uses service account credentials from `service_key.json`
97
+ - Caches the client for subsequent calls
98
+
99
+ ### `getSheetData(spreadsheetId, range)`
100
+ - Fetches data from a single range
101
+ - Returns an array of rows
102
+ - Returns empty array if no data found
103
+
104
+ ### `getMultipleRanges(spreadsheetId, ranges)`
105
+ - Fetches data from multiple ranges in one call
106
+ - Returns an object with ranges as keys and data as values
107
+ - More efficient for bulk reads
108
+
109
+ ## Security Notes
110
+
111
+ - **Never commit** `service_key.json` to version control
112
+ - Add `service_key.json` to `.gitignore` (already done)
113
+ - Rotate service account keys regularly
114
+ - Use environment variables for sensitive data
package/example.js ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Example: Using the Google Sheets API
3
+ *
4
+ * Before running this file:
5
+ * 1. Ensure service_key.json is in the project root
6
+ * 2. Share your spreadsheet with the service account email
7
+ * 3. Set your spreadsheet ID in .env
8
+ */
9
+
10
+ import 'dotenv/config';
11
+ import { initializeAuth, getSheetData, getMultipleRanges } from './index.js';
12
+
13
+ async function main() {
14
+ try {
15
+ // Initialize authentication
16
+ console.log('Initializing authentication...\n');
17
+ await initializeAuth();
18
+
19
+ // Example 1: Get data from a single range
20
+ console.log('📊 Fetching data from Sheet1!A1:D10...');
21
+ const data = await getSheetData(
22
+ process.env.TEST_SHEET_ID,
23
+ 'Sheet1!A1:D10'
24
+ );
25
+
26
+ if (data.length > 0) {
27
+ console.log(`✓ Retrieved ${data.length} rows`);
28
+ console.log('First row:', data[0]);
29
+ } else {
30
+ console.log('No data found in range');
31
+ }
32
+
33
+ // Example 2: Get multiple ranges (if you have multiple sheets)
34
+ // Uncomment to test:
35
+ /*
36
+ console.log('\n📊 Fetching multiple ranges...');
37
+ const multiData = await getMultipleRanges(
38
+ process.env.TEST_SHEET_ID,
39
+ ['Sheet1!A1:D10', 'Sheet2!A1:B5']
40
+ );
41
+
42
+ console.log('Sheet1 data:', multiData['Sheet1!A1:D10']);
43
+ console.log('Sheet2 data:', multiData['Sheet2!A1:B5']);
44
+ */
45
+
46
+ } catch (error) {
47
+ console.error('❌ Error:', error.message);
48
+
49
+ if (error.message.includes('The caller does not have permission')) {
50
+ console.log('\n💡 Solution: Share your spreadsheet with this email:');
51
+ console.log(' sheets@eco-emissary-484012-i1.iam.gserviceaccount.com');
52
+ console.log(' (Email from service_key.json client_email field)');
53
+ }
54
+
55
+ process.exit(1);
56
+ }
57
+ }
58
+
59
+ main();
package/index.js ADDED
@@ -0,0 +1,105 @@
1
+ import { google } from 'googleapis';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import fs from 'node:fs';
5
+
6
+ const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
7
+ let sheetsClient = null;
8
+
9
+ /**
10
+ * Initialize the Google Sheets API client using service account credentials
11
+ * @returns {Promise<void>}
12
+ */
13
+ export const initializeAuth = async () => {
14
+ if (sheetsClient) {
15
+ return; // Already initialized
16
+ }
17
+
18
+ try {
19
+ const keyPath = path.join(process.cwd(), 'service_key.json');
20
+
21
+ if (!fs.existsSync(keyPath)) {
22
+ throw new Error(`Service key not found at ${keyPath}`);
23
+ }
24
+
25
+ const keyData = JSON.parse(fs.readFileSync(keyPath, 'utf-8'));
26
+
27
+ const auth = new google.auth.GoogleAuth({
28
+ credentials: keyData,
29
+ scopes: SCOPES,
30
+ });
31
+
32
+ sheetsClient = google.sheets({
33
+ version: 'v4',
34
+ auth: auth,
35
+ });
36
+
37
+ console.log('✓ Google Sheets API authentication initialized successfully');
38
+ } catch (error) {
39
+ console.error('✗ Failed to initialize authentication:', error.message);
40
+ throw error;
41
+ }
42
+ };
43
+
44
+ /**
45
+ * Get data from a Google Sheet
46
+ * @param {string} spreadsheet_id - The spreadsheet ID
47
+ * @param {string} range - The range to read (e.g., 'Sheet1!A1:D10')
48
+ * @returns {Promise<Array>} Array of rows
49
+ */
50
+ export const getSheetData = async (spreadsheet_id, range) => {
51
+ // Ensure authentication is initialized
52
+ if (!sheetsClient) {
53
+ await initializeAuth();
54
+ }
55
+
56
+ try {
57
+ // Get the values from the spreadsheet.
58
+ const result = await sheetsClient.spreadsheets.values.get({
59
+ spreadsheetId: spreadsheet_id,
60
+ range: range,
61
+ });
62
+
63
+ const rows = result.data.values;
64
+
65
+ if (!rows || rows.length === 0) {
66
+ console.log('No data found.');
67
+ return [];
68
+ }
69
+
70
+ return rows;
71
+ } catch (error) {
72
+ console.error(`Failed to fetch sheet data: ${error.message}`);
73
+ throw error;
74
+ }
75
+ };
76
+
77
+ /**
78
+ * Get multiple ranges from a Google Sheet
79
+ * @param {string} spreadsheet_id - The spreadsheet ID
80
+ * @param {Array<string>} ranges - Array of ranges to read
81
+ * @returns {Promise<Object>} Object with ranges as keys and data as values
82
+ */
83
+ export const getMultipleRanges = async (spreadsheet_id, ranges) => {
84
+ if (!sheetsClient) {
85
+ await initializeAuth();
86
+ }
87
+
88
+ try {
89
+ const result = await sheetsClient.spreadsheets.values.batchGet({
90
+ spreadsheetId: spreadsheet_id,
91
+ ranges: ranges,
92
+ });
93
+
94
+ const data = {};
95
+ result.data.valueRanges.forEach((range, index) => {
96
+ data[ranges[index]] = range.values || [];
97
+ });
98
+
99
+ return data;
100
+ } catch (error) {
101
+ console.error(`Failed to fetch multiple ranges: ${error.message}`);
102
+ throw error;
103
+ }
104
+ };
105
+
package/index.test.js ADDED
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect, beforeAll } from 'bun:test';
2
+ import { getSheetData, initializeAuth } from './index.js';
3
+ import 'dotenv/config';
4
+
5
+ describe('getSheetData', () => {
6
+ beforeAll(async () => {
7
+ // Initialize authentication before running tests
8
+ await initializeAuth();
9
+ });
10
+
11
+ it('should return rows when data is found', async () => {
12
+ const result = await getSheetData(
13
+ process.env.TEST_SHEET_ID,
14
+ 'Film!A1:D10'
15
+ );
16
+
17
+ console.log('Result:', result);
18
+
19
+ // Since the actual data in the sheet may vary, we will just check if result is an array.
20
+ expect(Array.isArray(result)).toBe(true);
21
+ expect(result.length).toBeGreaterThan(0);
22
+ });
23
+ });
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@ianmaleney/sheets-api-helper",
3
+ "version": "1.0.0",
4
+ "description": "A helper library for interacting with the Google Sheets API using Node.js.",
5
+ "main": "index.js",
6
+ "author": "Ian Maleney <ian@fallowmedia.com>",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "dependencies": {
10
+ "@google-cloud/local-auth": "^2.1.0",
11
+ "dotenv": "^17.2.3",
12
+ "googleapis": "^105.0.0"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "test": "bun test"
19
+ }
20
+ }
21
+
package/setup-check.js ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Setup verification script for Google Sheets API
5
+ * Run with: node setup-check.js
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+
14
+ console.log('🔍 Google Sheets API Setup Verification\n');
15
+
16
+ // Check 1: Service Key File
17
+ console.log('1. Checking service_key.json...');
18
+ const serviceKeyPath = path.join(__dirname, 'service_key.json');
19
+ if (fs.existsSync(serviceKeyPath)) {
20
+ try {
21
+ const keyData = JSON.parse(fs.readFileSync(serviceKeyPath, 'utf-8'));
22
+ console.log(' ✓ service_key.json found');
23
+ console.log(` ✓ Project ID: ${keyData.project_id}`);
24
+ console.log(` ✓ Service Account Email: ${keyData.client_email}`);
25
+
26
+ if (!keyData.private_key) {
27
+ console.log(' ✗ WARNING: private_key is missing from service_key.json');
28
+ }
29
+ } catch (e) {
30
+ console.log(' ✗ service_key.json is not valid JSON');
31
+ }
32
+ } else {
33
+ console.log(` ✗ service_key.json not found at ${serviceKeyPath}`);
34
+ console.log(' Run: touch service_key.json and add your credentials');
35
+ }
36
+
37
+ // Check 2: Environment Variables
38
+ console.log('\n2. Checking environment variables...');
39
+ const envPath = path.join(__dirname, '.env');
40
+ if (fs.existsSync(envPath)) {
41
+ const envContent = fs.readFileSync(envPath, 'utf-8');
42
+ if (envContent.includes('TEST_SHEET_ID')) {
43
+ console.log(' ✓ TEST_SHEET_ID is set in .env');
44
+ } else {
45
+ console.log(' ✗ TEST_SHEET_ID is not set in .env');
46
+ }
47
+ } else {
48
+ console.log(' ✗ .env file not found');
49
+ }
50
+
51
+ // Check 3: Dependencies
52
+ console.log('\n3. Checking dependencies...');
53
+ const packageJsonPath = path.join(__dirname, 'package.json');
54
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
55
+ const requiredDeps = ['googleapis', '@google-cloud/local-auth'];
56
+ const nodeModulesPath = path.join(__dirname, 'node_modules');
57
+
58
+ requiredDeps.forEach(dep => {
59
+ const depPath = path.join(nodeModulesPath, dep);
60
+ if (fs.existsSync(depPath)) {
61
+ console.log(` ✓ ${dep} is installed`);
62
+ } else {
63
+ console.log(` ✗ ${dep} is not installed`);
64
+ console.log(' Run: npm install or bun install');
65
+ }
66
+ });
67
+
68
+ // Check 4: .gitignore
69
+ console.log('\n4. Checking .gitignore...');
70
+ const gitignorePath = path.join(__dirname, '.gitignore');
71
+ if (fs.existsSync(gitignorePath)) {
72
+ const gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
73
+ if (gitignoreContent.includes('service_key.json')) {
74
+ console.log(' ✓ service_key.json is in .gitignore');
75
+ } else {
76
+ console.log(' ⚠ service_key.json is NOT in .gitignore');
77
+ console.log(' Add this line to .gitignore: service_key.json');
78
+ }
79
+ } else {
80
+ console.log(' ⚠ .gitignore not found');
81
+ }
82
+
83
+ console.log('\n' + '='.repeat(50));
84
+ console.log('Next Steps:');
85
+ console.log('1. Ensure service_key.json is in the project root');
86
+ console.log('2. Share your spreadsheet with the service account email');
87
+ console.log('3. Set TEST_SHEET_ID in .env');
88
+ console.log('4. Run: bun test');
89
+ console.log('='.repeat(50));