@form-engine-ts/react 4.6.0 → 4.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/README.md +3 -2
- package/dist/index.cjs +69 -36
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +67 -35
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -80,8 +80,9 @@ single-slot or batch translation with stale/manual status handling. The MUI pack
|
|
|
80
80
|
schema-driven pre-submit confirmation controls in the builder.
|
|
81
81
|
|
|
82
82
|
`useTranslationWorkspace` validates added locales against `policy.allowedLocales` and `policy.maxLocales` and returns
|
|
83
|
-
a structured `{ success, error }` result from `addLocale`.
|
|
84
|
-
|
|
83
|
+
a structured `{ success, error }` result from `addLocale`. Built-in BCP 47, duplicate, and policy checks always run
|
|
84
|
+
before the optional `validateLocale` callback, which receives the locale, default locale, current locales, and policy.
|
|
85
|
+
Use `isAddLocaleAllowed` to disable locale controls before submission. Removing a locale also clears its
|
|
85
86
|
localized values and metadata; the default locale remains protected.
|
|
86
87
|
|
|
87
88
|
## Headless builder and renderer lifecycle
|
package/dist/index.cjs
CHANGED
|
@@ -39,7 +39,8 @@ __export(index_exports, {
|
|
|
39
39
|
useForm: () => useForm,
|
|
40
40
|
useFormBuilder: () => useFormBuilder,
|
|
41
41
|
useSubmissionReceipts: () => useSubmissionReceipts,
|
|
42
|
-
useTranslationWorkspace: () => useTranslationWorkspace
|
|
42
|
+
useTranslationWorkspace: () => useTranslationWorkspace,
|
|
43
|
+
validateLocalePipeline: () => validateLocalePipeline
|
|
43
44
|
});
|
|
44
45
|
module.exports = __toCommonJS(index_exports);
|
|
45
46
|
|
|
@@ -3281,37 +3282,87 @@ function asAsyncAdapter(adapter) {
|
|
|
3281
3282
|
)
|
|
3282
3283
|
};
|
|
3283
3284
|
}
|
|
3284
|
-
function
|
|
3285
|
-
|
|
3285
|
+
function isValidBcp47Locale(locale) {
|
|
3286
|
+
const language = locale.split("-")[0];
|
|
3287
|
+
if (language === void 0 || !/^[a-z]{2,3}$/iu.test(language)) return false;
|
|
3288
|
+
try {
|
|
3289
|
+
Intl.getCanonicalLocales(locale);
|
|
3290
|
+
return true;
|
|
3291
|
+
} catch {
|
|
3292
|
+
return false;
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator) => {
|
|
3296
|
+
const currentLocales = schema.supportedLocales ?? [];
|
|
3297
|
+
const defaultLocale = schema.defaultLocale ?? "";
|
|
3298
|
+
if (!isValidBcp47Locale(locale)) {
|
|
3286
3299
|
return {
|
|
3287
3300
|
valid: false,
|
|
3288
|
-
error: {
|
|
3301
|
+
error: {
|
|
3302
|
+
type: "invalid_locale_format",
|
|
3303
|
+
message: `Invalid BCP 47 locale format: "${locale}"`
|
|
3304
|
+
}
|
|
3289
3305
|
};
|
|
3290
3306
|
}
|
|
3291
|
-
|
|
3292
|
-
Intl.getCanonicalLocales(locale);
|
|
3293
|
-
} catch {
|
|
3307
|
+
if (locale === defaultLocale || currentLocales.includes(locale)) {
|
|
3294
3308
|
return {
|
|
3295
3309
|
valid: false,
|
|
3296
|
-
error: {
|
|
3310
|
+
error: {
|
|
3311
|
+
type: "locale_already_exists",
|
|
3312
|
+
message: `Locale "${locale}" is already registered.`
|
|
3313
|
+
}
|
|
3297
3314
|
};
|
|
3298
3315
|
}
|
|
3299
3316
|
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(locale)) {
|
|
3300
3317
|
return {
|
|
3301
3318
|
valid: false,
|
|
3302
|
-
error: {
|
|
3319
|
+
error: {
|
|
3320
|
+
type: "locale_not_allowed",
|
|
3321
|
+
message: `Locale "${locale}" is not allowed by policy.`
|
|
3322
|
+
}
|
|
3303
3323
|
};
|
|
3304
3324
|
}
|
|
3305
|
-
|
|
3325
|
+
const totalLocalesCount = 1 + currentLocales.length;
|
|
3326
|
+
if (policy?.maxLocales !== void 0 && totalLocalesCount >= policy.maxLocales) {
|
|
3306
3327
|
return {
|
|
3307
3328
|
valid: false,
|
|
3308
3329
|
error: {
|
|
3309
3330
|
type: "max_locales_exceeded",
|
|
3310
|
-
message: `
|
|
3331
|
+
message: `Cannot add locale: maximum allowed locales limit (${policy.maxLocales}) reached.`
|
|
3311
3332
|
}
|
|
3312
3333
|
};
|
|
3313
3334
|
}
|
|
3335
|
+
if (customValidator !== void 0) {
|
|
3336
|
+
const customContext = policy === void 0 ? Object.assign([...currentLocales], { locale, defaultLocale, currentLocales }) : Object.assign([...currentLocales], { locale, defaultLocale, currentLocales, policy });
|
|
3337
|
+
const customResult = customValidator(locale, customContext);
|
|
3338
|
+
if (customResult === false) {
|
|
3339
|
+
return {
|
|
3340
|
+
valid: false,
|
|
3341
|
+
error: {
|
|
3342
|
+
type: "custom_validation_failed",
|
|
3343
|
+
message: `Custom validation rejected locale "${locale}".`
|
|
3344
|
+
}
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
if (typeof customResult === "string") {
|
|
3348
|
+
return {
|
|
3349
|
+
valid: false,
|
|
3350
|
+
error: { type: "custom_validation_failed", message: customResult }
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
if (typeof customResult === "object" && customResult !== null && !customResult.valid) return customResult;
|
|
3354
|
+
}
|
|
3314
3355
|
return { valid: true };
|
|
3356
|
+
};
|
|
3357
|
+
function workspaceLocaleValidationMessage(validation) {
|
|
3358
|
+
if (validation.error?.type === "locale_not_allowed") {
|
|
3359
|
+
return validation.error.message.replace(" is not allowed by policy.", " is not allowed by the form policy.");
|
|
3360
|
+
}
|
|
3361
|
+
if (validation.error?.type === "max_locales_exceeded") {
|
|
3362
|
+
const maximum = validation.error.message.match(/\((\d+)\)/u)?.[1];
|
|
3363
|
+
return maximum === void 0 ? validation.error.message : `At most ${maximum} locales are allowed by the form policy.`;
|
|
3364
|
+
}
|
|
3365
|
+
return validation.error?.message ?? "Locale is not valid.";
|
|
3315
3366
|
}
|
|
3316
3367
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3317
3368
|
return {
|
|
@@ -3437,20 +3488,8 @@ function useTranslationWorkspace({
|
|
|
3437
3488
|
(locale) => {
|
|
3438
3489
|
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3439
3490
|
const normalized = locale.trim();
|
|
3440
|
-
const
|
|
3441
|
-
|
|
3442
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3443
|
-
sourceLocale,
|
|
3444
|
-
...(0, import_core4.collectSchemaLocales)(currentSchema).allUniqueLocales
|
|
3445
|
-
])
|
|
3446
|
-
];
|
|
3447
|
-
const validation = validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy);
|
|
3448
|
-
if (!validation.valid) return { success: false, error: validation.error?.message ?? "Locale is not valid." };
|
|
3449
|
-
const alreadyRegistered = normalized === sourceLocale || normalized === currentSchema.defaultLocale || (currentSchema.supportedLocales ?? []).includes(normalized);
|
|
3450
|
-
if (alreadyRegistered) {
|
|
3451
|
-
setSelectedLocale(normalized);
|
|
3452
|
-
return { success: true };
|
|
3453
|
-
}
|
|
3491
|
+
const validation = validateLocalePipeline(normalized, currentSchema, policy, validateLocale);
|
|
3492
|
+
if (!validation.valid) return { success: false, error: workspaceLocaleValidationMessage(validation) };
|
|
3454
3493
|
commit({
|
|
3455
3494
|
...currentSchema,
|
|
3456
3495
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
@@ -3458,22 +3497,15 @@ function useTranslationWorkspace({
|
|
|
3458
3497
|
setSelectedLocale(normalized);
|
|
3459
3498
|
return { success: true };
|
|
3460
3499
|
},
|
|
3461
|
-
[commit, currentSchema, policy, readOnly,
|
|
3500
|
+
[commit, currentSchema, policy, readOnly, validateLocale]
|
|
3462
3501
|
);
|
|
3463
3502
|
const isAddLocaleAllowed = (0, import_react4.useCallback)(
|
|
3464
3503
|
(locale) => {
|
|
3465
3504
|
if (readOnly) return false;
|
|
3466
3505
|
const normalized = locale.trim();
|
|
3467
|
-
|
|
3468
|
-
.../* @__PURE__ */ new Set([
|
|
3469
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3470
|
-
sourceLocale,
|
|
3471
|
-
...(0, import_core4.collectSchemaLocales)(currentSchema).allUniqueLocales
|
|
3472
|
-
])
|
|
3473
|
-
];
|
|
3474
|
-
return (validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy)).valid;
|
|
3506
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale).valid;
|
|
3475
3507
|
},
|
|
3476
|
-
[currentSchema, policy, readOnly,
|
|
3508
|
+
[currentSchema, policy, readOnly, validateLocale]
|
|
3477
3509
|
);
|
|
3478
3510
|
const removeLocale = (0, import_react4.useCallback)(
|
|
3479
3511
|
(locale) => {
|
|
@@ -4846,5 +4878,6 @@ function FormRenderer(props) {
|
|
|
4846
4878
|
useForm,
|
|
4847
4879
|
useFormBuilder,
|
|
4848
4880
|
useSubmissionReceipts,
|
|
4849
|
-
useTranslationWorkspace
|
|
4881
|
+
useTranslationWorkspace,
|
|
4882
|
+
validateLocalePipeline
|
|
4850
4883
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -708,12 +708,19 @@ interface UseTranslationWorkspaceOptions {
|
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
710
|
readonly policy?: FormPolicy;
|
|
711
|
-
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
711
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
712
712
|
}
|
|
713
|
+
interface LocaleValidationContext {
|
|
714
|
+
readonly locale: string;
|
|
715
|
+
readonly defaultLocale: string;
|
|
716
|
+
readonly currentLocales: readonly string[];
|
|
717
|
+
readonly policy?: FormPolicy;
|
|
718
|
+
}
|
|
719
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
713
720
|
interface LocaleValidationResult {
|
|
714
721
|
readonly valid: boolean;
|
|
715
722
|
readonly error?: {
|
|
716
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
723
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
717
724
|
readonly message: string;
|
|
718
725
|
};
|
|
719
726
|
}
|
|
@@ -744,6 +751,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
744
751
|
readonly isTranslating: boolean;
|
|
745
752
|
readonly error?: string;
|
|
746
753
|
}
|
|
754
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator) => LocaleValidationResult;
|
|
747
755
|
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
748
756
|
|
|
749
757
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
@@ -818,4 +826,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
818
826
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
819
827
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
820
828
|
|
|
821
|
-
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace };
|
|
829
|
+
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type CustomLocaleValidator, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationContext, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace, validateLocalePipeline };
|
package/dist/index.d.ts
CHANGED
|
@@ -708,12 +708,19 @@ interface UseTranslationWorkspaceOptions {
|
|
|
708
708
|
readonly translationAdapter?: TranslationAdapter | AsyncTranslationAdapter;
|
|
709
709
|
readonly readOnly?: boolean;
|
|
710
710
|
readonly policy?: FormPolicy;
|
|
711
|
-
readonly validateLocale?: (locale: string, currentLocales: readonly string[]) => LocaleValidationResult;
|
|
711
|
+
readonly validateLocale?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator;
|
|
712
712
|
}
|
|
713
|
+
interface LocaleValidationContext {
|
|
714
|
+
readonly locale: string;
|
|
715
|
+
readonly defaultLocale: string;
|
|
716
|
+
readonly currentLocales: readonly string[];
|
|
717
|
+
readonly policy?: FormPolicy;
|
|
718
|
+
}
|
|
719
|
+
type CustomLocaleValidator = (locale: string, context: LocaleValidationContext) => LocaleValidationResult | boolean | string | undefined | void;
|
|
713
720
|
interface LocaleValidationResult {
|
|
714
721
|
readonly valid: boolean;
|
|
715
722
|
readonly error?: {
|
|
716
|
-
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format";
|
|
723
|
+
readonly type: "locale_not_allowed" | "max_locales_exceeded" | "invalid_locale_format" | "locale_already_exists" | "custom_validation_failed";
|
|
717
724
|
readonly message: string;
|
|
718
725
|
};
|
|
719
726
|
}
|
|
@@ -744,6 +751,7 @@ interface UseTranslationWorkspaceResult {
|
|
|
744
751
|
readonly isTranslating: boolean;
|
|
745
752
|
readonly error?: string;
|
|
746
753
|
}
|
|
754
|
+
declare const validateLocalePipeline: (locale: string, schema: FormSchema, policy?: FormPolicy, customValidator?: ((locale: string, currentLocales: readonly string[]) => LocaleValidationResult) | CustomLocaleValidator) => LocaleValidationResult;
|
|
747
755
|
declare function useTranslationWorkspace({ schema, onChange, sourceLocale, targetLocale, translationAdapter, readOnly, policy, validateLocale }: UseTranslationWorkspaceOptions): UseTranslationWorkspaceResult;
|
|
748
756
|
|
|
749
757
|
declare const BUILDER_TRANSLATION_KEYS: {
|
|
@@ -818,4 +826,4 @@ interface StandaloneFormRendererProps extends FormRendererPresentationProps {
|
|
|
818
826
|
type FormRendererProps = FormRendererPresentationProps | StandaloneFormRendererProps;
|
|
819
827
|
declare function FormRenderer(props: FormRendererProps): react.JSX.Element;
|
|
820
828
|
|
|
821
|
-
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace };
|
|
829
|
+
export { BUILDER_TRANSLATION_ALIASES, BUILDER_TRANSLATION_KEYS, type BeforeSubmit, type BuilderActionContext, type BuilderActionError, type BuilderActionIconType, type BuilderActionResult, type BuilderButtonProps, type BuilderCheckboxProps, type BuilderErrorMessageProps, type BuilderFactories, type BuilderFieldEditorSlotProps, type BuilderFieldsetProps, type BuilderIconButtonProps, type BuilderIdKind, type BuilderLocalizationSlotProps, type BuilderOptionEditorSlotProps, type BuilderPagesSlotProps, type BuilderPolicy, type BuilderSectionProps, type BuilderSelectOption, type BuilderSelectProps, type BuilderSlotActions, type BuilderTextAreaProps, type BuilderTextInputProps, type BuilderTextTarget, type BuilderToolbarSlotProps, type BuilderTranslationActionsSlotProps, type BuilderTranslationKey, type ChoiceFieldLayoutMode, type ChoiceFieldTypeLayoutMap, type ChoiceGroupSlotProps, type ComponentBaseProps, type CustomLocaleValidator, type FieldComponentProps, type FieldComponents, type FieldEditorControlsConfig, type FieldEditorHeaderSlotProps, type FieldEditorMode, type FieldError, type FieldPropertyControlMode, type FieldState, type FieldTypeSelectOptionsConfig, type FieldTypeSelectOptionsContext, type FieldTypeSelectOptionsSorter, type FieldTypeSelectOptionsTransformer, type FieldTypeSelectSlotProps, FormBuilder, type FormBuilderActions, type FormBuilderComponents, type FormBuilderFeatures, type FormBuilderOptions, type FormBuilderProps, type FormBuilderResult, type FormBuilderSectionName, type FormBuilderSlots, type FormBuilderSubmissionSettingsOptions, type FormCompletionSlotProps, type FormContextValue, type FormFieldsSlotProps, FormProvider, type FormProviderProps, FormRenderer, type FormRendererAppearance, type FormRendererMessages, type FormRendererPresentationProps, type FormRendererProps, type FormRendererSlotProps, type FormRendererSlots, type FormServerErrorPayload, FormSubmissionError, type FormSubmitHandler, type FormSubmitState, type FormSubmitStatus, type FormSubmittedAnswerItem, type FormSuccessRenderMode, type IconButtonProps, type InputComponentProps, type LocaleValidationContext, type LocaleValidationResult, type LocalizationSummaryContext, type ManualTranslationContext, type ManualTranslationTarget, type RenderSubmitButtonProps, type SelectComponentProps, type StandaloneFormRendererProps, type SubmissionAttempt, type SubmissionAttemptStore, type SubmissionConfirmationOptions, type SubmissionConfirmationRenderMode, type SubmissionConfirmationSlotProps, type SubmissionGuard, type SubmissionGuardResult, type SubmissionProtectionProps, type SubmissionReceipt, type SubmissionReceiptQuery, type SubmissionReceiptStore, type SubmitContext, type SubmitResponse, type SubmitResult, type SubmitStatus, type TranslationSummary, type UseFormBuilderOptions, type UseFormBuilderResult, type UseSubmissionReceiptsResult, type UseTranslationWorkspaceOptions, type UseTranslationWorkspaceResult, createLocalStorageSubmissionAttemptStore, createLocalStorageSubmissionReceiptStore, isTranslationUnresolved, resolveChoiceFieldLayout, resolveFieldEditorControls, resolveFieldTypeSelectOptions, resolveInitialFieldType, resolveTranslation, submissionReceiptQueryKey, useField, useForm, useFormBuilder, useSubmissionReceipts, useTranslationWorkspace, validateLocalePipeline };
|
package/dist/index.js
CHANGED
|
@@ -3257,37 +3257,87 @@ function asAsyncAdapter(adapter) {
|
|
|
3257
3257
|
)
|
|
3258
3258
|
};
|
|
3259
3259
|
}
|
|
3260
|
-
function
|
|
3261
|
-
|
|
3260
|
+
function isValidBcp47Locale(locale) {
|
|
3261
|
+
const language = locale.split("-")[0];
|
|
3262
|
+
if (language === void 0 || !/^[a-z]{2,3}$/iu.test(language)) return false;
|
|
3263
|
+
try {
|
|
3264
|
+
Intl.getCanonicalLocales(locale);
|
|
3265
|
+
return true;
|
|
3266
|
+
} catch {
|
|
3267
|
+
return false;
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
var validateLocalePipeline = (locale, schema, policy, customValidator) => {
|
|
3271
|
+
const currentLocales = schema.supportedLocales ?? [];
|
|
3272
|
+
const defaultLocale = schema.defaultLocale ?? "";
|
|
3273
|
+
if (!isValidBcp47Locale(locale)) {
|
|
3262
3274
|
return {
|
|
3263
3275
|
valid: false,
|
|
3264
|
-
error: {
|
|
3276
|
+
error: {
|
|
3277
|
+
type: "invalid_locale_format",
|
|
3278
|
+
message: `Invalid BCP 47 locale format: "${locale}"`
|
|
3279
|
+
}
|
|
3265
3280
|
};
|
|
3266
3281
|
}
|
|
3267
|
-
|
|
3268
|
-
Intl.getCanonicalLocales(locale);
|
|
3269
|
-
} catch {
|
|
3282
|
+
if (locale === defaultLocale || currentLocales.includes(locale)) {
|
|
3270
3283
|
return {
|
|
3271
3284
|
valid: false,
|
|
3272
|
-
error: {
|
|
3285
|
+
error: {
|
|
3286
|
+
type: "locale_already_exists",
|
|
3287
|
+
message: `Locale "${locale}" is already registered.`
|
|
3288
|
+
}
|
|
3273
3289
|
};
|
|
3274
3290
|
}
|
|
3275
3291
|
if (policy?.allowedLocales !== void 0 && !policy.allowedLocales.includes(locale)) {
|
|
3276
3292
|
return {
|
|
3277
3293
|
valid: false,
|
|
3278
|
-
error: {
|
|
3294
|
+
error: {
|
|
3295
|
+
type: "locale_not_allowed",
|
|
3296
|
+
message: `Locale "${locale}" is not allowed by policy.`
|
|
3297
|
+
}
|
|
3279
3298
|
};
|
|
3280
3299
|
}
|
|
3281
|
-
|
|
3300
|
+
const totalLocalesCount = 1 + currentLocales.length;
|
|
3301
|
+
if (policy?.maxLocales !== void 0 && totalLocalesCount >= policy.maxLocales) {
|
|
3282
3302
|
return {
|
|
3283
3303
|
valid: false,
|
|
3284
3304
|
error: {
|
|
3285
3305
|
type: "max_locales_exceeded",
|
|
3286
|
-
message: `
|
|
3306
|
+
message: `Cannot add locale: maximum allowed locales limit (${policy.maxLocales}) reached.`
|
|
3287
3307
|
}
|
|
3288
3308
|
};
|
|
3289
3309
|
}
|
|
3310
|
+
if (customValidator !== void 0) {
|
|
3311
|
+
const customContext = policy === void 0 ? Object.assign([...currentLocales], { locale, defaultLocale, currentLocales }) : Object.assign([...currentLocales], { locale, defaultLocale, currentLocales, policy });
|
|
3312
|
+
const customResult = customValidator(locale, customContext);
|
|
3313
|
+
if (customResult === false) {
|
|
3314
|
+
return {
|
|
3315
|
+
valid: false,
|
|
3316
|
+
error: {
|
|
3317
|
+
type: "custom_validation_failed",
|
|
3318
|
+
message: `Custom validation rejected locale "${locale}".`
|
|
3319
|
+
}
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
if (typeof customResult === "string") {
|
|
3323
|
+
return {
|
|
3324
|
+
valid: false,
|
|
3325
|
+
error: { type: "custom_validation_failed", message: customResult }
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
if (typeof customResult === "object" && customResult !== null && !customResult.valid) return customResult;
|
|
3329
|
+
}
|
|
3290
3330
|
return { valid: true };
|
|
3331
|
+
};
|
|
3332
|
+
function workspaceLocaleValidationMessage(validation) {
|
|
3333
|
+
if (validation.error?.type === "locale_not_allowed") {
|
|
3334
|
+
return validation.error.message.replace(" is not allowed by policy.", " is not allowed by the form policy.");
|
|
3335
|
+
}
|
|
3336
|
+
if (validation.error?.type === "max_locales_exceeded") {
|
|
3337
|
+
const maximum = validation.error.message.match(/\((\d+)\)/u)?.[1];
|
|
3338
|
+
return maximum === void 0 ? validation.error.message : `At most ${maximum} locales are allowed by the form policy.`;
|
|
3339
|
+
}
|
|
3340
|
+
return validation.error?.message ?? "Locale is not valid.";
|
|
3291
3341
|
}
|
|
3292
3342
|
function manualMetadata(sourceText, sourceLocale) {
|
|
3293
3343
|
return {
|
|
@@ -3413,20 +3463,8 @@ function useTranslationWorkspace({
|
|
|
3413
3463
|
(locale) => {
|
|
3414
3464
|
if (readOnly) return { success: false, error: "Workspace is read-only." };
|
|
3415
3465
|
const normalized = locale.trim();
|
|
3416
|
-
const
|
|
3417
|
-
|
|
3418
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3419
|
-
sourceLocale,
|
|
3420
|
-
...collectSchemaLocales2(currentSchema).allUniqueLocales
|
|
3421
|
-
])
|
|
3422
|
-
];
|
|
3423
|
-
const validation = validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy);
|
|
3424
|
-
if (!validation.valid) return { success: false, error: validation.error?.message ?? "Locale is not valid." };
|
|
3425
|
-
const alreadyRegistered = normalized === sourceLocale || normalized === currentSchema.defaultLocale || (currentSchema.supportedLocales ?? []).includes(normalized);
|
|
3426
|
-
if (alreadyRegistered) {
|
|
3427
|
-
setSelectedLocale(normalized);
|
|
3428
|
-
return { success: true };
|
|
3429
|
-
}
|
|
3466
|
+
const validation = validateLocalePipeline(normalized, currentSchema, policy, validateLocale);
|
|
3467
|
+
if (!validation.valid) return { success: false, error: workspaceLocaleValidationMessage(validation) };
|
|
3430
3468
|
commit({
|
|
3431
3469
|
...currentSchema,
|
|
3432
3470
|
supportedLocales: [.../* @__PURE__ */ new Set([...currentSchema.supportedLocales ?? [], normalized])]
|
|
@@ -3434,22 +3472,15 @@ function useTranslationWorkspace({
|
|
|
3434
3472
|
setSelectedLocale(normalized);
|
|
3435
3473
|
return { success: true };
|
|
3436
3474
|
},
|
|
3437
|
-
[commit, currentSchema, policy, readOnly,
|
|
3475
|
+
[commit, currentSchema, policy, readOnly, validateLocale]
|
|
3438
3476
|
);
|
|
3439
3477
|
const isAddLocaleAllowed = useCallback3(
|
|
3440
3478
|
(locale) => {
|
|
3441
3479
|
if (readOnly) return false;
|
|
3442
3480
|
const normalized = locale.trim();
|
|
3443
|
-
|
|
3444
|
-
.../* @__PURE__ */ new Set([
|
|
3445
|
-
...currentSchema.defaultLocale === void 0 ? [] : [currentSchema.defaultLocale],
|
|
3446
|
-
sourceLocale,
|
|
3447
|
-
...collectSchemaLocales2(currentSchema).allUniqueLocales
|
|
3448
|
-
])
|
|
3449
|
-
];
|
|
3450
|
-
return (validateLocale?.(normalized, currentLocales) ?? validateLocaleByPolicy(normalized, currentLocales, policy)).valid;
|
|
3481
|
+
return validateLocalePipeline(normalized, currentSchema, policy, validateLocale).valid;
|
|
3451
3482
|
},
|
|
3452
|
-
[currentSchema, policy, readOnly,
|
|
3483
|
+
[currentSchema, policy, readOnly, validateLocale]
|
|
3453
3484
|
);
|
|
3454
3485
|
const removeLocale = useCallback3(
|
|
3455
3486
|
(locale) => {
|
|
@@ -4832,5 +4863,6 @@ export {
|
|
|
4832
4863
|
useForm,
|
|
4833
4864
|
useFormBuilder,
|
|
4834
4865
|
useSubmissionReceipts,
|
|
4835
|
-
useTranslationWorkspace
|
|
4866
|
+
useTranslationWorkspace,
|
|
4867
|
+
validateLocalePipeline
|
|
4836
4868
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@form-engine-ts/react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typescript"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@form-engine-ts/core": "4.
|
|
46
|
-
"@form-engine-ts/privacy": "4.
|
|
45
|
+
"@form-engine-ts/core": "4.7.0",
|
|
46
|
+
"@form-engine-ts/privacy": "4.7.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": ">=18.2 <20",
|