@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.
Files changed (134) hide show
  1. package/README.md +245 -0
  2. package/dist/commands/database/create.d.ts +8 -0
  3. package/dist/commands/database/create.d.ts.map +1 -0
  4. package/dist/commands/database/create.js +71 -0
  5. package/dist/commands/database/create.js.map +1 -0
  6. package/dist/commands/database/delete.d.ts +10 -0
  7. package/dist/commands/database/delete.d.ts.map +1 -0
  8. package/dist/commands/database/delete.js +83 -0
  9. package/dist/commands/database/delete.js.map +1 -0
  10. package/dist/commands/database/export.d.ts +11 -0
  11. package/dist/commands/database/export.d.ts.map +1 -0
  12. package/dist/commands/database/export.js +74 -0
  13. package/dist/commands/database/export.js.map +1 -0
  14. package/dist/commands/database/insert.d.ts +11 -0
  15. package/dist/commands/database/insert.d.ts.map +1 -0
  16. package/dist/commands/database/insert.js +93 -0
  17. package/dist/commands/database/insert.js.map +1 -0
  18. package/dist/commands/database/list.d.ts +10 -0
  19. package/dist/commands/database/list.d.ts.map +1 -0
  20. package/dist/commands/database/list.js +80 -0
  21. package/dist/commands/database/list.js.map +1 -0
  22. package/dist/commands/database/query.d.ts +10 -0
  23. package/dist/commands/database/query.d.ts.map +1 -0
  24. package/dist/commands/database/query.js +77 -0
  25. package/dist/commands/database/query.js.map +1 -0
  26. package/dist/commands/database/schema.d.ts +11 -0
  27. package/dist/commands/database/schema.d.ts.map +1 -0
  28. package/dist/commands/database/schema.js +76 -0
  29. package/dist/commands/database/schema.js.map +1 -0
  30. package/dist/commands/database/update.d.ts +11 -0
  31. package/dist/commands/database/update.d.ts.map +1 -0
  32. package/dist/commands/database/update.js +105 -0
  33. package/dist/commands/database/update.js.map +1 -0
  34. package/dist/commands/page/create.d.ts +11 -0
  35. package/dist/commands/page/create.d.ts.map +1 -0
  36. package/dist/commands/page/create.js +102 -0
  37. package/dist/commands/page/create.js.map +1 -0
  38. package/dist/commands/page/list.d.ts +10 -0
  39. package/dist/commands/page/list.d.ts.map +1 -0
  40. package/dist/commands/page/list.js +87 -0
  41. package/dist/commands/page/list.js.map +1 -0
  42. package/dist/commands/page/patch.d.ts +21 -0
  43. package/dist/commands/page/patch.d.ts.map +1 -0
  44. package/dist/commands/page/patch.js +156 -0
  45. package/dist/commands/page/patch.js.map +1 -0
  46. package/dist/commands/page/read.d.ts +10 -0
  47. package/dist/commands/page/read.d.ts.map +1 -0
  48. package/dist/commands/page/read.js +86 -0
  49. package/dist/commands/page/read.js.map +1 -0
  50. package/dist/commands/page/write-properties.d.ts +15 -0
  51. package/dist/commands/page/write-properties.d.ts.map +1 -0
  52. package/dist/commands/page/write-properties.js +128 -0
  53. package/dist/commands/page/write-properties.js.map +1 -0
  54. package/dist/commands/page/write.d.ts +14 -0
  55. package/dist/commands/page/write.d.ts.map +1 -0
  56. package/dist/commands/page/write.js +109 -0
  57. package/dist/commands/page/write.js.map +1 -0
  58. package/dist/commands/search.d.ts +18 -0
  59. package/dist/commands/search.d.ts.map +1 -0
  60. package/dist/commands/search.js +129 -0
  61. package/dist/commands/search.js.map +1 -0
  62. package/dist/index.d.ts +37 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +121 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/lib/block-patch.d.ts +61 -0
  67. package/dist/lib/block-patch.d.ts.map +1 -0
  68. package/dist/lib/block-patch.js +181 -0
  69. package/dist/lib/block-patch.js.map +1 -0
  70. package/dist/lib/client.d.ts +17 -0
  71. package/dist/lib/client.d.ts.map +1 -0
  72. package/dist/lib/client.js +63 -0
  73. package/dist/lib/client.js.map +1 -0
  74. package/dist/lib/config.d.ts +19 -0
  75. package/dist/lib/config.d.ts.map +1 -0
  76. package/dist/lib/config.js +65 -0
  77. package/dist/lib/config.js.map +1 -0
  78. package/dist/lib/csv.d.ts +45 -0
  79. package/dist/lib/csv.d.ts.map +1 -0
  80. package/dist/lib/csv.js +262 -0
  81. package/dist/lib/csv.js.map +1 -0
  82. package/dist/lib/db-properties.d.ts +11 -0
  83. package/dist/lib/db-properties.d.ts.map +1 -0
  84. package/dist/lib/db-properties.js +25 -0
  85. package/dist/lib/db-properties.js.map +1 -0
  86. package/dist/lib/errors.d.ts +34 -0
  87. package/dist/lib/errors.d.ts.map +1 -0
  88. package/dist/lib/errors.js +86 -0
  89. package/dist/lib/errors.js.map +1 -0
  90. package/dist/lib/file-upload.d.ts +25 -0
  91. package/dist/lib/file-upload.d.ts.map +1 -0
  92. package/dist/lib/file-upload.js +90 -0
  93. package/dist/lib/file-upload.js.map +1 -0
  94. package/dist/lib/markdown.d.ts +79 -0
  95. package/dist/lib/markdown.d.ts.map +1 -0
  96. package/dist/lib/markdown.js +320 -0
  97. package/dist/lib/markdown.js.map +1 -0
  98. package/dist/lib/output.d.ts +20 -0
  99. package/dist/lib/output.d.ts.map +1 -0
  100. package/dist/lib/output.js +67 -0
  101. package/dist/lib/output.js.map +1 -0
  102. package/dist/lib/patch.d.ts +23 -0
  103. package/dist/lib/patch.d.ts.map +1 -0
  104. package/dist/lib/patch.js +72 -0
  105. package/dist/lib/patch.js.map +1 -0
  106. package/dist/lib/rate-limit.d.ts +9 -0
  107. package/dist/lib/rate-limit.d.ts.map +1 -0
  108. package/dist/lib/rate-limit.js +67 -0
  109. package/dist/lib/rate-limit.js.map +1 -0
  110. package/dist/lib/safety.d.ts +17 -0
  111. package/dist/lib/safety.d.ts.map +1 -0
  112. package/dist/lib/safety.js +60 -0
  113. package/dist/lib/safety.js.map +1 -0
  114. package/dist/lib/types.d.ts +138 -0
  115. package/dist/lib/types.d.ts.map +1 -0
  116. package/dist/lib/types.js +13 -0
  117. package/dist/lib/types.js.map +1 -0
  118. package/dist/lib/validator.d.ts +33 -0
  119. package/dist/lib/validator.d.ts.map +1 -0
  120. package/dist/lib/validator.js +68 -0
  121. package/dist/lib/validator.js.map +1 -0
  122. package/dist/utils/id.d.ts +14 -0
  123. package/dist/utils/id.d.ts.map +1 -0
  124. package/dist/utils/id.js +33 -0
  125. package/dist/utils/id.js.map +1 -0
  126. package/dist/utils/logger.d.ts +20 -0
  127. package/dist/utils/logger.d.ts.map +1 -0
  128. package/dist/utils/logger.js +43 -0
  129. package/dist/utils/logger.js.map +1 -0
  130. package/dist/utils/string.d.ts +14 -0
  131. package/dist/utils/string.d.ts.map +1 -0
  132. package/dist/utils/string.js +37 -0
  133. package/dist/utils/string.js.map +1 -0
  134. package/package.json +64 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * notion-cli page list [--query <text>]
3
+ *
4
+ * Search/list Notion pages accessible to the integration.
5
+ *
6
+ * This command is idempotent and read-only.
7
+ */
8
+ import {} from 'commander';
9
+ import { isFullPage } from '@notionhq/client';
10
+ import { getClient } from '../../lib/client.js';
11
+ import { printSuccess, printError, formatTable } from '../../lib/output.js';
12
+ import { isJsonMode } from '../../lib/output.js';
13
+ import { withRateLimit } from '../../lib/rate-limit.js';
14
+ import {} from '../../lib/types.js';
15
+ import { toCliError } from '../../lib/errors.js';
16
+ import * as logger from '../../utils/logger.js';
17
+ export function registerPageListCommand(page) {
18
+ page
19
+ .command('list')
20
+ .description('List/search Notion pages.')
21
+ .option('-q, --query <text>', 'Search pages by title')
22
+ .option('-l, --limit <n>', 'Maximum results to return', '10')
23
+ .option('--cursor <cursor>', 'Pagination cursor from a previous response')
24
+ .action(async (cmdOpts) => {
25
+ try {
26
+ const opts = page.optsWithGlobals();
27
+ const client = getClient(opts.token);
28
+ const limit = parseInt(cmdOpts.limit ?? '10', 10);
29
+ const response = await withRateLimit(() => client.search({
30
+ query: cmdOpts.query ?? '',
31
+ filter: { value: 'page', property: 'object' },
32
+ page_size: Math.min(limit, 100),
33
+ start_cursor: cmdOpts.cursor,
34
+ }), 'search');
35
+ const results = response.results.map((item) => {
36
+ let title = 'Untitled';
37
+ if (isFullPage(item)) {
38
+ const props = item.properties;
39
+ for (const prop of Object.values(props)) {
40
+ if (prop.type === 'title' && prop.title.length > 0) {
41
+ title = prop.title[0]?.plain_text ?? 'Untitled';
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ return {
47
+ id: item.id,
48
+ type: 'page',
49
+ title,
50
+ url: 'url' in item ? item.url : '',
51
+ lastEditedTime: 'last_edited_time' in item ? item.last_edited_time : '',
52
+ };
53
+ });
54
+ if (isJsonMode()) {
55
+ printSuccess({ results }, {
56
+ hasMore: response.has_more,
57
+ cursor: response.next_cursor ?? undefined,
58
+ });
59
+ }
60
+ else {
61
+ if (results.length === 0) {
62
+ logger.info('No pages found.');
63
+ }
64
+ else {
65
+ for (const r of results) {
66
+ const table = formatTable([
67
+ ['ID', r.id],
68
+ ['Title', r.title],
69
+ ['URL', r.url],
70
+ ['Last edited', r.lastEditedTime],
71
+ ]);
72
+ process.stdout.write(`${table}\n\n`);
73
+ }
74
+ if (response.has_more) {
75
+ logger.info(`More results available. Use --cursor ${response.next_cursor ?? ''}`);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ catch (err) {
81
+ const cliErr = toCliError(err);
82
+ printError(cliErr.code, cliErr.message);
83
+ process.exitCode = cliErr.exitCode;
84
+ }
85
+ });
86
+ }
87
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/page/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * notion-cli page patch <page-id> [options]
3
+ *
4
+ * Partially edit a Notion page's Markdown content.
5
+ * Designed for AI agents / coding agents that need to make targeted edits.
6
+ *
7
+ * Line-range replacement (--lines START:END):
8
+ * notion-cli page patch <id> --lines 5:12 --content "new content"
9
+ * notion-cli page patch <id> --lines 5:12 --file patch.md
10
+ *
11
+ * Workflow (surgical patching):
12
+ * 1. Fetch page as MdBlocks (preserving block IDs)
13
+ * 2. Build block-to-line mapping
14
+ * 3. Compute which blocks need to be deleted/inserted
15
+ * 4. Apply changes surgically (only affected blocks)
16
+ *
17
+ * This command is NOT idempotent.
18
+ */
19
+ import { type Command } from 'commander';
20
+ export declare function registerPagePatchCommand(page: Command): void;
21
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../../../src/commands/page/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA8I5D"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * notion-cli page patch <page-id> [options]
3
+ *
4
+ * Partially edit a Notion page's Markdown content.
5
+ * Designed for AI agents / coding agents that need to make targeted edits.
6
+ *
7
+ * Line-range replacement (--lines START:END):
8
+ * notion-cli page patch <id> --lines 5:12 --content "new content"
9
+ * notion-cli page patch <id> --lines 5:12 --file patch.md
10
+ *
11
+ * Workflow (surgical patching):
12
+ * 1. Fetch page as MdBlocks (preserving block IDs)
13
+ * 2. Build block-to-line mapping
14
+ * 3. Compute which blocks need to be deleted/inserted
15
+ * 4. Apply changes surgically (only affected blocks)
16
+ *
17
+ * This command is NOT idempotent.
18
+ */
19
+ import {} from 'commander';
20
+ import { readFileSync } from 'node:fs';
21
+ import { getClient } from '../../lib/client.js';
22
+ import { fetchPageMdBlocks, markdownToNotionBlocks } from '../../lib/markdown.js';
23
+ import { buildBlockLineMap, computePatchPlan } from '../../lib/block-patch.js';
24
+ import { applyPatchOperation } from '../../lib/patch.js';
25
+ import { printSuccess, printError, isJsonMode } from '../../lib/output.js';
26
+ import { isDryRun, showDiffPreview } from '../../lib/safety.js';
27
+ import { withRateLimit } from '../../lib/rate-limit.js';
28
+ import { parseNotionId } from '../../utils/id.js';
29
+ import { lineRangeSchema } from '../../lib/validator.js';
30
+ import { unescapeString, dedentMarkdown } from '../../utils/string.js';
31
+ import {} from '../../lib/types.js';
32
+ import { toCliError, ValidationError } from '../../lib/errors.js';
33
+ import * as logger from '../../utils/logger.js';
34
+ export function registerPagePatchCommand(page) {
35
+ page
36
+ .command('patch', { hidden: true })
37
+ .description('Partially edit a Notion page using line-range replacement.')
38
+ .argument('<page-id>', 'Notion page ID or URL')
39
+ .option('--lines <range>', 'Line range to replace (e.g., "192:256")')
40
+ .option('-f, --file <path>', 'Path to content file')
41
+ .option('--content <text>', 'Inline content for replacement')
42
+ .action(async (rawId, cmdOpts) => {
43
+ try {
44
+ const opts = page.optsWithGlobals();
45
+ const pageId = parseNotionId(rawId);
46
+ const client = getClient(opts.token);
47
+ // 1. Parse patch operation
48
+ const operation = resolvePatchOperation(cmdOpts);
49
+ // 2. Fetch page as MdBlocks (preserving block IDs)
50
+ logger.info('Fetching current page content...');
51
+ const mdBlocks = await withRateLimit(() => fetchPageMdBlocks(client, pageId), 'fetchPageMdBlocks');
52
+ // 3. Build block-to-line mapping
53
+ const { markdown: original, mappings } = buildBlockLineMap(mdBlocks);
54
+ logger.debug(`Built mapping for ${String(mappings.length)} blocks.`);
55
+ // 4. Apply text patch to get new content and diff
56
+ const patchResult = applyPatchOperation(original, operation);
57
+ logger.debug(`${String(patchResult.linesChanged)} lines changed.`);
58
+ // 5. Compute surgical patch plan
59
+ const plan = computePatchPlan(mappings, operation.start, operation.end, operation.content);
60
+ logger.debug(`Patch plan: delete ${String(plan.blocksToDelete.length)} blocks, insert ${String(plan.blocksToInsert.length)} segment(s).`);
61
+ // 7. Dry run check
62
+ if (isDryRun(opts.dryRun)) {
63
+ const result = {
64
+ pageId,
65
+ linesChanged: patchResult.linesChanged,
66
+ diff: patchResult.diff,
67
+ };
68
+ if (isJsonMode()) {
69
+ printSuccess({
70
+ ...result,
71
+ dryRun: true,
72
+ plan: {
73
+ blocksToDelete: plan.blocksToDelete.length,
74
+ blocksToInsert: plan.blocksToInsert.length,
75
+ },
76
+ });
77
+ }
78
+ else {
79
+ showDiffPreview(original, patchResult.patched);
80
+ logger.info(`Dry run: Would delete ${String(plan.blocksToDelete.length)} blocks and insert ${String(plan.blocksToInsert.length)} segment(s).`);
81
+ }
82
+ return;
83
+ }
84
+ // 8. Execute surgical patch
85
+ // Delete affected blocks
86
+ for (const blockId of plan.blocksToDelete) {
87
+ logger.debug(`Deleting block ${blockId}`);
88
+ await withRateLimit(() => client.blocks.delete({ block_id: blockId }), 'blocks.delete');
89
+ }
90
+ // Insert new blocks
91
+ for (const insert of plan.blocksToInsert) {
92
+ // When inserting as children of a parent block, strip the
93
+ // cosmetic indentation so that @tryfabric/martian does not
94
+ // misinterpret indented list items as code blocks.
95
+ const rawMd = insert.parentBlockId !== undefined
96
+ ? dedentMarkdown(insert.markdown)
97
+ : insert.markdown;
98
+ const newBlocks = markdownToNotionBlocks(rawMd);
99
+ const targetBlockId = insert.parentBlockId ?? pageId;
100
+ // Append in chunks of 100
101
+ for (let i = 0; i < newBlocks.length; i += 100) {
102
+ const chunk = newBlocks.slice(i, i + 100);
103
+ logger.debug(`Inserting ${String(chunk.length)} blocks after ${insert.afterId ?? 'start'}${insert.parentBlockId !== undefined ? ` (as children of ${insert.parentBlockId})` : ''}`);
104
+ await withRateLimit(() => client.blocks.children.append({
105
+ block_id: targetBlockId,
106
+ children: chunk,
107
+ ...(insert.afterId !== null ? { after: insert.afterId } : {}),
108
+ }), 'blocks.children.append');
109
+ }
110
+ }
111
+ const result = {
112
+ pageId,
113
+ linesChanged: patchResult.linesChanged,
114
+ diff: patchResult.diff,
115
+ };
116
+ if (isJsonMode()) {
117
+ printSuccess(result);
118
+ }
119
+ else {
120
+ showDiffPreview(original, patchResult.patched);
121
+ logger.success(`Patched page ${pageId} (${String(patchResult.linesChanged)} lines changed, ${String(plan.blocksToDelete.length)} blocks affected).`);
122
+ }
123
+ }
124
+ catch (err) {
125
+ const cliErr = toCliError(err);
126
+ printError(cliErr.code, cliErr.message);
127
+ process.exitCode = cliErr.exitCode;
128
+ }
129
+ });
130
+ }
131
+ /**
132
+ * Determine the patch operation from command options.
133
+ */
134
+ function resolvePatchOperation(cmdOpts) {
135
+ if (cmdOpts.lines === undefined) {
136
+ throw new ValidationError('Missing --lines option. Example: --lines 192:256');
137
+ }
138
+ const range = lineRangeSchema.parse(cmdOpts.lines);
139
+ const content = resolveContentSync(cmdOpts.file, cmdOpts.content);
140
+ return {
141
+ mode: 'lines',
142
+ start: range.start,
143
+ end: range.end,
144
+ content,
145
+ };
146
+ }
147
+ function resolveContentSync(filePath, inlineContent) {
148
+ if (filePath !== undefined) {
149
+ return readFileSync(filePath, 'utf-8');
150
+ }
151
+ if (inlineContent !== undefined) {
152
+ return unescapeString(inlineContent);
153
+ }
154
+ throw new ValidationError('No content provided for patch. Use --file or --content.');
155
+ }
156
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../../../src/commands/page/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * notion-cli page read <page-id>
3
+ *
4
+ * Fetch a Notion page and output its content as Markdown.
5
+ *
6
+ * This command is idempotent and read-only.
7
+ */
8
+ import { type Command } from 'commander';
9
+ export declare function registerPageReadCommand(page: Command): void;
10
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/commands/page/read.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAgF3D"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * notion-cli page read <page-id>
3
+ *
4
+ * Fetch a Notion page and output its content as Markdown.
5
+ *
6
+ * This command is idempotent and read-only.
7
+ */
8
+ import {} from 'commander';
9
+ import { getClient } from '../../lib/client.js';
10
+ import { notionPageToMarkdown } from '../../lib/markdown.js';
11
+ import { withRateLimit } from '../../lib/rate-limit.js';
12
+ import { parseNotionId } from '../../utils/id.js';
13
+ import {} from '../../lib/types.js';
14
+ import { toCliError } from '../../lib/errors.js';
15
+ import { printSuccess, printError, isJsonMode } from '../../lib/output.js';
16
+ import * as logger from '../../utils/logger.js';
17
+ export function registerPageReadCommand(page) {
18
+ page
19
+ .command('read')
20
+ .description('Fetch a Notion page and output as Markdown.')
21
+ .argument('<page-id>', 'Notion page ID or URL')
22
+ .action(async (rawId) => {
23
+ try {
24
+ const opts = page.optsWithGlobals();
25
+ const pageId = parseNotionId(rawId);
26
+ const client = getClient(opts.token);
27
+ // Fetch page metadata
28
+ const pageObj = await withRateLimit(() => client.pages.retrieve({ page_id: pageId }), 'pages.retrieve');
29
+ // Extract title
30
+ const props = pageObj['properties'];
31
+ let title = 'Untitled';
32
+ if (props !== undefined) {
33
+ for (const prop of Object.values(props)) {
34
+ if (prop['type'] === 'title') {
35
+ const titleArr = prop['title'];
36
+ if (titleArr !== undefined && titleArr.length > 0) {
37
+ title = titleArr[0]?.['plain_text'] ?? 'Untitled';
38
+ }
39
+ break;
40
+ }
41
+ }
42
+ }
43
+ // Extract parent ID
44
+ const parent = pageObj['parent'];
45
+ let parentId;
46
+ if (parent) {
47
+ switch (parent.type) {
48
+ case 'database_id':
49
+ parentId = parent.database_id;
50
+ break;
51
+ case 'page_id':
52
+ parentId = parent.page_id;
53
+ break;
54
+ case 'block_id':
55
+ parentId = parent.block_id;
56
+ break;
57
+ case 'workspace':
58
+ parentId = 'workspace';
59
+ break;
60
+ }
61
+ }
62
+ // Fetch and convert content
63
+ const markdown = await withRateLimit(() => notionPageToMarkdown(client, pageId), 'pageToMarkdown');
64
+ const lastEditedTime = pageObj['last_edited_time'];
65
+ // Output
66
+ if (isJsonMode()) {
67
+ printSuccess({ pageId, parentId, title, markdown, lastEditedTime });
68
+ }
69
+ else {
70
+ process.stdout.write(`${markdown}\n`);
71
+ logger.success(`Read page: ${title}`);
72
+ }
73
+ }
74
+ catch (err) {
75
+ const cliErr = toCliError(err);
76
+ if (isJsonMode()) {
77
+ printError(cliErr.code, cliErr.message);
78
+ }
79
+ else {
80
+ logger.error(cliErr.message);
81
+ }
82
+ process.exitCode = cliErr.exitCode;
83
+ }
84
+ });
85
+ }
86
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/commands/page/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * notion-cli page write-properties <page-id> --json-data <json> | --file <file.json>
3
+ *
4
+ * Update a Notion page's properties from a JSON object.
5
+ * Only the properties present in the JSON are updated; all others remain unchanged.
6
+ * Merge strategy: existing page properties as base, JSON values overwrite.
7
+ *
8
+ * The JSON must be a flat object mapping property names to string values.
9
+ * Provide properties via --json-data (inline JSON string) or --file (path to a JSON file).
10
+ *
11
+ * This command is NOT idempotent (applies updates each run).
12
+ */
13
+ import { type Command } from 'commander';
14
+ export declare function registerPageWritePropertiesCommand(page: Command): void;
15
+ //# sourceMappingURL=write-properties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-properties.d.ts","sourceRoot":"","sources":["../../../src/commands/page/write-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAsDzC,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA6FtE"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * notion-cli page write-properties <page-id> --json-data <json> | --file <file.json>
3
+ *
4
+ * Update a Notion page's properties from a JSON object.
5
+ * Only the properties present in the JSON are updated; all others remain unchanged.
6
+ * Merge strategy: existing page properties as base, JSON values overwrite.
7
+ *
8
+ * The JSON must be a flat object mapping property names to string values.
9
+ * Provide properties via --json-data (inline JSON string) or --file (path to a JSON file).
10
+ *
11
+ * This command is NOT idempotent (applies updates each run).
12
+ */
13
+ import {} from 'commander';
14
+ import { readFileSync } from 'node:fs';
15
+ import { getClient, resolveDataSourceId } from '../../lib/client.js';
16
+ import { buildNotionProperties } from '../../lib/db-properties.js';
17
+ import { printSuccess, printError, isJsonMode } from '../../lib/output.js';
18
+ import { isDryRun } from '../../lib/safety.js';
19
+ import { withRateLimit } from '../../lib/rate-limit.js';
20
+ import { parseNotionId } from '../../utils/id.js';
21
+ import {} from '../../lib/types.js';
22
+ import { toCliError, ValidationError } from '../../lib/errors.js';
23
+ import * as logger from '../../utils/logger.js';
24
+ import { isFullPage } from '@notionhq/client';
25
+ function parseJsonProperties(opts) {
26
+ if (opts.jsonData !== undefined && opts.file !== undefined) {
27
+ throw new ValidationError('Provide either --json-data or --file, not both.');
28
+ }
29
+ if (opts.jsonData === undefined && opts.file === undefined) {
30
+ throw new ValidationError('One of --json-data or --file is required.');
31
+ }
32
+ let raw;
33
+ if (opts.file !== undefined) {
34
+ raw = readFileSync(opts.file, 'utf-8');
35
+ }
36
+ else {
37
+ raw = opts.jsonData;
38
+ }
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(raw);
42
+ }
43
+ catch {
44
+ throw new ValidationError('Invalid JSON: could not parse property data.');
45
+ }
46
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
47
+ throw new ValidationError('JSON property data must be a flat object mapping property names to string values.');
48
+ }
49
+ const record = parsed;
50
+ for (const [key, value] of Object.entries(record)) {
51
+ if (typeof value !== 'string') {
52
+ throw new ValidationError(`Property "${key}" must have a string value.`);
53
+ }
54
+ }
55
+ return record;
56
+ }
57
+ export function registerPageWritePropertiesCommand(page) {
58
+ page
59
+ .command('write-properties')
60
+ .description('Update a Notion page properties from a JSON object. Only specified properties are changed.')
61
+ .argument('<page-id>', 'Notion page ID or URL')
62
+ .option('--json-data <json>', 'JSON string mapping property names to string values')
63
+ .option('-f, --file <path>', 'Path to a JSON file mapping property names to string values')
64
+ .action(async (rawId, cmdOpts) => {
65
+ try {
66
+ const opts = page.optsWithGlobals();
67
+ const pageId = parseNotionId(rawId);
68
+ const client = getClient(opts.token);
69
+ const jsonProps = parseJsonProperties(cmdOpts);
70
+ // Retrieve page to find parent database for schema
71
+ const pageResponse = await withRateLimit(() => client.pages.retrieve({ page_id: pageId }), 'pages.retrieve');
72
+ if (!isFullPage(pageResponse)) {
73
+ throw new ValidationError('Could not retrieve full page object.');
74
+ }
75
+ // Get parent database ID for schema lookup
76
+ const parent = pageResponse.parent;
77
+ if (parent.type !== 'database_id') {
78
+ throw new ValidationError('Page does not belong to a database; cannot resolve property schema.');
79
+ }
80
+ const dbId = await resolveDataSourceId(client, parent.database_id);
81
+ const dataSource = await withRateLimit(
82
+ // @ts-ignore - dataSources might be new in the SDK
83
+ () => client.dataSources.retrieve({ data_source_id: dbId }), 'dataSources.retrieve');
84
+ const schemaProps = dataSource.properties;
85
+ const jsonProperties = buildNotionProperties(jsonProps, schemaProps);
86
+ // Merge: existing properties as base, JSON values overwrite
87
+ const existingProperties = pageResponse.properties;
88
+ const mergedProperties = {
89
+ ...existingProperties,
90
+ ...jsonProperties,
91
+ };
92
+ logger.debug(`Updating ${String(Object.keys(jsonProperties).length)} properties on page ${pageId}.`);
93
+ if (isDryRun(opts.dryRun)) {
94
+ if (isJsonMode()) {
95
+ printSuccess({
96
+ pageId,
97
+ properties: Object.keys(jsonProperties),
98
+ dryRun: true,
99
+ });
100
+ }
101
+ else {
102
+ logger.info(`Dry run: Would update properties [${Object.keys(jsonProperties).join(', ')}] on page ${pageId}.`);
103
+ }
104
+ return;
105
+ }
106
+ await withRateLimit(() => client.pages.update({
107
+ page_id: pageId,
108
+ properties: mergedProperties,
109
+ }), 'pages.update');
110
+ const result = {
111
+ pageId,
112
+ properties: Object.keys(jsonProperties),
113
+ };
114
+ if (isJsonMode()) {
115
+ printSuccess(result);
116
+ }
117
+ else {
118
+ logger.success(`Updated properties [${Object.keys(jsonProperties).join(', ')}] on page ${pageId}.`);
119
+ }
120
+ }
121
+ catch (err) {
122
+ const cliErr = toCliError(err);
123
+ printError(cliErr.code, cliErr.message);
124
+ process.exitCode = cliErr.exitCode;
125
+ }
126
+ });
127
+ }
128
+ //# sourceMappingURL=write-properties.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-properties.js","sourceRoot":"","sources":["../../../src/commands/page/write-properties.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * notion-cli page write <page-id> --file <path>
3
+ *
4
+ * Replace a Notion page's content with Markdown from a file or stdin.
5
+ * Uses @tryfabric/martian to convert Markdown → Notion blocks.
6
+ *
7
+ * WARNING: This replaces ALL existing content on the page.
8
+ * Use `page patch` for partial edits.
9
+ *
10
+ * This command is NOT idempotent (replaces content).
11
+ */
12
+ import { type Command } from 'commander';
13
+ export declare function registerPageWriteCommand(page: Command): void;
14
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/commands/page/write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CA2E5D"}