@dockstat/outline-sync 1.1.3 → 1.2.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 (5) hide show
  1. package/README.md +170 -188
  2. package/dist/index.js +13562 -0
  3. package/package.json +15 -39
  4. package/LICENSE +0 -373
  5. package/dist/cli.js +0 -40
package/README.md CHANGED
@@ -1,271 +1,253 @@
1
- # outline-sync — `@dockstat/outline-sync`
1
+ # Outline Sync
2
2
 
3
- Sync Outline (app.getoutline.com) collections Markdown in your repo.
4
- Designed for multi-collection pipelines.
5
- Features:
3
+ Bidirectional sync for Outline wiki to local folders with CI/CD support.
6
4
 
7
- * Two-way sync (pull / push / timestamp-based sync)
8
- * Multi-collection support (`--collection` repeatable)
9
- * Folder-based default storage (each page → `<slug>/README.md`, children inherit folders)
10
- * Per-collection mapping file for custom paths
11
- * Config-driven: `configs/outline-sync.json`, `<collection>.config.json`, `<collection>.pages.json`
12
- * Whitespace/newline-agnostic diffs (formatting-only changes are ignored)
13
- * Uses Git commit time when available (falls back to fs mtime)
14
- * Dry-run mode, backups of overwritten files
5
+ ## Installation
15
6
 
16
- ---
7
+ ```bash
8
+ bun install
9
+ bun run build
10
+ ```
11
+
12
+ ## Configuration
17
13
 
18
- # Install
14
+ ### Option 1: Config File (Recommended)
19
15
 
20
- Using `bunx` (recommended):
16
+ Create `outline-sync.config.json`:
21
17
 
22
18
  ```bash
23
- bunx @dockstat/outline-sync <command> [flags]
19
+ outline-sync init
24
20
  ```
25
21
 
26
- > Security note: prefer `OUTLINE_API_KEY=...` in CI or environment. Passing `--api-key="..."` is supported but may expose the key in process lists or shell history.
22
+ Then edit the generated file:
27
23
 
28
- ---
24
+ ```json
25
+ {
26
+ "url": "https://your-outline.com",
27
+ "token": "your_api_token",
28
+ "outputDir": "./outline-docs",
29
+ "customPaths": {
30
+ "doc-id-abc123": "../../README.md",
31
+ "doc-id-xyz789": "custom/important-doc.md"
32
+ }
33
+ }
34
+ ```
35
+
36
+ **Custom Paths:**
37
+ - Document IDs can be found in the Outline URL or via the API
38
+ - Paths starting with `..` are relative to current working directory
39
+ - Other paths are relative to `outputDir`
29
40
 
30
- # Quickstart (recommended)
41
+ **Finding Document IDs:**
42
+ 1. Open document in Outline
43
+ 2. Check URL: `https://outline.com/doc/my-document-abc123` (ID is `abc123`)
44
+ 3. Or run initial sync and check frontmatter in generated files
31
45
 
32
- 1. Set your API key:
46
+ ### Option 2: Environment Variables
33
47
 
34
48
  ```bash
35
- export OUTLINE_API_KEY="sk_..." # recommended for local / CI usage
49
+ export OUTLINE_URL=https://your-outline.com
50
+ export OUTLINE_TOKEN=your_api_token
51
+ export OUTLINE_OUTPUT_DIR=./outline-docs
36
52
  ```
37
53
 
38
- 2. Interactive setup (lists collections and lets you register one):
54
+ ### Option 3: CLI Arguments
39
55
 
40
- ```bash
41
- bunx @dockstat/outline-sync setup
42
- ```
56
+ Pass configuration via command line (takes precedence over other methods).
43
57
 
44
- 3. Bootstrap a collection (non-interactive):
58
+ ## Usage
45
59
 
46
- ```bash
47
- bunx @dockstat/outline-sync init --collection="COLLECTION_UUID"
48
- # or multiple:
49
- bunx @dockstat/outline-sync init --collection="id1" --collection="id2"
50
- ```
60
+ ### Commands
51
61
 
52
- This creates/updates:
62
+ **Initialize config file:**
53
63
 
54
- * `configs/outline-sync.json` — top-level config listing collections
55
- * `configs/<collection-id>.config.json` — per-collection mapping config
56
- * `configs/<collection-id>.pages.json` — assembled manifest of pages (used by sync)
57
- * `docs/...` — markdown files saved folder-based (`<slug>/README.md`)
64
+ ```bash
65
+ outline-sync init
66
+ ```
58
67
 
59
- 4. Run a dry-run sync:
68
+ **One-time sync (down only):**
60
69
 
61
70
  ```bash
62
- bunx @dockstat/outline-sync sync --collection="COLLECTION_UUID" --dry-run
71
+ outline-sync sync
72
+ # or with config file
73
+ outline-sync sync --config custom-config.json
63
74
  ```
64
75
 
65
- 5. Run an actual sync/push/pull:
76
+ **Watch mode (bidirectional):**
66
77
 
67
78
  ```bash
68
- bunx @dockstat/outline-sync sync --collection="COLLECTION_UUID"
69
- bunx @dockstat/outline-sync pull --collection="COLLECTION_UUID"
70
- bunx @dockstat/outline-sync push --collection="COLLECTION_UUID"
79
+ outline-sync watch
71
80
  ```
72
81
 
73
- You can pass `--collection` multiple times to run against several collections in one invocation:
82
+ **CI/CD mode:**
74
83
 
75
84
  ```bash
76
- bunx @dockstat/outline-sync sync \
77
- --collection="id-one" --collection="id-two" \
78
- --dry-run
85
+ outline-sync ci
79
86
  ```
80
87
 
81
- You can also pass `--api-key="sk_..."` or `--base-url="https://custom.outline"` on the CLI; these override environment variables.
88
+ ## API Reference (CLI Commands)
82
89
 
83
- ---
90
+ ### `outline-sync init`
84
91
 
85
- # CLI reference
92
+ Creates a sample `outline-sync.config.json` configuration file in the current directory.
86
93
 
87
- ```
88
- Usage:
89
- OUTLINE_API_KEY=... bunx @dockstat/outline-sync [command] [--collection=ID]... [--dry-run] [--api-key="..."]
90
-
91
- Commands:
92
- setup - interactive setup (list & add a collection)
93
- list-collections - print the collections visible to the API key
94
- init --collection=ID - bootstrap pages.json + markdown for collection (repeatable)
95
- pull --collection=ID - pull remote changes into local files (repeatable)
96
- push --collection=ID - push local changes to remote (repeatable)
97
- sync --collection=ID - bidirectional sync (timestamp-based) (repeatable)
98
-
99
- Flags:
100
- --collection=ID Repeatable; run command for multiple collection ids
101
- --dry-run Preview actions (no writes to Outline or disk)
102
- --api-key="..." Provide Outline API key (overrides env var)
103
- --base-url="..." Custom Outline base URL (overrides default)
104
- --help, -h
94
+ **Example:**
95
+ ```bash
96
+ outline-sync init
105
97
  ```
106
98
 
107
- ---
99
+ ### `outline-sync sync [options]`
108
100
 
109
- # Config layout & examples
101
+ Performs a one-time synchronization from your Outline wiki to your local folder. This command only pulls changes from Outline.
110
102
 
111
- Project layout:
103
+ **Options:**
112
104
 
113
- ```
114
- configs/
115
- outline-sync.json # top-level config (collections list)
116
- <collection_id>.config.json # mapping rules + saveDir for a collection
117
- <collection_id>.pages.json # assembled pages manifest used by sync
118
- docs/ # markdown files (default saveDir)
105
+ - `--url <url>`: The URL of your Outline instance. Can also be set via `OUTLINE_URL` env var or config file.
106
+ - `--token <token>`: Your Outline API token. Can also be set via `OUTLINE_TOKEN` env var or config file.
107
+ - `--output <dir>`: The local directory where documents will be stored. Default: `./outline-docs`
108
+ - `--config <path>`: Path to config file. Default: `./outline-sync.config.json`
109
+
110
+ **Example:**
111
+ ```bash
112
+ outline-sync sync --url https://my.outline.app --token <YOUR_TOKEN> --output ~/my-wiki
119
113
  ```
120
114
 
121
- ## `configs/outline-sync.json` (top-level)
115
+ ### `outline-sync watch [options]`
122
116
 
123
- ```json
124
- {
125
- "collections": [
126
- {
127
- "id": "COLLECTION_UUID",
128
- "name": "Support Docs",
129
- "saveDir": "docs",
130
- "pagesFile": "configs/COLLECTION_UUID.pages.json",
131
- "configFile": "configs/COLLECTION_UUID.config.json"
132
- }
133
- ]
134
- }
135
- ```
117
+ Starts a persistent process that watches for changes in your local directory and syncs them to Outline, and also pulls new changes from Outline periodically. This enables bidirectional synchronization.
136
118
 
137
- ## `configs/<collection_id>.config.json` (mapping rules)
119
+ **Options:**
138
120
 
139
- ```json
140
- {
141
- "collectionId": "COLLECTION_UUID",
142
- "saveDir": "docs",
143
- "mappings": [
144
- {
145
- "match": { "id": "doc-id-123" },
146
- "path": "guides/setup/" // directory mapping → will place doc as guides/setup/README.md
147
- },
148
- {
149
- "match": { "title": "API Reference" },
150
- "path": "reference/README.md" // explicit filename mapping
151
- }
152
- ]
153
- }
154
- ```
121
+ - `--url <url>`: The URL of your Outline instance.
122
+ - `--token <token>`: Your Outline API token.
123
+ - `--output <dir>`: The local directory being watched. Default: `./outline-docs`
124
+ - `--config <path>`: Path to config file. Default: `./outline-sync.config.json`
155
125
 
156
- Rules:
126
+ **Example:**
127
+ ```bash
128
+ outline-sync watch
129
+ ```
157
130
 
158
- * Match by `id` (preferred) or `title` (exact match).
159
- * `path` can be:
131
+ ### `outline-sync ci [options]`
160
132
 
161
- * directory-like (`guides/setup/` or any path without `.md`) page becomes `<path>/README.md` and children inherit that directory,
162
- * explicit file (`reference/README.md`) → used verbatim (relative to project root unless you give an absolute path),
163
- * bare filename → placed under parent directory or `saveDir`.
133
+ Executes a CI/CD-friendly synchronization. This command first pulls all documents from Outline, then checks for any local changes in your configured output directory, and finally pushes those local changes back to Outline. Designed for automated environments.
164
134
 
165
- ## `configs/<collection_id>.pages.json` (generated)
135
+ **Options:**
166
136
 
167
- This is a manifest of pages used by the sync engine. Example:
137
+ - `--url <url>`: The URL of your Outline instance.
138
+ - `--token <token>`: Your Outline API token.
139
+ - `--output <dir>`: The local directory to synchronize. Default: `./outline-docs`
140
+ - `--config <path>`: Path to config file. Default: `./outline-sync.config.json`
168
141
 
169
- ```json
170
- {
171
- "collectionId": "COLLECTION_UUID",
172
- "pages": [
173
- {
174
- "title": "Product",
175
- "file": "docs/product/README.md",
176
- "id": "doc-product-id",
177
- "children": [
178
- {
179
- "title": "Getting Started",
180
- "file": "docs/product/getting-started/README.md",
181
- "id": "doc-getting-started-id",
182
- "children": []
183
- }
184
- ]
185
- }
186
- ]
187
- }
142
+ **Example:**
143
+ ```bash
144
+ outline-sync ci --output ./project-docs
188
145
  ```
189
146
 
190
- `pages.json` is updated when new remote documents are created (IDs get written back).
191
-
192
147
  ---
193
148
 
194
- # How syncing & conflict resolution works
149
+ ## Examples
195
150
 
196
- * The tool uses Git commit timestamp (if file is tracked) as the authoritative local timestamp. If Git info isn't available, it falls back to filesystem modification time.
197
- * For `sync` (default bidirectional flow): the *newer* version wins (remote.updatedAt vs local timestamp). The tool **ignores whitespace/newline-only differences** when deciding whether content actually differs — if the only difference is formatting, no update is performed.
198
- * For `pull`: remote always wins and overwrites the local file if content differs (ignoring whitespace).
199
- * For `push`: local always wins; remote is updated if content differs (ignoring whitespace).
200
- * When writing to existing local files, a backup is created: `path/to/file.md.outline-sync.bak.<timestamp>`.
151
+ ### Example 1: Root README
201
152
 
202
- ---
153
+ Place a specific document as your project's main README:
203
154
 
204
- # File layout style
155
+ ```json
156
+ {
157
+ "customPaths": {
158
+ "doc-abc123": "../../README.md"
159
+ }
160
+ }
161
+ ```
205
162
 
206
- Default behavior: folder-based.
163
+ ### Example 2: Mixed Structure
207
164
 
208
- For each page:
165
+ Combine auto-organized docs with custom locations:
209
166
 
210
- ```
211
- <saveDir>/<ancestor-slug>/<page-slug>/README.md
167
+ ```json
168
+ {
169
+ "outputDir": "./docs",
170
+ "customPaths": {
171
+ "doc-home": "../../README.md",
172
+ "doc-api": "../../API.md",
173
+ "doc-contrib": "../../CONTRIBUTING.md"
174
+ }
175
+ }
212
176
  ```
213
177
 
214
- Example Outline structure:
178
+ Other documents without custom paths will be organized in `./docs` by collection.
215
179
 
216
- * Product
180
+ ### Example 3: Getting Document IDs
217
181
 
218
- * Getting Started
182
+ After first sync, check the frontmatter:
219
183
 
220
- * Install
221
-
222
- Results in:
184
+ ````markdown
185
+ ---
186
+ id: abc123def456
187
+ title: My Document
188
+ collectionId: xyz789
189
+ parentDocumentId: null
190
+ updatedAt: '2024-01-15T10:30:00Z'
191
+ urlId: my-document-abc123
192
+ ---
223
193
 
224
- ```
225
- docs/product/README.md
226
- docs/product/getting-started/README.md
227
- docs/product/getting-started/install/README.md
194
+ Document content here...
228
195
  ```
229
196
 
230
- You can override per-page locations in the `<collection_id>.config.json` mappings (see above).
197
+ Use the `id` field in your `customPaths` configuration.
231
198
 
232
- ---
233
-
234
- # Advanced notes
199
+ ## Features
235
200
 
236
- * **Multiple collections:** pass `--collection` multiple times to operate on multiple collections in order. If `--collection` is not provided, the tool will operate on all collections listed in `configs/outline-sync.json`.
237
- * **API key sources:** first CLI `--api-key`, then `OUTLINE_API_KEY` env var. Passing `--api-key` sets `process.env.OUTLINE_API_KEY` early so imports read it properly.
238
- * **Base URL:** `--base-url` for self-hosted/alternate Outline instances.
239
- * **Dry-run:** always test with `--dry-run` before doing real `push`/`sync`.
240
- * **Mappings precedence:** `id` match wins over `title` match; explicit mapping wins and children will inherit directories if mapping points to a directory.
241
- * **Mapping templates:** not supported by default; you can use direct paths and directories via `path` in mapping rules.
201
+ - Sync Outline Local
202
+ - Sync Local Outline
203
+ - Each doc in own folder with README.md
204
+ - Custom path mapping for specific documents
205
+ - Frontmatter metadata preservation
206
+ - CI/CD integration
207
+ - ✅ File watching
242
208
 
243
- ---
209
+ ## CI/CD Integration
244
210
 
245
- # Troubleshooting
211
+ ### GitHub Actions
246
212
 
247
- * "Manifest not found": run `init --collection=ID` or `setup` to create `configs/<collection_id>.pages.json`.
248
- * API errors / auth: check `OUTLINE_API_KEY` (or pass `--api-key`) and `--base-url`. Ensure token has access to the collection.
249
- * Permissions / writing files: make sure the process has write permissions for `docs/` and `configs/`.
250
- * Large collections: the Outline API is paginated (the client already pages with limit=100). If you hit rate limits, re-run with fewer collections at a time or add retries in your CI.
213
+ ```yaml
214
+ name: Outline Sync
251
215
 
252
- ---
216
+ on:
217
+ push:
218
+ branches: [main]
219
+ schedule:
220
+ - cron: "0 */6 * * *" # Every 6 hours
253
221
 
254
- # Contributing & development
222
+ jobs:
223
+ sync:
224
+ runs-on: ubuntu-latest
225
+ steps:
226
+ - uses: actions/checkout@v4
227
+ - uses: oven-sh/setup-bun@v1
228
+ - run: bun install
229
+ - run: bun run ci
230
+ env:
231
+ OUTLINE_URL: ${{ secrets.OUTLINE_URL }}
232
+ OUTLINE_TOKEN: ${{ secrets.OUTLINE_TOKEN }}
233
+ - uses: stefanzweifel/git-auto-commit-action@v5
234
+ with:
235
+ commit_message: "docs: sync from Outline"
236
+ ```
255
237
 
256
- Contributions welcome!
238
+ Store `outline-sync.config.json` in your repo (without sensitive tokens):
257
239
 
258
- * Repo layout is modular (`bin/cli.ts`, `lib/*.ts`) — feel free to add features:
240
+ ```json
241
+ {
242
+ "outputDir": "./docs",
243
+ "customPaths": {
244
+ "doc-abc123": "../../README.md"
245
+ }
246
+ }
247
+ ```
259
248
 
260
- * mapping templates (`{{slug}}`) or glob rules
261
- * parallel collection sync with a `--concurrent` flag
262
- * a `--api-key-file` option to read secrets from a file
263
- * richer conflict resolution (merge or interactive prompts)
249
+ Tokens can be passed via environment variables in CI.
264
250
 
265
- When developing locally:
251
+ ## License
266
252
 
267
- ```bash
268
- # run CLI against a local checkout
269
- bun run bin/cli.ts setup
270
- bun run bin/cli.ts init --collection="..."
271
- ```
253
+ MIT