@mostajs/wagon-engine 0.1.1 → 0.2.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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
4
4
  Format : Keep a Changelog ; versions : SemVer.
5
5
 
6
+ ## [0.2.0] — 2026-10-26
7
+
8
+ ### Ajouté
9
+ - `ctx.palier({ compte })` et `ctx.plafond(cle, actuel, { compte })` lisent le palier d'un compte **désigné**, et non plus seulement de celui de la session. Une requête sans session peut ainsi être bornée par le palier d'un compte : un élève qui rejoint le groupe de son école compte dans le plafond d'apprenants de l'école (Speeking). La charge ne désigne qu'un compte qu'elle a elle-même rattaché à la requête. Essai T-ENG-6.
10
+ - `ctx.courriel(message)` permet à la charge d'envoyer un courriel par le transport du wagon (SMTP du `.env`). La fonction rend `true` si le courriel est parti, `false` sans transport ou en cas d'échec ; elle ne lève jamais d'exception. Essai T-ENG-7.
11
+
6
12
  ## [0.1.1] — 2026-10-26
7
13
 
8
14
  ### Corrigé (suite de conformité)
package/llms.txt CHANGED
@@ -4,7 +4,7 @@
4
4
  > PayPal), espace client, administration, pages légales, robots/sitemap, sauvegarde, fr/en/ar. Une GREFFE n'apporte que
5
5
  > wagon.config.mjs, charge/, legal/ et son .env. N2 (pack). Extrait du gabarit mosta-wagon 0.11.0 le 26/10/2026.
6
6
 
7
- - Version: 0.1.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
7
+ - Version: 0.2.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
8
8
  - Chemin: mostajs/mosta-platform-stack/mosta-wagon-engine · gabarit d'une greffe : Entreprise/mosta-wagon
9
9
 
10
10
  ## EXPORTS
@@ -27,7 +27,8 @@ routes(req, res, ctx) (passent en premier ; true si servie) · conformite (essai
27
27
  démarrage) · exporterCompte(id), effacerCompte(id) (RGPD) · sitemap() · sauvegarde() · permissions (0.11 :
28
28
  { '/page': { microphone: 'self' }, '/dossier/*': … }, garde-http ≥ 0.2).
29
29
  ctx : chemin, methode, lang, t, ui, page({ titre, contenu }), envoyer, json, rediriger, formulaire, corpsJson({ strict }),
30
- compte, consommer(cle), refuser(v), palier(), plafond(cle, n), admin.
30
+ compte, consommer(cle), refuser(v), palier({ compte? }), plafond(cle, n, { compte? }), courriel({ to, subject, text, html? }) → bool, admin.
31
+ 0.2 : { compte } = identifiant d'un compte DÉSIGNÉ (requête sans session bornée par le palier d'un compte rattaché par la charge).
31
32
 
32
33
  ## PIÈGES
33
34
  - wagon-ui ≥ 0.9 exige config.marque.auteur.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/wagon-engine",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Le wagon d'un SaaS, une fois pour toutes : comptes, connexion, paliers, quotas, paiement (Stripe, Chargily, SATIM), espace client, administration, pages légales, fr/en/ar. Une greffe n'apporte que wagon.config.mjs, charge/, legal/ et son .env ; la suite de conformité publiée juge la greffe.",
5
5
  "keywords": [
6
6
  "mostajs",
package/src/wagon.mjs CHANGED
@@ -499,10 +499,13 @@ export async function creerWagon({ config, charge, sgbd, baseUrl, stripe = null,
499
499
  const texte = v.plafond && v.limit ? v.reason : v.permanent ? t('tarifs.aucun') : v.reason;
500
500
  return envoyer(code, page({ titre: String(code), contenu: `<h1>${code}</h1><p class="msg">${esc(texte)}</p><p><a class="bouton" href="/tarifs">${esc(t('nav.tarifs'))}</a></p>` }), en);
501
501
  };
502
- /** Le palier de la requête : celui du compte, sinon le palier anonyme. */
503
- const palier = async () => {
504
- if (compte) {
505
- const a = await facturation.activePlan(String(compte.id));
502
+ /** Le palier de la requête : celui du compte, sinon le palier anonyme. 0.2 (wagon-engine) — `{ compte }` désigne
503
+ * un AUTRE compte (son identifiant) : une requête sans session (l'élève qui rejoint son école) lit le palier de
504
+ * l'école. La charge n'en fait usage que pour un compte qu'elle a elle-même rattaché à la requête. */
505
+ const palier = async ({ compte: designe } = {}) => {
506
+ const cible = designe ? { id: String(designe) } : compte;
507
+ if (cible) {
508
+ const a = await facturation.activePlan(String(cible.id));
506
509
  const l = a?.limits ?? {};
507
510
  return { slug: a?.plan?.slug ?? null, nom: a?.plan?.name ?? null, limites: typeof l === 'string' ? JSON.parse(l) : l };
508
511
  }
@@ -514,10 +517,10 @@ export async function creerWagon({ config, charge, sgbd, baseUrl, stripe = null,
514
517
  * une unité de plus tient dans le palier. Refus toujours définitif (403) — un stock ne se recharge pas
515
518
  * avec le temps ; il se libère (suppression, archivage) ou se relève (palier supérieur).
516
519
  */
517
- const plafond = async (cle, actuel) => {
520
+ const plafond = async (cle, actuel, { compte: designe } = {}) => {
518
521
  const r = ressources.find((x) => x.cle === cle && x.plafond);
519
522
  if (!r) throw new Error(`ressource « ${cle} » : pas un plafond déclaré par la charge`);
520
- const limite = Number((await palier()).limites?.[r.limite] ?? 0);
523
+ const limite = Number((await palier({ compte: designe })).limites?.[r.limite] ?? 0);
521
524
  const n = Number(actuel) || 0;
522
525
  if (limite === -1) return { allowed: true, plafond: true, resource: cle, current: n, limit: -1 };
523
526
  if (!limite) return { allowed: false, permanent: true, plafond: true, resource: cle, current: n, limit: 0, reason: 'plan does not include this resource' };
@@ -540,7 +543,13 @@ export async function creerWagon({ config, charge, sgbd, baseUrl, stripe = null,
540
543
  };
541
544
  // 0.3.1 — `admin` : la session est celle d'un administrateur (config.admins + WAGON_ADMINS) — pour qu'une
542
545
  // charge réserve ses outils (ex. l'outil de l'agence de travel) sans recalculer la liste.
543
- const ctx = { chemin, methode, lang, t, ui, page, envoyer, json, rediriger, formulaire, corpsJson, compte, consommer, refuser, palier, plafond, admin: estAdmin && !compteCle };
546
+ /** 0.2 (wagon-engine) — un courriel de la charge, par le transport du wagon (SMTP du .env). Rend true s'il est parti,
547
+ * false s'il n'y a pas de transport ou s'il a échoué (journalisé) : jamais une exception dans la route. */
548
+ const courriel = async (m) => {
549
+ if (!transport) return false;
550
+ try { await transport.envoyer(m); return true; } catch (e) { journal.warn(`wagon : courriel de la charge non parti : ${e?.message ?? e}`); return false; }
551
+ };
552
+ const ctx = { chemin, methode, lang, t, ui, page, envoyer, json, rediriger, formulaire, corpsJson, compte, consommer, refuser, palier, plafond, courriel, admin: estAdmin && !compteCle };
544
553
  if (charge.routes && (await charge.routes(req, res, ctx))) return true;
545
554
  if (chemin.startsWith('/api/')) return json(404, { ok: false, error: 'introuvable' });
546
555