@embeddables/forms 0.0.4 → 0.0.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.
@@ -1,4 +1,27 @@
1
1
  import { EmbeddablesInstance } from "@embeddables/core";
2
+ //#region src/errors.d.ts
3
+ /**
4
+ * Typed error hierarchy. Every failure this SDK raises on its own behalf is an
5
+ * instance of one of these, so consumers branch on the type
6
+ * (`if (e instanceof SchemaError) …`) instead of string-matching messages.
7
+ * Catch `FormsError` to handle them all.
8
+ *
9
+ * An error thrown by a consumer's own custom validator is never wrapped in one
10
+ * of these — it propagates with its original type and stack.
11
+ */
12
+ /** Base class for every error the SDK throws. */
13
+ declare class FormsError extends Error {
14
+ constructor(message: string, options?: ErrorOptions);
15
+ }
16
+ /** The schema is malformed. */
17
+ declare class SchemaError extends FormsError {
18
+ constructor(message: string, options?: ErrorOptions);
19
+ }
20
+ /** A custom validator returned a thenable, or a shape that is not a message. */
21
+ declare class ValidatorError extends FormsError {
22
+ constructor(message: string, options?: ErrorOptions);
23
+ }
24
+ //#endregion
2
25
  //#region ../shared-types/dist/json.types.d.ts
3
26
  /**
4
27
  * Recursive JSON value, compatible with JSONB column values and public
@@ -61,6 +84,15 @@ type ProtocolFieldId<TSchema extends FormSchema> = [TSchema] extends [FormSchema
61
84
  protocolFieldId: string;
62
85
  }>['protocolFieldId'];
63
86
  //#endregion
87
+ //#region src/storage/storage.d.ts
88
+ /** Every form on the origin shares this one entry, indexed by form ID. */
89
+ declare const FORM_DATA_KEY = "EMBEDDABLES-FORM-DATA";
90
+ interface FormsStorage {
91
+ getItem(key: string): string | null;
92
+ setItem(key: string, value: string): void;
93
+ removeItem(key: string): void;
94
+ }
95
+ //#endregion
64
96
  //#region ../shared-types/dist/analytics-instance.types.d.ts
65
97
  /**
66
98
  * Hono-free analytics client surface other SDKs accept without depending on
@@ -95,6 +127,47 @@ interface AnalyticsInstance<TEvent = AnalyticsTrackEvent> {
95
127
  getProjectId(): string;
96
128
  }
97
129
  //#endregion
130
+ //#region ../shared-types/dist/analytics-ingest.types.d.ts
131
+ type FieldUpdatedType = 'text' | 'email' | 'number' | 'boolean' | 'select' | 'multiselect' | 'json';
132
+ /**
133
+ * Raw `field_value` accepted by ingest. Every forms-sdk `ValueOfFieldType` is a
134
+ * JSON value — scalar (`text`/`email`/`select` → string, `number` → number,
135
+ * `boolean` → boolean), `multiselect` → `string[]`, `json` → arbitrary JSON —
136
+ * so the contract collapses to `JsonValue`; the runtime ingest schema is what
137
+ * bounds each shape.
138
+ */
139
+ type FieldUpdatedValue = JsonValue;
140
+ type FunnelStepFields = {
141
+ is_funnel_step?: boolean;
142
+ funnel_step_label?: string;
143
+ };
144
+ type FieldUpdatedEvent = FunnelStepFields & {
145
+ event_name: 'field:updated';
146
+ /** Schema key of the updated field. */
147
+ field_key: string;
148
+ field_type: FieldUpdatedType;
149
+ field_value?: FieldUpdatedValue;
150
+ /** Field Registry identifier when the field is registry-backed. */
151
+ registry_field_id?: string;
152
+ /** Protocol question identifier when the field is protocol-backed. */
153
+ protocol_field_id?: string;
154
+ };
155
+ type DataUpdatedEntry = {
156
+ value: string;
157
+ label: string;
158
+ };
159
+ type DataUpdatedEvent = FunnelStepFields & {
160
+ event_name: 'data:updated';
161
+ data: Record<string, DataUpdatedEntry>;
162
+ };
163
+ type FormSubmittedEvent = FunnelStepFields & {
164
+ event_name: 'form:submitted';
165
+ form_key: string;
166
+ };
167
+ //#endregion
168
+ //#region src/core/analytics.d.ts
169
+ type FormsAnalyticsEvent = DataUpdatedEvent | FieldUpdatedEvent | FormSubmittedEvent;
170
+ //#endregion
98
171
  //#region src/core/form.d.ts
99
172
  /** Per-key validation errors. An empty object means the operation succeeded. */
100
173
  type FieldErrors<TSchema extends FormSchema> = Readonly<Partial<Record<FormFieldKey<TSchema>, readonly string[]>>>;
@@ -192,5 +265,5 @@ interface FormsClient<TSchemas extends FormSchemaMap = FormSchemaMap> {
192
265
  */
193
266
  declare function initForms<const TSchemas extends FormSchemaMap>(options: InitFormsOptions<TSchemas>): FormsClient<TSchemas>;
194
267
  //#endregion
195
- export { JsonValue as S, FieldValidator as _, FormsClient as a, FormValues as b, SubmitResult as c, AnalyticsInstance as d, AnalyticsTrackEvent as f, FieldValidations as g, FieldType as h, FormSchemaMap as i, ValidateResult as l, FieldConfig as m, FieldErrors as n, InitFormsOptions as o, AnalyticsTrackResult as p, FormInstance as r, SetResult as s, CustomValidationsFor as t, initForms as u, FormFieldKey as v, ProtocolFieldId as x, FormSchema as y };
196
- //# sourceMappingURL=form-CbU5ezg7.d.ts.map
268
+ export { FormsError as A, FieldValidations as C, FormValues as D, FormSchema as E, ValidatorError as M, ProtocolFieldId as O, FieldType as S, FormFieldKey as T, AnalyticsTrackEvent as _, FormsClient as a, FormsStorage as b, SubmitResult as c, FormsAnalyticsEvent as d, DataUpdatedEvent as f, AnalyticsInstance as g, FormSubmittedEvent as h, FormSchemaMap as i, SchemaError as j, JsonValue as k, ValidateResult as l, FieldUpdatedType as m, FieldErrors as n, InitFormsOptions as o, FieldUpdatedEvent as p, FormInstance as r, SetResult as s, CustomValidationsFor as t, initForms as u, AnalyticsTrackResult as v, FieldValidator as w, FieldConfig as x, FORM_DATA_KEY as y };
269
+ //# sourceMappingURL=index-qfBhdk5L.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-qfBhdk5L.d.ts","names":[],"sources":["../src/errors.ts","../../shared-types/dist/json.types.d.ts","../src/core/config.ts","../src/storage/storage.ts","../../shared-types/dist/analytics-instance.types.d.ts","../../shared-types/dist/analytics-ingest.types.d.ts","../src/core/analytics.ts","../src/core/form.ts"],"mappings":";;;;;;;;;;;;cAWa,mBAAmB;EAClB,YAAA,iBAAiB,UAAU;;;cAO5B,oBAAoB;EACnB,YAAA,iBAAiB,UAAU;;;cAO5B,uBAAuB;EACtB,YAAA,iBAAiB,UAAU;;;;;;;;KCxB7B,+CAA+C;GACtD,cAAc;;;;KCHP;;UAMF;EACR;EACA;EACA;EACA;EACA;EACA;EACA,MAAM;;KAGI,eAAe,eAAe,YAAY,cAAc;EAClE,OAAO;EACP,QAAQ,SAAS,eAAe;;UAUxB,oBAAoB,cAAc;WACjC;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA,iBAAiB;;WAEjB,SAAS,eAAe,iBAAiB;;UAG1C,eAAe,cAAc;WAC5B;WACA;WACA,MAAM;WACN,cAAc,oBAAoB;WAGlC;WACA;;KAUC,iBAAiB,KAAK,YAAY,eAAe,MAAK;KAEtD,sBAAsB,KAAK,YAAY,oBAAoB,MAAK;UAE3D;WACN;WACA;WACA,iBAAiB;;KAGvB,SAAS,gBAAgB,cAAc;KAEhC,aAAa,gBAAgB,cAAc,SAAS;KAEpD,WAAW,gBAAgB,iBACpC,KAAK,SAAS,YAAY,WAAW,iBAAiB;;KAI7C,gBAAgB,gBAAgB,eAAe,kBAAkB,uBAEzE,QAAQ,SAAS;EAAY;;;;;cC/EpB;UAEI;EACf,QAAQ;EACR,QAAQ,aAAa;EACrB,WAAW;;;;;;;;;;;;;;;UCCI;EACb;GACC;;;UAGY;EACb;EACA;EACA;;;;;;;UAOa,kBAAkB,SAAS;EACxC,WAAW,OAAO,kBAAkB,WAAW,QAAQ;;EAEvD;EACA;;;;KCxBQ;;;;;;;;KAQA,oBAAoB;KACpB;EACR;EACA;;KAsBQ,oBAAoB;EAC5B;;EAEA;EACA,YAAY;EACZ,cAAc;;EAEd;;EAEA;;KAEQ;EACR;EACA;;KAEQ,mBAAmB;EAC3B;EACA,MAAM,eAAe;;KAMb,qBAAqB;EAC7B;EACA;;;;KCpCQ,sBAAsB,mBAAmB,oBAAoB;;;;KCE7D,YAAY,gBAAgB,cAAc,SACpD,QAAQ,OAAO,aAAa;UAGb,UAAU,gBAAgB;EACzC;EACA,QAAQ,YAAY;;EAEpB;;UAGe,aAAa,gBAAgB;EAC5C;EACA,QAAQ,YAAY;EACpB,QAAQ,QAAQ,WAAW;EAC3B;;UAGe,eAAe,gBAAgB;EAC9C;EACA,QAAQ,YAAY;EACpB,QAAQ,QAAQ,WAAW;;UAGZ,aAAa,gBAAgB;WACnC,KAAK;;;;;;;EAOd,IAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,UAAU;;EAE5D,IAAI,UAAU,aAAa,UAAU,KAAK,IAAI,WAAW,SAAS;;;;;;EAMlE,0BAA0B,UAAU,gBAAgB,UAClD,iBAAiB,IAEf,WAAW,SAAS,QAAQ;IAA6B,iBAAiB;;EAE9E,UAAU,QAAQ,WAAW;;;;;;;;;EAS7B,UAAU,QAAQ,aAAa;;;;;;;;;;EAU/B,SAAS,QAAQ,QAAQ,WAAW,YAAY,QAAQ,eAAe;EACvE,UAAU,YAAY;EACtB;;EAEA,UAAU;;;KA6CA,gBAAgB,SAAS,eAAe;KAExC,qBAAqB,gBAAgB,0BACrC,KAAK,aAAa,YAAY;UAOzB,iBAAiB,iBAAiB,gBAAgB;EACjE,MAAM;;EAEN,SAAS;EACT,oBAAoB;;UAGL,YAAY,iBAAiB,gBAAgB;;;;;;;;;;;;;EAa5D,QAAQ,gBAAgB,mBAAmB;IACzC,QAAQ;IACR,oBAAoB,qBAAqB,SAAS;MAChD,aAAa,SAAS;;;;;;iBAOZ,gBAAgB,iBAAiB,eAC/C,SAAS,iBAAiB,YACzB,YAAY"}
package/dist/index.d.ts CHANGED
@@ -1,76 +1,2 @@
1
- import { S as JsonValue, _ as FieldValidator, a as FormsClient, b as FormValues, c as SubmitResult, d as AnalyticsInstance, f as AnalyticsTrackEvent, g as FieldValidations, h as FieldType, i as FormSchemaMap, l as ValidateResult, m as FieldConfig, n as FieldErrors, o as InitFormsOptions, p as AnalyticsTrackResult, r as FormInstance, s as SetResult, t as CustomValidationsFor, u as initForms, v as FormFieldKey, x as ProtocolFieldId, y as FormSchema } from "./form-CbU5ezg7.js";
2
- //#region src/errors.d.ts
3
- /**
4
- * Typed error hierarchy. Every failure this SDK raises on its own behalf is an
5
- * instance of one of these, so consumers branch on the type
6
- * (`if (e instanceof SchemaError) …`) instead of string-matching messages.
7
- * Catch `FormsError` to handle them all.
8
- *
9
- * An error thrown by a consumer's own custom validator is never wrapped in one
10
- * of these — it propagates with its original type and stack.
11
- */
12
- /** Base class for every error the SDK throws. */
13
- declare class FormsError extends Error {
14
- constructor(message: string, options?: ErrorOptions);
15
- }
16
- /** The schema is malformed. */
17
- declare class SchemaError extends FormsError {
18
- constructor(message: string, options?: ErrorOptions);
19
- }
20
- /** A custom validator returned a thenable, or a shape that is not a message. */
21
- declare class ValidatorError extends FormsError {
22
- constructor(message: string, options?: ErrorOptions);
23
- }
24
- //#endregion
25
- //#region src/storage/storage.d.ts
26
- /** Every form on the origin shares this one entry, indexed by form ID. */
27
- declare const FORM_DATA_KEY = "EMBEDDABLES-FORM-DATA";
28
- interface FormsStorage {
29
- getItem(key: string): string | null;
30
- setItem(key: string, value: string): void;
31
- removeItem(key: string): void;
32
- }
33
- //#endregion
34
- //#region ../shared-types/dist/analytics-ingest.types.d.ts
35
- type FieldUpdatedType = 'text' | 'email' | 'number' | 'boolean' | 'select' | 'multiselect' | 'json';
36
- /**
37
- * Raw `field_value` accepted by ingest. Every forms-sdk `ValueOfFieldType` is a
38
- * JSON value — scalar (`text`/`email`/`select` → string, `number` → number,
39
- * `boolean` → boolean), `multiselect` → `string[]`, `json` → arbitrary JSON —
40
- * so the contract collapses to `JsonValue`; the runtime ingest schema is what
41
- * bounds each shape.
42
- */
43
- type FieldUpdatedValue = JsonValue;
44
- type FunnelStepFields = {
45
- is_funnel_step?: boolean;
46
- funnel_step_label?: string;
47
- };
48
- type FieldUpdatedEvent = FunnelStepFields & {
49
- event_name: 'field:updated';
50
- /** Schema key of the updated field. */
51
- field_key: string;
52
- field_type: FieldUpdatedType;
53
- field_value?: FieldUpdatedValue;
54
- /** Field Registry identifier when the field is registry-backed. */
55
- registry_field_id?: string;
56
- /** Protocol question identifier when the field is protocol-backed. */
57
- protocol_field_id?: string;
58
- };
59
- type DataUpdatedEntry = {
60
- value: string;
61
- label: string;
62
- };
63
- type DataUpdatedEvent = FunnelStepFields & {
64
- event_name: 'data:updated';
65
- data: Record<string, DataUpdatedEntry>;
66
- };
67
- type FormSubmittedEvent = FunnelStepFields & {
68
- event_name: 'form:submitted';
69
- form_key: string;
70
- };
71
- //#endregion
72
- //#region src/core/analytics.d.ts
73
- type FormsAnalyticsEvent = DataUpdatedEvent | FieldUpdatedEvent | FormSubmittedEvent;
74
- //#endregion
75
- export { type AnalyticsInstance, type AnalyticsTrackEvent, type AnalyticsTrackResult, type CustomValidationsFor, type DataUpdatedEvent, FORM_DATA_KEY, type FieldConfig, type FieldErrors, type FieldType, type FieldUpdatedEvent, type FieldUpdatedType, type FieldValidations, type FieldValidator, type FormFieldKey, type FormInstance, type FormSchema, type FormSchemaMap, type FormSubmittedEvent, type FormValues, type FormsAnalyticsEvent, type FormsClient, FormsError, type FormsStorage, type InitFormsOptions, type JsonValue, type ProtocolFieldId, SchemaError, type SetResult, type SubmitResult, type ValidateResult, ValidatorError, initForms };
76
- //# sourceMappingURL=index.d.ts.map
1
+ import { A as FormsError, C as FieldValidations, D as FormValues, E as FormSchema, M as ValidatorError, O as ProtocolFieldId, S as FieldType, T as FormFieldKey, _ as AnalyticsTrackEvent, a as FormsClient, b as FormsStorage, c as SubmitResult, d as FormsAnalyticsEvent, f as DataUpdatedEvent, g as AnalyticsInstance, h as FormSubmittedEvent, i as FormSchemaMap, j as SchemaError, k as JsonValue, l as ValidateResult, m as FieldUpdatedType, n as FieldErrors, o as InitFormsOptions, p as FieldUpdatedEvent, r as FormInstance, s as SetResult, t as CustomValidationsFor, u as initForms, v as AnalyticsTrackResult, w as FieldValidator, x as FieldConfig, y as FORM_DATA_KEY } from "./index-qfBhdk5L.js";
2
+ export { type AnalyticsInstance, type AnalyticsTrackEvent, type AnalyticsTrackResult, type CustomValidationsFor, type DataUpdatedEvent, FORM_DATA_KEY, type FieldConfig, type FieldErrors, type FieldType, type FieldUpdatedEvent, type FieldUpdatedType, type FieldValidations, type FieldValidator, type FormFieldKey, type FormInstance, type FormSchema, type FormSchemaMap, type FormSubmittedEvent, type FormValues, type FormsAnalyticsEvent, type FormsClient, FormsError, type FormsStorage, type InitFormsOptions, type JsonValue, type ProtocolFieldId, SchemaError, type SetResult, type SubmitResult, type ValidateResult, ValidatorError, initForms };
package/dist/react.cjs CHANGED
@@ -47,6 +47,9 @@ const FORMS_MODULE_KEY = "forms";
47
47
  * from provider context, not from here.
48
48
  */
49
49
  const formsByCore = /* @__PURE__ */ new WeakMap();
50
+ function getRegisteredFormsClient(core) {
51
+ return formsByCore.get(core);
52
+ }
50
53
  //#endregion
51
54
  //#region src/react/use-forms.ts
52
55
  /** @internal Product hooks read the module client from provider context. */
@@ -163,6 +166,7 @@ function forms(options) {
163
166
  }
164
167
  //#endregion
165
168
  exports.forms = forms;
169
+ exports.getRegisteredFormsClient = getRegisteredFormsClient;
166
170
  exports.registerFormsClient = registerFormsClient;
167
171
  exports.useForm = useForm;
168
172
  exports.useFormErrors = useFormErrors;
@@ -1 +1 @@
1
- {"version":3,"file":"react.cjs","names":["useRef","useCallback","useSyncExternalStore","useEmbeddablesModule","useRef","useState","useCallback","initForms"],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useRef } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { CustomValidationsFor, FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n customValidations,\n}: {\n formId: TFormId\n customValidations?: CustomValidationsFor<TSchemas[TFormId]>\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n // * Held in a ref because only the first call for a form id builds the\n // * instance; a later render passing new validators must not look like a change.\n const customValidationsRef = useRef(customValidations)\n customValidationsRef.current = customValidations\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form =\n client === null\n ? null\n : client.getForm({ formId, customValidations: customValidationsRef.current })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n\n// prueba de la colisión\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K]) => void\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K]) => {\n setDraft(value)\n }, [])\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n onBlur,\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport function registerFormsClient<const TSchemas extends FormSchemaMap>({\n core,\n ...options\n}: InitFormsOptions<TSchemas>): FormsClient<TSchemas> {\n const existing = formsByCore.get(core) as FormsClient<TSchemas> | undefined\n if (existing !== undefined) return existing\n\n const analyticsInstance = options.analyticsInstance ?? resolveCoreAnalyticsInstance(core)\n\n const client = initForms({ ...options, core, analyticsInstance })\n formsByCore.set(core, client as FormsClient<FormSchemaMap>)\n return client\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsClient } from './register-forms-client.js'\n\nimport type { FormSchemaMap, FormsClient } from '../core/form.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\nexport function forms<const TSchemas extends FormSchemaMap>(\n options: FormsReactOptions<TSchemas>,\n): EmbeddablesReactModule<FormsClient<TSchemas>> {\n return {\n key: FORMS_MODULE_KEY,\n init: (core) => registerFormsClient({ core, ...options }),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.cjs","names":["useRef","useCallback","useSyncExternalStore","useEmbeddablesModule","useRef","useState","useCallback","initForms"],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n\nexport function getRegisteredFormsClient(core: object): FormsClient<FormSchemaMap> | undefined {\n return formsByCore.get(core)\n}\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useRef } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { CustomValidationsFor, FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n customValidations,\n}: {\n formId: TFormId\n customValidations?: CustomValidationsFor<TSchemas[TFormId]>\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n // * Held in a ref because only the first call for a form id builds the\n // * instance; a later render passing new validators must not look like a change.\n const customValidationsRef = useRef(customValidations)\n customValidationsRef.current = customValidations\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form =\n client === null\n ? null\n : client.getForm({ formId, customValidations: customValidationsRef.current })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K]) => void\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K]) => {\n setDraft(value)\n }, [])\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n onBlur,\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport function registerFormsClient<const TSchemas extends FormSchemaMap>({\n core,\n ...options\n}: InitFormsOptions<TSchemas>): FormsClient<TSchemas> {\n const existing = formsByCore.get(core) as FormsClient<TSchemas> | undefined\n if (existing !== undefined) return existing\n\n const analyticsInstance = options.analyticsInstance ?? resolveCoreAnalyticsInstance(core)\n\n const client = initForms({ ...options, core, analyticsInstance })\n formsByCore.set(core, client as FormsClient<FormSchemaMap>)\n return client\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsClient } from './register-forms-client.js'\n\nimport type { FormSchemaMap, FormsClient } from '../core/form.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\nexport function forms<const TSchemas extends FormSchemaMap>(\n options: FormsReactOptions<TSchemas>,\n): EmbeddablesReactModule<FormsClient<TSchemas>> {\n return {\n key: FORMS_MODULE_KEY,\n init: (core) => registerFormsClient({ core, ...options }),\n }\n}\n"],"mappings":"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"}
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as FormsClient, b as FormValues, i as FormSchemaMap, n as FieldErrors, o as InitFormsOptions, r as FormInstance, s as SetResult, t as CustomValidationsFor, v as FormFieldKey, y as FormSchema } from "./form-CbU5ezg7.js";
1
+ import { D as FormValues, E as FormSchema, T as FormFieldKey, a as FormsClient, i as FormSchemaMap, n as FieldErrors, o as InitFormsOptions, r as FormInstance, s as SetResult, t as CustomValidationsFor } from "./index-qfBhdk5L.js";
2
2
  import { EmbeddablesReactModule } from "@embeddables/core/react";
3
3
  //#region src/react/use-form-store.d.ts
4
4
  type FormSnapshot<TSchema extends FormSchema> = Readonly<{
@@ -76,5 +76,8 @@ declare function forms<const TSchemas extends FormSchemaMap>(options: FormsReact
76
76
  //#region src/react/register-forms-client.d.ts
77
77
  declare function registerFormsClient<const TSchemas extends FormSchemaMap>({ core, ...options }: InitFormsOptions<TSchemas>): FormsClient<TSchemas>;
78
78
  //#endregion
79
- export { type EmbeddablesSchemaRegistry, type FormsReactOptions, type RegisteredSchemas, forms, registerFormsClient, useForm, useFormErrors, useFormField };
79
+ //#region src/react/forms-registry.d.ts
80
+ declare function getRegisteredFormsClient(core: object): FormsClient<FormSchemaMap> | undefined;
81
+ //#endregion
82
+ export { type EmbeddablesSchemaRegistry, type FormsReactOptions, type RegisteredSchemas, forms, getRegisteredFormsClient, registerFormsClient, useForm, useFormErrors, useFormField };
80
83
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react/use-form-store.ts","../src/react/schema-registry.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/use-forms.ts","../src/react/forms-module.ts","../src/react/register-forms-client.ts"],"mappings":";;;KAKY,aAAa,gBAAgB,cAAc;EACrD,QAAQ,QAAQ,WAAW;EAC3B,QAAQ,YAAY;;iBAsBN,gBAAgB,gBAAgB,YAC9C,MAAM,aAAa,kBAClB,aAAa;;;;;;;;;;;;;;;;;;;;UCXC;;;;;;KAOL,oBAAoB;EAC9B,aAAa,iBAAiB;IAE5B,WACA;;;;;;;;;;;iBCfY,QACd,sBAAsB,mBACtB,iBAAiB,gBAAgB,qBAEjC,QACA;EAEA,QAAQ;EACR,oBAAoB,qBAAqB,SAAS;;EAElD,MAAM,aAAa,SAAS;EAC5B,QAAQ,kBAAkB,gBAAgB,SAAS;EACnD,QAAQ,kBAAkB,gBAAgB,SAAS;;;;iBCvBrC,cAAc,gBAAgB,YAC5C,MAAM,aAAa,kBAClB,YAAY;;;iBCKC,mBAAmB,gBAAgB,YAAY,UAAU,aAAa,YACpF,MACA;EAEA,MAAM,aAAa;EACnB,KAAK;;EAEL,OAAO,WAAW,SAAS;EAC3B;EACA,WAAW,OAAO,WAAW,SAAS;EACtC,cAAc,QAAQ,UAAU;;;;KChBtB,kBAAkB,iBAAiB,gBAAgB,iBAAiB,KAC9E,iBAAiB;;;iBCGH,YAAY,iBAAiB,eAC3C,SAAS,kBAAkB,YAC1B,uBAAuB,YAAY;;;iBCNtB,0BAA0B,iBAAiB,iBACzD,SACG,WACF,iBAAiB,YAAY,YAAY"}
1
+ {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react/use-form-store.ts","../src/react/schema-registry.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/use-forms.ts","../src/react/forms-module.ts","../src/react/register-forms-client.ts","../src/react/forms-registry.ts"],"mappings":";;;KAKY,aAAa,gBAAgB,cAAc;EACrD,QAAQ,QAAQ,WAAW;EAC3B,QAAQ,YAAY;;iBAsBN,gBAAgB,gBAAgB,YAC9C,MAAM,aAAa,kBAClB,aAAa;;;;;;;;;;;;;;;;;;;;UCXC;;;;;;KAOL,oBAAoB;EAC9B,aAAa,iBAAiB;IAE5B,WACA;;;;;;;;;;;iBCfY,QACd,sBAAsB,mBACtB,iBAAiB,gBAAgB,qBAEjC,QACA;EAEA,QAAQ;EACR,oBAAoB,qBAAqB,SAAS;;EAElD,MAAM,aAAa,SAAS;EAC5B,QAAQ,kBAAkB,gBAAgB,SAAS;EACnD,QAAQ,kBAAkB,gBAAgB,SAAS;;;;iBCvBrC,cAAc,gBAAgB,YAC5C,MAAM,aAAa,kBAClB,YAAY;;;iBCKC,mBAAmB,gBAAgB,YAAY,UAAU,aAAa,YACpF,MACA;EAEA,MAAM,aAAa;EACnB,KAAK;;EAEL,OAAO,WAAW,SAAS;EAC3B;EACA,WAAW,OAAO,WAAW,SAAS;EACtC,cAAc,QAAQ,UAAU;;;;KChBtB,kBAAkB,iBAAiB,gBAAgB,iBAAiB,KAC9E,iBAAiB;;;iBCGH,YAAY,iBAAiB,eAC3C,SAAS,kBAAkB,YAC1B,uBAAuB,YAAY;;;iBCNtB,0BAA0B,iBAAiB,iBACzD,SACG,WACF,iBAAiB,YAAY,YAAY;;;iBCC5B,yBAAyB,eAAe,YAAY"}
package/dist/react.js CHANGED
@@ -46,6 +46,9 @@ const FORMS_MODULE_KEY = "forms";
46
46
  * from provider context, not from here.
47
47
  */
48
48
  const formsByCore = /* @__PURE__ */ new WeakMap();
49
+ function getRegisteredFormsClient(core) {
50
+ return formsByCore.get(core);
51
+ }
49
52
  //#endregion
50
53
  //#region src/react/use-forms.ts
51
54
  /** @internal Product hooks read the module client from provider context. */
@@ -161,6 +164,6 @@ function forms(options) {
161
164
  };
162
165
  }
163
166
  //#endregion
164
- export { forms, registerFormsClient, useForm, useFormErrors, useFormField };
167
+ export { forms, getRegisteredFormsClient, registerFormsClient, useForm, useFormErrors, useFormField };
165
168
 
166
169
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useRef } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { CustomValidationsFor, FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n customValidations,\n}: {\n formId: TFormId\n customValidations?: CustomValidationsFor<TSchemas[TFormId]>\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n // * Held in a ref because only the first call for a form id builds the\n // * instance; a later render passing new validators must not look like a change.\n const customValidationsRef = useRef(customValidations)\n customValidationsRef.current = customValidations\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form =\n client === null\n ? null\n : client.getForm({ formId, customValidations: customValidationsRef.current })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n\n// prueba de la colisión\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K]) => void\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K]) => {\n setDraft(value)\n }, [])\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n onBlur,\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport function registerFormsClient<const TSchemas extends FormSchemaMap>({\n core,\n ...options\n}: InitFormsOptions<TSchemas>): FormsClient<TSchemas> {\n const existing = formsByCore.get(core) as FormsClient<TSchemas> | undefined\n if (existing !== undefined) return existing\n\n const analyticsInstance = options.analyticsInstance ?? resolveCoreAnalyticsInstance(core)\n\n const client = initForms({ ...options, core, analyticsInstance })\n formsByCore.set(core, client as FormsClient<FormSchemaMap>)\n return client\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsClient } from './register-forms-client.js'\n\nimport type { FormSchemaMap, FormsClient } from '../core/form.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\nexport function forms<const TSchemas extends FormSchemaMap>(\n options: FormsReactOptions<TSchemas>,\n): EmbeddablesReactModule<FormsClient<TSchemas>> {\n return {\n key: FORMS_MODULE_KEY,\n init: (core) => registerFormsClient({ core, ...options }),\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n\nexport function getRegisteredFormsClient(core: object): FormsClient<FormSchemaMap> | undefined {\n return formsByCore.get(core)\n}\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useRef } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { CustomValidationsFor, FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n customValidations,\n}: {\n formId: TFormId\n customValidations?: CustomValidationsFor<TSchemas[TFormId]>\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n // * Held in a ref because only the first call for a form id builds the\n // * instance; a later render passing new validators must not look like a change.\n const customValidationsRef = useRef(customValidations)\n customValidationsRef.current = customValidations\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form =\n client === null\n ? null\n : client.getForm({ formId, customValidations: customValidationsRef.current })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K]) => void\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K]) => {\n setDraft(value)\n }, [])\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n onBlur,\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport function registerFormsClient<const TSchemas extends FormSchemaMap>({\n core,\n ...options\n}: InitFormsOptions<TSchemas>): FormsClient<TSchemas> {\n const existing = formsByCore.get(core) as FormsClient<TSchemas> | undefined\n if (existing !== undefined) return existing\n\n const analyticsInstance = options.analyticsInstance ?? resolveCoreAnalyticsInstance(core)\n\n const client = initForms({ ...options, core, analyticsInstance })\n formsByCore.set(core, client as FormsClient<FormSchemaMap>)\n return client\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsClient } from './register-forms-client.js'\n\nimport type { FormSchemaMap, FormsClient } from '../core/form.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\nexport function forms<const TSchemas extends FormSchemaMap>(\n options: FormsReactOptions<TSchemas>,\n): EmbeddablesReactModule<FormsClient<TSchemas>> {\n return {\n key: FORMS_MODULE_KEY,\n init: (core) => registerFormsClient({ core, ...options }),\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddables/forms",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Schema-driven form state, local persistence, and analytics events for Embeddables funnels.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -53,8 +53,8 @@
53
53
  "typescript": "~6.0.2",
54
54
  "vitest": "^4.1.9",
55
55
  "@embeddables/core": "0.0.4",
56
- "backend-worker": "1.0.0",
57
- "@embeddables/shared-types": "1.0.0"
56
+ "@embeddables/shared-types": "1.0.0",
57
+ "backend-worker": "1.0.0"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsdown",
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-CbU5ezg7.d.ts","names":[],"sources":["../../shared-types/dist/json.types.d.ts","../src/core/config.ts","../../shared-types/dist/analytics-instance.types.d.ts","../src/core/form.ts"],"mappings":";;;;;;KAIY,+CAA+C;GACtD,cAAc;;;;KCHP;;UAMF;EACR;EACA;EACA;EACA;EACA;EACA;EACA,MAAM;;KAGI,eAAe,eAAe,YAAY,cAAc;EAClE,OAAO;EACP,QAAQ,SAAS,eAAe;;UAUxB,oBAAoB,cAAc;WACjC;WACA;WACA;WACA;WACA;;WAEA;;WAEA;WACA,iBAAiB;;WAEjB,SAAS,eAAe,iBAAiB;;UAG1C,eAAe,cAAc;WAC5B;WACA;WACA,MAAM;WACN,cAAc,oBAAoB;WAGlC;WACA;;KAUC,iBAAiB,KAAK,YAAY,eAAe,MAAK;KAEtD,sBAAsB,KAAK,YAAY,oBAAoB,MAAK;UAE3D;WACN;WACA;WACA,iBAAiB;;KAGvB,SAAS,gBAAgB,cAAc;KAEhC,aAAa,gBAAgB,cAAc,SAAS;KAEpD,WAAW,gBAAgB,iBACpC,KAAK,SAAS,YAAY,WAAW,iBAAiB;;KAI7C,gBAAgB,gBAAgB,eAAe,kBAAkB,uBAEzE,QAAQ,SAAS;EAAY;;;;;;;;;;;;;;;UCzEhB;EACb;GACC;;;UAGY;EACb;EACA;EACA;;;;;;;UAOa,kBAAkB,SAAS;EACxC,WAAW,OAAO,kBAAkB,WAAW,QAAQ;;EAEvD;EACA;;;;;KCAQ,YAAY,gBAAgB,cAAc,SACpD,QAAQ,OAAO,aAAa;UAGb,UAAU,gBAAgB;EACzC;EACA,QAAQ,YAAY;;EAEpB;;UAGe,aAAa,gBAAgB;EAC5C;EACA,QAAQ,YAAY;EACpB,QAAQ,QAAQ,WAAW;EAC3B;;UAGe,eAAe,gBAAgB;EAC9C;EACA,QAAQ,YAAY;EACpB,QAAQ,QAAQ,WAAW;;UAGZ,aAAa,gBAAgB;WACnC,KAAK;;;;;;;EAOd,IAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,UAAU;;EAE5D,IAAI,UAAU,aAAa,UAAU,KAAK,IAAI,WAAW,SAAS;;;;;;EAMlE,0BAA0B,UAAU,gBAAgB,UAClD,iBAAiB,IAEf,WAAW,SAAS,QAAQ;IAA6B,iBAAiB;;EAE9E,UAAU,QAAQ,WAAW;;;;;;;;;EAS7B,UAAU,QAAQ,aAAa;;;;;;;;;;EAU/B,SAAS,QAAQ,QAAQ,WAAW,YAAY,QAAQ,eAAe;EACvE,UAAU,YAAY;EACtB;;EAEA,UAAU;;;KA6CA,gBAAgB,SAAS,eAAe;KAExC,qBAAqB,gBAAgB,0BACrC,KAAK,aAAa,YAAY;UAOzB,iBAAiB,iBAAiB,gBAAgB;EACjE,MAAM;;EAEN,SAAS;EACT,oBAAoB;;UAGL,YAAY,iBAAiB,gBAAgB;;;;;;;;;;;;;EAa5D,QAAQ,gBAAgB,mBAAmB;IACzC,QAAQ;IACR,oBAAoB,qBAAqB,SAAS;MAChD,aAAa,SAAS;;;;;;iBAOZ,gBAAgB,iBAAiB,eAC/C,SAAS,iBAAiB,YACzB,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/errors.ts","../src/storage/storage.ts","../../shared-types/dist/analytics-ingest.types.d.ts","../src/core/analytics.ts"],"mappings":";;;;;;;;;;;;cAWa,mBAAmB;EAClB,YAAA,iBAAiB,UAAU;;;cAO5B,oBAAoB;EACnB,YAAA,iBAAiB,UAAU;;;cAO5B,uBAAuB;EACtB,YAAA,iBAAiB,UAAU;;;;;cCvB5B;UAEI;EACf,QAAQ;EACR,QAAQ,aAAa;EACrB,WAAW;;;;KCJD;;;;;;;;KAQA,oBAAoB;KACpB;EACR;EACA;;KAsBQ,oBAAoB;EAC5B;;EAEA;EACA,YAAY;EACZ,cAAc;;EAEd;;EAEA;;KAEQ;EACR;EACA;;KAEQ,mBAAmB;EAC3B;EACA,MAAM,eAAe;;KAMb,qBAAqB;EAC7B;EACA;;;;KCpCQ,sBAAsB,mBAAmB,oBAAoB"}