@communecter/cocolight-api-client 1.0.131 → 1.0.133

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 +9 -9
  8. package/src/ApiClient.ts +3 -1
  9. package/src/api/Action.ts +4 -0
  10. package/src/api/Answer.ts +846 -6
  11. package/src/api/Badge.ts +5 -0
  12. package/src/api/BaseEntity.ts +190 -11
  13. package/src/api/Classified.ts +4 -0
  14. package/src/api/Comment.ts +3 -0
  15. package/src/api/EndpointApi.ts +16 -1
  16. package/src/api/EndpointApi.types.ts +375 -44
  17. package/src/api/Event.ts +5 -0
  18. package/src/api/Form.ts +102 -2
  19. package/src/api/News.ts +3 -0
  20. package/src/api/Organization.ts +44 -2
  21. package/src/api/Poi.ts +4 -0
  22. package/src/api/Project.ts +48 -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 +1185 -208
  29. package/types/api/Action.d.ts +1 -0
  30. package/types/api/Answer.d.ts +296 -2
  31. package/types/api/Badge.d.ts +1 -0
  32. package/types/api/BaseEntity.d.ts +101 -3
  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 +10 -1
  36. package/types/api/EndpointApi.types.d.ts +334 -41
  37. package/types/api/Event.d.ts +1 -0
  38. package/types/api/Form.d.ts +58 -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 +21 -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 +1222 -1005
@@ -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,59 @@ 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>>;
57
+ /**
58
+ * {@inheritDoc BaseEntity#answer}
59
+ *
60
+ * Crée une instance d'Answer **rattachée à ce Form**.
61
+ *
62
+ * - Si `answerData.id` est fourni → fetch l'Answer existante (via `COFORM_ANSWERS_BY_ID`).
63
+ * - Sinon → crée un draft d'Answer avec `form: this.id` pré-rempli, prêt à
64
+ * être complété puis sauvegardé.
65
+ *
66
+ * Le parent de l'Answer renvoyée est ce Form — la chaîne `org > form > answer`
67
+ * est donc préservée, ce qui permet (à terme) à l'Answer de remonter le
68
+ * costumContext via `this.parent.parent` si besoin.
69
+ *
70
+ * @example
71
+ * // Fetch d'une Answer existante
72
+ * const form = await org.form({ id: "6925e2b05dd63b02ca70d6d9" });
73
+ * const answer = await form.answer({ id: "6925869ad76aaf6c5a2b2f8a" });
74
+ *
75
+ * // Création d'un draft
76
+ * const draft = await form.answer();
77
+ * draft.data.answers = { "field1": "value1" };
78
+ * await draft.save();
79
+ */
80
+ answer(answerData?: Parameters<BaseEntity<FormItemNormalized>["answer"]>[0]): Promise<Answer>;
23
81
  }
@@ -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
  *
@@ -136,6 +149,14 @@ export declare class Project extends BaseEntity<ProjectItemNormalized> {
136
149
  * Crée une instance de badge et la récupère si nécessaire.
137
150
  */
138
151
  badge(badgeData?: Parameters<BaseEntity<ProjectItemNormalized>["badge"]>[0]): Promise<import("./Badge.js").Badge>;
152
+ /**
153
+ * {@inheritDoc BaseEntity#form}
154
+ *
155
+ * Crée une instance de Form dans le contexte de ce projet : le Form aura
156
+ * `this.parent = Project`, ce qui permet à `form.getAnswers()` de déléguer
157
+ * `coformAnswersSearch` avec le bon costumContext.
158
+ */
159
+ form(formData?: Parameters<BaseEntity<ProjectItemNormalized>["form"]>[0]): Promise<import("./Form.js").Form>;
139
160
  /**
140
161
  * {@inheritDoc BaseEntity#news}
141
162
  *
@@ -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 {};