Version 2.2.0

Two new init() config knobs — intercept toggles and essentialKeys/Patterns — that make headless integrations cleaner without breaking any v2.1 consumer

@freshjuice/zest@2.2.0 adds two new init() config fields that fix real friction points discovered while wiring Zest into projects where the consent state machine was wanted, but the cookie / storage / script interceptors weren't. See the full release on GitHub.

Zero runtime change for v2.1 consumers. Every default is preserved.

Highlights

intercept toggles

Disable any of Zest's interceptor channels independently. Defaults are all on, so existing consumers keep their current behaviour.

Zest.init({
  intercept: {
    cookies: false,
    storage: false,
    scripts: false
  }
});

Use this when you gate optional scripts and storage yourself and want Zest only for the consent state machine — DNT/GPC handling, the signed zest_consent cookie, the Zest.on() event surface, and the consent proof.

Previously the only way to dodge the storage / cookie interceptor was to skip Zest.init() entirely, which also threw away the consent engine. The mode field only ever affected the script blocker, so headless consumers had no clean lever for the other two channels.

essentialKeys — declare strictly-necessary names

The easy case for "I have storage keys X and Y, treat them as essential":

Zest.init({
  essentialKeys: ['app_settings', 'theme_pref']
});

Each name is anchored as ^name$ and appended to the essential category. The built-in essential patterns (zest_*, csrf*, xsrf*, session*, __host-*, __secure-*) stay intact — your additions sit alongside.

essentialPatterns — regex source strings

For prefix or family matches:

Zest.init({
  essentialPatterns: ['^app_', '^_my_csrf']
});

Validated via the same safeRegExp used everywhere else in Zest. Invalid or catastrophic-backtracking patterns are dropped silently with a console warning. Capped at 500 characters per pattern.

Why not just use patterns.essential?

The existing patterns config is still there and still works, but it replaces the patterns for any category you pass in. If you set patterns.essential you silently lose the built-in defaults. The new fields are additive — you keep all defaults and just extend.

Use patterns when you want full control of the matcher for a category. Use essentialKeys / essentialPatterns when you just want to add a few entries.

TypeScript types

InitOptions now exposes intercept, essentialKeys, essentialPatterns, and patterns as first-class typed fields in both zest.d.ts and zest.headless.d.ts. The [key: string]: unknown escape hatch is no longer needed for any of these.

import Zest, { InitOptions, InterceptToggles } from '@freshjuice/zest/headless';

const config: InitOptions = {
  respectDNT: true,
  intercept: { scripts: false },
  essentialKeys: ['ct_settings'],
  essentialPatterns: ['^app_']
};

Zest.init(config);

Migration from v2.1

None required. Every existing config keeps working exactly as before.

If you previously worked around storage interception by overriding patterns.essential with the built-in list plus your own keys, you can now drop the boilerplate:

- patterns: {
-   essential: [
-     '^zest_',
-     '^csrf', '^xsrf', '^session',
-     '^__host-', '^__secure-',
-     '^my_app_storage$'
-   ]
- }
+ essentialKeys: ['my_app_storage']

Install

npm install @freshjuice/zest
// Full build (banner + modal + widget, auto-init)
import Zest from '@freshjuice/zest';

// Headless (BYO UI, manual init)
import Zest from '@freshjuice/zest/headless';
Zest.init({
  essentialKeys: ['my_app_settings'],
  intercept: { scripts: false }
});

Or via CDN:

<script src="https://unpkg.com/@freshjuice/zest@2"></script>