@valfuse-node/react 0.2.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/dist/index.js ADDED
@@ -0,0 +1,986 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ LocalizationProvider: () => LocalizationProvider,
24
+ ValfuseController: () => ValfuseController,
25
+ composeStorage: () => composeStorage,
26
+ cookieStrategy: () => cookieStrategy,
27
+ createLazyLocaleLoader: () => createLazyLocaleLoader,
28
+ createLocalizationStore: () => createLocalizationStore,
29
+ createSsrLocalizationState: () => createSsrLocalizationState,
30
+ localStorageStrategy: () => localStorageStrategy,
31
+ memoryStrategy: () => memoryStrategy,
32
+ sessionStorageStrategy: () => sessionStorageStrategy,
33
+ useLocalization: () => useLocalization,
34
+ useLocalizationTree: () => useLocalizationTree,
35
+ useValfuseForm: () => useValfuseForm
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/hooks/use-valfuse-form.ts
40
+ var import_react7 = require("react");
41
+
42
+ // src/hooks/sub-hooks/use-form-core.ts
43
+ var import_react = require("react");
44
+ function useFormCore(props) {
45
+ const { schema, defaultValues, mode = "onSubmit" } = props;
46
+ const [values, setValues] = (0, import_react.useState)(
47
+ () => ({ ...defaultValues })
48
+ );
49
+ const [errors, setErrorsState] = (0, import_react.useState)({});
50
+ const [isSubmitting, setIsSubmitting] = (0, import_react.useState)(false);
51
+ const [isSubmitted, setIsSubmitted] = (0, import_react.useState)(false);
52
+ const [isSubmitSuccessful, setIsSubmitSuccessful] = (0, import_react.useState)(false);
53
+ const [submitCount, setSubmitCount] = (0, import_react.useState)(0);
54
+ const [touchedFields, setTouchedFields] = (0, import_react.useState)(
55
+ () => /* @__PURE__ */ new Set()
56
+ );
57
+ const valuesRef = (0, import_react.useRef)(values);
58
+ valuesRef.current = values;
59
+ const errorsRef = (0, import_react.useRef)(errors);
60
+ errorsRef.current = errors;
61
+ const touchedFieldsRef = (0, import_react.useRef)(touchedFields);
62
+ touchedFieldsRef.current = touchedFields;
63
+ const modeRef = (0, import_react.useRef)(mode);
64
+ modeRef.current = mode;
65
+ const schemaRef = (0, import_react.useRef)(schema);
66
+ schemaRef.current = schema;
67
+ const watchSubscribersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
68
+ const lastChangedFieldRef = (0, import_react.useRef)(void 0);
69
+ return {
70
+ schema,
71
+ defaultValues,
72
+ mode,
73
+ values,
74
+ errors,
75
+ isSubmitting,
76
+ isSubmitted,
77
+ isSubmitSuccessful,
78
+ submitCount,
79
+ touchedFields,
80
+ setValues,
81
+ setErrorsState,
82
+ setTouchedFields,
83
+ setIsSubmitting,
84
+ setIsSubmitted,
85
+ setIsSubmitSuccessful,
86
+ setSubmitCount,
87
+ refs: {
88
+ valuesRef,
89
+ errorsRef,
90
+ touchedFieldsRef,
91
+ modeRef,
92
+ schemaRef,
93
+ lastChangedFieldRef,
94
+ watchSubscribersRef
95
+ }
96
+ };
97
+ }
98
+
99
+ // src/hooks/sub-hooks/use-field-validation.ts
100
+ var import_react2 = require("react");
101
+ var import_form = require("@valfuse-node/form");
102
+
103
+ // src/helpers/validation-mode.ts
104
+ function shouldValidateOnChange(mode, isTouched) {
105
+ return mode === "onChange" || mode === "all" || mode === "onTouched" && isTouched;
106
+ }
107
+ function shouldValidateOnBlur(mode) {
108
+ return mode === "onBlur" || mode === "all" || mode === "onTouched";
109
+ }
110
+
111
+ // src/helpers/field-error.ts
112
+ function buildFieldError(fieldErrors, field) {
113
+ const err = fieldErrors[field];
114
+ if (!err) return null;
115
+ return {
116
+ message: err.message,
117
+ type: err.type ?? "validation",
118
+ ...err.code !== void 0 && { code: err.code }
119
+ };
120
+ }
121
+ function mapToFieldErrors(schemaErrors) {
122
+ return Object.fromEntries(
123
+ Object.entries(schemaErrors).map(([key, err]) => {
124
+ const fieldError = {
125
+ message: err.message,
126
+ type: err.type ?? "validation",
127
+ ...err.code !== void 0 && { code: err.code },
128
+ ...err.metadata !== void 0 && { metadata: err.metadata }
129
+ };
130
+ return [key, fieldError];
131
+ })
132
+ );
133
+ }
134
+
135
+ // src/hooks/sub-hooks/use-field-validation.ts
136
+ function useFieldValidation({
137
+ schema,
138
+ setErrorsState
139
+ }) {
140
+ const validateField = (0, import_react2.useCallback)(
141
+ (name, currentValues) => {
142
+ if (!schema[name]) return;
143
+ const raw = (0, import_form.validateSchema)({ [name]: schema[name] }, currentValues);
144
+ const error = buildFieldError(raw, name);
145
+ setErrorsState((prev) => {
146
+ if (!error && !(name in prev)) return prev;
147
+ const prevError = prev[name];
148
+ if (error && prevError && error.message === prevError.message && error.type === prevError.type && error.code === prevError.code) return prev;
149
+ const next = { ...prev };
150
+ if (error) {
151
+ next[name] = error;
152
+ } else {
153
+ delete next[name];
154
+ }
155
+ return next;
156
+ });
157
+ },
158
+ [schema, setErrorsState]
159
+ );
160
+ const clearStaleFieldError = (0, import_react2.useCallback)(
161
+ (name) => {
162
+ setErrorsState((prev) => {
163
+ if (!(name in prev)) return prev;
164
+ const next = { ...prev };
165
+ delete next[name];
166
+ return next;
167
+ });
168
+ },
169
+ [setErrorsState]
170
+ );
171
+ return { validateField, clearStaleFieldError };
172
+ }
173
+
174
+ // src/hooks/sub-hooks/use-form-registration.ts
175
+ var import_react3 = require("react");
176
+ function useFormRegistration({
177
+ core,
178
+ validateField,
179
+ clearStaleFieldError
180
+ }) {
181
+ const { values, refs, touchedFields, setValues, setTouchedFields } = core;
182
+ const valuesRef = refs.valuesRef;
183
+ const modeRef = refs.modeRef;
184
+ const schemaRef = refs.schemaRef;
185
+ const touchedFieldsRef = refs.touchedFieldsRef;
186
+ const lastChangedFieldRef = refs.lastChangedFieldRef;
187
+ const register = (0, import_react3.useCallback)(
188
+ (name) => ({
189
+ name,
190
+ value: valuesRef.current[name],
191
+ onChange: (e) => {
192
+ const rawValue = e.target.value;
193
+ const fieldTransform = schemaRef.current[name]?.transform;
194
+ const newValue = fieldTransform ? fieldTransform(rawValue) : rawValue;
195
+ const updated = { ...valuesRef.current, [name]: newValue };
196
+ lastChangedFieldRef.current = name;
197
+ setValues(updated);
198
+ if (shouldValidateOnChange(modeRef.current, touchedFieldsRef.current.has(name))) {
199
+ validateField(name, updated);
200
+ } else {
201
+ clearStaleFieldError(name);
202
+ }
203
+ },
204
+ onBlur: () => {
205
+ setTouchedFields((prev) => {
206
+ if (prev.has(name)) return prev;
207
+ const next = new Set(prev);
208
+ next.add(name);
209
+ return next;
210
+ });
211
+ if (shouldValidateOnBlur(modeRef.current)) {
212
+ const current = valuesRef.current;
213
+ const fieldTransform = schemaRef.current[name]?.transform;
214
+ const forValidation = fieldTransform ? { ...current, [name]: fieldTransform(current[name]) } : current;
215
+ validateField(name, forValidation);
216
+ }
217
+ }
218
+ }),
219
+ // mode/touchedFields/schema read via refs — only stable deps here.
220
+ [validateField, clearStaleFieldError]
221
+ );
222
+ const _updateField = (0, import_react3.useCallback)(
223
+ (name, value) => {
224
+ const fieldTransform = schemaRef.current[name]?.transform;
225
+ const transformedValue = fieldTransform ? fieldTransform(value) : value;
226
+ const updated = { ...valuesRef.current, [name]: transformedValue };
227
+ lastChangedFieldRef.current = name;
228
+ setValues(updated);
229
+ if (shouldValidateOnChange(modeRef.current, touchedFieldsRef.current.has(name))) {
230
+ validateField(name, updated);
231
+ } else {
232
+ clearStaleFieldError(name);
233
+ }
234
+ },
235
+ // schema/mode/touchedFields/values read via refs.
236
+ [validateField, clearStaleFieldError]
237
+ );
238
+ const _touchField = (0, import_react3.useCallback)(
239
+ (name) => {
240
+ setTouchedFields((prev) => {
241
+ if (prev.has(name)) return prev;
242
+ const next = new Set(prev);
243
+ next.add(name);
244
+ return next;
245
+ });
246
+ if (shouldValidateOnBlur(modeRef.current)) {
247
+ const current = valuesRef.current;
248
+ const fieldTransform = schemaRef.current[name]?.transform;
249
+ const forValidation = fieldTransform ? { ...current, [name]: fieldTransform(current[name]) } : current;
250
+ validateField(name, forValidation);
251
+ }
252
+ },
253
+ // schema/mode/values read via refs — only validateField is a dep.
254
+ [validateField]
255
+ );
256
+ const control = (0, import_react3.useMemo)(
257
+ () => ({
258
+ _values: values,
259
+ _errors: core.errors,
260
+ _updateField,
261
+ _touchField,
262
+ _touchedFields: touchedFields
263
+ }),
264
+ [values, core.errors, touchedFields, _updateField, _touchField]
265
+ );
266
+ return { register, control, _updateField, _touchField };
267
+ }
268
+
269
+ // src/hooks/sub-hooks/use-form-watch.ts
270
+ var import_react4 = require("react");
271
+ function useFormWatch(core) {
272
+ const { watchSubscribersRef, lastChangedFieldRef } = core.refs;
273
+ (0, import_react4.useEffect)(() => {
274
+ if (watchSubscribersRef.current.size > 0) {
275
+ const info = {
276
+ name: lastChangedFieldRef.current,
277
+ type: lastChangedFieldRef.current ? "change" : void 0
278
+ };
279
+ watchSubscribersRef.current.forEach((cb) => cb(core.values, info));
280
+ }
281
+ lastChangedFieldRef.current = void 0;
282
+ }, [core.values, watchSubscribersRef, lastChangedFieldRef]);
283
+ const watch = (0, import_react4.useCallback)(
284
+ (nameOrNamesOrCallback) => {
285
+ const { valuesRef } = core.refs;
286
+ if (typeof nameOrNamesOrCallback === "function") {
287
+ const cb = nameOrNamesOrCallback;
288
+ watchSubscribersRef.current.add(cb);
289
+ return () => watchSubscribersRef.current.delete(cb);
290
+ }
291
+ if (Array.isArray(nameOrNamesOrCallback)) {
292
+ return nameOrNamesOrCallback.map(
293
+ (n) => valuesRef.current[n]
294
+ );
295
+ }
296
+ if (typeof nameOrNamesOrCallback === "string") {
297
+ return valuesRef.current[nameOrNamesOrCallback];
298
+ }
299
+ return valuesRef.current;
300
+ },
301
+ [core.refs, watchSubscribersRef]
302
+ );
303
+ return { watch };
304
+ }
305
+
306
+ // src/hooks/sub-hooks/use-form-actions.ts
307
+ var import_react5 = require("react");
308
+ var import_form2 = require("@valfuse-node/form");
309
+ function useFormActions(core, clearStaleFieldError) {
310
+ const handleSubmit = (0, import_react5.useCallback)(
311
+ (onValid) => async (e) => {
312
+ e?.preventDefault?.();
313
+ const { schema, refs } = core;
314
+ const currentValues = refs.valuesRef.current;
315
+ const transformedValues = (0, import_form2.transformValues)(
316
+ schema,
317
+ currentValues
318
+ );
319
+ const schemaErrors = (0, import_form2.validateSchema)(
320
+ schema,
321
+ transformedValues
322
+ );
323
+ core.setSubmitCount((c) => c + 1);
324
+ core.setIsSubmitted(true);
325
+ if (Object.keys(schemaErrors).length > 0) {
326
+ core.setErrorsState(mapToFieldErrors(schemaErrors));
327
+ core.setIsSubmitSuccessful(false);
328
+ return;
329
+ }
330
+ if (Object.keys(refs.errorsRef.current).length > 0) core.setErrorsState({});
331
+ core.setIsSubmitting(true);
332
+ try {
333
+ await onValid(transformedValues);
334
+ core.setIsSubmitSuccessful(true);
335
+ } catch (err) {
336
+ core.setIsSubmitSuccessful(false);
337
+ throw err;
338
+ } finally {
339
+ core.setIsSubmitting(false);
340
+ }
341
+ },
342
+ // All read via core.* — list each stable field explicitly.
343
+ [
344
+ core.schema,
345
+ core.refs.valuesRef,
346
+ core.refs.errorsRef,
347
+ core.setSubmitCount,
348
+ core.setIsSubmitted,
349
+ core.setIsSubmitting,
350
+ core.setIsSubmitSuccessful,
351
+ core.setErrorsState
352
+ ]
353
+ );
354
+ const setErrors = (0, import_react5.useCallback)(
355
+ (fieldErrors) => {
356
+ core.setErrorsState((prev) => {
357
+ const next = { ...prev };
358
+ let changed = false;
359
+ for (const [fieldName, rawError] of Object.entries(fieldErrors)) {
360
+ if (rawError === void 0) continue;
361
+ const normalized = (0, import_form2.normalizeError)(rawError);
362
+ const prevFieldError = prev[fieldName];
363
+ if (prevFieldError && prevFieldError.message === normalized.message && prevFieldError.type === (normalized.type ?? "manual") && prevFieldError.code === normalized.code) continue;
364
+ next[fieldName] = {
365
+ message: normalized.message,
366
+ type: normalized.type ?? "manual",
367
+ ...normalized.code !== void 0 && { code: normalized.code },
368
+ ...normalized.metadata !== void 0 && {
369
+ metadata: normalized.metadata
370
+ }
371
+ };
372
+ changed = true;
373
+ }
374
+ return changed ? next : prev;
375
+ });
376
+ },
377
+ [core.setErrorsState]
378
+ );
379
+ const clearErrors = (0, import_react5.useCallback)(
380
+ (name) => {
381
+ if (name === void 0) {
382
+ if (Object.keys(core.refs.errorsRef.current).length === 0) return;
383
+ core.setErrorsState({});
384
+ return;
385
+ }
386
+ const fields = Array.isArray(name) ? name : [name];
387
+ core.setErrorsState((prev) => {
388
+ const toDelete = fields.filter((k) => k in prev);
389
+ if (toDelete.length === 0) return prev;
390
+ const next = { ...prev };
391
+ for (const n of toDelete) delete next[n];
392
+ return next;
393
+ });
394
+ },
395
+ [core.refs.errorsRef, core.setErrorsState]
396
+ );
397
+ const trigger = (0, import_react5.useCallback)(
398
+ (name) => {
399
+ const { schema, refs } = core;
400
+ const current = refs.valuesRef.current;
401
+ const transformed = (0, import_form2.transformValues)(schema, current);
402
+ const fieldsToValidate = name === void 0 ? Object.keys(schema) : Array.isArray(name) ? name : [name];
403
+ let allValid = true;
404
+ let changed = false;
405
+ const prevErrors = refs.errorsRef.current;
406
+ const nextErrors = { ...prevErrors };
407
+ for (const field of fieldsToValidate) {
408
+ if (!schema[field]) continue;
409
+ const raw = (0, import_form2.validateSchema)({ [field]: schema[field] }, transformed);
410
+ const error = buildFieldError(raw, field);
411
+ if (error) {
412
+ allValid = false;
413
+ const prev = prevErrors[field];
414
+ if (!prev || prev.message !== error.message || prev.type !== error.type || prev.code !== error.code) {
415
+ changed = true;
416
+ }
417
+ nextErrors[field] = error;
418
+ } else {
419
+ if (field in nextErrors) {
420
+ changed = true;
421
+ delete nextErrors[field];
422
+ }
423
+ }
424
+ }
425
+ if (changed) core.setErrorsState(nextErrors);
426
+ return allValid;
427
+ },
428
+ [core.schema, core.refs.valuesRef, core.refs.errorsRef, core.setErrorsState]
429
+ );
430
+ const setValue = (0, import_react5.useCallback)(
431
+ (name, value, options) => {
432
+ const { schema, refs } = core;
433
+ const fieldTransform = refs.schemaRef.current[name]?.transform;
434
+ const transformedValue = fieldTransform ? fieldTransform(value) : value;
435
+ const updated = { ...refs.valuesRef.current, [name]: transformedValue };
436
+ refs.valuesRef.current = updated;
437
+ refs.lastChangedFieldRef.current = name;
438
+ core.setValues(updated);
439
+ if (options?.shouldValidate) {
440
+ const field = name;
441
+ if (!schema[field]) return;
442
+ const raw = (0, import_form2.validateSchema)(
443
+ { [field]: schema[field] },
444
+ updated
445
+ );
446
+ const error = buildFieldError(raw, field);
447
+ core.setErrorsState((prev) => {
448
+ if (!error && !(field in prev)) return prev;
449
+ const prevError = prev[name];
450
+ if (error && prevError && error.message === prevError.message && error.type === prevError.type && error.code === prevError.code) return prev;
451
+ const next = { ...prev };
452
+ if (error) {
453
+ next[name] = error;
454
+ } else {
455
+ delete next[name];
456
+ }
457
+ return next;
458
+ });
459
+ } else {
460
+ clearStaleFieldError(name);
461
+ }
462
+ },
463
+ [
464
+ core.schema,
465
+ core.refs.schemaRef,
466
+ core.refs.valuesRef,
467
+ core.refs.lastChangedFieldRef,
468
+ core.setValues,
469
+ core.setErrorsState,
470
+ clearStaleFieldError
471
+ ]
472
+ );
473
+ const reset = (0, import_react5.useCallback)(
474
+ (newValues) => {
475
+ core.setValues({ ...core.defaultValues, ...newValues });
476
+ core.setErrorsState({});
477
+ core.setTouchedFields(/* @__PURE__ */ new Set());
478
+ core.setIsSubmitted(false);
479
+ core.setIsSubmitSuccessful(false);
480
+ core.setSubmitCount(0);
481
+ },
482
+ [
483
+ core.defaultValues,
484
+ core.setValues,
485
+ core.setErrorsState,
486
+ core.setTouchedFields,
487
+ core.setIsSubmitted,
488
+ core.setIsSubmitSuccessful,
489
+ core.setSubmitCount
490
+ ]
491
+ );
492
+ return { handleSubmit, setErrors, clearErrors, setValue, trigger, reset };
493
+ }
494
+
495
+ // src/hooks/sub-hooks/use-form-derived-state.ts
496
+ var import_react6 = require("react");
497
+ var import_form3 = require("@valfuse-node/form");
498
+ function useFormDerivedState(core) {
499
+ const { schema, defaultValues, values, errors, touchedFields } = core;
500
+ const isDirty = (0, import_react6.useMemo)(
501
+ () => Object.keys(defaultValues).some(
502
+ (key) => values[key] !== defaultValues[key]
503
+ ),
504
+ [values, defaultValues]
505
+ );
506
+ const dirtyFields = (0, import_react6.useMemo)(
507
+ () => Object.keys(defaultValues).reduce(
508
+ (acc, key) => {
509
+ const k = key;
510
+ if (values[k] !== defaultValues[k]) acc[k] = true;
511
+ return acc;
512
+ },
513
+ {}
514
+ ),
515
+ [values, defaultValues]
516
+ );
517
+ const touchedFieldsRecord = (0, import_react6.useMemo)(
518
+ () => Array.from(touchedFields).reduce(
519
+ (acc, key) => {
520
+ acc[key] = true;
521
+ return acc;
522
+ },
523
+ {}
524
+ ),
525
+ [touchedFields]
526
+ );
527
+ const isValid = (0, import_react6.useMemo)(() => {
528
+ if (Object.keys(errors).length > 0) return false;
529
+ const transformed = (0, import_form3.transformValues)(
530
+ schema,
531
+ values
532
+ );
533
+ return Object.keys((0, import_form3.validateSchema)(schema, transformed)).length === 0;
534
+ }, [schema, values, errors]);
535
+ return { isDirty, dirtyFields, touchedFieldsRecord, isValid };
536
+ }
537
+
538
+ // src/hooks/use-valfuse-form.ts
539
+ function useValfuseForm(props) {
540
+ const { defaultValues } = props;
541
+ const core = useFormCore(props);
542
+ const { validateField, clearStaleFieldError } = useFieldValidation({
543
+ schema: core.schema,
544
+ setErrorsState: core.setErrorsState
545
+ });
546
+ const { register, control } = useFormRegistration({
547
+ core,
548
+ validateField,
549
+ clearStaleFieldError
550
+ });
551
+ const { watch } = useFormWatch(core);
552
+ const { handleSubmit, setErrors, clearErrors, setValue, trigger, reset } = useFormActions(core, clearStaleFieldError);
553
+ const { isDirty, dirtyFields, touchedFieldsRecord, isValid } = useFormDerivedState(core);
554
+ const formState = (0, import_react7.useMemo)(
555
+ () => ({
556
+ errors: core.errors,
557
+ isSubmitting: core.isSubmitting,
558
+ isSubmitted: core.isSubmitted,
559
+ isSubmitSuccessful: core.isSubmitSuccessful,
560
+ submitCount: core.submitCount,
561
+ isDirty,
562
+ isValid,
563
+ dirtyFields,
564
+ touchedFields: touchedFieldsRecord,
565
+ defaultValues
566
+ }),
567
+ [
568
+ core.errors,
569
+ core.isSubmitting,
570
+ core.isSubmitted,
571
+ core.isSubmitSuccessful,
572
+ core.submitCount,
573
+ isDirty,
574
+ isValid,
575
+ dirtyFields,
576
+ touchedFieldsRecord,
577
+ defaultValues
578
+ ]
579
+ );
580
+ return {
581
+ register,
582
+ control,
583
+ handleSubmit,
584
+ formState,
585
+ setErrors,
586
+ clearErrors,
587
+ setValue,
588
+ trigger,
589
+ watch,
590
+ reset
591
+ };
592
+ }
593
+
594
+ // src/components/valfuse-controller.tsx
595
+ var import_react8 = require("react");
596
+ function ValfuseController({
597
+ control,
598
+ name,
599
+ render
600
+ }) {
601
+ const onChange = (0, import_react8.useCallback)(
602
+ (value) => control._updateField(name, value),
603
+ [control._updateField, name]
604
+ );
605
+ const onBlur = (0, import_react8.useCallback)(
606
+ () => control._touchField(name),
607
+ [control._touchField, name]
608
+ );
609
+ const fieldValue = control._values[name];
610
+ const fieldError = control._errors[name];
611
+ const isTouched = control._touchedFields.has(name);
612
+ const field = (0, import_react8.useMemo)(
613
+ () => ({
614
+ name,
615
+ value: fieldValue,
616
+ onChange,
617
+ onBlur
618
+ }),
619
+ [name, fieldValue, onChange, onBlur]
620
+ );
621
+ const fieldState = (0, import_react8.useMemo)(
622
+ () => ({
623
+ error: fieldError,
624
+ isTouched
625
+ }),
626
+ [fieldError, isTouched]
627
+ );
628
+ return render({ field, fieldState });
629
+ }
630
+
631
+ // src/localization/provider/localization-provider.tsx
632
+ var import_react9 = require("react");
633
+
634
+ // src/localization/bridge/create-localization-store.ts
635
+ var import_runtime = require("@valfuse-node/localization/runtime");
636
+ function createLocalizationStore(manifest, initialLocale) {
637
+ let locale = initialLocale ?? manifest.base_locale;
638
+ return {
639
+ getLocale: () => locale,
640
+ setLocale(nextLocale) {
641
+ locale = nextLocale;
642
+ },
643
+ t(key, params) {
644
+ const value = (0, import_runtime.lookupMessage)(
645
+ {
646
+ locale,
647
+ fallbackLocale: manifest.fallback_locale,
648
+ messages: manifest.messages
649
+ },
650
+ key
651
+ );
652
+ return (0, import_runtime.interpolate)(value, params);
653
+ }
654
+ };
655
+ }
656
+
657
+ // src/localization/provider/localization-provider.tsx
658
+ var import_jsx_runtime = require("react/jsx-runtime");
659
+ var LocalizationContext = (0, import_react9.createContext)(null);
660
+ function determineInitialLocale(manifest, initialLocale, storage) {
661
+ const stored = storage?.get();
662
+ if (stored && manifest.locales.includes(stored)) return stored;
663
+ if (initialLocale && manifest.locales.includes(initialLocale)) return initialLocale;
664
+ return manifest.base_locale;
665
+ }
666
+ function LocalizationProvider({
667
+ manifest,
668
+ initialLocale,
669
+ storage,
670
+ children
671
+ }) {
672
+ const [locale, _setLocale] = (0, import_react9.useState)(
673
+ () => determineInitialLocale(manifest, initialLocale, storage)
674
+ );
675
+ const setLocale = (0, import_react9.useCallback)(
676
+ (nextLocale) => {
677
+ storage?.set(nextLocale);
678
+ _setLocale(nextLocale);
679
+ },
680
+ [storage]
681
+ );
682
+ const value = (0, import_react9.useMemo)(() => {
683
+ const store = createLocalizationStore(manifest, locale);
684
+ return { locale, setLocale, store, manifest };
685
+ }, [locale, manifest, setLocale]);
686
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalizationContext.Provider, { value, children });
687
+ }
688
+
689
+ // src/localization/hooks/use-localization.ts
690
+ var import_react10 = require("react");
691
+ var import_runtime2 = require("@valfuse-node/localization/runtime");
692
+ function pickFallbackText(fallback, key) {
693
+ if (!fallback) return void 0;
694
+ if (typeof fallback === "string") return fallback;
695
+ if (typeof fallback === "function") return fallback(key);
696
+ return fallback[key];
697
+ }
698
+ function useLocalization(options = {}) {
699
+ const context = (0, import_react10.useContext)(LocalizationContext);
700
+ if (!context) {
701
+ throw new Error("useLocalization must be used within LocalizationProvider.");
702
+ }
703
+ const { locale, manifest } = context;
704
+ const fallback = options.fallback;
705
+ const runtimeContext = (0, import_react10.useMemo)(
706
+ () => ({
707
+ locale,
708
+ fallbackLocale: manifest.fallback_locale,
709
+ messages: manifest.messages
710
+ }),
711
+ [locale, manifest.fallback_locale, manifest.messages]
712
+ );
713
+ const readTranslation = (0, import_react10.useCallback)(
714
+ (key) => (0, import_runtime2.lookupMessage)(runtimeContext, key),
715
+ [runtimeContext]
716
+ );
717
+ const hasKey = (0, import_react10.useCallback)(
718
+ (key) => key in (manifest.messages[locale] ?? {}) || key in (manifest.messages[manifest.fallback_locale] ?? {}),
719
+ [locale, manifest.fallback_locale, manifest.messages]
720
+ );
721
+ const translate = (0, import_react10.useCallback)(
722
+ (key, fallbackValue) => {
723
+ const translated = readTranslation(key);
724
+ if (!translated || translated === key) {
725
+ if (fallbackValue === null) return translated;
726
+ return fallbackValue ?? pickFallbackText(fallback, key) ?? translated;
727
+ }
728
+ return translated;
729
+ },
730
+ [fallback, readTranslation]
731
+ );
732
+ const format = (0, import_react10.useCallback)(
733
+ (key, params) => (0, import_runtime2.interpolate)(translate(key), params),
734
+ [translate]
735
+ );
736
+ const translateOrNull = (0, import_react10.useCallback)(
737
+ (key) => {
738
+ if (key == null) return null;
739
+ return hasKey(key) ? translate(key) : null;
740
+ },
741
+ [hasKey, translate]
742
+ );
743
+ const formatOrNull = (0, import_react10.useCallback)(
744
+ (key, params) => {
745
+ if (key == null) return null;
746
+ return hasKey(key) ? format(key, params) : null;
747
+ },
748
+ [hasKey, format]
749
+ );
750
+ const raw = (0, import_react10.useCallback)(
751
+ (key) => manifest.messages[locale]?.[key] ?? manifest.messages[manifest.fallback_locale]?.[key] ?? "",
752
+ [locale, manifest.fallback_locale, manifest.messages]
753
+ );
754
+ const plural = (0, import_react10.useCallback)(
755
+ (key, count) => (0, import_runtime2.pickStructuredPluralVariant)(raw(key), count),
756
+ [raw]
757
+ );
758
+ const pluralOrNull = (0, import_react10.useCallback)(
759
+ (key, count) => {
760
+ if (key == null) return null;
761
+ return hasKey(key) ? plural(key, count) : null;
762
+ },
763
+ [hasKey, plural]
764
+ );
765
+ const gender = (0, import_react10.useCallback)(
766
+ (key, value, params) => (0, import_runtime2.interpolate)((0, import_runtime2.pickStructuredGenderVariant)(raw(key), value), params),
767
+ [raw]
768
+ );
769
+ const contextFn = (0, import_react10.useCallback)(
770
+ (key, value, params) => {
771
+ const translated = (0, import_runtime2.pickStructuredContextVariant)(raw(key), value);
772
+ return params ? (0, import_runtime2.interpolate)(translated, params) : translated;
773
+ },
774
+ [raw]
775
+ );
776
+ const namespace = (0, import_react10.useCallback)(
777
+ (scope) => ({
778
+ translate: (key, fallbackValue) => translate(`${scope}.${key}`, fallbackValue),
779
+ translateOrNull: (key) => key == null ? null : translateOrNull(`${scope}.${key}`),
780
+ format: (key, params) => format(`${scope}.${key}`, params),
781
+ formatOrNull: (key, params) => key == null ? null : formatOrNull(`${scope}.${key}`, params),
782
+ plural: (key, count) => plural(`${scope}.${key}`, count),
783
+ pluralOrNull: (key, count) => key == null ? null : pluralOrNull(`${scope}.${key}`, count),
784
+ gender: (key, value, params) => gender(`${scope}.${key}`, value, params),
785
+ context: (key, value, params) => contextFn(`${scope}.${key}`, value, params)
786
+ }),
787
+ [translate, translateOrNull, format, formatOrNull, plural, pluralOrNull, gender, contextFn]
788
+ );
789
+ const entriesForLocale = (0, import_react10.useMemo)(
790
+ () => Object.entries(manifest.messages[locale] ?? {}).sort(
791
+ ([a], [b]) => a.localeCompare(b)
792
+ ),
793
+ [locale, manifest.messages]
794
+ );
795
+ return {
796
+ ...context,
797
+ translate,
798
+ translateOrNull,
799
+ format,
800
+ formatOrNull,
801
+ plural,
802
+ pluralOrNull,
803
+ gender,
804
+ context: contextFn,
805
+ namespace,
806
+ entriesForLocale
807
+ };
808
+ }
809
+
810
+ // src/localization/hooks/use-localization-tree.ts
811
+ var import_react11 = require("react");
812
+ function toSegments(key) {
813
+ return key.split(".").slice(1);
814
+ }
815
+ function useLocalizationTree() {
816
+ const { store, manifest } = useLocalization();
817
+ return (0, import_react11.useMemo)(() => {
818
+ const strings = {};
819
+ const placeholders = {};
820
+ for (const entry of manifest.entries) {
821
+ const segments = toSegments(entry.key);
822
+ if (segments.length === 0) continue;
823
+ const target = entry.placeholders.length > 0 ? placeholders : strings;
824
+ let cursor = target;
825
+ for (let i = 0; i < segments.length - 1; i += 1) {
826
+ const segment = segments[i];
827
+ if (!segment) continue;
828
+ cursor[segment] ?? (cursor[segment] = {});
829
+ cursor = cursor[segment];
830
+ }
831
+ const leaf = segments[segments.length - 1];
832
+ if (!leaf) continue;
833
+ if (entry.placeholders.length > 0) {
834
+ cursor[leaf] = (params) => store.t(entry.key, params);
835
+ } else {
836
+ cursor[leaf] = store.t(entry.key);
837
+ }
838
+ }
839
+ return { strings, placeholders };
840
+ }, [store, manifest]);
841
+ }
842
+
843
+ // src/localization/lazy/create-lazy-locale-loader.ts
844
+ function createLazyLocaleLoader(manifest) {
845
+ return async (locale) => manifest.messages[locale] ?? {};
846
+ }
847
+
848
+ // src/localization/ssr/create-ssr-localization-state.ts
849
+ function createSsrLocalizationState(manifest, locale) {
850
+ const activeLocale = locale ?? manifest.base_locale;
851
+ return {
852
+ locale: activeLocale,
853
+ messages: manifest.messages[activeLocale] ?? {}
854
+ };
855
+ }
856
+
857
+ // src/localization/storage/locale-storage.ts
858
+ function localStorageStrategy(options = {}) {
859
+ const key = options.key ?? "locale";
860
+ return {
861
+ get() {
862
+ try {
863
+ return window.localStorage.getItem(key) ?? void 0;
864
+ } catch {
865
+ return void 0;
866
+ }
867
+ },
868
+ set(locale) {
869
+ try {
870
+ window.localStorage.setItem(key, locale);
871
+ } catch {
872
+ }
873
+ },
874
+ remove() {
875
+ try {
876
+ window.localStorage.removeItem(key);
877
+ } catch {
878
+ }
879
+ }
880
+ };
881
+ }
882
+ function sessionStorageStrategy(options = {}) {
883
+ const key = options.key ?? "locale";
884
+ return {
885
+ get() {
886
+ try {
887
+ return window.sessionStorage.getItem(key) ?? void 0;
888
+ } catch {
889
+ return void 0;
890
+ }
891
+ },
892
+ set(locale) {
893
+ try {
894
+ window.sessionStorage.setItem(key, locale);
895
+ } catch {
896
+ }
897
+ },
898
+ remove() {
899
+ try {
900
+ window.sessionStorage.removeItem(key);
901
+ } catch {
902
+ }
903
+ }
904
+ };
905
+ }
906
+ function cookieStrategy(options = {}) {
907
+ const {
908
+ key = "locale",
909
+ domain,
910
+ path = "/",
911
+ maxAge = 31536e3,
912
+ sameSite = "Lax"
913
+ } = options;
914
+ function buildCookie(value, age) {
915
+ const isSecure = options.secure !== void 0 ? options.secure : typeof location !== "undefined" && location.protocol === "https:";
916
+ let cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
917
+ cookie += `; path=${path}`;
918
+ cookie += `; max-age=${age}`;
919
+ if (domain) cookie += `; domain=${domain}`;
920
+ if (isSecure) cookie += "; Secure";
921
+ cookie += `; SameSite=${sameSite}`;
922
+ return cookie;
923
+ }
924
+ return {
925
+ get() {
926
+ if (typeof document === "undefined") return void 0;
927
+ const encodedKey = encodeURIComponent(key);
928
+ const match = document.cookie.split("; ").find((c) => c.startsWith(`${encodedKey}=`));
929
+ const rawValue = match?.split("=").slice(1).join("=");
930
+ return rawValue !== void 0 ? decodeURIComponent(rawValue) : void 0;
931
+ },
932
+ set(locale) {
933
+ if (typeof document === "undefined") return;
934
+ document.cookie = buildCookie(locale, maxAge);
935
+ },
936
+ remove() {
937
+ if (typeof document === "undefined") return;
938
+ document.cookie = buildCookie("", 0);
939
+ }
940
+ };
941
+ }
942
+ function memoryStrategy(options = {}) {
943
+ let stored = options.initialLocale;
944
+ return {
945
+ get: () => stored,
946
+ set: (locale) => {
947
+ stored = locale;
948
+ },
949
+ remove: () => {
950
+ stored = void 0;
951
+ }
952
+ };
953
+ }
954
+ function composeStorage(...storages) {
955
+ return {
956
+ get() {
957
+ for (const s of storages) {
958
+ const v = s.get();
959
+ if (v !== void 0) return v;
960
+ }
961
+ return void 0;
962
+ },
963
+ set(locale) {
964
+ storages.forEach((s) => s.set(locale));
965
+ },
966
+ remove() {
967
+ storages.forEach((s) => s.remove?.());
968
+ }
969
+ };
970
+ }
971
+ // Annotate the CommonJS export names for ESM import in node:
972
+ 0 && (module.exports = {
973
+ LocalizationProvider,
974
+ ValfuseController,
975
+ composeStorage,
976
+ cookieStrategy,
977
+ createLazyLocaleLoader,
978
+ createLocalizationStore,
979
+ createSsrLocalizationState,
980
+ localStorageStrategy,
981
+ memoryStrategy,
982
+ sessionStorageStrategy,
983
+ useLocalization,
984
+ useLocalizationTree,
985
+ useValfuseForm
986
+ });