@cesarandreslopez/occ 0.3.1 → 0.4.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/README.md +130 -4
- package/dist/src/cli.js +8 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/code/build.js +22 -9
- package/dist/src/code/build.js.map +1 -1
- package/dist/src/code/command.js +2 -2
- package/dist/src/code/command.js.map +1 -1
- package/dist/src/code/output.js +12 -3
- package/dist/src/code/output.js.map +1 -1
- package/dist/src/code/parsers.js +52 -10
- package/dist/src/code/parsers.js.map +1 -1
- package/dist/src/code/query.js +82 -22
- package/dist/src/code/query.js.map +1 -1
- package/dist/src/code/types.d.ts +5 -3
- package/dist/src/doc/command.d.ts +2 -0
- package/dist/src/doc/command.js +54 -0
- package/dist/src/doc/command.js.map +1 -0
- package/dist/src/doc/inspect-docx.d.ts +14 -0
- package/dist/src/doc/inspect-docx.js +188 -0
- package/dist/src/doc/inspect-docx.js.map +1 -0
- package/dist/src/doc/inspect-odt.d.ts +8 -0
- package/dist/src/doc/inspect-odt.js +91 -0
- package/dist/src/doc/inspect-odt.js.map +1 -0
- package/dist/src/doc/inspect-pdf.d.ts +8 -0
- package/dist/src/doc/inspect-pdf.js +82 -0
- package/dist/src/doc/inspect-pdf.js.map +1 -0
- package/dist/src/doc/inspect.d.ts +2 -0
- package/dist/src/doc/inspect.js +70 -0
- package/dist/src/doc/inspect.js.map +1 -0
- package/dist/src/doc/output.d.ts +3 -0
- package/dist/src/doc/output.js +82 -0
- package/dist/src/doc/output.js.map +1 -0
- package/dist/src/doc/types.d.ts +59 -0
- package/dist/src/doc/types.js +2 -0
- package/dist/src/doc/types.js.map +1 -0
- package/dist/src/inspect/shared.d.ts +18 -0
- package/dist/src/inspect/shared.js +18 -0
- package/dist/src/inspect/shared.js.map +1 -0
- package/dist/src/parsers/xlsx.js +4 -4
- package/dist/src/parsers/xlsx.js.map +1 -1
- package/dist/src/sheet/command.d.ts +2 -0
- package/dist/src/sheet/command.js +70 -0
- package/dist/src/sheet/command.js.map +1 -0
- package/dist/src/sheet/inspect.d.ts +7 -0
- package/dist/src/sheet/inspect.js +454 -0
- package/dist/src/sheet/inspect.js.map +1 -0
- package/dist/src/sheet/output.d.ts +3 -0
- package/dist/src/sheet/output.js +158 -0
- package/dist/src/sheet/output.js.map +1 -0
- package/dist/src/sheet/types.d.ts +117 -0
- package/dist/src/sheet/types.js +2 -0
- package/dist/src/sheet/types.js.map +1 -0
- package/dist/src/slide/command.d.ts +2 -0
- package/dist/src/slide/command.js +54 -0
- package/dist/src/slide/command.js.map +1 -0
- package/dist/src/slide/inspect-odp.d.ts +7 -0
- package/dist/src/slide/inspect-odp.js +105 -0
- package/dist/src/slide/inspect-odp.js.map +1 -0
- package/dist/src/slide/inspect-pptx.d.ts +11 -0
- package/dist/src/slide/inspect-pptx.js +157 -0
- package/dist/src/slide/inspect-pptx.js.map +1 -0
- package/dist/src/slide/inspect.d.ts +2 -0
- package/dist/src/slide/inspect.js +76 -0
- package/dist/src/slide/inspect.js.map +1 -0
- package/dist/src/slide/output.d.ts +3 -0
- package/dist/src/slide/output.js +92 -0
- package/dist/src/slide/output.js.map +1 -0
- package/dist/src/slide/types.d.ts +59 -0
- package/dist/src/slide/types.js +2 -0
- package/dist/src/slide/types.js.map +1 -0
- package/dist/src/table/command.d.ts +2 -0
- package/dist/src/table/command.js +68 -0
- package/dist/src/table/command.js.map +1 -0
- package/dist/src/table/inspect-docx.d.ts +2 -0
- package/dist/src/table/inspect-docx.js +108 -0
- package/dist/src/table/inspect-docx.js.map +1 -0
- package/dist/src/table/inspect-odp.d.ts +2 -0
- package/dist/src/table/inspect-odp.js +106 -0
- package/dist/src/table/inspect-odp.js.map +1 -0
- package/dist/src/table/inspect-odt.d.ts +2 -0
- package/dist/src/table/inspect-odt.js +100 -0
- package/dist/src/table/inspect-odt.js.map +1 -0
- package/dist/src/table/inspect-pptx.d.ts +2 -0
- package/dist/src/table/inspect-pptx.js +112 -0
- package/dist/src/table/inspect-pptx.js.map +1 -0
- package/dist/src/table/inspect-xlsx.d.ts +2 -0
- package/dist/src/table/inspect-xlsx.js +161 -0
- package/dist/src/table/inspect-xlsx.js.map +1 -0
- package/dist/src/table/inspect.d.ts +2 -0
- package/dist/src/table/inspect.js +63 -0
- package/dist/src/table/inspect.js.map +1 -0
- package/dist/src/table/output.d.ts +3 -0
- package/dist/src/table/output.js +93 -0
- package/dist/src/table/output.js.map +1 -0
- package/dist/src/table/types.d.ts +38 -0
- package/dist/src/table/types.js +2 -0
- package/dist/src/table/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import Table from 'cli-table3';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { formatBytes, formatNumber } from '../utils.js';
|
|
4
|
+
import { sectionHeader, stripAnsi, tableChars } from '../output/tabular.js';
|
|
5
|
+
const colors = {
|
|
6
|
+
header: (value) => chalk.bold(value),
|
|
7
|
+
key: (value) => chalk.cyan(value),
|
|
8
|
+
value: (value) => value,
|
|
9
|
+
dim: (value) => chalk.dim(value),
|
|
10
|
+
ok: (value) => chalk.green(value),
|
|
11
|
+
warn: (value) => chalk.yellow(value),
|
|
12
|
+
};
|
|
13
|
+
const noColor = Object.fromEntries(Object.keys(colors).map(key => [key, (value) => value]));
|
|
14
|
+
function palette(ci = false) {
|
|
15
|
+
return ci ? noColor : colors;
|
|
16
|
+
}
|
|
17
|
+
export function formatSheetPayloadJson(payload) {
|
|
18
|
+
return JSON.stringify(payload, null, 2);
|
|
19
|
+
}
|
|
20
|
+
export function formatSheetInspection(result, ci = false) {
|
|
21
|
+
const c = palette(ci);
|
|
22
|
+
const sections = [];
|
|
23
|
+
const overviewLines = [
|
|
24
|
+
`${c.key('File')}: ${c.value(result.workbook.file)}`,
|
|
25
|
+
`${c.key('Format')}: ${c.value('XLSX')}`,
|
|
26
|
+
`${c.key('Size')}: ${c.value(formatBytes(result.workbook.size))}`,
|
|
27
|
+
`${c.key('Sheets')}: ${c.value(`${formatNumber(result.workbook.sheetCount)} total (${formatNumber(result.workbook.visibleSheetCount)} visible, ${formatNumber(result.workbook.hiddenSheetCount)} hidden, ${formatNumber(result.workbook.veryHiddenSheetCount)} very hidden)`)}`,
|
|
28
|
+
];
|
|
29
|
+
const propEntries = Object.entries(result.workbook.properties).filter(([, value]) => value);
|
|
30
|
+
if (propEntries.length > 0) {
|
|
31
|
+
overviewLines.push(`${c.key('Properties')}: ${propEntries.map(([key, value]) => `${key}=${value}`).join(' | ')}`);
|
|
32
|
+
}
|
|
33
|
+
if (result.workbook.definedNames.length > 0) {
|
|
34
|
+
overviewLines.push(`${c.key('Workbook Names')}: ${result.workbook.definedNames.map(name => `${name.name}=${name.ref}`).join(' | ')}`);
|
|
35
|
+
}
|
|
36
|
+
const riskFlags = Object.entries(result.workbook.riskFlags).filter(([, enabled]) => enabled).map(([key]) => key);
|
|
37
|
+
overviewLines.push(`${c.key('Risk Flags')}: ${riskFlags.length > 0 ? c.warn(riskFlags.join(', ')) : c.ok('none')}`);
|
|
38
|
+
sections.push(overviewLines.join('\n'));
|
|
39
|
+
if (result.sheets.length > 0) {
|
|
40
|
+
const inventory = new Table({
|
|
41
|
+
head: [
|
|
42
|
+
c.key('Sheet'),
|
|
43
|
+
c.key('Visibility'),
|
|
44
|
+
c.key('Range'),
|
|
45
|
+
c.key('Rows'),
|
|
46
|
+
c.key('Cols'),
|
|
47
|
+
c.key('Non-Empty'),
|
|
48
|
+
c.key('Formulae'),
|
|
49
|
+
c.key('Links'),
|
|
50
|
+
c.key('Comments'),
|
|
51
|
+
c.key('Tokens'),
|
|
52
|
+
],
|
|
53
|
+
chars: tableChars(ci),
|
|
54
|
+
style: { head: [], border: [] },
|
|
55
|
+
colAligns: ['left', 'left', 'left', 'right', 'right', 'right', 'right', 'right', 'right', 'right'],
|
|
56
|
+
});
|
|
57
|
+
for (const sheet of result.sheets) {
|
|
58
|
+
inventory.push([
|
|
59
|
+
`${sheet.index}. ${sheet.name}`,
|
|
60
|
+
sheet.visibility,
|
|
61
|
+
sheet.usedRange ?? '',
|
|
62
|
+
formatNumber(sheet.totalRows),
|
|
63
|
+
formatNumber(sheet.totalCols),
|
|
64
|
+
formatNumber(sheet.nonEmptyCellCount),
|
|
65
|
+
formatNumber(sheet.formulaCellCount),
|
|
66
|
+
formatNumber(sheet.hyperlinkCellCount),
|
|
67
|
+
formatNumber(sheet.commentCellCount),
|
|
68
|
+
formatNumber(sheet.fullTokenEstimate),
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
const rendered = inventory.toString();
|
|
72
|
+
const width = stripAnsi(rendered.split('\n')[0] ?? '').length;
|
|
73
|
+
sections.push(`${c.header(sectionHeader('Sheet Inventory', width, ci))}\n${rendered}`);
|
|
74
|
+
}
|
|
75
|
+
for (const sheet of result.sheets) {
|
|
76
|
+
sections.push(formatSheetProfile(sheet, ci));
|
|
77
|
+
}
|
|
78
|
+
return sections.join('\n\n') + '\n';
|
|
79
|
+
}
|
|
80
|
+
function formatSheetProfile(sheet, ci = false) {
|
|
81
|
+
const c = palette(ci);
|
|
82
|
+
const lines = [];
|
|
83
|
+
const title = `Sheet: ${sheet.name} (${sheet.visibility})`;
|
|
84
|
+
const summary = [
|
|
85
|
+
`${c.key('Range')}: ${sheet.usedRange ?? '(empty)'}`,
|
|
86
|
+
`${c.key('Grid')}: ${formatNumber(sheet.totalRows)} rows x ${formatNumber(sheet.totalCols)} cols (${formatNumber(sheet.rectangularRangeCellCount)} cells)`,
|
|
87
|
+
`${c.key('Signals')}: ${formatNumber(sheet.formulaCellCount)} formulae | ${formatNumber(sheet.commentCellCount)} comments | ${formatNumber(sheet.hyperlinkCellCount)} hyperlinks | ${formatNumber(sheet.mergedRangeCount)} merges`,
|
|
88
|
+
`${c.key('Layout')}: ${formatNumber(sheet.hiddenRowCount)} hidden rows | ${formatNumber(sheet.hiddenColumnCount)} hidden cols | autofilter=${sheet.autoFilterRef ?? 'none'} | protected=${sheet.protected ? 'yes' : 'no'}`,
|
|
89
|
+
`${c.key('Header')}: ${sheet.headerSelection.rowNumber ?? 'none'} (${sheet.headerSelection.mode})`,
|
|
90
|
+
`${c.key('Token Estimate')}: sample=${formatNumber(sheet.sampleTokenEstimate)} | full=${formatNumber(sheet.fullTokenEstimate)}`,
|
|
91
|
+
];
|
|
92
|
+
if (sheet.definedNames.length > 0) {
|
|
93
|
+
summary.push(`${c.key('Defined Names')}: ${sheet.definedNames.map(name => `${name.name}=${name.ref}`).join(' | ')}`);
|
|
94
|
+
}
|
|
95
|
+
lines.push(summary.join('\n'));
|
|
96
|
+
if (sheet.schema.columns.length > 0) {
|
|
97
|
+
lines.push(formatSchemaTable(sheet.schema.columns, sheet.schema.truncated, ci));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
lines.push(c.dim('No schema columns found.'));
|
|
101
|
+
}
|
|
102
|
+
if (sheet.sample.rows.length > 0) {
|
|
103
|
+
lines.push(formatSampleTable(sheet.sample.rows, sheet.sample.truncatedRows, sheet.sample.truncatedColumns, ci));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
lines.push(c.dim('No sample rows found.'));
|
|
107
|
+
}
|
|
108
|
+
const width = Math.max(...lines.map(line => stripAnsi(line.split('\n')[0] ?? '').length), title.length, 40);
|
|
109
|
+
return `${c.header(sectionHeader(title, width, ci))}\n${lines.join('\n\n')}`;
|
|
110
|
+
}
|
|
111
|
+
function formatSchemaTable(columns, truncated, ci = false) {
|
|
112
|
+
const c = palette(ci);
|
|
113
|
+
const table = new Table({
|
|
114
|
+
head: [
|
|
115
|
+
c.key('Col'),
|
|
116
|
+
c.key('Name'),
|
|
117
|
+
c.key('Type'),
|
|
118
|
+
c.key('Non-Empty'),
|
|
119
|
+
c.key('Coverage'),
|
|
120
|
+
c.key('Examples'),
|
|
121
|
+
],
|
|
122
|
+
chars: tableChars(ci),
|
|
123
|
+
style: { head: [], border: [] },
|
|
124
|
+
colAligns: ['left', 'left', 'left', 'right', 'right', 'left'],
|
|
125
|
+
});
|
|
126
|
+
for (const column of columns) {
|
|
127
|
+
table.push([
|
|
128
|
+
`${column.letter} (${column.index})`,
|
|
129
|
+
column.name,
|
|
130
|
+
column.dominantType,
|
|
131
|
+
formatNumber(column.nonEmptyCount),
|
|
132
|
+
`${Math.round(column.nonEmptyRatio * 100)}%`,
|
|
133
|
+
column.examples.join(' | '),
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
const suffix = truncated ? '\n(truncated columns)' : '';
|
|
137
|
+
return `${palette(ci).header('Schema')}\n${table.toString()}${suffix}`;
|
|
138
|
+
}
|
|
139
|
+
function formatSampleTable(rows, truncatedRows, truncatedColumns, ci = false) {
|
|
140
|
+
const c = palette(ci);
|
|
141
|
+
const columnNames = Object.keys(rows[0]?.values ?? {});
|
|
142
|
+
const table = new Table({
|
|
143
|
+
head: [c.key('Row'), ...columnNames.map(name => c.key(name))],
|
|
144
|
+
chars: tableChars(ci),
|
|
145
|
+
style: { head: [], border: [] },
|
|
146
|
+
});
|
|
147
|
+
for (const row of rows) {
|
|
148
|
+
table.push([String(row.rowNumber), ...columnNames.map(name => row.values[name] ?? '')]);
|
|
149
|
+
}
|
|
150
|
+
const notes = [];
|
|
151
|
+
if (truncatedRows)
|
|
152
|
+
notes.push('truncated rows');
|
|
153
|
+
if (truncatedColumns)
|
|
154
|
+
notes.push('truncated columns');
|
|
155
|
+
const suffix = notes.length > 0 ? `\n(${notes.join(', ')})` : '';
|
|
156
|
+
return `${palette(ci).header('Sample')}\n${table.toString()}${suffix}`;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/sheet/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export type SheetVisibility = 'visible' | 'hidden' | 'very_hidden';
|
|
2
|
+
export type HeaderSelectionMode = 'auto' | 'none' | 'explicit';
|
|
3
|
+
export type ColumnValueType = 'string' | 'number' | 'boolean' | 'date' | 'error' | 'blank_stub' | 'unknown';
|
|
4
|
+
export interface InspectSheetOptions {
|
|
5
|
+
sheet?: string;
|
|
6
|
+
sampleRows: number;
|
|
7
|
+
headerRow: 'auto' | 'none' | number;
|
|
8
|
+
maxColumns: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DefinedNameInfo {
|
|
11
|
+
name: string;
|
|
12
|
+
ref: string;
|
|
13
|
+
scope: 'workbook' | 'sheet';
|
|
14
|
+
sheetIndex?: number;
|
|
15
|
+
sheetName?: string;
|
|
16
|
+
external: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface WorkbookProperties {
|
|
19
|
+
title?: string;
|
|
20
|
+
subject?: string;
|
|
21
|
+
author?: string;
|
|
22
|
+
company?: string;
|
|
23
|
+
manager?: string;
|
|
24
|
+
createdDate?: string;
|
|
25
|
+
modifiedDate?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface WorkbookRiskFlags {
|
|
28
|
+
hiddenSheets: boolean;
|
|
29
|
+
formulas: boolean;
|
|
30
|
+
comments: boolean;
|
|
31
|
+
hyperlinks: boolean;
|
|
32
|
+
mergedCells: boolean;
|
|
33
|
+
protectedSheets: boolean;
|
|
34
|
+
externalFormulaRefs: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface CellTypeCounts {
|
|
37
|
+
string: number;
|
|
38
|
+
number: number;
|
|
39
|
+
boolean: number;
|
|
40
|
+
date: number;
|
|
41
|
+
error: number;
|
|
42
|
+
blankStub: number;
|
|
43
|
+
}
|
|
44
|
+
export interface ColumnProfile {
|
|
45
|
+
index: number;
|
|
46
|
+
letter: string;
|
|
47
|
+
name: string;
|
|
48
|
+
dominantType: ColumnValueType;
|
|
49
|
+
nonEmptyCount: number;
|
|
50
|
+
nonEmptyRatio: number;
|
|
51
|
+
examples: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface SampleRow {
|
|
54
|
+
rowNumber: number;
|
|
55
|
+
values: Record<string, string>;
|
|
56
|
+
}
|
|
57
|
+
export interface SheetProfile {
|
|
58
|
+
index: number;
|
|
59
|
+
name: string;
|
|
60
|
+
visibility: SheetVisibility;
|
|
61
|
+
usedRange: string | null;
|
|
62
|
+
totalRows: number;
|
|
63
|
+
totalCols: number;
|
|
64
|
+
rectangularRangeCellCount: number;
|
|
65
|
+
nonEmptyCellCount: number;
|
|
66
|
+
cellTypeCounts: CellTypeCounts;
|
|
67
|
+
formulaCellCount: number;
|
|
68
|
+
commentCellCount: number;
|
|
69
|
+
hyperlinkCellCount: number;
|
|
70
|
+
mergedRangeCount: number;
|
|
71
|
+
hiddenRowCount: number;
|
|
72
|
+
hiddenColumnCount: number;
|
|
73
|
+
autoFilterRef: string | null;
|
|
74
|
+
protected: boolean;
|
|
75
|
+
definedNames: DefinedNameInfo[];
|
|
76
|
+
externalFormulaRefCount: number;
|
|
77
|
+
headerSelection: {
|
|
78
|
+
requested: 'auto' | 'none' | number;
|
|
79
|
+
mode: HeaderSelectionMode;
|
|
80
|
+
rowNumber: number | null;
|
|
81
|
+
};
|
|
82
|
+
schema: {
|
|
83
|
+
truncated: boolean;
|
|
84
|
+
columns: ColumnProfile[];
|
|
85
|
+
};
|
|
86
|
+
sample: {
|
|
87
|
+
truncatedRows: boolean;
|
|
88
|
+
truncatedColumns: boolean;
|
|
89
|
+
rows: SampleRow[];
|
|
90
|
+
};
|
|
91
|
+
sampleTokenEstimate: number;
|
|
92
|
+
fullTokenEstimate: number;
|
|
93
|
+
estimateMethod: 'full_scan';
|
|
94
|
+
}
|
|
95
|
+
export interface WorkbookInspection {
|
|
96
|
+
file: string;
|
|
97
|
+
format: 'xlsx';
|
|
98
|
+
size: number;
|
|
99
|
+
properties: WorkbookProperties;
|
|
100
|
+
customPropertyCount: number;
|
|
101
|
+
customProperties: Record<string, unknown>;
|
|
102
|
+
sheetCount: number;
|
|
103
|
+
visibleSheetCount: number;
|
|
104
|
+
hiddenSheetCount: number;
|
|
105
|
+
veryHiddenSheetCount: number;
|
|
106
|
+
definedNames: DefinedNameInfo[];
|
|
107
|
+
riskFlags: WorkbookRiskFlags;
|
|
108
|
+
}
|
|
109
|
+
export interface SheetInspectionResult {
|
|
110
|
+
workbook: WorkbookInspection;
|
|
111
|
+
sheets: SheetProfile[];
|
|
112
|
+
}
|
|
113
|
+
export interface SheetInspectPayload {
|
|
114
|
+
file: string;
|
|
115
|
+
query: Record<string, unknown>;
|
|
116
|
+
results: SheetInspectionResult;
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/sheet/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { inspectPresentation } from './inspect.js';
|
|
4
|
+
import { formatSlideInspection, formatSlidePayloadJson } from './output.js';
|
|
5
|
+
import { createInspectPayload } from '../inspect/shared.js';
|
|
6
|
+
import { writeStream } from '../utils.js';
|
|
7
|
+
function getOptions(command) {
|
|
8
|
+
return command.optsWithGlobals();
|
|
9
|
+
}
|
|
10
|
+
function parsePositiveInt(value, fallback, label) {
|
|
11
|
+
if (!value)
|
|
12
|
+
return fallback;
|
|
13
|
+
const parsed = Number.parseInt(value, 10);
|
|
14
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
15
|
+
throw new Error(`Invalid ${label}: "${value}"`);
|
|
16
|
+
}
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
async function emit(output, options) {
|
|
20
|
+
if (options.output) {
|
|
21
|
+
await writeFile(options.output, output);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
await writeStream(process.stdout, output);
|
|
25
|
+
}
|
|
26
|
+
export function registerSlideCommands(program) {
|
|
27
|
+
const slide = program.command('slide').description('Inspect presentation structure and content before deep reading');
|
|
28
|
+
const inspect = slide.command('inspect <file>').description('Inspect PPTX or ODP presentation metadata, slides, and content preview');
|
|
29
|
+
inspect
|
|
30
|
+
.option('--format <type>', 'output format: tabular or json', 'tabular')
|
|
31
|
+
.option('-o, --output <file>', 'write output to file')
|
|
32
|
+
.option('--ci', 'ASCII-only output, no colors')
|
|
33
|
+
.option('--sample-slides <n>', 'preview slide count', '3')
|
|
34
|
+
.option('--slide <number>', 'inspect specific slide (1-based index)');
|
|
35
|
+
inspect.action(async (file, _options, command) => {
|
|
36
|
+
const options = getOptions(command);
|
|
37
|
+
const resolved = path.resolve(file);
|
|
38
|
+
const inspectOptions = {
|
|
39
|
+
sampleSlides: parsePositiveInt(options.sampleSlides, 3, 'sample slide count'),
|
|
40
|
+
slide: options.slide ? parsePositiveInt(options.slide, 1, 'slide number') : undefined,
|
|
41
|
+
};
|
|
42
|
+
const result = await inspectPresentation(resolved, inspectOptions);
|
|
43
|
+
const payload = createInspectPayload(resolved, {
|
|
44
|
+
command: 'slide.inspect',
|
|
45
|
+
sampleSlides: inspectOptions.sampleSlides,
|
|
46
|
+
slide: inspectOptions.slide,
|
|
47
|
+
}, result);
|
|
48
|
+
const output = options.format === 'json'
|
|
49
|
+
? formatSlidePayloadJson(payload)
|
|
50
|
+
: formatSlideInspection(result, options.ci);
|
|
51
|
+
await emit(output.endsWith('\n') ? output : `${output}\n`, options);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/slide/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DocumentProperties } from '../inspect/shared.js';
|
|
2
|
+
import type { SlideRiskFlags, SlideProfile } from './types.js';
|
|
3
|
+
export declare function inspectOdp(buffer: Buffer): Promise<{
|
|
4
|
+
properties: DocumentProperties;
|
|
5
|
+
riskFlags: SlideRiskFlags;
|
|
6
|
+
slideProfiles: SlideProfile[];
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import JSZip from 'jszip';
|
|
2
|
+
import { countWords } from '../utils.js';
|
|
3
|
+
import { asOptionalString, formatDateLike } from '../inspect/shared.js';
|
|
4
|
+
function parseXmlText(xml, tag) {
|
|
5
|
+
const regex = new RegExp(`<${tag}[^>]*>([^<]*)</${tag}>`, 'i');
|
|
6
|
+
const match = xml.match(regex);
|
|
7
|
+
return match?.[1]?.trim() || undefined;
|
|
8
|
+
}
|
|
9
|
+
function extractTextFromElement(xml) {
|
|
10
|
+
const matches = xml.match(/<text:(?:p|span)[^>]*>([^<]*)<\/text:(?:p|span)>/g) || [];
|
|
11
|
+
const texts = [];
|
|
12
|
+
for (const m of matches) {
|
|
13
|
+
const text = m.replace(/<[^>]+>/g, '').trim();
|
|
14
|
+
if (text)
|
|
15
|
+
texts.push(text);
|
|
16
|
+
}
|
|
17
|
+
return texts.join(' ');
|
|
18
|
+
}
|
|
19
|
+
export async function inspectOdp(buffer) {
|
|
20
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
21
|
+
// Parse metadata
|
|
22
|
+
const properties = {};
|
|
23
|
+
const metaXml = await zip.file('meta.xml')?.async('text');
|
|
24
|
+
if (metaXml) {
|
|
25
|
+
properties.title = asOptionalString(parseXmlText(metaXml, 'dc:title'));
|
|
26
|
+
properties.subject = asOptionalString(parseXmlText(metaXml, 'dc:subject'));
|
|
27
|
+
properties.author = asOptionalString(parseXmlText(metaXml, 'dc:creator') ?? parseXmlText(metaXml, 'meta:initial-creator'));
|
|
28
|
+
properties.keywords = asOptionalString(parseXmlText(metaXml, 'meta:keyword'));
|
|
29
|
+
properties.createdDate = formatDateLike(parseXmlText(metaXml, 'meta:creation-date'));
|
|
30
|
+
properties.modifiedDate = formatDateLike(parseXmlText(metaXml, 'dc:date'));
|
|
31
|
+
}
|
|
32
|
+
// Parse content.xml for slides
|
|
33
|
+
const contentXml = await zip.file('content.xml')?.async('text');
|
|
34
|
+
const slideProfiles = [];
|
|
35
|
+
const riskFlags = {
|
|
36
|
+
comments: false,
|
|
37
|
+
speakerNotes: false,
|
|
38
|
+
hyperlinks: false,
|
|
39
|
+
embeddedMedia: false,
|
|
40
|
+
animations: false,
|
|
41
|
+
macros: false,
|
|
42
|
+
charts: false,
|
|
43
|
+
tables: false,
|
|
44
|
+
};
|
|
45
|
+
if (contentXml) {
|
|
46
|
+
// Split by draw:page elements
|
|
47
|
+
const slideMatches = contentXml.match(/<draw:page\b[^>]*>[\s\S]*?<\/draw:page>/g) || [];
|
|
48
|
+
for (let i = 0; i < slideMatches.length; i++) {
|
|
49
|
+
const slideXml = slideMatches[i];
|
|
50
|
+
// Get title from draw:name attribute or first text frame
|
|
51
|
+
let title = null;
|
|
52
|
+
const nameMatch = slideXml.match(/draw:name="([^"]*)"/);
|
|
53
|
+
if (nameMatch && nameMatch[1]) {
|
|
54
|
+
title = nameMatch[1];
|
|
55
|
+
}
|
|
56
|
+
// Extract text
|
|
57
|
+
const text = extractTextFromElement(slideXml);
|
|
58
|
+
const words = countWords(text);
|
|
59
|
+
// Count tables and images
|
|
60
|
+
const tables = (slideXml.match(/<table:table\b/g) || []).length;
|
|
61
|
+
const images = (slideXml.match(/<draw:image\b/g) || []).length;
|
|
62
|
+
// Check for notes
|
|
63
|
+
let hasNotes = false;
|
|
64
|
+
let notePreview = null;
|
|
65
|
+
const notesMatch = slideXml.match(/<presentation:notes\b[^>]*>([\s\S]*?)<\/presentation:notes>/);
|
|
66
|
+
if (notesMatch) {
|
|
67
|
+
const notesText = extractTextFromElement(notesMatch[1]);
|
|
68
|
+
if (notesText.trim()) {
|
|
69
|
+
hasNotes = true;
|
|
70
|
+
notePreview = notesText.length > 200 ? notesText.slice(0, 200) + '...' : notesText;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
slideProfiles.push({
|
|
74
|
+
index: i + 1,
|
|
75
|
+
title,
|
|
76
|
+
words,
|
|
77
|
+
hasNotes,
|
|
78
|
+
notePreview,
|
|
79
|
+
images,
|
|
80
|
+
tables,
|
|
81
|
+
charts: 0,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Risk flags
|
|
85
|
+
if (/<office:annotation\b/.test(contentXml))
|
|
86
|
+
riskFlags.comments = true;
|
|
87
|
+
if (slideProfiles.some(p => p.hasNotes))
|
|
88
|
+
riskFlags.speakerNotes = true;
|
|
89
|
+
if (/<text:a\b[^>]*xlink:href=/.test(contentXml))
|
|
90
|
+
riskFlags.hyperlinks = true;
|
|
91
|
+
if (/<draw:image\b/.test(contentXml))
|
|
92
|
+
riskFlags.embeddedMedia = true;
|
|
93
|
+
if (/<presentation:transition\b/.test(contentXml) || /<anim:/.test(contentXml))
|
|
94
|
+
riskFlags.animations = true;
|
|
95
|
+
if (/<table:table\b/.test(contentXml))
|
|
96
|
+
riskFlags.tables = true;
|
|
97
|
+
if (/<draw:object\b/.test(contentXml))
|
|
98
|
+
riskFlags.charts = true;
|
|
99
|
+
}
|
|
100
|
+
// Check for macros
|
|
101
|
+
if (zip.file('Basic/') || zip.file('Scripts/'))
|
|
102
|
+
riskFlags.macros = true;
|
|
103
|
+
return { properties, riskFlags, slideProfiles };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=inspect-odp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect-odp.js","sourceRoot":"","sources":["../../../src/slide/inspect-odp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import JSZip from 'jszip';
|
|
2
|
+
import type { DocumentProperties } from '../inspect/shared.js';
|
|
3
|
+
import type { SlideRiskFlags, SlideProfile } from './types.js';
|
|
4
|
+
export declare function extractPptxProperties(zip: JSZip): Promise<DocumentProperties>;
|
|
5
|
+
export declare function extractPptxRiskFlags(zip: JSZip, slideProfiles: SlideProfile[]): Promise<SlideRiskFlags>;
|
|
6
|
+
export declare function extractPptxSlideProfiles(zip: JSZip): Promise<SlideProfile[]>;
|
|
7
|
+
export declare function inspectPptx(buffer: Buffer): Promise<{
|
|
8
|
+
properties: DocumentProperties;
|
|
9
|
+
riskFlags: SlideRiskFlags;
|
|
10
|
+
slideProfiles: SlideProfile[];
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import JSZip from 'jszip';
|
|
2
|
+
import { countWords } from '../utils.js';
|
|
3
|
+
import { asOptionalString, formatDateLike } from '../inspect/shared.js';
|
|
4
|
+
function parseXmlText(xml, tag) {
|
|
5
|
+
const regex = new RegExp(`<${tag}[^>]*>([^<]*)</${tag}>`, 'i');
|
|
6
|
+
const match = xml.match(regex);
|
|
7
|
+
return match?.[1]?.trim() || undefined;
|
|
8
|
+
}
|
|
9
|
+
function extractAllText(xml) {
|
|
10
|
+
const matches = xml.match(/<a:t>([^<]*)<\/a:t>/g) || [];
|
|
11
|
+
return matches.map(m => {
|
|
12
|
+
const text = m.match(/<a:t>([^<]*)<\/a:t>/);
|
|
13
|
+
return text?.[1] || '';
|
|
14
|
+
}).join(' ').trim();
|
|
15
|
+
}
|
|
16
|
+
export async function extractPptxProperties(zip) {
|
|
17
|
+
const props = {};
|
|
18
|
+
const coreXml = await zip.file('docProps/core.xml')?.async('text');
|
|
19
|
+
if (coreXml) {
|
|
20
|
+
props.title = asOptionalString(parseXmlText(coreXml, 'dc:title'));
|
|
21
|
+
props.subject = asOptionalString(parseXmlText(coreXml, 'dc:subject'));
|
|
22
|
+
props.author = asOptionalString(parseXmlText(coreXml, 'dc:creator'));
|
|
23
|
+
props.keywords = asOptionalString(parseXmlText(coreXml, 'cp:keywords'));
|
|
24
|
+
props.lastModifiedBy = asOptionalString(parseXmlText(coreXml, 'cp:lastModifiedBy'));
|
|
25
|
+
props.createdDate = formatDateLike(parseXmlText(coreXml, 'dcterms:created'));
|
|
26
|
+
props.modifiedDate = formatDateLike(parseXmlText(coreXml, 'dcterms:modified'));
|
|
27
|
+
}
|
|
28
|
+
const appXml = await zip.file('docProps/app.xml')?.async('text');
|
|
29
|
+
if (appXml) {
|
|
30
|
+
props.company = asOptionalString(parseXmlText(appXml, 'Company'));
|
|
31
|
+
}
|
|
32
|
+
return props;
|
|
33
|
+
}
|
|
34
|
+
export async function extractPptxRiskFlags(zip, slideProfiles) {
|
|
35
|
+
const flags = {
|
|
36
|
+
comments: false,
|
|
37
|
+
speakerNotes: false,
|
|
38
|
+
hyperlinks: false,
|
|
39
|
+
embeddedMedia: false,
|
|
40
|
+
animations: false,
|
|
41
|
+
macros: false,
|
|
42
|
+
charts: false,
|
|
43
|
+
tables: false,
|
|
44
|
+
};
|
|
45
|
+
// Check for comments folder
|
|
46
|
+
const commentFiles = Object.keys(zip.files).filter(name => /^ppt\/comments\//.test(name));
|
|
47
|
+
if (commentFiles.length > 0)
|
|
48
|
+
flags.comments = true;
|
|
49
|
+
// Check for speaker notes
|
|
50
|
+
if (slideProfiles.some(p => p.hasNotes))
|
|
51
|
+
flags.speakerNotes = true;
|
|
52
|
+
// Check for media files
|
|
53
|
+
const mediaFiles = Object.keys(zip.files).filter(name => /^ppt\/media\//.test(name));
|
|
54
|
+
if (mediaFiles.length > 0)
|
|
55
|
+
flags.embeddedMedia = true;
|
|
56
|
+
// Check for macros
|
|
57
|
+
if (zip.file('ppt/vbaProject.bin'))
|
|
58
|
+
flags.macros = true;
|
|
59
|
+
// Check for charts
|
|
60
|
+
const chartFiles = Object.keys(zip.files).filter(name => /^ppt\/charts\//.test(name));
|
|
61
|
+
if (chartFiles.length > 0)
|
|
62
|
+
flags.charts = true;
|
|
63
|
+
// Aggregate from profiles
|
|
64
|
+
if (slideProfiles.some(p => p.tables > 0))
|
|
65
|
+
flags.tables = true;
|
|
66
|
+
// Check slides for animations and hyperlinks
|
|
67
|
+
const slideFiles = Object.keys(zip.files)
|
|
68
|
+
.filter(name => /^ppt\/slides\/slide\d+\.xml$/.test(name))
|
|
69
|
+
.sort((a, b) => {
|
|
70
|
+
const numA = parseInt(a.match(/slide(\d+)/)?.[1] || '0', 10);
|
|
71
|
+
const numB = parseInt(b.match(/slide(\d+)/)?.[1] || '0', 10);
|
|
72
|
+
return numA - numB;
|
|
73
|
+
});
|
|
74
|
+
for (const slideFile of slideFiles) {
|
|
75
|
+
const xml = await zip.file(slideFile)?.async('text');
|
|
76
|
+
if (!xml)
|
|
77
|
+
continue;
|
|
78
|
+
if (/<p:transition\b/.test(xml) || /<p:anim\b/.test(xml) || /<p:animMotion\b/.test(xml)) {
|
|
79
|
+
flags.animations = true;
|
|
80
|
+
}
|
|
81
|
+
if (/<a:hlinkClick\b/.test(xml) || /<a:hlinkMouseOver\b/.test(xml)) {
|
|
82
|
+
flags.hyperlinks = true;
|
|
83
|
+
}
|
|
84
|
+
if (flags.animations && flags.hyperlinks)
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return flags;
|
|
88
|
+
}
|
|
89
|
+
export async function extractPptxSlideProfiles(zip) {
|
|
90
|
+
const slideFiles = Object.keys(zip.files)
|
|
91
|
+
.filter(name => /^ppt\/slides\/slide\d+\.xml$/.test(name))
|
|
92
|
+
.sort((a, b) => {
|
|
93
|
+
const numA = parseInt(a.match(/slide(\d+)/)?.[1] || '0', 10);
|
|
94
|
+
const numB = parseInt(b.match(/slide(\d+)/)?.[1] || '0', 10);
|
|
95
|
+
return numA - numB;
|
|
96
|
+
});
|
|
97
|
+
const profiles = [];
|
|
98
|
+
for (let i = 0; i < slideFiles.length; i++) {
|
|
99
|
+
const slideXml = await zip.file(slideFiles[i])?.async('text');
|
|
100
|
+
if (!slideXml)
|
|
101
|
+
continue;
|
|
102
|
+
// Extract title
|
|
103
|
+
let title = null;
|
|
104
|
+
// Look for title placeholder
|
|
105
|
+
const titleMatch = slideXml.match(/<p:sp>[\s\S]*?<p:ph[^>]*type="(?:title|ctrTitle)"[^>]*\/>[\s\S]*?<\/p:sp>/i);
|
|
106
|
+
if (titleMatch) {
|
|
107
|
+
const titleText = extractAllText(titleMatch[0]);
|
|
108
|
+
if (titleText)
|
|
109
|
+
title = titleText;
|
|
110
|
+
}
|
|
111
|
+
// Extract all text for word count
|
|
112
|
+
const allText = extractAllText(slideXml);
|
|
113
|
+
const words = countWords(allText);
|
|
114
|
+
// Count tables
|
|
115
|
+
const tables = (slideXml.match(/<a:tbl\b/g) || []).length;
|
|
116
|
+
// Count images
|
|
117
|
+
const images = (slideXml.match(/<p:pic\b/g) || []).length + (slideXml.match(/<a:blip\b/g) || []).length;
|
|
118
|
+
// Count charts (from relationships)
|
|
119
|
+
const slideNum = parseInt(slideFiles[i].match(/slide(\d+)/)?.[1] || '0', 10);
|
|
120
|
+
const relsFile = `ppt/slides/_rels/slide${slideNum}.xml.rels`;
|
|
121
|
+
const relsXml = await zip.file(relsFile)?.async('text');
|
|
122
|
+
const charts = relsXml ? (relsXml.match(/chart/gi) || []).length : 0;
|
|
123
|
+
// Check for speaker notes
|
|
124
|
+
const notesFile = `ppt/notesSlides/notesSlide${slideNum}.xml`;
|
|
125
|
+
const notesXml = await zip.file(notesFile)?.async('text');
|
|
126
|
+
let hasNotes = false;
|
|
127
|
+
let notePreview = null;
|
|
128
|
+
if (notesXml) {
|
|
129
|
+
const notesText = extractAllText(notesXml);
|
|
130
|
+
// Filter out slide number placeholder text
|
|
131
|
+
const cleanNotes = notesText.replace(/\d+/g, '').trim();
|
|
132
|
+
if (cleanNotes.length > 0) {
|
|
133
|
+
hasNotes = true;
|
|
134
|
+
notePreview = notesText.length > 200 ? notesText.slice(0, 200) + '...' : notesText;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
profiles.push({
|
|
138
|
+
index: i + 1,
|
|
139
|
+
title,
|
|
140
|
+
words,
|
|
141
|
+
hasNotes,
|
|
142
|
+
notePreview,
|
|
143
|
+
images,
|
|
144
|
+
tables,
|
|
145
|
+
charts,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return profiles;
|
|
149
|
+
}
|
|
150
|
+
export async function inspectPptx(buffer) {
|
|
151
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
152
|
+
const properties = await extractPptxProperties(zip);
|
|
153
|
+
const slideProfiles = await extractPptxSlideProfiles(zip);
|
|
154
|
+
const riskFlags = await extractPptxRiskFlags(zip, slideProfiles);
|
|
155
|
+
return { properties, riskFlags, slideProfiles };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=inspect-pptx.js.map
|