@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.
- package/package.json +6 -6
- package/src/domain/device.ts +4 -29
- package/src/domain/featureRegistry.ts +40 -108
- package/src/domain/home.ts +40 -101
- package/src/domain/live.ts +36 -87
- package/src/domain/metrics.ts +10 -14
- package/src/domain/notifications.ts +39 -110
- package/src/domain/project.ts +3 -162
- package/src/domain/report.ts +46 -95
- package/src/domain/secrecy.ts +3 -5
- package/src/domain/sharing.ts +33 -70
- package/src/domain/syncProtocol.ts +5 -12
- package/src/domain/user.ts +8 -14
- package/src/domain/workspace.ts +0 -3
- package/src/domain/workspaceRole.ts +34 -82
- package/src/features/agent.ts +306 -0
- package/src/features/live.ts +17 -37
- package/src/features/notify.ts +19 -57
- package/src/features/registry.ts +7 -44
- package/src/features/secrecy.ts +4 -9
- package/src/features/sharing.ts +12 -26
- package/src/features/user.ts +6 -11
- package/src/features/workspace.ts +6 -11
- package/src/http/auth.ts +6 -13
- package/src/http/device.ts +13 -17
- package/src/http/status.ts +6 -10
- package/src/index.ts +29 -932
- package/src/protocol/agent.ts +40 -70
- package/src/protocol/envelope.ts +3 -10
- package/src/sdk/client-ambient.d.ts +228 -21
- package/src/sdk/client.ts +277 -17
- package/src/sdk/devb.ts +120 -0
- package/src/sdk/manifest.test.ts +0 -1
- package/src/sdk/manifest.ts +94 -25
- package/src/sdk/providers.ts +310 -5
- package/src/sdk/server.ts +483 -19
- package/src/sdk/testing.test.ts +2 -2
- package/src/sdk/testing.ts +302 -26
- package/src/utils/version.ts +5 -8
- package/src/domain/audience.ts +0 -549
- package/src/domain/backup.ts +0 -355
- package/src/domain/credential.ts +0 -55
- package/src/domain/database.ts +0 -467
- package/src/domain/deploy.ts +0 -231
- package/src/domain/finance.ts +0 -477
- package/src/domain/git.ts +0 -419
- package/src/domain/mail.ts +0 -394
- package/src/domain/note.ts +0 -202
- package/src/domain/password.ts +0 -36
- package/src/domain/projectBoard.ts +0 -130
- package/src/domain/projectChat.ts +0 -46
- package/src/domain/projectHistory.ts +0 -82
- package/src/domain/projectLink.ts +0 -87
- package/src/domain/projectPlan.ts +0 -68
- package/src/domain/sentinel.ts +0 -623
- package/src/domain/uptime.ts +0 -216
- package/src/features/audience.ts +0 -275
- package/src/features/backup.ts +0 -230
- package/src/features/database.ts +0 -461
- package/src/features/deploy.ts +0 -245
- package/src/features/device.ts +0 -292
- package/src/features/deviceFiles.ts +0 -83
- package/src/features/deviceLogs.ts +0 -36
- package/src/features/deviceTerminal.ts +0 -57
- package/src/features/finance.ts +0 -360
- package/src/features/git.ts +0 -368
- package/src/features/mail.ts +0 -374
- package/src/features/metrics.ts +0 -185
- package/src/features/note.ts +0 -189
- package/src/features/password.ts +0 -67
- package/src/features/project.ts +0 -709
- package/src/features/sentinel.ts +0 -233
- package/src/features/uptime.ts +0 -186
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deveye/types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Shared contracts (types + zod schemas) for DevEye apps",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"lint": "eslint .",
|
|
42
42
|
"lint:fix": "eslint . --fix",
|
|
43
43
|
"typecheck": "tsc --noEmit",
|
|
44
|
-
"ci": "npm run lint && npm run typecheck && npm test",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
44
|
+
"ci": "npm run format:check && npm run lint && npm run typecheck && npm test",
|
|
45
|
+
"format:check": "prettier --check .",
|
|
46
|
+
"format:fix": "prettier --write .",
|
|
47
47
|
"test": "tsx --test \"src/**/*.test.ts\""
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"@eslint/js": "^8.0.0",
|
|
58
58
|
"@types/node": "^26.3.0",
|
|
59
59
|
"@types/react": "^19.0.0",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
61
|
-
"@typescript-eslint/parser": "^
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
61
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
62
62
|
"eslint": "^8.0.0",
|
|
63
63
|
"eslint-plugin-prettier": "^5.5.4",
|
|
64
64
|
"tsx": "^4.23.12",
|
package/src/domain/device.ts
CHANGED
|
@@ -91,18 +91,10 @@ export interface DeviceRow {
|
|
|
91
91
|
id: string;
|
|
92
92
|
owner_id: number;
|
|
93
93
|
/**
|
|
94
|
-
* Espace d'
|
|
95
|
-
* l'
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* Ce n'est plus la frontière d'accès : celle-ci est la table de jonction
|
|
100
|
-
* `device_workspaces`, un appareil pouvant être partagé avec plusieurs
|
|
101
|
-
* espaces. L'espace d'appairage y figure toujours et ne s'en retire pas.
|
|
102
|
-
*
|
|
103
|
-
* `null` quand cet espace a été supprimé : l'appareil survit — il perd son
|
|
104
|
-
* origine, pas son existence — et reste joignable par les espaces avec
|
|
105
|
-
* lesquels il est partagé.
|
|
94
|
+
* Espace d'appairage (celui du code de liaison) : porte l'unicité de
|
|
95
|
+
* l'empreinte (`uniq_workspace_fingerprint`) et sert le ré-enrôlement par la
|
|
96
|
+
* route publique. La frontière d'accès est `device_workspaces`, où il figure
|
|
97
|
+
* toujours. `null` si cet espace a été supprimé : l'appareil survit.
|
|
106
98
|
*/
|
|
107
99
|
workspace_id: number | null;
|
|
108
100
|
name: string;
|
|
@@ -131,23 +123,6 @@ export interface DeviceRow {
|
|
|
131
123
|
status_before_delete: string | null;
|
|
132
124
|
/** Last self-destruct failure message (deletion aborted); null otherwise. */
|
|
133
125
|
delete_error: string | null;
|
|
134
|
-
/**
|
|
135
|
-
* Sentinelle est-elle active sur cet appareil ? Éteinte par défaut : activer
|
|
136
|
-
* la feature ne doit lire les journaux d'authentification de personne, c'est
|
|
137
|
-
* un geste explicite appareil par appareil.
|
|
138
|
-
*/
|
|
139
|
-
sentinel_enabled: number;
|
|
140
|
-
/**
|
|
141
|
-
* Fin de la fenêtre d'apprentissage, unix ms. `null` tant que Sentinelle n'a
|
|
142
|
-
* jamais été activée ; une date passée signifie « apprentissage terminé ».
|
|
143
|
-
*/
|
|
144
|
-
sentinel_learning_until: number | null;
|
|
145
|
-
/** Cadence du manifeste de persistance, en minutes. */
|
|
146
|
-
sentinel_integrity_minutes: number;
|
|
147
|
-
/** Relever les issues d'authentification (interrupteur propre). */
|
|
148
|
-
sentinel_auth_events: number;
|
|
149
|
-
/** Unix ms du dernier manifeste reçu ; `null` = jamais mesuré. */
|
|
150
|
-
sentinel_last_integrity_at: number | null;
|
|
151
126
|
}
|
|
152
127
|
|
|
153
128
|
/**
|
|
@@ -6,24 +6,10 @@ import {
|
|
|
6
6
|
} from './workspaceRole';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Ce qu'est une fonctionnalité, dit
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* main dans `RoleDialog` **et** dans `FEATURE_CATALOG`. Trois copies d'un même
|
|
14
|
-
* couple identifiant / libellé, c'est la garantie qu'une fonctionnalité ajoutée
|
|
15
|
-
* n'arrivera que dans deux d'entre elles — et c'est exactement ce qui était
|
|
16
|
-
* arrivé à `monitoring`, présent dans un tableau et absent de l'autre sans que
|
|
17
|
-
* rien ne le dise.
|
|
18
|
-
*
|
|
19
|
-
* Ce registre porte donc le **descriptif** ; les deux enums gardent leur rôle,
|
|
20
|
-
* qui est de dire *où* une fonctionnalité a le droit d'apparaître. Ils ne sont
|
|
21
|
-
* volontairement pas fusionnés : leur écart est documenté et voulu (`devices`
|
|
22
|
-
* s'accorde mais n'a pas de tuile, `monitoring` a une tuile mais ne s'accorde
|
|
23
|
-
* pas — voir `workspaceRole.ts`).
|
|
24
|
-
*
|
|
25
|
-
* Les écrans qui parcourent les fonctionnalités — la coquille de réglages, la
|
|
26
|
-
* matrice de permissions, l'écran des rôles — lisent celui-ci et rien d'autre.
|
|
9
|
+
* Ce qu'est une fonctionnalité, dit une fois : le descriptif que lisent les
|
|
10
|
+
* écrans qui parcourent les fonctionnalités (coquille de réglages, matrice de
|
|
11
|
+
* permissions, écran des rôles). `HOME_FEATURE_IDS` et `WORKSPACE_FEATURE_IDS`
|
|
12
|
+
* gardent leur rôle : dire où une fonctionnalité a le droit d'apparaître.
|
|
27
13
|
*/
|
|
28
14
|
export interface FeatureDescriptor {
|
|
29
15
|
id: FeatureId;
|
|
@@ -38,18 +24,14 @@ export interface FeatureDescriptor {
|
|
|
38
24
|
/** Classe d'icône de `Styles/icons.css`, sans le préfixe `icon-`. */
|
|
39
25
|
icon: string;
|
|
40
26
|
/**
|
|
41
|
-
* Cette fonctionnalité sait prévenir
|
|
42
|
-
* de ses réglages
|
|
43
|
-
* quatre qui n'ont rien d'autre à régler.
|
|
27
|
+
* Cette fonctionnalité sait prévenir : décide de l'onglet « Notifications »
|
|
28
|
+
* de ses réglages.
|
|
44
29
|
*/
|
|
45
30
|
notifies: boolean;
|
|
46
31
|
/**
|
|
47
|
-
* Elle tient
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* Faux ne veut pas dire « aucune donnée » : Sentinelle a bien des constats,
|
|
51
|
-
* mais on ne règle pas un constat, on règle la surveillance. Le critère est
|
|
52
|
-
* « peut-on ouvrir les réglages de **cet** élément ? ».
|
|
32
|
+
* Elle tient des entités adressables sur lesquelles des réglages portent
|
|
33
|
+
* individuellement. Le critère : « peut-on ouvrir les réglages de CET
|
|
34
|
+
* élément ? » (Sentinelle a des constats, mais on règle la surveillance).
|
|
53
35
|
*/
|
|
54
36
|
hasItems: boolean;
|
|
55
37
|
/**
|
|
@@ -58,42 +40,23 @@ export interface FeatureDescriptor {
|
|
|
58
40
|
*/
|
|
59
41
|
itemNoun?: string;
|
|
60
42
|
/**
|
|
61
|
-
* Les
|
|
62
|
-
* (un jeton
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* fonctionnalité**, le seul endroit où les sources se créent et se
|
|
68
|
-
* corrigent. Les dialogues d'élément, eux, ne font que choisir dans la
|
|
69
|
-
* liste, avec un bouton qui mène ici. `hint` est la phrase de tête de
|
|
70
|
-
* l'onglet : elle dit ce qu'on y gère et qui s'en sert.
|
|
71
|
-
*
|
|
72
|
-
* Les canaux de notification suivent la même logique sans passer par ce
|
|
73
|
-
* champ : ce sont les sources des émetteurs (chaque feature a les siens,
|
|
74
|
-
* migration 091), gérées dans leur onglet « Notifications », qui porte
|
|
75
|
-
* aussi le routage, indissociable (voir `notifies`).
|
|
43
|
+
* Les sources de la fonctionnalité : des réglages d'espace réutilisables
|
|
44
|
+
* (un jeton, une destination) que chaque élément ne fait que désigner.
|
|
45
|
+
* Ouvre l'onglet « Sources » à l'échelle de la fonctionnalité, seul endroit
|
|
46
|
+
* où elles se créent et se corrigent ; `hint` est la phrase de tête de
|
|
47
|
+
* l'onglet. Les canaux de notification suivent la même logique dans
|
|
48
|
+
* l'onglet « Notifications » (voir `notifies`).
|
|
76
49
|
*/
|
|
77
50
|
sources?: { hint: string };
|
|
78
51
|
/**
|
|
79
52
|
* Un de ses éléments peut-il être rendu visible depuis un autre espace ?
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* D'où trois cas :
|
|
88
|
-
*
|
|
89
|
-
* - `'open'` — toute la fonctionnalité vit à l'étage ouvert : partageable
|
|
90
|
-
* sans condition (Uptime, Bases, Déploiement, Git, Audience, Sauvegardes) ;
|
|
91
|
-
* - `'perItem'` — l'étage se choisit élément par élément. Une note
|
|
92
|
-
* ordinaire se partage, une note privée non ; un compte mail « open »
|
|
93
|
-
* oui, un compte « guarded » non. Le serveur tranche à la ligne, jamais
|
|
94
|
-
* la fonctionnalité en bloc ;
|
|
95
|
-
* - `'never'` — rien n'y est partageable, pour une raison propre à chaque
|
|
96
|
-
* cas (voir la note sous le registre).
|
|
53
|
+
* Décidé par le chiffrement : un élément partagé reste chiffré sous la clé
|
|
54
|
+
* de son espace d'origine, et seule la clé de l'étage ouvert est résoluble
|
|
55
|
+
* par le serveur seul.
|
|
56
|
+
* - `'open'` : toute la fonctionnalité vit à l'étage ouvert ;
|
|
57
|
+
* - `'perItem'` : l'étage se choisit élément par élément, le serveur
|
|
58
|
+
* tranche à la ligne ;
|
|
59
|
+
* - `'never'` : rien n'y est partageable (voir la note sous le registre).
|
|
97
60
|
*/
|
|
98
61
|
shareTier: 'open' | 'perItem' | 'never';
|
|
99
62
|
}
|
|
@@ -290,13 +253,9 @@ export const FEATURE_REGISTRY: readonly (FeatureDescriptor & { id: WorkspaceFeat
|
|
|
290
253
|
const BY_ID = new Map<string, FeatureDescriptor>(FEATURE_REGISTRY.map((f) => [f.id, f]));
|
|
291
254
|
|
|
292
255
|
/**
|
|
293
|
-
* Les modules
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
* fois par module installé, depuis la glue générée des deux bundles. Il ne
|
|
297
|
-
* s'agit pas de chargement à chaud : la liste est figée à la compilation, la
|
|
298
|
-
* carte n'existe que parce qu'un fichier ne peut pas être à la fois publié dans
|
|
299
|
-
* ce package et généré par l'application qui l'installe.
|
|
256
|
+
* Les modules externes enregistrés dans ce processus, remplis au chargement
|
|
257
|
+
* par la glue générée. Pas de chargement à chaud : la liste est figée à la
|
|
258
|
+
* compilation.
|
|
300
259
|
*/
|
|
301
260
|
const EXTERNAL_BY_ID = new Map<ExternalFeatureId, FeatureDescriptor>();
|
|
302
261
|
|
|
@@ -316,13 +275,10 @@ export function allFeatureDescriptors(): readonly FeatureDescriptor[] {
|
|
|
316
275
|
}
|
|
317
276
|
|
|
318
277
|
/**
|
|
319
|
-
* Le descriptif d'une fonctionnalité, native ou externe.
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
* inconnu signifie que la glue générée n'a pas tourné, pas un cas d'exécution
|
|
324
|
-
* à traiter chez l'appelant. Les écrans qui veulent tolérer un module absent
|
|
325
|
-
* (une tuile orpheline) passent par {@link maybeFeatureDescriptor}.
|
|
278
|
+
* Le descriptif d'une fonctionnalité, native ou externe. Lève plutôt que de
|
|
279
|
+
* rendre `undefined` : une absence est un oubli d'entrée ici, ou une glue
|
|
280
|
+
* générée qui n'a pas tourné. Pour tolérer un module absent, voir
|
|
281
|
+
* {@link maybeFeatureDescriptor}.
|
|
326
282
|
*/
|
|
327
283
|
export function featureDescriptor(id: FeatureId): FeatureDescriptor {
|
|
328
284
|
const found = maybeFeatureDescriptor(id);
|
|
@@ -341,19 +297,10 @@ export function featureLabel(id: FeatureId): string {
|
|
|
341
297
|
}
|
|
342
298
|
|
|
343
299
|
/**
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* autre nature : `device_workspaces` (migration 072) est une adhésion à part
|
|
349
|
-
* entière, pas une projection. Le mécanisme d'ici ne s'y superpose pas.
|
|
350
|
-
* - **`password`** vit toujours à l'étage gardé — c'est la promesse du coffre.
|
|
351
|
-
* Le serveur sait certes le lire quand le chiffrement par mot de passe est
|
|
352
|
-
* éteint, mais un partage dont la survie dépend d'un réglage de sécurité
|
|
353
|
-
* qu'on encourage n'est pas un partage.
|
|
354
|
-
* - **`cloudsync`** range ses contenus dans un magasin de blobs sur disque,
|
|
355
|
-
* chiffrés par la BMK et non par une clé d'espace : ce serait un autre
|
|
356
|
-
* chantier.
|
|
300
|
+
* Les trois `never` : `devices` a déjà son partage inter-espaces, d'une autre
|
|
301
|
+
* nature (`device_workspaces` est une adhésion, pas une projection) ;
|
|
302
|
+
* `password` vit toujours à l'étage gardé ; `cloudsync` range ses contenus
|
|
303
|
+
* dans un magasin de blobs chiffrés par la BMK, pas par une clé d'espace.
|
|
357
304
|
*/
|
|
358
305
|
|
|
359
306
|
/** Les fonctionnalités dont un élément **pourrait** voyager, côté chiffrement. */
|
|
@@ -362,29 +309,14 @@ export const SHAREABLE_FEATURES: readonly WorkspaceFeatureId[] = FEATURE_REGISTR
|
|
|
362
309
|
).map((f) => f.id);
|
|
363
310
|
|
|
364
311
|
/**
|
|
365
|
-
* Celles dont la
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
* listage
|
|
370
|
-
*
|
|
371
|
-
* cocherait et rien n'apparaîtrait de l'autre côté.
|
|
372
|
-
*
|
|
373
|
-
* Partagée entre client et serveur **exprès** : le serveur refuse, le client
|
|
374
|
-
* n'affiche pas l'onglet. Deux listes séparées auraient donné un onglet qui ne
|
|
375
|
-
* mène nulle part — précisément ce que la coquille de réglages refuse.
|
|
376
|
-
*
|
|
377
|
-
* Brancher une fonctionnalité de plus : `listVisible` / `findVisible` dans son
|
|
378
|
-
* dépôt, le codec par ligne dans son listage, une entrée ici.
|
|
312
|
+
* Celles dont la lecture élargie est réellement branchée : `shareTier` dit ce
|
|
313
|
+
* que le chiffrement autorise, cette liste ce que le code fait. Partagée entre
|
|
314
|
+
* client et serveur : le serveur refuse, le client n'affiche pas l'onglet.
|
|
315
|
+
* Brancher une native : `listVisible` / `findVisible` dans son dépôt, le codec
|
|
316
|
+
* par ligne dans son listage, une entrée ici. Un module se déclare par son
|
|
317
|
+
* manifest (`shareTier` autre que `'never'`) et son entrée `items`.
|
|
379
318
|
*/
|
|
380
|
-
export const SHARE_WIRED_FEATURES: readonly WorkspaceFeatureId[] = [
|
|
381
|
-
'uptime',
|
|
382
|
-
'database',
|
|
383
|
-
'deploy',
|
|
384
|
-
'git',
|
|
385
|
-
'audience',
|
|
386
|
-
'backup'
|
|
387
|
-
];
|
|
319
|
+
export const SHARE_WIRED_FEATURES: readonly WorkspaceFeatureId[] = ['backup'];
|
|
388
320
|
|
|
389
321
|
/** Les fonctionnalités qui savent prévenir, dans l'ordre du registre. */
|
|
390
322
|
export const NOTIFYING_FEATURES: readonly WorkspaceFeatureId[] = FEATURE_REGISTRY.filter(
|
package/src/domain/home.ts
CHANGED
|
@@ -8,20 +8,20 @@ import {
|
|
|
8
8
|
} from './workspaceRole';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Disposition de l'accueil (par espace)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* groupe légèrement espacé, sans titre.
|
|
17
|
-
*
|
|
18
|
-
* Stockée en clair : métadonnée de personnalisation non sensible (comme le
|
|
19
|
-
* thème), jamais de charge zero-knowledge.
|
|
11
|
+
* Disposition de l'accueil (par espace) : des sections ordonnées, chacune
|
|
12
|
+
* tenant des tuiles ordonnées de n'importe quels genres (appareil,
|
|
13
|
+
* fonctionnalité, raccourci, dossier). Aucune section par défaut ; l'intitulé
|
|
14
|
+
* est facultatif. Stockée en clair : métadonnée de personnalisation, jamais de
|
|
15
|
+
* charge zero-knowledge.
|
|
20
16
|
*/
|
|
21
17
|
|
|
22
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Les seize tuiles de fonctionnalités natives. Une tuile porte l'id de sa
|
|
20
|
+
* feature : la tuile Monitoring est celle de la feature `devices`, dont le
|
|
21
|
+
* module fournit la carte et la vue.
|
|
22
|
+
*/
|
|
23
23
|
export const nativeHomeFeatureIdSchema = z.enum([
|
|
24
|
-
'
|
|
24
|
+
'devices',
|
|
25
25
|
'sentinel',
|
|
26
26
|
'weather',
|
|
27
27
|
'password',
|
|
@@ -50,22 +50,15 @@ export const homeFeatureIdSchema = z.union([nativeHomeFeatureIdSchema, externalF
|
|
|
50
50
|
export type HomeFeatureId = NativeHomeFeatureId | ExternalFeatureId;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Compact widgets
|
|
54
|
-
*
|
|
55
|
-
* empty (the navbar shows none until the user adds some).
|
|
53
|
+
* Compact widgets pinned to the top-right of the navbar, individually
|
|
54
|
+
* add/remove/reorderable; the default set is empty.
|
|
56
55
|
* - `weather` → current temperature of the primary city.
|
|
57
|
-
* - `devices` → online / total device count.
|
|
58
56
|
* - `secrecy` → password-encryption lock state + re-validation countdown.
|
|
59
|
-
* - `uptime` → services up / total monitored.
|
|
60
57
|
* - `live` → qui d'autre est dans l'espace, et où (bulles cliquables).
|
|
58
|
+
* A module's widget is declared by its manifest (`topbarWidget`) and keyed by
|
|
59
|
+
* its feature id (`homeTopbarWidgetIdSchema`).
|
|
61
60
|
*/
|
|
62
|
-
export const nativeHomeTopbarWidgetIdSchema = z.enum([
|
|
63
|
-
'weather',
|
|
64
|
-
'devices',
|
|
65
|
-
'secrecy',
|
|
66
|
-
'uptime',
|
|
67
|
-
'live'
|
|
68
|
-
]);
|
|
61
|
+
export const nativeHomeTopbarWidgetIdSchema = z.enum(['weather', 'secrecy', 'live']);
|
|
69
62
|
export type NativeHomeTopbarWidgetId = z.infer<typeof nativeHomeTopbarWidgetIdSchema>;
|
|
70
63
|
|
|
71
64
|
/**
|
|
@@ -77,12 +70,9 @@ export const homeTopbarWidgetIdSchema = z.union([nativeHomeTopbarWidgetIdSchema,
|
|
|
77
70
|
export type HomeTopbarWidgetId = NativeHomeTopbarWidgetId | FeatureId;
|
|
78
71
|
|
|
79
72
|
/**
|
|
80
|
-
* Shortcut preview type, auto-detected from the URL's domain
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* (GitHub API, YouTube/Spotify/SoundCloud/TikTok oEmbed, Wikipedia REST, npm
|
|
84
|
-
* registry), and the generic Open Graph + favicon adapter for the rest. `link`
|
|
85
|
-
* is that generic adapter; unknown domains fall back to it.
|
|
73
|
+
* Shortcut preview type, auto-detected from the URL's domain. Each value has a
|
|
74
|
+
* server-side adapter under `src/Services/shortcutTemplates/`; `link` is the
|
|
75
|
+
* generic Open Graph + favicon adapter, and unknown domains fall back to it.
|
|
86
76
|
*/
|
|
87
77
|
export const shortcutTemplateSchema = z.enum([
|
|
88
78
|
'link',
|
|
@@ -125,38 +115,19 @@ export const shortcutItemSchema = z.object({
|
|
|
125
115
|
export type ShortcutItem = z.infer<typeof shortcutItemSchema>;
|
|
126
116
|
|
|
127
117
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* Exporté, et pas seulement écrit dans le schéma : le client doit refuser
|
|
132
|
-
* **avant** d'écrire. Une disposition qui dépasse le plafond ne passe plus la
|
|
133
|
-
* validation, donc le serveur la rejette et le client la relit vide au
|
|
134
|
-
* démarrage suivant, ce qui revient à un accueil effacé sans un mot. Le
|
|
135
|
-
* plafond des dossiers n'est atteignable par aucun geste (il y a moins de
|
|
136
|
-
* fonctionnalités que ça, et aucune ne peut être rangée deux fois), celui des
|
|
137
|
-
* tuiles l'est en créant des dossiers à la chaîne.
|
|
118
|
+
* Plafonds d'une section et d'un dossier. Exportés parce que le client doit
|
|
119
|
+
* refuser avant d'écrire : une disposition qui dépasse ne passe plus la
|
|
120
|
+
* validation, et le client la relirait vide au démarrage suivant.
|
|
138
121
|
*/
|
|
139
122
|
export const HOME_SECTION_MAX_TILES = 60;
|
|
140
123
|
export const HOME_FOLDER_MAX_ITEMS = 20;
|
|
141
124
|
|
|
142
125
|
/**
|
|
143
|
-
* Un dossier de la grille : plusieurs fonctionnalités derrière une seule tuile
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* déploient par-dessus l'accueil).
|
|
149
|
-
*
|
|
150
|
-
* Il ne range que des **fonctionnalités**, là où une section range tout : une
|
|
151
|
-
* carte d'appareil et un raccourci sont déjà des tuiles courtes, les empiler
|
|
152
|
-
* derrière une tuile de pleine hauteur coûterait plus de place qu'il n'en
|
|
153
|
-
* gagnerait. C'est la seule asymétrie qui reste après l'unification, et elle
|
|
154
|
-
* est de mise en page, pas de modèle.
|
|
155
|
-
*
|
|
156
|
-
* Les fonctionnalités qu'il tient comptent comme **posées sur l'accueil** : le
|
|
157
|
-
* sélecteur d'ajout les exclut, exactement comme celles qui ont leur propre
|
|
158
|
-
* tuile. Une fonctionnalité n'est donc jamais à deux endroits à la fois, et la
|
|
159
|
-
* règle « pas deux fois la même » reste une seule règle.
|
|
126
|
+
* Un dossier de la grille : plusieurs fonctionnalités derrière une seule tuile,
|
|
127
|
+
* posée dans une section comme une tuile ordinaire. Il ne range que des
|
|
128
|
+
* fonctionnalités (les autres tuiles sont déjà courtes), et celles qu'il tient
|
|
129
|
+
* comptent comme posées sur l'accueil : une fonctionnalité n'est jamais à deux
|
|
130
|
+
* endroits à la fois.
|
|
160
131
|
*/
|
|
161
132
|
export const homeFolderSchema = z.object({
|
|
162
133
|
/** Discriminant : c'est lui qui distingue un dossier d'un raccourci. */
|
|
@@ -170,28 +141,11 @@ export const homeFolderSchema = z.object({
|
|
|
170
141
|
export type HomeFolder = z.infer<typeof homeFolderSchema>;
|
|
171
142
|
|
|
172
143
|
/**
|
|
173
|
-
* Une tuile de l'accueil
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* « raccourcis ») et ne tenaient qu'une sorte de tuile. Ça obligeait à choisir
|
|
179
|
-
* le genre **avant** d'avoir quelque chose à poser, à ouvrir une popup pour
|
|
180
|
-
* ajouter une section, et à trois sélecteurs d'ajout différents. Une section
|
|
181
|
-
* n'est plus qu'une rangée de tuiles ; c'est la tuile qui sait ce qu'elle est.
|
|
182
|
-
*
|
|
183
|
-
* ## Chaque tuile garde l'écriture qu'elle avait
|
|
184
|
-
*
|
|
185
|
-
* Un appareil et une fonctionnalité **sont** leur identifiant (l'entité vit
|
|
186
|
-
* ailleurs) ; un raccourci et un dossier portent l'objet lui-même, parce que
|
|
187
|
-
* rien d'autre ne les décrit. Les deux familles d'identifiants ne peuvent pas
|
|
188
|
-
* se confondre — les fonctionnalités forment un enum fermé, les appareils sont
|
|
189
|
-
* des UUID — et c'est {@link homeTileKind} qui tranche, en un seul endroit.
|
|
190
|
-
*
|
|
191
|
-
* Conséquence utile : les dispositions écrites avant l'unification restent
|
|
192
|
-
* valides telles quelles. Leurs sections portent encore un champ `kind`, qui
|
|
193
|
-
* tombe à la lecture comme n'importe quelle clé inconnue, et la première
|
|
194
|
-
* écriture le fait disparaître. Rien à migrer, rien à rattraper au chargement.
|
|
144
|
+
* Une tuile de l'accueil : appareil, fonctionnalité, raccourci ou dossier. Un
|
|
145
|
+
* appareil et une fonctionnalité SONT leur identifiant ; un raccourci et un
|
|
146
|
+
* dossier portent l'objet. Les deux familles d'identifiants ne se confondent
|
|
147
|
+
* pas (enum fermé contre UUID), et {@link homeTileKind} tranche en un seul
|
|
148
|
+
* endroit.
|
|
195
149
|
*/
|
|
196
150
|
export const homeTileSchema = z.union([
|
|
197
151
|
homeFolderSchema,
|
|
@@ -212,11 +166,8 @@ export type HomeTileKind = 'device' | 'feature' | 'shortcut' | 'folder';
|
|
|
212
166
|
export const HOME_FEATURE_IDS = nativeHomeFeatureIdSchema.options;
|
|
213
167
|
|
|
214
168
|
/**
|
|
215
|
-
* Le genre d'une tuile.
|
|
216
|
-
*
|
|
217
|
-
* **Le seul endroit qui connaisse la forme de l'union** : tout le reste passe
|
|
218
|
-
* par lui ou par les gardes ci-dessous, donc changer la représentation ne se
|
|
219
|
-
* paye qu'ici.
|
|
169
|
+
* Le genre d'une tuile. Le seul endroit qui connaisse la forme de l'union :
|
|
170
|
+
* changer la représentation ne se paye qu'ici.
|
|
220
171
|
*/
|
|
221
172
|
export function homeTileKind(tile: HomeTile): HomeTileKind {
|
|
222
173
|
if (typeof tile !== 'string') return 'kind' in tile ? 'folder' : 'shortcut';
|
|
@@ -254,31 +205,19 @@ const sectionBase = {
|
|
|
254
205
|
id: z.string().min(1).max(64),
|
|
255
206
|
/** User-chosen heading; absent → the section renders untitled on the home. */
|
|
256
207
|
title: z.string().max(40).optional(),
|
|
257
|
-
/**
|
|
258
|
-
* The section can be folded away from the home.
|
|
259
|
-
*
|
|
260
|
-
* Absent (the default) → it always shows, and there is nothing to click:
|
|
261
|
-
* a chevron on a section nobody wants to fold is one more thing to ignore.
|
|
262
|
-
*/
|
|
208
|
+
/** The section can be folded away. Absent = always shown, no chevron. */
|
|
263
209
|
collapsible: z.boolean().optional(),
|
|
264
210
|
/**
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* Only meaningful alongside `collapsible` — a section that cannot be
|
|
268
|
-
* unfolded but starts folded would simply be invisible. The home enforces
|
|
269
|
-
* that pairing rather than trusting the flag on its own.
|
|
211
|
+
* Starts folded. Only meaningful alongside `collapsible`; the home enforces
|
|
212
|
+
* the pairing rather than trusting the flag on its own.
|
|
270
213
|
*/
|
|
271
214
|
collapsed: z.boolean().optional()
|
|
272
215
|
};
|
|
273
216
|
|
|
274
217
|
/**
|
|
275
|
-
* Une section : une rangée ordonnée de tuiles
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* fonctionnalité et un raccourci cohabitent dans la même — mais par son seul
|
|
279
|
-
* `id`. Une ligne peut donc mêler une carte de pleine hauteur et des cartes
|
|
280
|
-
* courtes : c'est assumé, la grille aligne les hauts et laisse les cartes
|
|
281
|
-
* courtes à leur taille.
|
|
218
|
+
* Une section : une rangée ordonnée de tuiles de n'importe quels genres,
|
|
219
|
+
* distinguée par son seul `id`. Une ligne peut mêler une carte de pleine
|
|
220
|
+
* hauteur et des cartes courtes.
|
|
282
221
|
*/
|
|
283
222
|
export const homeSectionSchema = z.object({
|
|
284
223
|
...sectionBase,
|