diff --git a/packages/adapter-next/src/client.ts b/packages/adapter-next/src/client.ts index 16c1cec..f696ca1 100644 --- a/packages/adapter-next/src/client.ts +++ b/packages/adapter-next/src/client.ts @@ -8,8 +8,8 @@ import { type InferFormFieldTree, type UseFormOptions, type UseFormResult, - useForm as createForm, -} from '@holo-js/forms/client' + createFormClient, +} from '@holo-js/forms/internal/client' export { type ClientSubmitContext, @@ -19,7 +19,7 @@ export { type UseFormOptions, type UseFormResult, type ValidateOnMode, -} from '@holo-js/forms/client' +} from '@holo-js/forms/internal/client' function isPlainObject(value: unknown): value is Record { return !!value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Blob) @@ -112,13 +112,13 @@ export function useForm( !formRef.current || previousSchemaRef.current !== schemaDefinition ) { - formRef.current = createForm(schemaDefinition, resolvedOptions) + formRef.current = createFormClient(schemaDefinition, resolvedOptions) previousSchemaRef.current = schemaDefinition previousOptionsRef.current = options } else { const previousOptions = previousOptionsRef.current as UseFormOptions if (!areOptionsEqual(previousOptions, options)) { - formRef.current = createForm(schemaDefinition, resolvedOptions) + formRef.current = createFormClient(schemaDefinition, resolvedOptions) previousOptionsRef.current = options } } diff --git a/packages/adapter-next/tests/client.test.ts b/packages/adapter-next/tests/client.test.ts index 004b67d..fa723e9 100644 --- a/packages/adapter-next/tests/client.test.ts +++ b/packages/adapter-next/tests/client.test.ts @@ -41,7 +41,7 @@ describe('@holo-js/adapter-next client', () => { vi.resetModules() vi.clearAllMocks() vi.doUnmock('react') - vi.doUnmock('@holo-js/forms/client') + vi.doUnmock('@holo-js/forms/internal/client') }) it('wraps the shared form client with a React subscription bridge', async () => { @@ -54,8 +54,8 @@ describe('@holo-js/adapter-next client', () => { }, } - vi.doMock('@holo-js/forms/client', () => ({ - useForm: vi.fn(() => fakeForm), + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: vi.fn(() => fakeForm), })) vi.doMock('react', () => createReactMock({ @@ -115,8 +115,8 @@ describe('@holo-js/adapter-next client', () => { hookValues: [], } - vi.doMock('@holo-js/forms/client', () => ({ - useForm: vi.fn((_schema, options: { initialValues?: { email?: string } }) => ({ + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: vi.fn((_schema, options: { initialValues?: { email?: string } }) => ({ subscribe() { return () => {} }, @@ -217,8 +217,8 @@ describe('@holo-js/adapter-next client', () => { }, })) - vi.doMock('@holo-js/forms/client', () => ({ - useForm: createForm, + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: createForm, })) vi.doMock('react', () => createReactMock({ @@ -306,8 +306,8 @@ describe('@holo-js/adapter-next client', () => { }, })) - vi.doMock('@holo-js/forms/client', () => ({ - useForm: createForm, + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: createForm, })) vi.doMock('react', () => createReactMock({ @@ -418,8 +418,8 @@ describe('@holo-js/adapter-next client', () => { }, })) - vi.doMock('@holo-js/forms/client', () => ({ - useForm: createForm, + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: createForm, })) vi.doMock('react', () => createReactMock({ @@ -514,8 +514,8 @@ describe('@holo-js/adapter-next client', () => { } }) - vi.doMock('@holo-js/forms/client', () => ({ - useForm: createForm, + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: createForm, })) vi.doMock('react', () => createReactMock({ @@ -606,8 +606,8 @@ describe('@holo-js/adapter-next client', () => { }, })) - vi.doMock('@holo-js/forms/client', () => ({ - useForm: createForm, + vi.doMock('@holo-js/forms/internal/client', () => ({ + createFormClient: createForm, })) vi.doMock('react', () => createReactMock({ diff --git a/packages/adapter-next/tests/package.test.ts b/packages/adapter-next/tests/package.test.ts index af7d5e7..057271a 100644 --- a/packages/adapter-next/tests/package.test.ts +++ b/packages/adapter-next/tests/package.test.ts @@ -18,7 +18,7 @@ describe('@holo-js/adapter-next package boundaries', () => { const runtimeEntry = await readFile(runtimeEntryPath, 'utf8') expect(indexEntry).not.toContain("@holo-js/forms") - expect(clientEntry).toContain("@holo-js/forms/client") + expect(clientEntry).toContain("@holo-js/forms/internal/client") expect(clientEntry).not.toContain("@holo-js/auth") expect(runtimeEntry).not.toContain("@holo-js/forms") expect(runtimeEntry).not.toContain("@holo-js/auth") diff --git a/packages/adapter-next/tsconfig.json b/packages/adapter-next/tsconfig.json index dc585df..818f741 100644 --- a/packages/adapter-next/tsconfig.json +++ b/packages/adapter-next/tsconfig.json @@ -7,7 +7,7 @@ "@holo-js/config": ["../config/src/index.ts"], "@holo-js/core": ["../core/src/index.ts"], "@holo-js/forms": ["../forms/src/index.ts"], - "@holo-js/forms/client": ["../forms/src/client.ts"], + "@holo-js/forms/internal/client": ["../forms/src/internal/client.ts"], "@holo-js/security": ["../security/src/index.ts"], "@holo-js/security/client": ["../security/src/client.ts"], "@holo-js/session": ["../session/src/index.ts"], diff --git a/packages/adapter-next/vitest.config.ts b/packages/adapter-next/vitest.config.ts index b866dd7..44d8ffc 100644 --- a/packages/adapter-next/vitest.config.ts +++ b/packages/adapter-next/vitest.config.ts @@ -11,7 +11,7 @@ export default defineConfig({ '@holo-js/db-postgres': resolve(__dirname, '../db-postgres/src/index.ts'), '@holo-js/db-sqlite': resolve(__dirname, '../db-sqlite/src/index.ts'), '@holo-js/events': resolve(__dirname, '../events/src/index.ts'), - '@holo-js/forms/client': resolve(__dirname, '../forms/src/client.ts'), + '@holo-js/forms/internal/client': resolve(__dirname, '../forms/src/internal/client.ts'), '@holo-js/forms': resolve(__dirname, '../forms/src/index.ts'), '@holo-js/queue': resolve(__dirname, '../queue/src/index.ts'), '@holo-js/queue-redis': resolve(__dirname, '../queue-redis/src/index.ts'), diff --git a/packages/adapter-nuxt/src/runtime/composables/forms.d.ts b/packages/adapter-nuxt/src/runtime/composables/forms.d.ts index fe5030d..77f9dbe 100644 --- a/packages/adapter-nuxt/src/runtime/composables/forms.d.ts +++ b/packages/adapter-nuxt/src/runtime/composables/forms.d.ts @@ -6,6 +6,6 @@ export type { UseFormOptions, UseFormResult, ValidateOnMode, -} from '@holo-js/forms/client' +} from '@holo-js/forms/internal/client' -export declare const useForm: typeof import('@holo-js/forms/client').useForm +export declare const useForm: typeof import('@holo-js/forms/internal/client').createFormClient diff --git a/packages/adapter-nuxt/src/runtime/composables/forms.ts b/packages/adapter-nuxt/src/runtime/composables/forms.ts index 197d134..ba529e0 100644 --- a/packages/adapter-nuxt/src/runtime/composables/forms.ts +++ b/packages/adapter-nuxt/src/runtime/composables/forms.ts @@ -4,8 +4,8 @@ import { type InferFormFieldTree, type UseFormOptions, type UseFormResult, - useForm as createForm, -} from '@holo-js/forms/client' + createFormClient, +} from '@holo-js/forms/internal/client' export { type ClientSubmitContext, @@ -15,13 +15,15 @@ export { type UseFormOptions, type UseFormResult, type ValidateOnMode, -} from '@holo-js/forms/client' +} from '@holo-js/forms/internal/client' type FormValuesBridge = { readonly values: unknown setValue(path: string, value: unknown): Promise } +type FormValuesGetter = () => FormValuesBridge + const ARRAY_MUTATION_METHODS = new Set([ 'copyWithin', 'fill', @@ -66,7 +68,7 @@ function getValueAtPath(root: unknown, path: string): unknown { function createArrayMutationView( source: readonly unknown[], path: string, - form: { value: FormValuesBridge }, + getForm: FormValuesGetter, version: { value: number }, cache: WeakMap, ): unknown[] { @@ -78,7 +80,7 @@ function createArrayMutationView( const proxy = new Proxy([] as unknown[], { get(_target, key) { void version.value - const current = getValueAtPath(form.value.values, path) + const current = getValueAtPath(getForm().values, path) if (!Array.isArray(current)) { return undefined } @@ -90,14 +92,14 @@ function createArrayMutationView( if (typeof key === 'string' && ARRAY_MUTATION_METHODS.has(key)) { return (...args: unknown[]) => { - const latest = getValueAtPath(form.value.values, path) + const latest = getValueAtPath(getForm().values, path) if (!Array.isArray(latest)) { return undefined } const next = latest.slice() const result = Reflect.get(next, key, next).apply(next, args) - void form.value.setValue(path, next) + void getForm().setValue(path, next) return result } } @@ -105,7 +107,7 @@ function createArrayMutationView( return value.bind(current) }, set(_target, key, value) { - const current = getValueAtPath(form.value.values, path) + const current = getValueAtPath(getForm().values, path) if (!Array.isArray(current)) { return false } @@ -113,12 +115,12 @@ function createArrayMutationView( const next = current.slice() const updated = Reflect.set(next, key, value) if (updated) { - void form.value.setValue(path, next) + void getForm().setValue(path, next) } return updated }, deleteProperty(_target, key) { - const current = getValueAtPath(form.value.values, path) + const current = getValueAtPath(getForm().values, path) if (!Array.isArray(current)) { return false } @@ -126,7 +128,7 @@ function createArrayMutationView( const next = current.slice() const deleted = Reflect.deleteProperty(next, key) if (deleted) { - void form.value.setValue(path, next) + void getForm().setValue(path, next) } return deleted }, @@ -140,7 +142,7 @@ function defineLeafAccessor( target: Record, key: string, path: string, - form: { value: FormValuesBridge }, + getForm: FormValuesGetter, version: { value: number }, arrayCache: WeakMap, ): void { @@ -154,13 +156,13 @@ function defineLeafAccessor( configurable: true, get() { void version.value - const value = getValueAtPath(form.value.values, path) + const value = getValueAtPath(getForm().values, path) return Array.isArray(value) - ? createArrayMutationView(value, path, form, version, arrayCache) + ? createArrayMutationView(value, path, getForm, version, arrayCache) : value }, set(nextValue: unknown) { - void form.value.setValue(path, nextValue) + void getForm().setValue(path, nextValue) }, }) } @@ -168,7 +170,7 @@ function defineLeafAccessor( function syncValuesView( target: Record, source: unknown, - form: { value: FormValuesBridge }, + getForm: FormValuesGetter, version: { value: number }, arrayCache: WeakMap, prefix = '', @@ -196,7 +198,7 @@ function syncValuesView( delete target[key] } - defineLeafAccessor(target, key, path, form, version, arrayCache) + defineLeafAccessor(target, key, path, getForm, version, arrayCache) continue } @@ -205,7 +207,7 @@ function syncValuesView( delete target[key] } - defineLeafAccessor(target, key, path, form, version, arrayCache) + defineLeafAccessor(target, key, path, getForm, version, arrayCache) continue } @@ -213,7 +215,7 @@ function syncValuesView( target[key] = {} } - syncValuesView(target[key] as Record, item, form, version, arrayCache, path) + syncValuesView(target[key] as Record, item, getForm, version, arrayCache, path) } } @@ -221,30 +223,50 @@ export function useForm( schemaDefinition: TSchema, options: UseFormOptions, TSuccess> = {}, ): UseFormResult, TSuccess, InferFormFieldTree> { - const form = shallowRef(createForm(schemaDefinition, options)) + const form = shallowRef, TSuccess, InferFormFieldTree> | undefined>(undefined) const version = shallowRef(0) let versionCounter = 0 const rawValues: Record = {} const values = reactive(rawValues) as InferFormData const arrayCache = new WeakMap() + let activeForm: FormValuesBridge | undefined + + const getActiveForm = () => { + if (!activeForm) { + throw new TypeError('Expected form to be initialized.') + } + + return activeForm + } + + const currentForm = () => { + const current = form.value + if (!current) { + throw new TypeError('Expected form to be initialized.') + } + + return current + } - const syncValuesFromForm = () => { + const syncValuesFromForm = (localForm: UseFormResult, TSuccess, InferFormFieldTree>) => { + activeForm = localForm syncValuesView( rawValues, - form.value.values, - form as { value: FormValuesBridge }, + localForm.values, + getActiveForm, version, arrayCache, ) } const stopWatching = watchEffect((onCleanup) => { - form.value = createForm(schemaDefinition, options) - syncValuesFromForm() + const localForm = createFormClient(schemaDefinition, options) + syncValuesFromForm(localForm) + form.value = localForm version.value = ++versionCounter - const unsubscribe = form.value.subscribe(() => { - syncValuesFromForm() + const unsubscribe = localForm.subscribe(() => { + syncValuesFromForm(localForm) version.value = ++versionCounter }) @@ -256,45 +278,45 @@ export function useForm( return reactive({ get fields() { void version.value - return form.value.fields + return currentForm().fields }, values, get errors() { void version.value - return form.value.errors + return currentForm().errors }, get submitting() { void version.value - return form.value.submitting + return currentForm().submitting }, get valid() { void version.value - return form.value.valid + return currentForm().valid }, get lastSubmission() { void version.value - return form.value.lastSubmission + return currentForm().lastSubmission }, subscribe(listener: () => void) { - return form.value.subscribe(listener) + return currentForm().subscribe(listener) }, async validate() { - return await form.value.validate() + return await currentForm().validate() }, async validateField(path: string) { - return await form.value.validateField(path) + return await currentForm().validateField(path) }, async submit() { - return await form.value.submit() + return await currentForm().submit() }, reset(nextValues?: Partial>) { - form.value.reset(nextValues) + currentForm().reset(nextValues) }, async setValue(path: string, value: unknown) { - await form.value.setValue(path, value) + await currentForm().setValue(path, value) }, applyServerState(result: ReturnType, TSuccess>['applyServerState']>) { - return form.value.applyServerState(result) + return currentForm().applyServerState(result) }, }) as UseFormResult, TSuccess, InferFormFieldTree> } diff --git a/packages/adapter-nuxt/tests/package.test.ts b/packages/adapter-nuxt/tests/package.test.ts index 9d01f73..f35232d 100644 --- a/packages/adapter-nuxt/tests/package.test.ts +++ b/packages/adapter-nuxt/tests/package.test.ts @@ -32,7 +32,7 @@ describe('@holo-js/adapter-nuxt package boundaries', () => { expect(runtimeEntry).not.toMatch(/@holo-js\/forms/) expect(runtimeEntry).not.toMatch(/@holo-js\/storage\/runtime/) expect(storageEntry).toMatch(/@holo-js\/storage\/runtime/) - expect(clientEntry).toMatch(/@holo-js\/forms\/client/) + expect(clientEntry).toMatch(/@holo-js\/forms\/internal\/client/) expect(storagePlugin).toMatch(/@holo-js\/storage\/runtime/) expect(storageRoute).toMatch(/@holo-js\/storage/) expect(s3Driver).toMatch(/@holo-js\/storage-s3/) diff --git a/packages/adapter-nuxt/tsconfig.json b/packages/adapter-nuxt/tsconfig.json index 4c463e7..ecd52d9 100644 --- a/packages/adapter-nuxt/tsconfig.json +++ b/packages/adapter-nuxt/tsconfig.json @@ -12,7 +12,7 @@ "nitropack/runtime/plugin": ["./src/runtime/shims.d.ts"], "nitropack/runtime/storage": ["./src/runtime/shims.d.ts"], "@holo-js/forms": ["../forms/src/index.ts"], - "@holo-js/forms/client": ["../forms/src/client.ts"], + "@holo-js/forms/internal/client": ["../forms/src/internal/client.ts"], "@holo-js/security": ["../security/src/index.ts"], "@holo-js/security/client": ["../security/src/client.ts"], "@holo-js/validation": ["../validation/src/index.ts"] diff --git a/packages/adapter-nuxt/vitest.config.ts b/packages/adapter-nuxt/vitest.config.ts index ea7f542..28d7d7f 100644 --- a/packages/adapter-nuxt/vitest.config.ts +++ b/packages/adapter-nuxt/vitest.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ '@holo-js/db-mysql': resolve(__dirname, '../db-mysql/src/index.ts'), '@holo-js/db-postgres': resolve(__dirname, '../db-postgres/src/index.ts'), '@holo-js/db-sqlite': resolve(__dirname, '../db-sqlite/src/index.ts'), - '@holo-js/forms/client': resolve(__dirname, '../forms/src/client.ts'), + '@holo-js/forms/internal/client': resolve(__dirname, '../forms/src/internal/client.ts'), '@holo-js/forms': resolve(__dirname, '../forms/src/index.ts'), '@holo-js/queue': resolve(__dirname, '../queue/src/index.ts'), '@holo-js/queue-redis': resolve(__dirname, '../queue-redis/src/index.ts'), diff --git a/packages/adapter-sveltekit/src/client.ts b/packages/adapter-sveltekit/src/client.ts index ab4dbe2..6c3f54d 100644 --- a/packages/adapter-sveltekit/src/client.ts +++ b/packages/adapter-sveltekit/src/client.ts @@ -4,8 +4,8 @@ import { type InferFormFieldTree, type UseFormOptions, type UseFormResult, - useForm as createForm, -} from '@holo-js/forms/client' + createFormClient, +} from '@holo-js/forms/internal/client' export { type ClientSubmitContext, @@ -15,7 +15,7 @@ export { type UseFormOptions, type UseFormResult, type ValidateOnMode, -} from '@holo-js/forms/client' +} from '@holo-js/forms/internal/client' function isPlainObject(value: unknown): value is Record { return !!value @@ -87,7 +87,7 @@ export function useForm( ): UseFormResult, TSuccess, InferFormFieldTree> { type TData = InferFormData - const form = createForm(schemaDefinition, options) + const form = createFormClient(schemaDefinition, options) const subscribe = createSubscriber((update) => form.subscribe(update)) const cache = new WeakMap() diff --git a/packages/adapter-sveltekit/tests/package.test.ts b/packages/adapter-sveltekit/tests/package.test.ts index f9c8ec3..7709398 100644 --- a/packages/adapter-sveltekit/tests/package.test.ts +++ b/packages/adapter-sveltekit/tests/package.test.ts @@ -18,7 +18,8 @@ describe('@holo-js/adapter-sveltekit package boundaries', () => { const transportEntry = await readFile(transportEntryPath, 'utf8') expect(indexEntry).not.toContain("@holo-js/forms") - expect(clientEntry).toContain("@holo-js/forms/client") + expect(clientEntry).toContain("@holo-js/forms/internal/client") + expect(clientEntry).not.toContain("@holo-js/forms/client") expect(clientEntry).not.toContain("@holo-js/auth") expect(transportEntry).not.toContain("@holo-js/forms") expect(transportEntry).not.toContain("@holo-js/auth") diff --git a/packages/adapter-sveltekit/tsconfig.json b/packages/adapter-sveltekit/tsconfig.json index dc585df..818f741 100644 --- a/packages/adapter-sveltekit/tsconfig.json +++ b/packages/adapter-sveltekit/tsconfig.json @@ -7,7 +7,7 @@ "@holo-js/config": ["../config/src/index.ts"], "@holo-js/core": ["../core/src/index.ts"], "@holo-js/forms": ["../forms/src/index.ts"], - "@holo-js/forms/client": ["../forms/src/client.ts"], + "@holo-js/forms/internal/client": ["../forms/src/internal/client.ts"], "@holo-js/security": ["../security/src/index.ts"], "@holo-js/security/client": ["../security/src/client.ts"], "@holo-js/session": ["../session/src/index.ts"], diff --git a/packages/adapter-sveltekit/vitest.config.ts b/packages/adapter-sveltekit/vitest.config.ts index dabc49c..f6bc991 100644 --- a/packages/adapter-sveltekit/vitest.config.ts +++ b/packages/adapter-sveltekit/vitest.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ '@holo-js/db-mysql': resolve(__dirname, '../db-mysql/src/index.ts'), '@holo-js/db-postgres': resolve(__dirname, '../db-postgres/src/index.ts'), '@holo-js/db-sqlite': resolve(__dirname, '../db-sqlite/src/index.ts'), - '@holo-js/forms/client': resolve(__dirname, '../forms/src/client.ts'), + '@holo-js/forms/internal/client': resolve(__dirname, '../forms/src/internal/client.ts'), '@holo-js/forms': resolve(__dirname, '../forms/src/index.ts'), '@holo-js/queue': resolve(__dirname, '../queue/src/index.ts'), '@holo-js/queue-redis': resolve(__dirname, '../queue-redis/src/index.ts'), diff --git a/packages/forms/package.json b/packages/forms/package.json index cd1a13b..daac752 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -15,10 +15,10 @@ "import": "./dist/schema.mjs", "default": "./dist/schema.mjs" }, - "./client": { - "types": "./dist/client.d.ts", - "import": "./dist/client.mjs", - "default": "./dist/client.mjs" + "./internal/client": { + "types": "./dist/internal/client.d.ts", + "import": "./dist/internal/client.mjs", + "default": "./dist/internal/client.mjs" } }, "main": "./dist/index.mjs", diff --git a/packages/forms/src/client.ts b/packages/forms/src/internal/client.ts similarity index 98% rename from packages/forms/src/client.ts rename to packages/forms/src/internal/client.ts index f839e03..70e216a 100644 --- a/packages/forms/src/client.ts +++ b/packages/forms/src/internal/client.ts @@ -7,13 +7,13 @@ import type { FormSuccessPayload, SerializedFormSubmission as SerializedSubmissionState, SerializedFormSubmission, -} from './contracts' +} from '../contracts' import { normalizeFailureErrors, normalizeFailureInput, normalizeStatus, -} from './failure' -import { clearSensitiveInputValues, sanitizeFlashedInput } from './sensitiveInput' +} from '../failure' +import { clearSensitiveInputValues, sanitizeFlashedInput } from '../sensitiveInput' import { type FormLikeValidationInput, createErrorBag, @@ -25,7 +25,7 @@ import { type WebFileLike, validate as validateInput, } from '@holo-js/validation' -import { getClientCsrfField } from './client-security' +import { getClientCsrfField } from '../client-security' type PrimitiveLike = string | number | boolean | bigint | symbol | null | undefined | Date | Blob | WebFileLike @@ -698,7 +698,10 @@ function isSafeMethod(method: string): boolean { || normalized === 'TRACE' } -export function useForm( +/** + * @internal Shared headless form runtime for framework adapters. + */ +export function createFormClient( schemaDefinition: TSchema, options: UseFormOptions, TSuccess> = {}, ): UseFormResult, TSuccess, InferFormFieldTree> { diff --git a/packages/forms/tests/client.test.ts b/packages/forms/tests/client.test.ts index b8c721b..749e490 100644 --- a/packages/forms/tests/client.test.ts +++ b/packages/forms/tests/client.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { createFailedSubmission, createSuccessfulSubmission, field, schema } from '../src' -import { useForm } from '../src/client' +import { createFormClient as useForm } from '../src/internal/client' const browserGlobal = globalThis as typeof globalThis & { document?: Document } const originalFetch = globalThis.fetch diff --git a/packages/forms/tests/client.type.test.ts b/packages/forms/tests/client.type.test.ts index 4a30909..41e4217 100644 --- a/packages/forms/tests/client.type.test.ts +++ b/packages/forms/tests/client.type.test.ts @@ -1,6 +1,6 @@ import { describe, it } from 'vitest' import { field, schema } from '../src' -import { type ClientSubmitResult, type FormFieldState, type UseFormResult, useForm } from '../src/client' +import { type ClientSubmitResult, createFormClient as useForm, type FormFieldState, type UseFormResult } from '../src/internal/client' describe('@holo-js/forms client typing', () => { it('preserves typed fields, values, and nested field access', () => { diff --git a/packages/forms/tests/contracts.test.ts b/packages/forms/tests/contracts.test.ts index 48ea09a..c8e2ba4 100644 --- a/packages/forms/tests/contracts.test.ts +++ b/packages/forms/tests/contracts.test.ts @@ -1110,11 +1110,20 @@ describe('@holo-js/forms contracts', () => { const packageJson = JSON.parse(await import('node:fs/promises').then(module => module.readFile( new URL('../package.json', import.meta.url), 'utf8', - ))) as { version?: string; dependencies?: Record; devDependencies?: Record; peerDependencies?: Record; peerDependenciesMeta?: Record } + ))) as { + version?: string + exports: Record + dependencies?: Record + devDependencies?: Record + peerDependencies?: Record + peerDependenciesMeta?: Record + } expect(Object.keys(packageJson.dependencies ?? {})).not.toContain('@holo-js/adapter-next') expect(Object.keys(packageJson.dependencies ?? {})).not.toContain('@holo-js/adapter-nuxt') expect(Object.keys(packageJson.dependencies ?? {})).not.toContain('@holo-js/adapter-sveltekit') + expect(packageJson.exports).not.toHaveProperty('./client') + expect(packageJson.exports).toHaveProperty('./internal/client') expect(Object.keys(packageJson.devDependencies ?? {})).not.toContain('next') expect(Object.keys(packageJson.devDependencies ?? {})).not.toContain('nuxt') expect(packageJson.peerDependencies?.['@holo-js/security']).toBe(`^${packageJson.version}`) diff --git a/packages/forms/tests/docs-examples.test.ts b/packages/forms/tests/docs-examples.test.ts index 626b791..e925f18 100644 --- a/packages/forms/tests/docs-examples.test.ts +++ b/packages/forms/tests/docs-examples.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import { field, schema, validate } from '../src' -import { useForm } from '../src/client' +import { createFormClient as useForm } from '../src/internal/client' describe('@holo-js/forms documented examples', () => { it('covers the documented registration and password reset server flows', async () => { diff --git a/packages/forms/tsup.config.ts b/packages/forms/tsup.config.ts index ab7b471..3885485 100644 --- a/packages/forms/tsup.config.ts +++ b/packages/forms/tsup.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ entry: { index: 'src/index.ts', schema: 'src/schema.ts', - client: 'src/client.ts', + 'internal/client': 'src/internal/client.ts', }, format: ['esm'], dts: true,