@infograb/notion-cli 5.9.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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1386 -0
  3. package/bin/dev +17 -0
  4. package/bin/dev.cmd +3 -0
  5. package/bin/run +14 -0
  6. package/bin/run.cmd +3 -0
  7. package/dist/base-command.d.ts +73 -0
  8. package/dist/base-command.js +179 -0
  9. package/dist/base-flags.d.ts +14 -0
  10. package/dist/base-flags.js +59 -0
  11. package/dist/cache.d.ts +84 -0
  12. package/dist/cache.js +351 -0
  13. package/dist/commands/batch/retrieve.d.ts +43 -0
  14. package/dist/commands/batch/retrieve.js +265 -0
  15. package/dist/commands/block/append.d.ts +42 -0
  16. package/dist/commands/block/append.js +219 -0
  17. package/dist/commands/block/delete.d.ts +30 -0
  18. package/dist/commands/block/delete.js +94 -0
  19. package/dist/commands/block/retrieve/children.d.ts +31 -0
  20. package/dist/commands/block/retrieve/children.js +174 -0
  21. package/dist/commands/block/retrieve.d.ts +30 -0
  22. package/dist/commands/block/retrieve.js +98 -0
  23. package/dist/commands/block/update.d.ts +45 -0
  24. package/dist/commands/block/update.js +241 -0
  25. package/dist/commands/cache/info.d.ts +19 -0
  26. package/dist/commands/cache/info.js +145 -0
  27. package/dist/commands/config/set-token.d.ts +30 -0
  28. package/dist/commands/config/set-token.js +201 -0
  29. package/dist/commands/db/create.d.ts +31 -0
  30. package/dist/commands/db/create.js +124 -0
  31. package/dist/commands/db/query.d.ts +41 -0
  32. package/dist/commands/db/query.js +355 -0
  33. package/dist/commands/db/retrieve.d.ts +33 -0
  34. package/dist/commands/db/retrieve.js +134 -0
  35. package/dist/commands/db/schema.d.ts +32 -0
  36. package/dist/commands/db/schema.js +308 -0
  37. package/dist/commands/db/update.d.ts +31 -0
  38. package/dist/commands/db/update.js +117 -0
  39. package/dist/commands/doctor.d.ts +50 -0
  40. package/dist/commands/doctor.js +420 -0
  41. package/dist/commands/init.d.ts +57 -0
  42. package/dist/commands/init.js +471 -0
  43. package/dist/commands/list.d.ts +29 -0
  44. package/dist/commands/list.js +184 -0
  45. package/dist/commands/page/create.d.ts +33 -0
  46. package/dist/commands/page/create.js +240 -0
  47. package/dist/commands/page/retrieve/property_item.d.ts +24 -0
  48. package/dist/commands/page/retrieve/property_item.js +72 -0
  49. package/dist/commands/page/retrieve.d.ts +36 -0
  50. package/dist/commands/page/retrieve.js +244 -0
  51. package/dist/commands/page/update.d.ts +34 -0
  52. package/dist/commands/page/update.js +184 -0
  53. package/dist/commands/search.d.ts +40 -0
  54. package/dist/commands/search.js +348 -0
  55. package/dist/commands/sync.d.ts +24 -0
  56. package/dist/commands/sync.js +183 -0
  57. package/dist/commands/user/list.d.ts +27 -0
  58. package/dist/commands/user/list.js +99 -0
  59. package/dist/commands/user/retrieve/bot.d.ts +28 -0
  60. package/dist/commands/user/retrieve/bot.js +96 -0
  61. package/dist/commands/user/retrieve.d.ts +30 -0
  62. package/dist/commands/user/retrieve.js +103 -0
  63. package/dist/commands/whoami.d.ts +19 -0
  64. package/dist/commands/whoami.js +175 -0
  65. package/dist/deduplication.d.ts +41 -0
  66. package/dist/deduplication.js +71 -0
  67. package/dist/envelope.d.ts +169 -0
  68. package/dist/envelope.js +257 -0
  69. package/dist/errors/enhanced-errors.d.ts +168 -0
  70. package/dist/errors/enhanced-errors.js +570 -0
  71. package/dist/errors/index.d.ts +18 -0
  72. package/dist/errors/index.js +33 -0
  73. package/dist/examples/cache-retry-examples.d.ts +64 -0
  74. package/dist/examples/cache-retry-examples.js +375 -0
  75. package/dist/helper.d.ts +102 -0
  76. package/dist/helper.js +885 -0
  77. package/dist/http-agent.d.ts +38 -0
  78. package/dist/http-agent.js +60 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +4 -0
  81. package/dist/interface.d.ts +4 -0
  82. package/dist/interface.js +2 -0
  83. package/dist/notion.d.ts +144 -0
  84. package/dist/notion.js +547 -0
  85. package/dist/retry.d.ts +72 -0
  86. package/dist/retry.js +381 -0
  87. package/dist/utils/disk-cache.d.ts +80 -0
  88. package/dist/utils/disk-cache.js +291 -0
  89. package/dist/utils/markdown-to-blocks.d.ts +19 -0
  90. package/dist/utils/markdown-to-blocks.js +259 -0
  91. package/dist/utils/notion-resolver.d.ts +48 -0
  92. package/dist/utils/notion-resolver.js +262 -0
  93. package/dist/utils/notion-url-parser.d.ts +46 -0
  94. package/dist/utils/notion-url-parser.js +111 -0
  95. package/dist/utils/property-expander.d.ts +45 -0
  96. package/dist/utils/property-expander.js +323 -0
  97. package/dist/utils/schema-examples.d.ts +40 -0
  98. package/dist/utils/schema-examples.js +359 -0
  99. package/dist/utils/schema-extractor.d.ts +65 -0
  100. package/dist/utils/schema-extractor.js +235 -0
  101. package/dist/utils/table-formatter.d.ts +36 -0
  102. package/dist/utils/table-formatter.js +122 -0
  103. package/dist/utils/terminal-banner.d.ts +24 -0
  104. package/dist/utils/terminal-banner.js +34 -0
  105. package/dist/utils/token-validator.d.ts +55 -0
  106. package/dist/utils/token-validator.js +85 -0
  107. package/dist/utils/update-notifier.d.ts +26 -0
  108. package/dist/utils/update-notifier.js +54 -0
  109. package/dist/utils/workspace-cache.d.ts +58 -0
  110. package/dist/utils/workspace-cache.js +185 -0
  111. package/oclif.manifest.json +4497 -0
  112. package/package.json +115 -0
  113. package/scripts/banner.js +38 -0
  114. package/scripts/postinstall.js +56 -0
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * Table formatting utility to replace oclif v2's ux.table
4
+ * Provides backward-compatible table flags and formatting
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.tableFlags = void 0;
8
+ exports.formatTable = formatTable;
9
+ const core_1 = require("@oclif/core");
10
+ const Table = require("cli-table3");
11
+ /**
12
+ * Table flags compatible with oclif v2's ux.table.flags()
13
+ */
14
+ exports.tableFlags = {
15
+ columns: core_1.Flags.string({
16
+ description: 'Only show provided columns (comma-separated)',
17
+ exclusive: ['extended'],
18
+ }),
19
+ sort: core_1.Flags.string({
20
+ description: 'Property to sort by (prepend with - for descending)',
21
+ }),
22
+ filter: core_1.Flags.string({
23
+ description: 'Filter property by substring match',
24
+ }),
25
+ csv: core_1.Flags.boolean({
26
+ description: 'Output in CSV format',
27
+ exclusive: ['no-truncate'],
28
+ }),
29
+ extended: core_1.Flags.boolean({
30
+ char: 'x',
31
+ description: 'Show extra columns',
32
+ }),
33
+ 'no-truncate': core_1.Flags.boolean({
34
+ description: 'Do not truncate output to fit screen',
35
+ exclusive: ['csv'],
36
+ }),
37
+ 'no-header': core_1.Flags.boolean({
38
+ description: 'Hide table header from output',
39
+ }),
40
+ };
41
+ /**
42
+ * Format and display a table (compatible with oclif v2's ux.table)
43
+ */
44
+ function formatTable(data, columns, options = {}) {
45
+ if (data.length === 0) {
46
+ return;
47
+ }
48
+ const printLine = options.printLine || console.log;
49
+ // Filter columns based on options
50
+ let selectedColumns = Object.keys(columns);
51
+ if (options.columns) {
52
+ const requestedCols = options.columns.split(',').map(c => c.trim());
53
+ selectedColumns = selectedColumns.filter(col => requestedCols.includes(col));
54
+ }
55
+ if (!options.extended) {
56
+ selectedColumns = selectedColumns.filter(col => !columns[col].extended);
57
+ }
58
+ // Filter rows
59
+ let filteredData = data;
60
+ if (options.filter) {
61
+ const [filterCol, filterVal] = options.filter.split('=');
62
+ if (filterVal) {
63
+ filteredData = data.filter(row => {
64
+ var _a;
65
+ const val = ((_a = columns[filterCol]) === null || _a === void 0 ? void 0 : _a.get) ? columns[filterCol].get(row) : row[filterCol];
66
+ return String(val).includes(filterVal);
67
+ });
68
+ }
69
+ }
70
+ // Sort data
71
+ if (options.sort) {
72
+ const descending = options.sort.startsWith('-');
73
+ const sortCol = descending ? options.sort.slice(1) : options.sort;
74
+ filteredData = [...filteredData].sort((a, b) => {
75
+ var _a, _b;
76
+ const aVal = ((_a = columns[sortCol]) === null || _a === void 0 ? void 0 : _a.get) ? columns[sortCol].get(a) : a[sortCol];
77
+ const bVal = ((_b = columns[sortCol]) === null || _b === void 0 ? void 0 : _b.get) ? columns[sortCol].get(b) : b[sortCol];
78
+ const comparison = String(aVal).localeCompare(String(bVal));
79
+ return descending ? -comparison : comparison;
80
+ });
81
+ }
82
+ // Output as CSV
83
+ if (options.csv) {
84
+ if (!options['no-header']) {
85
+ const headers = selectedColumns.map(col => columns[col].header || col);
86
+ printLine(headers.join(','));
87
+ }
88
+ filteredData.forEach(row => {
89
+ const values = selectedColumns.map(col => {
90
+ const val = columns[col].get ? columns[col].get(row) : row[col];
91
+ const str = String(val || '');
92
+ // Escape CSV values
93
+ return str.includes(',') || str.includes('"') ? `"${str.replace(/"/g, '""')}"` : str;
94
+ });
95
+ printLine(values.join(','));
96
+ });
97
+ return;
98
+ }
99
+ // Output as table
100
+ const headers = selectedColumns.map(col => columns[col].header || col);
101
+ const table = new Table({
102
+ head: options['no-header'] ? [] : headers,
103
+ style: {
104
+ head: ['cyan'],
105
+ border: ['gray']
106
+ },
107
+ wordWrap: !options['no-truncate'],
108
+ colWidths: selectedColumns.map(col => {
109
+ if (options['no-truncate'])
110
+ return undefined;
111
+ return columns[col].minWidth || undefined;
112
+ }),
113
+ });
114
+ filteredData.forEach(row => {
115
+ const values = selectedColumns.map(col => {
116
+ const val = columns[col].get ? columns[col].get(row) : row[col];
117
+ return String(val !== undefined && val !== null ? val : '');
118
+ });
119
+ table.push(values);
120
+ });
121
+ printLine(table.toString());
122
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Terminal banner and color utilities for consistent branding
3
+ * Used across postinstall script and init command
4
+ */
5
+ /**
6
+ * ANSI color codes for cross-platform terminal compatibility
7
+ */
8
+ export declare const colors: {
9
+ readonly reset: "\u001B[0m";
10
+ readonly bright: "\u001B[1m";
11
+ readonly dim: "\u001B[2m";
12
+ readonly green: "\u001B[32m";
13
+ readonly blue: "\u001B[34m";
14
+ readonly cyan: "\u001B[36m";
15
+ readonly gray: "\u001B[90m";
16
+ readonly yellow: "\u001B[33m";
17
+ readonly magenta: "\u001B[35m";
18
+ };
19
+ /**
20
+ * ASCII art banner for Notion CLI
21
+ * Displayed during install and setup
22
+ * Uses terminal's default color (black/white depending on theme)
23
+ */
24
+ export declare const ASCII_BANNER = "\n\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\n\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\n\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\n\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\n\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\n\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\n";
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Terminal banner and color utilities for consistent branding
4
+ * Used across postinstall script and init command
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ASCII_BANNER = exports.colors = void 0;
8
+ /**
9
+ * ANSI color codes for cross-platform terminal compatibility
10
+ */
11
+ exports.colors = {
12
+ reset: '\x1b[0m',
13
+ bright: '\x1b[1m',
14
+ dim: '\x1b[2m',
15
+ green: '\x1b[32m',
16
+ blue: '\x1b[34m',
17
+ cyan: '\x1b[36m',
18
+ gray: '\x1b[90m',
19
+ yellow: '\x1b[33m',
20
+ magenta: '\x1b[35m',
21
+ };
22
+ /**
23
+ * ASCII art banner for Notion CLI
24
+ * Displayed during install and setup
25
+ * Uses terminal's default color (black/white depending on theme)
26
+ */
27
+ exports.ASCII_BANNER = `
28
+ ███╗ ██╗ ██████╗ ████████╗██╗ ██████╗ ███╗ ██╗ ██████╗██╗ ██╗
29
+ ████╗ ██║██╔═══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ ██╔════╝██║ ██║
30
+ ██╔██╗ ██║██║ ██║ ██║ ██║██║ ██║██╔██╗ ██║ ██║ ██║ ██║
31
+ ██║╚██╗██║██║ ██║ ██║ ██║██║ ██║██║╚██╗██║ ██║ ██║ ██║
32
+ ██║ ╚████║╚██████╔╝ ██║ ██║╚██████╔╝██║ ╚████║ ╚██████╗███████╗██║
33
+ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝╚══════╝╚═╝
34
+ `;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Token Validation Utility
3
+ *
4
+ * Provides consistent token validation across all commands that interact with the Notion API.
5
+ * This ensures users get helpful, actionable error messages before attempting API calls.
6
+ */
7
+ /**
8
+ * Masks a Notion token for safe display in logs and console output
9
+ *
10
+ * Shows only the prefix and last 3 characters to prevent token leakage
11
+ * in screen recordings, terminal sharing, or logs.
12
+ *
13
+ * @param token - The token to mask
14
+ * @returns Masked token string (e.g., "secret_***...***abc")
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const token = "secret_1234567890abcdef"
19
+ * const masked = maskToken(token)
20
+ * // Returns: "secret_***...***def"
21
+ * ```
22
+ */
23
+ export declare function maskToken(token: string): string;
24
+ /**
25
+ * Validates that NOTION_TOKEN environment variable is set
26
+ *
27
+ * @throws {NotionCLIError} If token is not set, throws with helpful suggestions
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { validateNotionToken } from '../utils/token-validator'
32
+ *
33
+ * // In your command's run() method:
34
+ * async run() {
35
+ * const { flags } = await this.parse(MyCommand)
36
+ * validateNotionToken() // Throws if token not set
37
+ *
38
+ * // Continue with API calls...
39
+ * }
40
+ * ```
41
+ */
42
+ /**
43
+ * Valid Notion token prefixes.
44
+ * - "secret_": Internal integration tokens (legacy format, still valid)
45
+ * - "ntn_": New token format introduced September 2024
46
+ */
47
+ export declare const VALID_TOKEN_PREFIXES: readonly ["secret_", "ntn_"];
48
+ /**
49
+ * Checks whether a token string has a recognized Notion token prefix.
50
+ *
51
+ * @param token - The token to check
52
+ * @returns true if the token starts with a known Notion prefix
53
+ */
54
+ export declare function hasValidTokenPrefix(token: string): boolean;
55
+ export declare function validateNotionToken(): void;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Token Validation Utility
4
+ *
5
+ * Provides consistent token validation across all commands that interact with the Notion API.
6
+ * This ensures users get helpful, actionable error messages before attempting API calls.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.VALID_TOKEN_PREFIXES = void 0;
10
+ exports.maskToken = maskToken;
11
+ exports.hasValidTokenPrefix = hasValidTokenPrefix;
12
+ exports.validateNotionToken = validateNotionToken;
13
+ const errors_1 = require("../errors");
14
+ /**
15
+ * Masks a Notion token for safe display in logs and console output
16
+ *
17
+ * Shows only the prefix and last 3 characters to prevent token leakage
18
+ * in screen recordings, terminal sharing, or logs.
19
+ *
20
+ * @param token - The token to mask
21
+ * @returns Masked token string (e.g., "secret_***...***abc")
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const token = "secret_1234567890abcdef"
26
+ * const masked = maskToken(token)
27
+ * // Returns: "secret_***...***def"
28
+ * ```
29
+ */
30
+ function maskToken(token) {
31
+ if (!token)
32
+ return "";
33
+ if (token.length <= 10) {
34
+ // Token too short to safely mask, obscure completely
35
+ // Threshold: 10 chars ensures at least 3 chars are masked after prefix+suffix
36
+ return "***";
37
+ }
38
+ // Show prefix (secret_ or ntn_) and last 3 chars
39
+ // For unknown prefixes: use max 4 chars to ensure at least 4 chars are masked
40
+ const prefix = token.startsWith("secret_")
41
+ ? "secret_"
42
+ : token.startsWith("ntn_")
43
+ ? "ntn_"
44
+ : token.slice(0, Math.min(4, token.length - 7));
45
+ const suffix = token.slice(-3);
46
+ return `${prefix}***...***${suffix}`;
47
+ }
48
+ /**
49
+ * Validates that NOTION_TOKEN environment variable is set
50
+ *
51
+ * @throws {NotionCLIError} If token is not set, throws with helpful suggestions
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { validateNotionToken } from '../utils/token-validator'
56
+ *
57
+ * // In your command's run() method:
58
+ * async run() {
59
+ * const { flags } = await this.parse(MyCommand)
60
+ * validateNotionToken() // Throws if token not set
61
+ *
62
+ * // Continue with API calls...
63
+ * }
64
+ * ```
65
+ */
66
+ /**
67
+ * Valid Notion token prefixes.
68
+ * - "secret_": Internal integration tokens (legacy format, still valid)
69
+ * - "ntn_": New token format introduced September 2024
70
+ */
71
+ exports.VALID_TOKEN_PREFIXES = ["secret_", "ntn_"];
72
+ /**
73
+ * Checks whether a token string has a recognized Notion token prefix.
74
+ *
75
+ * @param token - The token to check
76
+ * @returns true if the token starts with a known Notion prefix
77
+ */
78
+ function hasValidTokenPrefix(token) {
79
+ return exports.VALID_TOKEN_PREFIXES.some((prefix) => token.startsWith(prefix));
80
+ }
81
+ function validateNotionToken() {
82
+ if (!process.env.NOTION_TOKEN) {
83
+ throw errors_1.NotionCLIErrorFactory.tokenMissing();
84
+ }
85
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Update notifier utility
3
+ * Checks for new versions of notion-cli and notifies users non-intrusively
4
+ *
5
+ * Runs asynchronously in background, doesn't block CLI execution
6
+ * Caches results for 1 day to avoid unnecessary npm registry checks
7
+ * Respects NO_UPDATE_NOTIFIER environment variable and CI environments
8
+ */
9
+ /**
10
+ * Check for updates and notify user if a new version is available
11
+ *
12
+ * This runs asynchronously and won't block CLI execution.
13
+ * Checks are cached for 1 day by default.
14
+ *
15
+ * Set DEBUG=1 environment variable to see error messages if update check fails.
16
+ *
17
+ * @example
18
+ * ```bash
19
+ * # Silent mode (default)
20
+ * notion-cli --version
21
+ *
22
+ * # Debug mode
23
+ * DEBUG=1 notion-cli --version
24
+ * ```
25
+ */
26
+ export declare function checkForUpdates(): void;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * Update notifier utility
4
+ * Checks for new versions of notion-cli and notifies users non-intrusively
5
+ *
6
+ * Runs asynchronously in background, doesn't block CLI execution
7
+ * Caches results for 1 day to avoid unnecessary npm registry checks
8
+ * Respects NO_UPDATE_NOTIFIER environment variable and CI environments
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.checkForUpdates = checkForUpdates;
12
+ /**
13
+ * Check for updates and notify user if a new version is available
14
+ *
15
+ * This runs asynchronously and won't block CLI execution.
16
+ * Checks are cached for 1 day by default.
17
+ *
18
+ * Set DEBUG=1 environment variable to see error messages if update check fails.
19
+ *
20
+ * @example
21
+ * ```bash
22
+ * # Silent mode (default)
23
+ * notion-cli --version
24
+ *
25
+ * # Debug mode
26
+ * DEBUG=1 notion-cli --version
27
+ * ```
28
+ */
29
+ function checkForUpdates() {
30
+ try {
31
+ // Load dependencies dynamically to avoid rootDir issues
32
+ const updateNotifier = require('update-notifier').default || require('update-notifier');
33
+ const packageJson = require('../../package.json');
34
+ // Initialize update notifier with package info
35
+ const notifier = updateNotifier({
36
+ pkg: packageJson,
37
+ updateCheckInterval: 1000 * 60 * 60 * 24, // Check once per day
38
+ });
39
+ // Show notification if update is available
40
+ // This displays a yellow-bordered box with update info
41
+ notifier.notify({
42
+ defer: true, // Show notification after command completes (non-intrusive)
43
+ isGlobal: true, // This is a global CLI tool
44
+ });
45
+ }
46
+ catch (error) {
47
+ // Silently fail - don't break CLI if update check fails
48
+ // This could happen if npm registry is unreachable, network issues, etc.
49
+ // Debug mode: Show error details for troubleshooting
50
+ if (process.env.DEBUG) {
51
+ console.error('Update check failed:', error instanceof Error ? error.message : error);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Workspace Cache Utility
3
+ *
4
+ * Manages persistent caching of workspace databases for fast name-to-ID resolution.
5
+ * Cache is stored at ~/.notion-cli/databases.json
6
+ */
7
+ import { GetDataSourceResponse, DataSourceObjectResponse } from '@notionhq/client/build/src/api-endpoints';
8
+ export interface CachedDatabase {
9
+ id: string;
10
+ title: string;
11
+ titleNormalized: string;
12
+ aliases: string[];
13
+ url?: string;
14
+ lastEditedTime?: string;
15
+ properties?: Record<string, any>;
16
+ }
17
+ export interface WorkspaceCache {
18
+ version: string;
19
+ lastSync: string;
20
+ databases: CachedDatabase[];
21
+ }
22
+ /**
23
+ * Get the cache directory path
24
+ */
25
+ export declare function getCacheDir(): string;
26
+ /**
27
+ * Get the cache file path
28
+ */
29
+ export declare function getCachePath(): Promise<string>;
30
+ /**
31
+ * Ensure cache directory exists
32
+ */
33
+ export declare function ensureCacheDir(): Promise<void>;
34
+ /**
35
+ * Load cache from disk
36
+ * Returns null if cache doesn't exist or is corrupted
37
+ */
38
+ export declare function loadCache(): Promise<WorkspaceCache | null>;
39
+ /**
40
+ * Save cache to disk (atomic write)
41
+ */
42
+ export declare function saveCache(data: WorkspaceCache): Promise<void>;
43
+ /**
44
+ * Generate search aliases from a database title
45
+ *
46
+ * @example
47
+ * generateAliases('Tasks Database')
48
+ * // Returns: ['tasks database', 'tasks', 'task', 'tasks db', 'task db', 'td']
49
+ */
50
+ export declare function generateAliases(title: string): string[];
51
+ /**
52
+ * Build a cached database entry from a data source response
53
+ */
54
+ export declare function buildCacheEntry(dataSource: GetDataSourceResponse | DataSourceObjectResponse): CachedDatabase;
55
+ /**
56
+ * Create an empty cache
57
+ */
58
+ export declare function createEmptyCache(): WorkspaceCache;
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * Workspace Cache Utility
4
+ *
5
+ * Manages persistent caching of workspace databases for fast name-to-ID resolution.
6
+ * Cache is stored at ~/.notion-cli/databases.json
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getCacheDir = getCacheDir;
10
+ exports.getCachePath = getCachePath;
11
+ exports.ensureCacheDir = ensureCacheDir;
12
+ exports.loadCache = loadCache;
13
+ exports.saveCache = saveCache;
14
+ exports.generateAliases = generateAliases;
15
+ exports.buildCacheEntry = buildCacheEntry;
16
+ exports.createEmptyCache = createEmptyCache;
17
+ const fs = require("fs/promises");
18
+ const path = require("path");
19
+ const os = require("os");
20
+ const client_1 = require("@notionhq/client");
21
+ const CACHE_VERSION = '1.0.0';
22
+ const CACHE_DIR_NAME = '.notion-cli';
23
+ const CACHE_FILE_NAME = 'databases.json';
24
+ /**
25
+ * Get the cache directory path
26
+ */
27
+ function getCacheDir() {
28
+ return path.join(os.homedir(), CACHE_DIR_NAME);
29
+ }
30
+ /**
31
+ * Get the cache file path
32
+ */
33
+ async function getCachePath() {
34
+ return path.join(getCacheDir(), CACHE_FILE_NAME);
35
+ }
36
+ /**
37
+ * Ensure cache directory exists
38
+ */
39
+ async function ensureCacheDir() {
40
+ const cacheDir = getCacheDir();
41
+ try {
42
+ await fs.mkdir(cacheDir, { recursive: true });
43
+ }
44
+ catch (error) {
45
+ if (error.code !== 'EEXIST') {
46
+ throw new Error(`Failed to create cache directory: ${error.message}`);
47
+ }
48
+ }
49
+ }
50
+ /**
51
+ * Load cache from disk
52
+ * Returns null if cache doesn't exist or is corrupted
53
+ */
54
+ async function loadCache() {
55
+ try {
56
+ const cachePath = await getCachePath();
57
+ const content = await fs.readFile(cachePath, 'utf-8');
58
+ const cache = JSON.parse(content);
59
+ // Validate cache structure
60
+ if (!cache.version || !Array.isArray(cache.databases)) {
61
+ console.warn('Cache file is corrupted, will rebuild on next sync');
62
+ return null;
63
+ }
64
+ return cache;
65
+ }
66
+ catch (error) {
67
+ if (error.code === 'ENOENT') {
68
+ // Cache doesn't exist yet
69
+ return null;
70
+ }
71
+ // Parse error or other error
72
+ console.warn(`Failed to load cache: ${error.message}`);
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Save cache to disk (atomic write)
78
+ */
79
+ async function saveCache(data) {
80
+ await ensureCacheDir();
81
+ const cachePath = await getCachePath();
82
+ const tmpPath = `${cachePath}.tmp`;
83
+ try {
84
+ // Write to temporary file
85
+ await fs.writeFile(tmpPath, JSON.stringify(data, null, 2), 'utf-8');
86
+ // Atomic rename (replaces old file)
87
+ await fs.rename(tmpPath, cachePath);
88
+ }
89
+ catch (error) {
90
+ // Clean up temp file if it exists
91
+ try {
92
+ await fs.unlink(tmpPath);
93
+ }
94
+ catch {
95
+ // Intentionally empty - cache directory may not exist
96
+ }
97
+ throw new Error(`Failed to save cache: ${error.message}`);
98
+ }
99
+ }
100
+ /**
101
+ * Generate search aliases from a database title
102
+ *
103
+ * @example
104
+ * generateAliases('Tasks Database')
105
+ * // Returns: ['tasks database', 'tasks', 'task', 'tasks db', 'task db', 'td']
106
+ */
107
+ function generateAliases(title) {
108
+ const aliases = new Set();
109
+ const normalized = title.toLowerCase().trim();
110
+ // Add full title (normalized)
111
+ aliases.add(normalized);
112
+ // Add title without common suffixes
113
+ const withoutSuffixes = normalized.replace(/\s+(database|db|table|list|tracker|log)$/i, '');
114
+ if (withoutSuffixes !== normalized) {
115
+ aliases.add(withoutSuffixes);
116
+ }
117
+ // Add title with common suffixes
118
+ if (withoutSuffixes !== normalized) {
119
+ aliases.add(`${withoutSuffixes} db`);
120
+ aliases.add(`${withoutSuffixes} database`);
121
+ }
122
+ // Add singular/plural variants
123
+ if (withoutSuffixes.endsWith('s')) {
124
+ aliases.add(withoutSuffixes.slice(0, -1)); // Remove 's'
125
+ }
126
+ else {
127
+ aliases.add(`${withoutSuffixes}s`); // Add 's'
128
+ }
129
+ // Add acronym if multi-word (e.g., "Meeting Notes" → "mn")
130
+ const words = withoutSuffixes.split(/\s+/);
131
+ if (words.length > 1) {
132
+ const acronym = words.map(w => w[0]).join('');
133
+ if (acronym.length >= 2) {
134
+ aliases.add(acronym);
135
+ }
136
+ }
137
+ return Array.from(aliases);
138
+ }
139
+ /**
140
+ * Build a cached database entry from a data source response
141
+ */
142
+ function buildCacheEntry(dataSource) {
143
+ let title = 'Untitled';
144
+ let properties = {};
145
+ let url;
146
+ let lastEditedTime;
147
+ if ((0, client_1.isFullDataSource)(dataSource)) {
148
+ if (dataSource.title && dataSource.title.length > 0) {
149
+ title = dataSource.title[0].plain_text;
150
+ }
151
+ // Extract property schema
152
+ if (dataSource.properties) {
153
+ properties = dataSource.properties;
154
+ }
155
+ // Extract URL if available
156
+ if ('url' in dataSource) {
157
+ url = dataSource.url;
158
+ }
159
+ // Extract last edited time
160
+ if ('last_edited_time' in dataSource) {
161
+ lastEditedTime = dataSource.last_edited_time;
162
+ }
163
+ }
164
+ const titleNormalized = title.toLowerCase().trim();
165
+ const aliases = generateAliases(title);
166
+ return {
167
+ id: dataSource.id,
168
+ title,
169
+ titleNormalized,
170
+ aliases,
171
+ url,
172
+ lastEditedTime,
173
+ properties,
174
+ };
175
+ }
176
+ /**
177
+ * Create an empty cache
178
+ */
179
+ function createEmptyCache() {
180
+ return {
181
+ version: CACHE_VERSION,
182
+ lastSync: new Date().toISOString(),
183
+ databases: [],
184
+ };
185
+ }