@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,181 @@
1
+ /**
2
+ * Surgical block-level patching.
3
+ *
4
+ * Instead of deleting all blocks and recreating them, this module
5
+ * enables targeted edits by tracking which markdown lines correspond
6
+ * to which Notion blocks.
7
+ *
8
+ * Key concepts:
9
+ * - BlockLineMapping: Maps a block ID to its line range in the markdown
10
+ * - PatchPlan: Describes which blocks to delete and what to insert
11
+ *
12
+ * The flow:
13
+ * 1. Fetch MdBlocks from Notion (via notion-to-md)
14
+ * 2. Build line mapping via mdBlocksToMarkdown (unified converter in markdown.ts)
15
+ * 3. Given a line range edit, compute which blocks are affected
16
+ * 4. Execute the plan: delete affected blocks, insert new content
17
+ */
18
+ import {} from './types.js';
19
+ // Re-export the unified converter as buildBlockLineMap for backward compatibility
20
+ export { mdBlocksToMarkdown as buildBlockLineMap } from './markdown.js';
21
+ /**
22
+ * Compute a patch plan given block mappings and a line range to replace.
23
+ *
24
+ * Determines which blocks need to be deleted and where to insert new content.
25
+ *
26
+ * Rules:
27
+ * - Blocks fully contained in [startLine, endLine] are deleted
28
+ * - Blocks that partially overlap are also deleted (they need to be reconstructed)
29
+ * - New content is inserted after the last unaffected block before the range
30
+ *
31
+ * @param mappings - Block-to-line mappings from buildBlockLineMap
32
+ * @param startLine - 1-indexed start line of the edit (inclusive)
33
+ * @param endLine - 1-indexed end line of the edit (inclusive)
34
+ * @param newContent - The replacement markdown content
35
+ * @returns A plan describing deletions and insertions
36
+ */
37
+ export function computePatchPlan(mappings, startLine, endLine, newContent) {
38
+ const blocksToDelete = [];
39
+ let insertAfterId = null;
40
+ let parentBlockId;
41
+ // Find the block just before the edit range (for insertion point)
42
+ // and collect all blocks that overlap with the edit range
43
+ for (const mapping of mappings) {
44
+ const blockResult = analyzeBlockOverlap(mapping, startLine, endLine);
45
+ // Collect blocks to delete
46
+ blocksToDelete.push(...blockResult.blocksToDelete);
47
+ // If edits target only children, track the parent block ID for insertion
48
+ if (blockResult.parentBlockId !== undefined) {
49
+ parentBlockId = blockResult.parentBlockId;
50
+ insertAfterId = blockResult.childInsertAfterId;
51
+ }
52
+ else if (mapping.endLine < startLine) {
53
+ // Track the last top-level block that ends before our edit range
54
+ insertAfterId = mapping.blockId;
55
+ }
56
+ }
57
+ // Build insertion plan
58
+ const blocksToInsert = [];
59
+ if (newContent.trim() !== '') {
60
+ blocksToInsert.push({
61
+ afterId: insertAfterId,
62
+ markdown: newContent,
63
+ ...(parentBlockId !== undefined ? { parentBlockId } : {}),
64
+ });
65
+ }
66
+ return {
67
+ blocksToDelete,
68
+ blocksToInsert,
69
+ };
70
+ }
71
+ /**
72
+ * Analyze a block (and its children) for overlap with a line range.
73
+ *
74
+ * Key distinction: if only child blocks overlap (not the parent's own content),
75
+ * we delete only the affected children and mark the insertion as child-level.
76
+ * This prevents destroying the parent block when editing nested content.
77
+ *
78
+ * @returns Overlap analysis with block IDs to delete and insertion context
79
+ */
80
+ function analyzeBlockOverlap(mapping, startLine, endLine) {
81
+ const blocksToDelete = [];
82
+ // Calculate the parent's OWN line range (excluding children)
83
+ const parentOwnEndLine = mapping.startLine + mapping.markdown.split('\n').length - 1;
84
+ // Check if the parent's own content overlaps with the edit range
85
+ const parentOwnOverlaps = mapping.startLine <= endLine && parentOwnEndLine >= startLine;
86
+ if (parentOwnOverlaps) {
87
+ // Parent's own content is affected - delete the whole block
88
+ blocksToDelete.push(mapping.blockId);
89
+ // Also delete all children (they'll be recreated with the parent)
90
+ for (const child of mapping.children) {
91
+ blocksToDelete.push(...collectAllBlockIds(child));
92
+ }
93
+ return { blocksToDelete, childInsertAfterId: null };
94
+ }
95
+ // Parent's own content is NOT affected - check children only
96
+ let childInsertAfterId = null;
97
+ let hasChildOverlap = false;
98
+ for (const child of mapping.children) {
99
+ const childOverlaps = child.startLine <= endLine && child.endLine >= startLine;
100
+ if (childOverlaps) {
101
+ hasChildOverlap = true;
102
+ blocksToDelete.push(child.blockId);
103
+ // Also delete grandchildren of affected children
104
+ for (const grandchild of child.children) {
105
+ blocksToDelete.push(...collectAllBlockIds(grandchild));
106
+ }
107
+ }
108
+ else if (child.endLine < startLine) {
109
+ // Track last child before the edit range for insertion point
110
+ childInsertAfterId = child.blockId;
111
+ }
112
+ }
113
+ if (hasChildOverlap) {
114
+ return {
115
+ blocksToDelete,
116
+ parentBlockId: mapping.blockId,
117
+ childInsertAfterId,
118
+ };
119
+ }
120
+ return { blocksToDelete, childInsertAfterId: null };
121
+ }
122
+ /**
123
+ * Recursively collect all block IDs from a mapping tree.
124
+ */
125
+ function collectAllBlockIds(mapping) {
126
+ const ids = [mapping.blockId];
127
+ for (const child of mapping.children) {
128
+ ids.push(...collectAllBlockIds(child));
129
+ }
130
+ return ids;
131
+ }
132
+ /**
133
+ * Find a block mapping by line number.
134
+ *
135
+ * @param mappings - Block mappings to search
136
+ * @param line - The line number to find
137
+ * @returns The block containing this line, or undefined
138
+ */
139
+ export function findBlockAtLine(mappings, line) {
140
+ for (const mapping of mappings) {
141
+ if (line >= mapping.startLine && line <= mapping.endLine) {
142
+ // Check if it's in a child
143
+ const childMatch = findBlockAtLine(mapping.children, line);
144
+ if (childMatch !== undefined) {
145
+ return childMatch;
146
+ }
147
+ return mapping;
148
+ }
149
+ }
150
+ return undefined;
151
+ }
152
+ /**
153
+ * Get content that should be preserved from partially affected blocks.
154
+ *
155
+ * When a block partially overlaps with the edit range, we need to keep
156
+ * the lines that are outside the edit range.
157
+ *
158
+ * @param mapping - The block mapping
159
+ * @param markdown - Full markdown content
160
+ * @param startLine - Edit start line
161
+ * @param endLine - Edit end line
162
+ * @returns Lines to preserve (before and after the edit range)
163
+ */
164
+ export function getPreservedContent(mapping, markdown, startLine, endLine) {
165
+ const allLines = markdown.split('\n');
166
+ // Lines before the edit range that belong to this block
167
+ const beforeLines = [];
168
+ for (let i = mapping.startLine; i < startLine && i <= mapping.endLine; i++) {
169
+ beforeLines.push(allLines[i - 1] ?? '');
170
+ }
171
+ // Lines after the edit range that belong to this block
172
+ const afterLines = [];
173
+ for (let i = endLine + 1; i <= mapping.endLine; i++) {
174
+ afterLines.push(allLines[i - 1] ?? '');
175
+ }
176
+ return {
177
+ before: beforeLines.join('\n'),
178
+ after: afterLines.join('\n'),
179
+ };
180
+ }
181
+ //# sourceMappingURL=block-patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-patch.js","sourceRoot":"","sources":["../../src/lib/block-patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Notion API client singleton with token resolution.
3
+ */
4
+ import { Client } from '@notionhq/client';
5
+ /**
6
+ * Get or create the Notion API client.
7
+ * Token is resolved from CLI flag → env → config file.
8
+ */
9
+ export declare function getClient(cliToken?: string): Client;
10
+ /**
11
+ * Resolve a database ID to its primary data source ID.
12
+ * In the new Notion API (2025-09-03+), we must query data sources, not databases directly.
13
+ */
14
+ export declare function resolveDataSourceId(client: Client, dbId: string): Promise<string>;
15
+ /** Reset client (useful for testing). */
16
+ export declare function resetClient(): void;
17
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAkD,MAAM,kBAAkB,CAAC;AAO1F;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAcnD;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCvF;AAED,yCAAyC;AACzC,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Notion API client singleton with token resolution.
3
+ */
4
+ import { Client, APIResponseError, APIErrorCode, isFullDatabase } from '@notionhq/client';
5
+ import { resolveToken } from './config.js';
6
+ import * as logger from '../utils/logger.js';
7
+ import { withRateLimit } from './rate-limit.js';
8
+ let clientInstance;
9
+ /**
10
+ * Get or create the Notion API client.
11
+ * Token is resolved from CLI flag → env → config file.
12
+ */
13
+ export function getClient(cliToken) {
14
+ if (clientInstance !== undefined) {
15
+ return clientInstance;
16
+ }
17
+ const token = resolveToken(cliToken);
18
+ logger.debug('Initializing Notion API client.');
19
+ clientInstance = new Client({
20
+ auth: token,
21
+ timeoutMs: 30_000,
22
+ });
23
+ return clientInstance;
24
+ }
25
+ /**
26
+ * Resolve a database ID to its primary data source ID.
27
+ * In the new Notion API (2025-09-03+), we must query data sources, not databases directly.
28
+ */
29
+ export async function resolveDataSourceId(client, dbId) {
30
+ try {
31
+ // We attempt to retrieve as a database first
32
+ const db = await withRateLimit(() => client.databases.retrieve({ database_id: dbId }), 'databases.retrieve');
33
+ // Check if the database has data_sources (new API)
34
+ if (isFullDatabase(db) && db.data_sources.length > 0) {
35
+ const firstDataSource = db.data_sources[0];
36
+ if (firstDataSource !== undefined) {
37
+ return firstDataSource.id;
38
+ }
39
+ }
40
+ }
41
+ catch (err) {
42
+ // If retrieve fails with 404, it might already be a data_source ID (which databases.retrieve won't find)
43
+ if (APIResponseError.isAPIResponseError(err) && err.code === APIErrorCode.ObjectNotFound) {
44
+ try {
45
+ const ds = await withRateLimit(
46
+ // @ts-ignore - dataSources might be new in the SDK
47
+ () => client.dataSources.retrieve({ data_source_id: dbId }), 'dataSources.retrieve');
48
+ return ds.id;
49
+ }
50
+ catch {
51
+ // Fallback to original ID if all else fails
52
+ return dbId;
53
+ }
54
+ }
55
+ throw err;
56
+ }
57
+ return dbId;
58
+ }
59
+ /** Reset client (useful for testing). */
60
+ export function resetClient() {
61
+ clientInstance = undefined;
62
+ }
63
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,IAAI,cAAkC,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAEhD,cAAc,GAAG,IAAI,MAAM,CAAC;QAC1B,IAAI,EAAE,KAAK;QACX,SAAS,EAAE,MAAM;KAClB,CAAC,CAAC;IAEH,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc,EAAE,IAAY;IACpE,IAAI,CAAC;QACH,6CAA6C;QAC7C,MAAM,EAAE,GAAG,MAAM,aAAa,CAC5B,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,EACtD,oBAAoB,CACrB,CAAC;QAEF,mDAAmD;QACnD,IAAI,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,MAAM,eAAe,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC3C,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;gBAClC,OAAO,eAAe,CAAC,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,yGAAyG;QACzG,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,cAAc,EAAE,CAAC;YACzF,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,MAAM,aAAa;gBAC5B,mDAAmD;gBACnD,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,EAC3D,sBAAsB,CACvB,CAAC;gBACF,OAAO,EAAE,CAAC,EAAE,CAAC;YACf,CAAC;YAAC,MAAM,CAAC;gBACP,4CAA4C;gBAC5C,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,WAAW;IACzB,cAAc,GAAG,SAAS,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Configuration loading with precedence:
3
+ * 1. --token CLI flag (highest)
4
+ * 2. NOTION_TOKEN environment variable
5
+ * 3. ~/.notion-cli.json config file
6
+ * 4. ./.notion-cli.json in current directory
7
+ */
8
+ import { type CliConfig } from './types.js';
9
+ /**
10
+ * Resolve the Notion API token from available sources.
11
+ * Throws AuthError if no token is found.
12
+ */
13
+ export declare function resolveToken(cliToken?: string): string;
14
+ /**
15
+ * Load config from ~/.notion-cli.json or ./.notion-cli.json.
16
+ * Returns undefined if no config file exists.
17
+ */
18
+ export declare function loadConfig(): CliConfig | undefined;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsBtD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,SAAS,GAAG,SAAS,CAoBlD"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Configuration loading with precedence:
3
+ * 1. --token CLI flag (highest)
4
+ * 2. NOTION_TOKEN environment variable
5
+ * 3. ~/.notion-cli.json config file
6
+ * 4. ./.notion-cli.json in current directory
7
+ */
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ import { config as loadDotenv } from 'dotenv';
12
+ import {} from './types.js';
13
+ import { AuthError } from './errors.js';
14
+ import * as logger from '../utils/logger.js';
15
+ /** Load .env file if present. */
16
+ loadDotenv();
17
+ /**
18
+ * Resolve the Notion API token from available sources.
19
+ * Throws AuthError if no token is found.
20
+ */
21
+ export function resolveToken(cliToken) {
22
+ // 1. CLI flag
23
+ if (cliToken !== undefined && cliToken !== '') {
24
+ logger.debug(`Using token from --token flag: ${logger.maskToken(cliToken)}`);
25
+ return cliToken;
26
+ }
27
+ // 2. Environment variable
28
+ const envToken = process.env['NOTION_TOKEN'];
29
+ if (envToken !== undefined && envToken !== '') {
30
+ logger.debug(`Using token from NOTION_TOKEN env var: ${logger.maskToken(envToken)}`);
31
+ return envToken;
32
+ }
33
+ // 3. Config file (home dir, then current dir)
34
+ const config = loadConfig();
35
+ if (config?.token !== undefined && config.token !== '') {
36
+ logger.debug(`Using token from config file: ${logger.maskToken(config.token)}`);
37
+ return config.token;
38
+ }
39
+ throw new AuthError();
40
+ }
41
+ /**
42
+ * Load config from ~/.notion-cli.json or ./.notion-cli.json.
43
+ * Returns undefined if no config file exists.
44
+ */
45
+ export function loadConfig() {
46
+ const paths = [
47
+ join(homedir(), '.notion-cli.json'),
48
+ join(process.cwd(), '.notion-cli.json'),
49
+ ];
50
+ for (const configPath of paths) {
51
+ if (existsSync(configPath)) {
52
+ try {
53
+ const raw = readFileSync(configPath, 'utf-8');
54
+ const parsed = JSON.parse(raw);
55
+ logger.debug(`Loaded config from ${configPath}`);
56
+ return parsed;
57
+ }
58
+ catch (err) {
59
+ logger.warn(`Failed to parse config at ${configPath}: ${String(err)}`);
60
+ }
61
+ }
62
+ }
63
+ return undefined;
64
+ }
65
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAkB,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,oBAAoB,CAAC;AAE7C,iCAAiC;AACjC,UAAU,EAAE,CAAC;AAEb;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAiB;IAC5C,cAAc;IACd,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,kCAAkC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7E,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,0CAA0C,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACrF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,8CAA8C;IAC9C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,iCAAiC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChF,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,MAAM,IAAI,SAAS,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG;QACZ,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,kBAAkB,CAAC;KACxC,CAAC;IAEF,KAAK,MAAM,UAAU,IAAI,KAAK,EAAE,CAAC;QAC/B,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;gBAC5C,MAAM,CAAC,KAAK,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;gBACjD,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,6BAA6B,UAAU,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * CSV conversion utilities for Notion database rows.
3
+ *
4
+ * - Notion DB rows → CSV (export)
5
+ * - CSV → Notion DB row properties (import)
6
+ *
7
+ * Property type mapping:
8
+ * title, rich_text → string
9
+ * number → number
10
+ * select → string
11
+ * multi_select → semicolon-separated string
12
+ * date → ISO string (start / start|end for ranges)
13
+ * checkbox → "true" / "false"
14
+ * url, email, phone → string
15
+ * formula → computed string (read-only)
16
+ * relation → comma-separated page IDs
17
+ * rollup → computed string (read-only)
18
+ * status → string
19
+ * people → comma-separated user IDs
20
+ * files → comma-separated URLs
21
+ */
22
+ type NotionPropertyValue = Record<string, unknown>;
23
+ type NotionPageResult = Record<string, unknown> & {
24
+ id: string;
25
+ properties: Record<string, NotionPropertyValue>;
26
+ };
27
+ /**
28
+ * Convert Notion database query results to CSV string.
29
+ */
30
+ export declare function rowsToCsv(rows: readonly NotionPageResult[], propertyNames: readonly string[]): string;
31
+ /**
32
+ * Parse CSV string into row objects for database import.
33
+ * Returns an array of { id?: string, properties: Record<string, string> }.
34
+ */
35
+ export declare function csvToRows(csvContent: string): Array<{
36
+ id?: string;
37
+ properties: Record<string, string>;
38
+ }>;
39
+ /**
40
+ * Build Notion property objects from CSV row values.
41
+ * Maps string values back to Notion property types.
42
+ */
43
+ export declare function buildPropertyValue(type: string, value: string): NotionPropertyValue | undefined;
44
+ export {};
45
+ //# sourceMappingURL=csv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/lib/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,KAAK,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACnD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,aAAa,EAAE,SAAS,MAAM,EAAE,GAC/B,MAAM,CAgBR;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,GACjB,KAAK,CAAC;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,CAsB5D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,mBAAmB,GAAG,SAAS,CAsDjC"}