0.0.1-alpha.5
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: CHANGELOG.md -->
|
<!-- file: CHANGELOG.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Changelog SaseDev Knowledge
|
# Changelog SaseDev Knowledge
|
||||||
|
|
||||||
@@ -20,5 +20,6 @@ Ce fichier décrit les changements utiles des releases publiées. Les détails d
|
|||||||
- fixe le frontend Web PGR comme laboratoire de validation des templates statiques et des assets avant adaptation du markup runtime en Maud ;
|
- fixe le frontend Web PGR comme laboratoire de validation des templates statiques et des assets avant adaptation du markup runtime en Maud ;
|
||||||
- matérialise ce laboratoire sous `Web/knowledge-template-lab/` avec npm, TypeScript strict, Vite, Sass, Bootstrap et manifest Vite ;
|
- matérialise ce laboratoire sous `Web/knowledge-template-lab/` avec npm, TypeScript strict, Vite, Sass, Bootstrap et manifest Vite ;
|
||||||
- retient l’intégration production Actix/Maud via le manifest Vite comme contrat d’asset mapping à réimplémenter proprement, sans copier mécaniquement le corpus historique ;
|
- retient l’intégration production Actix/Maud via le manifest Vite comme contrat d’asset mapping à réimplémenter proprement, sans copier mécaniquement le corpus historique ;
|
||||||
|
- matérialise `knowledge-web-lib` et trois launchers Actix distincts avec rendu Maud, dev-server Vite configurable, résolution du manifest de production et service des assets hashés ;
|
||||||
- conserve comme corpus d'inspiration séparé les archives historiques `actix-auth-app` et `amahil-project`, sans importer leurs fonctionnalités inachevées comme exigences ;
|
- conserve comme corpus d'inspiration séparé les archives historiques `actix-auth-app` et `amahil-project`, sans importer leurs fonctionnalités inachevées comme exigences ;
|
||||||
- prépare un workspace Rust et une baseline Web composables sans construire prématurément la plateforme complète.
|
- prépare un workspace Rust et une baseline Web composables sans construire prématurément la plateforme complète.
|
||||||
|
|||||||
1160
Cargo.lock
generated
1160
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 8
|
# version: 9
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -7,16 +7,21 @@ members = [
|
|||||||
"crates/knowledge-domain-lib",
|
"crates/knowledge-domain-lib",
|
||||||
"crates/knowledge-git-api",
|
"crates/knowledge-git-api",
|
||||||
"crates/knowledge-sync-api",
|
"crates/knowledge-sync-api",
|
||||||
|
"crates/knowledge-web-lib",
|
||||||
|
"crates/knowledge-pgr-server",
|
||||||
|
"crates/knowledge-howtos-server",
|
||||||
|
"crates/knowledge-snippets-server",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.1-alpha.4.fix.2"
|
version = "0.0.1-alpha.5"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
repository = "https://git.sasedev.com/Sasedev/sasedev-knowledge"
|
repository = "https://git.sasedev.com/Sasedev/sasedev-knowledge"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
knowledge-domain-lib = { path = "crates/knowledge-domain-lib" }
|
knowledge-domain-lib = { path = "crates/knowledge-domain-lib" }
|
||||||
|
knowledge-web-lib = { path = "crates/knowledge-web-lib" }
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing_docs = "warn"
|
missing_docs = "warn"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: README.md -->
|
<!-- file: README.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# SaseDev Knowledge
|
# SaseDev Knowledge
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ PostgreSQL est mutualisable et constitue la baseline retenue pour l'état transv
|
|||||||
|
|
||||||
Chaque application Web doit pouvoir exposer sa propre surface d'administration protégée pour son domaine. Les chemins HTTP exacts ne sont pas encore figés. Une éventuelle quatrième application d'administration pourra agréger des fonctions transversales, mais elle ne remplacera pas les contrôles d'autorisation propres aux trois serveurs.
|
Chaque application Web doit pouvoir exposer sa propre surface d'administration protégée pour son domaine. Les chemins HTTP exacts ne sont pas encore figés. Une éventuelle quatrième application d'administration pourra agréger des fonctions transversales, mais elle ne remplacera pas les contrôles d'autorisation propres aux trois serveurs.
|
||||||
|
|
||||||
Le workspace Rust matérialise désormais trois frontières passives avant toute implémentation technique : `knowledge-domain-lib` pour les identités de famille/source, `knowledge-git-api` pour le contrat Git générique et `knowledge-sync-api` pour les signaux de synchronisation. Ces deux crates `-api` sont justifiées par des implémentations remplaçables ou multiples ; elles ne dépendent ni d'Actix, ni de Gitea, ni de PostgreSQL.
|
Le workspace Rust matérialise les trois frontières passives de fondation : `knowledge-domain-lib` pour les identités de famille/source, `knowledge-git-api` pour le contrat Git générique et `knowledge-sync-api` pour les signaux de synchronisation. Ces deux crates `-api` sont justifiées par des implémentations remplaçables ou multiples ; elles ne dépendent ni d'Actix, ni de Gitea, ni de PostgreSQL.
|
||||||
|
|
||||||
## Frontend de référence et rendu Maud
|
## Frontend de référence et rendu Maud
|
||||||
|
|
||||||
@@ -43,6 +43,10 @@ Les templates statiques sont un **harness de conception/validation**, pas le mot
|
|||||||
|
|
||||||
Le mécanisme observé dans `actix-auth-app` est documenté comme corpus technique à adapter, pas comme code autoritaire à copier.
|
Le mécanisme observé dans `actix-auth-app` est documenté comme corpus technique à adapter, pas comme code autoritaire à copier.
|
||||||
|
|
||||||
|
`alpha.5` matérialise `knowledge-web-lib` et les trois launchers `knowledge-*-server`. La bibliothèque commune possède le rendu Maud, la sélection dev-server/manifest Vite et le service des assets de production ; les trois `main.rs` ne contiennent que la sélection de façade et du nom de variable d'environnement de bind.
|
||||||
|
|
||||||
|
Le runtime exige un bind configuré par launcher et l'une des deux sources d'assets suivantes : `KNOWLEDGE_VITE_DEV_ORIGIN` en développement ou `KNOWLEDGE_VITE_MANIFEST_PATH` en production. `KNOWLEDGE_ASSET_BASE_PATH` peut remplacer le préfixe `/` de production. Aucune route admin/login n'est matérialisée à ce stade.
|
||||||
|
|
||||||
## Invariants de fondation
|
## Invariants de fondation
|
||||||
|
|
||||||
- Git est la source autoritaire du contenu versionné.
|
- Git est la source autoritaire du contenu versionné.
|
||||||
|
|||||||
6
RULES.md
6
RULES.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: RULES.md -->
|
<!-- file: RULES.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Règles SaseDev Knowledge
|
# Règles SaseDev Knowledge
|
||||||
|
|
||||||
@@ -177,6 +177,10 @@ Le build Vite de production MUST produire un mapping machine-readable des entré
|
|||||||
|
|
||||||
Le pattern de loader observé dans `actix-auth-app` est un corpus d’inspiration. Sa reprise doit être réévaluée selon les règles Rust Knowledge : pas de `panic!`/`unwrap`/`expect`, erreurs récupérables, API minimale, tests ciblés et séparation claire entre debug/dev et production.
|
Le pattern de loader observé dans `actix-auth-app` est un corpus d’inspiration. Sa reprise doit être réévaluée selon les règles Rust Knowledge : pas de `panic!`/`unwrap`/`expect`, erreurs récupérables, API minimale, tests ciblés et séparation claire entre debug/dev et production.
|
||||||
|
|
||||||
|
`knowledge-web-lib` possède ce contrat partagé à partir d'`alpha.5`. En développement, `KNOWLEDGE_VITE_DEV_ORIGIN` MUST fournir l'origin du dev server et le renderer charge `@vite/client` puis `src/main.ts`. En production, `KNOWLEDGE_VITE_MANIFEST_PATH` MUST désigner le manifest généré et les noms hashés sont résolus depuis l'entrée stable `src/main.ts`. `KNOWLEDGE_ASSET_BASE_PATH` MAY remplacer le préfixe de production `/`.
|
||||||
|
|
||||||
|
Les trois launchers MUST recevoir leur socket de bind par configuration (`KNOWLEDGE_PGR_BIND_ADDRESS`, `KNOWLEDGE_HOWTOS_BIND_ADDRESS`, `KNOWLEDGE_SNIPPETS_BIND_ADDRESS`). Aucun port de déploiement n'est choisi par le code. Les seules routes matérialisées par la fondation sont `/` et `/healthz`; cela ne fixe aucune convention future pour l'authentification ou l'administration.
|
||||||
|
|
||||||
## Rust et crates
|
## Rust et crates
|
||||||
|
|
||||||
Le préfixe de crate retenu est `knowledge-`.
|
Le préfixe de crate retenu est `knowledge-`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: Web/knowledge-template-lab/README.md -->
|
<!-- file: Web/knowledge-template-lab/README.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Knowledge template lab
|
# Knowledge template lab
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ L'entrée stable de départ est :
|
|||||||
src/main.ts
|
src/main.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
Cette entrée importe également la chaîne Sass. L'intégration Maud prévue en `alpha.5` devra résoudre les fichiers hashés depuis le manifest plutôt que coder leurs noms.
|
Cette entrée importe également la chaîne Sass. À partir d'`alpha.5`, `knowledge-web-lib` résout effectivement les fichiers hashés depuis ce manifest plutôt que coder leurs noms. En développement, la même bibliothèque peut cibler un origin Vite configuré et charger `@vite/client` puis `src/main.ts`.
|
||||||
|
|
||||||
## Routes et administration
|
## Routes et administration
|
||||||
|
|
||||||
|
|||||||
15
crates/knowledge-howtos-server/Cargo.toml
Normal file
15
crates/knowledge-howtos-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# file: crates/knowledge-howtos-server/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "knowledge-howtos-server"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
knowledge-web-lib.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
8
crates/knowledge-howtos-server/src/main.rs
Normal file
8
crates/knowledge-howtos-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// file: crates/knowledge-howtos-server/src/main.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Thin launcher for the knowledge-howtos-server process.
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Howtos, "KNOWLEDGE_HOWTOS_BIND_ADDRESS");
|
||||||
|
}
|
||||||
15
crates/knowledge-pgr-server/Cargo.toml
Normal file
15
crates/knowledge-pgr-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# file: crates/knowledge-pgr-server/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "knowledge-pgr-server"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
knowledge-web-lib.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
8
crates/knowledge-pgr-server/src/main.rs
Normal file
8
crates/knowledge-pgr-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// file: crates/knowledge-pgr-server/src/main.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Thin launcher for the knowledge-pgr-server process.
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Pgr, "KNOWLEDGE_PGR_BIND_ADDRESS");
|
||||||
|
}
|
||||||
15
crates/knowledge-snippets-server/Cargo.toml
Normal file
15
crates/knowledge-snippets-server/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# file: crates/knowledge-snippets-server/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "knowledge-snippets-server"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
knowledge-web-lib.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
8
crates/knowledge-snippets-server/src/main.rs
Normal file
8
crates/knowledge-snippets-server/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// file: crates/knowledge-snippets-server/src/main.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Thin launcher for the knowledge-snippets-server process.
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
return knowledge_web_lib::run_server(knowledge_web_lib::ServerSite::Snippets, "KNOWLEDGE_SNIPPETS_BIND_ADDRESS");
|
||||||
|
}
|
||||||
20
crates/knowledge-web-lib/Cargo.toml
Normal file
20
crates/knowledge-web-lib/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# file: crates/knowledge-web-lib/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "knowledge-web-lib"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-files = { version = "0.7.0", default-features = false }
|
||||||
|
actix-web = { version = "4.15.0", default-features = false }
|
||||||
|
knowledge-domain-lib.workspace = true
|
||||||
|
maud = { version = "0.27.0", features = ["actix-web"] }
|
||||||
|
serde = { version = "1.0.229", features = ["derive"] }
|
||||||
|
serde_json = "1.0.151"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
195
crates/knowledge-web-lib/src/assets.rs
Normal file
195
crates/knowledge-web-lib/src/assets.rs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/assets.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Vite development and production asset resolution.
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
|
struct ManifestEntry {
|
||||||
|
file: String,
|
||||||
|
#[serde(default)]
|
||||||
|
css: Vec<String>,
|
||||||
|
#[serde(default, rename = "isEntry")]
|
||||||
|
is_entry: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal resolved asset set consumed by the Maud renderer and Actix runtime.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub(crate) struct ViteAssets {
|
||||||
|
scripts: Vec<String>,
|
||||||
|
stylesheets: Vec<String>,
|
||||||
|
static_assets_directory: Option<std::path::PathBuf>,
|
||||||
|
static_mount_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::ViteAssets {
|
||||||
|
/// Build development asset references from a configured Vite origin.
|
||||||
|
pub(crate) fn development(origin: &str) -> Result<crate::ViteAssets, String> {
|
||||||
|
let normalized = origin.trim().trim_end_matches('/');
|
||||||
|
if normalized.is_empty() {
|
||||||
|
return Err("KNOWLEDGE_VITE_DEV_ORIGIN must not be empty".to_owned());
|
||||||
|
}
|
||||||
|
return Ok(crate::ViteAssets {
|
||||||
|
scripts: vec![format!("{normalized}/@vite/client"), format!("{normalized}/src/main.ts")],
|
||||||
|
stylesheets: Vec::new(),
|
||||||
|
static_assets_directory: None,
|
||||||
|
static_mount_path: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve production asset references from Vite manifest JSON text.
|
||||||
|
pub(crate) fn from_manifest_text(manifest: &str, asset_base: &str, static_assets_directory: std::path::PathBuf) -> Result<crate::ViteAssets, String> {
|
||||||
|
let entries: std::collections::BTreeMap<String, ManifestEntry> = match serde_json::from_str(manifest) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(format!("invalid Vite manifest JSON: {error}")),
|
||||||
|
};
|
||||||
|
let entry = match entries.get("src/main.ts") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Err("Vite manifest is missing src/main.ts".to_owned()),
|
||||||
|
};
|
||||||
|
if !entry.is_entry {
|
||||||
|
return Err("Vite manifest src/main.ts is not marked as an entry".to_owned());
|
||||||
|
}
|
||||||
|
if entry.file.trim().is_empty() {
|
||||||
|
return Err("Vite manifest src/main.ts has an empty JavaScript file".to_owned());
|
||||||
|
}
|
||||||
|
let javascript = match asset_url(asset_base, entry.file.as_str()) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let mut stylesheets = Vec::new();
|
||||||
|
for stylesheet in &entry.css {
|
||||||
|
let resolved = match asset_url(asset_base, stylesheet.as_str()) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
stylesheets.push(resolved);
|
||||||
|
}
|
||||||
|
let static_mount_path = local_static_mount_path(asset_base);
|
||||||
|
return Ok(crate::ViteAssets {
|
||||||
|
scripts: vec![javascript],
|
||||||
|
stylesheets,
|
||||||
|
static_assets_directory: Some(static_assets_directory),
|
||||||
|
static_mount_path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return module script URLs in load order.
|
||||||
|
pub(crate) fn scripts(&self) -> &[String] {
|
||||||
|
return self.scripts.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return production stylesheet URLs. Development CSS is injected by Vite.
|
||||||
|
pub(crate) fn stylesheets(&self) -> &[String] {
|
||||||
|
return self.stylesheets.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the local production assets directory when static serving is available.
|
||||||
|
pub(crate) fn static_assets_directory(&self) -> Option<&std::path::Path> {
|
||||||
|
match self.static_assets_directory.as_deref() {
|
||||||
|
Some(value) => return Some(value),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the local HTTP mount used for built assets, or `None` for an external asset base.
|
||||||
|
pub(crate) fn static_mount_path(&self) -> Option<&str> {
|
||||||
|
match self.static_mount_path.as_deref() {
|
||||||
|
Some(value) => return Some(value),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the active Vite asset mode from process environment variables.
|
||||||
|
pub(crate) fn load_assets_from_environment() -> Result<crate::ViteAssets, std::io::Error> {
|
||||||
|
let development_origin = environment_value("KNOWLEDGE_VITE_DEV_ORIGIN");
|
||||||
|
let manifest_path = environment_value("KNOWLEDGE_VITE_MANIFEST_PATH");
|
||||||
|
match (development_origin, manifest_path) {
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::InvalidInput,
|
||||||
|
"configure either KNOWLEDGE_VITE_DEV_ORIGIN or KNOWLEDGE_VITE_MANIFEST_PATH, not both",
|
||||||
|
));
|
||||||
|
},
|
||||||
|
(Some(origin), None) => {
|
||||||
|
let assets = match crate::ViteAssets::development(origin.as_str()) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error)),
|
||||||
|
};
|
||||||
|
return Ok(assets);
|
||||||
|
},
|
||||||
|
(None, Some(path)) => {
|
||||||
|
return load_manifest_assets(path.as_str());
|
||||||
|
},
|
||||||
|
(None, None) => {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "configure KNOWLEDGE_VITE_DEV_ORIGIN or KNOWLEDGE_VITE_MANIFEST_PATH"));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn environment_value(name: &str) -> Option<String> {
|
||||||
|
match std::env::var(name) {
|
||||||
|
Ok(value) => {
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(value);
|
||||||
|
},
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_manifest_assets(path: &str) -> Result<crate::ViteAssets, std::io::Error> {
|
||||||
|
let manifest_path = std::path::PathBuf::from(path);
|
||||||
|
let text = match std::fs::read_to_string(manifest_path.as_path()) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let manifest_directory = match manifest_path.parent() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Vite manifest path has no parent directory"));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let output_root = match manifest_directory.parent() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Vite manifest path must be located below an output root"));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let asset_base = match environment_value("KNOWLEDGE_ASSET_BASE_PATH") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => "/".to_owned(),
|
||||||
|
};
|
||||||
|
let assets = match crate::ViteAssets::from_manifest_text(text.as_str(), asset_base.as_str(), output_root.join("assets")) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error)),
|
||||||
|
};
|
||||||
|
return Ok(assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asset_url(base: &str, relative: &str) -> Result<String, String> {
|
||||||
|
let normalized_base = base.trim();
|
||||||
|
let normalized_relative = relative.trim().trim_start_matches('/');
|
||||||
|
if normalized_base.is_empty() {
|
||||||
|
return Err("KNOWLEDGE_ASSET_BASE_PATH must not be empty".to_owned());
|
||||||
|
}
|
||||||
|
if normalized_relative.is_empty() {
|
||||||
|
return Err("Vite manifest contains an empty asset path".to_owned());
|
||||||
|
}
|
||||||
|
if normalized_base == "/" {
|
||||||
|
return Ok(format!("/{normalized_relative}"));
|
||||||
|
}
|
||||||
|
return Ok(format!("{}/{normalized_relative}", normalized_base.trim_end_matches('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_static_mount_path(base: &str) -> Option<String> {
|
||||||
|
let normalized = base.trim();
|
||||||
|
if !normalized.starts_with('/') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if normalized == "/" {
|
||||||
|
return Some("/assets".to_owned());
|
||||||
|
}
|
||||||
|
return Some(format!("{}/assets", normalized.trim_end_matches('/')));
|
||||||
|
}
|
||||||
27
crates/knowledge-web-lib/src/lib.rs
Normal file
27
crates/knowledge-web-lib/src/lib.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/lib.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Shared Actix Web and Maud composition for the three Knowledge server launchers.
|
||||||
|
//!
|
||||||
|
//! The crate owns the minimal HTTP runtime, Maud rendering and Vite asset resolution shared by
|
||||||
|
//! PGR, HOWTOs and snippets. It does not own authentication, administration or persistence.
|
||||||
|
|
||||||
|
mod assets;
|
||||||
|
mod page;
|
||||||
|
mod runtime;
|
||||||
|
mod site;
|
||||||
|
|
||||||
|
/// Start one configured Knowledge Actix Web server.
|
||||||
|
pub use self::runtime::run_server;
|
||||||
|
/// Identify the public Knowledge facade owned by one server launcher.
|
||||||
|
pub use self::site::ServerSite;
|
||||||
|
|
||||||
|
/// Internal asset description shared by the renderer and runtime.
|
||||||
|
pub(crate) use self::assets::ViteAssets;
|
||||||
|
/// Internal loader that resolves development or production assets from environment configuration.
|
||||||
|
pub(crate) use self::assets::load_assets_from_environment;
|
||||||
|
/// Internal Maud renderer for the public foundation page.
|
||||||
|
pub(crate) use self::page::render_index_page;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod unit_tests;
|
||||||
52
crates/knowledge-web-lib/src/page.rs
Normal file
52
crates/knowledge-web-lib/src/page.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/page.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Maud rendering adapted from the static template-lab contract.
|
||||||
|
|
||||||
|
/// Render the minimal public page shared by the three server launchers.
|
||||||
|
pub(crate) fn render_index_page(site: crate::ServerSite, assets: &crate::ViteAssets) -> maud::Markup {
|
||||||
|
return maud::html! {
|
||||||
|
(maud::DOCTYPE)
|
||||||
|
html lang="fr" {
|
||||||
|
head {
|
||||||
|
meta charset="UTF-8";
|
||||||
|
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||||
|
title { (site.page_title()) }
|
||||||
|
@for stylesheet in assets.stylesheets() {
|
||||||
|
link rel="stylesheet" href=(stylesheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
main class="container py-5 knowledge-template-shell" data-content-family=(family_slug(site.content_family())) {
|
||||||
|
header class="mb-5" {
|
||||||
|
p class="text-body-secondary mb-2" { "SaseDev Knowledge" }
|
||||||
|
h1 class="display-5" { (site.heading()) }
|
||||||
|
p class="lead mb-0" { (site.lead()) }
|
||||||
|
}
|
||||||
|
section class="row g-4" aria-label="Famille de contenu" {
|
||||||
|
div class="col-12" {
|
||||||
|
article class="card knowledge-family-card" {
|
||||||
|
div class="card-body" {
|
||||||
|
h2 class="h4" { (site.heading()) }
|
||||||
|
p class="card-text" { (site.lead()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@for script in assets.scripts() {
|
||||||
|
script type="module" src=(script) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn family_slug(family: knowledge_domain_lib::ContentFamily) -> &'static str {
|
||||||
|
match family {
|
||||||
|
knowledge_domain_lib::ContentFamily::Pgr => return "pgr",
|
||||||
|
knowledge_domain_lib::ContentFamily::Howto => return "howtos",
|
||||||
|
knowledge_domain_lib::ContentFamily::Snippet => return "snippets",
|
||||||
|
knowledge_domain_lib::ContentFamily::ProjectLocal => return "project-local",
|
||||||
|
}
|
||||||
|
}
|
||||||
73
crates/knowledge-web-lib/src/runtime.rs
Normal file
73
crates/knowledge-web-lib/src/runtime.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/runtime.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Shared Actix Web process composition.
|
||||||
|
|
||||||
|
struct ServerState {
|
||||||
|
site: crate::ServerSite,
|
||||||
|
assets: crate::ViteAssets,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start one Knowledge HTTP server using environment-owned bind and asset configuration.
|
||||||
|
///
|
||||||
|
/// `bind_environment` names the launcher-specific environment variable containing the socket
|
||||||
|
/// address, for example `127.0.0.1:18081`. The function never chooses a deployment address.
|
||||||
|
pub fn run_server(site: crate::ServerSite, bind_environment: &str) -> std::io::Result<()> {
|
||||||
|
let bind_address = match std::env::var(bind_environment) {
|
||||||
|
Ok(value) => {
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{bind_environment} must not be empty")));
|
||||||
|
}
|
||||||
|
value
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{bind_environment} must be configured")));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let assets = match crate::load_assets_from_environment() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let static_assets_directory = match assets.static_assets_directory() {
|
||||||
|
Some(value) => Some(value.to_path_buf()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let static_mount_path = match assets.static_mount_path() {
|
||||||
|
Some(value) => Some(value.to_owned()),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
let state = actix_web::web::Data::new(ServerState { site, assets });
|
||||||
|
return actix_web::rt::System::new().block_on(async move {
|
||||||
|
let server = actix_web::HttpServer::new(move || {
|
||||||
|
let app_state = state.clone();
|
||||||
|
let static_directory = static_assets_directory.clone();
|
||||||
|
let static_mount = static_mount_path.clone();
|
||||||
|
return actix_web::App::new()
|
||||||
|
.app_data(app_state)
|
||||||
|
.route("/", actix_web::web::get().to(index))
|
||||||
|
.route("/healthz", actix_web::web::get().to(health))
|
||||||
|
.configure(move |configuration| {
|
||||||
|
configure_static_assets(configuration, static_mount.as_deref(), static_directory.as_deref());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let bound = match server.bind(bind_address.as_str()) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
return bound.run().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn index(state: actix_web::web::Data<ServerState>) -> maud::Markup {
|
||||||
|
return crate::render_index_page(state.site, &state.assets);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health() -> actix_web::HttpResponse {
|
||||||
|
return actix_web::HttpResponse::Ok().content_type("text/plain; charset=utf-8").body("ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_static_assets(configuration: &mut actix_web::web::ServiceConfig, mount_path: Option<&str>, directory: Option<&std::path::Path>) {
|
||||||
|
if let (Some(mount), Some(path)) = (mount_path, directory) {
|
||||||
|
configuration.service(actix_files::Files::new(mount, path.to_path_buf()));
|
||||||
|
}
|
||||||
|
}
|
||||||
53
crates/knowledge-web-lib/src/site.rs
Normal file
53
crates/knowledge-web-lib/src/site.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/site.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Public facade identities used by the shared Web runtime.
|
||||||
|
|
||||||
|
/// Public Knowledge facade served by one launcher.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum ServerSite {
|
||||||
|
/// Normative PGR facade.
|
||||||
|
Pgr,
|
||||||
|
/// Procedural HOWTOs facade.
|
||||||
|
Howtos,
|
||||||
|
/// Contextual snippets facade.
|
||||||
|
Snippets,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::ServerSite {
|
||||||
|
/// Return the domain content family associated with this facade.
|
||||||
|
pub fn content_family(self) -> knowledge_domain_lib::ContentFamily {
|
||||||
|
match self {
|
||||||
|
crate::ServerSite::Pgr => return knowledge_domain_lib::ContentFamily::Pgr,
|
||||||
|
crate::ServerSite::Howtos => return knowledge_domain_lib::ContentFamily::Howto,
|
||||||
|
crate::ServerSite::Snippets => return knowledge_domain_lib::ContentFamily::Snippet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the page title used by the foundation renderer.
|
||||||
|
pub fn page_title(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
crate::ServerSite::Pgr => return "SaseDev Knowledge — PGR",
|
||||||
|
crate::ServerSite::Howtos => return "SaseDev Knowledge — HOWTOs",
|
||||||
|
crate::ServerSite::Snippets => return "SaseDev Knowledge — Snippets",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the main heading used by the foundation renderer.
|
||||||
|
pub fn heading(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
crate::ServerSite::Pgr => return "Project Governance Rules",
|
||||||
|
crate::ServerSite::Howtos => return "HOWTOs",
|
||||||
|
crate::ServerSite::Snippets => return "Snippets",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the short explanatory lead used by the foundation renderer.
|
||||||
|
pub fn lead(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
crate::ServerSite::Pgr => return "Connaissance normative de gouvernance.",
|
||||||
|
crate::ServerSite::Howtos => return "Guides procéduraux et explicatifs.",
|
||||||
|
crate::ServerSite::Snippets => return "Fragments courts et contextualisés.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
crates/knowledge-web-lib/src/unit_tests.rs
Normal file
72
crates/knowledge-web-lib/src/unit_tests.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// file: crates/knowledge-web-lib/src/unit_tests.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Unit tests for shared Web composition primitives.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn development_assets_include_vite_client_before_main_entry() {
|
||||||
|
let assets = match crate::ViteAssets::development("http://127.0.0.1:5173/") {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
assert!(error.is_empty(), "unexpected development asset error: {error}");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(assets.scripts().len(), 2);
|
||||||
|
assert_eq!(assets.scripts()[0], "http://127.0.0.1:5173/@vite/client");
|
||||||
|
assert_eq!(assets.scripts()[1], "http://127.0.0.1:5173/src/main.ts");
|
||||||
|
assert!(assets.stylesheets().is_empty());
|
||||||
|
assert!(assets.static_assets_directory().is_none());
|
||||||
|
assert!(assets.static_mount_path().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn production_manifest_resolves_hashes_and_asset_base() {
|
||||||
|
let manifest = r#"{
|
||||||
|
"src/main.ts": {
|
||||||
|
"file": "assets/main-abc.js",
|
||||||
|
"css": ["assets/main-def.css"],
|
||||||
|
"isEntry": true
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let assets = match crate::ViteAssets::from_manifest_text(manifest, "/", std::path::PathBuf::from("/tmp/knowledge-assets")) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
assert!(error.is_empty(), "unexpected manifest error: {error}");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert_eq!(assets.scripts().len(), 1);
|
||||||
|
assert_eq!(assets.scripts()[0], "/assets/main-abc.js");
|
||||||
|
assert_eq!(assets.stylesheets().len(), 1);
|
||||||
|
assert_eq!(assets.stylesheets()[0], "/assets/main-def.css");
|
||||||
|
assert_eq!(assets.static_assets_directory(), Some(std::path::Path::new("/tmp/knowledge-assets")));
|
||||||
|
assert_eq!(assets.static_mount_path(), Some("/assets"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn manifest_requires_the_stable_main_entry() {
|
||||||
|
let manifest = r#"{"index.html": {"file": "assets/index.js", "isEntry": true}}"#;
|
||||||
|
let result = crate::ViteAssets::from_manifest_text(manifest, "/", std::path::PathBuf::from("/tmp/knowledge-assets"));
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maud_page_uses_resolved_assets_and_site_identity() {
|
||||||
|
let assets = match crate::ViteAssets::from_manifest_text(
|
||||||
|
r#"{"src/main.ts": {"file": "assets/main.js", "css": ["assets/main.css"], "isEntry": true}}"#,
|
||||||
|
"/",
|
||||||
|
std::path::PathBuf::from("/tmp/knowledge-assets"),
|
||||||
|
) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => {
|
||||||
|
assert!(error.is_empty(), "unexpected manifest error: {error}");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let rendered = crate::render_index_page(crate::ServerSite::Pgr, &assets).into_string();
|
||||||
|
assert!(rendered.contains("SaseDev Knowledge — PGR"));
|
||||||
|
assert!(rendered.contains("/assets/main.js"));
|
||||||
|
assert!(rendered.contains("/assets/main.css"));
|
||||||
|
assert!(rendered.contains("data-content-family=\"pgr\""));
|
||||||
|
}
|
||||||
75
deltas/0.0.1/alpha.5.md
Normal file
75
deltas/0.0.1/alpha.5.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<!-- file: deltas/0.0.1/alpha.5.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.1-alpha.5 — composition Actix/Maud et manifest Vite
|
||||||
|
|
||||||
|
## Base requise
|
||||||
|
|
||||||
|
État `0.0.1-alpha.4.fix.2` committé et poussé après application du history `PASS`.
|
||||||
|
|
||||||
|
La version devient `0.0.1-alpha.5`.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
La tranche matérialise :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-web-lib
|
||||||
|
knowledge-pgr-server
|
||||||
|
knowledge-howtos-server
|
||||||
|
knowledge-snippets-server
|
||||||
|
```
|
||||||
|
|
||||||
|
`knowledge-web-lib` possède la composition Actix Web/Maud partagée, la résolution des assets Vite et les deux routes publiques minimales `/` et `/healthz`. Les trois packages serveur restent des launchers minces.
|
||||||
|
|
||||||
|
## Dépendances Web Rust
|
||||||
|
|
||||||
|
Versions vérifiées à la préparation de la tranche :
|
||||||
|
|
||||||
|
```text
|
||||||
|
actix-web 4.15.0
|
||||||
|
actix-files 0.7.0
|
||||||
|
maud 0.27.0
|
||||||
|
serde 1.0.229
|
||||||
|
serde_json 1.0.151
|
||||||
|
```
|
||||||
|
|
||||||
|
Les dépendances restent dans `knowledge-web-lib`, seul owner qui les consomme directement.
|
||||||
|
|
||||||
|
## Configuration runtime
|
||||||
|
|
||||||
|
Chaque launcher exige sa variable de bind :
|
||||||
|
|
||||||
|
```text
|
||||||
|
KNOWLEDGE_PGR_BIND_ADDRESS
|
||||||
|
KNOWLEDGE_HOWTOS_BIND_ADDRESS
|
||||||
|
KNOWLEDGE_SNIPPETS_BIND_ADDRESS
|
||||||
|
```
|
||||||
|
|
||||||
|
La source d'assets est exclusive :
|
||||||
|
|
||||||
|
```text
|
||||||
|
KNOWLEDGE_VITE_DEV_ORIGIN
|
||||||
|
|
||||||
|
ou
|
||||||
|
|
||||||
|
KNOWLEDGE_VITE_MANIFEST_PATH
|
||||||
|
```
|
||||||
|
|
||||||
|
En production `KNOWLEDGE_ASSET_BASE_PATH` peut remplacer le préfixe `/`.
|
||||||
|
|
||||||
|
Aucun origin Vite, port de déploiement ou hostname n'est codé en dur.
|
||||||
|
|
||||||
|
## Rendu et assets
|
||||||
|
|
||||||
|
Le rendu runtime Maud reprend les invariants de classes/assets utiles du template lab. En développement, il charge `@vite/client` avant `src/main.ts`. En production, il lit `.vite/manifest.json`, résout l'entrée stable `src/main.ts` et sert le répertoire d'assets produit par Vite via Actix Files.
|
||||||
|
|
||||||
|
Une configuration absente, ambiguë ou un manifest invalide produit une erreur récupérable avant l'écoute réseau ; aucun `panic!`, `unwrap`, `expect` ou `?` n'est introduit.
|
||||||
|
|
||||||
|
## Hors scope
|
||||||
|
|
||||||
|
La tranche n'ajoute ni auth, ni administration fonctionnelle, ni routes admin/login, ni PostgreSQL, ni Gitea, ni publication Git, ni logging transverse sans besoin observé.
|
||||||
|
|
||||||
|
`Cargo.lock` n'est pas préfabriqué dans le delta : Cargo doit le régénérer dans l'environnement opérateur avec les nouvelles dépendances, puis le versionner.
|
||||||
|
|
||||||
|
Ne pas committe `alpha.5` avant production de son history à partir des preuves opérateur.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/001-PROJECT_PROFILE.md -->
|
<!-- file: docs/architecture/001-PROJECT_PROFILE.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Profil du projet Knowledge
|
# Profil du projet Knowledge
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ Les profils Java/Maven/Gradle, Android et autres technologies absentes ne sont p
|
|||||||
| Rust stable / édition 2024 | `ADOPT` | profil Rust/Cargo PGR |
|
| Rust stable / édition 2024 | `ADOPT` | profil Rust/Cargo PGR |
|
||||||
| Workspace Cargo | `ADOPT` | skeleton Rust/Cargo ; actif depuis `alpha.2` |
|
| Workspace Cargo | `ADOPT` | skeleton Rust/Cargo ; actif depuis `alpha.2` |
|
||||||
| `Cargo.lock` | `KEEP-LOCAL` | versionné : workspace applicatif déployable, résolution reproductible entre opérateur et livraisons |
|
| `Cargo.lock` | `KEEP-LOCAL` | versionné : workspace applicatif déployable, résolution reproductible entre opérateur et livraisons |
|
||||||
| Actix Web | `KEEP-LOCAL` | trois launchers serveur distincts ; PGR ne possède pas de profil Actix dédié |
|
| Actix Web | `KEEP-LOCAL` | trois launchers matérialisés en `alpha.5` et composition partagée dans `knowledge-web-lib` |
|
||||||
| Maud | `KEEP-LOCAL` | rendu SSR initial lorsque pertinent |
|
| Maud | `KEEP-LOCAL` | rendu SSR commun matérialisé dans `knowledge-web-lib` en `alpha.5` |
|
||||||
| TypeScript | `ADOPT` | conforme au owner `530` et à l'intention locale |
|
| TypeScript | `ADOPT` | conforme au owner `530` et à l'intention locale |
|
||||||
| Package manager frontend | `KEEP-LOCAL` | npm sélectionné en `alpha.4` ; `package-lock.json` versionné |
|
| Package manager frontend | `KEEP-LOCAL` | npm sélectionné en `alpha.4` ; `package-lock.json` versionné |
|
||||||
| Vite | `KEEP-LOCAL` + owner `550` adopté | build/preview des templates et assets de référence ; manifest de production consommé par l’intégration Maud |
|
| Vite | `KEEP-LOCAL` + owner `550` adopté | build/preview des templates et assets de référence ; manifest de production consommé par l’intégration Maud |
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Architecture de fondation Knowledge
|
# Architecture de fondation Knowledge
|
||||||
|
|
||||||
@@ -264,7 +264,11 @@ En particulier, Knowledge ne doit pas hériter mécaniquement :
|
|||||||
- d’une arborescence d’assets spécifique au corpus ;
|
- d’une arborescence d’assets spécifique au corpus ;
|
||||||
- de dépendances TypeScript `@types/*` non justifiées par les packages réellement consommés.
|
- de dépendances TypeScript `@types/*` non justifiées par les packages réellement consommés.
|
||||||
|
|
||||||
`alpha.4` possède le frontend indépendant et la production du manifest. La clé stable `src/main.ts` constitue l'entrée d'asset commune de départ ; son entrée de manifest doit référencer les sorties JavaScript et CSS générées. `alpha.5` possède l’intégration Actix/Maud et doit décider si le loader d’assets devient une crate/lib partagée entre les trois serveurs.
|
`alpha.4` possède le frontend indépendant et la production du manifest. La clé stable `src/main.ts` constitue l'entrée d'asset commune de départ ; son entrée de manifest doit référencer les sorties JavaScript et CSS générées.
|
||||||
|
|
||||||
|
`alpha.5` matérialise `knowledge-web-lib` comme owner partagé de l'intégration Actix/Maud. La crate choisit entre un dev server configuré par `KNOWLEDGE_VITE_DEV_ORIGIN` et un manifest configuré par `KNOWLEDGE_VITE_MANIFEST_PATH`. En production elle dérive le répertoire `assets/` depuis le manifest et le sert sous le préfixe `/assets`, tandis que `KNOWLEDGE_ASSET_BASE_PATH` garde le préfixe d'URL configurable. Les erreurs de configuration/manifest restent récupérables et remontent avant le démarrage du listener.
|
||||||
|
|
||||||
|
Les trois packages `knowledge-pgr-server`, `knowledge-howtos-server` et `knowledge-snippets-server` sont des launchers minces : chacun sélectionne `ServerSite` et le nom de sa variable de bind, puis délègue au runtime commun. Ils ne possèdent aucune logique de rendu ou de manifest dupliquée.
|
||||||
|
|
||||||
## 10. Administration
|
## 10. Administration
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/001-V0_0_1_FOUNDATION_PLAN.md -->
|
<!-- file: docs/plans/001-V0_0_1_FOUNDATION_PLAN.md -->
|
||||||
<!-- version: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
# Plan 0.0.1 — fondation SaseDev Knowledge
|
# Plan 0.0.1 — fondation SaseDev Knowledge
|
||||||
|
|
||||||
@@ -181,10 +181,11 @@ La gate opérateur d'`alpha.4.fix.1` reste **FAIL**. L'hygiène Git est corrigé
|
|||||||
L'audit structurel du fix contenait également une vérification trop large de tous les `*.d.ts` sous `Web/`, incluant `node_modules`; il doit être limité aux déclarations appartenant au source du projet.
|
L'audit structurel du fix contenait également une vérification trop large de tous les `*.d.ts` sous `Web/`, incluant `node_modules`; il doit être limité aux déclarations appartenant au source du projet.
|
||||||
|
|
||||||
`alpha.4.fix.2` ajoute donc `@types/node` sur la branche Node 24, expose les types `node`, ajoute `ESNext.Disposable` et corrige uniquement la gate structurelle associée. Aucun scope runtime n'est ajouté.
|
`alpha.4.fix.2` ajoute donc `@types/node` sur la branche Node 24, expose les types `node`, ajoute `ESNext.Disposable` et corrige uniquement la gate structurelle associée. Aucun scope runtime n'est ajouté.
|
||||||
|
Sa gate opérateur est **PASS** : typecheck et build Vite passent, le manifest est valide, les gates Rust restent propres et le warning `@parcel/watcher` demeure seulement observable sans approbation automatique.
|
||||||
|
|
||||||
### alpha.5 — composition Actix/Maud et manifest Vite
|
### alpha.5 — composition Actix/Maud et manifest Vite
|
||||||
|
|
||||||
Matérialiser le minimum Actix Web/Maud nécessaire pour prouver trois launchers minces et intégrer les assets produits par `alpha.4` :
|
Matérialiser `knowledge-web-lib` comme owner partagé de la composition Actix/Maud et trois launchers minces :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
knowledge-pgr-server
|
knowledge-pgr-server
|
||||||
@@ -192,11 +193,15 @@ knowledge-howtos-server
|
|||||||
knowledge-snippets-server
|
knowledge-snippets-server
|
||||||
```
|
```
|
||||||
|
|
||||||
Ils doivent composer les mêmes contrats/bibliothèques lorsque la responsabilité est partagée et rester capables de recevoir des configurations de sources Git distinctes.
|
Les launchers ne possèdent que la sélection de façade et du nom de variable de bind. `knowledge-web-lib` possède les routes publiques minimales `/` et `/healthz`, le rendu Maud et l'intégration Vite.
|
||||||
|
|
||||||
Le HTML runtime est rendu avec Maud. En développement, la composition MAY cibler le dev server Vite configuré ; en production, elle MUST résoudre les assets hashés via le manifest Vite ou un mapping équivalent produit par la chaîne Web. Le pattern de `actix-auth-app` est à adapter sous les règles Knowledge, notamment sans `panic!` et sans origin/port codé en dur.
|
En développement, `KNOWLEDGE_VITE_DEV_ORIGIN` fournit l'origin et la page charge `@vite/client` puis `src/main.ts`. En production, `KNOWLEDGE_VITE_MANIFEST_PATH` fournit le manifest ; l'entrée stable `src/main.ts` est résolue vers ses assets hashés et le répertoire `assets/` est servi par Actix Files. `KNOWLEDGE_ASSET_BASE_PATH` garde le préfixe de production configurable. Configurer simultanément dev-origin et manifest est une erreur récupérable.
|
||||||
|
|
||||||
La tranche ne doit pas introduire admin complète, auth complète, persistance métier complète ni forge obligatoire.
|
Les binds sont exclusivement configurés par `KNOWLEDGE_PGR_BIND_ADDRESS`, `KNOWLEDGE_HOWTOS_BIND_ADDRESS` et `KNOWLEDGE_SNIPPETS_BIND_ADDRESS`. Aucun port de déploiement n'est codé en dur.
|
||||||
|
|
||||||
|
La gate doit couvrir les tests `knowledge-web-lib`, la compilation/Clippy des quatre nouvelles crates, le build Vite et un smoke HTTP production des trois launchers vérifiant `/healthz`, le rendu Maud et le service réel des assets hashés.
|
||||||
|
|
||||||
|
La tranche ne doit pas introduire admin complète, auth complète, persistance métier complète, logging transverse artificiel ni forge obligatoire.
|
||||||
|
|
||||||
### beta.1 — réconciliation de fondation
|
### beta.1 — réconciliation de fondation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
|
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
|
||||||
<!-- version: 10 -->
|
<!-- version: 11 -->
|
||||||
|
|
||||||
# Matrice de validation Knowledge
|
# Matrice de validation Knowledge
|
||||||
|
|
||||||
@@ -295,3 +295,24 @@ Le second fix Web doit prouver :
|
|||||||
- gates Rust existantes toujours propres après le bump de version ;
|
- gates Rust existantes toujours propres après le bump de version ;
|
||||||
- aucun serveur Actix/Maud, loader Rust du manifest, route runtime, migration PostgreSQL ou adapter de forge ajouté ;
|
- aucun serveur Actix/Maud, loader Rust du manifest, route runtime, migration PostgreSQL ou adapter de forge ajouté ;
|
||||||
- staging Git propre et outputs générés absents du dépôt.
|
- staging Git propre et outputs générés absents du dépôt.
|
||||||
|
|
||||||
|
## Gate de `0.0.1-alpha.5`
|
||||||
|
|
||||||
|
La composition serveur doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.1-alpha.5` et version workspace identique ;
|
||||||
|
- history `alpha.4.fix.2` `PASS` committé avant application ;
|
||||||
|
- workspace contenant `knowledge-web-lib` et exactement les trois nouveaux launchers attendus ;
|
||||||
|
- `knowledge-web-lib` comme unique owner Actix/Maud/Vite runtime partagé ;
|
||||||
|
- launchers dépendant de `knowledge-web-lib` sans dépendance directe Actix/Maud/Serde ;
|
||||||
|
- Actix Web `4.15.0`, Actix Files `0.7.0`, Maud `0.27.0`, Serde `1.0.229` et Serde JSON `1.0.151` dans le manifest de `knowledge-web-lib` ;
|
||||||
|
- aucun `panic!`, `unwrap`, `expect` ou `?` ajouté ;
|
||||||
|
- aucune origin/port Vite ou adresse de bind choisie par le code ;
|
||||||
|
- mode dev résolvant `@vite/client` puis `src/main.ts` depuis `KNOWLEDGE_VITE_DEV_ORIGIN` ;
|
||||||
|
- mode production résolvant `src/main.ts` depuis le manifest configuré et servant les fichiers `/assets/*` depuis le build réel ;
|
||||||
|
- routes runtime limitées à `/` et `/healthz` pour cette tranche ;
|
||||||
|
- aucune route admin/login, auth, PostgreSQL ou forge ajoutée ;
|
||||||
|
- `npm ci`, typecheck et build du template lab toujours `PASS` ;
|
||||||
|
- audits Rust PGR, `cargo check`, Clippy strict et tests ciblés `PASS` ;
|
||||||
|
- smoke HTTP production `PASS` pour les trois launchers avec ports de test injectés par environnement ;
|
||||||
|
- `Cargo.lock` mis à jour/versionné et staging propre.
|
||||||
|
|||||||
95
history/0.0.1/alpha.5.md
Normal file
95
history/0.0.1/alpha.5.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<!-- file: history/0.0.1/alpha.5.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.1-alpha.5 — composition Actix/Maud et manifest Vite
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
FAIL
|
||||||
|
|
||||||
|
La gate technique de `0.0.1-alpha.5` exécutée le `2026-09-28` est propre, mais la revue opérateur relève avant clôture une non-conformité avec la convention de layout Rust souhaitée pour Knowledge : les tests unitaires sont implémentés sous `src/unit_tests.rs` au lieu d'une arborescence `unit_tests/` séparée.
|
||||||
|
|
||||||
|
## Baseline
|
||||||
|
|
||||||
|
`alpha.4.fix.2` `PASS` a été historisé, committé et poussé avant application d'`alpha.5`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.1/alpha.4.fix.2.md = PASS
|
||||||
|
commit 8f6f072
|
||||||
|
message 0.0.1-alpha.4.fix.2
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuves techniques PASS
|
||||||
|
|
||||||
|
La structure `alpha.5` est propre.
|
||||||
|
|
||||||
|
La baseline Web passe :
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm ci PASS
|
||||||
|
typecheck PASS
|
||||||
|
vite build PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Les audits Rust PGR passent sur 17 fichiers et 7 packages.
|
||||||
|
|
||||||
|
Cargo et Clippy strict passent.
|
||||||
|
|
||||||
|
Les tests workspace passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-domain-lib : 3 PASS
|
||||||
|
knowledge-git-api : 1 PASS
|
||||||
|
knowledge-sync-api : 1 PASS
|
||||||
|
knowledge-web-lib : 4 PASS
|
||||||
|
launchers : 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Le manifest Vite est valide.
|
||||||
|
|
||||||
|
Les trois smokes HTTP production passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-pgr-server PASS
|
||||||
|
knowledge-howtos-server PASS
|
||||||
|
knowledge-snippets-server PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
`git diff --cached --check` est propre.
|
||||||
|
|
||||||
|
## Défaut de gouvernance/layout
|
||||||
|
|
||||||
|
Le staging contient notamment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-web-lib/src/unit_tests.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
et les trois crates introduites plus tôt possèdent également leur `src/unit_tests.rs`.
|
||||||
|
|
||||||
|
PGR `0.1.1` ne rend pas ce layout globalement obligatoire : il indique qu'un workspace **MAY** adopter une arborescence `unit_tests/` séparée et miroir de `src/`.
|
||||||
|
|
||||||
|
Knowledge sélectionne maintenant explicitement cette option comme convention locale obligatoire.
|
||||||
|
|
||||||
|
La tranche ne peut donc pas être fermée `PASS` tant que la workspace conserve ses tests unitaires dans `src/`.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.1-alpha.5` est `FAIL`.
|
||||||
|
|
||||||
|
La transition autorisée est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.1-alpha.5.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fix doit être global et non limité à `knowledge-web-lib` :
|
||||||
|
|
||||||
|
- déplacer les tests unitaires existants sous `unit_tests/` ;
|
||||||
|
- rattacher les fichiers de tests aux modules source avec `#[cfg(test)]` + `#[path = "..."]` ;
|
||||||
|
- réserver `tests/` aux tests d'intégration Cargo ;
|
||||||
|
- formaliser la convention dans `RULES.md` ;
|
||||||
|
- automatiser le contrôle du layout ;
|
||||||
|
- rejouer l'intégralité des gates Rust/Web/HTTP sans changement fonctionnel.
|
||||||
Reference in New Issue
Block a user