@brandfine/client 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +41 -0
  3. package/dist/cache/index.cjs +16 -0
  4. package/dist/cache/index.cjs.map +1 -0
  5. package/dist/cache/index.d.cts +61 -0
  6. package/dist/cache/index.d.ts +61 -0
  7. package/dist/cache/index.js +3 -0
  8. package/dist/cache/index.js.map +1 -0
  9. package/dist/chunk-DHQHUIFO.js +95 -0
  10. package/dist/chunk-DHQHUIFO.js.map +1 -0
  11. package/dist/chunk-KHHMR2NX.cjs +75 -0
  12. package/dist/chunk-KHHMR2NX.cjs.map +1 -0
  13. package/dist/chunk-MTBSSTTG.js +101 -0
  14. package/dist/chunk-MTBSSTTG.js.map +1 -0
  15. package/dist/chunk-OKIEA3AD.cjs +107 -0
  16. package/dist/chunk-OKIEA3AD.cjs.map +1 -0
  17. package/dist/chunk-QQLAYITF.js +71 -0
  18. package/dist/chunk-QQLAYITF.js.map +1 -0
  19. package/dist/chunk-XJFKL2HU.cjs +98 -0
  20. package/dist/chunk-XJFKL2HU.cjs.map +1 -0
  21. package/dist/index-_XBy9Y81.d.cts +262 -0
  22. package/dist/index-_XBy9Y81.d.ts +262 -0
  23. package/dist/index.cjs +159 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +107 -0
  26. package/dist/index.d.ts +107 -0
  27. package/dist/index.js +115 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/resolvers/index.cjs +28 -0
  30. package/dist/resolvers/index.cjs.map +1 -0
  31. package/dist/resolvers/index.d.cts +1 -0
  32. package/dist/resolvers/index.d.ts +1 -0
  33. package/dist/resolvers/index.js +3 -0
  34. package/dist/resolvers/index.js.map +1 -0
  35. package/dist/webhook/index.cjs +20 -0
  36. package/dist/webhook/index.cjs.map +1 -0
  37. package/dist/webhook/index.d.cts +117 -0
  38. package/dist/webhook/index.d.ts +117 -0
  39. package/dist/webhook/index.js +3 -0
  40. package/dist/webhook/index.js.map +1 -0
  41. package/package.json +82 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Shared public types for the Brandfine SDK.
3
+ *
4
+ * Types that round-trip with the API are kept here so consumers
5
+ * can import them directly (`import type { BrandfinePost } from
6
+ * '@brandfine/client'`). Implementation-internal types stay
7
+ * private to their respective modules.
8
+ */
9
+ /**
10
+ * A post as returned by `/external/posts`. `TConfig` lets consumers
11
+ * narrow the `customConfig` field — Brandfine round-trips arbitrary
12
+ * JSON here without interpretation, so each consumer site defines
13
+ * its own shape (olavisa uses it for `apply`, `faq`, etc.).
14
+ *
15
+ * Default is `unknown` so misuse is caught at the type level rather
16
+ * than letting `any` leak into consumer code.
17
+ */
18
+ type BrandfinePost<TConfig = unknown> = {
19
+ postId: string;
20
+ /** Per-locale URL slug. */
21
+ slug: string;
22
+ /** Stable identifier shared across all locale variants of the
23
+ * same article. Use this for cross-locale lookups. */
24
+ canonicalSlug: string;
25
+ title: string;
26
+ metaDescription: string | null;
27
+ imageUrl: string | null;
28
+ tags: string[];
29
+ /** Wyrote-specific taxonomy fields, kept generic so other sources
30
+ * can populate them too. Null when not used. */
31
+ offerTitle: string | null;
32
+ offerSlug: string | null;
33
+ topicTitle: string | null;
34
+ topicSlug: string | null;
35
+ category: BrandfineCategory | null;
36
+ authorName: string | null;
37
+ customConfig: TConfig | null;
38
+ publishedAt: string;
39
+ contentHtml: string | null;
40
+ contentMarkdown: string | null;
41
+ /** Optional per-post JSON-LD payload. Shape is consumer-defined;
42
+ * Brandfine round-trips it untouched. */
43
+ jsonLd: unknown;
44
+ };
45
+ type BrandfinePostListResponse<TConfig = unknown> = {
46
+ items: BrandfinePost<TConfig>[];
47
+ pageInfo: {
48
+ page: number;
49
+ limit: number;
50
+ total: number;
51
+ totalPages: number;
52
+ hasNext: boolean;
53
+ hasPrev: boolean;
54
+ };
55
+ };
56
+ type BrandfineCategory = {
57
+ id: string;
58
+ slug: string;
59
+ name: string;
60
+ };
61
+ /**
62
+ * Workspace metadata + config. `TCustomConfig` and `TSchemaOrg`
63
+ * narrow the two JSON payload fields when the consumer wants
64
+ * stronger typing — both default to `Record<string, unknown>` so
65
+ * consumers can opt into typing incrementally.
66
+ */
67
+ type BrandfineWorkspace<TCustomConfig = Record<string, unknown>, TSchemaOrg = Record<string, unknown>> = {
68
+ id: string;
69
+ slug: string;
70
+ name: string;
71
+ /** BCP47 locale codes the workspace serves. */
72
+ locales: string[];
73
+ defaultLocale: string;
74
+ customConfig: TCustomConfig | null;
75
+ /** Schema.org structured-data payload keyed by `@type` (Organization,
76
+ * WebSite, LocalBusiness, …). Merged by the consumer into per-page
77
+ * JSON-LD output. */
78
+ schemaOrg: TSchemaOrg | null;
79
+ };
80
+ type BrandfineNavItemType = 'CUSTOM_URL' | 'POST' | 'HEADING';
81
+ type BrandfineNavItem = {
82
+ id: string;
83
+ parentId: string | null;
84
+ position: number;
85
+ type: BrandfineNavItemType;
86
+ /** CUSTOM_URL: free-form path or absolute URL. */
87
+ customUrl: string | null;
88
+ /** `{ [locale]: string }` map. May be null on POST items —
89
+ * consumers fall back to the post's per-locale `title` then. */
90
+ labels: Record<string, string> | null;
91
+ /** Locales where the item should not render. */
92
+ hiddenLocales: string[];
93
+ /** POST items only. Pre-resolved by the API so consumers can build
94
+ * per-locale URLs without a second round-trip. */
95
+ post: BrandfineNavPost | null;
96
+ };
97
+ type BrandfineNavPost = {
98
+ id: string;
99
+ canonicalSlug: string;
100
+ postTypeId: string | null;
101
+ /** Post-type slug — typically used by consumers as the URL prefix
102
+ * (`/${postTypeSlug}/${slug}`). Null when the post's post type was
103
+ * deleted (orphaned). */
104
+ postTypeSlug: string | null;
105
+ /** Every published translation of this post — `slug` is the
106
+ * per-locale URL slug, `title` is the per-locale title. Consumers
107
+ * pick the entry matching the active locale, falling back to the
108
+ * workspace default when missing. */
109
+ locales: Array<{
110
+ locale: string;
111
+ slug: string;
112
+ title: string;
113
+ }>;
114
+ };
115
+ type BrandfineNavigation = {
116
+ key: string;
117
+ name: string;
118
+ items: BrandfineNavItem[];
119
+ };
120
+ type ListPostsOptions = {
121
+ /** Post-type slug. Default server-side is `'blog'`; pass `'*'` to
122
+ * fetch every type. */
123
+ type?: string;
124
+ /** BCP47 locale filter. Omit to fetch every locale. */
125
+ locale?: string;
126
+ /** Opt past the API's default 50-per-page cap via the cms's
127
+ * `force_limit=` escape hatch. Worth doing for content types
128
+ * with 50+ entries to avoid multiple round-trips. */
129
+ forceLimit?: number;
130
+ };
131
+ type ListCategoriesOptions = {
132
+ /** Restrict to categories that have at least one published post
133
+ * in this locale. Defaults server-side to the workspace's
134
+ * `defaultLocale`. Pass `'*'` to ignore the filter. */
135
+ locale?: string;
136
+ };
137
+
138
+ /**
139
+ * Locale helpers.
140
+ *
141
+ * Pure functions over BCP47 locale codes. Olavisa's in-tree
142
+ * version reads `LOCALES` and `DEFAULT_LOCALE` from module-level
143
+ * constants — the package takes them as options instead so a
144
+ * single SDK instance can serve callers with different locale
145
+ * sets (multi-tenant, preview environments, tests).
146
+ *
147
+ * The conventional URL shape these helpers assume:
148
+ * - The `defaultLocale` is served at the bare URL: `/about`
149
+ * - Other locales get a path prefix: `/pt/about`, `/es/about`
150
+ * - `pickLocale` falls back to `defaultLocale` for unknown
151
+ * inputs. Don't throw — `Astro.currentLocale` is `string |
152
+ * undefined`, and callers shouldn't have to defend against
153
+ * every framework's quirk.
154
+ */
155
+ type LocaleOptions = {
156
+ /** Locales the consumer serves, in any order. The list is used
157
+ * for membership checks (`isLocale`), prefix detection
158
+ * (`stripLocalePrefix`), and as the codomain of `pickLocale`. */
159
+ locales: readonly string[];
160
+ /** The locale served at bare URLs. Must appear in `locales`. */
161
+ defaultLocale: string;
162
+ };
163
+ /**
164
+ * Type guard. Narrows `unknown` inputs to a known locale so the
165
+ * caller can use them without further coercion.
166
+ *
167
+ * if (isLocale(value, ['en', 'pt'])) {
168
+ * // value: string (known to be 'en' | 'pt' at runtime)
169
+ * }
170
+ */
171
+ declare function isLocale(value: unknown, locales: readonly string[]): value is string;
172
+ /**
173
+ * Coerce arbitrary input into a known locale, falling back to
174
+ * `defaultLocale` for unknowns. Convenient at the consumer's
175
+ * framework boundary (Astro.currentLocale, request headers, etc.).
176
+ */
177
+ declare function pickLocale(input: unknown, opts: LocaleOptions): string;
178
+ /**
179
+ * Convert a canonical (default-locale) path into the locale-prefixed
180
+ * variant for `locale`. The default locale's URLs are bare; every
181
+ * other locale prefixes with `/<locale>`.
182
+ *
183
+ * localizePath('/services/uk-eta', 'en', { defaultLocale: 'en' })
184
+ * → '/services/uk-eta'
185
+ * localizePath('/services/uk-eta', 'pt', { defaultLocale: 'en' })
186
+ * → '/pt/services/uk-eta'
187
+ * localizePath('/', 'pt', { defaultLocale: 'en' })
188
+ * → '/pt'
189
+ */
190
+ declare function localizePath(path: string, locale: string, opts: {
191
+ defaultLocale: string;
192
+ }): string;
193
+ /**
194
+ * Strip a non-default locale prefix off a pathname. The inverse
195
+ * of `localizePath` — useful for normalising back to a canonical
196
+ * path before re-localising for a different locale (the language
197
+ * switcher's main job).
198
+ *
199
+ * stripLocalePrefix('/pt/about', ...) → '/about'
200
+ * stripLocalePrefix('/about', ...) → '/about' (already canonical)
201
+ * stripLocalePrefix('/pt', ...) → '/'
202
+ * stripLocalePrefix('/en/foo', { defaultLocale: 'en', … })
203
+ * → '/en/foo' (en is default — no prefix to strip)
204
+ */
205
+ declare function stripLocalePrefix(pathname: string, opts: LocaleOptions): string;
206
+
207
+ /**
208
+ * Navigation resolver.
209
+ *
210
+ * Turns a `BrandfineNavigation` (locale-agnostic shape from the
211
+ * external API) into a `HydratedNav` ready for a specific locale —
212
+ * URLs computed, labels picked, hidden items dropped, children
213
+ * grouped under their parent. Header / Footer components consume
214
+ * the hydrated form directly.
215
+ *
216
+ * The semantics encoded here come from olavisa but are
217
+ * generalised:
218
+ * - POST items resolve URLs via `post.locales`. If the active
219
+ * locale has no translation, fall back to the default-locale
220
+ * URL. Consumers can override the URL pattern entirely via
221
+ * `urlForPost`.
222
+ * - CUSTOM_URL items: paths (start with `/`) run through
223
+ * `localizePath`; anything with a scheme (https://, mailto:)
224
+ * passes through unchanged.
225
+ * - Labels: per-locale override → default-locale label → any
226
+ * populated label (last-resort, covers single-locale fills) →
227
+ * for POST items, the post's per-locale title.
228
+ * - `hiddenLocales` drops the item entirely for that locale.
229
+ */
230
+
231
+ type HydratedNavItem = {
232
+ /** Final URL, ready to render in an `<a href>`. `null` for
233
+ * HEADING items (label-only) and for POST items whose post
234
+ * has been deleted (orphan). */
235
+ href: string | null;
236
+ label: string;
237
+ type: 'CUSTOM_URL' | 'POST' | 'HEADING';
238
+ children: HydratedNavItem[];
239
+ };
240
+ type HydratedNav = {
241
+ key: string;
242
+ name: string;
243
+ items: HydratedNavItem[];
244
+ };
245
+ type ResolveNavigationOptions = {
246
+ defaultLocale: string;
247
+ /** Override the URL pattern for POST items. Default:
248
+ * `localizePath('/${postTypeSlug}/${sibling.slug}', linkLocale)`.
249
+ *
250
+ * Use when a consumer routes posts under a non-default prefix
251
+ * (e.g. `/blog/<slug>` instead of `/posts/<slug>`), or wants
252
+ * to omit the locale prefix for specific post types. */
253
+ urlForPost?: (args: {
254
+ post: BrandfineNavPost;
255
+ sibling: BrandfineNavPost['locales'][number];
256
+ locale: string;
257
+ defaultLocale: string;
258
+ }) => string | null;
259
+ };
260
+ declare function resolveNavigation(nav: BrandfineNavigation, locale: string, opts: ResolveNavigationOptions): HydratedNav;
261
+
262
+ export { type BrandfinePost as B, type HydratedNav as H, type ListPostsOptions as L, type ResolveNavigationOptions as R, type ListCategoriesOptions as a, type BrandfineCategory as b, type BrandfineWorkspace as c, type BrandfineNavigation as d, type BrandfineNavItem as e, type BrandfineNavItemType as f, type BrandfineNavPost as g, type BrandfinePostListResponse as h, type HydratedNavItem as i, type LocaleOptions as j, isLocale as k, localizePath as l, pickLocale as p, resolveNavigation as r, stripLocalePrefix as s };
package/dist/index.cjs ADDED
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ var chunkXJFKL2HU_cjs = require('./chunk-XJFKL2HU.cjs');
4
+ var chunkOKIEA3AD_cjs = require('./chunk-OKIEA3AD.cjs');
5
+ var chunkKHHMR2NX_cjs = require('./chunk-KHHMR2NX.cjs');
6
+
7
+ // src/client.ts
8
+ var BrandfineApiError = class extends Error {
9
+ name = "BrandfineApiError";
10
+ status;
11
+ statusText;
12
+ body;
13
+ url;
14
+ constructor(args) {
15
+ super(
16
+ `[brandfine] ${args.status} ${args.statusText} on ${args.url} \u2014 ${args.body.slice(0, 200)}`
17
+ );
18
+ this.status = args.status;
19
+ this.statusText = args.statusText;
20
+ this.body = args.body;
21
+ this.url = args.url;
22
+ }
23
+ };
24
+ var DEFAULT_USER_AGENT = "@brandfine/client";
25
+ function createBrandfineClient(config) {
26
+ if (!config.baseUrl)
27
+ throw new Error("createBrandfineClient: `baseUrl` is required");
28
+ if (!config.apiKey)
29
+ throw new Error("createBrandfineClient: `apiKey` is required");
30
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
31
+ const apiKey = config.apiKey;
32
+ const fetchImpl = config.fetch ?? globalThis.fetch;
33
+ const userAgent = config.userAgent ?? DEFAULT_USER_AGENT;
34
+ async function get(path, opts = {}) {
35
+ const url = `${baseUrl}${path}`;
36
+ const res = await fetchImpl(url, {
37
+ method: "GET",
38
+ headers: {
39
+ "X-Api-Key": apiKey,
40
+ Accept: "application/json",
41
+ "User-Agent": userAgent
42
+ },
43
+ signal: opts.signal
44
+ });
45
+ if (res.status === 404 && opts.nullable404) {
46
+ await res.text().catch(() => "");
47
+ return null;
48
+ }
49
+ if (!res.ok) {
50
+ const body = await res.text().catch(() => "");
51
+ throw new BrandfineApiError({
52
+ status: res.status,
53
+ statusText: res.statusText,
54
+ body,
55
+ url
56
+ });
57
+ }
58
+ return await res.json();
59
+ }
60
+ const posts = {
61
+ async list(opts = {}) {
62
+ const out = [];
63
+ let page = 1;
64
+ const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : "";
65
+ const localeQuery = opts.locale ? `&locale=${encodeURIComponent(opts.locale)}` : "";
66
+ const sizeQuery = opts.forceLimit ? `&force_limit=${opts.forceLimit}` : "&limit=50";
67
+ const MAX_PAGES = 200;
68
+ while (page <= MAX_PAGES) {
69
+ const data = await get(
70
+ `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`
71
+ );
72
+ out.push(...data.items);
73
+ if (!data.pageInfo.hasNext) break;
74
+ page += 1;
75
+ }
76
+ return out;
77
+ },
78
+ async getBySlug(slug) {
79
+ return get(
80
+ `/external/posts/${encodeURIComponent(slug)}`,
81
+ { nullable404: true }
82
+ );
83
+ }
84
+ };
85
+ const categories = {
86
+ async list(opts = {}) {
87
+ const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : "";
88
+ const data = await get(
89
+ `/external/categories${qs}`
90
+ );
91
+ return data.items;
92
+ }
93
+ };
94
+ const workspace = {
95
+ get() {
96
+ return get(
97
+ "/external/workspace"
98
+ );
99
+ }
100
+ };
101
+ const navigations = {
102
+ get(key) {
103
+ return get(
104
+ `/external/navigations/${encodeURIComponent(key)}`,
105
+ { nullable404: true }
106
+ );
107
+ }
108
+ };
109
+ return { get, posts, categories, workspace, navigations };
110
+ }
111
+
112
+ // src/index.ts
113
+ var SDK_VERSION = "0.0.0";
114
+
115
+ Object.defineProperty(exports, "createCache", {
116
+ enumerable: true,
117
+ get: function () { return chunkXJFKL2HU_cjs.createCache; }
118
+ });
119
+ Object.defineProperty(exports, "createKeyedCache", {
120
+ enumerable: true,
121
+ get: function () { return chunkXJFKL2HU_cjs.createKeyedCache; }
122
+ });
123
+ Object.defineProperty(exports, "isLocale", {
124
+ enumerable: true,
125
+ get: function () { return chunkOKIEA3AD_cjs.isLocale; }
126
+ });
127
+ Object.defineProperty(exports, "localizePath", {
128
+ enumerable: true,
129
+ get: function () { return chunkOKIEA3AD_cjs.localizePath; }
130
+ });
131
+ Object.defineProperty(exports, "pickLocale", {
132
+ enumerable: true,
133
+ get: function () { return chunkOKIEA3AD_cjs.pickLocale; }
134
+ });
135
+ Object.defineProperty(exports, "resolveNavigation", {
136
+ enumerable: true,
137
+ get: function () { return chunkOKIEA3AD_cjs.resolveNavigation; }
138
+ });
139
+ Object.defineProperty(exports, "stripLocalePrefix", {
140
+ enumerable: true,
141
+ get: function () { return chunkOKIEA3AD_cjs.stripLocalePrefix; }
142
+ });
143
+ Object.defineProperty(exports, "createBrandfineWebhookHandler", {
144
+ enumerable: true,
145
+ get: function () { return chunkKHHMR2NX_cjs.createBrandfineWebhookHandler; }
146
+ });
147
+ Object.defineProperty(exports, "parseWebhookPayload", {
148
+ enumerable: true,
149
+ get: function () { return chunkKHHMR2NX_cjs.parseWebhookPayload; }
150
+ });
151
+ Object.defineProperty(exports, "verifyWebhookSecret", {
152
+ enumerable: true,
153
+ get: function () { return chunkKHHMR2NX_cjs.verifyWebhookSecret; }
154
+ });
155
+ exports.BrandfineApiError = BrandfineApiError;
156
+ exports.SDK_VERSION = SDK_VERSION;
157
+ exports.createBrandfineClient = createBrandfineClient;
158
+ //# sourceMappingURL=index.cjs.map
159
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * `createBrandfineClient` — the SDK's entry point.\n *\n * Returns a stateless, multi-instance-safe handle scoped to a\n * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /\n * Algolia / OpenAI SDKs — explicit construction with config,\n * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),\n * no module-level singletons.\n *\n * Why factory not module-level state: multi-tenant consumers\n * sometimes need two clients in the same process (e.g. main site\n * + admin preview). Module-level env reading makes that impossible\n * without monkey-patching.\n */\n\nimport type {\n BrandfineCategory,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n\nexport type BrandfineClientConfig = {\n /** Base URL of the Brandfine API. No trailing slash — the client\n * trims one if you pass it anyway. e.g. `https://api.brandfine.co` */\n baseUrl: string\n /** Workspace-scoped API key. Generated from the cms's Workspace\n * settings; identifies which workspace the client talks to. */\n apiKey: string\n /** Optional fetch override. Useful for tests (inject a stub),\n * for runtimes that need a custom implementation (edge workers\n * with non-standard fetch), or to add cross-cutting concerns\n * like tracing / retries. Defaults to `globalThis.fetch`. */\n fetch?: typeof globalThis.fetch\n /** Optional User-Agent header. Falls back to a generic SDK tag. */\n userAgent?: string\n}\n\n/**\n * Structured error thrown by every request helper on non-2xx\n * responses. Carries the raw body so consumers can log it for\n * debugging without re-fetching.\n */\nexport class BrandfineApiError extends Error {\n override readonly name = 'BrandfineApiError'\n readonly status: number\n readonly statusText: string\n readonly body: string\n readonly url: string\n\n constructor(args: {\n status: number\n statusText: string\n body: string\n url: string\n }) {\n super(\n `[brandfine] ${args.status} ${args.statusText} on ${args.url} — ${args.body.slice(0, 200)}`,\n )\n this.status = args.status\n this.statusText = args.statusText\n this.body = args.body\n this.url = args.url\n }\n}\n\ntype RequestOptions = {\n /** When true and the response is 404, return `null` instead of\n * throwing. Used by endpoints where 404 is a meaningful empty\n * state (navigation by key, single post by slug). */\n nullable404?: boolean\n signal?: AbortSignal\n}\n\nexport type BrandfineClient = {\n /** Low-level GET. Reserved for endpoints we don't have a typed\n * helper for yet. Adds the X-Api-Key header automatically. */\n get: <T>(path: string, opts?: RequestOptions) => Promise<T>\n posts: PostsApi\n categories: CategoriesApi\n workspace: WorkspaceApi\n navigations: NavigationsApi\n}\n\ntype PostsApi = {\n /** Paginated list of published posts. Handles the cms's\n * pagination transparently — caller gets a flat array. */\n list: <TConfig = unknown>(\n opts?: ListPostsOptions,\n ) => Promise<BrandfinePost<TConfig>[]>\n /** Single post by per-locale URL slug, scoped to the active\n * locale on the workspace's content. Returns `null` for 404 so\n * callers can render their own \"not found\" page without try/catch. */\n getBySlug: <TConfig = unknown>(\n slug: string,\n ) => Promise<BrandfinePost<TConfig> | null>\n}\n\ntype CategoriesApi = {\n list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>\n}\n\ntype WorkspaceApi = {\n get: <\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>\n}\n\ntype NavigationsApi = {\n /** Navigation by its workspace-scoped `key` (e.g. `'header'`).\n * Returns `null` for 404 so consumers can fall back to a\n * hardcoded default without try/catch. */\n get: (key: string) => Promise<BrandfineNavigation | null>\n}\n\nconst DEFAULT_USER_AGENT = '@brandfine/client'\n\nexport function createBrandfineClient(\n config: BrandfineClientConfig,\n): BrandfineClient {\n if (!config.baseUrl)\n throw new Error('createBrandfineClient: `baseUrl` is required')\n if (!config.apiKey)\n throw new Error('createBrandfineClient: `apiKey` is required')\n\n const baseUrl = config.baseUrl.replace(/\\/$/, '')\n const apiKey = config.apiKey\n // Resolve fetch lazily so consumers in environments without a\n // global fetch can polyfill before constructing the client.\n const fetchImpl: typeof fetch = config.fetch ?? globalThis.fetch\n const userAgent = config.userAgent ?? DEFAULT_USER_AGENT\n\n async function get<T>(path: string, opts: RequestOptions = {}): Promise<T> {\n const url = `${baseUrl}${path}`\n const res = await fetchImpl(url, {\n method: 'GET',\n headers: {\n 'X-Api-Key': apiKey,\n Accept: 'application/json',\n 'User-Agent': userAgent,\n },\n signal: opts.signal,\n })\n if (res.status === 404 && opts.nullable404) {\n // Drain the body so the underlying socket can be reused —\n // fetch implementations that don't auto-drain (older Node)\n // can leak otherwise.\n await res.text().catch(() => '')\n return null as T\n }\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new BrandfineApiError({\n status: res.status,\n statusText: res.statusText,\n body,\n url,\n })\n }\n return (await res.json()) as T\n }\n\n const posts: PostsApi = {\n async list<TConfig = unknown>(opts: ListPostsOptions = {}) {\n const out: BrandfinePost<TConfig>[] = []\n let page = 1\n const typeQuery = opts.type ? `&type=${encodeURIComponent(opts.type)}` : ''\n const localeQuery = opts.locale\n ? `&locale=${encodeURIComponent(opts.locale)}`\n : ''\n // Default pagination at the cms's 50-per-page cap. `forceLimit`\n // opts past it for content types that would otherwise need\n // many round-trips.\n const sizeQuery = opts.forceLimit\n ? `&force_limit=${opts.forceLimit}`\n : '&limit=50'\n // Pathological safety brake — 200 pages × 50 = 10k posts. If\n // a workspace ever needs more, callers should hit the API\n // directly with their own pagination logic.\n const MAX_PAGES = 200\n while (page <= MAX_PAGES) {\n const data = await get<BrandfinePostListResponse<TConfig>>(\n `/external/posts?include=content${sizeQuery}&page=${page}${typeQuery}${localeQuery}`,\n )\n out.push(...data.items)\n if (!data.pageInfo.hasNext) break\n page += 1\n }\n return out\n },\n async getBySlug<TConfig = unknown>(slug: string) {\n return get<BrandfinePost<TConfig> | null>(\n `/external/posts/${encodeURIComponent(slug)}`,\n { nullable404: true },\n )\n },\n }\n\n const categories: CategoriesApi = {\n async list(opts: ListCategoriesOptions = {}) {\n const qs = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : ''\n const data = await get<{ items: BrandfineCategory[] }>(\n `/external/categories${qs}`,\n )\n return data.items\n },\n }\n\n const workspace: WorkspaceApi = {\n get<\n TCustomConfig = Record<string, unknown>,\n TSchemaOrg = Record<string, unknown>,\n >() {\n return get<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>(\n '/external/workspace',\n )\n },\n }\n\n const navigations: NavigationsApi = {\n get(key: string) {\n return get<BrandfineNavigation | null>(\n `/external/navigations/${encodeURIComponent(key)}`,\n { nullable404: true },\n )\n },\n }\n\n return { get, posts, categories, workspace, navigations }\n}\n","/**\n * @brandfine/client — root entry.\n *\n * The full SDK surface is exposed here for \"import everything from\n * one place\" usage. Tree-shaking + `sideEffects: false` mean\n * consumers don't pay a bundle cost for what they don't import.\n *\n * Heavier or framework-coupled pieces still live under subpath\n * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so\n * consumers with poor tree-shaking — or who only need one slice —\n * can scope their imports.\n */\n\nexport const SDK_VERSION = '0.0.0' as const\n\nexport {\n BrandfineApiError,\n createBrandfineClient,\n type BrandfineClient,\n type BrandfineClientConfig,\n} from './client'\n\nexport {\n createCache,\n createKeyedCache,\n type Cache,\n type CacheOptions,\n type KeyedCache,\n type KeyedCacheOptions,\n} from './cache/index'\n\nexport {\n isLocale,\n localizePath,\n pickLocale,\n resolveNavigation,\n stripLocalePrefix,\n type HydratedNav,\n type HydratedNavItem,\n type LocaleOptions,\n type ResolveNavigationOptions,\n} from './resolvers/index'\n\nexport {\n createBrandfineWebhookHandler,\n parseWebhookPayload,\n verifyWebhookSecret,\n type BrandfineWebhookEvent,\n type BrandfineWebhookHandlerOptions,\n type BrandfineWebhookPayload,\n} from './webhook/index'\n\nexport type {\n BrandfineCategory,\n BrandfineNavItem,\n BrandfineNavItemType,\n BrandfineNavPost,\n BrandfineNavigation,\n BrandfinePost,\n BrandfinePostListResponse,\n BrandfineWorkspace,\n ListCategoriesOptions,\n ListPostsOptions,\n} from './types'\n"]}
@@ -0,0 +1,107 @@
1
+ import { L as ListPostsOptions, B as BrandfinePost, a as ListCategoriesOptions, b as BrandfineCategory, c as BrandfineWorkspace, d as BrandfineNavigation } from './index-_XBy9Y81.cjs';
2
+ export { e as BrandfineNavItem, f as BrandfineNavItemType, g as BrandfineNavPost, h as BrandfinePostListResponse, H as HydratedNav, i as HydratedNavItem, j as LocaleOptions, R as ResolveNavigationOptions, k as isLocale, l as localizePath, p as pickLocale, r as resolveNavigation, s as stripLocalePrefix } from './index-_XBy9Y81.cjs';
3
+ export { Cache, CacheOptions, KeyedCache, KeyedCacheOptions, createCache, createKeyedCache } from './cache/index.cjs';
4
+ export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhookPayload, createBrandfineWebhookHandler, parseWebhookPayload, verifyWebhookSecret } from './webhook/index.cjs';
5
+
6
+ /**
7
+ * `createBrandfineClient` — the SDK's entry point.
8
+ *
9
+ * Returns a stateless, multi-instance-safe handle scoped to a
10
+ * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /
11
+ * Algolia / OpenAI SDKs — explicit construction with config,
12
+ * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),
13
+ * no module-level singletons.
14
+ *
15
+ * Why factory not module-level state: multi-tenant consumers
16
+ * sometimes need two clients in the same process (e.g. main site
17
+ * + admin preview). Module-level env reading makes that impossible
18
+ * without monkey-patching.
19
+ */
20
+
21
+ type BrandfineClientConfig = {
22
+ /** Base URL of the Brandfine API. No trailing slash — the client
23
+ * trims one if you pass it anyway. e.g. `https://api.brandfine.co` */
24
+ baseUrl: string;
25
+ /** Workspace-scoped API key. Generated from the cms's Workspace
26
+ * settings; identifies which workspace the client talks to. */
27
+ apiKey: string;
28
+ /** Optional fetch override. Useful for tests (inject a stub),
29
+ * for runtimes that need a custom implementation (edge workers
30
+ * with non-standard fetch), or to add cross-cutting concerns
31
+ * like tracing / retries. Defaults to `globalThis.fetch`. */
32
+ fetch?: typeof globalThis.fetch;
33
+ /** Optional User-Agent header. Falls back to a generic SDK tag. */
34
+ userAgent?: string;
35
+ };
36
+ /**
37
+ * Structured error thrown by every request helper on non-2xx
38
+ * responses. Carries the raw body so consumers can log it for
39
+ * debugging without re-fetching.
40
+ */
41
+ declare class BrandfineApiError extends Error {
42
+ readonly name = "BrandfineApiError";
43
+ readonly status: number;
44
+ readonly statusText: string;
45
+ readonly body: string;
46
+ readonly url: string;
47
+ constructor(args: {
48
+ status: number;
49
+ statusText: string;
50
+ body: string;
51
+ url: string;
52
+ });
53
+ }
54
+ type RequestOptions = {
55
+ /** When true and the response is 404, return `null` instead of
56
+ * throwing. Used by endpoints where 404 is a meaningful empty
57
+ * state (navigation by key, single post by slug). */
58
+ nullable404?: boolean;
59
+ signal?: AbortSignal;
60
+ };
61
+ type BrandfineClient = {
62
+ /** Low-level GET. Reserved for endpoints we don't have a typed
63
+ * helper for yet. Adds the X-Api-Key header automatically. */
64
+ get: <T>(path: string, opts?: RequestOptions) => Promise<T>;
65
+ posts: PostsApi;
66
+ categories: CategoriesApi;
67
+ workspace: WorkspaceApi;
68
+ navigations: NavigationsApi;
69
+ };
70
+ type PostsApi = {
71
+ /** Paginated list of published posts. Handles the cms's
72
+ * pagination transparently — caller gets a flat array. */
73
+ list: <TConfig = unknown>(opts?: ListPostsOptions) => Promise<BrandfinePost<TConfig>[]>;
74
+ /** Single post by per-locale URL slug, scoped to the active
75
+ * locale on the workspace's content. Returns `null` for 404 so
76
+ * callers can render their own "not found" page without try/catch. */
77
+ getBySlug: <TConfig = unknown>(slug: string) => Promise<BrandfinePost<TConfig> | null>;
78
+ };
79
+ type CategoriesApi = {
80
+ list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>;
81
+ };
82
+ type WorkspaceApi = {
83
+ get: <TCustomConfig = Record<string, unknown>, TSchemaOrg = Record<string, unknown>>() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>;
84
+ };
85
+ type NavigationsApi = {
86
+ /** Navigation by its workspace-scoped `key` (e.g. `'header'`).
87
+ * Returns `null` for 404 so consumers can fall back to a
88
+ * hardcoded default without try/catch. */
89
+ get: (key: string) => Promise<BrandfineNavigation | null>;
90
+ };
91
+ declare function createBrandfineClient(config: BrandfineClientConfig): BrandfineClient;
92
+
93
+ /**
94
+ * @brandfine/client — root entry.
95
+ *
96
+ * The full SDK surface is exposed here for "import everything from
97
+ * one place" usage. Tree-shaking + `sideEffects: false` mean
98
+ * consumers don't pay a bundle cost for what they don't import.
99
+ *
100
+ * Heavier or framework-coupled pieces still live under subpath
101
+ * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so
102
+ * consumers with poor tree-shaking — or who only need one slice —
103
+ * can scope their imports.
104
+ */
105
+ declare const SDK_VERSION: "0.0.0";
106
+
107
+ export { BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, ListCategoriesOptions, ListPostsOptions, SDK_VERSION, createBrandfineClient };
@@ -0,0 +1,107 @@
1
+ import { L as ListPostsOptions, B as BrandfinePost, a as ListCategoriesOptions, b as BrandfineCategory, c as BrandfineWorkspace, d as BrandfineNavigation } from './index-_XBy9Y81.js';
2
+ export { e as BrandfineNavItem, f as BrandfineNavItemType, g as BrandfineNavPost, h as BrandfinePostListResponse, H as HydratedNav, i as HydratedNavItem, j as LocaleOptions, R as ResolveNavigationOptions, k as isLocale, l as localizePath, p as pickLocale, r as resolveNavigation, s as stripLocalePrefix } from './index-_XBy9Y81.js';
3
+ export { Cache, CacheOptions, KeyedCache, KeyedCacheOptions, createCache, createKeyedCache } from './cache/index.js';
4
+ export { BrandfineWebhookEvent, BrandfineWebhookHandlerOptions, BrandfineWebhookPayload, createBrandfineWebhookHandler, parseWebhookPayload, verifyWebhookSecret } from './webhook/index.js';
5
+
6
+ /**
7
+ * `createBrandfineClient` — the SDK's entry point.
8
+ *
9
+ * Returns a stateless, multi-instance-safe handle scoped to a
10
+ * single `(baseUrl, apiKey)` pair. Pattern follows the Stripe /
11
+ * Algolia / OpenAI SDKs — explicit construction with config,
12
+ * namespaced methods (`bf.posts.list(...)`, `bf.workspace.get()`),
13
+ * no module-level singletons.
14
+ *
15
+ * Why factory not module-level state: multi-tenant consumers
16
+ * sometimes need two clients in the same process (e.g. main site
17
+ * + admin preview). Module-level env reading makes that impossible
18
+ * without monkey-patching.
19
+ */
20
+
21
+ type BrandfineClientConfig = {
22
+ /** Base URL of the Brandfine API. No trailing slash — the client
23
+ * trims one if you pass it anyway. e.g. `https://api.brandfine.co` */
24
+ baseUrl: string;
25
+ /** Workspace-scoped API key. Generated from the cms's Workspace
26
+ * settings; identifies which workspace the client talks to. */
27
+ apiKey: string;
28
+ /** Optional fetch override. Useful for tests (inject a stub),
29
+ * for runtimes that need a custom implementation (edge workers
30
+ * with non-standard fetch), or to add cross-cutting concerns
31
+ * like tracing / retries. Defaults to `globalThis.fetch`. */
32
+ fetch?: typeof globalThis.fetch;
33
+ /** Optional User-Agent header. Falls back to a generic SDK tag. */
34
+ userAgent?: string;
35
+ };
36
+ /**
37
+ * Structured error thrown by every request helper on non-2xx
38
+ * responses. Carries the raw body so consumers can log it for
39
+ * debugging without re-fetching.
40
+ */
41
+ declare class BrandfineApiError extends Error {
42
+ readonly name = "BrandfineApiError";
43
+ readonly status: number;
44
+ readonly statusText: string;
45
+ readonly body: string;
46
+ readonly url: string;
47
+ constructor(args: {
48
+ status: number;
49
+ statusText: string;
50
+ body: string;
51
+ url: string;
52
+ });
53
+ }
54
+ type RequestOptions = {
55
+ /** When true and the response is 404, return `null` instead of
56
+ * throwing. Used by endpoints where 404 is a meaningful empty
57
+ * state (navigation by key, single post by slug). */
58
+ nullable404?: boolean;
59
+ signal?: AbortSignal;
60
+ };
61
+ type BrandfineClient = {
62
+ /** Low-level GET. Reserved for endpoints we don't have a typed
63
+ * helper for yet. Adds the X-Api-Key header automatically. */
64
+ get: <T>(path: string, opts?: RequestOptions) => Promise<T>;
65
+ posts: PostsApi;
66
+ categories: CategoriesApi;
67
+ workspace: WorkspaceApi;
68
+ navigations: NavigationsApi;
69
+ };
70
+ type PostsApi = {
71
+ /** Paginated list of published posts. Handles the cms's
72
+ * pagination transparently — caller gets a flat array. */
73
+ list: <TConfig = unknown>(opts?: ListPostsOptions) => Promise<BrandfinePost<TConfig>[]>;
74
+ /** Single post by per-locale URL slug, scoped to the active
75
+ * locale on the workspace's content. Returns `null` for 404 so
76
+ * callers can render their own "not found" page without try/catch. */
77
+ getBySlug: <TConfig = unknown>(slug: string) => Promise<BrandfinePost<TConfig> | null>;
78
+ };
79
+ type CategoriesApi = {
80
+ list: (opts?: ListCategoriesOptions) => Promise<BrandfineCategory[]>;
81
+ };
82
+ type WorkspaceApi = {
83
+ get: <TCustomConfig = Record<string, unknown>, TSchemaOrg = Record<string, unknown>>() => Promise<BrandfineWorkspace<TCustomConfig, TSchemaOrg>>;
84
+ };
85
+ type NavigationsApi = {
86
+ /** Navigation by its workspace-scoped `key` (e.g. `'header'`).
87
+ * Returns `null` for 404 so consumers can fall back to a
88
+ * hardcoded default without try/catch. */
89
+ get: (key: string) => Promise<BrandfineNavigation | null>;
90
+ };
91
+ declare function createBrandfineClient(config: BrandfineClientConfig): BrandfineClient;
92
+
93
+ /**
94
+ * @brandfine/client — root entry.
95
+ *
96
+ * The full SDK surface is exposed here for "import everything from
97
+ * one place" usage. Tree-shaking + `sideEffects: false` mean
98
+ * consumers don't pay a bundle cost for what they don't import.
99
+ *
100
+ * Heavier or framework-coupled pieces still live under subpath
101
+ * exports (`@brandfine/client/cache`, `/resolvers`, `/webhook`) so
102
+ * consumers with poor tree-shaking — or who only need one slice —
103
+ * can scope their imports.
104
+ */
105
+ declare const SDK_VERSION: "0.0.0";
106
+
107
+ export { BrandfineApiError, BrandfineCategory, type BrandfineClient, type BrandfineClientConfig, BrandfineNavigation, BrandfinePost, BrandfineWorkspace, ListCategoriesOptions, ListPostsOptions, SDK_VERSION, createBrandfineClient };