strapi-plugin-hubspot 0.9.0 → 0.11.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 (36) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/_chunks/{Forms-BsKys-Hc.mjs → Forms-DadTbe9Q.mjs} +203 -16
  3. package/dist/_chunks/{Forms-JuzNfsbL.js → Forms-Dn2W6OS4.js} +202 -15
  4. package/dist/_chunks/{HubspotFormInput-CIvHS5rp.mjs → HubspotFormInput-NO59AtM_.mjs} +1 -1
  5. package/dist/_chunks/{HubspotFormInput-ph9pe6xY.js → HubspotFormInput-S_Z2t_DN.js} +1 -1
  6. package/dist/_chunks/{HubspotObjectInput-dAd-lMdD.js → HubspotObjectInput-BIgIa_Dm.js} +3 -3
  7. package/dist/_chunks/{HubspotObjectInput-BX1qYD2c.mjs → HubspotObjectInput-DAo3FAMz.mjs} +3 -3
  8. package/dist/_chunks/{HubspotPropertyInput-BM3ZaY8f.js → HubspotPropertyInput-B-piLcIy.js} +3 -3
  9. package/dist/_chunks/{HubspotPropertyInput-C2Z6472q.mjs → HubspotPropertyInput-KX1Bhcsl.mjs} +3 -3
  10. package/dist/_chunks/{Settings-Cdcjy9DA.js → Settings-DMu_QThB.js} +2 -2
  11. package/dist/_chunks/{Settings-DnwGetcc.mjs → Settings-Dz0PEFOv.mjs} +2 -2
  12. package/dist/_chunks/{en-Cqfair98.js → en-BDGqW_yS.js} +24 -1
  13. package/dist/_chunks/{en-CcQWnXra.mjs → en-CH5LSPkL.mjs} +24 -1
  14. package/dist/_chunks/{fr-CBRZ9q8U.mjs → fr-65-RaB3w.mjs} +24 -1
  15. package/dist/_chunks/{fr-D117-3Ta.js → fr-C82LMqUM.js} +24 -1
  16. package/dist/_chunks/{index-BbRtXnAL.js → index-B7Ru1fu5.js} +6 -6
  17. package/dist/_chunks/{index-LTaVoyWJ.mjs → index-DA31TY2n.mjs} +6 -6
  18. package/dist/_chunks/{objectLabels-DE2xGq7f.mjs → objectLabels-Bl7sO5pb.mjs} +1 -1
  19. package/dist/_chunks/{objectLabels-DU0Cagt_.js → objectLabels-FTh984_h.js} +1 -1
  20. package/dist/_chunks/{useHubspotSchema-BSKow0X3.mjs → useHubspotSchema-BlDilCHc.mjs} +1 -1
  21. package/dist/_chunks/{useHubspotSchema-DzBMAxv7.js → useHubspotSchema-C2_PuXEm.js} +1 -1
  22. package/dist/admin/index.js +1 -1
  23. package/dist/admin/index.mjs +1 -1
  24. package/dist/admin/src/builder/CompanyMapEditor.d.ts +14 -0
  25. package/dist/admin/src/builder/types.d.ts +14 -2
  26. package/dist/server/index.js +1029 -17
  27. package/dist/server/index.mjs +1029 -17
  28. package/dist/server/src/__tests__/company.test.d.ts +1 -0
  29. package/dist/server/src/company.d.ts +79 -0
  30. package/dist/server/src/conditions.d.ts +7 -1
  31. package/dist/server/src/content-types.d.ts +3 -0
  32. package/dist/server/src/data/naf.d.ts +1 -0
  33. package/dist/server/src/forms.d.ts +16 -0
  34. package/dist/server/src/index.d.ts +13 -0
  35. package/dist/shared/types.d.ts +51 -2
  36. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The company field's data source: the French government's open
3
+ * "Recherche d'entreprises" API (recherche-entreprises.api.gouv.fr — INSEE
4
+ * SIRENE data, no key, ~7 req/s per IP). Two uses:
5
+ *
6
+ * - autocomplete: `searchCompanies(q)` behind the public route, TTL-cached;
7
+ * - authority at submission: `resolveSiret(siret)` re-reads the SIRENE
8
+ * record server-side — the browser only ever nominates a SIRET, never the
9
+ * data that reaches the CRM.
10
+ */
11
+ import type { Primitive } from "./conditions";
12
+ export type CompanyDatum = "name" | "siret" | "siren" | "address" | "zip" | "city" | "headquarters" | "naf" | "nafLabel" | "headcount";
13
+ export type CompanyMap = Partial<Record<CompanyDatum, {
14
+ object?: string;
15
+ property?: string;
16
+ }>>;
17
+ export interface CompanyHit {
18
+ siret: string;
19
+ siren: string;
20
+ name: string;
21
+ address?: string;
22
+ zip?: string;
23
+ city?: string;
24
+ headquarters: boolean;
25
+ naf?: string;
26
+ nafLabel?: string;
27
+ headcount?: string;
28
+ closed?: boolean;
29
+ }
30
+ /** The slice of the API answer this module reads — everything else ignored. */
31
+ interface ApiEtablissement {
32
+ siret?: string;
33
+ adresse?: string;
34
+ code_postal?: string;
35
+ libelle_commune?: string;
36
+ est_siege?: boolean;
37
+ etat_administratif?: string;
38
+ activite_principale?: string;
39
+ tranche_effectif_salarie?: string;
40
+ }
41
+ interface ApiResult {
42
+ nom_complet?: string;
43
+ nom_raison_sociale?: string;
44
+ siren?: string;
45
+ etat_administratif?: string;
46
+ activite_principale?: string;
47
+ tranche_effectif_salarie?: string;
48
+ siege?: ApiEtablissement;
49
+ matching_etablissements?: ApiEtablissement[];
50
+ }
51
+ export interface SearchPayload {
52
+ results?: ApiResult[];
53
+ }
54
+ export declare function nafLabelOf(code?: string): string | undefined;
55
+ export declare function headcountLabelOf(code?: string): string | undefined;
56
+ /**
57
+ * One entreprise → one hit per relevant établissement: the siège first, then
58
+ * the établissements the query matched — deduped (the siège often appears in
59
+ * both lists) and, for search results, stripped of closed ones.
60
+ */
61
+ export declare function normalizeCompanyHits(payload: SearchPayload): CompanyHit[];
62
+ /**
63
+ * The exact établissement a SIRET designates in a payload — closed ones
64
+ * included: at resolution time the visitor already chose it, and hiding the
65
+ * data would only make the lead poorer.
66
+ */
67
+ export declare function pickSiret(payload: SearchPayload, siret: string): CompanyHit | null;
68
+ /**
69
+ * The resolved record → one property bag per CRM object, honoring the field's
70
+ * `companyMap`. Only complete mappings with an actual value produce a write.
71
+ */
72
+ export declare function companyProperties(map: CompanyMap, hit: CompanyHit): Record<string, Record<string, Primitive>>;
73
+ /** Autocomplete search. Bounded, cached; empty on any failure. */
74
+ export declare function searchCompanies(q: string): Promise<CompanyHit[]>;
75
+ /** Authoritative re-resolution of a selected SIRET; null when unreachable. */
76
+ export declare function resolveSiret(siret: string): Promise<CompanyHit | null>;
77
+ /** Test hook. */
78
+ export declare function clearCompanyCache(): void;
79
+ export {};
@@ -2,12 +2,14 @@
2
2
  * The condition engine — pure functions, no Strapi.
3
3
  */
4
4
  export type Primitive = string | number | boolean;
5
- export type Operator = "eq" | "neq" | "contains" | "empty" | "notEmpty" | "gt" | "lt";
5
+ export type Operator = "eq" | "neq" | "contains" | "notContains" | "startsWith" | "endsWith" | "empty" | "notEmpty" | "gt" | "lt" | "in" | "notIn";
6
6
  export interface Rule {
7
7
  /** Stable id (`fld_…`) of the field the rule reads — never its name. */
8
8
  field: string;
9
9
  operator: Operator;
10
10
  value?: string;
11
+ /** For `in` / `notIn`: the values any one of which satisfies the rule. */
12
+ values?: string[];
11
13
  }
12
14
  export interface Condition {
13
15
  logic: "and" | "or";
@@ -55,6 +57,10 @@ export type DefinitionError = {
55
57
  code: "missing-name";
56
58
  fieldId: string;
57
59
  name: string;
60
+ } | {
61
+ code: "companion-collision";
62
+ fieldId: string;
63
+ name: string;
58
64
  } | {
59
65
  code: "unknown-field";
60
66
  fieldId?: string;
@@ -160,6 +160,9 @@ declare const _default: {
160
160
  rejected: {
161
161
  type: string;
162
162
  };
163
+ companies: {
164
+ type: string;
165
+ };
163
166
  };
164
167
  };
165
168
  };
@@ -0,0 +1 @@
1
+ export declare const NAF_LABELS: Record<string, string>;
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { Core } from "@strapi/strapi";
12
12
  import { type FormDefinition, type Primitive } from "./conditions";
13
+ import { type CompanyHit } from "./company";
13
14
  import { checkMapping, type Problem } from "./properties";
14
15
  export interface FormEntry {
15
16
  name: string;
@@ -76,6 +77,21 @@ export interface SubmitOutcome {
76
77
  label: string;
77
78
  }[];
78
79
  }
80
+ /** What a company field contributed to a submission — persisted with it. */
81
+ export interface CompanyRecord {
82
+ field: string;
83
+ siret?: string;
84
+ name?: string;
85
+ /** True when the SIRET was re-resolved against SIRENE server-side. */
86
+ resolved: boolean;
87
+ closed?: boolean;
88
+ }
89
+ /**
90
+ * The browser's snapshot of the selected hit (`<name>__company`), used ONLY
91
+ * when the live re-resolution failed: parsed defensively, whitelisted keys,
92
+ * clipped strings — never trusted further than a display fallback.
93
+ */
94
+ export declare function parseCompanySnapshot(raw: unknown): CompanyHit | null;
79
95
  export declare function createFormsService(strapi: Core.Strapi, _opts?: {
80
96
  sleep?: (ms: number) => Promise<void>;
81
97
  }): {
@@ -27,6 +27,7 @@ declare const _default: {
27
27
  displayName: string;
28
28
  description: string;
29
29
  };
30
+ /** RBAC action gating the settings screen and its routes. */
30
31
  options: {
31
32
  draftAndPublish: boolean;
32
33
  };
@@ -166,6 +167,9 @@ declare const _default: {
166
167
  rejected: {
167
168
  type: string;
168
169
  };
170
+ companies: {
171
+ type: string;
172
+ };
169
173
  };
170
174
  };
171
175
  };
@@ -261,6 +265,15 @@ declare const _default: {
261
265
  duplicate(ctx: import("./formsAdmin").Ctx): Promise<void>;
262
266
  remove(ctx: import("./formsAdmin").Ctx): Promise<void>;
263
267
  };
268
+ company: () => {
269
+ /** SIRENE autocomplete for the company field — bounded, cached upstream. */
270
+ search(ctx: {
271
+ query: {
272
+ q?: string;
273
+ };
274
+ body: unknown;
275
+ }): Promise<void>;
276
+ };
264
277
  submissions: ({ strapi }: {
265
278
  strapi: Core.Strapi;
266
279
  }) => {
@@ -8,13 +8,27 @@
8
8
  * POST /api/hubspot/forms/:slug/submit → SubmitResponse (SubmitRequest in)
9
9
  */
10
10
 
11
- export type Operator = "eq" | "neq" | "contains" | "empty" | "notEmpty" | "gt" | "lt";
11
+ export type Operator =
12
+ | "eq"
13
+ | "neq"
14
+ | "contains"
15
+ | "notContains"
16
+ | "startsWith"
17
+ | "endsWith"
18
+ | "empty"
19
+ | "notEmpty"
20
+ | "gt"
21
+ | "lt"
22
+ | "in"
23
+ | "notIn";
12
24
 
13
25
  export interface Rule {
14
26
  /** Stable id (`fld_…`) of the field the rule reads — never its name. */
15
27
  field: string;
16
28
  operator: Operator;
17
29
  value?: string;
30
+ /** For `in` / `notIn`: the values any one of which satisfies the rule. */
31
+ values?: string[];
18
32
  }
19
33
 
20
34
  export interface Condition {
@@ -31,13 +45,46 @@ export type FieldType =
31
45
  | "textarea"
32
46
  | "select"
33
47
  | "checkbox"
34
- | "radio";
48
+ | "radio"
49
+ | "company";
35
50
 
36
51
  export interface FieldOption {
37
52
  value: string;
38
53
  label?: string;
39
54
  }
40
55
 
56
+ /** One datum a company field can resolve from SIRENE and map to the CRM. */
57
+ export type CompanyDatum =
58
+ | "name"
59
+ | "siret"
60
+ | "siren"
61
+ | "address"
62
+ | "zip"
63
+ | "city"
64
+ | "headquarters"
65
+ | "naf"
66
+ | "nafLabel"
67
+ | "headcount";
68
+
69
+ /** One SIRENE hit, as GET /api/hubspot/company-search serves it. */
70
+ export interface CompanyHit {
71
+ siret: string;
72
+ siren: string;
73
+ name: string;
74
+ address?: string;
75
+ zip?: string;
76
+ city?: string;
77
+ headquarters: boolean;
78
+ naf?: string;
79
+ nafLabel?: string;
80
+ headcount?: string;
81
+ }
82
+
83
+ /** GET /api/hubspot/company-search?q= */
84
+ export interface CompanySearchResponse {
85
+ companies: CompanyHit[];
86
+ }
87
+
41
88
  /** A field as the public endpoint serves it — the CRM mapping is stripped. */
42
89
  export interface PublicFormField {
43
90
  id: string;
@@ -52,6 +99,8 @@ export interface PublicFormField {
52
99
  required?: boolean;
53
100
  persist?: "session" | "days30";
54
101
  options?: FieldOption[];
102
+ /** Company fields: rotating placeholder examples, in display order. */
103
+ placeholderExamples?: string[];
55
104
  visibleIf?: Condition | null;
56
105
  }
57
106
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-hubspot",
3
- "version": "0.9.0",
3
+ "version": "0.11.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",
@@ -100,4 +100,4 @@
100
100
  "engines": {
101
101
  "node": ">=18.0.0"
102
102
  }
103
- }
103
+ }