@dboio/cli 0.6.11 → 0.6.12

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
@@ -618,6 +618,7 @@ dbo content deploy albain3dwkofbhnd1qtd1q assets/css/colors.css
618
618
  dbo content deploy rncjivlghu65bmkbjnxynq assets/js/app.js
619
619
  dbo content deploy abc123 docs/readme.md --ticket myTicket
620
620
  dbo content deploy abc123 test.html --confirm false # validate only
621
+ dbo content deploy abc123 image.png --multipart # binary file upload
621
622
  ```
622
623
 
623
624
  This is a shorthand for:
@@ -632,6 +633,7 @@ dbo input -d 'RowUID:albain3dwkofbhnd1qtd1q;column:content.Content@assets/css/co
632
633
  | `-C, --confirm <true\|false>` | Commit (default: `true`) |
633
634
  | `--ticket <id>` | Override ticket ID |
634
635
  | `--modify-key <key>` | Provide ModifyKey directly (skips interactive prompt) |
636
+ | `--multipart` | Use multipart/form-data upload (for binary files) |
635
637
 
636
638
  #### `dbo content pull`
637
639
 
@@ -1361,6 +1363,17 @@ Create a `dbo.deploy.json` file in your project root to define named deployments
1361
1363
  "file": "docs/colors.md",
1362
1364
  "entity": "extension",
1363
1365
  "column": "Text"
1366
+ },
1367
+ "img:logo": {
1368
+ "uid": "x9fk2m3npqrs7tuvwyz1ab",
1369
+ "file": "assets/images/logo.png",
1370
+ "multipart": true
1371
+ },
1372
+ "upload:icons": {
1373
+ "uid": "7ddf10982a96457fa4f440",
1374
+ "file": "assets/css/launchpad-icons.css",
1375
+ "multipart": true,
1376
+ "filename": "launchpad-icons.css"
1364
1377
  }
1365
1378
  }
1366
1379
  }
@@ -1370,8 +1383,10 @@ Create a `dbo.deploy.json` file in your project root to define named deployments
1370
1383
  |-------|-------------|---------|
1371
1384
  | `uid` | Target record UID (required) | — |
1372
1385
  | `file` | Local file path (required) | — |
1373
- | `entity` | Target entity name | `content` |
1374
- | `column` | Target column name | `Content` |
1386
+ | `entity` | Target entity name | `content` (`media` when `multipart: true`) |
1387
+ | `column` | Target column name | `Content` (`File` when `multipart: true`) |
1388
+ | `multipart` | Use multipart/form-data upload (for binary files) | `false` |
1389
+ | `filename` | Set the `Filename` column on the target record | basename of `file` |
1375
1390
 
1376
1391
  This replaces the curl commands typically embedded in `package.json` scripts.
1377
1392
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dboio/cli",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "description": "CLI for the DBO.io framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,6 +31,7 @@ const deployCmd = new Command('deploy')
31
31
  .option('-C, --confirm <value>', 'Commit: true (default) or false', 'true')
32
32
  .option('--ticket <id>', 'Override ticket ID')
33
33
  .option('--modify-key <key>', 'Provide ModifyKey directly (skips interactive prompt)')
34
+ .option('--multipart', 'Use multipart/form-data upload (for binary files)')
34
35
  .option('--json', 'Output raw JSON')
35
36
  .option('-v, --verbose', 'Show HTTP request details')
36
37
  .option('--domain <host>', 'Override domain')
@@ -66,7 +67,8 @@ const deployCmd = new Command('deploy')
66
67
  if (options.ticket) extraParams['_OverrideTicketID'] = options.ticket;
67
68
  if (modifyKeyResult.modifyKey) extraParams['_modify_key'] = modifyKeyResult.modifyKey;
68
69
 
69
- const dataExprs = [`RowUID:${uid};column:content.Content@${filepath}`];
70
+ const isMultipart = options.multipart === true;
71
+ const dataExprs = isMultipart ? [] : [`RowUID:${uid};column:content.Content@${filepath}`];
70
72
 
71
73
  // Apply stored ticket if no --ticket flag
72
74
  if (!options.ticket) {
@@ -77,16 +79,36 @@ const deployCmd = new Command('deploy')
77
79
  }
78
80
  await applyStoredTicketToSubmission(dataExprs, 'content', uid, uid, options, sessionTicketOverride);
79
81
 
80
- let body = await buildInputBody(dataExprs, extraParams);
81
- let result = await client.postUrlEncoded('/api/input/submit', body);
82
+ // Submit helper handles both URL-encoded and multipart modes
83
+ async function submit() {
84
+ if (isMultipart) {
85
+ const fields = { ...extraParams };
86
+ for (const expr of dataExprs) {
87
+ const eqIdx = expr.indexOf('=');
88
+ if (eqIdx !== -1) {
89
+ fields[expr.substring(0, eqIdx)] = expr.substring(eqIdx + 1);
90
+ }
91
+ }
92
+ const files = [{
93
+ fieldName: `RowUID:${uid};column:content.Content`,
94
+ filePath: filepath,
95
+ fileName: filepath.split('/').pop(),
96
+ }];
97
+ return client.postMultipart('/api/input/submit', fields, files);
98
+ } else {
99
+ const body = await buildInputBody(dataExprs, extraParams);
100
+ return client.postUrlEncoded('/api/input/submit', body);
101
+ }
102
+ }
103
+
104
+ let result = await submit();
82
105
 
83
106
  // Reactive ModifyKey retry
84
107
  if (!result.successful && result.messages?.some(m => isModifyKeyError(m))) {
85
108
  const retryMK = await handleModifyKeyError();
86
109
  if (retryMK.cancel) { log.info('Submission cancelled'); return; }
87
110
  extraParams['_modify_key'] = retryMK.modifyKey;
88
- body = await buildInputBody(dataExprs, extraParams);
89
- result = await client.postUrlEncoded('/api/input/submit', body);
111
+ result = await submit();
90
112
  }
91
113
 
92
114
  // Retry with prompted params if needed (ticket, user, repo mismatch)
@@ -101,8 +123,7 @@ const deployCmd = new Command('deploy')
101
123
  }
102
124
  const params = errorResult.retryParams || errorResult;
103
125
  Object.assign(extraParams, params);
104
- body = await buildInputBody(dataExprs, extraParams);
105
- result = await client.postUrlEncoded('/api/input/submit', body);
126
+ result = await submit();
106
127
  }
107
128
 
108
129
  formatResponse(result, { json: options.json });
@@ -85,10 +85,12 @@ export const deployCommand = new Command('deploy')
85
85
  continue;
86
86
  }
87
87
 
88
- const entity = entry.entity || 'content';
89
- const column = entry.column || 'Content';
88
+ const isMultipart = entry.multipart === true;
89
+ const entity = entry.entity || (isMultipart ? 'media' : 'content');
90
+ const column = entry.column || (isMultipart ? 'File' : 'Content');
90
91
  const uid = entry.uid;
91
92
  const file = entry.file;
93
+ const filename = entry.filename || file.split('/').pop();
92
94
 
93
95
  if (!uid || !file) {
94
96
  log.warn(`Skipping "${entryName}": missing uid or file.`);
@@ -101,7 +103,9 @@ export const deployCommand = new Command('deploy')
101
103
  if (options.ticket) extraParams['_OverrideTicketID'] = options.ticket;
102
104
  if (activeModifyKey) extraParams['_modify_key'] = activeModifyKey;
103
105
 
104
- const dataExprs = [`RowUID:${uid};column:${entity}.${column}@${file}`];
106
+ const dataExprs = isMultipart
107
+ ? [`RowUID:${uid};column:${entity}.Filename=${filename}`]
108
+ : [`RowUID:${uid};column:${entity}.${column}@${file}`];
105
109
 
106
110
  // Apply stored ticket if no --ticket flag
107
111
  if (!options.ticket) {
@@ -112,8 +116,29 @@ export const deployCommand = new Command('deploy')
112
116
  }
113
117
  await applyStoredTicketToSubmission(dataExprs, entity, uid, uid, options, sessionTicketOverride);
114
118
 
115
- let body = await buildInputBody(dataExprs, extraParams);
116
- let result = await client.postUrlEncoded('/api/input/submit', body);
119
+ // Submit helper handles both URL-encoded and multipart modes
120
+ async function submit() {
121
+ if (isMultipart) {
122
+ const fields = { ...extraParams };
123
+ for (const expr of dataExprs) {
124
+ const eqIdx = expr.indexOf('=');
125
+ if (eqIdx !== -1) {
126
+ fields[expr.substring(0, eqIdx)] = expr.substring(eqIdx + 1);
127
+ }
128
+ }
129
+ const files = [{
130
+ fieldName: `RowUID:${uid};column:${entity}.${column}`,
131
+ filePath: file,
132
+ fileName: file.split('/').pop(),
133
+ }];
134
+ return client.postMultipart('/api/input/submit', fields, files);
135
+ } else {
136
+ const body = await buildInputBody(dataExprs, extraParams);
137
+ return client.postUrlEncoded('/api/input/submit', body);
138
+ }
139
+ }
140
+
141
+ let result = await submit();
117
142
 
118
143
  // Reactive ModifyKey retry
119
144
  if (!result.successful && result.messages?.some(m => isModifyKeyError(m))) {
@@ -121,8 +146,7 @@ export const deployCommand = new Command('deploy')
121
146
  if (retryMK.cancel) { log.info('Submission cancelled'); break; }
122
147
  activeModifyKey = retryMK.modifyKey;
123
148
  extraParams['_modify_key'] = activeModifyKey;
124
- body = await buildInputBody(dataExprs, extraParams);
125
- result = await client.postUrlEncoded('/api/input/submit', body);
149
+ result = await submit();
126
150
  }
127
151
 
128
152
  // Retry with prompted params if needed (ticket, user, repo mismatch)
@@ -141,8 +165,7 @@ export const deployCommand = new Command('deploy')
141
165
  }
142
166
  const params = errorResult.retryParams || errorResult;
143
167
  Object.assign(extraParams, params);
144
- body = await buildInputBody(dataExprs, extraParams);
145
- result = await client.postUrlEncoded('/api/input/submit', body);
168
+ result = await submit();
146
169
  }
147
170
 
148
171
  if (result.successful) {