@cmssy/next 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,12 +4,117 @@ var headers = require('next/headers');
4
4
  var navigation = require('next/navigation');
5
5
  var react = require('@cmssy/react');
6
6
  var client = require('@cmssy/react/client');
7
+ var jose = require('jose');
7
8
  var jsxRuntime = require('react/jsx-runtime');
8
9
  var crypto$1 = require('crypto');
9
10
  var server = require('next/server');
10
- var jose = require('jose');
11
11
 
12
12
  // src/create-cmssy-page.tsx
13
+ var CMSSY_SESSION_COOKIE = "cmssy_session";
14
+ var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
15
+ var MIN_SESSION_SECRET_LENGTH = 32;
16
+ var ACCESS_EXPIRY_SKEW_MS = 3e4;
17
+ async function deriveSessionKey(secret) {
18
+ if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
19
+ throw new Error(
20
+ `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
21
+ );
22
+ }
23
+ const digest = await crypto.subtle.digest(
24
+ "SHA-256",
25
+ new TextEncoder().encode(secret)
26
+ );
27
+ return new Uint8Array(digest);
28
+ }
29
+ async function sealSession(payload, secret, audience) {
30
+ const key = await deriveSessionKey(secret);
31
+ const jwt = new jose.EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
32
+ if (audience) jwt.setAudience(audience);
33
+ return jwt.encrypt(key);
34
+ }
35
+ async function openSession(token, secret, audience) {
36
+ const key = await deriveSessionKey(secret);
37
+ try {
38
+ const { payload } = await jose.jwtDecrypt(token, key, {
39
+ keyManagementAlgorithms: ["dir"],
40
+ contentEncryptionAlgorithms: ["A256GCM"],
41
+ ...audience ? { audience } : {}
42
+ });
43
+ const { accessToken, refreshToken, accessExpiresAt, user } = payload;
44
+ if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
45
+ return null;
46
+ }
47
+ return {
48
+ accessToken,
49
+ refreshToken,
50
+ accessExpiresAt,
51
+ user: {
52
+ recordId: user.recordId,
53
+ email: user.email
54
+ }
55
+ };
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+ function isAccessExpired(payload, now = Date.now()) {
61
+ return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
62
+ }
63
+ function sessionCookieOptions() {
64
+ return {
65
+ httpOnly: true,
66
+ secure: process.env.NODE_ENV !== "development",
67
+ sameSite: "lax",
68
+ path: "/",
69
+ maxAge: SESSION_MAX_AGE_SECONDS
70
+ };
71
+ }
72
+
73
+ // src/config.ts
74
+ var DEFAULT_CMSSY_EDITOR_ORIGIN = "https://www.cmssy.io";
75
+ function resolveEditorOrigin(editorOrigin) {
76
+ if (editorOrigin === void 0) return DEFAULT_CMSSY_EDITOR_ORIGIN;
77
+ if (Array.isArray(editorOrigin)) {
78
+ const cleaned = editorOrigin.filter((o) => o && o.trim().length > 0);
79
+ return cleaned.length > 0 ? cleaned : DEFAULT_CMSSY_EDITOR_ORIGIN;
80
+ }
81
+ return editorOrigin.trim().length > 0 ? editorOrigin : DEFAULT_CMSSY_EDITOR_ORIGIN;
82
+ }
83
+ function assertAuthConfig(config) {
84
+ const auth = config.auth;
85
+ if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
86
+ throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
87
+ }
88
+ if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
89
+ throw new Error(
90
+ `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
91
+ );
92
+ }
93
+ return auth;
94
+ }
95
+
96
+ // src/auth-server.ts
97
+ async function readValidSession(config) {
98
+ const auth = assertAuthConfig(config);
99
+ const jar = await headers.cookies();
100
+ const raw = jar.get(CMSSY_SESSION_COOKIE)?.value;
101
+ if (!raw) return null;
102
+ const session = await openSession(
103
+ raw,
104
+ auth.sessionSecret,
105
+ config.workspaceSlug
106
+ );
107
+ if (!session || isAccessExpired(session)) return null;
108
+ return session;
109
+ }
110
+ async function getCmssyUser(config) {
111
+ const session = await readValidSession(config);
112
+ return session?.user ?? null;
113
+ }
114
+ async function getCmssyAccessToken(config) {
115
+ const session = await readValidSession(config);
116
+ return session?.accessToken ?? null;
117
+ }
13
118
 
14
119
  // src/csp.ts
15
120
  function toCspOrigin(origin) {
@@ -26,7 +131,8 @@ function toCspOrigin(origin) {
26
131
  return parsed.origin;
27
132
  }
28
133
  function frameAncestors(editorOrigin) {
29
- const origins = Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin];
134
+ const resolved = resolveEditorOrigin(editorOrigin);
135
+ const origins = Array.isArray(resolved) ? resolved : [resolved];
30
136
  if (origins.length === 0) {
31
137
  throw new Error(
32
138
  "cmssy: editorOrigin must contain at least one valid origin"
@@ -70,6 +176,7 @@ function createCmssyPage(config, blocks, options) {
70
176
  apiUrl: config.apiUrl,
71
177
  workspaceSlug: config.workspaceSlug
72
178
  };
179
+ const client$1 = react.createCmssyClient(clientConfig);
73
180
  return async function CmssyCatchAllPage({
74
181
  params,
75
182
  searchParams
@@ -131,6 +238,27 @@ function createCmssyPage(config, blocks, options) {
131
238
  }
132
239
  ) });
133
240
  }
241
+ let auth;
242
+ if (config.auth) {
243
+ try {
244
+ const user = await getCmssyUser(config);
245
+ auth = {
246
+ isAuthenticated: !!user,
247
+ member: user ? { recordId: user.recordId, email: user.email } : null
248
+ };
249
+ } catch {
250
+ auth = void 0;
251
+ }
252
+ }
253
+ let workspace;
254
+ try {
255
+ workspace = {
256
+ id: await client$1.resolveWorkspaceId(),
257
+ slug: config.workspaceSlug
258
+ };
259
+ } catch {
260
+ workspace = void 0;
261
+ }
134
262
  return /* @__PURE__ */ jsxRuntime.jsx(client.CmssyLocaleProvider, { value: localeContext, children: /* @__PURE__ */ jsxRuntime.jsx(
135
263
  react.CmssyServerPage,
136
264
  {
@@ -139,13 +267,16 @@ function createCmssyPage(config, blocks, options) {
139
267
  locale,
140
268
  defaultLocale,
141
269
  enabledLocales,
142
- forms
270
+ forms,
271
+ auth,
272
+ workspace
143
273
  }
144
274
  ) });
145
275
  };
146
276
  }
147
277
  function resolveBridgeOrigin(editorOrigin) {
148
- const origins = Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin];
278
+ const resolved = resolveEditorOrigin(editorOrigin);
279
+ const origins = Array.isArray(resolved) ? resolved : [resolved];
149
280
  if (origins.length === 0) {
150
281
  throw new Error("cmssy: editorOrigin must be set to frame the editor");
151
282
  }
@@ -157,7 +288,7 @@ function resolveBridgeOrigin(editorOrigin) {
157
288
  const origin = toCspOrigin(origins[0].trim());
158
289
  if (origin === "*") {
159
290
  throw new Error(
160
- "cmssy: editorOrigin '*' is not allowed for the live-edit bridge; set the concrete editor origin (e.g. https://app.cmssy.io)"
291
+ "cmssy: editorOrigin '*' is not allowed for the live-edit bridge; set the concrete editor origin (e.g. https://www.cmssy.io)"
161
292
  );
162
293
  }
163
294
  return origin;
@@ -509,79 +640,6 @@ function createCmssyLocaleMiddleware(config) {
509
640
  return server.NextResponse.next({ request: { headers: headers3 } });
510
641
  };
511
642
  }
512
- var CMSSY_SESSION_COOKIE = "cmssy_session";
513
- var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
514
- var MIN_SESSION_SECRET_LENGTH = 32;
515
- var ACCESS_EXPIRY_SKEW_MS = 3e4;
516
- async function deriveSessionKey(secret) {
517
- if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
518
- throw new Error(
519
- `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
520
- );
521
- }
522
- const digest = await crypto.subtle.digest(
523
- "SHA-256",
524
- new TextEncoder().encode(secret)
525
- );
526
- return new Uint8Array(digest);
527
- }
528
- async function sealSession(payload, secret, audience) {
529
- const key = await deriveSessionKey(secret);
530
- const jwt = new jose.EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
531
- if (audience) jwt.setAudience(audience);
532
- return jwt.encrypt(key);
533
- }
534
- async function openSession(token, secret, audience) {
535
- const key = await deriveSessionKey(secret);
536
- try {
537
- const { payload } = await jose.jwtDecrypt(token, key, {
538
- keyManagementAlgorithms: ["dir"],
539
- contentEncryptionAlgorithms: ["A256GCM"],
540
- ...audience ? { audience } : {}
541
- });
542
- const { accessToken, refreshToken, accessExpiresAt, user } = payload;
543
- if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
544
- return null;
545
- }
546
- return {
547
- accessToken,
548
- refreshToken,
549
- accessExpiresAt,
550
- user: {
551
- recordId: user.recordId,
552
- email: user.email
553
- }
554
- };
555
- } catch {
556
- return null;
557
- }
558
- }
559
- function isAccessExpired(payload, now = Date.now()) {
560
- return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
561
- }
562
- function sessionCookieOptions() {
563
- return {
564
- httpOnly: true,
565
- secure: process.env.NODE_ENV !== "development",
566
- sameSite: "lax",
567
- path: "/",
568
- maxAge: SESSION_MAX_AGE_SECONDS
569
- };
570
- }
571
-
572
- // src/config.ts
573
- function assertAuthConfig(config) {
574
- const auth = config.auth;
575
- if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
576
- throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
577
- }
578
- if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
579
- throw new Error(
580
- `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
581
- );
582
- }
583
- return auth;
584
- }
585
643
  var LOGIN_MUTATION = `mutation SiteMemberLogin($input: SiteMemberLoginInput!) {
586
644
  siteMemberLogin(input: $input) {
587
645
  success message accessToken refreshToken accessTokenExpiresIn
@@ -612,7 +670,7 @@ var VERIFY_MUTATION = `mutation SiteMemberVerifyEmail($token: String!) {
612
670
  }`;
613
671
  var workspaceIdCache = /* @__PURE__ */ new Map();
614
672
  function workspaceIdFor(config) {
615
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
673
+ const key = `${react.resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
616
674
  const existing = workspaceIdCache.get(key);
617
675
  if (existing) return existing;
618
676
  const fresh = react.resolveWorkspaceId(config).catch((err) => {
@@ -999,7 +1057,7 @@ var PRODUCT = `query Product($workspaceId: String!, $modelSlug: String!, $filter
999
1057
  }`;
1000
1058
  var workspaceIdCache2 = /* @__PURE__ */ new Map();
1001
1059
  function workspaceIdFor2(config) {
1002
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
1060
+ const key = `${react.resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
1003
1061
  const existing = workspaceIdCache2.get(key);
1004
1062
  if (existing) return existing;
1005
1063
  const fresh = react.resolveWorkspaceId(config).catch((err) => {
@@ -1289,27 +1347,6 @@ function createCmssyCartRoute(config) {
1289
1347
  }
1290
1348
  };
1291
1349
  }
1292
- async function readValidSession(config) {
1293
- const auth = assertAuthConfig(config);
1294
- const jar = await headers.cookies();
1295
- const raw = jar.get(CMSSY_SESSION_COOKIE)?.value;
1296
- if (!raw) return null;
1297
- const session = await openSession(
1298
- raw,
1299
- auth.sessionSecret,
1300
- config.workspaceSlug
1301
- );
1302
- if (!session || isAccessExpired(session)) return null;
1303
- return session;
1304
- }
1305
- async function getCmssyUser(config) {
1306
- const session = await readValidSession(config);
1307
- return session?.user ?? null;
1308
- }
1309
- async function getCmssyAccessToken(config) {
1310
- const session = await readValidSession(config);
1311
- return session?.accessToken ?? null;
1312
- }
1313
1350
  function isPrefetch(request2) {
1314
1351
  return request2.headers.get("next-router-prefetch") !== null || request2.headers.get("purpose") === "prefetch" || (request2.headers.get("sec-purpose") ?? "").includes("prefetch");
1315
1352
  }
@@ -1428,7 +1465,7 @@ var MY_ORDER = `query MyOrder($workspaceId: ID!, $id: ID!) {
1428
1465
  }`;
1429
1466
  var workspaceIdCache3 = /* @__PURE__ */ new Map();
1430
1467
  function workspaceIdFor3(config) {
1431
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
1468
+ const key = `${react.resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
1432
1469
  const existing = workspaceIdCache3.get(key);
1433
1470
  if (existing) return existing;
1434
1471
  const fresh = react.resolveWorkspaceId(config).catch((err) => {
@@ -1581,11 +1618,20 @@ function verifyCmssyWebhook(options) {
1581
1618
  return parsed;
1582
1619
  }
1583
1620
 
1621
+ Object.defineProperty(exports, "DEFAULT_CMSSY_API_URL", {
1622
+ enumerable: true,
1623
+ get: function () { return react.DEFAULT_CMSSY_API_URL; }
1624
+ });
1625
+ Object.defineProperty(exports, "resolveApiUrl", {
1626
+ enumerable: true,
1627
+ get: function () { return react.resolveApiUrl; }
1628
+ });
1584
1629
  exports.CMSSY_CART_COOKIE = CMSSY_CART_COOKIE;
1585
1630
  exports.CMSSY_EDIT_HEADER = CMSSY_EDIT_HEADER;
1586
1631
  exports.CMSSY_LOCALE_HEADER = CMSSY_LOCALE_HEADER;
1587
1632
  exports.CMSSY_SESSION_COOKIE = CMSSY_SESSION_COOKIE;
1588
1633
  exports.CmssyWebhookError = CmssyWebhookError;
1634
+ exports.DEFAULT_CMSSY_EDITOR_ORIGIN = DEFAULT_CMSSY_EDITOR_ORIGIN;
1589
1635
  exports.SESSION_MAX_AGE_SECONDS = SESSION_MAX_AGE_SECONDS;
1590
1636
  exports.applyCmssyCsp = applyCmssyCsp;
1591
1637
  exports.assertAuthConfig = assertAuthConfig;
@@ -1611,6 +1657,7 @@ exports.isCmssyEditMode = isCmssyEditMode;
1611
1657
  exports.isCmssyEditRequest = isCmssyEditRequest;
1612
1658
  exports.localeForPathname = localeForPathname;
1613
1659
  exports.openSession = openSession;
1660
+ exports.resolveEditorOrigin = resolveEditorOrigin;
1614
1661
  exports.resolveLocaleFromPathname = resolveLocaleFromPathname;
1615
1662
  exports.sealSession = sealSession;
1616
1663
  exports.sessionCookieOptions = sessionCookieOptions;
package/dist/index.d.cts CHANGED
@@ -1,19 +1,36 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import { CmssyPageData, CmssyFormDefinition, BlockDefinition, CmssyClientConfig, CmssyProduct, CmssyOrder } from '@cmssy/react';
4
+ export { DEFAULT_CMSSY_API_URL, resolveApiUrl } from '@cmssy/react';
4
5
  import { EditBridgeConfig } from '@cmssy/react/client';
5
6
  import { MetadataRoute, Metadata } from 'next';
6
7
  import { NextRequest, NextResponse } from 'next/server';
7
8
 
9
+ /**
10
+ * Origin of the cmssy admin/editor that frames your site (postMessage source +
11
+ * CSP `frame-ancestors`). Identical for every workspace on cmssy cloud, so
12
+ * `editorOrigin` defaults to this. Self-hosted admins override it via config.
13
+ */
14
+ declare const DEFAULT_CMSSY_EDITOR_ORIGIN = "https://www.cmssy.io";
15
+ /** Resolves `editorOrigin`, falling back to the cmssy cloud admin when unset. */
16
+ declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
8
17
  interface CmssyAuthConfig {
9
18
  modelSlug: string;
10
19
  sessionSecret: string;
11
20
  }
12
21
  interface CmssyNextConfig {
13
- apiUrl: string;
22
+ /**
23
+ * Full GraphQL delivery endpoint. Defaults to the cmssy cloud endpoint
24
+ * (`https://api.cmssy.io/graphql`); set it only for self-hosted / staging.
25
+ */
26
+ apiUrl?: string;
14
27
  workspaceSlug: string;
15
28
  draftSecret: string;
16
- editorOrigin: string | string[];
29
+ /**
30
+ * Origin allowed to frame your app in the editor. Defaults to
31
+ * {@link DEFAULT_CMSSY_EDITOR_ORIGIN}; set it only for self-hosted admins.
32
+ */
33
+ editorOrigin?: string | string[];
17
34
  /**
18
35
  * Canonical absolute site URL (e.g. https://cmssy.com), used by
19
36
  * createCmssyRobots / createCmssySitemap. When omitted the helpers derive the
@@ -141,7 +158,8 @@ type CmssyDraftRouteConfig = Pick<CmssyNextConfig, "draftSecret"> & {
141
158
  declare function createDraftRoute(config: CmssyDraftRouteConfig): (request: Request) => Promise<Response>;
142
159
 
143
160
  interface CmssyCspOptions {
144
- editorOrigin: string | string[];
161
+ /** Defaults to the cmssy cloud admin origin when unset. */
162
+ editorOrigin?: string | string[];
145
163
  }
146
164
  interface MutableHeaders {
147
165
  headers: {
@@ -328,4 +346,4 @@ declare class CmssyWebhookError extends Error {
328
346
  */
329
347
  declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
330
348
 
331
- export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthConfig, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyNextConfig, type CmssyOrdersRouteHandlers, type CmssySessionPayload, type CmssySessionUser, CmssyWebhookError, type CmssyWebhookEvent, type CmssyWebhookOrder, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, type FetchProductOptions, type FetchProductsOptions, type MyOrdersResult, SESSION_MAX_AGE_SECONDS, type SessionCookieOptions, type VerifyCmssyWebhookOptions, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
349
+ export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthConfig, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyNextConfig, type CmssyOrdersRouteHandlers, type CmssySessionPayload, type CmssySessionUser, CmssyWebhookError, type CmssyWebhookEvent, type CmssyWebhookOrder, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGIN, type FetchProductOptions, type FetchProductsOptions, type MyOrdersResult, SESSION_MAX_AGE_SECONDS, type SessionCookieOptions, type VerifyCmssyWebhookOptions, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/dist/index.d.ts CHANGED
@@ -1,19 +1,36 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import { CmssyPageData, CmssyFormDefinition, BlockDefinition, CmssyClientConfig, CmssyProduct, CmssyOrder } from '@cmssy/react';
4
+ export { DEFAULT_CMSSY_API_URL, resolveApiUrl } from '@cmssy/react';
4
5
  import { EditBridgeConfig } from '@cmssy/react/client';
5
6
  import { MetadataRoute, Metadata } from 'next';
6
7
  import { NextRequest, NextResponse } from 'next/server';
7
8
 
9
+ /**
10
+ * Origin of the cmssy admin/editor that frames your site (postMessage source +
11
+ * CSP `frame-ancestors`). Identical for every workspace on cmssy cloud, so
12
+ * `editorOrigin` defaults to this. Self-hosted admins override it via config.
13
+ */
14
+ declare const DEFAULT_CMSSY_EDITOR_ORIGIN = "https://www.cmssy.io";
15
+ /** Resolves `editorOrigin`, falling back to the cmssy cloud admin when unset. */
16
+ declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
8
17
  interface CmssyAuthConfig {
9
18
  modelSlug: string;
10
19
  sessionSecret: string;
11
20
  }
12
21
  interface CmssyNextConfig {
13
- apiUrl: string;
22
+ /**
23
+ * Full GraphQL delivery endpoint. Defaults to the cmssy cloud endpoint
24
+ * (`https://api.cmssy.io/graphql`); set it only for self-hosted / staging.
25
+ */
26
+ apiUrl?: string;
14
27
  workspaceSlug: string;
15
28
  draftSecret: string;
16
- editorOrigin: string | string[];
29
+ /**
30
+ * Origin allowed to frame your app in the editor. Defaults to
31
+ * {@link DEFAULT_CMSSY_EDITOR_ORIGIN}; set it only for self-hosted admins.
32
+ */
33
+ editorOrigin?: string | string[];
17
34
  /**
18
35
  * Canonical absolute site URL (e.g. https://cmssy.com), used by
19
36
  * createCmssyRobots / createCmssySitemap. When omitted the helpers derive the
@@ -141,7 +158,8 @@ type CmssyDraftRouteConfig = Pick<CmssyNextConfig, "draftSecret"> & {
141
158
  declare function createDraftRoute(config: CmssyDraftRouteConfig): (request: Request) => Promise<Response>;
142
159
 
143
160
  interface CmssyCspOptions {
144
- editorOrigin: string | string[];
161
+ /** Defaults to the cmssy cloud admin origin when unset. */
162
+ editorOrigin?: string | string[];
145
163
  }
146
164
  interface MutableHeaders {
147
165
  headers: {
@@ -328,4 +346,4 @@ declare class CmssyWebhookError extends Error {
328
346
  */
329
347
  declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
330
348
 
331
- export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthConfig, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyNextConfig, type CmssyOrdersRouteHandlers, type CmssySessionPayload, type CmssySessionUser, CmssyWebhookError, type CmssyWebhookEvent, type CmssyWebhookOrder, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, type FetchProductOptions, type FetchProductsOptions, type MyOrdersResult, SESSION_MAX_AGE_SECONDS, type SessionCookieOptions, type VerifyCmssyWebhookOptions, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
349
+ export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthConfig, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyNextConfig, type CmssyOrdersRouteHandlers, type CmssySessionPayload, type CmssySessionUser, CmssyWebhookError, type CmssyWebhookEvent, type CmssyWebhookOrder, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGIN, type FetchProductOptions, type FetchProductsOptions, type MyOrdersResult, SESSION_MAX_AGE_SECONDS, type SessionCookieOptions, type VerifyCmssyWebhookOptions, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/dist/index.js CHANGED
@@ -1,13 +1,119 @@
1
1
  import { draftMode, headers, cookies } from 'next/headers';
2
2
  import { notFound, redirect } from 'next/navigation';
3
- import { resolveSiteLocales, splitLocaleFromPath, fetchPage, resolveForms, CmssyServerPage, fetchSiteConfig, fetchPageById, fetchPages, fetchPageMeta, normalizeSlug as normalizeSlug$1, resolveWorkspaceId, graphqlRequest } from '@cmssy/react';
3
+ import { createCmssyClient, resolveSiteLocales, splitLocaleFromPath, fetchPage, resolveForms, CmssyServerPage, fetchSiteConfig, fetchPageById, fetchPages, fetchPageMeta, normalizeSlug as normalizeSlug$1, resolveWorkspaceId, graphqlRequest, resolveApiUrl } from '@cmssy/react';
4
+ export { DEFAULT_CMSSY_API_URL, resolveApiUrl } from '@cmssy/react';
4
5
  import { CmssyLocaleProvider } from '@cmssy/react/client';
6
+ import { EncryptJWT, jwtDecrypt } from 'jose';
5
7
  import { jsx, jsxs } from 'react/jsx-runtime';
6
8
  import { createHmac, createHash, timingSafeEqual } from 'crypto';
7
9
  import { NextResponse } from 'next/server';
8
- import { EncryptJWT, jwtDecrypt } from 'jose';
9
10
 
10
11
  // src/create-cmssy-page.tsx
12
+ var CMSSY_SESSION_COOKIE = "cmssy_session";
13
+ var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
14
+ var MIN_SESSION_SECRET_LENGTH = 32;
15
+ var ACCESS_EXPIRY_SKEW_MS = 3e4;
16
+ async function deriveSessionKey(secret) {
17
+ if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
18
+ throw new Error(
19
+ `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
20
+ );
21
+ }
22
+ const digest = await crypto.subtle.digest(
23
+ "SHA-256",
24
+ new TextEncoder().encode(secret)
25
+ );
26
+ return new Uint8Array(digest);
27
+ }
28
+ async function sealSession(payload, secret, audience) {
29
+ const key = await deriveSessionKey(secret);
30
+ const jwt = new EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
31
+ if (audience) jwt.setAudience(audience);
32
+ return jwt.encrypt(key);
33
+ }
34
+ async function openSession(token, secret, audience) {
35
+ const key = await deriveSessionKey(secret);
36
+ try {
37
+ const { payload } = await jwtDecrypt(token, key, {
38
+ keyManagementAlgorithms: ["dir"],
39
+ contentEncryptionAlgorithms: ["A256GCM"],
40
+ ...audience ? { audience } : {}
41
+ });
42
+ const { accessToken, refreshToken, accessExpiresAt, user } = payload;
43
+ if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
44
+ return null;
45
+ }
46
+ return {
47
+ accessToken,
48
+ refreshToken,
49
+ accessExpiresAt,
50
+ user: {
51
+ recordId: user.recordId,
52
+ email: user.email
53
+ }
54
+ };
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function isAccessExpired(payload, now = Date.now()) {
60
+ return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
61
+ }
62
+ function sessionCookieOptions() {
63
+ return {
64
+ httpOnly: true,
65
+ secure: process.env.NODE_ENV !== "development",
66
+ sameSite: "lax",
67
+ path: "/",
68
+ maxAge: SESSION_MAX_AGE_SECONDS
69
+ };
70
+ }
71
+
72
+ // src/config.ts
73
+ var DEFAULT_CMSSY_EDITOR_ORIGIN = "https://www.cmssy.io";
74
+ function resolveEditorOrigin(editorOrigin) {
75
+ if (editorOrigin === void 0) return DEFAULT_CMSSY_EDITOR_ORIGIN;
76
+ if (Array.isArray(editorOrigin)) {
77
+ const cleaned = editorOrigin.filter((o) => o && o.trim().length > 0);
78
+ return cleaned.length > 0 ? cleaned : DEFAULT_CMSSY_EDITOR_ORIGIN;
79
+ }
80
+ return editorOrigin.trim().length > 0 ? editorOrigin : DEFAULT_CMSSY_EDITOR_ORIGIN;
81
+ }
82
+ function assertAuthConfig(config) {
83
+ const auth = config.auth;
84
+ if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
85
+ throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
86
+ }
87
+ if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
88
+ throw new Error(
89
+ `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
90
+ );
91
+ }
92
+ return auth;
93
+ }
94
+
95
+ // src/auth-server.ts
96
+ async function readValidSession(config) {
97
+ const auth = assertAuthConfig(config);
98
+ const jar = await cookies();
99
+ const raw = jar.get(CMSSY_SESSION_COOKIE)?.value;
100
+ if (!raw) return null;
101
+ const session = await openSession(
102
+ raw,
103
+ auth.sessionSecret,
104
+ config.workspaceSlug
105
+ );
106
+ if (!session || isAccessExpired(session)) return null;
107
+ return session;
108
+ }
109
+ async function getCmssyUser(config) {
110
+ const session = await readValidSession(config);
111
+ return session?.user ?? null;
112
+ }
113
+ async function getCmssyAccessToken(config) {
114
+ const session = await readValidSession(config);
115
+ return session?.accessToken ?? null;
116
+ }
11
117
 
12
118
  // src/csp.ts
13
119
  function toCspOrigin(origin) {
@@ -24,7 +130,8 @@ function toCspOrigin(origin) {
24
130
  return parsed.origin;
25
131
  }
26
132
  function frameAncestors(editorOrigin) {
27
- const origins = Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin];
133
+ const resolved = resolveEditorOrigin(editorOrigin);
134
+ const origins = Array.isArray(resolved) ? resolved : [resolved];
28
135
  if (origins.length === 0) {
29
136
  throw new Error(
30
137
  "cmssy: editorOrigin must contain at least one valid origin"
@@ -68,6 +175,7 @@ function createCmssyPage(config, blocks, options) {
68
175
  apiUrl: config.apiUrl,
69
176
  workspaceSlug: config.workspaceSlug
70
177
  };
178
+ const client = createCmssyClient(clientConfig);
71
179
  return async function CmssyCatchAllPage({
72
180
  params,
73
181
  searchParams
@@ -129,6 +237,27 @@ function createCmssyPage(config, blocks, options) {
129
237
  }
130
238
  ) });
131
239
  }
240
+ let auth;
241
+ if (config.auth) {
242
+ try {
243
+ const user = await getCmssyUser(config);
244
+ auth = {
245
+ isAuthenticated: !!user,
246
+ member: user ? { recordId: user.recordId, email: user.email } : null
247
+ };
248
+ } catch {
249
+ auth = void 0;
250
+ }
251
+ }
252
+ let workspace;
253
+ try {
254
+ workspace = {
255
+ id: await client.resolveWorkspaceId(),
256
+ slug: config.workspaceSlug
257
+ };
258
+ } catch {
259
+ workspace = void 0;
260
+ }
132
261
  return /* @__PURE__ */ jsx(CmssyLocaleProvider, { value: localeContext, children: /* @__PURE__ */ jsx(
133
262
  CmssyServerPage,
134
263
  {
@@ -137,13 +266,16 @@ function createCmssyPage(config, blocks, options) {
137
266
  locale,
138
267
  defaultLocale,
139
268
  enabledLocales,
140
- forms
269
+ forms,
270
+ auth,
271
+ workspace
141
272
  }
142
273
  ) });
143
274
  };
144
275
  }
145
276
  function resolveBridgeOrigin(editorOrigin) {
146
- const origins = Array.isArray(editorOrigin) ? editorOrigin : [editorOrigin];
277
+ const resolved = resolveEditorOrigin(editorOrigin);
278
+ const origins = Array.isArray(resolved) ? resolved : [resolved];
147
279
  if (origins.length === 0) {
148
280
  throw new Error("cmssy: editorOrigin must be set to frame the editor");
149
281
  }
@@ -155,7 +287,7 @@ function resolveBridgeOrigin(editorOrigin) {
155
287
  const origin = toCspOrigin(origins[0].trim());
156
288
  if (origin === "*") {
157
289
  throw new Error(
158
- "cmssy: editorOrigin '*' is not allowed for the live-edit bridge; set the concrete editor origin (e.g. https://app.cmssy.io)"
290
+ "cmssy: editorOrigin '*' is not allowed for the live-edit bridge; set the concrete editor origin (e.g. https://www.cmssy.io)"
159
291
  );
160
292
  }
161
293
  return origin;
@@ -507,79 +639,6 @@ function createCmssyLocaleMiddleware(config) {
507
639
  return NextResponse.next({ request: { headers: headers3 } });
508
640
  };
509
641
  }
510
- var CMSSY_SESSION_COOKIE = "cmssy_session";
511
- var SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
512
- var MIN_SESSION_SECRET_LENGTH = 32;
513
- var ACCESS_EXPIRY_SKEW_MS = 3e4;
514
- async function deriveSessionKey(secret) {
515
- if (typeof secret !== "string" || secret.length < MIN_SESSION_SECRET_LENGTH) {
516
- throw new Error(
517
- `cmssy auth sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
518
- );
519
- }
520
- const digest = await crypto.subtle.digest(
521
- "SHA-256",
522
- new TextEncoder().encode(secret)
523
- );
524
- return new Uint8Array(digest);
525
- }
526
- async function sealSession(payload, secret, audience) {
527
- const key = await deriveSessionKey(secret);
528
- const jwt = new EncryptJWT({ ...payload }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime(`${SESSION_MAX_AGE_SECONDS}s`);
529
- if (audience) jwt.setAudience(audience);
530
- return jwt.encrypt(key);
531
- }
532
- async function openSession(token, secret, audience) {
533
- const key = await deriveSessionKey(secret);
534
- try {
535
- const { payload } = await jwtDecrypt(token, key, {
536
- keyManagementAlgorithms: ["dir"],
537
- contentEncryptionAlgorithms: ["A256GCM"],
538
- ...audience ? { audience } : {}
539
- });
540
- const { accessToken, refreshToken, accessExpiresAt, user } = payload;
541
- if (typeof accessToken !== "string" || typeof refreshToken !== "string" || !Number.isFinite(accessExpiresAt) || !user || typeof user !== "object" || typeof user.recordId !== "string" || typeof user.email !== "string") {
542
- return null;
543
- }
544
- return {
545
- accessToken,
546
- refreshToken,
547
- accessExpiresAt,
548
- user: {
549
- recordId: user.recordId,
550
- email: user.email
551
- }
552
- };
553
- } catch {
554
- return null;
555
- }
556
- }
557
- function isAccessExpired(payload, now = Date.now()) {
558
- return payload.accessExpiresAt <= now + ACCESS_EXPIRY_SKEW_MS;
559
- }
560
- function sessionCookieOptions() {
561
- return {
562
- httpOnly: true,
563
- secure: process.env.NODE_ENV !== "development",
564
- sameSite: "lax",
565
- path: "/",
566
- maxAge: SESSION_MAX_AGE_SECONDS
567
- };
568
- }
569
-
570
- // src/config.ts
571
- function assertAuthConfig(config) {
572
- const auth = config.auth;
573
- if (!auth || typeof auth.modelSlug !== "string" || !auth.modelSlug) {
574
- throw new Error("cmssy: config.auth.modelSlug is required for auth routes");
575
- }
576
- if (typeof auth.sessionSecret !== "string" || auth.sessionSecret.length < MIN_SESSION_SECRET_LENGTH) {
577
- throw new Error(
578
- `cmssy: config.auth.sessionSecret must be at least ${MIN_SESSION_SECRET_LENGTH} characters`
579
- );
580
- }
581
- return auth;
582
- }
583
642
  var LOGIN_MUTATION = `mutation SiteMemberLogin($input: SiteMemberLoginInput!) {
584
643
  siteMemberLogin(input: $input) {
585
644
  success message accessToken refreshToken accessTokenExpiresIn
@@ -610,7 +669,7 @@ var VERIFY_MUTATION = `mutation SiteMemberVerifyEmail($token: String!) {
610
669
  }`;
611
670
  var workspaceIdCache = /* @__PURE__ */ new Map();
612
671
  function workspaceIdFor(config) {
613
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
672
+ const key = `${resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
614
673
  const existing = workspaceIdCache.get(key);
615
674
  if (existing) return existing;
616
675
  const fresh = resolveWorkspaceId(config).catch((err) => {
@@ -997,7 +1056,7 @@ var PRODUCT = `query Product($workspaceId: String!, $modelSlug: String!, $filter
997
1056
  }`;
998
1057
  var workspaceIdCache2 = /* @__PURE__ */ new Map();
999
1058
  function workspaceIdFor2(config) {
1000
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
1059
+ const key = `${resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
1001
1060
  const existing = workspaceIdCache2.get(key);
1002
1061
  if (existing) return existing;
1003
1062
  const fresh = resolveWorkspaceId(config).catch((err) => {
@@ -1287,27 +1346,6 @@ function createCmssyCartRoute(config) {
1287
1346
  }
1288
1347
  };
1289
1348
  }
1290
- async function readValidSession(config) {
1291
- const auth = assertAuthConfig(config);
1292
- const jar = await cookies();
1293
- const raw = jar.get(CMSSY_SESSION_COOKIE)?.value;
1294
- if (!raw) return null;
1295
- const session = await openSession(
1296
- raw,
1297
- auth.sessionSecret,
1298
- config.workspaceSlug
1299
- );
1300
- if (!session || isAccessExpired(session)) return null;
1301
- return session;
1302
- }
1303
- async function getCmssyUser(config) {
1304
- const session = await readValidSession(config);
1305
- return session?.user ?? null;
1306
- }
1307
- async function getCmssyAccessToken(config) {
1308
- const session = await readValidSession(config);
1309
- return session?.accessToken ?? null;
1310
- }
1311
1349
  function isPrefetch(request2) {
1312
1350
  return request2.headers.get("next-router-prefetch") !== null || request2.headers.get("purpose") === "prefetch" || (request2.headers.get("sec-purpose") ?? "").includes("prefetch");
1313
1351
  }
@@ -1426,7 +1464,7 @@ var MY_ORDER = `query MyOrder($workspaceId: ID!, $id: ID!) {
1426
1464
  }`;
1427
1465
  var workspaceIdCache3 = /* @__PURE__ */ new Map();
1428
1466
  function workspaceIdFor3(config) {
1429
- const key = `${config.apiUrl}::${config.workspaceSlug}`;
1467
+ const key = `${resolveApiUrl(config.apiUrl)}::${config.workspaceSlug}`;
1430
1468
  const existing = workspaceIdCache3.get(key);
1431
1469
  if (existing) return existing;
1432
1470
  const fresh = resolveWorkspaceId(config).catch((err) => {
@@ -1579,4 +1617,4 @@ function verifyCmssyWebhook(options) {
1579
1617
  return parsed;
1580
1618
  }
1581
1619
 
1582
- export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, CmssyWebhookError, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
1620
+ export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGIN, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/next",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "Next.js App Router bindings for cmssy headless sites (createCmssyPage + draft preview)",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -41,7 +41,7 @@
41
41
  "dist"
42
42
  ],
43
43
  "peerDependencies": {
44
- "@cmssy/react": "^0.5.4",
44
+ "@cmssy/react": "^0.5.6",
45
45
  "next": ">=15",
46
46
  "react": "^18.2.0 || ^19.0.0",
47
47
  "react-dom": "^18.2.0 || ^19.0.0"
@@ -54,7 +54,7 @@
54
54
  "tsup": "^8.3.0",
55
55
  "typescript": "^5.6.0",
56
56
  "vitest": "^2.1.0",
57
- "@cmssy/react": "0.5.4"
57
+ "@cmssy/react": "0.5.6"
58
58
  },
59
59
  "dependencies": {
60
60
  "jose": "^6.2.3"