1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Runtime for the Lex extension system.
//!
//! This crate hosts the registry, schema loader, namespace URI resolver,
//! transport adapters (subprocess, future WASM), and trust gate that turn
//! a set of [`lex_extension::Schema`]s plus [`lex_extension::LexHandler`]
//! implementations into a dispatch fabric the `lexd` CLI, `lex-lsp`
//! server, `lex-core` (for built-in `lex.*` resolvers), and Rust
//! embedders all share.
//!
//! Pre-1.0 the public API surface is unstable per Cargo convention. The
//! crate is published so that downstream crates in the lex toolchain —
//! especially `lex-core`, which carries the `lex.include` resolver as
//! the first built-in `LexHandler` — can depend on it. Handler authors
//! should depend on `lex-extension`, not this crate.
//!
//! # Writing a handler — the unified registration pattern (#615)
//!
//! Extension authors register one [`lex_extension::Schema`] per label,
//! attach the lifecycle hooks that label participates in, and provide
//! one [`lex_extension::LexHandler`] implementation per namespace. The
//! `Registry` routes each hook to the right method by namespace + label:
//!
//! ```ignore
//! use lex_extension::{LexHandler, Format, RenderOut, WireNode};
//! use lex_extension::handler::HandlerError;
//! use lex_extension::wire::LabelCtx;
//! use lex_extension::schema::{HookSet, RenderHook, Schema};
//! use lex_extension_host::Registry;
//!
//! struct AcmeHandler;
//! impl LexHandler for AcmeHandler {
//! // IR-construction lifecycle: hydrate verbatim payloads
//! // (`:: acme.table ::`, `:: acme.image ::`) into typed wire
//! // nodes the host's IR builder consumes.
//! fn on_ir_build(&self, ctx: &LabelCtx) -> Result<Option<WireNode>, HandlerError> {
//! match ctx.label.as_str() {
//! "acme.thing" => Ok(Some(WireNode::Verbatim { /* ... */ })),
//! _ => Ok(None),
//! }
//! }
//! // Pre-serialisation lifecycle: emit the format-specific
//! // representation (markdown, HTML, ...). One handler can
//! // participate in both IR-build and render against the same
//! // schema — a single registration, both lifecycles.
//! fn on_render(&self, ctx: &LabelCtx, fmt: Format) -> Result<Option<RenderOut>, HandlerError> {
//! /* ... */
//! Ok(None)
//! }
//! }
//!
//! let registry = Registry::new();
//! registry.register_namespace(
//! "acme",
//! vec![Schema {
//! schema_version: 1,
//! label: "acme.thing".into(),
//! hooks: HookSet {
//! ir_build: true, // declare IR-build participation
//! render: vec![RenderHook::new("html")], // declare render participation
//! ..HookSet::default()
//! },
//! /* ... rest of Schema ... */
//! # description: None, params: Default::default(), attaches_to: vec![],
//! # body: Default::default(), verbatim_label: false,
//! # capabilities: Default::default(), handler: None, diagnostics: Vec::new(),
//! }],
//! Box::new(AcmeHandler),
//! ).expect("registration ok");
//! ```
//!
//! ## Lifecycle hooks
//!
//! Three hook surfaces, each on its own lifecycle phase:
//!
//! | Hook | Lifecycle phase | Dispatch entry point | Built-in example |
//! |-----------------|-----------------------------|-------------------------------|-----------------------|
//! | `on_resolve` | AST substitution | [`Registry::dispatch_resolve`]| `lex.include` |
//! | `on_ir_build` | IR construction | [`Registry::dispatch_ir_build`]| `lex.tabular.table`, `lex.media.*` |
//! | `on_render` | Pre-serialisation | [`Registry::dispatch_render`] | `doc.title`, `doc.author`, ... |
//!
//! `on_resolve` and `on_ir_build` have the same shape
//! (`Result<Option<WireNode>, HandlerError>`); they're separate hooks
//! because they fire at different lifecycle phases and have different
//! consumer contracts. `on_resolve` returns a wire node spliced into
//! the host AST; `on_ir_build` returns a wire node consumed by the IR
//! builder. Pre-#615 these were a single overloaded hook
//! (`on_resolve`); the unified registry surface separates them so
//! extension authors can declare exactly the lifecycle phase they
//! participate in.
//!
//! # What's in this crate
//!
//! - [`Registry`] — namespace registration, label lookup, and dispatch
//! helpers wrapping every hook event with `HandlerError` folding and
//! panic catch.
//! - [`schema::SchemaLoader`] — YAML schema loader + post-deserialise
//! validator.
//! - [`transport::native`] — the trivial transport: a registered
//! `Box<dyn LexHandler>` is its own transport, no adapter required.
//! - [`transport::subprocess`] (behind the `subprocess` feature) —
//! spawn a handler binary and dispatch over LSP-framed JSON-RPC.
//! - [`trust::TrustGate`] — decides whether a handler is allowed to
//! run, per the β/γ-correct policy in the master tracking issue
//! (subprocess always prompts; native trusted by linkage).
//! - [`sandbox::Sandbox`] — OS-level enforcement facade. The
//! plumbing-PR default is [`sandbox::NullSandbox`] (no
//! enforcement, `available() == false`). Per-OS implementations
//! land in follow-up PRs (12a Linux, 12b macOS, 12c Windows); the
//! trust matrix flip (PR 12d) consumes [`Sandbox::available`] to
//! auto-trust declared-pure handlers under enforced sandboxing.
//!
//! Coming in later PRs:
//!
//! - PR 12a/b/c: per-OS sandbox enforcement.
//! - PR 12d: trust matrix flip (auto-trust pure handlers under
//! enforced sandbox).
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;