Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:33:53.158Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

package_json.rs

203 lines · 7.1 KB · rust
1use chrono::{DateTime, Local};
2use collections::{BTreeSet, HashMap};
3use serde_json_lenient::Value;
4use std::{path::Path, sync::Arc};
5
6#[derive(Clone, Debug)]
7pub struct PackageJson {
8    pub mtime: DateTime<Local>,
9    pub data: PackageJsonData,
10}
11
12#[derive(Clone, Debug, Default, PartialEq, Eq)]
13pub struct PackageJsonData {
14    pub jest_package_path: Option<Arc<Path>>,
15    pub mocha_package_path: Option<Arc<Path>>,
16    pub vitest_package_path: Option<Arc<Path>>,
17    pub jasmine_package_path: Option<Arc<Path>>,
18    pub bun_package_path: Option<Arc<Path>>,
19    pub node_package_path: Option<Arc<Path>>,
20    pub scripts: BTreeSet<(Arc<Path>, String)>,
21    pub package_manager: Option<&'static str>,
22}
23
24impl PackageJsonData {
25    pub fn new(path: Arc<Path>, package_json: HashMap<String, Value>) -> Self {
26        let mut scripts = BTreeSet::new();
27        if let Some(Value::Object(package_json_scripts)) = package_json.get("scripts") {
28            scripts.extend(
29                package_json_scripts
30                    .keys()
31                    .cloned()
32                    .map(|name| (path.clone(), name)),
33            );
34        }
35
36        let mut jest_package_path = None;
37        let mut mocha_package_path = None;
38        let mut vitest_package_path = None;
39        let mut jasmine_package_path = None;
40        let mut bun_package_path = None;
41        let mut node_package_path = None;
42        if let Some(Value::Object(dependencies)) = package_json.get("devDependencies") {
43            if dependencies.contains_key("jest") {
44                jest_package_path.get_or_insert_with(|| path.clone());
45            }
46            if dependencies.contains_key("mocha") {
47                mocha_package_path.get_or_insert_with(|| path.clone());
48            }
49            if dependencies.contains_key("vitest") {
50                vitest_package_path.get_or_insert_with(|| path.clone());
51            }
52            if dependencies.contains_key("jasmine") {
53                jasmine_package_path.get_or_insert_with(|| path.clone());
54            }
55            if dependencies.contains_key("@types/bun") {
56                bun_package_path.get_or_insert_with(|| path.clone());
57            }
58            if dependencies.contains_key("@types/node") {
59                node_package_path.get_or_insert_with(|| path.clone());
60            }
61        }
62        if let Some(Value::Object(dev_dependencies)) = package_json.get("dependencies") {
63            if dev_dependencies.contains_key("jest") {
64                jest_package_path.get_or_insert_with(|| path.clone());
65            }
66            if dev_dependencies.contains_key("mocha") {
67                mocha_package_path.get_or_insert_with(|| path.clone());
68            }
69            if dev_dependencies.contains_key("vitest") {
70                vitest_package_path.get_or_insert_with(|| path.clone());
71            }
72            if dev_dependencies.contains_key("jasmine") {
73                jasmine_package_path.get_or_insert_with(|| path.clone());
74            }
75            if dev_dependencies.contains_key("@types/bun") {
76                bun_package_path.get_or_insert_with(|| path.clone());
77            }
78            if dev_dependencies.contains_key("@types/node") {
79                node_package_path.get_or_insert_with(|| path.clone());
80            }
81        }
82
83        let package_manager = package_manager_from_package_json(&package_json);
84
85        Self {
86            jest_package_path,
87            mocha_package_path,
88            vitest_package_path,
89            jasmine_package_path,
90            bun_package_path,
91            node_package_path,
92            scripts,
93            package_manager,
94        }
95    }
96
97    pub fn merge(&mut self, other: Self) {
98        self.jest_package_path = self.jest_package_path.take().or(other.jest_package_path);
99        self.mocha_package_path = self.mocha_package_path.take().or(other.mocha_package_path);
100        self.vitest_package_path = self
101            .vitest_package_path
102            .take()
103            .or(other.vitest_package_path);
104        self.jasmine_package_path = self
105            .jasmine_package_path
106            .take()
107            .or(other.jasmine_package_path);
108        self.bun_package_path = self.bun_package_path.take().or(other.bun_package_path);
109        self.node_package_path = self.node_package_path.take().or(other.node_package_path);
110        self.scripts.extend(other.scripts);
111        self.package_manager = self.package_manager.or(other.package_manager);
112    }
113}
114
115fn package_manager_from_package_json(
116    package_json: &HashMap<String, Value>,
117) -> Option<&'static str> {
118    package_json
119        .get("packageManager")
120        .and_then(|value| value.as_str())
121        .and_then(package_manager_name)
122        .or_else(|| {
123            package_json
124                .get("devEngines")
125                .and_then(|value| value.as_object())
126                .and_then(|dev_engines| dev_engines.get("packageManager"))
127                .and_then(package_manager_from_dev_engine)
128        })
129}
130
131fn package_manager_from_dev_engine(value: &Value) -> Option<&'static str> {
132    match value {
133        Value::Object(package_manager) => package_manager
134            .get("name")
135            .and_then(|name| name.as_str())
136            .and_then(package_manager_name),
137        Value::Array(package_managers) => package_managers
138            .iter()
139            .find_map(package_manager_from_dev_engine),
140        _ => None,
141    }
142}
143
144fn package_manager_name(value: &str) -> Option<&'static str> {
145    let value = value.split_once('@').map(|(name, _)| name).unwrap_or(value);
146    match value {
147        "pnpm" => Some("pnpm"),
148        "yarn" => Some("yarn"),
149        "npm" => Some("npm"),
150        "bun" => Some("bun"),
151        _ => None,
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use std::path::Path;
158
159    use super::PackageJsonData;
160
161    #[test]
162    fn package_manager_detection() {
163        fn package_manager(source: &str) -> Option<&'static str> {
164            PackageJsonData::new(
165                Path::new("/root/package.json").into(),
166                serde_json_lenient::from_str(source).expect("provided source should be valid JSON"),
167            )
168            .package_manager
169        }
170
171        assert_eq!(
172            package_manager(r#"{"packageManager": "pnpm@11.1.3"}"#),
173            Some("pnpm")
174        );
175
176        assert_eq!(
177            package_manager(
178                r#"{"devEngines": {"packageManager": {"name": "pnpm", "version": "^11.1.3", "onFail": "download"}}}"#
179            ),
180            Some("pnpm"),
181        );
182
183        assert_eq!(
184            package_manager(
185                r#"{"devEngines": {"packageManager": [{"name": "foo"}, {"name": "yarn", "version": "^4.0.0"}]}}"#
186            ),
187            Some("yarn"),
188        );
189
190        assert_eq!(
191            package_manager(
192                r#"{"packageManager": "npm@10.0.0", "devEngines": {"packageManager": {"name": "pnpm"}}}"#
193            ),
194            Some("npm"),
195        );
196
197        assert_eq!(
198            package_manager(r#"{"devEngines": {"packageManager": {"version": "^11.1.3"}}}"#),
199            None,
200        );
201    }
202}
203
Served at tenant.openagents/omega Member data and write actions are omitted.