@dboio/cli 0.7.2 → 0.8.2
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/README.md +153 -11
- package/package.json +3 -2
- package/src/commands/add.js +64 -11
- package/src/commands/clone.js +749 -63
- package/src/commands/init.js +28 -4
- package/src/commands/install.js +10 -1
- package/src/commands/login.js +69 -0
- package/src/commands/push.js +102 -18
- package/src/lib/config.js +101 -0
- package/src/lib/delta.js +14 -1
- package/src/lib/diff.js +71 -15
- package/src/lib/ignore.js +145 -0
- package/src/lib/structure.js +114 -0
- package/src/lib/ticketing.js +6 -3
- package/src/lib/timestamps.js +31 -9
package/README.md
CHANGED
|
@@ -98,6 +98,9 @@ dbo --version
|
|
|
98
98
|
# 1. Initialize configuration for the current directory
|
|
99
99
|
dbo init --domain my-domain.com
|
|
100
100
|
|
|
101
|
+
# 1b. Combined with cloning an app to local project bin
|
|
102
|
+
dbo init --domain my-domain.com --app myapp --clone
|
|
103
|
+
|
|
101
104
|
# 2. Authenticate
|
|
102
105
|
dbo login
|
|
103
106
|
|
|
@@ -144,6 +147,7 @@ All configuration is **directory-scoped**. Each project folder maintains its own
|
|
|
144
147
|
"AppUID": "abc123",
|
|
145
148
|
"AppName": "My App",
|
|
146
149
|
"AppShortName": "myapp",
|
|
150
|
+
"cloneSource": "default",
|
|
147
151
|
"ContentPlacement": "bin",
|
|
148
152
|
"MediaPlacement": "fullpath"
|
|
149
153
|
}
|
|
@@ -159,6 +163,7 @@ All configuration is **directory-scoped**. Each project folder maintains its own
|
|
|
159
163
|
| `AppModifyKey` | string | ModifyKey for locked/production apps (set by `dbo clone`, used for submission guards) |
|
|
160
164
|
| `TransactionKeyPreset` | `RowUID` \| `RowID` | Row key type for auto-assembled expressions (set during `dbo init`/`dbo clone`, default `RowUID`) |
|
|
161
165
|
| `TicketSuggestionOutput` | string | Output UID for fetching ticket suggestions during error recovery (set during `dbo init`, default `ojaie9t3o0kfvliahnuuda`) |
|
|
166
|
+
| `cloneSource` | `"default"` \| file path \| URL | Where the last `dbo clone` fetched app JSON from. `"default"` = server fetch via `AppShortName`; any other value = the explicit local file path or URL used. Set automatically after each successful clone. |
|
|
162
167
|
| `ContentPlacement` | `bin` \| `path` \| `ask` | Where to place content files during clone |
|
|
163
168
|
| `MediaPlacement` | `bin` \| `fullpath` \| `ask` | Where to place media files during clone |
|
|
164
169
|
| `<Entity>FilenameCol` | column name | Filename column for entity-dir records (e.g., `ExtensionFilenameCol`) |
|
|
@@ -174,6 +179,59 @@ These are set interactively on first clone and saved for future use. Pre-set the
|
|
|
174
179
|
|
|
175
180
|
The `_domain` field in `app.json` stores the project's reference domain (set during `dbo clone`). This is committed to git and used for domain-change detection when running `dbo init --force` or `dbo clone --domain`. It provides a stable cross-user baseline — all collaborators share the same reference domain.
|
|
176
181
|
|
|
182
|
+
### `.dboignore`
|
|
183
|
+
|
|
184
|
+
A gitignore-style file in the project root that controls which files and directories are excluded from CLI operations. Created automatically by `dbo init` with sensible defaults.
|
|
185
|
+
|
|
186
|
+
**Used by:**
|
|
187
|
+
- `dbo init` — scaffold empty-check (determines if directory is "effectively empty")
|
|
188
|
+
- `dbo add .` — directory scanning (which files/dirs to skip); also checked in single-file mode (`dbo add file.html`)
|
|
189
|
+
- `dbo push` — metadata file discovery: skips matching `.metadata.json` / `_output~*.json` files AND any record whose companion content file (`@reference`) matches an ignore pattern
|
|
190
|
+
|
|
191
|
+
**Bypass:** Use `dbo input -d '...'` to submit expressions for a file that would otherwise be ignored — `dbo input` never does file discovery so `.dboignore` does not apply.
|
|
192
|
+
|
|
193
|
+
**Pattern examples:**
|
|
194
|
+
|
|
195
|
+
```gitignore
|
|
196
|
+
# Ignore all SQL companion files (output records with CustomSQL)
|
|
197
|
+
**/*.CustomSQL.sql
|
|
198
|
+
|
|
199
|
+
# Ignore a specific record (by metadata file path)
|
|
200
|
+
Bins/app/my-draft-page.metadata.json
|
|
201
|
+
|
|
202
|
+
# Ignore an entire directory
|
|
203
|
+
Bins/staging/
|
|
204
|
+
|
|
205
|
+
# Ignore all content in Bins/ (still push entity-dir records like Extensions/)
|
|
206
|
+
Bins/
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Syntax:** Same as `.gitignore` — glob patterns, `#` comments, blank lines, negation with `!`, directory-only patterns with trailing `/`.
|
|
210
|
+
|
|
211
|
+
**Default patterns:**
|
|
212
|
+
|
|
213
|
+
```gitignore
|
|
214
|
+
.dbo/ # DBO internal
|
|
215
|
+
*.dboio.json # Export files
|
|
216
|
+
app.json # Clone output
|
|
217
|
+
.app.json # Clone baseline
|
|
218
|
+
dbo.deploy.json # Deployment manifest
|
|
219
|
+
.git/ # Version control
|
|
220
|
+
.gitignore
|
|
221
|
+
node_modules/ # Node
|
|
222
|
+
package.json
|
|
223
|
+
package-lock.json
|
|
224
|
+
.claude/ # AI / tooling
|
|
225
|
+
.mcp.json
|
|
226
|
+
.idea/ # Editor / IDE
|
|
227
|
+
.vscode/
|
|
228
|
+
*.codekit3
|
|
229
|
+
README.md # Repo scaffolding
|
|
230
|
+
SETUP.md
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Edit `.dboignore` to add or remove patterns. Committed to git and shared across users.
|
|
234
|
+
|
|
177
235
|
### Legacy migration
|
|
178
236
|
|
|
179
237
|
If your project uses the older `.domain`, `.username`, `.password`, `.cookies` files, `dbo init` will detect them and offer to migrate automatically.
|
|
@@ -210,11 +268,12 @@ dbo init --scaffold --yes # scaffold dirs non-intera
|
|
|
210
268
|
| `--domain <host>` | DBO instance domain |
|
|
211
269
|
| `--username <user>` | DBO username (stored for login default) |
|
|
212
270
|
| `--force` | Overwrite existing configuration. Triggers a domain-change confirmation prompt when the new domain differs from the project reference domain |
|
|
213
|
-
| `--app <shortName>` | App short name (triggers clone after init) |
|
|
271
|
+
| `--app <shortName>` | App short name (triggers clone after init). Prompts for password and authenticates automatically before fetching app data from the server |
|
|
214
272
|
| `--clone` | Clone the app after initialization |
|
|
215
273
|
| `-g, --global` | Install Claude commands globally (`~/.claude/commands/`) |
|
|
216
274
|
| `--local` | Install Claude commands to project (`.claude/commands/`) |
|
|
217
275
|
| `--scaffold` | Pre-create standard project directories (`App Versions`, `Automations`, `Bins`, `Data Sources`, `Documentation`, `Extensions`, `Groups`, `Integrations`, `Sites`) |
|
|
276
|
+
| `--dboignore` | Create `.dboignore` with default patterns (use with `--force` to overwrite existing) |
|
|
218
277
|
| `-y, --yes` | Skip all interactive prompts (legacy migration, Claude Code setup) |
|
|
219
278
|
| `--non-interactive` | Alias for `--yes` |
|
|
220
279
|
|
|
@@ -236,14 +295,24 @@ dbo clone --app myapp
|
|
|
236
295
|
|
|
237
296
|
# Combined with init
|
|
238
297
|
dbo init --domain my-domain.com --app myapp --clone
|
|
298
|
+
|
|
299
|
+
# Extension descriptor sub-directory handling
|
|
300
|
+
dbo clone -e extension # Clone all extensions (sorted by descriptor type)
|
|
301
|
+
dbo clone -e extension --documentation-only # Clone only documentation extensions
|
|
302
|
+
dbo clone -e extension --documentation-only --force # Reset documentation preferences and re-clone
|
|
303
|
+
dbo clone -e extension --descriptor-types false # Clone extensions flat (no descriptor sorting)
|
|
239
304
|
```
|
|
240
305
|
|
|
241
306
|
| Flag | Description |
|
|
242
307
|
|------|-------------|
|
|
243
|
-
| `<source>` | Local
|
|
308
|
+
| `<source>` | Local file path or URL to load app JSON from (optional positional argument) |
|
|
244
309
|
| `--app <name>` | App short name to fetch from server |
|
|
310
|
+
| `-e, --entity <type>` | Only clone a specific entity type (e.g. `output`, `content`, `media`, `extension`) |
|
|
311
|
+
| `--documentation-only` | When used with `-e extension`, clone only documentation extensions |
|
|
312
|
+
| `--descriptor-types <bool>` | Sort extensions into descriptor sub-directories (default: `true`). Set to `false` to use flat `Extensions/` layout |
|
|
245
313
|
| `--domain <host>` | Override domain. Triggers a domain-change confirmation prompt when it differs from the project reference domain |
|
|
246
|
-
|
|
|
314
|
+
| `--force` | Skip source mismatch confirmation and change detection; re-processes all files |
|
|
315
|
+
| `-y, --yes` | Auto-accept all prompts (also skips source mismatch confirmation) |
|
|
247
316
|
| `-v, --verbose` | Show HTTP request details |
|
|
248
317
|
|
|
249
318
|
#### Domain change detection
|
|
@@ -254,17 +323,41 @@ The project's reference domain is stored in `app.json._domain` (committed to git
|
|
|
254
323
|
|
|
255
324
|
#### What clone does
|
|
256
325
|
|
|
257
|
-
1. **Loads app JSON** — from a local file, server API, or interactive prompt
|
|
258
|
-
2. **Updates `.dbo/config.json`** — saves `AppID`, `AppUID`, `AppName`, `AppShortName`,
|
|
326
|
+
1. **Loads app JSON** — from a local file, server API, or interactive prompt. A spinner shows progress while fetching from the server (responses can be slow as the JSON is assembled on demand)
|
|
327
|
+
2. **Updates `.dbo/config.json`** — saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
|
|
259
328
|
3. **Updates `package.json`** — populates `name`, `productName`, `description`, `homepage`, and `deploy` script
|
|
260
329
|
4. **Creates directories** — processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
|
|
261
330
|
5. **Saves `.dbo/structure.json`** — maps BinIDs to directory paths for file placement
|
|
262
|
-
6. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory
|
|
331
|
+
6. **Writes content files** — decodes base64 content, creates `*.metadata.json` + content files in the correct bin directory. When a record's `Extension` field is empty, prompts you to choose from `css`, `js`, `html`, `xml`, `txt`, `md`, `cs`, `json`, `sql` (or skip to keep no extension). The chosen extension is saved back into the `metadata.json` `Extension` field
|
|
263
332
|
7. **Downloads media files** — fetches binary files (images, CSS, fonts) from the server via `/api/media/{uid}` and saves with metadata
|
|
264
333
|
8. **Processes entity-dir records** — entities matching project directories (`extension`, `app_version`, `data_source`, `site`, `group`, `integration`, `automation`) are saved as `.metadata.json` files in their corresponding directory (e.g., `Extensions/`, `Data Sources/`)
|
|
265
334
|
9. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
|
|
266
335
|
10. **Saves `app.json`** — clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
|
|
267
336
|
|
|
337
|
+
#### Clone source tracking
|
|
338
|
+
|
|
339
|
+
After every successful clone, the source is persisted as `cloneSource` in `.dbo/config.json`:
|
|
340
|
+
|
|
341
|
+
- `"default"` — app JSON was fetched from the server using `AppShortName` (the normal flow)
|
|
342
|
+
- A file path or URL — set when an explicit `<source>` argument was provided (e.g. `dbo clone /path/to/export.json`)
|
|
343
|
+
|
|
344
|
+
On subsequent clones, if you provide an explicit source that **differs** from the stored `cloneSource`, the CLI warns and asks for confirmation before proceeding:
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
⚠ This project was previously cloned from: default
|
|
348
|
+
Requested source: /path/to/export.json
|
|
349
|
+
? Clone from the new source anyway? This will update the stored clone source. (y/N)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Use `--force` or `-y` to skip the prompt and override without being asked.
|
|
353
|
+
|
|
354
|
+
If the initial clone attempt fails (network error, file not found, empty response), the CLI prompts for a fallback source to retry with instead of aborting:
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
⚠ Source did not return expected results: No app found with ShortName "myapp"
|
|
358
|
+
? Enter another local file path or URL to retry (or leave empty to abort):
|
|
359
|
+
```
|
|
360
|
+
|
|
268
361
|
#### Change detection on re-clone
|
|
269
362
|
|
|
270
363
|
When cloning an app that was already cloned locally, the CLI detects existing files and compares modification times against the server's `_LastUpdated`. You'll be prompted to overwrite, compare differences, or skip — same as `dbo pull`. Use `-y` to auto-accept all changes.
|
|
@@ -312,7 +405,7 @@ Entity types that correspond to project directories (`extension`, `app_version`,
|
|
|
312
405
|
|
|
313
406
|
| Entity Key | Directory |
|
|
314
407
|
|------------|-----------|
|
|
315
|
-
| `extension` | `Extensions
|
|
408
|
+
| `extension` | `Extensions/<Descriptor>/` (see below) |
|
|
316
409
|
| `app_version` | `App Versions/` |
|
|
317
410
|
| `data_source` | `Data Sources/` |
|
|
318
411
|
| `site` | `Sites/` |
|
|
@@ -326,6 +419,44 @@ If any columns contain base64-encoded content, the CLI prompts to extract them a
|
|
|
326
419
|
|
|
327
420
|
Use `-y` to skip prompts (uses `Name` column, no content extraction).
|
|
328
421
|
|
|
422
|
+
#### Extension descriptor sub-directories
|
|
423
|
+
|
|
424
|
+
Extension records are organized into sub-directories under `Extensions/` based on their `Descriptor` column value. A special extension type called `descriptor_definition` (itself an extension with `Descriptor === "descriptor_definition"`) provides the mapping from descriptor key (`String1`) to display/directory name (`Name`).
|
|
425
|
+
|
|
426
|
+
During clone, the CLI:
|
|
427
|
+
|
|
428
|
+
1. **Pre-scans** all extension records for `descriptor_definition` entries and builds a `String1 → Name` mapping
|
|
429
|
+
2. **Creates sub-directories** under `Extensions/` for each mapped descriptor (e.g., `Extensions/Documentation/`, `Extensions/Includes/`)
|
|
430
|
+
3. **Always creates** `Extensions/Unsupported/` — extensions with an unmapped or null `Descriptor` are placed here
|
|
431
|
+
4. **Prompts per descriptor** — filename column and content extraction prompts fire once per unique `Descriptor` value (not once for all extensions)
|
|
432
|
+
5. **Persists the mapping** in `.dbo/structure.json` under `descriptorMapping`
|
|
433
|
+
|
|
434
|
+
**Config keys** (saved per descriptor in `config.json`):
|
|
435
|
+
|
|
436
|
+
| Key | Example | Purpose |
|
|
437
|
+
|-----|---------|---------|
|
|
438
|
+
| `Extension_<descriptor>_FilenameCol` | `Extension_documentation_FilenameCol` | Filename column for that descriptor type |
|
|
439
|
+
| `Extension_<descriptor>_ContentExtractions` | `Extension_documentation_ContentExtractions` | Content extraction preferences per column |
|
|
440
|
+
| `ExtensionDocumentationMDPlacement` | `"inline"` or `"root"` | Where to place documentation MD files |
|
|
441
|
+
|
|
442
|
+
**Documentation alternate placement**: When extracting MD content from `documentation` descriptor extensions, the CLI offers a choice:
|
|
443
|
+
|
|
444
|
+
- **Root placement** (`/Documentation/<filename>.md`) — recommended for easy access; creates `@/Documentation/<filename>.md` references in metadata
|
|
445
|
+
- **Inline placement** (`Extensions/Documentation/<filename>.md`) — keeps files alongside metadata
|
|
446
|
+
|
|
447
|
+
Root-placed files use absolute-from-root `@/` references (e.g., `@/Documentation/my-doc.md`) so `dbo push` can locate them regardless of where the metadata lives.
|
|
448
|
+
|
|
449
|
+
**Entity filter support**:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
dbo clone -e extension --documentation-only # Clone only documentation extensions
|
|
453
|
+
dbo clone -e extension --documentation-only --force # Reset documentation preferences and re-clone
|
|
454
|
+
dbo clone -e extension # Clone all extensions (all descriptor types)
|
|
455
|
+
dbo clone -e extension --descriptor-types false # Flat layout (no descriptor sorting)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**`dbo add` auto-inference**: Running `dbo add Documentation/my-doc.md` when `ExtensionDocumentationMDPlacement` is `"root"` auto-creates a companion `.metadata.json` in `Extensions/Documentation/` with the correct `@/` reference.
|
|
459
|
+
|
|
329
460
|
#### Output structure
|
|
330
461
|
|
|
331
462
|
```
|
|
@@ -344,9 +475,15 @@ project/
|
|
|
344
475
|
CurrentTicketID.metadata.json
|
|
345
476
|
ticket_test/ # ← another bin directory
|
|
346
477
|
...
|
|
347
|
-
Extensions/ # ←
|
|
348
|
-
|
|
349
|
-
|
|
478
|
+
Extensions/ # ← extension records organized by descriptor
|
|
479
|
+
Documentation/ # ← descriptor sub-directory (from descriptor_definition)
|
|
480
|
+
MyDoc.uid1.metadata.json
|
|
481
|
+
MyDoc.uid1.String10.md # ← extracted content column
|
|
482
|
+
Includes/
|
|
483
|
+
Header.uid2.metadata.json
|
|
484
|
+
Unsupported/ # ← always created (unmapped/null descriptors)
|
|
485
|
+
Documentation/ # ← root-placed documentation MD files (optional)
|
|
486
|
+
MyDoc.md
|
|
350
487
|
Data Sources/
|
|
351
488
|
MySQL-Primary.metadata.json
|
|
352
489
|
Sites/
|
|
@@ -896,6 +1033,8 @@ After accepting changes, file modification times are synced to the server's `_La
|
|
|
896
1033
|
|
|
897
1034
|
Push local files back to DBO.io using metadata from a previous pull. This is the counterpart to `dbo content pull` and `dbo output --save`.
|
|
898
1035
|
|
|
1036
|
+
Files and records matching `.dboignore` patterns are skipped — both by metadata file path (e.g. `*.metadata.json`) and by companion content file path (e.g. a record whose `@Content` points to an ignored `.sql` file). To push an ignored file directly, use `dbo input -d '...'` with an explicit expression.
|
|
1037
|
+
|
|
899
1038
|
#### Round-trip workflow
|
|
900
1039
|
|
|
901
1040
|
```bash
|
|
@@ -1069,6 +1208,8 @@ The next `dbo push` processes all pending deletions before pushing file changes.
|
|
|
1069
1208
|
|
|
1070
1209
|
Add a new file to DBO.io by creating a server record. Similar to `git add`, this registers a local file with the server.
|
|
1071
1210
|
|
|
1211
|
+
Files matching `.dboignore` patterns are skipped — both in directory-scan mode (`dbo add .`) and single-file mode (`dbo add file.html`). Use `dbo input` to create a record for an ignored file directly.
|
|
1212
|
+
|
|
1072
1213
|
#### Single file
|
|
1073
1214
|
|
|
1074
1215
|
```bash
|
|
@@ -1357,7 +1498,8 @@ Smart behavior:
|
|
|
1357
1498
|
- Compares file hashes — unchanged files are skipped
|
|
1358
1499
|
- Adds project-scoped plugins to `.gitignore` (source of truth is `plugins/claude/` at repo root)
|
|
1359
1500
|
- Per-plugin scope (project or global) is stored in `.dbo/config.local.json` and remembered for future upgrades
|
|
1360
|
-
- On
|
|
1501
|
+
- On re-install (e.g. after `npm install` or `npm link`), scope is inferred automatically from the global plugin registry (`~/.claude/plugins/installed_plugins.json`) or existing project installation — no prompt is shown
|
|
1502
|
+
- On first install with no prior preference and no existing installation, prompts for scope
|
|
1361
1503
|
- `--global` / `--local` flags override stored preferences and update them
|
|
1362
1504
|
|
|
1363
1505
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dboio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "CLI for the DBO.io framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
"test": "node --test src/**/*.test.js tests/**/*.test.js"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"commander": "^12.1.0",
|
|
25
24
|
"chalk": "^5.3.0",
|
|
25
|
+
"commander": "^12.1.0",
|
|
26
|
+
"ignore": "^7.0.5",
|
|
26
27
|
"inquirer": "^9.3.0"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
package/src/commands/add.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { readFile, readdir, stat, writeFile } from 'fs/promises';
|
|
2
|
+
import { readFile, readdir, stat, writeFile, mkdir } from 'fs/promises';
|
|
3
3
|
import { join, dirname, basename, extname, relative } from 'path';
|
|
4
4
|
import { DboClient } from '../lib/client.js';
|
|
5
5
|
import { buildInputBody, checkSubmitErrors } from '../lib/input-parser.js';
|
|
6
6
|
import { formatResponse, formatError } from '../lib/formatter.js';
|
|
7
7
|
import { log } from '../lib/logger.js';
|
|
8
8
|
import { shouldSkipColumn } from '../lib/columns.js';
|
|
9
|
-
import { loadAppConfig } from '../lib/config.js';
|
|
9
|
+
import { loadAppConfig, loadExtensionDocumentationMDPlacement, loadDescriptorFilenamePreference } from '../lib/config.js';
|
|
10
10
|
import { checkStoredTicket, clearGlobalTicket } from '../lib/ticketing.js';
|
|
11
11
|
import { checkModifyKey, isModifyKeyError, handleModifyKeyError } from '../lib/modify-key.js';
|
|
12
|
-
|
|
13
|
-
// Directories and patterns to skip when scanning with `dbo add .`
|
|
14
|
-
const IGNORE_DIRS = new Set(['.dbo', '.git', 'node_modules', '.svn', '.hg']);
|
|
12
|
+
import { loadIgnore } from '../lib/ignore.js';
|
|
15
13
|
|
|
16
14
|
export const addCommand = new Command('add')
|
|
17
15
|
.description('Add a new file to DBO.io (creates record on server)')
|
|
@@ -57,7 +55,32 @@ export const addCommand = new Command('add')
|
|
|
57
55
|
* Add a single file to DBO.io.
|
|
58
56
|
* Returns the defaults used (entity, contentColumn) for batch reuse.
|
|
59
57
|
*/
|
|
58
|
+
/**
|
|
59
|
+
* Detect whether a file lives in the project-root Documentation/ directory
|
|
60
|
+
* AND the ExtensionDocumentationMDPlacement preference is 'root'.
|
|
61
|
+
* Returns detection info object or null if not applicable.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} filePath - Path to the file being added
|
|
64
|
+
*/
|
|
65
|
+
async function detectDocumentationFile(filePath) {
|
|
66
|
+
const placement = await loadExtensionDocumentationMDPlacement();
|
|
67
|
+
if (placement !== 'root') return null;
|
|
68
|
+
|
|
69
|
+
const rel = relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
70
|
+
if (!rel.startsWith('Documentation/')) return null;
|
|
71
|
+
|
|
72
|
+
return { entity: 'extension', descriptor: 'documentation' };
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
async function addSingleFile(filePath, client, options, batchDefaults) {
|
|
76
|
+
// Check .dboignore before doing any processing
|
|
77
|
+
const ig = await loadIgnore();
|
|
78
|
+
const relPath = relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
79
|
+
if (ig.ignores(relPath)) {
|
|
80
|
+
log.dim(`Skipped (dboignored): ${relPath}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
const dir = dirname(filePath);
|
|
62
85
|
const ext = extname(filePath);
|
|
63
86
|
const base = basename(filePath, ext);
|
|
@@ -87,6 +110,33 @@ async function addSingleFile(filePath, client, options, batchDefaults) {
|
|
|
87
110
|
return await submitAdd(meta, metaPath, filePath, client, options);
|
|
88
111
|
}
|
|
89
112
|
|
|
113
|
+
// Step 3b: Auto-infer entity/descriptor for Documentation/ files when root placement is configured
|
|
114
|
+
const docInfo = await detectDocumentationFile(filePath);
|
|
115
|
+
if (docInfo) {
|
|
116
|
+
const filenameCol = await loadDescriptorFilenamePreference('documentation') || 'Name';
|
|
117
|
+
const docBase = basename(filePath, extname(filePath));
|
|
118
|
+
const companionDir = join(process.cwd(), 'Extensions', 'Documentation');
|
|
119
|
+
await mkdir(companionDir, { recursive: true });
|
|
120
|
+
|
|
121
|
+
const docMeta = {
|
|
122
|
+
_entity: 'extension',
|
|
123
|
+
Descriptor: 'documentation',
|
|
124
|
+
[filenameCol]: docBase,
|
|
125
|
+
Name: docBase,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Find the content column name from saved preferences
|
|
129
|
+
const contentCol = filenameCol === 'Name' ? 'String10' : 'String10';
|
|
130
|
+
const relPath = relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
131
|
+
docMeta[contentCol] = `@/${relPath}`;
|
|
132
|
+
docMeta._contentColumns = [contentCol];
|
|
133
|
+
|
|
134
|
+
const docMetaFile = join(companionDir, `${docBase}.metadata.json`);
|
|
135
|
+
await writeFile(docMetaFile, JSON.stringify(docMeta, null, 2) + '\n');
|
|
136
|
+
log.success(`Created companion metadata at ${docMetaFile}`);
|
|
137
|
+
return await submitAdd(docMeta, docMetaFile, filePath, client, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
90
140
|
// Step 4: No usable metadata — interactive wizard
|
|
91
141
|
const inquirer = (await import('inquirer')).default;
|
|
92
142
|
|
|
@@ -379,23 +429,26 @@ async function addDirectory(dirPath, client, options) {
|
|
|
379
429
|
* - It has no companion .metadata.json, OR
|
|
380
430
|
* - Its .metadata.json exists but has no _CreatedOn (never been on server)
|
|
381
431
|
*/
|
|
382
|
-
async function findUnaddedFiles(dir) {
|
|
432
|
+
async function findUnaddedFiles(dir, ig) {
|
|
433
|
+
if (!ig) ig = await loadIgnore();
|
|
434
|
+
|
|
383
435
|
const results = [];
|
|
384
436
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
385
437
|
|
|
386
438
|
for (const entry of entries) {
|
|
387
439
|
const fullPath = join(dir, entry.name);
|
|
440
|
+
const relPath = relative(process.cwd(), fullPath).replace(/\\/g, '/');
|
|
388
441
|
|
|
389
442
|
if (entry.isDirectory()) {
|
|
390
|
-
if (
|
|
391
|
-
results.push(...await findUnaddedFiles(fullPath));
|
|
443
|
+
if (ig.ignores(relPath + '/') || ig.ignores(relPath)) continue;
|
|
444
|
+
results.push(...await findUnaddedFiles(fullPath, ig));
|
|
392
445
|
continue;
|
|
393
446
|
}
|
|
394
447
|
|
|
395
|
-
// Skip metadata files themselves
|
|
448
|
+
// Skip metadata files themselves (structural, not user-configurable)
|
|
396
449
|
if (entry.name.endsWith('.metadata.json')) continue;
|
|
397
|
-
// Skip
|
|
398
|
-
if (
|
|
450
|
+
// Skip ignored files
|
|
451
|
+
if (ig.ignores(relPath)) continue;
|
|
399
452
|
|
|
400
453
|
// Check for companion metadata
|
|
401
454
|
const ext = extname(entry.name);
|