@jordancoin/notioncli 1.2.0 → 1.3.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/TECHNICAL.md ADDED
@@ -0,0 +1,125 @@
1
+ # Technical Notes
2
+
3
+ Deep dive into how notioncli works under the hood. You don't need any of this to use the CLI — it just works. This is for contributors and the curious.
4
+
5
+ ## Notion API 2025-09-03 — Dual ID System
6
+
7
+ The 2025 API introduced a dual-ID system for databases:
8
+
9
+ | ID | What it's for | Example endpoint |
10
+ |----|---------------|-----------------|
11
+ | `database_id` | Page creation (`parent`), `databases.retrieve()` | `pages.create({ parent: { database_id } })` |
12
+ | `data_source_id` | Querying, schema, property management | `dataSources.query()`, `dataSources.retrieve()`, `dataSources.update()` |
13
+
14
+ When you run `notion init`, both IDs are discovered and stored per alias. When you pass a raw UUID, notioncli resolves which ID to use depending on the operation.
15
+
16
+ ### The Silent Data Loss Bug
17
+
18
+ `databases.update()` silently ignores property changes in the 2025 API. It returns `200 OK` but drops all property modifications. You must use `dataSources.update()` for:
19
+
20
+ - Adding properties
21
+ - Removing properties
22
+ - Renaming properties
23
+
24
+ `databases.update()` still works for title changes only.
25
+
26
+ Similarly, `databases.create()` silently ignores non-title properties. notioncli handles this with a two-step approach:
27
+
28
+ 1. Create the database with title via `databases.create()`
29
+ 2. Add properties via `dataSources.update()`
30
+
31
+ ### Where Schema Lives
32
+
33
+ ```
34
+ databases.retrieve(database_id) → NO properties field
35
+ dataSources.retrieve(data_source_id) → HAS properties field (this is the schema)
36
+ ```
37
+
38
+ `getDbSchema()` in notioncli correctly uses `dataSources.retrieve()`.
39
+
40
+ ### dataSources Methods
41
+
42
+ The `dataSources` namespace provides: `retrieve`, `query`, `create`, `update`, `listTemplates`.
43
+
44
+ ## Architecture
45
+
46
+ ### Alias Resolution
47
+
48
+ Every command that targets a database goes through `resolveDb(alias_or_id)`:
49
+
50
+ 1. Check aliases in config (scoped to active workspace)
51
+ 2. If UUID, return as-is (used as `data_source_id`)
52
+ 3. If neither, error with helpful suggestion
53
+
54
+ ### Filter → Page Resolution
55
+
56
+ Commands that target a single page (update, delete, get, etc.) use `resolvePageId()`:
57
+
58
+ 1. If input is a UUID → return directly
59
+ 2. If input is an alias → require `--filter`, query the database, expect exactly 1 result
60
+ 3. Zero matches → error with "No pages found"
61
+ 4. Multiple matches → error with "Multiple pages found, refine your filter"
62
+
63
+ ### Dynamic Property Flags (v1.3+)
64
+
65
+ Commander.js `.allowUnknownOption()` lets unknown flags pass through. `extractDynamicProps()` parses raw `process.argv`:
66
+
67
+ 1. Find flags starting with `--` that aren't in the known flags list
68
+ 2. Convert `--kebab-case` to `Title Case` via `kebabToProperty()`
69
+ 3. Match against database schema (case-insensitive)
70
+ 4. Return as `Key=Value` pairs for property building
71
+
72
+ ### Rich Filter Operators (v1.3+)
73
+
74
+ `parseFilterOperator()` splits filter strings by checking operators in order: `>=`, `<=`, `!=`, `>`, `<`, `=` (multi-char first to avoid false splits).
75
+
76
+ Relative dates (`today`, `yesterday`, `tomorrow`, `last_week`, `next_week`) are resolved to ISO date strings at parse time.
77
+
78
+ Multiple `--filter` flags combine with AND logic via `buildCompoundFilter()`.
79
+
80
+ ### Markdown ↔ Blocks
81
+
82
+ `markdownToBlocks()` parses: headings (h1-h3), bullet lists, numbered lists, todo items, code blocks (fenced with language), blockquotes, dividers (`---`), and paragraphs with inline formatting (bold, italic, code, links).
83
+
84
+ `blocksToMarkdown()` reverses the process for export.
85
+
86
+ ### Multi-Workspace
87
+
88
+ Config format supports named workspace profiles:
89
+
90
+ ```json
91
+ {
92
+ "activeWorkspace": "default",
93
+ "workspaces": {
94
+ "default": { "apiKey": "ntn_...", "aliases": { ... } },
95
+ "work": { "apiKey": "ntn_...", "aliases": { ... } }
96
+ }
97
+ }
98
+ ```
99
+
100
+ Old flat configs (`{ apiKey, aliases }`) are auto-migrated to `{ workspaces: { default: { apiKey, aliases } } }` on first load.
101
+
102
+ ## Testing
103
+
104
+ - **`test/unit.test.js`** — Pure function tests (no API calls). Covers all helpers: parsing, formatting, filtering, markdown, CSV.
105
+ - **`test/mock.test.js`** — Command logic with mocked Notion client. Config management, filter building, schema resolution.
106
+ - **`test/integration.test.js`** — Live API tests (requires `NOTION_API_KEY`). Skipped in CI.
107
+
108
+ Run tests: `npm test`
109
+
110
+ ## Built On
111
+
112
+ - [`@notionhq/client`](https://github.com/makenotion/notion-sdk-js) v5.x (official Notion SDK)
113
+ - [`commander`](https://github.com/tj/commander.js) for CLI parsing
114
+ - `node:test` + `node:assert` for testing (zero test dependencies)
115
+
116
+ ## Contributing
117
+
118
+ ```bash
119
+ git clone https://github.com/JordanCoin/notioncli.git
120
+ cd notioncli
121
+ npm install
122
+ export NOTION_API_KEY=ntn_your_test_key
123
+ npm test
124
+ node bin/notion.js --help
125
+ ```