odoro 1.0.9 → 2.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 (88) hide show
  1. package/README.md +94 -0
  2. package/client.d.ts +90 -16
  3. package/dist/build-XJIP4GNT.js +5 -0
  4. package/dist/{chunk-34RGOZFA.js → chunk-4D56Z7G3.js} +49 -50
  5. package/dist/{chunk-JMEHF3KN.js → chunk-7ZB5MAM6.js} +4 -4
  6. package/dist/chunk-DF67LADH.js +735 -0
  7. package/dist/{chunk-LHZGX5ML.js → chunk-HS7NGZLM.js} +307 -196
  8. package/dist/chunk-LIKVHWB4.js +231 -0
  9. package/dist/chunk-QOVBLN7A.js +241 -0
  10. package/dist/chunk-RTWJ5GFB.js +729 -0
  11. package/dist/chunk-TONYIKCN.js +57 -0
  12. package/dist/cli.d.ts +12 -13
  13. package/dist/cli.js +86 -63
  14. package/dist/{commands-AHMXBWQQ.js → commands-GYA7NTCX.js} +124 -124
  15. package/dist/commands-UOWN3IK4.js +181 -0
  16. package/dist/{create-CYVSVDAD.js → create-B2TLV6XI.js} +198 -160
  17. package/dist/index.d.ts +603 -151
  18. package/dist/index.js +6 -6
  19. package/dist/{package-EIDLKAA6.js → package-3ZJOEXM4.js} +6 -5
  20. package/dist/preview-3IRG7MHV.js +4 -0
  21. package/dist/registry/index.d.ts +75 -76
  22. package/dist/registry/index.js +1 -1
  23. package/dist/{server-4UGN3SFS.js → server-ZB3P3EGS.js} +3 -3
  24. package/package.json +6 -5
  25. package/templates/react-ts/README.md +71 -35
  26. package/templates/react-ts/_env.example +9 -0
  27. package/templates/react-ts/_variants/with-engine/src/background.tsx +118 -0
  28. package/templates/react-ts/_variants/without-libs/src/App.tsx +380 -0
  29. package/templates/react-ts/_variants/without-libs/src/background.tsx +24 -0
  30. package/templates/react-ts/_variants/without-libs/src/entry-server.tsx +39 -0
  31. package/templates/react-ts/_variants/without-libs/src/main.tsx +26 -0
  32. package/templates/{react-ts-server/_variantes/sans-libs/client → react-ts/_variants/without-libs}/src/styles.css +137 -137
  33. package/templates/react-ts/{_variantes/sans-routeur → _variants/without-router}/src/App.tsx +141 -141
  34. package/templates/react-ts/_variants/without-router/src/entry-server.tsx +39 -0
  35. package/templates/react-ts/index.html +4 -4
  36. package/templates/react-ts/odoro.config.ts +5 -0
  37. package/templates/react-ts/src/App.tsx +219 -209
  38. package/templates/react-ts/src/background.tsx +66 -0
  39. package/templates/react-ts/src/entry-server.tsx +85 -0
  40. package/templates/react-ts/src/main.tsx +13 -4
  41. package/templates/react-ts/src/router.tsx +65 -45
  42. package/templates/react-ts/src/styles.css +5 -5
  43. package/templates/react-ts-server/Dockerfile +9 -9
  44. package/templates/react-ts-server/README.md +97 -45
  45. package/templates/react-ts-server/_env.example +31 -33
  46. package/templates/react-ts-server/_variants/with-engine/client/src/background.tsx +118 -0
  47. package/templates/react-ts-server/_variants/without-libs/client/src/App.tsx +380 -0
  48. package/templates/react-ts-server/_variants/without-libs/client/src/background.tsx +24 -0
  49. package/templates/react-ts-server/_variants/without-libs/client/src/entry-server.tsx +39 -0
  50. package/templates/react-ts-server/_variants/without-libs/client/src/main.tsx +26 -0
  51. package/templates/{react-ts/_variantes/sans-libs → react-ts-server/_variants/without-libs/client}/src/styles.css +137 -137
  52. package/templates/react-ts-server/{_variantes/sans-routeur → _variants/without-router}/client/src/App.tsx +141 -141
  53. package/templates/react-ts-server/_variants/without-router/client/src/entry-server.tsx +39 -0
  54. package/templates/react-ts-server/client/index.html +4 -4
  55. package/templates/react-ts-server/client/src/App.tsx +228 -210
  56. package/templates/react-ts-server/client/src/account.tsx +267 -0
  57. package/templates/react-ts-server/client/src/auth.tsx +139 -0
  58. package/templates/react-ts-server/client/src/background.tsx +66 -0
  59. package/templates/react-ts-server/client/src/entry-server.tsx +92 -0
  60. package/templates/react-ts-server/client/src/main.tsx +17 -5
  61. package/templates/react-ts-server/client/src/router.tsx +77 -45
  62. package/templates/react-ts-server/client/src/styles.css +5 -5
  63. package/templates/react-ts-server/odoro.config.ts +7 -4
  64. package/templates/react-ts-server/package.json +2 -0
  65. package/templates/react-ts-server/scripts/dev.mjs +14 -14
  66. package/templates/react-ts-server/server/src/main.ts +119 -45
  67. package/templates/react-ts-server/server/src/modules/auth/index.ts +238 -0
  68. package/templates/react-ts-server/server/src/modules/auth/password.ts +122 -0
  69. package/templates/react-ts-server/server/src/modules/auth/store.ts +193 -0
  70. package/templates/react-ts-server/server/src/modules/health/index.ts +45 -46
  71. package/dist/build-JFQHODAT.js +0 -5
  72. package/dist/chunk-22KJTV2R.js +0 -380
  73. package/dist/chunk-3SZIN6VG.js +0 -64
  74. package/dist/chunk-DL3NPC4H.js +0 -113
  75. package/dist/chunk-TQUJ3MFS.js +0 -268
  76. package/dist/chunk-ZVL7EXJO.js +0 -66
  77. package/dist/commands-4JRBD55Z.js +0 -245
  78. package/dist/preview-7DKEAQOQ.js +0 -4
  79. package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +0 -118
  80. package/templates/react-ts/_variantes/sans-libs/src/App.tsx +0 -382
  81. package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +0 -23
  82. package/templates/react-ts/_variantes/sans-libs/src/main.tsx +0 -17
  83. package/templates/react-ts/src/fond.tsx +0 -66
  84. package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +0 -118
  85. package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +0 -382
  86. package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +0 -23
  87. package/templates/react-ts-server/_variantes/sans-libs/client/src/main.tsx +0 -17
  88. package/templates/react-ts-server/client/src/fond.tsx +0 -66
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { guessAlias, defaultAliases } from './chunk-T6RLHSCW.js';
3
- import { object, string, _minLength, array, _default, record, literal, safeParse, resolveGraph, describeProblem, parseMeta } from './chunk-34RGOZFA.js';
4
- import { colors, info, warn, success, error } from './chunk-JMEHF3KN.js';
3
+ import { object, string, _minLength, array, _default, record, literal, safeParse, resolveGraph, describeProblem, parseMeta } from './chunk-4D56Z7G3.js';
4
+ import { colors, info, warn, success, error } from './chunk-7ZB5MAM6.js';
5
5
  import { readFile, writeFile, mkdir, rm, rename } from 'fs/promises';
6
6
  import { join, isAbsolute, resolve, dirname, posix } from 'path';
7
7
  import * as prompts from '@clack/prompts';
@@ -9,37 +9,37 @@ import { createHash } from 'crypto';
9
9
 
10
10
  var CONFIG_FILE = "odoro.json";
11
11
  var trackedFileSchema = object({
12
- /** Chemin dans le projet, relatif a sa racine, en barres obliques. */
12
+ /** Path in the project, relative to its root, in forward slashes. */
13
13
  path: string().check(_minLength(1)),
14
- /** Empreinte du contenu **tel qu'il a ete livre**. */
14
+ /** Hash of the content **as it was delivered**. */
15
15
  hash: string().check(_minLength(1))
16
16
  });
17
17
  var installedSchema = object({
18
- /** Date d'installation, en ISO 8601. */
18
+ /** Installation date, in ISO 8601. */
19
19
  installedAt: string().check(_minLength(1)),
20
- /** Fichiers ecrits par cette entree. */
20
+ /** Files written by this entry. */
21
21
  files: array(trackedFileSchema)
22
22
  });
23
23
  var aliasesSchema = object({
24
24
  /**
25
- * Prefixe d'import employe dans le code ecrit, sans barre finale.
26
- * Par exemple `@/components/odoro`.
25
+ * Import prefix used in the written code, without a trailing slash. For
26
+ * example `@/components/odoro`.
27
27
  */
28
28
  import: string().check(_minLength(1)),
29
29
  /**
30
- * Dossier correspondant sur le disque, relatif a la racine du projet.
31
- * Par exemple `src/components/odoro`.
30
+ * Matching directory on disk, relative to the project root. For example
31
+ * `src/components/odoro`.
32
32
  */
33
33
  directory: string().check(_minLength(1))
34
34
  });
35
35
  var projectSchema = object({
36
- /** Version du format, pour que la CLI sache si elle sait lire. */
36
+ /** Version of the format, so that the CLI knows whether it can read it. */
37
37
  version: _default(literal(1), 1),
38
- /** Adresse du registre : une URL, ou un chemin local pour le developper. */
38
+ /** Address of the registry: a URL, or a local path to develop it. */
39
39
  registry: string().check(_minLength(1)),
40
- /** Ou ecrire, et sous quel prefixe importer. */
40
+ /** Where to write, and under which prefix to import. */
41
41
  aliases: aliasesSchema,
42
- /** Ce qui a ete installe, indexe par identifiant de registre. */
42
+ /** What was installed, indexed by registry identifier. */
43
43
  installed: _default(record(string(), installedSchema), {})
44
44
  });
45
45
  function fingerprint(content) {
@@ -62,15 +62,15 @@ async function loadProject(root) {
62
62
  } catch (error2) {
63
63
  return {
64
64
  ok: false,
65
- reason: "invalide",
66
- problems: [`JSON illisible \u2014 ${error2.message}`]
65
+ reason: "invalid",
66
+ problems: [`Unreadable JSON \u2014 ${error2.message}`]
67
67
  };
68
68
  }
69
69
  const parsed = safeParse(projectSchema, value);
70
70
  if (!parsed.success) {
71
71
  return {
72
72
  ok: false,
73
- reason: "invalide",
73
+ reason: "invalid",
74
74
  problems: parsed.error.issues.map(
75
75
  (issue) => `${issue.path.join(".") || CONFIG_FILE} : ${issue.message}`
76
76
  )
@@ -89,28 +89,28 @@ async function saveProject(root, config) {
89
89
 
90
90
  // src/add/rewrite.ts
91
91
  var REGISTRY_TOKEN = "@registre";
92
- function estUnAlias(prefix) {
92
+ function isAlias(prefix) {
93
93
  return /^[@~#]/.test(prefix);
94
94
  }
95
- function cheminRelatif(depuis, vers) {
96
- const segmentsDepuis = depuis.split("/").slice(0, -1);
97
- const segmentsVers = vers.split("/");
98
- let commun = 0;
99
- while (commun < segmentsDepuis.length && commun < segmentsVers.length - 1 && segmentsDepuis[commun] === segmentsVers[commun]) {
100
- commun += 1;
95
+ function relativeImport(from, to) {
96
+ const fromSegments = from.split("/").slice(0, -1);
97
+ const toSegments = to.split("/");
98
+ let common = 0;
99
+ while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
100
+ common += 1;
101
101
  }
102
- const montees = segmentsDepuis.length - commun;
103
- const descente = segmentsVers.slice(commun).join("/");
104
- return montees === 0 ? `./${descente}` : `${"../".repeat(montees)}${descente}`;
102
+ const climbs = fromSegments.length - common;
103
+ const descent = toSegments.slice(common).join("/");
104
+ return climbs === 0 ? `./${descent}` : `${"../".repeat(climbs)}${descent}`;
105
105
  }
106
106
  function rewriteImports(source, importPrefix, target) {
107
107
  const prefix = importPrefix.replace(/\/$/, "");
108
- if (estUnAlias(prefix) || target === void 0) {
108
+ if (isAlias(prefix) || target === void 0) {
109
109
  return source.split(`${REGISTRY_TOKEN}/`).join(`${prefix}/`);
110
110
  }
111
111
  return source.replaceAll(
112
112
  new RegExp(`${REGISTRY_TOKEN}/([^'"\\s]+)`, "g"),
113
- (_tout, chemin) => cheminRelatif(target, chemin)
113
+ (_whole, path) => relativeImport(target, path)
114
114
  );
115
115
  }
116
116
  function usedTokens(source) {
@@ -125,22 +125,22 @@ function usedTokens(source) {
125
125
 
126
126
  // src/add/inspect.ts
127
127
  var STATE_LABEL = {
128
- "a-jour": "a jour",
129
- retouche: "retouche localement",
130
- "mise-a-jour": "une mise a jour existe",
131
- divergence: "retouche localement, et le registre a change",
132
- absent: "absent du projet",
133
- inconnu: "inconnu du registre"
128
+ "up-to-date": "up to date",
129
+ edited: "edited locally",
130
+ "update-available": "an update exists",
131
+ diverged: "edited locally, and the registry has changed",
132
+ missing: "missing from the project",
133
+ unknown: "unknown to the registry"
134
134
  };
135
135
  function verdict(local, deliveredHash, upstream) {
136
- if (local === null) return "absent";
137
- if (upstream === null) return "inconnu";
136
+ if (local === null) return "missing";
137
+ if (upstream === null) return "unknown";
138
138
  const localChanged = fingerprint(local) !== deliveredHash;
139
139
  const upstreamChanged = fingerprint(upstream) !== deliveredHash;
140
- if (!localChanged && !upstreamChanged) return "a-jour";
141
- if (localChanged && !upstreamChanged) return "retouche";
142
- if (!localChanged && upstreamChanged) return "mise-a-jour";
143
- return "divergence";
140
+ if (!localChanged && !upstreamChanged) return "up-to-date";
141
+ if (localChanged && !upstreamChanged) return "edited";
142
+ if (!localChanged && upstreamChanged) return "update-available";
143
+ return "diverged";
144
144
  }
145
145
  async function inspectEntry(root, config, id, upstream) {
146
146
  const installed = config.installed[id];
@@ -207,16 +207,16 @@ async function readDocument(location, relativePath) {
207
207
  } catch (error2) {
208
208
  return {
209
209
  ok: false,
210
- problems: [`${url} : injoignable \u2014 ${error2.message}`]
210
+ problems: [`${url} : unreachable \u2014 ${error2.message}`]
211
211
  };
212
212
  }
213
213
  if (!response.ok) {
214
- return { ok: false, problems: [`${url} : reponse ${String(response.status)}.`] };
214
+ return { ok: false, problems: [`${url} : response ${String(response.status)}.`] };
215
215
  }
216
216
  try {
217
217
  return { ok: true, value: await response.json() };
218
218
  } catch {
219
- return { ok: false, problems: [`${url} : la reponse n'est pas du JSON.`] };
219
+ return { ok: false, problems: [`${url} : the response is not JSON.`] };
220
220
  }
221
221
  }
222
222
  const file = join(location, relativePath);
@@ -224,14 +224,14 @@ async function readDocument(location, relativePath) {
224
224
  try {
225
225
  raw = await readFile(file, "utf8");
226
226
  } catch {
227
- return { ok: false, problems: [`${file} : introuvable.`] };
227
+ return { ok: false, problems: [`${file} : not found.`] };
228
228
  }
229
229
  try {
230
230
  return { ok: true, value: JSON.parse(raw) };
231
231
  } catch (error2) {
232
232
  return {
233
233
  ok: false,
234
- problems: [`${file} : JSON illisible \u2014 ${error2.message}`]
234
+ problems: [`${file} : unreadable JSON \u2014 ${error2.message}`]
235
235
  };
236
236
  }
237
237
  }
@@ -241,14 +241,14 @@ function asIndex(value, origin) {
241
241
  return {
242
242
  ok: false,
243
243
  problems: [
244
- `${origin} : version de format ${String(candidate.version)} non reconnue. Mettez la CLI a jour.`
244
+ `${origin} : format version ${String(candidate.version)} not recognised. Update the CLI.`
245
245
  ]
246
246
  };
247
247
  }
248
248
  if (!Array.isArray(candidate.entries)) {
249
249
  return {
250
250
  ok: false,
251
- problems: [`${origin} : l'index ne contient aucune liste d'entrees.`]
251
+ problems: [`${origin} : the index holds no list of entries.`]
252
252
  };
253
253
  }
254
254
  return { ok: true, value: candidate };
@@ -258,13 +258,15 @@ function asEntry(value, id) {
258
258
  if (!parsed.ok) return { ok: false, problems: parsed.problems };
259
259
  const sources = value.sources;
260
260
  if (typeof sources !== "object" || sources === null) {
261
- return { ok: false, problems: [`${id} : l'entree ne porte aucun code source.`] };
261
+ return { ok: false, problems: [`${id} : the entry carries no source code.`] };
262
262
  }
263
263
  const record2 = sources;
264
264
  const problems = [];
265
265
  for (const file of parsed.meta.files) {
266
266
  if (typeof record2[file.path] !== "string") {
267
- problems.push(`${id} : le fichier annonce "${file.path}" est absent de la reponse.`);
267
+ problems.push(
268
+ `${id} : the announced file "${file.path}" is missing from the response.`
269
+ );
268
270
  }
269
271
  }
270
272
  if (problems.length > 0) return { ok: false, problems };
@@ -299,20 +301,20 @@ async function planWrite(root, path, content, owner) {
299
301
  } catch {
300
302
  existing = null;
301
303
  }
302
- const action = existing === null ? "creation" : existing.replaceAll("\r\n", "\n") === content.replaceAll("\r\n", "\n") ? "inchange" : "remplacement";
304
+ const action = existing === null ? "create" : existing.replaceAll("\r\n", "\n") === content.replaceAll("\r\n", "\n") ? "unchanged" : "replace";
303
305
  return { path, content, action, owner };
304
306
  }
305
- var PENDING = ".odoro-en-cours";
307
+ var PENDING = ".odoro-pending";
306
308
  async function applyPlan(root, plan) {
307
- const todo = plan.filter((entry) => entry.action !== "inchange");
308
- const skipped = plan.filter((entry) => entry.action === "inchange").map((e) => e.path);
309
+ const todo = plan.filter((entry) => entry.action !== "unchanged");
310
+ const skipped = plan.filter((entry) => entry.action === "unchanged").map((entry) => entry.path);
309
311
  const pending = [];
310
312
  const previous = /* @__PURE__ */ new Map();
311
313
  try {
312
314
  for (const entry of todo) {
313
315
  const target = join(root, entry.path);
314
316
  await mkdir(dirname(target), { recursive: true });
315
- if (entry.action === "remplacement") {
317
+ if (entry.action === "replace") {
316
318
  previous.set(entry.path, await readFile(target, "utf8"));
317
319
  } else {
318
320
  previous.set(entry.path, null);
@@ -381,14 +383,12 @@ async function prepareInstall(registry, requested) {
381
383
  continue;
382
384
  }
383
385
  if (matches.length > 1) {
384
- problems.push(
385
- `"${asked}" est ambigu : ${matches.join(", ")}. Precisez la categorie.`
386
- );
386
+ problems.push(`"${asked}" is ambiguous: ${matches.join(", ")}. Name the category.`);
387
387
  continue;
388
388
  }
389
389
  const near = suggest(asked, known);
390
390
  problems.push(
391
- near.length > 0 ? `"${asked}" est introuvable. Vouliez-vous dire ${near.join(", ")} ?` : `"${asked}" est introuvable. "odoro list" donne le catalogue.`
391
+ near.length > 0 ? `"${asked}" was not found. Did you mean ${near.join(", ")}?` : `"${asked}" was not found. "odoro list" gives the catalogue.`
392
392
  );
393
393
  }
394
394
  if (problems.length > 0) return { ok: false, problems };
@@ -442,9 +442,9 @@ function recordInstall(previous, entries, plan, now) {
442
442
 
443
443
  // src/add/weight.ts
444
444
  var BACKEND_WEIGHT = {
445
- /** Scene 3D minimale : geometrie, materiau standard, lumiere directionnelle. */
445
+ /** Minimal 3D scene: geometry, standard material, directional light. */
446
446
  three: 130,
447
- /** Rendu plein ecran : contexte, programme, triangle. */
447
+ /** Full-screen render: context, program, triangle. */
448
448
  ogl: 13
449
449
  };
450
450
  function weighEntries(entries) {
@@ -459,12 +459,12 @@ function weighEntries(entries) {
459
459
  const warnings = [];
460
460
  for (const [backend, ids] of byBackend) {
461
461
  const kilobytes = BACKEND_WEIGHT[backend];
462
- const which = ids.length === 1 ? ids[0] : `${String(ids.length)} composants`;
462
+ const which = ids.length === 1 ? ids[0] : `${String(ids.length)} components`;
463
463
  warnings.push({
464
464
  backend,
465
465
  kilobytes,
466
466
  entries: ids,
467
- message: backend === "three" ? `${String(which)} charge une scene 3D : environ ${String(kilobytes)} Ko compresses au premier affichage. Le backend leger en demande ${String(BACKEND_WEIGHT.ogl)}, si un effet plein ecran suffit.` : `${String(which)} charge le backend leger : environ ${String(kilobytes)} Ko compresses.`
467
+ message: backend === "three" ? `${String(which)} loads a 3D scene: about ${String(kilobytes)} kB compressed on the first paint. The light backend asks for ${String(BACKEND_WEIGHT.ogl)}, when a full-screen effect is enough.` : `${String(which)} loads the light backend: about ${String(kilobytes)} kB compressed.`
468
468
  });
469
469
  }
470
470
  return warnings.sort((a, b) => b.kilobytes - a.kilobytes);
@@ -486,9 +486,7 @@ function canAsk(options) {
486
486
  return options.yes !== true && process.stdin.isTTY === true;
487
487
  }
488
488
  function needsConfirmation(what) {
489
- error(
490
- `${what} Relancez avec ${colors.cyan("--yes")} pour l'accepter sans question.`
491
- );
489
+ error(`${what} Run again with ${colors.cyan("--yes")} to accept it without asking.`);
492
490
  return 1;
493
491
  }
494
492
  function fail(title, problems) {
@@ -500,24 +498,24 @@ async function requireProject(root) {
500
498
  const loaded = await loadProject(root);
501
499
  if (loaded.ok) return loaded.config;
502
500
  if (loaded.reason === "absent") {
503
- error(`Aucun ${CONFIG_FILE} ici. Lancez ${colors.cyan("odoro init")} d'abord.`);
501
+ error(`No ${CONFIG_FILE} here. Run ${colors.cyan("odoro init")} first.`);
504
502
  return 1;
505
503
  }
506
- return fail(`${CONFIG_FILE} est illisible :`, loaded.problems);
504
+ return fail(`${CONFIG_FILE} is unreadable:`, loaded.problems);
507
505
  }
508
506
  async function initCommand(options) {
509
507
  const { root } = options;
510
508
  const existing = await loadProject(root);
511
509
  if (existing.ok && options.yes !== true) {
512
510
  if (!canAsk(options)) {
513
- return needsConfirmation(`${CONFIG_FILE} existe deja et serait remplace.`);
511
+ return needsConfirmation(`${CONFIG_FILE} already exists and would be replaced.`);
514
512
  }
515
513
  const replace = await prompts.confirm({
516
- message: `${CONFIG_FILE} existe deja. Le remplacer ?`,
514
+ message: `${CONFIG_FILE} already exists. Replace it?`,
517
515
  initialValue: false
518
516
  });
519
517
  if (prompts.isCancel(replace) || !replace) {
520
- info("Rien n a ete change.");
518
+ info("Nothing was changed.");
521
519
  return 0;
522
520
  }
523
521
  }
@@ -525,71 +523,69 @@ async function initCommand(options) {
525
523
  const suggested = defaultAliases(guess);
526
524
  if (guess === null) {
527
525
  warn(
528
- `Aucun alias trouve dans tsconfig.json : les composants iront dans ${colors.cyan(suggested.directory)}/ et s importeront entre eux en relatif.`
526
+ `No alias found in tsconfig.json: the components will go to ${colors.cyan(suggested.directory)}/ and will import each other relatively.`
529
527
  );
530
528
  } else {
531
529
  info(
532
- `Alias trouve dans tsconfig.json : ${colors.cyan(`${guess.prefix}/*`)} vers ${colors.cyan(`${guess.directory}/`)}.`
530
+ `Alias found in tsconfig.json: ${colors.cyan(`${guess.prefix}/*`)} towards ${colors.cyan(`${guess.directory}/`)}.`
533
531
  );
534
532
  }
535
533
  let aliases = suggested;
536
534
  let registry = options.registry ?? DEFAULT_REGISTRY;
537
535
  if (canAsk(options)) {
538
536
  const directory = await prompts.text({
539
- message: "Ou ecrire les composants ?",
537
+ message: "Where should the components be written?",
540
538
  initialValue: suggested.directory
541
539
  });
542
540
  if (prompts.isCancel(directory)) return 0;
543
541
  const importPrefix = await prompts.text({
544
- message: "Sous quel prefixe les importer ?",
542
+ message: "Under which prefix should they be imported?",
545
543
  initialValue: suggested.import
546
544
  });
547
545
  if (prompts.isCancel(importPrefix)) return 0;
548
- const source = await prompts.text({ message: "Registre ?", initialValue: registry });
546
+ const source = await prompts.text({ message: "Registry?", initialValue: registry });
549
547
  if (prompts.isCancel(source)) return 0;
550
548
  aliases = { directory, import: importPrefix };
551
549
  registry = source;
552
550
  }
553
551
  const config = { version: 1, registry, aliases, installed: {} };
554
552
  await saveProject(root, config);
555
- success(`${CONFIG_FILE} ecrit.`);
553
+ success(`${CONFIG_FILE} written.`);
556
554
  console.log(` ${colors.dim("destination")} ${aliases.directory}/`);
557
555
  console.log(` ${colors.dim("imports")} ${aliases.import}/\u2026`);
558
- console.log(` ${colors.dim("registre")} ${registry}`);
556
+ console.log(` ${colors.dim("registry")} ${registry}`);
559
557
  console.log(`
560
- ${colors.cyan("odoro list")} donne le catalogue.
558
+ ${colors.cyan("odoro list")} gives the catalogue.
561
559
  `);
562
560
  return 0;
563
561
  }
564
562
  var ACTION_MARK = {
565
- creation: colors.green("+"),
566
- remplacement: colors.yellow("~"),
567
- inchange: colors.dim("=")
563
+ create: colors.green("+"),
564
+ replace: colors.yellow("~"),
565
+ unchanged: colors.dim("=")
568
566
  };
569
567
  async function addCommand(options, requested) {
570
568
  const { root } = options;
571
569
  const config = await requireProject(root);
572
570
  if (typeof config === "number") return config;
573
571
  if (requested.length === 0) {
574
- error('Rien a installer. Donnez un nom, ou "odoro list" pour le catalogue.');
572
+ error('Nothing to install. Give a name, or "odoro list" for the catalogue.');
575
573
  return 1;
576
574
  }
577
575
  const registry = openRegistry(options.registry ?? config.registry, root);
578
576
  const prepared = await prepareInstall(registry, requested);
579
- if (!prepared.ok) return fail("Installation impossible :", prepared.problems);
577
+ if (!prepared.ok) return fail("Install impossible:", prepared.problems);
580
578
  const { entries, implied } = prepared;
581
579
  if (implied.length > 0) {
582
- info(`Dependances ajoutees : ${implied.map((id) => colors.cyan(id)).join(", ")}`);
580
+ info(`Dependencies added: ${implied.map((id) => colors.cyan(id)).join(", ")}`);
583
581
  }
584
582
  for (const warning of weighEntries(entries)) warn(warning.message);
585
583
  const missing = await missingPackages(root, requiredPackages(entries));
586
584
  if (missing.length > 0) {
587
- warn(
588
- `A installer ensuite : ${missing.map((name) => colors.cyan(name)).join(" ")}`
589
- );
585
+ warn(`To install next: ${missing.map((name) => colors.cyan(name)).join(" ")}`);
590
586
  }
591
587
  const plan = await planInstall(root, config, entries);
592
- const replacing = plan.filter((write) => write.action === "remplacement");
588
+ const replacing = plan.filter((write) => write.action === "replace");
593
589
  console.log("");
594
590
  for (const write of plan) {
595
591
  console.log(` ${ACTION_MARK[write.action]} ${write.path}`);
@@ -598,15 +594,15 @@ async function addCommand(options, requested) {
598
594
  if (replacing.length > 0 && options.yes !== true) {
599
595
  if (!canAsk(options)) {
600
596
  return needsConfirmation(
601
- `${String(replacing.length)} fichier(s) existant(s) seraient remplaces.`
597
+ `${String(replacing.length)} existing file(s) would be replaced.`
602
598
  );
603
599
  }
604
600
  const go = await prompts.confirm({
605
- message: `${String(replacing.length)} fichier(s) existant(s) seront remplaces. Continuer ?`,
601
+ message: `${String(replacing.length)} existing file(s) will be replaced. Continue?`,
606
602
  initialValue: false
607
603
  });
608
604
  if (prompts.isCancel(go) || !go) {
609
- info("Rien n a ete ecrit.");
605
+ info("Nothing was written.");
610
606
  return 0;
611
607
  }
612
608
  }
@@ -618,10 +614,10 @@ async function addCommand(options, requested) {
618
614
  };
619
615
  await saveProject(root, updated);
620
616
  const written = String(report.written.length);
621
- const skipped = report.skipped.length > 0 ? `, ${String(report.skipped.length)} inchange(s)` : "";
622
- success(`${written} fichier(s) ecrit(s)${skipped}.`);
617
+ const skipped = report.skipped.length > 0 ? `, ${String(report.skipped.length)} unchanged` : "";
618
+ success(`${written} file(s) written${skipped}.`);
623
619
  } catch (cause) {
624
- error("Ecriture interrompue : le projet est inchange.", cause);
620
+ error("Write interrupted: the project is unchanged.", cause);
625
621
  return 1;
626
622
  }
627
623
  warnUndeclared(entries);
@@ -637,7 +633,7 @@ function warnUndeclared(entries) {
637
633
  const matched = [...targets].some((target) => target.startsWith(token));
638
634
  if (!matched) {
639
635
  warn(
640
- `${entry.id} importe ${colors.cyan(token)} sans l'avoir declare : signalez-le au registre.`
636
+ `${entry.id} imports ${colors.cyan(token)} without declaring it: report it to the registry.`
641
637
  );
642
638
  }
643
639
  }
@@ -667,7 +663,7 @@ async function listCommand(options) {
667
663
  const installed = loaded.ok ? new Set(Object.keys(loaded.config.installed)) : /* @__PURE__ */ new Set();
668
664
  const registry = openRegistry(location, root);
669
665
  const index = await registry.index();
670
- if (!index.ok) return fail("Catalogue illisible :", index.problems);
666
+ if (!index.ok) return fail("Catalogue unreadable:", index.problems);
671
667
  const byCategory = /* @__PURE__ */ new Map();
672
668
  for (const entry of index.value.entries) {
673
669
  byCategory.set(entry.category, [...byCategory.get(entry.category) ?? [], entry]);
@@ -689,16 +685,16 @@ async function listCommand(options) {
689
685
  console.log("");
690
686
  }
691
687
  const total = String(index.value.entries.length);
692
- const marked = installed.size > 0 ? `, ${colors.green("\u2713")} = deja installe` : "";
693
- console.log(` ${colors.dim(`${total} entree(s)${marked}`)}
688
+ const marked = installed.size > 0 ? `, ${colors.green("\u2713")} = already installed` : "";
689
+ console.log(` ${colors.dim(`${total} entr${total === "1" ? "y" : "ies"}${marked}`)}
694
690
  `);
695
691
  return 0;
696
692
  }
697
693
  function stateColor(state) {
698
694
  const label = STATE_LABEL[state];
699
- if (state === "a-jour") return colors.green(label);
700
- if (state === "retouche") return colors.cyan(label);
701
- if (state === "absent" || state === "divergence") return colors.red(label);
695
+ if (state === "up-to-date") return colors.green(label);
696
+ if (state === "edited") return colors.cyan(label);
697
+ if (state === "missing" || state === "diverged") return colors.red(label);
702
698
  return colors.yellow(label);
703
699
  }
704
700
  async function diffCommand(options) {
@@ -707,7 +703,7 @@ async function diffCommand(options) {
707
703
  if (typeof config === "number") return config;
708
704
  const ids = Object.keys(config.installed);
709
705
  if (ids.length === 0) {
710
- info("Aucun composant installe.");
706
+ info("No component installed.");
711
707
  return 0;
712
708
  }
713
709
  const registry = openRegistry(options.registry ?? config.registry, root);
@@ -716,16 +712,18 @@ async function diffCommand(options) {
716
712
  console.log("");
717
713
  for (const report of reports) {
718
714
  if (report.orphan) {
719
- console.log(` ${colors.cyan(report.id)} ${colors.dim("\u2014 absent du registre")}`);
715
+ console.log(
716
+ ` ${colors.cyan(report.id)} ${colors.dim("\u2014 missing from the registry")}`
717
+ );
720
718
  interesting += 1;
721
719
  continue;
722
720
  }
723
721
  for (const file of report.files) {
724
- if (file.state === "a-jour") continue;
722
+ if (file.state === "up-to-date") continue;
725
723
  interesting += 1;
726
724
  console.log(` ${colors.cyan(report.id)} ${colors.dim(file.path)}`);
727
725
  console.log(` ${stateColor(file.state)}`);
728
- if (file.local !== null && file.upstream !== null && file.state !== "retouche") {
726
+ if (file.local !== null && file.upstream !== null && file.state !== "edited") {
729
727
  const changes = previewChanges(file.local, file.upstream);
730
728
  for (const line of changes.removed)
731
729
  console.log(` ${colors.red(`- ${line.trim()}`)}`);
@@ -736,11 +734,13 @@ async function diffCommand(options) {
736
734
  }
737
735
  }
738
736
  if (interesting === 0) {
739
- success(`${String(ids.length)} entree(s) installee(s), toutes a jour.`);
737
+ success(
738
+ `${String(ids.length)} entr${ids.length === 1 ? "y" : "ies"} installed, all up to date.`
739
+ );
740
740
  return 0;
741
741
  }
742
742
  console.log(
743
- ` ${colors.dim(`odoro add <nom> r\xE9\xE9crit une entr\xE9e depuis le registre. Vos retouches seraient perdues.`)}
743
+ ` ${colors.dim(`odoro add <name> rewrites an entry from the registry. Your edits would be lost.`)}
744
744
  `
745
745
  );
746
746
  return 0;
@@ -751,12 +751,12 @@ async function doctorCommand(options) {
751
751
  const notes = [];
752
752
  const loaded = await loadProject(root);
753
753
  if (!loaded.ok) {
754
- return loaded.reason === "absent" ? fail("Diagnostic impossible :", [`aucun ${CONFIG_FILE}. Lancez "odoro init".`]) : fail(`${CONFIG_FILE} est illisible :`, loaded.problems);
754
+ return loaded.reason === "absent" ? fail("Diagnostics impossible:", [`no ${CONFIG_FILE}. Run "odoro init".`]) : fail(`${CONFIG_FILE} is unreadable:`, loaded.problems);
755
755
  }
756
756
  const config = loaded.config;
757
- notes.push(`registre : ${config.registry}`);
757
+ notes.push(`registry: ${config.registry}`);
758
758
  if (!isRemote(config.registry)) {
759
- notes.push("registre local : les autres machines de l equipe ne le verront pas.");
759
+ notes.push("local registry: the other machines of the team will not see it.");
760
760
  }
761
761
  const { access } = await import('fs/promises');
762
762
  try {
@@ -764,49 +764,49 @@ async function doctorCommand(options) {
764
764
  } catch {
765
765
  if (Object.keys(config.installed).length > 0) {
766
766
  troubles.push(
767
- `${config.aliases.directory}/ n'existe pas, alors que des entrees y sont notees.`
767
+ `${config.aliases.directory}/ does not exist, while entries are recorded in it.`
768
768
  );
769
769
  }
770
770
  }
771
771
  const registry = openRegistry(options.registry ?? config.registry, root);
772
772
  const reports = await inspectAll(root, config, registry);
773
- let retouched = 0;
773
+ let edited = 0;
774
774
  for (const report of reports) {
775
775
  if (report.orphan) {
776
- notes.push(`${report.id} n'est plus servi par le registre.`);
776
+ notes.push(`${report.id} is no longer served by the registry.`);
777
777
  continue;
778
778
  }
779
779
  for (const file of report.files) {
780
- if (file.state === "absent") {
781
- troubles.push(`${report.id} : ${file.path} est note comme installe mais absent.`);
780
+ if (file.state === "missing") {
781
+ troubles.push(`${report.id}: ${file.path} is recorded as installed but missing.`);
782
782
  }
783
- if (file.state === "retouche") retouched += 1;
784
- if (file.state === "mise-a-jour" || file.state === "divergence") {
785
- notes.push(`${report.id} : ${STATE_LABEL[file.state]} (${file.path}).`);
783
+ if (file.state === "edited") edited += 1;
784
+ if (file.state === "update-available" || file.state === "diverged") {
785
+ notes.push(`${report.id}: ${STATE_LABEL[file.state]} (${file.path}).`);
786
786
  }
787
787
  }
788
788
  }
789
- if (retouched > 0) {
789
+ if (edited > 0) {
790
790
  notes.push(
791
- `${String(retouched)} fichier(s) retouche(s) localement : "odoro add" les reecrirait.`
791
+ `${String(edited)} file(s) edited locally: "odoro add" would rewrite them.`
792
792
  );
793
793
  }
794
794
  const served = reports.map((report) => report.upstream).filter((entry) => entry !== null);
795
795
  for (const name of await missingPackages(root, requiredPackages(served))) {
796
796
  troubles.push(
797
- `${name} est requis par un composant installe mais absent du package.json.`
797
+ `${name} is required by an installed component but missing from the package.json.`
798
798
  );
799
799
  }
800
800
  console.log("");
801
801
  for (const note of notes) console.log(` ${colors.dim("\xB7")} ${note}`);
802
802
  if (notes.length > 0) console.log("");
803
803
  if (troubles.length === 0) {
804
- success("Rien a signaler.");
804
+ success("Nothing to report.");
805
805
  return 0;
806
806
  }
807
807
  for (const trouble of troubles) console.error(` ${colors.red("\xB7")} ${trouble}`);
808
808
  console.log("");
809
- error(`${String(troubles.length)} probleme(s).`);
809
+ error(`${String(troubles.length)} problem(s).`);
810
810
  return 1;
811
811
  }
812
812