@cmssy/core 9.0.0 → 9.1.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/content-client-D0EdiqbQ.d.cts +63 -0
- package/dist/content-client-D0EdiqbQ.d.ts +63 -0
- package/dist/index.d.cts +4 -62
- package/dist/index.d.ts +4 -62
- package/dist/preflight.cjs +273 -0
- package/dist/preflight.d.cts +23 -0
- package/dist/preflight.d.ts +23 -0
- package/dist/preflight.js +267 -0
- package/package.json +6 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CmssyClientConfig, CmssyLayoutGroup, CmssyPageData, CmssyPageMeta, CmssyPageSummary } from '@cmssy/types';
|
|
2
|
+
|
|
3
|
+
/** HTTP-level failure from the cmssy API, with a machine-readable status. */
|
|
4
|
+
declare class CmssyRequestError extends Error {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
constructor(message: string, status: number);
|
|
7
|
+
}
|
|
8
|
+
interface RetryPolicy {
|
|
9
|
+
/** Additional attempts after the first request (default 3). */
|
|
10
|
+
maxRetries?: number;
|
|
11
|
+
/** Exponential backoff base in ms: base * 2^attempt (default 300). */
|
|
12
|
+
baseDelayMs?: number;
|
|
13
|
+
/** Upper bound for any single wait, including Retry-After (default 3000). */
|
|
14
|
+
maxDelayMs?: number;
|
|
15
|
+
/** HTTP statuses that trigger a retry (default [429, 503]). */
|
|
16
|
+
retryStatuses?: number[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The cmssy cloud GraphQL delivery endpoint. It is the same for every workspace,
|
|
21
|
+
* so consumers never need to set it - `apiUrl` defaults to this. Self-hosted /
|
|
22
|
+
* staging deployments override it via config.
|
|
23
|
+
*/
|
|
24
|
+
declare const DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
25
|
+
declare function resolveApiUrl(apiUrl: string | undefined): string;
|
|
26
|
+
/**
|
|
27
|
+
* Public delivery endpoint for a workspace: the org-scoped path
|
|
28
|
+
* `{base}/public/{orgSlug}/{workspaceSlug}/graphql`. `apiUrl` is the GraphQL
|
|
29
|
+
* base (its trailing `/graphql` is stripped); the org path is what tells the
|
|
30
|
+
* backend which workspace to serve, so slugs only need to be unique per org.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolvePublicUrl(config: CmssyClientConfig): string;
|
|
33
|
+
interface FetchLikeResponse {
|
|
34
|
+
ok: boolean;
|
|
35
|
+
status: number;
|
|
36
|
+
json: () => Promise<unknown>;
|
|
37
|
+
headers?: {
|
|
38
|
+
get: (name: string) => string | null;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
type FetchLike = (url: string, init: {
|
|
42
|
+
method: string;
|
|
43
|
+
headers: Record<string, string>;
|
|
44
|
+
body: string;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
}) => Promise<FetchLikeResponse>;
|
|
47
|
+
interface FetchPageOptions {
|
|
48
|
+
previewSecret?: string;
|
|
49
|
+
devPreview?: boolean;
|
|
50
|
+
devToken?: string;
|
|
51
|
+
workspaceId?: string;
|
|
52
|
+
fetch?: FetchLike;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
retry?: RetryPolicy | false;
|
|
55
|
+
}
|
|
56
|
+
declare function normalizeSlug(path: string | string[] | undefined): string;
|
|
57
|
+
declare function fetchPage(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyPageData | null>;
|
|
58
|
+
declare function fetchPageById(config: CmssyClientConfig, pageId: string, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageData | null>;
|
|
59
|
+
declare function fetchPages(config: CmssyClientConfig, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageSummary[]>;
|
|
60
|
+
declare function fetchPageMeta(config: CmssyClientConfig, path: string | string[] | undefined, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageMeta | null>;
|
|
61
|
+
declare function fetchLayouts(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyLayoutGroup[]>;
|
|
62
|
+
|
|
63
|
+
export { CmssyRequestError as C, DEFAULT_CMSSY_API_URL as D, type FetchLike as F, type RetryPolicy as R, type FetchLikeResponse as a, type FetchPageOptions as b, fetchPage as c, fetchPageById as d, fetchPageMeta as e, fetchLayouts as f, fetchPages as g, resolvePublicUrl as h, normalizeSlug as n, resolveApiUrl as r };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CmssyClientConfig, CmssyLayoutGroup, CmssyPageData, CmssyPageMeta, CmssyPageSummary } from '@cmssy/types';
|
|
2
|
+
|
|
3
|
+
/** HTTP-level failure from the cmssy API, with a machine-readable status. */
|
|
4
|
+
declare class CmssyRequestError extends Error {
|
|
5
|
+
readonly status: number;
|
|
6
|
+
constructor(message: string, status: number);
|
|
7
|
+
}
|
|
8
|
+
interface RetryPolicy {
|
|
9
|
+
/** Additional attempts after the first request (default 3). */
|
|
10
|
+
maxRetries?: number;
|
|
11
|
+
/** Exponential backoff base in ms: base * 2^attempt (default 300). */
|
|
12
|
+
baseDelayMs?: number;
|
|
13
|
+
/** Upper bound for any single wait, including Retry-After (default 3000). */
|
|
14
|
+
maxDelayMs?: number;
|
|
15
|
+
/** HTTP statuses that trigger a retry (default [429, 503]). */
|
|
16
|
+
retryStatuses?: number[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The cmssy cloud GraphQL delivery endpoint. It is the same for every workspace,
|
|
21
|
+
* so consumers never need to set it - `apiUrl` defaults to this. Self-hosted /
|
|
22
|
+
* staging deployments override it via config.
|
|
23
|
+
*/
|
|
24
|
+
declare const DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
25
|
+
declare function resolveApiUrl(apiUrl: string | undefined): string;
|
|
26
|
+
/**
|
|
27
|
+
* Public delivery endpoint for a workspace: the org-scoped path
|
|
28
|
+
* `{base}/public/{orgSlug}/{workspaceSlug}/graphql`. `apiUrl` is the GraphQL
|
|
29
|
+
* base (its trailing `/graphql` is stripped); the org path is what tells the
|
|
30
|
+
* backend which workspace to serve, so slugs only need to be unique per org.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolvePublicUrl(config: CmssyClientConfig): string;
|
|
33
|
+
interface FetchLikeResponse {
|
|
34
|
+
ok: boolean;
|
|
35
|
+
status: number;
|
|
36
|
+
json: () => Promise<unknown>;
|
|
37
|
+
headers?: {
|
|
38
|
+
get: (name: string) => string | null;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
type FetchLike = (url: string, init: {
|
|
42
|
+
method: string;
|
|
43
|
+
headers: Record<string, string>;
|
|
44
|
+
body: string;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
}) => Promise<FetchLikeResponse>;
|
|
47
|
+
interface FetchPageOptions {
|
|
48
|
+
previewSecret?: string;
|
|
49
|
+
devPreview?: boolean;
|
|
50
|
+
devToken?: string;
|
|
51
|
+
workspaceId?: string;
|
|
52
|
+
fetch?: FetchLike;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
retry?: RetryPolicy | false;
|
|
55
|
+
}
|
|
56
|
+
declare function normalizeSlug(path: string | string[] | undefined): string;
|
|
57
|
+
declare function fetchPage(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyPageData | null>;
|
|
58
|
+
declare function fetchPageById(config: CmssyClientConfig, pageId: string, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageData | null>;
|
|
59
|
+
declare function fetchPages(config: CmssyClientConfig, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageSummary[]>;
|
|
60
|
+
declare function fetchPageMeta(config: CmssyClientConfig, path: string | string[] | undefined, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageMeta | null>;
|
|
61
|
+
declare function fetchLayouts(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyLayoutGroup[]>;
|
|
62
|
+
|
|
63
|
+
export { CmssyRequestError as C, DEFAULT_CMSSY_API_URL as D, type FetchLike as F, type RetryPolicy as R, type FetchLikeResponse as a, type FetchPageOptions as b, fetchPage as c, fetchPageById as d, fetchPageMeta as e, fetchLayouts as f, fetchPages as g, resolvePublicUrl as h, normalizeSlug as n, resolveApiUrl as r };
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as _cmssy_types from '@cmssy/types';
|
|
2
|
-
import { CmssyAuthConfig, CmssyClientConfig,
|
|
2
|
+
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
3
3
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyAddress, CmssyAuthConfig, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductPage, CmssyProductVariant, CmssyRecordList, CmssySessionPayload, CmssySessionUser, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyStockState, CmssyTaxSummaryLine, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, MyOrdersResult, RawBlock, RawLayoutBlock, SessionCookieOptions, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup } from '@cmssy/types';
|
|
4
|
+
import { F as FetchLike, R as RetryPolicy } from './content-client-D0EdiqbQ.cjs';
|
|
5
|
+
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, a as FetchLikeResponse, b as FetchPageOptions, f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-D0EdiqbQ.cjs';
|
|
4
6
|
|
|
5
7
|
declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
|
|
6
8
|
declare function isDevelopment(): boolean;
|
|
@@ -65,66 +67,6 @@ type CmssyEnvConfig = Omit<CmssyConfig, "org" | "workspaceSlug" | "draftSecret">
|
|
|
65
67
|
declare function defineCmssyConfig(config: CmssyEnvConfig): CmssyConfig;
|
|
66
68
|
declare function assertAuthConfig(config: CmssyConfig): CmssyAuthConfig;
|
|
67
69
|
|
|
68
|
-
/** HTTP-level failure from the cmssy API, with a machine-readable status. */
|
|
69
|
-
declare class CmssyRequestError extends Error {
|
|
70
|
-
readonly status: number;
|
|
71
|
-
constructor(message: string, status: number);
|
|
72
|
-
}
|
|
73
|
-
interface RetryPolicy {
|
|
74
|
-
/** Additional attempts after the first request (default 3). */
|
|
75
|
-
maxRetries?: number;
|
|
76
|
-
/** Exponential backoff base in ms: base * 2^attempt (default 300). */
|
|
77
|
-
baseDelayMs?: number;
|
|
78
|
-
/** Upper bound for any single wait, including Retry-After (default 3000). */
|
|
79
|
-
maxDelayMs?: number;
|
|
80
|
-
/** HTTP statuses that trigger a retry (default [429, 503]). */
|
|
81
|
-
retryStatuses?: number[];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* The cmssy cloud GraphQL delivery endpoint. It is the same for every workspace,
|
|
86
|
-
* so consumers never need to set it - `apiUrl` defaults to this. Self-hosted /
|
|
87
|
-
* staging deployments override it via config.
|
|
88
|
-
*/
|
|
89
|
-
declare const DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
90
|
-
declare function resolveApiUrl(apiUrl: string | undefined): string;
|
|
91
|
-
/**
|
|
92
|
-
* Public delivery endpoint for a workspace: the org-scoped path
|
|
93
|
-
* `{base}/public/{orgSlug}/{workspaceSlug}/graphql`. `apiUrl` is the GraphQL
|
|
94
|
-
* base (its trailing `/graphql` is stripped); the org path is what tells the
|
|
95
|
-
* backend which workspace to serve, so slugs only need to be unique per org.
|
|
96
|
-
*/
|
|
97
|
-
declare function resolvePublicUrl(config: CmssyClientConfig): string;
|
|
98
|
-
interface FetchLikeResponse {
|
|
99
|
-
ok: boolean;
|
|
100
|
-
status: number;
|
|
101
|
-
json: () => Promise<unknown>;
|
|
102
|
-
headers?: {
|
|
103
|
-
get: (name: string) => string | null;
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
type FetchLike = (url: string, init: {
|
|
107
|
-
method: string;
|
|
108
|
-
headers: Record<string, string>;
|
|
109
|
-
body: string;
|
|
110
|
-
signal?: AbortSignal;
|
|
111
|
-
}) => Promise<FetchLikeResponse>;
|
|
112
|
-
interface FetchPageOptions {
|
|
113
|
-
previewSecret?: string;
|
|
114
|
-
devPreview?: boolean;
|
|
115
|
-
devToken?: string;
|
|
116
|
-
workspaceId?: string;
|
|
117
|
-
fetch?: FetchLike;
|
|
118
|
-
signal?: AbortSignal;
|
|
119
|
-
retry?: RetryPolicy | false;
|
|
120
|
-
}
|
|
121
|
-
declare function normalizeSlug(path: string | string[] | undefined): string;
|
|
122
|
-
declare function fetchPage(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyPageData | null>;
|
|
123
|
-
declare function fetchPageById(config: CmssyClientConfig, pageId: string, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageData | null>;
|
|
124
|
-
declare function fetchPages(config: CmssyClientConfig, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageSummary[]>;
|
|
125
|
-
declare function fetchPageMeta(config: CmssyClientConfig, path: string | string[] | undefined, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageMeta | null>;
|
|
126
|
-
declare function fetchLayouts(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyLayoutGroup[]>;
|
|
127
|
-
|
|
128
70
|
declare function getBlockContentForLanguage(content: unknown, locale: string, defaultLocale?: string, availableLocales?: string[]): Record<string, unknown>;
|
|
129
71
|
/**
|
|
130
72
|
* Coerce a block bucket (`style` / `advanced`) to a plain object. These buckets
|
|
@@ -502,4 +444,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
502
444
|
*/
|
|
503
445
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
504
446
|
|
|
505
|
-
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig,
|
|
447
|
+
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, applyCmssyCsp, asBucket, assertAuthConfig, backendAddToCart, backendApplyDiscount, backendCheckout, backendClearCart, backendForgotPassword, backendGetCart, backendMergeCart, backendMyOrder, backendMyOrders, backendOrderByToken, backendProduct, backendRefresh, backendRegister, backendRemoveDiscount, backendRemoveItem, backendResetPassword, backendSetShippingMethod, backendSignIn, backendSignOut, backendSignOutEverywhere, backendUpdateItem, backendVerifyEmail, buildBlockContext, buildLocaleSwitchHref, cachedWorkspaceId, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, createCmssyClient, decodeAccessClaims, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as _cmssy_types from '@cmssy/types';
|
|
2
|
-
import { CmssyAuthConfig, CmssyClientConfig,
|
|
2
|
+
import { CmssyAuthConfig, CmssyClientConfig, CmssySiteConfig, RawBlock, CmssyFormDefinition, CmssySiteLocales, BuildBlockContextExtra, CmssyBlockContext, CmssyLocaleContext, FieldOptions, TypedField, BlockPropsSchema, InferBlockContent, BlockRect, BlockSchema, BlockMeta, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, CmssyAddress, CmssyCart, CmssyOrder, CmssyProduct, MyOrdersResult, FetchProductOptions, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
|
|
3
3
|
export { BlockMeta, BlockPropsSchema, BlockRect, BlockSchema, BuildBlockContextExtra, CmssyAddress, CmssyAuthConfig, CmssyBlockAuthContext, CmssyBlockContext, CmssyBlockMember, CmssyBlockWorkspace, CmssyBranding, CmssyCart, CmssyCartDiscount, CmssyCartItem, CmssyCartItemSnapshot, CmssyClientConfig, CmssyFormDefinition, CmssyFormField, CmssyFormSettings, CmssyFormSubmitResponse, CmssyLayoutGroup, CmssyLayoutSettings, CmssyLocaleContext, CmssyLocalizedValue, CmssyModelDefinition, CmssyModelRecord, CmssyOrder, CmssyOrderDiscount, CmssyOrderItem, CmssyOrderTaxSummaryLine, CmssyPageData, CmssyPageMeta, CmssyPageSummary, CmssyPriceTier, CmssyProduct, CmssyProductPage, CmssyProductVariant, CmssyRecordList, CmssySessionPayload, CmssySessionUser, CmssyShippingMethod, CmssySiteConfig, CmssySiteLocales, CmssyStockState, CmssyTaxSummaryLine, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, FieldCondition, FieldConditionGroup, FieldConditionLogic, FieldControl, FieldDefinition, FieldOptions, FieldType, InferBlockContent, MyOrdersResult, RawBlock, RawLayoutBlock, SessionCookieOptions, SubmitFormInput, TypedField, VerifyCmssyWebhookOptions, evaluateFieldConditionGroup } from '@cmssy/types';
|
|
4
|
+
import { F as FetchLike, R as RetryPolicy } from './content-client-D0EdiqbQ.js';
|
|
5
|
+
export { C as CmssyRequestError, D as DEFAULT_CMSSY_API_URL, a as FetchLikeResponse, b as FetchPageOptions, f as fetchLayouts, c as fetchPage, d as fetchPageById, e as fetchPageMeta, g as fetchPages, n as normalizeSlug, r as resolveApiUrl, h as resolvePublicUrl } from './content-client-D0EdiqbQ.js';
|
|
4
6
|
|
|
5
7
|
declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
|
|
6
8
|
declare function isDevelopment(): boolean;
|
|
@@ -65,66 +67,6 @@ type CmssyEnvConfig = Omit<CmssyConfig, "org" | "workspaceSlug" | "draftSecret">
|
|
|
65
67
|
declare function defineCmssyConfig(config: CmssyEnvConfig): CmssyConfig;
|
|
66
68
|
declare function assertAuthConfig(config: CmssyConfig): CmssyAuthConfig;
|
|
67
69
|
|
|
68
|
-
/** HTTP-level failure from the cmssy API, with a machine-readable status. */
|
|
69
|
-
declare class CmssyRequestError extends Error {
|
|
70
|
-
readonly status: number;
|
|
71
|
-
constructor(message: string, status: number);
|
|
72
|
-
}
|
|
73
|
-
interface RetryPolicy {
|
|
74
|
-
/** Additional attempts after the first request (default 3). */
|
|
75
|
-
maxRetries?: number;
|
|
76
|
-
/** Exponential backoff base in ms: base * 2^attempt (default 300). */
|
|
77
|
-
baseDelayMs?: number;
|
|
78
|
-
/** Upper bound for any single wait, including Retry-After (default 3000). */
|
|
79
|
-
maxDelayMs?: number;
|
|
80
|
-
/** HTTP statuses that trigger a retry (default [429, 503]). */
|
|
81
|
-
retryStatuses?: number[];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* The cmssy cloud GraphQL delivery endpoint. It is the same for every workspace,
|
|
86
|
-
* so consumers never need to set it - `apiUrl` defaults to this. Self-hosted /
|
|
87
|
-
* staging deployments override it via config.
|
|
88
|
-
*/
|
|
89
|
-
declare const DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
90
|
-
declare function resolveApiUrl(apiUrl: string | undefined): string;
|
|
91
|
-
/**
|
|
92
|
-
* Public delivery endpoint for a workspace: the org-scoped path
|
|
93
|
-
* `{base}/public/{orgSlug}/{workspaceSlug}/graphql`. `apiUrl` is the GraphQL
|
|
94
|
-
* base (its trailing `/graphql` is stripped); the org path is what tells the
|
|
95
|
-
* backend which workspace to serve, so slugs only need to be unique per org.
|
|
96
|
-
*/
|
|
97
|
-
declare function resolvePublicUrl(config: CmssyClientConfig): string;
|
|
98
|
-
interface FetchLikeResponse {
|
|
99
|
-
ok: boolean;
|
|
100
|
-
status: number;
|
|
101
|
-
json: () => Promise<unknown>;
|
|
102
|
-
headers?: {
|
|
103
|
-
get: (name: string) => string | null;
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
type FetchLike = (url: string, init: {
|
|
107
|
-
method: string;
|
|
108
|
-
headers: Record<string, string>;
|
|
109
|
-
body: string;
|
|
110
|
-
signal?: AbortSignal;
|
|
111
|
-
}) => Promise<FetchLikeResponse>;
|
|
112
|
-
interface FetchPageOptions {
|
|
113
|
-
previewSecret?: string;
|
|
114
|
-
devPreview?: boolean;
|
|
115
|
-
devToken?: string;
|
|
116
|
-
workspaceId?: string;
|
|
117
|
-
fetch?: FetchLike;
|
|
118
|
-
signal?: AbortSignal;
|
|
119
|
-
retry?: RetryPolicy | false;
|
|
120
|
-
}
|
|
121
|
-
declare function normalizeSlug(path: string | string[] | undefined): string;
|
|
122
|
-
declare function fetchPage(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyPageData | null>;
|
|
123
|
-
declare function fetchPageById(config: CmssyClientConfig, pageId: string, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageData | null>;
|
|
124
|
-
declare function fetchPages(config: CmssyClientConfig, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageSummary[]>;
|
|
125
|
-
declare function fetchPageMeta(config: CmssyClientConfig, path: string | string[] | undefined, options?: Pick<FetchPageOptions, "fetch" | "signal" | "retry">): Promise<CmssyPageMeta | null>;
|
|
126
|
-
declare function fetchLayouts(config: CmssyClientConfig, path: string | string[] | undefined, options?: FetchPageOptions): Promise<CmssyLayoutGroup[]>;
|
|
127
|
-
|
|
128
70
|
declare function getBlockContentForLanguage(content: unknown, locale: string, defaultLocale?: string, availableLocales?: string[]): Record<string, unknown>;
|
|
129
71
|
/**
|
|
130
72
|
* Coerce a block bucket (`style` / `advanced`) to a plain object. These buckets
|
|
@@ -502,4 +444,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
502
444
|
*/
|
|
503
445
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): Promise<CmssyWebhookEvent>;
|
|
504
446
|
|
|
505
|
-
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig,
|
|
447
|
+
export { type AppToEditorMessage, type AuthMutationResult, type AuthTokenResult, type BoundsMessage, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CartRequestContext, type CheckoutInput, type ClickMessage, type CmssyClient, type CmssyConfig, type CmssyCspOptions, type CmssyEnvConfig, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, type EditorToAppMessage, FORM_QUERY, FetchLike, type GraphqlRequestOptions, MIN_SESSION_SECRET_LENGTH, MODEL_DEFINITIONS_QUERY, MODEL_RECORDS_QUERY, PRODUCTS_QUERY, PROTOCOL_VERSION, type ParentReadyMessage, type PatchMessage, type PostTarget, type QueryScopedOptions, type ReadyMessage, RetryPolicy, SESSION_MAX_AGE_SECONDS, SITE_CONFIG_QUERY, SUBMIT_FORM_MUTATION, type SelectMessage, applyCmssyCsp, asBucket, assertAuthConfig, backendAddToCart, backendApplyDiscount, backendCheckout, backendClearCart, backendForgotPassword, backendGetCart, backendMergeCart, backendMyOrder, backendMyOrders, backendOrderByToken, backendProduct, backendRefresh, backendRegister, backendRemoveDiscount, backendRemoveItem, backendResetPassword, backendSetShippingMethod, backendSignIn, backendSignOut, backendSignOutEverywhere, backendUpdateItem, backendVerifyEmail, buildBlockContext, buildLocaleSwitchHref, cachedWorkspaceId, clearWorkspaceIdCache, cmssyCspHeaders, cmssySecretsMatch, collectFormIds, createCmssyClient, decodeAccessClaims, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, fetchSiteConfig, fields, formatPrice, fractionDigits, fromMinorUnits, getBlockContentForLanguage, graphqlRequest, isAccessExpired, isDevelopment, isProtocolCompatible, isVerifiedEditUrl, localeForPath, localeForPathname, localesFromSiteConfig, localizeHref, localizeHtmlLinks, localizedPath, normalizeOrigin, openSession, parseEditorMessage, postToEditor, resolveEditorOrigin, resolveForms, resolveInitialTarget, resolveSiteLocales, resolveWorkspaceId, sealSession, sessionCookieOptions, splitCmssyLocale, splitLocaleFromPath, toCspOrigin, toMinorUnits, toSessionPayload, verifyCmssyWebhook };
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/content/content-client.ts
|
|
4
|
+
var DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
5
|
+
function resolveApiUrl(apiUrl) {
|
|
6
|
+
const explicit = apiUrl?.trim();
|
|
7
|
+
if (explicit) return explicit;
|
|
8
|
+
const env = globalThis.process?.env;
|
|
9
|
+
const fromEnv = env?.CMSSY_API_URL?.trim() ?? "";
|
|
10
|
+
return fromEnv.length > 0 ? fromEnv : DEFAULT_CMSSY_API_URL;
|
|
11
|
+
}
|
|
12
|
+
function resolvePublicUrl(config) {
|
|
13
|
+
const base = resolveApiUrl(config.apiUrl).replace(/\/graphql\/?$/, "");
|
|
14
|
+
return `${base}/public/${config.org}/${config.workspaceSlug}/graphql`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/preflight.ts
|
|
18
|
+
var CMSSY_ADMIN_ORIGIN = "https://www.cmssy.io";
|
|
19
|
+
var ALLOWED_FRAME_HOSTS = ["cmssy.io", "www.cmssy.io"];
|
|
20
|
+
var SETTINGS_PATH = "Settings \u2192 Headless";
|
|
21
|
+
var PREFLIGHT_SITE_CONFIG_QUERY = `query PreflightSiteConfig($workspaceSlug: String!) {
|
|
22
|
+
public {
|
|
23
|
+
siteConfig(workspaceSlug: $workspaceSlug) {
|
|
24
|
+
previewUrl
|
|
25
|
+
publicSiteUrl
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}`;
|
|
29
|
+
var DRAFT_SECRET_VALID_QUERY = `query PreflightDraftSecretValid($workspaceSlug: String!, $secret: String!) {
|
|
30
|
+
public {
|
|
31
|
+
draftSecretValid(workspaceSlug: $workspaceSlug, secret: $secret)
|
|
32
|
+
}
|
|
33
|
+
}`;
|
|
34
|
+
async function postPreflightQuery(config, query, variables) {
|
|
35
|
+
const doFetch = config.fetch ?? globalThis.fetch;
|
|
36
|
+
if (typeof doFetch !== "function") {
|
|
37
|
+
return {
|
|
38
|
+
kind: "network",
|
|
39
|
+
error: new Error("cmssy: no fetch implementation available")
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const url = resolvePublicUrl(config);
|
|
43
|
+
let response;
|
|
44
|
+
try {
|
|
45
|
+
response = await doFetch(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: { "content-type": "application/json" },
|
|
48
|
+
body: JSON.stringify({ query, variables })
|
|
49
|
+
});
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return { kind: "network", error };
|
|
52
|
+
}
|
|
53
|
+
let envelope = null;
|
|
54
|
+
try {
|
|
55
|
+
envelope = await response.json();
|
|
56
|
+
} catch {
|
|
57
|
+
envelope = null;
|
|
58
|
+
}
|
|
59
|
+
const errors = Array.isArray(envelope?.errors) ? envelope.errors : [];
|
|
60
|
+
if (errors.length > 0) {
|
|
61
|
+
return { kind: "errors", errors, httpStatus: response.status };
|
|
62
|
+
}
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
return { kind: "http", status: response.status };
|
|
65
|
+
}
|
|
66
|
+
return { kind: "data", data: envelope?.data };
|
|
67
|
+
}
|
|
68
|
+
function errorMessages(errors) {
|
|
69
|
+
return errors.map((error) => error.message ?? "GraphQL error").join("; ");
|
|
70
|
+
}
|
|
71
|
+
function hasErrorCode(errors, code) {
|
|
72
|
+
return errors.some((error) => error.extensions?.code === code);
|
|
73
|
+
}
|
|
74
|
+
function isValidationError(errors) {
|
|
75
|
+
return errors.some(
|
|
76
|
+
(error) => error.extensions?.code === "GRAPHQL_VALIDATION_FAILED" || /cannot query field/i.test(error.message ?? "")
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function isNotFound(errors, httpStatus) {
|
|
80
|
+
return httpStatus === 404 || hasErrorCode(errors, "NOT_FOUND") || errors.some((error) => /not found/i.test(error.message ?? ""));
|
|
81
|
+
}
|
|
82
|
+
async function checkWorkspaceReachable(config) {
|
|
83
|
+
const workspace = `${config.org}/${config.workspaceSlug}`;
|
|
84
|
+
const outcome = await postPreflightQuery(config, PREFLIGHT_SITE_CONFIG_QUERY, {
|
|
85
|
+
workspaceSlug: config.workspaceSlug
|
|
86
|
+
});
|
|
87
|
+
if (outcome.kind === "network") {
|
|
88
|
+
return {
|
|
89
|
+
status: "fail",
|
|
90
|
+
message: `cannot reach the cmssy API at ${resolvePublicUrl(config)}`,
|
|
91
|
+
fix: "check your network connection and CMSSY_API_URL (leave it unset for cmssy cloud)"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (outcome.kind === "http" && outcome.status === 429 || outcome.kind === "errors" && (outcome.httpStatus === 429 || hasErrorCode(outcome.errors, "TOO_MANY_REQUESTS"))) {
|
|
95
|
+
return {
|
|
96
|
+
status: "fail",
|
|
97
|
+
message: `workspace ${workspace} is over its delivery limit (rate limited)`,
|
|
98
|
+
fix: "upgrade the organization plan or wait for the usage window to reset"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (outcome.kind === "errors") {
|
|
102
|
+
if (isNotFound(outcome.errors, outcome.httpStatus)) {
|
|
103
|
+
return {
|
|
104
|
+
status: "fail",
|
|
105
|
+
message: `workspace ${workspace} was not found`,
|
|
106
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
status: "fail",
|
|
111
|
+
message: `the cmssy API rejected the request - ${errorMessages(outcome.errors)}`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (outcome.kind === "http") {
|
|
115
|
+
if (outcome.status === 404) {
|
|
116
|
+
return {
|
|
117
|
+
status: "fail",
|
|
118
|
+
message: `workspace ${workspace} was not found`,
|
|
119
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
status: "fail",
|
|
124
|
+
message: `the cmssy API responded with HTTP ${outcome.status}`
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const siteConfig = outcome.data?.public?.siteConfig;
|
|
128
|
+
if (!siteConfig) {
|
|
129
|
+
return {
|
|
130
|
+
status: "fail",
|
|
131
|
+
message: `workspace ${workspace} was not found`,
|
|
132
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const previewUrl = siteConfig.previewUrl?.trim();
|
|
136
|
+
return {
|
|
137
|
+
status: "ok",
|
|
138
|
+
message: `workspace ${workspace} is reachable`,
|
|
139
|
+
...previewUrl ? { previewUrl } : {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function checkDraftSecret(config) {
|
|
143
|
+
const secret = config.draftSecret?.trim();
|
|
144
|
+
if (!secret) {
|
|
145
|
+
return {
|
|
146
|
+
status: "fail",
|
|
147
|
+
message: "CMSSY_DRAFT_SECRET is not set",
|
|
148
|
+
fix: `copy the draft secret from ${SETTINGS_PATH}`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const outcome = await postPreflightQuery(config, DRAFT_SECRET_VALID_QUERY, {
|
|
152
|
+
workspaceSlug: config.workspaceSlug,
|
|
153
|
+
secret
|
|
154
|
+
});
|
|
155
|
+
if (outcome.kind === "network") {
|
|
156
|
+
return {
|
|
157
|
+
status: "unknown",
|
|
158
|
+
message: "could not verify the draft secret (network error)"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (outcome.kind === "errors") {
|
|
162
|
+
if (isValidationError(outcome.errors)) {
|
|
163
|
+
return {
|
|
164
|
+
status: "unknown",
|
|
165
|
+
message: "this cmssy platform does not support draft secret verification yet"
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
status: "unknown",
|
|
170
|
+
message: `could not verify the draft secret - ${errorMessages(outcome.errors)}`
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (outcome.kind === "http") {
|
|
174
|
+
return {
|
|
175
|
+
status: "unknown",
|
|
176
|
+
message: `could not verify the draft secret (HTTP ${outcome.status})`
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (outcome.data?.public?.draftSecretValid === true) {
|
|
180
|
+
return { status: "ok", message: "the draft secret is valid" };
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
status: "fail",
|
|
184
|
+
message: "the draft secret does not match this workspace",
|
|
185
|
+
fix: `copy the secret from ${SETTINGS_PATH} into CMSSY_DRAFT_SECRET`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function parseOrigin(value) {
|
|
189
|
+
try {
|
|
190
|
+
const origin = new URL(value).origin;
|
|
191
|
+
return origin === "null" ? null : origin;
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function checkPreviewUrl(previewUrl, devOrigin) {
|
|
197
|
+
const trimmed = previewUrl?.trim();
|
|
198
|
+
if (!trimmed) {
|
|
199
|
+
return {
|
|
200
|
+
status: "fail",
|
|
201
|
+
message: "no preview URL is set for this workspace",
|
|
202
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const previewOrigin = parseOrigin(trimmed);
|
|
206
|
+
if (!previewOrigin) {
|
|
207
|
+
return {
|
|
208
|
+
status: "fail",
|
|
209
|
+
message: `the workspace preview URL "${trimmed}" is not a valid URL`,
|
|
210
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const localOrigin = parseOrigin(devOrigin);
|
|
214
|
+
if (previewOrigin === localOrigin) {
|
|
215
|
+
return {
|
|
216
|
+
status: "ok",
|
|
217
|
+
message: `the workspace preview URL matches ${devOrigin}`
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
status: "fail",
|
|
222
|
+
message: `the workspace preview URL is ${previewOrigin} but your dev server runs at ${devOrigin}`,
|
|
223
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function frameAncestorSources(cspHeaderValue) {
|
|
227
|
+
const directive = cspHeaderValue.split(";").map((part) => part.trim()).find((part) => /^frame-ancestors\b/i.test(part));
|
|
228
|
+
if (!directive) return null;
|
|
229
|
+
return directive.split(/\s+/).slice(1).map((source) => source.toLowerCase().replace(/^'|'$/g, ""));
|
|
230
|
+
}
|
|
231
|
+
function allowsCmssyAdmin(source) {
|
|
232
|
+
if (source === "*" || source === "https:") return true;
|
|
233
|
+
const host = source.replace(/^[a-z][a-z0-9+.-]*:\/\//, "").split("/")[0];
|
|
234
|
+
if (!host) return false;
|
|
235
|
+
if (host === "*.cmssy.io") return true;
|
|
236
|
+
return ALLOWED_FRAME_HOSTS.includes(host);
|
|
237
|
+
}
|
|
238
|
+
function checkFrameAncestors(cspHeaderValue) {
|
|
239
|
+
if (!cspHeaderValue || cspHeaderValue.trim().length === 0) {
|
|
240
|
+
return {
|
|
241
|
+
status: "ok",
|
|
242
|
+
message: "no Content-Security-Policy restricts framing"
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const sources = frameAncestorSources(cspHeaderValue);
|
|
246
|
+
if (sources === null) {
|
|
247
|
+
return {
|
|
248
|
+
status: "ok",
|
|
249
|
+
message: "the Content-Security-Policy has no frame-ancestors directive"
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (sources.some(allowsCmssyAdmin)) {
|
|
253
|
+
return {
|
|
254
|
+
status: "ok",
|
|
255
|
+
message: "frame-ancestors allows the cmssy editor"
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
status: "fail",
|
|
260
|
+
message: `frame-ancestors (${sources.join(" ") || "'none'"}) blocks the cmssy editor`,
|
|
261
|
+
fix: "add https://cmssy.io https://www.cmssy.io to the frame-ancestors directive (applyCmssyCsp does this for you)"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function buildEditorUrl(config, pageId) {
|
|
265
|
+
const base = `${CMSSY_ADMIN_ORIGIN}/dashboard/organizations/${config.org}/workspaces/${config.workspaceSlug}/editor`;
|
|
266
|
+
return pageId ? `${base}?pageId=${encodeURIComponent(pageId)}` : base;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
exports.buildEditorUrl = buildEditorUrl;
|
|
270
|
+
exports.checkDraftSecret = checkDraftSecret;
|
|
271
|
+
exports.checkFrameAncestors = checkFrameAncestors;
|
|
272
|
+
exports.checkPreviewUrl = checkPreviewUrl;
|
|
273
|
+
exports.checkWorkspaceReachable = checkWorkspaceReachable;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { F as FetchLike } from './content-client-D0EdiqbQ.cjs';
|
|
2
|
+
import { CmssyClientConfig } from '@cmssy/types';
|
|
3
|
+
|
|
4
|
+
type PreflightStatus = "ok" | "fail" | "unknown";
|
|
5
|
+
interface PreflightResult {
|
|
6
|
+
status: PreflightStatus;
|
|
7
|
+
message: string;
|
|
8
|
+
fix?: string;
|
|
9
|
+
}
|
|
10
|
+
interface WorkspaceReachableResult extends PreflightResult {
|
|
11
|
+
previewUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
interface PreflightConfig extends CmssyClientConfig {
|
|
14
|
+
draftSecret?: string;
|
|
15
|
+
fetch?: FetchLike;
|
|
16
|
+
}
|
|
17
|
+
declare function checkWorkspaceReachable(config: PreflightConfig): Promise<WorkspaceReachableResult>;
|
|
18
|
+
declare function checkDraftSecret(config: PreflightConfig): Promise<PreflightResult>;
|
|
19
|
+
declare function checkPreviewUrl(previewUrl: string | null | undefined, devOrigin: string): PreflightResult;
|
|
20
|
+
declare function checkFrameAncestors(cspHeaderValue: string | null | undefined): PreflightResult;
|
|
21
|
+
declare function buildEditorUrl(config: Pick<CmssyClientConfig, "org" | "workspaceSlug">, pageId?: string): string;
|
|
22
|
+
|
|
23
|
+
export { type PreflightConfig, type PreflightResult, type PreflightStatus, type WorkspaceReachableResult, buildEditorUrl, checkDraftSecret, checkFrameAncestors, checkPreviewUrl, checkWorkspaceReachable };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { F as FetchLike } from './content-client-D0EdiqbQ.js';
|
|
2
|
+
import { CmssyClientConfig } from '@cmssy/types';
|
|
3
|
+
|
|
4
|
+
type PreflightStatus = "ok" | "fail" | "unknown";
|
|
5
|
+
interface PreflightResult {
|
|
6
|
+
status: PreflightStatus;
|
|
7
|
+
message: string;
|
|
8
|
+
fix?: string;
|
|
9
|
+
}
|
|
10
|
+
interface WorkspaceReachableResult extends PreflightResult {
|
|
11
|
+
previewUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
interface PreflightConfig extends CmssyClientConfig {
|
|
14
|
+
draftSecret?: string;
|
|
15
|
+
fetch?: FetchLike;
|
|
16
|
+
}
|
|
17
|
+
declare function checkWorkspaceReachable(config: PreflightConfig): Promise<WorkspaceReachableResult>;
|
|
18
|
+
declare function checkDraftSecret(config: PreflightConfig): Promise<PreflightResult>;
|
|
19
|
+
declare function checkPreviewUrl(previewUrl: string | null | undefined, devOrigin: string): PreflightResult;
|
|
20
|
+
declare function checkFrameAncestors(cspHeaderValue: string | null | undefined): PreflightResult;
|
|
21
|
+
declare function buildEditorUrl(config: Pick<CmssyClientConfig, "org" | "workspaceSlug">, pageId?: string): string;
|
|
22
|
+
|
|
23
|
+
export { type PreflightConfig, type PreflightResult, type PreflightStatus, type WorkspaceReachableResult, buildEditorUrl, checkDraftSecret, checkFrameAncestors, checkPreviewUrl, checkWorkspaceReachable };
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// src/content/content-client.ts
|
|
2
|
+
var DEFAULT_CMSSY_API_URL = "https://api.cmssy.io/graphql";
|
|
3
|
+
function resolveApiUrl(apiUrl) {
|
|
4
|
+
const explicit = apiUrl?.trim();
|
|
5
|
+
if (explicit) return explicit;
|
|
6
|
+
const env = globalThis.process?.env;
|
|
7
|
+
const fromEnv = env?.CMSSY_API_URL?.trim() ?? "";
|
|
8
|
+
return fromEnv.length > 0 ? fromEnv : DEFAULT_CMSSY_API_URL;
|
|
9
|
+
}
|
|
10
|
+
function resolvePublicUrl(config) {
|
|
11
|
+
const base = resolveApiUrl(config.apiUrl).replace(/\/graphql\/?$/, "");
|
|
12
|
+
return `${base}/public/${config.org}/${config.workspaceSlug}/graphql`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/preflight.ts
|
|
16
|
+
var CMSSY_ADMIN_ORIGIN = "https://www.cmssy.io";
|
|
17
|
+
var ALLOWED_FRAME_HOSTS = ["cmssy.io", "www.cmssy.io"];
|
|
18
|
+
var SETTINGS_PATH = "Settings \u2192 Headless";
|
|
19
|
+
var PREFLIGHT_SITE_CONFIG_QUERY = `query PreflightSiteConfig($workspaceSlug: String!) {
|
|
20
|
+
public {
|
|
21
|
+
siteConfig(workspaceSlug: $workspaceSlug) {
|
|
22
|
+
previewUrl
|
|
23
|
+
publicSiteUrl
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}`;
|
|
27
|
+
var DRAFT_SECRET_VALID_QUERY = `query PreflightDraftSecretValid($workspaceSlug: String!, $secret: String!) {
|
|
28
|
+
public {
|
|
29
|
+
draftSecretValid(workspaceSlug: $workspaceSlug, secret: $secret)
|
|
30
|
+
}
|
|
31
|
+
}`;
|
|
32
|
+
async function postPreflightQuery(config, query, variables) {
|
|
33
|
+
const doFetch = config.fetch ?? globalThis.fetch;
|
|
34
|
+
if (typeof doFetch !== "function") {
|
|
35
|
+
return {
|
|
36
|
+
kind: "network",
|
|
37
|
+
error: new Error("cmssy: no fetch implementation available")
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const url = resolvePublicUrl(config);
|
|
41
|
+
let response;
|
|
42
|
+
try {
|
|
43
|
+
response = await doFetch(url, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "content-type": "application/json" },
|
|
46
|
+
body: JSON.stringify({ query, variables })
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return { kind: "network", error };
|
|
50
|
+
}
|
|
51
|
+
let envelope = null;
|
|
52
|
+
try {
|
|
53
|
+
envelope = await response.json();
|
|
54
|
+
} catch {
|
|
55
|
+
envelope = null;
|
|
56
|
+
}
|
|
57
|
+
const errors = Array.isArray(envelope?.errors) ? envelope.errors : [];
|
|
58
|
+
if (errors.length > 0) {
|
|
59
|
+
return { kind: "errors", errors, httpStatus: response.status };
|
|
60
|
+
}
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
return { kind: "http", status: response.status };
|
|
63
|
+
}
|
|
64
|
+
return { kind: "data", data: envelope?.data };
|
|
65
|
+
}
|
|
66
|
+
function errorMessages(errors) {
|
|
67
|
+
return errors.map((error) => error.message ?? "GraphQL error").join("; ");
|
|
68
|
+
}
|
|
69
|
+
function hasErrorCode(errors, code) {
|
|
70
|
+
return errors.some((error) => error.extensions?.code === code);
|
|
71
|
+
}
|
|
72
|
+
function isValidationError(errors) {
|
|
73
|
+
return errors.some(
|
|
74
|
+
(error) => error.extensions?.code === "GRAPHQL_VALIDATION_FAILED" || /cannot query field/i.test(error.message ?? "")
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
function isNotFound(errors, httpStatus) {
|
|
78
|
+
return httpStatus === 404 || hasErrorCode(errors, "NOT_FOUND") || errors.some((error) => /not found/i.test(error.message ?? ""));
|
|
79
|
+
}
|
|
80
|
+
async function checkWorkspaceReachable(config) {
|
|
81
|
+
const workspace = `${config.org}/${config.workspaceSlug}`;
|
|
82
|
+
const outcome = await postPreflightQuery(config, PREFLIGHT_SITE_CONFIG_QUERY, {
|
|
83
|
+
workspaceSlug: config.workspaceSlug
|
|
84
|
+
});
|
|
85
|
+
if (outcome.kind === "network") {
|
|
86
|
+
return {
|
|
87
|
+
status: "fail",
|
|
88
|
+
message: `cannot reach the cmssy API at ${resolvePublicUrl(config)}`,
|
|
89
|
+
fix: "check your network connection and CMSSY_API_URL (leave it unset for cmssy cloud)"
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (outcome.kind === "http" && outcome.status === 429 || outcome.kind === "errors" && (outcome.httpStatus === 429 || hasErrorCode(outcome.errors, "TOO_MANY_REQUESTS"))) {
|
|
93
|
+
return {
|
|
94
|
+
status: "fail",
|
|
95
|
+
message: `workspace ${workspace} is over its delivery limit (rate limited)`,
|
|
96
|
+
fix: "upgrade the organization plan or wait for the usage window to reset"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (outcome.kind === "errors") {
|
|
100
|
+
if (isNotFound(outcome.errors, outcome.httpStatus)) {
|
|
101
|
+
return {
|
|
102
|
+
status: "fail",
|
|
103
|
+
message: `workspace ${workspace} was not found`,
|
|
104
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
status: "fail",
|
|
109
|
+
message: `the cmssy API rejected the request - ${errorMessages(outcome.errors)}`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (outcome.kind === "http") {
|
|
113
|
+
if (outcome.status === 404) {
|
|
114
|
+
return {
|
|
115
|
+
status: "fail",
|
|
116
|
+
message: `workspace ${workspace} was not found`,
|
|
117
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status: "fail",
|
|
122
|
+
message: `the cmssy API responded with HTTP ${outcome.status}`
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const siteConfig = outcome.data?.public?.siteConfig;
|
|
126
|
+
if (!siteConfig) {
|
|
127
|
+
return {
|
|
128
|
+
status: "fail",
|
|
129
|
+
message: `workspace ${workspace} was not found`,
|
|
130
|
+
fix: "check CMSSY_ORG_SLUG and CMSSY_WORKSPACE_SLUG against your dashboard URL"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const previewUrl = siteConfig.previewUrl?.trim();
|
|
134
|
+
return {
|
|
135
|
+
status: "ok",
|
|
136
|
+
message: `workspace ${workspace} is reachable`,
|
|
137
|
+
...previewUrl ? { previewUrl } : {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function checkDraftSecret(config) {
|
|
141
|
+
const secret = config.draftSecret?.trim();
|
|
142
|
+
if (!secret) {
|
|
143
|
+
return {
|
|
144
|
+
status: "fail",
|
|
145
|
+
message: "CMSSY_DRAFT_SECRET is not set",
|
|
146
|
+
fix: `copy the draft secret from ${SETTINGS_PATH}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const outcome = await postPreflightQuery(config, DRAFT_SECRET_VALID_QUERY, {
|
|
150
|
+
workspaceSlug: config.workspaceSlug,
|
|
151
|
+
secret
|
|
152
|
+
});
|
|
153
|
+
if (outcome.kind === "network") {
|
|
154
|
+
return {
|
|
155
|
+
status: "unknown",
|
|
156
|
+
message: "could not verify the draft secret (network error)"
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (outcome.kind === "errors") {
|
|
160
|
+
if (isValidationError(outcome.errors)) {
|
|
161
|
+
return {
|
|
162
|
+
status: "unknown",
|
|
163
|
+
message: "this cmssy platform does not support draft secret verification yet"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
status: "unknown",
|
|
168
|
+
message: `could not verify the draft secret - ${errorMessages(outcome.errors)}`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (outcome.kind === "http") {
|
|
172
|
+
return {
|
|
173
|
+
status: "unknown",
|
|
174
|
+
message: `could not verify the draft secret (HTTP ${outcome.status})`
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (outcome.data?.public?.draftSecretValid === true) {
|
|
178
|
+
return { status: "ok", message: "the draft secret is valid" };
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
status: "fail",
|
|
182
|
+
message: "the draft secret does not match this workspace",
|
|
183
|
+
fix: `copy the secret from ${SETTINGS_PATH} into CMSSY_DRAFT_SECRET`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function parseOrigin(value) {
|
|
187
|
+
try {
|
|
188
|
+
const origin = new URL(value).origin;
|
|
189
|
+
return origin === "null" ? null : origin;
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function checkPreviewUrl(previewUrl, devOrigin) {
|
|
195
|
+
const trimmed = previewUrl?.trim();
|
|
196
|
+
if (!trimmed) {
|
|
197
|
+
return {
|
|
198
|
+
status: "fail",
|
|
199
|
+
message: "no preview URL is set for this workspace",
|
|
200
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const previewOrigin = parseOrigin(trimmed);
|
|
204
|
+
if (!previewOrigin) {
|
|
205
|
+
return {
|
|
206
|
+
status: "fail",
|
|
207
|
+
message: `the workspace preview URL "${trimmed}" is not a valid URL`,
|
|
208
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const localOrigin = parseOrigin(devOrigin);
|
|
212
|
+
if (previewOrigin === localOrigin) {
|
|
213
|
+
return {
|
|
214
|
+
status: "ok",
|
|
215
|
+
message: `the workspace preview URL matches ${devOrigin}`
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
status: "fail",
|
|
220
|
+
message: `the workspace preview URL is ${previewOrigin} but your dev server runs at ${devOrigin}`,
|
|
221
|
+
fix: `paste ${devOrigin} into the preview URL field in ${SETTINGS_PATH}`
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function frameAncestorSources(cspHeaderValue) {
|
|
225
|
+
const directive = cspHeaderValue.split(";").map((part) => part.trim()).find((part) => /^frame-ancestors\b/i.test(part));
|
|
226
|
+
if (!directive) return null;
|
|
227
|
+
return directive.split(/\s+/).slice(1).map((source) => source.toLowerCase().replace(/^'|'$/g, ""));
|
|
228
|
+
}
|
|
229
|
+
function allowsCmssyAdmin(source) {
|
|
230
|
+
if (source === "*" || source === "https:") return true;
|
|
231
|
+
const host = source.replace(/^[a-z][a-z0-9+.-]*:\/\//, "").split("/")[0];
|
|
232
|
+
if (!host) return false;
|
|
233
|
+
if (host === "*.cmssy.io") return true;
|
|
234
|
+
return ALLOWED_FRAME_HOSTS.includes(host);
|
|
235
|
+
}
|
|
236
|
+
function checkFrameAncestors(cspHeaderValue) {
|
|
237
|
+
if (!cspHeaderValue || cspHeaderValue.trim().length === 0) {
|
|
238
|
+
return {
|
|
239
|
+
status: "ok",
|
|
240
|
+
message: "no Content-Security-Policy restricts framing"
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const sources = frameAncestorSources(cspHeaderValue);
|
|
244
|
+
if (sources === null) {
|
|
245
|
+
return {
|
|
246
|
+
status: "ok",
|
|
247
|
+
message: "the Content-Security-Policy has no frame-ancestors directive"
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (sources.some(allowsCmssyAdmin)) {
|
|
251
|
+
return {
|
|
252
|
+
status: "ok",
|
|
253
|
+
message: "frame-ancestors allows the cmssy editor"
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
status: "fail",
|
|
258
|
+
message: `frame-ancestors (${sources.join(" ") || "'none'"}) blocks the cmssy editor`,
|
|
259
|
+
fix: "add https://cmssy.io https://www.cmssy.io to the frame-ancestors directive (applyCmssyCsp does this for you)"
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function buildEditorUrl(config, pageId) {
|
|
263
|
+
const base = `${CMSSY_ADMIN_ORIGIN}/dashboard/organizations/${config.org}/workspaces/${config.workspaceSlug}/editor`;
|
|
264
|
+
return pageId ? `${base}?pageId=${encodeURIComponent(pageId)}` : base;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export { buildEditorUrl, checkDraftSecret, checkFrameAncestors, checkPreviewUrl, checkWorkspaceReachable };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/core",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.1.1",
|
|
4
4
|
"description": "Framework-agnostic cmssy client: content, commerce, config, editor protocol. No React, no Next.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"types": "./dist/testing.d.ts",
|
|
32
32
|
"import": "./dist/testing.js",
|
|
33
33
|
"require": "./dist/testing.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./preflight": {
|
|
36
|
+
"types": "./dist/preflight.d.ts",
|
|
37
|
+
"import": "./dist/preflight.js",
|
|
38
|
+
"require": "./dist/preflight.cjs"
|
|
34
39
|
}
|
|
35
40
|
},
|
|
36
41
|
"main": "./dist/index.cjs",
|