@dboio/cli 0.6.14 → 0.8.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/README.md +313 -8
- package/package.json +3 -2
- package/src/commands/add.js +56 -11
- package/src/commands/clone.js +1294 -75
- package/src/commands/content.js +1 -1
- package/src/commands/deploy.js +1 -1
- package/src/commands/init.js +39 -4
- package/src/commands/install.js +10 -1
- package/src/commands/login.js +4 -9
- package/src/commands/output.js +56 -3
- package/src/commands/pull.js +3 -3
- package/src/commands/push.js +24 -12
- package/src/lib/config.js +175 -8
- package/src/lib/diff.js +72 -14
- package/src/lib/ignore.js +145 -0
- package/src/lib/input-parser.js +87 -38
- package/src/lib/structure.js +130 -0
- 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
|
}
|
|
@@ -158,6 +162,8 @@ All configuration is **directory-scoped**. Each project folder maintains its own
|
|
|
158
162
|
| `AppShortName` | string | App short name (used for `dbo clone --app`) |
|
|
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`) |
|
|
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. |
|
|
161
167
|
| `ContentPlacement` | `bin` \| `path` \| `ask` | Where to place content files during clone |
|
|
162
168
|
| `MediaPlacement` | `bin` \| `fullpath` \| `ask` | Where to place media files during clone |
|
|
163
169
|
| `<Entity>FilenameCol` | column name | Filename column for entity-dir records (e.g., `ExtensionFilenameCol`) |
|
|
@@ -173,6 +179,41 @@ These are set interactively on first clone and saved for future use. Pre-set the
|
|
|
173
179
|
|
|
174
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.
|
|
175
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)
|
|
189
|
+
- `dbo push` — metadata file discovery (which dirs to skip)
|
|
190
|
+
|
|
191
|
+
**Syntax:** Same as `.gitignore` — glob patterns, `#` comments, blank lines, negation with `!`, directory-only patterns with trailing `/`.
|
|
192
|
+
|
|
193
|
+
**Default patterns:**
|
|
194
|
+
|
|
195
|
+
```gitignore
|
|
196
|
+
.dbo/ # DBO internal
|
|
197
|
+
*.dboio.json # Export files
|
|
198
|
+
app.json # Clone output
|
|
199
|
+
.app.json # Clone baseline
|
|
200
|
+
dbo.deploy.json # Deployment manifest
|
|
201
|
+
.git/ # Version control
|
|
202
|
+
.gitignore
|
|
203
|
+
node_modules/ # Node
|
|
204
|
+
package.json
|
|
205
|
+
package-lock.json
|
|
206
|
+
.claude/ # AI / tooling
|
|
207
|
+
.mcp.json
|
|
208
|
+
.idea/ # Editor / IDE
|
|
209
|
+
.vscode/
|
|
210
|
+
*.codekit3
|
|
211
|
+
README.md # Repo scaffolding
|
|
212
|
+
SETUP.md
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Edit `.dboignore` to add or remove patterns. Committed to git and shared across users.
|
|
216
|
+
|
|
176
217
|
### Legacy migration
|
|
177
218
|
|
|
178
219
|
If your project uses the older `.domain`, `.username`, `.password`, `.cookies` files, `dbo init` will detect them and offer to migrate automatically.
|
|
@@ -214,6 +255,7 @@ dbo init --scaffold --yes # scaffold dirs non-intera
|
|
|
214
255
|
| `-g, --global` | Install Claude commands globally (`~/.claude/commands/`) |
|
|
215
256
|
| `--local` | Install Claude commands to project (`.claude/commands/`) |
|
|
216
257
|
| `--scaffold` | Pre-create standard project directories (`App Versions`, `Automations`, `Bins`, `Data Sources`, `Documentation`, `Extensions`, `Groups`, `Integrations`, `Sites`) |
|
|
258
|
+
| `--dboignore` | Create `.dboignore` with default patterns (use with `--force` to overwrite existing) |
|
|
217
259
|
| `-y, --yes` | Skip all interactive prompts (legacy migration, Claude Code setup) |
|
|
218
260
|
| `--non-interactive` | Alias for `--yes` |
|
|
219
261
|
|
|
@@ -235,14 +277,24 @@ dbo clone --app myapp
|
|
|
235
277
|
|
|
236
278
|
# Combined with init
|
|
237
279
|
dbo init --domain my-domain.com --app myapp --clone
|
|
280
|
+
|
|
281
|
+
# Extension descriptor sub-directory handling
|
|
282
|
+
dbo clone -e extension # Clone all extensions (sorted by descriptor type)
|
|
283
|
+
dbo clone -e extension --documentation-only # Clone only documentation extensions
|
|
284
|
+
dbo clone -e extension --documentation-only --force # Reset documentation preferences and re-clone
|
|
285
|
+
dbo clone -e extension --descriptor-types false # Clone extensions flat (no descriptor sorting)
|
|
238
286
|
```
|
|
239
287
|
|
|
240
288
|
| Flag | Description |
|
|
241
289
|
|------|-------------|
|
|
242
|
-
| `<source>` | Local
|
|
290
|
+
| `<source>` | Local file path or URL to load app JSON from (optional positional argument) |
|
|
243
291
|
| `--app <name>` | App short name to fetch from server |
|
|
292
|
+
| `-e, --entity <type>` | Only clone a specific entity type (e.g. `output`, `content`, `media`, `extension`) |
|
|
293
|
+
| `--documentation-only` | When used with `-e extension`, clone only documentation extensions |
|
|
294
|
+
| `--descriptor-types <bool>` | Sort extensions into descriptor sub-directories (default: `true`). Set to `false` to use flat `Extensions/` layout |
|
|
244
295
|
| `--domain <host>` | Override domain. Triggers a domain-change confirmation prompt when it differs from the project reference domain |
|
|
245
|
-
|
|
|
296
|
+
| `--force` | Skip source mismatch confirmation and change detection; re-processes all files |
|
|
297
|
+
| `-y, --yes` | Auto-accept all prompts (also skips source mismatch confirmation) |
|
|
246
298
|
| `-v, --verbose` | Show HTTP request details |
|
|
247
299
|
|
|
248
300
|
#### Domain change detection
|
|
@@ -254,7 +306,7 @@ The project's reference domain is stored in `app.json._domain` (committed to git
|
|
|
254
306
|
#### What clone does
|
|
255
307
|
|
|
256
308
|
1. **Loads app JSON** — from a local file, server API, or interactive prompt
|
|
257
|
-
2. **Updates `.dbo/config.json`** — saves `AppID`, `AppUID`, `AppName`, `AppShortName`,
|
|
309
|
+
2. **Updates `.dbo/config.json`** — saves `AppID`, `AppUID`, `AppName`, `AppShortName`, `AppModifyKey` (if the app is locked), and `cloneSource` (the source used for this clone)
|
|
258
310
|
3. **Updates `package.json`** — populates `name`, `productName`, `description`, `homepage`, and `deploy` script
|
|
259
311
|
4. **Creates directories** — processes `children.bin` to build the directory hierarchy based on `ParentBinID` relationships
|
|
260
312
|
5. **Saves `.dbo/structure.json`** — maps BinIDs to directory paths for file placement
|
|
@@ -264,6 +316,30 @@ The project's reference domain is stored in `app.json._domain` (committed to git
|
|
|
264
316
|
9. **Processes other entities** — remaining entities with a `BinID` are placed in the corresponding bin directory
|
|
265
317
|
10. **Saves `app.json`** — clone of the original JSON with processed entries replaced by `@path/to/*.metadata.json` references
|
|
266
318
|
|
|
319
|
+
#### Clone source tracking
|
|
320
|
+
|
|
321
|
+
After every successful clone, the source is persisted as `cloneSource` in `.dbo/config.json`:
|
|
322
|
+
|
|
323
|
+
- `"default"` — app JSON was fetched from the server using `AppShortName` (the normal flow)
|
|
324
|
+
- A file path or URL — set when an explicit `<source>` argument was provided (e.g. `dbo clone /path/to/export.json`)
|
|
325
|
+
|
|
326
|
+
On subsequent clones, if you provide an explicit source that **differs** from the stored `cloneSource`, the CLI warns and asks for confirmation before proceeding:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
⚠ This project was previously cloned from: default
|
|
330
|
+
Requested source: /path/to/export.json
|
|
331
|
+
? Clone from the new source anyway? This will update the stored clone source. (y/N)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Use `--force` or `-y` to skip the prompt and override without being asked.
|
|
335
|
+
|
|
336
|
+
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:
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
⚠ Source did not return expected results: No app found with ShortName "myapp"
|
|
340
|
+
? Enter another local file path or URL to retry (or leave empty to abort):
|
|
341
|
+
```
|
|
342
|
+
|
|
267
343
|
#### Change detection on re-clone
|
|
268
344
|
|
|
269
345
|
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.
|
|
@@ -311,7 +387,7 @@ Entity types that correspond to project directories (`extension`, `app_version`,
|
|
|
311
387
|
|
|
312
388
|
| Entity Key | Directory |
|
|
313
389
|
|------------|-----------|
|
|
314
|
-
| `extension` | `Extensions
|
|
390
|
+
| `extension` | `Extensions/<Descriptor>/` (see below) |
|
|
315
391
|
| `app_version` | `App Versions/` |
|
|
316
392
|
| `data_source` | `Data Sources/` |
|
|
317
393
|
| `site` | `Sites/` |
|
|
@@ -325,6 +401,44 @@ If any columns contain base64-encoded content, the CLI prompts to extract them a
|
|
|
325
401
|
|
|
326
402
|
Use `-y` to skip prompts (uses `Name` column, no content extraction).
|
|
327
403
|
|
|
404
|
+
#### Extension descriptor sub-directories
|
|
405
|
+
|
|
406
|
+
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`).
|
|
407
|
+
|
|
408
|
+
During clone, the CLI:
|
|
409
|
+
|
|
410
|
+
1. **Pre-scans** all extension records for `descriptor_definition` entries and builds a `String1 → Name` mapping
|
|
411
|
+
2. **Creates sub-directories** under `Extensions/` for each mapped descriptor (e.g., `Extensions/Documentation/`, `Extensions/Includes/`)
|
|
412
|
+
3. **Always creates** `Extensions/Unsupported/` — extensions with an unmapped or null `Descriptor` are placed here
|
|
413
|
+
4. **Prompts per descriptor** — filename column and content extraction prompts fire once per unique `Descriptor` value (not once for all extensions)
|
|
414
|
+
5. **Persists the mapping** in `.dbo/structure.json` under `descriptorMapping`
|
|
415
|
+
|
|
416
|
+
**Config keys** (saved per descriptor in `config.json`):
|
|
417
|
+
|
|
418
|
+
| Key | Example | Purpose |
|
|
419
|
+
|-----|---------|---------|
|
|
420
|
+
| `Extension_<descriptor>_FilenameCol` | `Extension_documentation_FilenameCol` | Filename column for that descriptor type |
|
|
421
|
+
| `Extension_<descriptor>_ContentExtractions` | `Extension_documentation_ContentExtractions` | Content extraction preferences per column |
|
|
422
|
+
| `ExtensionDocumentationMDPlacement` | `"inline"` or `"root"` | Where to place documentation MD files |
|
|
423
|
+
|
|
424
|
+
**Documentation alternate placement**: When extracting MD content from `documentation` descriptor extensions, the CLI offers a choice:
|
|
425
|
+
|
|
426
|
+
- **Root placement** (`/Documentation/<filename>.md`) — recommended for easy access; creates `@/Documentation/<filename>.md` references in metadata
|
|
427
|
+
- **Inline placement** (`Extensions/Documentation/<filename>.md`) — keeps files alongside metadata
|
|
428
|
+
|
|
429
|
+
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.
|
|
430
|
+
|
|
431
|
+
**Entity filter support**:
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
dbo clone -e extension --documentation-only # Clone only documentation extensions
|
|
435
|
+
dbo clone -e extension --documentation-only --force # Reset documentation preferences and re-clone
|
|
436
|
+
dbo clone -e extension # Clone all extensions (all descriptor types)
|
|
437
|
+
dbo clone -e extension --descriptor-types false # Flat layout (no descriptor sorting)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**`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.
|
|
441
|
+
|
|
328
442
|
#### Output structure
|
|
329
443
|
|
|
330
444
|
```
|
|
@@ -343,9 +457,15 @@ project/
|
|
|
343
457
|
CurrentTicketID.metadata.json
|
|
344
458
|
ticket_test/ # ← another bin directory
|
|
345
459
|
...
|
|
346
|
-
Extensions/ # ←
|
|
347
|
-
|
|
348
|
-
|
|
460
|
+
Extensions/ # ← extension records organized by descriptor
|
|
461
|
+
Documentation/ # ← descriptor sub-directory (from descriptor_definition)
|
|
462
|
+
MyDoc.uid1.metadata.json
|
|
463
|
+
MyDoc.uid1.String10.md # ← extracted content column
|
|
464
|
+
Includes/
|
|
465
|
+
Header.uid2.metadata.json
|
|
466
|
+
Unsupported/ # ← always created (unmapped/null descriptors)
|
|
467
|
+
Documentation/ # ← root-placed documentation MD files (optional)
|
|
468
|
+
MyDoc.md
|
|
349
469
|
Data Sources/
|
|
350
470
|
MySQL-Primary.metadata.json
|
|
351
471
|
Sites/
|
|
@@ -571,8 +691,24 @@ dbo output myOutputUID --debug-sql
|
|
|
571
691
|
| `--maxrows <n>` | Maximum rows |
|
|
572
692
|
| `--rows <range>` | Row range, e.g., `1-10` |
|
|
573
693
|
| `--template <value>` | Custom template |
|
|
694
|
+
| `--limit <n>` | Maximum rows to return (preferred over `--maxrows`) |
|
|
695
|
+
| `--rowcount <bool>` | Include row count: `true` (default) or `false` for performance |
|
|
696
|
+
| `--display <expr>` | Show/hide template tags (repeatable), e.g., `sidebar=hide` |
|
|
697
|
+
| `--format-values` | Enable value formatting in `json_raw` output |
|
|
698
|
+
| `--empty-response-code <code>` | HTTP status code when output returns no results |
|
|
699
|
+
| `--fallback-content <expr>` | Fallback content UID for error codes, e.g., `404=contentUID` |
|
|
700
|
+
| `--escape-html <bool>` | Control HTML escaping: `true` or `false` |
|
|
701
|
+
| `--mime <type>` | Override MIME/content type |
|
|
702
|
+
| `--strict` | Strict error mode |
|
|
703
|
+
| `--confirm` | Confirmation flag |
|
|
704
|
+
| `--include <expr>` | Include token |
|
|
705
|
+
| `--no-transaction` | Disable transaction wrapping |
|
|
706
|
+
| `--skip <phase>` | Skip execution phases (repeatable, admin-only) |
|
|
707
|
+
| `--profile` | Enable MiniProfiler output |
|
|
574
708
|
| `--debug` | Include debug info |
|
|
575
709
|
| `--debug-sql` | Include SQL debug info |
|
|
710
|
+
| `--debug-verbose` | Verbose debug output |
|
|
711
|
+
| `--debug-analysis` | Analysis debug output |
|
|
576
712
|
| `--meta` | Use meta output endpoint |
|
|
577
713
|
| `--meta-column <uid>` | Column metadata |
|
|
578
714
|
| `--save` | Interactive save-to-disk mode |
|
|
@@ -1156,6 +1292,22 @@ When the server returns a `ticket_error` (record update requires a Ticket ID), t
|
|
|
1156
1292
|
Skip all updates that require a Ticket ID
|
|
1157
1293
|
```
|
|
1158
1294
|
|
|
1295
|
+
#### Ticket suggestions
|
|
1296
|
+
|
|
1297
|
+
When `TicketSuggestionOutput` is configured in `.dbo/config.json` (set during `dbo init`), the CLI automatically fetches relevant ticket suggestions from the server and presents them as selectable choices:
|
|
1298
|
+
|
|
1299
|
+
```
|
|
1300
|
+
? Select a Ticket ID:
|
|
1301
|
+
❯ 1 (TKT-001): Fix login page [login-fix]
|
|
1302
|
+
2 (TKT-002): Update dashboard [dashboard-v2]
|
|
1303
|
+
3 (TKT-003): Refactor auth [auth-refactor]
|
|
1304
|
+
Enter a Ticket ID manually…
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
The suggestions are fetched from the configured output endpoint, filtered by the current record's UID. Users can arrow-key through the list to select a ticket, or choose "Enter a Ticket ID manually" for custom input.
|
|
1308
|
+
|
|
1309
|
+
If `TicketSuggestionOutput` is not configured or the fetch fails, the CLI falls back to a plain text input prompt.
|
|
1310
|
+
|
|
1159
1311
|
When the server returns a `repo_mismatch` (Ticket ID belongs to a different repository), the CLI prompts:
|
|
1160
1312
|
|
|
1161
1313
|
```
|
|
@@ -1324,7 +1476,8 @@ Smart behavior:
|
|
|
1324
1476
|
- Compares file hashes — unchanged files are skipped
|
|
1325
1477
|
- Adds project-scoped plugins to `.gitignore` (source of truth is `plugins/claude/` at repo root)
|
|
1326
1478
|
- Per-plugin scope (project or global) is stored in `.dbo/config.local.json` and remembered for future upgrades
|
|
1327
|
-
- On
|
|
1479
|
+
- 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
|
|
1480
|
+
- On first install with no prior preference and no existing installation, prompts for scope
|
|
1328
1481
|
- `--global` / `--local` flags override stored preferences and update them
|
|
1329
1482
|
|
|
1330
1483
|
---
|
|
@@ -1508,6 +1661,158 @@ Both `--json` and `--jq` output use syntax highlighting:
|
|
|
1508
1661
|
|
|
1509
1662
|
---
|
|
1510
1663
|
|
|
1664
|
+
## DBO API Parameter Reference
|
|
1665
|
+
|
|
1666
|
+
The DBO.io API uses a token architecture for dynamic parameters. All parameters are passed as URL query string key-value pairs.
|
|
1667
|
+
|
|
1668
|
+
### Token Syntax
|
|
1669
|
+
|
|
1670
|
+
Parameters follow the token delimiter system:
|
|
1671
|
+
|
|
1672
|
+
```
|
|
1673
|
+
_tokenType@reference$target!defaultValue:modifier=value
|
|
1674
|
+
```
|
|
1675
|
+
|
|
1676
|
+
| Delimiter | Purpose | Example |
|
|
1677
|
+
|-----------|---------|---------|
|
|
1678
|
+
| `_` | Prefix for token type | `_filter`, `_sort`, `_limit` |
|
|
1679
|
+
| `@` | Reference (column, field, key) | `_filter@FirstName=John` |
|
|
1680
|
+
| `$` | Target (scope to a specific output/entity) | `_limit$outputUid=100` |
|
|
1681
|
+
| `!` | Default value (fallback) | `_filter@Status!active=value` |
|
|
1682
|
+
| `:` | Modifier(s) | `_filter@Name:Contains=john` |
|
|
1683
|
+
|
|
1684
|
+
### Output Parameters
|
|
1685
|
+
|
|
1686
|
+
These parameters control the output endpoint behavior (`/api/output/{uid}` and `/api/output/entity/{entityUid}`):
|
|
1687
|
+
|
|
1688
|
+
#### Filtering
|
|
1689
|
+
|
|
1690
|
+
```
|
|
1691
|
+
_filter@ColumnName=value # exact match (default)
|
|
1692
|
+
_filter@ColumnName:Contains=value # LIKE %value%
|
|
1693
|
+
_filter@ColumnName:StartsWith=value # LIKE value%
|
|
1694
|
+
_filter@ColumnName:EndsWith=value # LIKE %value
|
|
1695
|
+
_filter@ColumnName:LessThan=value # < comparison
|
|
1696
|
+
_filter@ColumnName:LessThanOrEqualTo=value # <= comparison
|
|
1697
|
+
_filter@ColumnName:GreaterThan=value # > comparison
|
|
1698
|
+
_filter@ColumnName:GreaterThanOrEqualTo=value # >= comparison
|
|
1699
|
+
_filter@ColumnName:Contains,And=value # AND logic (default is OR)
|
|
1700
|
+
_filter@ColumnName:Contains,Exclude=value # NOT LIKE
|
|
1701
|
+
_filter$outputTarget@ColumnName=value # scoped to specific output
|
|
1702
|
+
```
|
|
1703
|
+
|
|
1704
|
+
#### Sorting
|
|
1705
|
+
|
|
1706
|
+
```
|
|
1707
|
+
_sort=ColumnName # ascending (default)
|
|
1708
|
+
_sort=ColumnName:ASC # explicit ascending
|
|
1709
|
+
_sort=ColumnName:DESC # descending
|
|
1710
|
+
_sort=Column1:ASC,Column2:DESC # multiple sorts (comma-separated)
|
|
1711
|
+
_sort=ColumnName ASC # space-separated also works
|
|
1712
|
+
```
|
|
1713
|
+
|
|
1714
|
+
Multiple `_sort` parameters can be specified; earlier sorts take precedence.
|
|
1715
|
+
|
|
1716
|
+
#### Pagination
|
|
1717
|
+
|
|
1718
|
+
```
|
|
1719
|
+
_limit=30 # max 30 rows
|
|
1720
|
+
_limit=10-30 # rows 10 through 30 (range)
|
|
1721
|
+
_limit=10&_page=3 # 10 rows per page, page 3
|
|
1722
|
+
_rowcount=false # disable row count for performance
|
|
1723
|
+
```
|
|
1724
|
+
|
|
1725
|
+
Deprecated (still accepted): `_maxrows`, `_rows`, `_rowsperpage`
|
|
1726
|
+
|
|
1727
|
+
#### Search
|
|
1728
|
+
|
|
1729
|
+
```
|
|
1730
|
+
_search=keyword # full-text search across searchable columns
|
|
1731
|
+
_search@ColumnName=keyword # search specific column
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
#### Template & Format
|
|
1735
|
+
|
|
1736
|
+
```
|
|
1737
|
+
_template=json_raw # format name: json_raw, html, json, json_indented, csv, xml, txt, pdf
|
|
1738
|
+
_template=3iX9fHFTL064Shgol8Bktw # content UID (custom template)
|
|
1739
|
+
_format_values=true # enable value formatting in json_raw
|
|
1740
|
+
_format=json # legacy format specifier
|
|
1741
|
+
_mime=application/json # override MIME/content type
|
|
1742
|
+
_escape_html=false # disable HTML escaping of data values
|
|
1743
|
+
```
|
|
1744
|
+
|
|
1745
|
+
#### Display Control
|
|
1746
|
+
|
|
1747
|
+
```
|
|
1748
|
+
_display@tagName=show # show a template content tag
|
|
1749
|
+
_display@tagName=hide # hide a template content tag
|
|
1750
|
+
_empty_response_code=404 # HTTP status when results are empty
|
|
1751
|
+
_fallback_content:404=contentUID # render a different content on 404
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1754
|
+
#### Debug & Profiling
|
|
1755
|
+
|
|
1756
|
+
```
|
|
1757
|
+
_debug=true # general debug output
|
|
1758
|
+
_debug:sql=true # return SQL without executing
|
|
1759
|
+
_debug:verbose=true # verbose debug
|
|
1760
|
+
_debug:analysis=true # analysis debug
|
|
1761
|
+
_profile=true # MiniProfiler output
|
|
1762
|
+
```
|
|
1763
|
+
|
|
1764
|
+
Other debug sub-keys: `http_modules`, `instance`, `rewrites`, `cache`, `executable_contents`, `security`, `controllers`, `embeds`, `includes`, `contents`, `template_render`, `tokens`, `messages`, `media`, `request_stack`, `query_execution`, `query_building`
|
|
1765
|
+
|
|
1766
|
+
#### Control Flags
|
|
1767
|
+
|
|
1768
|
+
```
|
|
1769
|
+
_strict=true # strict error mode
|
|
1770
|
+
_confirm=true # confirmation flag for operations
|
|
1771
|
+
_no_transaction=true # disable transaction wrapping
|
|
1772
|
+
_skip=all # skip execution phases (admin-only)
|
|
1773
|
+
_include=value # include token
|
|
1774
|
+
_security=value # security filter enforcement
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
### Data Tokens (used in templates)
|
|
1778
|
+
|
|
1779
|
+
| Token | Description |
|
|
1780
|
+
|-------|-------------|
|
|
1781
|
+
| `#{value@ColumnName}` | Column value from output row |
|
|
1782
|
+
| `#{id}` | Row primary key |
|
|
1783
|
+
| `#{uid}` | Row UID |
|
|
1784
|
+
| `#{CurrentUser@field}` | Current user field (e.g., `FirstName`, `Email`) |
|
|
1785
|
+
| `#{request@key}` | URL parameter value |
|
|
1786
|
+
| `#{session@variable}` | Session variable |
|
|
1787
|
+
| `#{site@field}` | Site field |
|
|
1788
|
+
| `#{date:format}` | Current date/time |
|
|
1789
|
+
| `#{unique:uid}` | Generate unique UID |
|
|
1790
|
+
| `#{count}` | Row count |
|
|
1791
|
+
| `#{page}` | Current page number |
|
|
1792
|
+
|
|
1793
|
+
### Template Tags
|
|
1794
|
+
|
|
1795
|
+
System tags (used in output templates):
|
|
1796
|
+
|
|
1797
|
+
| Tag | Purpose |
|
|
1798
|
+
|-----|---------|
|
|
1799
|
+
| `<#_row>` | Row template |
|
|
1800
|
+
| `<#_header>` | Header template |
|
|
1801
|
+
| `<#_footer>` | Footer template |
|
|
1802
|
+
| `<#_cell>` | Cell template |
|
|
1803
|
+
| `<#_empty>` | Empty result template |
|
|
1804
|
+
| `<#_noresult>` | No result template |
|
|
1805
|
+
| `<#_prompt>` | Prompt template |
|
|
1806
|
+
| `<#_shared>` | Shared template |
|
|
1807
|
+
| `<#_rowdelimiter>` | Row delimiter |
|
|
1808
|
+
| `<#_celldelimiter>` | Cell delimiter |
|
|
1809
|
+
| `<#_value>` | Value template |
|
|
1810
|
+
| `<#_embed url="...">` | Embed nested content/output |
|
|
1811
|
+
|
|
1812
|
+
User-defined tags use `<#reference>` (no underscore prefix).
|
|
1813
|
+
|
|
1814
|
+
---
|
|
1815
|
+
|
|
1511
1816
|
## Migration from curl
|
|
1512
1817
|
|
|
1513
1818
|
The `dbo` CLI is a drop-in replacement for the curl-based workflow. Here's how common operations translate:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dboio/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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,6 +55,23 @@ 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) {
|
|
61
76
|
const dir = dirname(filePath);
|
|
62
77
|
const ext = extname(filePath);
|
|
@@ -87,6 +102,33 @@ async function addSingleFile(filePath, client, options, batchDefaults) {
|
|
|
87
102
|
return await submitAdd(meta, metaPath, filePath, client, options);
|
|
88
103
|
}
|
|
89
104
|
|
|
105
|
+
// Step 3b: Auto-infer entity/descriptor for Documentation/ files when root placement is configured
|
|
106
|
+
const docInfo = await detectDocumentationFile(filePath);
|
|
107
|
+
if (docInfo) {
|
|
108
|
+
const filenameCol = await loadDescriptorFilenamePreference('documentation') || 'Name';
|
|
109
|
+
const docBase = basename(filePath, extname(filePath));
|
|
110
|
+
const companionDir = join(process.cwd(), 'Extensions', 'Documentation');
|
|
111
|
+
await mkdir(companionDir, { recursive: true });
|
|
112
|
+
|
|
113
|
+
const docMeta = {
|
|
114
|
+
_entity: 'extension',
|
|
115
|
+
Descriptor: 'documentation',
|
|
116
|
+
[filenameCol]: docBase,
|
|
117
|
+
Name: docBase,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Find the content column name from saved preferences
|
|
121
|
+
const contentCol = filenameCol === 'Name' ? 'String10' : 'String10';
|
|
122
|
+
const relPath = relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
123
|
+
docMeta[contentCol] = `@/${relPath}`;
|
|
124
|
+
docMeta._contentColumns = [contentCol];
|
|
125
|
+
|
|
126
|
+
const docMetaFile = join(companionDir, `${docBase}.metadata.json`);
|
|
127
|
+
await writeFile(docMetaFile, JSON.stringify(docMeta, null, 2) + '\n');
|
|
128
|
+
log.success(`Created companion metadata at ${docMetaFile}`);
|
|
129
|
+
return await submitAdd(docMeta, docMetaFile, filePath, client, options);
|
|
130
|
+
}
|
|
131
|
+
|
|
90
132
|
// Step 4: No usable metadata — interactive wizard
|
|
91
133
|
const inquirer = (await import('inquirer')).default;
|
|
92
134
|
|
|
@@ -379,23 +421,26 @@ async function addDirectory(dirPath, client, options) {
|
|
|
379
421
|
* - It has no companion .metadata.json, OR
|
|
380
422
|
* - Its .metadata.json exists but has no _CreatedOn (never been on server)
|
|
381
423
|
*/
|
|
382
|
-
async function findUnaddedFiles(dir) {
|
|
424
|
+
async function findUnaddedFiles(dir, ig) {
|
|
425
|
+
if (!ig) ig = await loadIgnore();
|
|
426
|
+
|
|
383
427
|
const results = [];
|
|
384
428
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
385
429
|
|
|
386
430
|
for (const entry of entries) {
|
|
387
431
|
const fullPath = join(dir, entry.name);
|
|
432
|
+
const relPath = relative(process.cwd(), fullPath).replace(/\\/g, '/');
|
|
388
433
|
|
|
389
434
|
if (entry.isDirectory()) {
|
|
390
|
-
if (
|
|
391
|
-
results.push(...await findUnaddedFiles(fullPath));
|
|
435
|
+
if (ig.ignores(relPath + '/') || ig.ignores(relPath)) continue;
|
|
436
|
+
results.push(...await findUnaddedFiles(fullPath, ig));
|
|
392
437
|
continue;
|
|
393
438
|
}
|
|
394
439
|
|
|
395
|
-
// Skip metadata files themselves
|
|
440
|
+
// Skip metadata files themselves (structural, not user-configurable)
|
|
396
441
|
if (entry.name.endsWith('.metadata.json')) continue;
|
|
397
|
-
// Skip
|
|
398
|
-
if (
|
|
442
|
+
// Skip ignored files
|
|
443
|
+
if (ig.ignores(relPath)) continue;
|
|
399
444
|
|
|
400
445
|
// Check for companion metadata
|
|
401
446
|
const ext = extname(entry.name);
|