feat(profiles): seal builtin import provenance and expose read-only editor imports
Retain the evaluated JSON launch entrypoint, seal observed builtin source bytes and digests as verified provenance, and fail on missing/corrupt dependencies without Runtime builtin reevaluation. Cover real HTTP commits and launch resolution, including worker mode and Workspace Config feature composition. Expose read-only builtin import completions/diagnostics in the editor and rebuild wasm32 release assets with wasm-bindgen 0.2.117. Document public paths, ordinary composition, limits, resave semantics, and the lazy-cycle incident. Validation: worker-runtime 344 library + 34 integration tests; workspace-server 609 library + 11 binary tests plus final builtin HTTP/launch tests (2); archive tests (16); native WASM tests (2); real-WASM/web tests (37), editor component tests (10); root cargo check, cargo fmt --all -- --check and git diff --check HEAD pass.
This commit is contained in:
@@ -98,31 +98,47 @@ pub fn complete_current(
|
||||
let snapshot = session
|
||||
.as_ref()
|
||||
.ok_or_else(|| JsValue::from_str("config source snapshot is not initialized"))?;
|
||||
let utf8_byte_offset = utf16_to_utf8_offset(&source, utf16_offset)?;
|
||||
let result = session_environment(snapshot.clone())
|
||||
.complete_config(&entrypoint, &source, utf8_byte_offset, explicit)
|
||||
.map_err(|error| JsValue::from_str(&format!("{error:?}")))?;
|
||||
let result = result
|
||||
.map(|result| {
|
||||
Ok::<WasmCompletionResult, JsValue>(WasmCompletionResult {
|
||||
from: utf8_to_utf16_offset(&source, result.from)?,
|
||||
items: result
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| WasmCompletionItem {
|
||||
label: item.label,
|
||||
kind: format!("{:?}", item.kind).to_lowercase(),
|
||||
detail: item.detail,
|
||||
priority: item.priority,
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
encode(result)
|
||||
encode(complete_snapshot(
|
||||
snapshot,
|
||||
&entrypoint,
|
||||
&source,
|
||||
utf16_offset,
|
||||
explicit,
|
||||
)?)
|
||||
})
|
||||
}
|
||||
|
||||
fn complete_snapshot(
|
||||
snapshot: &ConfigTreeSnapshot,
|
||||
entrypoint: &VirtualPath,
|
||||
source: &str,
|
||||
utf16_offset: usize,
|
||||
explicit: bool,
|
||||
) -> Result<Option<WasmCompletionResult>, JsValue> {
|
||||
let utf8_byte_offset = utf16_to_utf8_offset(source, utf16_offset)?;
|
||||
let result = session_environment(snapshot.clone())
|
||||
.complete_config(entrypoint, source, utf8_byte_offset, explicit)
|
||||
.map_err(|error| JsValue::from_str(&format!("{error:?}")))?;
|
||||
let result = result
|
||||
.map(|result| {
|
||||
Ok::<WasmCompletionResult, JsValue>(WasmCompletionResult {
|
||||
from: utf8_to_utf16_offset(source, result.from)?,
|
||||
items: result
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| WasmCompletionItem {
|
||||
label: item.label,
|
||||
kind: format!("{:?}", item.kind).to_lowercase(),
|
||||
detail: item.detail,
|
||||
priority: item.priority,
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn evaluate_snapshot(snapshot: JsValue, contract: JsValue) -> Result<JsValue, JsValue> {
|
||||
let snapshot: ConfigTreeSnapshot = decode(snapshot)?;
|
||||
@@ -207,3 +223,98 @@ fn js_error(error: impl std::fmt::Display) -> JsValue {
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn _assert_serializable(_: EvaluationResult) {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use config_source::{ConfigContentType, ConfigEntry};
|
||||
|
||||
fn snapshot() -> ConfigTreeSnapshot {
|
||||
ConfigTreeSnapshot::from_entries(
|
||||
7,
|
||||
[
|
||||
ConfigEntry::new(
|
||||
VirtualPath::parse("main.dcdl").unwrap(),
|
||||
ConfigContentType::Decodal,
|
||||
"{}",
|
||||
)
|
||||
.unwrap(),
|
||||
// A same-suffix workspace file must not be an unknown builtin fallback.
|
||||
ConfigEntry::new(
|
||||
VirtualPath::parse("profiles/missing.dcdl").unwrap(),
|
||||
ConfigContentType::Decodal,
|
||||
"{}",
|
||||
)
|
||||
.unwrap(),
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wasm_completion_boundary_preserves_read_only_builtin_detail() {
|
||||
let snapshot = snapshot();
|
||||
let source = "let 名 = 1; import \"$builtin/profiles/comp\"";
|
||||
let cursor = source.encode_utf16().count() - 1;
|
||||
let result = complete_snapshot(
|
||||
&snapshot,
|
||||
&VirtualPath::parse("main.dcdl").unwrap(),
|
||||
source,
|
||||
cursor,
|
||||
true,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(result.from, "let 名 = 1; import \"".encode_utf16().count());
|
||||
let item = result
|
||||
.items
|
||||
.iter()
|
||||
.find(|item| item.label == "$builtin/profiles/companion.dcdl")
|
||||
.unwrap();
|
||||
assert_eq!(item.kind, "file");
|
||||
assert_eq!(
|
||||
item.detail.as_deref(),
|
||||
Some("read-only builtin Decodal source")
|
||||
);
|
||||
assert_eq!(snapshot.entries.len(), 2);
|
||||
assert!(
|
||||
snapshot
|
||||
.entries
|
||||
.keys()
|
||||
.all(|path| !path.as_str().starts_with("$builtin/"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wasm_analysis_boundary_reports_unknown_builtin_without_workspace_fallback() {
|
||||
let snapshot = snapshot();
|
||||
let path = VirtualPath::parse("main.dcdl").unwrap();
|
||||
let diagnostics = session_environment(snapshot.clone())
|
||||
.analyze(&path, Some("import \"$builtin/profiles/missing.dcdl\""));
|
||||
assert!(!diagnostics.is_empty());
|
||||
let diagnostic = diagnostics
|
||||
.iter()
|
||||
.find(|diagnostic| {
|
||||
diagnostic
|
||||
.message
|
||||
.contains("unknown or non-public read-only builtin source")
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(diagnostic.path, path);
|
||||
assert_eq!(diagnostic.revision, 7);
|
||||
assert_eq!(diagnostic.tree_digest, snapshot.digest);
|
||||
assert!(
|
||||
diagnostic
|
||||
.message
|
||||
.contains("$builtin/profiles/missing.dcdl")
|
||||
);
|
||||
assert!(
|
||||
session_environment(snapshot)
|
||||
.analyze(
|
||||
&VirtualPath::parse("main.dcdl").unwrap(),
|
||||
Some("import \"./profiles/missing.dcdl\"")
|
||||
)
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user