@typecms/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @typecms/sdk
2
+
3
+ Typed JavaScript/TypeScript SDK for Type CMS: content delivery, live preview, and feature flags.
4
+
5
+ ```bash
6
+ npm install @typecms/sdk
7
+ ```
8
+
9
+ ## Content client
10
+
11
+ ```typescript
12
+ import { createClient } from '@typecms/sdk'
13
+
14
+ const cms = createClient({
15
+ projectId: 'your-project-id',
16
+ token: process.env.TYPECMS_TOKEN!, // Project Settings → Tokens
17
+ })
18
+ ```
19
+
20
+ The token is bound to an environment — content is always served from that environment.
21
+
22
+ ### Fetch a single entry
23
+
24
+ ```typescript
25
+ // By URL path (localized path aliases match too)
26
+ const home = await cms.getEntry({ path: '/' })
27
+
28
+ // By slug or entry id
29
+ const post = await cms.getEntry({ slug: 'hello-world', language: 'fr-FR' })
30
+ const byId = await cms.getEntry({ entryId: '665f...' })
31
+
32
+ // null when nothing is published — no try/catch needed for missing pages
33
+ if (!home) return notFound()
34
+ ```
35
+
36
+ Type the `content` payload per call:
37
+
38
+ ```typescript
39
+ interface HomePage {
40
+ heading: string
41
+ hero: { image: Asset; cta: Link }
42
+ }
43
+
44
+ const home = await cms.getEntry<HomePage>({ path: '/' })
45
+ home?.content.heading // typed
46
+ ```
47
+
48
+ ### Personalization (audiences)
49
+
50
+ Ask for an audience's variant; when it has no published row you get the base
51
+ content automatically. The row's `variantSlug` tells you which one was served.
52
+
53
+ ```typescript
54
+ const page = await cms.getEntry({ path: '/', variant: 'vip' })
55
+ page?.variantSlug // 'vip' or null (base)
56
+
57
+ // Client-wide personalization lens
58
+ const vipCms = createClient({ projectId, token, variant: 'vip' })
59
+ await vipCms.getEntry({ path: '/' }) // vip with base fallback
60
+ await vipCms.getEntry({ path: '/', variant: null }) // force base
61
+
62
+ // Strict mode: no fallback
63
+ await cms.getEntry({ path: '/', variant: 'vip', variantFallback: false })
64
+ ```
65
+
66
+ ### Lists
67
+
68
+ ```typescript
69
+ const { entries, count } = await cms.getEntries({
70
+ templateId: '665f...',
71
+ filter: { tags: 'featured' },
72
+ sort: '-publishedAt', // '-' prefix = descending
73
+ limit: 10,
74
+ skip: 0,
75
+ })
76
+ ```
77
+
78
+ ### Drafts (live preview)
79
+
80
+ With a token bound to a **preview environment**, read unpublished content in
81
+ exactly the same shapes. Draft responses are never CDN-cached.
82
+
83
+ ```typescript
84
+ const draft = await cms.getEntry({ path: '/', draft: true })
85
+ ```
86
+
87
+ ### Full-text search
88
+
89
+ ```typescript
90
+ const { entries: hits } = await cms.search('astronaut', {
91
+ limit: 5,
92
+ filters: { publishedAt: ['2026-01-01', '2026-07-01'] },
93
+ })
94
+ ```
95
+
96
+ ### Escape hatch
97
+
98
+ Anything the helpers don't cover — POST a raw query body:
99
+
100
+ ```typescript
101
+ const page = await cms.query({ publishedBy: '665f...', language: 'de-DE' }, { limit: 20 })
102
+ ```
103
+
104
+ ### Errors
105
+
106
+ Missing content is `null` / an empty page. Real failures (auth, validation,
107
+ network, timeouts) throw `TypeCMSError` with `status` and optional `details`.
108
+
109
+ ```typescript
110
+ import { TypeCMSError } from '@typecms/sdk'
111
+
112
+ try {
113
+ await cms.getEntry({ path: '/' })
114
+ } catch (error) {
115
+ if (error instanceof TypeCMSError && error.status === 401) {
116
+ // bad/disabled token
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## Live preview — `@typecms/sdk/preview`
122
+
123
+ Real-time editing preview for sites embedded in the Type CMS editor's preview
124
+ pane. Edits stream over postMessage and merge over your fetched content.
125
+
126
+ ```tsx
127
+ import { PreviewProvider, usePreviewContent } from '@typecms/sdk/preview'
128
+
129
+ function Page({ entry }) {
130
+ const content = usePreviewContent(entry.content) // live values merged in
131
+ return <h1 data-preview-field="title">{content.title}</h1>
132
+ }
133
+ ```
134
+
135
+ `data-preview-field="<path>"` enables click-to-edit: clicking the element in
136
+ the preview scrolls the editor to that field.
137
+
138
+ ## Feature flags — `@typecms/sdk/flags`
139
+
140
+ ```typescript
141
+ import { initFlags } from '@typecms/sdk/flags'
142
+
143
+ const flags = await initFlags({
144
+ clientKey: 'sdk_xxxxxxxx',
145
+ attributes: { id: user.id, plan: user.plan },
146
+ })
147
+
148
+ if (flags.isOn('new-checkout')) {
149
+ // ...
150
+ }
151
+ ```
@@ -0,0 +1,92 @@
1
+ import { P as PreviewState, a as PreviewConfig } from './types-BNHDdA2G.mjs';
2
+
3
+ /**
4
+ * Preview Client
5
+ *
6
+ * Core client for handling live preview PostMessage communication.
7
+ * This runs on the preview site (your frontend) inside an iframe.
8
+ */
9
+
10
+ type PreviewListener = (state: PreviewState) => void;
11
+ /**
12
+ * Create a preview client instance.
13
+ *
14
+ * This handles PostMessage communication between the Type CMS editor
15
+ * and your preview site. The client runs in the preview iframe.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { createPreviewClient } from '@typecms/sdk/preview'
20
+ *
21
+ * const preview = createPreviewClient({
22
+ * debug: true,
23
+ * onUpdate: (data) => {
24
+ * console.log('Value changed:', data.path, data.value)
25
+ * },
26
+ * })
27
+ *
28
+ * // Start listening for messages
29
+ * preview.connect()
30
+ *
31
+ * // Get current preview state
32
+ * const state = preview.getState()
33
+ *
34
+ * // Cleanup when done
35
+ * preview.disconnect()
36
+ * ```
37
+ */
38
+ declare function createPreviewClient(config?: PreviewConfig): {
39
+ /**
40
+ * Start listening for preview messages.
41
+ * Call this on mount.
42
+ */
43
+ connect(): void;
44
+ /**
45
+ * Stop listening for preview messages.
46
+ * Call this on unmount.
47
+ */
48
+ disconnect(): void;
49
+ /**
50
+ * Get current preview state.
51
+ */
52
+ getState(): PreviewState;
53
+ /**
54
+ * Check if preview mode is active.
55
+ */
56
+ isEnabled(): boolean;
57
+ /**
58
+ * Get a preview value by path.
59
+ * Returns undefined if no preview value exists.
60
+ */
61
+ getValue<T = unknown>(path: string): T | undefined;
62
+ /**
63
+ * Subscribe to state changes.
64
+ */
65
+ subscribe(listener: PreviewListener): () => void;
66
+ /**
67
+ * Notify the editor that the preview has navigated.
68
+ */
69
+ notifyNavigation(pathname: string): void;
70
+ /**
71
+ * Notify the editor that a field was clicked in the preview.
72
+ * The editor will scroll to the corresponding field.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * // On click of an element with data-preview-field attribute:
77
+ * const path = el.closest('[data-preview-field]')?.dataset.previewField
78
+ * if (path) preview.notifyFieldFocus(path)
79
+ * ```
80
+ */
81
+ notifyFieldFocus(path: string): void;
82
+ };
83
+ /**
84
+ * Get a nested value by path (supports "items[0].name" array notation)
85
+ */
86
+ declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
87
+ /**
88
+ * Deep merge two objects
89
+ */
90
+ declare function deepMerge<T extends Record<string, unknown>>(target: T, source: Record<string, unknown>): T;
91
+
92
+ export { type PreviewListener as P, createPreviewClient as c, deepMerge as d, getNestedValue as g };
@@ -0,0 +1,100 @@
1
+ import { Attributes, GrowthBook, StickyBucketService } from '@growthbook/growthbook';
2
+ export { Attributes, GrowthBook } from '@growthbook/growthbook';
3
+
4
+ /**
5
+ * @typecms/sdk/flags
6
+ *
7
+ * Feature flags client for Type CMS. A thin, typed wrapper around the
8
+ * GrowthBook JS SDK (peer dependency) pointed at Type CMS as the flag
9
+ * backend — Type CMS serves the GrowthBook SDK payload contract at
10
+ * `GET {apiHost}/api/features/{clientKey}`.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { initFlags } from '@typecms/sdk/flags'
15
+ *
16
+ * const flags = await initFlags({
17
+ * clientKey: 'sdk_xxxxxxxx',
18
+ * attributes: { id: user.id, plan: user.plan },
19
+ * })
20
+ *
21
+ * if (flags.isOn('new-checkout')) {
22
+ * // ...
23
+ * }
24
+ * ```
25
+ */
26
+
27
+ interface TypeCMSFlagsConfig {
28
+ /** SDK connection client key (Project settings → Feature Flags → SDK connections) */
29
+ clientKey: string;
30
+ /** Override for self-hosted / regional API hosts */
31
+ apiHost?: string;
32
+ /** Targeting attributes (id, plan, country, …) */
33
+ attributes?: Attributes;
34
+ /**
35
+ * Decryption key for connections created with payload encryption.
36
+ * Never ship alongside truly sensitive flag values — encryption hides
37
+ * payloads from casual inspection, it is not access control.
38
+ */
39
+ decryptionKey?: string;
40
+ /** Callback fired on experiment exposure (wire to your analytics) */
41
+ trackingCallback?: ConstructorParameters<typeof GrowthBook>[0] extends infer C ? C extends {
42
+ trackingCallback?: infer T;
43
+ } ? T : never : never;
44
+ /** Enable SSE streaming updates when the host supports it (off by default) */
45
+ streaming?: boolean;
46
+ /**
47
+ * Resolve Type CMS audience membership for this visitor (headers, cookies,
48
+ * geo) and inject it as the `audiences` attribute, so flag conditions can
49
+ * target audiences: { audiences: { $in: ["beta-users"] } }. Adds one
50
+ * uncached request during init.
51
+ */
52
+ resolveAudiences?: boolean;
53
+ /**
54
+ * Sticky bucketing: keep experiment assignments consistent across
55
+ * sessions/devices even when weights or coverage change mid-experiment.
56
+ * 'cookie' / 'localStorage' use the GrowthBook browser implementations;
57
+ * pass a custom StickyBucketService for server-backed storage.
58
+ */
59
+ stickyBucketing?: 'cookie' | 'localStorage' | StickyBucketService;
60
+ }
61
+ /** Structural match for GrowthBook's js-cookie-shaped `JsCookiesCompat`. */
62
+ interface CookieAttributes {
63
+ path?: string;
64
+ expires?: number | Date;
65
+ domain?: string;
66
+ secure?: boolean;
67
+ sameSite?: string;
68
+ }
69
+ interface DocumentCookieAdapter {
70
+ set(name: string, value: string, options?: CookieAttributes): string | undefined;
71
+ get(name: string): string | undefined;
72
+ get(): Record<string, string>;
73
+ remove(name: string, options?: CookieAttributes): void;
74
+ }
75
+ /**
76
+ * Minimal document.cookie adapter satisfying GrowthBook's js-cookie-shaped
77
+ * dependency, so 'cookie' sticky bucketing works without shipping js-cookie.
78
+ * Exported for tests; not part of the public API surface.
79
+ */
80
+ declare function createDocumentCookieAdapter(): DocumentCookieAdapter;
81
+ /**
82
+ * Create a GrowthBook instance configured for Type CMS. Does not fetch —
83
+ * call `init()` yourself or use `initFlags` below.
84
+ */
85
+ declare function createFlagsClient(config: TypeCMSFlagsConfig): GrowthBook;
86
+ /**
87
+ * Fetch this visitor's Type CMS audience memberships (slugs). Audience rules
88
+ * evaluate from request attributes (IP/geo/headers) at the API edge, not from
89
+ * an authenticated session — so this request is credential-less (`omit`). The
90
+ * attributes endpoint deliberately does not accept credentials; sending them
91
+ * would only get the response blocked cross-origin. Failures return [] — flags
92
+ * must not break when the attributes endpoint is unreachable.
93
+ */
94
+ declare function fetchAudiences(config: TypeCMSFlagsConfig): Promise<string[]>;
95
+ /**
96
+ * Create + initialize (fetch the payload) in one call.
97
+ */
98
+ declare function initFlags(config: TypeCMSFlagsConfig): Promise<GrowthBook>;
99
+
100
+ export { type DocumentCookieAdapter, type TypeCMSFlagsConfig, createDocumentCookieAdapter, createFlagsClient, fetchAudiences, initFlags };
@@ -0,0 +1,99 @@
1
+ // src/flags/index.ts
2
+ import {
3
+ GrowthBook,
4
+ BrowserCookieStickyBucketService,
5
+ LocalStorageStickyBucketService
6
+ } from "@growthbook/growthbook";
7
+ var DEFAULT_API_HOST = "https://api.typecms.com";
8
+ function createDocumentCookieAdapter() {
9
+ const readAll = () => {
10
+ const out = {};
11
+ if (typeof document === "undefined" || !document.cookie) return out;
12
+ for (const pair of document.cookie.split("; ")) {
13
+ const eq = pair.indexOf("=");
14
+ if (eq === -1) continue;
15
+ out[decodeURIComponent(pair.slice(0, eq))] = decodeURIComponent(pair.slice(eq + 1));
16
+ }
17
+ return out;
18
+ };
19
+ function get(name) {
20
+ const all = readAll();
21
+ return name === void 0 ? all : all[name];
22
+ }
23
+ return {
24
+ get,
25
+ set(name, value, options) {
26
+ if (typeof document === "undefined") return void 0;
27
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}; path=${options?.path ?? "/"}`;
28
+ if (options?.expires !== void 0) {
29
+ const expires = typeof options.expires === "number" ? new Date(Date.now() + options.expires * 864e5) : options.expires;
30
+ cookie += `; expires=${expires.toUTCString()}`;
31
+ }
32
+ if (options?.domain) cookie += `; domain=${options.domain}`;
33
+ if (options?.secure) cookie += "; secure";
34
+ if (options?.sameSite) cookie += `; samesite=${options.sameSite}`;
35
+ document.cookie = cookie;
36
+ return cookie;
37
+ },
38
+ remove(name, options) {
39
+ this.set(name, "", { ...options, expires: -1 });
40
+ }
41
+ };
42
+ }
43
+ function resolveStickyBucketService(config) {
44
+ if (!config) return void 0;
45
+ if (config === "cookie") {
46
+ return new BrowserCookieStickyBucketService({ jsCookie: createDocumentCookieAdapter() });
47
+ }
48
+ if (config === "localStorage") return new LocalStorageStickyBucketService();
49
+ return config;
50
+ }
51
+ function createFlagsClient(config) {
52
+ const { clientKey, apiHost, attributes, decryptionKey, trackingCallback, streaming } = config;
53
+ if (!clientKey || !clientKey.startsWith("sdk_")) {
54
+ throw new Error("@typecms/sdk/flags: clientKey must be an SDK connection key (sdk_...)");
55
+ }
56
+ return new GrowthBook({
57
+ apiHost: (apiHost ?? DEFAULT_API_HOST).replace(/\/$/, ""),
58
+ clientKey,
59
+ attributes,
60
+ decryptionKey,
61
+ trackingCallback,
62
+ backgroundSync: streaming === true,
63
+ stickyBucketService: resolveStickyBucketService(config.stickyBucketing)
64
+ });
65
+ }
66
+ async function fetchAudiences(config) {
67
+ const host = (config.apiHost ?? DEFAULT_API_HOST).replace(/\/$/, "");
68
+ try {
69
+ const response = await fetch(`${host}/api/features/${config.clientKey}/attributes`, {
70
+ credentials: "omit"
71
+ });
72
+ if (!response.ok) return [];
73
+ const data = await response.json();
74
+ return Array.isArray(data.audiences) ? data.audiences : [];
75
+ } catch {
76
+ return [];
77
+ }
78
+ }
79
+ async function initFlags(config) {
80
+ const gb = createFlagsClient(config);
81
+ if (config.resolveAudiences) {
82
+ const [audiences] = await Promise.all([
83
+ fetchAudiences(config),
84
+ gb.init({ streaming: config.streaming === true })
85
+ ]);
86
+ gb.setAttributes({ ...gb.getAttributes(), audiences });
87
+ } else {
88
+ await gb.init({ streaming: config.streaming === true });
89
+ }
90
+ return gb;
91
+ }
92
+ export {
93
+ GrowthBook,
94
+ createDocumentCookieAdapter,
95
+ createFlagsClient,
96
+ fetchAudiences,
97
+ initFlags
98
+ };
99
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/flags/index.ts"],"sourcesContent":["/**\n * @typecms/sdk/flags\n *\n * Feature flags client for Type CMS. A thin, typed wrapper around the\n * GrowthBook JS SDK (peer dependency) pointed at Type CMS as the flag\n * backend — Type CMS serves the GrowthBook SDK payload contract at\n * `GET {apiHost}/api/features/{clientKey}`.\n *\n * @example\n * ```typescript\n * import { initFlags } from '@typecms/sdk/flags'\n *\n * const flags = await initFlags({\n * clientKey: 'sdk_xxxxxxxx',\n * attributes: { id: user.id, plan: user.plan },\n * })\n *\n * if (flags.isOn('new-checkout')) {\n * // ...\n * }\n * ```\n */\n\nimport {\n GrowthBook,\n BrowserCookieStickyBucketService,\n LocalStorageStickyBucketService,\n} from '@growthbook/growthbook'\nimport type { Attributes, StickyBucketService } from '@growthbook/growthbook'\n\nconst DEFAULT_API_HOST = 'https://api.typecms.com'\n\nexport interface TypeCMSFlagsConfig {\n /** SDK connection client key (Project settings → Feature Flags → SDK connections) */\n clientKey: string\n /** Override for self-hosted / regional API hosts */\n apiHost?: string\n /** Targeting attributes (id, plan, country, …) */\n attributes?: Attributes\n /**\n * Decryption key for connections created with payload encryption.\n * Never ship alongside truly sensitive flag values — encryption hides\n * payloads from casual inspection, it is not access control.\n */\n decryptionKey?: string\n /** Callback fired on experiment exposure (wire to your analytics) */\n trackingCallback?: ConstructorParameters<typeof GrowthBook>[0] extends infer C\n ? C extends { trackingCallback?: infer T }\n ? T\n : never\n : never\n /** Enable SSE streaming updates when the host supports it (off by default) */\n streaming?: boolean\n /**\n * Resolve Type CMS audience membership for this visitor (headers, cookies,\n * geo) and inject it as the `audiences` attribute, so flag conditions can\n * target audiences: { audiences: { $in: [\"beta-users\"] } }. Adds one\n * uncached request during init.\n */\n resolveAudiences?: boolean\n /**\n * Sticky bucketing: keep experiment assignments consistent across\n * sessions/devices even when weights or coverage change mid-experiment.\n * 'cookie' / 'localStorage' use the GrowthBook browser implementations;\n * pass a custom StickyBucketService for server-backed storage.\n */\n stickyBucketing?: 'cookie' | 'localStorage' | StickyBucketService\n}\n\n/** Structural match for GrowthBook's js-cookie-shaped `JsCookiesCompat`. */\ninterface CookieAttributes {\n path?: string\n expires?: number | Date\n domain?: string\n secure?: boolean\n sameSite?: string\n}\n\nexport interface DocumentCookieAdapter {\n set(name: string, value: string, options?: CookieAttributes): string | undefined\n get(name: string): string | undefined\n get(): Record<string, string>\n remove(name: string, options?: CookieAttributes): void\n}\n\n/**\n * Minimal document.cookie adapter satisfying GrowthBook's js-cookie-shaped\n * dependency, so 'cookie' sticky bucketing works without shipping js-cookie.\n * Exported for tests; not part of the public API surface.\n */\nexport function createDocumentCookieAdapter(): DocumentCookieAdapter {\n const readAll = (): Record<string, string> => {\n const out: Record<string, string> = {}\n if (typeof document === 'undefined' || !document.cookie) return out\n for (const pair of document.cookie.split('; ')) {\n const eq = pair.indexOf('=')\n if (eq === -1) continue\n out[decodeURIComponent(pair.slice(0, eq))] = decodeURIComponent(pair.slice(eq + 1))\n }\n return out\n }\n\n function get(): Record<string, string>\n function get(name: string): string | undefined\n function get(name?: string): Record<string, string> | string | undefined {\n const all = readAll()\n return name === undefined ? all : all[name]\n }\n\n return {\n get,\n set(name, value, options) {\n if (typeof document === 'undefined') return undefined\n let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}; path=${options?.path ?? '/'}`\n if (options?.expires !== undefined) {\n const expires =\n typeof options.expires === 'number'\n ? new Date(Date.now() + options.expires * 864e5)\n : options.expires\n cookie += `; expires=${expires.toUTCString()}`\n }\n if (options?.domain) cookie += `; domain=${options.domain}`\n if (options?.secure) cookie += '; secure'\n if (options?.sameSite) cookie += `; samesite=${options.sameSite}`\n document.cookie = cookie\n return cookie\n },\n remove(name, options) {\n this.set(name, '', { ...options, expires: -1 })\n },\n }\n}\n\nfunction resolveStickyBucketService(\n config: TypeCMSFlagsConfig['stickyBucketing']\n): StickyBucketService | undefined {\n if (!config) return undefined\n if (config === 'cookie') {\n return new BrowserCookieStickyBucketService({ jsCookie: createDocumentCookieAdapter() })\n }\n if (config === 'localStorage') return new LocalStorageStickyBucketService()\n return config\n}\n\n/**\n * Create a GrowthBook instance configured for Type CMS. Does not fetch —\n * call `init()` yourself or use `initFlags` below.\n */\nexport function createFlagsClient(config: TypeCMSFlagsConfig): GrowthBook {\n const { clientKey, apiHost, attributes, decryptionKey, trackingCallback, streaming } = config\n\n if (!clientKey || !clientKey.startsWith('sdk_')) {\n throw new Error('@typecms/sdk/flags: clientKey must be an SDK connection key (sdk_...)')\n }\n\n return new GrowthBook({\n apiHost: (apiHost ?? DEFAULT_API_HOST).replace(/\\/$/, ''),\n clientKey,\n attributes,\n decryptionKey,\n trackingCallback,\n backgroundSync: streaming === true,\n stickyBucketService: resolveStickyBucketService(config.stickyBucketing),\n })\n}\n\n/**\n * Fetch this visitor's Type CMS audience memberships (slugs). Audience rules\n * evaluate from request attributes (IP/geo/headers) at the API edge, not from\n * an authenticated session — so this request is credential-less (`omit`). The\n * attributes endpoint deliberately does not accept credentials; sending them\n * would only get the response blocked cross-origin. Failures return [] — flags\n * must not break when the attributes endpoint is unreachable.\n */\nexport async function fetchAudiences(config: TypeCMSFlagsConfig): Promise<string[]> {\n const host = (config.apiHost ?? DEFAULT_API_HOST).replace(/\\/$/, '')\n try {\n const response = await fetch(`${host}/api/features/${config.clientKey}/attributes`, {\n credentials: 'omit',\n })\n if (!response.ok) return []\n const data = (await response.json()) as { audiences?: string[] }\n return Array.isArray(data.audiences) ? data.audiences : []\n } catch {\n return []\n }\n}\n\n/**\n * Create + initialize (fetch the payload) in one call.\n */\nexport async function initFlags(config: TypeCMSFlagsConfig): Promise<GrowthBook> {\n const gb = createFlagsClient(config)\n\n if (config.resolveAudiences) {\n // Audience resolution and payload fetch run concurrently\n const [audiences] = await Promise.all([\n fetchAudiences(config),\n gb.init({ streaming: config.streaming === true }),\n ])\n gb.setAttributes({ ...gb.getAttributes(), audiences })\n } else {\n await gb.init({ streaming: config.streaming === true })\n }\n\n return gb\n}\n\n// Re-export the evaluation surface so consumers don't need a direct\n// @growthbook/growthbook import for common usage.\nexport { GrowthBook }\nexport type { Attributes }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,67 @@
1
+ import { TypeCMSClientConfig, TypeCMSClient, SortExpression } from './types/index.mjs';
2
+ export { APIError, Address, Asset, AssetType, BaseComponent, BaseEntry, CollectionReference, ContentEntry, EntryList, EntryReference, ExtractComponent, GetEntriesOptions, GetEntryOptions, Link, RichTextNode, SearchHit, SearchOptions, SearchResult, TipTapMark, TipTapNode, isComponentType } from './types/index.mjs';
3
+
4
+ /**
5
+ * Type CMS SDK Client
6
+ *
7
+ * Typed client for the Type CMS Content Delivery API
8
+ * (`POST /api/projects/:projectId/content` and `/content/search`).
9
+ *
10
+ * Design notes:
11
+ * - `getEntry` returns `null` (and `getEntries`/`search` return empty pages)
12
+ * when nothing matches — the API's "empty result" 404 is not an exception.
13
+ * - Personalization is first-class: set a client-wide `variant` or pass one
14
+ * per call; when the variant has no published row the base content is
15
+ * served automatically (opt out with `variantFallback: false`).
16
+ * - Drafts: `draft: true` reads unpublished content through the same shapes,
17
+ * gated server-side to preview-environment tokens.
18
+ */
19
+
20
+ /**
21
+ * Error thrown when an API request fails (auth, validation, server errors).
22
+ * Empty results are NOT errors — the typed reads return `null`/empty pages.
23
+ */
24
+ declare class TypeCMSError extends Error {
25
+ /** HTTP status code */
26
+ readonly status: number;
27
+ /** Validation error details from the API, when present */
28
+ readonly details?: unknown[];
29
+ constructor(message: string, status: number, details?: unknown[]);
30
+ }
31
+ /** `'-publishedAt'` → `'publishedAt:-1'`, `'title'` → `'title:1'`. */
32
+ declare function toApiSort(sort: SortExpression): string;
33
+ /**
34
+ * Create a Type CMS content client.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { createClient } from '@typecms/sdk'
39
+ *
40
+ * const cms = createClient({
41
+ * projectId: '665f00000000000000000001',
42
+ * token: process.env.TYPECMS_TOKEN!,
43
+ * })
44
+ *
45
+ * // By URL path — null when nothing is published there
46
+ * const home = await cms.getEntry<HomePage>({ path: '/' })
47
+ *
48
+ * // Personalized, with automatic fallback to base content
49
+ * const vipHome = await cms.getEntry({ path: '/', variant: 'vip' })
50
+ *
51
+ * // Lists
52
+ * const { entries, count } = await cms.getEntries({
53
+ * templateId: '665f00000000000000000002',
54
+ * sort: '-publishedAt',
55
+ * limit: 10,
56
+ * })
57
+ *
58
+ * // Drafts for live preview (preview-environment token)
59
+ * const draft = await cms.getEntry({ path: '/', draft: true })
60
+ *
61
+ * // Full-text search
62
+ * const hits = await cms.search('astronaut', { limit: 5 })
63
+ * ```
64
+ */
65
+ declare function createClient(config: TypeCMSClientConfig): TypeCMSClient;
66
+
67
+ export { SortExpression, TypeCMSClient, TypeCMSClientConfig, TypeCMSError, createClient, toApiSort };