@trackunit/react-components 2.6.5 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +32 -343
- package/index.esm.js +23 -336
- package/migrations/utils/jsx-utils.js +1 -1
- package/package.json +6 -7
- package/src/hooks/localStorage/useLocalStorage.d.ts +1 -1
- package/src/hooks/localStorage/useLocalStorageReducer.d.ts +1 -1
- package/src/hooks/localStorage/useSessionStorage.d.ts +1 -1
- package/src/hooks/localStorage/useSessionStorageReducer.d.ts +1 -1
- package/src/hooks/localStorage/useStorageSyncEffect.d.ts +1 -1
- package/src/hooks/localStorage/useWebStorage.d.ts +4 -2
- package/src/hooks/localStorage/useWebStorageReducer.d.ts +1 -1
- package/src/index.d.ts +1 -2
- package/migrations/entry.js.map +0 -1
- package/migrations/utils/jsx-utils.js.map +0 -1
- package/migrations/v2-0-0/breadcrumb-remove-deprecated-props.js.map +0 -1
- package/migrations/v2-0-0/cardheader-onclickclose-to-actions.js.map +0 -1
- package/migrations/v2-0-0/iconbutton-add-required-name.js.map +0 -1
- package/migrations/v2-0-0/kpi-tooltiplabel-to-wrapper.js.map +0 -1
- package/migrations/v2-0-0/notice-tooltiplabel-to-wrapper.js.map +0 -1
- package/migrations/v2-0-0/pagination-add-required-titles.js.map +0 -1
- package/migrations/v2-0-0/tag-add-removetaglabel.js.map +0 -1
- package/migrations/v2-0-0/togglegroup-remove-item-style.js.map +0 -1
- package/migrations/v3-0-0/menulist-rename-to-menucontent.js.map +0 -1
- package/src/hooks/localStorage/readFromStorage.d.ts +0 -39
- package/src/hooks/localStorage/runMigrations.d.ts +0 -13
- package/src/hooks/localStorage/salvageState.d.ts +0 -25
- package/src/hooks/localStorage/storageSerializer.d.ts +0 -4
- package/src/hooks/localStorage/storageVersionEnvelope.d.ts +0 -30
- package/src/hooks/localStorage/types.d.ts +0 -31
- package/src/hooks/localStorage/validateState.d.ts +0 -22
- package/src/hooks/localStorage/writeToStorage.d.ts +0 -11
package/index.cjs.js
CHANGED
|
@@ -22,8 +22,6 @@ var utils = require('@floating-ui/react/utils');
|
|
|
22
22
|
var reactHelmetAsync = require('react-helmet-async');
|
|
23
23
|
var reactTabs = require('@radix-ui/react-tabs');
|
|
24
24
|
var fflate = require('fflate');
|
|
25
|
-
var superjson = require('superjson');
|
|
26
|
-
var zod = require('zod');
|
|
27
25
|
var dequal = require('dequal');
|
|
28
26
|
|
|
29
27
|
var defaultTranslations = {
|
|
@@ -2363,7 +2361,7 @@ const Tag = ({ className, "data-testid": dataTestId, children, size = "medium",
|
|
|
2363
2361
|
color,
|
|
2364
2362
|
layout,
|
|
2365
2363
|
border: color === "white" ? "default" : "none",
|
|
2366
|
-
}), "data-testid": dataTestId, onMouseEnter: onMouseEnter, ref: ref, style: style, children: [icon !== null && icon !== undefined && size === "medium" ? (jsxRuntime.jsx("div", { className: cvaTagIconContainer(), "data-slot": "icon", children: icon })) : null, jsxRuntime.jsx("span", { className: cvaTagText({ truncate: shouldTruncate }), ref: textRef, children: children }), Boolean(onClickClose) && isSupportedDismissColor && size === "medium" && !disabled ? (jsxRuntime.jsx("div", { className: cvaTagIconContainer(), "data-slot": "dismiss", onClick: onClickClose, children: jsxRuntime.jsx(Tooltip, { disabled: disabled, label: removeTagLabel, placement: "bottom", children: jsxRuntime.jsx(Icon, { className: cvaTagIcon(), "data-testid": dataTestId
|
|
2364
|
+
}), "data-testid": dataTestId, onMouseEnter: onMouseEnter, ref: ref, style: style, children: [icon !== null && icon !== undefined && size === "medium" ? (jsxRuntime.jsx("div", { className: cvaTagIconContainer(), "data-slot": "icon", children: icon })) : null, jsxRuntime.jsx("span", { className: cvaTagText({ truncate: shouldTruncate }), ref: textRef, children: children }), Boolean(onClickClose) && isSupportedDismissColor && size === "medium" && !disabled ? (jsxRuntime.jsx("div", { className: cvaTagIconContainer(), "data-slot": "dismiss", onClick: onClickClose, children: jsxRuntime.jsx(Tooltip, { disabled: disabled, label: removeTagLabel, placement: "bottom", children: jsxRuntime.jsx(Icon, { className: cvaTagIcon(), "data-testid": `${dataTestId}Icon`, name: "XCircle", size: "small", style: { WebkitTransition: "-webkit-transform 0.150s" }, type: "solid" }) }) })) : null] }));
|
|
2367
2365
|
};
|
|
2368
2366
|
|
|
2369
2367
|
// Mirrors the slug logic in @trackunit/storybook-docs-components SectionName so anchors line up
|
|
@@ -4768,7 +4766,7 @@ function stylesToCss(styles, parentSelector, indent = "") {
|
|
|
4768
4766
|
}
|
|
4769
4767
|
else if (key.startsWith("@")) {
|
|
4770
4768
|
// At-rule (like @container): wrap in the at-rule
|
|
4771
|
-
const nestedCss = stylesToCss(value, parentSelector, indent
|
|
4769
|
+
const nestedCss = stylesToCss(value, parentSelector, `${indent} `);
|
|
4772
4770
|
lines.push(`${indent}${key} {\n${nestedCss}\n${indent}}`);
|
|
4773
4771
|
}
|
|
4774
4772
|
}
|
|
@@ -11865,334 +11863,6 @@ const useCustomEncoding = () => {
|
|
|
11865
11863
|
return react.useMemo(() => ({ encode, decode }), [encode, decode]);
|
|
11866
11864
|
};
|
|
11867
11865
|
|
|
11868
|
-
/**
|
|
11869
|
-
* Internal envelope used to tag superjson-serialized data in web storage.
|
|
11870
|
-
*
|
|
11871
|
-
* `__serializer` is a **reserved internal key** — consumer state objects must
|
|
11872
|
-
* not include it as a top-level key. The double-underscore prefix is a
|
|
11873
|
-
* deliberate signal that this is a private implementation detail.
|
|
11874
|
-
*
|
|
11875
|
-
* A runtime warning is emitted (via `writeToStorage`) if reserved keys are
|
|
11876
|
-
* detected in the value being written.
|
|
11877
|
-
*/
|
|
11878
|
-
const taggedSuperjsonEnvelopeSchema = zod.z.object({
|
|
11879
|
-
__serializer: zod.z.literal("superjson"),
|
|
11880
|
-
json: zod.z.custom(),
|
|
11881
|
-
meta: zod.z.custom().optional(),
|
|
11882
|
-
});
|
|
11883
|
-
const storageSerializer = {
|
|
11884
|
-
serialize: (value) => {
|
|
11885
|
-
const serialized = superjson.serialize(value);
|
|
11886
|
-
return JSON.stringify({ __serializer: "superjson", ...serialized });
|
|
11887
|
-
},
|
|
11888
|
-
deserialize: (value) => {
|
|
11889
|
-
const parsed = JSON.parse(value);
|
|
11890
|
-
const result = taggedSuperjsonEnvelopeSchema.safeParse(parsed);
|
|
11891
|
-
if (result.success) {
|
|
11892
|
-
return superjson.deserialize({ json: result.data.json, meta: result.data.meta });
|
|
11893
|
-
}
|
|
11894
|
-
return parsed;
|
|
11895
|
-
},
|
|
11896
|
-
};
|
|
11897
|
-
|
|
11898
|
-
/**
|
|
11899
|
-
* Runs a sequential migration pipeline on the provided data, applying
|
|
11900
|
-
* each migration whose version is in the range (fromVersion, toVersion].
|
|
11901
|
-
*
|
|
11902
|
-
* Throws if the filtered migrations have non-contiguous versions (gaps).
|
|
11903
|
-
*/
|
|
11904
|
-
const runMigrations = ({ data, fromVersion, toVersion, migrations, }) => {
|
|
11905
|
-
if (toVersion <= fromVersion) {
|
|
11906
|
-
return data;
|
|
11907
|
-
}
|
|
11908
|
-
const applicable = migrations
|
|
11909
|
-
.toSorted((a, b) => a.version - b.version)
|
|
11910
|
-
.filter(m => m.version > fromVersion && m.version <= toVersion);
|
|
11911
|
-
if (applicable.length === 0) {
|
|
11912
|
-
return data;
|
|
11913
|
-
}
|
|
11914
|
-
for (let i = 1; i < applicable.length; i++) {
|
|
11915
|
-
const prev = applicable[i - 1];
|
|
11916
|
-
const curr = applicable[i];
|
|
11917
|
-
if (prev && curr && curr.version !== prev.version + 1) {
|
|
11918
|
-
throw new Error(`Migration gap detected: found version ${prev.version} and ${curr.version} but no migration for version ${prev.version + 1}.`);
|
|
11919
|
-
}
|
|
11920
|
-
}
|
|
11921
|
-
return applicable.reduce((acc, migration) => migration.migrate(acc), data);
|
|
11922
|
-
};
|
|
11923
|
-
|
|
11924
|
-
/**
|
|
11925
|
-
* Recursively builds a salvaging schema from a ZodObject by injecting `.catch(defaultValue[key])`
|
|
11926
|
-
* for each field. This means invalid field values fall back to defaultState values rather than
|
|
11927
|
-
* failing the entire parse.
|
|
11928
|
-
*
|
|
11929
|
-
* Unwraps ZodOptional, ZodNullable, and ZodDefault to reach the inner schema.
|
|
11930
|
-
* ZodCatch is intentionally NOT unwrapped — consumer-defined catches take precedence.
|
|
11931
|
-
* Non-ZodObject schemas (unions, discriminated unions, refinements, etc.) are returned unchanged
|
|
11932
|
-
* so they behave as atomic units.
|
|
11933
|
-
*/
|
|
11934
|
-
function buildSalvagingSchema(schema, defaultValue) {
|
|
11935
|
-
if (schema instanceof zod.z.ZodOptional) {
|
|
11936
|
-
return buildSalvagingSchema(schema.unwrap(), defaultValue).optional();
|
|
11937
|
-
}
|
|
11938
|
-
if (schema instanceof zod.z.ZodNullable) {
|
|
11939
|
-
return buildSalvagingSchema(schema.unwrap(), defaultValue).nullable();
|
|
11940
|
-
}
|
|
11941
|
-
if (schema instanceof zod.z.ZodDefault) {
|
|
11942
|
-
return buildSalvagingSchema(schema._def.innerType, defaultValue);
|
|
11943
|
-
}
|
|
11944
|
-
if (!(schema instanceof zod.z.ZodObject)) {
|
|
11945
|
-
return schema;
|
|
11946
|
-
}
|
|
11947
|
-
const defaultRecordResult = zod.z.record(zod.z.string(), zod.z.unknown()).safeParse(defaultValue);
|
|
11948
|
-
if (!defaultRecordResult.success) {
|
|
11949
|
-
return schema;
|
|
11950
|
-
}
|
|
11951
|
-
const defaultRecord = defaultRecordResult.data;
|
|
11952
|
-
const salvagingShape = {};
|
|
11953
|
-
for (const key of Object.keys(schema.shape)) {
|
|
11954
|
-
const fieldSchema = schema.shape[key];
|
|
11955
|
-
if (fieldSchema === undefined)
|
|
11956
|
-
continue;
|
|
11957
|
-
const fieldDefault = defaultRecord[key];
|
|
11958
|
-
salvagingShape[key] = buildSalvagingSchema(fieldSchema, fieldDefault).catch(fieldDefault);
|
|
11959
|
-
}
|
|
11960
|
-
return zod.z.object(salvagingShape);
|
|
11961
|
-
}
|
|
11962
|
-
/**
|
|
11963
|
-
* Attempts to salvage partial state from raw data when full schema validation has failed.
|
|
11964
|
-
*
|
|
11965
|
-
* For ZodObject schemas, rebuilds the schema with per-field `.catch(defaultState[key])` fallbacks
|
|
11966
|
-
* and reparses. Valid field values from the stored data are kept; invalid fields fall back to their
|
|
11967
|
-
* corresponding values in `defaultState`. Works recursively for nested ZodObject fields.
|
|
11968
|
-
*
|
|
11969
|
-
* Non-object schemas (discriminated unions, unions with `.refine()`, etc.) cannot be partially
|
|
11970
|
-
* salvaged and return `null`.
|
|
11971
|
-
*
|
|
11972
|
-
* Consumer-defined `.catch()` wrappers take precedence over the injected salvage catches, since
|
|
11973
|
-
* Zod processes catches inside-out.
|
|
11974
|
-
*
|
|
11975
|
-
* Note: when every field is invalid, the salvaged result will be deeply equal to `defaultState`.
|
|
11976
|
-
* Callers should compare the result against `defaultState` to distinguish a genuine partial
|
|
11977
|
-
* salvage from a total loss where no stored data survived.
|
|
11978
|
-
*
|
|
11979
|
-
* @template TState - The type of the stored state.
|
|
11980
|
-
* @param schema - Zod schema for validation.
|
|
11981
|
-
* @param rawData - The raw deserialized value that failed validation.
|
|
11982
|
-
* @param defaultState - The authoritative fallback value; used as per-field catch defaults.
|
|
11983
|
-
* @returns {TState | null} The salvaged state, or `null` if salvaging is not possible or also fails.
|
|
11984
|
-
*/
|
|
11985
|
-
const salvageState = (schema, rawData, defaultState) => {
|
|
11986
|
-
try {
|
|
11987
|
-
const salvagingSchema = buildSalvagingSchema(schema, defaultState);
|
|
11988
|
-
const intermediate = salvagingSchema.safeParse(rawData);
|
|
11989
|
-
if (!intermediate.success) {
|
|
11990
|
-
return null;
|
|
11991
|
-
}
|
|
11992
|
-
// Re-validate through the original schema to ensure correctness and get the proper TState type.
|
|
11993
|
-
// By construction this should always succeed: each field value is either the valid stored value
|
|
11994
|
-
// or defaultState[key], both of which satisfy the original schema.
|
|
11995
|
-
const result = schema.safeParse(intermediate.data);
|
|
11996
|
-
return result.success ? result.data : null;
|
|
11997
|
-
}
|
|
11998
|
-
catch {
|
|
11999
|
-
return null;
|
|
12000
|
-
}
|
|
12001
|
-
};
|
|
12002
|
-
|
|
12003
|
-
/**
|
|
12004
|
-
* Internal envelope that pairs stored data with a schema version number.
|
|
12005
|
-
*
|
|
12006
|
-
* `__version` and `__data` are **reserved internal keys** — consumer state
|
|
12007
|
-
* objects must not include them as top-level keys. The double-underscore
|
|
12008
|
-
* prefix is a deliberate signal that these are private implementation details.
|
|
12009
|
-
*
|
|
12010
|
-
* A runtime warning is emitted (via `writeToStorage`) if reserved keys are
|
|
12011
|
-
* detected in the value being written.
|
|
12012
|
-
*/
|
|
12013
|
-
const storageVersionEnvelopeSchema = zod.z
|
|
12014
|
-
.object({
|
|
12015
|
-
__version: zod.z.number(),
|
|
12016
|
-
__data: zod.z.unknown(),
|
|
12017
|
-
})
|
|
12018
|
-
.refine((x) => "__data" in x, {
|
|
12019
|
-
message: "__data is required",
|
|
12020
|
-
});
|
|
12021
|
-
/** Wraps data and version into a versioned storage envelope. */
|
|
12022
|
-
const createStorageVersionEnvelope = (data, version) => ({
|
|
12023
|
-
__version: version,
|
|
12024
|
-
__data: data,
|
|
12025
|
-
});
|
|
12026
|
-
|
|
12027
|
-
/**
|
|
12028
|
-
* Validates raw data against the schema, attempting partial salvage on failure.
|
|
12029
|
-
*
|
|
12030
|
-
* On success: returns the parsed data.
|
|
12031
|
-
* On failure with a salvageable ZodObject schema where at least one stored field
|
|
12032
|
-
* survived: returns the salvaged state and calls `onValidationSalvaged`.
|
|
12033
|
-
* On failure where salvage produces a result identical to `defaultState` (i.e. every
|
|
12034
|
-
* field was invalid and caught to its default): treats this as a total failure and
|
|
12035
|
-
* calls `onValidationFailed` instead of `onValidationSalvaged`.
|
|
12036
|
-
* On failure with no salvage possible: returns `defaultState` and calls `onValidationFailed`.
|
|
12037
|
-
*/
|
|
12038
|
-
const validateOrSalvage = ({ rawValue, schema, defaultState, key, onValidationFailed, onValidationSalvaged, }) => {
|
|
12039
|
-
const parseResult = schema.safeParse(rawValue);
|
|
12040
|
-
if (parseResult.success) {
|
|
12041
|
-
return parseResult.data;
|
|
12042
|
-
}
|
|
12043
|
-
const salvaged = salvageState(schema, rawValue, defaultState);
|
|
12044
|
-
if (salvaged !== null && !esToolkit.isEqual(salvaged, defaultState)) {
|
|
12045
|
-
// eslint-disable-next-line no-console
|
|
12046
|
-
console.warn(`Partially invalid data in storage key "${key}" — salvaged valid fields, reset invalid fields to defaults.`, parseResult.error);
|
|
12047
|
-
onValidationSalvaged?.(parseResult.error, salvaged);
|
|
12048
|
-
return salvaged;
|
|
12049
|
-
}
|
|
12050
|
-
// eslint-disable-next-line no-console
|
|
12051
|
-
console.error(`Failed to parse and validate the state from storage key "${key}". Returning default state.`, parseResult.error);
|
|
12052
|
-
onValidationFailed?.(parseResult.error);
|
|
12053
|
-
return defaultState;
|
|
12054
|
-
};
|
|
12055
|
-
/**
|
|
12056
|
-
* Reads and deserializes a value from web storage, validating it against the provided schema.
|
|
12057
|
-
* Falls back to defaultState if the key is missing or data is corrupt.
|
|
12058
|
-
*
|
|
12059
|
-
* On partial validation failure for ZodObject schemas, salvages valid fields rather than
|
|
12060
|
-
* resetting the entire state to `defaultState`. See `salvageState` for salvage mechanics.
|
|
12061
|
-
* If the salvaged result is deeply equal to `defaultState` (i.e. every field was invalid),
|
|
12062
|
-
* the salvage is considered a total loss and `onValidationFailed` is called instead of
|
|
12063
|
-
* `onValidationSalvaged`.
|
|
12064
|
-
*
|
|
12065
|
-
* Deserialization failures (unparseable/corrupt raw data) are routed through
|
|
12066
|
-
* `onValidationFailed` so consumers are always notified when stored data is unusable.
|
|
12067
|
-
*
|
|
12068
|
-
* When `migration` is provided, detects versioned envelopes, runs the
|
|
12069
|
-
* migration pipeline, and validates the migrated result. Non-versioned consumers are
|
|
12070
|
-
* unaffected — the migration path is fully opt-in.
|
|
12071
|
-
*
|
|
12072
|
-
* @template TState - The type of the stored state.
|
|
12073
|
-
* @param params - Storage, key, default state, Zod schema, optional migration config, and optional callbacks.
|
|
12074
|
-
* @param params.storage - The web Storage instance.
|
|
12075
|
-
* @param params.key - The storage key.
|
|
12076
|
-
* @param params.defaultState - Fallback value when no stored data exists or data is corrupt.
|
|
12077
|
-
* @param params.schema - Zod schema to validate the deserialized data.
|
|
12078
|
-
* @param params.migration - Optional migration configuration (version, steps, fromKey).
|
|
12079
|
-
* @param params.onValidationFailed - Called when validation fails and no salvage is possible, or when
|
|
12080
|
-
* deserialization of the raw storage value fails entirely.
|
|
12081
|
-
* @param params.onValidationSalvaged - Called when validation fails but at least one stored field
|
|
12082
|
-
* was recovered (salvaged result differs from defaultState).
|
|
12083
|
-
* @returns {TState} The validated or salvaged state, or defaultState.
|
|
12084
|
-
*/
|
|
12085
|
-
const readFromStorage = ({ storage, key, defaultState, schema, migration, onValidationFailed, onValidationSalvaged, }) => {
|
|
12086
|
-
const version = migration?.version;
|
|
12087
|
-
const steps = migration?.steps;
|
|
12088
|
-
const fromKey = migration?.fromKey;
|
|
12089
|
-
let raw = storage.getItem(key);
|
|
12090
|
-
if (raw === null && fromKey !== undefined) {
|
|
12091
|
-
raw = storage.getItem(fromKey);
|
|
12092
|
-
}
|
|
12093
|
-
if (raw === null) {
|
|
12094
|
-
return defaultState;
|
|
12095
|
-
}
|
|
12096
|
-
try {
|
|
12097
|
-
const deserialized = storageSerializer.deserialize(raw);
|
|
12098
|
-
if (version === undefined) {
|
|
12099
|
-
return validateOrSalvage({
|
|
12100
|
-
rawValue: deserialized,
|
|
12101
|
-
schema,
|
|
12102
|
-
defaultState,
|
|
12103
|
-
key,
|
|
12104
|
-
onValidationFailed,
|
|
12105
|
-
onValidationSalvaged,
|
|
12106
|
-
});
|
|
12107
|
-
}
|
|
12108
|
-
let storedVersion;
|
|
12109
|
-
let data;
|
|
12110
|
-
const envelopeResult = storageVersionEnvelopeSchema.safeParse(deserialized);
|
|
12111
|
-
if (envelopeResult.success) {
|
|
12112
|
-
storedVersion = envelopeResult.data.__version;
|
|
12113
|
-
data = envelopeResult.data.__data;
|
|
12114
|
-
}
|
|
12115
|
-
else {
|
|
12116
|
-
storedVersion = 0;
|
|
12117
|
-
data = deserialized;
|
|
12118
|
-
}
|
|
12119
|
-
if (steps !== undefined && storedVersion < version) {
|
|
12120
|
-
try {
|
|
12121
|
-
data = runMigrations({ data, fromVersion: storedVersion, toVersion: version, migrations: steps });
|
|
12122
|
-
}
|
|
12123
|
-
catch (migrationError) {
|
|
12124
|
-
// eslint-disable-next-line no-console
|
|
12125
|
-
console.error(`Migration failed for storage key "${key}" (v${storedVersion} → v${version}). Returning default state.`, migrationError);
|
|
12126
|
-
return defaultState;
|
|
12127
|
-
}
|
|
12128
|
-
}
|
|
12129
|
-
return validateOrSalvage({ rawValue: data, schema, defaultState, key, onValidationFailed, onValidationSalvaged });
|
|
12130
|
-
}
|
|
12131
|
-
catch (deserializationError) {
|
|
12132
|
-
// eslint-disable-next-line no-console
|
|
12133
|
-
console.error(`Failed to deserialize storage key "${key}". Returning default state.`, deserializationError);
|
|
12134
|
-
onValidationFailed?.(deserializationError);
|
|
12135
|
-
return defaultState;
|
|
12136
|
-
}
|
|
12137
|
-
};
|
|
12138
|
-
|
|
12139
|
-
/**
|
|
12140
|
-
* Validates the state using a Zod schema. Returns the parsed state on success,
|
|
12141
|
-
* or defaultState on failure (with error logging and optional callbacks).
|
|
12142
|
-
*
|
|
12143
|
-
* @template TState - The type of the state.
|
|
12144
|
-
* @param params - The state, schema, defaultState, and optional callbacks.
|
|
12145
|
-
* @param params.state - The raw state to validate.
|
|
12146
|
-
* @param params.schema - The Zod schema for validation.
|
|
12147
|
-
* @param params.defaultState - The fallback value on failure.
|
|
12148
|
-
* @param params.onValidationFailed - Optional error callback.
|
|
12149
|
-
* @param params.onValidationSuccessful - Optional success callback.
|
|
12150
|
-
* @returns {TState} The validated state or defaultState.
|
|
12151
|
-
*/
|
|
12152
|
-
const validateState = ({ state, schema, onValidationFailed, onValidationSuccessful, defaultState, }) => {
|
|
12153
|
-
const result = schema.safeParse(state);
|
|
12154
|
-
if (result.success) {
|
|
12155
|
-
onValidationSuccessful?.(result.data);
|
|
12156
|
-
return result.data;
|
|
12157
|
-
}
|
|
12158
|
-
// eslint-disable-next-line no-console
|
|
12159
|
-
console.error("Failed to parse and validate the state from storage.", result.error);
|
|
12160
|
-
onValidationFailed?.(result.error);
|
|
12161
|
-
return defaultState;
|
|
12162
|
-
};
|
|
12163
|
-
|
|
12164
|
-
/**
|
|
12165
|
-
* Keys reserved for internal storage envelope metadata.
|
|
12166
|
-
* Consumer state objects must not use these as top-level keys.
|
|
12167
|
-
*/
|
|
12168
|
-
const RESERVED_STORAGE_KEYS = ["__data", "__version", "__serializer"];
|
|
12169
|
-
const warnIfReservedKeys = (value) => {
|
|
12170
|
-
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
12171
|
-
return;
|
|
12172
|
-
for (const key of RESERVED_STORAGE_KEYS) {
|
|
12173
|
-
if (key in value) {
|
|
12174
|
-
// eslint-disable-next-line no-console
|
|
12175
|
-
console.warn(`[useWebStorage] "${key}" is a reserved internal storage key and must not be used in state objects. ` +
|
|
12176
|
-
`It will conflict with the storage serialization envelope.`);
|
|
12177
|
-
}
|
|
12178
|
-
}
|
|
12179
|
-
};
|
|
12180
|
-
/**
|
|
12181
|
-
* Serializes and writes a value to web storage.
|
|
12182
|
-
* When a version is provided, wraps the data in a versioned envelope
|
|
12183
|
-
* before serializing so the migration pipeline can detect it on read.
|
|
12184
|
-
*
|
|
12185
|
-
* @param storage - The Storage instance (localStorage / sessionStorage).
|
|
12186
|
-
* @param key - The storage key.
|
|
12187
|
-
* @param value - The value to serialize and store.
|
|
12188
|
-
* @param version - Optional schema version to stamp onto the stored payload.
|
|
12189
|
-
*/
|
|
12190
|
-
const writeToStorage = (storage, key, value, version) => {
|
|
12191
|
-
warnIfReservedKeys(value);
|
|
12192
|
-
const payload = version === undefined ? value : createStorageVersionEnvelope(value, version);
|
|
12193
|
-
storage.setItem(key, storageSerializer.serialize(payload));
|
|
12194
|
-
};
|
|
12195
|
-
|
|
12196
11866
|
/**
|
|
12197
11867
|
* Effect that synchronizes React state to web storage on every state change,
|
|
12198
11868
|
* validating through the schema before writing.
|
|
@@ -12220,7 +11890,7 @@ const useStorageSyncEffect = ({ storage, key, state, defaultState, schema, versi
|
|
|
12220
11890
|
return;
|
|
12221
11891
|
}
|
|
12222
11892
|
const { schema: currentSchema, defaultState: currentDefault, onValidationFailed: currentOnFailed, onValidationSuccessful: currentOnSuccess, version: currentVersion, } = optionsRef.current;
|
|
12223
|
-
validateState({
|
|
11893
|
+
sharedUtils.validateState({
|
|
12224
11894
|
state,
|
|
12225
11895
|
schema: currentSchema,
|
|
12226
11896
|
defaultState: currentDefault,
|
|
@@ -12231,7 +11901,7 @@ const useStorageSyncEffect = ({ storage, key, state, defaultState, schema, versi
|
|
|
12231
11901
|
currentOnFailed?.(error);
|
|
12232
11902
|
},
|
|
12233
11903
|
onValidationSuccessful: data => {
|
|
12234
|
-
writeToStorage(storage, key, data, currentVersion);
|
|
11904
|
+
sharedUtils.writeToStorage(storage, key, data, currentVersion);
|
|
12235
11905
|
currentOnSuccess?.(data);
|
|
12236
11906
|
},
|
|
12237
11907
|
});
|
|
@@ -12248,20 +11918,32 @@ const useStorageSyncEffect = ({ storage, key, state, defaultState, schema, versi
|
|
|
12248
11918
|
* @param options.defaultState - Fallback value when no stored data exists.
|
|
12249
11919
|
* @param options.schema - Zod schema for validation.
|
|
12250
11920
|
* @param options.migration - Optional migration configuration (version, steps, fromKey).
|
|
11921
|
+
* @param options.legacyRawString - Narrow, opt-in recovery for keys known to hold a plain-text
|
|
11922
|
+
* value that was never JSON-encoded. See `WebStorageOptions.legacyRawString`.
|
|
12251
11923
|
* @param options.onValidationFailed - Called when validation fails and no salvage is possible.
|
|
12252
11924
|
* @param options.onValidationSuccessful - Called when validation succeeds during the write-sync.
|
|
12253
11925
|
* @param options.onValidationSalvaged - Called when validation fails but partial state was recovered.
|
|
12254
11926
|
* @returns {Array} A tuple of [state, setState, reset].
|
|
12255
11927
|
*/
|
|
12256
|
-
const useWebStorage = (storage, { key, defaultState, schema, migration, onValidationFailed, onValidationSuccessful, onValidationSalvaged, }) => {
|
|
11928
|
+
const useWebStorage = (storage, { key, defaultState, schema, migration, legacyRawString, onValidationFailed, onValidationSuccessful, onValidationSalvaged, }) => {
|
|
12257
11929
|
if (!key) {
|
|
12258
11930
|
throw new Error("useWebStorage: key must be a non-empty string");
|
|
12259
11931
|
}
|
|
12260
|
-
const [state, setState] = react.useState(() => readFromStorage({
|
|
11932
|
+
const [state, setState] = react.useState(() => sharedUtils.readFromStorage({
|
|
11933
|
+
storage,
|
|
11934
|
+
key,
|
|
11935
|
+
defaultState,
|
|
11936
|
+
schema,
|
|
11937
|
+
migration,
|
|
11938
|
+
legacyRawString,
|
|
11939
|
+
onValidationFailed,
|
|
11940
|
+
onValidationSalvaged,
|
|
11941
|
+
}));
|
|
12261
11942
|
const prevKeyRef = react.useRef(key);
|
|
12262
11943
|
const defaultStateRef = react.useRef(defaultState);
|
|
12263
11944
|
const schemaRef = react.useRef(schema);
|
|
12264
11945
|
const migrationRef = react.useRef(migration);
|
|
11946
|
+
const legacyRawStringRef = react.useRef(legacyRawString);
|
|
12265
11947
|
const onValidationFailedRef = react.useRef(onValidationFailed);
|
|
12266
11948
|
const onValidationSalvagedRef = react.useRef(onValidationSalvaged);
|
|
12267
11949
|
react.useEffect(() => {
|
|
@@ -12273,6 +11955,9 @@ const useWebStorage = (storage, { key, defaultState, schema, migration, onValida
|
|
|
12273
11955
|
react.useEffect(() => {
|
|
12274
11956
|
migrationRef.current = migration;
|
|
12275
11957
|
}, [migration]);
|
|
11958
|
+
react.useEffect(() => {
|
|
11959
|
+
legacyRawStringRef.current = legacyRawString;
|
|
11960
|
+
}, [legacyRawString]);
|
|
12276
11961
|
react.useEffect(() => {
|
|
12277
11962
|
onValidationFailedRef.current = onValidationFailed;
|
|
12278
11963
|
}, [onValidationFailed]);
|
|
@@ -12284,12 +11969,13 @@ const useWebStorage = (storage, { key, defaultState, schema, migration, onValida
|
|
|
12284
11969
|
if (key !== prevKeyRef.current) {
|
|
12285
11970
|
prevKeyRef.current = key;
|
|
12286
11971
|
keyChangedRef.current = true;
|
|
12287
|
-
setState(readFromStorage({
|
|
11972
|
+
setState(sharedUtils.readFromStorage({
|
|
12288
11973
|
storage,
|
|
12289
11974
|
key,
|
|
12290
11975
|
defaultState: defaultStateRef.current,
|
|
12291
11976
|
schema: schemaRef.current,
|
|
12292
11977
|
migration: migrationRef.current,
|
|
11978
|
+
legacyRawString: legacyRawStringRef.current,
|
|
12293
11979
|
onValidationFailed: onValidationFailedRef.current,
|
|
12294
11980
|
onValidationSalvaged: onValidationSalvagedRef.current,
|
|
12295
11981
|
}));
|
|
@@ -12383,7 +12069,7 @@ const useWebStorageReducer = (storage, { key, defaultState, schema, migration, r
|
|
|
12383
12069
|
return internal;
|
|
12384
12070
|
}
|
|
12385
12071
|
};
|
|
12386
|
-
const [state, internalDispatch] = react.useReducer(internalReducer, undefined, () => readFromStorage({ storage, key, defaultState, schema, migration, onValidationFailed, onValidationSalvaged }));
|
|
12072
|
+
const [state, internalDispatch] = react.useReducer(internalReducer, undefined, () => sharedUtils.readFromStorage({ storage, key, defaultState, schema, migration, onValidationFailed, onValidationSalvaged }));
|
|
12387
12073
|
const userDispatch = react.useCallback((action) => internalDispatch({ kind: "user", action }), []);
|
|
12388
12074
|
const prevKeyRef = react.useRef(key);
|
|
12389
12075
|
const defaultStateRef = react.useRef(defaultState);
|
|
@@ -12413,7 +12099,7 @@ const useWebStorageReducer = (storage, { key, defaultState, schema, migration, r
|
|
|
12413
12099
|
keyChangedRef.current = true;
|
|
12414
12100
|
internalDispatch({
|
|
12415
12101
|
kind: "reinit",
|
|
12416
|
-
state: readFromStorage({
|
|
12102
|
+
state: sharedUtils.readFromStorage({
|
|
12417
12103
|
storage,
|
|
12418
12104
|
key,
|
|
12419
12105
|
defaultState: defaultStateRef.current,
|
|
@@ -13224,7 +12910,7 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
|
|
|
13224
12910
|
try {
|
|
13225
12911
|
const raw = rawLocalStorage.read(storageKey);
|
|
13226
12912
|
if (raw) {
|
|
13227
|
-
const parsed = storageSerializer.deserialize(raw);
|
|
12913
|
+
const parsed = sharedUtils.storageSerializer.deserialize(raw);
|
|
13228
12914
|
storageState = validateState(parsed);
|
|
13229
12915
|
}
|
|
13230
12916
|
}
|
|
@@ -13356,7 +13042,7 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
|
|
|
13356
13042
|
if (localStorageEnabled) {
|
|
13357
13043
|
const serialized = serializeRef.current
|
|
13358
13044
|
? serializeRef.current(migrated)
|
|
13359
|
-
: storageSerializer.serialize(migrated);
|
|
13045
|
+
: sharedUtils.storageSerializer.serialize(migrated);
|
|
13360
13046
|
rawLocalStorage.write(storageKey, serialized);
|
|
13361
13047
|
}
|
|
13362
13048
|
const urlValue = toUrlValueRef.current ? toUrlValueRef.current(migrated) : migrated;
|
|
@@ -13370,7 +13056,7 @@ const usePersistedState = ({ key, validate, serialize, toUrlValue, fromUrlValue,
|
|
|
13370
13056
|
}
|
|
13371
13057
|
lastPersistedRef.current = state;
|
|
13372
13058
|
if (localStorageEnabled) {
|
|
13373
|
-
const serialized = serializeRef.current ? serializeRef.current(state) : storageSerializer.serialize(state);
|
|
13059
|
+
const serialized = serializeRef.current ? serializeRef.current(state) : sharedUtils.storageSerializer.serialize(state);
|
|
13374
13060
|
rawLocalStorage.write(storageKey, serialized);
|
|
13375
13061
|
}
|
|
13376
13062
|
const urlValue = toUrlValueRef.current ? toUrlValueRef.current(state) : state;
|
|
@@ -14586,6 +14272,10 @@ const useWindowActivity = ({ onFocus, onBlur, skip = false } = { onBlur: undefin
|
|
|
14586
14272
|
*/
|
|
14587
14273
|
setupLibraryTranslations();
|
|
14588
14274
|
|
|
14275
|
+
Object.defineProperty(exports, "storageSerializer", {
|
|
14276
|
+
enumerable: true,
|
|
14277
|
+
get: function () { return sharedUtils.storageSerializer; }
|
|
14278
|
+
});
|
|
14589
14279
|
exports.Alert = Alert;
|
|
14590
14280
|
exports.Badge = Badge;
|
|
14591
14281
|
exports.Breadcrumb = Breadcrumb;
|
|
@@ -14721,7 +14411,6 @@ exports.iconColorNames = iconColorNames;
|
|
|
14721
14411
|
exports.iconPalette = iconPalette;
|
|
14722
14412
|
exports.noPagination = noPagination;
|
|
14723
14413
|
exports.preferenceCardGrid = preferenceCardGrid;
|
|
14724
|
-
exports.storageSerializer = storageSerializer;
|
|
14725
14414
|
exports.useBidirectionalScroll = useBidirectionalScroll;
|
|
14726
14415
|
exports.useClickOutside = useClickOutside;
|
|
14727
14416
|
exports.useContainerBreakpoints = useContainerBreakpoints;
|