@duplojs/agents-kit 0.1.0-beta.7 → 0.1.0-beta.9
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/README.md +17 -9
- package/dist/bin/index.mjs +2 -2
- package/dist/bin/skills/index.d.ts +5 -0
- package/dist/bin/skills/index.mjs +11 -0
- package/dist/bin/{installSkill/index.d.ts → skills/install.d.ts} +1 -1
- package/dist/bin/{installSkill/index.mjs → skills/install.mjs} +2 -2
- package/dist/bin/{installSkill → skills}/skill.d.ts +7 -0
- package/dist/bin/{installSkill → skills}/skill.mjs +10 -8
- package/dist/bin/skills/update.d.ts +2 -0
- package/dist/bin/skills/update.mjs +19 -0
- package/docs/API/10-lang/10-common/10-asyncLoop.ts +4 -6
- package/docs/API/10-lang/10-common/10-asyncLoopAccumulator.ts +40 -0
- package/docs/API/10-lang/10-common/10-loop.ts +5 -7
- package/docs/API/10-lang/10-common/10-loopAccumulator.ts +35 -0
- package/docs/API/10-lang/10-common/10-loopAccumulatorFrom.ts +29 -0
- package/docs/API/10-lang/10-common/index.llms.md +14 -2
- package/docs/API/10-lang/10-generator/10-asyncLoop.ts +11 -20
- package/docs/API/10-lang/10-generator/10-asyncLoopAccumulator.ts +40 -0
- package/docs/API/10-lang/10-generator/10-loop.ts +11 -20
- package/docs/API/10-lang/10-generator/10-loopAccumulator.ts +33 -0
- package/docs/API/10-lang/10-generator/10-loopAccumulatorFrom.ts +30 -0
- package/docs/API/10-lang/10-generator/index.llms.md +14 -2
- package/docs/API/10-lang/10-object/10-discriminate.ts +84 -37
- package/docs/API/10-lang/10-object/index.llms.md +1 -5
- package/docs/API/20-server/10-file/10-appendFile.ts +50 -0
- package/docs/API/20-server/10-file/10-appendTextFile.ts +49 -0
- package/docs/API/20-server/10-file/10-copy.ts +50 -0
- package/docs/API/20-server/10-file/10-createEntryInterface.ts +13 -0
- package/docs/API/20-server/10-file/10-createFileInterface.ts +22 -0
- package/docs/API/20-server/10-file/10-createFolderInterface.ts +20 -0
- package/docs/API/20-server/10-file/10-createUnknownEntryInterface.ts +13 -0
- package/docs/API/20-server/10-file/10-ensureDirectory.ts +39 -0
- package/docs/API/20-server/10-file/10-ensureFile.ts +40 -0
- package/docs/API/20-server/10-file/10-exists.ts +36 -0
- package/docs/API/20-server/10-file/10-link.ts +51 -0
- package/docs/API/20-server/10-file/10-linkStat.ts +37 -0
- package/docs/API/20-server/10-file/10-makeDirectory.ts +41 -0
- package/docs/API/20-server/10-file/10-makeTemporaryDirectory.ts +35 -0
- package/docs/API/20-server/10-file/10-makeTemporaryFile.ts +40 -0
- package/docs/API/20-server/10-file/10-move.ts +51 -0
- package/docs/API/20-server/10-file/10-readDirectory.ts +52 -0
- package/docs/API/20-server/10-file/10-readFile.ts +38 -0
- package/docs/API/20-server/10-file/10-readJsonFile.ts +38 -0
- package/docs/API/20-server/10-file/10-readLink.ts +37 -0
- package/docs/API/20-server/10-file/10-readTextFile.ts +38 -0
- package/docs/API/20-server/10-file/10-realPath.ts +36 -0
- package/docs/API/20-server/10-file/10-relocate.ts +51 -0
- package/docs/API/20-server/10-file/10-remove.ts +40 -0
- package/docs/API/20-server/10-file/10-rename.ts +43 -0
- package/docs/API/20-server/10-file/10-setMode.ts +37 -0
- package/docs/API/20-server/10-file/10-setOwner.ts +38 -0
- package/docs/API/20-server/10-file/10-setTime.ts +42 -0
- package/docs/API/20-server/10-file/10-stat.ts +37 -0
- package/docs/API/20-server/10-file/10-symlink.ts +49 -0
- package/docs/API/20-server/10-file/10-truncate.ts +41 -0
- package/docs/API/20-server/10-file/10-walkDirectory.ts +75 -0
- package/docs/API/20-server/10-file/10-writeFile.ts +50 -0
- package/docs/API/20-server/10-file/10-writeJsonFile.ts +41 -0
- package/docs/API/20-server/10-file/10-writeTextFile.ts +49 -0
- package/docs/API/20-server/10-file/index.llms.md +144 -0
- package/docs/API/20-server/10-file/index.md +3 -0
- package/docs/API/20-server/20-common/10-environmentVariable.ts +57 -0
- package/docs/API/20-server/20-common/10-environmentVariableOrThrow.ts +21 -0
- package/docs/API/20-server/20-common/10-exitProcess.ts +9 -0
- package/docs/API/20-server/20-common/10-getCurrentDirectoryName.ts +10 -0
- package/docs/API/20-server/20-common/10-getCurrentWorkDirectory.ts +10 -0
- package/docs/API/20-server/20-common/10-getCurrentWorkDirectoryOrThrow.ts +10 -0
- package/docs/API/20-server/20-common/10-getProcessArguments.ts +10 -0
- package/docs/API/20-server/20-common/10-setCurrentWorkingDirectory.ts +14 -0
- package/docs/API/20-server/20-common/10-setCurrentWorkingDirectoryOrThrow.ts +12 -0
- package/docs/API/20-server/20-common/index.llms.md +40 -0
- package/docs/API/20-server/20-common/index.md +3 -0
- package/docs/API/20-server/30-dataStructure/10-file.ts +14 -0
- package/docs/API/20-server/30-dataStructure/10-fileExist.ts +27 -0
- package/docs/API/20-server/30-dataStructure/10-folder.ts +14 -0
- package/docs/API/20-server/30-dataStructure/10-folderExist.ts +14 -0
- package/docs/API/20-server/30-dataStructure/10-mimeType.ts +14 -0
- package/docs/API/20-server/30-dataStructure/10-size.ts +32 -0
- package/docs/API/20-server/30-dataStructure/index.llms.md +28 -0
- package/docs/API/20-server/30-dataStructure/index.md +3 -0
- package/docs/API/20-server/40-command/10-create.ts +39 -0
- package/docs/API/20-server/40-command/10-createArgument.ts +39 -0
- package/docs/API/20-server/40-command/10-createArrayOption.ts +36 -0
- package/docs/API/20-server/40-command/10-createBooleanOption.ts +28 -0
- package/docs/API/20-server/40-command/10-createOption.ts +46 -0
- package/docs/API/20-server/40-command/10-exec.ts +51 -0
- package/docs/API/20-server/40-command/10-execOptions.ts +30 -0
- package/docs/API/20-server/40-command/index.llms.md +32 -0
- package/docs/API/20-server/40-command/index.md +3 -0
- package/docs/API/20-server/index.md +3 -0
- package/docs/API.llms.md +20 -0
- package/package.json +14 -14
- package/skills/fundamental/SKILL.md +33 -33
- package/skills/fundamental/references/composition.md +9 -25
- package/skills/fundamental/references/constraints-and-validation.md +14 -51
- package/skills/fundamental/references/examples/iterations.ts +112 -0
- package/skills/fundamental/references/examples/result-selection.ts +65 -0
- package/skills/fundamental/references/iterations.md +24 -0
- package/skills/fundamental/references/results-and-errors.md +20 -45
- package/docs/API/10-lang/10-object/10-deepDiscriminate.ts +0 -63
|
@@ -17,64 +17,64 @@ Si le chemin du routeur manque, le retrouver depuis le projet consommateur :
|
|
|
17
17
|
node --input-type=module -e 'import { fileURLToPath } from "node:url"; console.log(fileURLToPath(import.meta.resolve("@duplojs/agents-kit/docs/concepts.llms.md")))'
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Utiliser
|
|
20
|
+
## Utiliser les API DuploJS
|
|
21
21
|
|
|
22
|
-
Utiliser obligatoirement
|
|
22
|
+
Utiliser obligatoirement l'équivalent DuploJS avant une API native, tierce ou un nouvel utilitaire, y compris dans les tests. Chercher dans `lang` pour les primitives indépendantes de la plateforme, dans `server` pour les interactions avec l'environnement, puis dans les packages spécialisés.
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
- `@duplojs/server` : interactions avec l'environnement d'exécution serveur, notamment le système de fichiers. Par exemple, calculer un chemin avec `lang/path` et lire un fichier avec `server/file`.
|
|
26
|
-
- Pour un besoin spécialisé, rechercher le package et les API adaptés dans l'écosystème à partir de la documentation.
|
|
24
|
+
Importer le sous-export du domaine avec son namespace long, comme dans les exemples des références.
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
## Modifier à l'endroit responsable
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
Lire les contrats, définitions et appelants concernés ; intégrer le besoin dans la fonction, le modèle ou le module responsable. Limiter le remaniement au besoin, adapter les usages et vérifier les garanties conservées.
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
Privilégier fonctions pures, immutabilité et transformations explicites ; isoler les interactions avec l'environnement.
|
|
33
31
|
|
|
34
|
-
##
|
|
32
|
+
## Laisser les types s'inférer
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
Laisser TypeScript inférer les valeurs et les retours des implémentations. Annoter un retour uniquement pour résoudre une récursivité ; les signatures d'interfaces et les déclarations de fonctions décrivent des contrats, pas cette annotation dans l'implémentation.
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
## Composer les transformations
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
Si la fonction retournée par une forme curifiée ne déclare pas ses propres génériques, écrire l'appel de configuration directement dans l'étape de `DCommon.pipe` ou `asyncPipe` : son contexte permet l'inférence. Ne pas préparer cette étape dans une variable ni forcer son typage par génériques, annotations ou casts. Lire [Composition](references/composition.md) lors de l'emploi d'une forme curifiée pour distinguer les deux niveaux de génériques.
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
Écrire chaque argument du pipe sur sa propre ligne, avec virgule finale et fermeture séparée, même pour une donnée et une seule étape. Un appel simple peut rester direct ; préparer séparément les données de sources indépendantes avant de les combiner.
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
Choisir `DCommon.asyncPipe` pour attendre les étapes, ou `DEither.rightPipe` / `rightAsyncPipe` pour transmettre les valeurs des `Right` et arrêter au premier `Left`. À toute étape produisant des états à sélectionner, composer production et sélection dans un pipe interne à sa callback, avant le déballage par le pipe extérieur. Lire [Composition](references/composition.md) pour ces enchaînements de résultats.
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
## Remplacer les boucles impératives
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
Ne pas écrire de `for`, de `while` ni leurs variantes. Utiliser :
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
- `DArray.reduce` pour parcourir un tableau avec un accumulateur ; `DGenerator.reduce` pour un autre itérable.
|
|
49
|
+
- `DGenerator.asyncReduce` pour un parcours asynchrone, y compris sur un tableau.
|
|
50
|
+
- `DCommon.loop` pour une répétition conditionnelle ; `DCommon.asyncLoop` en asynchrone.
|
|
51
|
+
- `DCommon.loopAccumulator` et `DCommon.asyncLoopAccumulator` pour transporter un accumulateur entre les tours d’une répétition conditionnelle.
|
|
52
|
+
- `DGenerator.loop` et `DGenerator.asyncLoop` pour produire une séquence ; leurs variantes `loopAccumulator` transportent aussi un accumulateur.
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Dans les variantes `loopAccumulator`, transmettre l'état avec `next(nouvelAccumulateur)` ; `DGenerator.loop` et `DGenerator.asyncLoop` utilisent `next` pour émettre des valeurs. Dans `DCommon.loop` et `DCommon.asyncLoop`, `next` poursuit la boucle sans transmettre son argument au tour suivant. Terminer avec `exit`. Avant de créer ou remplacer une boucle, lire [Itérations](references/iterations.md) pour les contrats de ces callbacks. Pour une simple transformation ou sélection, utiliser directement `DArray.map` ou `DArray.filter`.
|
|
53
55
|
|
|
54
56
|
## Valider les entrées et conserver les garanties
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Préciser les contraintes utiles : bornes, entier, format, longueur minimale, maximale ou exacte. Une contrainte enrichit le type ; sa seule déclaration ne valide rien au runtime.
|
|
58
|
+
Valider les données externes avec une `DDataStructure`, via l'intégration du module récepteur lorsqu'elle existe. Décrire les contraintes utiles : bornes, entier, format ou longueur. Une contrainte déclarée ne valide rien au runtime ; dans les traitements internes, réutiliser les garanties établies.
|
|
59
59
|
|
|
60
|
-
Choisir
|
|
60
|
+
Choisir selon la preuve disponible :
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
- **Littéral ou garantie compatible** : déclaration sûre ou `DCommon.cast` ; `DCommon.infer` pour conserver aussi le littéral. Ces fonctions calculent une compatibilité statique, sans validation runtime.
|
|
63
|
+
- **Valeur runtime sans preuve** : validation ou fabrique sûre, puis traitement du `Either`.
|
|
64
|
+
- **Refus du compilateur** : vérifier l'inférence et l'origine de la garantie ; ne pas la fabriquer avec `as`, `!` ou un helper sans preuve.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
Lire [Contraintes et validation](references/constraints-and-validation.md) pour construire un contrat, une valeur contrainte ou résoudre un refus de compatibilité. Après chaque transformation, suivre les garanties restantes et exploiter les accès garantis sans garde redondante.
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
## Représenter les résultats et propager les erreurs
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
Utiliser `DEither.result` lorsqu'une fonction doit établir des états sans décider de leur caractère favorable ou défavorable. L'information distingue les états ; la valeur porte leur contexte. Tous ces `Result` sont des `Right` : `isRight` ne choisit pas un état particulier.
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
Lorsque la fonction décide d'une réussite ou d'un refus prévisible, les exprimer en `Right` ou `Left`, y compris pour les absences et refus techniques. Si une branche renvoie un `Either`, toutes en renvoient un ; utiliser `DEither.ok()` pour une réussite sans valeur.
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Chez le consommateur, sélectionner les issues qui autorisent la suite avec `keepAsRightByInformation` ; les autres deviennent des `Left` en conservant information et valeur. Utiliser un sélecteur exhaustif comme `keepAsRightSelection` ou `matchInformation` lorsqu'une nouvelle variante doit obliger à réexaminer la décision.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
`unwrapRight` extrait les valeurs des `Right` et conserve les `Left` : le pattern sélection → `unwrapRight` → garde `isLeft` permet un retour anticipé sûr. Propager ou adapter les refus dans les fonctions réutilisables. Lire [Résultats et erreurs](references/results-and-errors.md) pour choisir la sélection et l'extraction.
|
|
77
77
|
|
|
78
|
-
Réserver les `throw` explicites et `OrThrow` à la consommation finale du résultat par la frontière d'exécution
|
|
78
|
+
Les exceptions imprévues des dépendances ou du runtime remontent implicitement. Réserver les `throw` explicites et `OrThrow` à la consommation finale du résultat par la frontière d'exécution. Une fonction réutilisable n'ajoute pas de `throw`, même appelée depuis cette frontière. La rareté du refus ou son coût de propagation ne changent pas cette règle.
|
|
79
79
|
|
|
80
|
-
Avant d'adapter une API qui lève sur un refus prévisible ou d'introduire `OrThrow`, lire [Résultats et erreurs](references/results-and-errors.md)
|
|
80
|
+
Avant d'adapter une API qui lève sur un refus prévisible ou d'introduire `OrThrow`, lire [Résultats et erreurs](references/results-and-errors.md). Préférer les déclarations sûres pour les constantes connues.
|
|
@@ -1,35 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Composition
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Configurer dans le contexte du pipe
|
|
4
4
|
|
|
5
|
-
La forme directe reçoit
|
|
5
|
+
La forme directe reçoit donnée et paramètres ; la forme curifiée configure les paramètres puis reçoit la donnée. Une fonction déjà unaire peut être passée directement.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Si la fonction retournée ne déclare pas ses propres génériques, ses types sont fixés à la configuration. Écrire cet appel dans l'étape du pipe apporte le contexte d'inférence ; passer ensuite une variable déjà configurée à un pipe ne le rétablit pas. Ne pas forcer cette préparation par génériques, annotations ou casts.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Pour réutiliser le traitement, écrire une fonction recevant la donnée et construisant son pipe, ou utiliser la forme directe. L'[exemple de composition](examples/composition.ts) montre les deux formes, le format multiligne et une préparation à éviter.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Le pipe suit ici une même donnée : utilisateurs, utilisateurs actifs, noms. Une fonction déjà unaire de l'API peut être passée directement, comme `DString.trim`. Pour une forme curifiée, écrire son appel de configuration dans l'étape. Ne pas supposer que toutes les fonctions exposent les mêmes surcharges.
|
|
14
|
-
|
|
15
|
-
## Mise en forme des pipes
|
|
16
|
-
|
|
17
|
-
Écrire tous les appels de pipe sur plusieurs lignes, avec chaque argument sur sa propre ligne, une virgule après le dernier argument et la parenthèse de fermeture sur une ligne séparée. Ce format s'applique aussi à une donnée suivie d'une seule étape, comme dans le premier pipe de l'exemple. L'appel qui configure la forme curifiée occupe la ligne de l'étape ; une variable préparée auparavant ne le remplace pas.
|
|
18
|
-
|
|
19
|
-
## Plusieurs sources indépendantes
|
|
20
|
-
|
|
21
|
-
Quand plusieurs données indépendantes demandent chacune leur propre préparation, les nommer avant de les combiner. Cette préparation concerne les données ; les appels curifiés restent écrits dans les étapes de leur pipe.
|
|
22
|
-
|
|
23
|
-
Voir [la préparation de deux arguments indépendants](examples/independent-inputs.ts).
|
|
24
|
-
|
|
25
|
-
Le résultat de cet appel peut ensuite devenir l'entrée d'un autre pipe si plusieurs transformations se suivent.
|
|
11
|
+
Préparer les données de sources indépendantes avant de les combiner ; les configurations curifiées restent dans leur pipe. Voir [deux sources indépendantes](examples/independent-inputs.ts).
|
|
26
12
|
|
|
27
13
|
## Asynchronisme et résultats
|
|
28
14
|
|
|
29
|
-
`DCommon.asyncPipe` attend chaque étape
|
|
30
|
-
|
|
31
|
-
Voir [un enchaînement asynchrone](examples/async-composition.ts).
|
|
15
|
+
`DCommon.asyncPipe` attend chaque étape ; un seul `await` final suffit. Voir l'[exemple asynchrone](examples/async-composition.ts).
|
|
32
16
|
|
|
33
|
-
|
|
17
|
+
`DEither.rightPipe` et `rightAsyncPipe` transmettent les valeurs des `Right` et arrêtent au premier `Left`. Un pipe ordinaire ne s'arrête pas automatiquement sur un `Left`. Pour sélectionner un état produit par une étape, composer production et sélection dans un `innerPipe` ou `asyncInnerPipe`, avant de rendre le résultat au pipe extérieur. Une callback construisant un `pipe` ou `asyncPipe` convient aussi.
|
|
34
18
|
|
|
35
|
-
|
|
19
|
+
Ce montage s'applique à toute étape ou callback imbriquée. À l'étape extérieure suivante, le `Right` serait déjà déballé, sans son information. Voir la [sélection intermédiaire](examples/result-selection.ts).
|
|
@@ -1,61 +1,24 @@
|
|
|
1
|
-
# Contraintes
|
|
1
|
+
# Contraintes et validation
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Établir la garantie
|
|
4
4
|
|
|
5
|
-
Une contrainte exprime
|
|
5
|
+
Une contrainte exprime une propriété dans les types : borne, longueur ou format. Les intersections cumulent ces garanties ; leur déclaration seule ne valide ni ne transforme une valeur.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Aux frontières, décrire le contrat complet avec une `DDataStructure` et les contraintes du besoin. Fournir cette structure au module récepteur lorsqu'il propose une intégration ; sinon, valider explicitement et traiter le `Either`. Réutiliser ensuite les garanties établies. Voir [le contrat d'entrée](examples/input-validation.ts).
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Choisir la construction
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- **Valeur connue statiquement ou garantie compatible** : `DCommon.cast` calcule les implications statiques que TypeScript ne déduit pas seul. La cible vient du type attendu ; aucune validation runtime n'est effectuée. Voir [les conversions](examples/cast.ts).
|
|
12
|
+
- **Littéral à conserver** : `DCommon.infer` conserve aussi le type littéral et exige une entrée littérale. Voir [une propriété littérale](examples/infer.ts).
|
|
13
|
+
- **Valeur runtime sans preuve** : validation par une structure, un prédicat ou une fabrique sûre. Une simple `string` ou un simple `number` ne prouve pas de contrainte.
|
|
14
|
+
- **Constante spécialisée** : chercher une déclaration sûre du domaine avant `OrThrow`. Vérifier la surcharge : selon l'argument, elle peut retourner une valeur ou un `Either`. Voir [les chemins et Chrono](examples/safe-declarations.ts).
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
Un refus de `cast` indique une compatibilité non établie : vérifier l'inférence ou le support du calcul pour une contrainte personnalisée, puis choisir une validation si nécessaire. Ne pas fabriquer la preuve avec une assertion ou un helper.
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
Si un paramètre générique direct ne fournit pas la cible de `cast` ou `infer`, l'expliciter avec `satisfies` après l'appel. Ce contexte ne valide rien et n'est pas nécessaire partout : comparer [le paramètre générique](examples/generic-inference.ts) et [la propriété littérale](examples/infer.ts).
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
## Exploiter les garanties restantes
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
Suivre ce que chaque transformation préserve ou perd. Une longueur minimale garantit certains accès ; l'exploiter sans garde redondante. Elle ne garantit pas les indices au-delà du minimum, et n'enlève pas `undefined` du type propre aux éléments. Un filtre peut perdre cette garantie de longueur.
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`DCommon.cast` restitue la valeur sans validation runtime. Le type attendu donne la cible du calcul. Une garantie plus forte peut impliquer une garantie plus faible ; un littéral peut aussi fournir assez d'information.
|
|
24
|
-
|
|
25
|
-
Voir [les conversions de contraintes et les déclarations littérales](examples/cast.ts).
|
|
26
|
-
|
|
27
|
-
Ce calcul de types comble les implications que TypeScript ne déduit pas de lui-même. Il n'y a pas de calcul dynamique de validité lors de l'exécution. Une simple `string` ou un simple `number` provenant du runtime ne prouve pas une longueur, un format ou une borne.
|
|
28
|
-
|
|
29
|
-
Si `cast` émet une erreur, considérer que la compatibilité n'est pas établie. Une règle personnalisée peut ne pas avoir de support de calcul enregistré. Ne pas remplacer ce refus par `as`, une double assertion ou un helper qui fabrique la contrainte sans preuve. Vérifier l'inférence, valider par une structure ou un prédicat adapté, ou utiliser une autre construction sûre.
|
|
30
|
-
|
|
31
|
-
## `infer` : conserver également le littéral
|
|
32
|
-
|
|
33
|
-
Préférer `cast` dans le cas courant. Utiliser `DCommon.infer` lorsqu'une valeur connue statiquement doit garder son type littéral en plus des contraintes calculées. Cette fonction exige une entrée littérale ; elle ne sert pas à valider une donnée runtime.
|
|
34
|
-
|
|
35
|
-
Voir [la conservation du littéral dans une propriété](examples/infer.ts).
|
|
36
|
-
|
|
37
|
-
## Difficulté d'inférence : cible d'un paramètre générique direct
|
|
38
|
-
|
|
39
|
-
Lorsque `cast` ou `infer` est passé à un paramètre lui-même générique, TypeScript peut ne pas fournir une cible assez précise au calcul. Si le type attendu n'est pas inféré dans cette situation, l'expliciter avec `satisfies` immédiatement après l'appel. Ce traitement répond à une difficulté de contexte ; il n'est pas nécessaire à chaque utilisation de ces fonctions.
|
|
40
|
-
|
|
41
|
-
Voir [le calcul des contraintes avec un paramètre générique direct](examples/generic-inference.ts).
|
|
42
|
-
|
|
43
|
-
`satisfies` fournit le contexte du calcul ; il ne contourne pas la validation. Une propriété contrainte d'un objet générique peut déjà fournir ce contexte, comme dans [l'exemple de propriété littérale](examples/infer.ts) : ne pas ajouter ce contournement d'inférence partout.
|
|
44
|
-
|
|
45
|
-
## Déclarations spécialisées et Chrono
|
|
46
|
-
|
|
47
|
-
Les domaines proposent aussi des helpers pour les valeurs connues. Préférer ces déclarations sûres à `OrThrow`.
|
|
48
|
-
|
|
49
|
-
Voir [les déclarations de chemins et de valeurs Chrono](examples/safe-declarations.ts).
|
|
50
|
-
|
|
51
|
-
Vérifier la surcharge réellement utilisée : le type de l'argument détermine si la fonction retourne directement la valeur ou un `Either`.
|
|
52
|
-
|
|
53
|
-
## Les contraintes simplifient les opérations suivantes
|
|
54
|
-
|
|
55
|
-
Voir [les accès garantis après validation du tableau](examples/guaranteed-array-access.ts).
|
|
56
|
-
|
|
57
|
-
`MinElements<2>` garantit les indices `0` et `1` et l'existence du dernier élément. L'indice `2` reste incertain : un minimum de deux n'impose pas un maximum de deux. Les éléments garantis sont retournés directement, sans `Maybe` ni `undefined` ajouté pour une éventuelle absence. Une borne maximale ou une longueur exacte peut aussi permettre de prouver qu'un indice est absent.
|
|
58
|
-
|
|
59
|
-
Exploiter ces garanties sans garde redondante ni assertion. Respecter cependant le type propre aux éléments : un tableau dont les éléments peuvent eux-mêmes être `undefined` conserve cette possibilité. Après une transformation, suivre les contraintes du résultat ; un filtre peut supprimer des éléments et perdre une garantie de minimum.
|
|
60
|
-
|
|
61
|
-
Pour approfondir, rechercher les contraintes, les data structures, Chrono ou l'intégration du module concerné dans `docs/concepts.llms.md`, depuis la racine AgentKit indiquée par les instructions du projet.
|
|
24
|
+
Voir [les accès garantis](examples/guaranteed-array-access.ts) pour distinguer présence prouvée et accès incertain.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Remplacer les boucles par les primitives DuploJS.
|
|
3
|
+
*
|
|
4
|
+
* Accumulateurs immuables, sortie anticipée et répétitions conditionnelles.
|
|
5
|
+
*/
|
|
6
|
+
import * as DArray from "@duplojs/lang/array";
|
|
7
|
+
import * as DCommon from "@duplojs/lang/common";
|
|
8
|
+
import * as DGenerator from "@duplojs/lang/generator";
|
|
9
|
+
import * as DNumber from "@duplojs/lang/number";
|
|
10
|
+
|
|
11
|
+
interface RunningTotals {
|
|
12
|
+
readonly total: number;
|
|
13
|
+
readonly subtotals: readonly number[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const runningTotals = DCommon.pipe(
|
|
17
|
+
[2, -1, 4],
|
|
18
|
+
DArray.reduce(
|
|
19
|
+
DArray.reduceFrom<RunningTotals>({
|
|
20
|
+
total: 0,
|
|
21
|
+
subtotals: [],
|
|
22
|
+
}),
|
|
23
|
+
({ element, lastValue, nextWithObject }) => {
|
|
24
|
+
const total = DNumber.sum([lastValue.total, element]);
|
|
25
|
+
return nextWithObject(lastValue, {
|
|
26
|
+
total,
|
|
27
|
+
subtotals: DArray.push(lastValue.subtotals, total),
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Résultat : { total: 5, subtotals: [2, 1, 5] }. Chaque étape dépend du total précédent.
|
|
34
|
+
// L'accumulateur initial et la source ne sont pas mutés.
|
|
35
|
+
type _CheckRunningTotals = DCommon.ExpectType<typeof runningTotals, RunningTotals, "strict">;
|
|
36
|
+
|
|
37
|
+
const totalOrStop = DCommon.pipe(
|
|
38
|
+
[2, -1, 4],
|
|
39
|
+
DArray.reduce(
|
|
40
|
+
0,
|
|
41
|
+
({ element, lastValue, next, exit }) => element < 0
|
|
42
|
+
? exit("negative-value")
|
|
43
|
+
: next(DNumber.sum([lastValue, element])),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Résultat : "negative-value" ; 4 n'est pas traité. exit peut changer le type du résultat.
|
|
48
|
+
type _CheckTotalOrStop = DCommon.ExpectType<typeof totalOrStop, number | string, "strict">;
|
|
49
|
+
|
|
50
|
+
declare function loadAmount(id: string): Promise<number>;
|
|
51
|
+
|
|
52
|
+
const loadedTotal = await DCommon.asyncPipe(
|
|
53
|
+
["first", "second"],
|
|
54
|
+
DGenerator.asyncReduce(
|
|
55
|
+
0,
|
|
56
|
+
async({ item, lastValue, next }) => {
|
|
57
|
+
const amount = await loadAmount(item);
|
|
58
|
+
return next(DNumber.sum([lastValue, amount]));
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Chaque chargement est attendu avant le suivant ; la source peut aussi être un AsyncIterable.
|
|
64
|
+
type _CheckLoadedTotal = DCommon.ExpectType<typeof loadedTotal, number, "strict">;
|
|
65
|
+
|
|
66
|
+
const reachedValue = DCommon.loopAccumulator(0, ({ accumulator, next, exit }) => {
|
|
67
|
+
// La condition est vérifiée avant le traitement, comme dans un while.
|
|
68
|
+
if (accumulator >= 3) {
|
|
69
|
+
return exit(accumulator);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return next(DNumber.sum([accumulator, 1]));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Résultat : 3. L'état circule dans l'accumulateur de la boucle.
|
|
76
|
+
type _CheckReachedValue = DCommon.ExpectType<typeof reachedValue, number, "strict">;
|
|
77
|
+
|
|
78
|
+
declare function readBatchSize(index: number): Promise<number>;
|
|
79
|
+
|
|
80
|
+
const processedTotal = await DCommon.asyncLoopAccumulator(0, async({
|
|
81
|
+
count,
|
|
82
|
+
accumulator,
|
|
83
|
+
next,
|
|
84
|
+
exit,
|
|
85
|
+
}) => {
|
|
86
|
+
const size = await readBatchSize(count);
|
|
87
|
+
|
|
88
|
+
// La première taille nulle termine la lecture ; exit retourne le total accumulé.
|
|
89
|
+
if (size === 0) {
|
|
90
|
+
return exit(accumulator);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return next(DNumber.sum([accumulator, size]));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
type _CheckProcessedTotal = DCommon.ExpectType<typeof processedTotal, number, "strict">;
|
|
97
|
+
|
|
98
|
+
const emitted = DArray.from(DGenerator.loop(({ count, next, exit }) => count === 3
|
|
99
|
+
? exit("done")
|
|
100
|
+
: next(count)));
|
|
101
|
+
|
|
102
|
+
// DGenerator.loop émet les sorties de next puis celles de exit.
|
|
103
|
+
type _CheckEmitted = DCommon.ExpectType<typeof emitted, readonly (number | "done")[], "strict">;
|
|
104
|
+
|
|
105
|
+
const emittedWithAccumulator = DArray.from(
|
|
106
|
+
DGenerator.loopAccumulator(0, ({ accumulator, count, next, exit }) => count === 3
|
|
107
|
+
? exit(accumulator)
|
|
108
|
+
: next(accumulator + 1, accumulator)),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Chaque sortie peut être produite au même moment que le nouvel accumulateur est choisi.
|
|
112
|
+
type _CheckEmittedWithAccumulator = DCommon.ExpectType<typeof emittedWithAccumulator, readonly number[], "strict">;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @title Sélectionner les résultats selon l'opération.
|
|
3
|
+
*
|
|
4
|
+
* Constater un état, sélectionner les issues utiles et conserver les refus.
|
|
5
|
+
*/
|
|
6
|
+
import * as DCommon from "@duplojs/lang/common";
|
|
7
|
+
import * as DEither from "@duplojs/lang/either";
|
|
8
|
+
|
|
9
|
+
interface Item {
|
|
10
|
+
id: string;
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function classifyItem(item: Item) {
|
|
15
|
+
if (item.enabled) {
|
|
16
|
+
return DEither.result("ready", item);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return DEither.result("blocked", item);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function prepareItem(item: Item) {
|
|
23
|
+
const selected = DCommon.pipe(
|
|
24
|
+
item,
|
|
25
|
+
classifyItem,
|
|
26
|
+
DEither.keepAsRightByInformation("ready"),
|
|
27
|
+
DEither.unwrapRight,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// unwrapRight conserve le Left : la garde peut suivre l'extraction.
|
|
31
|
+
if (DEither.isLeft(selected)) {
|
|
32
|
+
return selected;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return DEither.right("prepared", selected);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type _CheckPrepared = DCommon.ExpectType<
|
|
39
|
+
ReturnType<typeof prepareItem>,
|
|
40
|
+
DEither.Left<"blocked", Item> | DEither.Right<"prepared", Item>,
|
|
41
|
+
"strict"
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
declare function loadItem(id: string): Promise<DEither.Maybe<Item>>;
|
|
45
|
+
declare function saveItem(item: Item): Promise<DEither.Right<"saved", Item>>;
|
|
46
|
+
|
|
47
|
+
const saved = await DEither.rightAsyncPipe(
|
|
48
|
+
loadItem("item"),
|
|
49
|
+
// L'inner pipe intervient après la recherche, à une étape intermédiaire.
|
|
50
|
+
DCommon.innerPipe(
|
|
51
|
+
classifyItem,
|
|
52
|
+
DEither.keepAsRightSelection({
|
|
53
|
+
ready: true,
|
|
54
|
+
blocked: false,
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
// L'étape extérieure reçoit l'Item déballé ; un Left évite cet appel.
|
|
58
|
+
(item) => saveItem(item),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
type _CheckSaved = DCommon.ExpectType<
|
|
62
|
+
typeof saved,
|
|
63
|
+
DEither.None | DEither.Left<"blocked", Item> | DEither.Right<"saved", Item>,
|
|
64
|
+
"strict"
|
|
65
|
+
>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Itérations
|
|
2
|
+
|
|
3
|
+
## Parcourir une collection
|
|
4
|
+
|
|
5
|
+
Configurer `DArray.reduce(initial, callback)` dans l'étape d'un `DCommon.pipe` recevant le tableau. La callback reçoit `element`, `index` et `lastValue`, initialement égal à l'accumulateur de départ :
|
|
6
|
+
|
|
7
|
+
- `next(nouvelÉtat)` poursuit ; `next(lastValue)` ignore l'élément.
|
|
8
|
+
- `exit(résultat)` arrête immédiatement et peut changer le type de sortie.
|
|
9
|
+
|
|
10
|
+
Sans `exit`, le résultat est le dernier accumulateur ; une source vide retourne l'initial. Pour un état structuré, utiliser `DArray.reduceFrom<Type>(valeur)` et construire les nouveaux états sans mutation.
|
|
11
|
+
|
|
12
|
+
Pour un autre itérable, utiliser `DGenerator.reduce`. `DGenerator.asyncReduce` accepte tableaux, itérables synchrones ou asynchrones et attend chaque étape. Leurs callbacks reçoivent `item` ; initialiser leurs états structurés avec `DGenerator.reduceFrom`.
|
|
13
|
+
|
|
14
|
+
## Répéter jusqu'à une condition
|
|
15
|
+
|
|
16
|
+
`DCommon.loop` fournit `count` depuis zéro. `next()` continue et `exit(valeur)` retourne le résultat final. La donnée optionnelle de `next` n'est pas transmise au tour suivant ; utiliser `DCommon.loopAccumulator(initial, callback)` lorsque les tours doivent partager un état. Sa callback reçoit `accumulator`, `count`, `next(nouvelAccumulateur)` et `exit(résultat)`. `DCommon.asyncLoop` et `DCommon.asyncLoopAccumulator` offrent les mêmes comportements en attendant la callback asynchrone.
|
|
17
|
+
|
|
18
|
+
`DGenerator.loop(callback)` produit une séquence : chaque `next(...valeurs)` émet les valeurs définies et poursuit la boucle ; `exit(...valeurs)` émet sa dernière séquence puis termine. La callback reçoit `count`, pas la valeur du tour précédent. `DGenerator.asyncLoop` attend les callbacks et résout les valeurs promises avant de les émettre.
|
|
19
|
+
|
|
20
|
+
Les variantes `DGenerator.loopAccumulator(initial, callback)` et `DGenerator.asyncLoopAccumulator(initial, callback)` font circuler un accumulateur avec `next(nouvelAccumulateur, ...valeursÀÉmettre)`. `exit(...valeursÀÉmettre)` termine en émettant les dernières valeurs. Utiliser `DGenerator.loopAccumulatorFrom<Type>(valeur)` pour préserver un type d'accumulateur structuré ou une union.
|
|
21
|
+
|
|
22
|
+
Conserver la condition avant le traitement d'un `while`, après celui d'un `do...while`. Pour une répétition par compteur, utiliser aussi `loop`, sans fabriquer un tableau.
|
|
23
|
+
|
|
24
|
+
Voir les [exemples typés](examples/iterations.ts), avec accumulateurs, sorties anticipées et variantes asynchrones.
|
|
@@ -1,59 +1,34 @@
|
|
|
1
|
-
# Résultats
|
|
1
|
+
# Résultats et erreurs
|
|
2
2
|
|
|
3
|
-
## Choisir
|
|
3
|
+
## Choisir le contrat
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Une fonction qui établit des états sans décider lesquels conviennent à l'appelant produit des `DEither.Result` nommés. Tous sont des `Right` ; les distinguer par leur information.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Une fonction qui décide d'une réussite ou d'un refus prévisible les exprime en `Right` ou `Left`, y compris pour les absences et refus techniques. Toutes ses branches renvoient un `Either` ; `DEither.ok()` représente une réussite sans valeur.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Sélectionner et poursuivre
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
La frontière d'exécution consomme le résultat après le retour des fonctions appelées et décide de sa conséquence finale. C'est à ce stade que le code applicatif peut écrire un `throw` ou utiliser `OrThrow` pour interrompre l'exécution. Déclarer une fonction au niveau d'un module ou l'appeler depuis un point d'entrée ne lui donne pas cette responsabilité.
|
|
18
|
-
|
|
19
|
-
Exemples : initialisation du programme, callback d'une commande ou handler d'une route.
|
|
20
|
-
|
|
21
|
-
```text
|
|
22
|
-
entrée externe
|
|
23
|
-
→ validation par les schémas du module
|
|
24
|
-
→ point d'entrée du traitement
|
|
25
|
-
→ fonction applicative
|
|
26
|
-
→ sous-fonction : renvoie les issues prévues en Right ou Left
|
|
27
|
-
← résultat typé
|
|
28
|
-
← résultat typé
|
|
29
|
-
→ le point d'entrée décide de l'effet final
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Les fonctions applicatives et leurs sous-fonctions renvoient les refus prévisibles dans leur contrat de retour, sans créer leurs propres `throw`. Les exceptions déjà émises par les dépendances ou le runtime remontent implicitement à travers ces appels ; cette propagation ne demande pas d'ajouter un `throw`.
|
|
33
|
-
|
|
34
|
-
À cette frontière, interpréter le `Left` selon le contrat d'exécution : par exemple produire une réponse HTTP, un diagnostic de commande ou une exception adaptée. Le fait qu'un appelant ignore le retour d'une fonction ne suffit pas à autoriser cette fonction à lever une exception.
|
|
11
|
+
| Intention du consommateur | Outil |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| Autoriser certaines informations et refuser toutes les autres, même futures | `keepAsRightByInformation` |
|
|
14
|
+
| Décider explicitement du statut de chaque variante | `keepAsRightSelection` |
|
|
15
|
+
| Décider explicitement des variantes d'une garde | `isSelected` |
|
|
16
|
+
| Traiter chaque information | `matchInformation` |
|
|
35
17
|
|
|
36
|
-
|
|
18
|
+
Les sélecteurs exhaustifs imposent de revoir la décision lorsqu'une variante apparaît. Une sélection partielle ou un repli exprime au contraire un comportement commun voulu.
|
|
37
19
|
|
|
38
|
-
|
|
20
|
+
Le reclassement conserve information et valeur. `unwrapRight` extrait les valeurs acceptées et conserve les `Left` : une garde `isLeft` peut ensuite retourner le refus. Si le contrat garantit uniquement des `Right` admis, l'extraction peut être directe. Voir [la sélection et le retour anticipé](examples/result-selection.ts).
|
|
39
21
|
|
|
40
|
-
|
|
41
|
-
| --- | --- |
|
|
42
|
-
| Constante dont la validité est prouvable | `cast`, `infer`, helper de déclaration ou surcharge littérale sûre |
|
|
43
|
-
| Vérification d'une donnée externe ou valeur runtime sans preuve | Validation ou fabrique sûre, puis traitement du `Either` |
|
|
44
|
-
| Refus prévisible d'une fonction réutilisable, métier ou technique | Renvoyer, propager ou adapter le `Left` |
|
|
45
|
-
| Exception de défaillance imprévue émise par une dépendance ou le runtime | La laisser remonter implicitement ; propager le résultat si elle a été capturée |
|
|
46
|
-
| Point d'entrée décidant de l'effet final d'un résultat | Interpréter le `Left`, puis produire la réponse, la fin d'exécution ou l'exception adaptée |
|
|
47
|
-
| Test affirmant un résultat attendu | `OrThrow` permet de faire échouer le test si le résultat diffère |
|
|
22
|
+
Pour sélectionner à une étape d'un `rightPipe` avant son déballage automatique, lire [Composition](composition.md#asynchronisme-et-résultats).
|
|
48
23
|
|
|
49
|
-
|
|
24
|
+
## Propager ou adapter un refus
|
|
50
25
|
|
|
51
|
-
|
|
26
|
+
Une fonction réutilisable transmet ses refus à l'appelant, qui peut les propager, les adapter ou récupérer. Une absence exploitable reste un résultat, même issue d'une I/O. Voir [la propagation d'une erreur de lecture](examples/error-propagation.ts).
|
|
52
27
|
|
|
53
|
-
|
|
28
|
+
Préférer une API sûre. Si une API lève sur un refus documenté, `safeCallback` ou `asyncSafeCallback` permettent sa capture ; examiner la cause avant de la traduire. Une défaillance imprévue déjà levée remonte implicitement. Si elle est capturée, conserver sa cause dans le résultat jusqu'à la consommation finale, sans la requalifier en refus métier ni la relancer dans l'adaptation.
|
|
54
29
|
|
|
55
|
-
|
|
30
|
+
## Consommer le résultat final
|
|
56
31
|
|
|
57
|
-
|
|
32
|
+
Réserver `throw` et `OrThrow` au code qui décide de la conséquence finale : réponse, diagnostic ou interruption du programme. Une fonction appelée depuis cette frontière reste responsable de transmettre ses résultats.
|
|
58
33
|
|
|
59
|
-
|
|
34
|
+
Au démarrage, une configuration invalide peut empêcher l'exécution : voir [l'initialisation](examples/environment-initialization.ts). Dans un test, `OrThrow` peut affirmer un résultat ou préparer une fixture ; le code testé conserve ses refus typés. Pour les constantes prouvables, préférer les [déclarations sûres](examples/safe-declarations.ts).
|