@datum-cloud/strapi-revalidate 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.
@@ -25,7 +25,15 @@ export declare const revalidateConfigSchema: z.ZodObject<{
25
25
  secret: z.ZodOptional<z.ZodString>;
26
26
  tagMap: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
27
27
  onRevalidate: z.ZodOptional<z.ZodType<(event: WebhookEvent) => void | Promise<void>, z.ZodTypeDef, (event: WebhookEvent) => void | Promise<void>>>;
28
+ /**
29
+ * When true (default), an `onRevalidate` hook failure returns HTTP 502 with
30
+ * `{ ok: false, invalidated: true }` so webhook callers can detect partial
31
+ * failure after cache invalidation. Set false to preserve the pre-0.3.0
32
+ * behaviour of logging the error and returning 200.
33
+ */
34
+ failOnWarmError: z.ZodDefault<z.ZodBoolean>;
28
35
  }, "strip", z.ZodTypeAny, {
36
+ failOnWarmError: boolean;
29
37
  secret?: string | undefined;
30
38
  tagMap?: Record<string, string[]> | undefined;
31
39
  onRevalidate?: ((event: WebhookEvent) => void | Promise<void>) | undefined;
@@ -33,6 +41,7 @@ export declare const revalidateConfigSchema: z.ZodObject<{
33
41
  secret?: string | undefined;
34
42
  tagMap?: Record<string, string[]> | undefined;
35
43
  onRevalidate?: ((event: WebhookEvent) => void | Promise<void>) | undefined;
44
+ failOnWarmError?: boolean | undefined;
36
45
  }>>;
37
46
  cache: z.ZodDefault<z.ZodObject<{
38
47
  driver: z.ZodDefault<z.ZodEnum<["file", "memory", "redis"]>>;
@@ -76,6 +85,7 @@ export declare const revalidateConfigSchema: z.ZodObject<{
76
85
  debug: boolean;
77
86
  token?: string | undefined;
78
87
  webhook?: {
88
+ failOnWarmError: boolean;
79
89
  secret?: string | undefined;
80
90
  tagMap?: Record<string, string[]> | undefined;
81
91
  onRevalidate?: ((event: WebhookEvent) => void | Promise<void>) | undefined;
@@ -87,6 +97,7 @@ export declare const revalidateConfigSchema: z.ZodObject<{
87
97
  secret?: string | undefined;
88
98
  tagMap?: Record<string, string[]> | undefined;
89
99
  onRevalidate?: ((event: WebhookEvent) => void | Promise<void>) | undefined;
100
+ failOnWarmError?: boolean | undefined;
90
101
  } | undefined;
91
102
  cache?: {
92
103
  driver?: "file" | "memory" | "redis" | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,wCAAsC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,gCAA8B,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA6BhE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;sDAbb,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,wBAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;EAsBzD,CAAC;AAEH,oDAAoD;AACpD,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE3E,iDAAiD;AACjD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,wCAAsC,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,gCAA8B,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAoChE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;sDApBb,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,wBAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAIvD;;;;;WAKG;;;;;;gCATe,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;gCAArC,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;EA6BzD,CAAC;AAEH,oDAAoD;AACpD,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE3E,iDAAiD;AACjD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -30,6 +30,13 @@ const webhookConfigSchema = z
30
30
  onRevalidate: z
31
31
  .custom((val) => typeof val === 'function')
32
32
  .optional(),
33
+ /**
34
+ * When true (default), an `onRevalidate` hook failure returns HTTP 502 with
35
+ * `{ ok: false, invalidated: true }` so webhook callers can detect partial
36
+ * failure after cache invalidation. Set false to preserve the pre-0.3.0
37
+ * behaviour of logging the error and returning 200.
38
+ */
39
+ failOnWarmError: z.boolean().default(true),
33
40
  })
34
41
  .optional();
35
42
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,0CAA0C;AAC1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAGzE,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAG/D,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,CAAC;IACN,MAAM,EAAE,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,0BAA0B,CAAC;IACnD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,wBAAwB,CAAC;IACzD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC;KACD,OAAO,CAAC,EAAE,CAAC,CAAC;AAEf,MAAM,mBAAmB,GAAG,CAAC;KAC1B,MAAM,CAAC;IACN,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,iEAAiE;IACjE,YAAY,EAAE,CAAC;SACZ,MAAM,CACL,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,UAAU,CACnC;SACA,QAAQ,EAAE;CACd,CAAC;KACD,QAAQ,EAAE,CAAC;AAEd;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,mBAAmB;IAC5B,KAAK,EAAE,iBAAiB;IACxB,SAAS,EAAE,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC;IACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAClC,CAAC,CAAC","sourcesContent":["/**\n * Runtime configuration schema for strapi-revalidate.\n *\n * Validated with zod so misconfigurations surface at startup rather than\n * at the first cache miss or webhook delivery.\n */\n\nimport { z } from 'zod';\nimport type { WebhookEvent } from '../webhook/types.js';\n\n/** Supported cache driver identifiers. */\nexport const cacheDriverNameSchema = z.enum(['file', 'memory', 'redis']);\nexport type CacheDriverName = z.infer<typeof cacheDriverNameSchema>;\n\n/** Supported transport identifiers for the Strapi client. */\nexport const transportNameSchema = z.enum(['graphql', 'rest']);\nexport type TransportName = z.infer<typeof transportNameSchema>;\n\nconst cacheConfigSchema = z\n .object({\n driver: cacheDriverNameSchema.default('file'),\n ttl: z.number().int().positive().default(3600),\n dir: z.string().default('.cache/strapi-revalidate'),\n fallbackDir: z.string().default('.cache/strapi-fallback'),\n redisUrl: z.string().url().optional(),\n keyPrefix: z.string().optional(),\n })\n .default({});\n\nconst webhookConfigSchema = z\n .object({\n secret: z.string().optional(),\n tagMap: z.record(z.array(z.string())).optional(),\n // `z.function().args(...).returns(...)` is a zod v3-only chain — v4 reshaped\n // the function schema API and removed it. Use `z.custom` instead so the schema\n // loads under either major version. Runtime validation still ensures the value\n // is a function; the TS-level signature carries the typed shape.\n onRevalidate: z\n .custom<(event: WebhookEvent) => void | Promise<void>>(\n (val) => typeof val === 'function'\n )\n .optional(),\n })\n .optional();\n\n/**\n * Full runtime configuration schema.\n *\n * Use `revalidateConfigSchema.parse(input)` to validate user input and\n * apply defaults in one step.\n */\nexport const revalidateConfigSchema = z.object({\n url: z.string().url(),\n token: z.string().min(1).optional(),\n webhook: webhookConfigSchema,\n cache: cacheConfigSchema,\n transport: transportNameSchema.default('graphql'),\n timeout: z.number().int().positive().default(3000),\n retry: z.number().int().min(0).default(2),\n debug: z.boolean().default(false),\n});\n\n/** User-supplied configuration (pre-validation). */\nexport type RevalidateConfigInput = z.input<typeof revalidateConfigSchema>;\n\n/** Validated, defaults-applied configuration. */\nexport type RevalidateConfig = z.output<typeof revalidateConfigSchema>;\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,0CAA0C;AAC1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAGzE,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAG/D,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,CAAC;IACN,MAAM,EAAE,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,0BAA0B,CAAC;IACnD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,wBAAwB,CAAC;IACzD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC;KACD,OAAO,CAAC,EAAE,CAAC,CAAC;AAEf,MAAM,mBAAmB,GAAG,CAAC;KAC1B,MAAM,CAAC;IACN,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,iEAAiE;IACjE,YAAY,EAAE,CAAC;SACZ,MAAM,CACL,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,UAAU,CACnC;SACA,QAAQ,EAAE;IACb;;;;;OAKG;IACH,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC3C,CAAC;KACD,QAAQ,EAAE,CAAC;AAEd;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,mBAAmB;IAC5B,KAAK,EAAE,iBAAiB;IACxB,SAAS,EAAE,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC;IACjD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CAClC,CAAC,CAAC","sourcesContent":["/**\n * Runtime configuration schema for strapi-revalidate.\n *\n * Validated with zod so misconfigurations surface at startup rather than\n * at the first cache miss or webhook delivery.\n */\n\nimport { z } from 'zod';\nimport type { WebhookEvent } from '../webhook/types.js';\n\n/** Supported cache driver identifiers. */\nexport const cacheDriverNameSchema = z.enum(['file', 'memory', 'redis']);\nexport type CacheDriverName = z.infer<typeof cacheDriverNameSchema>;\n\n/** Supported transport identifiers for the Strapi client. */\nexport const transportNameSchema = z.enum(['graphql', 'rest']);\nexport type TransportName = z.infer<typeof transportNameSchema>;\n\nconst cacheConfigSchema = z\n .object({\n driver: cacheDriverNameSchema.default('file'),\n ttl: z.number().int().positive().default(3600),\n dir: z.string().default('.cache/strapi-revalidate'),\n fallbackDir: z.string().default('.cache/strapi-fallback'),\n redisUrl: z.string().url().optional(),\n keyPrefix: z.string().optional(),\n })\n .default({});\n\nconst webhookConfigSchema = z\n .object({\n secret: z.string().optional(),\n tagMap: z.record(z.array(z.string())).optional(),\n // `z.function().args(...).returns(...)` is a zod v3-only chain — v4 reshaped\n // the function schema API and removed it. Use `z.custom` instead so the schema\n // loads under either major version. Runtime validation still ensures the value\n // is a function; the TS-level signature carries the typed shape.\n onRevalidate: z\n .custom<(event: WebhookEvent) => void | Promise<void>>(\n (val) => typeof val === 'function'\n )\n .optional(),\n /**\n * When true (default), an `onRevalidate` hook failure returns HTTP 502 with\n * `{ ok: false, invalidated: true }` so webhook callers can detect partial\n * failure after cache invalidation. Set false to preserve the pre-0.3.0\n * behaviour of logging the error and returning 200.\n */\n failOnWarmError: z.boolean().default(true),\n })\n .optional();\n\n/**\n * Full runtime configuration schema.\n *\n * Use `revalidateConfigSchema.parse(input)` to validate user input and\n * apply defaults in one step.\n */\nexport const revalidateConfigSchema = z.object({\n url: z.string().url(),\n token: z.string().min(1).optional(),\n webhook: webhookConfigSchema,\n cache: cacheConfigSchema,\n transport: transportNameSchema.default('graphql'),\n timeout: z.number().int().positive().default(3000),\n retry: z.number().int().min(0).default(2),\n debug: z.boolean().default(false),\n});\n\n/** User-supplied configuration (pre-validation). */\nexport type RevalidateConfigInput = z.input<typeof revalidateConfigSchema>;\n\n/** Validated, defaults-applied configuration. */\nexport type RevalidateConfig = z.output<typeof revalidateConfigSchema>;\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Helpers for normalizing Strapi webhook payloads into {@link WebhookEvent}.
3
+ */
4
+ import type { WebhookEvent, WebhookPayload } from './types.js';
5
+ /**
6
+ * Resolve a stable entry identifier from a webhook entry payload.
7
+ * Strapi v5 usually sends `documentId`; older payloads may only include numeric `id`.
8
+ */
9
+ export declare function resolveEntryId(entry: WebhookPayload['entry']): string;
10
+ /**
11
+ * Build the normalized event passed to `onRevalidate` after tag resolution.
12
+ */
13
+ export declare function buildWebhookEvent(payload: WebhookPayload, tags: string[]): WebhookEvent;
14
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/webhook/event.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,MAAM,CAIrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CASvF"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Helpers for normalizing Strapi webhook payloads into {@link WebhookEvent}.
3
+ */
4
+ /**
5
+ * Resolve a stable entry identifier from a webhook entry payload.
6
+ * Strapi v5 usually sends `documentId`; older payloads may only include numeric `id`.
7
+ */
8
+ export function resolveEntryId(entry) {
9
+ if (typeof entry.documentId === 'string')
10
+ return entry.documentId;
11
+ if (typeof entry.id === 'number')
12
+ return String(entry.id);
13
+ return '';
14
+ }
15
+ /**
16
+ * Build the normalized event passed to `onRevalidate` after tag resolution.
17
+ */
18
+ export function buildWebhookEvent(payload, tags) {
19
+ return {
20
+ event: payload.event,
21
+ model: payload.model,
22
+ uid: payload.uid,
23
+ entryId: resolveEntryId(payload.entry),
24
+ slug: typeof payload.entry.slug === 'string' ? payload.entry.slug : undefined,
25
+ tags,
26
+ };
27
+ }
28
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/webhook/event.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAA8B;IAC3D,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IAClE,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1D,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAuB,EAAE,IAAc;IACvE,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC;QACtC,IAAI,EAAE,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QAC7E,IAAI;KACL,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Helpers for normalizing Strapi webhook payloads into {@link WebhookEvent}.\n */\n\nimport type { WebhookEvent, WebhookPayload } from './types.js';\n\n/**\n * Resolve a stable entry identifier from a webhook entry payload.\n * Strapi v5 usually sends `documentId`; older payloads may only include numeric `id`.\n */\nexport function resolveEntryId(entry: WebhookPayload['entry']): string {\n if (typeof entry.documentId === 'string') return entry.documentId;\n if (typeof entry.id === 'number') return String(entry.id);\n return '';\n}\n\n/**\n * Build the normalized event passed to `onRevalidate` after tag resolution.\n */\nexport function buildWebhookEvent(payload: WebhookPayload, tags: string[]): WebhookEvent {\n return {\n event: payload.event,\n model: payload.model,\n uid: payload.uid,\n entryId: resolveEntryId(payload.entry),\n slug: typeof payload.entry.slug === 'string' ? payload.entry.slug : undefined,\n tags,\n };\n}\n"]}
@@ -11,11 +11,12 @@
11
11
  * 1. 405 if method isn't POST.
12
12
  * 2. 401 if a secret is configured and the request's secret doesn't match
13
13
  * (constant-time compare; supports `Authorization: Bearer <secret>`,
14
- * `X-Strapi-Signature`, or a raw `strapi-webhook-secret` header).
14
+ * `X-Strapi-Signature`, `strapi-webhook-secret`, or `X-Webhook-Secret`).
15
15
  * 3. 400 if the body isn't a parseable Strapi v5 webhook payload.
16
16
  * 4. Resolves tags via `mapModelToTags`, calls `cache.invalidate(tag)` for
17
17
  * each, and invokes `onRevalidate` if configured.
18
- * 5. 200 with `{ ok: true, tags }`.
18
+ * 5. 200 with `{ ok: true, tags }`, or 502 when `onRevalidate` throws and
19
+ * `webhook.failOnWarmError` is true (default).
19
20
  */
20
21
  import type { CacheManager } from '../cache/manager.js';
21
22
  import type { RevalidateConfig, RevalidateConfigInput } from '../types/config.js';
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,KAAK,EAGV,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc,CAAC;AACnF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,YAAY,CAAC;CACrB,GAAG,cAAc,CAAC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAGlF,OAAO,KAAK,EAAkB,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElF,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,qBAAqB,GAAG,cAAc,CAAC;AACnF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,YAAY,CAAC;CACrB,GAAG,cAAc,CAAC"}
@@ -11,15 +11,17 @@
11
11
  * 1. 405 if method isn't POST.
12
12
  * 2. 401 if a secret is configured and the request's secret doesn't match
13
13
  * (constant-time compare; supports `Authorization: Bearer <secret>`,
14
- * `X-Strapi-Signature`, or a raw `strapi-webhook-secret` header).
14
+ * `X-Strapi-Signature`, `strapi-webhook-secret`, or `X-Webhook-Secret`).
15
15
  * 3. 400 if the body isn't a parseable Strapi v5 webhook payload.
16
16
  * 4. Resolves tags via `mapModelToTags`, calls `cache.invalidate(tag)` for
17
17
  * each, and invokes `onRevalidate` if configured.
18
- * 5. 200 with `{ ok: true, tags }`.
18
+ * 5. 200 with `{ ok: true, tags }`, or 502 when `onRevalidate` throws and
19
+ * `webhook.failOnWarmError` is true (default).
19
20
  */
20
21
  import { timingSafeEqual } from 'node:crypto';
21
22
  import { createCache } from '../cache/index.js';
22
23
  import { revalidateConfigSchema } from '../types/config.js';
24
+ import { buildWebhookEvent } from './event.js';
23
25
  import { mapModelToTags } from './tags.js';
24
26
  export function createWebhookHandler(input) {
25
27
  const { config, cache } = resolveDeps(input);
@@ -54,22 +56,25 @@ export function createWebhookHandler(input) {
54
56
  for (const tag of tags) {
55
57
  await cache.invalidate(tag);
56
58
  }
57
- const event = {
58
- event: payload.event,
59
- model: payload.model,
60
- uid: payload.uid,
61
- entryId: payload.entry.documentId,
62
- slug: typeof payload.entry.slug === 'string' ? payload.entry.slug : undefined,
63
- tags,
64
- };
59
+ const event = buildWebhookEvent(payload, tags);
60
+ const failOnWarmError = config.webhook?.failOnWarmError ?? true;
65
61
  if (config.webhook?.onRevalidate) {
66
62
  try {
67
63
  await config.webhook.onRevalidate(event);
68
64
  }
69
65
  catch (err) {
70
- // The cache is already invalidated; surface the hook failure to the caller
71
- // but treat the revalidation itself as successful.
72
66
  console.error('[strapi-revalidate] onRevalidate hook threw:', err);
67
+ if (failOnWarmError) {
68
+ res.status(502);
69
+ res.json({
70
+ ok: false,
71
+ invalidated: true,
72
+ error: 'Cache warm failed',
73
+ message: err instanceof Error ? err.message : 'Unknown warm error',
74
+ tags,
75
+ });
76
+ return;
77
+ }
73
78
  }
74
79
  }
75
80
  res.status(200);
@@ -100,6 +105,7 @@ function readSecret(req) {
100
105
  }
101
106
  return (headerValue(req, 'x-strapi-signature') ??
102
107
  headerValue(req, 'strapi-webhook-secret') ??
108
+ headerValue(req, 'x-webhook-secret') ??
103
109
  null);
104
110
  }
105
111
  function headerValue(req, name) {
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Framework-agnostic webhook handler.\n *\n * `createWebhookHandler(config)` returns an `async (req, res) => void`\n * function whose `req` and `res` accept the lowest-common-denominator\n * shapes used by Astro, Next.js (Route Handlers), SvelteKit, Express,\n * and raw Node http. Each framework's docs page in the README shows the\n * single line of glue code needed.\n *\n * Behaviour:\n * 1. 405 if method isn't POST.\n * 2. 401 if a secret is configured and the request's secret doesn't match\n * (constant-time compare; supports `Authorization: Bearer <secret>`,\n * `X-Strapi-Signature`, or a raw `strapi-webhook-secret` header).\n * 3. 400 if the body isn't a parseable Strapi v5 webhook payload.\n * 4. Resolves tags via `mapModelToTags`, calls `cache.invalidate(tag)` for\n * each, and invokes `onRevalidate` if configured.\n * 5. 200 with `{ ok: true, tags }`.\n */\n\nimport { timingSafeEqual } from 'node:crypto';\nimport { createCache } from '../cache/index.js';\nimport type { CacheManager } from '../cache/manager.js';\nimport { revalidateConfigSchema } from '../types/config.js';\nimport type { RevalidateConfig, RevalidateConfigInput } from '../types/config.js';\nimport { mapModelToTags } from './tags.js';\nimport type {\n WebhookEvent,\n WebhookPayload,\n WebhookRequest,\n WebhookResponse,\n} from './types.js';\n\n/** Async function shape returned by `createWebhookHandler`. */\nexport type WebhookHandler = (req: WebhookRequest, res: WebhookResponse) => Promise<void>;\n\n/**\n * Build a webhook handler bound to a validated config and the cache\n * manager derived from it.\n *\n * Accepts either a `RevalidateConfigInput` (raw user object — gets validated)\n * or a pair of pre-built dependencies, so power users can share a single\n * `CacheManager` across handlers and content fetchers.\n */\nexport function createWebhookHandler(input: RevalidateConfigInput): WebhookHandler;\nexport function createWebhookHandler(deps: {\n config: RevalidateConfig;\n cache: CacheManager;\n}): WebhookHandler;\nexport function createWebhookHandler(\n input: RevalidateConfigInput | { config: RevalidateConfig; cache: CacheManager }\n): WebhookHandler {\n const { config, cache } = resolveDeps(input);\n\n return async function handle(req, res) {\n if (req.method && req.method.toUpperCase() !== 'POST') {\n res.status(405);\n res.json({ ok: false, error: 'Method not allowed' });\n return;\n }\n\n const configuredSecret = config.webhook?.secret;\n if (configuredSecret) {\n const provided = readSecret(req);\n if (!provided || !constantTimeEqual(provided, configuredSecret)) {\n res.status(401);\n res.json({ ok: false, error: 'Invalid secret' });\n return;\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = await readPayload(req);\n } catch (err) {\n res.status(400);\n res.json({\n ok: false,\n error: err instanceof Error ? err.message : 'Bad payload',\n });\n return;\n }\n\n const tags = mapModelToTags(payload, config.webhook?.tagMap);\n for (const tag of tags) {\n await cache.invalidate(tag);\n }\n\n const event: WebhookEvent = {\n event: payload.event,\n model: payload.model,\n uid: payload.uid,\n entryId: payload.entry.documentId,\n slug: typeof payload.entry.slug === 'string' ? payload.entry.slug : undefined,\n tags,\n };\n\n if (config.webhook?.onRevalidate) {\n try {\n await config.webhook.onRevalidate(event);\n } catch (err) {\n // The cache is already invalidated; surface the hook failure to the caller\n // but treat the revalidation itself as successful.\n console.error('[strapi-revalidate] onRevalidate hook threw:', err);\n }\n }\n\n res.status(200);\n res.json({ ok: true, tags });\n };\n}\n\nfunction resolveDeps(\n input: RevalidateConfigInput | { config: RevalidateConfig; cache: CacheManager }\n): { config: RevalidateConfig; cache: CacheManager } {\n if (isPreBuilt(input)) return input;\n const config = revalidateConfigSchema.parse(input);\n const cache = createCache(config);\n return { config, cache };\n}\n\nfunction isPreBuilt(\n value: unknown\n): value is { config: RevalidateConfig; cache: CacheManager } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'config' in value &&\n 'cache' in value &&\n typeof (value as { cache: unknown }).cache === 'object'\n );\n}\n\nfunction readSecret(req: WebhookRequest): string | null {\n // Headers can arrive as Fetch-style Headers (`.get()`) or as a plain map.\n const direct = headerValue(req, 'authorization');\n if (direct) {\n const match = /^Bearer\\s+(.+)$/i.exec(direct);\n if (match) return match[1].trim();\n }\n\n return (\n headerValue(req, 'x-strapi-signature') ??\n headerValue(req, 'strapi-webhook-secret') ??\n null\n );\n}\n\nfunction headerValue(req: WebhookRequest, name: string): string | null {\n const headers = req.headers;\n if (typeof headers.get === 'function') {\n return headers.get(name);\n }\n const lower = name.toLowerCase();\n const raw = (headers as Record<string, unknown>)[lower] ?? (headers as Record<string, unknown>)[name];\n if (typeof raw === 'string') return raw;\n if (Array.isArray(raw) && typeof raw[0] === 'string') return raw[0];\n return null;\n}\n\nasync function readPayload(req: WebhookRequest): Promise<WebhookPayload> {\n let raw: unknown;\n if (typeof req.json === 'function') {\n raw = await req.json();\n } else if (typeof req.text === 'function') {\n const text = await req.text();\n raw = text.length > 0 ? JSON.parse(text) : {};\n } else {\n throw new Error('Request object exposes neither json() nor text()');\n }\n\n if (!isWebhookPayload(raw)) {\n throw new Error('Payload is not a valid Strapi v5 webhook event');\n }\n return raw;\n}\n\nfunction isWebhookPayload(value: unknown): value is WebhookPayload {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.event !== 'string' || typeof v.model !== 'string' || typeof v.uid !== 'string') {\n return false;\n }\n if (typeof v.entry !== 'object' || v.entry === null) return false;\n const entry = v.entry as Record<string, unknown>;\n return typeof entry.documentId === 'string' || typeof entry.id === 'number';\n}\n\nfunction constantTimeEqual(a: string, b: string): boolean {\n // timingSafeEqual requires equal-length buffers; pad both to the longer length so the\n // length check itself is constant-time-safe (different lengths still return false).\n const bufA = Buffer.from(a, 'utf-8');\n const bufB = Buffer.from(b, 'utf-8');\n if (bufA.length !== bufB.length) return false;\n return timingSafeEqual(bufA, bufB);\n}\n"]}
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Framework-agnostic webhook handler.\n *\n * `createWebhookHandler(config)` returns an `async (req, res) => void`\n * function whose `req` and `res` accept the lowest-common-denominator\n * shapes used by Astro, Next.js (Route Handlers), SvelteKit, Express,\n * and raw Node http. Each framework's docs page in the README shows the\n * single line of glue code needed.\n *\n * Behaviour:\n * 1. 405 if method isn't POST.\n * 2. 401 if a secret is configured and the request's secret doesn't match\n * (constant-time compare; supports `Authorization: Bearer <secret>`,\n * `X-Strapi-Signature`, `strapi-webhook-secret`, or `X-Webhook-Secret`).\n * 3. 400 if the body isn't a parseable Strapi v5 webhook payload.\n * 4. Resolves tags via `mapModelToTags`, calls `cache.invalidate(tag)` for\n * each, and invokes `onRevalidate` if configured.\n * 5. 200 with `{ ok: true, tags }`, or 502 when `onRevalidate` throws and\n * `webhook.failOnWarmError` is true (default).\n */\n\nimport { timingSafeEqual } from 'node:crypto';\nimport { createCache } from '../cache/index.js';\nimport type { CacheManager } from '../cache/manager.js';\nimport { revalidateConfigSchema } from '../types/config.js';\nimport type { RevalidateConfig, RevalidateConfigInput } from '../types/config.js';\nimport { buildWebhookEvent } from './event.js';\nimport { mapModelToTags } from './tags.js';\nimport type { WebhookPayload, WebhookRequest, WebhookResponse } from './types.js';\n\n/** Async function shape returned by `createWebhookHandler`. */\nexport type WebhookHandler = (req: WebhookRequest, res: WebhookResponse) => Promise<void>;\n\n/**\n * Build a webhook handler bound to a validated config and the cache\n * manager derived from it.\n *\n * Accepts either a `RevalidateConfigInput` (raw user object — gets validated)\n * or a pair of pre-built dependencies, so power users can share a single\n * `CacheManager` across handlers and content fetchers.\n */\nexport function createWebhookHandler(input: RevalidateConfigInput): WebhookHandler;\nexport function createWebhookHandler(deps: {\n config: RevalidateConfig;\n cache: CacheManager;\n}): WebhookHandler;\nexport function createWebhookHandler(\n input: RevalidateConfigInput | { config: RevalidateConfig; cache: CacheManager }\n): WebhookHandler {\n const { config, cache } = resolveDeps(input);\n\n return async function handle(req, res) {\n if (req.method && req.method.toUpperCase() !== 'POST') {\n res.status(405);\n res.json({ ok: false, error: 'Method not allowed' });\n return;\n }\n\n const configuredSecret = config.webhook?.secret;\n if (configuredSecret) {\n const provided = readSecret(req);\n if (!provided || !constantTimeEqual(provided, configuredSecret)) {\n res.status(401);\n res.json({ ok: false, error: 'Invalid secret' });\n return;\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = await readPayload(req);\n } catch (err) {\n res.status(400);\n res.json({\n ok: false,\n error: err instanceof Error ? err.message : 'Bad payload',\n });\n return;\n }\n\n const tags = mapModelToTags(payload, config.webhook?.tagMap);\n for (const tag of tags) {\n await cache.invalidate(tag);\n }\n\n const event = buildWebhookEvent(payload, tags);\n const failOnWarmError = config.webhook?.failOnWarmError ?? true;\n\n if (config.webhook?.onRevalidate) {\n try {\n await config.webhook.onRevalidate(event);\n } catch (err) {\n console.error('[strapi-revalidate] onRevalidate hook threw:', err);\n if (failOnWarmError) {\n res.status(502);\n res.json({\n ok: false,\n invalidated: true,\n error: 'Cache warm failed',\n message: err instanceof Error ? err.message : 'Unknown warm error',\n tags,\n });\n return;\n }\n }\n }\n\n res.status(200);\n res.json({ ok: true, tags });\n };\n}\n\nfunction resolveDeps(\n input: RevalidateConfigInput | { config: RevalidateConfig; cache: CacheManager }\n): { config: RevalidateConfig; cache: CacheManager } {\n if (isPreBuilt(input)) return input;\n const config = revalidateConfigSchema.parse(input);\n const cache = createCache(config);\n return { config, cache };\n}\n\nfunction isPreBuilt(\n value: unknown\n): value is { config: RevalidateConfig; cache: CacheManager } {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'config' in value &&\n 'cache' in value &&\n typeof (value as { cache: unknown }).cache === 'object'\n );\n}\n\nfunction readSecret(req: WebhookRequest): string | null {\n // Headers can arrive as Fetch-style Headers (`.get()`) or as a plain map.\n const direct = headerValue(req, 'authorization');\n if (direct) {\n const match = /^Bearer\\s+(.+)$/i.exec(direct);\n if (match) return match[1].trim();\n }\n\n return (\n headerValue(req, 'x-strapi-signature') ??\n headerValue(req, 'strapi-webhook-secret') ??\n headerValue(req, 'x-webhook-secret') ??\n null\n );\n}\n\nfunction headerValue(req: WebhookRequest, name: string): string | null {\n const headers = req.headers;\n if (typeof headers.get === 'function') {\n return headers.get(name);\n }\n const lower = name.toLowerCase();\n const raw = (headers as Record<string, unknown>)[lower] ?? (headers as Record<string, unknown>)[name];\n if (typeof raw === 'string') return raw;\n if (Array.isArray(raw) && typeof raw[0] === 'string') return raw[0];\n return null;\n}\n\nasync function readPayload(req: WebhookRequest): Promise<WebhookPayload> {\n let raw: unknown;\n if (typeof req.json === 'function') {\n raw = await req.json();\n } else if (typeof req.text === 'function') {\n const text = await req.text();\n raw = text.length > 0 ? JSON.parse(text) : {};\n } else {\n throw new Error('Request object exposes neither json() nor text()');\n }\n\n if (!isWebhookPayload(raw)) {\n throw new Error('Payload is not a valid Strapi v5 webhook event');\n }\n return raw;\n}\n\nfunction isWebhookPayload(value: unknown): value is WebhookPayload {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.event !== 'string' || typeof v.model !== 'string' || typeof v.uid !== 'string') {\n return false;\n }\n if (typeof v.entry !== 'object' || v.entry === null) return false;\n const entry = v.entry as Record<string, unknown>;\n return typeof entry.documentId === 'string' || typeof entry.id === 'number';\n}\n\nfunction constantTimeEqual(a: string, b: string): boolean {\n // timingSafeEqual requires equal-length buffers; pad both to the longer length so the\n // length check itself is constant-time-safe (different lengths still return false).\n const bufA = Buffer.from(a, 'utf-8');\n const bufB = Buffer.from(b, 'utf-8');\n if (bufA.length !== bufB.length) return false;\n return timingSafeEqual(bufA, bufB);\n}\n"]}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { createWebhookHandler } from './handler.js';
6
6
  export type { WebhookHandler } from './handler.js';
7
+ export { buildWebhookEvent, resolveEntryId } from './event.js';
7
8
  export { mapModelToTags } from './tags.js';
8
9
  export type { WebhookEvent, WebhookEventName, WebhookPayload, WebhookRequest, WebhookResponse, } from './types.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,YAAY,CAAC"}
@@ -3,5 +3,6 @@
3
3
  * subpath.
4
4
  */
5
5
  export { createWebhookHandler } from './handler.js';
6
+ export { buildWebhookEvent, resolveEntryId } from './event.js';
6
7
  export { mapModelToTags } from './tags.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC","sourcesContent":["/**\n * Public webhook surface — re-exports for the `strapi-revalidate/webhook`\n * subpath.\n */\n\nexport { createWebhookHandler } from './handler.js';\nexport type { WebhookHandler } from './handler.js';\nexport { mapModelToTags } from './tags.js';\nexport type {\n WebhookEvent,\n WebhookEventName,\n WebhookPayload,\n WebhookRequest,\n WebhookResponse,\n} from './types.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC","sourcesContent":["/**\n * Public webhook surface — re-exports for the `strapi-revalidate/webhook`\n * subpath.\n */\n\nexport { createWebhookHandler } from './handler.js';\nexport type { WebhookHandler } from './handler.js';\nexport { buildWebhookEvent, resolveEntryId } from './event.js';\nexport { mapModelToTags } from './tags.js';\nexport type {\n WebhookEvent,\n WebhookEventName,\n WebhookPayload,\n WebhookRequest,\n WebhookResponse,\n} from './types.js';\n"]}
@@ -6,8 +6,12 @@
6
6
  * - `api::author.author` → `["authors"]` (+ `author:<slug>` if entry has slug)
7
7
  * - any other `api::<name>.<name>` → `[<name>]` (+ `<name>:<slug>` if present)
8
8
  *
9
+ * Slug-derived tags include the current slug and optional prior slugs when the
10
+ * webhook entry carries `previousSlug`, `oldSlug`, or `previous_slug` (e.g. from
11
+ * a Strapi lifecycle that records the value before a rename).
12
+ *
9
13
  * Override per-UID via `config.webhook.tagMap` — passing `["foo", "bar"]`
10
- * replaces the default tag list for that UID (the slug-derived tag is still
14
+ * replaces the default tag list for that UID (slug-derived tags are still
11
15
  * appended automatically when present).
12
16
  */
13
17
  import type { WebhookPayload } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/webhook/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAQjD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,MAAM,EAAE,CAKV"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/webhook/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAWjD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,MAAM,EAAE,CAIV"}
@@ -6,8 +6,12 @@
6
6
  * - `api::author.author` → `["authors"]` (+ `author:<slug>` if entry has slug)
7
7
  * - any other `api::<name>.<name>` → `[<name>]` (+ `<name>:<slug>` if present)
8
8
  *
9
+ * Slug-derived tags include the current slug and optional prior slugs when the
10
+ * webhook entry carries `previousSlug`, `oldSlug`, or `previous_slug` (e.g. from
11
+ * a Strapi lifecycle that records the value before a rename).
12
+ *
9
13
  * Override per-UID via `config.webhook.tagMap` — passing `["foo", "bar"]`
10
- * replaces the default tag list for that UID (the slug-derived tag is still
14
+ * replaces the default tag list for that UID (slug-derived tags are still
11
15
  * appended automatically when present).
12
16
  */
13
17
  /** Built-in UID → tag list. */
@@ -15,6 +19,8 @@ const DEFAULT_TAG_MAP = {
15
19
  'api::article.article': ['articles'],
16
20
  'api::author.author': ['authors'],
17
21
  };
22
+ /** Optional entry fields that may carry a slug from before a rename. */
23
+ const PREVIOUS_SLUG_KEYS = ['previousSlug', 'oldSlug', 'previous_slug'];
18
24
  /**
19
25
  * Resolve cache tags for a webhook payload.
20
26
  *
@@ -24,9 +30,22 @@ const DEFAULT_TAG_MAP = {
24
30
  */
25
31
  export function mapModelToTags(payload, overrides) {
26
32
  const base = overrides?.[payload.uid] ?? DEFAULT_TAG_MAP[payload.uid] ?? [extractModel(payload)];
27
- const slug = typeof payload.entry?.slug === 'string' ? payload.entry.slug : undefined;
28
- const tags = slug ? [...base, `${payload.model}:${slug}`] : base;
29
- return Array.from(new Set(tags));
33
+ const slugTags = collectSlugTags(payload);
34
+ return Array.from(new Set([...base, ...slugTags]));
35
+ }
36
+ function collectSlugTags(payload) {
37
+ const slugs = new Set();
38
+ const entry = payload.entry;
39
+ if (typeof entry.slug === 'string' && entry.slug.length > 0) {
40
+ slugs.add(entry.slug);
41
+ }
42
+ for (const key of PREVIOUS_SLUG_KEYS) {
43
+ const value = entry[key];
44
+ if (typeof value === 'string' && value.length > 0) {
45
+ slugs.add(value);
46
+ }
47
+ }
48
+ return Array.from(slugs).map((slug) => `${payload.model}:${slug}`);
30
49
  }
31
50
  /**
32
51
  * Extract the model name from a UID. `api::article.article` → `article`.
@@ -1 +1 @@
1
- {"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/webhook/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,+BAA+B;AAC/B,MAAM,eAAe,GAA6B;IAChD,sBAAsB,EAAE,CAAC,UAAU,CAAC;IACpC,oBAAoB,EAAE,CAAC,SAAS,CAAC;CAClC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAuB,EACvB,SAAoC;IAEpC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACjG,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,OAAuB;IAC3C,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;AACrC,CAAC","sourcesContent":["/**\n * Strapi UID → cache tag mapping.\n *\n * Default rules:\n * - `api::article.article` → `[\"articles\"]` (+ `article:<slug>` if entry has slug)\n * - `api::author.author` → `[\"authors\"]` (+ `author:<slug>` if entry has slug)\n * - any other `api::<name>.<name>` → `[<name>]` (+ `<name>:<slug>` if present)\n *\n * Override per-UID via `config.webhook.tagMap` — passing `[\"foo\", \"bar\"]`\n * replaces the default tag list for that UID (the slug-derived tag is still\n * appended automatically when present).\n */\n\nimport type { WebhookPayload } from './types.js';\n\n/** Built-in UID → tag list. */\nconst DEFAULT_TAG_MAP: Record<string, string[]> = {\n 'api::article.article': ['articles'],\n 'api::author.author': ['authors'],\n};\n\n/**\n * Resolve cache tags for a webhook payload.\n *\n * @param payload - Raw Strapi v5 webhook payload.\n * @param overrides - Optional UID → tag list overrides from config.\n * @returns Deduplicated list of tags to invalidate.\n */\nexport function mapModelToTags(\n payload: WebhookPayload,\n overrides?: Record<string, string[]>\n): string[] {\n const base = overrides?.[payload.uid] ?? DEFAULT_TAG_MAP[payload.uid] ?? [extractModel(payload)];\n const slug = typeof payload.entry?.slug === 'string' ? payload.entry.slug : undefined;\n const tags = slug ? [...base, `${payload.model}:${slug}`] : base;\n return Array.from(new Set(tags));\n}\n\n/**\n * Extract the model name from a UID. `api::article.article` → `article`.\n * Falls back to `payload.model` if the UID is in an unexpected shape.\n */\nfunction extractModel(payload: WebhookPayload): string {\n const match = /^api::([^.]+)\\./.exec(payload.uid);\n return match?.[1] ?? payload.model;\n}\n"]}
1
+ {"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/webhook/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,+BAA+B;AAC/B,MAAM,eAAe,GAA6B;IAChD,sBAAsB,EAAE,CAAC,UAAU,CAAC;IACpC,oBAAoB,EAAE,CAAC,SAAS,CAAC;CAClC,CAAC;AAEF,wEAAwE;AACxE,MAAM,kBAAkB,GAAG,CAAC,cAAc,EAAE,SAAS,EAAE,eAAe,CAAU,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAuB,EACvB,SAAoC;IAEpC,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACjG,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,eAAe,CAAC,OAAuB;IAC9C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAE5B,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,OAAuB;IAC3C,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClD,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;AACrC,CAAC","sourcesContent":["/**\n * Strapi UID → cache tag mapping.\n *\n * Default rules:\n * - `api::article.article` → `[\"articles\"]` (+ `article:<slug>` if entry has slug)\n * - `api::author.author` → `[\"authors\"]` (+ `author:<slug>` if entry has slug)\n * - any other `api::<name>.<name>` → `[<name>]` (+ `<name>:<slug>` if present)\n *\n * Slug-derived tags include the current slug and optional prior slugs when the\n * webhook entry carries `previousSlug`, `oldSlug`, or `previous_slug` (e.g. from\n * a Strapi lifecycle that records the value before a rename).\n *\n * Override per-UID via `config.webhook.tagMap` — passing `[\"foo\", \"bar\"]`\n * replaces the default tag list for that UID (slug-derived tags are still\n * appended automatically when present).\n */\n\nimport type { WebhookPayload } from './types.js';\n\n/** Built-in UID → tag list. */\nconst DEFAULT_TAG_MAP: Record<string, string[]> = {\n 'api::article.article': ['articles'],\n 'api::author.author': ['authors'],\n};\n\n/** Optional entry fields that may carry a slug from before a rename. */\nconst PREVIOUS_SLUG_KEYS = ['previousSlug', 'oldSlug', 'previous_slug'] as const;\n\n/**\n * Resolve cache tags for a webhook payload.\n *\n * @param payload - Raw Strapi v5 webhook payload.\n * @param overrides - Optional UID → tag list overrides from config.\n * @returns Deduplicated list of tags to invalidate.\n */\nexport function mapModelToTags(\n payload: WebhookPayload,\n overrides?: Record<string, string[]>\n): string[] {\n const base = overrides?.[payload.uid] ?? DEFAULT_TAG_MAP[payload.uid] ?? [extractModel(payload)];\n const slugTags = collectSlugTags(payload);\n return Array.from(new Set([...base, ...slugTags]));\n}\n\nfunction collectSlugTags(payload: WebhookPayload): string[] {\n const slugs = new Set<string>();\n const entry = payload.entry;\n\n if (typeof entry.slug === 'string' && entry.slug.length > 0) {\n slugs.add(entry.slug);\n }\n\n for (const key of PREVIOUS_SLUG_KEYS) {\n const value = entry[key];\n if (typeof value === 'string' && value.length > 0) {\n slugs.add(value);\n }\n }\n\n return Array.from(slugs).map((slug) => `${payload.model}:${slug}`);\n}\n\n/**\n * Extract the model name from a UID. `api::article.article` → `article`.\n * Falls back to `payload.model` if the UID is in an unexpected shape.\n */\nfunction extractModel(payload: WebhookPayload): string {\n const match = /^api::([^.]+)\\./.exec(payload.uid);\n return match?.[1] ?? payload.model;\n}\n"]}
@@ -13,8 +13,8 @@ export interface WebhookPayload {
13
13
  /** Strapi UID, e.g. `"api::article.article"`. */
14
14
  uid: string;
15
15
  entry: {
16
- id: number;
17
- documentId: string;
16
+ id?: number;
17
+ documentId?: string;
18
18
  slug?: string;
19
19
  [key: string]: unknown;
20
20
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webhook/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,cAAc,GACd,cAAc,GACd,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,cAAc,CAAC;AAEnB,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;QACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webhook/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+CAA+C;AAC/C,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,cAAc,GACd,cAAc,GACd,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,cAAc,CAAC;AAEnB,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,gBAAgB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;QACtC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webhook/types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * Strapi v5 webhook payload types and the normalized event surfaced to\n * downstream handlers.\n */\n\n/** All webhook event names Strapi v5 emits. */\nexport type WebhookEventName =\n | 'entry.create'\n | 'entry.update'\n | 'entry.delete'\n | 'entry.publish'\n | 'entry.unpublish'\n | 'media.create'\n | 'media.update'\n | 'media.delete';\n\n/** Raw payload Strapi v5 POSTs to the webhook URL. */\nexport interface WebhookPayload {\n event: WebhookEventName;\n createdAt: string;\n /** Singular model name, e.g. `\"article\"`. */\n model: string;\n /** Strapi UID, e.g. `\"api::article.article\"`. */\n uid: string;\n entry: {\n id: number;\n documentId: string;\n slug?: string;\n [key: string]: unknown;\n };\n}\n\n/** Normalized event passed to `onRevalidate` after tag resolution. */\nexport interface WebhookEvent {\n event: WebhookEventName;\n model: string;\n uid: string;\n entryId: string;\n slug?: string;\n /** Cache tags resolved from `uid` (and `slug`, if present). */\n tags: string[];\n}\n\n/**\n * Minimal request interface the webhook handler accepts.\n *\n * Designed so an Astro `APIContext.request`, a Next.js `Request`, a\n * SvelteKit `RequestEvent.request`, an Express `req`, and a Node\n * `IncomingMessage` can all satisfy it (after light adapter code in the\n * user's route handler).\n */\nexport interface WebhookRequest {\n method?: string;\n headers: {\n get?: (name: string) => string | null;\n [key: string]: unknown;\n };\n text?: () => Promise<string>;\n json?: () => Promise<unknown>;\n}\n\n/**\n * Minimal response interface the webhook handler writes to.\n *\n * The handler always calls `status(code)` first and then `json(body)`. Both\n * calls are independent — `status()`'s return value is ignored — so adapters\n * in framework routes can be a few lines of capture-and-forward.\n */\nexport interface WebhookResponse {\n status: (code: number) => unknown;\n json: (body: unknown) => void | Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/webhook/types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * Strapi v5 webhook payload types and the normalized event surfaced to\n * downstream handlers.\n */\n\n/** All webhook event names Strapi v5 emits. */\nexport type WebhookEventName =\n | 'entry.create'\n | 'entry.update'\n | 'entry.delete'\n | 'entry.publish'\n | 'entry.unpublish'\n | 'media.create'\n | 'media.update'\n | 'media.delete';\n\n/** Raw payload Strapi v5 POSTs to the webhook URL. */\nexport interface WebhookPayload {\n event: WebhookEventName;\n createdAt: string;\n /** Singular model name, e.g. `\"article\"`. */\n model: string;\n /** Strapi UID, e.g. `\"api::article.article\"`. */\n uid: string;\n entry: {\n id?: number;\n documentId?: string;\n slug?: string;\n [key: string]: unknown;\n };\n}\n\n/** Normalized event passed to `onRevalidate` after tag resolution. */\nexport interface WebhookEvent {\n event: WebhookEventName;\n model: string;\n uid: string;\n entryId: string;\n slug?: string;\n /** Cache tags resolved from `uid` (and `slug`, if present). */\n tags: string[];\n}\n\n/**\n * Minimal request interface the webhook handler accepts.\n *\n * Designed so an Astro `APIContext.request`, a Next.js `Request`, a\n * SvelteKit `RequestEvent.request`, an Express `req`, and a Node\n * `IncomingMessage` can all satisfy it (after light adapter code in the\n * user's route handler).\n */\nexport interface WebhookRequest {\n method?: string;\n headers: {\n get?: (name: string) => string | null;\n [key: string]: unknown;\n };\n text?: () => Promise<string>;\n json?: () => Promise<unknown>;\n}\n\n/**\n * Minimal response interface the webhook handler writes to.\n *\n * The handler always calls `status(code)` first and then `json(body)`. Both\n * calls are independent — `status()`'s return value is ignored — so adapters\n * in framework routes can be a few lines of capture-and-forward.\n */\nexport interface WebhookResponse {\n status: (code: number) => unknown;\n json: (body: unknown) => void | Promise<void>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datum-cloud/strapi-revalidate",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Framework-agnostic runtime cache invalidation for Strapi v5 in SSR frontends. Receives Strapi publish webhooks, clears affected cache tags, next request serves fresh data.",
5
5
  "keywords": [
6
6
  "strapi",