@lpenguin/notion-cli 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.
- package/README.md +245 -0
- package/dist/commands/database/create.d.ts +8 -0
- package/dist/commands/database/create.d.ts.map +1 -0
- package/dist/commands/database/create.js +71 -0
- package/dist/commands/database/create.js.map +1 -0
- package/dist/commands/database/delete.d.ts +10 -0
- package/dist/commands/database/delete.d.ts.map +1 -0
- package/dist/commands/database/delete.js +83 -0
- package/dist/commands/database/delete.js.map +1 -0
- package/dist/commands/database/export.d.ts +11 -0
- package/dist/commands/database/export.d.ts.map +1 -0
- package/dist/commands/database/export.js +74 -0
- package/dist/commands/database/export.js.map +1 -0
- package/dist/commands/database/insert.d.ts +11 -0
- package/dist/commands/database/insert.d.ts.map +1 -0
- package/dist/commands/database/insert.js +93 -0
- package/dist/commands/database/insert.js.map +1 -0
- package/dist/commands/database/list.d.ts +10 -0
- package/dist/commands/database/list.d.ts.map +1 -0
- package/dist/commands/database/list.js +80 -0
- package/dist/commands/database/list.js.map +1 -0
- package/dist/commands/database/query.d.ts +10 -0
- package/dist/commands/database/query.d.ts.map +1 -0
- package/dist/commands/database/query.js +77 -0
- package/dist/commands/database/query.js.map +1 -0
- package/dist/commands/database/schema.d.ts +11 -0
- package/dist/commands/database/schema.d.ts.map +1 -0
- package/dist/commands/database/schema.js +76 -0
- package/dist/commands/database/schema.js.map +1 -0
- package/dist/commands/database/update.d.ts +11 -0
- package/dist/commands/database/update.d.ts.map +1 -0
- package/dist/commands/database/update.js +105 -0
- package/dist/commands/database/update.js.map +1 -0
- package/dist/commands/page/create.d.ts +11 -0
- package/dist/commands/page/create.d.ts.map +1 -0
- package/dist/commands/page/create.js +102 -0
- package/dist/commands/page/create.js.map +1 -0
- package/dist/commands/page/list.d.ts +10 -0
- package/dist/commands/page/list.d.ts.map +1 -0
- package/dist/commands/page/list.js +87 -0
- package/dist/commands/page/list.js.map +1 -0
- package/dist/commands/page/patch.d.ts +21 -0
- package/dist/commands/page/patch.d.ts.map +1 -0
- package/dist/commands/page/patch.js +156 -0
- package/dist/commands/page/patch.js.map +1 -0
- package/dist/commands/page/read.d.ts +10 -0
- package/dist/commands/page/read.d.ts.map +1 -0
- package/dist/commands/page/read.js +86 -0
- package/dist/commands/page/read.js.map +1 -0
- package/dist/commands/page/write-properties.d.ts +15 -0
- package/dist/commands/page/write-properties.d.ts.map +1 -0
- package/dist/commands/page/write-properties.js +128 -0
- package/dist/commands/page/write-properties.js.map +1 -0
- package/dist/commands/page/write.d.ts +14 -0
- package/dist/commands/page/write.d.ts.map +1 -0
- package/dist/commands/page/write.js +109 -0
- package/dist/commands/page/write.js.map +1 -0
- package/dist/commands/search.d.ts +18 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +129 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/block-patch.d.ts +61 -0
- package/dist/lib/block-patch.d.ts.map +1 -0
- package/dist/lib/block-patch.js +181 -0
- package/dist/lib/block-patch.js.map +1 -0
- package/dist/lib/client.d.ts +17 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/client.js +63 -0
- package/dist/lib/client.js.map +1 -0
- package/dist/lib/config.d.ts +19 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +65 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/csv.d.ts +45 -0
- package/dist/lib/csv.d.ts.map +1 -0
- package/dist/lib/csv.js +262 -0
- package/dist/lib/csv.js.map +1 -0
- package/dist/lib/db-properties.d.ts +11 -0
- package/dist/lib/db-properties.d.ts.map +1 -0
- package/dist/lib/db-properties.js +25 -0
- package/dist/lib/db-properties.js.map +1 -0
- package/dist/lib/errors.d.ts +34 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +86 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-upload.d.ts +25 -0
- package/dist/lib/file-upload.d.ts.map +1 -0
- package/dist/lib/file-upload.js +90 -0
- package/dist/lib/file-upload.js.map +1 -0
- package/dist/lib/markdown.d.ts +79 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +320 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/output.d.ts +20 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +67 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/patch.d.ts +23 -0
- package/dist/lib/patch.d.ts.map +1 -0
- package/dist/lib/patch.js +72 -0
- package/dist/lib/patch.js.map +1 -0
- package/dist/lib/rate-limit.d.ts +9 -0
- package/dist/lib/rate-limit.d.ts.map +1 -0
- package/dist/lib/rate-limit.js +67 -0
- package/dist/lib/rate-limit.js.map +1 -0
- package/dist/lib/safety.d.ts +17 -0
- package/dist/lib/safety.d.ts.map +1 -0
- package/dist/lib/safety.js +60 -0
- package/dist/lib/safety.js.map +1 -0
- package/dist/lib/types.d.ts +138 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +13 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/validator.d.ts +33 -0
- package/dist/lib/validator.d.ts.map +1 -0
- package/dist/lib/validator.js +68 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/utils/id.d.ts +14 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +33 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +43 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/string.d.ts +14 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +37 -0
- package/dist/utils/string.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown conversion wrappers.
|
|
3
|
+
*
|
|
4
|
+
* - Markdown → Notion blocks: @tryfabric/martian
|
|
5
|
+
* - Notion → Markdown: notion-to-md (with unified converter)
|
|
6
|
+
*
|
|
7
|
+
* This module provides a clean interface over both libraries.
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT: All Notion→Markdown conversion uses `mdBlocksToMarkdown()`
|
|
10
|
+
* to ensure consistent output between `page read` and `page patch`.
|
|
11
|
+
*/
|
|
12
|
+
import { markdownToBlocks } from '@tryfabric/martian';
|
|
13
|
+
import { NotionToMarkdown } from 'notion-to-md';
|
|
14
|
+
import {} from 'notion-to-md/build/types/index.js';
|
|
15
|
+
import {} from '@notionhq/client';
|
|
16
|
+
import {} from '@notionhq/client/build/src/api-endpoints.js';
|
|
17
|
+
import {} from './types.js';
|
|
18
|
+
import * as logger from '../utils/logger.js';
|
|
19
|
+
import { withRateLimit } from './rate-limit.js';
|
|
20
|
+
import { isFileUrl, fileUrlToPath, uploadFileToNotion } from './file-upload.js';
|
|
21
|
+
/**
|
|
22
|
+
* Custom implementation of blocksToMarkdown that fetches children in parallel using a queue.
|
|
23
|
+
*/
|
|
24
|
+
async function blocksToMarkdownParallel(n2m, client, blocks) {
|
|
25
|
+
if (!blocks)
|
|
26
|
+
return [];
|
|
27
|
+
const mdBlocks = await Promise.all(blocks.map(async (block, index) => {
|
|
28
|
+
const result = {
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
type: block.type,
|
|
31
|
+
blockId: block.id,
|
|
32
|
+
parent: '',
|
|
33
|
+
children: [],
|
|
34
|
+
};
|
|
35
|
+
// Skip unsupported or restricted blocks
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
if (block.type === 'unsupported' || !('type' in block)) {
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
// Handle numbered list items: inject the current index for markdown conversion
|
|
41
|
+
// Notion API returns chunks, and typically notion-to-md expects to calculate
|
|
42
|
+
// these IDs sequentially. Here we simulate the index for the current list.
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
if (block.type === 'numbered_list_item') {
|
|
45
|
+
// Calculate the relative index in the current list sequence
|
|
46
|
+
let listIndex = 1;
|
|
47
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
48
|
+
const prevBlock = blocks[i];
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
if (prevBlock && 'type' in prevBlock && prevBlock.type === 'numbered_list_item') {
|
|
51
|
+
listIndex++;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// @ts-ignore
|
|
58
|
+
block.numbered_list_item.number = listIndex;
|
|
59
|
+
}
|
|
60
|
+
// Convert the block itself to markdown.
|
|
61
|
+
// We don't queue this as it's a local CPU operation, but we await it.
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
result.parent = await n2m.blockToMarkdown(block);
|
|
64
|
+
// Recursive fetch for children
|
|
65
|
+
if ('has_children' in block && block.has_children) {
|
|
66
|
+
const blockId = block.id;
|
|
67
|
+
// Fetch children using the shared specialized queue
|
|
68
|
+
const childBlocks = await withRateLimit(async () => {
|
|
69
|
+
const results = [];
|
|
70
|
+
let cursor;
|
|
71
|
+
do {
|
|
72
|
+
const response = await client.blocks.children.list({
|
|
73
|
+
block_id: blockId,
|
|
74
|
+
start_cursor: cursor,
|
|
75
|
+
});
|
|
76
|
+
results.push(...response.results);
|
|
77
|
+
cursor = response.next_cursor ?? undefined;
|
|
78
|
+
} while (cursor !== undefined);
|
|
79
|
+
return results;
|
|
80
|
+
}, `blocks.children.list(${blockId})`);
|
|
81
|
+
// Recursively convert children
|
|
82
|
+
result.children = await blocksToMarkdownParallel(n2m, client, childBlocks);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}));
|
|
86
|
+
return mdBlocks;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Convert a Markdown string to an array of Notion block objects.
|
|
90
|
+
* Uses @tryfabric/martian for the conversion.
|
|
91
|
+
*/
|
|
92
|
+
export function markdownToNotionBlocks(markdown) {
|
|
93
|
+
logger.debug(`Converting ${String(markdown.length)} chars of Markdown to Notion blocks.`);
|
|
94
|
+
const blocks = markdownToBlocks(markdown);
|
|
95
|
+
logger.debug(`Produced ${String(blocks.length)} Notion blocks.`);
|
|
96
|
+
return blocks;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Process blocks and upload any file:// images to Notion.
|
|
100
|
+
* Returns modified blocks with uploaded file IDs.
|
|
101
|
+
*/
|
|
102
|
+
export async function processImageUploads(client, blocks) {
|
|
103
|
+
const processedBlocks = [];
|
|
104
|
+
for (const block of blocks) {
|
|
105
|
+
let processedBlock = block;
|
|
106
|
+
// Check if this is an image block with external URL
|
|
107
|
+
if (block.type === 'image' && 'image' in block) {
|
|
108
|
+
const imageBlock = block.image;
|
|
109
|
+
// Check if it's an external image
|
|
110
|
+
if ('type' in imageBlock && imageBlock.type === 'external' && 'external' in imageBlock) {
|
|
111
|
+
const external = imageBlock.external;
|
|
112
|
+
if ('url' in external && typeof external.url === 'string' && isFileUrl(external.url)) {
|
|
113
|
+
// This is a file:// URL, upload it
|
|
114
|
+
const filePath = fileUrlToPath(external.url);
|
|
115
|
+
try {
|
|
116
|
+
const fileUploadId = await uploadFileToNotion(client, filePath);
|
|
117
|
+
// Replace with file_upload reference
|
|
118
|
+
processedBlock = {
|
|
119
|
+
...block,
|
|
120
|
+
image: {
|
|
121
|
+
type: 'file_upload',
|
|
122
|
+
file_upload: {
|
|
123
|
+
id: fileUploadId,
|
|
124
|
+
},
|
|
125
|
+
caption: 'caption' in imageBlock ? imageBlock.caption : undefined,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
logger.debug(`Replaced file:// image with upload ID: ${fileUploadId}`);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
logger.debug(`Failed to upload image ${filePath}: ${String(err)}`);
|
|
132
|
+
// Keep the original block if upload fails
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
processedBlocks.push(processedBlock);
|
|
138
|
+
}
|
|
139
|
+
return processedBlocks;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Unified MdBlock to Markdown converter.
|
|
143
|
+
*
|
|
144
|
+
* Converts MdBlocks to markdown while tracking which lines each block contributes.
|
|
145
|
+
* This is the SINGLE source of truth for Notion→Markdown conversion.
|
|
146
|
+
*
|
|
147
|
+
* Used by both `page read` and `page patch` to ensure consistent line numbers.
|
|
148
|
+
*
|
|
149
|
+
* @param blocks - Array of MdBlocks from notion-to-md
|
|
150
|
+
* @returns The markdown string and a mapping of blocks to line ranges
|
|
151
|
+
*/
|
|
152
|
+
export function mdBlocksToMarkdown(blocks) {
|
|
153
|
+
const mappings = [];
|
|
154
|
+
const outputParts = [];
|
|
155
|
+
let currentLine = 1;
|
|
156
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
157
|
+
const block = blocks[i];
|
|
158
|
+
// Add blank line before headers (except the first block).
|
|
159
|
+
// Only add if previous part isn't already empty to avoid triple newlines.
|
|
160
|
+
if (i > 0 && block.parent.trimStart().startsWith('#')) {
|
|
161
|
+
const prevPart = outputParts[outputParts.length - 1] ?? '';
|
|
162
|
+
if (prevPart !== '') {
|
|
163
|
+
outputParts.push(''); // blank-line separator
|
|
164
|
+
currentLine += 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const result = processBlockToMarkdown(block, currentLine, 0);
|
|
168
|
+
mappings.push(result.mapping);
|
|
169
|
+
outputParts.push(result.markdown);
|
|
170
|
+
// Count lines in this block's markdown
|
|
171
|
+
const blockLineCount = result.markdown.split('\n').length;
|
|
172
|
+
currentLine += blockLineCount;
|
|
173
|
+
}
|
|
174
|
+
const markdown = outputParts.join('\n').trim();
|
|
175
|
+
return { markdown, mappings };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Process a single block and its children recursively.
|
|
179
|
+
* Returns the markdown and line mapping for this block.
|
|
180
|
+
*/
|
|
181
|
+
function processBlockToMarkdown(block, startLine, indentLevel) {
|
|
182
|
+
const indent = ' '.repeat(indentLevel);
|
|
183
|
+
let markdown = '';
|
|
184
|
+
const childMappings = [];
|
|
185
|
+
// The block's own content (already converted to markdown by notion-to-md)
|
|
186
|
+
const parentContent = block.parent;
|
|
187
|
+
const parentLines = parentContent.split('\n');
|
|
188
|
+
// Apply indent to each line of parent content
|
|
189
|
+
const indentedParent = parentLines
|
|
190
|
+
.map((line) => (line === '' ? '' : `${indent}${line}`))
|
|
191
|
+
.join('\n');
|
|
192
|
+
markdown += indentedParent;
|
|
193
|
+
let currentLine = startLine + parentLines.length;
|
|
194
|
+
// Process children with increased indentation
|
|
195
|
+
if (block.children.length > 0) {
|
|
196
|
+
for (const child of block.children) {
|
|
197
|
+
// Add newline separator (ends the previous line, doesn't create a blank line)
|
|
198
|
+
markdown += '\n';
|
|
199
|
+
const childResult = processBlockToMarkdown(child, currentLine, indentLevel + 1);
|
|
200
|
+
childMappings.push(childResult.mapping);
|
|
201
|
+
markdown += childResult.markdown;
|
|
202
|
+
currentLine += childResult.markdown.split('\n').length;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Calculate end line
|
|
206
|
+
const endLine = startLine + markdown.split('\n').length - 1;
|
|
207
|
+
const mapping = {
|
|
208
|
+
blockId: block.blockId,
|
|
209
|
+
type: block.type ?? 'unknown',
|
|
210
|
+
startLine,
|
|
211
|
+
endLine,
|
|
212
|
+
markdown: indentedParent, // Just this block's content, not children
|
|
213
|
+
children: childMappings,
|
|
214
|
+
};
|
|
215
|
+
return { mapping, markdown };
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Fetch a Notion page's content as MdBlocks (preserving block IDs).
|
|
219
|
+
* Used for surgical patching where we need to track block → line mappings.
|
|
220
|
+
*/
|
|
221
|
+
export async function fetchPageMdBlocks(client, pageId) {
|
|
222
|
+
logger.debug(`Fetching page ${pageId} as MdBlocks.`);
|
|
223
|
+
const n2m = new NotionToMarkdown({ notionClient: client });
|
|
224
|
+
// Custom transformer to prevent embedding child pages (only show a link/title)
|
|
225
|
+
n2m.setCustomTransformer('child_page', (block) => {
|
|
226
|
+
const cpBlock = block;
|
|
227
|
+
const title = cpBlock.child_page?.title ?? 'Untitled Page';
|
|
228
|
+
const id = block.id.replace(/-/g, '');
|
|
229
|
+
return `[[${title}]](https://www.notion.so/${id})`;
|
|
230
|
+
});
|
|
231
|
+
// Fetch top-level blocks
|
|
232
|
+
const topLevelBlocks = await withRateLimit(async () => {
|
|
233
|
+
const results = [];
|
|
234
|
+
let cursor;
|
|
235
|
+
do {
|
|
236
|
+
const response = await client.blocks.children.list({
|
|
237
|
+
block_id: pageId,
|
|
238
|
+
start_cursor: cursor,
|
|
239
|
+
});
|
|
240
|
+
results.push(...response.results);
|
|
241
|
+
cursor = response.next_cursor ?? undefined;
|
|
242
|
+
} while (cursor !== undefined);
|
|
243
|
+
return results;
|
|
244
|
+
}, `blocks.children.list(${pageId})`);
|
|
245
|
+
// Convert blocks to markdown with parallel fetching for children via queue
|
|
246
|
+
const blocks = await blocksToMarkdownParallel(n2m, client, topLevelBlocks);
|
|
247
|
+
// Fix child_page blocks to be treated as paragraphs
|
|
248
|
+
fixChildPageBlocks(blocks);
|
|
249
|
+
logger.debug(`Fetched ${String(blocks.length)} MdBlocks.`);
|
|
250
|
+
return blocks;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Fetch a Notion page's content and convert to Markdown.
|
|
254
|
+
* Uses the unified mdBlocksToMarkdown converter.
|
|
255
|
+
*/
|
|
256
|
+
export async function notionPageToMarkdown(client, pageId) {
|
|
257
|
+
logger.debug(`Fetching page ${pageId} and converting to Markdown.`);
|
|
258
|
+
const blocks = await fetchPageMdBlocks(client, pageId);
|
|
259
|
+
const { markdown } = mdBlocksToMarkdown(blocks);
|
|
260
|
+
logger.debug(`Converted page to ${String(markdown.length)} chars of Markdown.`);
|
|
261
|
+
return markdown;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Clean raw Markdown output:
|
|
265
|
+
* - Trim leading/trailing whitespace
|
|
266
|
+
* - Collapse 3+ consecutive newlines into exactly 2
|
|
267
|
+
*/
|
|
268
|
+
export function cleanMarkdownOutput(raw) {
|
|
269
|
+
return raw
|
|
270
|
+
.trim()
|
|
271
|
+
.replace(/\n{3,}/g, '\n\n');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Recursively rewrite child_page blocks to paragraph so that
|
|
275
|
+
* toMarkdownString includes them in the output.
|
|
276
|
+
*/
|
|
277
|
+
export function fixChildPageBlocks(mdBlocks) {
|
|
278
|
+
for (const b of mdBlocks) {
|
|
279
|
+
if (b.type === 'child_page') {
|
|
280
|
+
b.type = 'paragraph';
|
|
281
|
+
}
|
|
282
|
+
if (b.children.length > 0) {
|
|
283
|
+
fixChildPageBlocks(b.children);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Convert Markdown to numbered-line format for patch operations.
|
|
289
|
+
* Each line is prefixed with its 1-based line number.
|
|
290
|
+
*
|
|
291
|
+
* Example output:
|
|
292
|
+
* 1: # Hello World
|
|
293
|
+
* 2:
|
|
294
|
+
* 3: Some content here.
|
|
295
|
+
*/
|
|
296
|
+
export function addLineNumbers(markdown) {
|
|
297
|
+
const lines = markdown.split('\n');
|
|
298
|
+
const padWidth = String(lines.length).length;
|
|
299
|
+
return lines
|
|
300
|
+
.map((line, i) => `${String(i + 1).padStart(padWidth)}: ${line}`)
|
|
301
|
+
.join('\n');
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Remove line numbers from numbered-line format.
|
|
305
|
+
* Strips the "N: " prefix from each line.
|
|
306
|
+
*/
|
|
307
|
+
export function stripLineNumbers(numbered) {
|
|
308
|
+
return numbered
|
|
309
|
+
.split('\n')
|
|
310
|
+
.map((line) => line.replace(/^\s*\d+:\s?/, ''))
|
|
311
|
+
.join('\n');
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Extract the title (first H1) from Markdown content.
|
|
315
|
+
*/
|
|
316
|
+
export function extractTitle(markdown) {
|
|
317
|
+
const match = /^#\s+(.+)$/m.exec(markdown);
|
|
318
|
+
return match?.[1]?.trim() ?? 'Untitled';
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatter — JSON mode for AI agents, human-readable for terminals.
|
|
3
|
+
* Data always goes to stdout; status/progress goes to stderr (via logger).
|
|
4
|
+
*/
|
|
5
|
+
import { type CliResponse, type ResponseMeta } from './types.js';
|
|
6
|
+
export declare function setJsonMode(enabled: boolean): void;
|
|
7
|
+
export declare function isJsonMode(): boolean;
|
|
8
|
+
/** Output a successful result. */
|
|
9
|
+
export declare function printSuccess<T>(data: T, meta?: ResponseMeta): void;
|
|
10
|
+
/** Output an error result. */
|
|
11
|
+
export declare function printError(code: string, message: string, details?: unknown): void;
|
|
12
|
+
/** Format a table of key-value pairs for human display. */
|
|
13
|
+
export declare function formatTable(rows: readonly (readonly [string, string])[]): string;
|
|
14
|
+
/** Format a list of items with index numbers. */
|
|
15
|
+
export declare function formatList(items: readonly string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* Wrap a command handler: catches errors, formats output, sets exit code.
|
|
18
|
+
*/
|
|
19
|
+
export declare function wrapOutput(response: CliResponse): void;
|
|
20
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,WAAW,EAAkD,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAIjH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAElD;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,kCAAkC;AAClC,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAWlE;AAED,8BAA8B;AAC9B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAajF;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,MAAM,CAKhF;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAMtD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatter — JSON mode for AI agents, human-readable for terminals.
|
|
3
|
+
* Data always goes to stdout; status/progress goes to stderr (via logger).
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import {} from './types.js';
|
|
7
|
+
let jsonMode = false;
|
|
8
|
+
export function setJsonMode(enabled) {
|
|
9
|
+
jsonMode = enabled;
|
|
10
|
+
}
|
|
11
|
+
export function isJsonMode() {
|
|
12
|
+
return jsonMode;
|
|
13
|
+
}
|
|
14
|
+
/** Output a successful result. */
|
|
15
|
+
export function printSuccess(data, meta) {
|
|
16
|
+
if (jsonMode) {
|
|
17
|
+
const response = { ok: true, data, ...(meta !== undefined ? { meta } : {}) };
|
|
18
|
+
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
if (typeof data === 'string') {
|
|
22
|
+
process.stdout.write(`${data}\n`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Output an error result. */
|
|
30
|
+
export function printError(code, message, details) {
|
|
31
|
+
if (jsonMode) {
|
|
32
|
+
const response = {
|
|
33
|
+
ok: false,
|
|
34
|
+
error: { code, message, ...(details !== undefined ? { details } : {}) },
|
|
35
|
+
};
|
|
36
|
+
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
process.stderr.write(`${chalk.red('Error')} [${code}]: ${message}\n`);
|
|
40
|
+
if (details !== undefined) {
|
|
41
|
+
process.stderr.write(`${chalk.gray(JSON.stringify(details, null, 2))}\n`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Format a table of key-value pairs for human display. */
|
|
46
|
+
export function formatTable(rows) {
|
|
47
|
+
const maxKeyLen = Math.max(...rows.map(([key]) => key.length));
|
|
48
|
+
return rows
|
|
49
|
+
.map(([key, value]) => ` ${chalk.bold(key.padEnd(maxKeyLen))} ${value}`)
|
|
50
|
+
.join('\n');
|
|
51
|
+
}
|
|
52
|
+
/** Format a list of items with index numbers. */
|
|
53
|
+
export function formatList(items) {
|
|
54
|
+
return items.map((item, i) => ` ${chalk.gray(`${String(i + 1)}.`)} ${item}`).join('\n');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Wrap a command handler: catches errors, formats output, sets exit code.
|
|
58
|
+
*/
|
|
59
|
+
export function wrapOutput(response) {
|
|
60
|
+
if (response.ok) {
|
|
61
|
+
printSuccess(response.data, response.meta);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
printError(response.error.code, response.error.message, response.error.details);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch engine — apply edits to Markdown content.
|
|
3
|
+
*
|
|
4
|
+
* Line-range replacement (--lines START:END)
|
|
5
|
+
* Replace lines START through END with new content.
|
|
6
|
+
* Familiar to coding agents that work with line numbers.
|
|
7
|
+
*/
|
|
8
|
+
import { type PatchOperation } from './types.js';
|
|
9
|
+
/** Result of applying a patch. */
|
|
10
|
+
export interface PatchResult {
|
|
11
|
+
/** The patched Markdown content. */
|
|
12
|
+
readonly patched: string;
|
|
13
|
+
/** Human-readable unified diff of the changes. */
|
|
14
|
+
readonly diff: string;
|
|
15
|
+
/** Number of lines changed. */
|
|
16
|
+
readonly linesChanged: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Apply a patch operation to Markdown content.
|
|
20
|
+
* Returns the patched content, a diff of changes, and line count.
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyPatchOperation(original: string, operation: PatchOperation): PatchResult;
|
|
23
|
+
//# sourceMappingURL=patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../src/lib/patch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,YAAY,CAAC;AAItE,kCAAkC;AAClC,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,cAAc,GACxB,WAAW,CAEb"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patch engine — apply edits to Markdown content.
|
|
3
|
+
*
|
|
4
|
+
* Line-range replacement (--lines START:END)
|
|
5
|
+
* Replace lines START through END with new content.
|
|
6
|
+
* Familiar to coding agents that work with line numbers.
|
|
7
|
+
*/
|
|
8
|
+
import { createTwoFilesPatch } from 'diff';
|
|
9
|
+
import {} from './types.js';
|
|
10
|
+
import { ValidationError } from './errors.js';
|
|
11
|
+
import * as logger from '../utils/logger.js';
|
|
12
|
+
/**
|
|
13
|
+
* Apply a patch operation to Markdown content.
|
|
14
|
+
* Returns the patched content, a diff of changes, and line count.
|
|
15
|
+
*/
|
|
16
|
+
export function applyPatchOperation(original, operation) {
|
|
17
|
+
return applyLineRange(original, operation);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Line-range replacement.
|
|
21
|
+
*
|
|
22
|
+
* Replace lines [start, end] (1-indexed, inclusive) with new content.
|
|
23
|
+
*
|
|
24
|
+
* Special cases:
|
|
25
|
+
* - start === end: Replace a single line.
|
|
26
|
+
* - Content is empty: Delete the lines.
|
|
27
|
+
*/
|
|
28
|
+
function applyLineRange(original, op) {
|
|
29
|
+
const lines = original.split('\n');
|
|
30
|
+
const totalLines = lines.length;
|
|
31
|
+
// Validate range
|
|
32
|
+
if (op.start < 1) {
|
|
33
|
+
throw new ValidationError('Line range start must be >= 1.');
|
|
34
|
+
}
|
|
35
|
+
const effectiveEnd = Math.min(op.end, totalLines);
|
|
36
|
+
if (op.start > totalLines + 1) {
|
|
37
|
+
throw new ValidationError(`Start line ${String(op.start)} exceeds document length (${String(totalLines)} lines).`);
|
|
38
|
+
}
|
|
39
|
+
logger.debug(`Replacing lines ${String(op.start)}-${String(effectiveEnd)} (of ${String(totalLines)}) with ${String(op.content.split('\n').length)} lines.`);
|
|
40
|
+
// Build new content
|
|
41
|
+
const before = lines.slice(0, op.start - 1);
|
|
42
|
+
const after = lines.slice(effectiveEnd);
|
|
43
|
+
const newLines = op.content === '' ? [] : op.content.split('\n');
|
|
44
|
+
const patched = [...before, ...newLines, ...after].join('\n');
|
|
45
|
+
return buildResult(original, patched);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build a PatchResult from before/after content.
|
|
49
|
+
*/
|
|
50
|
+
function buildResult(original, patched) {
|
|
51
|
+
const diff = createTwoFilesPatch('before', 'after', original, patched, '', '', {
|
|
52
|
+
context: 3,
|
|
53
|
+
});
|
|
54
|
+
// Count changed lines
|
|
55
|
+
const diffLines = diff.split('\n');
|
|
56
|
+
let added = 0;
|
|
57
|
+
let removed = 0;
|
|
58
|
+
for (const line of diffLines) {
|
|
59
|
+
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
60
|
+
added++;
|
|
61
|
+
}
|
|
62
|
+
if (line.startsWith('-') && !line.startsWith('---')) {
|
|
63
|
+
removed++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
patched,
|
|
68
|
+
diff,
|
|
69
|
+
linesChanged: added + removed,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/lib/patch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate-limit handling with exponential backoff.
|
|
3
|
+
* Notion API has a 3 requests/second rate limit.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Execute an async function with both concurrency control and automatic retry on rate-limits.
|
|
7
|
+
*/
|
|
8
|
+
export declare function withRateLimit<T>(fn: () => Promise<T>, label?: string): Promise<T>;
|
|
9
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/lib/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH;;GAEG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,SAAa,GACjB,OAAO,CAAC,CAAC,CAAC,CAEZ"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate-limit handling with exponential backoff.
|
|
3
|
+
* Notion API has a 3 requests/second rate limit.
|
|
4
|
+
*/
|
|
5
|
+
import * as logger from '../utils/logger.js';
|
|
6
|
+
import { RateLimitError } from './errors.js';
|
|
7
|
+
import PQueue from 'p-queue';
|
|
8
|
+
const MAX_RETRIES = 5;
|
|
9
|
+
const BASE_DELAY_MS = 1000;
|
|
10
|
+
const MAX_DELAY_MS = 30_000;
|
|
11
|
+
/**
|
|
12
|
+
* Global queue for Notion API requests.
|
|
13
|
+
* Concurrency is set to 3 to stay within Notion's recommended rate limits.
|
|
14
|
+
*/
|
|
15
|
+
const notionQueue = new PQueue({ concurrency: 3 });
|
|
16
|
+
/**
|
|
17
|
+
* Execute an async function with both concurrency control and automatic retry on rate-limits.
|
|
18
|
+
*/
|
|
19
|
+
export async function withRateLimit(fn, label = 'API call') {
|
|
20
|
+
return notionQueue.add(() => withRetry(fn, label));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Execute an async function with automatic retry on rate-limit (429) errors.
|
|
24
|
+
* Uses exponential backoff with jitter.
|
|
25
|
+
*/
|
|
26
|
+
async function withRetry(fn, label = 'API call') {
|
|
27
|
+
let lastError;
|
|
28
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
29
|
+
try {
|
|
30
|
+
return await fn();
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
lastError = err;
|
|
34
|
+
if (!isRateLimitError(err)) {
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
if (attempt === MAX_RETRIES) {
|
|
38
|
+
throw new RateLimitError(MAX_DELAY_MS);
|
|
39
|
+
}
|
|
40
|
+
const delay = calculateDelay(attempt);
|
|
41
|
+
logger.warn(`${label}: Rate limited (attempt ${String(attempt + 1)}/${String(MAX_RETRIES)}). Retrying in ${String(Math.ceil(delay / 1000))}s...`);
|
|
42
|
+
await sleep(delay);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// This should be unreachable, but satisfies noImplicitReturns
|
|
46
|
+
throw lastError;
|
|
47
|
+
}
|
|
48
|
+
function isRateLimitError(err) {
|
|
49
|
+
if (typeof err !== 'object' || err === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const status = err['status'];
|
|
53
|
+
return status === 429;
|
|
54
|
+
}
|
|
55
|
+
function calculateDelay(attempt) {
|
|
56
|
+
const exponential = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
57
|
+
const capped = Math.min(exponential, MAX_DELAY_MS);
|
|
58
|
+
// Add jitter: 50-100% of the delay
|
|
59
|
+
const jitter = capped * (0.5 + Math.random() * 0.5);
|
|
60
|
+
return Math.round(jitter);
|
|
61
|
+
}
|
|
62
|
+
function sleep(ms) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
setTimeout(resolve, ms);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/lib/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B;;;GAGG;AACH,MAAM,WAAW,GAAW,IAAI,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAoB,EACpB,KAAK,GAAG,UAAU;IAElB,OAAO,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,SAAS,CACtB,EAAoB,EACpB,KAAK,GAAG,UAAU;IAElB,IAAI,SAAkB,CAAC;IAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,SAAS,GAAG,GAAG,CAAC;YAEhB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,GAAG,CAAC;YACZ,CAAC;YAED,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;gBAC5B,MAAM,IAAI,cAAc,CAAC,YAAY,CAAC,CAAC;YACzC,CAAC;YAED,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CACT,GAAG,KAAK,2BAA2B,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,kBAAkB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,MAAM,CACrI,CAAC;YACF,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAI,GAA+B,CAAC,QAAQ,CAAC,CAAC;IAC1D,OAAO,MAAM,KAAK,GAAG,CAAC;AACxB,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,WAAW,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACnD,mCAAmC;IACnC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety module — confirmation prompts, dry-run, and write guards.
|
|
3
|
+
*
|
|
4
|
+
* This is the single point of control for write safety.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Guard for dry-run mode.
|
|
8
|
+
* Returns true if we should skip the actual write (dry-run is active).
|
|
9
|
+
*/
|
|
10
|
+
export declare function isDryRun(dryRun?: boolean): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Display a diff preview before applying changes.
|
|
13
|
+
*
|
|
14
|
+
* Shows a colored unified diff of the changes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function showDiffPreview(before: string, after: string): void;
|
|
17
|
+
//# sourceMappingURL=safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/lib/safety.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAwBnE"}
|