strapi-plugin-hubspot 0.15.0 → 0.16.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +28 -13
  3. package/dist/_chunks/{Forms-ClyI4MJ1.js → Forms-D0QXkYAN.js} +216 -112
  4. package/dist/_chunks/{Forms-B7eIm0Fi.mjs → Forms-DEoZGrEt.mjs} +218 -114
  5. package/dist/_chunks/{HubspotFormInput-Do7F83V6.js → HubspotFormInput-4ZMYXWM_.js} +1 -1
  6. package/dist/_chunks/{HubspotFormInput-cZAiaAaq.mjs → HubspotFormInput-CPep55GL.mjs} +1 -1
  7. package/dist/_chunks/{HubspotObjectInput-DWqk2C26.js → HubspotObjectInput-BTqYXJIa.js} +3 -3
  8. package/dist/_chunks/{HubspotObjectInput-ZZk_7hWq.mjs → HubspotObjectInput-CQszIx1W.mjs} +3 -3
  9. package/dist/_chunks/{HubspotPropertyInput-v9B3wb1a.js → HubspotPropertyInput-2MgIKlR_.js} +3 -3
  10. package/dist/_chunks/{HubspotPropertyInput-5bkDGmGo.mjs → HubspotPropertyInput-ClNph0f1.mjs} +3 -3
  11. package/dist/_chunks/{Settings-QcFbm3s5.mjs → Settings-CZ-qQSKT.mjs} +78 -5
  12. package/dist/_chunks/{Settings-X3EHnTaO.js → Settings-DhiHQC6U.js} +77 -4
  13. package/dist/_chunks/{en-ZHvaZLi7.js → en-CSgFmKmM.js} +16 -3
  14. package/dist/_chunks/{en-DMJeF7EN.mjs → en-Dh3bMS6N.mjs} +16 -3
  15. package/dist/_chunks/{fr-Ds_2EAzZ.js → fr-D_EhZS0P.js} +16 -3
  16. package/dist/_chunks/{fr-CVAntLmc.mjs → fr-aWOIOlS9.mjs} +16 -3
  17. package/dist/_chunks/{index-BpcilDy1.js → index-ciliGv0E.js} +6 -6
  18. package/dist/_chunks/{index-BSJAhj8r.mjs → index-gKM4jVf8.mjs} +6 -6
  19. package/dist/_chunks/{objectLabels-CYXXFpNB.js → objectLabels-BjYfdhRB.js} +1 -1
  20. package/dist/_chunks/{objectLabels-CrYRzsqe.mjs → objectLabels-DQpSLUaJ.mjs} +1 -1
  21. package/dist/_chunks/{useHubspotSchema-CBF4GkmK.mjs → useHubspotSchema-CE7tJgOM.mjs} +1 -1
  22. package/dist/_chunks/{useHubspotSchema-Ba6xZUCL.js → useHubspotSchema-KKVppjC2.js} +1 -1
  23. package/dist/admin/index.js +1 -1
  24. package/dist/admin/index.mjs +1 -1
  25. package/dist/admin/src/builder/Sortable.d.ts +25 -12
  26. package/dist/admin/src/builder/__tests__/relocateField.test.d.ts +1 -0
  27. package/dist/admin/src/builder/relocateField.d.ts +14 -0
  28. package/dist/server/index.js +216 -7
  29. package/dist/server/index.mjs +216 -7
  30. package/dist/server/src/__tests__/formSync.test.d.ts +1 -0
  31. package/dist/server/src/formSync.d.ts +37 -0
  32. package/dist/server/src/forms.d.ts +6 -8
  33. package/dist/server/src/hsforms.d.ts +8 -0
  34. package/dist/server/src/importHubspot.d.ts +4 -0
  35. package/dist/server/src/index.d.ts +6 -0
  36. package/dist/server/src/settings.d.ts +32 -3
  37. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Policy helpers for HubSpot form field sync and leftover CRM writes.
3
+ *
4
+ * Two independent user wills, neither assumed:
5
+ * - Strapi-first: mapped contact fields missing on the HubSpot form can be
6
+ * PATCHed onto it at publish time (opt-in).
7
+ * - HubSpot-first: extras must not 400 the Forms API submit, but they can
8
+ * still land on the contact via a CRM upsert of the leftovers.
9
+ *
10
+ * CRM properties are never created here — the picker only maps to ones that
11
+ * already exist on the portal.
12
+ */
13
+ import type { FormDefinition } from "./conditions";
14
+ export interface MappedFormField {
15
+ name: string;
16
+ label?: string;
17
+ type: string;
18
+ required?: boolean;
19
+ object?: string;
20
+ property: string;
21
+ options?: {
22
+ value?: string;
23
+ label?: string;
24
+ }[];
25
+ }
26
+ export declare function leftoverContactProps(contact: Record<string, string>, sentFieldNames: Iterable<string>): Record<string, string>;
27
+ export declare function mappedContactFields(definition: FormDefinition): MappedFormField[];
28
+ export declare function missingMappedFields(definition: FormDefinition, existingNames: Iterable<string>): MappedFormField[];
29
+ export declare function hsFieldType(type: string, options?: unknown[]): string;
30
+ export declare function toHsFormField(field: MappedFormField): Record<string, unknown>;
31
+ export interface RawFieldGroupLike {
32
+ groupType?: string;
33
+ richTextType?: string;
34
+ richText?: string;
35
+ fields?: unknown[];
36
+ }
37
+ export declare function appendFieldGroups(existing: RawFieldGroupLike[], extra: MappedFormField[]): Record<string, unknown>[];
@@ -2,14 +2,12 @@
2
2
  * The form-builder server side: shaping a published form for the public API,
3
3
  * and turning a submission into a HubSpot marketing-form conversion.
4
4
  *
5
- * The pipeline: post the answers to HubSpot's Forms API (native conversion,
6
- * workflows, original source, lead-center notifications) with the visitor's
7
- * `hutk`; look the contact back up by email; when the email is on a corporate
8
- * domain the company is found-or-created by `domain` and associated. A CRM
9
- * contact upsert is the fallback when no form GUID is configured — it does
10
- * not count as a form conversion. Timeline notes are opt-in and only used
11
- * on that fallback. Company sync is best-effort; the form submission is
12
- * the lead.
5
+ * The pipeline is a policy the install picks in Settings: native Forms API
6
+ * conversion (with leftover CRM writes for mapped fields the HubSpot form
7
+ * dropped), CRM upsert only, or automatic (Forms API when a GUID is linked).
8
+ * Company find-or-create still runs after a successful submit. Timeline notes
9
+ * are opt-in and only used on the CRM-upsert path. The form submission is
10
+ * the lead — a CRM outage never loses it.
13
11
  */
14
12
  import type { Core } from "@strapi/strapi";
15
13
  import { type FormDefinition, type Primitive } from "./conditions";
@@ -10,6 +10,7 @@
10
10
  * Portal id, region and form GUIDs are configuration — a test portal today,
11
11
  * production tomorrow, no code change.
12
12
  */
13
+ import type { FormDefinition } from "./conditions";
13
14
  export declare function isFormGuid(value: string): boolean;
14
15
  export declare function sanitizeHutk(value: unknown): string | undefined;
15
16
  export declare function sanitizePortalId(value: unknown): string | undefined;
@@ -58,6 +59,7 @@ export interface FormSubmitInput {
58
59
  };
59
60
  formHasLegalConsent?: boolean;
60
61
  }
62
+ export declare function clearFormShapeCache(formGuid?: string): void;
61
63
  export declare function loadFormShape(apiKey: string, formGuid: string): Promise<{
62
64
  names: Set<string>;
63
65
  hasLegalConsent: boolean;
@@ -73,3 +75,9 @@ export declare function submitMarketingForm(input: FormSubmitInput): Promise<{
73
75
  status: number;
74
76
  error?: string;
75
77
  }>;
78
+ /**
79
+ * Opt-in: add mapped contact fields that the HubSpot form doesn't already
80
+ * declare. Never creates CRM properties — only form fields pointing at ones
81
+ * that already exist. Returns the property names that were added.
82
+ */
83
+ export declare function syncFieldsToHubspotForm(apiKey: string, formGuid: string, definition: FormDefinition): Promise<string[]>;
@@ -83,4 +83,8 @@ export declare function convertHubspotForm(raw: RawHubspotForm): ConvertedHubspo
83
83
  export declare function listHubspotForms(apiKey: string): Promise<HubspotFormSummary[]>;
84
84
  /** One form, full definition. */
85
85
  export declare function fetchHubspotForm(apiKey: string, formId: string): Promise<RawHubspotForm>;
86
+ /** Replace `fieldGroups` (HubSpot PATCH is a full replacement of that array). */
87
+ export declare function patchHubspotForm(apiKey: string, formId: string, body: {
88
+ fieldGroups: unknown[];
89
+ }): Promise<RawHubspotForm>;
86
90
  export {};
@@ -13,6 +13,9 @@ declare const _default: {
13
13
  companyFromDomain: boolean;
14
14
  timelineNote: boolean;
15
15
  defaultFormId: string;
16
+ submissionMode: "auto" | "crm" | "forms";
17
+ writeExtraProperties: boolean;
18
+ syncFieldsOnPublish: boolean;
16
19
  rateLimit: {
17
20
  submitPerMinute: number;
18
21
  searchPerMinute: number;
@@ -364,6 +367,9 @@ declare const _default: {
364
367
  portalId?: string;
365
368
  region?: string;
366
369
  defaultFormId?: string;
370
+ submissionMode?: string;
371
+ writeExtraProperties?: boolean;
372
+ syncFieldsOnPublish?: boolean;
367
373
  };
368
374
  };
369
375
  body: unknown;
@@ -15,11 +15,23 @@ export declare const ENV_PORTAL = "HUBSPOT_PORTAL_ID";
15
15
  export declare const ENV_REGION = "HUBSPOT_REGION";
16
16
  export declare const ENV_DEFAULT_FORM = "HUBSPOT_DEFAULT_FORM_ID";
17
17
  export type SettingSource = "settings" | "config" | "env" | null;
18
+ export type SubmissionMode = "auto" | "crm" | "forms";
18
19
  export interface StoredSettings {
19
20
  apiKey?: string;
20
21
  portalId?: string;
21
22
  region?: string;
22
23
  defaultFormId?: string;
24
+ submissionMode?: SubmissionMode;
25
+ writeExtraProperties?: boolean;
26
+ syncFieldsOnPublish?: boolean;
27
+ }
28
+ export interface HubspotPolicy {
29
+ /** How submissions reach HubSpot. `auto` uses Forms API when a GUID is set. */
30
+ submissionMode: SubmissionMode;
31
+ /** After a Forms API conversion, CRM-write mapped contact props the form dropped. */
32
+ writeExtraProperties: boolean;
33
+ /** On publish, PATCH missing mapped contact fields onto the HubSpot form. */
34
+ syncFieldsOnPublish: boolean;
23
35
  }
24
36
  export interface PublicSettings {
25
37
  configured: boolean;
@@ -33,6 +45,9 @@ export interface PublicSettings {
33
45
  portalSource: SettingSource;
34
46
  regionSource: SettingSource;
35
47
  formSource: SettingSource;
48
+ submissionMode: SubmissionMode;
49
+ writeExtraProperties: boolean;
50
+ syncFieldsOnPublish: boolean;
36
51
  }
37
52
  export interface HubspotAccount {
38
53
  portalId: string;
@@ -41,6 +56,15 @@ export interface HubspotAccount {
41
56
  }
42
57
  export declare function getStoredSettings(strapi: Core.Strapi): Promise<StoredSettings>;
43
58
  export declare function setStoredSettings(strapi: Core.Strapi, value: StoredSettings | null): Promise<void>;
59
+ export declare function asSubmissionMode(value: unknown): SubmissionMode | undefined;
60
+ interface FormsCfg {
61
+ defaultFormId?: string;
62
+ submissionMode?: unknown;
63
+ writeExtraProperties?: unknown;
64
+ syncFieldsOnPublish?: unknown;
65
+ }
66
+ export declare function policyFrom(stored: StoredSettings, forms: FormsCfg): HubspotPolicy;
67
+ export declare function resolvePolicy(strapi: Core.Strapi): Promise<HubspotPolicy>;
44
68
  export declare function resolveApiKey(strapi: Core.Strapi): Promise<{
45
69
  apiKey: string;
46
70
  source: SettingSource;
@@ -48,12 +72,17 @@ export declare function resolveApiKey(strapi: Core.Strapi): Promise<{
48
72
  /** Portal / region / default marketing form — settings UI overrides env. */
49
73
  export declare function resolveAccount(strapi: Core.Strapi): Promise<HubspotAccount>;
50
74
  export declare function publicSettings(strapi: Core.Strapi): Promise<PublicSettings>;
51
- /** Merge a PATCH-like body into the store without dropping the saved key. */
52
- export declare function patchStoredSettings(strapi: Core.Strapi, body: {
75
+ export interface SettingsPatch {
53
76
  apiKey?: string;
54
77
  portalId?: unknown;
55
78
  region?: unknown;
56
79
  defaultFormId?: unknown;
57
- }): Promise<void>;
80
+ submissionMode?: unknown;
81
+ writeExtraProperties?: unknown;
82
+ syncFieldsOnPublish?: unknown;
83
+ }
84
+ /** Merge a PATCH-like body into the store without dropping the saved key. */
85
+ export declare function patchStoredSettings(strapi: Core.Strapi, body: SettingsPatch): Promise<void>;
58
86
  /** Drop only the stored token; portal / form settings stay. */
59
87
  export declare function clearStoredApiKey(strapi: Core.Strapi): Promise<void>;
88
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-hubspot",
3
- "version": "0.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "The form builder for HubSpot in Strapi — multi-step forms with conditional fields mapped to real CRM properties, a public submit pipeline with retries, and save-time mapping validation.",
5
5
  "keywords": [
6
6
  "strapi",