formatferry 0.1.3 → 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.
Files changed (85) hide show
  1. package/dist/commands/auth.d.ts +17 -0
  2. package/dist/commands/auth.d.ts.map +1 -0
  3. package/dist/commands/auth.js +102 -0
  4. package/dist/commands/auth.js.map +1 -0
  5. package/dist/commands/batch.d.ts +10 -0
  6. package/dist/commands/batch.d.ts.map +1 -0
  7. package/dist/commands/batch.js +92 -0
  8. package/dist/commands/batch.js.map +1 -0
  9. package/dist/commands/convert.d.ts +26 -0
  10. package/dist/commands/convert.d.ts.map +1 -0
  11. package/dist/commands/convert.js +198 -0
  12. package/dist/commands/convert.js.map +1 -0
  13. package/dist/config-store.d.ts +27 -0
  14. package/dist/config-store.d.ts.map +1 -0
  15. package/dist/config-store.js +58 -0
  16. package/dist/config-store.js.map +1 -0
  17. package/dist/entitlement.d.ts +11 -0
  18. package/dist/entitlement.d.ts.map +1 -0
  19. package/dist/entitlement.js +91 -0
  20. package/dist/entitlement.js.map +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +31 -11
  24. package/dist/index.js.map +1 -0
  25. package/dist/packages/cli/src/commands/auth.d.ts +17 -0
  26. package/dist/packages/cli/src/commands/auth.d.ts.map +1 -0
  27. package/dist/packages/cli/src/commands/auth.js +102 -0
  28. package/dist/packages/cli/src/commands/auth.js.map +1 -0
  29. package/dist/packages/cli/src/commands/batch.d.ts +10 -0
  30. package/dist/packages/cli/src/commands/batch.d.ts.map +1 -0
  31. package/dist/packages/cli/src/commands/batch.js +92 -0
  32. package/dist/packages/cli/src/commands/batch.js.map +1 -0
  33. package/dist/packages/cli/src/commands/convert.d.ts +26 -0
  34. package/dist/packages/cli/src/commands/convert.d.ts.map +1 -0
  35. package/dist/packages/cli/src/commands/convert.js +198 -0
  36. package/dist/packages/cli/src/commands/convert.js.map +1 -0
  37. package/dist/packages/cli/src/config-store.d.ts +27 -0
  38. package/dist/packages/cli/src/config-store.d.ts.map +1 -0
  39. package/dist/packages/cli/src/config-store.js +58 -0
  40. package/dist/packages/cli/src/config-store.js.map +1 -0
  41. package/dist/packages/cli/src/entitlement.d.ts +11 -0
  42. package/dist/packages/cli/src/entitlement.d.ts.map +1 -0
  43. package/dist/packages/cli/src/entitlement.js +91 -0
  44. package/dist/packages/cli/src/entitlement.js.map +1 -0
  45. package/dist/packages/cli/src/index.d.ts +2 -0
  46. package/dist/packages/cli/src/index.d.ts.map +1 -0
  47. package/dist/packages/cli/src/index.js +28 -0
  48. package/dist/packages/cli/src/index.js.map +1 -0
  49. package/dist/packages/cli/src/shim.d.ts +2 -0
  50. package/dist/packages/cli/src/shim.d.ts.map +1 -0
  51. package/dist/packages/cli/src/shim.js +21 -0
  52. package/dist/packages/cli/src/shim.js.map +1 -0
  53. package/dist/shim.d.ts +2 -0
  54. package/dist/shim.d.ts.map +1 -0
  55. package/dist/shim.js +21 -0
  56. package/dist/shim.js.map +1 -0
  57. package/dist/src/shared/adapters/docx-adapter.d.ts +85 -0
  58. package/dist/src/shared/adapters/docx-adapter.d.ts.map +1 -0
  59. package/dist/src/shared/adapters/docx-adapter.js +164 -0
  60. package/dist/src/shared/adapters/docx-adapter.js.map +1 -0
  61. package/dist/src/shared/adapters/pdf-adapter.d.ts +94 -0
  62. package/dist/src/shared/adapters/pdf-adapter.d.ts.map +1 -0
  63. package/dist/src/shared/adapters/pdf-adapter.js +322 -0
  64. package/dist/src/shared/adapters/pdf-adapter.js.map +1 -0
  65. package/dist/src/shared/adapters/spreadsheet-adapter.d.ts +98 -0
  66. package/dist/src/shared/adapters/spreadsheet-adapter.d.ts.map +1 -0
  67. package/dist/src/shared/adapters/spreadsheet-adapter.js +424 -0
  68. package/dist/src/shared/adapters/spreadsheet-adapter.js.map +1 -0
  69. package/dist/src/shared/atlassian-detector.d.ts +6 -0
  70. package/dist/src/shared/atlassian-detector.d.ts.map +1 -0
  71. package/dist/src/shared/atlassian-detector.js +23 -0
  72. package/dist/src/shared/atlassian-detector.js.map +1 -0
  73. package/dist/src/shared/conversion-engine.d.ts +114 -0
  74. package/dist/src/shared/conversion-engine.d.ts.map +1 -0
  75. package/dist/src/shared/conversion-engine.js +790 -0
  76. package/dist/src/shared/conversion-engine.js.map +1 -0
  77. package/dist/src/shared/entitlement-service.d.ts +42 -0
  78. package/dist/src/shared/entitlement-service.d.ts.map +1 -0
  79. package/dist/src/shared/entitlement-service.js +38 -0
  80. package/dist/src/shared/entitlement-service.js.map +1 -0
  81. package/dist/src/shared/input-sanitizer.d.ts +93 -0
  82. package/dist/src/shared/input-sanitizer.d.ts.map +1 -0
  83. package/dist/src/shared/input-sanitizer.js +527 -0
  84. package/dist/src/shared/input-sanitizer.js.map +1 -0
  85. package/package.json +6 -2
@@ -0,0 +1,198 @@
1
+ /**
2
+ * CLI convert command
3
+ *
4
+ * Handles stdin → stdout, file input, file output, format selection,
5
+ * and binary file processing (DOCX, PDF, XLSX/CSV).
6
+ *
7
+ * Requirements: 7.3, 7.4, 7.5, 7.6, 7.7, 7.10, 11.1, 11.2, 11.3, 11.5
8
+ */
9
+ import { readFileSync, writeFileSync } from 'fs';
10
+ import { extname } from 'path';
11
+ import { ConversionEngine } from '../../../../src/shared/conversion-engine.js';
12
+ import { docxAdapter } from '../../../../src/shared/adapters/docx-adapter.js';
13
+ import { pdfAdapter } from '../../../../src/shared/adapters/pdf-adapter.js';
14
+ import { spreadsheetAdapter } from '../../../../src/shared/adapters/spreadsheet-adapter.js';
15
+ import { sanitizeHTML, normalizeAtlassianHTML } from '../../../../src/shared/input-sanitizer.js';
16
+ import { isAtlassianHTML } from '../../../../src/shared/atlassian-detector.js';
17
+ import { resolveEntitlement } from '../entitlement.js';
18
+ /** Valid markdown format values */
19
+ const VALID_FORMATS = [
20
+ 'github',
21
+ 'commonmark',
22
+ 'discord',
23
+ 'reddit',
24
+ 'rmarkdown',
25
+ ];
26
+ /**
27
+ * Detect input type from file extension.
28
+ * Writes error to stderr and exits with code 1 on unsupported extension.
29
+ */
30
+ export function detectInputType(filePath) {
31
+ const ext = extname(filePath).toLowerCase();
32
+ const map = {
33
+ '.html': 'html',
34
+ '.htm': 'html',
35
+ '.docx': 'docx',
36
+ '.pdf': 'pdf',
37
+ '.xlsx': 'xlsx',
38
+ '.csv': 'csv',
39
+ };
40
+ const type = map[ext];
41
+ if (!type) {
42
+ process.stderr.write(`Unsupported file type: ${ext}\nSupported: .html, .htm, .docx, .pdf, .xlsx, .csv\n`);
43
+ process.exit(1);
44
+ }
45
+ return type;
46
+ }
47
+ /**
48
+ * Validate the --format flag value.
49
+ * Writes error to stderr and exits with code 1 on unknown format.
50
+ */
51
+ export function validateFormat(format) {
52
+ if (VALID_FORMATS.includes(format)) {
53
+ return format;
54
+ }
55
+ process.stderr.write(`Unknown format: "${format}"\nSupported formats: ${VALID_FORMATS.join(', ')}\n`);
56
+ process.exit(1);
57
+ }
58
+ /**
59
+ * Read all data from stdin as a string.
60
+ */
61
+ function readStdin() {
62
+ return new Promise((resolve, reject) => {
63
+ let data = '';
64
+ process.stdin.setEncoding('utf8');
65
+ process.stdin.on('data', (chunk) => { data += chunk; });
66
+ process.stdin.on('end', () => resolve(data));
67
+ process.stdin.on('error', reject);
68
+ });
69
+ }
70
+ /**
71
+ * Convert HTML string to Markdown using ConversionEngine.
72
+ * Applies Atlassian normalization if the HTML is Atlassian and the user has the feature.
73
+ * Req 2.3, 2.4
74
+ */
75
+ async function htmlToMarkdown(html, format, applyAtlassianNormalization = false) {
76
+ const processedHtml = applyAtlassianNormalization ? normalizeAtlassianHTML(html) : html;
77
+ const engine = new ConversionEngine();
78
+ const result = await engine.convert(processedHtml, {
79
+ format,
80
+ preserveTables: true,
81
+ preserveLists: true,
82
+ customRules: [],
83
+ });
84
+ return result.markdown;
85
+ }
86
+ /**
87
+ * Convert a binary file (DOCX/PDF/XLSX/CSV) buffer to Markdown.
88
+ * Req 11.1, 11.2, 11.3, 11.5
89
+ */
90
+ async function binaryToMarkdown(buffer, type, format) {
91
+ // Convert Buffer → ArrayBuffer (Req 11.5: read as Buffer, not UTF-8)
92
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
93
+ const engine = new ConversionEngine();
94
+ switch (type) {
95
+ case 'docx': {
96
+ // Req 11.1: use mammoth integration
97
+ const result = await docxAdapter.parseToHtml(arrayBuffer);
98
+ const sanitized = sanitizeHTML(result.html);
99
+ const converted = await engine.convert(sanitized, {
100
+ format,
101
+ preserveTables: true,
102
+ preserveLists: true,
103
+ customRules: [],
104
+ });
105
+ return converted.markdown;
106
+ }
107
+ case 'pdf': {
108
+ // Req 11.2: use pdfjs-dist integration
109
+ const result = await pdfAdapter.parseToText(arrayBuffer);
110
+ const sanitized = sanitizeHTML(result.html);
111
+ const converted = await engine.convert(sanitized, {
112
+ format,
113
+ preserveTables: true,
114
+ preserveLists: true,
115
+ customRules: [],
116
+ });
117
+ return converted.markdown;
118
+ }
119
+ case 'xlsx':
120
+ case 'csv': {
121
+ // Req 11.3: use spreadsheet processing
122
+ const result = await spreadsheetAdapter.parseToHtml(arrayBuffer, type, 0);
123
+ const sanitized = sanitizeHTML(result.html);
124
+ const converted = await engine.convert(sanitized, {
125
+ format,
126
+ preserveTables: true,
127
+ preserveLists: true,
128
+ customRules: [],
129
+ });
130
+ return converted.markdown;
131
+ }
132
+ }
133
+ }
134
+ /**
135
+ * Main convert command action.
136
+ * Called by Commander when the user runs `formatferry [options]`.
137
+ */
138
+ export async function convertCommand(options) {
139
+ // Validate format (default: github)
140
+ const format = validateFormat(options.format ?? 'github');
141
+ let markdown;
142
+ if (options.input) {
143
+ // File input mode (Req 7.4, 7.10)
144
+ let fileBuffer;
145
+ try {
146
+ fileBuffer = readFileSync(options.input);
147
+ }
148
+ catch {
149
+ process.stderr.write(`Error: Cannot read file "${options.input}"\n`);
150
+ process.exit(1);
151
+ }
152
+ const inputType = detectInputType(options.input);
153
+ if (inputType === 'html') {
154
+ // Text file — decode as UTF-8
155
+ const html = fileBuffer.toString('utf8');
156
+ // Req 2.3, 2.4: Atlassian entitlement check
157
+ let applyAtlassian = false;
158
+ if (isAtlassianHTML(html)) {
159
+ const provider = await resolveEntitlement();
160
+ if (!provider.hasFeature('atlassian')) {
161
+ process.stderr.write('Error: Atlassian HTML detected. A Premium license is required to process Atlassian content.\n' +
162
+ 'Run `formatferry auth --license-key <KEY>` to activate your license.\n');
163
+ process.exit(1);
164
+ }
165
+ applyAtlassian = true;
166
+ }
167
+ markdown = await htmlToMarkdown(html, format, applyAtlassian);
168
+ }
169
+ else {
170
+ // Binary file — pass Buffer directly (Req 11.5)
171
+ markdown = await binaryToMarkdown(fileBuffer, inputType, format);
172
+ }
173
+ }
174
+ else {
175
+ // Stdin mode (Req 7.3)
176
+ const html = await readStdin();
177
+ // Req 2.3, 2.4: Atlassian entitlement check for stdin
178
+ let applyAtlassian = false;
179
+ if (isAtlassianHTML(html)) {
180
+ const provider = await resolveEntitlement();
181
+ if (!provider.hasFeature('atlassian')) {
182
+ process.stderr.write('Error: Atlassian HTML detected. A Premium license is required to process Atlassian content.\n' +
183
+ 'Run `formatferry auth --license-key <KEY>` to activate your license.\n');
184
+ process.exit(1);
185
+ }
186
+ applyAtlassian = true;
187
+ }
188
+ markdown = await htmlToMarkdown(html, format, applyAtlassian);
189
+ }
190
+ // Output (Req 7.5)
191
+ if (options.output) {
192
+ writeFileSync(options.output, markdown, 'utf8');
193
+ }
194
+ else {
195
+ process.stdout.write(markdown);
196
+ }
197
+ }
198
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../../../../src/commands/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export declare let CONFIG_PATH: string;
2
+ /** Override the config path (for testing only). */
3
+ export declare function _setConfigPathForTesting(p: string): void;
4
+ export interface StoredConfig {
5
+ licenseKey?: string;
6
+ tier?: 'free' | 'premium';
7
+ features?: string[];
8
+ expiresAt?: string | null;
9
+ lastValidated?: string;
10
+ apiKey?: string;
11
+ }
12
+ /**
13
+ * Read the stored config. Returns `null` if the file is missing or corrupted.
14
+ * Silently discards errors so the CLI always falls back to free tier (Req 10.7).
15
+ */
16
+ export declare function readConfig(): StoredConfig | null;
17
+ /**
18
+ * Persist config to disk. Creates parent directories as needed.
19
+ * Sets mode 0o600 on POSIX (owner read/write only) per Req 9.5.
20
+ */
21
+ export declare function writeConfig(config: StoredConfig): void;
22
+ /**
23
+ * Reset the config to an empty object (effectively clearing all stored values).
24
+ * Silently ignores errors (e.g. if the file doesn't exist yet).
25
+ */
26
+ export declare function clearConfig(): void;
27
+ //# sourceMappingURL=config-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../../../src/config-store.ts"],"names":[],"mappings":"AAsBA,eAAO,IAAI,WAAW,QAAgC,CAAC;AAEvD,mDAAmD;AACnD,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,YAAY,GAAG,IAAI,CAOhD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAGtD;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAKlC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Cross-platform config store for FormatFerry CLI.
3
+ *
4
+ * Config path:
5
+ * - POSIX: ~/.config/formatferry/config.json (Req 9.2)
6
+ * - Windows: %APPDATA%\formatferry\config.json (Req 9.2)
7
+ *
8
+ * File permissions:
9
+ * - POSIX: 0o600 (owner read/write only) (Req 9.5)
10
+ * - Windows: 0o600 is silently ignored by Node.js — security relies on
11
+ * %APPDATA% being inside the user's profile directory, which is protected
12
+ * by Windows NTFS user account ACLs. No additional code is needed.
13
+ *
14
+ * Corrupted or missing config → returns null → Free tier (Req 10.7)
15
+ */
16
+ import envPaths from 'env-paths';
17
+ import { readFileSync, writeFileSync, mkdirSync } from 'fs';
18
+ import { dirname } from 'path';
19
+ const paths = envPaths('formatferry', { suffix: '' });
20
+ // Exported so tests can override via module-level patching
21
+ export let CONFIG_PATH = `${paths.config}/config.json`;
22
+ /** Override the config path (for testing only). */
23
+ export function _setConfigPathForTesting(p) {
24
+ CONFIG_PATH = p;
25
+ }
26
+ /**
27
+ * Read the stored config. Returns `null` if the file is missing or corrupted.
28
+ * Silently discards errors so the CLI always falls back to free tier (Req 10.7).
29
+ */
30
+ export function readConfig() {
31
+ try {
32
+ const raw = readFileSync(CONFIG_PATH, 'utf-8');
33
+ return JSON.parse(raw);
34
+ }
35
+ catch {
36
+ return null; // Missing or corrupted → Free tier
37
+ }
38
+ }
39
+ /**
40
+ * Persist config to disk. Creates parent directories as needed.
41
+ * Sets mode 0o600 on POSIX (owner read/write only) per Req 9.5.
42
+ */
43
+ export function writeConfig(config) {
44
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
45
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
46
+ }
47
+ /**
48
+ * Reset the config to an empty object (effectively clearing all stored values).
49
+ * Silently ignores errors (e.g. if the file doesn't exist yet).
50
+ */
51
+ export function clearConfig() {
52
+ try {
53
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
54
+ writeFileSync(CONFIG_PATH, '{}', { mode: 0o600 });
55
+ }
56
+ catch { /* ignore */ }
57
+ }
58
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../../../src/config-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AAEtD,2DAA2D;AAC3D,MAAM,CAAC,IAAI,WAAW,GAAG,GAAG,KAAK,CAAC,MAAM,cAAc,CAAC;AAEvD,mDAAmD;AACnD,MAAM,UAAU,wBAAwB,CAAC,CAAS;IAChD,WAAW,GAAG,CAAC,CAAC;AAClB,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,mCAAmC;IAClD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * CLI startup license validation with caching (Design §5.5)
3
+ * Validates: Requirements 10.1–10.7
4
+ */
5
+ import { type EntitlementProvider } from '../../../src/shared/entitlement-service.js';
6
+ /**
7
+ * Resolve the entitlement provider for the current CLI session.
8
+ * Called once at CLI startup before any command runs.
9
+ */
10
+ export declare function resolveEntitlement(): Promise<EntitlementProvider>;
11
+ //# sourceMappingURL=entitlement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.d.ts","sourceRoot":"","sources":["../../../../src/entitlement.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAGL,KAAK,mBAAmB,EAGzB,MAAM,4CAA4C,CAAC;AAepD;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CA0FvE"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * CLI startup license validation with caching (Design §5.5)
3
+ * Validates: Requirements 10.1–10.7
4
+ */
5
+ import { readConfig, writeConfig, clearConfig } from './config-store.js';
6
+ import { FreeEntitlementProvider, CachedEntitlementProvider, } from '../../../src/shared/entitlement-service.js';
7
+ const LICENSE_SERVER_URL = 'https://formatferry.vibingfun.com';
8
+ const VALIDATE_ENDPOINT = `${LICENSE_SERVER_URL}/v1/licenses/validate`;
9
+ const STARTUP_TIMEOUT_MS = 2_000;
10
+ const STALE_CACHE_DAYS = 7;
11
+ /**
12
+ * Resolve the entitlement provider for the current CLI session.
13
+ * Called once at CLI startup before any command runs.
14
+ */
15
+ export async function resolveEntitlement() {
16
+ // Req 10.7: Corrupted ConfigStore → silently discard, free tier
17
+ let config;
18
+ try {
19
+ config = readConfig();
20
+ }
21
+ catch {
22
+ return new FreeEntitlementProvider();
23
+ }
24
+ if (!config || !config.licenseKey) {
25
+ return new FreeEntitlementProvider();
26
+ }
27
+ const { licenseKey, lastValidated } = config;
28
+ // Req 10.1: Ping License Server with 2s timeout
29
+ let data = null;
30
+ let networkError = false;
31
+ try {
32
+ const controller = new AbortController();
33
+ const timer = setTimeout(() => controller.abort(), STARTUP_TIMEOUT_MS);
34
+ const response = await fetch(VALIDATE_ENDPOINT, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({ key: licenseKey }),
38
+ signal: controller.signal,
39
+ });
40
+ clearTimeout(timer);
41
+ data = (await response.json());
42
+ }
43
+ catch {
44
+ networkError = true;
45
+ }
46
+ if (!networkError && data !== null) {
47
+ // Req 10.6: Revoked → delete key, downgrade to free
48
+ if (data.revoked) {
49
+ clearConfig();
50
+ return new FreeEntitlementProvider();
51
+ }
52
+ if (!data.valid) {
53
+ return new FreeEntitlementProvider();
54
+ }
55
+ // Req 10.2 + 10.4: valid → update cache with fresh data + ISO 8601 timestamp
56
+ const freshState = {
57
+ tier: data.tier ?? 'premium',
58
+ features: (data.features ?? []),
59
+ expiresAt: data.expiresAt ?? null,
60
+ lastValidated: new Date().toISOString(),
61
+ };
62
+ writeConfig({
63
+ ...config,
64
+ tier: freshState.tier,
65
+ features: freshState.features,
66
+ expiresAt: freshState.expiresAt,
67
+ lastValidated: freshState.lastValidated,
68
+ });
69
+ return new CachedEntitlementProvider(freshState);
70
+ }
71
+ // Req 10.3 + 10.5: Network error/timeout — check cache age
72
+ const cacheAgeMs = lastValidated
73
+ ? Date.now() - new Date(lastValidated).getTime()
74
+ : Infinity;
75
+ const staleCacheMs = STALE_CACHE_DAYS * 24 * 60 * 60 * 1000;
76
+ if (cacheAgeMs < staleCacheMs) {
77
+ // Req 10.3: Fresh cache → use cached entitlements
78
+ const cachedState = {
79
+ tier: config.tier ?? 'free',
80
+ features: (config.features ?? []),
81
+ expiresAt: config.expiresAt ?? null,
82
+ lastValidated: lastValidated ?? '',
83
+ };
84
+ return new CachedEntitlementProvider(cachedState);
85
+ }
86
+ // Req 10.5: Stale cache + unreachable → warn stderr, free tier
87
+ process.stderr.write('Warning: Could not reach license server and cached license is older than 7 days. ' +
88
+ 'Operating in free tier. Please reconnect to validate your license.\n');
89
+ return new FreeEntitlementProvider();
90
+ }
91
+ //# sourceMappingURL=entitlement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.js","sourceRoot":"","sources":["../../../../src/entitlement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import './shim.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,CAAC"}
@@ -0,0 +1,28 @@
1
+ // Entry point — shim MUST be imported first to set up DOM globals before any shared modules
2
+ import './shim.js';
3
+ import { program } from 'commander';
4
+ import { convertCommand } from './commands/convert.js';
5
+ import { authCommand } from './commands/auth.js';
6
+ // Version is read from package.json at build time
7
+ const VERSION = '0.1.0';
8
+ program
9
+ .name('formatferry')
10
+ .version(VERSION)
11
+ .description('Convert HTML, DOCX, PDF, XLSX to Markdown')
12
+ .option('-i, --input <path>', 'Input file path')
13
+ .option('-o, --output <path>', 'Output file path')
14
+ .option('-f, --format <format>', 'Markdown format (github, commonmark, discord, reddit, rmarkdown)', 'github')
15
+ .option('--url <url>', 'Fetch article from URL')
16
+ .option('--batch <glob>', 'Batch convert files (Premium)')
17
+ .option('--output-dir <dir>', 'Output directory for batch')
18
+ .action(convertCommand);
19
+ // Auth subcommand
20
+ program
21
+ .command('auth')
22
+ .description('Manage license key authentication')
23
+ .option('--license-key <key>', 'Activate a license key')
24
+ .option('--status', 'Show current license status')
25
+ .option('--logout', 'Remove stored license key')
26
+ .action(authCommand);
27
+ program.parse();
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,OAAO,WAAW,CAAC;AACnB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,kDAAkD;AAClD,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CAAC,oBAAoB,EAAE,iBAAiB,CAAC;KAC/C,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,uBAAuB,EAAE,kEAAkE,EAAE,QAAQ,CAAC;KAC7G,MAAM,CAAC,aAAa,EAAE,wBAAwB,CAAC;KAC/C,MAAM,CAAC,gBAAgB,EAAE,+BAA+B,CAAC;KACzD,MAAM,CAAC,oBAAoB,EAAE,4BAA4B,CAAC;KAC1D,MAAM,CAAC,cAAc,CAAC,CAAC;AAE1B,kBAAkB;AAClB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,qBAAqB,EAAE,wBAAwB,CAAC;KACvD,MAAM,CAAC,UAAU,EAAE,6BAA6B,CAAC;KACjD,MAAM,CAAC,UAAU,EAAE,2BAA2B,CAAC;KAC/C,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../../../../src/shim.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Global DOM shim for Node.js CLI context.
3
+ *
4
+ * This file MUST be imported as the very first import in index.ts.
5
+ * It assigns linkedom-backed globals to satisfy third-party libraries
6
+ * (e.g. turndown) that contain hardcoded checks for window.document.
7
+ *
8
+ * Compatibility outcome (Task 5.4): linkedom compatible.
9
+ * Smoke test (src/tests/shim-smoke.test.ts) confirmed that linkedom-backed
10
+ * globals satisfy turndown and DOMPurify without errors. No jsdom fallback needed.
11
+ */
12
+ import { parseHTML } from 'linkedom';
13
+ const { document, window } = parseHTML('<!DOCTYPE html><html><body></body></html>');
14
+ // Satisfy turndown's hardcoded window.document checks
15
+ globalThis.window = window;
16
+ globalThis.document = document;
17
+ globalThis.DOMParser = window.DOMParser;
18
+ globalThis.Node = window.Node;
19
+ globalThis.HTMLElement = window.HTMLElement;
20
+ globalThis.HTMLTableElement = window.HTMLTableElement;
21
+ //# sourceMappingURL=shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shim.js","sourceRoot":"","sources":["../../../../src/shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,2CAA2C,CAAC,CAAC;AAEpF,sDAAsD;AACrD,UAAsC,CAAC,MAAM,GAAG,MAAM,CAAC;AACvD,UAAsC,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC3D,UAAsC,CAAC,SAAS,GAAI,MAA4C,CAAC,SAAS,CAAC;AAC3G,UAAsC,CAAC,IAAI,GAAI,MAAuC,CAAC,IAAI,CAAC;AAC5F,UAAsC,CAAC,WAAW,GAAI,MAA8C,CAAC,WAAW,CAAC;AACjH,UAAsC,CAAC,gBAAgB,GAAI,MAAmD,CAAC,gBAAgB,CAAC"}
package/dist/shim.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../src/shim.ts"],"names":[],"mappings":""}
package/dist/shim.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Global DOM shim for Node.js CLI context.
3
+ *
4
+ * This file MUST be imported as the very first import in index.ts.
5
+ * It assigns linkedom-backed globals to satisfy third-party libraries
6
+ * (e.g. turndown) that contain hardcoded checks for window.document.
7
+ *
8
+ * Compatibility outcome (Task 5.4): linkedom compatible.
9
+ * Smoke test (src/tests/shim-smoke.test.ts) confirmed that linkedom-backed
10
+ * globals satisfy turndown and DOMPurify without errors. No jsdom fallback needed.
11
+ */
12
+ import { parseHTML } from 'linkedom';
13
+ const { document, window } = parseHTML('<!DOCTYPE html><html><body></body></html>');
14
+ // Satisfy turndown's hardcoded window.document checks
15
+ globalThis.window = window;
16
+ globalThis.document = document;
17
+ globalThis.DOMParser = window.DOMParser;
18
+ globalThis.Node = window.Node;
19
+ globalThis.HTMLElement = window.HTMLElement;
20
+ globalThis.HTMLTableElement = window.HTMLTableElement;
21
+ //# sourceMappingURL=shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shim.js","sourceRoot":"","sources":["../src/shim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,2CAA2C,CAAC,CAAC;AAEpF,sDAAsD;AACrD,UAAsC,CAAC,MAAM,GAAG,MAAM,CAAC;AACvD,UAAsC,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC3D,UAAsC,CAAC,SAAS,GAAI,MAA4C,CAAC,SAAS,CAAC;AAC3G,UAAsC,CAAC,IAAI,GAAI,MAAuC,CAAC,IAAI,CAAC;AAC5F,UAAsC,CAAC,WAAW,GAAI,MAA8C,CAAC,WAAW,CAAC;AACjH,UAAsC,CAAC,gBAAgB,GAAI,MAAmD,CAAC,gBAAgB,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * DOCX Adapter Module
3
+ *
4
+ * Converts DOCX files to HTML using Mammoth.js for processing through
5
+ * the existing ConversionEngine pipeline.
6
+ *
7
+ * Requirements: 1.1, 1.3, 1.4, 1.5, 1.6
8
+ */
9
+ /** Image reference extracted from DOCX */
10
+ export interface ImageReference {
11
+ /** Original filename from the DOCX */
12
+ filename: string;
13
+ /** Image width in pixels (if available) */
14
+ width?: number;
15
+ /** Image height in pixels (if available) */
16
+ height?: number;
17
+ /** Alt text from the document */
18
+ altText?: string;
19
+ /** Content type (e.g., 'image/png') */
20
+ contentType?: string;
21
+ }
22
+ /** Document metadata extracted from DOCX */
23
+ export interface DocumentMetadata {
24
+ /** Document title (if available) */
25
+ title?: string;
26
+ /** Document author (if available) */
27
+ author?: string;
28
+ /** Number of images in the document */
29
+ imageCount: number;
30
+ }
31
+ /** Result of parsing a DOCX file */
32
+ export interface DocxParseResult {
33
+ /** HTML content extracted from the DOCX */
34
+ html: string;
35
+ /** Images found in the document */
36
+ images: ImageReference[];
37
+ /** Document metadata */
38
+ metadata: DocumentMetadata;
39
+ /** Any warnings generated during parsing */
40
+ warnings: string[];
41
+ }
42
+ /** Error thrown when DOCX parsing fails */
43
+ export declare class DocxParseError extends Error {
44
+ readonly code: 'CORRUPTED' | 'INVALID_FORMAT' | 'PARSE_ERROR';
45
+ readonly originalError?: Error | undefined;
46
+ constructor(message: string, code: 'CORRUPTED' | 'INVALID_FORMAT' | 'PARSE_ERROR', originalError?: Error | undefined);
47
+ }
48
+ /**
49
+ * Reset image counter (primarily for testing)
50
+ */
51
+ export declare function resetImageCounter(): void;
52
+ /**
53
+ * DOCX Adapter class
54
+ *
55
+ * Handles conversion of DOCX files to HTML using Mammoth.js.
56
+ * Images are converted to placeholder text rather than base64 data
57
+ * to keep output clean and avoid bloating the markdown.
58
+ */
59
+ export declare class DocxAdapter {
60
+ private mammoth;
61
+ /**
62
+ * Load the Mammoth library if not already loaded
63
+ */
64
+ private ensureMammothLoaded;
65
+ /**
66
+ * Parse a DOCX file and convert it to HTML
67
+ *
68
+ * @param arrayBuffer - The DOCX file content as an ArrayBuffer
69
+ * @returns Promise resolving to the parse result with HTML and metadata
70
+ * @throws DocxParseError if the file cannot be parsed
71
+ */
72
+ parseToHtml(arrayBuffer: ArrayBuffer): Promise<DocxParseResult>;
73
+ /**
74
+ * Extract raw text from a DOCX file (without formatting)
75
+ *
76
+ * @param arrayBuffer - The DOCX file content as an ArrayBuffer
77
+ * @returns Promise resolving to the raw text content
78
+ */
79
+ extractRawText(arrayBuffer: ArrayBuffer): Promise<string>;
80
+ }
81
+ /**
82
+ * Singleton instance for convenience
83
+ */
84
+ export declare const docxAdapter: DocxAdapter;
85
+ //# sourceMappingURL=docx-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docx-adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/shared/adapters/docx-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,wBAAwB;IACxB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,2CAA2C;AAC3C,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,WAAW,GAAG,gBAAgB,GAAG,aAAa;aACpD,aAAa,CAAC,EAAE,KAAK;gBAFrC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,WAAW,GAAG,gBAAgB,GAAG,aAAa,EACpD,aAAa,CAAC,EAAE,KAAK,YAAA;CAKxC;AAOD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA2B;IAE1C;;OAEG;YACW,mBAAmB;IAOjC;;;;;;OAMG;IACG,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAsHrE;;;;;OAKG;IACG,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;CAehE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,aAAoB,CAAC"}