@dboio/cli 0.9.8 → 0.10.1
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 +38 -1
- package/bin/dbo.js +2 -0
- package/package.json +1 -1
- package/src/commands/add.js +46 -0
- package/src/commands/clone.js +557 -239
- package/src/commands/init.js +30 -32
- package/src/commands/pull.js +264 -87
- package/src/commands/push.js +502 -57
- package/src/commands/rm.js +1 -1
- package/src/commands/sync.js +68 -0
- package/src/lib/config.js +49 -8
- package/src/lib/delta.js +86 -25
- package/src/lib/diff.js +9 -3
- package/src/lib/folder-icon.js +120 -0
- package/src/lib/ignore.js +1 -1
- package/src/lib/input-parser.js +37 -10
- package/src/lib/scaffold.js +82 -2
- package/src/lib/structure.js +2 -0
package/README.md
CHANGED
|
@@ -169,9 +169,12 @@ All configuration is **directory-scoped**. Each project folder maintains its own
|
|
|
169
169
|
| `cookies.txt` | Session cookie (Netscape format) | Gitignored (per-user) |
|
|
170
170
|
| `structure.json` | Bin directory mapping (created by `dbo clone`) | Committable (shared) |
|
|
171
171
|
| `metadata_templates.json` | Column templates per entity/descriptor (auto-generated by `dbo clone`) | Committable (shared) |
|
|
172
|
+
| `.app_baseline.json` | App JSON baseline for delta detection (auto-generated by `dbo clone`) | Gitignored (per-machine) |
|
|
172
173
|
|
|
173
174
|
`dbo init` automatically adds `.dbo/credentials.json`, `.dbo/cookies.txt`, `.dbo/config.local.json`, and `.dbo/ticketing.local.json` to `.gitignore` (creates the file if it doesn't exist).
|
|
174
175
|
|
|
176
|
+
> **Upgrading from pre-0.9.9**: The baseline file `.app.json` in the project root has moved to `.dbo/.app_baseline.json`. Running any `dbo` command will auto-migrate the file. You can also manually remove the `.app.json` entry from your `.gitignore`.
|
|
177
|
+
|
|
175
178
|
#### config.json reference
|
|
176
179
|
|
|
177
180
|
```json
|
|
@@ -248,7 +251,6 @@ bins/
|
|
|
248
251
|
.dbo/ # DBO internal
|
|
249
252
|
*.dboio.json # Export files
|
|
250
253
|
app.json # Clone output
|
|
251
|
-
.app.json # Clone baseline
|
|
252
254
|
dbo.deploy.json # Deployment manifest
|
|
253
255
|
.git/ # Version control
|
|
254
256
|
.gitignore
|
|
@@ -572,6 +574,41 @@ The CLI handles leading `/` in metadata paths flexibly:
|
|
|
572
574
|
|
|
573
575
|
This ensures compatibility with various path formats from the server while maintaining correct local file organization.
|
|
574
576
|
|
|
577
|
+
#### Output hierarchy format
|
|
578
|
+
|
|
579
|
+
Each root output record produces a **single compound JSON file** containing all child entities (columns, joins, filters) embedded inline:
|
|
580
|
+
|
|
581
|
+
```
|
|
582
|
+
bins/app/
|
|
583
|
+
_output~Sales~abc123.json ← root output with inline children
|
|
584
|
+
_output~Sales~abc123.column~col1.CustomSQL.sql ← companion SQL file
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
The root JSON structure:
|
|
588
|
+
```json
|
|
589
|
+
{
|
|
590
|
+
"_entity": "output",
|
|
591
|
+
"UID": "abc123",
|
|
592
|
+
"Name": "Sales",
|
|
593
|
+
"children": {
|
|
594
|
+
"column": [
|
|
595
|
+
{ "_entity": "output_value", "UID": "col1", "Title": "Amount", "children": { "column": [], "join": [], "filter": [] } }
|
|
596
|
+
],
|
|
597
|
+
"join": [],
|
|
598
|
+
"filter": [
|
|
599
|
+
{ "_entity": "output_value_filter", "UID": "f1", "ShortName": "Active", "children": { "column": [], "join": [], "filter": [] } }
|
|
600
|
+
]
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Key points:
|
|
606
|
+
- All three child keys (`column`, `join`, `filter`) are always present (empty arrays when unused)
|
|
607
|
+
- Each child retains `_entity` set to its physical table name for push routing
|
|
608
|
+
- Children nest recursively following FK relationships (e.g. a column can have filters)
|
|
609
|
+
- `CustomSQL` content is extracted to companion `.sql` files per extraction rules
|
|
610
|
+
- Re-cloning moves orphaned old-format child `.json` files to `/trash`
|
|
611
|
+
|
|
575
612
|
#### Metadata Templates
|
|
576
613
|
|
|
577
614
|
During `dbo clone`, the CLI auto-generates `.dbo/metadata_templates.json` — a file that records which columns each entity/descriptor uses. This file is seeded from the first cloned record of each type and can be manually edited afterwards.
|
package/bin/dbo.js
CHANGED
|
@@ -30,6 +30,7 @@ import { cloneCommand } from '../src/commands/clone.js';
|
|
|
30
30
|
import { diffCommand } from '../src/commands/diff.js';
|
|
31
31
|
import { rmCommand } from '../src/commands/rm.js';
|
|
32
32
|
import { mvCommand } from '../src/commands/mv.js';
|
|
33
|
+
import { syncCommand } from '../src/commands/sync.js';
|
|
33
34
|
|
|
34
35
|
// First-run welcome message
|
|
35
36
|
function checkFirstRun() {
|
|
@@ -89,6 +90,7 @@ program.addCommand(cloneCommand);
|
|
|
89
90
|
program.addCommand(diffCommand);
|
|
90
91
|
program.addCommand(rmCommand);
|
|
91
92
|
program.addCommand(mvCommand);
|
|
93
|
+
program.addCommand(syncCommand);
|
|
92
94
|
|
|
93
95
|
// Show welcome message on first run
|
|
94
96
|
checkFirstRun();
|
package/package.json
CHANGED
package/src/commands/add.js
CHANGED
|
@@ -13,6 +13,7 @@ import { setFileTimestamps } from '../lib/timestamps.js';
|
|
|
13
13
|
import { checkStoredTicket, clearGlobalTicket } from '../lib/ticketing.js';
|
|
14
14
|
import { checkModifyKey, isModifyKeyError, handleModifyKeyError } from '../lib/modify-key.js';
|
|
15
15
|
import { loadIgnore } from '../lib/ignore.js';
|
|
16
|
+
import { loadStructureFile, findBinByPath } from '../lib/structure.js';
|
|
16
17
|
|
|
17
18
|
export const addCommand = new Command('add')
|
|
18
19
|
.description('Add a new file to DBO.io (creates record on server)')
|
|
@@ -75,6 +76,45 @@ async function detectDocumentationFile(filePath) {
|
|
|
75
76
|
return { entity: 'extension', descriptor: 'documentation' };
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Detect whether a file is manifest.json at the project root.
|
|
81
|
+
* Returns { meta, metaPath } or null if not applicable.
|
|
82
|
+
*
|
|
83
|
+
* Creates companion metadata in bins/app/ with pre-filled fields.
|
|
84
|
+
*/
|
|
85
|
+
async function detectManifestFile(filePath) {
|
|
86
|
+
const rel = relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
87
|
+
if (rel.toLowerCase() !== 'manifest.json') return null;
|
|
88
|
+
|
|
89
|
+
const appConfig = await loadAppConfig();
|
|
90
|
+
const structure = await loadStructureFile();
|
|
91
|
+
const appBin = findBinByPath('app', structure);
|
|
92
|
+
|
|
93
|
+
const companionDir = join(process.cwd(), 'bins', 'app');
|
|
94
|
+
await mkdir(companionDir, { recursive: true });
|
|
95
|
+
|
|
96
|
+
const meta = {
|
|
97
|
+
_entity: 'content',
|
|
98
|
+
_contentColumns: ['Content'],
|
|
99
|
+
Content: '@/manifest.json',
|
|
100
|
+
Path: 'manifest.json',
|
|
101
|
+
Name: 'manifest.json',
|
|
102
|
+
Extension: 'JSON',
|
|
103
|
+
Public: 1,
|
|
104
|
+
Active: 1,
|
|
105
|
+
Title: 'PWA Manifest',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (appBin) meta.BinID = appBin.binId;
|
|
109
|
+
if (appConfig.AppID) meta.AppID = appConfig.AppID;
|
|
110
|
+
|
|
111
|
+
const metaPath = join(companionDir, 'manifest.metadata.json');
|
|
112
|
+
await writeFile(metaPath, JSON.stringify(meta, null, 2) + '\n');
|
|
113
|
+
log.success(`Created manifest metadata at ${metaPath}`);
|
|
114
|
+
|
|
115
|
+
return { meta, metaPath };
|
|
116
|
+
}
|
|
117
|
+
|
|
78
118
|
async function addSingleFile(filePath, client, options, batchDefaults) {
|
|
79
119
|
// Check .dboignore before doing any processing
|
|
80
120
|
const ig = await loadIgnore();
|
|
@@ -186,6 +226,12 @@ async function addSingleFile(filePath, client, options, batchDefaults) {
|
|
|
186
226
|
return await submitAdd(docMeta, docMetaFile, filePath, client, options);
|
|
187
227
|
}
|
|
188
228
|
|
|
229
|
+
// Step 3d: Auto-detect manifest.json at project root
|
|
230
|
+
const manifestMeta = await detectManifestFile(filePath);
|
|
231
|
+
if (manifestMeta) {
|
|
232
|
+
return await submitAdd(manifestMeta.meta, manifestMeta.metaPath, filePath, client, options);
|
|
233
|
+
}
|
|
234
|
+
|
|
189
235
|
// Step 4: No usable metadata — interactive wizard
|
|
190
236
|
const inquirer = (await import('inquirer')).default;
|
|
191
237
|
|