@bettercms-ai/sdk 1.13.1 → 1.14.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 +136 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -8
- package/dist/index.d.ts +57 -8
- package/dist/index.js +134 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -51,11 +51,13 @@ declare function listContentAll(client: BetterCMSDeliveryClient, opts?: ListCont
|
|
|
51
51
|
declare class BetterCMSDeliveryClient {
|
|
52
52
|
readonly workspace: string;
|
|
53
53
|
readonly baseUrl: string;
|
|
54
|
+
protected readonly timeout: number;
|
|
54
55
|
private readonly apiKey?;
|
|
55
56
|
constructor(opts: {
|
|
56
57
|
workspace: string;
|
|
57
58
|
apiKey?: string;
|
|
58
59
|
baseUrl: string;
|
|
60
|
+
timeout?: number;
|
|
59
61
|
});
|
|
60
62
|
getContent(slug: string): Promise<_bettercms_ai_types.Content>;
|
|
61
63
|
listContent(opts?: Parameters<typeof listContent>[1]): Promise<_bettercms_ai_types.PaginatedResult<_bettercms_ai_types.Content>>;
|
|
@@ -71,8 +73,13 @@ declare class BetterCMSDeliveryClient {
|
|
|
71
73
|
/** Build request headers, including auth if provided. */
|
|
72
74
|
headers(): Record<string, string>;
|
|
73
75
|
/**
|
|
74
|
-
* Generic fetch helper —
|
|
75
|
-
* and throws a typed BetterCMSError on non-OK responses.
|
|
76
|
+
* Generic fetch helper — retries 429/503 honouring Retry-After, applies a
|
|
77
|
+
* per-attempt timeout, and throws a typed BetterCMSError on non-OK responses.
|
|
78
|
+
*
|
|
79
|
+
* This path (BetterCMS.site()) previously had NO retry and NO timeout: the first
|
|
80
|
+
* 429 from the delivery limiter threw straight through and killed the caller. In a
|
|
81
|
+
* static build that is a failed deploy. See ./retry.ts for why a fixed ladder was
|
|
82
|
+
* never going to be enough against a 60-second fixed window.
|
|
76
83
|
*/
|
|
77
84
|
fetchJSON<T>(url: string): Promise<T>;
|
|
78
85
|
}
|
|
@@ -139,7 +146,7 @@ interface WebhookListResponse {
|
|
|
139
146
|
*
|
|
140
147
|
* Extends BetterCMSDeliveryClient but overrides:
|
|
141
148
|
* - `headers()` — uses Bearer token auth instead of API key
|
|
142
|
-
* - `fetchJSON()` — adds retry on 429/503
|
|
149
|
+
* - `fetchJSON()` — adds Retry-After-aware retry on 429/503 (see ./retry.ts)
|
|
143
150
|
*
|
|
144
151
|
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
145
152
|
*/
|
|
@@ -158,13 +165,12 @@ interface BetterCMSAdminOptions {
|
|
|
158
165
|
* fetchJSON() for Bearer auth + retry/timeout.
|
|
159
166
|
*/
|
|
160
167
|
declare class BetterCMSAdminClient extends BetterCMSDeliveryClient {
|
|
161
|
-
readonly timeout: number;
|
|
162
168
|
private readonly _token;
|
|
163
169
|
constructor(options: BetterCMSAdminOptions);
|
|
164
170
|
headers(): Record<string, string>;
|
|
165
171
|
/**
|
|
166
|
-
* Admin fetchJSON with retry (429, 503) and per-request
|
|
167
|
-
* Each retry gets a fresh timeout.
|
|
172
|
+
* Admin fetchJSON with Retry-After-aware retry (429, 503) and a per-request
|
|
173
|
+
* timeout (AbortController). Each retry gets a fresh timeout.
|
|
168
174
|
*/
|
|
169
175
|
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
170
176
|
/** Build the admin API URL. */
|
|
@@ -741,7 +747,11 @@ declare class BetterCMSManagementClient extends BetterCMSDeliveryClient {
|
|
|
741
747
|
headers(): Record<string, string>;
|
|
742
748
|
/** Management API URL: baseUrl + path (path is workspace-scoped via the key). */
|
|
743
749
|
url(path: string): string;
|
|
744
|
-
/**
|
|
750
|
+
/**
|
|
751
|
+
* fetchJSON with Retry-After-aware retry (429, 503) + per-request timeout
|
|
752
|
+
* (mirrors the admin client). See ./retry.ts for why the delay must be able
|
|
753
|
+
* to outlast the backend's 60-second rate-limit window.
|
|
754
|
+
*/
|
|
745
755
|
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
746
756
|
listModels(): Promise<ManagedContentModel[]>;
|
|
747
757
|
getModel(id: string): Promise<ManagedContentModel>;
|
|
@@ -864,6 +874,12 @@ interface BetterCMSSiteOptions {
|
|
|
864
874
|
apiKey?: string;
|
|
865
875
|
/** Base URL of the Delivery API. Defaults to https://api.bettercms.ai/v1. */
|
|
866
876
|
baseUrl?: string;
|
|
877
|
+
/**
|
|
878
|
+
* Per-attempt request timeout in ms. Defaults to 10000. Each of the 3 retries gets
|
|
879
|
+
* a fresh one. Raise it for a slow delivery origin rather than losing the timeout —
|
|
880
|
+
* without one, a hung socket stalls a build indefinitely.
|
|
881
|
+
*/
|
|
882
|
+
timeout?: number;
|
|
867
883
|
}
|
|
868
884
|
/**
|
|
869
885
|
* BetterCMS factory — returns a typed client for the Delivery API.
|
|
@@ -931,6 +947,21 @@ interface CreateClientOptions {
|
|
|
931
947
|
stega?: boolean;
|
|
932
948
|
/** Per-attempt timeout in ms. Defaults to 10000. */
|
|
933
949
|
timeout?: number;
|
|
950
|
+
/**
|
|
951
|
+
* Collapse identical GETs to one network round-trip for the life of this client.
|
|
952
|
+
* Default false.
|
|
953
|
+
*
|
|
954
|
+
* Only correct where the content is a POINT-IN-TIME SNAPSHOT — i.e. a static build,
|
|
955
|
+
* where every route renders from the same published dataset. `@bettercms-ai/astro`
|
|
956
|
+
* turns this on automatically when Astro's `command === "build"` and never for `dev`,
|
|
957
|
+
* SSR or the drafts perspective, where a repeat read must see the current row.
|
|
958
|
+
*
|
|
959
|
+
* A static build without it re-fetches the same nav/settings/collection once PER ROUTE.
|
|
960
|
+
* That is what made a large site exhaust the delivery rate limit: hundreds of identical
|
|
961
|
+
* requests, and `getPage()` re-scanning every page of the paginated list each time it is
|
|
962
|
+
* called.
|
|
963
|
+
*/
|
|
964
|
+
cache?: boolean;
|
|
934
965
|
}
|
|
935
966
|
interface ListEntriesOptions {
|
|
936
967
|
/** Filter by content-model slug. */
|
|
@@ -1048,6 +1079,24 @@ interface RichTextValue {
|
|
|
1048
1079
|
*/
|
|
1049
1080
|
readonly doc?: DocValue;
|
|
1050
1081
|
}
|
|
1082
|
+
/** The marker on a Portable Text envelope. `format` is a plain string, never a union — see below. */
|
|
1083
|
+
declare const PORTABLE_TEXT_FORMAT = "portable-text-1";
|
|
1084
|
+
/**
|
|
1085
|
+
* Structured blocks, when the field stores Portable Text.
|
|
1086
|
+
*
|
|
1087
|
+
* ── The compatibility guarantee, stated once ────────────────────────────────────────────
|
|
1088
|
+
* Portable Text did NOT change this envelope. `format`, `value` and `html` are the same three
|
|
1089
|
+
* keys they always were, so `isRichText`, `plain()` and `rich()` keep working untouched — and
|
|
1090
|
+
* they have to, because sites compiled months ago read `.html` directly and cannot be
|
|
1091
|
+
* rebuilt. `bettercms-starter-modern/lib/cms.ts` even carries its OWN copy of `rich()`, baked
|
|
1092
|
+
* into deployments nobody can reach. Storing a bare array instead would have made
|
|
1093
|
+
* `isRichText` return false (it rejects arrays), every shipped `rich()` call return "", and
|
|
1094
|
+
* every one of those pages go blank at 200 OK.
|
|
1095
|
+
*
|
|
1096
|
+
* `portableText()` is therefore ADDITIVE: `html` remains the always-present field, and this is
|
|
1097
|
+
* the opt-in for consumers that want to render structure with `@portabletext/react` instead.
|
|
1098
|
+
*/
|
|
1099
|
+
declare function portableText(value: TextOrRich): readonly unknown[] | null;
|
|
1051
1100
|
/**
|
|
1052
1101
|
* A field that may arrive as either shape. Type author-editable text fields with this and the
|
|
1053
1102
|
* compiler routes you through `plain()`/`rich()`, so a type switch in the CMS can never reach
|
|
@@ -1362,4 +1411,4 @@ declare function inviteMember(client: BetterCMSAdminClient, workspaceId: string,
|
|
|
1362
1411
|
declare function updateMemberRole(client: BetterCMSAdminClient, workspaceId: string, memberId: string, role: MemberRole): Promise<Member>;
|
|
1363
1412
|
declare function removeMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<void>;
|
|
1364
1413
|
|
|
1365
|
-
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
|
1414
|
+
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, PORTABLE_TEXT_FORMAT, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, portableText, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
package/dist/index.d.ts
CHANGED
|
@@ -51,11 +51,13 @@ declare function listContentAll(client: BetterCMSDeliveryClient, opts?: ListCont
|
|
|
51
51
|
declare class BetterCMSDeliveryClient {
|
|
52
52
|
readonly workspace: string;
|
|
53
53
|
readonly baseUrl: string;
|
|
54
|
+
protected readonly timeout: number;
|
|
54
55
|
private readonly apiKey?;
|
|
55
56
|
constructor(opts: {
|
|
56
57
|
workspace: string;
|
|
57
58
|
apiKey?: string;
|
|
58
59
|
baseUrl: string;
|
|
60
|
+
timeout?: number;
|
|
59
61
|
});
|
|
60
62
|
getContent(slug: string): Promise<_bettercms_ai_types.Content>;
|
|
61
63
|
listContent(opts?: Parameters<typeof listContent>[1]): Promise<_bettercms_ai_types.PaginatedResult<_bettercms_ai_types.Content>>;
|
|
@@ -71,8 +73,13 @@ declare class BetterCMSDeliveryClient {
|
|
|
71
73
|
/** Build request headers, including auth if provided. */
|
|
72
74
|
headers(): Record<string, string>;
|
|
73
75
|
/**
|
|
74
|
-
* Generic fetch helper —
|
|
75
|
-
* and throws a typed BetterCMSError on non-OK responses.
|
|
76
|
+
* Generic fetch helper — retries 429/503 honouring Retry-After, applies a
|
|
77
|
+
* per-attempt timeout, and throws a typed BetterCMSError on non-OK responses.
|
|
78
|
+
*
|
|
79
|
+
* This path (BetterCMS.site()) previously had NO retry and NO timeout: the first
|
|
80
|
+
* 429 from the delivery limiter threw straight through and killed the caller. In a
|
|
81
|
+
* static build that is a failed deploy. See ./retry.ts for why a fixed ladder was
|
|
82
|
+
* never going to be enough against a 60-second fixed window.
|
|
76
83
|
*/
|
|
77
84
|
fetchJSON<T>(url: string): Promise<T>;
|
|
78
85
|
}
|
|
@@ -139,7 +146,7 @@ interface WebhookListResponse {
|
|
|
139
146
|
*
|
|
140
147
|
* Extends BetterCMSDeliveryClient but overrides:
|
|
141
148
|
* - `headers()` — uses Bearer token auth instead of API key
|
|
142
|
-
* - `fetchJSON()` — adds retry on 429/503
|
|
149
|
+
* - `fetchJSON()` — adds Retry-After-aware retry on 429/503 (see ./retry.ts)
|
|
143
150
|
*
|
|
144
151
|
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
145
152
|
*/
|
|
@@ -158,13 +165,12 @@ interface BetterCMSAdminOptions {
|
|
|
158
165
|
* fetchJSON() for Bearer auth + retry/timeout.
|
|
159
166
|
*/
|
|
160
167
|
declare class BetterCMSAdminClient extends BetterCMSDeliveryClient {
|
|
161
|
-
readonly timeout: number;
|
|
162
168
|
private readonly _token;
|
|
163
169
|
constructor(options: BetterCMSAdminOptions);
|
|
164
170
|
headers(): Record<string, string>;
|
|
165
171
|
/**
|
|
166
|
-
* Admin fetchJSON with retry (429, 503) and per-request
|
|
167
|
-
* Each retry gets a fresh timeout.
|
|
172
|
+
* Admin fetchJSON with Retry-After-aware retry (429, 503) and a per-request
|
|
173
|
+
* timeout (AbortController). Each retry gets a fresh timeout.
|
|
168
174
|
*/
|
|
169
175
|
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
170
176
|
/** Build the admin API URL. */
|
|
@@ -741,7 +747,11 @@ declare class BetterCMSManagementClient extends BetterCMSDeliveryClient {
|
|
|
741
747
|
headers(): Record<string, string>;
|
|
742
748
|
/** Management API URL: baseUrl + path (path is workspace-scoped via the key). */
|
|
743
749
|
url(path: string): string;
|
|
744
|
-
/**
|
|
750
|
+
/**
|
|
751
|
+
* fetchJSON with Retry-After-aware retry (429, 503) + per-request timeout
|
|
752
|
+
* (mirrors the admin client). See ./retry.ts for why the delay must be able
|
|
753
|
+
* to outlast the backend's 60-second rate-limit window.
|
|
754
|
+
*/
|
|
745
755
|
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
746
756
|
listModels(): Promise<ManagedContentModel[]>;
|
|
747
757
|
getModel(id: string): Promise<ManagedContentModel>;
|
|
@@ -864,6 +874,12 @@ interface BetterCMSSiteOptions {
|
|
|
864
874
|
apiKey?: string;
|
|
865
875
|
/** Base URL of the Delivery API. Defaults to https://api.bettercms.ai/v1. */
|
|
866
876
|
baseUrl?: string;
|
|
877
|
+
/**
|
|
878
|
+
* Per-attempt request timeout in ms. Defaults to 10000. Each of the 3 retries gets
|
|
879
|
+
* a fresh one. Raise it for a slow delivery origin rather than losing the timeout —
|
|
880
|
+
* without one, a hung socket stalls a build indefinitely.
|
|
881
|
+
*/
|
|
882
|
+
timeout?: number;
|
|
867
883
|
}
|
|
868
884
|
/**
|
|
869
885
|
* BetterCMS factory — returns a typed client for the Delivery API.
|
|
@@ -931,6 +947,21 @@ interface CreateClientOptions {
|
|
|
931
947
|
stega?: boolean;
|
|
932
948
|
/** Per-attempt timeout in ms. Defaults to 10000. */
|
|
933
949
|
timeout?: number;
|
|
950
|
+
/**
|
|
951
|
+
* Collapse identical GETs to one network round-trip for the life of this client.
|
|
952
|
+
* Default false.
|
|
953
|
+
*
|
|
954
|
+
* Only correct where the content is a POINT-IN-TIME SNAPSHOT — i.e. a static build,
|
|
955
|
+
* where every route renders from the same published dataset. `@bettercms-ai/astro`
|
|
956
|
+
* turns this on automatically when Astro's `command === "build"` and never for `dev`,
|
|
957
|
+
* SSR or the drafts perspective, where a repeat read must see the current row.
|
|
958
|
+
*
|
|
959
|
+
* A static build without it re-fetches the same nav/settings/collection once PER ROUTE.
|
|
960
|
+
* That is what made a large site exhaust the delivery rate limit: hundreds of identical
|
|
961
|
+
* requests, and `getPage()` re-scanning every page of the paginated list each time it is
|
|
962
|
+
* called.
|
|
963
|
+
*/
|
|
964
|
+
cache?: boolean;
|
|
934
965
|
}
|
|
935
966
|
interface ListEntriesOptions {
|
|
936
967
|
/** Filter by content-model slug. */
|
|
@@ -1048,6 +1079,24 @@ interface RichTextValue {
|
|
|
1048
1079
|
*/
|
|
1049
1080
|
readonly doc?: DocValue;
|
|
1050
1081
|
}
|
|
1082
|
+
/** The marker on a Portable Text envelope. `format` is a plain string, never a union — see below. */
|
|
1083
|
+
declare const PORTABLE_TEXT_FORMAT = "portable-text-1";
|
|
1084
|
+
/**
|
|
1085
|
+
* Structured blocks, when the field stores Portable Text.
|
|
1086
|
+
*
|
|
1087
|
+
* ── The compatibility guarantee, stated once ────────────────────────────────────────────
|
|
1088
|
+
* Portable Text did NOT change this envelope. `format`, `value` and `html` are the same three
|
|
1089
|
+
* keys they always were, so `isRichText`, `plain()` and `rich()` keep working untouched — and
|
|
1090
|
+
* they have to, because sites compiled months ago read `.html` directly and cannot be
|
|
1091
|
+
* rebuilt. `bettercms-starter-modern/lib/cms.ts` even carries its OWN copy of `rich()`, baked
|
|
1092
|
+
* into deployments nobody can reach. Storing a bare array instead would have made
|
|
1093
|
+
* `isRichText` return false (it rejects arrays), every shipped `rich()` call return "", and
|
|
1094
|
+
* every one of those pages go blank at 200 OK.
|
|
1095
|
+
*
|
|
1096
|
+
* `portableText()` is therefore ADDITIVE: `html` remains the always-present field, and this is
|
|
1097
|
+
* the opt-in for consumers that want to render structure with `@portabletext/react` instead.
|
|
1098
|
+
*/
|
|
1099
|
+
declare function portableText(value: TextOrRich): readonly unknown[] | null;
|
|
1051
1100
|
/**
|
|
1052
1101
|
* A field that may arrive as either shape. Type author-editable text fields with this and the
|
|
1053
1102
|
* compiler routes you through `plain()`/`rich()`, so a type switch in the CMS can never reach
|
|
@@ -1362,4 +1411,4 @@ declare function inviteMember(client: BetterCMSAdminClient, workspaceId: string,
|
|
|
1362
1411
|
declare function updateMemberRole(client: BetterCMSAdminClient, workspaceId: string, memberId: string, role: MemberRole): Promise<Member>;
|
|
1363
1412
|
declare function removeMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<void>;
|
|
1364
1413
|
|
|
1365
|
-
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
|
1414
|
+
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CommandManagedLayoutInput, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type ExtractionCandidate, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type GetManagedLayoutOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedLayoutClient, type ManagedLayoutDocument, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, PORTABLE_TEXT_FORMAT, type PageListOptions, type PendingInvite, type ResolvedSeo, type RichTextValue, type SearchHit, type SearchOptions, type SeoInput, type SeoLocation, type SeoMeta, type SeoMetaInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type TextOrRich, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, type WriteContentInput, addModelFields, addPageFields, asStringArray, commandManagedLayout, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedLayout, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, isMultiValueField, isRichText, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, plain, portableText, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, rich, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, toggleOption, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,35 @@ var BetterCMSError = class _BetterCMSError extends Error {
|
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
// src/retry.ts
|
|
93
|
+
var RETRY_STATUSES = [429, 503];
|
|
94
|
+
var MAX_RETRIES = 3;
|
|
95
|
+
var MAX_DELAY_MS = 65e3;
|
|
96
|
+
var BASE_DELAY_MS = 1e3;
|
|
97
|
+
var JITTER_MS = 250;
|
|
98
|
+
function isRetryableStatus(status) {
|
|
99
|
+
return RETRY_STATUSES.includes(status);
|
|
100
|
+
}
|
|
101
|
+
function retryAfterMs(res) {
|
|
102
|
+
const raw = res.headers?.get?.("retry-after");
|
|
103
|
+
if (!raw) return null;
|
|
104
|
+
const seconds = Number(raw);
|
|
105
|
+
if (Number.isFinite(seconds)) {
|
|
106
|
+
return Math.min(Math.max(seconds, 0) * 1e3, MAX_DELAY_MS);
|
|
107
|
+
}
|
|
108
|
+
const at = Date.parse(raw);
|
|
109
|
+
if (!Number.isNaN(at)) {
|
|
110
|
+
return Math.min(Math.max(0, at - Date.now()), MAX_DELAY_MS);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function backoffMs(attempt, res) {
|
|
115
|
+
const fromServer = res ? retryAfterMs(res) : null;
|
|
116
|
+
const base = Math.min(fromServer ?? BASE_DELAY_MS * 2 ** attempt, MAX_DELAY_MS - JITTER_MS);
|
|
117
|
+
return base + Math.random() * JITTER_MS;
|
|
118
|
+
}
|
|
119
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
120
|
+
|
|
92
121
|
// src/methods/getContent.ts
|
|
93
122
|
async function getContent(client, slug) {
|
|
94
123
|
const data = await client.fetchJSON(client.url(slug));
|
|
@@ -153,14 +182,17 @@ function listContentAll(client, opts) {
|
|
|
153
182
|
}
|
|
154
183
|
|
|
155
184
|
// src/delivery-client.ts
|
|
185
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
156
186
|
var BetterCMSDeliveryClient = class {
|
|
157
187
|
workspace;
|
|
158
188
|
baseUrl;
|
|
189
|
+
timeout;
|
|
159
190
|
apiKey;
|
|
160
191
|
constructor(opts) {
|
|
161
192
|
this.workspace = opts.workspace;
|
|
162
193
|
this.baseUrl = opts.baseUrl;
|
|
163
194
|
this.apiKey = opts.apiKey;
|
|
195
|
+
this.timeout = opts.timeout ?? DEFAULT_TIMEOUT;
|
|
164
196
|
}
|
|
165
197
|
getContent(slug) {
|
|
166
198
|
return getContent(this, slug);
|
|
@@ -193,24 +225,46 @@ var BetterCMSDeliveryClient = class {
|
|
|
193
225
|
return headers;
|
|
194
226
|
}
|
|
195
227
|
/**
|
|
196
|
-
* Generic fetch helper —
|
|
197
|
-
* and throws a typed BetterCMSError on non-OK responses.
|
|
228
|
+
* Generic fetch helper — retries 429/503 honouring Retry-After, applies a
|
|
229
|
+
* per-attempt timeout, and throws a typed BetterCMSError on non-OK responses.
|
|
230
|
+
*
|
|
231
|
+
* This path (BetterCMS.site()) previously had NO retry and NO timeout: the first
|
|
232
|
+
* 429 from the delivery limiter threw straight through and killed the caller. In a
|
|
233
|
+
* static build that is a failed deploy. See ./retry.ts for why a fixed ladder was
|
|
234
|
+
* never going to be enough against a 60-second fixed window.
|
|
198
235
|
*/
|
|
199
236
|
async fetchJSON(url) {
|
|
200
|
-
let
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
237
|
+
for (let attempt = 0; ; attempt++) {
|
|
238
|
+
const controller = new AbortController();
|
|
239
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
240
|
+
let res;
|
|
241
|
+
try {
|
|
242
|
+
res = await globalThis.fetch(url, {
|
|
243
|
+
headers: this.headers(),
|
|
244
|
+
signal: controller.signal
|
|
245
|
+
});
|
|
246
|
+
} catch (err) {
|
|
247
|
+
const timedOut = err instanceof Error && err.name === "AbortError";
|
|
248
|
+
if (attempt < MAX_RETRIES) {
|
|
249
|
+
await sleep(backoffMs(attempt));
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (timedOut) throw new BetterCMSError("Request timeout", 408, "NETWORK_ERROR");
|
|
253
|
+
throw new BetterCMSError(
|
|
254
|
+
`Network error: ${err instanceof Error ? err.message : String(err)}`,
|
|
255
|
+
0,
|
|
256
|
+
"NETWORK_ERROR"
|
|
257
|
+
);
|
|
258
|
+
} finally {
|
|
259
|
+
clearTimeout(timer);
|
|
260
|
+
}
|
|
261
|
+
if (res.ok) return await res.json();
|
|
262
|
+
if (attempt < MAX_RETRIES && isRetryableStatus(res.status)) {
|
|
263
|
+
await sleep(backoffMs(attempt, res));
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
211
266
|
throw await BetterCMSError.from(res);
|
|
212
267
|
}
|
|
213
|
-
return await res.json();
|
|
214
268
|
}
|
|
215
269
|
};
|
|
216
270
|
|
|
@@ -291,19 +345,15 @@ function addWebhookMethods(client) {
|
|
|
291
345
|
|
|
292
346
|
// src/admin-client.ts
|
|
293
347
|
var DEFAULT_BASE_URL = "https://api.bettercms.ai/v1";
|
|
294
|
-
var DEFAULT_TIMEOUT = 1e4;
|
|
295
|
-
var RETRY_STATUSES = [429, 503];
|
|
296
|
-
var RETRY_DELAYS = [1e3, 2e3, 4e3];
|
|
297
348
|
var BetterCMSAdminClient = class extends BetterCMSDeliveryClient {
|
|
298
|
-
timeout;
|
|
299
349
|
_token;
|
|
300
350
|
constructor(options) {
|
|
301
351
|
super({
|
|
302
352
|
workspace: "",
|
|
303
|
-
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL
|
|
353
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
354
|
+
timeout: options.timeout
|
|
304
355
|
});
|
|
305
356
|
this._token = options.token;
|
|
306
|
-
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
307
357
|
}
|
|
308
358
|
headers() {
|
|
309
359
|
return {
|
|
@@ -313,12 +363,12 @@ var BetterCMSAdminClient = class extends BetterCMSDeliveryClient {
|
|
|
313
363
|
};
|
|
314
364
|
}
|
|
315
365
|
/**
|
|
316
|
-
* Admin fetchJSON with retry (429, 503) and per-request
|
|
317
|
-
* Each retry gets a fresh timeout.
|
|
366
|
+
* Admin fetchJSON with Retry-After-aware retry (429, 503) and a per-request
|
|
367
|
+
* timeout (AbortController). Each retry gets a fresh timeout.
|
|
318
368
|
*/
|
|
319
369
|
async fetchJSON(url, init) {
|
|
320
370
|
let lastError;
|
|
321
|
-
for (let attempt = 0; attempt <=
|
|
371
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
322
372
|
const controller = new AbortController();
|
|
323
373
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
324
374
|
let nonRetryable = false;
|
|
@@ -332,12 +382,12 @@ var BetterCMSAdminClient = class extends BetterCMSDeliveryClient {
|
|
|
332
382
|
if (res.ok) {
|
|
333
383
|
return res.json();
|
|
334
384
|
}
|
|
335
|
-
const shouldRetry = attempt <
|
|
385
|
+
const shouldRetry = attempt < MAX_RETRIES && isRetryableStatus(res.status);
|
|
336
386
|
if (!shouldRetry) {
|
|
337
387
|
nonRetryable = true;
|
|
338
388
|
throw await BetterCMSError.from(res);
|
|
339
389
|
}
|
|
340
|
-
await sleep(
|
|
390
|
+
await sleep(backoffMs(attempt, res));
|
|
341
391
|
} catch (err) {
|
|
342
392
|
clearTimeout(timeoutId);
|
|
343
393
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -347,8 +397,8 @@ var BetterCMSAdminClient = class extends BetterCMSDeliveryClient {
|
|
|
347
397
|
throw err;
|
|
348
398
|
}
|
|
349
399
|
lastError = err;
|
|
350
|
-
if (attempt <
|
|
351
|
-
await sleep(
|
|
400
|
+
if (attempt < MAX_RETRIES) {
|
|
401
|
+
await sleep(backoffMs(attempt));
|
|
352
402
|
continue;
|
|
353
403
|
}
|
|
354
404
|
throw err;
|
|
@@ -365,9 +415,6 @@ var BetterCMSAdminClient = class extends BetterCMSDeliveryClient {
|
|
|
365
415
|
return addWebhookMethods(this);
|
|
366
416
|
}
|
|
367
417
|
};
|
|
368
|
-
function sleep(ms) {
|
|
369
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
370
|
-
}
|
|
371
418
|
|
|
372
419
|
// src/methods/management/content.ts
|
|
373
420
|
async function listModels(client) {
|
|
@@ -508,7 +555,14 @@ var MIME_BY_EXT = {
|
|
|
508
555
|
avif: "image/avif",
|
|
509
556
|
mp4: "video/mp4",
|
|
510
557
|
webm: "video/webm",
|
|
511
|
-
pdf: "application/pdf"
|
|
558
|
+
pdf: "application/pdf",
|
|
559
|
+
// mp3/ogg were server-allowed all along; the SDK sent application/octet-stream for them,
|
|
560
|
+
// which the allowlist then refused — so an agent could never upload audio.
|
|
561
|
+
mp3: "audio/mpeg",
|
|
562
|
+
ogg: "audio/ogg",
|
|
563
|
+
txt: "text/plain",
|
|
564
|
+
md: "text/markdown",
|
|
565
|
+
markdown: "text/markdown"
|
|
512
566
|
};
|
|
513
567
|
function basenameOf(p) {
|
|
514
568
|
const clean = p.split(/[?#]/)[0] ?? p;
|
|
@@ -737,8 +791,6 @@ async function commandManagedLayout(client, input) {
|
|
|
737
791
|
// src/management-client.ts
|
|
738
792
|
var DEFAULT_BASE_URL2 = "https://api.bettercms.ai/v1";
|
|
739
793
|
var DEFAULT_TIMEOUT2 = 1e4;
|
|
740
|
-
var RETRY_STATUSES2 = [429, 503];
|
|
741
|
-
var RETRY_DELAYS2 = [1e3, 2e3, 4e3];
|
|
742
794
|
var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
|
|
743
795
|
timeout;
|
|
744
796
|
_apiKey;
|
|
@@ -760,10 +812,14 @@ var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
|
|
|
760
812
|
url(path) {
|
|
761
813
|
return `${this.baseUrl}${path}`;
|
|
762
814
|
}
|
|
763
|
-
/**
|
|
815
|
+
/**
|
|
816
|
+
* fetchJSON with Retry-After-aware retry (429, 503) + per-request timeout
|
|
817
|
+
* (mirrors the admin client). See ./retry.ts for why the delay must be able
|
|
818
|
+
* to outlast the backend's 60-second rate-limit window.
|
|
819
|
+
*/
|
|
764
820
|
async fetchJSON(url, init) {
|
|
765
821
|
let lastError;
|
|
766
|
-
for (let attempt = 0; attempt <=
|
|
822
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
767
823
|
const controller = new AbortController();
|
|
768
824
|
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
769
825
|
let nonRetryable = false;
|
|
@@ -777,12 +833,12 @@ var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
|
|
|
777
833
|
if (res.ok) {
|
|
778
834
|
return res.json();
|
|
779
835
|
}
|
|
780
|
-
const shouldRetry = attempt <
|
|
836
|
+
const shouldRetry = attempt < MAX_RETRIES && isRetryableStatus(res.status);
|
|
781
837
|
if (!shouldRetry) {
|
|
782
838
|
nonRetryable = true;
|
|
783
839
|
throw await BetterCMSError.from(res);
|
|
784
840
|
}
|
|
785
|
-
await
|
|
841
|
+
await sleep(backoffMs(attempt, res));
|
|
786
842
|
} catch (err) {
|
|
787
843
|
clearTimeout(timeoutId);
|
|
788
844
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -790,8 +846,8 @@ var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
|
|
|
790
846
|
}
|
|
791
847
|
if (nonRetryable) throw err;
|
|
792
848
|
lastError = err;
|
|
793
|
-
if (attempt <
|
|
794
|
-
await
|
|
849
|
+
if (attempt < MAX_RETRIES) {
|
|
850
|
+
await sleep(backoffMs(attempt));
|
|
795
851
|
continue;
|
|
796
852
|
}
|
|
797
853
|
throw err;
|
|
@@ -923,9 +979,6 @@ var BetterCMSManagementClient = class extends BetterCMSDeliveryClient {
|
|
|
923
979
|
return generateSeoMeta(this, input);
|
|
924
980
|
}
|
|
925
981
|
};
|
|
926
|
-
function sleep2(ms) {
|
|
927
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
928
|
-
}
|
|
929
982
|
|
|
930
983
|
// src/auth.ts
|
|
931
984
|
var AUTH_BASE_URL = "https://api.bettercms.ai/api/v1/auth";
|
|
@@ -1016,7 +1069,8 @@ var BetterCMS = {
|
|
|
1016
1069
|
return new BetterCMSDeliveryClient({
|
|
1017
1070
|
workspace: options.workspace,
|
|
1018
1071
|
apiKey: options.apiKey,
|
|
1019
|
-
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL3
|
|
1072
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL3,
|
|
1073
|
+
timeout: options.timeout
|
|
1020
1074
|
});
|
|
1021
1075
|
},
|
|
1022
1076
|
/**
|
|
@@ -1048,10 +1102,7 @@ var BetterCMS = {
|
|
|
1048
1102
|
};
|
|
1049
1103
|
|
|
1050
1104
|
// src/read-client.ts
|
|
1051
|
-
var RETRY_STATUSES3 = [429, 503];
|
|
1052
|
-
var RETRY_DELAYS3 = [250, 750, 1500];
|
|
1053
1105
|
var DEFAULT_TIMEOUT3 = 1e4;
|
|
1054
|
-
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1055
1106
|
function qs(params2) {
|
|
1056
1107
|
const sp = new URLSearchParams();
|
|
1057
1108
|
for (const [k, v] of Object.entries(params2)) {
|
|
@@ -1068,6 +1119,27 @@ function unwrap(json) {
|
|
|
1068
1119
|
}
|
|
1069
1120
|
return json;
|
|
1070
1121
|
}
|
|
1122
|
+
function createRequestCache() {
|
|
1123
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
1124
|
+
const settled = /* @__PURE__ */ new Map();
|
|
1125
|
+
const copy = (value) => typeof structuredClone === "function" ? structuredClone(value) : JSON.parse(JSON.stringify(value));
|
|
1126
|
+
return async function collapse(url, fetcher) {
|
|
1127
|
+
if (settled.has(url)) return copy(settled.get(url));
|
|
1128
|
+
const pending = inflight.get(url);
|
|
1129
|
+
if (pending) return copy(await pending);
|
|
1130
|
+
const run = (async () => {
|
|
1131
|
+
try {
|
|
1132
|
+
const value = await fetcher();
|
|
1133
|
+
settled.set(url, value);
|
|
1134
|
+
return value;
|
|
1135
|
+
} finally {
|
|
1136
|
+
inflight.delete(url);
|
|
1137
|
+
}
|
|
1138
|
+
})();
|
|
1139
|
+
inflight.set(url, run);
|
|
1140
|
+
return copy(await run);
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1071
1143
|
function createClient(options) {
|
|
1072
1144
|
const apiUrl = options.apiUrl.replace(/\/+$/, "");
|
|
1073
1145
|
const { workspace, apiKey, perspective = "published", previewToken } = options;
|
|
@@ -1079,9 +1151,11 @@ function createClient(options) {
|
|
|
1079
1151
|
if (apiKey) h["X-API-Key"] = apiKey;
|
|
1080
1152
|
return h;
|
|
1081
1153
|
};
|
|
1082
|
-
|
|
1154
|
+
const collapse = options.cache && perspective === "published" ? createRequestCache() : void 0;
|
|
1155
|
+
const fetchJSON = (url) => collapse ? collapse(url, () => request(url)) : request(url);
|
|
1156
|
+
async function request(url) {
|
|
1083
1157
|
let lastError;
|
|
1084
|
-
for (let attempt = 0; attempt <=
|
|
1158
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
1085
1159
|
const controller = new AbortController();
|
|
1086
1160
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
1087
1161
|
try {
|
|
@@ -1091,9 +1165,9 @@ function createClient(options) {
|
|
|
1091
1165
|
});
|
|
1092
1166
|
clearTimeout(timer);
|
|
1093
1167
|
if (res.ok) return await res.json();
|
|
1094
|
-
const retryable = attempt <
|
|
1168
|
+
const retryable = attempt < MAX_RETRIES && isRetryableStatus(res.status);
|
|
1095
1169
|
if (!retryable) throw await BetterCMSError.from(res);
|
|
1096
|
-
await
|
|
1170
|
+
await sleep(backoffMs(attempt, res));
|
|
1097
1171
|
} catch (err) {
|
|
1098
1172
|
clearTimeout(timer);
|
|
1099
1173
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -1101,8 +1175,8 @@ function createClient(options) {
|
|
|
1101
1175
|
}
|
|
1102
1176
|
if (err instanceof BetterCMSError) throw err;
|
|
1103
1177
|
lastError = err;
|
|
1104
|
-
if (attempt <
|
|
1105
|
-
await
|
|
1178
|
+
if (attempt < MAX_RETRIES) {
|
|
1179
|
+
await sleep(backoffMs(attempt));
|
|
1106
1180
|
continue;
|
|
1107
1181
|
}
|
|
1108
1182
|
throw new BetterCMSError(
|
|
@@ -1224,6 +1298,12 @@ function stripStega(value) {
|
|
|
1224
1298
|
}
|
|
1225
1299
|
|
|
1226
1300
|
// src/richtext.ts
|
|
1301
|
+
var PORTABLE_TEXT_FORMAT = "portable-text-1";
|
|
1302
|
+
function portableText(value) {
|
|
1303
|
+
if (!isRichText(value)) return null;
|
|
1304
|
+
const v = value;
|
|
1305
|
+
return v.format === PORTABLE_TEXT_FORMAT && Array.isArray(v.value) ? v.value : null;
|
|
1306
|
+
}
|
|
1227
1307
|
function isRichText(value) {
|
|
1228
1308
|
return typeof value === "object" && value !== null && !Array.isArray(value) && ("html" in value || "format" in value);
|
|
1229
1309
|
}
|
|
@@ -1611,6 +1691,7 @@ export {
|
|
|
1611
1691
|
BetterCMSError,
|
|
1612
1692
|
BetterCMSManagementClient,
|
|
1613
1693
|
ErrorCodes,
|
|
1694
|
+
PORTABLE_TEXT_FORMAT,
|
|
1614
1695
|
addModelFields,
|
|
1615
1696
|
addPageFields,
|
|
1616
1697
|
asStringArray,
|
|
@@ -1660,6 +1741,7 @@ export {
|
|
|
1660
1741
|
listSubmissions,
|
|
1661
1742
|
listWorkspaces,
|
|
1662
1743
|
plain,
|
|
1744
|
+
portableText,
|
|
1663
1745
|
publishPage,
|
|
1664
1746
|
regenerateApiKey,
|
|
1665
1747
|
removeMember,
|