@character-foundry/character-foundry 0.1.5 → 0.1.6
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/dist/app-framework.cjs +143 -26
- package/dist/app-framework.cjs.map +1 -1
- package/dist/app-framework.d.cts +16 -1
- package/dist/app-framework.d.ts +16 -1
- package/dist/app-framework.js +151 -34
- package/dist/app-framework.js.map +1 -1
- package/dist/charx.cjs +70 -8
- package/dist/charx.cjs.map +1 -1
- package/dist/charx.js +70 -8
- package/dist/charx.js.map +1 -1
- package/dist/core.cjs +93 -6
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +42 -1
- package/dist/core.d.ts +42 -1
- package/dist/core.js +93 -6
- package/dist/core.js.map +1 -1
- package/dist/exporter.cjs +88 -8
- package/dist/exporter.cjs.map +1 -1
- package/dist/exporter.js +89 -9
- package/dist/exporter.js.map +1 -1
- package/dist/federation.cjs +1 -0
- package/dist/federation.cjs.map +1 -1
- package/dist/federation.js +1 -0
- package/dist/federation.js.map +1 -1
- package/dist/index.cjs +122 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +123 -19
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +42 -14
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.js +43 -15
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.cjs +1 -0
- package/dist/lorebook.cjs.map +1 -1
- package/dist/lorebook.js +1 -0
- package/dist/lorebook.js.map +1 -1
- package/dist/media.cjs +1 -0
- package/dist/media.cjs.map +1 -1
- package/dist/media.js +1 -0
- package/dist/media.js.map +1 -1
- package/dist/normalizer.cjs +1 -0
- package/dist/normalizer.cjs.map +1 -1
- package/dist/normalizer.d.cts +1 -0
- package/dist/normalizer.d.ts +1 -0
- package/dist/normalizer.js +1 -0
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +19 -0
- package/dist/png.cjs.map +1 -1
- package/dist/png.js +19 -0
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs +80 -0
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +30 -0
- package/dist/schemas.d.ts +30 -0
- package/dist/schemas.js +80 -0
- package/dist/schemas.js.map +1 -1
- package/dist/voxta.cjs +14 -102
- package/dist/voxta.cjs.map +1 -1
- package/dist/voxta.js +15 -103
- package/dist/voxta.js.map +1 -1
- package/package.json +4 -4
package/dist/app-framework.d.cts
CHANGED
|
@@ -681,6 +681,7 @@ export declare function flattenSchema<T extends z.ZodRawShape>(schema: z.ZodObje
|
|
|
681
681
|
export declare function analyzeField(name: string, zodType: z.ZodTypeAny): FieldInfo;
|
|
682
682
|
/**
|
|
683
683
|
* Get the value at a dot-notation path from an object.
|
|
684
|
+
* SECURITY: Rejects dangerous keys (__proto__, constructor, prototype) to prevent prototype pollution.
|
|
684
685
|
*
|
|
685
686
|
* @example
|
|
686
687
|
* ```ts
|
|
@@ -690,6 +691,7 @@ export declare function analyzeField(name: string, zodType: z.ZodTypeAny): Field
|
|
|
690
691
|
export declare function getValueAtPath(obj: Record<string, unknown>, path: string): unknown;
|
|
691
692
|
/**
|
|
692
693
|
* Set a value at a dot-notation path in an object (immutably).
|
|
694
|
+
* SECURITY: Rejects dangerous keys (__proto__, constructor, prototype) to prevent prototype pollution.
|
|
693
695
|
*
|
|
694
696
|
* @example
|
|
695
697
|
* ```ts
|
|
@@ -809,11 +811,24 @@ export declare function TextInput({ value, onChange, name, label, error, disable
|
|
|
809
811
|
* ```
|
|
810
812
|
*/
|
|
811
813
|
export declare function Textarea({ value, onChange, name, label, error, disabled, required, hint, }: FieldWidgetProps<string>): react_jsx_runtime.JSX.Element;
|
|
814
|
+
/**
|
|
815
|
+
* Props for NumberInput - allows number | undefined since empty inputs are undefined
|
|
816
|
+
*/
|
|
817
|
+
export type NumberInputProps = Omit<FieldWidgetProps<number | undefined>, "onChange"> & {
|
|
818
|
+
/** Value can be number or undefined (empty) */
|
|
819
|
+
value: number | undefined;
|
|
820
|
+
/** onChange receives number or undefined (empty) */
|
|
821
|
+
onChange: (value: number | undefined) => void;
|
|
822
|
+
};
|
|
812
823
|
/**
|
|
813
824
|
* Headless number input widget.
|
|
814
825
|
* Renders a number input with min/max/step support.
|
|
826
|
+
*
|
|
827
|
+
* Note: This widget properly handles empty inputs as `undefined`,
|
|
828
|
+
* not as a type-cast lie. The form resolver handles validation
|
|
829
|
+
* for required fields.
|
|
815
830
|
*/
|
|
816
|
-
export declare function NumberInput({ value, onChange, name, label, error, disabled, required, hint, }:
|
|
831
|
+
export declare function NumberInput({ value, onChange, name, label, error, disabled, required, hint, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
817
832
|
/**
|
|
818
833
|
* Headless switch/toggle widget.
|
|
819
834
|
* Renders a checkbox with switch semantics (role="switch").
|
package/dist/app-framework.d.ts
CHANGED
|
@@ -681,6 +681,7 @@ export declare function flattenSchema<T extends z.ZodRawShape>(schema: z.ZodObje
|
|
|
681
681
|
export declare function analyzeField(name: string, zodType: z.ZodTypeAny): FieldInfo;
|
|
682
682
|
/**
|
|
683
683
|
* Get the value at a dot-notation path from an object.
|
|
684
|
+
* SECURITY: Rejects dangerous keys (__proto__, constructor, prototype) to prevent prototype pollution.
|
|
684
685
|
*
|
|
685
686
|
* @example
|
|
686
687
|
* ```ts
|
|
@@ -690,6 +691,7 @@ export declare function analyzeField(name: string, zodType: z.ZodTypeAny): Field
|
|
|
690
691
|
export declare function getValueAtPath(obj: Record<string, unknown>, path: string): unknown;
|
|
691
692
|
/**
|
|
692
693
|
* Set a value at a dot-notation path in an object (immutably).
|
|
694
|
+
* SECURITY: Rejects dangerous keys (__proto__, constructor, prototype) to prevent prototype pollution.
|
|
693
695
|
*
|
|
694
696
|
* @example
|
|
695
697
|
* ```ts
|
|
@@ -809,11 +811,24 @@ export declare function TextInput({ value, onChange, name, label, error, disable
|
|
|
809
811
|
* ```
|
|
810
812
|
*/
|
|
811
813
|
export declare function Textarea({ value, onChange, name, label, error, disabled, required, hint, }: FieldWidgetProps<string>): react_jsx_runtime.JSX.Element;
|
|
814
|
+
/**
|
|
815
|
+
* Props for NumberInput - allows number | undefined since empty inputs are undefined
|
|
816
|
+
*/
|
|
817
|
+
export type NumberInputProps = Omit<FieldWidgetProps<number | undefined>, "onChange"> & {
|
|
818
|
+
/** Value can be number or undefined (empty) */
|
|
819
|
+
value: number | undefined;
|
|
820
|
+
/** onChange receives number or undefined (empty) */
|
|
821
|
+
onChange: (value: number | undefined) => void;
|
|
822
|
+
};
|
|
812
823
|
/**
|
|
813
824
|
* Headless number input widget.
|
|
814
825
|
* Renders a number input with min/max/step support.
|
|
826
|
+
*
|
|
827
|
+
* Note: This widget properly handles empty inputs as `undefined`,
|
|
828
|
+
* not as a type-cast lie. The form resolver handles validation
|
|
829
|
+
* for required fields.
|
|
815
830
|
*/
|
|
816
|
-
export declare function NumberInput({ value, onChange, name, label, error, disabled, required, hint, }:
|
|
831
|
+
export declare function NumberInput({ value, onChange, name, label, error, disabled, required, hint, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
817
832
|
/**
|
|
818
833
|
* Headless switch/toggle widget.
|
|
819
834
|
* Renders a checkbox with switch semantics (role="switch").
|
package/dist/app-framework.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ../app-framework/dist/index.js
|
|
2
|
-
import { useMemo, useEffect as useEffect2, useCallback as useCallback5, useRef as useRef3 } from "react";
|
|
2
|
+
import { useMemo as useMemo2, useEffect as useEffect2, useCallback as useCallback5, useRef as useRef3, useState as useState5 } from "react";
|
|
3
3
|
import "zod";
|
|
4
4
|
import { useForm, Controller, FormProvider } from "react-hook-form";
|
|
5
5
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
@@ -10,7 +10,7 @@ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
|
10
10
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
11
11
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
12
12
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
13
|
-
import { useState, useCallback, useRef, useEffect } from "react";
|
|
13
|
+
import { useState, useCallback, useRef, useEffect, useMemo } from "react";
|
|
14
14
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
15
15
|
import { useState as useState2, useCallback as useCallback2 } from "react";
|
|
16
16
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
@@ -20,7 +20,7 @@ import { useState as useState4, useCallback as useCallback4, useRef as useRef2 }
|
|
|
20
20
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
21
21
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
22
22
|
import { Fragment, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
23
|
-
import { useState as
|
|
23
|
+
import { useState as useState6, useCallback as useCallback6 } from "react";
|
|
24
24
|
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
25
25
|
var noopServices = {
|
|
26
26
|
toast: {
|
|
@@ -352,13 +352,23 @@ function analyzeField(name, zodType) {
|
|
|
352
352
|
constraints: extractConstraints(currentType)
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
|
+
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
356
|
+
function isSafeKey(key) {
|
|
357
|
+
return !DANGEROUS_KEYS.has(key);
|
|
358
|
+
}
|
|
355
359
|
function getValueAtPath(obj, path) {
|
|
356
360
|
const parts = path.split(".");
|
|
357
361
|
let current = obj;
|
|
358
362
|
for (const part of parts) {
|
|
363
|
+
if (!isSafeKey(part)) {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
359
366
|
if (current == null || typeof current !== "object") {
|
|
360
367
|
return void 0;
|
|
361
368
|
}
|
|
369
|
+
if (!Object.hasOwn(current, part)) {
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
362
372
|
current = current[part];
|
|
363
373
|
}
|
|
364
374
|
return current;
|
|
@@ -368,12 +378,16 @@ function setValueAtPath(obj, path, value) {
|
|
|
368
378
|
if (parts.length === 0 || parts[0] === void 0) {
|
|
369
379
|
return obj;
|
|
370
380
|
}
|
|
381
|
+
const first = parts[0];
|
|
382
|
+
if (!isSafeKey(first)) {
|
|
383
|
+
console.warn(`Rejected dangerous property key in path: ${first}`);
|
|
384
|
+
return obj;
|
|
385
|
+
}
|
|
371
386
|
if (parts.length === 1) {
|
|
372
|
-
return { ...obj, [
|
|
387
|
+
return { ...obj, [first]: value };
|
|
373
388
|
}
|
|
374
|
-
const first = parts[0];
|
|
375
389
|
const rest = parts.slice(1);
|
|
376
|
-
const nested = obj[first] ?? {};
|
|
390
|
+
const nested = (Object.hasOwn(obj, first) ? obj[first] : {}) ?? {};
|
|
377
391
|
return {
|
|
378
392
|
...obj,
|
|
379
393
|
[first]: setValueAtPath(nested, rest.join("."), value)
|
|
@@ -765,11 +779,31 @@ function SearchableSelect({
|
|
|
765
779
|
const [isOpen, setIsOpen] = useState(false);
|
|
766
780
|
const [searchTerm, setSearchTerm] = useState("");
|
|
767
781
|
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
768
|
-
const
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
782
|
+
const rawOptions = hint?.options ?? [];
|
|
783
|
+
const indexedOptions = useMemo(() => {
|
|
784
|
+
return rawOptions.map((opt) => ({
|
|
785
|
+
option: opt,
|
|
786
|
+
labelLower: opt.label.toLowerCase(),
|
|
787
|
+
valueLower: opt.value.toLowerCase()
|
|
788
|
+
}));
|
|
789
|
+
}, [rawOptions]);
|
|
790
|
+
const optionsByValue = useMemo(() => {
|
|
791
|
+
const map = /* @__PURE__ */ new Map();
|
|
792
|
+
for (const opt of rawOptions) {
|
|
793
|
+
map.set(opt.value, opt);
|
|
794
|
+
}
|
|
795
|
+
return map;
|
|
796
|
+
}, [rawOptions]);
|
|
797
|
+
const filteredOptions = useMemo(() => {
|
|
798
|
+
if (!searchTerm) {
|
|
799
|
+
return rawOptions;
|
|
800
|
+
}
|
|
801
|
+
const searchLower = searchTerm.toLowerCase();
|
|
802
|
+
return indexedOptions.filter(
|
|
803
|
+
(indexed) => indexed.labelLower.includes(searchLower) || indexed.valueLower.includes(searchLower)
|
|
804
|
+
).map((indexed) => indexed.option);
|
|
805
|
+
}, [searchTerm, indexedOptions, rawOptions]);
|
|
806
|
+
const selectedOption = optionsByValue.get(value ?? "");
|
|
773
807
|
const displayValue = selectedOption?.label ?? value ?? "";
|
|
774
808
|
useEffect(() => {
|
|
775
809
|
function handleClickOutside(e) {
|
|
@@ -1392,9 +1426,9 @@ function AutoForm({
|
|
|
1392
1426
|
widgetRegistry: widgetRegistry2,
|
|
1393
1427
|
children
|
|
1394
1428
|
}) {
|
|
1395
|
-
const fieldInfoMap =
|
|
1396
|
-
const flatFieldInfoMap =
|
|
1397
|
-
const schemaDefaults =
|
|
1429
|
+
const fieldInfoMap = useMemo2(() => analyzeSchema(schema), [schema]);
|
|
1430
|
+
const flatFieldInfoMap = useMemo2(() => flattenSchema(schema), [schema]);
|
|
1431
|
+
const schemaDefaults = useMemo2(() => {
|
|
1398
1432
|
const defaults = {};
|
|
1399
1433
|
function extractDefaults(fields, prefix = "") {
|
|
1400
1434
|
fields.forEach((info, key) => {
|
|
@@ -1410,33 +1444,84 @@ function AutoForm({
|
|
|
1410
1444
|
extractDefaults(fieldInfoMap);
|
|
1411
1445
|
return defaults;
|
|
1412
1446
|
}, [fieldInfoMap]);
|
|
1447
|
+
const mergedDefaults = useMemo2(() => {
|
|
1448
|
+
return deepMerge(
|
|
1449
|
+
deepMerge(schemaDefaults, defaultValues ?? {}),
|
|
1450
|
+
values ?? {}
|
|
1451
|
+
);
|
|
1452
|
+
}, [schemaDefaults, defaultValues, values]);
|
|
1413
1453
|
const methods = useForm({
|
|
1414
1454
|
resolver: zodResolver(schema),
|
|
1415
|
-
defaultValues:
|
|
1416
|
-
...schemaDefaults,
|
|
1417
|
-
...defaultValues,
|
|
1418
|
-
...values
|
|
1419
|
-
},
|
|
1455
|
+
defaultValues: mergedDefaults,
|
|
1420
1456
|
mode: "onChange",
|
|
1421
1457
|
shouldUnregister: true
|
|
1422
1458
|
});
|
|
1423
|
-
const { control, handleSubmit, watch, formState, reset } = methods;
|
|
1424
|
-
const watchedValues = watch();
|
|
1459
|
+
const { control, handleSubmit, watch, formState, reset, getValues } = methods;
|
|
1425
1460
|
const prevValuesRef = useRef3(values);
|
|
1461
|
+
const conditionFields = useMemo2(() => {
|
|
1462
|
+
const fields = /* @__PURE__ */ new Set();
|
|
1463
|
+
const extractConditionFields = (hints, prefix = "") => {
|
|
1464
|
+
for (const [key, value] of Object.entries(hints)) {
|
|
1465
|
+
if (!value || typeof value !== "object") continue;
|
|
1466
|
+
const hint = value;
|
|
1467
|
+
if (hint.condition && typeof hint.condition === "object") {
|
|
1468
|
+
const condition = hint.condition;
|
|
1469
|
+
if (condition.field) {
|
|
1470
|
+
fields.add(condition.field);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (!("widget" in hint) && !("label" in hint) && !("condition" in hint)) {
|
|
1474
|
+
extractConditionFields(hint, prefix ? `${prefix}.${key}` : key);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
extractConditionFields(uiHints);
|
|
1479
|
+
return Array.from(fields);
|
|
1480
|
+
}, [uiHints]);
|
|
1481
|
+
const [conditionValues, setConditionValues] = useState5({});
|
|
1482
|
+
useEffect2(() => {
|
|
1483
|
+
if (conditionFields.length === 0) return;
|
|
1484
|
+
const initial = {};
|
|
1485
|
+
const currentValues = getValues();
|
|
1486
|
+
for (const field of conditionFields) {
|
|
1487
|
+
initial[field] = getValueAtPath(currentValues, field);
|
|
1488
|
+
}
|
|
1489
|
+
setConditionValues(initial);
|
|
1490
|
+
const subscription = watch((formValues, { name }) => {
|
|
1491
|
+
if (name && conditionFields.includes(name)) {
|
|
1492
|
+
setConditionValues((prev) => ({
|
|
1493
|
+
...prev,
|
|
1494
|
+
[name]: getValueAtPath(formValues, name)
|
|
1495
|
+
}));
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
return () => subscription.unsubscribe();
|
|
1499
|
+
}, [conditionFields, watch, getValues]);
|
|
1426
1500
|
useEffect2(() => {
|
|
1427
1501
|
if (values && !shallowEqual(values, prevValuesRef.current)) {
|
|
1428
1502
|
prevValuesRef.current = values;
|
|
1429
|
-
|
|
1503
|
+
const resetValues = deepMerge(
|
|
1504
|
+
deepMerge(schemaDefaults, defaultValues ?? {}),
|
|
1505
|
+
values ?? {}
|
|
1506
|
+
);
|
|
1507
|
+
reset(resetValues);
|
|
1430
1508
|
}
|
|
1431
1509
|
}, [values, reset, schemaDefaults, defaultValues]);
|
|
1510
|
+
const onChangeRef = useRef3(onChange);
|
|
1511
|
+
onChangeRef.current = onChange;
|
|
1512
|
+
const schemaRef = useRef3(schema);
|
|
1513
|
+
schemaRef.current = schema;
|
|
1432
1514
|
useEffect2(() => {
|
|
1433
|
-
if (
|
|
1434
|
-
|
|
1435
|
-
if (
|
|
1436
|
-
|
|
1515
|
+
if (!onChangeRef.current) return;
|
|
1516
|
+
const subscription = watch((formValues, { type }) => {
|
|
1517
|
+
if (type !== "change") return;
|
|
1518
|
+
const result = schemaRef.current.safeParse(formValues);
|
|
1519
|
+
if (result.success && onChangeRef.current) {
|
|
1520
|
+
onChangeRef.current(result.data);
|
|
1437
1521
|
}
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1522
|
+
});
|
|
1523
|
+
return () => subscription.unsubscribe();
|
|
1524
|
+
}, [watch]);
|
|
1440
1525
|
const HINT_KEYS = /* @__PURE__ */ new Set([
|
|
1441
1526
|
"widget",
|
|
1442
1527
|
"label",
|
|
@@ -1482,9 +1567,9 @@ function AutoForm({
|
|
|
1482
1567
|
const isConditionMet = useCallback5(
|
|
1483
1568
|
(condition) => {
|
|
1484
1569
|
if (!condition) return true;
|
|
1485
|
-
const fieldValue = getValueAtPath(
|
|
1570
|
+
const fieldValue = conditionFields.length > 0 ? conditionValues[condition.field] : getValueAtPath(getValues(), condition.field);
|
|
1486
1571
|
if (condition.when) {
|
|
1487
|
-
return condition.when(fieldValue,
|
|
1572
|
+
return condition.when(fieldValue, getValues());
|
|
1488
1573
|
}
|
|
1489
1574
|
if ("equals" in condition && condition.equals !== void 0) {
|
|
1490
1575
|
return fieldValue === condition.equals;
|
|
@@ -1500,9 +1585,9 @@ function AutoForm({
|
|
|
1500
1585
|
}
|
|
1501
1586
|
return true;
|
|
1502
1587
|
},
|
|
1503
|
-
[
|
|
1588
|
+
[conditionFields, conditionValues, getValues]
|
|
1504
1589
|
);
|
|
1505
|
-
const orderedFields =
|
|
1590
|
+
const orderedFields = useMemo2(() => {
|
|
1506
1591
|
if (fieldOrder) {
|
|
1507
1592
|
return fieldOrder.map((f) => String(f));
|
|
1508
1593
|
}
|
|
@@ -1594,19 +1679,51 @@ function AutoForm({
|
|
|
1594
1679
|
}
|
|
1595
1680
|
return formContent;
|
|
1596
1681
|
}
|
|
1682
|
+
function deepMerge(base, override) {
|
|
1683
|
+
const result = { ...base };
|
|
1684
|
+
for (const key of Object.keys(override)) {
|
|
1685
|
+
if (!isSafeKey2(key)) continue;
|
|
1686
|
+
const baseVal = base[key];
|
|
1687
|
+
const overrideVal = override[key];
|
|
1688
|
+
if (isPlainObject(baseVal) && isPlainObject(overrideVal)) {
|
|
1689
|
+
result[key] = deepMerge(
|
|
1690
|
+
baseVal,
|
|
1691
|
+
overrideVal
|
|
1692
|
+
);
|
|
1693
|
+
} else if (overrideVal !== void 0) {
|
|
1694
|
+
result[key] = overrideVal;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return result;
|
|
1698
|
+
}
|
|
1699
|
+
function isPlainObject(val) {
|
|
1700
|
+
return val !== null && typeof val === "object" && !Array.isArray(val) && Object.getPrototypeOf(val) === Object.prototype;
|
|
1701
|
+
}
|
|
1702
|
+
var DANGEROUS_KEYS2 = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1703
|
+
function isSafeKey2(key) {
|
|
1704
|
+
return !DANGEROUS_KEYS2.has(key);
|
|
1705
|
+
}
|
|
1597
1706
|
function setNestedValue(obj, path, value) {
|
|
1598
1707
|
const parts = path.split(".");
|
|
1599
1708
|
let current = obj;
|
|
1600
1709
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
1601
1710
|
const part = parts[i];
|
|
1602
1711
|
if (part === void 0) continue;
|
|
1603
|
-
if (!(part
|
|
1712
|
+
if (!isSafeKey2(part)) {
|
|
1713
|
+
console.warn(`Rejected dangerous property key in path: ${part}`);
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
if (!Object.hasOwn(current, part) || typeof current[part] !== "object") {
|
|
1604
1717
|
current[part] = {};
|
|
1605
1718
|
}
|
|
1606
1719
|
current = current[part];
|
|
1607
1720
|
}
|
|
1608
1721
|
const lastPart = parts[parts.length - 1];
|
|
1609
1722
|
if (lastPart !== void 0) {
|
|
1723
|
+
if (!isSafeKey2(lastPart)) {
|
|
1724
|
+
console.warn(`Rejected dangerous property key in path: ${lastPart}`);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1610
1727
|
current[lastPart] = value;
|
|
1611
1728
|
}
|
|
1612
1729
|
}
|
|
@@ -1636,7 +1753,7 @@ function FieldGroup({
|
|
|
1636
1753
|
className,
|
|
1637
1754
|
children
|
|
1638
1755
|
}) {
|
|
1639
|
-
const [isCollapsed, setIsCollapsed] =
|
|
1756
|
+
const [isCollapsed, setIsCollapsed] = useState6(defaultCollapsed);
|
|
1640
1757
|
const toggleCollapsed = useCallback6(() => {
|
|
1641
1758
|
if (collapsible) {
|
|
1642
1759
|
setIsCollapsed((prev) => !prev);
|