@deveye/types 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.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/package.json +68 -0
- package/src/domain/audience.ts +549 -0
- package/src/domain/backup.ts +355 -0
- package/src/domain/credential.ts +55 -0
- package/src/domain/database.ts +467 -0
- package/src/domain/deploy.ts +231 -0
- package/src/domain/device.ts +172 -0
- package/src/domain/deviceFiles.ts +84 -0
- package/src/domain/deviceLogs.ts +82 -0
- package/src/domain/featureRegistry.ts +392 -0
- package/src/domain/finance.ts +477 -0
- package/src/domain/git.ts +419 -0
- package/src/domain/home.ts +314 -0
- package/src/domain/live.ts +272 -0
- package/src/domain/logs.ts +117 -0
- package/src/domain/mail.ts +394 -0
- package/src/domain/metrics.ts +127 -0
- package/src/domain/note.ts +202 -0
- package/src/domain/notifications.ts +268 -0
- package/src/domain/packages.ts +35 -0
- package/src/domain/password.ts +36 -0
- package/src/domain/presence.ts +21 -0
- package/src/domain/project.ts +168 -0
- package/src/domain/projectBoard.ts +130 -0
- package/src/domain/projectChat.ts +46 -0
- package/src/domain/projectHistory.ts +82 -0
- package/src/domain/projectLink.ts +87 -0
- package/src/domain/projectPlan.ts +68 -0
- package/src/domain/report.ts +492 -0
- package/src/domain/role.ts +8 -0
- package/src/domain/secrecy.ts +66 -0
- package/src/domain/sentinel.ts +623 -0
- package/src/domain/sharing.ts +186 -0
- package/src/domain/syncProtocol.ts +116 -0
- package/src/domain/twoFactor.ts +40 -0
- package/src/domain/uptime.ts +216 -0
- package/src/domain/user.ts +141 -0
- package/src/domain/workspace.ts +56 -0
- package/src/domain/workspaceRole.ts +251 -0
- package/src/features/admin.ts +112 -0
- package/src/features/audience.ts +275 -0
- package/src/features/backup.ts +230 -0
- package/src/features/database.ts +461 -0
- package/src/features/deploy.ts +245 -0
- package/src/features/device.ts +292 -0
- package/src/features/deviceFiles.ts +83 -0
- package/src/features/deviceLogs.ts +36 -0
- package/src/features/deviceTerminal.ts +57 -0
- package/src/features/finance.ts +360 -0
- package/src/features/git.ts +368 -0
- package/src/features/home.ts +32 -0
- package/src/features/live.ts +113 -0
- package/src/features/logs.ts +86 -0
- package/src/features/mail.ts +374 -0
- package/src/features/metrics.ts +185 -0
- package/src/features/note.ts +189 -0
- package/src/features/notify.ts +164 -0
- package/src/features/password.ts +67 -0
- package/src/features/project.ts +709 -0
- package/src/features/registry.ts +103 -0
- package/src/features/secrecy.ts +120 -0
- package/src/features/sentinel.ts +233 -0
- package/src/features/sharing.ts +79 -0
- package/src/features/twoFactor.ts +47 -0
- package/src/features/uptime.ts +186 -0
- package/src/features/user.ts +91 -0
- package/src/features/workspace.ts +200 -0
- package/src/http/auth.ts +94 -0
- package/src/http/device.ts +222 -0
- package/src/http/status.ts +45 -0
- package/src/index.ts +1700 -0
- package/src/protocol/agent.ts +1171 -0
- package/src/protocol/envelope.ts +46 -0
- package/src/protocol/error.ts +27 -0
- package/src/protocol/result.ts +17 -0
- package/src/protocol/version.ts +6 -0
- package/src/sdk/client-ambient.d.ts +238 -0
- package/src/sdk/client.ts +66 -0
- package/src/sdk/ids.ts +25 -0
- package/src/sdk/index.ts +11 -0
- package/src/sdk/manifest.ts +326 -0
- package/src/sdk/providers.ts +48 -0
- package/src/sdk/server.ts +378 -0
- package/src/sdk/testing.ts +179 -0
- package/src/utils/version.ts +28 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Projets : le suivi d'un travail, de ses premières phases à son déploiement.
|
|
5
|
+
*
|
|
6
|
+
* Découpage du stockage (voir `Docs/SECURITY_MODEL.md`). En **clair** tout ce
|
|
7
|
+
* dont le serveur a besoin pour lister, trier, compter et router sans rien
|
|
8
|
+
* déchiffrer — `workspace_id`, `status`, `security_tier`, `sort_order`, les
|
|
9
|
+
* dates, `archived_at`. **Chiffré** tout ce qui identifie : titre, description,
|
|
10
|
+
* étiquettes, version.
|
|
11
|
+
*
|
|
12
|
+
* Comme le mail, et contrairement à Uptime ou au coffre, l'étage de chiffrement
|
|
13
|
+
* n'est pas fixé par la feature mais **choisi par projet** (`securityTier`) :
|
|
14
|
+
* un projet `open` peut être synchronisé en tâche de fond (dépôt git,
|
|
15
|
+
* déploiement) ; un projet `guarded` ne se déchiffre que pendant une session
|
|
16
|
+
* vivante et déverrouillée, et perd donc ses intégrations automatiques.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const PROJECT_TITLE_MAX_LENGTH = 120;
|
|
20
|
+
export const PROJECT_DESCRIPTION_MAX_LENGTH = 4000;
|
|
21
|
+
export const PROJECT_VERSION_MAX_LENGTH = 40;
|
|
22
|
+
export const PROJECT_TAG_LABEL_MAX_LENGTH = 32;
|
|
23
|
+
export const PROJECT_MAX_TAGS = 24;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Borne de l'icône d'un projet, en caractères de son URL de données.
|
|
27
|
+
*
|
|
28
|
+
* ~400 ko : de quoi loger confortablement une vignette carrée redimensionnée
|
|
29
|
+
* par le client, sans laisser une charge utile WS grossir au gré de ce qu'on
|
|
30
|
+
* dépose. L'icône vit **dans le payload chiffré** comme le titre : elle
|
|
31
|
+
* identifie le projet autant qu'un nom, et un projet confidentiel ne doit pas
|
|
32
|
+
* la laisser lire.
|
|
33
|
+
*/
|
|
34
|
+
export const PROJECT_ICON_MAX_LENGTH = 400_000;
|
|
35
|
+
|
|
36
|
+
/** Vide = icône par défaut. Sinon, une URL de données d'image. */
|
|
37
|
+
export const projectIconSchema = z
|
|
38
|
+
.string()
|
|
39
|
+
.max(PROJECT_ICON_MAX_LENGTH)
|
|
40
|
+
.refine((v) => v === '' || /^data:image\/(png|jpeg|webp);base64,[A-Za-z0-9+/]+=*$/.test(v), {
|
|
41
|
+
message: 'L’icône doit être une image encodée en base64.'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/** Quel coffre chiffre l'arbre du projet. Voir l'en-tête de ce fichier. */
|
|
45
|
+
export const projectSecurityTierSchema = z.enum(['open', 'guarded']);
|
|
46
|
+
export type ProjectSecurityTier = z.infer<typeof projectSecurityTierSchema>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Où en est le projet. Volontairement court : c'est un état de pilotage, pas un
|
|
50
|
+
* workflow — le détail de l'avancement vit dans les colonnes du kanban.
|
|
51
|
+
*/
|
|
52
|
+
export const projectStatusSchema = z.enum(['draft', 'active', 'paused', 'done']);
|
|
53
|
+
export type ProjectStatus = z.infer<typeof projectStatusSchema>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* D'où vient le numéro de version affiché.
|
|
57
|
+
* - `manual` — saisi à la main dans le profil du projet ;
|
|
58
|
+
* - `github_release` — la dernière release publiée du dépôt lié. Le champ
|
|
59
|
+
* devient alors en lecture seule dans l'interface, et un projet `guarded` ne
|
|
60
|
+
* peut pas le choisir (sa synchronisation de fond est impossible).
|
|
61
|
+
*/
|
|
62
|
+
export const projectVersionSourceSchema = z.enum(['manual', 'github_release']);
|
|
63
|
+
export type ProjectVersionSource = z.infer<typeof projectVersionSourceSchema>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Les deux familles d'étiquettes, cumulables sur un même projet :
|
|
67
|
+
* - `type` — ce que le projet *est* (app mobile, site web, service…) ;
|
|
68
|
+
* - `tech` — ce avec quoi il est fait (react, react native, express, vite…).
|
|
69
|
+
*
|
|
70
|
+
* Le libellé est libre plutôt qu'énuméré : une pile technique se renouvelle plus
|
|
71
|
+
* vite qu'un schéma, et une valeur inconnue ne doit jamais faire disparaître un
|
|
72
|
+
* projet de la liste. Les étiquettes voyagent **dans le payload chiffré** — le
|
|
73
|
+
* filtrage se fait donc côté client, ce qui suffit largement à cette échelle.
|
|
74
|
+
*/
|
|
75
|
+
export const projectTagKindSchema = z.enum(['type', 'tech']);
|
|
76
|
+
export type ProjectTagKind = z.infer<typeof projectTagKindSchema>;
|
|
77
|
+
|
|
78
|
+
export const projectTagSchema = z.object({
|
|
79
|
+
kind: projectTagKindSchema,
|
|
80
|
+
label: z.string().min(1).max(PROJECT_TAG_LABEL_MAX_LENGTH)
|
|
81
|
+
});
|
|
82
|
+
export type ProjectTag = z.infer<typeof projectTagSchema>;
|
|
83
|
+
|
|
84
|
+
export const projectSchema = z.object({
|
|
85
|
+
id: z.number().int().positive(),
|
|
86
|
+
title: z.string().max(PROJECT_TITLE_MAX_LENGTH),
|
|
87
|
+
/** Vignette du projet ; vide = l'icône par défaut de la feature. */
|
|
88
|
+
icon: projectIconSchema,
|
|
89
|
+
description: z.string().max(PROJECT_DESCRIPTION_MAX_LENGTH),
|
|
90
|
+
tags: z.array(projectTagSchema).max(PROJECT_MAX_TAGS),
|
|
91
|
+
version: z.string().max(PROJECT_VERSION_MAX_LENGTH),
|
|
92
|
+
versionSource: projectVersionSourceSchema,
|
|
93
|
+
status: projectStatusSchema,
|
|
94
|
+
securityTier: projectSecurityTierSchema,
|
|
95
|
+
/** Bornes de la fenêtre du projet, en secondes unix. Facultatives. */
|
|
96
|
+
startDate: z.number().int().nullable(),
|
|
97
|
+
dueDate: z.number().int().nullable(),
|
|
98
|
+
sortOrder: z.number().int().nonnegative(),
|
|
99
|
+
/**
|
|
100
|
+
* Qui l'a créé : attribution, jamais une frontière d'accès. `null` quand le
|
|
101
|
+
* compte a été supprimé depuis — un départ n'emporte pas le travail d'un
|
|
102
|
+
* espace partagé (cf. `ON DELETE SET NULL` dans la migration).
|
|
103
|
+
*/
|
|
104
|
+
authorUserId: z.number().int().positive().nullable(),
|
|
105
|
+
archived: z.boolean(),
|
|
106
|
+
created: z.number().int(),
|
|
107
|
+
updated: z.number().int()
|
|
108
|
+
});
|
|
109
|
+
export type Project = z.infer<typeof projectSchema>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* La ligne du portefeuille : le projet, plus ce que le serveur sait compter
|
|
113
|
+
* **sans déchiffrer** (colonnes en clair uniquement).
|
|
114
|
+
*
|
|
115
|
+
* `masked: true` désigne un projet `guarded` dont le corps n'a pas pu être
|
|
116
|
+
* déchiffré parce que la session est verrouillée. La ligne reste listée, avec
|
|
117
|
+
* ses compteurs — on doit pouvoir voir qu'un projet existe, et le déverrouiller
|
|
118
|
+
* en connaissance de cause, sans que la liste entière disparaisse. Même parti
|
|
119
|
+
* pris que les notes privées.
|
|
120
|
+
*/
|
|
121
|
+
export const projectSummarySchema = z.object({
|
|
122
|
+
project: projectSchema,
|
|
123
|
+
masked: z.boolean(),
|
|
124
|
+
/** Cartes actives (non archivées) et celles assises dans une colonne de fin. */
|
|
125
|
+
cardTotal: z.number().int().nonnegative(),
|
|
126
|
+
cardDone: z.number().int().nonnegative(),
|
|
127
|
+
/** Cartes actives dont l'échéance est dépassée. */
|
|
128
|
+
cardOverdue: z.number().int().nonnegative(),
|
|
129
|
+
/** Prochaine échéance à venir, toutes cartes confondues. */
|
|
130
|
+
nextDueDate: z.number().int().nullable(),
|
|
131
|
+
/** Messages non lus par l'appelant sur tout le projet. */
|
|
132
|
+
unread: z.number().int().nonnegative()
|
|
133
|
+
});
|
|
134
|
+
export type ProjectSummary = z.infer<typeof projectSummarySchema>;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Ce que le client peut poser sur un projet. La version n'y est pas quand elle
|
|
138
|
+
* est pilotée par les releases : `project.setVersion` la traite à part, pour que
|
|
139
|
+
* l'édition du profil ne puisse pas écraser en silence une valeur synchronisée.
|
|
140
|
+
*/
|
|
141
|
+
export const projectDraftSchema = z.object({
|
|
142
|
+
title: z.string().min(1).max(PROJECT_TITLE_MAX_LENGTH),
|
|
143
|
+
icon: projectIconSchema,
|
|
144
|
+
description: z.string().max(PROJECT_DESCRIPTION_MAX_LENGTH),
|
|
145
|
+
tags: z.array(projectTagSchema).max(PROJECT_MAX_TAGS),
|
|
146
|
+
status: projectStatusSchema,
|
|
147
|
+
startDate: z.number().int().nullable(),
|
|
148
|
+
dueDate: z.number().int().nullable()
|
|
149
|
+
});
|
|
150
|
+
export type ProjectDraft = z.infer<typeof projectDraftSchema>;
|
|
151
|
+
|
|
152
|
+
/** Ligne SQL (serveur uniquement). `content` porte le payload chiffré. */
|
|
153
|
+
export interface ProjectRow {
|
|
154
|
+
id: number;
|
|
155
|
+
workspace_id: number;
|
|
156
|
+
/** Auteur. En espace partagé il dit qui a créé la ligne, rien de plus. */
|
|
157
|
+
user_id: number | null;
|
|
158
|
+
status: ProjectStatus;
|
|
159
|
+
security_tier: ProjectSecurityTier;
|
|
160
|
+
version_source: ProjectVersionSource;
|
|
161
|
+
sort_order: number;
|
|
162
|
+
start_date: number | null;
|
|
163
|
+
due_date: number | null;
|
|
164
|
+
archived_at: number | null;
|
|
165
|
+
content: string;
|
|
166
|
+
created: number;
|
|
167
|
+
updated: number;
|
|
168
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Le tableau d'un projet : ses colonnes, et les « blocs » qui y circulent.
|
|
5
|
+
*
|
|
6
|
+
* Découpage clair / chiffré, comme partout dans ce module. En **clair** tout ce
|
|
7
|
+
* qui sert à ordonner, filtrer et compter sans clé — `column_id`, `sort_order`,
|
|
8
|
+
* `assignee_user_id`, `priority`, les dates, `archived_at`, `message_count`.
|
|
9
|
+
* **Chiffré** le titre, la description et la liste de sous-tâches.
|
|
10
|
+
*
|
|
11
|
+
* `assignee_user_id` reste en clair à dessein : c'est ce qui rend « toutes mes
|
|
12
|
+
* tâches, tous projets confondus » possible en une requête, et le serveur
|
|
13
|
+
* connaît déjà l'appartenance à l'espace — il n'apprend rien de neuf.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const PROJECT_COLUMN_NAME_MAX_LENGTH = 48;
|
|
17
|
+
export const PROJECT_MAX_COLUMNS = 12;
|
|
18
|
+
export const PROJECT_CARD_TITLE_MAX_LENGTH = 160;
|
|
19
|
+
export const PROJECT_CARD_DESCRIPTION_MAX_LENGTH = 4000;
|
|
20
|
+
export const PROJECT_CHECKLIST_LABEL_MAX_LENGTH = 160;
|
|
21
|
+
export const PROJECT_MAX_CHECKLIST_ITEMS = 50;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Priorité d'une carte.
|
|
25
|
+
*
|
|
26
|
+
* **L'indice dans ce tableau est la valeur stockée** (`project_cards.priority`,
|
|
27
|
+
* un TINYINT) : `none` = 0 … `high` = 3. Un enum de chaînes côté fil pour que le
|
|
28
|
+
* code soit lisible, un entier en base pour que le tri reste du SQL. Ne jamais
|
|
29
|
+
* réordonner ces valeurs — ce serait réétiqueter toutes les cartes existantes.
|
|
30
|
+
*/
|
|
31
|
+
export const projectPrioritySchema = z.enum(['none', 'low', 'normal', 'high']);
|
|
32
|
+
export type ProjectPriority = z.infer<typeof projectPrioritySchema>;
|
|
33
|
+
export const PROJECT_PRIORITIES = projectPrioritySchema.options;
|
|
34
|
+
|
|
35
|
+
/** Une sous-tâche. `id` est engendré par le client : c'est sa clé de rendu. */
|
|
36
|
+
export const projectChecklistItemSchema = z.object({
|
|
37
|
+
id: z.string().min(1).max(64),
|
|
38
|
+
label: z.string().max(PROJECT_CHECKLIST_LABEL_MAX_LENGTH),
|
|
39
|
+
done: z.boolean()
|
|
40
|
+
});
|
|
41
|
+
export type ProjectChecklistItem = z.infer<typeof projectChecklistItemSchema>;
|
|
42
|
+
|
|
43
|
+
export const projectColumnSchema = z.object({
|
|
44
|
+
id: z.number().int().positive(),
|
|
45
|
+
projectId: z.number().int().positive(),
|
|
46
|
+
name: z.string().max(PROJECT_COLUMN_NAME_MAX_LENGTH),
|
|
47
|
+
sortOrder: z.number().int().nonnegative(),
|
|
48
|
+
/**
|
|
49
|
+
* Cette colonne vaut « terminé ». En clair, et c'est ce qui rend
|
|
50
|
+
* l'avancement d'un projet calculable en SQL sans déchiffrer son nom.
|
|
51
|
+
*/
|
|
52
|
+
countsAsDone: z.boolean(),
|
|
53
|
+
/** Limite de travail en cours ; `null` = aucune. Indicative, jamais bloquante. */
|
|
54
|
+
wipLimit: z.number().int().positive().nullable()
|
|
55
|
+
});
|
|
56
|
+
export type ProjectColumn = z.infer<typeof projectColumnSchema>;
|
|
57
|
+
|
|
58
|
+
export const projectCardSchema = z.object({
|
|
59
|
+
id: z.number().int().positive(),
|
|
60
|
+
projectId: z.number().int().positive(),
|
|
61
|
+
columnId: z.number().int().positive(),
|
|
62
|
+
title: z.string().max(PROJECT_CARD_TITLE_MAX_LENGTH),
|
|
63
|
+
description: z.string().max(PROJECT_CARD_DESCRIPTION_MAX_LENGTH),
|
|
64
|
+
checklist: z.array(projectChecklistItemSchema).max(PROJECT_MAX_CHECKLIST_ITEMS),
|
|
65
|
+
sortOrder: z.number().int().nonnegative(),
|
|
66
|
+
priority: projectPrioritySchema,
|
|
67
|
+
/** `null` quand le compte a été supprimé depuis (voir la migration). */
|
|
68
|
+
authorUserId: z.number().int().positive().nullable(),
|
|
69
|
+
/** Membre de l'espace à qui la carte est attribuée ; `null` = personne. */
|
|
70
|
+
assigneeUserId: z.number().int().positive().nullable(),
|
|
71
|
+
startDate: z.number().int().nullable(),
|
|
72
|
+
dueDate: z.number().int().nullable(),
|
|
73
|
+
estimateMinutes: z.number().int().nonnegative().nullable(),
|
|
74
|
+
milestoneId: z.number().int().positive().nullable(),
|
|
75
|
+
archived: z.boolean(),
|
|
76
|
+
archivedAt: z.number().int().nullable(),
|
|
77
|
+
/** Total des messages du fil, et ce que l'appelant n'a pas encore lu. */
|
|
78
|
+
messageCount: z.number().int().nonnegative(),
|
|
79
|
+
unread: z.number().int().nonnegative(),
|
|
80
|
+
created: z.number().int(),
|
|
81
|
+
updated: z.number().int()
|
|
82
|
+
});
|
|
83
|
+
export type ProjectCard = z.infer<typeof projectCardSchema>;
|
|
84
|
+
|
|
85
|
+
/** Ce que le client peut poser sur une carte. */
|
|
86
|
+
export const projectCardDraftSchema = z.object({
|
|
87
|
+
title: z.string().min(1).max(PROJECT_CARD_TITLE_MAX_LENGTH),
|
|
88
|
+
description: z.string().max(PROJECT_CARD_DESCRIPTION_MAX_LENGTH),
|
|
89
|
+
checklist: z.array(projectChecklistItemSchema).max(PROJECT_MAX_CHECKLIST_ITEMS),
|
|
90
|
+
priority: projectPrioritySchema,
|
|
91
|
+
assigneeUserId: z.number().int().positive().nullable(),
|
|
92
|
+
startDate: z.number().int().nullable(),
|
|
93
|
+
dueDate: z.number().int().nullable(),
|
|
94
|
+
estimateMinutes: z.number().int().nonnegative().nullable()
|
|
95
|
+
});
|
|
96
|
+
export type ProjectCardDraft = z.infer<typeof projectCardDraftSchema>;
|
|
97
|
+
|
|
98
|
+
/** Ligne SQL (serveur uniquement). */
|
|
99
|
+
export interface ProjectColumnRow {
|
|
100
|
+
id: number;
|
|
101
|
+
project_id: number;
|
|
102
|
+
workspace_id: number;
|
|
103
|
+
sort_order: number;
|
|
104
|
+
counts_as_done: number;
|
|
105
|
+
wip_limit: number | null;
|
|
106
|
+
content: string;
|
|
107
|
+
created: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Ligne SQL (serveur uniquement). */
|
|
111
|
+
export interface ProjectCardRow {
|
|
112
|
+
id: number;
|
|
113
|
+
project_id: number;
|
|
114
|
+
workspace_id: number;
|
|
115
|
+
column_id: number;
|
|
116
|
+
sort_order: number;
|
|
117
|
+
author_user_id: number | null;
|
|
118
|
+
assignee_user_id: number | null;
|
|
119
|
+
priority: number;
|
|
120
|
+
start_date: number | null;
|
|
121
|
+
due_date: number | null;
|
|
122
|
+
estimate_minutes: number | null;
|
|
123
|
+
milestone_id: number | null;
|
|
124
|
+
archived_at: number | null;
|
|
125
|
+
message_count: number;
|
|
126
|
+
last_message_at: number | null;
|
|
127
|
+
content: string;
|
|
128
|
+
created: number;
|
|
129
|
+
updated: number;
|
|
130
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Le fil de discussion d'une carte.
|
|
5
|
+
*
|
|
6
|
+
* Découpage clair / chiffré, comme le reste du module : le **texte** est
|
|
7
|
+
* chiffré sous l'étage du projet ; l'auteur, l'horodatage et les mentions
|
|
8
|
+
* restent en colonnes claires.
|
|
9
|
+
*
|
|
10
|
+
* Les mentions en clair, c'est délibéré : ce sont des identifiants de membres
|
|
11
|
+
* de l'espace, que le serveur connaît déjà par `workspace_members`. Les garder
|
|
12
|
+
* lisibles est ce qui permet de compter « mes mentions non lues » sans
|
|
13
|
+
* déchiffrer un seul message.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const PROJECT_MESSAGE_MAX_LENGTH = 4000;
|
|
17
|
+
|
|
18
|
+
/** Combien de messages une page de fil rend au plus. */
|
|
19
|
+
export const PROJECT_MESSAGE_PAGE_SIZE = 60;
|
|
20
|
+
|
|
21
|
+
export const projectMessageSchema = z.object({
|
|
22
|
+
id: z.number().int().positive(),
|
|
23
|
+
cardId: z.number().int().positive(),
|
|
24
|
+
/** `null` quand le compte a été supprimé depuis : le message, lui, reste. */
|
|
25
|
+
authorUserId: z.number().int().positive().nullable(),
|
|
26
|
+
text: z.string().max(PROJECT_MESSAGE_MAX_LENGTH),
|
|
27
|
+
mentions: z.array(z.number().int().positive()),
|
|
28
|
+
created: z.number().int(),
|
|
29
|
+
/** Horodatage de la dernière retouche, ou `null` si jamais modifié. */
|
|
30
|
+
edited: z.number().int().nullable()
|
|
31
|
+
});
|
|
32
|
+
export type ProjectMessage = z.infer<typeof projectMessageSchema>;
|
|
33
|
+
|
|
34
|
+
/** Ligne SQL (serveur uniquement). */
|
|
35
|
+
export interface ProjectMessageRow {
|
|
36
|
+
id: number;
|
|
37
|
+
card_id: number;
|
|
38
|
+
project_id: number;
|
|
39
|
+
workspace_id: number;
|
|
40
|
+
author_user_id: number | null;
|
|
41
|
+
/** Tableau JSON d'identifiants de membres, ou `null`. */
|
|
42
|
+
mentions: string | null;
|
|
43
|
+
created: number;
|
|
44
|
+
edited: number | null;
|
|
45
|
+
content: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* L'historique d'un projet : la frise verticale de tout ce qui lui est arrivé.
|
|
5
|
+
*
|
|
6
|
+
* C'est le pendant du principe « rien ne se supprime, tout s'archive ». Un bloc
|
|
7
|
+
* archivé quitte l'espace de travail mais reste ici, consultable en lecture
|
|
8
|
+
* seule ; un renommage, un changement de version ou de statut y laisse aussi sa
|
|
9
|
+
* trace. L'historique est donc la mémoire du projet, pas un journal technique —
|
|
10
|
+
* ce dernier existe déjà, c'est l'audit (`ctx.audit`, page Journaux).
|
|
11
|
+
*
|
|
12
|
+
* Découpage clair / chiffré : le **type**, l'auteur, l'horodatage et la
|
|
13
|
+
* référence à l'objet concerné sont en clair (ils servent à trier et à router
|
|
14
|
+
* sans clé) ; le libellé et le avant/après sont chiffrés — ce sont eux qui
|
|
15
|
+
* portent le contenu.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export const PROJECT_EVENT_LABEL_MAX_LENGTH = 200;
|
|
19
|
+
export const PROJECT_EVENT_PAGE_SIZE = 50;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ce qui mérite d'entrer dans l'histoire d'un projet.
|
|
23
|
+
*
|
|
24
|
+
* Volontairement court : une frise qui consigne tout ne se lit plus. Y figurent
|
|
25
|
+
* les changements qu'on cherche des mois plus tard — « quand a-t-on archivé
|
|
26
|
+
* ça ? », « depuis quand est-on en v2 ? » — et rien du va-et-vient quotidien
|
|
27
|
+
* des cartes entre colonnes.
|
|
28
|
+
*/
|
|
29
|
+
export const projectEventKindSchema = z.enum([
|
|
30
|
+
'project.created',
|
|
31
|
+
'project.renamed',
|
|
32
|
+
'project.version',
|
|
33
|
+
'project.status',
|
|
34
|
+
'project.securityTier',
|
|
35
|
+
'project.archived',
|
|
36
|
+
'project.restored',
|
|
37
|
+
'card.archived',
|
|
38
|
+
'card.restored',
|
|
39
|
+
'milestone.reached',
|
|
40
|
+
'deploy.triggered',
|
|
41
|
+
'deploy.succeeded',
|
|
42
|
+
'deploy.failed'
|
|
43
|
+
]);
|
|
44
|
+
export type ProjectEventKind = z.infer<typeof projectEventKindSchema>;
|
|
45
|
+
|
|
46
|
+
/** Ce que la référence désigne, pour que la frise sache quoi ouvrir. */
|
|
47
|
+
export const projectEventRefSchema = z.enum(['card', 'milestone']);
|
|
48
|
+
export type ProjectEventRef = z.infer<typeof projectEventRefSchema>;
|
|
49
|
+
|
|
50
|
+
export const projectEventSchema = z.object({
|
|
51
|
+
id: z.number().int().positive(),
|
|
52
|
+
projectId: z.number().int().positive(),
|
|
53
|
+
/** `null` = une tâche de fond, ou un compte supprimé depuis. */
|
|
54
|
+
actorUserId: z.number().int().positive().nullable(),
|
|
55
|
+
/**
|
|
56
|
+
* Un type inconnu ne fait pas disparaître la ligne : un client plus ancien
|
|
57
|
+
* qu'un serveur doit encore pouvoir lire la frise, quitte à afficher une
|
|
58
|
+
* entrée générique. Même parti pris que `shortcutTemplateSchema`.
|
|
59
|
+
*/
|
|
60
|
+
kind: projectEventKindSchema.catch('project.status'),
|
|
61
|
+
refType: projectEventRefSchema.nullable(),
|
|
62
|
+
refId: z.number().int().positive().nullable(),
|
|
63
|
+
label: z.string().max(PROJECT_EVENT_LABEL_MAX_LENGTH),
|
|
64
|
+
/** Avant / après, quand le changement en a un (renommage, version, statut). */
|
|
65
|
+
from: z.string().max(PROJECT_EVENT_LABEL_MAX_LENGTH).nullable(),
|
|
66
|
+
to: z.string().max(PROJECT_EVENT_LABEL_MAX_LENGTH).nullable(),
|
|
67
|
+
created: z.number().int()
|
|
68
|
+
});
|
|
69
|
+
export type ProjectEvent = z.infer<typeof projectEventSchema>;
|
|
70
|
+
|
|
71
|
+
/** Ligne SQL (serveur uniquement). */
|
|
72
|
+
export interface ProjectEventRow {
|
|
73
|
+
id: number;
|
|
74
|
+
project_id: number;
|
|
75
|
+
workspace_id: number;
|
|
76
|
+
actor_user_id: number | null;
|
|
77
|
+
kind: string;
|
|
78
|
+
ref_type: string | null;
|
|
79
|
+
ref_id: number | null;
|
|
80
|
+
created: number;
|
|
81
|
+
content: string;
|
|
82
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { projectCardSchema } from './projectBoard';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ce qui relie un projet au reste de DevEye, et ce qui relie une personne à ses
|
|
6
|
+
* tâches à travers tous les projets.
|
|
7
|
+
*
|
|
8
|
+
* Deux notions transverses réunies ici parce qu'elles répondent à la même
|
|
9
|
+
* question posée dans les deux sens : « qu'est-ce qui touche ce projet ? » et
|
|
10
|
+
* « qu'est-ce qui me touche, moi, dans tous les projets ? ».
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ce qui relie un projet à un service surveillé.
|
|
15
|
+
*
|
|
16
|
+
* On ne stocke que l'identifiant : la cible garde ses propres droits, et rien
|
|
17
|
+
* d'identifiant n'a besoin d'être chiffré. Un membre sans accès à Uptime voit
|
|
18
|
+
* qu'il y a des services rattachés sans pouvoir les nommer — c'est la feature
|
|
19
|
+
* visée qui tranche, pas celle-ci.
|
|
20
|
+
*
|
|
21
|
+
* La liaison est **non exclusive dans les deux sens** : un projet suit plusieurs
|
|
22
|
+
* services, et un même service peut être suivi par plusieurs projets. Même forme
|
|
23
|
+
* que la liaison au dépôt git, et pour la même raison — ce sont deux objets
|
|
24
|
+
* d'espace, pas des propriétés d'un projet.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Une tâche qui m'est attribuée, vue depuis l'extérieur de son projet.
|
|
29
|
+
*
|
|
30
|
+
* `masked` marque une carte d'un projet confidentiel verrouillé : elle est
|
|
31
|
+
* comptée et située, mais son titre reste illisible. La faire disparaître
|
|
32
|
+
* donnerait une liste de tâches fausse, ce qui est pire que de dire « il y a
|
|
33
|
+
* quelque chose ici que tu ne peux pas lire maintenant ».
|
|
34
|
+
*/
|
|
35
|
+
export const myTaskSchema = z.object({
|
|
36
|
+
card: projectCardSchema,
|
|
37
|
+
projectId: z.number().int().positive(),
|
|
38
|
+
projectTitle: z.string(),
|
|
39
|
+
masked: z.boolean()
|
|
40
|
+
});
|
|
41
|
+
export type MyTask = z.infer<typeof myTaskSchema>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Combien d'éléments chaque intégration d'un projet a à montrer.
|
|
45
|
+
*
|
|
46
|
+
* Des **compteurs seulement** : ils décident si l'écran a quelque chose à
|
|
47
|
+
* ouvrir, pas ce qu'il montrera. Un projet neuf n'a ni dépôt, ni base, ni site,
|
|
48
|
+
* ni déploiement — lui présenter quatre onglets vides revient à lui demander de
|
|
49
|
+
* fouiller pour trouver le vide. Zéro fait donc disparaître l'onglet, et le
|
|
50
|
+
* geste d'ajout se replie dans un menu unique.
|
|
51
|
+
*
|
|
52
|
+
* Les clés portent le nom de la **feature d'espace** pointée (`git`,
|
|
53
|
+
* `database`, `audience`), pas celui de l'onglet qui les affiche : ce sont les
|
|
54
|
+
* mêmes identifiants que les droits (`workspaceFeatureIdSchema`), et c'est ce
|
|
55
|
+
* qui permet à l'appelant de demander « ai-je le droit d'y ajouter ? » sans
|
|
56
|
+
* table de correspondance.
|
|
57
|
+
*
|
|
58
|
+
* Un compte ne dit rien de ce que l'appelant a le droit de **lire** : les
|
|
59
|
+
* liaisons relèvent de `projects`, leur contenu de la feature visée. Un membre
|
|
60
|
+
* sans accès à Git voit donc l'onglet Git et, dedans, la phrase qui explique ce
|
|
61
|
+
* qui lui manque — plutôt qu'un onglet escamoté qui lui cacherait l'existence
|
|
62
|
+
* même du lien.
|
|
63
|
+
*/
|
|
64
|
+
export const projectLinkCountsSchema = z.object({
|
|
65
|
+
/** Dépôts reliés. */
|
|
66
|
+
git: z.number().int().nonnegative(),
|
|
67
|
+
/** Bases de données reliées. */
|
|
68
|
+
database: z.number().int().nonnegative(),
|
|
69
|
+
/** Sites suivis reliés. */
|
|
70
|
+
audience: z.number().int().nonnegative(),
|
|
71
|
+
/**
|
|
72
|
+
* La cible de déploiement (0 ou 1) **plus** les services surveillés
|
|
73
|
+
* rattachés. Les deux vivent dans le même onglet : compter la seule cible
|
|
74
|
+
* ferait disparaître un onglet qui a encore de quoi répondre à « est-ce en
|
|
75
|
+
* ligne ? ».
|
|
76
|
+
*/
|
|
77
|
+
deploy: z.number().int().nonnegative()
|
|
78
|
+
});
|
|
79
|
+
export type ProjectLinkCounts = z.infer<typeof projectLinkCountsSchema>;
|
|
80
|
+
|
|
81
|
+
/** Ligne SQL (serveur uniquement). */
|
|
82
|
+
export interface ProjectUptimeLinkRow {
|
|
83
|
+
project_id: number;
|
|
84
|
+
service_id: number;
|
|
85
|
+
workspace_id: number;
|
|
86
|
+
created: number;
|
|
87
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* La planification d'un projet : ses jalons, et les dépendances entre cartes.
|
|
5
|
+
*
|
|
6
|
+
* Ce sont les deux choses que le kanban ne sait pas dire. Un tableau montre
|
|
7
|
+
* *où en est* chaque carte ; une frise montre *quand*, et ce qui attend quoi.
|
|
8
|
+
*
|
|
9
|
+
* Découpage clair / chiffré habituel : les dates, l'atteinte et le graphe des
|
|
10
|
+
* dépendances sont en colonnes claires — c'est ce qui permet de dessiner la
|
|
11
|
+
* frise et de détecter un cycle sans déchiffrer quoi que ce soit. Seuls le nom
|
|
12
|
+
* et la description d'un jalon sont chiffrés.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const PROJECT_MILESTONE_NAME_MAX_LENGTH = 80;
|
|
16
|
+
export const PROJECT_MILESTONE_DESCRIPTION_MAX_LENGTH = 1000;
|
|
17
|
+
|
|
18
|
+
export const projectMilestoneSchema = z.object({
|
|
19
|
+
id: z.number().int().positive(),
|
|
20
|
+
projectId: z.number().int().positive(),
|
|
21
|
+
name: z.string().max(PROJECT_MILESTONE_NAME_MAX_LENGTH),
|
|
22
|
+
description: z.string().max(PROJECT_MILESTONE_DESCRIPTION_MAX_LENGTH),
|
|
23
|
+
/** Échéance visée, en secondes unix. Un jalon est toujours daté. */
|
|
24
|
+
dueDate: z.number().int(),
|
|
25
|
+
/** Horodatage d'atteinte, ou `null` s'il est encore devant nous. */
|
|
26
|
+
reachedAt: z.number().int().nullable(),
|
|
27
|
+
sortOrder: z.number().int().nonnegative()
|
|
28
|
+
});
|
|
29
|
+
export type ProjectMilestone = z.infer<typeof projectMilestoneSchema>;
|
|
30
|
+
|
|
31
|
+
export const projectMilestoneDraftSchema = z.object({
|
|
32
|
+
name: z.string().min(1).max(PROJECT_MILESTONE_NAME_MAX_LENGTH),
|
|
33
|
+
description: z.string().max(PROJECT_MILESTONE_DESCRIPTION_MAX_LENGTH),
|
|
34
|
+
dueDate: z.number().int()
|
|
35
|
+
});
|
|
36
|
+
export type ProjectMilestoneDraft = z.infer<typeof projectMilestoneDraftSchema>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Une dépendance : `cardId` est bloquée par `blockedByCardId`.
|
|
40
|
+
*
|
|
41
|
+
* Le sens est fixé une fois pour toutes — « bloquée par » et non « bloque » —
|
|
42
|
+
* pour qu'il n'y ait jamais à se demander dans quel sens lire une arête.
|
|
43
|
+
*/
|
|
44
|
+
export const projectCardDepSchema = z.object({
|
|
45
|
+
cardId: z.number().int().positive(),
|
|
46
|
+
blockedByCardId: z.number().int().positive()
|
|
47
|
+
});
|
|
48
|
+
export type ProjectCardDep = z.infer<typeof projectCardDepSchema>;
|
|
49
|
+
|
|
50
|
+
/** Ligne SQL (serveur uniquement). */
|
|
51
|
+
export interface ProjectMilestoneRow {
|
|
52
|
+
id: number;
|
|
53
|
+
project_id: number;
|
|
54
|
+
workspace_id: number;
|
|
55
|
+
due_date: number;
|
|
56
|
+
reached_at: number | null;
|
|
57
|
+
sort_order: number;
|
|
58
|
+
content: string;
|
|
59
|
+
created: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Ligne SQL (serveur uniquement). */
|
|
63
|
+
export interface ProjectCardDepRow {
|
|
64
|
+
card_id: number;
|
|
65
|
+
blocked_by_card_id: number;
|
|
66
|
+
project_id: number;
|
|
67
|
+
created: number;
|
|
68
|
+
}
|