@communecter/cocolight-api-client 1.0.130 → 1.0.132

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 (48) hide show
  1. package/dist/cocolight-api-client.browser.js +3 -3
  2. package/dist/cocolight-api-client.cjs +1 -1
  3. package/dist/cocolight-api-client.mjs.js +1 -1
  4. package/dist/cocolight-api-client.vite.mjs.js +1 -1
  5. package/dist/cocolight-api-client.vite.mjs.js.map +1 -1
  6. package/package.json +1 -1
  7. package/src/Api.ts +4 -4
  8. package/src/ApiClient.ts +3 -1
  9. package/src/api/Action.ts +535 -4
  10. package/src/api/Answer.ts +4 -1
  11. package/src/api/Badge.ts +5 -0
  12. package/src/api/BaseEntity.ts +118 -7
  13. package/src/api/Classified.ts +4 -0
  14. package/src/api/Comment.ts +3 -0
  15. package/src/api/EndpointApi.ts +96 -1
  16. package/src/api/EndpointApi.types.ts +448 -44
  17. package/src/api/Event.ts +5 -0
  18. package/src/api/Form.ts +64 -2
  19. package/src/api/News.ts +3 -0
  20. package/src/api/Organization.ts +47 -3
  21. package/src/api/Poi.ts +4 -0
  22. package/src/api/Project.ts +61 -0
  23. package/src/api/User.ts +10 -0
  24. package/src/api/serverDataType/Answer.ts +25 -0
  25. package/src/api/serverDataType/Form.ts +84 -4
  26. package/src/api/serverDataType/Organization.ts +13 -0
  27. package/src/api/serverDataType/Project.ts +15 -0
  28. package/src/endpoints.module.ts +1252 -268
  29. package/types/api/Action.d.ts +276 -2
  30. package/types/api/Answer.d.ts +1 -0
  31. package/types/api/Badge.d.ts +1 -0
  32. package/types/api/BaseEntity.d.ts +62 -0
  33. package/types/api/Classified.d.ts +1 -0
  34. package/types/api/Comment.d.ts +1 -0
  35. package/types/api/EndpointApi.d.ts +60 -1
  36. package/types/api/EndpointApi.types.d.ts +397 -41
  37. package/types/api/Event.d.ts +1 -0
  38. package/types/api/Form.d.ts +34 -0
  39. package/types/api/News.d.ts +1 -0
  40. package/types/api/Organization.d.ts +20 -1
  41. package/types/api/Poi.d.ts +1 -0
  42. package/types/api/Project.d.ts +29 -0
  43. package/types/api/User.d.ts +7 -0
  44. package/types/api/serverDataType/Answer.d.ts +22 -0
  45. package/types/api/serverDataType/Form.d.ts +89 -4
  46. package/types/api/serverDataType/Organization.d.ts +13 -0
  47. package/types/api/serverDataType/Project.d.ts +15 -0
  48. package/types/endpoints.module.d.ts +2825 -1491
@@ -72,6 +72,7 @@ export declare class Event extends BaseEntity<EventItemNormalized> {
72
72
  }): Promise<PaginatorPage<User>>;
73
73
  project(): Promise<never>;
74
74
  poi(): Promise<never>;
75
+ form(): Promise<never>;
75
76
  event(): Promise<never>;
76
77
  badge(): Promise<never>;
77
78
  /**
@@ -1,4 +1,7 @@
1
1
  import { BaseEntity } from "./BaseEntity.js";
2
+ import type { Answer } from "./Answer.js";
3
+ import type { PaginatorPage, PaginatorState } from "./BaseEntity.js";
4
+ import type { CoformAnswersSearchData } from "./EndpointApi.types.js";
2
5
  import type { FormItemNormalized } from "./serverDataType/Form.js";
3
6
  export declare class Form extends BaseEntity<FormItemNormalized> {
4
7
  static entityType: string;
@@ -20,4 +23,35 @@ export declare class Form extends BaseEntity<FormItemNormalized> {
20
23
  * Constant : GET_COFORM_BY_ID
21
24
  */
22
25
  get(): Promise<Record<string, any>>;
26
+ /**
27
+ * Recherche paginée des Answers liées à CE Form, dans le costumContext de son parent.
28
+ *
29
+ * **Délégation au parent** : le Form lui-même n'a pas de costumContext (pas de
30
+ * `slug` côté serverData). Cette méthode remonte `this.parent` (qui doit être
31
+ * une `Organization` ou un `Project` ayant un slug) et délègue à
32
+ * `parent.coformAnswersSearch({ filters: { form: this.id, ... } })`.
33
+ *
34
+ * Pattern identique à `Comment._add` qui remonte `this.parent` pour récupérer
35
+ * le contexte d'ancrage.
36
+ *
37
+ * **Important** : pour utiliser cette méthode, le Form doit avoir été créé via
38
+ * `org.form({id})` ou `project.form({id})` — pas via `api.form({id})` direct
39
+ * (qui crée un Form avec `parent = ApiClient`, sans costumContext).
40
+ *
41
+ * @param data - Paramètres de recherche (`filters`, `fields`, `sortBy`, etc.).
42
+ * Le champ `filters.form` est automatiquement injecté avec `this.id`.
43
+ * @param options - Options de pagination (`restoredState` pour reprendre une navigation)
44
+ * @returns Première page paginée avec `results` (Answer[]), `count`, `hasNext`, etc.
45
+ * @throws {ApiError} si le Form n'a pas d'id ou si son parent n'a pas de costumContext.
46
+ *
47
+ * @example
48
+ * const org = await api.organization({ slug: "navigatorDesTierslieux" });
49
+ * const form = await org.form({ id: "6925e2b05dd63b02ca70d6d9" });
50
+ * const page = await form.getAnswers({ sortBy: { created: -1 } });
51
+ * page.results; // Answer[]
52
+ * page.count.total; // nombre total
53
+ */
54
+ getAnswers(data?: Partial<CoformAnswersSearchData>, options?: {
55
+ restoredState?: PaginatorState;
56
+ }): Promise<PaginatorPage<Answer>>;
23
57
  }
@@ -141,4 +141,5 @@ export declare class News extends BaseEntity<NewsItemNormalized> {
141
141
  isAuthor(options?: {
142
142
  silent?: boolean;
143
143
  }): boolean;
144
+ form(): Promise<never>;
144
145
  }
@@ -72,6 +72,17 @@ export declare class Organization extends BaseEntity<OrganizationItemNormalized>
72
72
  defaultFields: Record<string, any>;
73
73
  removeFields: string[];
74
74
  transforms: Record<string, (val: any, full: any) => any>;
75
+ /**
76
+ * Transforme les Answer docs imbriqués dans `data.answers` (renvoyé par /costum/navigator/gettl)
77
+ * en instances d'entité Answer. Le payload backend fait un PHDB::find(Answer::COLLECTION, ...) brut,
78
+ * donc on injecte `collection: "answers"` avant linkage pour rester cohérent avec les autres entités.
79
+ *
80
+ * Structure : `data.answers = { [formId]: AnswerDoc[] }` → `{ [formId]: Answer[] }`
81
+ * (le formId reste une string ; charger le Form se fait via `api.form({ id: formId })`).
82
+ *
83
+ * @protected
84
+ */
85
+ protected _transformServerData(data: OrganizationItemNormalized): OrganizationItemNormalized;
75
86
  _add: (payload: Record<string, any>) => Promise<void>;
76
87
  _update: (payload: Record<string, any>) => Promise<boolean>;
77
88
  /**
@@ -207,6 +218,14 @@ export declare class Organization extends BaseEntity<OrganizationItemNormalized>
207
218
  * Crée une instance de news et la récupère si nécessaire.
208
219
  */
209
220
  news(newsData?: Parameters<BaseEntity<OrganizationItemNormalized>["news"]>[0]): Promise<import("./News.js").News>;
221
+ /**
222
+ * {@inheritDoc BaseEntity#form}
223
+ *
224
+ * Crée une instance de Form dans le contexte de cette organisation : le Form aura
225
+ * `this.parent = Organization`, ce qui permet à `form.getAnswers()` de déléguer
226
+ * `coformAnswersSearch` avec le bon costumContext.
227
+ */
228
+ form(formData?: Parameters<BaseEntity<OrganizationItemNormalized>["form"]>[0]): Promise<import("./Form.js").Form>;
210
229
  /**
211
230
  * Met à jour les horaires d'ouverture de l'organisation.
212
231
  * Utilise UPDATE_PATH_VALUE pour modifier le champ openingHours.
@@ -294,7 +313,7 @@ export declare class Organization extends BaseEntity<OrganizationItemNormalized>
294
313
  *
295
314
  * Cette surcharge précise que la recherche est faite dans le contexte de l'organisation.
296
315
  */
297
- searchCostum(data: Parameters<BaseEntity<OrganizationItemNormalized>["searchCostum"]>[0]): Promise<PaginatorPage<any>>;
316
+ searchCostum(data: Parameters<BaseEntity<OrganizationItemNormalized>["searchCostum"]>[0], options?: Parameters<BaseEntity<OrganizationItemNormalized>["searchCostum"]>[1]): Promise<PaginatorPage<any>>;
298
317
  /**
299
318
  * {@inheritDoc BaseEntity#coformAnswersSearch}
300
319
  *
@@ -33,6 +33,7 @@ export declare class Poi extends BaseEntity<PoiItemNormalized> {
33
33
  project(): Promise<never>;
34
34
  poi(): Promise<never>;
35
35
  event(): Promise<never>;
36
+ form(): Promise<never>;
36
37
  badge(): Promise<never>;
37
38
  /**
38
39
  * {@inheritDoc BaseEntity#news}
@@ -15,6 +15,13 @@ export declare class Project extends BaseEntity<ProjectItemNormalized> {
15
15
  defaultFields: Record<string, any>;
16
16
  removeFields: string[];
17
17
  transforms: ProjectTransforms;
18
+ /**
19
+ * Transforme les Answer docs imbriqués dans `data.answers` (renvoyé par /costum/navigator/gettl)
20
+ * en instances d'entité Answer. Voir `Organization._transformServerData` pour le même pattern.
21
+ *
22
+ * @protected
23
+ */
24
+ protected _transformServerData(data: ProjectItemNormalized): ProjectItemNormalized;
18
25
  _add: (payload: Record<string, any>) => Promise<void>;
19
26
  _update: (payload: Record<string, any>) => Promise<boolean>;
20
27
  /**
@@ -112,6 +119,12 @@ export declare class Project extends BaseEntity<ProjectItemNormalized> {
112
119
  * Récupère la galerie d'un projet.
113
120
  */
114
121
  getGallery(data?: Parameters<BaseEntity<ProjectItemNormalized>["getGallery"]>[0]): Promise<Record<string, any>>;
122
+ /**
123
+ * {@inheritDoc BaseEntity#searchCostum}
124
+ *
125
+ * Cette surcharge précise que la recherche est faite dans le contexte du projet.
126
+ */
127
+ searchCostum(data: Parameters<BaseEntity<ProjectItemNormalized>["searchCostum"]>[0], options?: Parameters<BaseEntity<ProjectItemNormalized>["searchCostum"]>[1]): Promise<PaginatorPage<any>>;
115
128
  /**
116
129
  * {@inheritDoc BaseEntity#project}
117
130
  *
@@ -141,7 +154,23 @@ export declare class Project extends BaseEntity<ProjectItemNormalized> {
141
154
  *
142
155
  * Crée une instance de news et la récupère si nécessaire.
143
156
  */
157
+ /**
158
+ * {@inheritDoc BaseEntity#form}
159
+ *
160
+ * Crée une instance de Form dans le contexte de ce projet : le Form aura
161
+ * `this.parent = Project`, ce qui permet à `form.getAnswers()` de déléguer
162
+ * `coformAnswersSearch` avec le bon costumContext.
163
+ */
164
+ form(formData?: Parameters<BaseEntity<ProjectItemNormalized>["form"]>[0]): Promise<import("./Form.js").Form>;
144
165
  news(newsData?: Parameters<BaseEntity<ProjectItemNormalized>["news"]>[0]): Promise<import("./News.js").News>;
166
+ /**
167
+ * Vérifie qu'un milestoneId existe dans `serverData.oceco.milestones[]`.
168
+ * Sert à valider une référence avant d'attacher une entité (Action) à un milestone.
169
+ *
170
+ * @param milestoneId - L'ID du milestone à chercher.
171
+ * @returns `true` si trouvé, `false` sinon (y compris si oceco/milestones absents).
172
+ */
173
+ hasMilestone(milestoneId: string): boolean;
145
174
  /**
146
175
  * {@inheritDoc BaseEntity#action}
147
176
  *
@@ -45,6 +45,7 @@ export declare class User extends BaseEntity<UserItemNormalized> {
45
45
  News: typeof import("./News.js").News;
46
46
  Comment: typeof import("./Comment.js").Comment;
47
47
  Answer: typeof import("./Answer.js").Answer;
48
+ Form?: typeof import("./Form.js").Form;
48
49
  Classified?: typeof import("./Classified.js").Classified;
49
50
  Action?: typeof import("./Action.js").Action;
50
51
  });
@@ -243,6 +244,12 @@ export declare class User extends BaseEntity<UserItemNormalized> {
243
244
  * Crée une instance de projet et récupère son profil si nécessaire.
244
245
  */
245
246
  project(projectData?: Parameters<BaseEntity<UserItemNormalized>["project"]>[0]): Promise<import("./Project.js").Project>;
247
+ /**
248
+ * Bloqué sur User : un Form nécessite un costumContext (slug d'Organization/Project)
249
+ * que User n'a pas. Utilisez `user.organization({slug}).then(o => o.form({id}))`
250
+ * ou `user.project({slug}).then(p => p.form({id}))` pour la chaîne propagée.
251
+ */
252
+ form(): Promise<never>;
246
253
  /**
247
254
  * {@inheritDoc BaseEntity#news}
248
255
  *
@@ -1,10 +1,32 @@
1
1
  import { DateValue, IdObject, ParentsMap } from "./common.js";
2
2
  import type EJSONType from "../../EJSONType.js";
3
+ import type { Answer } from "../Answer.js";
3
4
  import type { Organization } from "../Organization.js";
4
5
  import type { Project } from "../Project.js";
5
6
  import type { User } from "../User.js";
6
7
  type ObjectIDCtor = typeof EJSONType["ObjectID"];
7
8
  type ObjectID = InstanceType<ObjectIDCtor>;
9
+ /**
10
+ * Identifiant d'un Form (string 24 hex). Alias purement sémantique de `string` :
11
+ * sert à lever l'ambiguïté dans `Record<FormId, ...>` — sans ce nom, on pourrait croire
12
+ * qu'il s'agit d'un answerId ou autre.
13
+ *
14
+ * ⚠️ Ces formIds proviennent de la config costum (ex: `navigatorDesTierslieux`)
15
+ * et ne correspondent **pas toujours** à des docs queriables via `api.form({id}).get()` —
16
+ * il s'agit parfois de références internes au costum (templates, sous-forms, etc.).
17
+ * À traiter comme un opaque token côté caller.
18
+ */
19
+ export type FormId = string;
20
+ /**
21
+ * Forme JSON brute : Answer docs (MongoDB) groupés par formId.
22
+ * Apparaît dans `result.answers` du payload `/costum/navigator/gettl` avant linkage.
23
+ */
24
+ export type AnswerDocsByFormId = Record<FormId, AnswerItemJson[]>;
25
+ /**
26
+ * Forme normalisée : instances `Answer` groupées par formId.
27
+ * Construite par `Organization/Project._transformServerData` à partir de `AnswerDocsByFormId`.
28
+ */
29
+ export type AnswersByFormId = Record<FormId, Answer[]>;
8
30
  export interface AnswerLinksBlock {
9
31
  answered: string[];
10
32
  }
@@ -7,9 +7,22 @@ export interface FormSubFormInputField {
7
7
  placeholder?: string;
8
8
  info?: string;
9
9
  type?: string;
10
+ position?: string;
10
11
  positions?: Record<string, string>;
12
+ width?: string;
11
13
  markdown?: boolean;
12
14
  uploader?: Record<string, any>;
15
+ rules?: {
16
+ width?: {
17
+ min: number;
18
+ max: number;
19
+ };
20
+ syntaxe?: {
21
+ regexp: string;
22
+ errormsg: string;
23
+ };
24
+ [key: string]: unknown;
25
+ };
13
26
  [key: string]: unknown;
14
27
  }
15
28
  export interface FormSubFormInputs {
@@ -21,31 +34,103 @@ export interface FormSubFormInputs {
21
34
  inputs: Record<string, FormSubFormInputField>;
22
35
  [key: string]: unknown;
23
36
  }
37
+ /**
38
+ * Bloc d'accès au Form — décrit qui peut répondre, quand, comment.
39
+ * Renvoyé par `GET_COFORM_BY_ID` (cf. /survey/coform/getformbyid).
40
+ */
41
+ export interface FormAccess {
42
+ /** Si l'utilisateur courant peut répondre maintenant. */
43
+ canAnswer: boolean;
44
+ /** Raison si `canAnswer` est false (ex: "not_logged_in", "form_closed"). */
45
+ reason?: string;
46
+ /** État global du form (ex: "open", "closed", "pending"). */
47
+ formStatus?: string;
48
+ /** Id de la réponse existante de l'user courant (s'il en a une). */
49
+ existingAnswerId?: string | null;
50
+ /** Doc de la réponse existante (peut être enrichi). */
51
+ existingAnswer?: Record<string, unknown> | null;
52
+ /** Si le form requiert d'être connecté. */
53
+ requiresLogin?: boolean;
54
+ /** Réponse temporaire (brouillon) autorisée. */
55
+ allowTemporary?: boolean;
56
+ /** Réponse nécessite confirmation. */
57
+ withConfirmation?: boolean;
58
+ /** Réservé aux membres uniquement. */
59
+ isOnlyMember?: boolean;
60
+ /** Une seule réponse par personne. */
61
+ isOneAnswerPerPers?: boolean;
62
+ /** Form actif (vs désactivé). */
63
+ isActive?: boolean;
64
+ /** Fenêtres de dates (ouverture/fermeture, avec/sans confirmation). */
65
+ dates?: {
66
+ start?: string | null;
67
+ end?: string | null;
68
+ startNoConfirmation?: string | null;
69
+ endNoConfirmation?: string | null;
70
+ };
71
+ /** Liste des champs restreints (non visibles selon le rôle). */
72
+ restrictedFields?: string[];
73
+ [key: string]: unknown;
74
+ }
75
+ /**
76
+ * Forme JSON brute du Form telle que renvoyée par `GET_COFORM_BY_ID`.
77
+ *
78
+ * ⚠️ Le champ `collection` n'est PAS présent dans le payload backend natif
79
+ * (PHDB::find renvoie le doc brut sans nom de table). Il doit être injecté
80
+ * côté SDK par `Form.get()` pour cohérence avec les autres entités.
81
+ */
24
82
  export interface FormItemJson {
25
83
  _id: IdObject;
26
- config?: string | null;
84
+ id?: string;
85
+ /** Injecté côté SDK (le backend ne le renvoie pas). */
86
+ collection?: "forms";
87
+ /** Nom du Form (ex: "Coworking", "Hébergement"). */
88
+ name?: string;
89
+ /** Type du Form (ex: "coformgeneric", "template"). */
90
+ type: string;
91
+ config?: Record<string, unknown> | null;
27
92
  parent?: ParentsMap | null;
28
93
  subForms?: string[] | null;
29
- type: string;
30
94
  created?: DateValue;
31
95
  creator?: string;
32
96
  params?: Record<string, unknown> | null;
33
97
  updated?: DateValue;
34
98
  inputs?: Record<string, FormSubFormInputs> | null;
99
+ /** Logique d'accès calculée côté backend pour l'user courant. */
100
+ access?: FormAccess;
101
+ /** Champs réservés aux admins du lieu/projet. */
102
+ placeAdminOnlyFields?: string[];
103
+ /** Champs réservés aux membres du lieu/projet. */
104
+ placeMemberOnlyFields?: string[];
105
+ /** Si le grand public peut éditer une réponse partagée. */
106
+ publicCanEditSharedAnswer?: boolean;
107
+ /** Path vers une question partagée (legacy / advanced usage). */
108
+ sharedQuestionPath?: Record<string, unknown> | null;
109
+ /** Utilise une image de bannière (présent surtout sur `type=template`). */
110
+ useBannerImg?: boolean;
35
111
  [key: string]: unknown;
36
112
  }
37
113
  export interface FormItemNormalized {
38
114
  id: string;
39
115
  _id: ObjectID;
40
- config?: string | null;
116
+ /** Injecté côté SDK (le backend ne le renvoie pas). */
117
+ collection?: "forms";
118
+ name?: string;
119
+ type: string;
120
+ config?: Record<string, unknown> | null;
41
121
  parent?: ParentsMap | null;
42
122
  subForms?: string[] | null;
43
- type: string;
44
123
  created?: Date;
45
124
  creator?: string;
46
125
  params?: Record<string, unknown> | null;
47
126
  updated?: Date;
48
127
  inputs?: Record<string, FormSubFormInputs> | null;
128
+ access?: FormAccess;
129
+ placeAdminOnlyFields?: string[];
130
+ placeMemberOnlyFields?: string[];
131
+ publicCanEditSharedAnswer?: boolean;
132
+ sharedQuestionPath?: Record<string, unknown> | null;
133
+ useBannerImg?: boolean;
49
134
  [key: string]: unknown;
50
135
  }
51
136
  export {};
@@ -1,3 +1,4 @@
1
+ import type { AnswerDocsByFormId, AnswersByFormId } from "./Answer.js";
1
2
  import type { GeoCoordinates, GeoPosition, IdObject, LinkMemberOfRef, LinkMemberRef, LinkProjectRef, LinkEventsRef, OpeningHoursEntry, ParentsMap, PostalAddress, DateValue, SocialNetwork } from "./common.js";
2
3
  import type EJSONType from "../../EJSONType.js";
3
4
  type ObjectIDCtor = typeof EJSONType["ObjectID"];
@@ -40,6 +41,12 @@ export interface OrganizationItemJson {
40
41
  email?: string;
41
42
  url?: string;
42
43
  socialNetwork?: SocialNetwork;
44
+ /**
45
+ * Enrichissement par les Answers liées au résultat — renvoyé uniquement par
46
+ * `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
47
+ * Forme JSON brute (avant linkage par `_transformServerData`).
48
+ */
49
+ answers?: AnswerDocsByFormId;
43
50
  [key: string]: unknown;
44
51
  }
45
52
  export interface OrganizationItemNormalized {
@@ -69,6 +76,12 @@ export interface OrganizationItemNormalized {
69
76
  email?: string;
70
77
  url?: string;
71
78
  socialNetwork?: SocialNetwork;
79
+ /**
80
+ * Enrichissement par les Answers liées au résultat — renvoyé uniquement par
81
+ * `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
82
+ * Forme normalisée (après linkage par `Organization._transformServerData`) : `Answer[]` par formId.
83
+ */
84
+ answers?: AnswersByFormId;
72
85
  [key: string]: unknown;
73
86
  }
74
87
  export {};
@@ -1,3 +1,4 @@
1
+ import type { AnswerDocsByFormId, AnswersByFormId } from "./Answer.js";
1
2
  import type { GeoCoordinates, GeoPosition, IdObject, LinkContributorsRef, LinkProjectRef, LinkEventsRef, ParentsMap, PostalAddress, DateValue, SocialNetwork } from "./common.js";
2
3
  import type EJSONType from "../../EJSONType.js";
3
4
  type ObjectIDCtor = typeof EJSONType["ObjectID"];
@@ -39,6 +40,13 @@ export interface ProjectItemJson {
39
40
  email?: string;
40
41
  url?: string;
41
42
  answer?: string;
43
+ /**
44
+ * Enrichissement par les Answers liées au résultat — renvoyé uniquement par
45
+ * `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
46
+ * Forme JSON brute (avant linkage par `_transformServerData`).
47
+ * Distinct du champ `answer` (singulier) qui est l'id d'une Answer principale.
48
+ */
49
+ answers?: AnswerDocsByFormId;
42
50
  [key: string]: unknown;
43
51
  }
44
52
  export interface ProjectItemNormalized {
@@ -68,6 +76,13 @@ export interface ProjectItemNormalized {
68
76
  email?: string;
69
77
  url?: string;
70
78
  answer?: string;
79
+ /**
80
+ * Enrichissement par les Answers liées au résultat — renvoyé uniquement par
81
+ * `/costum/navigator/gettl` (variant `navigator-tl` de `searchCostum`).
82
+ * Forme normalisée (après linkage par `Project._transformServerData`) : `Answer[]` par formId.
83
+ * Distinct du champ `answer` (singulier) qui est l'id d'une Answer principale.
84
+ */
85
+ answers?: AnswersByFormId;
71
86
  [key: string]: unknown;
72
87
  }
73
88
  export {};