@sentientui/react 0.2.1 → 0.3.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.
@@ -37,6 +37,12 @@ type AdaptiveProviderProps = {
37
37
  * (Mixpanel, PostHog, Segment, etc.) without having to wrap `useAssignment`.
38
38
  */
39
39
  onAssignment?: (componentId: string, variantId: string) => void;
40
+ /**
41
+ * SSR-preloaded section order from `loadAdaptiveDecision()`.
42
+ * Pass the `layoutOrder` field from `DecideResult`. When set,
43
+ * `useLayoutOrder()` returns this on first render so there is no layout shift.
44
+ */
45
+ initialLayoutOrder?: string[] | null;
40
46
  children: ReactNode;
41
47
  };
42
48
 
@@ -45,44 +51,55 @@ type PreloadComponent = {
45
51
  variantIds: string[];
46
52
  };
47
53
  type AdaptiveRootProps = Omit<AdaptiveProviderProps, 'initialAssignments'> & {
48
- /** Slots to resolve on the server before HTML is sent (SEO-safe). */
54
+ /** Components to assign server-side (SEO-safe). */
49
55
  components: PreloadComponent[];
50
- /** Server-only key for `/v1/sessions` and `/v1/assign` (usually the same `pk_` as `apiKey`). */
51
- serverApiKey: string;
52
56
  /**
53
- * App origin for SSR preload requests (must be in the project's `allowed_origins`).
54
- * @default `http://localhost:3001` in development
57
+ * Declare page section IDs in their default order. When provided,
58
+ * AdaptiveRoot calls `/v1/decide` (single round trip) instead of
59
+ * individual `/v1/assign` calls, and `useLayoutOrder()` returns the
60
+ * persona-specific order on first render.
61
+ *
62
+ * @example sections={['hero', 'pricing', 'features', 'social_proof']}
55
63
  */
64
+ sections?: string[];
65
+ /** Server-only API key for `/v1/sessions`, `/v1/assign`, and `/v1/decide`. */
66
+ serverApiKey: string;
67
+ /** App origin — must be in the project's `allowed_origins`. */
56
68
  appOrigin?: string;
57
- /**
58
- * Optional preloaded map; when set, `components` is not fetched (useful for tests
59
- * or custom loaders). Normally omit and let `loadAdaptiveAssignments` run.
60
- */
69
+ /** Override: when set no network fetch is made. Useful for tests. */
61
70
  initialAssignments?: ServerAssignments;
62
71
  children: ReactNode;
63
72
  };
64
73
  /**
65
- * Server Component wrapper: loads assignments, then renders `AdaptiveProvider`
66
- * with `initialAssignments` so crawlers and hydration see real variant content.
74
+ * Next.js Server Component that resolves variant assignments (and optionally
75
+ * section layout order) server-side for zero layout shift on first paint.
76
+ *
77
+ * When `sections` is provided, a single `POST /v1/decide` call returns both
78
+ * the persona-specific section order and all component assignments.
79
+ * Without `sections`, individual `/v1/assign` calls are made per component.
67
80
  *
68
81
  * @example
69
- * ```tsx
70
- * // app/layout.tsx
82
+ * // app/page.tsx — with section layout
71
83
  * import { AdaptiveRoot } from '@sentientui/react/next';
72
84
  *
73
- * export default function Layout({ children }) {
85
+ * export default async function Page() {
74
86
  * return (
75
87
  * <AdaptiveRoot
76
- * components={[{ id: 'hero_cta', variantIds: ['default', 'accent'] }]}
77
- * serverApiKey={process.env.SENTIENT_API_KEY!}
78
88
  * apiKey={process.env.NEXT_PUBLIC_SENTIENT_API_KEY!}
79
- * context="saas"
89
+ * serverApiKey={process.env.SENTIENT_SECRET_KEY!}
90
+ * context={{ appId: 'my-app' }}
91
+ * sections={['hero', 'pricing', 'features', 'social_proof']}
92
+ * components={[
93
+ * { id: 'hero_cta', variantIds: ['default', 'accent'] },
94
+ * ]}
80
95
  * >
81
- * {children}
96
+ * <HeroSection />
97
+ * <PricingSection />
98
+ * <FeaturesSection />
99
+ * <SocialProofSection />
82
100
  * </AdaptiveRoot>
83
101
  * );
84
102
  * }
85
- * ```
86
103
  */
87
104
  declare function AdaptiveRoot(props: AdaptiveRootProps): Promise<JSX.Element>;
88
105
 
@@ -39,6 +39,7 @@ import {
39
39
  preloadAssignments,
40
40
  readSessionCookie
41
41
  } from "@sentientui/core";
42
+ import { preloadDecisions } from "@sentientui/core";
42
43
  function defaultSessionId() {
43
44
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
44
45
  return crypto.randomUUID();
@@ -56,6 +57,25 @@ async function loadAdaptiveAssignments(components, options) {
56
57
  referer: options.referer
57
58
  });
58
59
  }
60
+ async function loadAdaptiveDecision(options) {
61
+ var _a, _b, _c, _d;
62
+ const { preloadDecisions: preloadDecisions2, readSessionCookie: readSessionCookie2 } = await import("@sentientui/core");
63
+ const sessionId = (_c = (_b = readSessionCookie2(options.cookies)) != null ? _b : (_a = options.createSessionId) == null ? void 0 : _a.call(options)) != null ? _c : defaultSessionId();
64
+ return preloadDecisions2(
65
+ {
66
+ sections: options.sections,
67
+ components: (_d = options.components) != null ? _d : []
68
+ },
69
+ sessionId,
70
+ {
71
+ apiKey: options.apiKey,
72
+ baseUrl: options.baseUrl,
73
+ origin: options.origin,
74
+ userAgent: options.userAgent,
75
+ referer: options.referer
76
+ }
77
+ );
78
+ }
59
79
 
60
80
  // src/next/adaptive-root.tsx
61
81
  import { AdaptiveRootClient } from "./adaptive-root-client.js";
@@ -65,12 +85,14 @@ async function AdaptiveRoot(props) {
65
85
  var _b, _c;
66
86
  const _a = props, {
67
87
  components,
88
+ sections,
68
89
  serverApiKey,
69
90
  appOrigin,
70
91
  initialAssignments: initialAssignmentsOverride,
71
92
  children
72
93
  } = _a, providerProps = __objRest(_a, [
73
94
  "components",
95
+ "sections",
74
96
  "serverApiKey",
75
97
  "appOrigin",
76
98
  "initialAssignments",
@@ -81,18 +103,39 @@ async function AdaptiveRoot(props) {
81
103
  const userAgent = (_b = headerStore.get("user-agent")) != null ? _b : void 0;
82
104
  const referer = (_c = headerStore.get("referer")) != null ? _c : void 0;
83
105
  const sessionSegment = deriveSessionSegment({ userAgent, referer, appOrigin: resolvedOrigin });
84
- const initialAssignments = initialAssignmentsOverride != null ? initialAssignmentsOverride : await loadAdaptiveAssignments(components, {
85
- cookies: await cookies(),
86
- apiKey: serverApiKey,
87
- baseUrl: DEFAULT_API_BASE_URL,
88
- origin: resolvedOrigin,
89
- userAgent,
90
- referer
91
- });
106
+ const cookieStore = await cookies();
107
+ let initialAssignments;
108
+ let initialLayoutOrder = null;
109
+ if (initialAssignmentsOverride) {
110
+ initialAssignments = initialAssignmentsOverride;
111
+ } else if (sections && sections.length > 0) {
112
+ const decision = await loadAdaptiveDecision({
113
+ sections,
114
+ components,
115
+ cookies: cookieStore,
116
+ apiKey: serverApiKey,
117
+ baseUrl: DEFAULT_API_BASE_URL,
118
+ origin: resolvedOrigin,
119
+ userAgent,
120
+ referer
121
+ });
122
+ initialAssignments = decision.assignments;
123
+ initialLayoutOrder = decision.layoutOrder;
124
+ } else {
125
+ initialAssignments = await loadAdaptiveAssignments(components, {
126
+ cookies: cookieStore,
127
+ apiKey: serverApiKey,
128
+ baseUrl: DEFAULT_API_BASE_URL,
129
+ origin: resolvedOrigin,
130
+ userAgent,
131
+ referer
132
+ });
133
+ }
92
134
  return /* @__PURE__ */ jsx(
93
135
  AdaptiveRootClient,
94
136
  __spreadProps(__spreadValues({}, providerProps), {
95
137
  initialAssignments,
138
+ initialLayoutOrder,
96
139
  sessionSegment,
97
140
  children
98
141
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/adaptive-root.tsx","../../src/server.ts"],"sourcesContent":["import { deriveSessionSegment } from '@sentientui/core';\nimport { cookies, headers } from 'next/headers';\nimport type { ReactNode } from 'react';\nimport type { AdaptiveProviderProps } from '../provider.js';\nimport { loadAdaptiveAssignments, type ServerAssignments } from '../server.js';\nimport { AdaptiveRootClient } from './adaptive-root-client.js';\n\nconst DEFAULT_API_BASE_URL = 'https://sentient-api.fly.dev/v1';\n\nexport type PreloadComponent = { id: string; variantIds: string[] };\n\nexport type AdaptiveRootProps = Omit<AdaptiveProviderProps, 'initialAssignments'> & {\n /** Slots to resolve on the server before HTML is sent (SEO-safe). */\n components: PreloadComponent[];\n /** Server-only key for `/v1/sessions` and `/v1/assign` (usually the same `pk_` as `apiKey`). */\n serverApiKey: string;\n /**\n * App origin for SSR preload requests (must be in the project's `allowed_origins`).\n * @default `http://localhost:3001` in development\n */\n appOrigin?: string;\n /**\n * Optional preloaded map; when set, `components` is not fetched (useful for tests\n * or custom loaders). Normally omit and let `loadAdaptiveAssignments` run.\n */\n initialAssignments?: ServerAssignments;\n children: ReactNode;\n};\n\n/**\n * Server Component wrapper: loads assignments, then renders `AdaptiveProvider`\n * with `initialAssignments` so crawlers and hydration see real variant content.\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { AdaptiveRoot } from '@sentientui/react/next';\n *\n * export default function Layout({ children }) {\n * return (\n * <AdaptiveRoot\n * components={[{ id: 'hero_cta', variantIds: ['default', 'accent'] }]}\n * serverApiKey={process.env.SENTIENT_API_KEY!}\n * apiKey={process.env.NEXT_PUBLIC_SENTIENT_API_KEY!}\n * context=\"saas\"\n * >\n * {children}\n * </AdaptiveRoot>\n * );\n * }\n * ```\n */\nexport async function AdaptiveRoot(props: AdaptiveRootProps): Promise<JSX.Element> {\n const {\n components,\n serverApiKey,\n appOrigin,\n initialAssignments: initialAssignmentsOverride,\n children,\n ...providerProps\n } = props;\n\n const headerStore = await headers();\n const resolvedOrigin = appOrigin ?? 'http://localhost:3001';\n const userAgent = headerStore.get('user-agent') ?? undefined;\n const referer = headerStore.get('referer') ?? undefined;\n const sessionSegment = deriveSessionSegment({ userAgent, referer, appOrigin: resolvedOrigin });\n const initialAssignments =\n initialAssignmentsOverride ??\n (await loadAdaptiveAssignments(components, {\n cookies: await cookies(),\n apiKey: serverApiKey,\n baseUrl: DEFAULT_API_BASE_URL,\n origin: resolvedOrigin,\n userAgent,\n referer,\n }));\n\n return (\n <AdaptiveRootClient\n {...providerProps}\n initialAssignments={initialAssignments}\n sessionSegment={sessionSegment}\n >\n {children}\n </AdaptiveRootClient>\n );\n}\n","/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,4BAA4B;AACrC,SAAS,SAAS,eAAe;;;ACEjC;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAoBP,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,6BAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAO,mBAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;;;ADlDA,SAAS,0BAA0B;AA0E/B;AAxEJ,IAAM,uBAAuB;AA6C7B,eAAsB,aAAa,OAAgD;AApDnF;AAqDE,QAOI,YANF;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,IACpB;AAAA,EA1DJ,IA4DM,IADC,0BACD,IADC;AAAA,IALH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAIF,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,iBAAiB,gCAAa;AACpC,QAAM,aAAY,iBAAY,IAAI,YAAY,MAA5B,YAAiC;AACnD,QAAM,WAAU,iBAAY,IAAI,SAAS,MAAzB,YAA8B;AAC9C,QAAM,iBAAiB,qBAAqB,EAAE,WAAW,SAAS,WAAW,eAAe,CAAC;AAC7F,QAAM,qBACJ,kEACC,MAAM,wBAAwB,YAAY;AAAA,IACzC,SAAS,MAAM,QAAQ;AAAA,IACvB,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,EACF,CAAC;AAEH,SACE;AAAA,IAAC;AAAA,qCACK,gBADL;AAAA,MAEC;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/next/adaptive-root.tsx","../../src/server.ts"],"sourcesContent":["import { deriveSessionSegment } from '@sentientui/core';\nimport { cookies, headers } from 'next/headers';\nimport type { ReactNode } from 'react';\nimport type { AdaptiveProviderProps } from '../provider.js';\nimport {\n loadAdaptiveAssignments,\n loadAdaptiveDecision,\n type ServerAssignments,\n} from '../server.js';\nimport { AdaptiveRootClient } from './adaptive-root-client.js';\n\nconst DEFAULT_API_BASE_URL = 'https://sentient-api.fly.dev/v1';\n\nexport type PreloadComponent = { id: string; variantIds: string[] };\n\nexport type AdaptiveRootProps = Omit<AdaptiveProviderProps, 'initialAssignments'> & {\n /** Components to assign server-side (SEO-safe). */\n components: PreloadComponent[];\n /**\n * Declare page section IDs in their default order. When provided,\n * AdaptiveRoot calls `/v1/decide` (single round trip) instead of\n * individual `/v1/assign` calls, and `useLayoutOrder()` returns the\n * persona-specific order on first render.\n *\n * @example sections={['hero', 'pricing', 'features', 'social_proof']}\n */\n sections?: string[];\n /** Server-only API key for `/v1/sessions`, `/v1/assign`, and `/v1/decide`. */\n serverApiKey: string;\n /** App origin must be in the project's `allowed_origins`. */\n appOrigin?: string;\n /** Override: when set no network fetch is made. Useful for tests. */\n initialAssignments?: ServerAssignments;\n children: ReactNode;\n};\n\n/**\n * Next.js Server Component that resolves variant assignments (and optionally\n * section layout order) server-side for zero layout shift on first paint.\n *\n * When `sections` is provided, a single `POST /v1/decide` call returns both\n * the persona-specific section order and all component assignments.\n * Without `sections`, individual `/v1/assign` calls are made per component.\n *\n * @example\n * // app/page.tsx — with section layout\n * import { AdaptiveRoot } from '@sentientui/react/next';\n *\n * export default async function Page() {\n * return (\n * <AdaptiveRoot\n * apiKey={process.env.NEXT_PUBLIC_SENTIENT_API_KEY!}\n * serverApiKey={process.env.SENTIENT_SECRET_KEY!}\n * context={{ appId: 'my-app' }}\n * sections={['hero', 'pricing', 'features', 'social_proof']}\n * components={[\n * { id: 'hero_cta', variantIds: ['default', 'accent'] },\n * ]}\n * >\n * <HeroSection />\n * <PricingSection />\n * <FeaturesSection />\n * <SocialProofSection />\n * </AdaptiveRoot>\n * );\n * }\n */\nexport async function AdaptiveRoot(props: AdaptiveRootProps): Promise<JSX.Element> {\n const {\n components,\n sections,\n serverApiKey,\n appOrigin,\n initialAssignments: initialAssignmentsOverride,\n children,\n ...providerProps\n } = props;\n\n const headerStore = await headers();\n const resolvedOrigin = appOrigin ?? 'http://localhost:3001';\n const userAgent = headerStore.get('user-agent') ?? undefined;\n const referer = headerStore.get('referer') ?? undefined;\n const sessionSegment = deriveSessionSegment({ userAgent, referer, appOrigin: resolvedOrigin });\n const cookieStore = await cookies();\n\n let initialAssignments: ServerAssignments;\n let initialLayoutOrder: string[] | null = null;\n\n if (initialAssignmentsOverride) {\n initialAssignments = initialAssignmentsOverride;\n } else if (sections && sections.length > 0) {\n const decision = await loadAdaptiveDecision({\n sections,\n components,\n cookies: cookieStore,\n apiKey: serverApiKey,\n baseUrl: DEFAULT_API_BASE_URL,\n origin: resolvedOrigin,\n userAgent,\n referer,\n });\n initialAssignments = decision.assignments;\n initialLayoutOrder = decision.layoutOrder;\n } else {\n initialAssignments = await loadAdaptiveAssignments(components, {\n cookies: cookieStore,\n apiKey: serverApiKey,\n baseUrl: DEFAULT_API_BASE_URL,\n origin: resolvedOrigin,\n userAgent,\n referer,\n });\n }\n\n return (\n <AdaptiveRootClient\n {...providerProps}\n initialAssignments={initialAssignments}\n initialLayoutOrder={initialLayoutOrder}\n sessionSegment={sessionSegment}\n >\n {children}\n </AdaptiveRootClient>\n );\n}\n","/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n\nexport { preloadDecisions, type DecideResult } from '@sentientui/core';\n\nexport type LoadAdaptiveDecisionOptions = LoadAdaptiveAssignmentsOptions & {\n /** Section IDs in default order. Passed to /v1/decide as the candidate layout. */\n sections: string[];\n /** Components to assign in the same decide call. */\n components?: Array<{ id: string; variantIds?: string[] }>;\n};\n\n/**\n * SSR helper for pages with a declared section layout. Calls `/v1/decide`\n * instead of multiple `/v1/assign` round trips.\n */\nexport async function loadAdaptiveDecision(\n options: LoadAdaptiveDecisionOptions,\n): Promise<import('@sentientui/core').DecideResult> {\n const { preloadDecisions, readSessionCookie } = await import('@sentientui/core');\n\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadDecisions(\n {\n sections: options.sections,\n components: options.components ?? [],\n },\n sessionId,\n {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,4BAA4B;AACrC,SAAS,SAAS,eAAe;;;ACEjC;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAiDP,SAAS,wBAA2C;AA7BpD,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,6BAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAO,mBAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;AAeA,eAAsB,qBACpB,SACkD;AAxEpD;AAyEE,QAAM,EAAE,kBAAAA,mBAAkB,mBAAAC,mBAAkB,IAAI,MAAM,OAAO,kBAAkB;AAE/E,QAAM,aACJ,WAAAA,mBAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAOD;AAAA,IACL;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,aAAY,aAAQ,eAAR,YAAsB,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,IACnB;AAAA,EACF;AACF;;;ADrFA,SAAS,0BAA0B;AA0G/B;AAxGJ,IAAM,uBAAuB;AAwD7B,eAAsB,aAAa,OAAgD;AAnEnF;AAoEE,QAQI,YAPF;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB;AAAA,IACpB;AAAA,EA1EJ,IA4EM,IADC,0BACD,IADC;AAAA,IANH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAIF,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,iBAAiB,gCAAa;AACpC,QAAM,aAAY,iBAAY,IAAI,YAAY,MAA5B,YAAiC;AACnD,QAAM,WAAU,iBAAY,IAAI,SAAS,MAAzB,YAA8B;AAC9C,QAAM,iBAAiB,qBAAqB,EAAE,WAAW,SAAS,WAAW,eAAe,CAAC;AAC7F,QAAM,cAAc,MAAM,QAAQ;AAElC,MAAI;AACJ,MAAI,qBAAsC;AAE1C,MAAI,4BAA4B;AAC9B,yBAAqB;AAAA,EACvB,WAAW,YAAY,SAAS,SAAS,GAAG;AAC1C,UAAM,WAAW,MAAM,qBAAqB;AAAA,MAC1C;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AACD,yBAAqB,SAAS;AAC9B,yBAAqB,SAAS;AAAA,EAChC,OAAO;AACL,yBAAqB,MAAM,wBAAwB,YAAY;AAAA,MAC7D,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SACE;AAAA,IAAC;AAAA,qCACK,gBADL;AAAA,MAEC;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":["preloadDecisions","readSessionCookie"]}
package/dist/server.d.cts CHANGED
@@ -1,9 +1,6 @@
1
+ import * as _sentientui_core from '@sentientui/core';
1
2
  import { ServerAssignments } from '@sentientui/core';
2
- export { ServerAssignConfig, ServerAssignments, preloadAssignments, readSessionCookie } from '@sentientui/core';
3
-
4
- /**
5
- * Server-only helpers for Next.js / SSR. No React or DOM APIs.
6
- */
3
+ export { DecideResult, ServerAssignConfig, ServerAssignments, preloadAssignments, preloadDecisions, readSessionCookie } from '@sentientui/core';
7
4
 
8
5
  type LoadAdaptiveAssignmentsOptions = {
9
6
  /** Next.js `cookies()` return value, or any object with `get(name)`. */
@@ -32,4 +29,19 @@ declare function loadAdaptiveAssignments(components: Array<{
32
29
  variantIds: string[];
33
30
  }>, options: LoadAdaptiveAssignmentsOptions): Promise<ServerAssignments>;
34
31
 
35
- export { type LoadAdaptiveAssignmentsOptions, loadAdaptiveAssignments };
32
+ type LoadAdaptiveDecisionOptions = LoadAdaptiveAssignmentsOptions & {
33
+ /** Section IDs in default order. Passed to /v1/decide as the candidate layout. */
34
+ sections: string[];
35
+ /** Components to assign in the same decide call. */
36
+ components?: Array<{
37
+ id: string;
38
+ variantIds?: string[];
39
+ }>;
40
+ };
41
+ /**
42
+ * SSR helper for pages with a declared section layout. Calls `/v1/decide`
43
+ * instead of multiple `/v1/assign` round trips.
44
+ */
45
+ declare function loadAdaptiveDecision(options: LoadAdaptiveDecisionOptions): Promise<_sentientui_core.DecideResult>;
46
+
47
+ export { type LoadAdaptiveAssignmentsOptions, type LoadAdaptiveDecisionOptions, loadAdaptiveAssignments, loadAdaptiveDecision };
package/dist/server.d.ts CHANGED
@@ -1,9 +1,6 @@
1
+ import * as _sentientui_core from '@sentientui/core';
1
2
  import { ServerAssignments } from '@sentientui/core';
2
- export { ServerAssignConfig, ServerAssignments, preloadAssignments, readSessionCookie } from '@sentientui/core';
3
-
4
- /**
5
- * Server-only helpers for Next.js / SSR. No React or DOM APIs.
6
- */
3
+ export { DecideResult, ServerAssignConfig, ServerAssignments, preloadAssignments, preloadDecisions, readSessionCookie } from '@sentientui/core';
7
4
 
8
5
  type LoadAdaptiveAssignmentsOptions = {
9
6
  /** Next.js `cookies()` return value, or any object with `get(name)`. */
@@ -32,4 +29,19 @@ declare function loadAdaptiveAssignments(components: Array<{
32
29
  variantIds: string[];
33
30
  }>, options: LoadAdaptiveAssignmentsOptions): Promise<ServerAssignments>;
34
31
 
35
- export { type LoadAdaptiveAssignmentsOptions, loadAdaptiveAssignments };
32
+ type LoadAdaptiveDecisionOptions = LoadAdaptiveAssignmentsOptions & {
33
+ /** Section IDs in default order. Passed to /v1/decide as the candidate layout. */
34
+ sections: string[];
35
+ /** Components to assign in the same decide call. */
36
+ components?: Array<{
37
+ id: string;
38
+ variantIds?: string[];
39
+ }>;
40
+ };
41
+ /**
42
+ * SSR helper for pages with a declared section layout. Calls `/v1/decide`
43
+ * instead of multiple `/v1/assign` round trips.
44
+ */
45
+ declare function loadAdaptiveDecision(options: LoadAdaptiveDecisionOptions): Promise<_sentientui_core.DecideResult>;
46
+
47
+ export { type LoadAdaptiveAssignmentsOptions, type LoadAdaptiveDecisionOptions, loadAdaptiveAssignments, loadAdaptiveDecision };
package/dist/server.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,17 +17,28 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/server.ts
21
31
  var server_exports = {};
22
32
  __export(server_exports, {
23
33
  loadAdaptiveAssignments: () => loadAdaptiveAssignments,
34
+ loadAdaptiveDecision: () => loadAdaptiveDecision,
24
35
  preloadAssignments: () => import_core.preloadAssignments,
36
+ preloadDecisions: () => import_core2.preloadDecisions,
25
37
  readSessionCookie: () => import_core.readSessionCookie
26
38
  });
27
39
  module.exports = __toCommonJS(server_exports);
28
40
  var import_core = require("@sentientui/core");
41
+ var import_core2 = require("@sentientui/core");
29
42
  function defaultSessionId() {
30
43
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
31
44
  return crypto.randomUUID();
@@ -43,10 +56,31 @@ async function loadAdaptiveAssignments(components, options) {
43
56
  referer: options.referer
44
57
  });
45
58
  }
59
+ async function loadAdaptiveDecision(options) {
60
+ var _a, _b, _c, _d;
61
+ const { preloadDecisions: preloadDecisions2, readSessionCookie: readSessionCookie2 } = await import("@sentientui/core");
62
+ const sessionId = (_c = (_b = readSessionCookie2(options.cookies)) != null ? _b : (_a = options.createSessionId) == null ? void 0 : _a.call(options)) != null ? _c : defaultSessionId();
63
+ return preloadDecisions2(
64
+ {
65
+ sections: options.sections,
66
+ components: (_d = options.components) != null ? _d : []
67
+ },
68
+ sessionId,
69
+ {
70
+ apiKey: options.apiKey,
71
+ baseUrl: options.baseUrl,
72
+ origin: options.origin,
73
+ userAgent: options.userAgent,
74
+ referer: options.referer
75
+ }
76
+ );
77
+ }
46
78
  // Annotate the CommonJS export names for ESM import in node:
47
79
  0 && (module.exports = {
48
80
  loadAdaptiveAssignments,
81
+ loadAdaptiveDecision,
49
82
  preloadAssignments,
83
+ preloadDecisions,
50
84
  readSessionCookie
51
85
  });
52
86
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAKO;AAoBP,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,8CAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,aAAO,gCAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n\nexport { preloadDecisions, type DecideResult } from '@sentientui/core';\n\nexport type LoadAdaptiveDecisionOptions = LoadAdaptiveAssignmentsOptions & {\n /** Section IDs in default order. Passed to /v1/decide as the candidate layout. */\n sections: string[];\n /** Components to assign in the same decide call. */\n components?: Array<{ id: string; variantIds?: string[] }>;\n};\n\n/**\n * SSR helper for pages with a declared section layout. Calls `/v1/decide`\n * instead of multiple `/v1/assign` round trips.\n */\nexport async function loadAdaptiveDecision(\n options: LoadAdaptiveDecisionOptions,\n): Promise<import('@sentientui/core').DecideResult> {\n const { preloadDecisions, readSessionCookie } = await import('@sentientui/core');\n\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadDecisions(\n {\n sections: options.sections,\n components: options.components ?? [],\n },\n sessionId,\n {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,kBAKO;AAiDP,IAAAA,eAAoD;AA7BpD,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,8CAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,aAAO,gCAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;AAeA,eAAsB,qBACpB,SACkD;AAxEpD;AAyEE,QAAM,EAAE,kBAAAC,mBAAkB,mBAAAC,mBAAkB,IAAI,MAAM,OAAO,kBAAkB;AAE/E,QAAM,aACJ,WAAAA,mBAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAOD;AAAA,IACL;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,aAAY,aAAQ,eAAR,YAAsB,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,IACnB;AAAA,EACF;AACF;","names":["import_core","preloadDecisions","readSessionCookie"]}
package/dist/server.mjs CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  preloadAssignments,
4
4
  readSessionCookie
5
5
  } from "@sentientui/core";
6
+ import { preloadDecisions } from "@sentientui/core";
6
7
  function defaultSessionId() {
7
8
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
8
9
  return crypto.randomUUID();
@@ -20,9 +21,30 @@ async function loadAdaptiveAssignments(components, options) {
20
21
  referer: options.referer
21
22
  });
22
23
  }
24
+ async function loadAdaptiveDecision(options) {
25
+ var _a, _b, _c, _d;
26
+ const { preloadDecisions: preloadDecisions2, readSessionCookie: readSessionCookie2 } = await import("@sentientui/core");
27
+ const sessionId = (_c = (_b = readSessionCookie2(options.cookies)) != null ? _b : (_a = options.createSessionId) == null ? void 0 : _a.call(options)) != null ? _c : defaultSessionId();
28
+ return preloadDecisions2(
29
+ {
30
+ sections: options.sections,
31
+ components: (_d = options.components) != null ? _d : []
32
+ },
33
+ sessionId,
34
+ {
35
+ apiKey: options.apiKey,
36
+ baseUrl: options.baseUrl,
37
+ origin: options.origin,
38
+ userAgent: options.userAgent,
39
+ referer: options.referer
40
+ }
41
+ );
42
+ }
23
43
  export {
24
44
  loadAdaptiveAssignments,
45
+ loadAdaptiveDecision,
25
46
  preloadAssignments,
47
+ preloadDecisions,
26
48
  readSessionCookie
27
49
  };
28
50
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n"],"mappings":";AAGA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAoBP,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,6BAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAO,mBAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * Server-only helpers for Next.js / SSR. No React or DOM APIs.\n */\nimport {\n preloadAssignments,\n readSessionCookie,\n type ServerAssignConfig,\n type ServerAssignments,\n} from '@sentientui/core';\n\nexport { preloadAssignments, readSessionCookie };\nexport type { ServerAssignConfig, ServerAssignments };\n\nexport type LoadAdaptiveAssignmentsOptions = {\n /** Next.js `cookies()` return value, or any object with `get(name)`. */\n cookies: { get(name: string): { value: string } | undefined };\n apiKey: string;\n baseUrl: string;\n /** Used when `_snt_uid` is absent (e.g. first visit, many crawlers). */\n createSessionId?: () => string;\n /** Must match a value in the project's `allowed_origins` (e.g. `http://localhost:3001`). */\n origin?: string;\n /** From Next.js `headers().get('user-agent')` — aligns SSR segment with the client. */\n userAgent?: string;\n /** From Next.js `headers().get('referer')`. */\n referer?: string;\n};\n\nfunction defaultSessionId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `snt-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n}\n\n/**\n * Fetches bandit assignments on the server for SEO-safe HTML.\n * Pass the result as `initialAssignments` on `<AdaptiveProvider>`.\n */\nexport async function loadAdaptiveAssignments(\n components: Array<{ id: string; variantIds: string[] }>,\n options: LoadAdaptiveAssignmentsOptions,\n): Promise<ServerAssignments> {\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadAssignments(components, sessionId, {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n });\n}\n\nexport { preloadDecisions, type DecideResult } from '@sentientui/core';\n\nexport type LoadAdaptiveDecisionOptions = LoadAdaptiveAssignmentsOptions & {\n /** Section IDs in default order. Passed to /v1/decide as the candidate layout. */\n sections: string[];\n /** Components to assign in the same decide call. */\n components?: Array<{ id: string; variantIds?: string[] }>;\n};\n\n/**\n * SSR helper for pages with a declared section layout. Calls `/v1/decide`\n * instead of multiple `/v1/assign` round trips.\n */\nexport async function loadAdaptiveDecision(\n options: LoadAdaptiveDecisionOptions,\n): Promise<import('@sentientui/core').DecideResult> {\n const { preloadDecisions, readSessionCookie } = await import('@sentientui/core');\n\n const sessionId =\n readSessionCookie(options.cookies) ??\n options.createSessionId?.() ??\n defaultSessionId();\n\n return preloadDecisions(\n {\n sections: options.sections,\n components: options.components ?? [],\n },\n sessionId,\n {\n apiKey: options.apiKey,\n baseUrl: options.baseUrl,\n origin: options.origin,\n userAgent: options.userAgent,\n referer: options.referer,\n },\n );\n}\n"],"mappings":";AAGA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAiDP,SAAS,wBAA2C;AA7BpD,SAAS,mBAA2B;AAClC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrE;AAMA,eAAsB,wBACpB,YACA,SAC4B;AA1C9B;AA2CE,QAAM,aACJ,6BAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAO,mBAAmB,YAAY,WAAW;AAAA,IAC/C,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH;AAeA,eAAsB,qBACpB,SACkD;AAxEpD;AAyEE,QAAM,EAAE,kBAAAA,mBAAkB,mBAAAC,mBAAkB,IAAI,MAAM,OAAO,kBAAkB;AAE/E,QAAM,aACJ,WAAAA,mBAAkB,QAAQ,OAAO,MAAjC,aACA,aAAQ,oBAAR,qCADA,YAEA,iBAAiB;AAEnB,SAAOD;AAAA,IACL;AAAA,MACE,UAAU,QAAQ;AAAA,MAClB,aAAY,aAAQ,eAAR,YAAsB,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,IACnB;AAAA,EACF;AACF;","names":["preloadDecisions","readSessionCookie"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentientui/react",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",