@bettercms-ai/sdk 1.8.0 → 1.9.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/dist/index.cjs CHANGED
@@ -71,6 +71,7 @@ __export(index_exports, {
71
71
  imageUrlBuilder: () => import_image_url.default,
72
72
  inviteMember: () => inviteMember,
73
73
  isBlock: () => import_types.isBlock,
74
+ isRichText: () => isRichText,
74
75
  listApiKeys: () => listApiKeys,
75
76
  listEntries: () => listEntries,
76
77
  listForms: () => listForms2,
@@ -81,11 +82,13 @@ __export(index_exports, {
81
82
  listPages: () => listPages,
82
83
  listSubmissions: () => listSubmissions,
83
84
  listWorkspaces: () => listWorkspaces,
85
+ plain: () => plain,
84
86
  publishPage: () => publishPage,
85
87
  regenerateApiKey: () => regenerateApiKey,
86
88
  removeMember: () => removeMember,
87
89
  resolveSeo: () => resolveSeo,
88
90
  revokeApiKey: () => revokeApiKey,
91
+ rich: () => rich,
89
92
  search: () => search,
90
93
  setPageContent: () => setPageContent,
91
94
  shouldShowField: () => shouldShowField,
@@ -136,12 +139,20 @@ var BetterCMSError = class _BetterCMSError extends Error {
136
139
  * shares a status with others (e.g. 409 slug-conflict vs 409 project-deleted).
137
140
  */
138
141
  bodyCode;
139
- constructor(message, status, code, bodyCode) {
142
+ /**
143
+ * Per-field validation messages from a 400 `{ errors: { field: message } }` body.
144
+ * The backend returns validation failures in this shape (not `message`/`error`), so
145
+ * without this the SDK collapsed every validation 400 to a bare "Bad Request" with no
146
+ * hint which field or rule failed (FLO-474). Mirrors the forms client's `fieldErrors`.
147
+ */
148
+ fieldErrors;
149
+ constructor(message, status, code, bodyCode, fieldErrors) {
140
150
  super(message);
141
151
  this.name = "BetterCMSError";
142
152
  this.status = status;
143
153
  this.code = code;
144
154
  this.bodyCode = bodyCode;
155
+ this.fieldErrors = fieldErrors;
145
156
  if (Error.captureStackTrace) {
146
157
  Error.captureStackTrace(this, _BetterCMSError);
147
158
  }
@@ -152,25 +163,40 @@ var BetterCMSError = class _BetterCMSError extends Error {
152
163
  message: this.message,
153
164
  status: this.status,
154
165
  code: this.code,
155
- bodyCode: this.bodyCode
166
+ bodyCode: this.bodyCode,
167
+ fieldErrors: this.fieldErrors
156
168
  };
157
169
  }
158
170
  /**
159
- * Factory — creates a BetterCMSError from a failed fetch Response. Reads the
160
- * body's `message` (or `error`) for the human message and `code` for a
161
- * machine-readable condition the SDK surfaces as `bodyCode`.
171
+ * Factory — creates a BetterCMSError from a failed fetch Response. Reads the body's
172
+ * `message` (or `error`) for the human message and `code` for a machine-readable
173
+ * condition surfaced as `bodyCode`. Validation failures arrive as `{ errors: {...} }`
174
+ * (no `message`/`error`), so fall back to the first field error for the message and
175
+ * expose the full map as `fieldErrors` (FLO-474).
162
176
  */
163
177
  static async from(res) {
164
178
  let message = res.statusText || "An error occurred";
165
179
  let bodyCode;
180
+ let fieldErrors;
181
+ let code = statusToCode(res.status);
166
182
  try {
167
183
  const body = await res.json();
184
+ if (body?.errors && typeof body.errors === "object") {
185
+ fieldErrors = body.errors;
186
+ code = ErrorCodes.VALIDATION_ERROR;
187
+ }
168
188
  if (body?.message) message = body.message;
169
189
  else if (body?.error) message = body.error;
190
+ else if (fieldErrors) {
191
+ const [field, detail] = Object.entries(fieldErrors).find(
192
+ ([, v]) => typeof v === "string" && v.length > 0
193
+ ) ?? [];
194
+ if (field) message = `${field}: ${detail}`;
195
+ }
170
196
  if (body?.code) bodyCode = body.code;
171
197
  } catch {
172
198
  }
173
- return new _BetterCMSError(message, res.status, statusToCode(res.status), bodyCode);
199
+ return new _BetterCMSError(message, res.status, code, bodyCode, fieldErrors);
174
200
  }
175
201
  };
176
202
 
@@ -1235,6 +1261,30 @@ function stripStega(value) {
1235
1261
  return value.replace(FRAME, "");
1236
1262
  }
1237
1263
 
1264
+ // src/richtext.ts
1265
+ function isRichText(value) {
1266
+ return typeof value === "object" && value !== null && !Array.isArray(value) && ("html" in value || "format" in value);
1267
+ }
1268
+ function plain(value) {
1269
+ if (typeof value === "string") return value;
1270
+ if (!isRichText(value) || typeof value.html !== "string") return "";
1271
+ return decodeEntities(value.html.replace(/<[^>]+>/g, "")).trim();
1272
+ }
1273
+ function rich(value, fallback = "") {
1274
+ const html = (typeof value === "string" ? escapeHtml(value) : isRichText(value) ? value.html ?? "" : "").trim();
1275
+ return html ? unwrapLoneBlock(html) : escapeHtml(fallback);
1276
+ }
1277
+ function unwrapLoneBlock(html) {
1278
+ const m = html.match(/^<(p|div|h[1-6])(?:\s[^>]*)?>([\s\S]*)<\/\1>$/i);
1279
+ return m && !new RegExp(`</${m[1]}>`, "i").test(m[2]) ? m[2] : html;
1280
+ }
1281
+ function escapeHtml(s) {
1282
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1283
+ }
1284
+ function decodeEntities(s) {
1285
+ return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&");
1286
+ }
1287
+
1238
1288
  // src/seo.ts
1239
1289
  function jsonLdNodes(schema) {
1240
1290
  if (!schema) return [];
@@ -1608,6 +1658,7 @@ var import_types = require("@bettercms-ai/types");
1608
1658
  imageUrlBuilder,
1609
1659
  inviteMember,
1610
1660
  isBlock,
1661
+ isRichText,
1611
1662
  listApiKeys,
1612
1663
  listEntries,
1613
1664
  listForms,
@@ -1618,11 +1669,13 @@ var import_types = require("@bettercms-ai/types");
1618
1669
  listPages,
1619
1670
  listSubmissions,
1620
1671
  listWorkspaces,
1672
+ plain,
1621
1673
  publishPage,
1622
1674
  regenerateApiKey,
1623
1675
  removeMember,
1624
1676
  resolveSeo,
1625
1677
  revokeApiKey,
1678
+ rich,
1626
1679
  search,
1627
1680
  setPageContent,
1628
1681
  shouldShowField,