sitevision-cli 1.0.0-beta.8 → 1.0.0-beta.9

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.
@@ -160,16 +160,36 @@ async function delay(ms) {
160
160
  */
161
161
  export function summarizeErrorBody(body, headers) {
162
162
  const contentType = headers['content-type'] ?? 'unknown';
163
- const isText = contentType.includes('text') ||
163
+ const declaredText = contentType.includes('text') ||
164
164
  contentType.includes('json') ||
165
165
  contentType.includes('xml');
166
- if (!isText) {
167
- return `(${contentType}, ${body.length} bytes)`;
166
+ const text = body.toString('utf8');
167
+ // Show the body when the server declares it text OR the bytes decode to
168
+ // mostly-printable text. Sitevision sometimes returns a text/JSON error with
169
+ // a missing or non-text content-type, and hiding it obscures the real cause.
170
+ if (declaredText || isMostlyPrintable(text)) {
171
+ const collapsed = text.replaceAll(/\s+/g, ' ').trim();
172
+ const max = 300;
173
+ const summary = collapsed.length > max ? collapsed.slice(0, max) + '…' : collapsed;
174
+ if (summary.length > 0)
175
+ return summary;
168
176
  }
169
- const text = body.toString('utf8').replaceAll(/\s+/g, ' ').trim();
170
- const max = 300;
171
- const summary = text.length > max ? text.slice(0, max) + '…' : text;
172
- return summary.length > 0 ? summary : `(${contentType}, empty body)`;
177
+ return `(${contentType}, ${body.length} bytes)`;
178
+ }
179
+ /** Heuristic: a decoded string is text if <10% of chars are control/undecodable. */
180
+ function isMostlyPrintable(text) {
181
+ if (text.length === 0)
182
+ return false;
183
+ let bad = 0;
184
+ for (const ch of text) {
185
+ const code = ch.codePointAt(0) ?? 0;
186
+ // Undecodable byte (replacement char), or a control char other than
187
+ // tab/newline/vertical-tab/form-feed/carriage-return.
188
+ if (code === 0xff_fd || code < 9 || (code > 13 && code < 32)) {
189
+ bad++;
190
+ }
191
+ }
192
+ return bad / text.length < 0.1;
173
193
  }
174
194
  /** ZIP local-file-header magic bytes: "PK\x03\x04". */
175
195
  const ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);
@@ -316,9 +336,10 @@ export async function deployApp(zipPath, config, appType, force = false) {
316
336
  if (force) {
317
337
  url += '?force=true';
318
338
  }
319
- // Create multipart form data
339
+ // The import endpoints expect the archive in a multipart part named "data"
340
+ // (per the webAppImport/restAppImport docs). Signing uses "file" separately.
320
341
  const boundary = generateBoundary();
321
- const { body, contentType } = createMultipartFormData(zipPath, 'file', boundary);
342
+ const { body, contentType } = createMultipartFormData(zipPath, 'data', boundary);
322
343
  const { auth, kind } = configAuth(config);
323
344
  try {
324
345
  const response = await makeRequest(url, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sitevision-cli",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0-beta.9",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "svc": "dist/cli.js"