@dboio/cli 0.9.6 → 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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dboio/cli",
3
- "version": "0.9.6",
3
+ "version": "0.10.1",
4
4
  "description": "CLI for the DBO.io framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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