@wix/headless-forms 0.0.1 → 0.0.3

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.
@@ -1,5 +1,9 @@
1
1
  import { forms } from '@wix/forms';
2
- import { type Signal } from '@wix/services-definitions/core-services/signals';
2
+ import { type ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
3
+ /**
4
+ * Response type for form submission operations.
5
+ * Represents the different states a form submission can be in.
6
+ */
3
7
  export type SubmitResponse = {
4
8
  type: 'success';
5
9
  message?: string;
@@ -9,181 +13,78 @@ export type SubmitResponse = {
9
13
  } | {
10
14
  type: 'idle';
11
15
  };
16
+ /**
17
+ * API interface for the Form service, providing reactive form data management.
18
+ * This service handles loading and managing form data, loading state, and errors.
19
+ * It supports both pre-loaded form data and lazy loading with form IDs.
20
+ *
21
+ * @interface FormServiceAPI
22
+ */
12
23
  export interface FormServiceAPI {
13
- form: Signal<forms.Form | null>;
14
- isLoading: Signal<boolean>;
15
- error: Signal<string | null>;
16
- submitResponse: Signal<SubmitResponse>;
24
+ /** Reactive signal containing the current form data */
25
+ formSignal: ReadOnlySignal<forms.Form | null>;
26
+ /** Reactive signal indicating if a form is currently being loaded */
27
+ isLoadingSignal: ReadOnlySignal<boolean>;
28
+ /** Reactive signal containing any error message, or null if no error */
29
+ errorSignal: ReadOnlySignal<string | null>;
17
30
  }
31
+ /**
32
+ * Service definition for the Form service.
33
+ * This defines the contract that the FormService must implement.
34
+ *
35
+ * @constant
36
+ */
18
37
  export declare const FormServiceDefinition: string & {
19
38
  __api: FormServiceAPI;
20
39
  __config: {};
21
40
  isServiceDefinition?: boolean;
22
41
  } & FormServiceAPI;
23
42
  /**
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
43
+ * Configuration type for the Form service.
44
+ * Supports two distinct patterns for providing form data:
45
+ * - Pre-loaded form data (SSR/SSG scenarios)
46
+ * - Lazy loading with form ID (client-side routing)
33
47
  *
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
48
+ * @type {FormServiceConfig}
90
49
  */
91
- export interface FormServiceConfig {
92
- form?: forms.Form;
93
- formId?: string;
94
- }
50
+ export type FormServiceConfig = {
51
+ formId: string;
52
+ } | {
53
+ form: forms.Form;
54
+ };
95
55
  /**
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
56
+ * Implementation of the Form service that manages reactive form data.
57
+ * This service provides signals for form data, loading state, and error handling.
58
+ * It supports both pre-loaded form data and lazy loading with form IDs.
119
59
  *
120
60
  * @example
121
61
  * ```tsx
122
- * // Service automatically handles loading states
123
- * const service = useService(FormServiceDefinition);
62
+ * // Pre-loaded form data (SSR/SSG)
63
+ * const formService = FormService.withConfig({ form: formData });
124
64
  *
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();
65
+ * // Lazy loading with form ID (client-side)
66
+ * const formService = FormService.withConfig({ formId: 'form-123' });
133
67
  * ```
134
- *
135
68
  */
136
69
  export declare const FormService: import("@wix/services-definitions").ServiceFactory<string & {
137
70
  __api: FormServiceAPI;
138
71
  __config: {};
139
72
  isServiceDefinition?: boolean;
140
73
  } & FormServiceAPI, FormServiceConfig>;
141
- export type FormServiceConfigResult = {
142
- type: 'success';
143
- config: FormServiceConfig;
144
- } | {
145
- type: 'notFound';
146
- };
147
74
  /**
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');
75
+ * Loads form service configuration from the Wix Forms API for SSR initialization.
76
+ * This function is designed to be used during Server-Side Rendering (SSR) to preload
77
+ * a specific form by ID that will be used to configure the FormService.
165
78
  *
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
- * ```
79
+ * @param {string} formId - The unique identifier of the form to load
80
+ * @returns {Promise<FormServiceConfig>} Configuration object with pre-loaded form data
81
+ * @throws {Error} When the form cannot be loaded
174
82
  *
175
83
  * @example
176
84
  * ```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>
85
+ * // In your SSR/SSG setup
86
+ * const formConfig = await loadFormServiceConfig('form-123');
87
+ * const formService = FormService.withConfig(formConfig);
185
88
  * ```
186
- *
187
- * @throws {Error} Logs errors to console but returns 'notFound' result instead of throwing
188
89
  */
189
- export declare function loadFormServiceConfig(id: string): Promise<FormServiceConfigResult>;
90
+ export declare function loadFormServiceConfig(formId: string): Promise<FormServiceConfig>;
@@ -1,137 +1,92 @@
1
1
  import { forms } from '@wix/forms';
2
2
  import { defineService, implementService } from '@wix/services-definitions';
3
3
  import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
4
- export const FormServiceDefinition = defineService('formService');
5
4
  /**
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
5
+ * Service definition for the Form service.
6
+ * This defines the contract that the FormService must implement.
18
7
  *
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
8
+ * @constant
9
+ */
10
+ export const FormServiceDefinition = defineService('formService');
11
+ /**
12
+ * Implementation of the Form service that manages reactive form data.
13
+ * This service provides signals for form data, loading state, and error handling.
14
+ * It supports both pre-loaded form data and lazy loading with form IDs.
29
15
  *
30
16
  * @example
31
17
  * ```tsx
32
- * // Service automatically handles loading states
33
- * const service = useService(FormServiceDefinition);
18
+ * // Pre-loaded form data (SSR/SSG)
19
+ * const formService = FormService.withConfig({ form: formData });
34
20
  *
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();
21
+ * // Lazy loading with form ID (client-side)
22
+ * const formService = FormService.withConfig({ formId: 'form-123' });
43
23
  * ```
44
- *
45
24
  */
46
25
  export const FormService = implementService.withConfig()(FormServiceDefinition, ({ getService, config }) => {
47
26
  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"');
27
+ const isLoadingSignal = signalsService.signal(false);
28
+ const errorSignal = signalsService.signal(null);
29
+ const hasSchema = 'form' in config;
30
+ const formSignal = signalsService.signal(hasSchema ? config.form : null);
31
+ if (!hasSchema) {
32
+ isLoadingSignal.set(true);
33
+ loadForm(config.formId);
52
34
  }
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);
35
+ async function loadForm(id) {
36
+ try {
37
+ const result = await fetchForm(id);
38
+ if (result) {
39
+ formSignal.set(result);
40
+ console.log('result', !!result);
66
41
  }
67
42
  else {
68
- error.set('Form not found');
69
- isLoading.set(false);
43
+ errorSignal.set('Form not found');
70
44
  }
71
- })
72
- .catch((err) => {
73
- console.error('Failed to load form:', err);
74
- error.set('Failed to load form');
75
- isLoading.set(false);
76
- });
45
+ }
46
+ catch (err) {
47
+ errorSignal.set('Failed to load form');
48
+ throw err;
49
+ }
50
+ finally {
51
+ isLoadingSignal.set(false);
52
+ }
77
53
  }
78
- return { form, isLoading, error, submitResponse };
54
+ return {
55
+ formSignal: formSignal,
56
+ isLoadingSignal: isLoadingSignal,
57
+ errorSignal: errorSignal,
58
+ };
79
59
  });
60
+ async function fetchForm(id) {
61
+ try {
62
+ const result = await forms.getForm(id);
63
+ if (!result) {
64
+ throw new Error(`Form ${id} not found`);
65
+ }
66
+ return result;
67
+ }
68
+ catch (err) {
69
+ console.error('Failed to load form:', id, err);
70
+ throw err;
71
+ }
72
+ }
80
73
  /**
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';
74
+ * Loads form service configuration from the Wix Forms API for SSR initialization.
75
+ * This function is designed to be used during Server-Side Rendering (SSR) to preload
76
+ * a specific form by ID that will be used to configure the FormService.
95
77
  *
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
- * ```
78
+ * @param {string} formId - The unique identifier of the form to load
79
+ * @returns {Promise<FormServiceConfig>} Configuration object with pre-loaded form data
80
+ * @throws {Error} When the form cannot be loaded
107
81
  *
108
82
  * @example
109
83
  * ```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>
84
+ * // In your SSR/SSG setup
85
+ * const formConfig = await loadFormServiceConfig('form-123');
86
+ * const formService = FormService.withConfig(formConfig);
118
87
  * ```
119
- *
120
- * @throws {Error} Logs errors to console but returns 'notFound' result instead of throwing
121
88
  */
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
- }
89
+ export async function loadFormServiceConfig(formId) {
90
+ const form = await fetchForm(formId);
91
+ return { form };
137
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-forms",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "npm run build:esm && npm run build:cjs",
@@ -41,6 +41,7 @@
41
41
  "vitest": "^3.1.4"
42
42
  },
43
43
  "dependencies": {
44
+ "@wix/form-public": "^0.9.0",
44
45
  "@wix/forms": "^1.0.292",
45
46
  "@wix/headless-utils": "0.0.3",
46
47
  "@wix/services-definitions": "^0.1.4",