@bettercms-ai/sdk 1.7.0 → 1.8.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/dist/index.d.cts CHANGED
@@ -404,18 +404,28 @@ declare class BetterCMSError extends Error {
404
404
  * shares a status with others (e.g. 409 slug-conflict vs 409 project-deleted).
405
405
  */
406
406
  readonly bodyCode?: string;
407
- constructor(message: string, status: number, code: BetterCMSErrorCode, bodyCode?: string);
407
+ /**
408
+ * Per-field validation messages from a 400 `{ errors: { field: message } }` body.
409
+ * The backend returns validation failures in this shape (not `message`/`error`), so
410
+ * without this the SDK collapsed every validation 400 to a bare "Bad Request" with no
411
+ * hint which field or rule failed (FLO-474). Mirrors the forms client's `fieldErrors`.
412
+ */
413
+ readonly fieldErrors?: Record<string, string>;
414
+ constructor(message: string, status: number, code: BetterCMSErrorCode, bodyCode?: string, fieldErrors?: Record<string, string>);
408
415
  toJSON(): {
409
416
  name: string;
410
417
  message: string;
411
418
  status: number;
412
419
  code: BetterCMSErrorCode;
413
420
  bodyCode: string | undefined;
421
+ fieldErrors: Record<string, string> | undefined;
414
422
  };
415
423
  /**
416
- * Factory — creates a BetterCMSError from a failed fetch Response. Reads the
417
- * body's `message` (or `error`) for the human message and `code` for a
418
- * machine-readable condition the SDK surfaces as `bodyCode`.
424
+ * Factory — creates a BetterCMSError from a failed fetch Response. Reads the body's
425
+ * `message` (or `error`) for the human message and `code` for a machine-readable
426
+ * condition surfaced as `bodyCode`. Validation failures arrive as `{ errors: {...} }`
427
+ * (no `message`/`error`), so fall back to the first field error for the message and
428
+ * expose the full map as `fieldErrors` (FLO-474).
419
429
  */
420
430
  static from(res: Response): Promise<BetterCMSError>;
421
431
  }
@@ -594,12 +604,20 @@ interface BetterCMSManagementOptions {
594
604
  baseUrl?: string;
595
605
  /** A content:manage scoped API key. */
596
606
  apiKey: string;
607
+ /**
608
+ * Target project (id or slug), sent as the X-BCMS-Project header.
609
+ * REQUIRED for workspace-scoped keys — content writes (createEntry, createPage,
610
+ * createModel, …) must land in a project. Project-scoped keys (`bcms_proj_…`)
611
+ * don't need it; if set, it must match the key's own project.
612
+ */
613
+ project?: string;
597
614
  /** Request timeout per attempt in ms. Defaults to 10000. */
598
615
  timeout?: number;
599
616
  }
600
617
  declare class BetterCMSManagementClient extends BetterCMSDeliveryClient {
601
618
  readonly timeout: number;
602
619
  private readonly _apiKey;
620
+ private readonly _project?;
603
621
  constructor(options: BetterCMSManagementOptions);
604
622
  headers(): Record<string, string>;
605
623
  /** Management API URL: baseUrl + path (path is workspace-scoped via the key). */
package/dist/index.d.ts CHANGED
@@ -404,18 +404,28 @@ declare class BetterCMSError extends Error {
404
404
  * shares a status with others (e.g. 409 slug-conflict vs 409 project-deleted).
405
405
  */
406
406
  readonly bodyCode?: string;
407
- constructor(message: string, status: number, code: BetterCMSErrorCode, bodyCode?: string);
407
+ /**
408
+ * Per-field validation messages from a 400 `{ errors: { field: message } }` body.
409
+ * The backend returns validation failures in this shape (not `message`/`error`), so
410
+ * without this the SDK collapsed every validation 400 to a bare "Bad Request" with no
411
+ * hint which field or rule failed (FLO-474). Mirrors the forms client's `fieldErrors`.
412
+ */
413
+ readonly fieldErrors?: Record<string, string>;
414
+ constructor(message: string, status: number, code: BetterCMSErrorCode, bodyCode?: string, fieldErrors?: Record<string, string>);
408
415
  toJSON(): {
409
416
  name: string;
410
417
  message: string;
411
418
  status: number;
412
419
  code: BetterCMSErrorCode;
413
420
  bodyCode: string | undefined;
421
+ fieldErrors: Record<string, string> | undefined;
414
422
  };
415
423
  /**
416
- * Factory — creates a BetterCMSError from a failed fetch Response. Reads the
417
- * body's `message` (or `error`) for the human message and `code` for a
418
- * machine-readable condition the SDK surfaces as `bodyCode`.
424
+ * Factory — creates a BetterCMSError from a failed fetch Response. Reads the body's
425
+ * `message` (or `error`) for the human message and `code` for a machine-readable
426
+ * condition surfaced as `bodyCode`. Validation failures arrive as `{ errors: {...} }`
427
+ * (no `message`/`error`), so fall back to the first field error for the message and
428
+ * expose the full map as `fieldErrors` (FLO-474).
419
429
  */
420
430
  static from(res: Response): Promise<BetterCMSError>;
421
431
  }
@@ -594,12 +604,20 @@ interface BetterCMSManagementOptions {
594
604
  baseUrl?: string;
595
605
  /** A content:manage scoped API key. */
596
606
  apiKey: string;
607
+ /**
608
+ * Target project (id or slug), sent as the X-BCMS-Project header.
609
+ * REQUIRED for workspace-scoped keys — content writes (createEntry, createPage,
610
+ * createModel, …) must land in a project. Project-scoped keys (`bcms_proj_…`)
611
+ * don't need it; if set, it must match the key's own project.
612
+ */
613
+ project?: string;
597
614
  /** Request timeout per attempt in ms. Defaults to 10000. */
598
615
  timeout?: number;
599
616
  }
600
617
  declare class BetterCMSManagementClient extends BetterCMSDeliveryClient {
601
618
  readonly timeout: number;
602
619
  private readonly _apiKey;
620
+ private readonly _project?;
603
621
  constructor(options: BetterCMSManagementOptions);
604
622
  headers(): Record<string, string>;
605
623
  /** Management API URL: baseUrl + path (path is workspace-scoped via the key). */
package/dist/index.js CHANGED
@@ -28,12 +28,20 @@ var BetterCMSError = class _BetterCMSError extends Error {
28
28
  * shares a status with others (e.g. 409 slug-conflict vs 409 project-deleted).
29
29
  */
30
30
  bodyCode;
31
- constructor(message, status, code, bodyCode) {
31
+ /**
32
+ * Per-field validation messages from a 400 `{ errors: { field: message } }` body.
33
+ * The backend returns validation failures in this shape (not `message`/`error`), so
34
+ * without this the SDK collapsed every validation 400 to a bare "Bad Request" with no
35
+ * hint which field or rule failed (FLO-474). Mirrors the forms client's `fieldErrors`.
36
+ */
37
+ fieldErrors;
38
+ constructor(message, status, code, bodyCode, fieldErrors) {
32
39
  super(message);
33
40
  this.name = "BetterCMSError";
34
41
  this.status = status;
35
42
  this.code = code;
36
43
  this.bodyCode = bodyCode;
44
+ this.fieldErrors = fieldErrors;
37
45
  if (Error.captureStackTrace) {
38
46
  Error.captureStackTrace(this, _BetterCMSError);
39
47
  }
@@ -44,25 +52,40 @@ var BetterCMSError = class _BetterCMSError extends Error {
44
52
  message: this.message,
45
53
  status: this.status,
46
54
  code: this.code,
47
- bodyCode: this.bodyCode
55
+ bodyCode: this.bodyCode,
56
+ fieldErrors: this.fieldErrors
48
57
  };
49
58
  }
50
59
  /**
51
- * Factory — creates a BetterCMSError from a failed fetch Response. Reads the
52
- * body's `message` (or `error`) for the human message and `code` for a
53
- * machine-readable condition the SDK surfaces as `bodyCode`.
60
+ * Factory — creates a BetterCMSError from a failed fetch Response. Reads the body's
61
+ * `message` (or `error`) for the human message and `code` for a machine-readable
62
+ * condition surfaced as `bodyCode`. Validation failures arrive as `{ errors: {...} }`
63
+ * (no `message`/`error`), so fall back to the first field error for the message and
64
+ * expose the full map as `fieldErrors` (FLO-474).
54
65
  */
55
66
  static async from(res) {
56
67
  let message = res.statusText || "An error occurred";
57
68
  let bodyCode;
69
+ let fieldErrors;
70
+ let code = statusToCode(res.status);
58
71
  try {
59
72
  const body = await res.json();
73
+ if (body?.errors && typeof body.errors === "object") {
74
+ fieldErrors = body.errors;
75
+ code = ErrorCodes.VALIDATION_ERROR;
76
+ }
60
77
  if (body?.message) message = body.message;
61
78
  else if (body?.error) message = body.error;
79
+ else if (fieldErrors) {
80
+ const [field, detail] = Object.entries(fieldErrors).find(
81
+ ([, v]) => typeof v === "string" && v.length > 0
82
+ ) ?? [];
83
+ if (field) message = `${field}: ${detail}`;
84
+ }
62
85
  if (body?.code) bodyCode = body.code;
63
86
  } catch {
64
87
  }
65
- return new _BetterCMSError(message, res.status, statusToCode(res.status), bodyCode);
88
+ return new _BetterCMSError(message, res.status, code, bodyCode, fieldErrors);
66
89
  }
67
90
  };
68
91
 
@@ -673,15 +696,18 @@ var RETRY_DELAYS2 = [1e3, 2e3, 4e3];
673
696
  var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
674
697
  timeout;
675
698
  _apiKey;
699
+ _project;
676
700
  constructor(options) {
677
701
  super({ workspace: "", baseUrl: options.baseUrl ?? DEFAULT_BASE_URL2 });
678
702
  this._apiKey = options.apiKey;
703
+ this._project = options.project;
679
704
  this.timeout = options.timeout ?? DEFAULT_TIMEOUT2;
680
705
  }
681
706
  headers() {
682
707
  return {
683
708
  "Content-Type": "application/json",
684
- "X-API-Key": this._apiKey
709
+ "X-API-Key": this._apiKey,
710
+ ...this._project ? { "X-BCMS-Project": this._project } : {}
685
711
  };
686
712
  }
687
713
  /** Management API URL: baseUrl + path (path is workspace-scoped via the key). */