@mostajs/data-plug
v1.2.6
Published
Data access plug — interchangeable backend (ORM direct, REST proxy, …) with multi-dialect registry + auto-register apikey via @mostajs/auth-flow (MOSTA_AUTH_FLOW_URL distinct, forceRefresh, clearCachedApiKey) + system dialect séparé du singleton métier (b
Maintainers
Readme
@mostajs/data-plug
Data access plug — ORM direct or NET transport, one interface, zero config in modules.
Principe
MOSTA_DATA=orm → @mostajs/orm → SQL direct (SQLite, PostgreSQL, Oracle, MSSQL, etc.)
MOSTA_DATA=net → @mostajs/net → Transport distant (REST, GraphQL, gRPC, WS, MCP)Les modules @mostajs (auth, rbac, audit, settings, ticketing, secu) appellent getDialect() depuis data-plug — ils ne savent pas si les données viennent d'une base locale ou d'un serveur distant.
Installation
npm install @mostajs/data-plugUsage dans un module
import { getDialect } from '@mostajs/data-plug'
import { UserRepository } from './repositories/user.repository.js'
const dialect = await getDialect()
const repo = new UserRepository(dialect)
const users = await repo.findAll()Configuration (dans l'app, pas dans le module)
Mode ORM (accès direct base de données)
MOSTA_DATA=orm
DB_DIALECT=sqlite
SGBD_URI=./data/app.dbMode NET (accès distant via transport)
MOSTA_DATA=net
MOSTA_NET_URL=https://mcp.amia.fr/astro_08/
MOSTA_NET_TRANSPORT=restQuick Start — auto-register a new client in 5 minutes
This guide shows how to connect a fresh Node.js project to a remote Octonet
data plane without manually copying API keys around. The auto-register
flow uses OAuth 2.0 Device Authorization Grant (RFC 8628) — your client
prints a user_code, you approve once in your browser, and the API key
is cached locally for all future runs.
Pré-requis côté Octocloud (one-time, par utilisateur)
The portal you point to (MOSTA_AUTH_FLOW_URL) must already have :
- ✅ An account — sign up at
https://octocloud.amia.fr/auth/register - ✅ An active plan — pick a plan at
/dashboard/billing(Free works) - ✅ At least one project — create one at
/dashboard/projects/new(or let the device flow redirect you there automatically — N+7 patch)
The API key the device flow issues is scoped to the project you select.
Step 1 — Install
npm install @mostajs/data-plug
# + driver(s) for direct ORM mode (optional — only if you need MOSTA_DATA=orm)
npm install better-sqlite3 pg mongooseStep 2 — .env.local
# ── Profile cascade (@mostajs/config) ─────────────
MOSTA_ENV=DEV
# ── Data plane mode ───────────────────────────────
MOSTA_DATA=net
MOSTA_NET_URL=https://octonet.amia.fr/
MOSTA_NET_TRANSPORT=rest
# ── Auth-flow portal (where the device flow runs) ─
# Same name on both client and server side — no duplication.
MOSTA_AUTH_FLOW_URL=https://octocloud.amia.fr/
# ── Identity (used by AuthStore for ~/.config/<host>/auth.json) ─
MOSTA_HOST=my-app
MOSTA_CLIENT_ID=my-app-server
# ── Optional ──────────────────────────────────────
# If you already have an API key, export it to skip the device flow :
# MOSTA_NET_API_KEY=sk_live_...
#
# Disable interactive prompt for CI / non-interactive scripts :
# MOSTA_NO_AUTOREGISTER=trueStep 3 — Bootstrap script
Create test-scripts/bootstrap.ts :
// my-app — one-shot bootstrap to obtain and cache the API key.
// Run once : `npm run bootstrap` (or whatever the script is named in package.json).
import { initDataPlane } from '@mostajs/data-plug'
import { getEnv, getCurrentProfile } from '@mostajs/config'
async function main() {
console.log('[bootstrap] profile=' + getCurrentProfile())
console.log('[bootstrap] MOSTA_NET_URL=' + getEnv('MOSTA_NET_URL'))
console.log('[bootstrap] MOSTA_HOST=' + getEnv('MOSTA_HOST', 'mostajs'))
console.log()
console.log('[bootstrap] starting data plane bootstrap...')
console.log('[bootstrap] (no cached key + no env key → device flow will display below)')
console.log()
const { dialect } = await initDataPlane()
console.log()
console.log('[bootstrap] ✓ data plane ready')
console.log('[bootstrap] cache : ~/.config/' + getEnv('MOSTA_HOST', 'mostajs') + '/auth.json')
}
main().catch((err) => {
console.error('[bootstrap] fatal:', err)
process.exit(1)
})Add to package.json :
{
"scripts": {
"bootstrap": "node --env-file=.env.local --import tsx test-scripts/bootstrap.ts"
}
}Step 4 — Run the bootstrap
npm run bootstrapYou'll see :
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Octocloud authentication — first-time setup
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Open this URL in a browser :
https://octocloud.amia.fr/device
2. Enter this code : PGN8-KU6R
(Or scan directly : https://octocloud.amia.fr/device?user_code=PGN8-KU6R)
Expires in 30 min...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Open the URL in your browser :
- Sign in (or sign up — the redirect chain handles it)
- Pick the project the API key should be scoped to (dropdown if you have several)
- Click Authorize
The polling client receives the API key, writes it to
~/.config/<MOSTA_HOST>/auth.json (mode 0600, XDG-compliant), and exits.
Step 5 — Use getDialect() from anywhere in your app
import { getDialect } from '@mostajs/data-plug'
import { MyEntityRepository } from './repositories/my-entity.repository.js'
const dialect = await getDialect()
const repo = new MyEntityRepository(dialect)
const items = await repo.findAll()Subsequent runs skip the device flow — the cached API key is loaded from
~/.config/<MOSTA_HOST>/auth.json. To force re-auth : delete the file.
Switch to ORM (direct DB) mode without changing app code
# Same .env.local, just flip MOSTA_DATA :
MOSTA_DATA=orm
DB_DIALECT=postgres
SGBD_URI=postgresql://user:pass@localhost:5432/mydbYour getDialect() calls work identically. No code change. This is the
core promise of @mostajs/data-plug.
Common issues
| Symptom | Cause | Fix |
|---------|-------|-----|
| device_pending loop forever | You didn't click Authorize | Open the verification URL and approve |
| verification_uri points to wrong domain | MOSTA_AUTH_FLOW_URL env not set, fallback hardcoded | Add MOSTA_AUTH_FLOW_URL=https://your-portal.example.com |
| auth.json missing after Authorize | Bootstrap process was killed too soon | Re-run npm run bootstrap and wait for the message |
| slow_down errors during polling | Polling too fast | Library auto-respects interval from server response — usually 5s |
| not in tenant scope 403 | API key scoped to wrong project | Re-run device flow and pick the correct project on /device |
Headless / CI usage
# Pre-provision the key offline (CI/CD pipeline) :
MOSTA_NET_API_KEY=sk_live_xxxx npm start
# Or fail-fast if no key cached (no interactive prompt) :
MOSTA_NO_AUTOREGISTER=true npm startReal-world example — orphan-care
orphan-care is a Fastify app that uses @mostajs/data-plug in NET
mode against a remote Octonet portal. The full bootstrap took ~3 minutes
end-to-end, including signing in, picking the project, and approving
the device. Below is the concrete file layout that was validated.
.env.local
MOSTA_ENV=DEV
# Data plane → remote Octonet (test environment)
MOSTA_DATA=net
MOSTA_NET_URL=https://octonet-test.amia.fr/
MOSTA_NET_TRANSPORT=rest
# Auth flow → Octocloud (test portal)
MOSTA_AUTH_FLOW_URL=https://octocloud-test.amia.fr/
# Identity → ~/.config/orphan-care/auth.json
MOSTA_HOST=orphan-care
MOSTA_CLIENT_ID=orphan-care-server
# Account ID retrieved post device flow (frontière de tenancy côté Octonet) :
# SELECT account FROM api_keys WHERE label LIKE 'auth-flow:%'
ORPHAN_CARE_ACCOUNT_ID=4dcf4d67-f7f8-4e29-8b45-f8a577046b3b
# Local server port + storage
PORT=3010
HOST=0.0.0.0
STORAGE_FS_ROOT=./data/storagetest-scripts/bootstrap.ts — same shape as the generic example above,
calling initDataPlane() from ../lib/db.js.
Output of npm run bootstrap
[bootstrap] profile=DEV
[bootstrap] MOSTA_NET_URL=https://octonet-test.amia.fr/
[bootstrap] MOSTA_HOST=orphan-care
[bootstrap] starting data plane bootstrap...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Octocloud authentication — first-time setup
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Open this URL in a browser :
https://octocloud-test.amia.fr/device
2. Enter this code : PGN8-KU6R
Expires in 30 min...After clicking Authorize on /device?user_code=PGN8-KU6R :
[bootstrap] ✓ data plane ready (dialect=net-proxy)
[bootstrap] cache : ~/.config/orphan-care/auth.json~/.config/orphan-care/auth.json (mode 0600, persisted across runs) :
{
"version": 1,
"host": "orphan-care",
"endpoint": "https://octocloud-test.amia.fr",
"client_id": "orphan-care-server",
"token": {
"access_token": "sk_live_135059af16b55f27e44cec5b67acba7435323fbeb6884fe3631ab33446ab3336",
"token_type": "Bearer",
"expires_in": 31536000,
"scope": "data-plug.full",
"expires_at": 1809556993484
},
"issued_at": 1778020993484
}npm run dev — server boot is then a no-op for the device flow,
the cached key is loaded automatically :
[orphan-care] boot — profile=DEV, MOSTA_DATA=net, MOSTA_NET_URL=https://octonet-test.amia.fr/
[orphan-care] initializing data plane via @mostajs/data-plug...
[orphan-care] data plane ready (dialect=net-proxy)
Server listening at http://127.0.0.1:3010The API key issued by the device flow has the project ID embedded in its
metadata (metadata.projectId) so the Octonet runtime can scope every
subsequent CRUD call to that project — no extra config needed in the
client app.
API
getDialect(): Promise<IDataDialect>
Retourne le dialect singleton — ORM ou NET selon MOSTA_DATA.
getDataMode(): DataMode
Retourne 'orm' ou 'net'.
isNetMode(): boolean
Raccourci pour getDataMode() === 'net'.
isOrmMode(): boolean
Raccourci pour getDataMode() === 'orm'.
setDialect(dialect): void
Injecte un dialect externe (pour les tests).
resetDialect(): void
Réinitialise le singleton (pour les tests).
getSystemDialect(): Promise<IDataDialect> (v1.2.2+)
Retourne le dialect système (apikeys, RBAC, audit, plans, payments, project-life). Stable au runtime, jamais muté par les routes admin métier. Si bootstrapSystemDialect() n'a pas été appelé, fallback transparent vers le singleton métier.
bootstrapSystemDialect(): Promise<IDataDialect> (v1.2.2+)
Initialise le dialect système au démarrage de l'app. Lit MOSTA_SYSTEM_DIALECT + MOSTA_SYSTEM_URI ; si présents → ouvre une connexion isolée dédiée via openIsolatedDialect, sinon alias singleton métier (rétro-compat). Idempotent.
setSystemDialect(dialect): void (v1.2.2+)
Injecte un dialect système externe (testing / custom implementation). Pendant naturel de setDialect() côté métier.
resetSystemDialect(): void (v1.2.2+)
Réinitialise le dialect système (testing / teardown). Ne ferme PAS la connexion physique.
Architecture
App (.env.local)
├── MOSTA_DATA=net + MOSTA_NET_URL=...
│
├── @mostajs/auth → @mostajs/rbac → data-plug → getDialect()
├── @mostajs/rbac → data-plug → getDialect()
├── @mostajs/audit → data-plug → getDialect()
├── @mostajs/settings → data-plug → getDialect()
├── @mostajs/ticketing → data-plug → getDialect()
└── @mostajs/secu → data-plug → getDialect()
│
┌─────────┴─────────┐
│ │
MOSTA_DATA=orm MOSTA_DATA=net
@mostajs/orm @mostajs/net
SQL direct REST/GraphQL/gRPCModules migrés
| Module | Avant | Après | |--------|-------|-------| | rbac | 212 lignes (data-mode.ts + NET factories) | 106 lignes | | audit | 130 lignes (data-mode.ts + NET factories) | 41 lignes | | settings | 119 lignes (data-mode.ts + NET factories) | 41 lignes | | ticketing | hardcodé ORM | 1 import changé | | secu | hardcodé ORM | 1 import changé |
Release Notes
v1.2.5 — 2026-05-04 — bootstrapSystemDialect passe schemaStrategy au dialect isolé
Bug fix (régression du chantier system dialect, détectée au déploiement test sur amia).
Symptôme
Au premier boot d'octonet-mcp avec MOSTA_SYSTEM_DIALECT + MOSTA_SYSTEM_URI définis (mode multi-base), les tables système (RBAC permission_categories, roles, users, …, api_keys, scopes, scope_values) n'étaient jamais créées dans la base système. Logs :
⚠ RBAC bootstrap skipped: relation "permission_categories" does not exist
⚠ Apikey scope registration skipped: relation "api_key_scopes" does not existCause
bootstrapSystemDialect() appelait openIsolatedDialect({dialect, uri}) sans schemaStrategy. Tous les dialects implémentent initSchema(schemas) ainsi :
async initSchema(schemas) {
const strategy = this.config?.schemaStrategy ?? 'none';
// si strategy === 'none' → no-op
// si strategy === 'update'/'create' → crée/migre tables
}→ Sans schemaStrategy, strategy='none' par défaut → initSchema no-op → tables jamais créées même quand bootstrapRbac ou registerScope les demandent.
Fix
// data-plug v1.2.5
export async function bootstrapSystemDialect() {
// …
const schemaStrategy = getEnv('MOSTA_SYSTEM_SCHEMA_STRATEGY', 'update');
const sys = await openIsolatedDialect({ dialect, uri, schemaStrategy });
// …
}Override possible via env var MOSTA_SYSTEM_SCHEMA_STRATEGY (default 'update' — idempotent : crée si absent, sinon migration douce).
Bump
1.2.4 → 1.2.5 (patch — bug fix sans changement d'API).
v1.2.4 — 2026-05-03 — Façade ORM complète (registry helpers)
Complète la façade v1.2.3 en ré-exportant aussi les registry helpers (registre global des schemas). Permet aux modules consumers de résoudre des entités par nom à l'exécution sans import('@mostajs/orm') direct.
export {
registerSchema, registerSchemas,
getSchema, getSchemaByCollection,
getAllSchemas, getEntityNames,
hasSchema, validateSchemas, clearRegistry,
} from '@mostajs/orm'Bump 1.2.3 → 1.2.4 (patch — additif pur).
v1.2.3 — 2026-05-03 — Façade ORM (re-exports)
data-plug ré-exporte les primitives @mostajs/orm consommées par les modules applicatifs (api-keys, rbac, audit, project-life, payment, subscriptions-plan, …). Conformément au principe « les modules @mostajs passent par data-plug, jamais hardcoder un dialect ou importer @mostajs/orm directement », data-plug devient le point d'entrée unique data-access.
Exports ajoutés (rétro-compat 100 %, additif pur)
// Class
export { BaseRepository, normalizeDoc, normalizeDocs } from '@mostajs/orm'
// Types
export type {
IDialect, EntitySchema, FilterQuery, QueryOptions, IRepository,
FieldDef, FieldType, RelationDef, RelationType, IndexDef,
} from '@mostajs/orm'Migration des modules consumers
// Avant (couplage dur à orm)
import { BaseRepository } from '@mostajs/orm'
import type { IDialect, EntitySchema } from '@mostajs/orm'
// Après (façade data-plug)
import { BaseRepository } from '@mostajs/data-plug'
import type { IDialect, EntitySchema } from '@mostajs/data-plug'Bump
1.2.2 → 1.2.3 (patch — pure addition d'exports).
v1.2.2 — 2026-05-03 — System dialect séparé du singleton métier
Introduit getSystemDialect / bootstrapSystemDialect / setSystemDialect / resetSystemDialect pour découpler les modules SYSTÈME (apikeys, RBAC, audit, plans, payments, project-life) du singleton MÉTIER mutable.
Motivation
Le singleton getDialect() est mutable au runtime via /api/change-dialect, /api/reload-config, /api/reconnect — légitime côté métier. Mais les modules système (qui hébergent les apikeys, scopes, RBAC users, plans de souscription, audit) doivent vivre dans une base STABLE qui ne suit pas ces mutations, sinon les apikeys deviennent introuvables après un changement de dialect métier.
API ajoutée (rétro-compat 100 %)
getSystemDialect()— retourne le dialect système. Fallback singleton métier si non bootstrappé.bootstrapSystemDialect()— initialise au démarrage. LitMOSTA_SYSTEM_DIALECT+MOSTA_SYSTEM_URI:- présents → connexion isolée dédiée via
openIsolatedDialect(prod multi-base) - absents → alias singleton métier (rétro-compat mono-base)
- présents → connexion isolée dédiée via
setSystemDialect(d)— injection (testing / custom implementation, pendant naturel desetDialectcôté métier).resetSystemDialect()— reset (testing / teardown).
Configuration cible (prod multi-base)
# Métier (mutable via IHM admin) :
DB_DIALECT=postgres
SGBD_URI=postgresql://hmd:***@127.0.0.1:5432/octonet_business
# Système (stable, jamais touché par /api/change-dialect) :
MOSTA_SYSTEM_DIALECT=postgres
MOSTA_SYSTEM_URI=postgresql://hmd:***@127.0.0.1:5432/octonet_systemTests
6 / 6 passent (tests-scripts/test-system-dialect.mjs) :
| # | Assertion |
|---|-----------|
| T1 | fallback alias business |
| T2 | getSystemDialect() returns bootstrapped |
| T3 | system distinct from business |
| T4 | system survives business disconnect() |
| T5 | reset then bootstrap re-fallbacks |
| T6 | bootstrap idempotent |
Bump
1.2.1 → 1.2.2 (minor — API additive uniquement, aucun breaking change).
v1.2.1 — 2026-05-03 — Trajet 2 (apikey périmée) + URL découplée
3 ajouts purement techniques pour clôturer le flow auto-register E2E. Cohérent avec l'architecture Octonet=cité, Octocloud=maire (cf. memory/reference_octonet_octocloud_architecture.md).
1. MOSTA_AUTH_FLOW_URL env distinct
Le device flow tape sur Octocloud (le maire qui distribue les clés), PAS sur Octonet (la cité qui héberge le data plane). Si MOSTA_AUTH_FLOW_URL est défini, on l'utilise ; sinon fallback MOSTA_NET_URL pour rétro-compat v1.2.0.
# Configuration cible recommandée v1.2.1+ :
MOSTA_AUTH_FLOW_URL=https://octocloud.amia.fr # device flow (le maire)
MOSTA_NET_URL=https://octonet.amia.fr # data plane (la cité)
# Rétro-compat v1.2.0 (un seul endpoint pour tout) :
MOSTA_NET_URL=https://octocloud.amia.fr # auth + data via même endpoint2. EnsureApiKeyOptions.forceRefresh
Cas d'usage trajet 2 : @mostajs/net-client-js détecte 401 sur un call data plane → callback onApiKeyInvalid → appel à ensureApiKey({ forceRefresh: true }) qui invalide le cache local et redéclenche le device flow vers Octocloud.
import { ensureApiKey } from '@mostajs/data-plug'
netClient.onApiKeyInvalid(async () => {
return await ensureApiKey({ forceRefresh: true })
})Comportement avec forceRefresh: true :
- Ignore le cache
~/.config/<host>/auth.json - Ignore
MOSTA_NET_API_KEYenv (peut être elle-même périmée) - Ignore
MOSTA_NO_AUTOREGISTER(caller veut absolument refresh, il a déjà eu un 401) - Appelle
flow.refresh()qui clear le store + lance un nouveau device flow
Note CI / non-interactif : si
forceRefresh: true+ environnement non-interactif (pas de browser pour approve), le flow timeout naturellement. Le caller doit gérer ce cas.
3. clearCachedApiKey({ host? }) helper exporté
Supprime l'apikey cached localement (~/.config/<host>/auth.json). No-op si pas de cache existant ou si @mostajs/auth-flow non installé.
import { clearCachedApiKey } from '@mostajs/data-plug'
// Sign-out explicite client (orphan-care --logout)
await clearCachedApiKey({ host: 'orphan-care' })
// Le prochain ensureApiKey() redéclenchera un device flowCas d'usage :
- Sign-out explicite côté client
- Outils admin qui purgent les apikeys locales
- Tests qui doivent partir d'un état propre
Tests
11 nouveaux assertions dans tests-scripts/test-auto-register.ts (12 → 23 assertions) :
- T2.A/B/C —
MOSTA_AUTH_FLOW_URLpriorité + fallback NET_URL + throw si aucun - T3.A —
forceRefreshignoreMOSTA_NET_API_KEYenv - T3.B —
forceRefreshignoreMOSTA_NO_AUTOREGISTER+ cache - T4 —
clearCachedApiKey()no-op tolérant
Rétro-compat
100% — apps existantes en v1.2.0 (qui utilisent uniquement MOSTA_NET_URL) continuent à fonctionner sans changement. Les 3 ajouts sont opt-in (env var ou option de fonction).
Plan détaillé
Cf. docs/PLAN-V1.2.1.md — checklist d'exécution + tests + commit format.
Récap technique fichier-par-fichier
src/auto-register.ts (modifié) :
MOSTA_AUTH_FLOW_URLenv distinct (fallbackMOSTA_NET_URLpour rétro-compat v1.2.0). Le device flow tape sur Octocloud (le maire qui distribue les clés), PAS sur Octonet (la cité qui héberge le data plane).EnsureApiKeyOptions.forceRefresh— invalide cache + relance fresh device flow.- Cas d'usage : net-client-js détecte 401 → callback
onApiKeyInvalid→ensureApiKey({ forceRefresh: true })→ re-auth automatique. - Comportement : ignore env
MOSTA_NET_API_KEY, ignoreMOSTA_NO_AUTOREGISTER, appelleflow.refresh()au lieu deflow.run().
- Cas d'usage : net-client-js détecte 401 → callback
clearCachedApiKey({ host })helper exporté pour sign-out explicite, outils admin, tests propres. No-op tolérant si pas de cache ou@mostajs/auth-flowpas installé.
src/index.ts (modifié) : re-export clearCachedApiKey en plus d'ensureApiKey.
tests-scripts/test-auto-register.ts (modifié) : +11 nouveaux assertions (12 → 23) :
- T2.A/B/C —
MOSTA_AUTH_FLOW_URLpriorité + fallbackNET_URL+ throw si aucun - T3.A —
forceRefreshignoreMOSTA_NET_API_KEYenv - T3.B —
forceRefreshignoreMOSTA_NO_AUTOREGISTER+ cache - T4 —
clearCachedApiKey()no-op tolérant
README.md : section Release Notes v1.2.1 + usage exemple trajet 2.
docs/PLAN-V1.2.1.md (nouveau) : plan détaillé d'exécution (checklist 12 items, tests, commit format) — référence pour reprise future.
Bump
1.2.0 → 1.2.1.
Note rétro-compat
100% — apps en v1.2.0 ne sont pas affectées (les 3 ajouts sont opt-in via env var ou option de fonction).
v1.2.0 — 2026-05-02 — Auto-register apikey via @mostajs/auth-flow (Session N+4 a)
Première moitié de Session N+4 du chantier auto-register Octonet (cf. Octonet-as-Supabase/11-AUTOREGISTER-FLOW-ROADMAP.md §2.2).
Quand data-plug tourne en mode NET (MOSTA_DATA=net) et que MOSTA_NET_API_KEY n'est pas défini, on déclenche un device flow OAuth 2.0 (RFC 8628) auprès du serveur Octonet pour obtenir une apikey valide. C'est l'étape "data-plug appelle Octonet en premier" du flow end-to-end.
src/auto-register.ts (~140 lignes)
ensureApiKey({ store?, clientId?, scope?, host?, onCodeIssued? })— async- Ordre de résolution :
MOSTA_NET_API_KEYenv var → court-circuit, return tel quel- Token cached dans
~/.config/<host>/auth.json(60s skew) → return - Si
MOSTA_NO_AUTOREGISTER=true→ throw avec message explicite - Sinon → device flow auprès de
${MOSTA_NET_URL}/api/v1/auth/device/*
@mostajs/auth-flowen LAZY IMPORT (peerDep optional) — data-plug reste utilisable sans lui pour les hôtes ayant leur propre stratégie d'apikeydefaultTerminalRenderer: UX CLI propre (URL, user_code, expiration en min)- Lecture env via
@mostajs/config(cascadeMOSTA_ENV=DEV|TEST|PRODhonorée pourMOSTA_NET_URL,MOSTA_NET_API_KEY,MOSTA_HOST,MOSTA_CLIENT_ID,MOSTA_NO_AUTOREGISTER)
src/index.ts modifié
initNetDialect(): siMOSTA_NET_API_KEYabsent, appelleensureApiKey()AVANT d'instancierNetClient. Pattern lazy-import (./auto-register.js) pour ne pas tirer le code si l'host fournit l'apikey explicitement.- Re-export
ensureApiKey+EnsureApiKeyOptionsau bottom du barrel pour les hôtes qui veulent déclencher manuellement (script CLI bootstrap).
Dépendances
@mostajs/auth-flowajouté enpeerDependencyoptional +devDependency^0.1.0-alpha.3(pour les types tsc, pas requis runtime sans device flow).
Tests
12 nouveaux assertions (tests-scripts/test-auto-register.ts) — pattern @mostajs/config :
- Cascade
MOSTA_ENV=TEST→TEST_MOSTA_NET_API_KEYpriorité sur plain MOSTA_NET_API_KEYexplicite → court-circuit- Cached token valide via store custom → return direct (URL réelle
https://octonet.amia.fr/) MOSTA_NO_AUTOREGISTER=true+ pas de cache → throw expliciteMOSTA_NO_AUTOREGISTER=true+ cached → return cached- Sans
MOSTA_NET_URL→ throw clair MOSTA_CLIENT_IDoverride via env
URL réelle utilisée dans les tests : https://octonet.amia.fr/ — c'est la cible qu'on attaquera dans les sessions N+4(b/c) côté octonet-cloud routes + /device.
Bump 1.1.0 → 1.2.0.
Reste pour clore Session N+4
- N+4(b) :
octonet-cloudroutes/api/v1/auth/device/{authorize,token,approve} - N+4(c) :
octonet-cloud/devicepage (Server Component branding + consent)
Trajet d'un disconnect()
mosta-net/server.ts → data-plug.disconnect()
data-plug/index.ts:139 → orm.disconnectDialect()
mosta-orm/core/factory.ts:176 → currentDialect.disconnect()
postgres.dialect.ts:92 → pool.end()
this.pool = null
factory.ts:179-180 → currentDialect = null
currentConfig = null
index.ts:153 → setGlobalDialect(null) ← cache
data-plug vidé
Trajet d'un getDialect() immédiatement après
data-plug.getDialect() index.ts:99 cached = getGlobalDialect() = null index.ts:106 initOrmDialect() index.ts:163 orm.getDialect() factory.ts:134 currentDialect = null factory.ts:138 cfg = getConfigFromEnv() ← relit DB_DIALECT, SGBD_URI factory.ts:143 currentDialect = createDialect() ← nouvelle instance pg factory.ts:144 await connect(cfg) ← nouveau Pool ouvert factory.ts:148 initSchema(getAllSchemas()) index.ts:109 setGlobalDialect(dialect) ← ré-injecté dans le cache global
Conclusion clé : la chaîne de reconnexion est fonctionnellement saine
Usage exemple
// Variables d'environnement (cascade MOSTA_ENV honorée)
// MOSTA_DATA=net
// MOSTA_NET_URL=https://octonet.amia.fr/
// (pas de MOSTA_NET_API_KEY → device flow déclenché au premier appel)
import { getDialect } from '@mostajs/data-plug'
// Au boot, si pas d'apikey en env :
// 1. ~/.config/octonet/auth.json lu — si valide → utilisée
// 2. Sinon → terminal affiche URL + user_code, l'user approuve dans browser,
// apikey émise + persistée
// 3. NetClient initialisé avec l'apikey, calls DB possibles
const dialect = await getDialect()
const users = await dialect.find(UserSchema, {})Pour bypass en CI ou scripts non-interactifs :
export MOSTA_NET_API_KEY=ok_existing_apikey
export MOSTA_NO_AUTOREGISTER=trueLicence
AGPL-3.0-or-later — Dr Hamid MADANI [email protected]
