Releases: wraithyy/attunement
Releases · wraithyy/attunement
Release list
v0.6.0
Minor Changes
- 043f2b9: New
safeUrlOrPathpredicate: refinement for config values that become
network targets — accepts same-origin paths and absolute http(s) URLs,
rejects protocol-relative URLs (//evil,/\evil), non-http schemes and
bare hostnames. Compose it via.refine(safeUrlOrPath, ...)with any
Standard Schema library. Plus hardening recipes: build-id cache-busting,
environment-dependent validation (buildSchema({ isProd })+ superRefine),
and a localized error fallback built fromConfigError.issues.
Patch Changes
- 5c1214c: Validation error messages drop the "you didn't say the magic word" easter egg
in production builds — custom errorFallbacks rendererror.messageto end
users, and a broken deploy is not a dev-only situation. Dev builds and the
CLI keep it. For localized error UIs, build fromConfigError.issues
instead oferror.message. Docs:OverrideRecoveryusage is shown
DEV-gated (ungated renders null in production but ships the devtools bytes).
v0.5.2
Patch Changes
-
610b35c: Override recovery moved where the knowledge lives (field report #3):
fromOverrides(storageKey?)now registers a recovery handler (internal
globalThis registry) that knows its own storage key and theconfig.*URL
bootstrap. "Clear overrides and reload" therefore works with a custom
storageKeyand also stripsconfig.*query params — a broken shared
repro link no longer reseeds the override on reload.- The react entry no longer carries any override knowledge (hardcoded key,
localStorage parsing, dead-in-production recovery UI paths) — the default
errorFallback just renders what registered handlers report, which in
production is nothing becausefromOverridesdoesn't run there. - New
OverrideRecoverycomponent inattunement/devtools: the same
recovery block for custom/branded errorFallbacks; renders nothing when no
overrides are stored.
v0.5.1
Patch Changes
-
7bcd7e5: Field report #2 fixes:
DevtoolsProps.config(andattunementDevtoolsPlugin) now accept the core
Attunedhandle instead ofAttunedReact. The React handle is invariant in
its config type (context), so the documentedReact.lazydevtools pattern
failed to typecheck with a typed schema — andbindReactusers had to
export a bound handle just for the panel. The panel only ever needed
load()and the schema; a React handle still works.- Default errorFallback detects active dev overrides
(localStorage["attunement:overrides"]), lists them and offers
"Clear overrides and reload" — a persisted invalid override otherwise turns
Retry into a loop with the devtools panel unreachable behind the error
boundary (the same failure modecache: "no-store"fixed for CDNs, second
persistence source). - README: the lazy devtools import now sits inside the
import.meta.env.DEV
gate — an ungatedlazy(() => import(...))still emits and deploys the
devtools chunk in production builds.
v0.5.0
Minor Changes
-
ee8ddab: API wave — field-report driven, panel-validated:
onReady(cb)on the attune handle: register render-critical wiring from
modules the config module must not import (router basepath, i18n) — no more
import cycles. One queue withonLoad(which is now just the first
callback), awaited in registration order beforeload()resolves; callback
errors reject the load asConfigError. Registered after resolve: runs
immediately with a DEV warning; after a failed load: never runs.bindReact(attuned): React binding over an existing core handle, so the
schema can live in a leaf module importable byattunement checkand tests.
attuneReact(options)is unchanged (it is nowbindReact(attune(options))).errorFallback(error, retry): function fallbacks get aretryargument
(currently a full page reload); the default fallback gains a Retry button.
The config boundary now rethrows errors that aren'tConfigError, so app
bugs reach your own error boundary instead of masquerading as config
failures. NewonErrorprop on Provider for Sentry-style reporting.optional(source)exported: failure → undefined → merge/chain falls
through (was a recipe).fromJsonnow defaultscache: "no-store"— a CDN-cached stale config
survives reloads and turns Retry into a loop. Override with
fromJson(url, { cache: "default" })if you serve config with proper
invalidation.- Devtools:
fields?: FieldInfo[]onDevtoolsProps/docsTablebypasses
zod introspection (escape hatch for Valibot/ArkType).
attunementDevtoolsPlugin(config, { storageKey, fields })— second argument
is now an options object (wasstorageKeystring). - CLI:
check --strictturns secret-hygiene warnings into failures;
check --print-fingerprintprints the same hash the running app reports via
fingerprint(). - React entries ship a
"use client"banner. @standard-schema/specmoved todependencies(types-only) — public d.ts
imports it; consumers without a transitive copy previously got TS2307.- Note for type-level consumers:
AttunedgainedonReadyand an internal
_schema— hand-built mocks of the handle need updating.
-
bcb1108: Config fingerprint:
attune()/attuneReact()handles exposefingerprint()
resolving to{ hash, version?, generatedAt? }— a stable FNV-1a hash of the
validated config (key-order independent) plus_version/_generatedAtread
off the raw config when the deploy pipeline stamps them.onLoadreceives the
fingerprint as a second argument for Sentry scope / log prefix wiring.
Patch Changes
-
b205980: First batch of field-report fixes:
- Schema introspection unwraps
z.preprocess— zod 3ZodEffects
(_def.schema) and zod 4 pipes (_def.out) — so the documented
all-strings recipe renders checkboxes/selects in the devtools panel
instead of text inputs. ProviderwithouterrorFallbacknow renders a minimal "Configuration
failed to load." notice instead of a white page; the error message (config
keys, source URLs) shows in dev builds only. PasserrorFallback={null}
for the old behavior.AttunementDevtoolsacceptsposition("bottom-right" default,
"bottom-left", "top-right", "top-left") to dodge TanStack Query devtools
and friends.attunement check --diffwarns when fewer than two files matched instead
of silently skipping the diff.- README: migration-from-hand-rolled-loader section,
onLoadordering
guarantee vs.then(), lazy devtools import as the default pattern,
injectKeyin the Vite snippet, zod-only introspection caveat moved to
the Standard Schema bullet.
- Schema introspection unwraps
v0.4.4
Patch Changes
- b6921dc: Devtools panel rows no longer stretch to the full TanStack Devtools shell
height. The shell applies> * > * { height: 100% }to plugin content, which
blew each field row up to ~400px; rows, buttons and the note now pin
height: autoinline.
v0.4.3
Patch Changes
-
69bdbd2: Fix devtools panel for Zod 4 schemas and overflowing content:
- Schema introspection (
introspectShape,docsTable, devtools form) now reads
both Zod 3 (_def.typeName, enumvalues, default thunk) and Zod 4
(_def.type, enumentries, plain default value) internals. With Zod 4,
booleans render as checkboxes and enums as populated selects again instead of
falling back to plain text inputs. - The panel scrolls its own overflow (
max-height+overflow-y: auto), so all
fields and the Save/Clear buttons stay reachable inside the TanStack Devtools
shell's fixed-height,overflow: hiddencontainer — and the standalone
floating widget caps at 70vh.
- Schema introspection (
v0.4.2
Patch Changes
- 8f0f965:
attunementDevtoolsPlugin's return type now declaresrender: ReactElementinstead ofReactNode—ReactNodeincludesundefined, which the TanStack Devtools shell'sTanStackDevtoolsReactPlugin.render(JSX.Element | (...) => JSX.Element) rejects. The panel always renders a real element, so the wider type was never earning its keep.
v0.4.1
Patch Changes
- 9361f18: Make
DevtoolsProps/AttunementDevtoolsPanel/AttunementDevtools/attunementDevtoolsPlugingeneric over the config shape, so a devtools consumer typed with a concrete schema (e.g.AttunedReact<RuntimeConfig, typeof schema>) doesn't need a cast to pass itsattuneReact()instance in.
v0.4.0
Minor Changes
- 9d74bec:
attunement/devtools: dev override panel generated from the schema (enum → select, boolean → checkbox) — standalone floating widget (AttunementDevtools) or TanStack Devtools plugin (attunementDevtoolsPlugin).fromOverrides()source layers localStorage overrides +?config.KEY=valueURL bootstrap over real config viamerge, validated by the schema like everything else. - cfaf690:
attunement/vite: Vite plugin — dev server serves the config file (full reload on change), optionalinjectKeyinjects config into index.html forfromWindow(real content in dev, deploy-replaceable placeholder in builds).
v0.3.0
Minor Changes
- e98bf43: v0.3 — CI story:
attunementCLI.check --schema <module> <files...>validates config files against the app schema (same did-you-mean errors as runtime),--difffails on top-level key drift between environments, secret hygiene warnings (credential-like names, high-entropy values),docsrenders a markdown table of keys/types/defaults from a zod schema. Newattunement/clientry +attunementbin.