strapi-plugin-hubspot 0.14.0 → 0.15.0

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 (30) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +16 -7
  3. package/dist/_chunks/{Forms-ws6d8rdC.mjs → Forms-B7eIm0Fi.mjs} +3 -3
  4. package/dist/_chunks/{Forms-Cch-7zei.js → Forms-ClyI4MJ1.js} +3 -3
  5. package/dist/_chunks/{HubspotFormInput-M2l_8w_e.js → HubspotFormInput-Do7F83V6.js} +1 -1
  6. package/dist/_chunks/{HubspotFormInput-18pWTITT.mjs → HubspotFormInput-cZAiaAaq.mjs} +1 -1
  7. package/dist/_chunks/{HubspotObjectInput-BDO99qXs.js → HubspotObjectInput-DWqk2C26.js} +3 -3
  8. package/dist/_chunks/{HubspotObjectInput-DshusXZP.mjs → HubspotObjectInput-ZZk_7hWq.mjs} +3 -3
  9. package/dist/_chunks/{HubspotPropertyInput-C-kHDgwx.mjs → HubspotPropertyInput-5bkDGmGo.mjs} +3 -3
  10. package/dist/_chunks/{HubspotPropertyInput-XGnH2tHn.js → HubspotPropertyInput-v9B3wb1a.js} +3 -3
  11. package/dist/_chunks/{Settings-Bm7WkzK1.mjs → Settings-QcFbm3s5.mjs} +132 -36
  12. package/dist/_chunks/{Settings-CRnsu6ob.js → Settings-X3EHnTaO.js} +131 -35
  13. package/dist/_chunks/{en-DKV-8FWP.mjs → en-DMJeF7EN.mjs} +15 -6
  14. package/dist/_chunks/{en-BXMU5CjT.js → en-ZHvaZLi7.js} +15 -6
  15. package/dist/_chunks/{fr-GB6T3ZyU.mjs → fr-CVAntLmc.mjs} +14 -5
  16. package/dist/_chunks/{fr-Dx4XmZ4X.js → fr-Ds_2EAzZ.js} +14 -5
  17. package/dist/_chunks/{index-CPVfElYB.mjs → index-BSJAhj8r.mjs} +6 -6
  18. package/dist/_chunks/{index-BlaG8oIq.js → index-BpcilDy1.js} +6 -6
  19. package/dist/_chunks/{objectLabels-CPc21Tdd.js → objectLabels-CYXXFpNB.js} +1 -1
  20. package/dist/_chunks/{objectLabels-6OUnIDB5.mjs → objectLabels-CrYRzsqe.mjs} +1 -1
  21. package/dist/_chunks/{useHubspotSchema-0MvCM1AC.js → useHubspotSchema-Ba6xZUCL.js} +1 -1
  22. package/dist/_chunks/{useHubspotSchema-qCUs2gIX.mjs → useHubspotSchema-CBF4GkmK.mjs} +1 -1
  23. package/dist/admin/index.js +1 -1
  24. package/dist/admin/index.mjs +1 -1
  25. package/dist/server/index.js +88 -11
  26. package/dist/server/index.mjs +88 -11
  27. package/dist/server/src/__tests__/settings.test.d.ts +1 -0
  28. package/dist/server/src/index.d.ts +5 -1
  29. package/dist/server/src/settings.d.ts +36 -5
  30. package/package.json +1 -1
@@ -159,6 +159,9 @@ function resolveObjects(configured) {
159
159
  return out;
160
160
  }
161
161
  const ENV_VAR = "HUBSPOT_API_KEY";
162
+ const ENV_PORTAL = "HUBSPOT_PORTAL_ID";
163
+ const ENV_REGION = "HUBSPOT_REGION";
164
+ const ENV_DEFAULT_FORM = "HUBSPOT_DEFAULT_FORM_ID";
162
165
  const store = (strapi) => strapi.store({ type: "plugin", name: "hubspot" });
163
166
  async function getStoredSettings(strapi) {
164
167
  return await store(strapi).get({ key: "settings" }) ?? {};
@@ -167,6 +170,13 @@ async function setStoredSettings(strapi, value) {
167
170
  await store(strapi).set({ key: "settings", value });
168
171
  clearCache();
169
172
  }
173
+ const first = (values, fallback = "") => {
174
+ for (const item of values) {
175
+ const value = (item.value ?? "").trim();
176
+ if (value) return { value, source: item.source };
177
+ }
178
+ return { value: fallback, source: null };
179
+ };
170
180
  async function resolveApiKey(strapi) {
171
181
  const stored = await getStoredSettings(strapi);
172
182
  if (stored.apiKey) return { apiKey: stored.apiKey, source: "settings" };
@@ -176,14 +186,71 @@ async function resolveApiKey(strapi) {
176
186
  if (fromEnv) return { apiKey: fromEnv, source: "env" };
177
187
  return { apiKey: "", source: null };
178
188
  }
179
- async function publicSettings(strapi) {
180
- const { apiKey, source } = await resolveApiKey(strapi);
189
+ async function resolveAccount(strapi) {
190
+ const resolved = await resolveAccountSources(strapi);
191
+ return {
192
+ portalId: resolved.portalId,
193
+ region: resolved.region || "eu1",
194
+ defaultFormId: resolved.defaultFormId
195
+ };
196
+ }
197
+ async function resolveAccountSources(strapi) {
198
+ const stored = await getStoredSettings(strapi);
199
+ const formsCfg = strapi.plugin("hubspot").config("forms", {}) ?? {};
200
+ const portal = first([
201
+ { value: stored.portalId, source: "settings" },
202
+ { value: strapi.plugin("hubspot").config("portalId", ""), source: "config" },
203
+ { value: process.env[ENV_PORTAL], source: "env" }
204
+ ]);
205
+ const region = first(
206
+ [
207
+ { value: stored.region, source: "settings" },
208
+ { value: strapi.plugin("hubspot").config("region", ""), source: "config" },
209
+ { value: process.env[ENV_REGION], source: "env" }
210
+ ],
211
+ "eu1"
212
+ );
213
+ const form = first([
214
+ { value: stored.defaultFormId, source: "settings" },
215
+ { value: formsCfg.defaultFormId, source: "config" },
216
+ { value: process.env[ENV_DEFAULT_FORM], source: "env" }
217
+ ]);
218
+ const { apiKey, source: keySource } = await resolveApiKey(strapi);
181
219
  return {
182
220
  configured: Boolean(apiKey),
183
- keySource: source,
184
- hint: apiKey ? `…${apiKey.slice(-4)}` : ""
221
+ keySource,
222
+ hint: apiKey ? `…${apiKey.slice(-4)}` : "",
223
+ portalId: portal.value,
224
+ region: region.value || "eu1",
225
+ defaultFormId: form.value,
226
+ portalSource: portal.source,
227
+ regionSource: region.source,
228
+ formSource: form.source
185
229
  };
186
230
  }
231
+ async function publicSettings(strapi) {
232
+ return resolveAccountSources(strapi);
233
+ }
234
+ async function patchStoredSettings(strapi, body) {
235
+ const current = await getStoredSettings(strapi);
236
+ const next = { ...current };
237
+ if (typeof body.apiKey === "string" && body.apiKey.trim()) {
238
+ next.apiKey = body.apiKey.trim();
239
+ }
240
+ if ("portalId" in body) next.portalId = String(body.portalId ?? "").trim();
241
+ if ("region" in body) {
242
+ const region = String(body.region ?? "").trim().toLowerCase();
243
+ next.region = region || "eu1";
244
+ }
245
+ if ("defaultFormId" in body) next.defaultFormId = String(body.defaultFormId ?? "").trim();
246
+ await setStoredSettings(strapi, next);
247
+ }
248
+ async function clearStoredApiKey(strapi) {
249
+ const current = await getStoredSettings(strapi);
250
+ const { apiKey: _apiKey, ...rest } = current;
251
+ const empty = !rest.portalId && !rest.region && !rest.defaultFormId;
252
+ await setStoredSettings(strapi, empty ? null : rest);
253
+ }
187
254
  function collectMappings(node, target, found = []) {
188
255
  if (Array.isArray(node)) {
189
256
  for (const item of node) collectMappings(item, target, found);
@@ -2137,10 +2204,11 @@ function createFormsService(strapi, _opts = {}) {
2137
2204
  }
2138
2205
  const partition = await partitionBySchema(apiKey, groups);
2139
2206
  rejected = partition.rejected;
2140
- const portalId = sanitizePortalId(strapi.plugin("hubspot").config("portalId", ""));
2141
- const region = String(strapi.plugin("hubspot").config("region", "eu1") || "eu1");
2207
+ const account = await resolveAccount(strapi);
2208
+ const portalId = sanitizePortalId(account.portalId);
2209
+ const region = account.region || "eu1";
2142
2210
  const ownGuid = typeof form.hubspotFormId === "string" ? form.hubspotFormId.trim() : "";
2143
- const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(config2.defaultFormId) ? config2.defaultFormId.trim() : "";
2211
+ const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(account.defaultFormId) ? account.defaultFormId.trim() : "";
2144
2212
  if (portalId && formGuid) {
2145
2213
  try {
2146
2214
  const shape = await loadFormShape(apiKey, formGuid);
@@ -3141,15 +3209,24 @@ const controllers = {
3141
3209
  },
3142
3210
  settings: ({ strapi }) => ({
3143
3211
  async get(ctx) {
3144
- ctx.body = await publicSettings(strapi);
3212
+ const settings = await publicSettings(strapi);
3213
+ let forms = [];
3214
+ const { apiKey } = await resolveApiKey(strapi);
3215
+ if (apiKey) {
3216
+ try {
3217
+ forms = await listHubspotForms(apiKey);
3218
+ } catch {
3219
+ forms = [];
3220
+ }
3221
+ }
3222
+ ctx.body = { ...settings, forms };
3145
3223
  },
3146
3224
  async update(ctx) {
3147
- const apiKey = (ctx.request.body?.apiKey ?? "").trim();
3148
- await setStoredSettings(strapi, apiKey ? { apiKey } : null);
3225
+ await patchStoredSettings(strapi, ctx.request.body ?? {});
3149
3226
  ctx.body = await publicSettings(strapi);
3150
3227
  },
3151
3228
  async reset(ctx) {
3152
- await setStoredSettings(strapi, null);
3229
+ await clearStoredApiKey(strapi);
3153
3230
  ctx.body = await publicSettings(strapi);
3154
3231
  }
3155
3232
  })
@@ -158,6 +158,9 @@ function resolveObjects(configured) {
158
158
  return out;
159
159
  }
160
160
  const ENV_VAR = "HUBSPOT_API_KEY";
161
+ const ENV_PORTAL = "HUBSPOT_PORTAL_ID";
162
+ const ENV_REGION = "HUBSPOT_REGION";
163
+ const ENV_DEFAULT_FORM = "HUBSPOT_DEFAULT_FORM_ID";
161
164
  const store = (strapi) => strapi.store({ type: "plugin", name: "hubspot" });
162
165
  async function getStoredSettings(strapi) {
163
166
  return await store(strapi).get({ key: "settings" }) ?? {};
@@ -166,6 +169,13 @@ async function setStoredSettings(strapi, value) {
166
169
  await store(strapi).set({ key: "settings", value });
167
170
  clearCache();
168
171
  }
172
+ const first = (values, fallback = "") => {
173
+ for (const item of values) {
174
+ const value = (item.value ?? "").trim();
175
+ if (value) return { value, source: item.source };
176
+ }
177
+ return { value: fallback, source: null };
178
+ };
169
179
  async function resolveApiKey(strapi) {
170
180
  const stored = await getStoredSettings(strapi);
171
181
  if (stored.apiKey) return { apiKey: stored.apiKey, source: "settings" };
@@ -175,14 +185,71 @@ async function resolveApiKey(strapi) {
175
185
  if (fromEnv) return { apiKey: fromEnv, source: "env" };
176
186
  return { apiKey: "", source: null };
177
187
  }
178
- async function publicSettings(strapi) {
179
- const { apiKey, source } = await resolveApiKey(strapi);
188
+ async function resolveAccount(strapi) {
189
+ const resolved = await resolveAccountSources(strapi);
190
+ return {
191
+ portalId: resolved.portalId,
192
+ region: resolved.region || "eu1",
193
+ defaultFormId: resolved.defaultFormId
194
+ };
195
+ }
196
+ async function resolveAccountSources(strapi) {
197
+ const stored = await getStoredSettings(strapi);
198
+ const formsCfg = strapi.plugin("hubspot").config("forms", {}) ?? {};
199
+ const portal = first([
200
+ { value: stored.portalId, source: "settings" },
201
+ { value: strapi.plugin("hubspot").config("portalId", ""), source: "config" },
202
+ { value: process.env[ENV_PORTAL], source: "env" }
203
+ ]);
204
+ const region = first(
205
+ [
206
+ { value: stored.region, source: "settings" },
207
+ { value: strapi.plugin("hubspot").config("region", ""), source: "config" },
208
+ { value: process.env[ENV_REGION], source: "env" }
209
+ ],
210
+ "eu1"
211
+ );
212
+ const form = first([
213
+ { value: stored.defaultFormId, source: "settings" },
214
+ { value: formsCfg.defaultFormId, source: "config" },
215
+ { value: process.env[ENV_DEFAULT_FORM], source: "env" }
216
+ ]);
217
+ const { apiKey, source: keySource } = await resolveApiKey(strapi);
180
218
  return {
181
219
  configured: Boolean(apiKey),
182
- keySource: source,
183
- hint: apiKey ? `…${apiKey.slice(-4)}` : ""
220
+ keySource,
221
+ hint: apiKey ? `…${apiKey.slice(-4)}` : "",
222
+ portalId: portal.value,
223
+ region: region.value || "eu1",
224
+ defaultFormId: form.value,
225
+ portalSource: portal.source,
226
+ regionSource: region.source,
227
+ formSource: form.source
184
228
  };
185
229
  }
230
+ async function publicSettings(strapi) {
231
+ return resolveAccountSources(strapi);
232
+ }
233
+ async function patchStoredSettings(strapi, body) {
234
+ const current = await getStoredSettings(strapi);
235
+ const next = { ...current };
236
+ if (typeof body.apiKey === "string" && body.apiKey.trim()) {
237
+ next.apiKey = body.apiKey.trim();
238
+ }
239
+ if ("portalId" in body) next.portalId = String(body.portalId ?? "").trim();
240
+ if ("region" in body) {
241
+ const region = String(body.region ?? "").trim().toLowerCase();
242
+ next.region = region || "eu1";
243
+ }
244
+ if ("defaultFormId" in body) next.defaultFormId = String(body.defaultFormId ?? "").trim();
245
+ await setStoredSettings(strapi, next);
246
+ }
247
+ async function clearStoredApiKey(strapi) {
248
+ const current = await getStoredSettings(strapi);
249
+ const { apiKey: _apiKey, ...rest } = current;
250
+ const empty = !rest.portalId && !rest.region && !rest.defaultFormId;
251
+ await setStoredSettings(strapi, empty ? null : rest);
252
+ }
186
253
  function collectMappings(node, target, found = []) {
187
254
  if (Array.isArray(node)) {
188
255
  for (const item of node) collectMappings(item, target, found);
@@ -2136,10 +2203,11 @@ function createFormsService(strapi, _opts = {}) {
2136
2203
  }
2137
2204
  const partition = await partitionBySchema(apiKey, groups);
2138
2205
  rejected = partition.rejected;
2139
- const portalId = sanitizePortalId(strapi.plugin("hubspot").config("portalId", ""));
2140
- const region = String(strapi.plugin("hubspot").config("region", "eu1") || "eu1");
2206
+ const account = await resolveAccount(strapi);
2207
+ const portalId = sanitizePortalId(account.portalId);
2208
+ const region = account.region || "eu1";
2141
2209
  const ownGuid = typeof form.hubspotFormId === "string" ? form.hubspotFormId.trim() : "";
2142
- const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(config2.defaultFormId) ? config2.defaultFormId.trim() : "";
2210
+ const formGuid = isFormGuid(ownGuid) ? ownGuid : isFormGuid(account.defaultFormId) ? account.defaultFormId.trim() : "";
2143
2211
  if (portalId && formGuid) {
2144
2212
  try {
2145
2213
  const shape = await loadFormShape(apiKey, formGuid);
@@ -3140,15 +3208,24 @@ const controllers = {
3140
3208
  },
3141
3209
  settings: ({ strapi }) => ({
3142
3210
  async get(ctx) {
3143
- ctx.body = await publicSettings(strapi);
3211
+ const settings = await publicSettings(strapi);
3212
+ let forms = [];
3213
+ const { apiKey } = await resolveApiKey(strapi);
3214
+ if (apiKey) {
3215
+ try {
3216
+ forms = await listHubspotForms(apiKey);
3217
+ } catch {
3218
+ forms = [];
3219
+ }
3220
+ }
3221
+ ctx.body = { ...settings, forms };
3144
3222
  },
3145
3223
  async update(ctx) {
3146
- const apiKey = (ctx.request.body?.apiKey ?? "").trim();
3147
- await setStoredSettings(strapi, apiKey ? { apiKey } : null);
3224
+ await patchStoredSettings(strapi, ctx.request.body ?? {});
3148
3225
  ctx.body = await publicSettings(strapi);
3149
3226
  },
3150
3227
  async reset(ctx) {
3151
- await setStoredSettings(strapi, null);
3228
+ await clearStoredApiKey(strapi);
3152
3229
  ctx.body = await publicSettings(strapi);
3153
3230
  }
3154
3231
  })
@@ -0,0 +1 @@
1
+ export {};
@@ -50,6 +50,7 @@ declare const _default: {
50
50
  };
51
51
  attributes: {
52
52
  name: {
53
+ /** RBAC action gating the settings screen and its routes. */
53
54
  type: string;
54
55
  required: boolean;
55
56
  pluginOptions: {
@@ -358,8 +359,11 @@ declare const _default: {
358
359
  }): Promise<void>;
359
360
  update(ctx: {
360
361
  request: {
361
- body: {
362
+ body?: {
362
363
  apiKey?: string;
364
+ portalId?: string;
365
+ region?: string;
366
+ defaultFormId?: string;
363
367
  };
364
368
  };
365
369
  body: unknown;
@@ -1,28 +1,59 @@
1
1
  import type { Core } from "@strapi/strapi";
2
2
  /**
3
- * API key resolution, in order of precedence:
3
+ * API key + portal resolution, in order of precedence:
4
4
  * 1. saved from the admin UI (plugin core store)
5
5
  * 2. plugin config (config/plugins of the host app)
6
- * 3. environment variable (HUBSPOT_API_KEY)
6
+ * 3. environment variable
7
7
  *
8
8
  * The key is only ever read server-side; `publicSettings` is what the admin UI
9
- * receives, and it deliberately never carries the key itself.
9
+ * receives, and it deliberately never carries the key itself. Portal id, region
10
+ * and the default form GUID are not secrets — they round-trip so they can be
11
+ * edited in Settings → HubSpot.
10
12
  */
11
13
  export declare const ENV_VAR = "HUBSPOT_API_KEY";
14
+ export declare const ENV_PORTAL = "HUBSPOT_PORTAL_ID";
15
+ export declare const ENV_REGION = "HUBSPOT_REGION";
16
+ export declare const ENV_DEFAULT_FORM = "HUBSPOT_DEFAULT_FORM_ID";
17
+ export type SettingSource = "settings" | "config" | "env" | null;
12
18
  export interface StoredSettings {
13
19
  apiKey?: string;
20
+ portalId?: string;
21
+ region?: string;
22
+ defaultFormId?: string;
14
23
  }
15
24
  export interface PublicSettings {
16
25
  configured: boolean;
17
26
  /** Where the key comes from, so the UI can explain why it can't be edited. */
18
- keySource: "settings" | "config" | "env" | null;
27
+ keySource: SettingSource;
19
28
  /** Last four characters, enough to recognise a key without exposing it. */
20
29
  hint: string;
30
+ portalId: string;
31
+ region: string;
32
+ defaultFormId: string;
33
+ portalSource: SettingSource;
34
+ regionSource: SettingSource;
35
+ formSource: SettingSource;
36
+ }
37
+ export interface HubspotAccount {
38
+ portalId: string;
39
+ region: string;
40
+ defaultFormId: string;
21
41
  }
22
42
  export declare function getStoredSettings(strapi: Core.Strapi): Promise<StoredSettings>;
23
43
  export declare function setStoredSettings(strapi: Core.Strapi, value: StoredSettings | null): Promise<void>;
24
44
  export declare function resolveApiKey(strapi: Core.Strapi): Promise<{
25
45
  apiKey: string;
26
- source: PublicSettings["keySource"];
46
+ source: SettingSource;
27
47
  }>;
48
+ /** Portal / region / default marketing form — settings UI overrides env. */
49
+ export declare function resolveAccount(strapi: Core.Strapi): Promise<HubspotAccount>;
28
50
  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: {
53
+ apiKey?: string;
54
+ portalId?: unknown;
55
+ region?: unknown;
56
+ defaultFormId?: unknown;
57
+ }): Promise<void>;
58
+ /** Drop only the stored token; portal / form settings stay. */
59
+ export declare function clearStoredApiKey(strapi: Core.Strapi): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-hubspot",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
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",