@wix/headless-forms 0.0.1

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,189 @@
1
+ import { forms } from '@wix/forms';
2
+ import { type Signal } from '@wix/services-definitions/core-services/signals';
3
+ export type SubmitResponse = {
4
+ type: 'success';
5
+ message?: string;
6
+ } | {
7
+ type: 'error';
8
+ message: string;
9
+ } | {
10
+ type: 'idle';
11
+ };
12
+ export interface FormServiceAPI {
13
+ form: Signal<forms.Form | null>;
14
+ isLoading: Signal<boolean>;
15
+ error: Signal<string | null>;
16
+ submitResponse: Signal<SubmitResponse>;
17
+ }
18
+ export declare const FormServiceDefinition: string & {
19
+ __api: FormServiceAPI;
20
+ __config: {};
21
+ isServiceDefinition?: boolean;
22
+ } & FormServiceAPI;
23
+ /**
24
+ * Configuration object for initializing the Form service.
25
+ *
26
+ * The Form service supports two distinct patterns for providing form data:
27
+ *
28
+ * **Pattern 1: Pre-loaded Form Data (SSR/SSG)**
29
+ * - Use when you have form data available at service initialization
30
+ * - Recommended for server-side rendering and static site generation
31
+ * - Provides immediate form availability with no loading states
32
+ * - Better performance and SEO as form data is available immediately
33
+ *
34
+ * **Pattern 2: Lazy Loading with Form ID (Client-side)**
35
+ * - Use when you only have a form ID and need to load form data asynchronously
36
+ * - Ideal for client-side routing and dynamic form loading
37
+ * - Service will automatically fetch form data using the provided formId
38
+ * - Provides loading states and error handling during form fetch
39
+ *
40
+ * @interface FormServiceConfig
41
+ * @property {forms.Form} [form] - Pre-loaded form data. When provided, the service uses this data immediately without any network requests. Recommended for SSR/SSG scenarios.
42
+ * @property {string} [formId] - Form ID for lazy loading. When provided (and no form data), the service will fetch form data asynchronously from the Wix Forms API. Ideal for client-side routing.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Pattern 1: Pre-loaded form data (SSR/SSG)
47
+ * // Server-side: Load form data first
48
+ * const formServiceConfigResult = await loadFormServiceConfig('form-123');
49
+ * if (formServiceConfigResult.type === 'success') {
50
+ * // Use pre-loaded form data
51
+ * <Form.Root formServiceConfig={formServiceConfigResult.config} />
52
+ * }
53
+ *
54
+ * // Or with direct form data
55
+ * const config1: FormServiceConfig = { form: myForm };
56
+ * <Form.Root formServiceConfig={config1} />
57
+ * ```
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * // Pattern 2: Lazy loading with form ID (Client-side)
62
+ * // Simple formId-based loading - service handles the rest
63
+ * const config2: FormServiceConfig = { formId: 'form-123' };
64
+ * <Form.Root formServiceConfig={config2} />
65
+ *
66
+ * // With loading and error handling
67
+ * <Form.Root formServiceConfig={{ formId: 'form-123' }}>
68
+ * <Form.Loading>
69
+ * {({ isLoading }) => isLoading ? <div>Loading form...</div> : null}
70
+ * </Form.Loading>
71
+ * <Form.LoadingError>
72
+ * {({ error, hasError }) => hasError ? <div>Error: {error}</div> : null}
73
+ * </Form.LoadingError>
74
+ * <Form.Fields fieldMap={FIELD_MAP} />
75
+ * </Form.Root>
76
+ * ```
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * // Pattern 3: Both provided (form takes precedence)
81
+ * // The pre-loaded form data will be used, formId is ignored
82
+ * const config3: FormServiceConfig = {
83
+ * form: myForm,
84
+ * formId: 'form-123' // This will be ignored
85
+ * };
86
+ * <Form.Root formServiceConfig={config3} />
87
+ * ```
88
+ *
89
+ * @throws {Error} Throws an error if neither form nor formId is provided
90
+ */
91
+ export interface FormServiceConfig {
92
+ form?: forms.Form;
93
+ formId?: string;
94
+ }
95
+ /**
96
+ * Form service implementation that supports both pre-loaded form data and lazy loading.
97
+ *
98
+ * This service provides reactive state management for form data, loading states, errors, and submission responses.
99
+ * It automatically handles form loading when only a formId is provided, making it suitable for both SSR and client-side scenarios.
100
+ *
101
+ * ## Service Behavior
102
+ *
103
+ * **Configuration Resolution:**
104
+ * - If `form` is provided: Uses pre-loaded form data immediately (SSR/SSG pattern)
105
+ * - If only `formId` is provided: Loads form data asynchronously from Wix Forms API
106
+ * - If both are provided: Uses pre-loaded `form` data and ignores `formId`
107
+ * - If neither is provided: Throws an error during service initialization
108
+ *
109
+ * **Loading States:**
110
+ * - `isLoading`: `true` when loading form data via `formId`, `false` otherwise
111
+ * - `form`: `null` initially when using `formId`, populated after successful load
112
+ * - `error`: `null` initially, populated if form loading fails
113
+ * - `submitResponse`: `{ type: 'idle' }` initially, updated during form submission
114
+ *
115
+ * **Error Handling:**
116
+ * - Network errors during form loading are caught and stored in the `error` signal
117
+ * - "Form not found" errors are handled when the formId doesn't exist
118
+ * - All errors are logged to console for debugging
119
+ *
120
+ * @example
121
+ * ```tsx
122
+ * // Service automatically handles loading states
123
+ * const service = useService(FormServiceDefinition);
124
+ *
125
+ * // Check loading state
126
+ * const isLoading = service.isLoading.get();
127
+ *
128
+ * // Access form data (null during loading)
129
+ * const form = service.form.get();
130
+ *
131
+ * // Check for errors
132
+ * const error = service.error.get();
133
+ * ```
134
+ *
135
+ */
136
+ export declare const FormService: import("@wix/services-definitions").ServiceFactory<string & {
137
+ __api: FormServiceAPI;
138
+ __config: {};
139
+ isServiceDefinition?: boolean;
140
+ } & FormServiceAPI, FormServiceConfig>;
141
+ export type FormServiceConfigResult = {
142
+ type: 'success';
143
+ config: FormServiceConfig;
144
+ } | {
145
+ type: 'notFound';
146
+ };
147
+ /**
148
+ * Loads form service configuration by form ID.
149
+ *
150
+ * This function fetches form data from the Wix Forms API and returns a configuration
151
+ * object that can be used to initialize the Form service. This is the recommended approach
152
+ * for server-side rendering (SSR) and static site generation (SSG) scenarios.
153
+ *
154
+ * @param {string} id - The unique identifier of the form to load
155
+ * @returns {Promise<FormServiceConfigResult>} A promise that resolves to either:
156
+ * - `{ type: 'success', config: FormServiceConfig }` if the form is found and loaded successfully
157
+ * - `{ type: 'notFound' }` if the form doesn't exist or an error occurs during loading
158
+ *
159
+ * @example
160
+ * ```tsx
161
+ * import { loadFormServiceConfig } from '@wix/headless-forms/services';
162
+ *
163
+ * // Server-side loading (Astro/SSR) - pre-loads form data
164
+ * const formServiceConfigResult = await loadFormServiceConfig('form-id');
165
+ *
166
+ * if (formServiceConfigResult.type === 'notFound') {
167
+ * return Astro.redirect('/404');
168
+ * }
169
+ *
170
+ * // Use pre-loaded form data
171
+ * const formServiceConfig = formServiceConfigResult.config;
172
+ * <Form.Root formServiceConfig={formServiceConfig} />
173
+ * ```
174
+ *
175
+ * @example
176
+ * ```tsx
177
+ * // Alternative: Client-side loading with formId
178
+ * // No need to pre-load, service handles loading automatically
179
+ * <Form.Root formServiceConfig={{ formId: 'form-id' }}>
180
+ * <Form.Loading>
181
+ * {({ isLoading }) => isLoading ? <div>Loading...</div> : null}
182
+ * </Form.Loading>
183
+ * <Form.Fields fieldMap={FIELD_MAP} />
184
+ * </Form.Root>
185
+ * ```
186
+ *
187
+ * @throws {Error} Logs errors to console but returns 'notFound' result instead of throwing
188
+ */
189
+ export declare function loadFormServiceConfig(id: string): Promise<FormServiceConfigResult>;
@@ -0,0 +1,137 @@
1
+ import { forms } from '@wix/forms';
2
+ import { defineService, implementService } from '@wix/services-definitions';
3
+ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
4
+ export const FormServiceDefinition = defineService('formService');
5
+ /**
6
+ * Form service implementation that supports both pre-loaded form data and lazy loading.
7
+ *
8
+ * This service provides reactive state management for form data, loading states, errors, and submission responses.
9
+ * It automatically handles form loading when only a formId is provided, making it suitable for both SSR and client-side scenarios.
10
+ *
11
+ * ## Service Behavior
12
+ *
13
+ * **Configuration Resolution:**
14
+ * - If `form` is provided: Uses pre-loaded form data immediately (SSR/SSG pattern)
15
+ * - If only `formId` is provided: Loads form data asynchronously from Wix Forms API
16
+ * - If both are provided: Uses pre-loaded `form` data and ignores `formId`
17
+ * - If neither is provided: Throws an error during service initialization
18
+ *
19
+ * **Loading States:**
20
+ * - `isLoading`: `true` when loading form data via `formId`, `false` otherwise
21
+ * - `form`: `null` initially when using `formId`, populated after successful load
22
+ * - `error`: `null` initially, populated if form loading fails
23
+ * - `submitResponse`: `{ type: 'idle' }` initially, updated during form submission
24
+ *
25
+ * **Error Handling:**
26
+ * - Network errors during form loading are caught and stored in the `error` signal
27
+ * - "Form not found" errors are handled when the formId doesn't exist
28
+ * - All errors are logged to console for debugging
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * // Service automatically handles loading states
33
+ * const service = useService(FormServiceDefinition);
34
+ *
35
+ * // Check loading state
36
+ * const isLoading = service.isLoading.get();
37
+ *
38
+ * // Access form data (null during loading)
39
+ * const form = service.form.get();
40
+ *
41
+ * // Check for errors
42
+ * const error = service.error.get();
43
+ * ```
44
+ *
45
+ */
46
+ export const FormService = implementService.withConfig()(FormServiceDefinition, ({ getService, config }) => {
47
+ const signalsService = getService(SignalsServiceDefinition);
48
+ const { form: initialForm, formId } = config;
49
+ // Validation: ensure either form or formId is provided
50
+ if (!initialForm && !formId) {
51
+ throw new Error('FormServiceConfig must provide either "form" or "formId"');
52
+ }
53
+ const form = signalsService.signal(initialForm || null);
54
+ const isLoading = signalsService.signal(!!formId && !initialForm);
55
+ const error = signalsService.signal(null);
56
+ const submitResponse = signalsService.signal({ type: 'idle' });
57
+ // Client-side form loading for formId case
58
+ if (formId && !initialForm) {
59
+ // Load form asynchronously
60
+ forms
61
+ .getForm(formId)
62
+ .then((loadedForm) => {
63
+ if (loadedForm) {
64
+ form.set(loadedForm);
65
+ isLoading.set(false);
66
+ }
67
+ else {
68
+ error.set('Form not found');
69
+ isLoading.set(false);
70
+ }
71
+ })
72
+ .catch((err) => {
73
+ console.error('Failed to load form:', err);
74
+ error.set('Failed to load form');
75
+ isLoading.set(false);
76
+ });
77
+ }
78
+ return { form, isLoading, error, submitResponse };
79
+ });
80
+ /**
81
+ * Loads form service configuration by form ID.
82
+ *
83
+ * This function fetches form data from the Wix Forms API and returns a configuration
84
+ * object that can be used to initialize the Form service. This is the recommended approach
85
+ * for server-side rendering (SSR) and static site generation (SSG) scenarios.
86
+ *
87
+ * @param {string} id - The unique identifier of the form to load
88
+ * @returns {Promise<FormServiceConfigResult>} A promise that resolves to either:
89
+ * - `{ type: 'success', config: FormServiceConfig }` if the form is found and loaded successfully
90
+ * - `{ type: 'notFound' }` if the form doesn't exist or an error occurs during loading
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * import { loadFormServiceConfig } from '@wix/headless-forms/services';
95
+ *
96
+ * // Server-side loading (Astro/SSR) - pre-loads form data
97
+ * const formServiceConfigResult = await loadFormServiceConfig('form-id');
98
+ *
99
+ * if (formServiceConfigResult.type === 'notFound') {
100
+ * return Astro.redirect('/404');
101
+ * }
102
+ *
103
+ * // Use pre-loaded form data
104
+ * const formServiceConfig = formServiceConfigResult.config;
105
+ * <Form.Root formServiceConfig={formServiceConfig} />
106
+ * ```
107
+ *
108
+ * @example
109
+ * ```tsx
110
+ * // Alternative: Client-side loading with formId
111
+ * // No need to pre-load, service handles loading automatically
112
+ * <Form.Root formServiceConfig={{ formId: 'form-id' }}>
113
+ * <Form.Loading>
114
+ * {({ isLoading }) => isLoading ? <div>Loading...</div> : null}
115
+ * </Form.Loading>
116
+ * <Form.Fields fieldMap={FIELD_MAP} />
117
+ * </Form.Root>
118
+ * ```
119
+ *
120
+ * @throws {Error} Logs errors to console but returns 'notFound' result instead of throwing
121
+ */
122
+ export async function loadFormServiceConfig(id) {
123
+ try {
124
+ const form = await forms.getForm(id);
125
+ if (!form) {
126
+ return { type: 'notFound' };
127
+ }
128
+ return {
129
+ type: 'success',
130
+ config: { form },
131
+ };
132
+ }
133
+ catch (error) {
134
+ console.error('Failed to load form:', error);
135
+ return { type: 'notFound' };
136
+ }
137
+ }
@@ -0,0 +1 @@
1
+ export * from './form-service.js';
@@ -0,0 +1 @@
1
+ export * from './form-service.js';
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@wix/headless-forms",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "npm run build:esm && npm run build:cjs",
7
+ "build:esm": "tsc -p tsconfig.json",
8
+ "build:cjs": "tsc -p tsconfig.cjs.json",
9
+ "build:watch": "tsc -p tsconfig.json --watch",
10
+ "test": "vitest",
11
+ "lint:fix": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
12
+ "lint:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,md}\""
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "cjs",
17
+ "react",
18
+ "services"
19
+ ],
20
+ "exports": {
21
+ "./react": {
22
+ "types": "./dist/react/index.d.ts",
23
+ "import": "./dist/react/index.js",
24
+ "require": "./cjs/dist/react/index.js"
25
+ },
26
+ "./services": {
27
+ "types": "./dist/services/index.d.ts",
28
+ "import": "./dist/services/index.js",
29
+ "require": "./cjs/dist/services/index.js"
30
+ }
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/dom": "^10.4.0",
34
+ "@testing-library/jest-dom": "^6.6.3",
35
+ "@testing-library/react": "^16.3.0",
36
+ "@types/node": "^20.9.0",
37
+ "@vitest/ui": "^3.1.4",
38
+ "jsdom": "^26.1.0",
39
+ "prettier": "^3.4.2",
40
+ "typescript": "^5.8.3",
41
+ "vitest": "^3.1.4"
42
+ },
43
+ "dependencies": {
44
+ "@wix/forms": "^1.0.292",
45
+ "@wix/headless-utils": "0.0.3",
46
+ "@wix/services-definitions": "^0.1.4",
47
+ "@wix/services-manager-react": "^0.1.26"
48
+ }
49
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "main": "../cjs/dist/react/index.js",
3
+ "types": "../dist/react/index.d.ts"
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "main": "../cjs/dist/services/index.js",
3
+ "types": "../dist/services/index.d.ts"
4
+ }