@nitpicker/report-google-sheets 0.4.2 → 0.4.4

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 (37) hide show
  1. package/package.json +7 -4
  2. package/CHANGELOG.md +0 -16
  3. package/src/__tests__/api/create-sheets.api.ts +0 -234
  4. package/src/__tests__/api/helpers.ts +0 -148
  5. package/src/__tests__/api/sheets.api.ts +0 -217
  6. package/src/archive.ts +0 -29
  7. package/src/data/add-to-summary.ts +0 -10
  8. package/src/data/create-discrepancies.ts +0 -81
  9. package/src/data/create-image-list.ts +0 -74
  10. package/src/data/create-links.ts +0 -134
  11. package/src/data/create-page-list.ts +0 -472
  12. package/src/data/create-referrers-relational-table.ts +0 -115
  13. package/src/data/create-resources-relational-table.ts +0 -104
  14. package/src/data/create-resources.ts +0 -51
  15. package/src/data/create-violations.spec.ts +0 -95
  16. package/src/data/create-violations.ts +0 -47
  17. package/src/debug.ts +0 -7
  18. package/src/index.ts +0 -1
  19. package/src/load-config.spec.ts +0 -37
  20. package/src/load-config.ts +0 -17
  21. package/src/report.ts +0 -231
  22. package/src/reports/get-plugin-reports.spec.ts +0 -42
  23. package/src/reports/get-plugin-reports.ts +0 -24
  24. package/src/sheets/create-cell-data.ts +0 -1
  25. package/src/sheets/create-sheets.ts +0 -523
  26. package/src/sheets/default-cell-format.spec.ts +0 -13
  27. package/src/sheets/default-cell-format.ts +0 -8
  28. package/src/sheets/format.spec.ts +0 -17
  29. package/src/sheets/format.ts +0 -14
  30. package/src/sheets/types.ts +0 -106
  31. package/src/types.ts +0 -11
  32. package/src/utils/has-prop-filter.spec.ts +0 -25
  33. package/src/utils/has-prop-filter.ts +0 -21
  34. package/src/utils/non-null-filter.spec.ts +0 -27
  35. package/src/utils/non-null-filter.ts +0 -15
  36. package/tsconfig.json +0 -11
  37. package/tsconfig.tsbuildinfo +0 -1
@@ -1,523 +0,0 @@
1
- import type { CreateSheet } from './types.js';
2
- import type { Lanes } from '@d-zero/dealer';
3
- import type { Sheet, Sheets, Cell } from '@d-zero/google-sheets';
4
- import type { Archive, Page } from '@nitpicker/crawler';
5
- import type { Report } from '@nitpicker/types';
6
-
7
- import c from 'ansi-colors';
8
-
9
- import { sheetLog } from '../debug.js';
10
- import { hasPropFilter } from '../utils/has-prop-filter.js';
11
-
12
- const SEND_CHUNK_SIZE = 2500;
13
-
14
- /**
15
- * 行データを Google Sheets に送信する。行数が {@link SEND_CHUNK_SIZE} を超える場合は
16
- * チャンク分割して逐次送信する。
17
- * @param sheet
18
- * @param rows
19
- * @param name
20
- * @param lanes
21
- * @param laneId
22
- * @param onProgress 送信の進捗を 0.0〜1.0 の fraction で通知するコールバック。
23
- * Phase 2/3 のヘッダー加重平均に送信進捗を反映させるために使用する。
24
- */
25
- async function sendRowsInChunks(
26
- sheet: Sheet,
27
- rows: Cell[][],
28
- name: string,
29
- lanes: Lanes | undefined,
30
- laneId: number,
31
- onProgress?: (fraction: number) => void,
32
- ) {
33
- if (rows.length <= SEND_CHUNK_SIZE) {
34
- lanes?.update(laneId, `${name}: Sending ${rows.length} rows%dots%`);
35
- onProgress?.(1);
36
- await sheet.addRowData(rows, true);
37
- } else {
38
- let sent = 0;
39
- for (let i = 0; i < rows.length; i += SEND_CHUNK_SIZE) {
40
- const chunk = rows.slice(i, i + SEND_CHUNK_SIZE);
41
- sent += chunk.length;
42
- const pct = Math.round((sent / rows.length) * 100);
43
- lanes?.update(
44
- laneId,
45
- `${name}: Sending rows ${sent}/${rows.length} (${pct}%)%dots%`,
46
- );
47
- onProgress?.(sent / rows.length);
48
- await sheet.addRowData(chunk, true);
49
- }
50
- }
51
- }
52
-
53
- /**
54
- * Parameters for {@link createSheets}.
55
- */
56
- export interface CreateSheetsParams {
57
- /** Google Sheets API ラッパー */
58
- readonly sheets: Sheets;
59
- /** クロール結果のアーカイブ */
60
- readonly archive: Archive;
61
- /** 監査プラグインのレポート配列 */
62
- readonly reports: Report[];
63
- /** getPagesWithRefs のバッチサイズ(デフォルト 100,000) */
64
- readonly limit: number;
65
- /** シート設定のファクトリ関数配列 */
66
- readonly createSheetList: CreateSheet[];
67
- /** Lanes インスタンスを含むオプション */
68
- readonly options?: {
69
- /** Lanes instance for terminal progress display. */
70
- readonly lanes: Lanes;
71
- };
72
- }
73
-
74
- /**
75
- * Google Sheets にシートを作成し、データを投入してフォーマットする。
76
- *
77
- * ## 処理フェーズ
78
- *
79
- * 5つのフェーズで構成され、Phase 2+3 と Phase 4 は並列実行される:
80
- *
81
- * ```
82
- * Phase 1 (Creating sheets)
83
- * → Phase 2 (Processing pages) ─→ Phase 3 (Processing resources)
84
- * → Phase 4 (Plugin data / addRows) ← Phase 2+3 と並列
85
- * → Phase 5 (Formatting sheets)
86
- * ```
87
- *
88
- * ## Lanes 進捗表示
89
- *
90
- * ### ヘッダー: 加重平均による集計
91
- *
92
- * Phase 2/3 のヘッダーは全子タスク(シート)の進捗を加重平均で集計する。
93
- *
94
- * 以前の実装では `Math.min()` で最遅シートのページ生成進捗のみを表示していたが、
95
- * シートごとに「生成完了→行送信中」「まだ生成中」「送信完了」と異なるサブフェーズに
96
- * いる場合、ヘッダーの数値と各レーンの表示が乖離する問題があった。
97
- * 例: ヘッダー「174/755 (23%)」だが Page List は既に Sent、
98
- * Referrers RT は Sending rows 7500/13256 — ヘッダーが全体を見ていない。
99
- *
100
- * 解決策として、各シートの進捗を 0.0〜1.0 の fraction で管理し、
101
- * 全シートの平均値をヘッダーに表示する:
102
- *
103
- * - **生成フェーズ**: `(pageNum / max) * 0.5` → 0%〜50%
104
- * - **送信フェーズ**: `0.5 + (sentRows / totalRows) * 0.5` → 50%〜100%
105
- * (`sendRowsInChunks` の `onProgress` コールバック経由)
106
- * - **完了**: 1.0 (100%)
107
- *
108
- * 生成と送信を 50:50 で重み付けしている理由:
109
- * - 生成だけで 0〜100% にすると、全シートが生成完了(100%)になっても
110
- * 送信が残っている状態で「100%」と表示されてしまう
111
- * - 送信(特に大量行のチャンク送信)は Google API の
112
- * レート制限により生成と同程度の時間がかかるため、等配分が妥当
113
- *
114
- * ### レーン: フェーズ遷移に応じた状態表示
115
- *
116
- * - **アクティブフェーズ内で完了 + 将来フェーズあり**: `c.green("Sent (N rows)")`
117
- * - **アクティブフェーズ内で完了 + 全フェーズ完了**: `c.green("Done (N rows)")`
118
- * - **非アクティブ + 完了済み**: `c.dim` で同じテキスト(色だけ変化、Waiting に戻らない)
119
- * - **非アクティブ + 未着手**: `c.dim("Waiting...")`
120
- * @param params - シート作成に必要なパラメータ
121
- */
122
- export async function createSheets(params: CreateSheetsParams) {
123
- const { sheets, archive, reports, limit, createSheetList, options } = params;
124
- if (!createSheetList) {
125
- sheetLog('createSheetList is empty');
126
- return;
127
- }
128
-
129
- const lanes = options?.lanes;
130
- let lineId = 0;
131
- const sheetIds = new Map<string, number>();
132
-
133
- /**
134
- * Returns a stable numeric lane ID for the given sheet name.
135
- * IDs are assigned sequentially on first access and cached.
136
- * @param name - The sheet display name.
137
- */
138
- function getSheetId(name: string) {
139
- let id = sheetIds.get(name);
140
- if (id == null) {
141
- id = lineId++;
142
- sheetIds.set(name, id);
143
- }
144
- return id;
145
- }
146
-
147
- sheetLog('Initializing %d sheet setting(s)', createSheetList.length);
148
- const settings = await Promise.all(
149
- createSheetList.map((createSheet) => createSheet(reports)),
150
- );
151
- sheetLog(
152
- 'Sheet settings initialized: %O',
153
- settings.map((s) => s.name),
154
- );
155
-
156
- // Filter variables (early declaration for phase counting)
157
- const preEachPageRoutineList = settings.filter(hasPropFilter('preEachPage'));
158
- const eachPageRoutineList = settings.filter(hasPropFilter('eachPage'));
159
- const eachResourceRoutineList = settings.filter(hasPropFilter('eachResource'));
160
- const addRowsSettings = settings.filter((s) => s.addRows);
161
- const updateSheetSettings = settings.filter((s) => s.updateSheet);
162
- const needsPageIteration =
163
- preEachPageRoutineList.length > 0 || eachPageRoutineList.length > 0;
164
-
165
- sheetLog(
166
- 'Routines: preEachPage=%d, eachPage=%d, eachResource=%d',
167
- preEachPageRoutineList.length,
168
- eachPageRoutineList.length,
169
- eachResourceRoutineList.length,
170
- );
171
-
172
- // Phase tracking
173
- const phaseLabels: string[] = ['Creating sheets'];
174
- if (needsPageIteration) phaseLabels.push('Processing pages');
175
- if (eachResourceRoutineList.length > 0) phaseLabels.push('Processing resources');
176
- if (updateSheetSettings.length > 0) phaseLabels.push('Formatting sheets');
177
- const totalPhases = phaseLabels.length;
178
- let currentPhase = 0;
179
-
180
- /**
181
- * Advances to the next phase and updates the Lanes header line.
182
- * @param detail - Optional custom text; defaults to the phase label.
183
- */
184
- function setPhaseHeader(detail?: string) {
185
- currentPhase++;
186
- const prefix = c.bold(`[${currentPhase}/${totalPhases}]`);
187
- lanes?.header(`${prefix} ${detail ?? phaseLabels[currentPhase - 1]}`);
188
- }
189
-
190
- /**
191
- * Updates the Lanes header text without advancing the phase counter.
192
- * @param detail - New header text (e.g. progress percentage).
193
- */
194
- function updatePhaseHeader(detail: string) {
195
- const prefix = c.bold(`[${currentPhase}/${totalPhases}]`);
196
- lanes?.header(`${prefix} ${detail}`);
197
- }
198
-
199
- // Completion tracking
200
- const completionDetails = new Map<string, string>();
201
- let phase4Complete = false;
202
-
203
- /**
204
- * Returns the sequential phase numbers (2, 3, 5) that the named sheet
205
- * participates in. Used to determine whether a sheet has future work
206
- * remaining (for "Sent" vs "Done" labeling).
207
- * @param name - The sheet display name.
208
- */
209
- function getSeqPhases(name: string): number[] {
210
- const phases: number[] = [];
211
- if (
212
- eachPageRoutineList.some((s) => s.name === name) ||
213
- preEachPageRoutineList.some((s) => s.name === name)
214
- )
215
- phases.push(2);
216
- if (eachResourceRoutineList.some((s) => s.name === name)) phases.push(3);
217
- if (updateSheetSettings.some((s) => s.name === name)) phases.push(5);
218
- return phases;
219
- }
220
-
221
- /**
222
- * Marks a sheet as completed for the current phase and updates its
223
- * lane display. Shows "Sent" if future phases remain, "Done" otherwise.
224
- * @param name - The sheet display name.
225
- * @param detail - Optional detail text (e.g. row count).
226
- */
227
- function markDone(name: string, detail?: string) {
228
- completionDetails.set(name, detail ?? '');
229
- const id = getSheetId(name);
230
- const hasFuture = getSeqPhases(name).some((p) => p > currentPhase);
231
- if (hasFuture) {
232
- lanes?.update(id, c.green(`${name}: Sent${detail ? ` (${detail})` : ''}`));
233
- } else {
234
- lanes?.update(id, c.green(`${name}: Done${detail ? ` (${detail})` : ''}`));
235
- }
236
- }
237
-
238
- /**
239
- * Formats a status string for a completed sheet, choosing
240
- * "Sent" or "Done" based on whether future phases remain.
241
- * @param name - The sheet display name.
242
- */
243
- function formatSheetStatus(name: string): string {
244
- const detail = completionDetails.get(name);
245
- const hasFuture = getSeqPhases(name).some((p) => p > currentPhase);
246
- if (hasFuture) {
247
- return `${name}: Sent${detail ? ` (${detail})` : ''}`;
248
- }
249
- return `${name}: Done${detail ? ` (${detail})` : ''}`;
250
- }
251
-
252
- /**
253
- * Dims lane displays for sheets that are not active in the given phase.
254
- * Completed sheets show their status in dim color; unstarted sheets
255
- * show "Waiting...". Phase 4 (addRows) sheets are left bright while
256
- * that phase is still running since it executes in parallel.
257
- * @param seqPhaseNum - The current sequential phase number (2, 3, or 5).
258
- */
259
- function dimInactiveSheets(seqPhaseNum: number) {
260
- for (const setting of settings) {
261
- const name = setting.name;
262
- const seqPhases = getSeqPhases(name);
263
-
264
- // このフェーズでアクティブ → スキップ
265
- if (seqPhases.includes(seqPhaseNum)) continue;
266
-
267
- // Phase 4 がまだ実行中のシート → スキップ
268
- const inPhase4 = addRowsSettings.some((s) => s.name === name);
269
- if (inPhase4 && !phase4Complete) continue;
270
-
271
- const id = getSheetId(name);
272
-
273
- if (completionDetails.has(name)) {
274
- lanes?.update(id, c.dim(formatSheetStatus(name)));
275
- } else {
276
- lanes?.update(id, c.dim(`${name}: Waiting...`));
277
- }
278
- }
279
- }
280
-
281
- // Phase 1: Create sheets + set headers
282
- sheetLog('Phase 1: Creating %d sheet(s) and setting headers', settings.length);
283
- setPhaseHeader();
284
- await Promise.all(
285
- settings.map(async (setting) => {
286
- const name = setting.name;
287
- const id = getSheetId(name);
288
- sheetLog('[%s] Creating sheet via API', name);
289
- lanes?.update(id, `${name}: Creating sheet%dots%`);
290
- const sheet = await sheets.create(name);
291
- sheetLog('[%s] Setting headers', name);
292
- lanes?.update(id, `${name}: Setting headers%dots%`);
293
- const headers = await setting.createHeaders();
294
- await sheet.setHeaders(headers);
295
- sheetLog('[%s] Headers set (%d columns)', name, headers.length);
296
- lanes?.update(id, `${name}: Ready`);
297
- }),
298
- );
299
- sheetLog('Phase 1 complete');
300
-
301
- await Promise.all([
302
- (async () => {
303
- // Phase 2: Page processing (preEachPage + eachPage unified)
304
- if (needsPageIteration) {
305
- sheetLog('Phase 2: Starting page iteration');
306
- setPhaseHeader();
307
- dimInactiveSheets(2);
308
- sheetLog('Loading pages from archive (limit=%d)', limit);
309
- const sheetProgress = new Map<string, number>();
310
- for (const setting of preEachPageRoutineList) {
311
- sheetProgress.set(setting.name, 0);
312
- }
313
- for (const setting of eachPageRoutineList) {
314
- sheetProgress.set(setting.name, 0);
315
- }
316
-
317
- /**
318
- * Recalculates and displays the weighted-average progress
319
- * across all page-processing sheets for the Phase 2 header.
320
- */
321
- function updatePhase2Header() {
322
- if (sheetProgress.size === 0) return;
323
- const avg =
324
- [...sheetProgress.values()].reduce((a, b) => a + b, 0) / sheetProgress.size;
325
- const pct = Math.round(avg * 100);
326
- updatePhaseHeader(`Processing pages (${pct}%)`);
327
- }
328
-
329
- await archive.getPagesWithRefs(limit, async (pages, offset, max) => {
330
- sheetLog(
331
- 'Batch received: %d pages (offset=%d, total=%d)',
332
- pages.length,
333
- offset,
334
- max,
335
- );
336
- updatePhase2Header();
337
-
338
- // preEachPage first
339
- if (preEachPageRoutineList.length > 0) {
340
- sheetLog(
341
- 'Running preEachPage for %d routine(s)',
342
- preEachPageRoutineList.length,
343
- );
344
- await Promise.all(
345
- preEachPageRoutineList.map(async (setting) => {
346
- const id = getSheetId(setting.name);
347
- let num = 1;
348
- let prevPage: Page | null = null;
349
- for (const page of pages) {
350
- const pageNum = offset + num;
351
- lanes?.update(
352
- id,
353
- `${setting.name}: Pre-processing ${pageNum}/${max}%dots%`,
354
- );
355
- sheetProgress.set(setting.name, pageNum / max);
356
- updatePhase2Header();
357
- await setting.preEachPage(page, pageNum, max, prevPage);
358
- prevPage = page;
359
- num++;
360
- }
361
- }),
362
- );
363
- sheetLog('preEachPage complete for batch (offset=%d)', offset);
364
- }
365
-
366
- // eachPage second
367
- if (eachPageRoutineList.length > 0) {
368
- sheetLog('Running eachPage for %d routine(s)', eachPageRoutineList.length);
369
- await Promise.all(
370
- eachPageRoutineList.map(async (setting) => {
371
- const id = getSheetId(setting.name);
372
- let num = 1;
373
- const rows: Cell[][] = [];
374
- const name = setting.name;
375
- let prevPage: Page | null = null;
376
- for (const page of pages) {
377
- const pageNum = offset + num;
378
- lanes?.update(id, `${name}: Generating row ${pageNum}/${max}%dots%`);
379
- sheetProgress.set(name, (pageNum / max) * 0.5);
380
- updatePhase2Header();
381
- const data = await setting.eachPage(page, pageNum, max, prevPage);
382
- prevPage = page;
383
- if (!data) {
384
- num++;
385
- continue;
386
- }
387
- for (const row of data) {
388
- rows.push(row);
389
- }
390
- num++;
391
- }
392
- sheetLog(
393
- '[%s] Sending %d rows (offset=%d/%d)',
394
- name,
395
- rows.length,
396
- offset,
397
- max,
398
- );
399
- const sheet = await sheets.create(name);
400
- await sendRowsInChunks(sheet, rows, name, lanes, id, (fraction) => {
401
- sheetProgress.set(name, 0.5 + fraction * 0.5);
402
- updatePhase2Header();
403
- });
404
- sheetLog('[%s] Send complete (offset=%d)', name, offset);
405
- sheetProgress.set(name, 1);
406
- updatePhase2Header();
407
- markDone(name, `${rows.length} rows`);
408
- }),
409
- );
410
- }
411
- });
412
- sheetLog('Phase 2 complete');
413
- }
414
-
415
- // Phase 3: Resource processing
416
- if (eachResourceRoutineList.length > 0) {
417
- sheetLog('Phase 3: Starting resource processing');
418
- setPhaseHeader();
419
- dimInactiveSheets(3);
420
- const resources = await archive.getResources();
421
- sheetLog('Resources loaded: %d', resources.length);
422
- const resourceProgress = new Map<string, number>();
423
- for (const setting of eachResourceRoutineList) {
424
- resourceProgress.set(setting.name, 0);
425
- }
426
-
427
- /**
428
- * Recalculates and displays the weighted-average progress
429
- * across all resource-processing sheets for the Phase 3 header.
430
- */
431
- function updatePhase3Header() {
432
- if (resourceProgress.size === 0) return;
433
- const avg =
434
- [...resourceProgress.values()].reduce((a, b) => a + b, 0) /
435
- resourceProgress.size;
436
- const pct = Math.round(avg * 100);
437
- updatePhaseHeader(`Processing resources (${pct}%)`);
438
- }
439
-
440
- await Promise.all(
441
- eachResourceRoutineList.map(async (setting) => {
442
- const id = getSheetId(setting.name);
443
- const sheet = await sheets.create(setting.name);
444
- const rows: Cell[][] = [];
445
- let i = 0;
446
- for (const resource of resources) {
447
- i++;
448
- lanes?.update(
449
- id,
450
- `${setting.name}: Processing ${i}/${resources.length}%dots%`,
451
- );
452
- resourceProgress.set(setting.name, (i / resources.length) * 0.5);
453
- updatePhase3Header();
454
- const resourceData = await setting.eachResource(resource);
455
- if (resourceData) {
456
- rows.push(...resourceData);
457
- }
458
- }
459
- sheetLog('[%s] Sending %d resource rows', setting.name, rows.length);
460
- await sendRowsInChunks(sheet, rows, setting.name, lanes, id, (fraction) => {
461
- resourceProgress.set(setting.name, 0.5 + fraction * 0.5);
462
- updatePhase3Header();
463
- });
464
- sheetLog('[%s] Resource send complete', setting.name);
465
- resourceProgress.set(setting.name, 1);
466
- updatePhase3Header();
467
- markDone(setting.name, `${rows.length} rows`);
468
- }),
469
- );
470
- sheetLog('Phase 3 complete');
471
- }
472
- })(),
473
- (async () => {
474
- // Phase 4: Plugin data (addRows)
475
- if (addRowsSettings.length > 0) {
476
- sheetLog('Phase 4: Processing %d addRows routine(s)', addRowsSettings.length);
477
- await Promise.all(
478
- addRowsSettings.map(async (setting) => {
479
- const name = setting.name;
480
- const id = getSheetId(name);
481
- sheetLog('[%s] Creating plugin data', name);
482
- lanes?.update(id, `${name}: Writing plugin data%dots%`);
483
- const data = await setting.addRows!();
484
- if (!data) {
485
- sheetLog('[%s] Plugin data is empty', name);
486
- return;
487
- }
488
- const sheet = await sheets.create(name);
489
- sheetLog('[%s] Sending %d rows', name, data.length);
490
- await sendRowsInChunks(sheet, data, name, lanes, id);
491
- sheetLog('[%s] Plugin data send complete', name);
492
- if (!completionDetails.has(name)) {
493
- markDone(name, `${data.length} rows`);
494
- }
495
- }),
496
- );
497
- phase4Complete = true;
498
- sheetLog('Phase 4 complete');
499
- }
500
- })(),
501
- ]);
502
-
503
- // Phase 5: Formatting
504
- if (updateSheetSettings.length > 0) {
505
- sheetLog('Phase 5: Formatting %d sheet(s)', updateSheetSettings.length);
506
- setPhaseHeader();
507
- dimInactiveSheets(5);
508
- await Promise.all(
509
- updateSheetSettings.map(async (setting) => {
510
- const name = setting.name;
511
- const id = getSheetId(name);
512
- sheetLog('[%s] Applying formatting', name);
513
- lanes?.update(id, `${name}: Applying formatting%dots%`);
514
- const sheet = await sheets.create(name);
515
- await setting.updateSheet!(sheet);
516
- await sheet.overwriteHeaderFormat();
517
- sheetLog('[%s] Formatting complete', name);
518
- markDone(name);
519
- }),
520
- );
521
- sheetLog('Phase 5 complete');
522
- }
523
- }
@@ -1,13 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- import { defaultCellFormat } from './default-cell-format.js';
4
-
5
- describe('defaultCellFormat', () => {
6
- it('has OVERFLOW_CELL wrapStrategy', () => {
7
- expect(defaultCellFormat.wrapStrategy).toBe('OVERFLOW_CELL');
8
- });
9
-
10
- it('is frozen', () => {
11
- expect(Object.isFrozen(defaultCellFormat)).toBe(true);
12
- });
13
- });
@@ -1,8 +0,0 @@
1
- /**
2
- * デフォルトのセルフォーマット設定。
3
- * テキストの折り返し戦略を `OVERFLOW_CELL` に設定し、
4
- * セル内容がセル幅を超える場合に隣接セルへ溢れるようにする。
5
- */
6
- export const defaultCellFormat = Object.freeze({
7
- wrapStrategy: 'OVERFLOW_CELL' as const,
8
- });
@@ -1,17 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- import { booleanFormatError } from './format.js';
4
-
5
- describe('booleanFormatError', () => {
6
- it('has red background color', () => {
7
- expect(booleanFormatError.backgroundColor).toEqual({ red: 0.9 });
8
- });
9
-
10
- it('has white text color', () => {
11
- expect(booleanFormatError.textFormat?.foregroundColor).toEqual({
12
- red: 1,
13
- green: 1,
14
- blue: 1,
15
- });
16
- });
17
- });
@@ -1,14 +0,0 @@
1
- import type { sheets_v4 } from 'googleapis';
2
-
3
- export const booleanFormatError: sheets_v4.Schema$CellFormat = {
4
- backgroundColor: {
5
- red: 0.9,
6
- },
7
- textFormat: {
8
- foregroundColor: {
9
- red: 1,
10
- green: 1,
11
- blue: 1,
12
- },
13
- },
14
- };
@@ -1,106 +0,0 @@
1
- import type { Cell, Sheet } from '@d-zero/google-sheets';
2
- import type { Page, ArchiveResource as Resource } from '@nitpicker/crawler';
3
- import type { Report } from '@nitpicker/types';
4
- import type { sheets_v4 } from 'googleapis';
5
-
6
- /** A value that may be synchronous or wrapped in a Promise. */
7
- export type Promiseable<T> = Promise<T> | T;
8
-
9
- /** A single header cell value (plain string). */
10
- export type HeaderCell = string;
11
-
12
- /** Google Sheets text format options (font, color, link, etc.). */
13
- export type createCellTextFormat = sheets_v4.Schema$TextFormat;
14
-
15
- /**
16
- * Factory function that produces a {@link CreateSheetSetting} for one sheet.
17
- *
18
- * Receives the analyze plugin reports so that sheets like "Violations" or
19
- * "Discrepancies" can incorporate plugin data alongside crawler data.
20
- * @example
21
- * ```ts
22
- * const createMySheet: CreateSheet = (reports) => ({
23
- * name: 'My Sheet',
24
- * createHeaders: () => ['URL', 'Title'],
25
- * eachPage: (page) => [[
26
- * createCellData({ value: page.url.href }, defaultCellFormat),
27
- * createCellData({ value: page.title }, defaultCellFormat),
28
- * ]],
29
- * });
30
- * ```
31
- */
32
- export type CreateSheet = (reports: Report[]) => Promiseable<CreateSheetSetting>;
33
-
34
- /**
35
- * Configuration for a single Google Sheet tab within the report.
36
- *
37
- * The `createSheets` pipeline calls these hooks in a defined order:
38
- *
39
- * 1. `createHeaders()` - Called once to set the header row.
40
- * 2. `preEachPage()` - (Phase 2) Called per page for pre-processing
41
- * (e.g. accumulating state). No row data is returned.
42
- * 3. `eachPage()` - (Phase 2) Called per page to generate row data.
43
- * Returns `Cell[][]` (one or more rows per page), `null` to skip,
44
- * or `void` if this sheet does not use page data.
45
- * 4. `eachResource()` - (Phase 3) Called per network resource.
46
- * Same return semantics as `eachPage`.
47
- * 5. `addRows()` - (Phase 4) Called once to add plugin-derived data
48
- * that does not come from page/resource iteration (e.g. Violations).
49
- * Runs in parallel with Phases 2-3.
50
- * 6. `updateSheet()` - (Phase 5) Called once after all data is written,
51
- * for formatting (frozen rows, conditional formatting, etc.).
52
- */
53
- export interface CreateSheetSetting {
54
- /** Display name of the sheet tab in Google Sheets. */
55
- name: string;
56
- /** Returns the header row cell values. */
57
- createHeaders: () => Promiseable<HeaderCell[]>;
58
- /**
59
- * Pre-processing hook called for each page before `eachPage`.
60
- * Useful for accumulating state (e.g. index titles, referrer maps)
61
- * that subsequent `eachPage` calls depend on.
62
- * @param page - The current page object.
63
- * @param num - 1-based page number within the total.
64
- * @param total - Total number of pages.
65
- * @param prevPage - The previous page in iteration order, or `null` for the first.
66
- */
67
- preEachPage?: (
68
- page: Page,
69
- num: number,
70
- total: number,
71
- prevPage: Page | null,
72
- ) => Promiseable<void>;
73
- /**
74
- * Generates row data for a single page.
75
- * @param page - The current page object.
76
- * @param num - 1-based page number within the total.
77
- * @param total - Total number of pages.
78
- * @param prevPage - The previous page, or `null` for the first.
79
- * @returns Row data (one or more rows), `null`/`void` to skip.
80
- */
81
- eachPage?: (
82
- page: Page,
83
- num: number,
84
- total: number,
85
- prevPage: Page | null,
86
- ) => Promiseable<Cell[][] | null | void>;
87
- /**
88
- * Generates row data for a single network resource.
89
- * @param resource - The resource record from the archive.
90
- * @returns Row data, `null`/`void` to skip.
91
- */
92
- eachResource?: (resource: Resource) => Promiseable<Cell[][] | null | void>;
93
- /**
94
- * Generates additional rows from plugin report data.
95
- * Called once after the factory, not per-page. Runs in parallel
96
- * with page/resource phases so it does not block them.
97
- */
98
- addRows?: () => Promiseable<Cell[][] | null | void>;
99
- /**
100
- * Post-data formatting hook. Called after all rows have been sent.
101
- * Typically used for freezing rows/columns, conditional formatting,
102
- * and hiding unused columns.
103
- * @param sheet - The Google Sheets API wrapper for this tab.
104
- */
105
- updateSheet?: (sheet: Sheet) => Promiseable<void>;
106
- }
package/src/types.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Makes the specified properties of `T` required while keeping
3
- * the rest unchanged. Used by `hasPropFilter` to narrow types
4
- * where an optional callback property is known to be defined.
5
- * @example
6
- * ```ts
7
- * type WithEachPage = RequiredProp<CreateSheetSetting, 'eachPage'>;
8
- * // eachPage is now non-optional
9
- * ```
10
- */
11
- export type RequiredProp<T, P extends keyof T> = T & Required<Pick<T, P>>;