@deveye/types 0.15.2 → 0.16.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 (73) hide show
  1. package/package.json +6 -6
  2. package/src/domain/device.ts +4 -29
  3. package/src/domain/featureRegistry.ts +40 -108
  4. package/src/domain/home.ts +40 -101
  5. package/src/domain/live.ts +36 -87
  6. package/src/domain/metrics.ts +10 -14
  7. package/src/domain/notifications.ts +39 -110
  8. package/src/domain/project.ts +3 -162
  9. package/src/domain/report.ts +46 -95
  10. package/src/domain/secrecy.ts +3 -5
  11. package/src/domain/sharing.ts +33 -70
  12. package/src/domain/syncProtocol.ts +5 -12
  13. package/src/domain/user.ts +8 -14
  14. package/src/domain/workspace.ts +0 -3
  15. package/src/domain/workspaceRole.ts +34 -82
  16. package/src/features/agent.ts +306 -0
  17. package/src/features/live.ts +17 -37
  18. package/src/features/notify.ts +19 -57
  19. package/src/features/registry.ts +7 -44
  20. package/src/features/secrecy.ts +4 -9
  21. package/src/features/sharing.ts +12 -26
  22. package/src/features/user.ts +6 -11
  23. package/src/features/workspace.ts +6 -11
  24. package/src/http/auth.ts +6 -13
  25. package/src/http/device.ts +13 -17
  26. package/src/http/status.ts +6 -10
  27. package/src/index.ts +29 -932
  28. package/src/protocol/agent.ts +40 -70
  29. package/src/protocol/envelope.ts +3 -10
  30. package/src/sdk/client-ambient.d.ts +228 -21
  31. package/src/sdk/client.ts +277 -17
  32. package/src/sdk/devb.ts +120 -0
  33. package/src/sdk/manifest.test.ts +0 -1
  34. package/src/sdk/manifest.ts +94 -25
  35. package/src/sdk/providers.ts +310 -5
  36. package/src/sdk/server.ts +483 -19
  37. package/src/sdk/testing.test.ts +2 -2
  38. package/src/sdk/testing.ts +302 -26
  39. package/src/utils/version.ts +5 -8
  40. package/src/domain/audience.ts +0 -549
  41. package/src/domain/backup.ts +0 -355
  42. package/src/domain/credential.ts +0 -55
  43. package/src/domain/database.ts +0 -467
  44. package/src/domain/deploy.ts +0 -231
  45. package/src/domain/finance.ts +0 -477
  46. package/src/domain/git.ts +0 -419
  47. package/src/domain/mail.ts +0 -394
  48. package/src/domain/note.ts +0 -202
  49. package/src/domain/password.ts +0 -36
  50. package/src/domain/projectBoard.ts +0 -130
  51. package/src/domain/projectChat.ts +0 -46
  52. package/src/domain/projectHistory.ts +0 -82
  53. package/src/domain/projectLink.ts +0 -87
  54. package/src/domain/projectPlan.ts +0 -68
  55. package/src/domain/sentinel.ts +0 -623
  56. package/src/domain/uptime.ts +0 -216
  57. package/src/features/audience.ts +0 -275
  58. package/src/features/backup.ts +0 -230
  59. package/src/features/database.ts +0 -461
  60. package/src/features/deploy.ts +0 -245
  61. package/src/features/device.ts +0 -292
  62. package/src/features/deviceFiles.ts +0 -83
  63. package/src/features/deviceLogs.ts +0 -36
  64. package/src/features/deviceTerminal.ts +0 -57
  65. package/src/features/finance.ts +0 -360
  66. package/src/features/git.ts +0 -368
  67. package/src/features/mail.ts +0 -374
  68. package/src/features/metrics.ts +0 -185
  69. package/src/features/note.ts +0 -189
  70. package/src/features/password.ts +0 -67
  71. package/src/features/project.ts +0 -709
  72. package/src/features/sentinel.ts +0 -233
  73. package/src/features/uptime.ts +0 -186
@@ -1,230 +0,0 @@
1
- import { z } from 'zod';
2
- import {
3
- BACKUP_ACCESS_KEY_MAX,
4
- BACKUP_BUCKET_MAX,
5
- BACKUP_DESTINATION_NAME_MAX,
6
- BACKUP_ENDPOINT_MAX,
7
- BACKUP_JOB_NAME_MAX,
8
- BACKUP_PATH_MAX,
9
- BACKUP_SECRET_MAX,
10
- backupDestinationKindSchema,
11
- backupDestinationProbeSchema,
12
- backupDestinationSchema,
13
- backupEncryptionSchema,
14
- backupJobSchema,
15
- backupRunSchema,
16
- backupScheduleKindSchema,
17
- backupSourceCandidateSchema,
18
- backupSourceKindSchema
19
- } from '../domain/backup';
20
-
21
- /**
22
- * Commandes des sauvegardes.
23
- *
24
- * Deux moitiés qui ne se recouvrent pas: les **destinations** (où) et les
25
- * **travaux** (quoi, quand, combien de copies). Tout est à l'étage ouvert, donc
26
- * aucune de ces commandes ne demande de session déverrouillée — c'est la
27
- * condition pour qu'une sauvegarde parte à 3 h du matin.
28
- *
29
- * `backup.deveyeDump` n'existe pas comme commande: la base de DevEye se
30
- * sauvegarde par un travail de source `deveye` comme les autres, et il n'y a
31
- * aucune raison de lui inventer un chemin à part.
32
- */
33
-
34
- const destinationId = z.number().int().positive();
35
- const jobId = z.number().int().positive();
36
-
37
- // ---------------------------------------------------------- destinations
38
-
39
- /** Les destinations de l'espace, dans l'ordre où elles ont été déclarées. */
40
- export const backupDestinationList = {
41
- command: 'backup.destinationList' as const,
42
- input: z.object({}),
43
- output: z.object({ destinations: z.array(backupDestinationSchema) })
44
- };
45
-
46
- /**
47
- * Déclare une destination.
48
- *
49
- * Un seul schéma pour les trois genres, avec les champs des deux autres à
50
- * `null`: la forme alternative (trois commandes, ou une union discriminée)
51
- * obligerait l'écran à trois formulaires là où il n'en a qu'un, dont les champs
52
- * apparaissent selon le genre choisi.
53
- */
54
- export const backupDestinationAdd = {
55
- command: 'backup.destinationAdd' as const,
56
- input: z.object({
57
- kind: backupDestinationKindSchema,
58
- name: z.string().min(1).max(BACKUP_DESTINATION_NAME_MAX),
59
- deviceId: z.uuid().nullable(),
60
- path: z.string().max(BACKUP_PATH_MAX),
61
- endpoint: z.string().max(BACKUP_ENDPOINT_MAX).nullable(),
62
- region: z.string().max(64).nullable(),
63
- bucket: z.string().max(BACKUP_BUCKET_MAX).nullable(),
64
- accessKeyId: z.string().max(BACKUP_ACCESS_KEY_MAX).nullable(),
65
- secret: z.string().max(BACKUP_SECRET_MAX).nullable(),
66
- pathStyle: z.boolean()
67
- }),
68
- output: z.object({ destination: backupDestinationSchema })
69
- };
70
-
71
- /** `secret` omis = inchangé: le serveur ne l'a jamais rendu, on ne le réécrit pas. */
72
- export const backupDestinationUpdate = {
73
- command: 'backup.destinationUpdate' as const,
74
- input: z.object({
75
- destinationId,
76
- name: z.string().min(1).max(BACKUP_DESTINATION_NAME_MAX),
77
- deviceId: z.uuid().nullable(),
78
- path: z.string().max(BACKUP_PATH_MAX),
79
- endpoint: z.string().max(BACKUP_ENDPOINT_MAX).nullable(),
80
- region: z.string().max(64).nullable(),
81
- bucket: z.string().max(BACKUP_BUCKET_MAX).nullable(),
82
- accessKeyId: z.string().max(BACKUP_ACCESS_KEY_MAX).nullable(),
83
- secret: z.string().max(BACKUP_SECRET_MAX).optional(),
84
- pathStyle: z.boolean()
85
- }),
86
- output: z.object({ destination: backupDestinationSchema })
87
- };
88
-
89
- /**
90
- * Retire une destination. **Refusée** tant qu'un travail la vise.
91
- *
92
- * Contrairement aux jetons de déploiement, qui laissent la cible orpheline mais
93
- * vivante: un travail sans destination n'a aucun comportement raisonnable — il
94
- * ne peut ni s'exécuter ni le dire à l'avance. Mieux vaut obliger à trancher.
95
- */
96
- export const backupDestinationRemove = {
97
- command: 'backup.destinationRemove' as const,
98
- input: z.object({ destinationId }),
99
- output: z.object({ destinationId })
100
- };
101
-
102
- /**
103
- * Contrôle qu'une destination est joignable et inscriptible, **maintenant**.
104
- *
105
- * Écrit puis relit puis efface un petit objet témoin: lister un bucket ne prouve
106
- * pas qu'on peut y écrire, et découvrir le contraire à 3 h du matin est
107
- * exactement ce que cette commande existe pour éviter.
108
- */
109
- export const backupDestinationTest = {
110
- command: 'backup.destinationTest' as const,
111
- input: z.object({ destinationId }),
112
- output: backupDestinationProbeSchema
113
- };
114
-
115
- // ---------------------------------------------------------------- travaux
116
-
117
- /** Les travaux de l'espace, avec l'état de leur dernier passage. */
118
- export const backupJobList = {
119
- command: 'backup.jobList' as const,
120
- input: z.object({}),
121
- output: z.object({ jobs: z.array(backupJobSchema) })
122
- };
123
-
124
- /** Compte les travaux actifs. Métadonnée en clair pure, pour la tuile d'accueil. */
125
- export const backupCount = {
126
- command: 'backup.count' as const,
127
- input: z.object({}),
128
- output: z.object({
129
- count: z.number().int().nonnegative(),
130
- /** Combien ont échoué à leur dernier passage — ce que la tuile signale. */
131
- failing: z.number().int().nonnegative()
132
- })
133
- };
134
-
135
- /** Un travail et son historique, du plus récent au plus ancien. */
136
- export const backupJobGet = {
137
- command: 'backup.jobGet' as const,
138
- input: z.object({ jobId, limit: z.number().int().positive().max(100).optional() }),
139
- output: z.object({ job: backupJobSchema, runs: z.array(backupRunSchema) })
140
- };
141
-
142
- const jobBody = {
143
- name: z.string().min(1).max(BACKUP_JOB_NAME_MAX),
144
- destinationId,
145
- encryption: backupEncryptionSchema,
146
- source: backupSourceKindSchema,
147
- sourceId: z.number().int().positive().nullable(),
148
- enabled: z.boolean(),
149
- schedule: backupScheduleKindSchema,
150
- scheduleHour: z.number().int().min(0).max(23),
151
- scheduleWeekday: z.number().int().min(0).max(6),
152
- scheduleDay: z.number().int().min(1).max(28),
153
- keepLast: z.number().int().min(1).max(365)
154
- };
155
-
156
- export const backupJobAdd = {
157
- command: 'backup.jobAdd' as const,
158
- input: z.object(jobBody),
159
- output: z.object({ job: backupJobSchema })
160
- };
161
-
162
- export const backupJobUpdate = {
163
- command: 'backup.jobUpdate' as const,
164
- input: z.object({ jobId, ...jobBody }),
165
- output: z.object({ job: backupJobSchema })
166
- };
167
-
168
- /**
169
- * Supprime un travail et son historique.
170
- *
171
- * Les archives déjà écrites ne sont **pas** touchées: DevEye a produit des
172
- * fichiers chez quelqu'un d'autre, et les effacer parce qu'on range sa
173
- * configuration serait le contraire d'une sauvegarde. L'écran le dit.
174
- */
175
- export const backupJobRemove = {
176
- command: 'backup.jobRemove' as const,
177
- input: z.object({ jobId }),
178
- output: z.object({ jobId })
179
- };
180
-
181
- /**
182
- * Lance un travail tout de suite.
183
- *
184
- * Ne rend pas l'archive: une sauvegarde dure des minutes, et attendre la réponse
185
- * d'une commande WS pendant ce temps-là ne marcherait pas. Elle inscrit
186
- * l'exécution en `running` et rend sa ligne; l'avancement se lit en
187
- * re-sollicitant, comme partout ailleurs (le sujet `backup` est diffusé à
188
- * chaque transition).
189
- */
190
- export const backupJobRun = {
191
- command: 'backup.jobRun' as const,
192
- input: z.object({ jobId }),
193
- output: z.object({ run: backupRunSchema })
194
- };
195
-
196
- /** Les sources sauvegardables de l'espace, pour remplir le sélecteur. */
197
- export const backupSources = {
198
- command: 'backup.sources' as const,
199
- input: z.object({}),
200
- output: z.object({ candidates: z.array(backupSourceCandidateSchema) })
201
- };
202
-
203
- /**
204
- * Les dernières exécutions de l'espace, tous travaux confondus.
205
- *
206
- * Ce que la vue d'ensemble montre: on veut savoir « est-ce que mes sauvegardes
207
- * tournent », pas ouvrir sept fiches pour le découvrir.
208
- */
209
- export const backupRuns = {
210
- command: 'backup.runs' as const,
211
- input: z.object({ limit: z.number().int().positive().max(200).optional() }),
212
- output: z.object({ runs: z.array(backupRunSchema) })
213
- };
214
-
215
- export const backupCommands = [
216
- backupDestinationList,
217
- backupDestinationAdd,
218
- backupDestinationUpdate,
219
- backupDestinationRemove,
220
- backupDestinationTest,
221
- backupJobList,
222
- backupCount,
223
- backupJobGet,
224
- backupJobAdd,
225
- backupJobUpdate,
226
- backupJobRemove,
227
- backupJobRun,
228
- backupSources,
229
- backupRuns
230
- ] as const;
@@ -1,461 +0,0 @@
1
- import { z } from 'zod';
2
- import {
3
- DATABASE_ALERT_MESSAGE_MAX_LENGTH,
4
- DATABASE_ALERT_NAME_MAX_LENGTH,
5
- DATABASE_HOST_MAX_LENGTH,
6
- DATABASE_NAME_MAX_LENGTH,
7
- DATABASE_SECRET_MAX_LENGTH,
8
- DATABASE_SQL_MAX_LENGTH,
9
- DATABASE_USER_MAX_LENGTH,
10
- databaseAccessKindSchema,
11
- databaseAlertSchema,
12
- databaseCellSchema,
13
- databaseCombinatorSchema,
14
- databaseConditionSchema,
15
- databaseEngineSchema,
16
- databaseExecutionSchema,
17
- databaseExportFormatSchema,
18
- databaseFilterSchema,
19
- databaseIdRangeSchema,
20
- databaseProbeSchema,
21
- databaseRowsSchema,
22
- databaseSchema,
23
- databaseSortSchema,
24
- databaseSshAuthSchema,
25
- databaseStructureSchema,
26
- databaseTableSchema,
27
- databaseUsageSchema
28
- } from '../domain/database';
29
-
30
- /**
31
- * Commandes des bases de données de l'espace.
32
- *
33
- * Préfixe unique `database.`, comme `git.` et `project.` — d'où le camelCase
34
- * derrière le point.
35
- *
36
- * ⚠️ Conséquence à connaître : le filet de démarrage (`MUTATION_VERB` dans
37
- * `src/features/_topics.ts`) cherche un verbe **juste après le point**. Il ne
38
- * verra donc **aucune** de ces commandes, et un `mutates` oublié ne produira
39
- * aucun avertissement. Il se relit à la main.
40
- *
41
- * ## Rien ne se connecte tout seul
42
- *
43
- * Seules `test`, `inspect`, `tableList`, `tableRows` et `alertTest` joignent la
44
- * base, et toutes sont déclenchées par un geste explicite. `list` et `get`
45
- * lisent le cache local — ouvrir la feature ne réveille aucun serveur.
46
- *
47
- * L'espace visé n'apparaît dans aucune entrée : il voyage sur l'enveloppe WS et
48
- * le dispatcheur le résout, appartenance vérifiée, avant le handler.
49
- */
50
-
51
- const databaseId = z.number().int().positive();
52
- const alertId = z.number().int().positive();
53
-
54
- /** La partie réglable d'un accès, secret compris (jamais rendu en retour). */
55
- const accessInput = z.object({
56
- kind: databaseAccessKindSchema,
57
- host: z.string().max(DATABASE_HOST_MAX_LENGTH),
58
- port: z.number().int().min(1).max(65535).nullable(),
59
- username: z.string().max(DATABASE_USER_MAX_LENGTH),
60
- auth: databaseSshAuthSchema,
61
- /**
62
- * Mot de passe SSH ou clé privée. Absent = on garde celui en place ; une
63
- * chaîne vide l'efface. Le client ne le reçoit jamais, il ne peut donc pas
64
- * le renvoyer inchangé — d'où cette convention plutôt qu'un champ obligatoire.
65
- */
66
- secret: z.string().max(DATABASE_SECRET_MAX_LENGTH).optional()
67
- });
68
-
69
- // ------------------------------------------------------------------- bases
70
-
71
- /** Le nombre de bases de l'espace, pour la tuile de l'accueil. */
72
- export const databaseCount = {
73
- command: 'database.count' as const,
74
- input: z.object({}),
75
- output: z.object({ count: z.number().int().nonnegative() })
76
- };
77
-
78
- /** Les bases de l'espace, dans l'ordre de l'utilisateur. Lit le cache local. */
79
- export const databaseList = {
80
- command: 'database.list' as const,
81
- input: z.object({}),
82
- output: z.object({ databases: z.array(databaseSchema) })
83
- };
84
-
85
- /** Une base, avec les projets qui s'en servent et ses alertes. */
86
- export const databaseGet = {
87
- command: 'database.get' as const,
88
- input: z.object({ databaseId }),
89
- output: z.object({
90
- database: databaseSchema,
91
- usage: z.array(databaseUsageSchema),
92
- alerts: z.array(databaseAlertSchema)
93
- })
94
- };
95
-
96
- export const databaseAdd = {
97
- command: 'database.add' as const,
98
- input: z.object({
99
- engine: databaseEngineSchema,
100
- name: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
101
- host: z.string().min(1).max(DATABASE_HOST_MAX_LENGTH),
102
- port: z.number().int().min(1).max(65535),
103
- database: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
104
- username: z.string().max(DATABASE_USER_MAX_LENGTH),
105
- password: z.string().max(DATABASE_SECRET_MAX_LENGTH),
106
- access: accessInput,
107
- monitorEnabled: z.boolean(),
108
- intervalSeconds: z.number().int().min(60).max(86400),
109
- autoLoadTables: z.boolean()
110
- }),
111
- output: z.object({ database: databaseSchema })
112
- };
113
-
114
- /**
115
- * Modifie une base. `password` absent = on garde celui en place ; une chaîne
116
- * vide l'efface. Même convention que le secret du tunnel, et pour la même
117
- * raison : un secret rendu au client est un secret qu'on ne peut plus reprendre.
118
- */
119
- export const databaseUpdate = {
120
- command: 'database.update' as const,
121
- input: z.object({
122
- databaseId,
123
- name: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
124
- host: z.string().min(1).max(DATABASE_HOST_MAX_LENGTH),
125
- port: z.number().int().min(1).max(65535),
126
- database: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
127
- username: z.string().max(DATABASE_USER_MAX_LENGTH),
128
- password: z.string().max(DATABASE_SECRET_MAX_LENGTH).optional(),
129
- access: accessInput,
130
- monitorEnabled: z.boolean(),
131
- intervalSeconds: z.number().int().min(60).max(86400),
132
- autoLoadTables: z.boolean()
133
- }),
134
- output: z.object({ database: databaseSchema })
135
- };
136
-
137
- /**
138
- * Retire une base de l'espace, avec ses alertes et toutes ses liaisons.
139
- *
140
- * Les projets liés ne sont **pas** touchés : ils perdent leur base, rien
141
- * d'autre. Le serveur distant, lui, n'est évidemment jamais atteint.
142
- */
143
- export const databaseRemove = {
144
- command: 'database.remove' as const,
145
- input: z.object({ databaseId }),
146
- output: z.object({ databaseId })
147
- };
148
-
149
- /**
150
- * Range les bases de l'espace : `ids` est la liste **complète** dans son ordre
151
- * final. Une nouvelle base prend le rang suivant, donc la fin de la liste.
152
- */
153
- export const databaseReorder = {
154
- command: 'database.reorder' as const,
155
- input: z.object({ ids: z.array(databaseId).min(1) }),
156
- output: z.object({ ids: z.array(databaseId) })
157
- };
158
-
159
- // ------------------------------------------------------- à la demande
160
-
161
- /**
162
- * Essaie de joindre la base, maintenant.
163
- *
164
- * Ne lève **jamais** sur un échec de connexion : un serveur injoignable est une
165
- * réponse, pas une erreur de commande. Le message revient dans `error`, déjà
166
- * traduit, et l'interface le montre sans rien bloquer.
167
- */
168
- export const databaseTest = {
169
- command: 'database.test' as const,
170
- input: z.object({ databaseId }),
171
- output: z.object({ probe: databaseProbeSchema })
172
- };
173
-
174
- /**
175
- * Essaie une connexion **avant** de l'enregistrer.
176
- *
177
- * Le formulaire d'ajout décrit une base qui n'existe pas encore : `test` ne peut
178
- * rien pour lui, il part d'un identifiant. Celui-ci prend les réglages tels
179
- * qu'ils sont saisis, et n'écrit rien — ni ligne, ni état.
180
- *
181
- * `databaseId` sert à la modification : les secrets ne redescendant jamais au
182
- * client, un champ laissé intact n'a rien à renvoyer, et le serveur reprend
183
- * alors celui qu'il détient déjà. Sans cela, « Tester » échouerait sur une base
184
- * qui fonctionne, faute de mot de passe.
185
- */
186
- export const databaseTestDraft = {
187
- command: 'database.testDraft' as const,
188
- input: z.object({
189
- databaseId: databaseId.optional(),
190
- engine: databaseEngineSchema,
191
- host: z.string().min(1).max(DATABASE_HOST_MAX_LENGTH),
192
- port: z.number().int().min(1).max(65535),
193
- database: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
194
- username: z.string().max(DATABASE_USER_MAX_LENGTH),
195
- password: z.string().max(DATABASE_SECRET_MAX_LENGTH).optional(),
196
- access: accessInput
197
- }),
198
- output: z.object({ probe: databaseProbeSchema })
199
- };
200
-
201
- /**
202
- * Relève l'inventaire maintenant : version du serveur, taille, nombre de
203
- * tables, et l'état des alertes. C'est ce que fait le relevé périodique, sur
204
- * demande explicite — ce qui le rend utile même sur une base au repos.
205
- */
206
- export const databaseInspect = {
207
- command: 'database.inspect' as const,
208
- input: z.object({ databaseId }),
209
- output: z.object({ database: databaseSchema, probe: databaseProbeSchema })
210
- };
211
-
212
- /** Les tables de la base, lues chez le serveur au moment de la demande. */
213
- export const databaseTableList = {
214
- command: 'database.tableList' as const,
215
- input: z.object({ databaseId }),
216
- output: z.object({ tables: z.array(databaseTableSchema) })
217
- };
218
-
219
- /**
220
- * Le contenu d'une table, page par page.
221
- *
222
- * ## Rien de ce qui vient du client n'entre tel quel dans une requête
223
- *
224
- * Un identifiant ne peut pas être un paramètre lié : nom de table, nom de
225
- * colonne, sens du tri sont donc **confrontés au catalogue réel** avant d'être
226
- * cités, et un nom qui n'y figure pas n'atteint jamais le moteur. Les valeurs,
227
- * elles, sont toujours liées ; l'opérateur d'un filtre est choisi dans une
228
- * énumération fermée. Aucun fragment de SQL ne traverse le contrat.
229
- *
230
- * `withStructure` évite un second aller-retour à la sélection d'une table : la
231
- * structure et la première page arrivent alors dans la **même session**, ce qui
232
- * compte quand chaque connexion rouvre un tunnel SSH.
233
- */
234
- export const databaseTableRows = {
235
- command: 'database.tableRows' as const,
236
- input: z.object({
237
- databaseId,
238
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH),
239
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
240
- offset: z.number().int().nonnegative().max(1_000_000).optional(),
241
- limit: z.number().int().positive().max(200).optional(),
242
- filters: z.array(databaseFilterSchema).max(8).optional(),
243
- combinator: databaseCombinatorSchema.optional(),
244
- sort: databaseSortSchema.optional(),
245
- withStructure: z.boolean().optional()
246
- }),
247
- output: z.object({ rows: databaseRowsSchema, structure: databaseStructureSchema.optional() })
248
- };
249
-
250
- /** La structure seule : colonnes, clé primaire, clés étrangères, index. */
251
- export const databaseTableStructure = {
252
- command: 'database.tableStructure' as const,
253
- input: z.object({
254
- databaseId,
255
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH),
256
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH)
257
- }),
258
- output: z.object({ structure: databaseStructureSchema })
259
- };
260
-
261
- // ------------------------------------------------------- écrire des lignes
262
-
263
- /**
264
- * Les trois écritures de l'explorateur.
265
- *
266
- * ## Ce que le serveur refuse, et pourquoi
267
- *
268
- * Modifier ou supprimer exige une **clé primaire**. Sans elle, aucune condition
269
- * ne désigne *une* ligne : un `UPDATE` en toucherait plusieurs, un `DELETE` en
270
- * emporterait autant, et rien ne permettrait de revenir en arrière. Le serveur
271
- * refuse alors, avec la raison ; l'interface ne propose même pas le geste.
272
- *
273
- * ## Ce qui décide en dernier ressort
274
- *
275
- * Le compte saisi dans les réglages de la base. DevEye peut demander une
276
- * écriture ; c'est le serveur distant qui l'accorde ou la refuse. Un compte en
277
- * lecture seule rend donc tout ceci inoffensif, ce que dit le formulaire.
278
- */
279
- export const databaseRowInsert = {
280
- command: 'database.rowInsert' as const,
281
- input: z.object({
282
- databaseId,
283
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH),
284
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
285
- values: z.array(databaseCellSchema).min(1).max(200)
286
- }),
287
- output: z.object({ inserted: z.number().int().nonnegative() })
288
- };
289
-
290
- export const databaseRowUpdate = {
291
- command: 'database.rowUpdate' as const,
292
- input: z.object({
293
- databaseId,
294
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH),
295
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
296
- /** La ligne visée, par ses colonnes de clé primaire. */
297
- key: z.array(databaseCellSchema).min(1).max(16),
298
- values: z.array(databaseCellSchema).min(1).max(200)
299
- }),
300
- output: z.object({ updated: z.number().int().nonnegative() })
301
- };
302
-
303
- export const databaseRowDelete = {
304
- command: 'database.rowDelete' as const,
305
- input: z.object({
306
- databaseId,
307
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH),
308
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH),
309
- /** Une entrée par ligne, chacune par ses colonnes de clé primaire. */
310
- keys: z.array(z.array(databaseCellSchema).min(1).max(16)).min(1).max(200)
311
- }),
312
- output: z.object({ deleted: z.number().int().nonnegative() })
313
- };
314
-
315
- /**
316
- * Une instruction libre, écriture comprise — le terminal.
317
- *
318
- * Distincte de `query`, qui refuse tout ce qui n'est pas une lecture parce
319
- * qu'elle sert à mettre au point une condition d'alerte. Ici l'intention est
320
- * l'inverse : administrer. Une seule instruction à la fois malgré tout, le
321
- * point-virgule interne restant refusé — c'est ce qui empêche qu'un copier-coller
322
- * en exécute trois quand on en visait une.
323
- */
324
- export const databaseExecute = {
325
- command: 'database.execute' as const,
326
- input: z.object({ databaseId, sql: z.string().min(1).max(DATABASE_SQL_MAX_LENGTH) }),
327
- output: z.object({ result: databaseExecutionSchema })
328
- };
329
-
330
- /**
331
- * Exporte une table, ou toute la base.
332
- *
333
- * L'export est **complet** : il lit tout ce que la portée désigne, page par
334
- * page. Le seul plafond qui subsiste est un garde-fou mémoire du serveur, très
335
- * au-dessus de ce qu'un export normal atteint ; s'il est touché, `truncated` le
336
- * dit et l'interface le répète. Pour une copie fidèle d'un serveur entier,
337
- * `mysqldump` et `pg_dump` restent malgré tout les bons outils — eux savent
338
- * rejouer schéma, index et contraintes.
339
- *
340
- * `idRanges` restreint l'export aux lignes dont la **clé primaire** tombe dans
341
- * l'une des plages. Réservé à une portée d'une seule table : sur toute la base,
342
- * les clés n'ont ni le même nom ni le même sens d'une table à l'autre.
343
- */
344
- export const databaseExport = {
345
- command: 'database.export' as const,
346
- input: z.object({
347
- databaseId,
348
- format: databaseExportFormatSchema,
349
- /** Absents : toute la base. Présents : cette table seule. */
350
- schema: z.string().max(DATABASE_NAME_MAX_LENGTH).optional(),
351
- table: z.string().min(1).max(DATABASE_NAME_MAX_LENGTH).optional(),
352
- /** Plages d'identifiants, bornes comprises. Sans effet sans `table`. */
353
- idRanges: z.array(databaseIdRangeSchema).max(64).optional()
354
- }),
355
- output: z.object({
356
- filename: z.string(),
357
- content: z.string(),
358
- rowCount: z.number().int().nonnegative(),
359
- tableCount: z.number().int().nonnegative(),
360
- /** Le plafond a été atteint : ce qui suit manque. */
361
- truncated: z.boolean()
362
- })
363
- };
364
-
365
- // ----------------------------------------------------------------- alertes
366
-
367
- export const databaseAlertList = {
368
- command: 'database.alertList' as const,
369
- input: z.object({ databaseId }),
370
- output: z.object({ alerts: z.array(databaseAlertSchema) })
371
- };
372
-
373
- export const databaseAlertAdd = {
374
- command: 'database.alertAdd' as const,
375
- input: z.object({
376
- databaseId,
377
- name: z.string().min(1).max(DATABASE_ALERT_NAME_MAX_LENGTH),
378
- enabled: z.boolean(),
379
- combinator: databaseCombinatorSchema,
380
- conditions: z.array(databaseConditionSchema).min(1).max(8),
381
- message: z.string().min(1).max(DATABASE_ALERT_MESSAGE_MAX_LENGTH)
382
- }),
383
- output: z.object({ alert: databaseAlertSchema })
384
- };
385
-
386
- export const databaseAlertUpdate = {
387
- command: 'database.alertUpdate' as const,
388
- input: z.object({
389
- alertId,
390
- name: z.string().min(1).max(DATABASE_ALERT_NAME_MAX_LENGTH),
391
- enabled: z.boolean(),
392
- combinator: databaseCombinatorSchema,
393
- conditions: z.array(databaseConditionSchema).min(1).max(8),
394
- message: z.string().min(1).max(DATABASE_ALERT_MESSAGE_MAX_LENGTH)
395
- }),
396
- output: z.object({ alert: databaseAlertSchema })
397
- };
398
-
399
- export const databaseAlertRemove = {
400
- command: 'database.alertRemove' as const,
401
- input: z.object({ alertId }),
402
- output: z.object({ alertId })
403
- };
404
-
405
- /**
406
- * Évalue une alerte tout de suite et rend ce que chaque condition a mesuré,
407
- * **sans notifier personne**.
408
- *
409
- * C'est ce qui rend une condition écrivable : on voit le nombre que rend sa
410
- * requête avant de choisir un seuil, au lieu d'attendre une notification pour
411
- * découvrir qu'on s'est trompé de colonne.
412
- */
413
- export const databaseAlertTest = {
414
- command: 'database.alertTest' as const,
415
- input: z.object({
416
- databaseId,
417
- combinator: databaseCombinatorSchema,
418
- conditions: z.array(databaseConditionSchema).min(1).max(8)
419
- }),
420
- output: z.object({
421
- firing: z.boolean(),
422
- values: z.array(z.number().nullable()),
423
- /** L'erreur de chaque condition, à sa place — `null` si elle a abouti. */
424
- errors: z.array(z.string().nullable()),
425
- elapsedMs: z.number().int().nonnegative()
426
- })
427
- };
428
-
429
- /** Une requête libre en lecture seule, pour mettre au point une condition. */
430
- export const databaseQuery = {
431
- command: 'database.query' as const,
432
- input: z.object({ databaseId, sql: z.string().min(1).max(DATABASE_SQL_MAX_LENGTH) }),
433
- output: z.object({ rows: databaseRowsSchema })
434
- };
435
-
436
- export const databaseCommands = [
437
- databaseCount,
438
- databaseList,
439
- databaseGet,
440
- databaseAdd,
441
- databaseUpdate,
442
- databaseRemove,
443
- databaseReorder,
444
- databaseTest,
445
- databaseTestDraft,
446
- databaseInspect,
447
- databaseTableList,
448
- databaseTableRows,
449
- databaseTableStructure,
450
- databaseRowInsert,
451
- databaseRowUpdate,
452
- databaseRowDelete,
453
- databaseExecute,
454
- databaseExport,
455
- databaseAlertList,
456
- databaseAlertAdd,
457
- databaseAlertUpdate,
458
- databaseAlertRemove,
459
- databaseAlertTest,
460
- databaseQuery
461
- ] as const;