@trackunit/react-core-hooks 1.14.4 → 1.14.5-alpha-58b6b568fca.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 CHANGED
@@ -4,6 +4,7 @@ var reactCoreContextsApi = require('@trackunit/react-core-contexts-api');
4
4
  var react = require('react');
5
5
  var esToolkit = require('es-toolkit');
6
6
  var irisAppRuntimeCore = require('@trackunit/iris-app-runtime-core');
7
+ var zod = require('zod');
7
8
 
8
9
  /**
9
10
  * Hook to get the analytics context.
@@ -1056,6 +1057,128 @@ const useUserPermission = (requiredPermission) => {
1056
1057
  }, [permissions, requiredPermission]);
1057
1058
  };
1058
1059
 
1060
+ /**
1061
+ * Zod pipeline that parses a non-empty JSON string into an unknown value.
1062
+ * Handles double-encoded JSON by attempting a second parse when the first
1063
+ * result is still a string. Returns a Zod issue (rather than throwing)
1064
+ * when the input is not valid JSON.
1065
+ */
1066
+ const jsonParsePipeline = zod.z
1067
+ .string()
1068
+ .min(1)
1069
+ .transform((str, ctx) => {
1070
+ let parsed;
1071
+ try {
1072
+ parsed = JSON.parse(str);
1073
+ }
1074
+ catch {
1075
+ ctx.addIssue({
1076
+ code: zod.z.ZodIssueCode.custom,
1077
+ message: "Invalid JSON string",
1078
+ });
1079
+ return zod.z.NEVER;
1080
+ }
1081
+ if (typeof parsed === "string") {
1082
+ try {
1083
+ parsed = JSON.parse(parsed);
1084
+ }
1085
+ catch {
1086
+ // Not double-encoded, keep the first-level parse
1087
+ }
1088
+ }
1089
+ return parsed;
1090
+ });
1091
+ /**
1092
+ * Safely parses a JSON string and validates the result against a Zod schema.
1093
+ * Returns the validated value when parsing and validation succeed, otherwise `null`.
1094
+ * The return type is inferred from the schema — no type assertions needed.
1095
+ *
1096
+ * @param jsonString - The JSON string to parse, or `null`.
1097
+ * @param schema - A Zod schema that describes the expected shape of the parsed value.
1098
+ * @example
1099
+ * ```ts
1100
+ * const result = safeParse('{"name":"Ada"}', z.object({ name: z.string() }));
1101
+ * // result: { name: "Ada" }
1102
+ *
1103
+ * const num = safeParse("42", z.number());
1104
+ * // num: 42
1105
+ *
1106
+ * const miss = safeParse('{"a":1}', z.object({ b: z.string() }));
1107
+ * // miss: null
1108
+ * ```
1109
+ */
1110
+ const safeParse = (jsonString, schema) => {
1111
+ if (!jsonString) {
1112
+ return null;
1113
+ }
1114
+ const jsonResult = jsonParsePipeline.pipe(schema).safeParse(jsonString);
1115
+ if (jsonResult.success) {
1116
+ return jsonResult.data;
1117
+ }
1118
+ // Not valid JSON — try validating the raw string directly
1119
+ const rawResult = schema.safeParse(jsonString);
1120
+ return rawResult.success ? rawResult.data : null;
1121
+ };
1122
+ /**
1123
+ * Zod pipeline that validates a non-null value and converts it to a JSON string.
1124
+ * Strings are returned as-is; all other values go through `JSON.stringify`.
1125
+ * Non-serialisable values (e.g. circular references) produce a Zod issue
1126
+ * instead of throwing.
1127
+ */
1128
+ const stringifySchema = zod.z
1129
+ .unknown()
1130
+ .refine((val) => val !== null && val !== undefined)
1131
+ .transform((val, ctx) => {
1132
+ if (typeof val === "string") {
1133
+ return val;
1134
+ }
1135
+ try {
1136
+ return JSON.stringify(val);
1137
+ }
1138
+ catch {
1139
+ ctx.addIssue({
1140
+ code: zod.z.ZodIssueCode.custom,
1141
+ message: "Value is not serializable to JSON",
1142
+ });
1143
+ return zod.z.NEVER;
1144
+ }
1145
+ })
1146
+ .pipe(zod.z.string());
1147
+ /**
1148
+ * Checks whether a string is valid, non-null JSON.
1149
+ *
1150
+ * Reuses the internal {@link jsonParsePipeline} so that the parsing logic
1151
+ * stays in one place. Returns `false` for the JSON literal `"null"` to
1152
+ * match the legacy behaviour where `JSON.parse(str) !== null` was required.
1153
+ *
1154
+ * @param jsonString - The string to test.
1155
+ * @returns {boolean} `true` when the string can be parsed as non-null JSON.
1156
+ */
1157
+ const isJSON = (jsonString) => {
1158
+ const result = jsonParsePipeline.safeParse(jsonString);
1159
+ return result.success && result.data !== null;
1160
+ };
1161
+ /**
1162
+ * Safely converts a value to a JSON string using a Zod validation pipeline.
1163
+ *
1164
+ * - Returns the value unchanged if it is already a string.
1165
+ * - Returns `null` for `null` / `undefined` or if serialisation fails
1166
+ * (e.g. circular references).
1167
+ *
1168
+ * @param value - The value to stringify.
1169
+ * @returns {string | null} The JSON string representation, or `null` on failure.
1170
+ * @example
1171
+ * ```ts
1172
+ * safeStringify({ a: 1 }) // '{"a":1}'
1173
+ * safeStringify("hello") // "hello"
1174
+ * safeStringify(null) // null
1175
+ * ```
1176
+ */
1177
+ const safeStringify = (value) => {
1178
+ const result = stringifySchema.safeParse(value);
1179
+ return result.success ? result.data : null;
1180
+ };
1181
+
1059
1182
  /**
1060
1183
  * This is a hook to use the WidgetConfigProvider.
1061
1184
  *
@@ -1183,6 +1306,9 @@ const useWidgetConfig = () => {
1183
1306
  };
1184
1307
 
1185
1308
  exports.fetchAssetBlobUrl = fetchAssetBlobUrl;
1309
+ exports.isJSON = isJSON;
1310
+ exports.safeParse = safeParse;
1311
+ exports.safeStringify = safeStringify;
1186
1312
  exports.useAnalytics = useAnalytics;
1187
1313
  exports.useAssetRuntime = useAssetRuntime;
1188
1314
  exports.useAssetSorting = useAssetSorting;
package/index.esm.js CHANGED
@@ -2,6 +2,7 @@ import { AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, Envir
2
2
  import { useContext, useMemo, useState, useCallback, useEffect, useReducer, useRef } from 'react';
3
3
  import { isEqual } from 'es-toolkit';
4
4
  import { AssetRuntime, CustomerRuntime, EventRuntime, ParamsRuntime, SiteRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
5
+ import { z } from 'zod';
5
6
 
6
7
  /**
7
8
  * Hook to get the analytics context.
@@ -1054,6 +1055,128 @@ const useUserPermission = (requiredPermission) => {
1054
1055
  }, [permissions, requiredPermission]);
1055
1056
  };
1056
1057
 
1058
+ /**
1059
+ * Zod pipeline that parses a non-empty JSON string into an unknown value.
1060
+ * Handles double-encoded JSON by attempting a second parse when the first
1061
+ * result is still a string. Returns a Zod issue (rather than throwing)
1062
+ * when the input is not valid JSON.
1063
+ */
1064
+ const jsonParsePipeline = z
1065
+ .string()
1066
+ .min(1)
1067
+ .transform((str, ctx) => {
1068
+ let parsed;
1069
+ try {
1070
+ parsed = JSON.parse(str);
1071
+ }
1072
+ catch {
1073
+ ctx.addIssue({
1074
+ code: z.ZodIssueCode.custom,
1075
+ message: "Invalid JSON string",
1076
+ });
1077
+ return z.NEVER;
1078
+ }
1079
+ if (typeof parsed === "string") {
1080
+ try {
1081
+ parsed = JSON.parse(parsed);
1082
+ }
1083
+ catch {
1084
+ // Not double-encoded, keep the first-level parse
1085
+ }
1086
+ }
1087
+ return parsed;
1088
+ });
1089
+ /**
1090
+ * Safely parses a JSON string and validates the result against a Zod schema.
1091
+ * Returns the validated value when parsing and validation succeed, otherwise `null`.
1092
+ * The return type is inferred from the schema — no type assertions needed.
1093
+ *
1094
+ * @param jsonString - The JSON string to parse, or `null`.
1095
+ * @param schema - A Zod schema that describes the expected shape of the parsed value.
1096
+ * @example
1097
+ * ```ts
1098
+ * const result = safeParse('{"name":"Ada"}', z.object({ name: z.string() }));
1099
+ * // result: { name: "Ada" }
1100
+ *
1101
+ * const num = safeParse("42", z.number());
1102
+ * // num: 42
1103
+ *
1104
+ * const miss = safeParse('{"a":1}', z.object({ b: z.string() }));
1105
+ * // miss: null
1106
+ * ```
1107
+ */
1108
+ const safeParse = (jsonString, schema) => {
1109
+ if (!jsonString) {
1110
+ return null;
1111
+ }
1112
+ const jsonResult = jsonParsePipeline.pipe(schema).safeParse(jsonString);
1113
+ if (jsonResult.success) {
1114
+ return jsonResult.data;
1115
+ }
1116
+ // Not valid JSON — try validating the raw string directly
1117
+ const rawResult = schema.safeParse(jsonString);
1118
+ return rawResult.success ? rawResult.data : null;
1119
+ };
1120
+ /**
1121
+ * Zod pipeline that validates a non-null value and converts it to a JSON string.
1122
+ * Strings are returned as-is; all other values go through `JSON.stringify`.
1123
+ * Non-serialisable values (e.g. circular references) produce a Zod issue
1124
+ * instead of throwing.
1125
+ */
1126
+ const stringifySchema = z
1127
+ .unknown()
1128
+ .refine((val) => val !== null && val !== undefined)
1129
+ .transform((val, ctx) => {
1130
+ if (typeof val === "string") {
1131
+ return val;
1132
+ }
1133
+ try {
1134
+ return JSON.stringify(val);
1135
+ }
1136
+ catch {
1137
+ ctx.addIssue({
1138
+ code: z.ZodIssueCode.custom,
1139
+ message: "Value is not serializable to JSON",
1140
+ });
1141
+ return z.NEVER;
1142
+ }
1143
+ })
1144
+ .pipe(z.string());
1145
+ /**
1146
+ * Checks whether a string is valid, non-null JSON.
1147
+ *
1148
+ * Reuses the internal {@link jsonParsePipeline} so that the parsing logic
1149
+ * stays in one place. Returns `false` for the JSON literal `"null"` to
1150
+ * match the legacy behaviour where `JSON.parse(str) !== null` was required.
1151
+ *
1152
+ * @param jsonString - The string to test.
1153
+ * @returns {boolean} `true` when the string can be parsed as non-null JSON.
1154
+ */
1155
+ const isJSON = (jsonString) => {
1156
+ const result = jsonParsePipeline.safeParse(jsonString);
1157
+ return result.success && result.data !== null;
1158
+ };
1159
+ /**
1160
+ * Safely converts a value to a JSON string using a Zod validation pipeline.
1161
+ *
1162
+ * - Returns the value unchanged if it is already a string.
1163
+ * - Returns `null` for `null` / `undefined` or if serialisation fails
1164
+ * (e.g. circular references).
1165
+ *
1166
+ * @param value - The value to stringify.
1167
+ * @returns {string | null} The JSON string representation, or `null` on failure.
1168
+ * @example
1169
+ * ```ts
1170
+ * safeStringify({ a: 1 }) // '{"a":1}'
1171
+ * safeStringify("hello") // "hello"
1172
+ * safeStringify(null) // null
1173
+ * ```
1174
+ */
1175
+ const safeStringify = (value) => {
1176
+ const result = stringifySchema.safeParse(value);
1177
+ return result.success ? result.data : null;
1178
+ };
1179
+
1057
1180
  /**
1058
1181
  * This is a hook to use the WidgetConfigProvider.
1059
1182
  *
@@ -1180,4 +1303,4 @@ const useWidgetConfig = () => {
1180
1303
  return result;
1181
1304
  };
1182
1305
 
1183
- export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
1306
+ export { fetchAssetBlobUrl, isJSON, safeParse, safeStringify, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-core-hooks",
3
- "version": "1.14.4",
3
+ "version": "1.14.5-alpha-58b6b568fca.0",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -8,9 +8,11 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "es-toolkit": "^1.39.10",
11
- "@trackunit/iris-app-runtime-core": "1.14.3",
12
- "@trackunit/iris-app-runtime-core-api": "1.13.3",
13
- "@trackunit/react-core-contexts-api": "1.14.3"
11
+ "@trackunit/iris-app-runtime-core": "1.14.4-alpha-58b6b568fca.0",
12
+ "@trackunit/iris-app-runtime-core-api": "1.13.4-alpha-58b6b568fca.0",
13
+ "@trackunit/react-core-contexts-api": "1.14.4-alpha-58b6b568fca.0",
14
+ "@trackunit/react-core-contexts-test": "1.14.5-alpha-58b6b568fca.0",
15
+ "zod": "^3.23.8"
14
16
  },
15
17
  "peerDependencies": {
16
18
  "react": "^19.0.0"
package/src/index.d.ts CHANGED
@@ -27,4 +27,5 @@ export * from "./useFeatureBranchQueryString";
27
27
  export * from "./user/useCurrentUser";
28
28
  export * from "./user/useCurrentUserPreference";
29
29
  export * from "./user/useUserPermission";
30
+ export * from "./utilities/SettingsUtils";
30
31
  export * from "./widgetConfig/useWidgetConfig";
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Safely parses a JSON string and validates the result against a Zod schema.
4
+ * Returns the validated value when parsing and validation succeed, otherwise `null`.
5
+ * The return type is inferred from the schema — no type assertions needed.
6
+ *
7
+ * @param jsonString - The JSON string to parse, or `null`.
8
+ * @param schema - A Zod schema that describes the expected shape of the parsed value.
9
+ * @example
10
+ * ```ts
11
+ * const result = safeParse('{"name":"Ada"}', z.object({ name: z.string() }));
12
+ * // result: { name: "Ada" }
13
+ *
14
+ * const num = safeParse("42", z.number());
15
+ * // num: 42
16
+ *
17
+ * const miss = safeParse('{"a":1}', z.object({ b: z.string() }));
18
+ * // miss: null
19
+ * ```
20
+ */
21
+ export declare const safeParse: <TValue>(jsonString: string | null, schema: z.ZodType<TValue>) => TValue | null;
22
+ /**
23
+ * Checks whether a string is valid, non-null JSON.
24
+ *
25
+ * Reuses the internal {@link jsonParsePipeline} so that the parsing logic
26
+ * stays in one place. Returns `false` for the JSON literal `"null"` to
27
+ * match the legacy behaviour where `JSON.parse(str) !== null` was required.
28
+ *
29
+ * @param jsonString - The string to test.
30
+ * @returns {boolean} `true` when the string can be parsed as non-null JSON.
31
+ */
32
+ export declare const isJSON: (jsonString: string) => boolean;
33
+ /**
34
+ * Safely converts a value to a JSON string using a Zod validation pipeline.
35
+ *
36
+ * - Returns the value unchanged if it is already a string.
37
+ * - Returns `null` for `null` / `undefined` or if serialisation fails
38
+ * (e.g. circular references).
39
+ *
40
+ * @param value - The value to stringify.
41
+ * @returns {string | null} The JSON string representation, or `null` on failure.
42
+ * @example
43
+ * ```ts
44
+ * safeStringify({ a: 1 }) // '{"a":1}'
45
+ * safeStringify("hello") // "hello"
46
+ * safeStringify(null) // null
47
+ * ```
48
+ */
49
+ export declare const safeStringify: (value: unknown) => string | null;