{"_id":"@smartive/utils","_rev":"4-1d5590cb3edbd9494f71df71e667d9aa","name":"@smartive/utils","dist-tags":{"latest":"4.2.0"},"versions":{"4.0.0":{"name":"@smartive/utils","version":"4.0.0","keywords":["utils","helpers"],"author":{"name":"smartive AG"},"license":"MIT","_id":"@smartive/utils@4.0.0","maintainers":[{"name":"smartive-ci","email":"it@smartive.ch"},{"name":"mfeltscher","email":"moreno@smartive.ch"},{"name":"petermanser","email":"mail@petermanser.ch"},{"name":"dwirz","email":"dominique.wirz@gmail.com"},{"name":"nmaro","email":"nicolamr@gmail.com"}],"homepage":"https://github.com/smartive/utils#readme","bugs":{"url":"https://github.com/smartive/utils/issues"},"dist":{"shasum":"8fdc0a8b0b4b44b7dd711b928e7d9da87ddafcd2","tarball":"https://registry.npmjs.org/@smartive/utils/-/utils-4.0.0.tgz","fileCount":15,"integrity":"sha512-Qp04VnLdPqwPGgRGKDOEU/q2hju4HY3r9Yzg6/QbOWAknLRYdSsBF5YXRxKQTbep/t8I1fUSsqiFWKQUnRHR7w==","signatures":[{"sig":"MEYCIQDSy8M7pDMpErOsJa3WyvcweY1SqMLfMochiSTYiHoR9wIhAPCdYKNsQlVajP2v+/mylpaeYuz08PwNyUwYWq7Y1OuM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":7019},"type":"module","source":"./src/index.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"96369c33e98bae6862dc169e3525e545761ca2a8","scripts":{"lint":"eslint src","build":"tsc","clean":"rimraf dist","prebuild":"npm run clean","prettier":"prettier --check src"},"_npmUser":{"name":"smartive-ci","email":"it@smartive.ch"},"repository":{"url":"git+https://github.com/smartive/utils.git","type":"git"},"_npmVersion":"11.16.0","description":"A set of general purpose utilities and helpers for web projects.","directories":{},"_nodeVersion":"24.18.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"9.39.5","rimraf":"6.1.3","prettier":"3.9.6","typescript":"5.9.3","@types/node":"24.13.3","@smartive/eslint-config":"7.0.1","@smartive/prettier-config":"3.1.2","eslint-import-resolver-typescript":"4.4.5"},"_npmOperationalInternal":{"tmp":"tmp/utils_4.0.0_1785488271545_0.16099021096312272","host":"s3://npm-registry-packages-npm-production"}},"4.1.0":{"name":"@smartive/utils","version":"4.1.0","keywords":["utils","helpers","datocms","nextjs"],"author":{"name":"smartive AG"},"license":"MIT","_id":"@smartive/utils@4.1.0","maintainers":[{"name":"smartive-ci","email":"it@smartive.ch"},{"name":"mfeltscher","email":"moreno@smartive.ch"},{"name":"petermanser","email":"mail@petermanser.ch"},{"name":"dwirz","email":"dominique.wirz@gmail.com"},{"name":"nmaro","email":"nicolamr@gmail.com"}],"homepage":"https://github.com/smartive/utils#readme","bugs":{"url":"https://github.com/smartive/utils/issues"},"dist":{"shasum":"adc2a4083c6ec2f137e934c9d01d5c88c7a9ba74","tarball":"https://registry.npmjs.org/@smartive/utils/-/utils-4.1.0.tgz","fileCount":59,"integrity":"sha512-J++Z8AzMiFz06NyYcJmHD8nCRNjtzFURK3MBhqZ6C7oKhVIueEpdF4zF1fRMu8GDKIUKTjSqjw/GRLVFT01zDQ==","signatures":[{"sig":"MEQCIEIKMbJIWd2baT1Gu9HM4uOnad/QbvhbefnU0HCVJYmaAiAZgpQhmZjBHA0ypnubUtx4ksqVi4z7qmBWjRaQEvUzlA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@smartive%2futils@4.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":65424},"type":"module","source":"./src/index.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./http":{"types":"./dist/http/index.d.ts","import":"./dist/http/index.js"},"./next":{"types":"./dist/next/index.d.ts","import":"./dist/next/index.js"},"./datocms":{"types":"./dist/datocms/index.d.ts","import":"./dist/datocms/index.js"}},"gitHead":"8cee74c57b33dfb3420365af12c9cfff3d3ef4d7","scripts":{"lint":"eslint src","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rimraf dist","prebuild":"npm run clean","prettier":"prettier --check src"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:581848d8-c12f-42a2-bfdb-fbfea05c5ad1"}},"overrides":{"sharp":"0.35.3","postcss":"8.5.25","brace-expansion":"5.0.9"},"repository":{"url":"git+https://github.com/smartive/utils.git","type":"git"},"_npmVersion":"11.16.0","description":"A set of general purpose utilities and helpers for web projects.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"next":"16.2.12","react":"19.2.8","eslint":"9.39.5","rimraf":"6.1.3","vitest":"4.1.10","prettier":"3.9.6","react-dom":"19.2.8","typescript":"5.9.3","@types/node":"24.13.3","@datocms/cda-client":"0.2.10","@smartive/eslint-config":"7.0.1","@smartive/prettier-config":"3.1.2","eslint-import-resolver-typescript":"4.4.5"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","@datocms/cda-client":"^0.2.10"},"peerDependenciesMeta":{"next":{"optional":true},"@datocms/cda-client":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_4.1.0_1785497900143_0.6807925495845031","host":"s3://npm-registry-packages-npm-production"}},"4.1.1":{"name":"@smartive/utils","version":"4.1.1","keywords":["utils","helpers","datocms","nextjs"],"author":{"name":"smartive AG"},"license":"MIT","_id":"@smartive/utils@4.1.1","maintainers":[{"name":"smartive-ci","email":"it@smartive.ch"},{"name":"mfeltscher","email":"moreno@smartive.ch"},{"name":"petermanser","email":"mail@petermanser.ch"},{"name":"dwirz","email":"dominique.wirz@gmail.com"},{"name":"nmaro","email":"nicolamr@gmail.com"}],"homepage":"https://github.com/smartive/utils#readme","bugs":{"url":"https://github.com/smartive/utils/issues"},"dist":{"shasum":"1a204acd30e1bb58b005f60dc1bd96ae33f41631","tarball":"https://registry.npmjs.org/@smartive/utils/-/utils-4.1.1.tgz","fileCount":59,"integrity":"sha512-0WSnqOUcuyveoYJlKD2BBeWcPiPpb9sWMU8WH9WJlditi+ysFgR66S7cBGT4oN6CiWXfHS7Sg5coofpOJy+Z8g==","signatures":[{"sig":"MEYCIQCfyRt4mxm+A28sKxZn7iKouI+ocTZ4Tlvx+4SP4V0v4AIhAO43tn/8HC262NG+Ga4r5prWtVCeh9sT0MDzZ6pzUjsa","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@smartive%2futils@4.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":65432},"type":"module","source":"./src/index.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./http":{"types":"./dist/http/index.d.ts","import":"./dist/http/index.js"},"./next":{"types":"./dist/next/index.d.ts","import":"./dist/next/index.js"},"./datocms":{"types":"./dist/datocms/index.d.ts","import":"./dist/datocms/index.js"}},"gitHead":"1d9fdc135ed81dffe7c8fe6f44cdd25bfe712963","scripts":{"lint":"eslint src","test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rimraf dist","prebuild":"npm run clean","prettier":"prettier --check src"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:581848d8-c12f-42a2-bfdb-fbfea05c5ad1"}},"overrides":{"sharp":"0.35.3","postcss":"8.5.26","brace-expansion":"5.0.9"},"repository":{"url":"git+https://github.com/smartive/utils.git","type":"git"},"_npmVersion":"11.17.0","description":"A set of general purpose utilities and helpers for web projects.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"next":"16.3.1","react":"19.2.8","eslint":"9.39.5","rimraf":"6.1.3","vitest":"4.1.10","prettier":"3.9.6","react-dom":"19.2.8","typescript":"5.9.3","@types/node":"24.13.3","@datocms/cda-client":"0.3.0","@smartive/eslint-config":"7.0.1","@smartive/prettier-config":"3.1.2","eslint-import-resolver-typescript":"4.4.5"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","@datocms/cda-client":"^0.2.10 || ^0.3.0"},"peerDependenciesMeta":{"next":{"optional":true},"@datocms/cda-client":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/utils_4.1.1_1787124953613_0.6782832450925569","host":"s3://npm-registry-packages-npm-production"}},"4.2.0":{"name":"@smartive/utils","version":"4.2.0","description":"A set of general purpose utilities and helpers for web projects.","type":"module","source":"./src/index.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./http":{"types":"./dist/http/index.d.ts","import":"./dist/http/index.js"},"./datocms":{"types":"./dist/datocms/index.d.ts","import":"./dist/datocms/index.js"},"./datocms/next":{"types":"./dist/datocms/next/index.d.ts","import":"./dist/datocms/next/index.js"},"./cache-tags":{"types":"./dist/cache-tags/index.d.ts","import":"./dist/cache-tags/index.js"},"./cache-tags/neon":{"types":"./dist/cache-tags/neon.d.ts","import":"./dist/cache-tags/neon.js"},"./next":{"types":"./dist/next/index.d.ts","import":"./dist/next/index.js"}},"scripts":{"clean":"rimraf dist","prebuild":"npm run clean","build":"tsc -p tsconfig.build.json","lint":"eslint src","prettier":"prettier --check src","test":"vitest run","typecheck":"tsc -p tsconfig.json --noEmit"},"publishConfig":{"access":"public"},"keywords":["utils","helpers","datocms","nextjs","cache-components","cache-tags"],"author":{"name":"smartive AG"},"license":"MIT","repository":{"url":"git+https://github.com/smartive/utils.git","type":"git"},"peerDependencies":{"@datocms/cda-client":"^0.2.10 || ^0.3.0","@neondatabase/serverless":"^1.0.0","next":"^15.0.0 || ^16.0.0"},"peerDependenciesMeta":{"@neondatabase/serverless":{"optional":true},"@datocms/cda-client":{"optional":true},"next":{"optional":true}},"overrides":{"brace-expansion":"5.0.9","postcss":"8.5.26","sharp":"0.35.4"},"devDependencies":{"@datocms/cda-client":"0.3.0","@neondatabase/serverless":"1.1.0","@smartive/eslint-config":"7.0.1","@smartive/prettier-config":"3.1.2","@types/node":"24.13.3","eslint":"9.39.5","eslint-import-resolver-typescript":"4.4.5","next":"16.3.3","prettier":"3.9.6","react":"19.2.8","react-dom":"19.2.8","rimraf":"6.1.3","typescript":"5.9.3","vitest":"4.1.11"},"gitHead":"106fc5da2b156b002ddd63f14d895150b8a17e9e","_id":"@smartive/utils@4.2.0","bugs":{"url":"https://github.com/smartive/utils/issues"},"homepage":"https://github.com/smartive/utils#readme","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-yFoC+LkOFwVQUW32ZxH3ZGflTukxltaczcWF/9s2hbp3iQNSrBIJAw76RbbXrY/WFm3OPslXs9Ok6P37+D6+iA==","shasum":"2ac6ba9e279509717ee094934c7f7e5eff64fe99","tarball":"https://registry.npmjs.org/@smartive/utils/-/utils-4.2.0.tgz","fileCount":119,"unpackedSize":195898,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@smartive%2futils@4.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDHqBvYecHNPL253zqdxiSXcZF3YaBBbdJQ9JUEWoCRRAiBHYakCEYyH/KmyOhmXjjmMAGTaSS3FLNibY5f6DtOsjg=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:581848d8-c12f-42a2-bfdb-fbfea05c5ad1"}},"directories":{},"maintainers":[{"name":"smartive-ci","email":"it@smartive.ch"},{"name":"mfeltscher","email":"moreno@smartive.ch"},{"name":"petermanser","email":"mail@petermanser.ch"},{"name":"dwirz","email":"dominique.wirz@gmail.com"},{"name":"nmaro","email":"nicolamr@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/utils_4.2.0_1788422150465_0.8388171209644064"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T08:57:51.378Z","modified":"2026-09-03T07:55:51.011Z","4.0.0":"2026-07-31T08:57:51.664Z","4.1.0":"2026-07-31T11:38:20.296Z","4.1.1":"2026-08-19T07:35:53.784Z","4.2.0":"2026-09-03T07:55:50.611Z"},"bugs":{"url":"https://github.com/smartive/utils/issues"},"author":{"name":"smartive AG"},"license":"MIT","homepage":"https://github.com/smartive/utils#readme","keywords":["utils","helpers","datocms","nextjs","cache-components","cache-tags"],"repository":{"url":"git+https://github.com/smartive/utils.git","type":"git"},"description":"A set of general purpose utilities and helpers for web projects.","maintainers":[{"name":"smartive-ci","email":"it@smartive.ch"},{"name":"mfeltscher","email":"moreno@smartive.ch"},{"name":"petermanser","email":"mail@petermanser.ch"},{"name":"dwirz","email":"dominique.wirz@gmail.com"},{"name":"nmaro","email":"nicolamr@gmail.com"}],"readme":"# smartive Utilities\n\nA collection of general purpose utilities and helpers for web projects.\n\n## Installation\n\n```bash\nnpm install @smartive/utils\n```\n\nThe root export (`@smartive/utils`) stays dependency-free. Optional peer dependencies are only\nrequired when you import the corresponding subpath.\n\n## Utilities\n\n### `classNames`\n\nCleans and joins an array of class names (strings and numbers), filtering out undefined and boolean values.\n\n```typescript\nimport { classNames } from '@smartive/utils';\n\nconst className = classNames('btn', isActive && 'btn-active', 42, undefined, 'btn-primary');\n// Result: \"btn btn-active 42 btn-primary\"\n```\n\n### `getTelLink`\n\nConverts a phone number into a `tel:` link by removing non-digit characters (except `+` for international numbers).\n\n```typescript\nimport { getTelLink } from '@smartive/utils';\n\nconst link = getTelLink('+1 (555) 123-4567');\n// Result: \"tel:+15551234567\"\n```\n\n## `@smartive/utils/http`\n\nFramework-agnostic HTTP helpers for token checks, open-redirect protection, and CORS headers.\n\n```typescript\nimport { isSafeRelativePath, isValidToken, withCORS } from '@smartive/utils/http';\n\nisValidToken(request.headers.get('Webhook-Token'), process.env.CACHE_INVALIDATION_SECRET_TOKEN);\nisSafeRelativePath('/relative/path');\nwithCORS({ status: 401 });\n```\n\nNo environment variables are required by this subpath; callers pass secrets explicitly.\n\n## `@smartive/utils/datocms`\n\nTyped GraphQL client factory wrapping [`@datocms/cda-client`](https://www.npmjs.com/package/@datocms/cda-client).\n\n```bash\nnpm install @datocms/cda-client\n```\n\n```typescript\nimport { createDatoClient, queryDatoCMS } from '@smartive/utils/datocms';\n\n// Default client (reads env vars)\nconst data = await queryDatoCMS({ document: MyDocument, includeDrafts: true });\n\n// Or configure explicitly\nconst query = createDatoClient({\n  apiToken: process.env.DATOCMS_API_TOKEN,\n  revalidate: 60 * 60,\n});\n```\n\n| Env var                         | Purpose                                           |\n| ------------------------------- | ------------------------------------------------- |\n| `DATOCMS_API_TOKEN`             | Read-only CDA token (draft-capable when needed)   |\n| `DATOCMS_ENVIRONMENT`           | Optional `X-Environment` header                   |\n| `NEXT_DATOCMS_BASE_EDITING_URL` | Enables Content Link headers when querying drafts |\n\nConfig passed to `createDatoClient` always wins over environment variables.\n\n## `@smartive/utils/datocms/next`\n\nThe same client, backed by [Next.js Cache Components](https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents)\ninstead of the `fetch` data cache. Queries are wrapped in `use cache`, tagged with a\ndeterministic query ID, and given a `cacheLife` profile — so a DatoCMS publish can\ninvalidate exactly the affected queries instead of purging the whole app.\n\n**Requires Next.js >= 16 with `cacheComponents: true`.** Without that flag, use\n`@smartive/utils/datocms`.\n\n```bash\nnpm install @datocms/cda-client @neondatabase/serverless\n```\n\n```typescript\n// lib/dato.ts\nimport { createNeonCacheTagStore } from '@smartive/utils/cache-tags/neon';\nimport { createCachedDatoClient } from '@smartive/utils/datocms/next';\n\nexport const store = createNeonCacheTagStore();\n\nexport const queryDatoCMS = createCachedDatoClient({ store });\n```\n\n```typescript\n// app/api/invalidate-cache-tags/route.ts\nimport { createCacheTagInvalidationHandler } from '@smartive/utils/next';\nimport { store } from '@/lib/dato';\n\nexport const { POST, GET, OPTIONS } = createCacheTagInvalidationHandler({ store });\n```\n\nPoint a DatoCMS **cache tags invalidation** webhook at that `POST` route, with the secret in\nthe `Webhook-Token` header.\n\nThere is deliberately **no default client**: one without a `store` looks like it works but can\nnever be invalidated, so the store is a required, visible decision. Omitting it is still\nsupported for local development — every entry then falls back to the short `unstored` lifetime.\n\n### `cacheLife` profiles\n\n| Profile    | Default     | When it applies                                                     |\n| ---------- | ----------- | ------------------------------------------------------------------- |\n| `cached`   | `'days'`    | The tag mapping persisted, so the webhook can reach the entry       |\n| `unstored` | `'minutes'` | The mapping could not be stored — the entry must expire on its own  |\n| `draft`    | `'seconds'` | Draft mode is enabled (Next writes nothing to the cache regardless) |\n\nOverride per client via `profiles`, or per query via `cacheProfile`. A per-query override is\nignored when the mapping did not persist, so it can never extend the lifetime of an entry the\nwebhook cannot reach.\n\n### Draft mode\n\n`draftMode().isEnabled` is read _inside_ the cached scope, which Next.js permits. While draft\nmode is on, cached functions re-execute per request and nothing is written to the cache, so\n`includeDrafts` no longer needs threading through your data layer.\n\nPass `includeDrafts: true` (or `skipCache: true`) explicitly only when you need a guaranteed\nfresh read — preview slug resolution, for example. That bypasses the cached scope entirely.\n\n## `@smartive/utils/cache-tags`\n\nThe query-ID ⇄ DatoCMS cache-tag mapping used by `@smartive/utils/datocms/next`. Zero\ndependencies; no Next.js import.\n\n```typescript\nimport { buildQueryId, createMemoryCacheTagStore, parseXCacheTagsResponseHeader } from '@smartive/utils/cache-tags';\n```\n\n`createMemoryCacheTagStore()` is for tests, local development, and single-instance deployments\nonly — on a serverless platform each instance would see a different subset of the mapping. It\nalso accepts `{ configured: false }` and `{ failing: true }` for exercising fail-soft paths.\n\n### `@smartive/utils/cache-tags/neon`\n\n```typescript\nimport { cacheTagStoreSchemaSql, createNeonCacheTagStore } from '@smartive/utils/cache-tags/neon';\n\nconst store = createNeonCacheTagStore({ onError: (error) => captureException(error) });\n```\n\nCreate the table once per Neon branch:\n\n```bash\npsql \"$CACHETAGS_POSTGRES_URL\" -c \"$(node -e \"import('@smartive/utils/cache-tags/neon').then((m) => console.log(m.cacheTagStoreSchemaSql()))\")\"\n```\n\nThe store **never throws**: every method fails soft to a documented fallback and arms a 30-second\nbackoff, because a store outage must degrade cache lifetimes rather than fail a page render.\nWrites report a `boolean`, and lookups return `null` on failure versus `[]` for \"nothing matched\" —\nthat distinction is what drives the `cacheLife` choice and the webhook's 503-vs-200 response.\n\nIsolate the store per DatoCMS environment (a Neon branch per deployment environment). The query ID\nalready includes the resolved environment, but a table shared between _apps_ also needs a\n`tagPrefix`.\n\n| Env var                  | Purpose                                      |\n| ------------------------ | -------------------------------------------- |\n| `CACHETAGS_POSTGRES_URL` | Neon connection string for the mapping table |\n\n> One synthetic tag per query, rather than one tag per DatoCMS record, is deliberate: Vercel caps a\n> cache entry at 128 tags.\n\n## `@smartive/utils/next`\n\nNext.js App Router helpers for draft mode, DatoCMS web previews, and cache revalidation.\n\n```bash\nnpm install next\n```\n\n```typescript\n// app/api/draft/enable/route.ts\nimport { createDraftHandlers } from '@smartive/utils/next';\n\nexport const { enable: GET } = createDraftHandlers();\n\n// app/api/draft/preview-links/route.ts\nimport { createWebPreviewsHandler } from '@smartive/utils/next';\n\nexport const { OPTIONS, POST } = createWebPreviewsHandler({\n  baseUrl: 'https://example.com/api/draft',\n  resolvePreviewUrl: async ({ item, itemType }) => {\n    if (itemType.attributes.api_key === 'page') return `/${item.attributes.slug}`;\n    return null;\n  },\n});\n\n// app/api/revalidate-path/route.ts\nimport { createRevalidateHandler } from '@smartive/utils/next';\n\nexport const POST = createRevalidateHandler({ paths: ['/sitemap.xml'] });\n```\n\nDraft enable/disable and web-preview links use the `url` query parameter for redirect targets\n(e.g. `/api/draft/enable?url=/page&token=…`).\n\n`createRevalidateHandler` revalidates `'/'` with `'layout'` — the whole app — on every call. For\nper-query invalidation, use `createCacheTagInvalidationHandler` with a\n[cache-tag store](#smartiveutilscache-tagsneon) instead. `createCacheTagInvalidateAllHandler`\ncovers the manual full reset (a missed webhook, or a change to the query-ID format).\n\n| Env var                           | Purpose                                                            |\n| --------------------------------- | ------------------------------------------------------------------ |\n| `DRAFT_SECRET_TOKEN`              | Authorizes draft enable/disable and web-previews                   |\n| `CACHE_INVALIDATION_SECRET_TOKEN` | Authorizes the revalidate and cache-tag webhooks (`Webhook-Token`) |\n\n## Migrating from `@smartive/datocms-utils`\n\nThis package was previously published as `@smartive/datocms-utils`. With `4.0.0` it was renamed to\n`@smartive/utils` and the old cache-tag utilities were removed.\n\n- `classNames` and `getTelLink` are unchanged — only the import specifier needs to be updated.\n- DatoCMS / Next.js helpers live under `@smartive/utils/http`, `@smartive/utils/datocms`, and\n  `@smartive/utils/next`.\n\n### Cache tags\n\nThe cache-tag utilities are **back**, rebuilt for Next.js Cache Components. You no longer need to\nstay on `@smartive/datocms-utils@3`.\n\n| Removed in 4.0.0                | Replacement                                             |\n| ------------------------------- | ------------------------------------------------------- |\n| `CacheTagsProvider` (interface) | `CacheTagStore` from `@smartive/utils/cache-tags`       |\n| `NeonCacheTagsProvider`         | `createNeonCacheTagStore` (`/cache-tags/neon`)          |\n| `NoopCacheTagsProvider`         | `createMemoryCacheTagStore` (`/cache-tags`)             |\n| `RedisCacheTagsProvider`        | Not ported — open an issue if you need it               |\n| `generateQueryId`               | `buildQueryId` — **different output format**, see below |\n| `parseXCacheTagsResponseHeader` | Unchanged, now from `@smartive/utils/cache-tags`        |\n| `CacheTag` (branded type)       | Plain `string`                                          |\n| `CacheTagsInvalidateWebhook`    | Unchanged, plus an `isCacheTagsInvalidateWebhook` guard |\n\nBehavioural changes worth knowing:\n\n- **Query IDs changed format.** `buildQueryId` emits `<operationName>-<hash16>` rather than a bare\n  sha1, so every stored mapping from v3 is unreachable. Run the invalidate-all handler once after\n  upgrading to clear them.\n- **Stores never throw.** The `throwOnError` option is gone: a store outage must degrade cache\n  lifetimes, not fail a render. `onError` remains, for telemetry.\n- **Return types carry more signal.** `storeQueryCacheTags` returns `boolean` and\n  `queriesReferencingCacheTags` returns `string[] | null`, which is what lets the client pick a\n  `cacheLife` profile and the webhook distinguish 503 from 200.\n- **`buildQueryId` needs no `graphql` dependency.** It hashes the document structurally, ignoring\n  `loc`, so `graphql-tag` output is stable across unrelated edits in the same file.\n\n## License\n\nMIT © [smartive AG](https://github.com/smartive)\n","readmeFilename":"README.md"}