alepha 0.20.3 → 0.20.5
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/api/audits/index.d.ts.map +1 -1
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/jobs/index.d.ts +14 -14
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/organizations/index.d.ts.map +1 -1
- package/dist/api/parameters/index.d.ts +6 -1
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +20 -4
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.d.ts.map +1 -1
- package/dist/api/users/index.browser.js +6 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +5032 -134
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +58 -10
- package/dist/api/users/index.js.map +1 -1
- package/dist/bin/index.js +0 -0
- package/dist/bucket/index.d.ts +77 -107
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +148 -4
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +7 -1
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +26 -0
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +11 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +11 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/cli/config/index.d.ts +7 -5
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +2 -3
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +419 -12
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +22 -511
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +4 -8
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js +13 -15
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +10 -13
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +18 -15
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +10 -13
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +16 -13
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.d.ts +1 -1
- package/dist/core/index.browser.js +27 -3
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +6 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -3
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -3
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/datetime/index.d.ts +69 -10
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +135 -13
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/smtp/index.js +10636 -2
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.d.ts +8085 -4
- package/dist/fake/index.d.ts.map +1 -1
- package/dist/fake/index.js +33554 -3
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +30 -2
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +35 -12
- package/dist/lock/core/index.js.map +1 -1
- package/dist/mcp/index.d.ts +238 -31
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +198 -71
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +1 -1
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +4 -3
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +4877 -9
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +4 -3
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +608 -1
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/react/core/index.d.ts +102 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +65 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +6 -0
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +7 -7
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +7 -1
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +6 -0
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/router/index.browser.js +20 -2
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +36 -4
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +20 -2
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/chunk-6Ep1yQYe.js +16 -0
- package/dist/react/testing/index.d.ts +411 -1
- package/dist/react/testing/index.d.ts.map +1 -1
- package/dist/react/testing/index.js +12293 -13
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +195 -1
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js +61 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +84 -3
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +390 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +390 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.d.ts +325 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1361 -2
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +1054 -1
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +1223 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/core/index.browser.js +10 -3
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +28 -5
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/metrics/index.d.ts +514 -1
- package/dist/server/metrics/index.d.ts.map +1 -1
- package/dist/server/metrics/index.js +4374 -4
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +3 -4
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/websocket/index.browser.js +11 -5
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +3 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +21 -6
- package/dist/websocket/index.js.map +1 -1
- package/package.json +416 -8
- package/src/api/parameters/services/ParameterProvider.ts +21 -4
- package/src/api/users/__tests__/SessionService.spec.ts +99 -0
- package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/jobs/UserJobs.ts +44 -17
- package/src/api/users/providers/RealmProvider.ts +4 -0
- package/src/api/users/services/SessionService.ts +27 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
- package/src/bucket/index.ts +19 -2
- package/src/bucket/primitives/$bucket.ts +9 -1
- package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
- package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
- package/src/cache/core/index.ts +29 -0
- package/src/cache/core/primitives/$cache.ts +14 -1
- package/src/cli/config/defineConfig.ts +13 -15
- package/src/cli/core/__tests__/init.spec.ts +6 -7
- package/src/cli/core/services/ProjectScaffolder.ts +18 -14
- package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
- package/src/cli/core/templates/agentMd.ts +2 -10
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +3 -3
- package/src/cli/devtools/index.ts +12 -26
- package/src/cli/platform/index.ts +15 -24
- package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
- package/src/cli/vendor/index.ts +14 -23
- package/src/core/Alepha.ts +11 -1
- package/src/core/helpers/ref.ts +18 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/providers/SchemaValidator.ts +9 -1
- package/src/core/providers/TypeProvider.ts +1 -2
- package/src/datetime/REFACTORING.md +118 -0
- package/src/datetime/providers/DateTimeProvider.ts +203 -24
- package/src/lock/core/index.ts +31 -0
- package/src/lock/core/primitives/$lock.ts +14 -1
- package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
- package/src/mcp/helpers/jsonrpc.ts +26 -1
- package/src/mcp/index.ts +10 -5
- package/src/mcp/interfaces/McpTypes.ts +83 -6
- package/src/mcp/primitives/$prompt.ts +18 -1
- package/src/mcp/primitives/$resource.ts +18 -1
- package/src/mcp/primitives/$tool.ts +83 -7
- package/src/mcp/providers/McpServerProvider.ts +74 -16
- package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
- package/src/orm/REFACTORING.md +330 -0
- package/src/orm/core/primitives/$transactional.ts +11 -0
- package/src/orm/core/schemas/updateSchema.ts +1 -1
- package/src/orm/core/services/PgRelationManager.ts +4 -2
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
- package/src/react/core/hooks/useQuery.ts +153 -0
- package/src/react/core/index.ts +1 -0
- package/src/react/form/services/FormModel.ts +15 -6
- package/src/react/form/services/parseField.ts +8 -0
- package/src/react/i18n/providers/I18nProvider.ts +8 -2
- package/src/react/router/__tests__/$page.spec.tsx +0 -16
- package/src/react/router/__tests__/ssr.spec.tsx +339 -0
- package/src/react/router/primitives/$page.ts +28 -4
- package/src/react/router/providers/ReactPageProvider.ts +27 -9
- package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
- package/src/react/ui/index.ts +6 -0
- package/src/react/ui/services/SchemaControl.ts +209 -0
- package/src/security/primitives/$issuer.ts +6 -3
- package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
- package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
- package/src/server/core/errors/ValidationError.ts +13 -1
- package/src/server/core/primitives/$action.ts +16 -5
- package/src/server/core/providers/ServerRouterProvider.ts +26 -4
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -7
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
- package/src/websocket/services/WebSocketClient.ts +11 -5
- package/src/mcp/transports/SseMcpTransport.ts +0 -182
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/services/FormModel.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/index.ts"],"sourcesContent":["import { type TObject, TypeBoxError } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormStateReturn {\n loading: boolean;\n dirty: boolean;\n values?: Record<string, any>;\n error?: Error;\n}\n\nexport const useFormState = <\n T extends TObject,\n Keys extends keyof UseFormStateReturn,\n>(\n target: FormModel<T> | { form: FormModel<T>; path: string },\n _events: Keys[] = [\"loading\", \"dirty\", \"error\"] as Keys[],\n): Pick<UseFormStateReturn, Keys> => {\n const alepha = useAlepha();\n const events = _events as string[];\n\n const [dirty, setDirty] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [values, setValues] = useState<Record<string, any> | undefined>(\n undefined,\n );\n\n const form = \"form\" in target ? target.form : target;\n const path = \"path\" in target ? target.path : undefined;\n\n const hasValues = events.includes(\"values\");\n const hasErrors = events.includes(\"error\");\n const hasDirty = events.includes(\"dirty\");\n const hasLoading = events.includes(\"loading\");\n\n useEffect(() => {\n const listeners: Function[] = [];\n\n if (hasErrors || hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:change\", (event) => {\n if (event.id === form.id) {\n if (!path || event.path === path) {\n if (hasDirty) {\n setDirty(true);\n }\n if (hasErrors) {\n setError(undefined);\n }\n }\n if (hasValues) {\n setValues(form.currentValues);\n }\n }\n }),\n );\n }\n\n if (hasLoading) {\n listeners.push(\n alepha.events.on(\"form:submit:begin\", (event) => {\n if (event.id === form.id) {\n setLoading(true);\n }\n }),\n alepha.events.on(\"form:submit:end\", (event) => {\n if (event.id === form.id) {\n setLoading(false);\n }\n }),\n );\n }\n\n if (hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:submit:success\", (event) => {\n if (event.id === form.id) {\n if (hasValues) {\n setValues(event.values);\n }\n if (hasDirty) {\n setDirty(false);\n }\n }\n }),\n );\n }\n\n if (hasDirty) {\n listeners.push(\n alepha.events.on(\"form:reset\", (event) => {\n if (event.id === form.id) {\n setDirty(false);\n }\n }),\n );\n }\n\n if (hasErrors) {\n listeners.push(\n alepha.events.on(\"form:submit:error\", (event) => {\n if (event.id === form.id) {\n if (\n !path ||\n (event.error instanceof TypeBoxError &&\n event.error.value.path === path)\n ) {\n setError(event.error);\n }\n }\n }),\n );\n }\n\n return () => {\n for (const unsub of listeners) {\n unsub();\n }\n };\n }, []);\n\n return {\n dirty,\n loading,\n error,\n values,\n } as Pick<UseFormStateReturn, Keys>;\n};\n","import type { TObject } from \"alepha\";\nimport type { ReactNode } from \"react\";\nimport { useFormState } from \"../hooks/useFormState.ts\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nconst FormState = <T extends TObject>(props: {\n form: FormModel<T>;\n children: (state: { loading: boolean; dirty: boolean }) => ReactNode;\n}) => {\n const formState = useFormState(props.form);\n return props.children({\n loading: formState.loading,\n dirty: formState.dirty,\n });\n};\n\nexport default FormState;\n","import { TypeBoxError } from \"alepha\";\n\nexport class FormValidationError extends TypeBoxError {\n readonly name = \"ValidationError\";\n\n constructor(options: {\n message: string;\n path: string;\n }) {\n super({\n message: options.message,\n instancePath: options.path,\n schemaPath: \"\",\n keyword: \"not\",\n params: {},\n });\n }\n}\n","import { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { BaseInputField } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to a single form field's value.\n * Only re-renders when this specific field changes.\n *\n * @returns A tuple of [value, setValue] similar to useState.\n */\nexport const useFieldValue = (\n input: BaseInputField,\n): [any, (value: any) => void] => {\n const alepha = useAlepha();\n const [value, setValue] = useState(input?.initialValue);\n\n useEffect(() => {\n if (!input?.form || !alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === input.form.id && ev.path === input.path) {\n setValue(ev.value);\n }\n });\n }, []);\n\n const setFieldValue = (newValue: any) => {\n input.set(newValue);\n };\n\n return [value, setFieldValue];\n};\n","import type { TArray } from \"alepha\";\nimport {\n $inject,\n Alepha,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { InputHTMLAttributes } from \"react\";\n\n/**\n * FormModel is a dynamic form handler that generates form inputs based on a provided TypeBox schema.\n * It manages form state, handles input changes, and processes form submissions with validation.\n *\n * It means to be injected and used within React components to provide a structured way to create and manage forms.\n *\n * @see {@link useForm}\n */\nexport class FormModel<T extends TObject> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly values: Record<string, any> = {};\n protected readonly initialValues: Record<string, any> = {};\n protected submitInProgress = false;\n\n public input: SchemaToInput<T>;\n\n public get submitting(): boolean {\n return this.submitInProgress;\n }\n\n constructor(\n public readonly id: string,\n public readonly options: FormCtrlOptions<T>,\n ) {\n this.options = options;\n\n // Initialize with schema defaults first, then override with initialValues\n const schemaDefaults = this.extractSchemaDefaults(options.schema);\n if (Object.keys(schemaDefaults).length > 0) {\n Object.assign(this.values, schemaDefaults);\n }\n\n if (options.initialValues) {\n const decoded = this.alepha.codec.decode(\n options.schema,\n options.initialValues,\n ) as Record<string, any>;\n Object.assign(this.values, decoded);\n }\n\n this.initialValues = { ...this.values };\n\n this.input = this.createProxyFromSchema(options, options.schema, {\n store: this.values,\n parent: \"\",\n });\n }\n\n /**\n * Extract default values from a TypeBox schema.\n * Recursively handles nested objects.\n */\n protected extractSchemaDefaults(\n schema: TObject,\n prefix: string = \"\",\n ): Record<string, any> {\n const defaults: Record<string, any> = {};\n\n if (!schema.properties) {\n return defaults;\n }\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (\"default\" in propSchema && propSchema.default !== undefined) {\n defaults[fullKey] = propSchema.default;\n } else if (\n propSchema &&\n \"type\" in propSchema &&\n propSchema.type === \"object\" &&\n \"properties\" in propSchema\n ) {\n // Recursively extract defaults from nested objects\n const nestedDefaults = this.extractSchemaDefaults(\n propSchema as TObject,\n fullKey,\n );\n Object.assign(defaults, nestedDefaults);\n }\n }\n\n return defaults;\n }\n\n public get currentValues(): Record<string, any> {\n return this.restructureValues(this.values);\n }\n\n public get props() {\n return {\n id: this.id,\n noValidate: true,\n onSubmit: (ev?: FormEventLike) => {\n ev?.preventDefault?.();\n this.submit();\n },\n onReset: (event: FormEventLike) => this.reset(event),\n };\n }\n\n public readonly setInitialValues = (values: Record<string, any>) => {\n const decoded = this.alepha.codec.decode(\n this.options.schema,\n values,\n ) as Record<string, any>;\n\n for (const key in this.initialValues) {\n delete (this.initialValues as Record<string, any>)[key];\n }\n Object.assign(this.initialValues, decoded);\n\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n\n for (const [key, value] of Object.entries(this.values)) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value },\n { catch: true },\n );\n }\n };\n\n public readonly reset = (event?: FormEventLike) => {\n event?.preventDefault?.();\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n for (const [key, value] of Object.entries(this.values)) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value },\n { catch: true },\n );\n }\n this.alepha.events.emit(\"form:reset\", { id: this.id }, { catch: true });\n this.options.onReset?.();\n };\n\n public readonly submit = async () => {\n if (this.submitInProgress) {\n this.log.warn(\n \"Form submission already in progress, ignoring duplicate submit.\",\n );\n return;\n }\n\n // emit both action and form events\n await this.alepha.events.emit(\"react:action:begin\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:begin\", {\n id: this.id,\n });\n\n this.submitInProgress = true;\n\n const options = this.options;\n\n try {\n let values: Record<string, any> = this.restructureValues(this.values);\n\n if (t.schema.isSchema(options.schema)) {\n values = this.alepha.codec.decode(options.schema, values) as Record<\n string,\n any\n >;\n }\n\n await options.handler(values as any);\n\n await this.alepha.events.emit(\"react:action:success\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:success\", {\n id: this.id,\n values,\n });\n } catch (error) {\n this.log.error(\"Form submission error:\", error);\n\n options.onError?.(error as Error);\n\n await this.alepha.events.emit(\"react:action:error\", {\n type: \"form\",\n id: this.id,\n error: error as Error,\n });\n await this.alepha.events.emit(\"form:submit:error\", {\n error: error as Error,\n id: this.id,\n });\n } finally {\n this.submitInProgress = false;\n }\n\n await this.alepha.events.emit(\"react:action:end\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:end\", {\n id: this.id,\n });\n };\n\n /**\n * Restructures flat keys like \"address.city\" into nested objects like { address: { city: ... } }\n * Values are already typed from onChange, so no conversion is needed.\n */\n protected restructureValues(store: Record<string, any>): Record<string, any> {\n const values: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(store)) {\n if (key.includes(\".\")) {\n // nested object: restructure flat key to nested structure\n this.restructureNestedValue(values, key, value);\n } else {\n // value is already typed, just copy it\n values[key] = value;\n }\n }\n\n return values;\n }\n\n /**\n * Helper to restructure a flat key like \"address.city\" into nested object structure.\n * The value is already typed, so we just assign it to the nested path.\n */\n protected restructureNestedValue(\n values: Record<string, any>,\n key: string,\n value: any,\n ) {\n const pathSegments = key.split(\".\");\n const finalPropertyKey = pathSegments.pop();\n if (!finalPropertyKey) {\n return;\n }\n\n let currentObjectLevel = values;\n\n // traverse/create the nested structure\n for (const segment of pathSegments) {\n currentObjectLevel[segment] ??= {};\n currentObjectLevel = currentObjectLevel[segment];\n }\n\n // value is already typed from onChange, just assign it\n currentObjectLevel[finalPropertyKey] = value;\n }\n\n protected createProxyFromSchema<T extends TObject>(\n options: FormCtrlOptions<T>,\n schema: TSchema,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): SchemaToInput<T> {\n const parent = context.parent || \"\";\n return new Proxy<SchemaToInput<T>>({} as SchemaToInput<T>, {\n get: (_, prop: string) => {\n if (!options.schema || !t.schema.isObject(schema)) {\n return {};\n }\n\n if (prop in schema.properties) {\n // // it's a nested object, create another proxy\n // if (t.schema.isObject(schema.properties[prop])) {\n // return this.createProxyFromSchema(\n // options,\n // schema.properties[prop],\n // {\n // parent: parent ? `${parent}.${prop}` : prop,\n // store: context.store,\n // },\n // );\n // }\n\n return this.createInputFromSchema<T>(\n prop as keyof Static<T> & string,\n options,\n schema,\n schema.required?.includes(prop as string) || false,\n context,\n );\n }\n },\n });\n }\n\n protected createInputFromSchema<T extends TObject>(\n name: keyof Static<T> & string,\n options: FormCtrlOptions<T>,\n schema: TObject,\n required: boolean,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): BaseInputField {\n const parent = context.parent || \"\";\n const field = schema.properties?.[name];\n if (!field) {\n return {\n path: \"\",\n required,\n initialValue: undefined,\n props: {} as InputHTMLAttributes<unknown>,\n schema: schema,\n set: () => {},\n form: this,\n };\n }\n\n const isRequired = schema.required?.includes(name) ?? false;\n const key = parent ? `${parent}.${name}` : name;\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n\n const set = (value: any) => {\n const typedValue = this.getValueFromInput(value, field);\n context.store[key] = typedValue;\n if (options.onChange) {\n options.onChange(key, typedValue, context.store);\n }\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path: path, value: typedValue },\n { catch: true },\n );\n };\n\n const attr: InputHTMLAttributesLike = {\n name: key,\n };\n\n if (options.id) {\n attr.id = `${options.id}-${key}`;\n (attr as any)[\"data-testid\"] = attr.id;\n }\n\n if (t.schema.isString(field)) {\n if (field.maxLength != null) {\n attr.maxLength = Number(field.maxLength);\n }\n\n if (field.minLength != null) {\n attr.minLength = Number(field.minLength);\n }\n }\n\n if (isRequired) {\n attr.required = true;\n }\n\n if (\"description\" in field && typeof field.description === \"string\") {\n attr[\"aria-label\"] = field.description;\n }\n\n if (t.schema.isInteger(field) || t.schema.isNumber(field)) {\n attr.type = \"number\";\n } else if (name === \"password\") {\n attr.type = \"password\";\n } else if (name === \"email\") {\n attr.type = \"email\";\n } else if (name === \"url\") {\n attr.type = \"url\";\n } else if (t.schema.isString(field)) {\n if (field.format === \"binary\") {\n attr.type = \"file\";\n } else if (field.format === \"date\") {\n attr.type = \"date\";\n } else if (field.format === \"time\") {\n attr.type = \"time\";\n } else if (field.format === \"date-time\") {\n attr.type = \"datetime-local\";\n } else {\n attr.type = \"text\";\n }\n } else if (t.schema.isBoolean(field)) {\n attr.type = \"checkbox\";\n }\n\n if (options.onCreateField) {\n const customAttr = options.onCreateField(name, field);\n Object.assign(attr, customAttr);\n }\n\n // if type = object, add items: { [key: string]: InputField }\n if (t.schema.isObject(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: this.createProxyFromSchema(options, field, {\n parent: key,\n store: context.store,\n }),\n } as ObjectInputField<any>;\n }\n\n // if type = array, add items: InputField[]\n if (t.schema.isArray(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: [], // <- will be populated dynamically in the UI\n } as ArrayInputField<any>;\n }\n\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n };\n }\n\n /**\n * Convert an input value to the correct type based on the schema.\n * Handles raw DOM values (strings, booleans from checkboxes, Files, etc.)\n */\n protected getValueFromInput(input: any, schema: TSchema): any {\n if (input instanceof File) {\n // for file inputs, return the File object directly\n if (t.schema.isString(schema) && schema.format === \"binary\") {\n return input;\n }\n // for now, ignore other formats\n return null;\n }\n\n if (t.schema.isBoolean(schema)) {\n // Handle string representations from Select components (Yes/No dropdown)\n if (input === \"true\") return true;\n if (input === \"false\") return false;\n if (input === \"\" || input === null || input === undefined)\n return undefined;\n // Handle actual boolean values\n return !!input;\n }\n\n if (t.schema.isNumber(schema)) {\n const num = Number(input);\n return Number.isNaN(num) ? null : num;\n }\n\n if (t.schema.isString(schema)) {\n if (schema.format === \"date\") {\n return new Date(input).toISOString().slice(0, 10); // For date input\n }\n if (schema.format === \"time\") {\n return new Date(`1970-01-01T${input}`).toISOString().slice(11, 16); // For time input\n }\n if (schema.format === \"date-time\") {\n return new Date(input).toISOString(); // For datetime-local input\n }\n return String(input);\n }\n\n return input; // fallback for other types\n }\n}\n\nexport type SchemaToInput<T extends TObject> = {\n [K in keyof T[\"properties\"]]: InputField<T[\"properties\"][K]>;\n};\n\nexport interface FormEventLike {\n preventDefault?: () => void;\n stopPropagation?: () => void;\n}\n\nexport type InputField<T extends TSchema> = T extends TObject\n ? ObjectInputField<T>\n : T extends TArray<infer U>\n ? ArrayInputField<U>\n : BaseInputField;\n\nexport interface BaseInputField {\n path: string;\n required: boolean;\n initialValue: any;\n props: InputHTMLAttributesLike;\n schema: TSchema;\n set: (value: any) => void;\n form: FormModel<any>;\n items?: any;\n}\n\nexport interface ObjectInputField<T extends TObject> extends BaseInputField {\n items: SchemaToInput<T>;\n}\n\nexport interface ArrayInputField<T extends TSchema> extends BaseInputField {\n items: Array<InputField<T>>;\n}\n\nexport type InputHTMLAttributesLike = Pick<\n InputHTMLAttributes<unknown>,\n | \"id\"\n | \"name\"\n | \"type\"\n | \"value\"\n | \"required\"\n | \"maxLength\"\n | \"minLength\"\n | \"aria-label\"\n> & {\n value?: any;\n};\n\nexport type FormCtrlOptions<T extends TObject> = {\n /**\n * The schema defining the structure and validation rules for the form.\n * This should be a TypeBox schema object.\n */\n schema: T;\n\n /**\n * Callback function to handle form submission.\n * This function will receive the parsed and validated form values.\n */\n handler: (values: Static<T>) => unknown;\n\n /**\n * Optional initial values for the form fields.\n * This can be used to pre-populate the form with existing data.\n */\n initialValues?: Partial<Static<T>>;\n\n /**\n * Optional function to create custom field attributes.\n * This can be used to add custom validation, styles, or other attributes.\n */\n onCreateField?: (\n name: keyof Static<T> & string,\n schema: TSchema,\n ) => InputHTMLAttributes<unknown>;\n\n /**\n * If defined, this will generate a unique ID for each field, prefixed with this string.\n *\n * > \"username\" with id=\"form-123\" will become \"form-123-username\".\n *\n * If omitted, IDs will not be generated.\n */\n id?: string;\n\n onError?: (error: Error) => void;\n\n onChange?: (key: string, value: any, store: Record<string, any>) => void;\n\n onReset?: () => void;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\nimport { type FormCtrlOptions, FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Custom hook to create a form with validation and field management.\n * This hook uses TypeBox schemas to define the structure and validation rules for the form.\n * It provides a way to handle form submission, field creation, and value management.\n *\n * @example\n * ```tsx\n * import { t } from \"alepha\";\n *\n * const form = useForm({\n * schema: t.object({\n * username: t.text(),\n * password: t.text(),\n * }),\n * handler: (values) => {\n * console.log(\"Form submitted with values:\", values);\n * },\n * });\n *\n * return (\n * <form {...form.props}>\n * <input {...form.input.username.props} />\n * <input {...form.input.password.props} />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * ```\n */\nexport const useForm = <T extends TObject>(\n options: FormCtrlOptions<T>,\n deps: any[] = [],\n): FormModel<T> => {\n const alepha = useAlepha();\n const formId = useId();\n const initialValuesRef = useRef(options.initialValues);\n\n const form = useMemo(() => {\n return alepha.inject(FormModel<T>, {\n lifetime: \"transient\",\n args: [options.id || formId, options],\n });\n }, deps);\n\n useEffect(() => {\n if (initialValuesRef.current !== options.initialValues) {\n initialValuesRef.current = options.initialValues;\n if (options.initialValues) {\n form.setInitialValues(options.initialValues as Record<string, any>);\n }\n }\n }, [options.initialValues]);\n\n return form;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to all form values.\n * Re-renders on every field change — use only when needed (debug panels, live previews).\n */\nexport const useFormValues = <T extends TObject>(\n form: FormModel<T>,\n): Record<string, any> => {\n const alepha = useAlepha();\n const [values, setValues] = useState<Record<string, any>>(form.currentValues);\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === form.id) {\n setValues(form.currentValues);\n }\n });\n }, []);\n\n return values;\n};\n","/**\n * Converts a path or identifier string into a pretty display name.\n * For paths like \"/contacts/0/name\", extracts just the field name \"Name\".\n * Handles camelCase and snake_case conversion to Title Case.\n *\n * @example\n * prettyName(\"/userName\") // \"User Name\"\n * prettyName(\"/contacts/0/email\") // \"Email\"\n * prettyName(\"/address/streetName\") // \"Street Name\"\n * prettyName(\"first_name\") // \"First Name\"\n */\nexport const prettyName = (name: string): string => {\n const segments = name.split(\"/\").filter((s) => s && !/^\\d+$/.test(s));\n const fieldName = segments[segments.length - 1] || name.replaceAll(\"/\", \"\");\n return fieldName\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n};\n","import { type TSchema, TypeBoxError } from \"alepha\";\nimport type { BaseInputField } from \"./FormModel.ts\";\nimport { prettyName } from \"./prettyName.ts\";\n\n/**\n * Semantic icon hint derived from schema metadata. UI layers map this\n * to their own icon set — this module is headless and ships no JSX.\n */\nexport type IconHint =\n | \"email\"\n | \"password\"\n | \"phone\"\n | \"url\"\n | \"number\"\n | \"calendar\"\n | \"clock\"\n | \"list\"\n | \"text\"\n | \"user\"\n | \"file\"\n | \"switch\";\n\nexport interface FieldConstraints {\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n}\n\nexport interface FieldMeta {\n id?: string;\n label: string;\n description?: string;\n error?: string;\n required: boolean;\n type?: string;\n format?: string;\n isEnum: boolean;\n isArray: boolean;\n isObject: boolean;\n isArrayOfObjects: boolean;\n enum?: readonly unknown[];\n iconHint?: IconHint;\n constraints: FieldConstraints;\n testId?: string;\n schema: TSchema;\n}\n\nexport interface ParseFieldOptions {\n label?: string;\n description?: string;\n error?: Error;\n}\n\n/**\n * Derives a {@link FieldMeta} from an `InputField` (from `useForm`) plus\n * optional overrides. Pure — no React, no JSX, no UI library coupling.\n *\n * UI components consume this metadata to render labels, descriptions,\n * error messages, icons, and validation constraints.\n */\nexport const parseField = (\n input: BaseInputField,\n options: ParseFieldOptions = {},\n): FieldMeta => {\n const schema = input.schema as TSchema & {\n type?: string;\n format?: string;\n title?: string;\n description?: string;\n enum?: readonly unknown[];\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n properties?: unknown;\n items?: { properties?: unknown };\n };\n\n const label =\n options.label ??\n (typeof schema.title === \"string\" ? schema.title : undefined) ??\n prettyName(input.path);\n\n const description =\n options.description ??\n (typeof schema.description === \"string\" ? schema.description : undefined);\n\n const error =\n options.error instanceof TypeBoxError\n ? (options.error as TypeBoxError).value?.message\n : undefined;\n\n const type = schema.type;\n const format = typeof schema.format === \"string\" ? schema.format : undefined;\n const isEnum = Array.isArray(schema.enum);\n const isArray = type === \"array\";\n const isObject = type === \"object\" && Boolean(schema.properties);\n const isArrayOfObjects =\n isArray && Boolean(schema.items && (schema.items as any).properties);\n\n const name = input.props.name;\n const iconHint = inferIconHint({ type, format, name, isEnum, isArray });\n\n const constraints: FieldConstraints = {};\n if (typeof schema.minLength === \"number\")\n constraints.minLength = schema.minLength;\n if (typeof schema.maxLength === \"number\")\n constraints.maxLength = schema.maxLength;\n if (typeof schema.minimum === \"number\") constraints.minimum = schema.minimum;\n if (typeof schema.maximum === \"number\") constraints.maximum = schema.maximum;\n if (typeof schema.pattern === \"string\") constraints.pattern = schema.pattern;\n\n return {\n id: input.props.id,\n label,\n description,\n error,\n required: input.required,\n type,\n format,\n isEnum,\n isArray,\n isObject,\n isArrayOfObjects,\n enum: schema.enum,\n iconHint,\n constraints,\n testId: (input.props as Record<string, unknown>)[\"data-testid\"] as\n | string\n | undefined,\n schema: input.schema,\n };\n};\n\nconst inferIconHint = (params: {\n type?: string;\n format?: string;\n name?: string;\n isEnum: boolean;\n isArray: boolean;\n}): IconHint | undefined => {\n const { type, format, name, isEnum, isArray } = params;\n\n if (format === \"email\") return \"email\";\n if (format === \"url\" || format === \"uri\") return \"url\";\n if (format === \"tel\" || format === \"phone\") return \"phone\";\n if (format === \"date\" || format === \"date-time\") return \"calendar\";\n if (format === \"time\") return \"clock\";\n\n if (name?.toLowerCase().includes(\"password\")) return \"password\";\n if (name?.toLowerCase().includes(\"email\")) return \"email\";\n if (name?.toLowerCase().includes(\"phone\")) return \"phone\";\n\n if (type === \"boolean\") return \"switch\";\n if (type === \"number\" || type === \"integer\") return \"number\";\n if (isEnum || isArray) return \"list\";\n if (type === \"string\") return \"text\";\n\n return undefined;\n};\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport { default as FormState } from \"./components/FormState.tsx\";\nexport * from \"./errors/FormValidationError.ts\";\nexport * from \"./hooks/useFieldValue.ts\";\nexport * from \"./hooks/useForm.ts\";\nexport * from \"./hooks/useFormState.ts\";\nexport * from \"./hooks/useFormValues.ts\";\nexport * from \"./services/FormModel.ts\";\nexport * from \"./services/parseField.ts\";\nexport * from \"./services/prettyName.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"form:change\": { id: string; path: string; value: any };\n \"form:submit:begin\": { id: string };\n \"form:submit:success\": { id: string; values: Record<string, any> };\n \"form:submit:error\": { id: string; error: Error };\n \"form:submit:end\": { id: string };\n \"form:reset\": { id: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe forms with validation.\n *\n * **Features:**\n * - Form state management\n * - TypeBox schema validation\n * - Field-level error handling\n * - Submit handling with loading state\n * - Form reset\n *\n * @module alepha.react.form\n */\nexport const AlephaReactForm = $module({\n name: \"alepha.react.form\",\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/form/hooks/useFormState.ts","../../../src/react/form/components/FormState.tsx","../../../src/react/form/errors/FormValidationError.ts","../../../src/react/form/hooks/useFieldValue.ts","../../../src/react/form/services/FormModel.ts","../../../src/react/form/hooks/useForm.ts","../../../src/react/form/hooks/useFormValues.ts","../../../src/react/form/services/prettyName.ts","../../../src/react/form/services/parseField.ts","../../../src/react/form/index.ts"],"sourcesContent":["import { type TObject, TypeBoxError } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nexport interface UseFormStateReturn {\n loading: boolean;\n dirty: boolean;\n values?: Record<string, any>;\n error?: Error;\n}\n\nexport const useFormState = <\n T extends TObject,\n Keys extends keyof UseFormStateReturn,\n>(\n target: FormModel<T> | { form: FormModel<T>; path: string },\n _events: Keys[] = [\"loading\", \"dirty\", \"error\"] as Keys[],\n): Pick<UseFormStateReturn, Keys> => {\n const alepha = useAlepha();\n const events = _events as string[];\n\n const [dirty, setDirty] = useState(false);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>(undefined);\n const [values, setValues] = useState<Record<string, any> | undefined>(\n undefined,\n );\n\n const form = \"form\" in target ? target.form : target;\n const path = \"path\" in target ? target.path : undefined;\n\n const hasValues = events.includes(\"values\");\n const hasErrors = events.includes(\"error\");\n const hasDirty = events.includes(\"dirty\");\n const hasLoading = events.includes(\"loading\");\n\n useEffect(() => {\n const listeners: Function[] = [];\n\n if (hasErrors || hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:change\", (event) => {\n if (event.id === form.id) {\n if (!path || event.path === path) {\n if (hasDirty) {\n setDirty(true);\n }\n if (hasErrors) {\n setError(undefined);\n }\n }\n if (hasValues) {\n setValues(form.currentValues);\n }\n }\n }),\n );\n }\n\n if (hasLoading) {\n listeners.push(\n alepha.events.on(\"form:submit:begin\", (event) => {\n if (event.id === form.id) {\n setLoading(true);\n }\n }),\n alepha.events.on(\"form:submit:end\", (event) => {\n if (event.id === form.id) {\n setLoading(false);\n }\n }),\n );\n }\n\n if (hasValues || hasDirty) {\n listeners.push(\n alepha.events.on(\"form:submit:success\", (event) => {\n if (event.id === form.id) {\n if (hasValues) {\n setValues(event.values);\n }\n if (hasDirty) {\n setDirty(false);\n }\n }\n }),\n );\n }\n\n if (hasDirty) {\n listeners.push(\n alepha.events.on(\"form:reset\", (event) => {\n if (event.id === form.id) {\n setDirty(false);\n }\n }),\n );\n }\n\n if (hasErrors) {\n listeners.push(\n alepha.events.on(\"form:submit:error\", (event) => {\n if (event.id === form.id) {\n if (\n !path ||\n (event.error instanceof TypeBoxError &&\n event.error.value.path === path)\n ) {\n setError(event.error);\n }\n }\n }),\n );\n }\n\n return () => {\n for (const unsub of listeners) {\n unsub();\n }\n };\n }, []);\n\n return {\n dirty,\n loading,\n error,\n values,\n } as Pick<UseFormStateReturn, Keys>;\n};\n","import type { TObject } from \"alepha\";\nimport type { ReactNode } from \"react\";\nimport { useFormState } from \"../hooks/useFormState.ts\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\nconst FormState = <T extends TObject>(props: {\n form: FormModel<T>;\n children: (state: { loading: boolean; dirty: boolean }) => ReactNode;\n}) => {\n const formState = useFormState(props.form);\n return props.children({\n loading: formState.loading,\n dirty: formState.dirty,\n });\n};\n\nexport default FormState;\n","import { TypeBoxError } from \"alepha\";\n\nexport class FormValidationError extends TypeBoxError {\n readonly name = \"ValidationError\";\n\n constructor(options: {\n message: string;\n path: string;\n }) {\n super({\n message: options.message,\n instancePath: options.path,\n schemaPath: \"\",\n keyword: \"not\",\n params: {},\n });\n }\n}\n","import { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { BaseInputField } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to a single form field's value.\n * Only re-renders when this specific field changes.\n *\n * @returns A tuple of [value, setValue] similar to useState.\n */\nexport const useFieldValue = (\n input: BaseInputField,\n): [any, (value: any) => void] => {\n const alepha = useAlepha();\n const [value, setValue] = useState(input?.initialValue);\n\n useEffect(() => {\n if (!input?.form || !alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === input.form.id && ev.path === input.path) {\n setValue(ev.value);\n }\n });\n }, []);\n\n const setFieldValue = (newValue: any) => {\n input.set(newValue);\n };\n\n return [value, setFieldValue];\n};\n","import type { TArray } from \"alepha\";\nimport {\n $inject,\n Alepha,\n type Static,\n type TObject,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { InputHTMLAttributes } from \"react\";\n\n/**\n * FormModel is a dynamic form handler that generates form inputs based on a provided TypeBox schema.\n * It manages form state, handles input changes, and processes form submissions with validation.\n *\n * It means to be injected and used within React components to provide a structured way to create and manage forms.\n *\n * @see {@link useForm}\n */\nexport class FormModel<T extends TObject> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly values: Record<string, any> = {};\n protected readonly initialValues: Record<string, any> = {};\n protected submitInProgress = false;\n\n public input: SchemaToInput<T>;\n\n public get submitting(): boolean {\n return this.submitInProgress;\n }\n\n constructor(\n public readonly id: string,\n public readonly options: FormCtrlOptions<T>,\n ) {\n this.options = options;\n\n // Initialize with schema defaults first, then override with initialValues\n const schemaDefaults = this.extractSchemaDefaults(options.schema);\n if (Object.keys(schemaDefaults).length > 0) {\n Object.assign(this.values, schemaDefaults);\n }\n\n if (options.initialValues) {\n const decoded = this.alepha.codec.decode(\n options.schema,\n options.initialValues,\n ) as Record<string, any>;\n Object.assign(this.values, decoded);\n }\n\n this.initialValues = { ...this.values };\n\n this.input = this.createProxyFromSchema(options, options.schema, {\n store: this.values,\n parent: \"\",\n });\n }\n\n /**\n * Extract default values from a TypeBox schema.\n * Recursively handles nested objects.\n */\n protected extractSchemaDefaults(\n schema: TObject,\n prefix: string = \"\",\n ): Record<string, any> {\n const defaults: Record<string, any> = {};\n\n if (!schema.properties) {\n return defaults;\n }\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n\n if (\"default\" in propSchema && propSchema.default !== undefined) {\n defaults[fullKey] = propSchema.default;\n } else if (\n propSchema &&\n \"type\" in propSchema &&\n propSchema.type === \"object\" &&\n \"properties\" in propSchema\n ) {\n // Recursively extract defaults from nested objects\n const nestedDefaults = this.extractSchemaDefaults(\n propSchema as TObject,\n fullKey,\n );\n Object.assign(defaults, nestedDefaults);\n }\n }\n\n return defaults;\n }\n\n public get currentValues(): Record<string, any> {\n return this.restructureValues(this.values);\n }\n\n public get props() {\n return {\n id: this.id,\n noValidate: true,\n onSubmit: (ev?: FormEventLike) => {\n ev?.preventDefault?.();\n this.submit();\n },\n onReset: (event: FormEventLike) => this.reset(event),\n };\n }\n\n public readonly setInitialValues = (values: Record<string, any>) => {\n const decoded = this.alepha.codec.decode(\n this.options.schema,\n values,\n ) as Record<string, any>;\n\n for (const key in this.initialValues) {\n delete (this.initialValues as Record<string, any>)[key];\n }\n Object.assign(this.initialValues, decoded);\n\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n\n for (const [key, value] of Object.entries(this.values)) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value },\n { catch: true },\n );\n }\n };\n\n public readonly reset = (event?: FormEventLike) => {\n event?.preventDefault?.();\n // Snapshot all keys that need notification — both keys present\n // before reset (so subscribers learn the cleared value) and keys\n // restored from initialValues. Without the union, fields that were\n // typed but absent from initialValues stay visually stale.\n const keys = new Set<string>([\n ...Object.keys(this.values),\n ...Object.keys(this.initialValues),\n ]);\n for (const key in this.values) {\n delete this.values[key];\n }\n Object.assign(this.values, { ...this.initialValues });\n for (const key of keys) {\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path, value: this.values[key] },\n { catch: true },\n );\n }\n this.alepha.events.emit(\"form:reset\", { id: this.id }, { catch: true });\n this.options.onReset?.();\n };\n\n public readonly submit = async () => {\n if (this.submitInProgress) {\n this.log.warn(\n \"Form submission already in progress, ignoring duplicate submit.\",\n );\n return;\n }\n\n // emit both action and form events\n await this.alepha.events.emit(\"react:action:begin\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:begin\", {\n id: this.id,\n });\n\n this.submitInProgress = true;\n\n const options = this.options;\n\n try {\n let values: Record<string, any> = this.restructureValues(this.values);\n\n if (t.schema.isSchema(options.schema)) {\n values = this.alepha.codec.decode(options.schema, values) as Record<\n string,\n any\n >;\n }\n\n await options.handler(values as any);\n\n await this.alepha.events.emit(\"react:action:success\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:success\", {\n id: this.id,\n values,\n });\n } catch (error) {\n this.log.error(\"Form submission error:\", error);\n\n options.onError?.(error as Error);\n\n await this.alepha.events.emit(\"react:action:error\", {\n type: \"form\",\n id: this.id,\n error: error as Error,\n });\n await this.alepha.events.emit(\"form:submit:error\", {\n error: error as Error,\n id: this.id,\n });\n } finally {\n this.submitInProgress = false;\n }\n\n await this.alepha.events.emit(\"react:action:end\", {\n type: \"form\",\n id: this.id,\n });\n await this.alepha.events.emit(\"form:submit:end\", {\n id: this.id,\n });\n };\n\n /**\n * Restructures flat keys like \"address.city\" into nested objects like { address: { city: ... } }\n * Values are already typed from onChange, so no conversion is needed.\n */\n protected restructureValues(store: Record<string, any>): Record<string, any> {\n const values: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(store)) {\n if (key.includes(\".\")) {\n // nested object: restructure flat key to nested structure\n this.restructureNestedValue(values, key, value);\n } else {\n // value is already typed, just copy it\n values[key] = value;\n }\n }\n\n return values;\n }\n\n /**\n * Helper to restructure a flat key like \"address.city\" into nested object structure.\n * The value is already typed, so we just assign it to the nested path.\n */\n protected restructureNestedValue(\n values: Record<string, any>,\n key: string,\n value: any,\n ) {\n const pathSegments = key.split(\".\");\n const finalPropertyKey = pathSegments.pop();\n if (!finalPropertyKey) {\n return;\n }\n\n let currentObjectLevel = values;\n\n // traverse/create the nested structure\n for (const segment of pathSegments) {\n currentObjectLevel[segment] ??= {};\n currentObjectLevel = currentObjectLevel[segment];\n }\n\n // value is already typed from onChange, just assign it\n currentObjectLevel[finalPropertyKey] = value;\n }\n\n protected createProxyFromSchema<T extends TObject>(\n options: FormCtrlOptions<T>,\n schema: TSchema,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): SchemaToInput<T> {\n const parent = context.parent || \"\";\n return new Proxy<SchemaToInput<T>>({} as SchemaToInput<T>, {\n get: (_, prop: string) => {\n if (!options.schema || !t.schema.isObject(schema)) {\n return {};\n }\n\n if (prop in schema.properties) {\n // // it's a nested object, create another proxy\n // if (t.schema.isObject(schema.properties[prop])) {\n // return this.createProxyFromSchema(\n // options,\n // schema.properties[prop],\n // {\n // parent: parent ? `${parent}.${prop}` : prop,\n // store: context.store,\n // },\n // );\n // }\n\n return this.createInputFromSchema<T>(\n prop as keyof Static<T> & string,\n options,\n schema,\n schema.required?.includes(prop as string) || false,\n context,\n );\n }\n },\n });\n }\n\n protected createInputFromSchema<T extends TObject>(\n name: keyof Static<T> & string,\n options: FormCtrlOptions<T>,\n schema: TObject,\n required: boolean,\n context: {\n parent: string;\n store: Record<string, any>;\n },\n ): BaseInputField {\n const parent = context.parent || \"\";\n const field = schema.properties?.[name];\n if (!field) {\n return {\n path: \"\",\n required,\n initialValue: undefined,\n props: {} as InputHTMLAttributes<unknown>,\n schema: schema,\n set: () => {},\n form: this,\n };\n }\n\n const isRequired = schema.required?.includes(name) ?? false;\n const key = parent ? `${parent}.${name}` : name;\n const path = `/${key.replaceAll(\".\", \"/\")}`;\n\n const set = (value: any) => {\n const typedValue = this.getValueFromInput(value, field);\n context.store[key] = typedValue;\n if (options.onChange) {\n options.onChange(key, typedValue, context.store);\n }\n this.alepha.events.emit(\n \"form:change\",\n { id: this.id, path: path, value: typedValue },\n { catch: true },\n );\n };\n\n const attr: InputHTMLAttributesLike = {\n name: key,\n };\n\n // Use the form's runtime id (always set — comes from `useId()` when\n // no explicit `options.id` was provided). This guarantees stable\n // per-field DOM ids without forcing callers to pass `id`.\n attr.id = `${this.id}-${key}`;\n (attr as any)[\"data-testid\"] = attr.id;\n\n if (t.schema.isString(field)) {\n if (field.maxLength != null) {\n attr.maxLength = Number(field.maxLength);\n }\n\n if (field.minLength != null) {\n attr.minLength = Number(field.minLength);\n }\n }\n\n if (isRequired) {\n attr.required = true;\n }\n\n if (\"description\" in field && typeof field.description === \"string\") {\n attr[\"aria-label\"] = field.description;\n }\n\n if (t.schema.isInteger(field) || t.schema.isNumber(field)) {\n attr.type = \"number\";\n } else if (name === \"password\") {\n attr.type = \"password\";\n } else if (name === \"email\") {\n attr.type = \"email\";\n } else if (name === \"url\") {\n attr.type = \"url\";\n } else if (t.schema.isString(field)) {\n if (field.format === \"binary\") {\n attr.type = \"file\";\n } else if (field.format === \"date\") {\n attr.type = \"date\";\n } else if (field.format === \"time\") {\n attr.type = \"time\";\n } else if (field.format === \"date-time\") {\n attr.type = \"datetime-local\";\n } else {\n attr.type = \"text\";\n }\n } else if (t.schema.isBoolean(field)) {\n attr.type = \"checkbox\";\n }\n\n if (options.onCreateField) {\n const customAttr = options.onCreateField(name, field);\n Object.assign(attr, customAttr);\n }\n\n // if type = object, add items: { [key: string]: InputField }\n if (t.schema.isObject(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: this.createProxyFromSchema(options, field, {\n parent: key,\n store: context.store,\n }),\n } as ObjectInputField<any>;\n }\n\n // if type = array, add items: InputField[]\n if (t.schema.isArray(field)) {\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n items: [], // <- will be populated dynamically in the UI\n } as ArrayInputField<any>;\n }\n\n return {\n path,\n props: attr,\n schema: field,\n set,\n form: this,\n required,\n initialValue: context.store[key],\n };\n }\n\n /**\n * Convert an input value to the correct type based on the schema.\n * Handles raw DOM values (strings, booleans from checkboxes, Files, etc.)\n */\n protected getValueFromInput(input: any, schema: TSchema): any {\n if (input instanceof File) {\n // for file inputs, return the File object directly\n if (t.schema.isString(schema) && schema.format === \"binary\") {\n return input;\n }\n // for now, ignore other formats\n return null;\n }\n\n if (t.schema.isBoolean(schema)) {\n // Handle string representations from Select components (Yes/No dropdown)\n if (input === \"true\") return true;\n if (input === \"false\") return false;\n if (input === \"\" || input === null || input === undefined)\n return undefined;\n // Handle actual boolean values\n return !!input;\n }\n\n if (t.schema.isNumber(schema)) {\n const num = Number(input);\n return Number.isNaN(num) ? null : num;\n }\n\n if (t.schema.isString(schema)) {\n if (schema.format === \"date\") {\n return new Date(input).toISOString().slice(0, 10); // For date input\n }\n if (schema.format === \"time\") {\n return new Date(`1970-01-01T${input}`).toISOString().slice(11, 16); // For time input\n }\n if (schema.format === \"date-time\") {\n return new Date(input).toISOString(); // For datetime-local input\n }\n return String(input);\n }\n\n return input; // fallback for other types\n }\n}\n\nexport type SchemaToInput<T extends TObject> = {\n [K in keyof T[\"properties\"]]: InputField<T[\"properties\"][K]>;\n};\n\nexport interface FormEventLike {\n preventDefault?: () => void;\n stopPropagation?: () => void;\n}\n\nexport type InputField<T extends TSchema> = T extends TObject\n ? ObjectInputField<T>\n : T extends TArray<infer U>\n ? ArrayInputField<U>\n : BaseInputField;\n\nexport interface BaseInputField {\n path: string;\n required: boolean;\n initialValue: any;\n props: InputHTMLAttributesLike;\n schema: TSchema;\n set: (value: any) => void;\n form: FormModel<any>;\n items?: any;\n}\n\nexport interface ObjectInputField<T extends TObject> extends BaseInputField {\n items: SchemaToInput<T>;\n}\n\nexport interface ArrayInputField<T extends TSchema> extends BaseInputField {\n items: Array<InputField<T>>;\n}\n\nexport type InputHTMLAttributesLike = Pick<\n InputHTMLAttributes<unknown>,\n | \"id\"\n | \"name\"\n | \"type\"\n | \"value\"\n | \"required\"\n | \"maxLength\"\n | \"minLength\"\n | \"aria-label\"\n> & {\n value?: any;\n};\n\nexport type FormCtrlOptions<T extends TObject> = {\n /**\n * The schema defining the structure and validation rules for the form.\n * This should be a TypeBox schema object.\n */\n schema: T;\n\n /**\n * Callback function to handle form submission.\n * This function will receive the parsed and validated form values.\n */\n handler: (values: Static<T>) => unknown;\n\n /**\n * Optional initial values for the form fields.\n * This can be used to pre-populate the form with existing data.\n */\n initialValues?: Partial<Static<T>>;\n\n /**\n * Optional function to create custom field attributes.\n * This can be used to add custom validation, styles, or other attributes.\n */\n onCreateField?: (\n name: keyof Static<T> & string,\n schema: TSchema,\n ) => InputHTMLAttributes<unknown>;\n\n /**\n * If defined, this will generate a unique ID for each field, prefixed with this string.\n *\n * > \"username\" with id=\"form-123\" will become \"form-123-username\".\n *\n * If omitted, IDs will not be generated.\n */\n id?: string;\n\n onError?: (error: Error) => void;\n\n onChange?: (key: string, value: any, store: Record<string, any>) => void;\n\n onReset?: () => void;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\nimport { type FormCtrlOptions, FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Custom hook to create a form with validation and field management.\n * This hook uses TypeBox schemas to define the structure and validation rules for the form.\n * It provides a way to handle form submission, field creation, and value management.\n *\n * @example\n * ```tsx\n * import { t } from \"alepha\";\n *\n * const form = useForm({\n * schema: t.object({\n * username: t.text(),\n * password: t.text(),\n * }),\n * handler: (values) => {\n * console.log(\"Form submitted with values:\", values);\n * },\n * });\n *\n * return (\n * <form {...form.props}>\n * <input {...form.input.username.props} />\n * <input {...form.input.password.props} />\n * <button type=\"submit\">Submit</button>\n * </form>\n * );\n * ```\n */\nexport const useForm = <T extends TObject>(\n options: FormCtrlOptions<T>,\n deps: any[] = [],\n): FormModel<T> => {\n const alepha = useAlepha();\n const formId = useId();\n const initialValuesRef = useRef(options.initialValues);\n\n const form = useMemo(() => {\n return alepha.inject(FormModel<T>, {\n lifetime: \"transient\",\n args: [options.id || formId, options],\n });\n }, deps);\n\n useEffect(() => {\n if (initialValuesRef.current !== options.initialValues) {\n initialValuesRef.current = options.initialValues;\n if (options.initialValues) {\n form.setInitialValues(options.initialValues as Record<string, any>);\n }\n }\n }, [options.initialValues]);\n\n return form;\n};\n","import type { TObject } from \"alepha\";\nimport { useAlepha } from \"alepha/react\";\nimport { useEffect, useState } from \"react\";\nimport type { FormModel } from \"../services/FormModel.ts\";\n\n/**\n * Hook to subscribe to all form values.\n * Re-renders on every field change — use only when needed (debug panels, live previews).\n */\nexport const useFormValues = <T extends TObject>(\n form: FormModel<T>,\n): Record<string, any> => {\n const alepha = useAlepha();\n const [values, setValues] = useState<Record<string, any>>(form.currentValues);\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n return alepha.events.on(\"form:change\", (ev) => {\n if (ev.id === form.id) {\n setValues(form.currentValues);\n }\n });\n }, []);\n\n return values;\n};\n","/**\n * Converts a path or identifier string into a pretty display name.\n * For paths like \"/contacts/0/name\", extracts just the field name \"Name\".\n * Handles camelCase and snake_case conversion to Title Case.\n *\n * @example\n * prettyName(\"/userName\") // \"User Name\"\n * prettyName(\"/contacts/0/email\") // \"Email\"\n * prettyName(\"/address/streetName\") // \"Street Name\"\n * prettyName(\"first_name\") // \"First Name\"\n */\nexport const prettyName = (name: string): string => {\n const segments = name.split(\"/\").filter((s) => s && !/^\\d+$/.test(s));\n const fieldName = segments[segments.length - 1] || name.replaceAll(\"/\", \"\");\n return fieldName\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n};\n","import { type TSchema, TypeBoxError } from \"alepha\";\nimport type { BaseInputField } from \"./FormModel.ts\";\nimport { prettyName } from \"./prettyName.ts\";\n\n/**\n * Semantic icon hint derived from schema metadata. UI layers map this\n * to their own icon set — this module is headless and ships no JSX.\n */\nexport type IconHint =\n | \"email\"\n | \"password\"\n | \"phone\"\n | \"url\"\n | \"number\"\n | \"calendar\"\n | \"clock\"\n | \"list\"\n | \"text\"\n | \"user\"\n | \"file\"\n | \"switch\";\n\nexport interface FieldConstraints {\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n}\n\nexport interface FieldMeta {\n id?: string;\n label: string;\n description?: string;\n error?: string;\n required: boolean;\n type?: string;\n format?: string;\n isEnum: boolean;\n isArray: boolean;\n isObject: boolean;\n isArrayOfObjects: boolean;\n enum?: readonly unknown[];\n iconHint?: IconHint;\n constraints: FieldConstraints;\n testId?: string;\n schema: TSchema;\n /**\n * Raw `$control` value from the schema, untyped here. The UI layer\n * (`alepha/react/ui`) provides the strict {@link SchemaControl} type and\n * a `resolveSchemaControl` helper to evaluate the function form.\n */\n control?: unknown;\n}\n\nexport interface ParseFieldOptions {\n label?: string;\n description?: string;\n error?: Error;\n}\n\n/**\n * Derives a {@link FieldMeta} from an `InputField` (from `useForm`) plus\n * optional overrides. Pure — no React, no JSX, no UI library coupling.\n *\n * UI components consume this metadata to render labels, descriptions,\n * error messages, icons, and validation constraints.\n */\nexport const parseField = (\n input: BaseInputField,\n options: ParseFieldOptions = {},\n): FieldMeta => {\n const schema = input.schema as TSchema & {\n type?: string;\n format?: string;\n title?: string;\n description?: string;\n enum?: readonly unknown[];\n minLength?: number;\n maxLength?: number;\n minimum?: number;\n maximum?: number;\n pattern?: string;\n properties?: unknown;\n items?: { properties?: unknown };\n $control?: unknown;\n };\n\n const label =\n options.label ??\n (typeof schema.title === \"string\" ? schema.title : undefined) ??\n prettyName(input.path);\n\n const description =\n options.description ??\n (typeof schema.description === \"string\" ? schema.description : undefined);\n\n const error =\n options.error instanceof TypeBoxError\n ? (options.error as TypeBoxError).value?.message\n : undefined;\n\n const type = schema.type;\n const format = typeof schema.format === \"string\" ? schema.format : undefined;\n const isEnum = Array.isArray(schema.enum);\n const isArray = type === \"array\";\n const isObject = type === \"object\" && Boolean(schema.properties);\n const isArrayOfObjects =\n isArray && Boolean(schema.items && (schema.items as any).properties);\n\n const name = input.props.name;\n const iconHint = inferIconHint({ type, format, name, isEnum, isArray });\n\n const constraints: FieldConstraints = {};\n if (typeof schema.minLength === \"number\")\n constraints.minLength = schema.minLength;\n if (typeof schema.maxLength === \"number\")\n constraints.maxLength = schema.maxLength;\n if (typeof schema.minimum === \"number\") constraints.minimum = schema.minimum;\n if (typeof schema.maximum === \"number\") constraints.maximum = schema.maximum;\n if (typeof schema.pattern === \"string\") constraints.pattern = schema.pattern;\n\n return {\n id: input.props.id,\n label,\n description,\n error,\n required: input.required,\n type,\n format,\n isEnum,\n isArray,\n isObject,\n isArrayOfObjects,\n enum: schema.enum,\n iconHint,\n constraints,\n testId: (input.props as Record<string, unknown>)[\"data-testid\"] as\n | string\n | undefined,\n schema: input.schema,\n control: schema.$control,\n };\n};\n\nconst inferIconHint = (params: {\n type?: string;\n format?: string;\n name?: string;\n isEnum: boolean;\n isArray: boolean;\n}): IconHint | undefined => {\n const { type, format, name, isEnum, isArray } = params;\n\n if (format === \"email\") return \"email\";\n if (format === \"url\" || format === \"uri\") return \"url\";\n if (format === \"tel\" || format === \"phone\") return \"phone\";\n if (format === \"date\" || format === \"date-time\") return \"calendar\";\n if (format === \"time\") return \"clock\";\n\n if (name?.toLowerCase().includes(\"password\")) return \"password\";\n if (name?.toLowerCase().includes(\"email\")) return \"email\";\n if (name?.toLowerCase().includes(\"phone\")) return \"phone\";\n\n if (type === \"boolean\") return \"switch\";\n if (type === \"number\" || type === \"integer\") return \"number\";\n if (isEnum || isArray) return \"list\";\n if (type === \"string\") return \"text\";\n\n return undefined;\n};\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport { default as FormState } from \"./components/FormState.tsx\";\nexport * from \"./errors/FormValidationError.ts\";\nexport * from \"./hooks/useFieldValue.ts\";\nexport * from \"./hooks/useForm.ts\";\nexport * from \"./hooks/useFormState.ts\";\nexport * from \"./hooks/useFormValues.ts\";\nexport * from \"./services/FormModel.ts\";\nexport * from \"./services/parseField.ts\";\nexport * from \"./services/prettyName.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"form:change\": { id: string; path: string; value: any };\n \"form:submit:begin\": { id: string };\n \"form:submit:success\": { id: string; values: Record<string, any> };\n \"form:submit:error\": { id: string; error: Error };\n \"form:submit:end\": { id: string };\n \"form:reset\": { id: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Type-safe forms with validation.\n *\n * **Features:**\n * - Form state management\n * - TypeBox schema validation\n * - Field-level error handling\n * - Submit handling with loading state\n * - Form reset\n *\n * @module alepha.react.form\n */\nexport const AlephaReactForm = $module({\n name: \"alepha.react.form\",\n});\n"],"mappings":"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"}
|
|
@@ -64,7 +64,13 @@ declare class I18nProvider<S extends object, K extends keyof ServiceDictionary<S
|
|
|
64
64
|
get lang(): string;
|
|
65
65
|
translate: (key: string, args?: string[]) => string;
|
|
66
66
|
readonly l: (value: I18nLocalizeType, options?: I18nLocalizeOptions) => string | number;
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Look up `key` in the registered dictionaries. The `(string & {})` arm
|
|
69
|
+
* keeps autocomplete for the typed dictionary keys while allowing shared
|
|
70
|
+
* library components to pass arbitrary string keys (with a `default`
|
|
71
|
+
* fallback) without casting to `as never`.
|
|
72
|
+
*/
|
|
73
|
+
readonly tr: (key: keyof ServiceDictionary<S>[K] | (string & {}), options?: {
|
|
68
74
|
args?: string[];
|
|
69
75
|
default?: string;
|
|
70
76
|
}) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/react/i18n/components/Localize.tsx","../../../src/react/i18n/providers/I18nProvider.ts","../../../src/react/i18n/primitives/$dictionary.ts","../../../src/react/i18n/hooks/useI18n.ts","../../../src/react/i18n/index.ts"],"mappings":";;;;;;;UAIiB,aAAA;EACf,KAAA,oBAAyB,IAAA,GAAO,QAAA,GAAW,YAAA;;;;AAD7C;EAME,MAAA,GAAS,IAAA,CAAK,mBAAA;;;;;;;;;;EAUd,IAAA,wBAA4B,IAAA,CAAK,qBAAA;EAfD;;;;;EAqBhC,QAAA;AAAA;AAAA,cAGI,QAAA,GAAY,KAAA,EAAO,aAAA;;;cCvBZ,YAAA,mCAEK,iBAAA,CAAkB,CAAA;EAAA,UAExB,GAAA,EAFuB,gBAAA,CAEpB,MAAA;EAAA,UACH,MAAA,EAAM,MAAA;EAAA,UACN,gBAAA,EAAgB,gBAAA;EAAA,UAEhB,MAAA,EAAM,wBAAA,CAAA,uBAAA,CAFU,QAAA,CAEV,OAAA;EAAA,SAMA,QAAA,EAAU,KAAA;IACxB,MAAA;IACA,IAAA;IACA,IAAA;IACA,MAAA,QAAc,OAAA,CAAQ,MAAA;IACtB,YAAA,EAAc,MAAA;EAAA;EAGhB,OAAA;;;EAIO,UAAA;IAAc,MAAA,GAAS,KAAA,EAAO,IAAA;EAAA;EAG9B,YAAA;IAAgB,MAAA,GAAS,KAAA;EAAA;EAAA,IAGrB,SAAA,CAAA;;qBAcQ,QAAA,EApBsB,QAAA,CAoBd,aAAA;EAAA,mBAQR,OAAA,EARQ,QAAA,CAQD,aAAA;EAAA,UA6BhB,aAAA,CAAA;EAOH,OAAA,GAAiB,IAAA,aAAY,OAAA;EAAA,mBAiBjB,MAAA,EAjBiB,QAAA,CAiBX,aAAA;EAAA,IAwBd,YAAA,CAAA;EAAA,IASA,IAAA,CAAA;EAIJ,SAAA,GAAa,GAAA,UAAa,IAAA;EAAA,SAoBjB,CAAA,GACd,KAAA,EAAO,gBAAA,EACP,OAAA,GAAS,mBAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/react/i18n/components/Localize.tsx","../../../src/react/i18n/providers/I18nProvider.ts","../../../src/react/i18n/primitives/$dictionary.ts","../../../src/react/i18n/hooks/useI18n.ts","../../../src/react/i18n/index.ts"],"mappings":";;;;;;;UAIiB,aAAA;EACf,KAAA,oBAAyB,IAAA,GAAO,QAAA,GAAW,YAAA;;;;AAD7C;EAME,MAAA,GAAS,IAAA,CAAK,mBAAA;;;;;;;;;;EAUd,IAAA,wBAA4B,IAAA,CAAK,qBAAA;EAfD;;;;;EAqBhC,QAAA;AAAA;AAAA,cAGI,QAAA,GAAY,KAAA,EAAO,aAAA;;;cCvBZ,YAAA,mCAEK,iBAAA,CAAkB,CAAA;EAAA,UAExB,GAAA,EAFuB,gBAAA,CAEpB,MAAA;EAAA,UACH,MAAA,EAAM,MAAA;EAAA,UACN,gBAAA,EAAgB,gBAAA;EAAA,UAEhB,MAAA,EAAM,wBAAA,CAAA,uBAAA,CAFU,QAAA,CAEV,OAAA;EAAA,SAMA,QAAA,EAAU,KAAA;IACxB,MAAA;IACA,IAAA;IACA,IAAA;IACA,MAAA,QAAc,OAAA,CAAQ,MAAA;IACtB,YAAA,EAAc,MAAA;EAAA;EAGhB,OAAA;;;EAIO,UAAA;IAAc,MAAA,GAAS,KAAA,EAAO,IAAA;EAAA;EAG9B,YAAA;IAAgB,MAAA,GAAS,KAAA;EAAA;EAAA,IAGrB,SAAA,CAAA;;qBAcQ,QAAA,EApBsB,QAAA,CAoBd,aAAA;EAAA,mBAQR,OAAA,EARQ,QAAA,CAQD,aAAA;EAAA,UA6BhB,aAAA,CAAA;EAOH,OAAA,GAAiB,IAAA,aAAY,OAAA;EAAA,mBAiBjB,MAAA,EAjBiB,QAAA,CAiBX,aAAA;EAAA,IAwBd,YAAA,CAAA;EAAA,IASA,IAAA,CAAA;EAIJ,SAAA,GAAa,GAAA,UAAa,IAAA;EAAA,SAoBjB,CAAA,GACd,KAAA,EAAO,gBAAA,EACP,OAAA,GAAS,mBAAA;;;;AAtKb;;;WAwOkB,EAAA,GACd,GAAA,QAAW,iBAAA,CAAkB,CAAA,EAAG,CAAA,mBAChC,OAAA;IACE,IAAA;IACA,OAAA;EAAA;EAAA,UAUM,MAAA,CAAO,IAAA,UAAc,IAAA;AAAA;AAAA,KASrB,gBAAA,qBAAqC,IAAA,GAAO,QAAA,GAAW,YAAA;AAAA,UAElD,mBAAA;EA/OC;;;;EAoPhB,MAAA,GAAS,IAAA,CAAK,mBAAA;EAxNa;;;;;;;;;EAkO3B,IAAA,wBAA4B,IAAA,CAAK,qBAAA;EA/QjC;;;;;EAqRA,QAAA;AAAA;;;;;;;;ADxRF;;;;;;;;;;;;;;;;;;;;;;;AAuBC;;;cEOY,WAAA;EAAA,WAAyB,MAAA,kBAAsB,OAAA,EACjD,0BAAA,CAA2B,CAAA,IACnC,mBAAA,CAAoB,CAAA;EAAA;;UAMN,0BAAA,WAAqC,MAAA;EACpD,IAAA;EACA,IAAA;EACA,IAAA,QAAY,KAAA;IAAQ,OAAA,EAAS,CAAA;EAAA;AAAA;AAAA,cAKlB,mBAAA,WACD,MAAA,0BACF,SAAA,CAAU,0BAAA,CAA2B,CAAA;EAAA,UACnC,QAAA,EAAQ,YAAA;EAAA,UACR,MAAA,CAAA;AAAA;;;;;;cC/CC,OAAA,qCAEK,iBAAA,CAAkB,CAAA,QAC/B,YAAA,CAAa,CAAA,EAAG,CAAA;AAAA,KAKT,iBAAA,mCACE,CAAA,GAAI,CAAA,CAAE,CAAA,UAAW,mBAAA,YAA+B,CAAA;;;;YCD3C,KAAA;IACf,wBAAA;EAAA;AAAA;;;;;;;;;;;cAgBS,eAAA,EAAe,QAAA,CAAA,OAAA,CAI1B,QAAA,CAJ0B,MAAA"}
|
package/dist/react/i18n/index.js
CHANGED
|
@@ -119,6 +119,12 @@ var I18nProvider = class {
|
|
|
119
119
|
if (value instanceof TypeBoxError) return TypeProvider.translateError(value, this.lang);
|
|
120
120
|
return value;
|
|
121
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* Look up `key` in the registered dictionaries. The `(string & {})` arm
|
|
124
|
+
* keeps autocomplete for the typed dictionary keys while allowing shared
|
|
125
|
+
* library components to pass arbitrary string keys (with a `default`
|
|
126
|
+
* fallback) without casting to `as never`.
|
|
127
|
+
*/
|
|
122
128
|
tr = (key, options = {}) => {
|
|
123
129
|
const translation = this.translate(key, options.args || []);
|
|
124
130
|
if (translation === key && options.default) return options.default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/i18n/providers/I18nProvider.ts","../../../src/react/i18n/primitives/$dictionary.ts","../../../src/react/i18n/hooks/useI18n.ts","../../../src/react/i18n/components/Localize.tsx","../../../src/react/i18n/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha, TypeBoxError, TypeProvider, t } from \"alepha\";\nimport { type DateTime, DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $cookie } from \"alepha/server/cookies\";\nimport type { ServiceDictionary } from \"../hooks/useI18n.ts\";\n\nexport class I18nProvider<\n S extends object,\n K extends keyof ServiceDictionary<S>,\n> {\n protected log = $logger();\n protected alepha = $inject(Alepha);\n protected dateTimeProvider = $inject(DateTimeProvider);\n\n protected cookie = $cookie({\n name: \"lang\",\n schema: t.text(),\n ttl: [1, \"year\"],\n });\n\n public readonly registry: Array<{\n target: string;\n name: string;\n lang: string;\n loader: () => Promise<Record<string, string>>;\n translations: Record<string, string>;\n }> = [];\n\n options = {\n fallbackLang: \"en\",\n };\n\n public dateFormat: { format: (value: Date) => string } =\n new Intl.DateTimeFormat(this.lang);\n\n public numberFormat: { format: (value: number) => string } =\n new Intl.NumberFormat(this.lang);\n\n public get languages() {\n const languages = new Set<string>();\n\n for (const item of this.registry) {\n languages.add(item.lang);\n }\n\n return Array.from(languages);\n }\n\n constructor() {\n this.refreshLocale();\n }\n\n protected readonly onRender = $hook({\n on: \"server:onRequest\",\n priority: \"last\",\n handler: async ({ request }) => {\n this.alepha.store.set(\"alepha.react.i18n.lang\", this.cookie.get(request));\n },\n });\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n if (this.alepha.isBrowser()) {\n // get cookie lang\n const cookieLang = this.cookie.get();\n if (cookieLang) {\n this.alepha.store.set(\"alepha.react.i18n.lang\", cookieLang);\n }\n\n for (const item of this.registry) {\n if (item.lang === this.lang || item.lang === this.fallbackLang) {\n this.log.trace(\"Loading language\", {\n lang: item.lang,\n name: item.name,\n target: item.target,\n });\n item.translations = await item.loader();\n }\n }\n return;\n }\n\n for (const item of this.registry) {\n item.translations = await item.loader();\n }\n },\n });\n\n protected refreshLocale() {\n this.numberFormat = new Intl.NumberFormat(this.lang);\n this.dateFormat = new Intl.DateTimeFormat(this.lang);\n this.dateTimeProvider.setLocale(this.lang);\n TypeProvider.setLocale(this.lang);\n }\n\n public setLang = async (lang: string) => {\n if (this.alepha.isBrowser()) {\n for (const item of this.registry) {\n if (lang === item.lang) {\n if (Object.keys(item.translations).length > 0) {\n continue; // already loaded\n }\n item.translations = await item.loader();\n }\n }\n this.cookie.set(lang);\n }\n\n this.alepha.store.set(\"alepha.react.i18n.lang\", lang);\n this.refreshLocale();\n };\n\n protected readonly mutate = $hook({\n on: \"state:mutate\",\n handler: async ({ key, value }) => {\n if (key === \"alepha.react.i18n.lang\" && this.alepha.isBrowser()) {\n let hasChanged = false;\n for (const item of this.registry) {\n if (value === item.lang) {\n if (Object.keys(item.translations).length > 0) {\n continue; // already loaded\n }\n item.translations = await item.loader();\n hasChanged = true;\n }\n }\n\n this.refreshLocale();\n\n if (hasChanged) {\n this.alepha.store.set(\"alepha.react.i18n.lang\", value);\n }\n }\n },\n });\n\n public get fallbackLang(): string {\n const configured = this.options.fallbackLang;\n const hasDict = this.registry.some((item) => item.lang === configured);\n if (hasDict) {\n return configured;\n }\n return this.registry[0]?.lang ?? configured;\n }\n\n public get lang(): string {\n return this.alepha.store.get(\"alepha.react.i18n.lang\") || this.fallbackLang;\n }\n\n public translate = (key: string, args: string[] = []) => {\n for (const item of this.registry) {\n if (item.lang === this.lang) {\n if (item.translations[key]) {\n return this.render(item.translations[key], args); // append lang for fallback\n }\n }\n }\n\n for (const item of this.registry) {\n if (item.lang === this.fallbackLang) {\n if (item.translations[key]) {\n return this.render(item.translations[key], args); // append lang for fallback\n }\n }\n }\n\n return key; // fallback to the key itself if not found\n };\n\n public readonly l = (\n value: I18nLocalizeType,\n options: I18nLocalizeOptions = {},\n ) => {\n // Handle numbers\n if (typeof value === \"number\" && !options.date) {\n return new Intl.NumberFormat(this.lang, options.number).format(value);\n }\n\n // Handle dates\n if (\n value instanceof Date ||\n this.dateTimeProvider.isDateTime(value) ||\n (typeof value === \"string\" && options.date) ||\n (typeof value === \"number\" && options.date)\n ) {\n // convert to DateTime with locale applied\n let dt = this.dateTimeProvider.of(value);\n\n // apply timezone if specified\n if (options.timezone) {\n dt = dt.tz(options.timezone);\n }\n\n // format using dayjs format string\n if (typeof options.date === \"string\") {\n if (options.date === \"fromNow\") {\n return dt.locale(this.lang).fromNow();\n }\n return dt.locale(this.lang).format(options.date);\n }\n\n // format using Intl.DateTimeFormatOptions\n if (options.date) {\n return new Intl.DateTimeFormat(\n this.lang,\n options.timezone\n ? { ...options.date, timeZone: options.timezone }\n : options.date,\n ).format(dt.toDate());\n }\n\n // default formatting with timezone\n if (options.timezone) {\n return new Intl.DateTimeFormat(this.lang, {\n timeZone: options.timezone,\n }).format(dt.toDate());\n }\n\n // default formatting\n return new Intl.DateTimeFormat(this.lang).format(dt.toDate());\n }\n\n // handle TypeBox errors\n if (value instanceof TypeBoxError) {\n return TypeProvider.translateError(value, this.lang);\n }\n\n // return string values as-is\n return value;\n };\n\n public readonly tr = (\n key: keyof ServiceDictionary<S>[K],\n options: {\n args?: string[];\n default?: string;\n } = {},\n ) => {\n const translation = this.translate(key as string, options.args || []);\n if (translation === key && options.default) {\n return options.default;\n }\n return translation;\n };\n\n protected render(item: string, args: string[]): string {\n let result = item;\n for (let i = 0; i < args.length; i++) {\n result = result.replace(`$${i + 1}`, args[i]);\n }\n return result;\n }\n}\n\nexport type I18nLocalizeType = string | number | Date | DateTime | TypeBoxError;\n\nexport interface I18nLocalizeOptions {\n /**\n * Options for number formatting (when value is a number)\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat\n */\n number?: Intl.NumberFormatOptions;\n /**\n * Options for date formatting (when value is a Date or DateTime)\n * Can be:\n * - A dayjs format string (e.g., \"LLL\", \"YYYY-MM-DD\", \"dddd, MMMM D YYYY\")\n * - \"fromNow\" for relative time (e.g., \"2 hours ago\")\n * - Intl.DateTimeFormatOptions for native formatting\n * @see https://day.js.org/docs/en/display/format\n * @see https://day.js.org/docs/en/display/from-now\n */\n date?: string | \"fromNow\" | Intl.DateTimeFormatOptions;\n /**\n * Timezone to display dates in (when value is a Date or DateTime)\n * Uses IANA timezone names (e.g., \"America/New_York\", \"Europe/Paris\", \"Asia/Tokyo\")\n * @see https://day.js.org/docs/en/timezone/timezone\n */\n timezone?: string;\n}\n","import { $inject, type Async, createPrimitive, KIND, Primitive } from \"alepha\";\nimport { I18nProvider } from \"../providers/I18nProvider.ts\";\n\n/**\n * Register a dictionary entry for translations.\n *\n * It allows you to define a set of translations for a specific language.\n * Entry can be lazy-loaded, which is useful for large dictionaries or when translations are not needed immediately.\n *\n * @example\n * ```ts\n * import { $dictionary } from \"alepha/react/i18n\";\n *\n * const Example = () => {\n * const { tr } = useI18n<App, \"en\">();\n * return <div>{tr(\"hello\")}</div>; //\n * }\n *\n * class App {\n *\n * en = $dictionary({\n * // { default: { hello: \"Hey\" } }\n * lazy: () => import(\"./translations/en.ts\"),\n * });\n *\n * home = $page({\n * path: \"/\",\n * component: Example,\n * })\n * }\n *\n * run(App);\n * ```\n */\nexport const $dictionary = <T extends Record<string, string>>(\n options: DictionaryPrimitiveOptions<T>,\n): DictionaryPrimitive<T> => {\n return createPrimitive(DictionaryPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface DictionaryPrimitiveOptions<T extends Record<string, string>> {\n lang?: string;\n name?: string;\n lazy: () => Async<{ default: T }>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class DictionaryPrimitive<\n T extends Record<string, string>,\n> extends Primitive<DictionaryPrimitiveOptions<T>> {\n protected provider = $inject(I18nProvider);\n protected onInit() {\n this.provider.registry.push({\n target: this.config.service.name,\n name: this.options.name ?? this.config.propertyKey,\n lang: this.options.lang ?? this.config.propertyKey,\n loader: async () => {\n const mod = await this.options.lazy();\n return mod.default;\n },\n translations: {},\n });\n }\n}\n\n$dictionary[KIND] = DictionaryPrimitive;\n","import { useInject, useStore } from \"alepha/react\";\nimport type { DictionaryPrimitive } from \"../primitives/$dictionary.ts\";\nimport { I18nProvider } from \"../providers/I18nProvider.ts\";\n\n/**\n * Hook to access the i18n service.\n */\nexport const useI18n = <\n S extends object,\n K extends keyof ServiceDictionary<S>,\n>(): I18nProvider<S, K> => {\n useStore(\"alepha.react.i18n.lang\");\n return useInject(I18nProvider<S, K>);\n};\n\nexport type ServiceDictionary<T extends object> = {\n [K in keyof T]: T[K] extends DictionaryPrimitive<infer U> ? U : never;\n};\n","import type { TypeBoxError } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { useI18n } from \"../hooks/useI18n.ts\";\n\nexport interface LocalizeProps {\n value: string | number | Date | DateTime | TypeBoxError;\n /**\n * Options for number formatting (when value is a number)\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat\n */\n number?: Intl.NumberFormatOptions;\n /**\n * Options for date formatting (when value is a Date or DateTime)\n * Can be:\n * - A dayjs format string (e.g., \"LLL\", \"YYYY-MM-DD\", \"dddd, MMMM D YYYY\")\n * - \"fromNow\" for relative time (e.g., \"2 hours ago\")\n * - Intl.DateTimeFormatOptions for native formatting\n * @see https://day.js.org/docs/en/display/format\n * @see https://day.js.org/docs/en/display/from-now\n */\n date?: string | \"fromNow\" | Intl.DateTimeFormatOptions;\n /**\n * Timezone to display dates in (when value is a Date or DateTime)\n * Uses IANA timezone names (e.g., \"America/New_York\", \"Europe/Paris\", \"Asia/Tokyo\")\n * @see https://day.js.org/docs/en/timezone/timezone\n */\n timezone?: string;\n}\n\nconst Localize = (props: LocalizeProps) => {\n const i18n = useI18n();\n return i18n.l(props.value, props);\n};\n\nexport default Localize;\n","import { $module } from \"alepha\";\nimport { $dictionary } from \"./primitives/$dictionary.ts\";\nimport { I18nProvider } from \"./providers/I18nProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type { LocalizeProps } from \"./components/Localize.tsx\";\nexport { default as Localize } from \"./components/Localize.tsx\";\nexport * from \"./hooks/useI18n.ts\";\nexport * from \"./primitives/$dictionary.ts\";\nexport * from \"./providers/I18nProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n export interface State {\n \"alepha.react.i18n.lang\"?: string;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Multi-language support.\n *\n * **Features:**\n * - Translation loading\n * - Locale detection\n * - Pluralization\n *\n * @module alepha.react.i18n\n */\nexport const AlephaReactI18n = $module({\n name: \"alepha.react.i18n\",\n primitives: [$dictionary],\n services: [I18nProvider],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/react/i18n/providers/I18nProvider.ts","../../../src/react/i18n/primitives/$dictionary.ts","../../../src/react/i18n/hooks/useI18n.ts","../../../src/react/i18n/components/Localize.tsx","../../../src/react/i18n/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha, TypeBoxError, TypeProvider, t } from \"alepha\";\nimport { type DateTime, DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $cookie } from \"alepha/server/cookies\";\nimport type { ServiceDictionary } from \"../hooks/useI18n.ts\";\n\nexport class I18nProvider<\n S extends object,\n K extends keyof ServiceDictionary<S>,\n> {\n protected log = $logger();\n protected alepha = $inject(Alepha);\n protected dateTimeProvider = $inject(DateTimeProvider);\n\n protected cookie = $cookie({\n name: \"lang\",\n schema: t.text(),\n ttl: [1, \"year\"],\n });\n\n public readonly registry: Array<{\n target: string;\n name: string;\n lang: string;\n loader: () => Promise<Record<string, string>>;\n translations: Record<string, string>;\n }> = [];\n\n options = {\n fallbackLang: \"en\",\n };\n\n public dateFormat: { format: (value: Date) => string } =\n new Intl.DateTimeFormat(this.lang);\n\n public numberFormat: { format: (value: number) => string } =\n new Intl.NumberFormat(this.lang);\n\n public get languages() {\n const languages = new Set<string>();\n\n for (const item of this.registry) {\n languages.add(item.lang);\n }\n\n return Array.from(languages);\n }\n\n constructor() {\n this.refreshLocale();\n }\n\n protected readonly onRender = $hook({\n on: \"server:onRequest\",\n priority: \"last\",\n handler: async ({ request }) => {\n this.alepha.store.set(\"alepha.react.i18n.lang\", this.cookie.get(request));\n },\n });\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n if (this.alepha.isBrowser()) {\n // get cookie lang\n const cookieLang = this.cookie.get();\n if (cookieLang) {\n this.alepha.store.set(\"alepha.react.i18n.lang\", cookieLang);\n }\n\n for (const item of this.registry) {\n if (item.lang === this.lang || item.lang === this.fallbackLang) {\n this.log.trace(\"Loading language\", {\n lang: item.lang,\n name: item.name,\n target: item.target,\n });\n item.translations = await item.loader();\n }\n }\n return;\n }\n\n for (const item of this.registry) {\n item.translations = await item.loader();\n }\n },\n });\n\n protected refreshLocale() {\n this.numberFormat = new Intl.NumberFormat(this.lang);\n this.dateFormat = new Intl.DateTimeFormat(this.lang);\n this.dateTimeProvider.setLocale(this.lang);\n TypeProvider.setLocale(this.lang);\n }\n\n public setLang = async (lang: string) => {\n if (this.alepha.isBrowser()) {\n for (const item of this.registry) {\n if (lang === item.lang) {\n if (Object.keys(item.translations).length > 0) {\n continue; // already loaded\n }\n item.translations = await item.loader();\n }\n }\n this.cookie.set(lang);\n }\n\n this.alepha.store.set(\"alepha.react.i18n.lang\", lang);\n this.refreshLocale();\n };\n\n protected readonly mutate = $hook({\n on: \"state:mutate\",\n handler: async ({ key, value }) => {\n if (key === \"alepha.react.i18n.lang\" && this.alepha.isBrowser()) {\n let hasChanged = false;\n for (const item of this.registry) {\n if (value === item.lang) {\n if (Object.keys(item.translations).length > 0) {\n continue; // already loaded\n }\n item.translations = await item.loader();\n hasChanged = true;\n }\n }\n\n this.refreshLocale();\n\n if (hasChanged) {\n this.alepha.store.set(\"alepha.react.i18n.lang\", value);\n }\n }\n },\n });\n\n public get fallbackLang(): string {\n const configured = this.options.fallbackLang;\n const hasDict = this.registry.some((item) => item.lang === configured);\n if (hasDict) {\n return configured;\n }\n return this.registry[0]?.lang ?? configured;\n }\n\n public get lang(): string {\n return this.alepha.store.get(\"alepha.react.i18n.lang\") || this.fallbackLang;\n }\n\n public translate = (key: string, args: string[] = []) => {\n for (const item of this.registry) {\n if (item.lang === this.lang) {\n if (item.translations[key]) {\n return this.render(item.translations[key], args); // append lang for fallback\n }\n }\n }\n\n for (const item of this.registry) {\n if (item.lang === this.fallbackLang) {\n if (item.translations[key]) {\n return this.render(item.translations[key], args); // append lang for fallback\n }\n }\n }\n\n return key; // fallback to the key itself if not found\n };\n\n public readonly l = (\n value: I18nLocalizeType,\n options: I18nLocalizeOptions = {},\n ) => {\n // Handle numbers\n if (typeof value === \"number\" && !options.date) {\n return new Intl.NumberFormat(this.lang, options.number).format(value);\n }\n\n // Handle dates\n if (\n value instanceof Date ||\n this.dateTimeProvider.isDateTime(value) ||\n (typeof value === \"string\" && options.date) ||\n (typeof value === \"number\" && options.date)\n ) {\n // convert to DateTime with locale applied\n let dt = this.dateTimeProvider.of(value);\n\n // apply timezone if specified\n if (options.timezone) {\n dt = dt.tz(options.timezone);\n }\n\n // format using dayjs format string\n if (typeof options.date === \"string\") {\n if (options.date === \"fromNow\") {\n return dt.locale(this.lang).fromNow();\n }\n return dt.locale(this.lang).format(options.date);\n }\n\n // format using Intl.DateTimeFormatOptions\n if (options.date) {\n return new Intl.DateTimeFormat(\n this.lang,\n options.timezone\n ? { ...options.date, timeZone: options.timezone }\n : options.date,\n ).format(dt.toDate());\n }\n\n // default formatting with timezone\n if (options.timezone) {\n return new Intl.DateTimeFormat(this.lang, {\n timeZone: options.timezone,\n }).format(dt.toDate());\n }\n\n // default formatting\n return new Intl.DateTimeFormat(this.lang).format(dt.toDate());\n }\n\n // handle TypeBox errors\n if (value instanceof TypeBoxError) {\n return TypeProvider.translateError(value, this.lang);\n }\n\n // return string values as-is\n return value;\n };\n\n /**\n * Look up `key` in the registered dictionaries. The `(string & {})` arm\n * keeps autocomplete for the typed dictionary keys while allowing shared\n * library components to pass arbitrary string keys (with a `default`\n * fallback) without casting to `as never`.\n */\n public readonly tr = (\n key: keyof ServiceDictionary<S>[K] | (string & {}),\n options: {\n args?: string[];\n default?: string;\n } = {},\n ) => {\n const translation = this.translate(key as string, options.args || []);\n if (translation === (key as string) && options.default) {\n return options.default;\n }\n return translation;\n };\n\n protected render(item: string, args: string[]): string {\n let result = item;\n for (let i = 0; i < args.length; i++) {\n result = result.replace(`$${i + 1}`, args[i]);\n }\n return result;\n }\n}\n\nexport type I18nLocalizeType = string | number | Date | DateTime | TypeBoxError;\n\nexport interface I18nLocalizeOptions {\n /**\n * Options for number formatting (when value is a number)\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat\n */\n number?: Intl.NumberFormatOptions;\n /**\n * Options for date formatting (when value is a Date or DateTime)\n * Can be:\n * - A dayjs format string (e.g., \"LLL\", \"YYYY-MM-DD\", \"dddd, MMMM D YYYY\")\n * - \"fromNow\" for relative time (e.g., \"2 hours ago\")\n * - Intl.DateTimeFormatOptions for native formatting\n * @see https://day.js.org/docs/en/display/format\n * @see https://day.js.org/docs/en/display/from-now\n */\n date?: string | \"fromNow\" | Intl.DateTimeFormatOptions;\n /**\n * Timezone to display dates in (when value is a Date or DateTime)\n * Uses IANA timezone names (e.g., \"America/New_York\", \"Europe/Paris\", \"Asia/Tokyo\")\n * @see https://day.js.org/docs/en/timezone/timezone\n */\n timezone?: string;\n}\n","import { $inject, type Async, createPrimitive, KIND, Primitive } from \"alepha\";\nimport { I18nProvider } from \"../providers/I18nProvider.ts\";\n\n/**\n * Register a dictionary entry for translations.\n *\n * It allows you to define a set of translations for a specific language.\n * Entry can be lazy-loaded, which is useful for large dictionaries or when translations are not needed immediately.\n *\n * @example\n * ```ts\n * import { $dictionary } from \"alepha/react/i18n\";\n *\n * const Example = () => {\n * const { tr } = useI18n<App, \"en\">();\n * return <div>{tr(\"hello\")}</div>; //\n * }\n *\n * class App {\n *\n * en = $dictionary({\n * // { default: { hello: \"Hey\" } }\n * lazy: () => import(\"./translations/en.ts\"),\n * });\n *\n * home = $page({\n * path: \"/\",\n * component: Example,\n * })\n * }\n *\n * run(App);\n * ```\n */\nexport const $dictionary = <T extends Record<string, string>>(\n options: DictionaryPrimitiveOptions<T>,\n): DictionaryPrimitive<T> => {\n return createPrimitive(DictionaryPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface DictionaryPrimitiveOptions<T extends Record<string, string>> {\n lang?: string;\n name?: string;\n lazy: () => Async<{ default: T }>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class DictionaryPrimitive<\n T extends Record<string, string>,\n> extends Primitive<DictionaryPrimitiveOptions<T>> {\n protected provider = $inject(I18nProvider);\n protected onInit() {\n this.provider.registry.push({\n target: this.config.service.name,\n name: this.options.name ?? this.config.propertyKey,\n lang: this.options.lang ?? this.config.propertyKey,\n loader: async () => {\n const mod = await this.options.lazy();\n return mod.default;\n },\n translations: {},\n });\n }\n}\n\n$dictionary[KIND] = DictionaryPrimitive;\n","import { useInject, useStore } from \"alepha/react\";\nimport type { DictionaryPrimitive } from \"../primitives/$dictionary.ts\";\nimport { I18nProvider } from \"../providers/I18nProvider.ts\";\n\n/**\n * Hook to access the i18n service.\n */\nexport const useI18n = <\n S extends object,\n K extends keyof ServiceDictionary<S>,\n>(): I18nProvider<S, K> => {\n useStore(\"alepha.react.i18n.lang\");\n return useInject(I18nProvider<S, K>);\n};\n\nexport type ServiceDictionary<T extends object> = {\n [K in keyof T]: T[K] extends DictionaryPrimitive<infer U> ? U : never;\n};\n","import type { TypeBoxError } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { useI18n } from \"../hooks/useI18n.ts\";\n\nexport interface LocalizeProps {\n value: string | number | Date | DateTime | TypeBoxError;\n /**\n * Options for number formatting (when value is a number)\n * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat\n */\n number?: Intl.NumberFormatOptions;\n /**\n * Options for date formatting (when value is a Date or DateTime)\n * Can be:\n * - A dayjs format string (e.g., \"LLL\", \"YYYY-MM-DD\", \"dddd, MMMM D YYYY\")\n * - \"fromNow\" for relative time (e.g., \"2 hours ago\")\n * - Intl.DateTimeFormatOptions for native formatting\n * @see https://day.js.org/docs/en/display/format\n * @see https://day.js.org/docs/en/display/from-now\n */\n date?: string | \"fromNow\" | Intl.DateTimeFormatOptions;\n /**\n * Timezone to display dates in (when value is a Date or DateTime)\n * Uses IANA timezone names (e.g., \"America/New_York\", \"Europe/Paris\", \"Asia/Tokyo\")\n * @see https://day.js.org/docs/en/timezone/timezone\n */\n timezone?: string;\n}\n\nconst Localize = (props: LocalizeProps) => {\n const i18n = useI18n();\n return i18n.l(props.value, props);\n};\n\nexport default Localize;\n","import { $module } from \"alepha\";\nimport { $dictionary } from \"./primitives/$dictionary.ts\";\nimport { I18nProvider } from \"./providers/I18nProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type { LocalizeProps } from \"./components/Localize.tsx\";\nexport { default as Localize } from \"./components/Localize.tsx\";\nexport * from \"./hooks/useI18n.ts\";\nexport * from \"./primitives/$dictionary.ts\";\nexport * from \"./providers/I18nProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n export interface State {\n \"alepha.react.i18n.lang\"?: string;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Multi-language support.\n *\n * **Features:**\n * - Translation loading\n * - Locale detection\n * - Pluralization\n *\n * @module alepha.react.i18n\n */\nexport const AlephaReactI18n = $module({\n name: \"alepha.react.i18n\",\n primitives: [$dictionary],\n services: [I18nProvider],\n});\n"],"mappings":"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"}
|
|
@@ -961,6 +961,10 @@ var ReactPageProvider = class {
|
|
|
961
961
|
throw e;
|
|
962
962
|
}
|
|
963
963
|
}
|
|
964
|
+
if (state.layers.length > 0 && !this.isSSR(route)) {
|
|
965
|
+
const rootLayer = state.layers[0];
|
|
966
|
+
rootLayer.element = createElement(ClientOnly, {}, rootLayer.element);
|
|
967
|
+
}
|
|
964
968
|
return { state };
|
|
965
969
|
}
|
|
966
970
|
getErrorHandler(route) {
|
|
@@ -1037,12 +1041,26 @@ var ReactPageProvider = class {
|
|
|
1037
1041
|
}
|
|
1038
1042
|
renderView(index, path, view, page) {
|
|
1039
1043
|
view ??= this.renderEmptyView();
|
|
1040
|
-
const element = page.client ? createElement(ClientOnly, typeof page.client === "object" ? page.client : {}, view) : view;
|
|
1041
1044
|
return createElement(RouterLayerContext.Provider, { value: {
|
|
1042
1045
|
index,
|
|
1043
1046
|
path,
|
|
1044
1047
|
onError: this.getErrorHandler(page) ?? ((error) => this.renderError(error))
|
|
1045
|
-
} },
|
|
1048
|
+
} }, view);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Resolve the effective `ssr` value for a route by walking up the parent
|
|
1052
|
+
* chain. Returns the nearest explicit `ssr` value, defaulting to `true`.
|
|
1053
|
+
*
|
|
1054
|
+
* The decision is made at the leaf: a parent's `ssr` only acts as a default
|
|
1055
|
+
* for descendants that did not set their own value.
|
|
1056
|
+
*/
|
|
1057
|
+
isSSR(route) {
|
|
1058
|
+
let current = route;
|
|
1059
|
+
while (current) {
|
|
1060
|
+
if (typeof current.ssr === "boolean") return current.ssr;
|
|
1061
|
+
current = current.parent;
|
|
1062
|
+
}
|
|
1063
|
+
return true;
|
|
1046
1064
|
}
|
|
1047
1065
|
map(pages, target) {
|
|
1048
1066
|
const children = target.options.children ? Array.isArray(target.options.children) ? target.options.children : target.options.children() : [];
|