strapi-plugin-hubspot 0.1.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.
@@ -0,0 +1,42 @@
1
+ const PLUGIN_ID = "hubspot";
2
+ const index = {
3
+ register(app) {
4
+ app.customFields.register({
5
+ name: "property",
6
+ pluginId: PLUGIN_ID,
7
+ type: "string",
8
+ intlLabel: {
9
+ id: `${PLUGIN_ID}.property.label`,
10
+ defaultMessage: "Propriété HubSpot"
11
+ },
12
+ intlDescription: {
13
+ id: `${PLUGIN_ID}.property.description`,
14
+ defaultMessage: "Choisie dans les propriétés réelles du portail"
15
+ },
16
+ components: {
17
+ Input: async () => import("./HubspotPropertyInput-CTZYZS_7.mjs")
18
+ },
19
+ options: {}
20
+ });
21
+ app.createSettingSection(
22
+ {
23
+ id: PLUGIN_ID,
24
+ intlLabel: { id: `${PLUGIN_ID}.settings.section`, defaultMessage: "HubSpot" }
25
+ },
26
+ [
27
+ {
28
+ intlLabel: { id: `${PLUGIN_ID}.settings.link`, defaultMessage: "Configuration" },
29
+ id: `${PLUGIN_ID}-settings`,
30
+ to: `/settings/${PLUGIN_ID}`,
31
+ permissions: [],
32
+ Component: async () => (await import("./Settings-Byupubny.mjs")).default
33
+ }
34
+ ]
35
+ );
36
+ app.registerPlugin({ id: PLUGIN_ID, name: PLUGIN_ID });
37
+ }
38
+ };
39
+ export {
40
+ PLUGIN_ID as P,
41
+ index as i
42
+ };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ const PLUGIN_ID = "hubspot";
3
+ const index = {
4
+ register(app) {
5
+ app.customFields.register({
6
+ name: "property",
7
+ pluginId: PLUGIN_ID,
8
+ type: "string",
9
+ intlLabel: {
10
+ id: `${PLUGIN_ID}.property.label`,
11
+ defaultMessage: "Propriété HubSpot"
12
+ },
13
+ intlDescription: {
14
+ id: `${PLUGIN_ID}.property.description`,
15
+ defaultMessage: "Choisie dans les propriétés réelles du portail"
16
+ },
17
+ components: {
18
+ Input: async () => Promise.resolve().then(() => require("./HubspotPropertyInput-B0HpXoqz.js"))
19
+ },
20
+ options: {}
21
+ });
22
+ app.createSettingSection(
23
+ {
24
+ id: PLUGIN_ID,
25
+ intlLabel: { id: `${PLUGIN_ID}.settings.section`, defaultMessage: "HubSpot" }
26
+ },
27
+ [
28
+ {
29
+ intlLabel: { id: `${PLUGIN_ID}.settings.link`, defaultMessage: "Configuration" },
30
+ id: `${PLUGIN_ID}-settings`,
31
+ to: `/settings/${PLUGIN_ID}`,
32
+ permissions: [],
33
+ Component: async () => (await Promise.resolve().then(() => require("./Settings-BgRpG_a6.js"))).default
34
+ }
35
+ ]
36
+ );
37
+ app.registerPlugin({ id: PLUGIN_ID, name: PLUGIN_ID });
38
+ }
39
+ };
40
+ exports.PLUGIN_ID = PLUGIN_ID;
41
+ exports.index = index;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ const index = require("../_chunks/index-DVRLoCa1.js");
3
+ module.exports = index.index;
@@ -0,0 +1,4 @@
1
+ import { i } from "../_chunks/index-CER4BKjS.mjs";
2
+ export {
3
+ i as default
4
+ };
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ interface InputProps {
3
+ name: string;
4
+ value?: string;
5
+ onChange: (event: {
6
+ target: {
7
+ name: string;
8
+ value: string;
9
+ type: string;
10
+ };
11
+ }) => void;
12
+ attribute?: unknown;
13
+ disabled?: boolean;
14
+ error?: string;
15
+ required?: boolean;
16
+ labelAction?: React.ReactNode;
17
+ hint?: React.ReactNode;
18
+ label?: string;
19
+ placeholder?: string;
20
+ }
21
+ /**
22
+ * Picker over the portal's writable properties.
23
+ *
24
+ * Contact and company properties share one list, each prefixed by its object,
25
+ * rather than being filtered by a sibling `hsObject` field: reading a sibling
26
+ * value from inside a repeatable component nested in a dynamic zone is brittle,
27
+ * and showing the object inline is arguably clearer anyway — the editor sees at
28
+ * a glance which record the answer lands on.
29
+ *
30
+ * Falls back to a plain text input when no API key is configured, so the field
31
+ * never blocks authoring on a fresh install.
32
+ */
33
+ declare const HubspotPropertyInput: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
34
+ export default HubspotPropertyInput;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Registers two things:
3
+ *
4
+ * - the `hubspot.property` custom field — a searchable picker fed by the
5
+ * portal's real schema, in place of a free-text CRM property name;
6
+ * - a Settings → HubSpot section holding the private app token.
7
+ *
8
+ * NOTE: the Component/Input entries must be async functions returning the
9
+ * module. React.lazy here crashes the admin silently — empty #strapi, no
10
+ * console error — the same trap link-graph hit with its menu link.
11
+ */
12
+ declare const _default: {
13
+ register(app: {
14
+ customFields: {
15
+ register: (field: unknown) => void;
16
+ };
17
+ createSettingSection: (section: {
18
+ id: string;
19
+ intlLabel: {
20
+ id: string;
21
+ defaultMessage: string;
22
+ };
23
+ }, links: unknown[]) => void;
24
+ registerPlugin: (plugin: {
25
+ id: string;
26
+ name: string;
27
+ }) => void;
28
+ }): void;
29
+ };
30
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ declare const HubspotSettings: () => React.JSX.Element;
3
+ export default HubspotSettings;
@@ -0,0 +1 @@
1
+ export declare const PLUGIN_ID = "hubspot";
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ const utils = require("@strapi/utils");
3
+ const HS_BASE = "https://api.hubapi.com";
4
+ const TTL_MS = 10 * 60 * 1e3;
5
+ const PLURAL = {
6
+ contact: "contacts",
7
+ company: "companies"
8
+ };
9
+ let cache = null;
10
+ let inFlight = null;
11
+ async function fetchObject(apiKey, object) {
12
+ const res = await fetch(`${HS_BASE}/crm/v3/properties/${PLURAL[object]}`, {
13
+ headers: { Authorization: `Bearer ${apiKey}` }
14
+ }).then(async (r) => {
15
+ if (!r.ok) throw new Error(`HubSpot ${r.status} sur ${PLURAL[object]}`);
16
+ return r.json();
17
+ });
18
+ return (res.results ?? []).filter((p) => !p.modificationMetadata?.readOnlyValue).map((p) => ({
19
+ name: p.name,
20
+ label: p.label || p.name,
21
+ object,
22
+ type: p.type,
23
+ options: (p.options ?? []).map((o) => o.value),
24
+ group: p.groupName
25
+ }));
26
+ }
27
+ async function listProperties(strapi, apiKey, { force = false } = {}) {
28
+ if (!force && cache && Date.now() - cache.at < TTL_MS) return cache.properties;
29
+ if (!force && inFlight) return inFlight;
30
+ inFlight = (async () => {
31
+ try {
32
+ const [contact, company] = await Promise.all([
33
+ fetchObject(apiKey, "contact"),
34
+ fetchObject(apiKey, "company")
35
+ ]);
36
+ const properties = [...contact, ...company].sort(
37
+ (a, b) => a.object.localeCompare(b.object) || a.label.localeCompare(b.label)
38
+ );
39
+ cache = { at: Date.now(), properties };
40
+ return properties;
41
+ } finally {
42
+ inFlight = null;
43
+ }
44
+ })();
45
+ return inFlight;
46
+ }
47
+ function clearCache() {
48
+ cache = null;
49
+ }
50
+ function checkProperty(properties, object, name) {
51
+ const trimmed = name.trim();
52
+ if (!trimmed) return null;
53
+ const match = properties.find((p) => p.object === object && p.name === trimmed);
54
+ if (match) return trimmed === name ? null : `« ${name} » contient un espace superflu`;
55
+ const onOther = properties.find((p) => p.object !== object && p.name === trimmed);
56
+ if (onOther) {
57
+ return `« ${trimmed} » existe sur l'objet ${onOther.object === "contact" ? "Contact" : "Société"}, pas sur ${object === "contact" ? "Contact" : "Société"}`;
58
+ }
59
+ return `« ${trimmed} » n'existe pas dans ce portail HubSpot`;
60
+ }
61
+ const ENV_VAR = "HUBSPOT_API_KEY";
62
+ const store = (strapi) => strapi.store({ type: "plugin", name: "hubspot" });
63
+ async function getStoredSettings(strapi) {
64
+ return await store(strapi).get({ key: "settings" }) ?? {};
65
+ }
66
+ async function setStoredSettings(strapi, value) {
67
+ await store(strapi).set({ key: "settings", value });
68
+ clearCache();
69
+ }
70
+ async function resolveApiKey(strapi) {
71
+ const stored = await getStoredSettings(strapi);
72
+ if (stored.apiKey) return { apiKey: stored.apiKey, source: "settings" };
73
+ const fromConfig = strapi.plugin("hubspot").config("apiKey", "");
74
+ if (fromConfig) return { apiKey: fromConfig, source: "config" };
75
+ const fromEnv = process.env[ENV_VAR];
76
+ if (fromEnv) return { apiKey: fromEnv, source: "env" };
77
+ return { apiKey: "", source: null };
78
+ }
79
+ async function publicSettings(strapi) {
80
+ const { apiKey, source } = await resolveApiKey(strapi);
81
+ return {
82
+ configured: Boolean(apiKey),
83
+ keySource: source,
84
+ hint: apiKey ? `…${apiKey.slice(-4)}` : ""
85
+ };
86
+ }
87
+ const config = {
88
+ default: {
89
+ apiKey: "",
90
+ validate: []
91
+ },
92
+ validator(cfg) {
93
+ if (cfg.validate && !Array.isArray(cfg.validate)) {
94
+ throw new Error("hubspot: `validate` must be an array of { uid, objectField, propertyField }");
95
+ }
96
+ }
97
+ };
98
+ const controllers = {
99
+ properties: ({ strapi }) => ({
100
+ async list(ctx) {
101
+ const { apiKey } = await resolveApiKey(strapi);
102
+ if (!apiKey) {
103
+ ctx.body = { configured: false, properties: [] };
104
+ return;
105
+ }
106
+ try {
107
+ const properties = await listProperties(strapi, apiKey, {
108
+ force: ctx.query.refresh === "1"
109
+ });
110
+ ctx.body = { configured: true, properties };
111
+ } catch (err) {
112
+ strapi.log.error(`[hubspot] ${err.message}`);
113
+ ctx.throw(502, "Impossible de joindre HubSpot — vérifiez la clé API.");
114
+ }
115
+ }
116
+ }),
117
+ settings: ({ strapi }) => ({
118
+ async get(ctx) {
119
+ ctx.body = await publicSettings(strapi);
120
+ },
121
+ async update(ctx) {
122
+ const apiKey = (ctx.request.body?.apiKey ?? "").trim();
123
+ await setStoredSettings(strapi, apiKey ? { apiKey } : null);
124
+ ctx.body = await publicSettings(strapi);
125
+ },
126
+ async reset(ctx) {
127
+ await setStoredSettings(strapi, null);
128
+ ctx.body = await publicSettings(strapi);
129
+ }
130
+ })
131
+ };
132
+ const adminRoute = (method, path, handler) => ({
133
+ method,
134
+ path,
135
+ handler,
136
+ config: { policies: ["admin::isAuthenticatedAdmin"] }
137
+ });
138
+ const routes = {
139
+ admin: {
140
+ type: "admin",
141
+ routes: [
142
+ adminRoute("GET", "/properties", "properties.list"),
143
+ adminRoute("GET", "/settings", "settings.get"),
144
+ adminRoute("PUT", "/settings", "settings.update"),
145
+ adminRoute("DELETE", "/settings", "settings.reset")
146
+ ]
147
+ }
148
+ };
149
+ function collectMappings(node, target, found = []) {
150
+ if (Array.isArray(node)) {
151
+ for (const item of node) collectMappings(item, target, found);
152
+ return found;
153
+ }
154
+ if (!node || typeof node !== "object") return found;
155
+ const obj = node;
156
+ const property = obj[target.propertyField];
157
+ if (typeof property === "string" && property.trim()) {
158
+ found.push({
159
+ object: obj[target.objectField] === "company" ? "company" : "contact",
160
+ property
161
+ });
162
+ }
163
+ for (const value of Object.values(obj)) collectMappings(value, target, found);
164
+ return found;
165
+ }
166
+ const index = {
167
+ config,
168
+ controllers,
169
+ routes,
170
+ register({ strapi }) {
171
+ strapi.customFields.register({
172
+ name: "property",
173
+ plugin: "hubspot",
174
+ type: "string"
175
+ });
176
+ },
177
+ bootstrap({ strapi }) {
178
+ const targets = strapi.plugin("hubspot").config("validate", []);
179
+ if (!targets.length) return;
180
+ for (const target of targets) {
181
+ strapi.documents.use(async (context, next) => {
182
+ const isWrite = ["create", "update"].includes(context.action);
183
+ if (!isWrite || context.uid !== target.uid) return next();
184
+ const mappings = collectMappings(context.params?.data, target);
185
+ if (!mappings.length) return next();
186
+ const { apiKey } = await resolveApiKey(strapi);
187
+ if (!apiKey) return next();
188
+ let properties;
189
+ try {
190
+ properties = await listProperties(strapi, apiKey);
191
+ } catch {
192
+ strapi.log.warn("[hubspot] schéma indisponible — validation ignorée");
193
+ return next();
194
+ }
195
+ const problems = mappings.map((m) => checkProperty(properties, m.object, m.property)).filter((reason) => Boolean(reason));
196
+ if (problems.length) {
197
+ throw new utils.errors.ValidationError(
198
+ `Mapping HubSpot invalide — ${[...new Set(problems)].join(" ; ")}`
199
+ );
200
+ }
201
+ return next();
202
+ });
203
+ }
204
+ }
205
+ };
206
+ module.exports = index;
@@ -0,0 +1,207 @@
1
+ import { errors } from "@strapi/utils";
2
+ const HS_BASE = "https://api.hubapi.com";
3
+ const TTL_MS = 10 * 60 * 1e3;
4
+ const PLURAL = {
5
+ contact: "contacts",
6
+ company: "companies"
7
+ };
8
+ let cache = null;
9
+ let inFlight = null;
10
+ async function fetchObject(apiKey, object) {
11
+ const res = await fetch(`${HS_BASE}/crm/v3/properties/${PLURAL[object]}`, {
12
+ headers: { Authorization: `Bearer ${apiKey}` }
13
+ }).then(async (r) => {
14
+ if (!r.ok) throw new Error(`HubSpot ${r.status} sur ${PLURAL[object]}`);
15
+ return r.json();
16
+ });
17
+ return (res.results ?? []).filter((p) => !p.modificationMetadata?.readOnlyValue).map((p) => ({
18
+ name: p.name,
19
+ label: p.label || p.name,
20
+ object,
21
+ type: p.type,
22
+ options: (p.options ?? []).map((o) => o.value),
23
+ group: p.groupName
24
+ }));
25
+ }
26
+ async function listProperties(strapi, apiKey, { force = false } = {}) {
27
+ if (!force && cache && Date.now() - cache.at < TTL_MS) return cache.properties;
28
+ if (!force && inFlight) return inFlight;
29
+ inFlight = (async () => {
30
+ try {
31
+ const [contact, company] = await Promise.all([
32
+ fetchObject(apiKey, "contact"),
33
+ fetchObject(apiKey, "company")
34
+ ]);
35
+ const properties = [...contact, ...company].sort(
36
+ (a, b) => a.object.localeCompare(b.object) || a.label.localeCompare(b.label)
37
+ );
38
+ cache = { at: Date.now(), properties };
39
+ return properties;
40
+ } finally {
41
+ inFlight = null;
42
+ }
43
+ })();
44
+ return inFlight;
45
+ }
46
+ function clearCache() {
47
+ cache = null;
48
+ }
49
+ function checkProperty(properties, object, name) {
50
+ const trimmed = name.trim();
51
+ if (!trimmed) return null;
52
+ const match = properties.find((p) => p.object === object && p.name === trimmed);
53
+ if (match) return trimmed === name ? null : `« ${name} » contient un espace superflu`;
54
+ const onOther = properties.find((p) => p.object !== object && p.name === trimmed);
55
+ if (onOther) {
56
+ return `« ${trimmed} » existe sur l'objet ${onOther.object === "contact" ? "Contact" : "Société"}, pas sur ${object === "contact" ? "Contact" : "Société"}`;
57
+ }
58
+ return `« ${trimmed} » n'existe pas dans ce portail HubSpot`;
59
+ }
60
+ const ENV_VAR = "HUBSPOT_API_KEY";
61
+ const store = (strapi) => strapi.store({ type: "plugin", name: "hubspot" });
62
+ async function getStoredSettings(strapi) {
63
+ return await store(strapi).get({ key: "settings" }) ?? {};
64
+ }
65
+ async function setStoredSettings(strapi, value) {
66
+ await store(strapi).set({ key: "settings", value });
67
+ clearCache();
68
+ }
69
+ async function resolveApiKey(strapi) {
70
+ const stored = await getStoredSettings(strapi);
71
+ if (stored.apiKey) return { apiKey: stored.apiKey, source: "settings" };
72
+ const fromConfig = strapi.plugin("hubspot").config("apiKey", "");
73
+ if (fromConfig) return { apiKey: fromConfig, source: "config" };
74
+ const fromEnv = process.env[ENV_VAR];
75
+ if (fromEnv) return { apiKey: fromEnv, source: "env" };
76
+ return { apiKey: "", source: null };
77
+ }
78
+ async function publicSettings(strapi) {
79
+ const { apiKey, source } = await resolveApiKey(strapi);
80
+ return {
81
+ configured: Boolean(apiKey),
82
+ keySource: source,
83
+ hint: apiKey ? `…${apiKey.slice(-4)}` : ""
84
+ };
85
+ }
86
+ const config = {
87
+ default: {
88
+ apiKey: "",
89
+ validate: []
90
+ },
91
+ validator(cfg) {
92
+ if (cfg.validate && !Array.isArray(cfg.validate)) {
93
+ throw new Error("hubspot: `validate` must be an array of { uid, objectField, propertyField }");
94
+ }
95
+ }
96
+ };
97
+ const controllers = {
98
+ properties: ({ strapi }) => ({
99
+ async list(ctx) {
100
+ const { apiKey } = await resolveApiKey(strapi);
101
+ if (!apiKey) {
102
+ ctx.body = { configured: false, properties: [] };
103
+ return;
104
+ }
105
+ try {
106
+ const properties = await listProperties(strapi, apiKey, {
107
+ force: ctx.query.refresh === "1"
108
+ });
109
+ ctx.body = { configured: true, properties };
110
+ } catch (err) {
111
+ strapi.log.error(`[hubspot] ${err.message}`);
112
+ ctx.throw(502, "Impossible de joindre HubSpot — vérifiez la clé API.");
113
+ }
114
+ }
115
+ }),
116
+ settings: ({ strapi }) => ({
117
+ async get(ctx) {
118
+ ctx.body = await publicSettings(strapi);
119
+ },
120
+ async update(ctx) {
121
+ const apiKey = (ctx.request.body?.apiKey ?? "").trim();
122
+ await setStoredSettings(strapi, apiKey ? { apiKey } : null);
123
+ ctx.body = await publicSettings(strapi);
124
+ },
125
+ async reset(ctx) {
126
+ await setStoredSettings(strapi, null);
127
+ ctx.body = await publicSettings(strapi);
128
+ }
129
+ })
130
+ };
131
+ const adminRoute = (method, path, handler) => ({
132
+ method,
133
+ path,
134
+ handler,
135
+ config: { policies: ["admin::isAuthenticatedAdmin"] }
136
+ });
137
+ const routes = {
138
+ admin: {
139
+ type: "admin",
140
+ routes: [
141
+ adminRoute("GET", "/properties", "properties.list"),
142
+ adminRoute("GET", "/settings", "settings.get"),
143
+ adminRoute("PUT", "/settings", "settings.update"),
144
+ adminRoute("DELETE", "/settings", "settings.reset")
145
+ ]
146
+ }
147
+ };
148
+ function collectMappings(node, target, found = []) {
149
+ if (Array.isArray(node)) {
150
+ for (const item of node) collectMappings(item, target, found);
151
+ return found;
152
+ }
153
+ if (!node || typeof node !== "object") return found;
154
+ const obj = node;
155
+ const property = obj[target.propertyField];
156
+ if (typeof property === "string" && property.trim()) {
157
+ found.push({
158
+ object: obj[target.objectField] === "company" ? "company" : "contact",
159
+ property
160
+ });
161
+ }
162
+ for (const value of Object.values(obj)) collectMappings(value, target, found);
163
+ return found;
164
+ }
165
+ const index = {
166
+ config,
167
+ controllers,
168
+ routes,
169
+ register({ strapi }) {
170
+ strapi.customFields.register({
171
+ name: "property",
172
+ plugin: "hubspot",
173
+ type: "string"
174
+ });
175
+ },
176
+ bootstrap({ strapi }) {
177
+ const targets = strapi.plugin("hubspot").config("validate", []);
178
+ if (!targets.length) return;
179
+ for (const target of targets) {
180
+ strapi.documents.use(async (context, next) => {
181
+ const isWrite = ["create", "update"].includes(context.action);
182
+ if (!isWrite || context.uid !== target.uid) return next();
183
+ const mappings = collectMappings(context.params?.data, target);
184
+ if (!mappings.length) return next();
185
+ const { apiKey } = await resolveApiKey(strapi);
186
+ if (!apiKey) return next();
187
+ let properties;
188
+ try {
189
+ properties = await listProperties(strapi, apiKey);
190
+ } catch {
191
+ strapi.log.warn("[hubspot] schéma indisponible — validation ignorée");
192
+ return next();
193
+ }
194
+ const problems = mappings.map((m) => checkProperty(properties, m.object, m.property)).filter((reason) => Boolean(reason));
195
+ if (problems.length) {
196
+ throw new errors.ValidationError(
197
+ `Mapping HubSpot invalide — ${[...new Set(problems)].join(" ; ")}`
198
+ );
199
+ }
200
+ return next();
201
+ });
202
+ }
203
+ }
204
+ };
205
+ export {
206
+ index as default
207
+ };
@@ -0,0 +1,83 @@
1
+ import type { Core } from "@strapi/strapi";
2
+ /**
3
+ * Plugin configuration (config/plugins.ts of the host app):
4
+ *
5
+ * hubspot: {
6
+ * enabled: true,
7
+ * config: {
8
+ * apiKey: env("HUBSPOT_API_KEY"), // optional — can be set from the admin UI
9
+ * validate: [ // optional — entries whose mappings are checked on save
10
+ * { uid: "api::form.form", objectField: "hsObject", propertyField: "hsProperty" },
11
+ * ],
12
+ * },
13
+ * }
14
+ *
15
+ * The API key set from the admin UI takes precedence over both.
16
+ */
17
+ interface ValidateTarget {
18
+ uid: string;
19
+ objectField: string;
20
+ propertyField: string;
21
+ }
22
+ declare const _default: {
23
+ config: {
24
+ default: {
25
+ apiKey: string;
26
+ validate: ValidateTarget[];
27
+ };
28
+ validator(cfg: {
29
+ validate?: unknown;
30
+ }): void;
31
+ };
32
+ controllers: {
33
+ properties: ({ strapi }: {
34
+ strapi: Core.Strapi;
35
+ }) => {
36
+ list(ctx: {
37
+ query: {
38
+ refresh?: string;
39
+ };
40
+ body: unknown;
41
+ throw: (s: number, m: string) => never;
42
+ }): Promise<void>;
43
+ };
44
+ settings: ({ strapi }: {
45
+ strapi: Core.Strapi;
46
+ }) => {
47
+ get(ctx: {
48
+ body: unknown;
49
+ }): Promise<void>;
50
+ update(ctx: {
51
+ request: {
52
+ body: {
53
+ apiKey?: string;
54
+ };
55
+ };
56
+ body: unknown;
57
+ }): Promise<void>;
58
+ reset(ctx: {
59
+ body: unknown;
60
+ }): Promise<void>;
61
+ };
62
+ };
63
+ routes: {
64
+ admin: {
65
+ type: string;
66
+ routes: {
67
+ method: string;
68
+ path: string;
69
+ handler: string;
70
+ config: {
71
+ policies: string[];
72
+ };
73
+ }[];
74
+ };
75
+ };
76
+ register({ strapi }: {
77
+ strapi: Core.Strapi;
78
+ }): void;
79
+ bootstrap({ strapi }: {
80
+ strapi: Core.Strapi;
81
+ }): void;
82
+ };
83
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import type { Core } from "@strapi/strapi";
2
+ export type HsObject = "contact" | "company";
3
+ export interface HsProperty {
4
+ name: string;
5
+ label: string;
6
+ object: HsObject;
7
+ type?: string;
8
+ /** Allowed values, for enumeration properties. */
9
+ options: string[];
10
+ /** HubSpot's own grouping, shown to help editors locate a property. */
11
+ group?: string;
12
+ }
13
+ /** Writable properties of both objects, cached and de-duplicated across calls. */
14
+ export declare function listProperties(strapi: Core.Strapi, apiKey: string, { force }?: {
15
+ force?: boolean;
16
+ }): Promise<HsProperty[]>;
17
+ /** Drop the cache — used after the API key changes. */
18
+ export declare function clearCache(): void;
19
+ /**
20
+ * Why a property can't be written, or `null` when it's fine. Returning a reason
21
+ * rather than a boolean lets the caller tell an editor what to fix.
22
+ */
23
+ export declare function checkProperty(properties: HsProperty[], object: HsObject, name: string): string | null;