# Google Indexing API Service (NYTSolvers)

Automated standalone Google Indexing API v3 service for notifying Google search crawlers immediately when puzzle hint pages, solver tools, or blog articles are updated or removed.

---

## 1. Setup & Credentials

The service authenticates via Google Service Account JWT with scope `https://www.googleapis.com/auth/indexing`.

It automatically searches for credentials in the following order:
1. `options.credentials` (direct object or JSON string)
2. `options.keyPath` (explicit path to service account JSON)
3. `SERVICE_ACCOUNT_KEY_PATH` (environment variable)
4. `GOOGLE_APPLICATION_CREDENTIALS` (standard Google env variable)
5. `GOOGLE_SERVICE_ACCOUNT_JSON` (raw JSON string in env variable)
6. `./service_account.json` or `./credential.json` in the current working directory.

> **Security Note:** Service account JSON files (`service_account.json`, `credential.json`) are ignored in `.gitignore` so secrets are never pushed to version control.

### Search Console Permission Requirement
To publish live indexing requests, add your service account's `client_email` (e.g. `nytsolvers@our-tract-512312-m1.iam.gserviceaccount.com`) as an **Owner** under **Google Search Console > Settings > Users and permissions** for `https://nytsolvers.com/`.

---

## 2. CLI Tool (`scripts/test-indexing.js`)

A CLI diagnostic runner is provided with built-in `--dry-run` simulation to test payloads and flows without consuming daily quotas (Google's standard quota is 200 requests/day).

```bash
# Run dry-run simulation across sample daily URLs (Safe, 0 quota used)
npm run test:indexing

# Or run directly with node
node scripts/test-indexing.js --dry-run

# Test all 16 daily puzzle pages + homepage in dry-run mode
node scripts/test-indexing.js --daily --dry-run

# Test Bing IndexNow for all daily pages
node scripts/test-indexing.js --daily --bing --dry-run

# Live submission of all daily pages to both Google & Bing
node scripts/test-indexing.js --daily --all --live

# Test a specific single URL in dry-run mode
node scripts/test-indexing.js --dry-run --url https://nytsolvers.com/wordle-hints

# Test URL removal (URL_DELETED)
node scripts/test-indexing.js --dry-run --type URL_DELETED --url https://nytsolvers.com/old-page

# Check metadata indexing status for a URL
node scripts/test-indexing.js --dry-run --status https://nytsolvers.com/wordle-hints

# Live submission to Google (Requires Search Console permissions)
node scripts/test-indexing.js --live --url https://nytsolvers.com/wordle-hints

# Batch live submission with 500ms pacing
node scripts/test-indexing.js --live https://nytsolvers.com/wordle-hints https://nytsolvers.com/connections-hints
```

---

## 3. Programmatic Usage

### Single URL Notification
```javascript
import { publishUrlNotification } from './src/services/indexingService.js';

// Publish URL update
const result = await publishUrlNotification('https://nytsolvers.com/wordle-hints', 'URL_UPDATED');
if (result.success) {
  console.log('Successfully notified Google:', result.data);
} else {
  console.error(`Failed (${result.status}):`, result.error);
}

// Publish URL deletion
await publishUrlNotification('https://nytsolvers.com/old-url', 'URL_DELETED');

// Safe dry run
await publishUrlNotification('https://nytsolvers.com/wordle-hints', 'URL_UPDATED', { dryRun: true });
```

### Batch URL Notification (with Rate-Limit Pacing)
```javascript
import { batchPublishUrls } from './src/services/indexingService.js';

const urls = [
  'https://nytsolvers.com/wordle-hints',
  'https://nytsolvers.com/connections-hints',
  'https://nytsolvers.com/nyt-strands-hints',
];

// Sequential requests spaced 500ms apart
const summary = await batchPublishUrls(urls, 500);
console.log(`Success: ${summary.successful}, Failed: ${summary.failed}`);
```

### Class Instance Usage
```javascript
import { GoogleIndexingService } from './src/services/indexingService.js';

const indexing = new GoogleIndexingService({
  keyPath: './service_account.json', // optional, auto-detected
  dryRun: false,
});

await indexing.publishUrl('https://nytsolvers.com/wordle-hints');
await indexing.publishBatch(['https://nytsolvers.com/wordle-hints', 'https://nytsolvers.com/connections-hints'], 500);
```

---

## 4. Test Suite

Run the full automated test suite (19 unit & integration tests covering authentication, mocking, batching, quota limits, and error handling):

```bash
npm test
```
