@@ -9,12 +9,14 @@ import {
99 isOverlayRuntimeToggleModifierActive ,
1010 LEVEL17_OVERLAY_CYCLE_KEY ,
1111} from '/samples/phase-17/shared/overlayCycleInput.js' ;
12+ import { cloneJsonData , safeJsonParse , safeJsonStringify } from '/src/shared/io/index.js' ;
1213import { asFiniteNumber } from '/src/shared/number/index.js' ;
1314
1415const overlayRuntimePreferenceMemoryStore = new Map ( ) ;
1516const OVERLAY_RUNTIME_SHARE_PACKAGE_FORMAT = 'overlay-runtime-share-package' ;
1617const OVERLAY_RUNTIME_SHARE_PACKAGE_VERSION = 1 ;
1718const OVERLAY_RUNTIME_PROFILE_SCHEMA_VERSION = 1 ;
19+ const OVERLAY_RUNTIME_INVALID_JSON_PARSE = Symbol ( 'overlay-runtime-invalid-json-parse' ) ;
1820const OVERLAY_RUNTIME_DEFAULT_PRESET_DEFINITIONS = Object . freeze ( [
1921 Object . freeze ( {
2022 id : 'minimal' ,
@@ -234,17 +236,6 @@ function normalizeOverlayRuntimePreferenceStorageKey(preferenceStorageKey) {
234236 return String ( preferenceStorageKey || '' ) . trim ( ) ;
235237}
236238
237- function cloneJsonCompatibleValue ( value ) {
238- if ( value === null || value === undefined ) {
239- return null ;
240- }
241- try {
242- return JSON . parse ( JSON . stringify ( value ) ) ;
243- } catch {
244- return null ;
245- }
246- }
247-
248239function readOverlayRuntimePreferencePayloadFromStorage ( preferenceStorageKey , storage = null ) {
249240 const key = normalizeOverlayRuntimePreferenceStorageKey ( preferenceStorageKey ) ;
250241 if ( ! key ) {
@@ -269,20 +260,16 @@ function readOverlayRuntimePreferencePayloadFromStorage(preferenceStorageKey, st
269260 return null ;
270261 }
271262
272- try {
273- const parsed = JSON . parse ( raw ) ;
274- return parsed && typeof parsed === 'object' ? parsed : null ;
275- } catch {
276- return null ;
277- }
263+ const parsed = safeJsonParse ( raw , null ) ;
264+ return parsed && typeof parsed === 'object' ? parsed : null ;
278265}
279266
280267function writeOverlayRuntimePreferencePayloadToStorage ( preferenceStorageKey , payload , storage = null ) {
281268 const key = normalizeOverlayRuntimePreferenceStorageKey ( preferenceStorageKey ) ;
282269 if ( ! key ) {
283270 return false ;
284271 }
285- const serialized = JSON . stringify ( payload || { } ) ;
272+ const serialized = safeJsonStringify ( payload || { } , '{}' ) ;
286273 overlayRuntimePreferenceMemoryStore . set ( key , serialized ) ;
287274
288275 const storageWriter = storage && typeof storage . setItem === 'function'
@@ -376,7 +363,7 @@ function validateOverlayRuntimePreferencePayload(payload) {
376363 keybindProfile . contextInputMap = null ;
377364 keybindProfile . contextInputMapSpecified = true ;
378365 } else if ( contextInputMap && typeof contextInputMap === 'object' && ! Array . isArray ( contextInputMap ) ) {
379- const clonedContextInputMap = cloneJsonCompatibleValue ( contextInputMap ) ;
366+ const clonedContextInputMap = cloneJsonData ( contextInputMap ) ;
380367 if ( clonedContextInputMap && typeof clonedContextInputMap === 'object' ) {
381368 keybindProfile . contextInputMap = clonedContextInputMap ;
382369 keybindProfile . contextInputMapSpecified = true ;
@@ -436,7 +423,7 @@ function applyOverlayRuntimePreferencePayload(runtime, validatedPayload) {
436423 if ( keybindProfile . contextInputMapSpecified === true ) {
437424 runtime . interactionContextInputMap = keybindProfile . contextInputMap === null
438425 ? null
439- : ( cloneJsonCompatibleValue ( keybindProfile . contextInputMap ) ?? null ) ;
426+ : ( cloneJsonData ( keybindProfile . contextInputMap ) ?? null ) ;
440427 }
441428 }
442429 return true ;
@@ -455,7 +442,7 @@ function createOverlayRuntimePreferencePayloadFromValidated(validatedPayload) {
455442 payload . visibility = validatedPayload . visibility === true ;
456443 }
457444 if ( validatedPayload . hasLayout ) {
458- payload . layout = cloneJsonCompatibleValue ( validatedPayload . layout ) || { } ;
445+ payload . layout = cloneJsonData ( validatedPayload . layout ) || { } ;
459446 }
460447 if ( validatedPayload . hasKeybindProfile ) {
461448 const keybindProfile = { } ;
@@ -468,7 +455,7 @@ function createOverlayRuntimePreferencePayloadFromValidated(validatedPayload) {
468455 if ( validatedPayload . keybindProfile . contextInputMapSpecified === true ) {
469456 keybindProfile . contextInputMap = validatedPayload . keybindProfile . contextInputMap === null
470457 ? null
471- : ( cloneJsonCompatibleValue ( validatedPayload . keybindProfile . contextInputMap ) || { } ) ;
458+ : ( cloneJsonData ( validatedPayload . keybindProfile . contextInputMap ) || { } ) ;
472459 }
473460 payload . keybindProfile = keybindProfile ;
474461 }
@@ -1856,7 +1843,7 @@ export function setOverlayGameplayRuntimeKeybindProfile(runtime, { id = '', cycl
18561843 }
18571844 if ( contextInputMap !== undefined ) {
18581845 runtime . interactionContextInputMap = contextInputMap && typeof contextInputMap === 'object'
1859- ? cloneJsonCompatibleValue ( contextInputMap ) ?? contextInputMap
1846+ ? cloneJsonData ( contextInputMap ) ?? contextInputMap
18601847 : null ;
18611848 }
18621849 saveOverlayGameplayRuntimePreferences ( runtime , { silent : true } ) ;
@@ -1881,7 +1868,7 @@ export function getOverlayGameplayRuntimePreferencesSnapshot(runtime) {
18811868 cycleKey : String ( runtime . interactionCycleKey || LEVEL17_OVERLAY_CYCLE_KEY ) . trim ( ) || LEVEL17_OVERLAY_CYCLE_KEY ,
18821869 } ;
18831870 if ( runtime . interactionContextInputMap && typeof runtime . interactionContextInputMap === 'object' ) {
1884- const clonedContextInputMap = cloneJsonCompatibleValue ( runtime . interactionContextInputMap ) ;
1871+ const clonedContextInputMap = cloneJsonData ( runtime . interactionContextInputMap ) ;
18851872 if ( clonedContextInputMap && typeof clonedContextInputMap === 'object' ) {
18861873 keybindProfile . contextInputMap = clonedContextInputMap ;
18871874 }
@@ -1997,16 +1984,15 @@ export function importOverlayGameplayRuntimeProfile(runtime, profileInput, optio
19971984
19981985 let parsedInput = null ;
19991986 if ( typeof profileInput === 'string' ) {
2000- try {
2001- parsedInput = JSON . parse ( profileInput ) ;
2002- } catch {
1987+ parsedInput = safeJsonParse ( profileInput , OVERLAY_RUNTIME_INVALID_JSON_PARSE ) ;
1988+ if ( parsedInput === OVERLAY_RUNTIME_INVALID_JSON_PARSE ) {
20031989 return Object . freeze ( {
20041990 success : false ,
20051991 errors : Object . freeze ( [ 'Overlay runtime profile JSON is invalid.' ] ) ,
20061992 } ) ;
20071993 }
20081994 } else {
2009- parsedInput = cloneJsonCompatibleValue ( profileInput ) ;
1995+ parsedInput = cloneJsonData ( profileInput ) ;
20101996 }
20111997
20121998 const validated = validateOverlayRuntimePreferencePayload ( parsedInput ) ;
@@ -2039,9 +2025,8 @@ export function importOverlayGameplayRuntimeSharePackage(runtime, sharePackageIn
20392025
20402026 let parsedInput = null ;
20412027 if ( typeof sharePackageInput === 'string' ) {
2042- try {
2043- parsedInput = JSON . parse ( sharePackageInput ) ;
2044- } catch {
2028+ parsedInput = safeJsonParse ( sharePackageInput , OVERLAY_RUNTIME_INVALID_JSON_PARSE ) ;
2029+ if ( parsedInput === OVERLAY_RUNTIME_INVALID_JSON_PARSE ) {
20452030 return Object . freeze ( {
20462031 success : false ,
20472032 errors : Object . freeze ( [ 'Overlay runtime share package JSON is invalid.' ] ) ,
@@ -2050,7 +2035,7 @@ export function importOverlayGameplayRuntimeSharePackage(runtime, sharePackageIn
20502035 } ) ;
20512036 }
20522037 } else {
2053- parsedInput = cloneJsonCompatibleValue ( sharePackageInput ) ;
2038+ parsedInput = cloneJsonData ( sharePackageInput ) ;
20542039 }
20552040
20562041 const validated = validateOverlayRuntimeSharePackagePayload ( parsedInput , runtime ) ;
0 commit comments