odoro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +12 -0
  2. package/client.d.ts +84 -0
  3. package/dist/build-SNTGJH2J.js +4 -0
  4. package/dist/chunk-2YDI5NKV.js +55 -0
  5. package/dist/chunk-G2WW7N4C.js +1872 -0
  6. package/dist/chunk-G5QXVBYT.js +1139 -0
  7. package/dist/chunk-JMEHF3KN.js +35 -0
  8. package/dist/chunk-PEMYUK2D.js +64 -0
  9. package/dist/chunk-T42X2NJN.js +98 -0
  10. package/dist/chunk-T6RLHSCW.js +123 -0
  11. package/dist/chunk-UGRSODHU.js +154 -0
  12. package/dist/cli.d.ts +40 -0
  13. package/dist/cli.js +236 -0
  14. package/dist/commands-FVAHVVVN.js +793 -0
  15. package/dist/commands-V44Y5G4F.js +245 -0
  16. package/dist/create-SH2M722Y.js +288 -0
  17. package/dist/index.d.ts +354 -0
  18. package/dist/index.js +7 -0
  19. package/dist/package-AUINPBEX.js +62 -0
  20. package/dist/preview-LOAO5Y6V.js +3 -0
  21. package/dist/registry/index.d.ts +316 -0
  22. package/dist/registry/index.js +2 -0
  23. package/dist/server-MZ76LPAG.js +3 -0
  24. package/package.json +57 -0
  25. package/templates/react-ts/README.md +52 -0
  26. package/templates/react-ts/_gitignore +8 -0
  27. package/templates/react-ts/index.html +13 -0
  28. package/templates/react-ts/odoro.config.ts +10 -0
  29. package/templates/react-ts/package.json +23 -0
  30. package/templates/react-ts/public/favicon.svg +4 -0
  31. package/templates/react-ts/src/App.tsx +66 -0
  32. package/templates/react-ts/src/main.tsx +18 -0
  33. package/templates/react-ts/src/odoro-env.d.ts +1 -0
  34. package/templates/react-ts/src/routes/About.tsx +19 -0
  35. package/templates/react-ts/src/routes/Home.tsx +80 -0
  36. package/templates/react-ts/src/routes/NotFound.tsx +14 -0
  37. package/templates/react-ts/src/styles.css +13 -0
  38. package/templates/react-ts/tsconfig.json +25 -0
  39. package/templates/react-ts-server/Dockerfile +51 -0
  40. package/templates/react-ts-server/README.md +87 -0
  41. package/templates/react-ts-server/_dockerignore +8 -0
  42. package/templates/react-ts-server/_env.example +78 -0
  43. package/templates/react-ts-server/_gitignore +8 -0
  44. package/templates/react-ts-server/client/index.html +13 -0
  45. package/templates/react-ts-server/client/public/favicon.svg +4 -0
  46. package/templates/react-ts-server/client/src/App.tsx +66 -0
  47. package/templates/react-ts-server/client/src/main.tsx +18 -0
  48. package/templates/react-ts-server/client/src/odoro-env.d.ts +1 -0
  49. package/templates/react-ts-server/client/src/routes/About.tsx +19 -0
  50. package/templates/react-ts-server/client/src/routes/Home.tsx +148 -0
  51. package/templates/react-ts-server/client/src/routes/NotFound.tsx +14 -0
  52. package/templates/react-ts-server/client/src/styles.css +13 -0
  53. package/templates/react-ts-server/odoro.config.ts +21 -0
  54. package/templates/react-ts-server/package.json +33 -0
  55. package/templates/react-ts-server/scripts/dev.mjs +74 -0
  56. package/templates/react-ts-server/server/src/main.ts +131 -0
  57. package/templates/react-ts-server/server/src/modules/health/index.ts +144 -0
  58. package/templates/react-ts-server/server/tsconfig.json +23 -0
  59. package/templates/react-ts-server/tsconfig.json +27 -0
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import colors from 'picocolors';
3
+ export { default as colors } from 'picocolors';
4
+
5
+ var TAG = colors.bold(colors.magenta("odoro"));
6
+ function stamp() {
7
+ return colors.dim((/* @__PURE__ */ new Date()).toLocaleTimeString("fr-FR", { hour12: false }));
8
+ }
9
+ function info(message) {
10
+ console.log(`${stamp()} ${TAG} ${message}`);
11
+ }
12
+ function success(message) {
13
+ console.log(`${stamp()} ${TAG} ${colors.green(message)}`);
14
+ }
15
+ function warn(message) {
16
+ console.warn(`${stamp()} ${TAG} ${colors.yellow(message)}`);
17
+ }
18
+ function error(message, cause) {
19
+ console.error(`${stamp()} ${TAG} ${colors.red(message)}`);
20
+ if (cause instanceof Error && cause.stack !== void 0) {
21
+ console.error(colors.dim(cause.stack));
22
+ } else if (cause !== void 0) {
23
+ console.error(colors.dim(String(cause)));
24
+ }
25
+ }
26
+ function duration(milliseconds) {
27
+ return milliseconds < 1e3 ? `${Math.round(milliseconds)} ms` : `${(milliseconds / 1e3).toFixed(2)} s`;
28
+ }
29
+ function size(bytes) {
30
+ if (bytes < 1024) return `${bytes} o`;
31
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} Ko`;
32
+ return `${(bytes / (1024 * 1024)).toFixed(2)} Mo`;
33
+ }
34
+
35
+ export { duration, error, info, size, success, warn };
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, writeFile, appendFile } from 'fs/promises';
3
+ import { existsSync } from 'fs';
4
+ import { join } from 'path';
5
+
6
+ function checkDatabaseUrl(value) {
7
+ const url = value.trim();
8
+ if (url.length === 0) return "L'URL ne peut pas etre vide.";
9
+ if (!/^postgres(ql)?:\/\//i.test(url)) {
10
+ return "Une URL PostgreSQL commence par postgres:// ou postgresql://";
11
+ }
12
+ let parsed;
13
+ try {
14
+ parsed = new URL(url);
15
+ } catch {
16
+ return "Cette chaine n'est pas une URL valide.";
17
+ }
18
+ if (parsed.hostname.length === 0) return "Aucun hote dans cette URL.";
19
+ if (parsed.pathname.replace(/^\//, "").length === 0) {
20
+ return "Aucun nom de base : ajoutez-le apres le port, par exemple /mon_projet";
21
+ }
22
+ if (!/^(localhost|127\.0\.0\.1|\[::1\])$/i.test(parsed.hostname) && parsed.searchParams.get("sslmode") === "disable") {
23
+ return "sslmode=disable sur une base distante : le trafic passerait en clair.";
24
+ }
25
+ return void 0;
26
+ }
27
+ async function writeDatabaseUrl(target, url) {
28
+ const path = join(target, ".env");
29
+ const line = `DATABASE_URL=${url}
30
+ `;
31
+ if (!existsSync(path)) {
32
+ const example = join(target, ".env.example");
33
+ const base = existsSync(example) ? await readFile(example, "utf8") : "";
34
+ await writeFile(path, base.replace(/^DATABASE_URL=.*$/m, line.trimEnd()), "utf8");
35
+ if (!base.includes("DATABASE_URL=")) await appendFile(path, line, "utf8");
36
+ return;
37
+ }
38
+ const current = await readFile(path, "utf8");
39
+ await writeFile(
40
+ path,
41
+ current.includes("DATABASE_URL=") ? current.replace(/^DATABASE_URL=.*$/m, line.trimEnd()) : current + line,
42
+ "utf8"
43
+ );
44
+ }
45
+ async function assertEnvIgnored(target) {
46
+ const path = join(target, ".gitignore");
47
+ if (!existsSync(path)) {
48
+ return "Aucun .gitignore : le fichier .env risque d'etre versionne.";
49
+ }
50
+ const content = await readFile(path, "utf8");
51
+ const ignored = content.split("\n").map((line) => line.trim()).some((line) => line === ".env" || line === ".env*" || line === "*.env");
52
+ return ignored ? void 0 : ".env ne figure pas dans le .gitignore : vos identifiants risquent d etre versionnes.";
53
+ }
54
+ var PROVIDER_PENDING = [
55
+ "Le provisionnement passe par @odoro/cloud-sdk, installe a part :",
56
+ "",
57
+ " npm install --save-dev @odoro/cloud-sdk",
58
+ " odoro db:login",
59
+ " odoro db:create --env production",
60
+ "",
61
+ "Il ne vient pas avec odoro : ce binaire est telecharge a chaque creation de projet, et la plupart emploient leur propre base."
62
+ ].join("\n");
63
+
64
+ export { PROVIDER_PENDING, assertEnvIgnored, checkDatabaseUrl, writeDatabaseUrl };
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ import { guessAliasPaths } from './chunk-T6RLHSCW.js';
3
+ import { mkdir, writeFile, rm } from 'fs/promises';
4
+ import { existsSync } from 'fs';
5
+ import { resolve, join } from 'path';
6
+ import { pathToFileURL } from 'url';
7
+ import { build } from 'esbuild';
8
+
9
+ function defineConfig(config) {
10
+ return config;
11
+ }
12
+ var CONFIG_FILES = ["odoro.config.ts", "odoro.config.js", "odoro.config.mjs"];
13
+ async function importConfigFile(file, root) {
14
+ const directory = join(root, "node_modules", ".odoro");
15
+ const output = join(directory, `config.${Date.now().toString(36)}.mjs`);
16
+ await mkdir(directory, { recursive: true });
17
+ try {
18
+ const result = await build({
19
+ entryPoints: [file],
20
+ bundle: true,
21
+ format: "esm",
22
+ platform: "node",
23
+ target: "node20",
24
+ write: false,
25
+ // Seul le code du projet est inline ; ses dependances restent externes,
26
+ // sans quoi il faudrait resoudre tout node_modules pour lire trois lignes.
27
+ packages: "external"
28
+ });
29
+ const code = result.outputFiles[0]?.text;
30
+ if (code === void 0) {
31
+ throw new Error(`[odoro] La configuration "${file}" n'a produit aucun code.`);
32
+ }
33
+ await writeFile(output, code, "utf8");
34
+ const module = await import(pathToFileURL(output).href);
35
+ if (module.default === void 0) {
36
+ throw new Error(
37
+ `[odoro] La configuration "${file}" doit avoir un export par defaut.`
38
+ );
39
+ }
40
+ return module.default;
41
+ } finally {
42
+ await rm(output, { force: true });
43
+ }
44
+ }
45
+ async function loadConfig(root, overrides = {}) {
46
+ const absoluteRoot = resolve(root);
47
+ let file;
48
+ let loaded = {};
49
+ for (const candidate of CONFIG_FILES) {
50
+ const path = join(absoluteRoot, candidate);
51
+ if (existsSync(path)) {
52
+ file = path;
53
+ loaded = await importConfigFile(path, absoluteRoot);
54
+ break;
55
+ }
56
+ }
57
+ const merged = {
58
+ ...loaded,
59
+ ...overrides,
60
+ server: { ...loaded.server, ...overrides.server },
61
+ build: { ...loaded.build, ...overrides.build },
62
+ alias: { ...loaded.alias, ...overrides.alias },
63
+ define: { ...loaded.define, ...overrides.define }
64
+ };
65
+ const base = merged.base ?? "/";
66
+ const projectRoot = merged.root === void 0 ? absoluteRoot : resolve(absoluteRoot, merged.root);
67
+ return {
68
+ root: projectRoot,
69
+ base: base.endsWith("/") ? base : `${base}/`,
70
+ publicDir: resolve(projectRoot, merged.publicDir ?? "public"),
71
+ outDir: resolve(projectRoot, merged.build?.outDir ?? "dist"),
72
+ server: {
73
+ port: merged.server?.port ?? 5180,
74
+ host: merged.server?.host ?? "localhost",
75
+ proxy: merged.server?.proxy ?? {}
76
+ },
77
+ build: {
78
+ outDir: merged.build?.outDir ?? "dist",
79
+ minify: merged.build?.minify ?? true,
80
+ sourcemap: merged.build?.sourcemap ?? true,
81
+ target: merged.build?.target ?? "es2022"
82
+ },
83
+ // Les alias declares dans `tsconfig.json` sont repris d'office. Sans
84
+ // cela, un projet qui suit `odoro init` — lequel deduit son prefixe du
85
+ // tsconfig — aurait a redeclarer le meme alias ici pour que le serveur
86
+ // sache le resoudre. Deux endroits pour la meme verite, et une erreur
87
+ // qui n'apparait qu'au premier import.
88
+ //
89
+ // La configuration l'emporte : c'est elle qu'on ecrit pour corriger un
90
+ // cas que la deduction n'attrape pas.
91
+ alias: { ...await guessAliasPaths(projectRoot), ...merged.alias },
92
+ define: merged.define ?? {},
93
+ envPrefix: merged.envPrefix ?? "ODORO_",
94
+ configFile: file
95
+ };
96
+ }
97
+
98
+ export { defineConfig, loadConfig };
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from 'fs/promises';
3
+ import { join, posix } from 'path';
4
+
5
+ function stripJsonComments(source) {
6
+ let output = "";
7
+ let inString = false;
8
+ let inLine = false;
9
+ let inBlock = false;
10
+ for (let index = 0; index < source.length; index += 1) {
11
+ const char = source[index] ?? "";
12
+ const next = source[index + 1] ?? "";
13
+ if (inLine) {
14
+ if (char === "\n") {
15
+ inLine = false;
16
+ output += char;
17
+ }
18
+ continue;
19
+ }
20
+ if (inBlock) {
21
+ if (char === "*" && next === "/") {
22
+ inBlock = false;
23
+ index += 1;
24
+ }
25
+ continue;
26
+ }
27
+ if (inString) {
28
+ output += char;
29
+ if (char === "\\") {
30
+ output += next;
31
+ index += 1;
32
+ } else if (char === '"') {
33
+ inString = false;
34
+ }
35
+ continue;
36
+ }
37
+ if (char === '"') {
38
+ inString = true;
39
+ output += char;
40
+ continue;
41
+ }
42
+ if (char === "/" && next === "/") {
43
+ inLine = true;
44
+ index += 1;
45
+ continue;
46
+ }
47
+ if (char === "/" && next === "*") {
48
+ inBlock = true;
49
+ index += 1;
50
+ continue;
51
+ }
52
+ output += char;
53
+ }
54
+ return output.replace(/,(\s*[}\]])/g, "$1");
55
+ }
56
+ function toDirectory(target, baseUrl) {
57
+ const withoutStar = target.replace(/\/?\*+$/, "");
58
+ const cleaned = withoutStar.replace(/^\.\//, "");
59
+ const base = (baseUrl ?? ".").replace(/^\.\/?/, "");
60
+ const joined = base === "" ? cleaned : posix.join(base, cleaned);
61
+ return joined === "" ? "." : joined;
62
+ }
63
+ async function guessAlias(root) {
64
+ let raw;
65
+ try {
66
+ raw = await readFile(join(root, "tsconfig.json"), "utf8");
67
+ } catch {
68
+ return null;
69
+ }
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(stripJsonComments(raw));
73
+ } catch {
74
+ return null;
75
+ }
76
+ const paths = parsed.compilerOptions?.paths;
77
+ if (paths === void 0) return null;
78
+ const candidates = [];
79
+ for (const [pattern, targets] of Object.entries(paths)) {
80
+ if (!pattern.endsWith("/*")) continue;
81
+ const target = targets[0];
82
+ if (target === void 0) continue;
83
+ candidates.push({
84
+ prefix: pattern.slice(0, -2),
85
+ directory: toDirectory(target, parsed.compilerOptions?.baseUrl)
86
+ });
87
+ }
88
+ candidates.sort((a, b) => a.directory.split("/").length - b.directory.split("/").length);
89
+ return candidates[0] ?? null;
90
+ }
91
+ function defaultAliases(guess) {
92
+ if (guess === null) {
93
+ return { import: "src/odoro", directory: "src/odoro" };
94
+ }
95
+ return {
96
+ import: `${guess.prefix}/odoro`,
97
+ directory: posix.join(guess.directory, "odoro")
98
+ };
99
+ }
100
+ async function guessAliasPaths(root) {
101
+ let raw;
102
+ try {
103
+ raw = await readFile(join(root, "tsconfig.json"), "utf8");
104
+ } catch {
105
+ return {};
106
+ }
107
+ let parsed;
108
+ try {
109
+ parsed = JSON.parse(stripJsonComments(raw));
110
+ } catch {
111
+ return {};
112
+ }
113
+ const aliases = {};
114
+ for (const [pattern, targets] of Object.entries(parsed.compilerOptions?.paths ?? {})) {
115
+ if (!pattern.endsWith("/*")) continue;
116
+ const target = targets[0];
117
+ if (target === void 0) continue;
118
+ aliases[pattern.slice(0, -2)] = toDirectory(target, parsed.compilerOptions?.baseUrl);
119
+ }
120
+ return aliases;
121
+ }
122
+
123
+ export { defaultAliases, guessAlias, guessAliasPaths };
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import { extractEntries, applyAlias, isBareSpecifier } from './chunk-G5QXVBYT.js';
3
+ import { info, colors, size, success, duration } from './chunk-JMEHF3KN.js';
4
+ import { existsSync } from 'fs';
5
+ import { readFile, rm, mkdir, writeFile, cp } from 'fs/promises';
6
+ import { join, basename, relative, resolve, sep } from 'path';
7
+ import { build } from 'esbuild';
8
+
9
+ function toPosix(path) {
10
+ return path.split(sep).join("/");
11
+ }
12
+ function buildEnv(config) {
13
+ const env = {
14
+ MODE: "production",
15
+ DEV: false,
16
+ PROD: true,
17
+ BASE_URL: config.base
18
+ };
19
+ for (const [key, value] of Object.entries(process.env)) {
20
+ if (key.startsWith(config.envPrefix) && value !== void 0) env[key] = value;
21
+ }
22
+ return env;
23
+ }
24
+ function outputsForEntry(result, entry, outDir, root) {
25
+ const toRelative = (file) => toPosix(relative(outDir, resolve(root, file)));
26
+ for (const [file, meta] of Object.entries(result.metafile.outputs)) {
27
+ if (meta.entryPoint === void 0) continue;
28
+ if (resolve(root, meta.entryPoint) !== resolve(entry)) continue;
29
+ if (!file.endsWith(".js")) continue;
30
+ return {
31
+ script: toRelative(file),
32
+ styles: meta.cssBundle === void 0 ? [] : [toRelative(meta.cssBundle)]
33
+ };
34
+ }
35
+ return { script: void 0, styles: [] };
36
+ }
37
+ async function buildProject(config) {
38
+ const started = Date.now();
39
+ const indexFile = join(config.root, "index.html");
40
+ if (!existsSync(indexFile)) {
41
+ throw new Error(`[odoro] Aucun "index.html" a la racine du projet (${config.root}).`);
42
+ }
43
+ const html = await readFile(indexFile, "utf8");
44
+ const entries = extractEntries(html, config.root);
45
+ if (entries.length === 0) {
46
+ throw new Error(
47
+ `[odoro] Aucun point d'entree : "index.html" doit contenir un <script type="module" src="...">.`
48
+ );
49
+ }
50
+ await rm(config.outDir, { recursive: true, force: true });
51
+ await mkdir(config.outDir, { recursive: true });
52
+ const result = await build({
53
+ entryPoints: [...entries],
54
+ bundle: true,
55
+ format: "esm",
56
+ platform: "browser",
57
+ target: config.build.target,
58
+ splitting: true,
59
+ minify: config.build.minify,
60
+ sourcemap: config.build.sourcemap,
61
+ metafile: true,
62
+ outdir: join(config.outDir, "assets"),
63
+ absWorkingDir: config.root,
64
+ publicPath: `${config.base}assets`,
65
+ // Les empreintes rendent les fichiers immuables : ils peuvent etre mis en
66
+ // cache indefiniment, et un deploiement n'invalide que ce qui a change.
67
+ entryNames: "[name]-[hash]",
68
+ chunkNames: "chunk-[hash]",
69
+ assetNames: "[name]-[hash]",
70
+ jsx: "automatic",
71
+ logLevel: "silent",
72
+ define: {
73
+ "import.meta.env": JSON.stringify(buildEnv(config)),
74
+ "process.env.NODE_ENV": JSON.stringify("production"),
75
+ ...config.define
76
+ },
77
+ loader: {
78
+ ".svg": "file",
79
+ ".png": "file",
80
+ ".jpg": "file",
81
+ ".jpeg": "file",
82
+ ".gif": "file",
83
+ ".webp": "file",
84
+ ".avif": "file",
85
+ ".ico": "file",
86
+ ".woff": "file",
87
+ ".woff2": "file",
88
+ ".mp4": "file",
89
+ ".webm": "file"
90
+ },
91
+ plugins: [
92
+ {
93
+ name: "odoro-alias",
94
+ setup(builder) {
95
+ builder.onResolve({ filter: /.*/ }, (args) => {
96
+ if (args.kind === "entry-point") return null;
97
+ const aliased = applyAlias(args.path, config);
98
+ if (aliased === args.path || isBareSpecifier(aliased)) return null;
99
+ return builder.resolve(aliased, {
100
+ kind: "import-statement",
101
+ resolveDir: args.resolveDir,
102
+ importer: args.importer,
103
+ pluginData: { aliased: true }
104
+ });
105
+ });
106
+ }
107
+ }
108
+ ]
109
+ });
110
+ let output = html;
111
+ for (const entry of entries) {
112
+ const { script, styles } = outputsForEntry(
113
+ result,
114
+ entry,
115
+ join(config.outDir, "assets"),
116
+ config.root
117
+ );
118
+ if (script === void 0) continue;
119
+ const original = new RegExp(
120
+ `<script[^>]*type=["']module["'][^>]*src=["'][^"']*${basename(entry)}["'][^>]*></script>`,
121
+ "i"
122
+ );
123
+ const tags = [
124
+ ...styles.map(
125
+ (style) => `<link rel="stylesheet" href="${config.base}assets/${style}">`
126
+ ),
127
+ `<script type="module" crossorigin src="${config.base}assets/${script}"></script>`
128
+ ].join("\n ");
129
+ output = output.replace(original, tags);
130
+ }
131
+ await writeFile(join(config.outDir, "index.html"), output, "utf8");
132
+ if (existsSync(config.publicDir)) {
133
+ await cp(config.publicDir, config.outDir, { recursive: true });
134
+ }
135
+ const files = Object.entries(result.metafile.outputs).map(([file, meta]) => ({
136
+ path: toPosix(relative(config.outDir, resolve(config.root, file))),
137
+ bytes: meta.bytes
138
+ })).sort((a, b) => b.bytes - a.bytes);
139
+ return { outDir: config.outDir, files, elapsed: Date.now() - started };
140
+ }
141
+ function reportBuild(output, root) {
142
+ const directory = toPosix(relative(root, output.outDir)) || ".";
143
+ let total = 0;
144
+ for (const file of output.files) {
145
+ total += file.bytes;
146
+ if (file.path.endsWith(".map")) continue;
147
+ info(
148
+ ` ${colors.dim(`${directory}/`)}${file.path} ${colors.dim(size(file.bytes))}`
149
+ );
150
+ }
151
+ success(`compile en ${duration(output.elapsed)} \u2014 ${size(total)} au total`);
152
+ }
153
+
154
+ export { buildProject, reportBuild };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Entree en ligne de commande du moteur Odoro.
3
+ *
4
+ * L'analyse des arguments est ecrite a la main : elle tient en quarante
5
+ * lignes, et le binaire est telecharge a chaque `npm create odoro`, donc son
6
+ * poids compte.
7
+ *
8
+ * @module
9
+ */
10
+ /** Arguments analyses. */
11
+ interface ParsedArgs {
12
+ /** Sous-commande demandee. */
13
+ command: string;
14
+ /** Arguments positionnels restants. */
15
+ positional: string[];
16
+ /** Options nommees. */
17
+ flags: Record<string, string | boolean>;
18
+ }
19
+ /**
20
+ * Analyse une ligne de commande.
21
+ *
22
+ * Reconnait `--option`, `--option=valeur`, `--option valeur`, `--no-option`
23
+ * et les alias courts `-h` et `-v`.
24
+ *
25
+ * @example
26
+ * parseArgs(['create', 'site', '--template=react-ts', '--no-git'])
27
+ * // { command: 'create', positional: ['site'], flags: { template: 'react-ts', git: false } }
28
+ */
29
+ declare function parseArgs(argv: readonly string[]): ParsedArgs;
30
+ /**
31
+ * Point d'entree du binaire.
32
+ *
33
+ * @returns Le code de sortie du processus.
34
+ *
35
+ * @example
36
+ * const code = await run(['dev', '--port', '3000'])
37
+ */
38
+ declare function run(argv: readonly string[]): Promise<number>;
39
+
40
+ export { type ParsedArgs, parseArgs, run };