fazer-lang 2.2.1 → 2.3.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/README.md +29 -42
- package/docs/README.md +26 -0
- package/docs/examples.md +60 -0
- package/docs/getting-started.md +46 -0
- package/docs/stdlib.md +139 -0
- package/docs/syntax.md +86 -0
- package/fazer.js +163 -1
- package/package.json +13 -5
- package/tools/announce.fz +48 -0
package/README.md
CHANGED
|
@@ -1,62 +1,49 @@
|
|
|
1
1
|
# Fazer
|
|
2
2
|
|
|
3
|
-
**Fazer**
|
|
3
|
+
**Fazer** — Le langage de script nouvelle génération par **L'EMPRISE**.
|
|
4
|
+
|
|
5
|
+
Conçu pour l'automatisation, la sécurité et le traitement de données, Fazer combine une syntaxe concise avec une bibliothèque standard "batteries included".
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Installez Fazer globalement via npm :
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
12
|
npm install -g fazer-lang
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
Run the REPL (interactive mode):
|
|
15
|
+
## Utilisation Rapide
|
|
16
16
|
|
|
17
|
+
Lancer le REPL (mode interactif) :
|
|
17
18
|
```bash
|
|
18
19
|
fazer
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
Exécuter un script :
|
|
23
23
|
```bash
|
|
24
|
-
fazer
|
|
24
|
+
fazer mon_script.fz
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
files := ls(".")
|
|
46
|
-
println("Found " + len(files) + " files.")
|
|
47
|
-
|
|
48
|
-
case exists("secret.txt")
|
|
49
|
-
true →
|
|
50
|
-
content := readText("secret.txt")
|
|
51
|
-
println("Secret content: " + content)
|
|
52
|
-
end
|
|
53
|
-
else → end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
main()
|
|
58
|
-
```
|
|
27
|
+
## Documentation
|
|
28
|
+
|
|
29
|
+
La documentation complète est disponible dans le dossier [`docs/`](./docs/README.md).
|
|
30
|
+
|
|
31
|
+
* [Guide de Démarrage](./docs/getting-started.md)
|
|
32
|
+
* [Syntaxe du Langage](./docs/syntax.md)
|
|
33
|
+
* [Bibliothèque Standard (Stdlib)](./docs/stdlib.md)
|
|
34
|
+
* [Exemples](./docs/examples.md)
|
|
35
|
+
|
|
36
|
+
## Fonctionnalités Clés
|
|
37
|
+
|
|
38
|
+
* **Pipe Operator (`->>`)** : Enchaînez les opérations proprement.
|
|
39
|
+
* **Pattern Matching (`case`)** : Contrôle de flux expressif.
|
|
40
|
+
* **Sécurité Intégrée** : Chiffrement AES-256-GCM et SHA256 natifs.
|
|
41
|
+
* **Réseau & Web** : Client HTTP `fetch`, serveur web `server`, et module `discord`.
|
|
42
|
+
* **Système de Fichiers** : Manipulation simple et puissante.
|
|
43
|
+
* **Automation** : Gestion du presse-papier (`clipboard`), menus interactifs, et persistance (`db`).
|
|
44
|
+
* **Extensible** : Système de modules `import`.
|
|
59
45
|
|
|
60
|
-
##
|
|
46
|
+
## Copyright
|
|
61
47
|
|
|
62
|
-
|
|
48
|
+
© 2024 **L'EMPRISE**. Tous droits réservés.
|
|
49
|
+
Distribué sous licence MIT.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Documentation Fazer
|
|
2
|
+
|
|
3
|
+
Bienvenue dans la documentation officielle du langage **Fazer**.
|
|
4
|
+
|
|
5
|
+
## Table des Matières
|
|
6
|
+
|
|
7
|
+
1. **[Guide de Démarrage](./getting-started.md)**
|
|
8
|
+
* Installation
|
|
9
|
+
* Votre premier script
|
|
10
|
+
* Mode interactif (REPL)
|
|
11
|
+
|
|
12
|
+
2. **[Syntaxe du Langage](./syntax.md)**
|
|
13
|
+
* Variables et Types
|
|
14
|
+
* Opérateur Pipe (`->>`)
|
|
15
|
+
* Contrôle de Flux (`case`)
|
|
16
|
+
* Fonctions
|
|
17
|
+
|
|
18
|
+
3. **[Bibliothèque Standard (Stdlib)](./stdlib.md)**
|
|
19
|
+
* E/S Console (`println`, `ask`...)
|
|
20
|
+
* Système de Fichiers (`readText`, `writeText`...)
|
|
21
|
+
* Chiffrement (`encText`, `sha256`...)
|
|
22
|
+
* Réseau & Web (`server`, `fetch`, `discord`...)
|
|
23
|
+
* Utilitaires (`json`, `exec`...)
|
|
24
|
+
|
|
25
|
+
4. **[Exemples](./examples.md)**
|
|
26
|
+
* Scripts complets pour apprendre par l'exemple.
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Exemples Fazer
|
|
2
|
+
|
|
3
|
+
## 1. Hello World
|
|
4
|
+
|
|
5
|
+
```fazer
|
|
6
|
+
"Hello World" ->> println
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 2. Demander le nom
|
|
10
|
+
|
|
11
|
+
```fazer
|
|
12
|
+
"Quel est votre nom ? " ->> ask ->> println
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 3. Chiffrement de Fichier (Logique simplifiée)
|
|
16
|
+
|
|
17
|
+
Ce script montre comment on pourrait structurer un outil de chiffrement.
|
|
18
|
+
|
|
19
|
+
```fazer
|
|
20
|
+
fn main () {
|
|
21
|
+
"1. Chiffrer" ->> println
|
|
22
|
+
"2. Déchiffrer" ->> println
|
|
23
|
+
"Choix : " ->> ask ->> case {
|
|
24
|
+
"1" : {
|
|
25
|
+
"Fichier à chiffrer : " ->> ask ->> enc_flow
|
|
26
|
+
}
|
|
27
|
+
"2" : {
|
|
28
|
+
"Fichier à déchiffrer : " ->> ask ->> dec_flow
|
|
29
|
+
}
|
|
30
|
+
_ : {
|
|
31
|
+
"Choix invalide" ->> println
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn enc_flow (path) {
|
|
37
|
+
path ->> exists ->> case {
|
|
38
|
+
true : {
|
|
39
|
+
"Mot de passe : " ->> ask ->> pass
|
|
40
|
+
path ->> readText ->> encText(pass) ->> saveText(path)
|
|
41
|
+
"Fichier chiffré !" ->> println
|
|
42
|
+
}
|
|
43
|
+
_ : { "Fichier introuvable" ->> println }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Lancer le main
|
|
48
|
+
main()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 4. Serveur Web Simple
|
|
52
|
+
|
|
53
|
+
```fazer
|
|
54
|
+
fn handle_request (req) {
|
|
55
|
+
"<h1>Bienvenue sur Fazer Web</h1>" // Retourne le HTML
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
"Démarrage du serveur sur 8080..." ->> println
|
|
59
|
+
8080 ->> server("handle_request")
|
|
60
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Guide de Démarrage
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
La façon la plus simple d'installer Fazer est via NPM (Node Package Manager).
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g fazer-lang
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Vérifiez l'installation :
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
fazer --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Votre Premier Script
|
|
18
|
+
|
|
19
|
+
Créez un fichier nommé `hello.fz` avec le contenu suivant :
|
|
20
|
+
|
|
21
|
+
```fazer
|
|
22
|
+
"Hello World!" ->> println
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Exécutez-le via la ligne de commande :
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
fazer hello.fz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Vous devriez voir `Hello World!` s'afficher.
|
|
32
|
+
|
|
33
|
+
## Le Mode Interactif (REPL)
|
|
34
|
+
|
|
35
|
+
Si vous lancez `fazer` sans arguments, vous entrez dans le mode interactif. C'est idéal pour tester des petites lignes de code.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
$ fazer
|
|
39
|
+
Welcome to Fazer v2.2.1
|
|
40
|
+
> 10 ->> println
|
|
41
|
+
10
|
|
42
|
+
> "test" ->> sha256 ->> println
|
|
43
|
+
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Tapez `exit` ou faites `Ctrl+C` pour quitter.
|
package/docs/stdlib.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Bibliothèque Standard (Stdlib)
|
|
2
|
+
|
|
3
|
+
Fazer inclut une bibliothèque standard riche pour interagir avec le système, les fichiers, le réseau et plus encore.
|
|
4
|
+
|
|
5
|
+
## Entrées / Sorties
|
|
6
|
+
|
|
7
|
+
### `print(valeur)` / `println(valeur)`
|
|
8
|
+
Affiche une valeur sur la sortie standard (console).
|
|
9
|
+
|
|
10
|
+
### `input(message)`
|
|
11
|
+
Affiche un message et attend une entrée utilisateur (retourne une chaîne).
|
|
12
|
+
|
|
13
|
+
### `box(titre, contenu)`
|
|
14
|
+
Affiche une boîte stylisée avec un titre et du contenu (texte ou liste).
|
|
15
|
+
|
|
16
|
+
### `menu(options)`
|
|
17
|
+
Affiche un menu interactif dans la console et retourne l'option choisie.
|
|
18
|
+
```fazer
|
|
19
|
+
choix := menu(["Option A", "Option B"])
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### `style(texte, couleur)`
|
|
23
|
+
Applique une couleur ANSI au texte pour l'affichage console.
|
|
24
|
+
Couleurs : "red", "green", "blue", "yellow", "cyan", "magenta", "white", "bold", "dim".
|
|
25
|
+
```fazer
|
|
26
|
+
println(style("Succès !", "green"))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Fichiers (File System)
|
|
30
|
+
|
|
31
|
+
### `read(path)`
|
|
32
|
+
Lit un fichier texte entier.
|
|
33
|
+
|
|
34
|
+
### `write(path, content)`
|
|
35
|
+
Écrit du contenu dans un fichier (écrase le fichier).
|
|
36
|
+
|
|
37
|
+
### `exists(path)`
|
|
38
|
+
Retourne `true` si le fichier ou dossier existe.
|
|
39
|
+
|
|
40
|
+
### `readBytes(path)` / `writeBytes(path, b64)`
|
|
41
|
+
Lecture/Écriture binaire (encodé en Base64).
|
|
42
|
+
|
|
43
|
+
### `import(chemin)`
|
|
44
|
+
Charge et exécute un autre script Fazer.
|
|
45
|
+
```fazer
|
|
46
|
+
import("utils.fz")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Système & Exécution
|
|
50
|
+
|
|
51
|
+
### `exec(cmd)`
|
|
52
|
+
Exécute une commande shell et retourne la sortie (stdout) sous forme de chaîne.
|
|
53
|
+
```fazer
|
|
54
|
+
files := exec("ls -la")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### `notify(titre, message)`
|
|
58
|
+
Affiche une notification système (Windows Toast).
|
|
59
|
+
```fazer
|
|
60
|
+
notify("Fazer", "Tâche terminée avec succès !")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `clipboard`
|
|
64
|
+
Objet global pour interagir avec le presse-papier système.
|
|
65
|
+
* `clipboard.read()` : Retourne le contenu texte du presse-papier.
|
|
66
|
+
* `clipboard.write(texte)` : Écrit du texte dans le presse-papier.
|
|
67
|
+
|
|
68
|
+
### `sleep(ms)`
|
|
69
|
+
Met le script en pause pour `ms` millisecondes.
|
|
70
|
+
|
|
71
|
+
### `env(nom_var)`
|
|
72
|
+
Lit une variable d'environnement.
|
|
73
|
+
|
|
74
|
+
### `cwd()`
|
|
75
|
+
Retourne le dossier de travail actuel.
|
|
76
|
+
|
|
77
|
+
### `argv()`
|
|
78
|
+
Retourne les arguments de la ligne de commande.
|
|
79
|
+
|
|
80
|
+
## Réseau & Web
|
|
81
|
+
|
|
82
|
+
### `fetch(url, options)`
|
|
83
|
+
Effectue une requête HTTP. `options` est un objet (method, headers, body).
|
|
84
|
+
Retourne `{ status, body, headers }`.
|
|
85
|
+
|
|
86
|
+
### `server(port, handler)`
|
|
87
|
+
Démarre un serveur HTTP simple.
|
|
88
|
+
`handler` peut être une fonction qui prend `req` et retourne `res`, ou un objet de routage simple.
|
|
89
|
+
```fazer
|
|
90
|
+
fn handler(req) ->
|
|
91
|
+
"Hello from Fazer!"
|
|
92
|
+
end
|
|
93
|
+
server(8080, handler)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `discord(token)`
|
|
97
|
+
Crée un client Discord (Bot).
|
|
98
|
+
* `.on("message", fn)`
|
|
99
|
+
* `.send(channelId, content)`
|
|
100
|
+
|
|
101
|
+
## Cryptographie
|
|
102
|
+
|
|
103
|
+
### `sha256(texte)`
|
|
104
|
+
Retourne le hash SHA-256 (hex).
|
|
105
|
+
|
|
106
|
+
### `encrypt(texte, mot_de_passe)`
|
|
107
|
+
Chiffre le texte avec AES-256-GCM (retourne Base64).
|
|
108
|
+
|
|
109
|
+
### `decrypt(b64, mot_de_passe)`
|
|
110
|
+
Déchiffre le texte.
|
|
111
|
+
|
|
112
|
+
## JSON & Données
|
|
113
|
+
|
|
114
|
+
### `json(obj)`
|
|
115
|
+
Convertit un objet en chaîne JSON.
|
|
116
|
+
|
|
117
|
+
### `parseJson(str)`
|
|
118
|
+
Parse une chaîne JSON en objet.
|
|
119
|
+
|
|
120
|
+
### `int(valeur)`
|
|
121
|
+
Convertit une valeur en entier.
|
|
122
|
+
|
|
123
|
+
### `float(valeur)`
|
|
124
|
+
Convertit une valeur en nombre flottant.
|
|
125
|
+
|
|
126
|
+
### `db(chemin_json)`
|
|
127
|
+
Crée ou charge une base de données JSON persistante.
|
|
128
|
+
Retourne un objet avec `.get(k)`, `.set(k, v)`, `.save()` et `.data()`.
|
|
129
|
+
```fazer
|
|
130
|
+
mydb := db("data.json")
|
|
131
|
+
mydb.set("score", 100)
|
|
132
|
+
mydb.save()
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### `keys(obj)`
|
|
136
|
+
Retourne les clés d'un objet JSON.
|
|
137
|
+
|
|
138
|
+
### `get(obj, clé)`
|
|
139
|
+
Récupère une valeur dans un objet/map.
|
package/docs/syntax.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Syntaxe du Langage Fazer
|
|
2
|
+
|
|
3
|
+
Fazer est un langage orienté flux. La plupart des opérations se font de gauche à droite via l'opérateur pipe.
|
|
4
|
+
|
|
5
|
+
## Commentaires
|
|
6
|
+
|
|
7
|
+
```fazer
|
|
8
|
+
// Ceci est un commentaire sur une ligne
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variables
|
|
12
|
+
|
|
13
|
+
Il n'y a pas de mot-clé de déclaration explicite comme `var` ou `let` pour l'assignation simple dans le flux, mais vous pouvez nommer des valeurs dans des arguments de fonctions.
|
|
14
|
+
|
|
15
|
+
Cependant, Fazer utilise principalement le passage de valeurs. Pour stocker des états globaux ou complexes, on utilise souvent des objets JSON ou la mémoire interne via `set`/`get`.
|
|
16
|
+
|
|
17
|
+
```fazer
|
|
18
|
+
// Exemple d'utilisation de set/get (mémoire clé-valeur)
|
|
19
|
+
"ma_valeur" "cle" ->> set
|
|
20
|
+
"cle" ->> get ->> println // Affiche "ma_valeur"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Opérateur Pipe (`->>`)
|
|
24
|
+
|
|
25
|
+
L'opérateur `->>` passe le résultat de l'expression de gauche comme **premier argument** de la fonction de droite.
|
|
26
|
+
|
|
27
|
+
```fazer
|
|
28
|
+
"Bonjour" ->> println
|
|
29
|
+
// Équivalent à println("Bonjour") dans d'autres langages
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Chaînage :
|
|
33
|
+
|
|
34
|
+
```fazer
|
|
35
|
+
"mot_de_passe" ->> sha256 ->> println
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Chaînes de Caractères et Nombres
|
|
39
|
+
|
|
40
|
+
```fazer
|
|
41
|
+
"Ceci est une chaîne"
|
|
42
|
+
123
|
|
43
|
+
12.34
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Fonctions (Définition)
|
|
47
|
+
|
|
48
|
+
Fazer supporte la définition de fonctions nommées.
|
|
49
|
+
|
|
50
|
+
```fazer
|
|
51
|
+
fn ma_fonction (arg1) {
|
|
52
|
+
arg1 ->> println
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
"test" ->> ma_fonction
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Contrôle de Flux : `case`
|
|
59
|
+
|
|
60
|
+
Fazer n'a pas de `if/else` traditionnel. Tout se fait via `case` (pattern matching).
|
|
61
|
+
|
|
62
|
+
```fazer
|
|
63
|
+
valeur ->> case {
|
|
64
|
+
"option1" : {
|
|
65
|
+
"C'est l'option 1" ->> println
|
|
66
|
+
}
|
|
67
|
+
"option2" : {
|
|
68
|
+
"C'est l'option 2" ->> println
|
|
69
|
+
}
|
|
70
|
+
_ : {
|
|
71
|
+
"Cas par défaut (wildcard)" ->> println
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Conditionnelle (If-Else simulé)
|
|
77
|
+
|
|
78
|
+
Pour faire un `if`, on matche souvent sur un booléen ou une valeur spécifique. Notez que Fazer évalue l'égalité stricte.
|
|
79
|
+
|
|
80
|
+
```fazer
|
|
81
|
+
// Exemple conceptuel
|
|
82
|
+
variable ->> case {
|
|
83
|
+
"vrai" : { ... }
|
|
84
|
+
_ : { ... }
|
|
85
|
+
}
|
|
86
|
+
```
|
package/fazer.js
CHANGED
|
@@ -18,7 +18,7 @@ const { Lexer, createToken, EmbeddedActionsParser } = require("chevrotain");
|
|
|
18
18
|
/* ────────────────────────────────────────────────────────────────────────── */
|
|
19
19
|
|
|
20
20
|
const WhiteSpace = createToken({ name: "WhiteSpace", pattern: /[ \t\r\n]+/, group: Lexer.SKIPPED });
|
|
21
|
-
const Comment = createToken({ name: "Comment", pattern:
|
|
21
|
+
const Comment = createToken({ name: "Comment", pattern: /(#|\/\/)[^\n]*/, group: Lexer.SKIPPED });
|
|
22
22
|
|
|
23
23
|
const Assign = createToken({ name: "Assign", pattern: /:=/ });
|
|
24
24
|
|
|
@@ -780,6 +780,8 @@ class FazerRuntime {
|
|
|
780
780
|
|
|
781
781
|
json: (x) => JSON.stringify(x, null, 2),
|
|
782
782
|
parseJson: (s) => JSON.parse(s),
|
|
783
|
+
int: (x) => parseInt(String(x)) || 0,
|
|
784
|
+
float: (x) => parseFloat(String(x)) || 0.0,
|
|
783
785
|
|
|
784
786
|
exec: (cmd) => {
|
|
785
787
|
try {
|
|
@@ -900,6 +902,166 @@ class FazerRuntime {
|
|
|
900
902
|
};
|
|
901
903
|
},
|
|
902
904
|
|
|
905
|
+
import: async (p) => {
|
|
906
|
+
const pAbs = path.resolve(String(p));
|
|
907
|
+
if (!fs.existsSync(pAbs)) throw new FazerError("Module not found: " + p);
|
|
908
|
+
const code = fs.readFileSync(pAbs, "utf8");
|
|
909
|
+
const lex = lexer.tokenize(code);
|
|
910
|
+
if (lex.errors.length) throw new FazerError("Lexer error in module: " + lex.errors[0].message);
|
|
911
|
+
const parser = new FazerParser();
|
|
912
|
+
parser.input = lex.tokens;
|
|
913
|
+
const ast = parser.program();
|
|
914
|
+
if (parser.errors.length) throw new FazerError("Parser error in module: " + parser.errors[0].message);
|
|
915
|
+
return await this._execBlock(ast, this.global);
|
|
916
|
+
},
|
|
917
|
+
|
|
918
|
+
db: (dbPath) => {
|
|
919
|
+
const absPath = path.resolve(String(dbPath));
|
|
920
|
+
let data = {};
|
|
921
|
+
if (fs.existsSync(absPath)) {
|
|
922
|
+
try { data = JSON.parse(fs.readFileSync(absPath, "utf8")); } catch(e) {}
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
get: (k) => data[String(k)],
|
|
926
|
+
set: (k, v) => { data[String(k)] = v; return null; },
|
|
927
|
+
save: () => { fs.writeFileSync(absPath, JSON.stringify(data, null, 2), "utf8"); return null; },
|
|
928
|
+
data: () => data
|
|
929
|
+
};
|
|
930
|
+
},
|
|
931
|
+
|
|
932
|
+
clipboard: {
|
|
933
|
+
read: () => {
|
|
934
|
+
try {
|
|
935
|
+
if (process.platform === "win32") {
|
|
936
|
+
return require("child_process").execSync("powershell -command \"Get-Clipboard\"", {encoding: "utf8"}).trim();
|
|
937
|
+
}
|
|
938
|
+
return "";
|
|
939
|
+
} catch (e) { return ""; }
|
|
940
|
+
},
|
|
941
|
+
write: (s) => {
|
|
942
|
+
try {
|
|
943
|
+
const str = String(s);
|
|
944
|
+
if (process.platform === "win32") {
|
|
945
|
+
require("child_process").execSync("powershell -command \"Set-Clipboard -Value '" + str.replace(/'/g, "''") + "'\"");
|
|
946
|
+
}
|
|
947
|
+
} catch (e) {}
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
|
|
952
|
+
menu: async (options) => {
|
|
953
|
+
if (!Array.isArray(options)) throw new FazerError("menu expects a list of options");
|
|
954
|
+
options.forEach((o, i) => console.log(`[${i+1}] ${o}`));
|
|
955
|
+
const readline = require("readline");
|
|
956
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
957
|
+
return new Promise(resolve => {
|
|
958
|
+
rl.question("Choice > ", (ans) => {
|
|
959
|
+
rl.close();
|
|
960
|
+
const n = Number(ans);
|
|
961
|
+
if (isNaN(n) || n < 1 || n > options.length) resolve(null);
|
|
962
|
+
else resolve(options[n-1]);
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
notify: (title, message) => {
|
|
968
|
+
try {
|
|
969
|
+
if (process.platform === "win32") {
|
|
970
|
+
const psScript = `
|
|
971
|
+
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
|
|
972
|
+
$obj = New-Object System.Windows.Forms.NotifyIcon
|
|
973
|
+
$obj.Icon = [System.Drawing.SystemIcons]::Information
|
|
974
|
+
$obj.Visible = $true
|
|
975
|
+
$obj.ShowBalloonTip(3000, "${String(title).replace(/"/g, '`"')}", "${String(message).replace(/"/g, '`"')}", [System.Windows.Forms.ToolTipIcon]::Info)
|
|
976
|
+
Start-Sleep -s 3
|
|
977
|
+
$obj.Visible = $false
|
|
978
|
+
`;
|
|
979
|
+
require("child_process").execSync("powershell -command \"" + psScript.replace(/\n/g, ";") + "\"");
|
|
980
|
+
}
|
|
981
|
+
} catch(e) {}
|
|
982
|
+
return null;
|
|
983
|
+
},
|
|
984
|
+
|
|
985
|
+
exec: (cmd) => {
|
|
986
|
+
try {
|
|
987
|
+
return require("child_process").execSync(String(cmd), { encoding: "utf8" }).trim();
|
|
988
|
+
} catch (e) {
|
|
989
|
+
throw new FazerError("exec failed: " + e.message);
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
|
|
993
|
+
style: (text, color) => {
|
|
994
|
+
const codes = {
|
|
995
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
996
|
+
red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m",
|
|
997
|
+
blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m", white: "\x1b[37m"
|
|
998
|
+
};
|
|
999
|
+
return (codes[String(color)] || "") + String(text) + codes.reset;
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
server: async (port, handler) => {
|
|
1003
|
+
const http = require("http");
|
|
1004
|
+
const srv = http.createServer(async (req, res) => {
|
|
1005
|
+
const bodyParts = [];
|
|
1006
|
+
for await (const chunk of req) bodyParts.push(chunk);
|
|
1007
|
+
const bodyStr = Buffer.concat(bodyParts).toString();
|
|
1008
|
+
|
|
1009
|
+
const reqObj = {
|
|
1010
|
+
method: req.method,
|
|
1011
|
+
url: req.url,
|
|
1012
|
+
headers: req.headers,
|
|
1013
|
+
body: bodyStr
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
try {
|
|
1017
|
+
let result = null;
|
|
1018
|
+
if (typeof handler === "object" && handler !== null && !handler.__fnref__) {
|
|
1019
|
+
const url = req.url.split("?")[0];
|
|
1020
|
+
if (handler[url]) {
|
|
1021
|
+
const h = handler[url];
|
|
1022
|
+
if (typeof h === "function" || (typeof h === "object" && h.__fnref__)) {
|
|
1023
|
+
result = await this._call(h, [reqObj], this.global);
|
|
1024
|
+
} else {
|
|
1025
|
+
result = h;
|
|
1026
|
+
}
|
|
1027
|
+
} else {
|
|
1028
|
+
result = { status: 404, body: "Not Found" };
|
|
1029
|
+
}
|
|
1030
|
+
} else {
|
|
1031
|
+
result = await this._call(handler, [reqObj], this.global);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
let status = 200;
|
|
1035
|
+
let headers = { "Content-Type": "text/plain" };
|
|
1036
|
+
let resBody = "";
|
|
1037
|
+
|
|
1038
|
+
if (typeof result === "object" && result !== null) {
|
|
1039
|
+
if (result.status) status = Number(result.status);
|
|
1040
|
+
if (result.headers) headers = { ...headers, ...result.headers };
|
|
1041
|
+
if (result.body) resBody = String(result.body);
|
|
1042
|
+
else if (result.status === undefined) resBody = JSON.stringify(result);
|
|
1043
|
+
} else {
|
|
1044
|
+
resBody = String(result);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
res.writeHead(status, headers);
|
|
1048
|
+
res.end(resBody);
|
|
1049
|
+
} catch (e) {
|
|
1050
|
+
res.writeHead(500);
|
|
1051
|
+
res.end("Internal Server Error: " + e.message);
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
return new Promise((resolve) => {
|
|
1056
|
+
srv.listen(Number(port), () => {
|
|
1057
|
+
console.log(`Server listening on port ${port}`);
|
|
1058
|
+
resolve({
|
|
1059
|
+
close: () => { srv.close(); return null; }
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
});
|
|
1063
|
+
},
|
|
1064
|
+
|
|
903
1065
|
argv: argvFn,
|
|
904
1066
|
env: envFn,
|
|
905
1067
|
cwd: cwdFn,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fazer-lang",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Fazer —
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Fazer — The ultimate automation language. Batteries-included: HTTP Server, System Exec, Clipboard, Discord, Crypto, and Pipe Operators (->).",
|
|
5
5
|
"main": "fazer.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"fazer": "fazer.js"
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"fazer.js",
|
|
11
11
|
"README.md",
|
|
12
12
|
"LICENSE",
|
|
13
|
-
"
|
|
13
|
+
"docs/",
|
|
14
|
+
"tools/"
|
|
14
15
|
],
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"chevrotain": "^11.0.3",
|
|
@@ -29,12 +30,19 @@
|
|
|
29
30
|
"pipe-operator",
|
|
30
31
|
"functional",
|
|
31
32
|
"secure",
|
|
32
|
-
"automation"
|
|
33
|
+
"automation",
|
|
34
|
+
"discord-bot",
|
|
35
|
+
"http-server",
|
|
36
|
+
"system-automation"
|
|
33
37
|
],
|
|
34
38
|
"author": "Fazer Corp",
|
|
35
39
|
"license": "MIT",
|
|
36
40
|
"repository": {
|
|
37
41
|
"type": "git",
|
|
38
42
|
"url": "https://github.com/viced/fazer-lang.git"
|
|
39
|
-
}
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/viced/fazer-lang/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/viced/fazer-lang#readme"
|
|
40
48
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// tools/announce.fz
|
|
2
|
+
webhook := "https://canary.discord.com/api/webhooks/1463728147202838652/vEd4MqqtYmAFg15GKX3FBxLYS1XImycormdOMwTa3Dbwcm6r8lYI3dfIWlZiLH2Dxq9e"
|
|
3
|
+
|
|
4
|
+
fn send(title, desc, color) ->
|
|
5
|
+
payload := {
|
|
6
|
+
"embeds": [{
|
|
7
|
+
"title": title,
|
|
8
|
+
"description": desc,
|
|
9
|
+
"color": int(color),
|
|
10
|
+
"footer": { "text": "Fazer Automation System" }
|
|
11
|
+
}]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
println("Envoi vers Discord...")
|
|
15
|
+
res := fetch(webhook, {
|
|
16
|
+
"method": "POST",
|
|
17
|
+
"headers": { "Content-Type": "application/json" },
|
|
18
|
+
"body": json(payload)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
case res.status >= 200 and res.status < 300
|
|
22
|
+
true -> println("Succès !") end
|
|
23
|
+
else -> println("Erreur " + res.status + ": " + res.body) end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
a := argv()
|
|
28
|
+
start := 1
|
|
29
|
+
|
|
30
|
+
case len(a) <= start
|
|
31
|
+
true -> println("Usage: fazer tools/announce.fz \"Titre\" \"Description\" [color]") end
|
|
32
|
+
else ->
|
|
33
|
+
title := a[start]
|
|
34
|
+
desc := ""
|
|
35
|
+
case len(a) > start + 1
|
|
36
|
+
true -> desc := a[start + 1] end
|
|
37
|
+
else -> end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
color := 5763719
|
|
41
|
+
case len(a) > start + 2
|
|
42
|
+
true -> color := a[start + 2] end
|
|
43
|
+
else -> end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
send(title, desc, color)
|
|
47
|
+
end
|
|
48
|
+
end
|