@flexacms/next 0.2.0 → 0.3.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/dist/index.cjs CHANGED
@@ -51,11 +51,11 @@ function flexaTags(path) {
51
51
  }
52
52
  function createFlexaClient(options = {}) {
53
53
  const { revalidate = 60, ...clientOptions } = options;
54
- const url = clientOptions.url ?? process.env.FLEXA_URL;
54
+ const url = clientOptions.url || process.env.FLEXA_URL || import_client.DEFAULT_URL;
55
55
  const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;
56
- if (!url || !apiKey) {
56
+ if (!apiKey) {
57
57
  throw new Error(
58
- 'Flexa: set the FLEXA_URL and FLEXA_API_KEY environment variables, or pass "url" and "apiKey".'
58
+ 'Flexa: set the FLEXA_API_KEY environment variable, or pass "apiKey".'
59
59
  );
60
60
  }
61
61
  const baseFetch = clientOptions.fetch ?? globalThis.fetch;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createClient } from '@flexacms/client';\nimport type {\n ContentSchema,\n FlexaClient,\n FlexaClientOptions,\n} from '@flexacms/client';\nimport { revalidateTag } from 'next/cache';\n\nexport * from '@flexacms/client';\n\n/** Tag on every cached Flexa response. */\nexport const FLEXA_TAG = 'flexa';\n\nexport interface FlexaNextOptions extends Partial<FlexaClientOptions> {\n /**\n * Seconds before a cached response is refreshed in the background, or\n * `false` to keep it until it is revalidated by tag. Defaults to 60.\n */\n revalidate?: number | false;\n}\n\ntype NextFetchInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\n/** The URL of any `fetch` input (string, URL or Request). */\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') {\n return input;\n }\n\n return input instanceof URL ? input.href : input.url;\n}\n\n/**\n * Cache tags for an API path: `flexa`, `flexa:<entity>` and, for one entry,\n * `flexa:<entity>:<id or slug>`.\n */\nexport function flexaTags(path: string): string[] {\n const [entity, entry] = path\n .split('?')[0]!\n .split('/')\n .filter(Boolean)\n .map(decodeURIComponent);\n const tags = [FLEXA_TAG];\n\n if (entity) {\n tags.push(`${FLEXA_TAG}:${entity}`);\n }\n\n if (entity && entry && entry !== 'schema') {\n tags.push(`${FLEXA_TAG}:${entity}:${entry}`);\n }\n\n return tags;\n}\n\n/**\n * A Flexa client for Server Components, Route Handlers and Server Actions.\n *\n * Reads go through Next's data cache, tagged per entity and entry so they can\n * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and\n * API key default to the `FLEXA_URL` and `FLEXA_API_KEY` environment variables.\n * Only use it on the server: the API key must not reach the browser.\n */\nexport function createFlexaClient<TSchema extends object = ContentSchema>(\n options: FlexaNextOptions = {},\n): FlexaClient<TSchema> {\n const { revalidate = 60, ...clientOptions } = options;\n const url = clientOptions.url ?? process.env.FLEXA_URL;\n const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;\n\n if (!url || !apiKey) {\n throw new Error(\n 'Flexa: set the FLEXA_URL and FLEXA_API_KEY environment variables, or pass \"url\" and \"apiKey\".',\n );\n }\n\n const baseFetch = clientOptions.fetch ?? globalThis.fetch;\n let client: FlexaClient<TSchema>;\n\n const cachedFetch = ((\n input: string | URL | Request,\n init: NextFetchInit = {},\n ) => {\n if ((init.method ?? 'GET').toUpperCase() !== 'GET') {\n return baseFetch(input, { ...init, cache: 'no-store' });\n }\n\n const path = new URL(requestUrl(input)).pathname.slice(\n new URL(client.baseUrl).pathname.length,\n );\n\n return baseFetch(input, {\n ...init,\n next: {\n revalidate,\n ...init.next,\n tags: [...flexaTags(path), ...(init.next?.tags ?? [])],\n },\n } as RequestInit);\n }) as typeof fetch;\n\n client = createClient<TSchema>({\n ...clientOptions,\n url,\n apiKey,\n fetch: cachedFetch,\n });\n\n return client;\n}\n\n/**\n * Refresh cached Flexa content: everything, one entity, or one entry. Call it\n * from a Server Action or Route Handler after content changes.\n */\nexport function revalidateFlexa(\n entity?: string,\n idOrSlug?: number | string,\n): void {\n const tag =\n entity === undefined\n ? FLEXA_TAG\n : idOrSlug === undefined\n ? `${FLEXA_TAG}:${entity}`\n : `${FLEXA_TAG}:${entity}:${idOrSlug}`;\n\n revalidateTag(tag, 'max');\n}\n\n/**\n * A `POST` Route Handler that revalidates Flexa content, for webhooks or\n * deploy hooks. Send `{ \"entity\": \"posts\", \"entry\": \"hello-world\" }` (both\n * optional) with the header `Authorization: Bearer <secret>`.\n *\n * // app/api/flexa/revalidate/route.ts\n * export const POST = createRevalidateHandler({ secret: process.env.FLEXA_REVALIDATE_SECRET });\n */\nexport function createRevalidateHandler(options: {\n secret: string | undefined;\n}) {\n return async function POST(request: Request): Promise<Response> {\n if (\n !options.secret ||\n request.headers.get('authorization') !== `Bearer ${options.secret}`\n ) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 });\n }\n\n let body: { entity?: unknown; entry?: unknown } = {};\n\n try {\n body = (await request.json()) as typeof body;\n } catch {\n // An empty body revalidates everything.\n }\n\n const entity =\n typeof body.entity === 'string' && body.entity !== ''\n ? body.entity\n : undefined;\n const entry =\n typeof body.entry === 'string' || typeof body.entry === 'number'\n ? body.entry\n : undefined;\n\n revalidateFlexa(entity, entity === undefined ? undefined : entry);\n\n return Response.json({\n revalidated: true,\n entity: entity ?? null,\n entry: entity === undefined ? null : (entry ?? null),\n });\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createClient, DEFAULT_URL } from '@flexacms/client';\nimport type {\n ContentSchema,\n FlexaClient,\n FlexaClientOptions,\n} from '@flexacms/client';\nimport { revalidateTag } from 'next/cache';\n\nexport * from '@flexacms/client';\n\n/** Tag on every cached Flexa response. */\nexport const FLEXA_TAG = 'flexa';\n\nexport interface FlexaNextOptions extends Partial<FlexaClientOptions> {\n /**\n * Seconds before a cached response is refreshed in the background, or\n * `false` to keep it until it is revalidated by tag. Defaults to 60.\n */\n revalidate?: number | false;\n}\n\ntype NextFetchInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\n/** The URL of any `fetch` input (string, URL or Request). */\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') {\n return input;\n }\n\n return input instanceof URL ? input.href : input.url;\n}\n\n/**\n * Cache tags for an API path: `flexa`, `flexa:<entity>` and, for one entry,\n * `flexa:<entity>:<id or slug>`.\n */\nexport function flexaTags(path: string): string[] {\n const [entity, entry] = path\n .split('?')[0]!\n .split('/')\n .filter(Boolean)\n .map(decodeURIComponent);\n const tags = [FLEXA_TAG];\n\n if (entity) {\n tags.push(`${FLEXA_TAG}:${entity}`);\n }\n\n if (entity && entry && entry !== 'schema') {\n tags.push(`${FLEXA_TAG}:${entity}:${entry}`);\n }\n\n return tags;\n}\n\n/**\n * A Flexa client for Server Components, Route Handlers and Server Actions.\n *\n * Reads go through Next's data cache, tagged per entity and entry so they can\n * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and\n * URL defaults to `FLEXA_URL` or `https://flexacms.com`; the API key defaults to\n * the `FLEXA_API_KEY` environment variable.\n * Only use it on the server: the API key must not reach the browser.\n */\nexport function createFlexaClient<TSchema extends object = ContentSchema>(\n options: FlexaNextOptions = {},\n): FlexaClient<TSchema> {\n const { revalidate = 60, ...clientOptions } = options;\n const url = clientOptions.url || process.env.FLEXA_URL || DEFAULT_URL;\n const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'Flexa: set the FLEXA_API_KEY environment variable, or pass \"apiKey\".',\n );\n }\n\n const baseFetch = clientOptions.fetch ?? globalThis.fetch;\n let client: FlexaClient<TSchema>;\n\n const cachedFetch = ((\n input: string | URL | Request,\n init: NextFetchInit = {},\n ) => {\n if ((init.method ?? 'GET').toUpperCase() !== 'GET') {\n return baseFetch(input, { ...init, cache: 'no-store' });\n }\n\n const path = new URL(requestUrl(input)).pathname.slice(\n new URL(client.baseUrl).pathname.length,\n );\n\n return baseFetch(input, {\n ...init,\n next: {\n revalidate,\n ...init.next,\n tags: [...flexaTags(path), ...(init.next?.tags ?? [])],\n },\n } as RequestInit);\n }) as typeof fetch;\n\n client = createClient<TSchema>({\n ...clientOptions,\n url,\n apiKey,\n fetch: cachedFetch,\n });\n\n return client;\n}\n\n/**\n * Refresh cached Flexa content: everything, one entity, or one entry. Call it\n * from a Server Action or Route Handler after content changes.\n */\nexport function revalidateFlexa(\n entity?: string,\n idOrSlug?: number | string,\n): void {\n const tag =\n entity === undefined\n ? FLEXA_TAG\n : idOrSlug === undefined\n ? `${FLEXA_TAG}:${entity}`\n : `${FLEXA_TAG}:${entity}:${idOrSlug}`;\n\n revalidateTag(tag, 'max');\n}\n\n/**\n * A `POST` Route Handler that revalidates Flexa content, for webhooks or\n * deploy hooks. Send `{ \"entity\": \"posts\", \"entry\": \"hello-world\" }` (both\n * optional) with the header `Authorization: Bearer <secret>`.\n *\n * // app/api/flexa/revalidate/route.ts\n * export const POST = createRevalidateHandler({ secret: process.env.FLEXA_REVALIDATE_SECRET });\n */\nexport function createRevalidateHandler(options: {\n secret: string | undefined;\n}) {\n return async function POST(request: Request): Promise<Response> {\n if (\n !options.secret ||\n request.headers.get('authorization') !== `Bearer ${options.secret}`\n ) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 });\n }\n\n let body: { entity?: unknown; entry?: unknown } = {};\n\n try {\n body = (await request.json()) as typeof body;\n } catch {\n // An empty body revalidates everything.\n }\n\n const entity =\n typeof body.entity === 'string' && body.entity !== ''\n ? body.entity\n : undefined;\n const entry =\n typeof body.entry === 'string' || typeof body.entry === 'number'\n ? body.entry\n : undefined;\n\n revalidateFlexa(entity, entity === undefined ? undefined : entry);\n\n return Response.json({\n revalidated: true,\n entity: entity ?? null,\n entry: entity === undefined ? null : (entry ?? null),\n });\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -20,7 +20,8 @@ declare function flexaTags(path: string): string[];
20
20
  *
21
21
  * Reads go through Next's data cache, tagged per entity and entry so they can
22
22
  * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and
23
- * API key default to the `FLEXA_URL` and `FLEXA_API_KEY` environment variables.
23
+ * URL defaults to `FLEXA_URL` or `https://flexacms.com`; the API key defaults to
24
+ * the `FLEXA_API_KEY` environment variable.
24
25
  * Only use it on the server: the API key must not reach the browser.
25
26
  */
26
27
  declare function createFlexaClient<TSchema extends object = ContentSchema>(options?: FlexaNextOptions): FlexaClient<TSchema>;
package/dist/index.d.ts CHANGED
@@ -20,7 +20,8 @@ declare function flexaTags(path: string): string[];
20
20
  *
21
21
  * Reads go through Next's data cache, tagged per entity and entry so they can
22
22
  * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and
23
- * API key default to the `FLEXA_URL` and `FLEXA_API_KEY` environment variables.
23
+ * URL defaults to `FLEXA_URL` or `https://flexacms.com`; the API key defaults to
24
+ * the `FLEXA_API_KEY` environment variable.
24
25
  * Only use it on the server: the API key must not reach the browser.
25
26
  */
26
27
  declare function createFlexaClient<TSchema extends object = ContentSchema>(options?: FlexaNextOptions): FlexaClient<TSchema>;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { createClient } from "@flexacms/client";
2
+ import { createClient, DEFAULT_URL } from "@flexacms/client";
3
3
  import { revalidateTag } from "next/cache";
4
4
  export * from "@flexacms/client";
5
5
  var FLEXA_TAG = "flexa";
@@ -22,11 +22,11 @@ function flexaTags(path) {
22
22
  }
23
23
  function createFlexaClient(options = {}) {
24
24
  const { revalidate = 60, ...clientOptions } = options;
25
- const url = clientOptions.url ?? process.env.FLEXA_URL;
25
+ const url = clientOptions.url || process.env.FLEXA_URL || DEFAULT_URL;
26
26
  const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;
27
- if (!url || !apiKey) {
27
+ if (!apiKey) {
28
28
  throw new Error(
29
- 'Flexa: set the FLEXA_URL and FLEXA_API_KEY environment variables, or pass "url" and "apiKey".'
29
+ 'Flexa: set the FLEXA_API_KEY environment variable, or pass "apiKey".'
30
30
  );
31
31
  }
32
32
  const baseFetch = clientOptions.fetch ?? globalThis.fetch;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createClient } from '@flexacms/client';\nimport type {\n ContentSchema,\n FlexaClient,\n FlexaClientOptions,\n} from '@flexacms/client';\nimport { revalidateTag } from 'next/cache';\n\nexport * from '@flexacms/client';\n\n/** Tag on every cached Flexa response. */\nexport const FLEXA_TAG = 'flexa';\n\nexport interface FlexaNextOptions extends Partial<FlexaClientOptions> {\n /**\n * Seconds before a cached response is refreshed in the background, or\n * `false` to keep it until it is revalidated by tag. Defaults to 60.\n */\n revalidate?: number | false;\n}\n\ntype NextFetchInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\n/** The URL of any `fetch` input (string, URL or Request). */\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') {\n return input;\n }\n\n return input instanceof URL ? input.href : input.url;\n}\n\n/**\n * Cache tags for an API path: `flexa`, `flexa:<entity>` and, for one entry,\n * `flexa:<entity>:<id or slug>`.\n */\nexport function flexaTags(path: string): string[] {\n const [entity, entry] = path\n .split('?')[0]!\n .split('/')\n .filter(Boolean)\n .map(decodeURIComponent);\n const tags = [FLEXA_TAG];\n\n if (entity) {\n tags.push(`${FLEXA_TAG}:${entity}`);\n }\n\n if (entity && entry && entry !== 'schema') {\n tags.push(`${FLEXA_TAG}:${entity}:${entry}`);\n }\n\n return tags;\n}\n\n/**\n * A Flexa client for Server Components, Route Handlers and Server Actions.\n *\n * Reads go through Next's data cache, tagged per entity and entry so they can\n * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and\n * API key default to the `FLEXA_URL` and `FLEXA_API_KEY` environment variables.\n * Only use it on the server: the API key must not reach the browser.\n */\nexport function createFlexaClient<TSchema extends object = ContentSchema>(\n options: FlexaNextOptions = {},\n): FlexaClient<TSchema> {\n const { revalidate = 60, ...clientOptions } = options;\n const url = clientOptions.url ?? process.env.FLEXA_URL;\n const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;\n\n if (!url || !apiKey) {\n throw new Error(\n 'Flexa: set the FLEXA_URL and FLEXA_API_KEY environment variables, or pass \"url\" and \"apiKey\".',\n );\n }\n\n const baseFetch = clientOptions.fetch ?? globalThis.fetch;\n let client: FlexaClient<TSchema>;\n\n const cachedFetch = ((\n input: string | URL | Request,\n init: NextFetchInit = {},\n ) => {\n if ((init.method ?? 'GET').toUpperCase() !== 'GET') {\n return baseFetch(input, { ...init, cache: 'no-store' });\n }\n\n const path = new URL(requestUrl(input)).pathname.slice(\n new URL(client.baseUrl).pathname.length,\n );\n\n return baseFetch(input, {\n ...init,\n next: {\n revalidate,\n ...init.next,\n tags: [...flexaTags(path), ...(init.next?.tags ?? [])],\n },\n } as RequestInit);\n }) as typeof fetch;\n\n client = createClient<TSchema>({\n ...clientOptions,\n url,\n apiKey,\n fetch: cachedFetch,\n });\n\n return client;\n}\n\n/**\n * Refresh cached Flexa content: everything, one entity, or one entry. Call it\n * from a Server Action or Route Handler after content changes.\n */\nexport function revalidateFlexa(\n entity?: string,\n idOrSlug?: number | string,\n): void {\n const tag =\n entity === undefined\n ? FLEXA_TAG\n : idOrSlug === undefined\n ? `${FLEXA_TAG}:${entity}`\n : `${FLEXA_TAG}:${entity}:${idOrSlug}`;\n\n revalidateTag(tag, 'max');\n}\n\n/**\n * A `POST` Route Handler that revalidates Flexa content, for webhooks or\n * deploy hooks. Send `{ \"entity\": \"posts\", \"entry\": \"hello-world\" }` (both\n * optional) with the header `Authorization: Bearer <secret>`.\n *\n * // app/api/flexa/revalidate/route.ts\n * export const POST = createRevalidateHandler({ secret: process.env.FLEXA_REVALIDATE_SECRET });\n */\nexport function createRevalidateHandler(options: {\n secret: string | undefined;\n}) {\n return async function POST(request: Request): Promise<Response> {\n if (\n !options.secret ||\n request.headers.get('authorization') !== `Bearer ${options.secret}`\n ) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 });\n }\n\n let body: { entity?: unknown; entry?: unknown } = {};\n\n try {\n body = (await request.json()) as typeof body;\n } catch {\n // An empty body revalidates everything.\n }\n\n const entity =\n typeof body.entity === 'string' && body.entity !== ''\n ? body.entity\n : undefined;\n const entry =\n typeof body.entry === 'string' || typeof body.entry === 'number'\n ? body.entry\n : undefined;\n\n revalidateFlexa(entity, entity === undefined ? undefined : entry);\n\n return Response.json({\n revalidated: true,\n entity: entity ?? null,\n entry: entity === undefined ? null : (entry ?? null),\n });\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createClient, DEFAULT_URL } from '@flexacms/client';\nimport type {\n ContentSchema,\n FlexaClient,\n FlexaClientOptions,\n} from '@flexacms/client';\nimport { revalidateTag } from 'next/cache';\n\nexport * from '@flexacms/client';\n\n/** Tag on every cached Flexa response. */\nexport const FLEXA_TAG = 'flexa';\n\nexport interface FlexaNextOptions extends Partial<FlexaClientOptions> {\n /**\n * Seconds before a cached response is refreshed in the background, or\n * `false` to keep it until it is revalidated by tag. Defaults to 60.\n */\n revalidate?: number | false;\n}\n\ntype NextFetchInit = RequestInit & {\n next?: { revalidate?: number | false; tags?: string[] };\n};\n\n/** The URL of any `fetch` input (string, URL or Request). */\nfunction requestUrl(input: string | URL | Request): string {\n if (typeof input === 'string') {\n return input;\n }\n\n return input instanceof URL ? input.href : input.url;\n}\n\n/**\n * Cache tags for an API path: `flexa`, `flexa:<entity>` and, for one entry,\n * `flexa:<entity>:<id or slug>`.\n */\nexport function flexaTags(path: string): string[] {\n const [entity, entry] = path\n .split('?')[0]!\n .split('/')\n .filter(Boolean)\n .map(decodeURIComponent);\n const tags = [FLEXA_TAG];\n\n if (entity) {\n tags.push(`${FLEXA_TAG}:${entity}`);\n }\n\n if (entity && entry && entry !== 'schema') {\n tags.push(`${FLEXA_TAG}:${entity}:${entry}`);\n }\n\n return tags;\n}\n\n/**\n * A Flexa client for Server Components, Route Handlers and Server Actions.\n *\n * Reads go through Next's data cache, tagged per entity and entry so they can\n * be refreshed with `revalidateFlexa()`; writes are never cached. The URL and\n * URL defaults to `FLEXA_URL` or `https://flexacms.com`; the API key defaults to\n * the `FLEXA_API_KEY` environment variable.\n * Only use it on the server: the API key must not reach the browser.\n */\nexport function createFlexaClient<TSchema extends object = ContentSchema>(\n options: FlexaNextOptions = {},\n): FlexaClient<TSchema> {\n const { revalidate = 60, ...clientOptions } = options;\n const url = clientOptions.url || process.env.FLEXA_URL || DEFAULT_URL;\n const apiKey = clientOptions.apiKey ?? process.env.FLEXA_API_KEY;\n\n if (!apiKey) {\n throw new Error(\n 'Flexa: set the FLEXA_API_KEY environment variable, or pass \"apiKey\".',\n );\n }\n\n const baseFetch = clientOptions.fetch ?? globalThis.fetch;\n let client: FlexaClient<TSchema>;\n\n const cachedFetch = ((\n input: string | URL | Request,\n init: NextFetchInit = {},\n ) => {\n if ((init.method ?? 'GET').toUpperCase() !== 'GET') {\n return baseFetch(input, { ...init, cache: 'no-store' });\n }\n\n const path = new URL(requestUrl(input)).pathname.slice(\n new URL(client.baseUrl).pathname.length,\n );\n\n return baseFetch(input, {\n ...init,\n next: {\n revalidate,\n ...init.next,\n tags: [...flexaTags(path), ...(init.next?.tags ?? [])],\n },\n } as RequestInit);\n }) as typeof fetch;\n\n client = createClient<TSchema>({\n ...clientOptions,\n url,\n apiKey,\n fetch: cachedFetch,\n });\n\n return client;\n}\n\n/**\n * Refresh cached Flexa content: everything, one entity, or one entry. Call it\n * from a Server Action or Route Handler after content changes.\n */\nexport function revalidateFlexa(\n entity?: string,\n idOrSlug?: number | string,\n): void {\n const tag =\n entity === undefined\n ? FLEXA_TAG\n : idOrSlug === undefined\n ? `${FLEXA_TAG}:${entity}`\n : `${FLEXA_TAG}:${entity}:${idOrSlug}`;\n\n revalidateTag(tag, 'max');\n}\n\n/**\n * A `POST` Route Handler that revalidates Flexa content, for webhooks or\n * deploy hooks. Send `{ \"entity\": \"posts\", \"entry\": \"hello-world\" }` (both\n * optional) with the header `Authorization: Bearer <secret>`.\n *\n * // app/api/flexa/revalidate/route.ts\n * export const POST = createRevalidateHandler({ secret: process.env.FLEXA_REVALIDATE_SECRET });\n */\nexport function createRevalidateHandler(options: {\n secret: string | undefined;\n}) {\n return async function POST(request: Request): Promise<Response> {\n if (\n !options.secret ||\n request.headers.get('authorization') !== `Bearer ${options.secret}`\n ) {\n return Response.json({ message: 'Unauthorized.' }, { status: 401 });\n }\n\n let body: { entity?: unknown; entry?: unknown } = {};\n\n try {\n body = (await request.json()) as typeof body;\n } catch {\n // An empty body revalidates everything.\n }\n\n const entity =\n typeof body.entity === 'string' && body.entity !== ''\n ? body.entity\n : undefined;\n const entry =\n typeof body.entry === 'string' || typeof body.entry === 'number'\n ? body.entry\n : undefined;\n\n revalidateFlexa(entity, entity === undefined ? undefined : entry);\n\n return Response.json({\n revalidated: true,\n entity: entity ?? null,\n entry: entity === undefined ? null : (entry ?? null),\n });\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexacms/next",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Next.js integration for the Flexa headless CMS: cached server-side fetching with tag revalidation.",
5
5
  "keywords": [
6
6
  "cms",
@@ -49,7 +49,7 @@
49
49
  "typecheck": "tsc -p tsconfig.json"
50
50
  },
51
51
  "dependencies": {
52
- "@flexacms/client": "^0.2.0"
52
+ "@flexacms/client": "^0.3.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "next": "^16.4.0",