strapi-plugin-hubspot 0.16.7 → 0.16.9
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.
- package/CHANGELOG.md +26 -0
- package/dist/_chunks/{Forms-DyV6KyiO.mjs → Forms-CUNHDvhI.mjs} +52 -6
- package/dist/_chunks/{Forms-uUN4XjhW.js → Forms-CylJ0nD3.js} +52 -6
- package/dist/_chunks/{HubspotFormInput-BkWat_wu.js → HubspotFormInput-D2H2u9fZ.js} +1 -1
- package/dist/_chunks/{HubspotFormInput-DEXPX-6j.mjs → HubspotFormInput-Dppgr_rd.mjs} +1 -1
- package/dist/_chunks/{HubspotObjectInput-CFDYW-TM.js → HubspotObjectInput-BjRCaqXr.js} +3 -3
- package/dist/_chunks/{HubspotObjectInput-DL8fYK2x.mjs → HubspotObjectInput-CWFddNNw.mjs} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-CFMkbQeT.js → HubspotPropertyInput-BlDarkcj.js} +3 -3
- package/dist/_chunks/{HubspotPropertyInput-WU5FMJj3.mjs → HubspotPropertyInput-TGG9ozTa.mjs} +3 -3
- package/dist/_chunks/{Settings-B_hhdpHa.js → Settings-DOx7YSqA.js} +2 -2
- package/dist/_chunks/{Settings-CiXEps_C.mjs → Settings-tct49GYU.mjs} +2 -2
- package/dist/_chunks/{index-BeBn7bJa.mjs → index-Au3W85GX.mjs} +5 -5
- package/dist/_chunks/{index-CG_WQzSa.js → index-DhGVA3ZL.js} +5 -5
- package/dist/_chunks/{objectLabels-CS_0QCNz.mjs → objectLabels-3rnJ0WUc.mjs} +1 -1
- package/dist/_chunks/{objectLabels-BGq-Ypgn.js → objectLabels-Bx7x8fFO.js} +1 -1
- package/dist/_chunks/{useHubspotSchema-CP7LRdzO.mjs → useHubspotSchema-BlVXfwju.mjs} +1 -1
- package/dist/_chunks/{useHubspotSchema-BZP2qkAu.js → useHubspotSchema-DrGeidXC.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/builder/types.d.ts +2 -1
- package/dist/server/index.js +243 -41
- package/dist/server/index.mjs +243 -41
- package/dist/server/src/conditions.d.ts +1 -0
- package/dist/server/src/formSync.d.ts +20 -0
- package/dist/server/src/formsAdmin.d.ts +1 -0
- package/dist/server/src/hsforms.d.ts +14 -1
- package/dist/server/src/importHubspot.d.ts +2 -1
- package/dist/server/src/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1599,6 +1599,48 @@ function missingMappedFields(definition, existingNames) {
|
|
|
1599
1599
|
const existing = new Set(existingNames);
|
|
1600
1600
|
return mappedContactFields(definition).filter((field) => !existing.has(field.property));
|
|
1601
1601
|
}
|
|
1602
|
+
const OBJECT_TYPE_BY_NAME = {
|
|
1603
|
+
contact: "0-1",
|
|
1604
|
+
company: "0-2"
|
|
1605
|
+
};
|
|
1606
|
+
function collectedHubspotKeys(definition) {
|
|
1607
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1608
|
+
const add = (object, property) => {
|
|
1609
|
+
const typeId = OBJECT_TYPE_BY_NAME[object] || object;
|
|
1610
|
+
if (property) keys.add(`${typeId}:${property}`);
|
|
1611
|
+
};
|
|
1612
|
+
for (const step of definition.steps ?? []) {
|
|
1613
|
+
for (const fld of step.fields ?? []) {
|
|
1614
|
+
if (fld.type === "company") {
|
|
1615
|
+
const map = fld.companyMap ?? {};
|
|
1616
|
+
for (const slot of Object.values(map)) {
|
|
1617
|
+
if (slot?.property?.trim()) add(slot.object?.trim() || "company", slot.property.trim());
|
|
1618
|
+
}
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
const mapping = fld.hubspot ?? {};
|
|
1622
|
+
const object = mapping.object?.trim() || "contact";
|
|
1623
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1624
|
+
if (property) add(object, property);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return keys;
|
|
1628
|
+
}
|
|
1629
|
+
function missingRequiredHubspotFields(definition, required) {
|
|
1630
|
+
const collected = collectedHubspotKeys(definition);
|
|
1631
|
+
const missing = [];
|
|
1632
|
+
for (const field of required) {
|
|
1633
|
+
if (!field.name) continue;
|
|
1634
|
+
const objectTypeId = field.objectTypeId?.trim() || "0-1";
|
|
1635
|
+
if (collected.has(`${objectTypeId}:${field.name}`)) continue;
|
|
1636
|
+
missing.push({
|
|
1637
|
+
name: field.name,
|
|
1638
|
+
objectTypeId,
|
|
1639
|
+
object: objectTypeId === "0-2" ? "company" : "contact"
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
return missing;
|
|
1643
|
+
}
|
|
1602
1644
|
function hsFieldType(type, options) {
|
|
1603
1645
|
switch (type) {
|
|
1604
1646
|
case "email":
|
|
@@ -1649,6 +1691,74 @@ function appendFieldGroups(existing, extra) {
|
|
|
1649
1691
|
}))
|
|
1650
1692
|
];
|
|
1651
1693
|
}
|
|
1694
|
+
function conditionToHubspot(condition, parentId) {
|
|
1695
|
+
const rules = (condition.rules ?? []).filter((rule) => rule.field === parentId);
|
|
1696
|
+
if (!rules.length || rules.length !== (condition.rules ?? []).length) return null;
|
|
1697
|
+
const op = rules[0]?.operator;
|
|
1698
|
+
if (rules.some((rule) => rule.operator !== op)) return null;
|
|
1699
|
+
const values = rules.map((rule) => rule.value).filter((value) => Boolean(value));
|
|
1700
|
+
switch (op) {
|
|
1701
|
+
case "eq":
|
|
1702
|
+
return { operator: values.length > 1 ? "SET_ANY" : "EQ", values };
|
|
1703
|
+
case "neq":
|
|
1704
|
+
return { operator: values.length > 1 ? "NOT_SET_ANY" : "NEQ", values };
|
|
1705
|
+
case "contains":
|
|
1706
|
+
return { operator: "CONTAINS", values };
|
|
1707
|
+
case "gt":
|
|
1708
|
+
return { operator: "GT", values };
|
|
1709
|
+
case "lt":
|
|
1710
|
+
return { operator: "LT", values };
|
|
1711
|
+
case "notEmpty":
|
|
1712
|
+
return { operator: "SET", values: [] };
|
|
1713
|
+
case "empty":
|
|
1714
|
+
return { operator: "NOT_SET", values: [] };
|
|
1715
|
+
default:
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
function applyVisibleIfToFieldGroups(groups, definition) {
|
|
1720
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1721
|
+
for (const step of definition.steps ?? []) {
|
|
1722
|
+
for (const fld of step.fields ?? []) {
|
|
1723
|
+
const mapping = fld.hubspot ?? {};
|
|
1724
|
+
byId.set(fld.id, { name: fld.name, property: mapping.property?.trim() || fld.name });
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
const moves = /* @__PURE__ */ new Map();
|
|
1728
|
+
for (const step of definition.steps ?? []) {
|
|
1729
|
+
for (const fld of step.fields ?? []) {
|
|
1730
|
+
const visibleIf = fld.visibleIf;
|
|
1731
|
+
const parentId = visibleIf?.rules?.[0]?.field;
|
|
1732
|
+
if (!visibleIf || !parentId) continue;
|
|
1733
|
+
const converted = conditionToHubspot(visibleIf, parentId);
|
|
1734
|
+
const parent = byId.get(parentId);
|
|
1735
|
+
const mapping = fld.hubspot ?? {};
|
|
1736
|
+
const property = mapping.property?.trim() || fld.name;
|
|
1737
|
+
if (!converted || !parent) continue;
|
|
1738
|
+
moves.set(property, { parent: parent.property, ...converted });
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
if (!moves.size) return groups;
|
|
1742
|
+
const cloned = JSON.parse(JSON.stringify(groups));
|
|
1743
|
+
const flat = [];
|
|
1744
|
+
for (const group of cloned) {
|
|
1745
|
+
(group.fields ?? []).forEach((field, index2) => flat.push({ group, index: index2, field }));
|
|
1746
|
+
}
|
|
1747
|
+
for (const [property, move] of moves) {
|
|
1748
|
+
const child = flat.find((item) => item.field.name === property);
|
|
1749
|
+
const parent = flat.find((item) => item.field.name === move.parent);
|
|
1750
|
+
if (!child || !parent || child === parent) continue;
|
|
1751
|
+
parent.field.dependentFields = [
|
|
1752
|
+
...parent.field.dependentFields ?? [],
|
|
1753
|
+
{
|
|
1754
|
+
dependentCondition: { operator: move.operator, values: move.values },
|
|
1755
|
+
field: child.field
|
|
1756
|
+
}
|
|
1757
|
+
];
|
|
1758
|
+
child.group.fields?.splice(child.index, 1);
|
|
1759
|
+
}
|
|
1760
|
+
return cloned;
|
|
1761
|
+
}
|
|
1652
1762
|
const HS_BASE$2 = "https://api.hubapi.com";
|
|
1653
1763
|
const OBJECT_BY_TYPE_ID = {
|
|
1654
1764
|
"0-1": "contact",
|
|
@@ -1730,7 +1840,7 @@ function convertField(raw, ctx) {
|
|
|
1730
1840
|
helpText: raw.description || void 0,
|
|
1731
1841
|
...options.length ? { options } : {},
|
|
1732
1842
|
hubspot: { object, property: name },
|
|
1733
|
-
...ctx.parentCondition ? { visibleIf: ctx.parentCondition } : {}
|
|
1843
|
+
...ctx.parentCondition ? { visibleIf: ctx.parentCondition, visibleIfSource: "hubspot" } : {}
|
|
1734
1844
|
};
|
|
1735
1845
|
const out = [field];
|
|
1736
1846
|
for (const dependent of raw.dependentFields ?? []) {
|
|
@@ -1753,6 +1863,12 @@ function convertHubspotForm(raw) {
|
|
|
1753
1863
|
const usedNames = /* @__PURE__ */ new Set();
|
|
1754
1864
|
const fields = [];
|
|
1755
1865
|
for (const group of raw.fieldGroups ?? []) {
|
|
1866
|
+
if (group.groupType === "progressive" || group.groupType === "queued") {
|
|
1867
|
+
skipped.push({
|
|
1868
|
+
code: "progressive",
|
|
1869
|
+
detail: group.groupType
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1756
1872
|
if (group.richText && !(group.fields ?? []).length) {
|
|
1757
1873
|
skipped.push({ code: "rich-text", detail: group.richText.slice(0, 80) });
|
|
1758
1874
|
continue;
|
|
@@ -1824,7 +1940,10 @@ function mergeHubspotImport(current, incoming) {
|
|
|
1824
1940
|
existing.helpText = next.helpText;
|
|
1825
1941
|
if (next.options) existing.options = next.options;
|
|
1826
1942
|
else delete existing.options;
|
|
1827
|
-
if (
|
|
1943
|
+
if (next.visibleIf && (existing.visibleIfSource === "hubspot" || !existing.visibleIf)) {
|
|
1944
|
+
existing.visibleIf = next.visibleIf;
|
|
1945
|
+
if (existing.visibleIfSource !== "editor") existing.visibleIfSource = "hubspot";
|
|
1946
|
+
}
|
|
1828
1947
|
updated.push(key);
|
|
1829
1948
|
continue;
|
|
1830
1949
|
}
|
|
@@ -2047,24 +2166,20 @@ async function loadFormShape(apiKey, formGuid) {
|
|
|
2047
2166
|
return null;
|
|
2048
2167
|
}
|
|
2049
2168
|
}
|
|
2050
|
-
async function findContactIdByEmail(apiKey, email) {
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
});
|
|
2065
|
-
const body = await res.json().catch(() => ({}));
|
|
2066
|
-
if (!res.ok) return void 0;
|
|
2067
|
-
return body.results?.[0]?.id;
|
|
2169
|
+
async function findContactIdByEmail(apiKey, email, retryDelaysMs = [400, 1200]) {
|
|
2170
|
+
for (const delay of [0, ...retryDelaysMs]) {
|
|
2171
|
+
if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2172
|
+
const res = await fetch(
|
|
2173
|
+
`https://api.hubapi.com/crm/v3/objects/contacts/${encodeURIComponent(email)}?idProperty=email`,
|
|
2174
|
+
{ headers: { Authorization: `Bearer ${apiKey}` } }
|
|
2175
|
+
);
|
|
2176
|
+
if (res.ok) {
|
|
2177
|
+
const body = await res.json().catch(() => ({}));
|
|
2178
|
+
return body.id || void 0;
|
|
2179
|
+
}
|
|
2180
|
+
if (res.status !== 404) return void 0;
|
|
2181
|
+
}
|
|
2182
|
+
return void 0;
|
|
2068
2183
|
}
|
|
2069
2184
|
function contextPayload(ctx) {
|
|
2070
2185
|
const out = {};
|
|
@@ -2145,13 +2260,62 @@ async function submitMarketingForm(input) {
|
|
|
2145
2260
|
async function syncFieldsToHubspotForm(apiKey, formGuid, definition) {
|
|
2146
2261
|
const raw = await fetchHubspotForm(apiKey, formGuid);
|
|
2147
2262
|
const extra = missingMappedFields(definition, formFieldNames(raw));
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2263
|
+
const fieldGroups = applyVisibleIfToFieldGroups(
|
|
2264
|
+
appendFieldGroups(raw.fieldGroups ?? [], extra),
|
|
2265
|
+
definition
|
|
2266
|
+
);
|
|
2267
|
+
if (!extra.length && JSON.stringify(fieldGroups) === JSON.stringify(appendFieldGroups(raw.fieldGroups ?? [], []))) {
|
|
2268
|
+
return [];
|
|
2269
|
+
}
|
|
2270
|
+
await patchHubspotForm(apiKey, formGuid, { fieldGroups });
|
|
2152
2271
|
clearFormShapeCache(formGuid);
|
|
2153
2272
|
return extra.map((field) => field.property);
|
|
2154
2273
|
}
|
|
2274
|
+
async function createMarketingForm(apiKey, name, definition) {
|
|
2275
|
+
const fields = mappedContactFields(definition).map((field) => toHsFormField(field));
|
|
2276
|
+
if (!fields.length) {
|
|
2277
|
+
throw new Error("Add at least one mapped contact field before creating a HubSpot form");
|
|
2278
|
+
}
|
|
2279
|
+
const res = await fetch("https://api.hubapi.com/marketing/v3/forms", {
|
|
2280
|
+
method: "POST",
|
|
2281
|
+
headers: {
|
|
2282
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2283
|
+
"Content-Type": "application/json"
|
|
2284
|
+
},
|
|
2285
|
+
body: JSON.stringify({
|
|
2286
|
+
name,
|
|
2287
|
+
formType: "hubspot",
|
|
2288
|
+
fieldGroups: applyVisibleIfToFieldGroups(
|
|
2289
|
+
fields.map((field) => ({
|
|
2290
|
+
groupType: "default_group",
|
|
2291
|
+
richTextType: "text",
|
|
2292
|
+
fields: [field]
|
|
2293
|
+
})),
|
|
2294
|
+
definition
|
|
2295
|
+
),
|
|
2296
|
+
configuration: {
|
|
2297
|
+
language: "fr",
|
|
2298
|
+
recaptchaEnabled: false,
|
|
2299
|
+
createNewContactForNewEmail: true,
|
|
2300
|
+
prePopulateKnownValues: true,
|
|
2301
|
+
notifyContactOwner: false,
|
|
2302
|
+
notifyRecipients: [],
|
|
2303
|
+
editable: true,
|
|
2304
|
+
archivable: true,
|
|
2305
|
+
cloneable: true,
|
|
2306
|
+
allowLinkToResetKnownValues: false,
|
|
2307
|
+
postSubmitAction: { type: "thank_you", value: "Merci." }
|
|
2308
|
+
},
|
|
2309
|
+
displayOptions: { submitButtonText: "Envoyer", style: { submitButtonText: "Envoyer" } },
|
|
2310
|
+
legalConsentOptions: { type: "none" }
|
|
2311
|
+
})
|
|
2312
|
+
});
|
|
2313
|
+
const body = await res.json().catch(() => ({}));
|
|
2314
|
+
if (!res.ok || !body.id) {
|
|
2315
|
+
throw new Error(body.message || `HubSpot ${res.status}`);
|
|
2316
|
+
}
|
|
2317
|
+
return body.id;
|
|
2318
|
+
}
|
|
2155
2319
|
function publicForm(entry) {
|
|
2156
2320
|
return {
|
|
2157
2321
|
slug: entry.slug,
|
|
@@ -2165,7 +2329,9 @@ function publicForm(entry) {
|
|
|
2165
2329
|
steps: (entry.definition?.steps ?? []).map((step) => ({
|
|
2166
2330
|
...step,
|
|
2167
2331
|
// The CRM mappings are the server's business — never the browser's.
|
|
2168
|
-
fields: (step.fields ?? []).map(
|
|
2332
|
+
fields: (step.fields ?? []).map(
|
|
2333
|
+
({ hubspot: _hubspot, companyMap: _companyMap, visibleIfSource: _source, ...fld }) => fld
|
|
2334
|
+
)
|
|
2169
2335
|
}))
|
|
2170
2336
|
};
|
|
2171
2337
|
}
|
|
@@ -2909,6 +3075,22 @@ function createFormsAdminController(strapi) {
|
|
|
2909
3075
|
ctx.body = { errors: structural, problems: mappings };
|
|
2910
3076
|
return;
|
|
2911
3077
|
}
|
|
3078
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3079
|
+
const account = await resolveAccount(strapi);
|
|
3080
|
+
const ownGuid = typeof entry.hubspotFormId === "string" ? entry.hubspotFormId.trim() : "";
|
|
3081
|
+
const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(account.defaultFormId) ? account.defaultFormId.trim() : "";
|
|
3082
|
+
if (apiKey && formGuid) {
|
|
3083
|
+
const shape = await loadFormShape(apiKey, formGuid);
|
|
3084
|
+
const missingHubspot = shape ? missingRequiredHubspotFields(
|
|
3085
|
+
entry.definition ?? EMPTY_DEFINITION,
|
|
3086
|
+
[...shape.fields.filter((field) => field.required)]
|
|
3087
|
+
) : [];
|
|
3088
|
+
if (missingHubspot.length) {
|
|
3089
|
+
ctx.status = 400;
|
|
3090
|
+
ctx.body = { errors: structural, problems: mappings, missingHubspot };
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
2912
3094
|
await documents().publish({
|
|
2913
3095
|
documentId: ctx.params.documentId,
|
|
2914
3096
|
locale: ctx.query.locale
|
|
@@ -2916,22 +3098,16 @@ function createFormsAdminController(strapi) {
|
|
|
2916
3098
|
const policy = await resolvePolicy(strapi);
|
|
2917
3099
|
let addedFields = [];
|
|
2918
3100
|
let syncWarning;
|
|
2919
|
-
if (policy.syncFieldsOnPublish) {
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
entry.definition ?? EMPTY_DEFINITION
|
|
2930
|
-
);
|
|
2931
|
-
} catch (err) {
|
|
2932
|
-
syncWarning = err.message;
|
|
2933
|
-
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2934
|
-
}
|
|
3101
|
+
if (policy.syncFieldsOnPublish && apiKey && formGuid) {
|
|
3102
|
+
try {
|
|
3103
|
+
addedFields = await syncFieldsToHubspotForm(
|
|
3104
|
+
apiKey,
|
|
3105
|
+
formGuid,
|
|
3106
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3107
|
+
);
|
|
3108
|
+
} catch (err) {
|
|
3109
|
+
syncWarning = err.message;
|
|
3110
|
+
strapi.log.warn(`[hubspot] field sync on publish failed — ${syncWarning}`);
|
|
2935
3111
|
}
|
|
2936
3112
|
}
|
|
2937
3113
|
ctx.body = {
|
|
@@ -2940,6 +3116,31 @@ function createFormsAdminController(strapi) {
|
|
|
2940
3116
|
...syncWarning ? { syncWarning } : {}
|
|
2941
3117
|
};
|
|
2942
3118
|
},
|
|
3119
|
+
async createInHubspot(ctx) {
|
|
3120
|
+
const entry = await documents().findOne({
|
|
3121
|
+
documentId: ctx.params.documentId,
|
|
3122
|
+
locale: ctx.query.locale,
|
|
3123
|
+
status: "draft"
|
|
3124
|
+
});
|
|
3125
|
+
if (!entry) ctx.throw(404, "Form not found");
|
|
3126
|
+
const { apiKey } = await resolveApiKey(strapi);
|
|
3127
|
+
if (!apiKey) ctx.throw(400, "HubSpot API key is missing");
|
|
3128
|
+
try {
|
|
3129
|
+
const id = await createMarketingForm(
|
|
3130
|
+
apiKey,
|
|
3131
|
+
entry.name || entry.slug || "Formulaire",
|
|
3132
|
+
entry.definition ?? EMPTY_DEFINITION
|
|
3133
|
+
);
|
|
3134
|
+
const updated = await documents().update({
|
|
3135
|
+
documentId: ctx.params.documentId,
|
|
3136
|
+
locale: ctx.query.locale,
|
|
3137
|
+
data: { hubspotFormId: id }
|
|
3138
|
+
});
|
|
3139
|
+
ctx.body = { form: updated, hubspotFormId: id };
|
|
3140
|
+
} catch (err) {
|
|
3141
|
+
ctx.throw(400, err.message);
|
|
3142
|
+
}
|
|
3143
|
+
},
|
|
2943
3144
|
async unpublish(ctx) {
|
|
2944
3145
|
await documents().unpublish({
|
|
2945
3146
|
documentId: ctx.params.documentId,
|
|
@@ -3706,6 +3907,7 @@ const routes = {
|
|
|
3706
3907
|
adminRoute("GET", "/builder/forms/:documentId", "formsAdmin.findOne", [FORMS_ACTION]),
|
|
3707
3908
|
adminRoute("PUT", "/builder/forms/:documentId", "formsAdmin.update", [FORMS_ACTION]),
|
|
3708
3909
|
adminRoute("POST", "/builder/forms/:documentId/publish", "formsAdmin.publish", [FORMS_ACTION]),
|
|
3910
|
+
adminRoute("POST", "/builder/forms/:documentId/create-hubspot", "formsAdmin.createInHubspot", [FORMS_ACTION]),
|
|
3709
3911
|
adminRoute("POST", "/builder/forms/:documentId/unpublish", "formsAdmin.unpublish", [FORMS_ACTION]),
|
|
3710
3912
|
adminRoute("DELETE", "/builder/forms/:documentId", "formsAdmin.remove", [FORMS_ACTION]),
|
|
3711
3913
|
adminRoute("POST", "/builder/forms/:documentId/duplicate", "formsAdmin.duplicate", [FORMS_ACTION]),
|
|
@@ -26,6 +26,21 @@ export interface MappedFormField {
|
|
|
26
26
|
export declare function leftoverContactProps(contact: Record<string, string>, sentFieldNames: Iterable<string>): Record<string, string>;
|
|
27
27
|
export declare function mappedContactFields(definition: FormDefinition): MappedFormField[];
|
|
28
28
|
export declare function missingMappedFields(definition: FormDefinition, existingNames: Iterable<string>): MappedFormField[];
|
|
29
|
+
export interface MissingHubspotRequired {
|
|
30
|
+
name: string;
|
|
31
|
+
objectTypeId: string;
|
|
32
|
+
object: string;
|
|
33
|
+
}
|
|
34
|
+
/** Properties the Strapi form can actually send (`objectTypeId:property`). */
|
|
35
|
+
export declare function collectedHubspotKeys(definition: FormDefinition): Set<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Required marketing-form fields the Strapi form never collects. HubSpot
|
|
38
|
+
* rejects the whole conversion when one of these is absent.
|
|
39
|
+
*/
|
|
40
|
+
export declare function missingRequiredHubspotFields(definition: FormDefinition, required: {
|
|
41
|
+
name: string;
|
|
42
|
+
objectTypeId?: string;
|
|
43
|
+
}[]): MissingHubspotRequired[];
|
|
29
44
|
export declare function hsFieldType(type: string, options?: unknown[]): string;
|
|
30
45
|
export declare function toHsFormField(field: MappedFormField): Record<string, unknown>;
|
|
31
46
|
export interface RawFieldGroupLike {
|
|
@@ -35,3 +50,8 @@ export interface RawFieldGroupLike {
|
|
|
35
50
|
fields?: unknown[];
|
|
36
51
|
}
|
|
37
52
|
export declare function appendFieldGroups(existing: RawFieldGroupLike[], extra: MappedFormField[]): Record<string, unknown>[];
|
|
53
|
+
/**
|
|
54
|
+
* Nest Strapi `visibleIf` fields under their HubSpot parent as dependentFields.
|
|
55
|
+
* Conditions edited in Strapi are included; unsupported multi-parent rules stay flat.
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyVisibleIfToFieldGroups(groups: Record<string, unknown>[], definition: FormDefinition): Record<string, unknown>[];
|
|
@@ -26,6 +26,7 @@ export declare function createFormsAdminController(strapi: Core.Strapi): {
|
|
|
26
26
|
create(ctx: Ctx): Promise<void>;
|
|
27
27
|
update(ctx: Ctx): Promise<void>;
|
|
28
28
|
publish(ctx: Ctx): Promise<void>;
|
|
29
|
+
createInHubspot(ctx: Ctx): Promise<void>;
|
|
29
30
|
unpublish(ctx: Ctx): Promise<void>;
|
|
30
31
|
/**
|
|
31
32
|
* Feeds the `hubspot.form-picker` custom field: every form of the builder
|
|
@@ -133,7 +133,18 @@ export interface FormSubmitInput {
|
|
|
133
133
|
}
|
|
134
134
|
export declare function clearFormShapeCache(formGuid?: string): void;
|
|
135
135
|
export declare function loadFormShape(apiKey: string, formGuid: string): Promise<FormShape | null>;
|
|
136
|
-
|
|
136
|
+
/**
|
|
137
|
+
* The contact's id right after a Forms API conversion, via the DIRECT object
|
|
138
|
+
* lookup (`idProperty=email`) — never the Search API: the search index lags by
|
|
139
|
+
* seconds to minutes on freshly created records, so a contact the conversion
|
|
140
|
+
* itself just created was invisible, silently costing brand-new leads their
|
|
141
|
+
* timeline note and company association. The object store has no such lag.
|
|
142
|
+
*
|
|
143
|
+
* The conversion is processed asynchronously on HubSpot's side, so a 404 is
|
|
144
|
+
* retried briefly (the delays are injectable for tests). Any other error
|
|
145
|
+
* (auth, 5xx) gives up immediately — retrying wouldn't change it.
|
|
146
|
+
*/
|
|
147
|
+
export declare function findContactIdByEmail(apiKey: string, email: string, retryDelaysMs?: readonly number[]): Promise<string | undefined>;
|
|
137
148
|
/**
|
|
138
149
|
* Submit a marketing form. Extra fields HubSpot doesn't know on that form
|
|
139
150
|
* 400 the whole request, so callers should filter first. A GDPR block on the
|
|
@@ -153,3 +164,5 @@ export declare function submitMarketingForm(input: FormSubmitInput): Promise<{
|
|
|
153
164
|
* that already exist. Returns the property names that were added.
|
|
154
165
|
*/
|
|
155
166
|
export declare function syncFieldsToHubspotForm(apiKey: string, formGuid: string, definition: FormDefinition): Promise<string[]>;
|
|
167
|
+
/** Create a marketing form from the Strapi definition and return its GUID. */
|
|
168
|
+
export declare function createMarketingForm(apiKey: string, name: string, definition: FormDefinition): Promise<string>;
|
|
@@ -21,7 +21,7 @@ export interface HubspotFormSummary {
|
|
|
21
21
|
}
|
|
22
22
|
/** Something the converter could not express in the builder's model. */
|
|
23
23
|
export interface SkippedItem {
|
|
24
|
-
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent";
|
|
24
|
+
code: "field-type" | "hidden-field" | "object" | "duplicate" | "condition" | "rich-text" | "legal-consent" | "progressive";
|
|
25
25
|
/** Field label or name, when the item is a field. */
|
|
26
26
|
label?: string;
|
|
27
27
|
/** Raw detail (the fieldType, the operator…) for the report. */
|
|
@@ -58,6 +58,7 @@ interface RawField {
|
|
|
58
58
|
dependentFields?: RawDependent[];
|
|
59
59
|
}
|
|
60
60
|
interface RawFieldGroup {
|
|
61
|
+
groupType?: string;
|
|
61
62
|
richText?: string;
|
|
62
63
|
fields?: RawField[];
|
|
63
64
|
}
|
|
@@ -274,6 +274,7 @@ declare const _default: {
|
|
|
274
274
|
create(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
275
275
|
update(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
276
276
|
publish(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
277
|
+
createInHubspot(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
277
278
|
unpublish(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
278
279
|
options(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
|
279
280
|
listSources(ctx: import("./formsAdmin").Ctx): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-hubspot",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.9",
|
|
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",
|