git.delta.rocks / jrsonnet / refs/commits / c93de7763652

difftreelog

feat jrb

orkooprxYaroslav Bolyukin2026-05-06parent: #3732ed2.patch.diff
in: master

11 files changed

modifiedCargo.lockdiffbeforeafterboth
2# It is not intended for manual editing.2# It is not intended for manual editing.
3version = 43version = 4
4
5[[package]]
6name = "adler2"
7version = "2.0.1"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
410
5[[package]]11[[package]]
6name = "ahash"12name = "ahash"
109source = "registry+https://github.com/rust-lang/crates.io-index"115source = "registry+https://github.com/rust-lang/crates.io-index"
110checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"116checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
111dependencies = [117dependencies = [
112 "windows-sys",118 "windows-sys 0.61.2",
113]119]
114120
115[[package]]121[[package]]
120dependencies = [126dependencies = [
121 "anstyle",127 "anstyle",
122 "once_cell_polyfill",128 "once_cell_polyfill",
123 "windows-sys",129 "windows-sys 0.61.2",
124]130]
125131
126[[package]]132[[package]]
138 "object",144 "object",
139]145]
146
147[[package]]
148name = "arc-swap"
149version = "1.9.1"
150source = "registry+https://github.com/rust-lang/crates.io-index"
151checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
152dependencies = [
153 "rustversion",
154]
140155
141[[package]]156[[package]]
142name = "arraydeque"157name = "arraydeque"
143version = "0.5.1"158version = "0.5.1"
144source = "registry+https://github.com/rust-lang/crates.io-index"159source = "registry+https://github.com/rust-lang/crates.io-index"
145checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"160checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
161
162[[package]]
163name = "arrayvec"
164version = "0.7.6"
165source = "registry+https://github.com/rust-lang/crates.io-index"
166checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
167
168[[package]]
169name = "atomic-waker"
170version = "1.1.2"
171source = "registry+https://github.com/rust-lang/crates.io-index"
172checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
146173
147[[package]]174[[package]]
148name = "autocfg"175name = "autocfg"
149version = "1.5.0"176version = "1.5.0"
150source = "registry+https://github.com/rust-lang/crates.io-index"177source = "registry+https://github.com/rust-lang/crates.io-index"
151checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"178checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
179
180[[package]]
181name = "aws-lc-rs"
182version = "1.16.3"
183source = "registry+https://github.com/rust-lang/crates.io-index"
184checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
185dependencies = [
186 "aws-lc-sys",
187 "zeroize",
188]
189
190[[package]]
191name = "aws-lc-sys"
192version = "0.40.0"
193source = "registry+https://github.com/rust-lang/crates.io-index"
194checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
195dependencies = [
196 "cc",
197 "cmake",
198 "dunce",
199 "fs_extra",
200]
152201
153[[package]]202[[package]]
154name = "base64"203name = "base64"
162source = "registry+https://github.com/rust-lang/crates.io-index"211source = "registry+https://github.com/rust-lang/crates.io-index"
163checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"212checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
213
214[[package]]
215name = "block-buffer"
216version = "0.10.4"
217source = "registry+https://github.com/rust-lang/crates.io-index"
218checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
219dependencies = [
220 "generic-array",
221]
164222
165[[package]]223[[package]]
166name = "block-buffer"224name = "block-buffer"
178checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"236checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
179dependencies = [237dependencies = [
180 "memchr",238 "memchr",
239 "regex-automata",
181 "serde",240 "serde",
182]241]
183242
190 "allocator-api2",249 "allocator-api2",
191]250]
251
252[[package]]
253name = "byteorder"
254version = "1.5.0"
255source = "registry+https://github.com/rust-lang/crates.io-index"
256checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
257
258[[package]]
259name = "bytes"
260version = "1.11.1"
261source = "registry+https://github.com/rust-lang/crates.io-index"
262checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
263
264[[package]]
265name = "bytesize"
266version = "2.3.1"
267source = "registry+https://github.com/rust-lang/crates.io-index"
268checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
269
270[[package]]
271name = "camino"
272version = "1.2.2"
273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
275dependencies = [
276 "serde_core",
277]
192278
193[[package]]279[[package]]
194name = "cast"280name = "cast"
203checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"289checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
204dependencies = [290dependencies = [
205 "find-msvc-tools",291 "find-msvc-tools",
292 "jobserver",
293 "libc",
206 "shlex",294 "shlex",
207]295]
208296
294source = "registry+https://github.com/rust-lang/crates.io-index"382source = "registry+https://github.com/rust-lang/crates.io-index"
295checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"383checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
384
385[[package]]
386name = "clru"
387version = "0.6.3"
388source = "registry+https://github.com/rust-lang/crates.io-index"
389checksum = "197fd99cb113a8d5d9b6376f3aa817f32c1078f2343b714fff7d2ca44fdf67d5"
390dependencies = [
391 "hashbrown 0.16.1",
392]
393
394[[package]]
395name = "cmake"
396version = "0.1.58"
397source = "registry+https://github.com/rust-lang/crates.io-index"
398checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
399dependencies = [
400 "cc",
401]
296402
297[[package]]403[[package]]
298name = "colorchoice"404name = "colorchoice"
299version = "1.0.5"405version = "1.0.5"
300source = "registry+https://github.com/rust-lang/crates.io-index"406source = "registry+https://github.com/rust-lang/crates.io-index"
301checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"407checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
408
409[[package]]
410name = "combine"
411version = "4.6.7"
412source = "registry+https://github.com/rust-lang/crates.io-index"
413checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
414dependencies = [
415 "bytes",
416 "memchr",
417]
302418
303[[package]]419[[package]]
304name = "console"420name = "console"
308dependencies = [424dependencies = [
309 "encode_unicode",425 "encode_unicode",
310 "libc",426 "libc",
311 "windows-sys",427 "windows-sys 0.61.2",
312]428]
313429
314[[package]]430[[package]]
327source = "registry+https://github.com/rust-lang/crates.io-index"443source = "registry+https://github.com/rust-lang/crates.io-index"
328checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"444checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
445
446[[package]]
447name = "core-foundation"
448version = "0.10.1"
449source = "registry+https://github.com/rust-lang/crates.io-index"
450checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
451dependencies = [
452 "core-foundation-sys",
453 "libc",
454]
455
456[[package]]
457name = "core-foundation-sys"
458version = "0.8.7"
459source = "registry+https://github.com/rust-lang/crates.io-index"
460checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
329461
330[[package]]462[[package]]
331name = "countme"463name = "countme"
332version = "3.0.1"464version = "3.0.1"
333source = "registry+https://github.com/rust-lang/crates.io-index"465source = "registry+https://github.com/rust-lang/crates.io-index"
334checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"466checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
467
468[[package]]
469name = "cpufeatures"
470version = "0.2.17"
471source = "registry+https://github.com/rust-lang/crates.io-index"
472checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
473dependencies = [
474 "libc",
475]
335476
336[[package]]477[[package]]
337name = "cpufeatures"478name = "cpufeatures"
342 "libc",483 "libc",
343]484]
485
486[[package]]
487name = "crc32fast"
488version = "1.5.0"
489source = "registry+https://github.com/rust-lang/crates.io-index"
490checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
491dependencies = [
492 "cfg-if",
493]
344494
345[[package]]495[[package]]
346name = "criterion"496name = "criterion"
377 "itertools 0.13.0",527 "itertools 0.13.0",
378]528]
529
530[[package]]
531name = "crossbeam-channel"
532version = "0.5.15"
533source = "registry+https://github.com/rust-lang/crates.io-index"
534checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
535dependencies = [
536 "crossbeam-utils",
537]
379538
380[[package]]539[[package]]
381name = "crossbeam-deque"540name = "crossbeam-deque"
408source = "registry+https://github.com/rust-lang/crates.io-index"567source = "registry+https://github.com/rust-lang/crates.io-index"
409checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"568checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
569
570[[package]]
571name = "crypto-common"
572version = "0.1.7"
573source = "registry+https://github.com/rust-lang/crates.io-index"
574checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
575dependencies = [
576 "generic-array",
577 "typenum",
578]
410579
411[[package]]580[[package]]
412name = "crypto-common"581name = "crypto-common"
417 "hybrid-array",586 "hybrid-array",
418]587]
588
589[[package]]
590name = "dashmap"
591version = "6.1.0"
592source = "registry+https://github.com/rust-lang/crates.io-index"
593checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
594dependencies = [
595 "cfg-if",
596 "crossbeam-utils",
597 "hashbrown 0.14.5",
598 "lock_api",
599 "once_cell",
600 "parking_lot_core",
601]
602
603[[package]]
604name = "digest"
605version = "0.10.7"
606source = "registry+https://github.com/rust-lang/crates.io-index"
607checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
608dependencies = [
609 "block-buffer 0.10.4",
610 "crypto-common 0.1.7",
611]
419612
420[[package]]613[[package]]
421name = "digest"614name = "digest"
422version = "0.11.3"615version = "0.11.3"
423source = "registry+https://github.com/rust-lang/crates.io-index"616source = "registry+https://github.com/rust-lang/crates.io-index"
424checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"617checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
425dependencies = [618dependencies = [
426 "block-buffer",619 "block-buffer 0.12.0",
427 "const-oid",620 "const-oid",
428 "crypto-common",621 "crypto-common 0.2.1",
429]622]
623
624[[package]]
625name = "directories"
626version = "6.0.0"
627source = "registry+https://github.com/rust-lang/crates.io-index"
628checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
629dependencies = [
630 "dirs-sys",
631]
632
633[[package]]
634name = "dirs-sys"
635version = "0.5.0"
636source = "registry+https://github.com/rust-lang/crates.io-index"
637checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
638dependencies = [
639 "libc",
640 "option-ext",
641 "redox_users",
642 "windows-sys 0.61.2",
643]
430644
431[[package]]645[[package]]
432name = "displaydoc"646name = "displaydoc"
460source = "registry+https://github.com/rust-lang/crates.io-index"674source = "registry+https://github.com/rust-lang/crates.io-index"
461checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"675checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1"
676
677[[package]]
678name = "dunce"
679version = "1.0.5"
680source = "registry+https://github.com/rust-lang/crates.io-index"
681checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
462682
463[[package]]683[[package]]
464name = "educe"684name = "educe"
535checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"755checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
536dependencies = [756dependencies = [
537 "libc",757 "libc",
538 "windows-sys",758 "windows-sys 0.61.2",
539]759]
540760
541[[package]]761[[package]]
549 "syn",769 "syn",
550]770]
771
772[[package]]
773name = "faster-hex"
774version = "0.10.0"
775source = "registry+https://github.com/rust-lang/crates.io-index"
776checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73"
777dependencies = [
778 "heapless",
779 "serde",
780]
551781
552[[package]]782[[package]]
553name = "fastrand"783name = "fastrand"
554version = "2.4.1"784version = "2.4.1"
555source = "registry+https://github.com/rust-lang/crates.io-index"785source = "registry+https://github.com/rust-lang/crates.io-index"
556checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"786checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
787
788[[package]]
789name = "filetime"
790version = "0.2.27"
791source = "registry+https://github.com/rust-lang/crates.io-index"
792checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
793dependencies = [
794 "cfg-if",
795 "libc",
796 "libredox",
797]
557798
558[[package]]799[[package]]
559name = "find-msvc-tools"800name = "find-msvc-tools"
560version = "0.1.9"801version = "0.1.9"
561source = "registry+https://github.com/rust-lang/crates.io-index"802source = "registry+https://github.com/rust-lang/crates.io-index"
562checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"803checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
804
805[[package]]
806name = "flate2"
807version = "1.1.9"
808source = "registry+https://github.com/rust-lang/crates.io-index"
809checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
810dependencies = [
811 "miniz_oxide",
812 "zlib-rs",
813]
563814
564[[package]]815[[package]]
565name = "fnv"816name = "fnv"
588 "percent-encoding",839 "percent-encoding",
589]840]
841
842[[package]]
843name = "fs_extra"
844version = "1.3.0"
845source = "registry+https://github.com/rust-lang/crates.io-index"
846checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
847
848[[package]]
849name = "futures-channel"
850version = "0.3.32"
851source = "registry+https://github.com/rust-lang/crates.io-index"
852checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
853dependencies = [
854 "futures-core",
855 "futures-sink",
856]
857
858[[package]]
859name = "futures-core"
860version = "0.3.32"
861source = "registry+https://github.com/rust-lang/crates.io-index"
862checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
863
864[[package]]
865name = "futures-io"
866version = "0.3.32"
867source = "registry+https://github.com/rust-lang/crates.io-index"
868checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
869
870[[package]]
871name = "futures-sink"
872version = "0.3.32"
873source = "registry+https://github.com/rust-lang/crates.io-index"
874checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
875
876[[package]]
877name = "futures-task"
878version = "0.3.32"
879source = "registry+https://github.com/rust-lang/crates.io-index"
880checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
881
882[[package]]
883name = "futures-util"
884version = "0.3.32"
885source = "registry+https://github.com/rust-lang/crates.io-index"
886checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
887dependencies = [
888 "futures-core",
889 "futures-io",
890 "futures-sink",
891 "futures-task",
892 "memchr",
893 "pin-project-lite",
894 "slab",
895]
896
897[[package]]
898name = "generic-array"
899version = "0.14.7"
900source = "registry+https://github.com/rust-lang/crates.io-index"
901checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
902dependencies = [
903 "typenum",
904 "version_check",
905]
906
907[[package]]
908name = "getrandom"
909version = "0.2.17"
910source = "registry+https://github.com/rust-lang/crates.io-index"
911checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
912dependencies = [
913 "cfg-if",
914 "js-sys",
915 "libc",
916 "wasi",
917 "wasm-bindgen",
918]
590919
591[[package]]920[[package]]
592name = "getrandom"921name = "getrandom"
615 "wasip3",944 "wasip3",
616]945]
946
947[[package]]
948name = "gix"
949version = "0.83.0"
950source = "registry+https://github.com/rust-lang/crates.io-index"
951checksum = "6ce52001b946a6249d5d0d3011df0a042ac3f8a4d013460db6476577b0b9c567"
952dependencies = [
953 "gix-actor",
954 "gix-archive",
955 "gix-attributes",
956 "gix-blame",
957 "gix-command",
958 "gix-commitgraph",
959 "gix-config",
960 "gix-credentials",
961 "gix-date",
962 "gix-diff",
963 "gix-dir",
964 "gix-discover",
965 "gix-error",
966 "gix-features",
967 "gix-filter",
968 "gix-fs",
969 "gix-glob",
970 "gix-hash",
971 "gix-hashtable",
972 "gix-ignore",
973 "gix-index",
974 "gix-lock",
975 "gix-mailmap",
976 "gix-merge",
977 "gix-negotiate",
978 "gix-object",
979 "gix-odb",
980 "gix-pack",
981 "gix-path",
982 "gix-pathspec",
983 "gix-prompt",
984 "gix-protocol",
985 "gix-ref",
986 "gix-refspec",
987 "gix-revision",
988 "gix-revwalk",
989 "gix-sec",
990 "gix-shallow",
991 "gix-status",
992 "gix-submodule",
993 "gix-tempfile",
994 "gix-trace",
995 "gix-transport",
996 "gix-traverse",
997 "gix-url",
998 "gix-utils",
999 "gix-validate",
1000 "gix-worktree",
1001 "gix-worktree-state",
1002 "gix-worktree-stream",
1003 "nonempty",
1004 "parking_lot",
1005 "regex",
1006 "signal-hook",
1007 "smallvec",
1008 "thiserror",
1009]
1010
1011[[package]]
1012name = "gix-actor"
1013version = "0.41.0"
1014source = "registry+https://github.com/rust-lang/crates.io-index"
1015checksum = "272916673b83714734b15d4ef3c8b5f1ccddb15fea8ff548430b97c1ab7b7ed8"
1016dependencies = [
1017 "bstr",
1018 "gix-date",
1019 "gix-error",
1020]
1021
1022[[package]]
1023name = "gix-archive"
1024version = "0.32.0"
1025source = "registry+https://github.com/rust-lang/crates.io-index"
1026checksum = "9a20ec244b733338d4cb60e5e05eac700dab7fcc689647b1d1daa9396b119342"
1027dependencies = [
1028 "bstr",
1029 "gix-date",
1030 "gix-error",
1031 "gix-object",
1032 "gix-worktree-stream",
1033]
1034
1035[[package]]
1036name = "gix-attributes"
1037version = "0.33.0"
1038source = "registry+https://github.com/rust-lang/crates.io-index"
1039checksum = "fe17c5a1c0b6f2ef1476aa1d3222ea50cdff67608016613a58bfc3e078046000"
1040dependencies = [
1041 "bstr",
1042 "gix-glob",
1043 "gix-path",
1044 "gix-quote",
1045 "gix-trace",
1046 "kstring",
1047 "smallvec",
1048 "thiserror",
1049 "unicode-bom",
1050]
1051
1052[[package]]
1053name = "gix-bitmap"
1054version = "0.3.1"
1055source = "registry+https://github.com/rust-lang/crates.io-index"
1056checksum = "1ecbfc77ec6852294e341ecc305a490b59f2813e6ca42d79efda5099dcab1894"
1057dependencies = [
1058 "gix-error",
1059]
1060
1061[[package]]
1062name = "gix-blame"
1063version = "0.13.0"
1064source = "registry+https://github.com/rust-lang/crates.io-index"
1065checksum = "14dab9a942ab54a9661ded7397c3bf927274e7afa94494db0d75cfcbde02ca0a"
1066dependencies = [
1067 "gix-commitgraph",
1068 "gix-date",
1069 "gix-diff",
1070 "gix-error",
1071 "gix-hash",
1072 "gix-object",
1073 "gix-revwalk",
1074 "gix-trace",
1075 "gix-traverse",
1076 "gix-worktree",
1077 "smallvec",
1078 "thiserror",
1079]
1080
1081[[package]]
1082name = "gix-chunk"
1083version = "0.7.1"
1084source = "registry+https://github.com/rust-lang/crates.io-index"
1085checksum = "edf288be9b60fe7231de03771faa292be1493d84786f68727e33ad1f91764320"
1086dependencies = [
1087 "gix-error",
1088]
1089
1090[[package]]
1091name = "gix-command"
1092version = "0.9.0"
1093source = "registry+https://github.com/rust-lang/crates.io-index"
1094checksum = "86335306511abe43d75c866d4b1f3d90932fe202edcd43e1314036333e7384d8"
1095dependencies = [
1096 "bstr",
1097 "gix-path",
1098 "gix-quote",
1099 "gix-trace",
1100 "shell-words",
1101]
1102
1103[[package]]
1104name = "gix-commitgraph"
1105version = "0.37.0"
1106source = "registry+https://github.com/rust-lang/crates.io-index"
1107checksum = "fe3b5aa0f24e19028c261d229aeeedafcaaa52ebd71021cc15184620fc9d32eb"
1108dependencies = [
1109 "bstr",
1110 "gix-chunk",
1111 "gix-error",
1112 "gix-hash",
1113 "memmap2",
1114 "nonempty",
1115]
1116
1117[[package]]
1118name = "gix-config"
1119version = "0.56.0"
1120source = "registry+https://github.com/rust-lang/crates.io-index"
1121checksum = "8c01848aebd21c67f6ba41f1de8efd46ae96df21f001954a3c9e1517e514d410"
1122dependencies = [
1123 "bstr",
1124 "gix-config-value",
1125 "gix-features",
1126 "gix-glob",
1127 "gix-path",
1128 "gix-ref",
1129 "gix-sec",
1130 "smallvec",
1131 "thiserror",
1132 "unicode-bom",
1133]
1134
1135[[package]]
1136name = "gix-config-value"
1137version = "0.18.0"
1138source = "registry+https://github.com/rust-lang/crates.io-index"
1139checksum = "13b39ed39ee4c10a3b157f9fb94bac8098d9f8e56201f0cf7dee6c187416c4b2"
1140dependencies = [
1141 "bitflags",
1142 "bstr",
1143 "gix-path",
1144 "libc",
1145 "thiserror",
1146]
1147
1148[[package]]
1149name = "gix-credentials"
1150version = "0.38.0"
1151source = "registry+https://github.com/rust-lang/crates.io-index"
1152checksum = "65ca11598b70811d7b16ff90945a6e57dfe521e85b744e51636965fe39cc8f60"
1153dependencies = [
1154 "bstr",
1155 "gix-command",
1156 "gix-config-value",
1157 "gix-date",
1158 "gix-path",
1159 "gix-prompt",
1160 "gix-sec",
1161 "gix-trace",
1162 "gix-url",
1163 "thiserror",
1164]
1165
1166[[package]]
1167name = "gix-date"
1168version = "0.15.3"
1169source = "registry+https://github.com/rust-lang/crates.io-index"
1170checksum = "b94cdae4eb4b0f4136e3d9b3aa2d2cd03cfb5bb9b636b31263aea2df86d41543"
1171dependencies = [
1172 "bstr",
1173 "gix-error",
1174 "itoa",
1175 "jiff",
1176 "smallvec",
1177]
1178
1179[[package]]
1180name = "gix-diff"
1181version = "0.63.0"
1182source = "registry+https://github.com/rust-lang/crates.io-index"
1183checksum = "dc08e0fa1a91ff5f24affeab052f198056645e1de004910bde7b82b50ea5982a"
1184dependencies = [
1185 "bstr",
1186 "gix-attributes",
1187 "gix-command",
1188 "gix-filter",
1189 "gix-fs",
1190 "gix-hash",
1191 "gix-imara-diff",
1192 "gix-index",
1193 "gix-object",
1194 "gix-path",
1195 "gix-pathspec",
1196 "gix-tempfile",
1197 "gix-trace",
1198 "gix-traverse",
1199 "gix-worktree",
1200 "thiserror",
1201]
1202
1203[[package]]
1204name = "gix-dir"
1205version = "0.25.0"
1206source = "registry+https://github.com/rust-lang/crates.io-index"
1207checksum = "32a0fc06e9e1e430cbf0a313666976d90f822f461a6525320427aa9b8af5236c"
1208dependencies = [
1209 "bstr",
1210 "gix-discover",
1211 "gix-fs",
1212 "gix-ignore",
1213 "gix-index",
1214 "gix-object",
1215 "gix-path",
1216 "gix-pathspec",
1217 "gix-trace",
1218 "gix-utils",
1219 "gix-worktree",
1220 "thiserror",
1221]
1222
1223[[package]]
1224name = "gix-discover"
1225version = "0.51.0"
1226source = "registry+https://github.com/rust-lang/crates.io-index"
1227checksum = "17852e6a501e688a1702b24ebe5b3761d4719455bc869fd29f38b0b859bcad34"
1228dependencies = [
1229 "bstr",
1230 "dunce",
1231 "gix-fs",
1232 "gix-path",
1233 "gix-ref",
1234 "gix-sec",
1235 "thiserror",
1236]
1237
1238[[package]]
1239name = "gix-error"
1240version = "0.2.3"
1241source = "registry+https://github.com/rust-lang/crates.io-index"
1242checksum = "e207b971746ab724fccdfced2e4e19e854744611904a0195d3aa8fda8a110613"
1243dependencies = [
1244 "bstr",
1245]
1246
1247[[package]]
1248name = "gix-features"
1249version = "0.48.0"
1250source = "registry+https://github.com/rust-lang/crates.io-index"
1251checksum = "af375693ad5333d0a2c66b4c5b2cbe9ccc38e34f8e8bf24e4ae42c12307fdc4f"
1252dependencies = [
1253 "bytes",
1254 "bytesize",
1255 "crc32fast",
1256 "crossbeam-channel",
1257 "gix-path",
1258 "gix-trace",
1259 "gix-utils",
1260 "libc",
1261 "once_cell",
1262 "parking_lot",
1263 "prodash",
1264 "thiserror",
1265 "walkdir",
1266 "zlib-rs",
1267]
1268
1269[[package]]
1270name = "gix-filter"
1271version = "0.30.0"
1272source = "registry+https://github.com/rust-lang/crates.io-index"
1273checksum = "dac917dbe9653c9b615d248db91907a365bd779750c9e1b457a9d9fdeece3a08"
1274dependencies = [
1275 "bstr",
1276 "encoding_rs",
1277 "gix-attributes",
1278 "gix-command",
1279 "gix-hash",
1280 "gix-object",
1281 "gix-packetline",
1282 "gix-path",
1283 "gix-quote",
1284 "gix-trace",
1285 "gix-utils",
1286 "smallvec",
1287 "thiserror",
1288]
1289
1290[[package]]
1291name = "gix-fs"
1292version = "0.21.1"
1293source = "registry+https://github.com/rust-lang/crates.io-index"
1294checksum = "1e1967daac9848757c47c2aef0c57bcadc1a897347f559778249bf286a536c86"
1295dependencies = [
1296 "bstr",
1297 "fastrand",
1298 "gix-features",
1299 "gix-path",
1300 "gix-utils",
1301 "thiserror",
1302]
1303
1304[[package]]
1305name = "gix-glob"
1306version = "0.26.0"
1307source = "registry+https://github.com/rust-lang/crates.io-index"
1308checksum = "08bf29249a069bf2507f5964f80997f37b134d320ea348d66527726b9be2c38c"
1309dependencies = [
1310 "bitflags",
1311 "bstr",
1312 "gix-features",
1313 "gix-path",
1314]
1315
1316[[package]]
1317name = "gix-hash"
1318version = "0.25.0"
1319source = "registry+https://github.com/rust-lang/crates.io-index"
1320checksum = "bcf70d1e252337eed16360f8b8ebb71865ece58eab7954b39ce38b420de703d2"
1321dependencies = [
1322 "faster-hex",
1323 "gix-features",
1324 "sha1-checked",
1325 "thiserror",
1326]
1327
1328[[package]]
1329name = "gix-hashtable"
1330version = "0.15.0"
1331source = "registry+https://github.com/rust-lang/crates.io-index"
1332checksum = "d33b455e07b3c16d3b2eeebc7b38d2dafcbf8a653de1138ef55d4c2a1fd0b08b"
1333dependencies = [
1334 "gix-hash",
1335 "hashbrown 0.16.1",
1336 "parking_lot",
1337]
1338
1339[[package]]
1340name = "gix-ignore"
1341version = "0.21.0"
1342source = "registry+https://github.com/rust-lang/crates.io-index"
1343checksum = "6bb13fbbeeafee943e52b61fcc88dfddf6a452fcaf0c4d0cdc8f218fa25bbec5"
1344dependencies = [
1345 "bstr",
1346 "gix-glob",
1347 "gix-path",
1348 "gix-trace",
1349 "unicode-bom",
1350]
1351
1352[[package]]
1353name = "gix-imara-diff"
1354version = "0.2.1"
1355source = "registry+https://github.com/rust-lang/crates.io-index"
1356checksum = "39eb0623e15e4cb83c02ce6a959e48fadd1ae3b715b36b5acc01816e01388c82"
1357dependencies = [
1358 "bstr",
1359 "hashbrown 0.16.1",
1360]
1361
1362[[package]]
1363name = "gix-index"
1364version = "0.51.0"
1365source = "registry+https://github.com/rust-lang/crates.io-index"
1366checksum = "54c3ef97ad08121e4327a6226bd63fed6b9e3c6b976d48bddd4356d9d41191db"
1367dependencies = [
1368 "bitflags",
1369 "bstr",
1370 "filetime",
1371 "fnv",
1372 "gix-bitmap",
1373 "gix-features",
1374 "gix-fs",
1375 "gix-hash",
1376 "gix-lock",
1377 "gix-object",
1378 "gix-traverse",
1379 "gix-utils",
1380 "gix-validate",
1381 "hashbrown 0.16.1",
1382 "itoa",
1383 "libc",
1384 "memmap2",
1385 "rustix",
1386 "smallvec",
1387 "thiserror",
1388]
1389
1390[[package]]
1391name = "gix-lock"
1392version = "23.0.0"
1393source = "registry+https://github.com/rust-lang/crates.io-index"
1394checksum = "09b3bc074e5723027b482dcd9ab99d95804a53742f6de812d0172fbba4a186c1"
1395dependencies = [
1396 "gix-tempfile",
1397 "gix-utils",
1398 "thiserror",
1399]
1400
1401[[package]]
1402name = "gix-mailmap"
1403version = "0.33.0"
1404source = "registry+https://github.com/rust-lang/crates.io-index"
1405checksum = "023d3a6561cbebe45b89e0764d48928ad970667076f16fa5889e6f86d8432086"
1406dependencies = [
1407 "bstr",
1408 "gix-actor",
1409 "gix-date",
1410 "gix-error",
1411]
1412
1413[[package]]
1414name = "gix-merge"
1415version = "0.16.0"
1416source = "registry+https://github.com/rust-lang/crates.io-index"
1417checksum = "74bbcdcc52b70a32f0a151b024dff9d0fcf56ee48f00d9503e735af9d99ea881"
1418dependencies = [
1419 "bstr",
1420 "gix-command",
1421 "gix-diff",
1422 "gix-filter",
1423 "gix-fs",
1424 "gix-hash",
1425 "gix-imara-diff",
1426 "gix-index",
1427 "gix-object",
1428 "gix-path",
1429 "gix-quote",
1430 "gix-revision",
1431 "gix-revwalk",
1432 "gix-tempfile",
1433 "gix-trace",
1434 "gix-worktree",
1435 "nonempty",
1436 "thiserror",
1437]
1438
1439[[package]]
1440name = "gix-negotiate"
1441version = "0.31.0"
1442source = "registry+https://github.com/rust-lang/crates.io-index"
1443checksum = "103d42bfade1b8a96ca5005933127bdad461ce588d92422b2c2daa3ff20d780c"
1444dependencies = [
1445 "bitflags",
1446 "gix-commitgraph",
1447 "gix-date",
1448 "gix-hash",
1449 "gix-object",
1450 "gix-revwalk",
1451]
1452
1453[[package]]
1454name = "gix-object"
1455version = "0.60.0"
1456source = "registry+https://github.com/rust-lang/crates.io-index"
1457checksum = "a38075a95d7cc5df8afd38e72c617026c1456952207a4120a7f55a3fbf93b4d7"
1458dependencies = [
1459 "bstr",
1460 "gix-actor",
1461 "gix-date",
1462 "gix-features",
1463 "gix-hash",
1464 "gix-hashtable",
1465 "gix-utils",
1466 "gix-validate",
1467 "itoa",
1468 "smallvec",
1469 "thiserror",
1470]
1471
1472[[package]]
1473name = "gix-odb"
1474version = "0.80.0"
1475source = "registry+https://github.com/rust-lang/crates.io-index"
1476checksum = "aeeda12a9663120418735ecdc1250d06eeab0be75700e47b3402a981331716ba"
1477dependencies = [
1478 "arc-swap",
1479 "gix-features",
1480 "gix-fs",
1481 "gix-hash",
1482 "gix-hashtable",
1483 "gix-object",
1484 "gix-pack",
1485 "gix-path",
1486 "gix-quote",
1487 "memmap2",
1488 "parking_lot",
1489 "tempfile",
1490 "thiserror",
1491]
1492
1493[[package]]
1494name = "gix-pack"
1495version = "0.70.0"
1496source = "registry+https://github.com/rust-lang/crates.io-index"
1497checksum = "daf02e6f5c8f07a069c9ea5245f40d9b14856ada4086091dc99941b49002b4fa"
1498dependencies = [
1499 "clru",
1500 "gix-chunk",
1501 "gix-error",
1502 "gix-features",
1503 "gix-hash",
1504 "gix-hashtable",
1505 "gix-object",
1506 "gix-path",
1507 "gix-tempfile",
1508 "memmap2",
1509 "parking_lot",
1510 "smallvec",
1511 "thiserror",
1512 "uluru",
1513]
1514
1515[[package]]
1516name = "gix-packetline"
1517version = "0.21.3"
1518source = "registry+https://github.com/rust-lang/crates.io-index"
1519checksum = "362246df440ee691699f0664cbf7006a6ece477db6734222be95e4198e5656e6"
1520dependencies = [
1521 "bstr",
1522 "faster-hex",
1523 "gix-trace",
1524 "thiserror",
1525]
1526
1527[[package]]
1528name = "gix-path"
1529version = "0.12.0"
1530source = "registry+https://github.com/rust-lang/crates.io-index"
1531checksum = "671a6059e8a4c1b7f406e24716499cefa3926e060876fb1959ef225efeee346e"
1532dependencies = [
1533 "bstr",
1534 "gix-trace",
1535 "gix-validate",
1536 "thiserror",
1537]
1538
1539[[package]]
1540name = "gix-pathspec"
1541version = "0.18.0"
1542source = "registry+https://github.com/rust-lang/crates.io-index"
1543checksum = "2a84a4f083dd70fb49f4377e13afa6d90df2daaa1c705c49d6ff1331fc7e8855"
1544dependencies = [
1545 "bitflags",
1546 "bstr",
1547 "gix-attributes",
1548 "gix-config-value",
1549 "gix-glob",
1550 "gix-path",
1551 "thiserror",
1552]
1553
1554[[package]]
1555name = "gix-prompt"
1556version = "0.15.0"
1557source = "registry+https://github.com/rust-lang/crates.io-index"
1558checksum = "e041a626c64cb69e4117fcdf80da8d0e454fba3b1f420412792d191f52251aee"
1559dependencies = [
1560 "gix-command",
1561 "gix-config-value",
1562 "parking_lot",
1563 "rustix",
1564 "thiserror",
1565]
1566
1567[[package]]
1568name = "gix-protocol"
1569version = "0.61.0"
1570source = "registry+https://github.com/rust-lang/crates.io-index"
1571checksum = "aa4bee82db63ec635996b96efae71cf467c155fa3f34a556184373224a26c4fd"
1572dependencies = [
1573 "bstr",
1574 "gix-credentials",
1575 "gix-date",
1576 "gix-features",
1577 "gix-hash",
1578 "gix-lock",
1579 "gix-negotiate",
1580 "gix-object",
1581 "gix-ref",
1582 "gix-refspec",
1583 "gix-revwalk",
1584 "gix-shallow",
1585 "gix-trace",
1586 "gix-transport",
1587 "gix-utils",
1588 "maybe-async",
1589 "nonempty",
1590 "thiserror",
1591]
1592
1593[[package]]
1594name = "gix-quote"
1595version = "0.7.1"
1596source = "registry+https://github.com/rust-lang/crates.io-index"
1597checksum = "6e97b73791a64bc0fa7dd2c5b3e551136115f97750b876ed1c952c7a7dbaf8be"
1598dependencies = [
1599 "bstr",
1600 "gix-error",
1601 "gix-utils",
1602]
1603
1604[[package]]
1605name = "gix-ref"
1606version = "0.63.0"
1607source = "registry+https://github.com/rust-lang/crates.io-index"
1608checksum = "d8ba9cc15f558b274c99349b83130f5ec83459660828fde9718bbbb43a726167"
1609dependencies = [
1610 "gix-actor",
1611 "gix-features",
1612 "gix-fs",
1613 "gix-hash",
1614 "gix-lock",
1615 "gix-object",
1616 "gix-path",
1617 "gix-tempfile",
1618 "gix-utils",
1619 "gix-validate",
1620 "memmap2",
1621 "thiserror",
1622]
1623
1624[[package]]
1625name = "gix-refspec"
1626version = "0.41.0"
1627source = "registry+https://github.com/rust-lang/crates.io-index"
1628checksum = "61755b27d57edc8940a1b1593c8c61548ca8e4c02da1ed8d5bfeda9eb2a6b761"
1629dependencies = [
1630 "bstr",
1631 "gix-error",
1632 "gix-glob",
1633 "gix-hash",
1634 "gix-revision",
1635 "gix-validate",
1636 "smallvec",
1637 "thiserror",
1638]
1639
1640[[package]]
1641name = "gix-revision"
1642version = "0.45.0"
1643source = "registry+https://github.com/rust-lang/crates.io-index"
1644checksum = "1fb5288fac706d3ea3e4e2ba9ec38b78743b8c02f422e18cb342299cfd6ab7e8"
1645dependencies = [
1646 "bitflags",
1647 "bstr",
1648 "gix-commitgraph",
1649 "gix-date",
1650 "gix-error",
1651 "gix-hash",
1652 "gix-hashtable",
1653 "gix-object",
1654 "gix-revwalk",
1655 "gix-trace",
1656 "nonempty",
1657]
1658
1659[[package]]
1660name = "gix-revwalk"
1661version = "0.31.0"
1662source = "registry+https://github.com/rust-lang/crates.io-index"
1663checksum = "313813706b073a12ff7f9b2896bf3e6504cdac7cfbc97b1920114724705069f0"
1664dependencies = [
1665 "gix-commitgraph",
1666 "gix-date",
1667 "gix-error",
1668 "gix-hash",
1669 "gix-hashtable",
1670 "gix-object",
1671 "smallvec",
1672 "thiserror",
1673]
1674
1675[[package]]
1676name = "gix-sec"
1677version = "0.14.0"
1678source = "registry+https://github.com/rust-lang/crates.io-index"
1679checksum = "f5a3a2d3e504a238136751e646a6c028252286a0ea64ea9974bf0498633407c6"
1680dependencies = [
1681 "bitflags",
1682 "gix-path",
1683 "libc",
1684 "windows-sys 0.61.2",
1685]
1686
1687[[package]]
1688name = "gix-shallow"
1689version = "0.12.0"
1690source = "registry+https://github.com/rust-lang/crates.io-index"
1691checksum = "29187305521bfacf4aefd284ab28dbfa9fb74abd39a5e63dd313b1baa5808c27"
1692dependencies = [
1693 "bstr",
1694 "gix-hash",
1695 "gix-lock",
1696 "nonempty",
1697 "thiserror",
1698]
1699
1700[[package]]
1701name = "gix-status"
1702version = "0.30.0"
1703source = "registry+https://github.com/rust-lang/crates.io-index"
1704checksum = "68c6d2a8c521ffa205fe7e268c82e6d1378ba37cd826ca10ab6129fdc29a4b65"
1705dependencies = [
1706 "bstr",
1707 "filetime",
1708 "gix-diff",
1709 "gix-dir",
1710 "gix-features",
1711 "gix-filter",
1712 "gix-fs",
1713 "gix-hash",
1714 "gix-index",
1715 "gix-object",
1716 "gix-path",
1717 "gix-pathspec",
1718 "gix-worktree",
1719 "portable-atomic",
1720 "thiserror",
1721]
1722
1723[[package]]
1724name = "gix-submodule"
1725version = "0.30.0"
1726source = "registry+https://github.com/rust-lang/crates.io-index"
1727checksum = "9fd5fc8692890bd71a596e540fd4c364f8460eaa82c4eaaedebde6e1e3eb4d91"
1728dependencies = [
1729 "bstr",
1730 "gix-config",
1731 "gix-path",
1732 "gix-pathspec",
1733 "gix-refspec",
1734 "gix-url",
1735 "thiserror",
1736]
1737
1738[[package]]
1739name = "gix-tempfile"
1740version = "23.0.0"
1741source = "registry+https://github.com/rust-lang/crates.io-index"
1742checksum = "691ea1e31435c7e7d4d04705ec9d1c0d9482c46b2acf512bc723939d8f0af7fb"
1743dependencies = [
1744 "dashmap",
1745 "gix-fs",
1746 "libc",
1747 "parking_lot",
1748 "signal-hook",
1749 "signal-hook-registry",
1750 "tempfile",
1751]
1752
1753[[package]]
1754name = "gix-trace"
1755version = "0.1.19"
1756source = "registry+https://github.com/rust-lang/crates.io-index"
1757checksum = "6f23569e55f2ffaf958617353b9734a7d52a7c19c439eeaa5e3efc217fd2270e"
1758
1759[[package]]
1760name = "gix-transport"
1761version = "0.57.0"
1762source = "registry+https://github.com/rust-lang/crates.io-index"
1763checksum = "ffd6a5c676b92d4ead5f5a2b2935024415dec69edc997b6090ca9cac010a3018"
1764dependencies = [
1765 "base64",
1766 "bstr",
1767 "gix-command",
1768 "gix-credentials",
1769 "gix-features",
1770 "gix-packetline",
1771 "gix-quote",
1772 "gix-sec",
1773 "gix-url",
1774 "reqwest",
1775 "thiserror",
1776]
1777
1778[[package]]
1779name = "gix-traverse"
1780version = "0.57.0"
1781source = "registry+https://github.com/rust-lang/crates.io-index"
1782checksum = "a14b7052c0786676c03e71fcfde7d7f0f8e8316e642b5cec6bb3998719b2ce5c"
1783dependencies = [
1784 "bitflags",
1785 "gix-commitgraph",
1786 "gix-date",
1787 "gix-hash",
1788 "gix-hashtable",
1789 "gix-object",
1790 "gix-revwalk",
1791 "smallvec",
1792 "thiserror",
1793]
1794
1795[[package]]
1796name = "gix-url"
1797version = "0.36.0"
1798source = "registry+https://github.com/rust-lang/crates.io-index"
1799checksum = "35842d099e813f6f6bba529e88d4670572149c3df79b7a412952259887721ece"
1800dependencies = [
1801 "bstr",
1802 "gix-path",
1803 "percent-encoding",
1804 "thiserror",
1805]
1806
1807[[package]]
1808name = "gix-utils"
1809version = "0.3.2"
1810source = "registry+https://github.com/rust-lang/crates.io-index"
1811checksum = "4e477b4f07a6e8da4ba791c53c858102959703c60d70f199932010d5b94adb2c"
1812dependencies = [
1813 "bstr",
1814 "fastrand",
1815 "unicode-normalization",
1816]
1817
1818[[package]]
1819name = "gix-validate"
1820version = "0.11.1"
1821source = "registry+https://github.com/rust-lang/crates.io-index"
1822checksum = "e26ac2602b43eadfdca0560b81d3341944162a3c9f64ccdeef8fc501ad80dad5"
1823dependencies = [
1824 "bstr",
1825]
1826
1827[[package]]
1828name = "gix-worktree"
1829version = "0.52.0"
1830source = "registry+https://github.com/rust-lang/crates.io-index"
1831checksum = "d69955eb5e2910832f88d041964b809eee01dadd579237e0b55efec58fd406fd"
1832dependencies = [
1833 "bstr",
1834 "gix-attributes",
1835 "gix-fs",
1836 "gix-glob",
1837 "gix-hash",
1838 "gix-ignore",
1839 "gix-index",
1840 "gix-object",
1841 "gix-path",
1842 "gix-validate",
1843]
1844
1845[[package]]
1846name = "gix-worktree-state"
1847version = "0.30.0"
1848source = "registry+https://github.com/rust-lang/crates.io-index"
1849checksum = "8a96dccbcf9e8fe0291c55f06e08da93ebb2e691c1311276f541eefcc6d70800"
1850dependencies = [
1851 "bstr",
1852 "gix-features",
1853 "gix-filter",
1854 "gix-fs",
1855 "gix-index",
1856 "gix-object",
1857 "gix-path",
1858 "gix-worktree",
1859 "io-close",
1860 "thiserror",
1861]
1862
1863[[package]]
1864name = "gix-worktree-stream"
1865version = "0.32.0"
1866source = "registry+https://github.com/rust-lang/crates.io-index"
1867checksum = "9a8444b8ed4662e1a0c97f3eceda29630001a1bbb2632201e50312623e594213"
1868dependencies = [
1869 "gix-attributes",
1870 "gix-error",
1871 "gix-features",
1872 "gix-filter",
1873 "gix-fs",
1874 "gix-hash",
1875 "gix-object",
1876 "gix-path",
1877 "gix-traverse",
1878 "parking_lot",
1879]
6171880
618[[package]]1881[[package]]
619name = "globset"1882name = "globset"
638 "smallvec",1901 "smallvec",
639]1902]
1903
1904[[package]]
1905name = "h2"
1906version = "0.4.14"
1907source = "registry+https://github.com/rust-lang/crates.io-index"
1908checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
1909dependencies = [
1910 "atomic-waker",
1911 "bytes",
1912 "fnv",
1913 "futures-core",
1914 "futures-sink",
1915 "http",
1916 "indexmap",
1917 "slab",
1918 "tokio",
1919 "tokio-util",
1920 "tracing",
1921]
6401922
641[[package]]1923[[package]]
642name = "half"1924name = "half"
649 "zerocopy",1931 "zerocopy",
650]1932]
1933
1934[[package]]
1935name = "hash32"
1936version = "0.3.1"
1937source = "registry+https://github.com/rust-lang/crates.io-index"
1938checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
1939dependencies = [
1940 "byteorder",
1941]
6511942
652[[package]]1943[[package]]
653name = "hashbrown"1944name = "hashbrown"
666 "foldhash 0.1.5",1957 "foldhash 0.1.5",
667]1958]
1959
1960[[package]]
1961name = "hashbrown"
1962version = "0.16.1"
1963source = "registry+https://github.com/rust-lang/crates.io-index"
1964checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
1965dependencies = [
1966 "allocator-api2",
1967 "equivalent",
1968 "foldhash 0.2.0",
1969]
6681970
669[[package]]1971[[package]]
670name = "hashbrown"1972name = "hashbrown"
677 "foldhash 0.2.0",1979 "foldhash 0.2.0",
678]1980]
1981
1982[[package]]
1983name = "heapless"
1984version = "0.8.0"
1985source = "registry+https://github.com/rust-lang/crates.io-index"
1986checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
1987dependencies = [
1988 "hash32",
1989 "stable_deref_trait",
1990]
6791991
680[[package]]1992[[package]]
681name = "heck"1993name = "heck"
722 "str_indices",2034 "str_indices",
723]2035]
2036
2037[[package]]
2038name = "http"
2039version = "1.4.0"
2040source = "registry+https://github.com/rust-lang/crates.io-index"
2041checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
2042dependencies = [
2043 "bytes",
2044 "itoa",
2045]
2046
2047[[package]]
2048name = "http-body"
2049version = "1.0.1"
2050source = "registry+https://github.com/rust-lang/crates.io-index"
2051checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
2052dependencies = [
2053 "bytes",
2054 "http",
2055]
2056
2057[[package]]
2058name = "http-body-util"
2059version = "0.1.3"
2060source = "registry+https://github.com/rust-lang/crates.io-index"
2061checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
2062dependencies = [
2063 "bytes",
2064 "futures-core",
2065 "http",
2066 "http-body",
2067 "pin-project-lite",
2068]
2069
2070[[package]]
2071name = "httparse"
2072version = "1.10.1"
2073source = "registry+https://github.com/rust-lang/crates.io-index"
2074checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
2075
2076[[package]]
2077name = "human_format"
2078version = "1.2.1"
2079source = "registry+https://github.com/rust-lang/crates.io-index"
2080checksum = "eaec953f16e5bcf6b8a3cb3aa959b17e5577dbd2693e94554c462c08be22624b"
7242081
725[[package]]2082[[package]]
726name = "hybrid-array"2083name = "hybrid-array"
731 "typenum",2088 "typenum",
732]2089]
2090
2091[[package]]
2092name = "hyper"
2093version = "1.9.0"
2094source = "registry+https://github.com/rust-lang/crates.io-index"
2095checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
2096dependencies = [
2097 "atomic-waker",
2098 "bytes",
2099 "futures-channel",
2100 "futures-core",
2101 "h2",
2102 "http",
2103 "http-body",
2104 "httparse",
2105 "itoa",
2106 "pin-project-lite",
2107 "smallvec",
2108 "tokio",
2109 "want",
2110]
2111
2112[[package]]
2113name = "hyper-rustls"
2114version = "0.27.9"
2115source = "registry+https://github.com/rust-lang/crates.io-index"
2116checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
2117dependencies = [
2118 "http",
2119 "hyper",
2120 "hyper-util",
2121 "rustls",
2122 "tokio",
2123 "tokio-rustls",
2124 "tower-service",
2125]
2126
2127[[package]]
2128name = "hyper-util"
2129version = "0.1.20"
2130source = "registry+https://github.com/rust-lang/crates.io-index"
2131checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
2132dependencies = [
2133 "base64",
2134 "bytes",
2135 "futures-channel",
2136 "futures-util",
2137 "http",
2138 "http-body",
2139 "hyper",
2140 "ipnet",
2141 "libc",
2142 "percent-encoding",
2143 "pin-project-lite",
2144 "socket2",
2145 "tokio",
2146 "tower-service",
2147 "tracing",
2148]
7332149
734[[package]]2150[[package]]
735name = "icu_collections"2151name = "icu_collections"
875 "walkdir",2291 "walkdir",
876]2292]
2293
2294[[package]]
2295name = "io-close"
2296version = "0.3.7"
2297source = "registry+https://github.com/rust-lang/crates.io-index"
2298checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc"
2299dependencies = [
2300 "libc",
2301 "winapi",
2302]
2303
2304[[package]]
2305name = "ipnet"
2306version = "2.12.0"
2307source = "registry+https://github.com/rust-lang/crates.io-index"
2308checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
8772309
878[[package]]2310[[package]]
879name = "is_terminal_polyfill"2311name = "is_terminal_polyfill"
905source = "registry+https://github.com/rust-lang/crates.io-index"2337source = "registry+https://github.com/rust-lang/crates.io-index"
906checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"2338checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
2339
2340[[package]]
2341name = "jiff"
2342version = "0.2.24"
2343source = "registry+https://github.com/rust-lang/crates.io-index"
2344checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d"
2345dependencies = [
2346 "jiff-static",
2347 "jiff-tzdb-platform",
2348 "log",
2349 "portable-atomic",
2350 "portable-atomic-util",
2351 "serde_core",
2352 "windows-sys 0.61.2",
2353]
2354
2355[[package]]
2356name = "jiff-static"
2357version = "0.2.24"
2358source = "registry+https://github.com/rust-lang/crates.io-index"
2359checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7"
2360dependencies = [
2361 "proc-macro2",
2362 "quote",
2363 "syn",
2364]
2365
2366[[package]]
2367name = "jiff-tzdb"
2368version = "0.1.6"
2369source = "registry+https://github.com/rust-lang/crates.io-index"
2370checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"
2371
2372[[package]]
2373name = "jiff-tzdb-platform"
2374version = "0.1.3"
2375source = "registry+https://github.com/rust-lang/crates.io-index"
2376checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
2377dependencies = [
2378 "jiff-tzdb",
2379]
2380
2381[[package]]
2382name = "jni"
2383version = "0.22.4"
2384source = "registry+https://github.com/rust-lang/crates.io-index"
2385checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
2386dependencies = [
2387 "cfg-if",
2388 "combine",
2389 "jni-macros",
2390 "jni-sys",
2391 "log",
2392 "simd_cesu8",
2393 "thiserror",
2394 "walkdir",
2395 "windows-link",
2396]
2397
2398[[package]]
2399name = "jni-macros"
2400version = "0.22.4"
2401source = "registry+https://github.com/rust-lang/crates.io-index"
2402checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
2403dependencies = [
2404 "proc-macro2",
2405 "quote",
2406 "rustc_version",
2407 "simd_cesu8",
2408 "syn",
2409]
2410
2411[[package]]
2412name = "jni-sys"
2413version = "0.4.1"
2414source = "registry+https://github.com/rust-lang/crates.io-index"
2415checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
2416dependencies = [
2417 "jni-sys-macros",
2418]
2419
2420[[package]]
2421name = "jni-sys-macros"
2422version = "0.4.1"
2423source = "registry+https://github.com/rust-lang/crates.io-index"
2424checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
2425dependencies = [
2426 "quote",
2427 "syn",
2428]
2429
2430[[package]]
2431name = "jobserver"
2432version = "0.1.34"
2433source = "registry+https://github.com/rust-lang/crates.io-index"
2434checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
2435dependencies = [
2436 "getrandom 0.3.4",
2437 "libc",
2438]
2439
2440[[package]]
2441name = "jrb"
2442version = "0.5.0-pre98"
2443dependencies = [
2444 "clap",
2445 "jrsonnet-pkg",
2446 "serde",
2447 "serde_json",
2448 "tracing",
2449 "tracing-subscriber",
2450]
9072451
908[[package]]2452[[package]]
909name = "jrsonnet"2453name = "jrsonnet"
1077 "peg",2621 "peg",
1078]2622]
2623
2624[[package]]
2625name = "jrsonnet-pkg"
2626version = "0.5.0-pre98"
2627dependencies = [
2628 "camino",
2629 "directories",
2630 "gix",
2631 "peg",
2632 "reqwest",
2633 "serde",
2634 "serde_json",
2635 "thiserror",
2636 "tracing",
2637 "url",
2638 "zip",
2639]
10792640
1080[[package]]2641[[package]]
1081name = "jrsonnet-rowan-parser"2642name = "jrsonnet-rowan-parser"
1109 "serde",2670 "serde",
1110 "serde-saphyr",2671 "serde-saphyr",
1111 "serde_json",2672 "serde_json",
1112 "sha1",2673 "sha1 0.11.0",
1113 "sha2",2674 "sha2",
1114 "sha3",2675 "sha3",
1115]2676]
1158checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"2719checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
1159dependencies = [2720dependencies = [
1160 "cfg-if",2721 "cfg-if",
1161 "cpufeatures",2722 "cpufeatures 0.3.0",
1162]2723]
2724
2725[[package]]
2726name = "kstring"
2727version = "2.0.2"
2728source = "registry+https://github.com/rust-lang/crates.io-index"
2729checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1"
2730dependencies = [
2731 "static_assertions",
2732]
2733
2734[[package]]
2735name = "lazy_static"
2736version = "1.5.0"
2737source = "registry+https://github.com/rust-lang/crates.io-index"
2738checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
11632739
1164[[package]]2740[[package]]
1165name = "leb128fmt"2741name = "leb128fmt"
1184 "jrsonnet-stdlib",2760 "jrsonnet-stdlib",
1185]2761]
2762
2763[[package]]
2764name = "libredox"
2765version = "0.1.16"
2766source = "registry+https://github.com/rust-lang/crates.io-index"
2767checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
2768dependencies = [
2769 "bitflags",
2770 "libc",
2771 "plain",
2772 "redox_syscall 0.7.5",
2773]
11862774
1187[[package]]2775[[package]]
1188name = "linux-raw-sys"2776name = "linux-raw-sys"
1196source = "registry+https://github.com/rust-lang/crates.io-index"2784source = "registry+https://github.com/rust-lang/crates.io-index"
1197checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"2785checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
2786
2787[[package]]
2788name = "lock_api"
2789version = "0.4.14"
2790source = "registry+https://github.com/rust-lang/crates.io-index"
2791checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
2792dependencies = [
2793 "scopeguard",
2794]
11982795
1199[[package]]2796[[package]]
1200name = "log"2797name = "log"
1243 "hashbrown 0.17.0",2840 "hashbrown 0.17.0",
1244]2841]
2842
2843[[package]]
2844name = "lru-slab"
2845version = "0.1.2"
2846source = "registry+https://github.com/rust-lang/crates.io-index"
2847checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
2848
2849[[package]]
2850name = "matchers"
2851version = "0.2.0"
2852source = "registry+https://github.com/rust-lang/crates.io-index"
2853checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
2854dependencies = [
2855 "regex-automata",
2856]
2857
2858[[package]]
2859name = "maybe-async"
2860version = "0.2.10"
2861source = "registry+https://github.com/rust-lang/crates.io-index"
2862checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
2863dependencies = [
2864 "proc-macro2",
2865 "quote",
2866 "syn",
2867]
12452868
1246[[package]]2869[[package]]
1247name = "md5"2870name = "md5"
1255source = "registry+https://github.com/rust-lang/crates.io-index"2878source = "registry+https://github.com/rust-lang/crates.io-index"
1256checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"2879checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
2880
2881[[package]]
2882name = "memmap2"
2883version = "0.9.10"
2884source = "registry+https://github.com/rust-lang/crates.io-index"
2885checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
2886dependencies = [
2887 "libc",
2888]
12572889
1258[[package]]2890[[package]]
1259name = "mimalloc-sys"2891name = "mimalloc-sys"
1274 "mimalloc-sys",2906 "mimalloc-sys",
1275]2907]
2908
2909[[package]]
2910name = "mime"
2911version = "0.3.17"
2912source = "registry+https://github.com/rust-lang/crates.io-index"
2913checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
2914
2915[[package]]
2916name = "miniz_oxide"
2917version = "0.8.9"
2918source = "registry+https://github.com/rust-lang/crates.io-index"
2919checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
2920dependencies = [
2921 "adler2",
2922 "simd-adler32",
2923]
2924
2925[[package]]
2926name = "mio"
2927version = "1.2.0"
2928source = "registry+https://github.com/rust-lang/crates.io-index"
2929checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
2930dependencies = [
2931 "libc",
2932 "wasi",
2933 "windows-sys 0.61.2",
2934]
12762935
1277[[package]]2936[[package]]
1278name = "nix"2937name = "nix"
1286 "libc",2945 "libc",
1287]2946]
2947
2948[[package]]
2949name = "nonempty"
2950version = "0.12.0"
2951source = "registry+https://github.com/rust-lang/crates.io-index"
2952checksum = "9737e026353e5cd0736f98eddae28665118eb6f6600902a7f50db585621fecb6"
2953
2954[[package]]
2955name = "nu-ansi-term"
2956version = "0.50.3"
2957source = "registry+https://github.com/rust-lang/crates.io-index"
2958checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
2959dependencies = [
2960 "windows-sys 0.61.2",
2961]
12882962
1289[[package]]2963[[package]]
1290name = "num-bigint"2964name = "num-bigint"
1342source = "registry+https://github.com/rust-lang/crates.io-index"3016source = "registry+https://github.com/rust-lang/crates.io-index"
1343checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"3017checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
3018
3019[[package]]
3020name = "openssl-probe"
3021version = "0.2.1"
3022source = "registry+https://github.com/rust-lang/crates.io-index"
3023checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
3024
3025[[package]]
3026name = "option-ext"
3027version = "0.2.0"
3028source = "registry+https://github.com/rust-lang/crates.io-index"
3029checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
13443030
1345[[package]]3031[[package]]
1346name = "ouroboros"3032name = "ouroboros"
1376 "winapi",3062 "winapi",
1377]3063]
3064
3065[[package]]
3066name = "parking_lot"
3067version = "0.12.5"
3068source = "registry+https://github.com/rust-lang/crates.io-index"
3069checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
3070dependencies = [
3071 "lock_api",
3072 "parking_lot_core",
3073]
3074
3075[[package]]
3076name = "parking_lot_core"
3077version = "0.9.12"
3078source = "registry+https://github.com/rust-lang/crates.io-index"
3079checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
3080dependencies = [
3081 "cfg-if",
3082 "libc",
3083 "redox_syscall 0.5.18",
3084 "smallvec",
3085 "windows-link",
3086]
13783087
1379[[package]]3088[[package]]
1380name = "pathdiff"3089name = "pathdiff"
1415source = "registry+https://github.com/rust-lang/crates.io-index"3124source = "registry+https://github.com/rust-lang/crates.io-index"
1416checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"3125checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
3126
3127[[package]]
3128name = "pin-project-lite"
3129version = "0.2.17"
3130source = "registry+https://github.com/rust-lang/crates.io-index"
3131checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
3132
3133[[package]]
3134name = "plain"
3135version = "0.2.3"
3136source = "registry+https://github.com/rust-lang/crates.io-index"
3137checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
14173138
1418[[package]]3139[[package]]
1419name = "plotters"3140name = "plotters"
1443 "plotters-backend",3164 "plotters-backend",
1444]3165]
3166
3167[[package]]
3168name = "portable-atomic"
3169version = "1.13.1"
3170source = "registry+https://github.com/rust-lang/crates.io-index"
3171checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
3172
3173[[package]]
3174name = "portable-atomic-util"
3175version = "0.2.7"
3176source = "registry+https://github.com/rust-lang/crates.io-index"
3177checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
3178dependencies = [
3179 "portable-atomic",
3180]
14453181
1446[[package]]3182[[package]]
1447name = "potential_utf"3183name = "potential_utf"
1493 "yansi",3229 "yansi",
1494]3230]
3231
3232[[package]]
3233name = "prodash"
3234version = "31.0.0"
3235source = "registry+https://github.com/rust-lang/crates.io-index"
3236checksum = "962200e2d7d551451297d9fdce85138374019ada198e30ea9ede38034e27604c"
3237dependencies = [
3238 "bytesize",
3239 "human_format",
3240 "parking_lot",
3241]
14953242
1496[[package]]3243[[package]]
1497name = "psm"3244name = "psm"
1503 "cc",3250 "cc",
1504]3251]
3252
3253[[package]]
3254name = "quinn"
3255version = "0.11.9"
3256source = "registry+https://github.com/rust-lang/crates.io-index"
3257checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
3258dependencies = [
3259 "bytes",
3260 "cfg_aliases",
3261 "pin-project-lite",
3262 "quinn-proto",
3263 "quinn-udp",
3264 "rustc-hash 2.1.2",
3265 "rustls",
3266 "socket2",
3267 "thiserror",
3268 "tokio",
3269 "tracing",
3270 "web-time",
3271]
3272
3273[[package]]
3274name = "quinn-proto"
3275version = "0.11.14"
3276source = "registry+https://github.com/rust-lang/crates.io-index"
3277checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
3278dependencies = [
3279 "aws-lc-rs",
3280 "bytes",
3281 "getrandom 0.3.4",
3282 "lru-slab",
3283 "rand",
3284 "ring",
3285 "rustc-hash 2.1.2",
3286 "rustls",
3287 "rustls-pki-types",
3288 "slab",
3289 "thiserror",
3290 "tinyvec",
3291 "tracing",
3292 "web-time",
3293]
3294
3295[[package]]
3296name = "quinn-udp"
3297version = "0.5.14"
3298source = "registry+https://github.com/rust-lang/crates.io-index"
3299checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
3300dependencies = [
3301 "cfg_aliases",
3302 "libc",
3303 "once_cell",
3304 "socket2",
3305 "tracing",
3306 "windows-sys 0.60.2",
3307]
15053308
1506[[package]]3309[[package]]
1507name = "quote"3310name = "quote"
1591 "crossbeam-utils",3394 "crossbeam-utils",
1592]3395]
3396
3397[[package]]
3398name = "redox_syscall"
3399version = "0.5.18"
3400source = "registry+https://github.com/rust-lang/crates.io-index"
3401checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
3402dependencies = [
3403 "bitflags",
3404]
3405
3406[[package]]
3407name = "redox_syscall"
3408version = "0.7.5"
3409source = "registry+https://github.com/rust-lang/crates.io-index"
3410checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
3411dependencies = [
3412 "bitflags",
3413]
3414
3415[[package]]
3416name = "redox_users"
3417version = "0.5.2"
3418source = "registry+https://github.com/rust-lang/crates.io-index"
3419checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
3420dependencies = [
3421 "getrandom 0.2.17",
3422 "libredox",
3423 "thiserror",
3424]
15933425
1594[[package]]3426[[package]]
1595name = "regex"3427name = "regex"
1620source = "registry+https://github.com/rust-lang/crates.io-index"3452source = "registry+https://github.com/rust-lang/crates.io-index"
1621checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"3453checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
3454
3455[[package]]
3456name = "reqwest"
3457version = "0.13.3"
3458source = "registry+https://github.com/rust-lang/crates.io-index"
3459checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
3460dependencies = [
3461 "base64",
3462 "bytes",
3463 "encoding_rs",
3464 "futures-channel",
3465 "futures-core",
3466 "futures-util",
3467 "h2",
3468 "http",
3469 "http-body",
3470 "http-body-util",
3471 "hyper",
3472 "hyper-rustls",
3473 "hyper-util",
3474 "js-sys",
3475 "log",
3476 "mime",
3477 "percent-encoding",
3478 "pin-project-lite",
3479 "quinn",
3480 "rustls",
3481 "rustls-pki-types",
3482 "rustls-platform-verifier",
3483 "sync_wrapper",
3484 "tokio",
3485 "tokio-rustls",
3486 "tower",
3487 "tower-http",
3488 "tower-service",
3489 "url",
3490 "wasm-bindgen",
3491 "wasm-bindgen-futures",
3492 "web-sys",
3493]
3494
3495[[package]]
3496name = "ring"
3497version = "0.17.14"
3498source = "registry+https://github.com/rust-lang/crates.io-index"
3499checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
3500dependencies = [
3501 "cc",
3502 "cfg-if",
3503 "getrandom 0.2.17",
3504 "libc",
3505 "untrusted",
3506 "windows-sys 0.52.0",
3507]
16223508
1623[[package]]3509[[package]]
1624name = "rowan"3510name = "rowan"
1644source = "registry+https://github.com/rust-lang/crates.io-index"3530source = "registry+https://github.com/rust-lang/crates.io-index"
1645checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"3531checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
3532
3533[[package]]
3534name = "rustc_version"
3535version = "0.4.1"
3536source = "registry+https://github.com/rust-lang/crates.io-index"
3537checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
3538dependencies = [
3539 "semver",
3540]
16463541
1647[[package]]3542[[package]]
1648name = "rustix"3543name = "rustix"
1654 "errno",3549 "errno",
1655 "libc",3550 "libc",
1656 "linux-raw-sys",3551 "linux-raw-sys",
1657 "windows-sys",3552 "windows-sys 0.61.2",
1658]3553]
3554
3555[[package]]
3556name = "rustls"
3557version = "0.23.40"
3558source = "registry+https://github.com/rust-lang/crates.io-index"
3559checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
3560dependencies = [
3561 "aws-lc-rs",
3562 "once_cell",
3563 "rustls-pki-types",
3564 "rustls-webpki",
3565 "subtle",
3566 "zeroize",
3567]
3568
3569[[package]]
3570name = "rustls-native-certs"
3571version = "0.8.3"
3572source = "registry+https://github.com/rust-lang/crates.io-index"
3573checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
3574dependencies = [
3575 "openssl-probe",
3576 "rustls-pki-types",
3577 "schannel",
3578 "security-framework",
3579]
3580
3581[[package]]
3582name = "rustls-pki-types"
3583version = "1.14.1"
3584source = "registry+https://github.com/rust-lang/crates.io-index"
3585checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
3586dependencies = [
3587 "web-time",
3588 "zeroize",
3589]
3590
3591[[package]]
3592name = "rustls-platform-verifier"
3593version = "0.7.0"
3594source = "registry+https://github.com/rust-lang/crates.io-index"
3595checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
3596dependencies = [
3597 "core-foundation",
3598 "core-foundation-sys",
3599 "jni",
3600 "log",
3601 "once_cell",
3602 "rustls",
3603 "rustls-native-certs",
3604 "rustls-platform-verifier-android",
3605 "rustls-webpki",
3606 "security-framework",
3607 "security-framework-sys",
3608 "webpki-root-certs",
3609 "windows-sys 0.61.2",
3610]
3611
3612[[package]]
3613name = "rustls-platform-verifier-android"
3614version = "0.1.1"
3615source = "registry+https://github.com/rust-lang/crates.io-index"
3616checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
3617
3618[[package]]
3619name = "rustls-webpki"
3620version = "0.103.13"
3621source = "registry+https://github.com/rust-lang/crates.io-index"
3622checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
3623dependencies = [
3624 "aws-lc-rs",
3625 "ring",
3626 "rustls-pki-types",
3627 "untrusted",
3628]
16593629
1660[[package]]3630[[package]]
1661name = "rustversion"3631name = "rustversion"
1672 "winapi-util",3642 "winapi-util",
1673]3643]
3644
3645[[package]]
3646name = "schannel"
3647version = "0.1.29"
3648source = "registry+https://github.com/rust-lang/crates.io-index"
3649checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
3650dependencies = [
3651 "windows-sys 0.61.2",
3652]
3653
3654[[package]]
3655name = "scopeguard"
3656version = "1.2.0"
3657source = "registry+https://github.com/rust-lang/crates.io-index"
3658checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
3659
3660[[package]]
3661name = "security-framework"
3662version = "3.7.0"
3663source = "registry+https://github.com/rust-lang/crates.io-index"
3664checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
3665dependencies = [
3666 "bitflags",
3667 "core-foundation",
3668 "core-foundation-sys",
3669 "libc",
3670 "security-framework-sys",
3671]
3672
3673[[package]]
3674name = "security-framework-sys"
3675version = "2.17.0"
3676source = "registry+https://github.com/rust-lang/crates.io-index"
3677checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
3678dependencies = [
3679 "core-foundation-sys",
3680 "libc",
3681]
16743682
1675[[package]]3683[[package]]
1676name = "semver"3684name = "semver"
1738 "zmij",3746 "zmij",
1739]3747]
3748
3749[[package]]
3750name = "sha1"
3751version = "0.10.6"
3752source = "registry+https://github.com/rust-lang/crates.io-index"
3753checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
3754dependencies = [
3755 "cfg-if",
3756 "cpufeatures 0.2.17",
3757 "digest 0.10.7",
3758]
17403759
1741[[package]]3760[[package]]
1742name = "sha1"3761name = "sha1"
1745checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"3764checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
1746dependencies = [3765dependencies = [
1747 "cfg-if",3766 "cfg-if",
1748 "cpufeatures",3767 "cpufeatures 0.3.0",
1749 "digest",3768 "digest 0.11.3",
1750]3769]
3770
3771[[package]]
3772name = "sha1-checked"
3773version = "0.10.0"
3774source = "registry+https://github.com/rust-lang/crates.io-index"
3775checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423"
3776dependencies = [
3777 "digest 0.10.7",
3778 "sha1 0.10.6",
3779]
17513780
1752[[package]]3781[[package]]
1753name = "sha2"3782name = "sha2"
1756checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"3785checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
1757dependencies = [3786dependencies = [
1758 "cfg-if",3787 "cfg-if",
1759 "cpufeatures",3788 "cpufeatures 0.3.0",
1760 "digest",3789 "digest 0.11.3",
1761]3790]
17623791
1763[[package]]3792[[package]]
1766source = "registry+https://github.com/rust-lang/crates.io-index"3795source = "registry+https://github.com/rust-lang/crates.io-index"
1767checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"3796checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
1768dependencies = [3797dependencies = [
1769 "digest",3798 "digest 0.11.3",
1770 "keccak",3799 "keccak",
1771]3800]
3801
3802[[package]]
3803name = "sharded-slab"
3804version = "0.1.7"
3805source = "registry+https://github.com/rust-lang/crates.io-index"
3806checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
3807dependencies = [
3808 "lazy_static",
3809]
3810
3811[[package]]
3812name = "shell-words"
3813version = "1.1.1"
3814source = "registry+https://github.com/rust-lang/crates.io-index"
3815checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
17723816
1773[[package]]3817[[package]]
1774name = "shlex"3818name = "shlex"
1775version = "1.3.0"3819version = "1.3.0"
1776source = "registry+https://github.com/rust-lang/crates.io-index"3820source = "registry+https://github.com/rust-lang/crates.io-index"
1777checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"3821checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
3822
3823[[package]]
3824name = "signal-hook"
3825version = "0.4.4"
3826source = "registry+https://github.com/rust-lang/crates.io-index"
3827checksum = "b2a0c28ca5908dbdbcd52e6fdaa00358ab88637f8ab33e1f188dd510eb44b53d"
3828dependencies = [
3829 "libc",
3830 "signal-hook-registry",
3831]
3832
3833[[package]]
3834name = "signal-hook-registry"
3835version = "1.4.8"
3836source = "registry+https://github.com/rust-lang/crates.io-index"
3837checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
3838dependencies = [
3839 "errno",
3840 "libc",
3841]
3842
3843[[package]]
3844name = "simd-adler32"
3845version = "0.3.9"
3846source = "registry+https://github.com/rust-lang/crates.io-index"
3847checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
3848
3849[[package]]
3850name = "simd_cesu8"
3851version = "1.1.1"
3852source = "registry+https://github.com/rust-lang/crates.io-index"
3853checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
3854dependencies = [
3855 "rustc_version",
3856 "simdutf8",
3857]
3858
3859[[package]]
3860name = "simdutf8"
3861version = "0.1.5"
3862source = "registry+https://github.com/rust-lang/crates.io-index"
3863checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
17783864
1779[[package]]3865[[package]]
1780name = "similar"3866name = "similar"
1781version = "2.7.0"3867version = "2.7.0"
1782source = "registry+https://github.com/rust-lang/crates.io-index"3868source = "registry+https://github.com/rust-lang/crates.io-index"
1783checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"3869checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
3870
3871[[package]]
3872name = "slab"
3873version = "0.4.12"
3874source = "registry+https://github.com/rust-lang/crates.io-index"
3875checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
17843876
1785[[package]]3877[[package]]
1786name = "smallvec"3878name = "smallvec"
1787version = "1.15.1"3879version = "1.15.1"
1788source = "registry+https://github.com/rust-lang/crates.io-index"3880source = "registry+https://github.com/rust-lang/crates.io-index"
1789checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"3881checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
3882
3883[[package]]
3884name = "socket2"
3885version = "0.6.3"
3886source = "registry+https://github.com/rust-lang/crates.io-index"
3887checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
3888dependencies = [
3889 "libc",
3890 "windows-sys 0.61.2",
3891]
17903892
1791[[package]]3893[[package]]
1792name = "stable_deref_trait"3894name = "stable_deref_trait"
1804 "cfg-if",3906 "cfg-if",
1805 "libc",3907 "libc",
1806 "psm",3908 "psm",
1807 "windows-sys",3909 "windows-sys 0.61.2",
1808]3910]
18093911
1810[[package]]3912[[package]]
1834source = "registry+https://github.com/rust-lang/crates.io-index"3936source = "registry+https://github.com/rust-lang/crates.io-index"
1835checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"3937checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
3938
3939[[package]]
3940name = "subtle"
3941version = "2.6.1"
3942source = "registry+https://github.com/rust-lang/crates.io-index"
3943checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
18363944
1837[[package]]3945[[package]]
1838name = "syn"3946name = "syn"
1856 "syn",3964 "syn",
1857]3965]
3966
3967[[package]]
3968name = "sync_wrapper"
3969version = "1.0.2"
3970source = "registry+https://github.com/rust-lang/crates.io-index"
3971checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
3972dependencies = [
3973 "futures-core",
3974]
18583975
1859[[package]]3976[[package]]
1860name = "synstructure"3977name = "synstructure"
1877 "getrandom 0.4.2",3994 "getrandom 0.4.2",
1878 "once_cell",3995 "once_cell",
1879 "rustix",3996 "rustix",
1880 "windows-sys",3997 "windows-sys 0.61.2",
1881]3998]
18823999
1883[[package]]4000[[package]]
1920 "syn",4037 "syn",
1921]4038]
4039
4040[[package]]
4041name = "thread_local"
4042version = "1.1.9"
4043source = "registry+https://github.com/rust-lang/crates.io-index"
4044checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
4045dependencies = [
4046 "cfg-if",
4047]
19224048
1923[[package]]4049[[package]]
1924name = "tinystr"4050name = "tinystr"
1940 "serde_json",4066 "serde_json",
1941]4067]
4068
4069[[package]]
4070name = "tinyvec"
4071version = "1.11.0"
4072source = "registry+https://github.com/rust-lang/crates.io-index"
4073checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
4074dependencies = [
4075 "tinyvec_macros",
4076]
4077
4078[[package]]
4079name = "tinyvec_macros"
4080version = "0.1.1"
4081source = "registry+https://github.com/rust-lang/crates.io-index"
4082checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
4083
4084[[package]]
4085name = "tokio"
4086version = "1.52.2"
4087source = "registry+https://github.com/rust-lang/crates.io-index"
4088checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
4089dependencies = [
4090 "bytes",
4091 "libc",
4092 "mio",
4093 "pin-project-lite",
4094 "socket2",
4095 "windows-sys 0.61.2",
4096]
4097
4098[[package]]
4099name = "tokio-rustls"
4100version = "0.26.4"
4101source = "registry+https://github.com/rust-lang/crates.io-index"
4102checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
4103dependencies = [
4104 "rustls",
4105 "tokio",
4106]
4107
4108[[package]]
4109name = "tokio-util"
4110version = "0.7.18"
4111source = "registry+https://github.com/rust-lang/crates.io-index"
4112checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
4113dependencies = [
4114 "bytes",
4115 "futures-core",
4116 "futures-sink",
4117 "pin-project-lite",
4118 "tokio",
4119]
4120
4121[[package]]
4122name = "tower"
4123version = "0.5.3"
4124source = "registry+https://github.com/rust-lang/crates.io-index"
4125checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
4126dependencies = [
4127 "futures-core",
4128 "futures-util",
4129 "pin-project-lite",
4130 "sync_wrapper",
4131 "tokio",
4132 "tower-layer",
4133 "tower-service",
4134]
4135
4136[[package]]
4137name = "tower-http"
4138version = "0.6.9"
4139source = "registry+https://github.com/rust-lang/crates.io-index"
4140checksum = "a28f0d049ccfaa566e14e9663d304d8577427b368cb4710a20528690287a738b"
4141dependencies = [
4142 "bitflags",
4143 "bytes",
4144 "futures-util",
4145 "http",
4146 "http-body",
4147 "pin-project-lite",
4148 "tower",
4149 "tower-layer",
4150 "tower-service",
4151 "url",
4152]
4153
4154[[package]]
4155name = "tower-layer"
4156version = "0.3.3"
4157source = "registry+https://github.com/rust-lang/crates.io-index"
4158checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
4159
4160[[package]]
4161name = "tower-service"
4162version = "0.3.3"
4163source = "registry+https://github.com/rust-lang/crates.io-index"
4164checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
4165
4166[[package]]
4167name = "tracing"
4168version = "0.1.44"
4169source = "registry+https://github.com/rust-lang/crates.io-index"
4170checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
4171dependencies = [
4172 "pin-project-lite",
4173 "tracing-attributes",
4174 "tracing-core",
4175]
4176
4177[[package]]
4178name = "tracing-attributes"
4179version = "0.1.31"
4180source = "registry+https://github.com/rust-lang/crates.io-index"
4181checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
4182dependencies = [
4183 "proc-macro2",
4184 "quote",
4185 "syn",
4186]
4187
4188[[package]]
4189name = "tracing-core"
4190version = "0.1.36"
4191source = "registry+https://github.com/rust-lang/crates.io-index"
4192checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
4193dependencies = [
4194 "once_cell",
4195 "valuable",
4196]
4197
4198[[package]]
4199name = "tracing-log"
4200version = "0.2.0"
4201source = "registry+https://github.com/rust-lang/crates.io-index"
4202checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
4203dependencies = [
4204 "log",
4205 "once_cell",
4206 "tracing-core",
4207]
4208
4209[[package]]
4210name = "tracing-subscriber"
4211version = "0.3.23"
4212source = "registry+https://github.com/rust-lang/crates.io-index"
4213checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
4214dependencies = [
4215 "matchers",
4216 "nu-ansi-term",
4217 "once_cell",
4218 "regex-automata",
4219 "sharded-slab",
4220 "smallvec",
4221 "thread_local",
4222 "tracing",
4223 "tracing-core",
4224 "tracing-log",
4225]
4226
4227[[package]]
4228name = "try-lock"
4229version = "0.2.5"
4230source = "registry+https://github.com/rust-lang/crates.io-index"
4231checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
4232
4233[[package]]
4234name = "typed-path"
4235version = "0.12.3"
4236source = "registry+https://github.com/rust-lang/crates.io-index"
4237checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
19424238
1943[[package]]4239[[package]]
1944name = "typenum"4240name = "typenum"
1945version = "1.20.0"4241version = "1.20.0"
1946source = "registry+https://github.com/rust-lang/crates.io-index"4242source = "registry+https://github.com/rust-lang/crates.io-index"
1947checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"4243checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
4244
4245[[package]]
4246name = "uluru"
4247version = "3.1.0"
4248source = "registry+https://github.com/rust-lang/crates.io-index"
4249checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da"
4250dependencies = [
4251 "arrayvec",
4252]
19484253
1949[[package]]4254[[package]]
1950name = "ungrammar"4255name = "ungrammar"
1951version = "1.16.1"4256version = "1.16.1"
1952source = "registry+https://github.com/rust-lang/crates.io-index"4257source = "registry+https://github.com/rust-lang/crates.io-index"
1953checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"4258checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f"
4259
4260[[package]]
4261name = "unicode-bom"
4262version = "2.0.3"
4263source = "registry+https://github.com/rust-lang/crates.io-index"
4264checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
19544265
1955[[package]]4266[[package]]
1956name = "unicode-box-drawing"4267name = "unicode-box-drawing"
1964source = "registry+https://github.com/rust-lang/crates.io-index"4275source = "registry+https://github.com/rust-lang/crates.io-index"
1965checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"4276checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
4277
4278[[package]]
4279name = "unicode-normalization"
4280version = "0.1.25"
4281source = "registry+https://github.com/rust-lang/crates.io-index"
4282checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
4283dependencies = [
4284 "tinyvec",
4285]
19664286
1967[[package]]4287[[package]]
1968name = "unicode-width"4288name = "unicode-width"
1976source = "registry+https://github.com/rust-lang/crates.io-index"4296source = "registry+https://github.com/rust-lang/crates.io-index"
1977checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"4297checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
4298
4299[[package]]
4300name = "untrusted"
4301version = "0.9.0"
4302source = "registry+https://github.com/rust-lang/crates.io-index"
4303checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
19784304
1979[[package]]4305[[package]]
1980name = "url"4306name = "url"
2000source = "registry+https://github.com/rust-lang/crates.io-index"4326source = "registry+https://github.com/rust-lang/crates.io-index"
2001checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"4327checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
4328
4329[[package]]
4330name = "valuable"
4331version = "0.1.1"
4332source = "registry+https://github.com/rust-lang/crates.io-index"
4333checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
20024334
2003[[package]]4335[[package]]
2004name = "version_check"4336name = "version_check"
2025 "winapi-util",4357 "winapi-util",
2026]4358]
4359
4360[[package]]
4361name = "want"
4362version = "0.3.1"
4363source = "registry+https://github.com/rust-lang/crates.io-index"
4364checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
4365dependencies = [
4366 "try-lock",
4367]
4368
4369[[package]]
4370name = "wasi"
4371version = "0.11.1+wasi-snapshot-preview1"
4372source = "registry+https://github.com/rust-lang/crates.io-index"
4373checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
20274374
2028[[package]]4375[[package]]
2029name = "wasip2"4376name = "wasip2"
2145 "wasm-bindgen",4492 "wasm-bindgen",
2146]4493]
4494
4495[[package]]
4496name = "web-time"
4497version = "1.1.0"
4498source = "registry+https://github.com/rust-lang/crates.io-index"
4499checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
4500dependencies = [
4501 "js-sys",
4502 "wasm-bindgen",
4503]
4504
4505[[package]]
4506name = "webpki-root-certs"
4507version = "1.0.7"
4508source = "registry+https://github.com/rust-lang/crates.io-index"
4509checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
4510dependencies = [
4511 "rustls-pki-types",
4512]
21474513
2148[[package]]4514[[package]]
2149name = "winapi"4515name = "winapi"
2167source = "registry+https://github.com/rust-lang/crates.io-index"4533source = "registry+https://github.com/rust-lang/crates.io-index"
2168checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"4534checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
2169dependencies = [4535dependencies = [
2170 "windows-sys",4536 "windows-sys 0.61.2",
2171]4537]
21724538
2173[[package]]4539[[package]]
2182source = "registry+https://github.com/rust-lang/crates.io-index"4548source = "registry+https://github.com/rust-lang/crates.io-index"
2183checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"4549checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
4550
4551[[package]]
4552name = "windows-sys"
4553version = "0.52.0"
4554source = "registry+https://github.com/rust-lang/crates.io-index"
4555checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
4556dependencies = [
4557 "windows-targets 0.52.6",
4558]
4559
4560[[package]]
4561name = "windows-sys"
4562version = "0.60.2"
4563source = "registry+https://github.com/rust-lang/crates.io-index"
4564checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
4565dependencies = [
4566 "windows-targets 0.53.5",
4567]
21844568
2185[[package]]4569[[package]]
2186name = "windows-sys"4570name = "windows-sys"
2191 "windows-link",4575 "windows-link",
2192]4576]
4577
4578[[package]]
4579name = "windows-targets"
4580version = "0.52.6"
4581source = "registry+https://github.com/rust-lang/crates.io-index"
4582checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
4583dependencies = [
4584 "windows_aarch64_gnullvm 0.52.6",
4585 "windows_aarch64_msvc 0.52.6",
4586 "windows_i686_gnu 0.52.6",
4587 "windows_i686_gnullvm 0.52.6",
4588 "windows_i686_msvc 0.52.6",
4589 "windows_x86_64_gnu 0.52.6",
4590 "windows_x86_64_gnullvm 0.52.6",
4591 "windows_x86_64_msvc 0.52.6",
4592]
4593
4594[[package]]
4595name = "windows-targets"
4596version = "0.53.5"
4597source = "registry+https://github.com/rust-lang/crates.io-index"
4598checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
4599dependencies = [
4600 "windows-link",
4601 "windows_aarch64_gnullvm 0.53.1",
4602 "windows_aarch64_msvc 0.53.1",
4603 "windows_i686_gnu 0.53.1",
4604 "windows_i686_gnullvm 0.53.1",
4605 "windows_i686_msvc 0.53.1",
4606 "windows_x86_64_gnu 0.53.1",
4607 "windows_x86_64_gnullvm 0.53.1",
4608 "windows_x86_64_msvc 0.53.1",
4609]
4610
4611[[package]]
4612name = "windows_aarch64_gnullvm"
4613version = "0.52.6"
4614source = "registry+https://github.com/rust-lang/crates.io-index"
4615checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
4616
4617[[package]]
4618name = "windows_aarch64_gnullvm"
4619version = "0.53.1"
4620source = "registry+https://github.com/rust-lang/crates.io-index"
4621checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
4622
4623[[package]]
4624name = "windows_aarch64_msvc"
4625version = "0.52.6"
4626source = "registry+https://github.com/rust-lang/crates.io-index"
4627checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
4628
4629[[package]]
4630name = "windows_aarch64_msvc"
4631version = "0.53.1"
4632source = "registry+https://github.com/rust-lang/crates.io-index"
4633checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
4634
4635[[package]]
4636name = "windows_i686_gnu"
4637version = "0.52.6"
4638source = "registry+https://github.com/rust-lang/crates.io-index"
4639checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
4640
4641[[package]]
4642name = "windows_i686_gnu"
4643version = "0.53.1"
4644source = "registry+https://github.com/rust-lang/crates.io-index"
4645checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
4646
4647[[package]]
4648name = "windows_i686_gnullvm"
4649version = "0.52.6"
4650source = "registry+https://github.com/rust-lang/crates.io-index"
4651checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
4652
4653[[package]]
4654name = "windows_i686_gnullvm"
4655version = "0.53.1"
4656source = "registry+https://github.com/rust-lang/crates.io-index"
4657checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
4658
4659[[package]]
4660name = "windows_i686_msvc"
4661version = "0.52.6"
4662source = "registry+https://github.com/rust-lang/crates.io-index"
4663checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
4664
4665[[package]]
4666name = "windows_i686_msvc"
4667version = "0.53.1"
4668source = "registry+https://github.com/rust-lang/crates.io-index"
4669checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
4670
4671[[package]]
4672name = "windows_x86_64_gnu"
4673version = "0.52.6"
4674source = "registry+https://github.com/rust-lang/crates.io-index"
4675checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
4676
4677[[package]]
4678name = "windows_x86_64_gnu"
4679version = "0.53.1"
4680source = "registry+https://github.com/rust-lang/crates.io-index"
4681checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
4682
4683[[package]]
4684name = "windows_x86_64_gnullvm"
4685version = "0.52.6"
4686source = "registry+https://github.com/rust-lang/crates.io-index"
4687checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
4688
4689[[package]]
4690name = "windows_x86_64_gnullvm"
4691version = "0.53.1"
4692source = "registry+https://github.com/rust-lang/crates.io-index"
4693checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
4694
4695[[package]]
4696name = "windows_x86_64_msvc"
4697version = "0.52.6"
4698source = "registry+https://github.com/rust-lang/crates.io-index"
4699checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
4700
4701[[package]]
4702name = "windows_x86_64_msvc"
4703version = "0.53.1"
4704source = "registry+https://github.com/rust-lang/crates.io-index"
4705checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
21934706
2194[[package]]4707[[package]]
2195name = "wit-bindgen"4708name = "wit-bindgen"
2391 "synstructure",4904 "synstructure",
2392]4905]
4906
4907[[package]]
4908name = "zeroize"
4909version = "1.8.2"
4910source = "registry+https://github.com/rust-lang/crates.io-index"
4911checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
23934912
2394[[package]]4913[[package]]
2395name = "zerotrie"4914name = "zerotrie"
2424 "syn",4943 "syn",
2425]4944]
4945
4946[[package]]
4947name = "zip"
4948version = "8.6.0"
4949source = "registry+https://github.com/rust-lang/crates.io-index"
4950checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
4951dependencies = [
4952 "crc32fast",
4953 "flate2",
4954 "indexmap",
4955 "memchr",
4956 "typed-path",
4957 "zopfli",
4958]
4959
4960[[package]]
4961name = "zlib-rs"
4962version = "0.6.3"
4963source = "registry+https://github.com/rust-lang/crates.io-index"
4964checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
24264965
2427[[package]]4966[[package]]
2428name = "zmij"4967name = "zmij"
2429version = "1.0.21"4968version = "1.0.21"
2430source = "registry+https://github.com/rust-lang/crates.io-index"4969source = "registry+https://github.com/rust-lang/crates.io-index"
2431checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"4970checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
4971
4972[[package]]
4973name = "zopfli"
4974version = "0.8.3"
4975source = "registry+https://github.com/rust-lang/crates.io-index"
4976checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
4977dependencies = [
4978 "bumpalo",
4979 "crc32fast",
4980 "log",
4981 "simd-adler32",
4982]
24324983
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@
 jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre98" }
 jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre98" }
 jrsonnet-lexer = { path = "./crates/jrsonnet-lexer", version = "0.5.0-pre98" }
+jrsonnet-pkg = { path = "./crates/jrsonnet-pkg", version = "0.5.0-pre98" }
 jrsonnet-gcmodule = { version = "0.5.0" }
 # Diagnostics.
 # hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
@@ -116,6 +117,21 @@
 console_error_panic_hook = "0.1"
 getrandom = "0.3.4"
 
+# Bundler
+tracing = "0.1.44"
+tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
+reqwest = { version = "0.13", features = [
+  "blocking",
+  "rustls",
+], default-features = false }
+zip = { version = "8", default-features = false, features = ["deflate"] }
+directories = "6.0.0"
+gix = { version = "0.83.0", features = [
+  "blocking-network-client",
+  "blocking-http-transport-reqwest-rust-tls",
+] }
+camino = { version = "1.2.2", features = ["serde1"] }
+
 [workspace.lints.rust]
 unsafe_op_in_unsafe_fn = "deny"
 
addedcmds/jrb/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/cmds/jrb/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "jrb"
+description = "jsonnet package manager"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+jrsonnet-pkg.workspace = true
+
+clap = { workspace = true, features = ["derive"] }
+serde = { workspace = true }
+serde_json.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
addedcmds/jrb/src/main.rsdiffbeforeafterboth
--- /dev/null
+++ b/cmds/jrb/src/main.rs
@@ -0,0 +1,222 @@
+use std::{
+	path::{Path, PathBuf},
+	process::exit,
+};
+
+use clap::{Parser, Subcommand};
+use jrsonnet_pkg::{
+	install,
+	jsonnet_bundler::{GitSource, JsonnetFile},
+};
+use tracing::{error, info, warn};
+
+#[derive(Parser)]
+#[clap(about = "A jsonnet package manager")]
+struct Opts {
+	/// The directory used to cache packages in.
+	#[clap(long, default_value = "vendor")]
+	jsonnetpkg_home: PathBuf,
+	#[clap(subcommand)]
+	command: Command,
+}
+
+#[derive(Subcommand)]
+enum Command {
+	/// Initialize a new empty jsonnetfile
+	Init,
+	/// Install new dependencies. Existing ones are silently skipped
+	Install {
+		/// Package URIs to install
+		uris: Vec<String>,
+		/// Show what would be done without making changes
+		#[clap(long)]
+		dry_run: bool,
+	},
+	/// Update all or specific dependencies
+	Update {
+		/// Package URIs to update (all if empty)
+		uris: Vec<String>,
+		/// Show what would be done without making changes
+		#[clap(long)]
+		dry_run: bool,
+	},
+	/// Remove dependencies by name
+	Remove {
+		/// Dependency names (matched against both canonical and legacy names)
+		names: Vec<String>,
+		/// Show what would be removed without making changes
+		#[clap(long)]
+		dry_run: bool,
+	},
+}
+
+const MANIFEST: &str = "jsonnetfile.json";
+const LOCKFILE: &str = "jsonnetfile.lock.json";
+
+fn load_manifest() -> JsonnetFile {
+	let path = Path::new(MANIFEST);
+	if path.exists() {
+		JsonnetFile::load(path).unwrap_or_else(|e| {
+			error!("failed to load {MANIFEST}: {e}");
+			exit(1);
+		})
+	} else {
+		JsonnetFile {
+			version: 1,
+			dependencies: Vec::new(),
+			legacy_imports: true,
+		}
+	}
+}
+
+fn save_json(path: &Path, value: &impl serde::Serialize) {
+	let json = serde_json::to_string_pretty(value).expect("serialization failed");
+	std::fs::write(path, format!("{json}\n")).unwrap_or_else(|e| {
+		error!("failed to write {}: {e}", path.display());
+		exit(1);
+	});
+}
+
+fn load_lockfile() -> Option<JsonnetFile> {
+	let path = Path::new(LOCKFILE);
+	if path.exists() {
+		Some(JsonnetFile::load(path).unwrap_or_else(|e| {
+			error!("failed to load {LOCKFILE}: {e}");
+			exit(1);
+		}))
+	} else {
+		None
+	}
+}
+
+fn do_install(
+	manifest: &JsonnetFile,
+	lock: Option<&JsonnetFile>,
+	vendor_dir: &Path,
+	dry_run: bool,
+) {
+	let new_lock = install::install(manifest, lock, vendor_dir, dry_run).unwrap_or_else(|e| {
+		error!("install failed: {e}");
+		exit(1);
+	});
+	if !dry_run {
+		save_json(Path::new(LOCKFILE), &new_lock);
+	}
+}
+
+fn main() {
+	tracing_subscriber::fmt().init();
+
+	let opts = Opts::parse();
+
+	match opts.command {
+		Command::Init => {
+			let path = Path::new(MANIFEST);
+			if path.exists() {
+				warn!("{MANIFEST} already exists");
+				exit(1);
+			}
+			let jf = JsonnetFile {
+				version: 1,
+				dependencies: Vec::new(),
+				legacy_imports: true,
+			};
+			save_json(path, &jf);
+		}
+		Command::Install { uris, dry_run } => {
+			let mut manifest = load_manifest();
+
+			for uri in &uris {
+				let dep = GitSource::parse(uri).unwrap_or_else(|| {
+					eprintln!("failed to parse URI: {uri}");
+					exit(1);
+				});
+				let is_new = !manifest.dependencies.iter().any(|d| {
+					std::mem::discriminant(&d.source) == std::mem::discriminant(&dep.source)
+						&& d.canonical_name() == dep.canonical_name()
+				});
+				if is_new {
+					manifest.dependencies.push(dep);
+				}
+			}
+
+			if !uris.is_empty() {
+				save_json(Path::new(MANIFEST), &manifest);
+			}
+
+			let lock = load_lockfile();
+			do_install(&manifest, lock.as_ref(), &opts.jsonnetpkg_home, dry_run);
+		}
+		Command::Update { uris, dry_run } => {
+			let mut manifest = load_manifest();
+
+			if !uris.is_empty() {
+				for uri in &uris {
+					let dep = GitSource::parse(uri).unwrap_or_else(|| {
+						eprintln!("failed to parse URI: {uri}");
+						exit(1);
+					});
+					if let Some(existing) = manifest
+						.dependencies
+						.iter_mut()
+						.find(|d| d.canonical_name() == dep.canonical_name())
+					{
+						*existing = dep;
+					} else {
+						manifest.dependencies.push(dep);
+					}
+				}
+				save_json(Path::new(MANIFEST), &manifest);
+			}
+
+			do_install(&manifest, None, &opts.jsonnetpkg_home, dry_run);
+		}
+		Command::Remove { names, dry_run } => {
+			let mut manifest = load_manifest();
+
+			let matched: Vec<_> = manifest
+				.dependencies
+				.iter()
+				.filter(|dep| {
+					names.iter().any(|name| {
+						dep.canonical_name() == *name || dep.legacy_link_name() == *name
+					})
+				})
+				.cloned()
+				.collect::<Vec<_>>();
+
+			if matched.is_empty() {
+				eprintln!("no matching dependencies found");
+				exit(1);
+			}
+
+			for dep in &matched {
+				let canonical = dep.canonical_name();
+				let dir = opts.jsonnetpkg_home.join(&canonical);
+				let legacy = dep.legacy_link_name();
+				let link = opts.jsonnetpkg_home.join(&legacy);
+				if dry_run {
+					info!("would remove: {canonical} ({})", dir.display());
+				} else {
+					info!("removing: {canonical}");
+					if dir.exists() {
+						let _ = std::fs::remove_dir_all(&dir);
+					}
+					if link.symlink_metadata().is_ok() {
+						let _ = std::fs::remove_file(&link);
+					}
+				}
+			}
+
+			if !dry_run {
+				manifest.dependencies.retain(|dep| {
+					!names.iter().any(|name| {
+						dep.canonical_name() == *name || dep.legacy_link_name() == *name
+					})
+				});
+				save_json(Path::new(MANIFEST), &manifest);
+				save_json(Path::new(LOCKFILE), &manifest);
+			}
+		}
+	}
+}
addedcrates/jrsonnet-pkg/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "jrsonnet-pkg"
+description = "jsonnet-bundler jsonnetfile parser and installer"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+serde = { workspace = true, features = ["derive"] }
+serde_json.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+
+# Source url parser
+peg.workspace = true
+
+# Gix for git repos, reqwest + zip for github
+gix.workspace = true
+reqwest.workspace = true
+zip.workspace = true
+url.workspace = true
+camino.workspace = true
+
+# Global cache dir
+directories.workspace = true
addedcrates/jrsonnet-pkg/src/install/accessor.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/accessor.rs
@@ -0,0 +1,132 @@
+use std::{
+	fs::File,
+	io::{self, Read},
+	result,
+	str::FromStr as _,
+	sync::Mutex,
+};
+
+use tracing::warn;
+use zip::{ZipArchive, result::ZipError};
+
+use crate::jsonnet_bundler::{SubDir, SubDirEscapeError};
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+	#[error(transparent)]
+	Zip(#[from] ZipError),
+	#[error("invalid prefixed archive")]
+	ZipInvalidPrefix,
+	#[error("zip io: {0}")]
+	ZipIo(io::Error),
+	#[error("subdir not found: {0}")]
+	SubDirNotFound(SubDir),
+	#[error(transparent)]
+	SubdirEscape(#[from] SubDirEscapeError),
+}
+type Result<T, E = Error> = result::Result<T, E>;
+
+pub trait SourceAccessor {}
+
+pub struct ZipFileAccessor {
+	archive: Mutex<ZipArchive<File>>,
+	// Github archives have top-level directory with repo name
+	prefix: SubDir,
+}
+
+impl ZipFileAccessor {
+	pub fn new_prefixed(file: File) -> Result<Self> {
+		let archive = ZipArchive::new(file)?;
+		let prefix = archive.name_for_index(0).ok_or(Error::ZipInvalidPrefix)?;
+
+		Ok(Self {
+			prefix: SubDir::from_str(prefix)?,
+			archive: Mutex::new(archive),
+		})
+	}
+	/// Read a file from inside the archive's logical root (after stripping the
+	/// github-style `<repo>-<sha>/` prefix).
+	#[allow(clippy::significant_drop_tightening, reason = "false-positive")]
+	pub fn read(&self, name: &SubDir) -> Result<Option<Vec<u8>>> {
+		let prefixed = self
+			.prefix
+			.join(name)
+			.expect("prefix and name are both subdirs");
+		let mut archive = self.archive.lock().expect("not poisoned");
+		let mut v = match archive.by_name(prefixed.as_str()) {
+			Ok(v) => v,
+			Err(ZipError::FileNotFound) => return Ok(None),
+			Err(e) => return Err(e.into()),
+		};
+		if !v.is_file() {
+			return Ok(None);
+		}
+		let mut out = Vec::new();
+		v.read_to_end(&mut out).map_err(Error::ZipIo)?;
+		Ok(Some(out))
+	}
+	#[allow(clippy::significant_drop_tightening, reason = "false-positive")]
+	pub fn iter<E>(
+		&self,
+		subdir: &SubDir,
+		cb: &mut dyn FnMut(SubDir, AccessorEntry) -> Result<(), E>,
+	) -> Result<(), E>
+	where
+		E: From<Error>,
+	{
+		let mut archive = self.archive.lock().expect("not poisoned");
+		let len = archive.len();
+
+		let mut found = false;
+		for i in 0..len {
+			let mut entry = archive.by_index(i).map_err(Error::from)?;
+			let raw = entry.name();
+			let Ok(full_name) = SubDir::from_str(raw) else {
+				warn!("invalid zip entry name: {raw}");
+				continue;
+			};
+			// Peel off the github-archive top-level `<repo>-<sha>/` prefix.
+			let Some(in_repo) = full_name.strip_prefix(&self.prefix) else {
+				continue;
+			};
+			let Some(name) = in_repo.strip_prefix(subdir) else {
+				continue;
+			};
+			found = true;
+			if name.is_empty() && entry.is_dir() {
+				continue;
+			}
+
+			cb(
+				name.clone(),
+				if entry.is_dir() {
+					AccessorEntry::Dir
+				} else if entry.is_file() {
+					let mut data = Vec::new();
+					entry.read_to_end(&mut data).map_err(Error::ZipIo)?;
+					AccessorEntry::File(data)
+				} else {
+					// TODO: Symlinks?
+					panic!("unknown accessor entry type: {name:?}")
+				},
+			)?;
+		}
+
+		if !found {
+			return Err(Error::SubDirNotFound(subdir.clone()).into());
+		}
+
+		Ok(())
+	}
+	pub fn len(&self) -> usize {
+		self.archive.lock().expect("not poisoned").len()
+	}
+	pub fn is_empty(&self) -> bool {
+		self.len() == 0
+	}
+}
+
+pub enum AccessorEntry {
+	Dir,
+	File(Vec<u8>),
+}
addedcrates/jrsonnet-pkg/src/install/git.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/git.rs
@@ -0,0 +1,212 @@
+#![allow(clippy::result_large_err)]
+
+use std::{collections::HashSet, fs, path::Path};
+
+use gix::{
+	bstr::{self, ByteSlice},
+	interrupt, progress,
+	remote::{self, ref_map},
+};
+use tracing::info;
+
+use super::{Error, LocalExtraction, ResolveResult, Result, VendorSource, cache_dir};
+use crate::jsonnet_bundler::{Dependency, GitSource, JsonnetFile, Source, SubDir};
+
+fn repo_cache_path(remote: &GitSource) -> Result<std::path::PathBuf> {
+	Ok(cache_dir("git")?.join(&remote.host).join(&remote.repo))
+}
+
+fn ensure_repo(remote: &GitSource) -> Result<gix::Repository> {
+	let cache_path = repo_cache_path(remote)?;
+
+	if cache_path.exists() {
+		if let Ok(repo) = gix::open(&cache_path) {
+			fetch_remote(&repo, &remote.remote())?;
+			return Ok(repo);
+		}
+		fs::remove_dir_all(&cache_path).map_err(|e| Error::Io(cache_path.clone(), e))?;
+	}
+
+	fs::create_dir_all(cache_path.parent().expect("has parent"))
+		.map_err(|e| Error::Io(cache_path.clone(), e))?;
+
+	let mut clone = gix::prepare_clone_bare(remote.remote(), &cache_path)?;
+	let (repo, _) = clone.fetch_only(progress::Discard, &interrupt::IS_INTERRUPTED)?;
+	fetch_remote(&repo, &remote.remote())?;
+
+	Ok(repo)
+}
+
+fn fetch_remote(repo: &gix::Repository, remote: &str) -> Result<(), Error> {
+	repo.remote_at(remote)?
+		.with_refspecs(["+refs/*:refs/*"], remote::Direction::Fetch)?
+		.connect(remote::Direction::Fetch)?
+		.prepare_fetch(progress::Discard, ref_map::Options::default())?
+		.receive(progress::Discard, &interrupt::IS_INTERRUPTED)?;
+	Ok(())
+}
+
+fn extract_tree(
+	repo: &gix::Repository,
+	tree: &gix::Tree<'_>,
+	subdir: &SubDir,
+	dest: &Path,
+) -> Result<(), Error> {
+	let target_tree;
+	let tree = if subdir.is_empty() {
+		tree
+	} else {
+		let mut t = tree.clone();
+		let entry = t
+			.peel_to_entry_by_path(subdir.as_path().as_std_path())?
+			.ok_or_else(|| Error::SubdirNotFound(subdir.to_string()))?;
+		target_tree = entry.object()?.into_tree();
+		&target_tree
+	};
+
+	let files = tree.traverse().breadthfirst.files()?;
+
+	for entry in &files {
+		if !entry.mode.is_blob() {
+			continue;
+		}
+		let rel_path = entry
+			.filepath
+			.to_str()
+			.map_err(|_| Error::InvalidPath(entry.filepath.to_string()))?;
+		let file_path = dest.join(rel_path);
+
+		if let Some(parent) = file_path.parent() {
+			fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+		}
+
+		let blob = repo.find_object(entry.oid)?;
+		fs::write(&file_path, &blob.data).map_err(|e| Error::Io(file_path, e))?;
+	}
+
+	Ok(())
+}
+
+fn resolve_version<'r>(repo: &'r gix::Repository, version: &str) -> Result<gix::Id<'r>> {
+	let spec: &bstr::BStr = version.into();
+	if let Ok(id) = repo.rev_parse_single(spec) {
+		return Ok(id);
+	}
+	for prefix in ["refs/heads/", "refs/tags/"] {
+		let refname = format!("{prefix}{version}");
+		if let Ok(r) = repo.find_reference(&refname) {
+			return Ok(r.into_fully_peeled_id()?);
+		}
+	}
+	Ok(repo.rev_parse_single(spec)?)
+}
+
+fn read_blob_at_path(
+	repo: &gix::Repository,
+	tree: &gix::Tree<'_>,
+	path: &SubDir,
+) -> Option<Vec<u8>> {
+	let mut t = tree.clone();
+	let entry = t
+		.peel_to_entry_by_path(path.as_path().as_std_path())
+		.ok()??;
+	let blob = repo.find_object(entry.oid()).ok()?;
+	Some(blob.data.clone())
+}
+
+fn collect_tree_deps(
+	repo: &gix::Repository,
+	tree: &gix::Tree<'_>,
+	dir: &SubDir,
+	git_deps: &mut Vec<Dependency>,
+	local_extractions: &mut Vec<LocalExtraction>,
+	visited: &mut HashSet<SubDir>,
+) {
+	if !visited.insert(dir.clone()) {
+		return;
+	}
+
+	let manifest_path = dir
+		.join("jsonnetfile.json")
+		.expect("appending a literal filename keeps it within parent");
+	let Some(data) = read_blob_at_path(repo, tree, &manifest_path) else {
+		return;
+	};
+	let Ok(manifest) = serde_json::from_slice::<JsonnetFile>(&data) else {
+		return;
+	};
+
+	for dep in manifest.dependencies {
+		match &dep.source {
+			Source::Git(_) => git_deps.push(dep),
+			Source::Local(local) => {
+				let Ok(child_dir) = local.resolve_under(dir) else {
+					info!("local source {local} escapes its package; skipping");
+					continue;
+				};
+				let name = child_dir
+					.file_name()
+					.map_or_else(|| local.to_string(), str::to_owned);
+				local_extractions.push(LocalExtraction {
+					tree_path: child_dir.clone(),
+					name,
+				});
+				collect_tree_deps(repo, tree, &child_dir, git_deps, local_extractions, visited);
+			}
+		}
+	}
+}
+
+pub(super) fn resolve(
+	git_source: &GitSource,
+	version: Option<&str>,
+) -> Result<ResolveResult, Error> {
+	info!("fetching via git: {}", git_source.remote());
+	let repo = ensure_repo(git_source)?;
+	let id = match version {
+		Some(v) => resolve_version(&repo, v)?,
+		None => repo.head_id()?,
+	};
+	let commit = repo.find_object(id)?.into_commit();
+	let tree = commit.tree()?;
+
+	let mut transitive_git_deps = Vec::new();
+	let mut local_extractions = Vec::new();
+	let mut visited = HashSet::new();
+	collect_tree_deps(
+		&repo,
+		&tree,
+		&git_source.subdir,
+		&mut transitive_git_deps,
+		&mut local_extractions,
+		&mut visited,
+	);
+
+	let repo_path = repo_cache_path(git_source)?;
+	let sha = id.to_string();
+
+	Ok(ResolveResult {
+		version: sha.clone(),
+		transitive_git_deps,
+		local_extractions,
+		source: VendorSource::GitTree {
+			repo_path,
+			commit_sha: sha,
+			subdir: git_source.subdir.clone(),
+		},
+	})
+}
+
+pub(super) fn extract(
+	repo_path: &Path,
+	commit_sha: &str,
+	subdir: &SubDir,
+	dest: &Path,
+) -> Result<(), Error> {
+	let repo = gix::open(repo_path)?;
+	let spec: &bstr::BStr = commit_sha.into();
+	let id = repo.rev_parse_single(spec)?;
+	let commit = repo.find_object(id)?.into_commit();
+	let tree = commit.tree()?;
+	extract_tree(&repo, &tree, subdir, dest)
+}
addedcrates/jrsonnet-pkg/src/install/github.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/github.rs
@@ -0,0 +1,190 @@
+#![allow(clippy::result_large_err)]
+
+use std::{
+	collections::HashSet,
+	fs::{self, File},
+	io::Write as _,
+	path::{Path, PathBuf},
+};
+
+use reqwest::{blocking::Response, header};
+use tracing::{debug, info};
+
+use super::{
+	Error, LocalExtraction, ResolveResult, Result, VendorSource,
+	accessor::{AccessorEntry, ZipFileAccessor},
+};
+use crate::{
+	install::{PKG_USER_AGENT, cache_dir},
+	jsonnet_bundler::{Dependency, GitSource, JsonnetFile, Source, SubDir},
+};
+
+fn is_sha(s: &str) -> bool {
+	s.len() == 40 && s.bytes().all(|b| b.is_ascii_hexdigit())
+}
+
+fn commit_cache_path(source: &GitSource, sha: &str) -> Result<PathBuf> {
+	Ok(cache_dir("github")?
+		.join(source.plain_repo_name())
+		.join(format!("{sha}.zip")))
+}
+
+fn resolve_sha(source: &GitSource, version: &str) -> Result<String> {
+	let url = format!(
+		"https://api.github.com/repos/{}/commits/{}",
+		source.plain_repo_name(),
+		version
+	);
+	let response = reqwest::blocking::Client::new()
+		.get(&url)
+		.header(header::ACCEPT, "application/vnd.github.sha")
+		.header(header::USER_AGENT, PKG_USER_AGENT)
+		.send()
+		.and_then(Response::error_for_status)?;
+	let sha = response.text()?;
+	Ok(sha.trim().to_owned())
+}
+
+fn fetch_zip(source: &GitSource, sha: &str) -> Result<ZipFileAccessor> {
+	let cached = commit_cache_path(source, sha)?;
+	if cached.exists() {
+		debug!("using cached archive {}", cached.display());
+		return Ok(ZipFileAccessor::new_prefixed(
+			File::open(&cached).map_err(|e| Error::Io(cached.clone(), e))?,
+		)?);
+	}
+
+	let url = format!(
+		"https://github.com/{}/archive/{}.zip",
+		source.plain_repo_name(),
+		sha
+	);
+	info!("downloading {url}");
+
+	let bytes = reqwest::blocking::Client::new()
+		.get(&url)
+		.header(header::USER_AGENT, PKG_USER_AGENT)
+		.send()
+		.and_then(Response::error_for_status)?
+		.bytes()?;
+
+	if let Some(parent) = cached.parent() {
+		fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+	}
+	let mut downloaded = File::create_new(&cached).map_err(|e| Error::Io(cached.clone(), e))?;
+	downloaded
+		.write_all(&bytes)
+		.map_err(|e| Error::Io(cached.clone(), e))?;
+
+	Ok(ZipFileAccessor::new_prefixed(downloaded)?)
+}
+
+fn open_cached_zip(zip_path: &Path) -> Result<ZipFileAccessor> {
+	Ok(ZipFileAccessor::new_prefixed(
+		File::open(zip_path).map_err(|e| Error::Io(zip_path.to_owned(), e))?,
+	)?)
+}
+
+fn extract_subdir(archive: &ZipFileAccessor, subdir: &SubDir, dest: &Path) -> Result<()> {
+	archive.iter(subdir, &mut |name, entry| {
+		let target = dest.join(name);
+		match entry {
+			AccessorEntry::Dir => {
+				fs::create_dir_all(&target).map_err(|e| Error::Io(target, e))?;
+			}
+			AccessorEntry::File(data) => {
+				if let Some(parent) = target.parent() {
+					fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;
+				}
+				fs::write(&target, &data).map_err(|e| Error::Io(target, e))?;
+			}
+		}
+		Ok(())
+	})
+}
+
+fn collect_archive_deps(
+	archive: &ZipFileAccessor,
+	dir: &SubDir,
+	git_deps: &mut Vec<Dependency>,
+	local_extractions: &mut Vec<LocalExtraction>,
+	visited: &mut HashSet<SubDir>,
+) -> Result<()> {
+	if !visited.insert(dir.clone()) {
+		return Ok(());
+	}
+
+	let manifest_path = dir
+		.join("jsonnetfile.json")
+		.expect("appending a literal filename keeps it within parent");
+
+	let Some(data) = archive.read(&manifest_path)? else {
+		return Ok(());
+	};
+	let Ok(manifest) = serde_json::from_slice::<JsonnetFile>(&data) else {
+		return Ok(());
+	};
+
+	for dep in manifest.dependencies {
+		match &dep.source {
+			Source::Git(_) => git_deps.push(dep),
+			Source::Local(local) => {
+				let Ok(child_dir) = local.resolve_under(dir) else {
+					tracing::info!("local source {local} escapes its package; skipping");
+					continue;
+				};
+				let name = child_dir
+					.file_name()
+					.map_or_else(|| local.to_string(), str::to_owned);
+				local_extractions.push(LocalExtraction {
+					tree_path: child_dir.clone(),
+					name,
+				});
+				collect_archive_deps(archive, &child_dir, git_deps, local_extractions, visited)?;
+			}
+		}
+	}
+	Ok(())
+}
+
+pub(super) fn resolve(source: &GitSource, version: Option<&str>) -> Result<ResolveResult> {
+	let version_str = version.unwrap_or("HEAD");
+	let sha = if is_sha(version_str) {
+		version_str.to_owned()
+	} else {
+		let resolved = resolve_sha(source, version_str)?;
+		info!("resolved {version_str} to {resolved}");
+		resolved
+	};
+
+	let archive = fetch_zip(source, &sha)?;
+
+	let mut transitive_git_deps = Vec::new();
+	let mut local_extractions = Vec::new();
+	let mut visited = HashSet::new();
+	collect_archive_deps(
+		&archive,
+		&source.subdir,
+		&mut transitive_git_deps,
+		&mut local_extractions,
+		&mut visited,
+	)?;
+
+	let zip_path = commit_cache_path(source, &sha)?;
+
+	Ok(ResolveResult {
+		version: sha.clone(),
+		transitive_git_deps,
+		local_extractions,
+		source: VendorSource::GithubZip {
+			zip_path,
+			commit_sha: sha,
+			subdir: source.subdir.clone(),
+		},
+	})
+}
+
+pub(super) fn extract(zip_path: &Path, subdir: &SubDir, dest: &Path) -> Result<()> {
+	let archive = open_cached_zip(zip_path)?;
+	extract_subdir(&archive, subdir, dest)
+}
addedcrates/jrsonnet-pkg/src/install/mod.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/install/mod.rs
@@ -0,0 +1,406 @@
+#![allow(clippy::result_large_err)]
+
+pub mod accessor;
+mod git;
+mod github;
+
+use std::{
+	collections::{BTreeMap, HashSet},
+	fs,
+	path::{Path, PathBuf},
+	result,
+};
+
+use camino::Utf8PathBuf;
+use tracing::info;
+
+use crate::jsonnet_bundler::{Dependency, GitScheme, GitSource, JsonnetFile, Source, SubDir};
+
+pub const PKG_USER_AGENT: &str = "jrsonnet-pkg (https://delta.rocks/jrsonnet)";
+
+pub fn cache_dir(subdir: &str) -> Result<std::path::PathBuf> {
+	Ok(directories::ProjectDirs::from("rocks", "delta", "jrsonnet")
+		.ok_or(Error::XdgUnavailable)?
+		.cache_dir()
+		.join(subdir))
+}
+
+pub(crate) struct LocalExtraction {
+	/// Path inside the parent repo's tree where this local source lives.
+	pub tree_path: SubDir,
+	pub name: String,
+}
+
+pub(crate) struct ResolveResult {
+	pub version: String,
+	pub transitive_git_deps: Vec<Dependency>,
+	pub local_extractions: Vec<LocalExtraction>,
+	pub source: VendorSource,
+}
+
+const VERSION_FILE: &str = ".version";
+
+/// How to populate a vendor path.
+pub enum VendorSource {
+	GitTree {
+		repo_path: PathBuf,
+		commit_sha: String,
+		subdir: SubDir,
+	},
+	GithubZip {
+		zip_path: PathBuf,
+		commit_sha: String,
+		subdir: SubDir,
+	},
+	Symlink(Utf8PathBuf),
+}
+
+impl VendorSource {
+	fn with_subdir(&self, new_subdir: SubDir) -> Self {
+		match self {
+			VendorSource::GitTree {
+				repo_path,
+				commit_sha,
+				..
+			} => VendorSource::GitTree {
+				repo_path: repo_path.clone(),
+				commit_sha: commit_sha.clone(),
+				subdir: new_subdir,
+			},
+			VendorSource::GithubZip {
+				zip_path,
+				commit_sha,
+				..
+			} => VendorSource::GithubZip {
+				zip_path: zip_path.clone(),
+				commit_sha: commit_sha.clone(),
+				subdir: new_subdir,
+			},
+			VendorSource::Symlink(target) => VendorSource::Symlink(target.clone()),
+		}
+	}
+}
+
+pub struct InstallPlan {
+	pub lock: JsonnetFile,
+	/// vendor-relative path -> how to obtain it.
+	pub entries: BTreeMap<Utf8PathBuf, VendorSource>,
+}
+
+pub fn install(
+	manifest: &JsonnetFile,
+	lock: Option<&JsonnetFile>,
+	vendor_dir: &Path,
+	dry_run: bool,
+) -> Result<JsonnetFile, Error> {
+	let plan = resolve(manifest, lock)?;
+	execute(&plan, vendor_dir, dry_run)?;
+	Ok(plan.lock)
+}
+
+pub fn resolve(manifest: &JsonnetFile, lock: Option<&JsonnetFile>) -> Result<InstallPlan, Error> {
+	let mut plan = InstallPlan {
+		lock: JsonnetFile {
+			version: manifest.version,
+			dependencies: Vec::new(),
+			legacy_imports: manifest.legacy_imports,
+		},
+		entries: BTreeMap::new(),
+	};
+	let mut installed = HashSet::new();
+
+	resolve_deps(
+		&manifest.dependencies,
+		lock,
+		manifest.legacy_imports,
+		&mut plan,
+		&mut installed,
+	)?;
+
+	Ok(plan)
+}
+
+fn is_up_to_date(dest: &Path, version: &str) -> bool {
+	fs::read_to_string(dest.join(VERSION_FILE)).is_ok_and(|v| v.trim() == version)
+}
+
+fn write_version(dest: &Path, version: &str) -> Result<(), Error> {
+	fs::write(dest.join(VERSION_FILE), format!("{version}\n"))
+		.map_err(|e| Error::Io(dest.join(VERSION_FILE), e))
+}
+
+pub fn execute(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {
+	if !dry_run {
+		for (path, source) in &plan.entries {
+			let dest = vendor_dir.join(path);
+			match source {
+				VendorSource::GitTree {
+					repo_path,
+					commit_sha,
+					subdir,
+				} => {
+					if is_up_to_date(&dest, commit_sha) {
+						continue;
+					}
+					info!("extract {path}");
+					if dest.exists() {
+						fs::remove_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+					}
+					fs::create_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+					git::extract(repo_path, commit_sha, subdir, &dest)?;
+					write_version(&dest, commit_sha)?;
+				}
+				VendorSource::GithubZip {
+					zip_path,
+					commit_sha,
+					subdir,
+				} => {
+					if is_up_to_date(&dest, commit_sha) {
+						continue;
+					}
+					info!("extract {path}");
+					if dest.exists() {
+						fs::remove_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+					}
+					fs::create_dir_all(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+					github::extract(zip_path, subdir, &dest)?;
+					write_version(&dest, commit_sha)?;
+				}
+				VendorSource::Symlink(_) => {}
+			}
+		}
+		for (path, source) in &plan.entries {
+			if let VendorSource::Symlink(target) = source {
+				let dest = vendor_dir.join(path);
+				if dest
+					.symlink_metadata()
+					.is_ok_and(|m| m.file_type().is_symlink())
+				{
+					if fs::read_link(&dest).is_ok_and(|t| t == target.as_std_path()) {
+						continue;
+					}
+					fs::remove_file(&dest).map_err(|e| Error::Io(dest.clone(), e))?;
+				}
+				info!("symlink {path} -> {target}");
+				std::os::unix::fs::symlink(target.as_std_path(), &dest)
+					.map_err(|e| Error::Io(dest.clone(), e))?;
+			}
+		}
+	}
+	prune(plan, vendor_dir, dry_run)?;
+	Ok(())
+}
+
+fn prune(plan: &InstallPlan, vendor_dir: &Path, dry_run: bool) -> Result<(), Error> {
+	if !vendor_dir.is_dir() {
+		return Ok(());
+	}
+	prune_recursive(plan, vendor_dir, vendor_dir, dry_run)
+}
+
+fn prune_recursive(
+	plan: &InstallPlan,
+	vendor_dir: &Path,
+	dir: &Path,
+	dry_run: bool,
+) -> Result<(), Error> {
+	let entries = fs::read_dir(dir).map_err(|e| Error::Io(dir.to_owned(), e))?;
+	for entry in entries {
+		let entry = entry.map_err(|e| Error::Io(dir.to_owned(), e))?;
+		let path = entry.path();
+		let rel = path
+			.strip_prefix(vendor_dir)
+			.expect("path is under vendor_dir");
+		let Ok(rel) = Utf8PathBuf::try_from(rel.to_owned()) else {
+			info!("prune (non-utf8) {}", rel.display());
+			continue;
+		};
+
+		if plan.entries.contains_key(&rel) {
+			continue;
+		}
+
+		let ft = entry.file_type().map_err(|e| Error::Io(path.clone(), e))?;
+		if ft.is_symlink() {
+			info!("prune {rel}");
+			if !dry_run {
+				fs::remove_file(&path).map_err(|e| Error::Io(path, e))?;
+			}
+		} else if ft.is_dir() {
+			let prefix: Utf8PathBuf = format!("{rel}/").into();
+			let has_descendants = plan
+				.entries
+				.range(prefix.clone()..)
+				.next()
+				.is_some_and(|(k, _)| k.starts_with(&prefix));
+			if has_descendants {
+				prune_recursive(plan, vendor_dir, &path, dry_run)?;
+			} else {
+				info!("prune {rel}");
+				if !dry_run {
+					fs::remove_dir_all(&path).map_err(|e| Error::Io(path, e))?;
+				}
+			}
+		} else {
+			info!("prune {rel}");
+			if !dry_run {
+				fs::remove_file(&path).map_err(|e| Error::Io(path, e))?;
+			}
+		}
+	}
+
+	if !dry_run
+		&& dir != vendor_dir
+		&& let Ok(mut entries) = fs::read_dir(dir)
+		&& entries.next().is_none()
+	{
+		let _ = fs::remove_dir(dir);
+	}
+
+	Ok(())
+}
+
+fn resolve_one(git_source: &GitSource, version: Option<&str>) -> Result<ResolveResult, Error> {
+	if git_source.host == "github.com" && git_source.scheme == GitScheme::Https {
+		match github::resolve(git_source, version) {
+			Ok(result) => return Ok(result),
+			Err(e) => {
+				info!("github archive failed ({e}), falling back to git");
+			}
+		}
+	}
+	git::resolve(git_source, version)
+}
+
+fn locked_version<'a>(dep: &Dependency, lock: Option<&'a JsonnetFile>) -> Option<&'a str> {
+	let lock = lock?;
+	let key = dep.canonical_name();
+	lock.dependencies
+		.iter()
+		.find(|d| d.canonical_name() == key)
+		.and_then(|d| d.version.as_deref())
+}
+
+fn resolve_deps(
+	deps: &[Dependency],
+	lock: Option<&JsonnetFile>,
+	legacy_imports: bool,
+	plan: &mut InstallPlan,
+	installed: &mut HashSet<Utf8PathBuf>,
+) -> Result<(), Error> {
+	for dep in deps {
+		let Source::Git(git_source) = &dep.source else {
+			continue;
+		};
+
+		let canonical = dep.canonical_name();
+		if !installed.insert(canonical.clone()) {
+			continue;
+		}
+
+		let version = locked_version(dep, lock).or(dep.version.as_deref());
+
+		info!(
+			"resolving {canonical} (version: {})",
+			version.unwrap_or("<TBD>")
+		);
+
+		let result = resolve_one(git_source, version)?;
+
+		plan.lock.dependencies.push(Dependency {
+			source: dep.source.clone(),
+			version: Some(result.version),
+			sum: dep.sum.clone(),
+			name: dep.name.clone(),
+			single: dep.single,
+		});
+
+		let mut repo_base = Utf8PathBuf::from(git_source.host.as_str());
+		repo_base.push(git_source.plain_repo_name());
+
+		// Legacy symlink for the dep. Skipped if `legacyImports: false`, unless
+		// the user explicitly set `dep.name` (which is always honored).
+		if legacy_imports || dep.name.is_some() {
+			let legacy = Utf8PathBuf::from(dep.legacy_link_name());
+			if legacy != canonical {
+				plan.entries
+					.insert(legacy, VendorSource::Symlink(canonical.clone()));
+			}
+		}
+
+		for extraction in &result.local_extractions {
+			let extraction_canonical = repo_base.join(&extraction.tree_path);
+			plan.entries.insert(
+				extraction_canonical.clone(),
+				result.source.with_subdir(extraction.tree_path.clone()),
+			);
+			if legacy_imports {
+				let extraction_name = Utf8PathBuf::from(&extraction.name);
+				if extraction_name != extraction_canonical {
+					plan.entries
+						.insert(extraction_name, VendorSource::Symlink(extraction_canonical));
+				}
+			}
+		}
+
+		// Main entry (after local extractions used with_subdir)
+		plan.entries.insert(canonical, result.source);
+
+		resolve_deps(
+			&result.transitive_git_deps,
+			lock,
+			legacy_imports,
+			plan,
+			installed,
+		)?;
+	}
+
+	Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+	#[error("io error for {0}: {1}")]
+	Io(PathBuf, std::io::Error),
+	#[error("failed to discover xdg directories")]
+	XdgUnavailable,
+	#[error("git clone failed: {0}")]
+	GitClone(#[from] gix::clone::Error),
+	#[error(transparent)]
+	GitRemote(#[from] gix::remote::init::Error),
+	#[error(transparent)]
+	GitConnect(#[from] gix::remote::connect::Error),
+	#[error(transparent)]
+	GitFetchPrepare(#[from] gix::remote::fetch::prepare::Error),
+	#[error(transparent)]
+	GitRemoteFetch(#[from] gix::remote::fetch::Error),
+	#[error(transparent)]
+	GitCloneFetch(#[from] gix::clone::fetch::Error),
+	#[error(transparent)]
+	GitFindObject(#[from] gix::object::find::existing::Error),
+	#[error(transparent)]
+	GitTraverse(#[from] gix::traverse::tree::breadthfirst::Error),
+	#[error(transparent)]
+	GitHead(#[from] gix::reference::head_id::Error),
+	#[error(transparent)]
+	GitCommit(#[from] gix::object::commit::Error),
+	#[error(transparent)]
+	GitRevparse(#[from] gix::revision::spec::parse::single::Error),
+	#[error(transparent)]
+	GitRefspec(#[from] gix::refspec::parse::Error),
+	#[error(transparent)]
+	GitPeel(#[from] gix::reference::peel::Error),
+	#[error(transparent)]
+	GitOpen(#[from] gix::open::Error),
+	#[error("http error: {0}")]
+	Http(#[from] reqwest::Error),
+	#[error("zip error: {0}")]
+	Zip(Box<zip::result::ZipError>),
+	#[error(transparent)]
+	Accessor(#[from] accessor::Error),
+	#[error("unknown subdir: {0}")]
+	SubdirNotFound(String),
+	#[error("invalid path in tree: {0}")]
+	InvalidPath(String),
+}
+pub(crate) type Result<T, E = Error> = result::Result<T, E>;
addedcrates/jrsonnet-pkg/src/jsonnet_bundler.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/jsonnet_bundler.rs
@@ -0,0 +1,883 @@
+use std::{fmt, path::Path, str::FromStr};
+
+use camino::{Utf8Component, Utf8Path, Utf8PathBuf};
+use serde::{Deserialize, Serialize, de};
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct JsonnetFile {
+	pub version: u32,
+	#[serde(default)]
+	pub dependencies: Vec<Dependency>,
+	#[serde(default = "legacy_imports_default", rename = "legacyImports")]
+	pub legacy_imports: bool,
+}
+
+fn legacy_imports_default() -> bool {
+	true
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Dependency {
+	pub source: Source,
+	#[serde(default, skip_serializing_if = "Option::is_none")]
+	pub version: Option<String>,
+	#[serde(default, skip_serializing_if = "Option::is_none")]
+	pub sum: Option<String>,
+	#[serde(default, skip_serializing_if = "Option::is_none")]
+	pub name: Option<String>,
+	#[serde(default, skip_serializing_if = "is_false")]
+	pub single: bool,
+}
+
+#[allow(clippy::trivially_copy_pass_by_ref, reason = "serde")]
+fn is_false(v: &bool) -> bool {
+	!v
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum Source {
+	Git(GitSource),
+	Local(LocalSource),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum GitScheme {
+	Https,
+	Ssh,
+}
+
+/// Wrapper over `Utf8PathBuf`, ensuring it can't escape to either an absolute
+/// path or a parent directory.
+#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct SubDir(Utf8PathBuf);
+
+#[derive(Debug, thiserror::Error)]
+#[error("subdir attempted to escape")]
+pub struct SubDirEscapeError;
+
+impl FromStr for SubDir {
+	type Err = SubDirEscapeError;
+	fn from_str(s: &str) -> Result<Self, Self::Err> {
+		Self::try_from(Utf8PathBuf::from(s))
+	}
+}
+impl TryFrom<Utf8PathBuf> for SubDir {
+	type Error = SubDirEscapeError;
+
+	fn try_from(buf: Utf8PathBuf) -> Result<Self, Self::Error> {
+		for ele in buf.components() {
+			match ele {
+				Utf8Component::Prefix(_) | Utf8Component::RootDir | Utf8Component::ParentDir => {
+					return Err(SubDirEscapeError);
+				}
+				Utf8Component::CurDir | Utf8Component::Normal(_) => {}
+			}
+		}
+		Ok(Self(buf))
+	}
+}
+
+impl SubDir {
+	pub fn empty() -> Self {
+		Self(Utf8PathBuf::new())
+	}
+	pub fn as_str(&self) -> &str {
+		self.0.as_str()
+	}
+	pub fn as_path(&self) -> &Utf8Path {
+		&self.0
+	}
+	pub fn into_inner(self) -> Utf8PathBuf {
+		self.0
+	}
+	pub fn join(&self, other: impl AsRef<Utf8Path>) -> Result<SubDir, SubDirEscapeError> {
+		SubDir::try_from(self.0.join(other))
+	}
+	pub fn strip_prefix(&self, prefix: &SubDir) -> Option<SubDir> {
+		Some(
+			SubDir::try_from(self.0.strip_prefix(&prefix.0).ok()?.to_owned())
+				.expect("stripping would not result in escape"),
+		)
+	}
+	pub fn is_empty(&self) -> bool {
+		self.0.as_str().is_empty()
+	}
+	pub fn file_name(&self) -> Option<&str> {
+		self.0.file_name()
+	}
+	/// Strip a trailing `.git` extension, if any.
+	#[must_use]
+	pub fn without_git_suffix(&self) -> SubDir {
+		let mut p = self.0.clone();
+		if p.extension() == Some("git") {
+			p.set_extension("");
+		}
+		SubDir(p)
+	}
+}
+impl AsRef<Utf8Path> for SubDir {
+	fn as_ref(&self) -> &Utf8Path {
+		&self.0
+	}
+}
+impl AsRef<Path> for SubDir {
+	fn as_ref(&self) -> &Path {
+		self.0.as_ref()
+	}
+}
+impl AsRef<str> for SubDir {
+	fn as_ref(&self) -> &str {
+		self.0.as_str()
+	}
+}
+impl fmt::Display for SubDir {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		write!(f, "{}", self.0)
+	}
+}
+impl PartialEq<str> for SubDir {
+	fn eq(&self, other: &str) -> bool {
+		self.0.as_str() == other
+	}
+}
+impl PartialEq<&str> for SubDir {
+	fn eq(&self, other: &&str) -> bool {
+		self.0.as_str() == *other
+	}
+}
+
+/// Wrapper over `String`, guaranteeing the value is a valid host: only ASCII
+/// alphanumerics, dashes and dots, with at least one segment.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Hostname(String);
+
+#[derive(Debug, thiserror::Error)]
+#[error("invalid hostname")]
+pub struct InvalidHostnameError;
+
+impl FromStr for Hostname {
+	type Err = InvalidHostnameError;
+
+	fn from_str(s: &str) -> Result<Self, Self::Err> {
+		if s.is_empty() || s == "." || s == ".." {
+			return Err(InvalidHostnameError);
+		}
+		for seg in s.split('.') {
+			if seg.is_empty() {
+				return Err(InvalidHostnameError);
+			}
+			if !seg.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') {
+				return Err(InvalidHostnameError);
+			}
+		}
+		Ok(Self(s.to_owned()))
+	}
+}
+
+impl Hostname {
+	pub fn as_str(&self) -> &str {
+		&self.0
+	}
+}
+impl AsRef<str> for Hostname {
+	fn as_ref(&self) -> &str {
+		&self.0
+	}
+}
+impl AsRef<Path> for Hostname {
+	fn as_ref(&self) -> &Path {
+		self.0.as_ref()
+	}
+}
+impl AsRef<Utf8Path> for Hostname {
+	fn as_ref(&self) -> &Utf8Path {
+		self.0.as_str().into()
+	}
+}
+impl fmt::Display for Hostname {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		f.write_str(&self.0)
+	}
+}
+impl PartialEq<str> for Hostname {
+	fn eq(&self, other: &str) -> bool {
+		self.0 == other
+	}
+}
+impl PartialEq<&str> for Hostname {
+	fn eq(&self, other: &&str) -> bool {
+		self.0 == *other
+	}
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GitSource {
+	pub scheme: GitScheme,
+	pub host: Hostname,
+	/// Repo path relative to host: `user/repo[.git]` (or with subgroups).
+	pub repo: SubDir,
+	/// Subdirectory within the repo. Empty means the repo root.
+	pub subdir: SubDir,
+}
+
+/// A relative path that may climb out of its package via `..` parts, but only
+/// at the head - once you go down (`SubDir` portion) you can't go back up.
+///
+/// The total upward count is bounded only at resolution time, against the
+/// containing package's depth.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LocalSource {
+	pub ups: usize,
+	pub dir: SubDir,
+}
+
+impl FromStr for LocalSource {
+	// Technically incorrect, as it only rejects mid-path ../'s...
+	type Err = SubDirEscapeError;
+
+	fn from_str(s: &str) -> Result<Self, Self::Err> {
+		let mut ups = 0usize;
+		let mut rest = s;
+		loop {
+			if let Some(r) = rest.strip_prefix("./") {
+				rest = r;
+			} else if rest == "." {
+				rest = "";
+				break;
+			} else if let Some(r) = rest.strip_prefix("../") {
+				ups = ups.checked_add(1).expect("can't be longer than s length");
+				rest = r;
+			} else if rest == ".." {
+				ups = ups.checked_add(1).expect("can't be longer than s length");
+				rest = "";
+				break;
+			} else {
+				break;
+			}
+		}
+		Ok(Self {
+			ups,
+			dir: SubDir::from_str(rest)?,
+		})
+	}
+}
+
+impl fmt::Display for LocalSource {
+	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+		let mut out = String::with_capacity(self.ups * 3 + self.dir.as_str().len());
+		for _ in 0..self.ups {
+			out.push_str("../");
+		}
+		out.push_str(self.dir.as_str());
+		if out.is_empty() {
+			out.push('.');
+		} else if out.ends_with('/') {
+			out.pop();
+		}
+		// TODO: I didn't finish
+		f.write_str(&out)
+	}
+}
+
+impl LocalSource {
+	pub fn resolve_under(&self, parent: &SubDir) -> Result<SubDir, SubDirEscapeError> {
+		let mut comps: Vec<&str> = parent.as_path().components().map(|c| c.as_str()).collect();
+		if self.ups > comps.len() {
+			return Err(SubDirEscapeError);
+		}
+		comps.truncate(comps.len() - self.ups);
+		let mut buf = Utf8PathBuf::from_iter(comps);
+		buf.push(self.dir.as_path());
+		SubDir::try_from(buf)
+	}
+}
+
+impl Serialize for LocalSource {
+	fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
+		#[derive(Serialize)]
+		struct JsonLocal<'a> {
+			directory: &'a str,
+		}
+		let rendered = self.to_string();
+		JsonLocal {
+			directory: &rendered,
+		}
+		.serialize(ser)
+	}
+}
+
+impl<'de> Deserialize<'de> for LocalSource {
+	fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
+		#[derive(Deserialize)]
+		struct JsonLocal {
+			directory: String,
+		}
+		let j = JsonLocal::deserialize(de)?;
+		LocalSource::from_str(&j.directory)
+			.map_err(|e| de::Error::custom(format!("invalid local path {:?}: {e}", j.directory)))
+	}
+}
+
+impl GitSource {
+	/// Repo path with the trailing `.git` (if any) stripped.
+	pub fn plain_repo_name(&self) -> SubDir {
+		self.repo.without_git_suffix()
+	}
+
+	/// Canonical install path: `host/user/repo[/subdir]`.
+	pub fn name(&self) -> SubDir {
+		let mut p = Utf8PathBuf::from(self.host.as_str());
+		p.push(self.plain_repo_name());
+		if !self.subdir.is_empty() {
+			p.push(self.subdir.as_path());
+		}
+		SubDir::try_from(p).expect("host + subdirs is a valid SubDir")
+	}
+
+	/// Last path component of `repo[/subdir]`, used as the legacy symlink name.
+	pub fn legacy_name(&self) -> String {
+		self.name()
+			.file_name()
+			.expect("name has at least one component")
+			.to_owned()
+	}
+
+	/// Git remote URL for cloning.
+	pub fn remote(&self) -> String {
+		let host = self.host.as_str();
+		let repo = self.repo.as_str();
+		match self.scheme {
+			GitScheme::Ssh => format!("ssh://git@{host}/{repo}"),
+			GitScheme::Https => format!("https://{host}/{repo}"),
+		}
+	}
+
+	/// Parse a URI like `github.com/user/repo/subdir@version` into a
+	/// `Dependency`.
+	pub fn parse(uri: &str) -> Option<Dependency> {
+		git_uri::parse(uri).ok()
+	}
+}
+
+peg::parser! {
+	grammar git_uri() for str {
+		rule host_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '-']+;
+		rule host() -> Hostname
+			= s:$(host_segment()++".")
+			{ Hostname::from_str(s).expect("grammar restricted to valid host chars") }
+
+		// User/repo path segments. `~` is allowed for Bitbucket personal repos.
+		rule path_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '~']+;
+		// Subdir segments allow dots (e.g. `ksonnet.beta.3`).
+		rule subdir_segment() = ['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | '.']+;
+
+		// `user[/group...]/repo.git`
+		rule repo_dotgit() -> SubDir
+			= s:$(path_segment()++"/" ".git")
+			{ SubDir::from_str(s).expect("grammar restricted to subpath chars") }
+		// `user/repo` (exactly two segments, no `.git`)
+		rule repo_simple() -> SubDir
+			= s:$(path_segment() "/" path_segment())
+			{ SubDir::from_str(s).expect("grammar restricted to subpath chars") }
+
+		// Subdir starts with `/`. May be empty.
+		rule subdir() -> SubDir
+			= "/" s:$(subdir_segment() ** "/") "/"?
+			{ SubDir::from_str(s).expect("grammar restricted to subdir chars") }
+			/ { SubDir::empty() }
+
+		rule version() -> &'input str
+			= "@" v:$([_]+) { v }
+
+
+		// git@host:path.git[/subdir][@version]  (SCP style)
+		rule scp_uri() -> Dependency
+			= "git@" h:host() ":" repo:repo_dotgit() subdir:subdir()
+			  v:version()?
+		{
+			make_dep(GitScheme::Ssh, h, repo, subdir, v)
+		}
+
+		// ssh://git@host/path.git[/subdir][@version]
+		rule ssh_uri() -> Dependency
+			= "ssh://git@" h:host() "/" repo:repo_dotgit() subdir:subdir()
+			  v:version()?
+		{
+			make_dep(GitScheme::Ssh, h, repo, subdir, v)
+		}
+
+		// [https://]host/path.git[/subdir][@version]
+		rule https_dotgit() -> Dependency
+			= "https://"? h:host() "/" repo:repo_dotgit() subdir:subdir()
+			  v:version()?
+		{
+			make_dep(GitScheme::Https, h, repo, subdir, v)
+		}
+
+		// [https://]host/user/repo[/subdir[/...]][@version]
+		rule https_simple() -> Dependency
+			= "https://"? h:host() "/" repo:repo_simple() subdir:subdir()
+			  v:version()?
+		{
+			make_dep(GitScheme::Https, h, repo, subdir, v)
+		}
+
+		pub rule parse() -> Dependency
+			= ssh_uri() / scp_uri() / https_dotgit() / https_simple()
+	}
+}
+
+fn make_dep(
+	scheme: GitScheme,
+	host: Hostname,
+	repo: SubDir,
+	subdir: SubDir,
+	version: Option<&str>,
+) -> Dependency {
+	Dependency {
+		source: Source::Git(GitSource {
+			scheme,
+			host,
+			repo,
+			subdir,
+		}),
+		version: version.map(str::to_owned),
+		sum: None,
+		name: None,
+		single: false,
+	}
+}
+
+impl Dependency {
+	/// Canonical install path for deduplication and vendor extraction.
+	pub fn canonical_name(&self) -> Utf8PathBuf {
+		match &self.source {
+			Source::Git(git) => git.name().into_inner(),
+			Source::Local(local) => Utf8PathBuf::from(local.to_string()),
+		}
+	}
+
+	/// Legacy symlink name: `dep.name` override, or last path component.
+	pub fn legacy_link_name(&self) -> String {
+		if let Some(name) = &self.name {
+			return name.clone();
+		}
+		match &self.source {
+			Source::Git(git) => git.legacy_name(),
+			Source::Local(local) => local
+				.dir
+				.file_name()
+				.map_or_else(|| local.to_string(), str::to_owned),
+		}
+	}
+}
+
+impl Serialize for GitSource {
+	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+		#[derive(Serialize)]
+		struct JsonGit<'a> {
+			remote: String,
+			#[serde(skip_serializing_if = "str::is_empty")]
+			subdir: &'a str,
+		}
+		JsonGit {
+			remote: self.remote(),
+			subdir: self.subdir.as_str(),
+		}
+		.serialize(serializer)
+	}
+}
+
+impl<'de> Deserialize<'de> for GitSource {
+	fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+		#[derive(Deserialize)]
+		struct JsonGit {
+			remote: String,
+			#[serde(default)]
+			subdir: String,
+		}
+		let j = JsonGit::deserialize(deserializer)?;
+
+		let parsed = GitSource::parse(&j.remote)
+			.ok_or_else(|| de::Error::custom(format!("unable to parse git url {:?}", j.remote)))?;
+		let Source::Git(mut gs) = parsed.source else {
+			unreachable!()
+		};
+
+		if !j.subdir.is_empty() {
+			gs.subdir = SubDir::from_str(j.subdir.trim_start_matches('/'))
+				.map_err(|e| de::Error::custom(format!("invalid subdir {:?}: {e}", j.subdir)))?;
+		}
+
+		Ok(gs)
+	}
+}
+
+impl JsonnetFile {
+	pub fn load(path: &Path) -> Result<Self, Error> {
+		let data = std::fs::read(path).map_err(|e| Error::Io(path.to_owned(), e))?;
+		serde_json::from_slice(&data).map_err(Error::Json)
+	}
+}
+
+#[derive(Debug)]
+pub enum Error {
+	Io(std::path::PathBuf, std::io::Error),
+	Json(serde_json::Error),
+}
+impl std::fmt::Display for Error {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		match self {
+			Error::Io(path, e) => write!(f, "{}: {e}", path.display()),
+			Error::Json(e) => write!(f, "{e}"),
+		}
+	}
+}
+impl std::error::Error for Error {}
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	fn host(s: &str) -> Hostname {
+		Hostname::from_str(s).expect("test host")
+	}
+	fn sd(s: &str) -> SubDir {
+		SubDir::from_str(s).expect("test subdir")
+	}
+
+	#[test]
+	fn parse_basic() {
+		let input = r#"{
+			"version": 1,
+			"dependencies": [
+				{
+					"source": {
+						"git": {
+							"remote": "https://github.com/grafana/jsonnet-libs.git",
+							"subdir": "grafana-builder"
+						}
+					},
+					"version": "54865853ebc1f901964e25a2e7a0e4d2cb6b9648",
+					"sum": "ELsYwK+kGdzX1mee2Yy+/b2mdO4Y503BOCDkFzwmGbE="
+				}
+			],
+			"legacyImports": false
+		}"#;
+
+		let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+		assert_eq!(jf.version, 1);
+		assert!(!jf.legacy_imports);
+		assert_eq!(jf.dependencies.len(), 1);
+
+		let dep = &jf.dependencies[0];
+		let Source::Git(git) = &dep.source else {
+			panic!("expected git source");
+		};
+		assert_eq!(git.host, "github.com");
+		assert_eq!(git.repo, "grafana/jsonnet-libs.git");
+		assert_eq!(git.subdir, "grafana-builder");
+		assert_eq!(
+			git.name(),
+			"github.com/grafana/jsonnet-libs/grafana-builder"
+		);
+		assert_eq!(git.legacy_name(), "grafana-builder");
+		assert_eq!(git.remote(), "https://github.com/grafana/jsonnet-libs.git");
+		assert_eq!(
+			dep.version.as_deref(),
+			Some("54865853ebc1f901964e25a2e7a0e4d2cb6b9648")
+		);
+	}
+
+	#[test]
+	fn parse_local_source() {
+		let input = r#"{
+			"version": 1,
+			"dependencies": [
+				{
+					"source": {
+						"local": { "directory": "../shared-lib" }
+					},
+					"version": ""
+				}
+			]
+		}"#;
+
+		let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+		let dep = &jf.dependencies[0];
+		let Source::Local(local) = &dep.source else {
+			panic!("expected local source");
+		};
+		assert_eq!(local.ups, 1);
+		assert_eq!(local.dir, "shared-lib");
+		assert_eq!(local.to_string(), "../shared-lib");
+		assert!(jf.legacy_imports);
+	}
+
+	#[test]
+	fn parse_uri_github_slug() {
+		let dep = GitSource::parse("github.com/ksonnet/ksonnet-lib/ksonnet.beta.3").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.scheme, GitScheme::Https);
+		assert_eq!(gs.host, "github.com");
+		assert_eq!(gs.repo, "ksonnet/ksonnet-lib");
+		assert_eq!(gs.subdir, "ksonnet.beta.3");
+		assert_eq!(dep.version, None);
+		assert_eq!(gs.remote(), "https://github.com/ksonnet/ksonnet-lib");
+	}
+
+	#[test]
+	fn parse_uri_ssh() {
+		let dep = GitSource::parse("ssh://git@example.com/user/repo.git/foobar@v1").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.scheme, GitScheme::Ssh);
+		assert_eq!(gs.host, "example.com");
+		assert_eq!(gs.repo, "user/repo.git");
+		assert_eq!(gs.subdir, "foobar");
+		assert_eq!(dep.version.as_deref(), Some("v1"));
+		assert_eq!(gs.remote(), "ssh://git@example.com/user/repo.git");
+	}
+
+	#[test]
+	fn parse_uri_scp() {
+		let dep = GitSource::parse("git@my.host:user/repo.git/foobar@v1").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.scheme, GitScheme::Ssh);
+		assert_eq!(gs.host, "my.host");
+		assert_eq!(gs.subdir, "foobar");
+		assert_eq!(dep.version.as_deref(), Some("v1"));
+		assert_eq!(gs.remote(), "ssh://git@my.host/user/repo.git");
+	}
+
+	#[test]
+	fn parse_uri_https_explicit() {
+		let dep = GitSource::parse("https://example.com/foo/bar").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.scheme, GitScheme::Https);
+		assert_eq!(gs.host, "example.com");
+		assert_eq!(gs.repo, "foo/bar");
+		assert_eq!(gs.subdir, "");
+		assert_eq!(gs.remote(), "https://example.com/foo/bar");
+	}
+
+	#[test]
+	fn parse_uri_no_scheme() {
+		let dep = GitSource::parse("example.com/foo/bar").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.scheme, GitScheme::Https);
+		assert_eq!(gs.host, "example.com");
+		assert_eq!(gs.remote(), "https://example.com/foo/bar");
+	}
+
+	#[test]
+	fn parse_uri_path_and_version() {
+		let dep = GitSource::parse("example.com/foo/bar/baz@bat").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.repo, "foo/bar");
+		assert_eq!(gs.subdir, "baz");
+		assert_eq!(dep.version.as_deref(), Some("bat"));
+	}
+
+	#[test]
+	fn parse_uri_version_only() {
+		let dep = GitSource::parse("example.com/foo/bar@baz").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.repo, "foo/bar");
+		assert_eq!(gs.subdir, "");
+		assert_eq!(dep.version.as_deref(), Some("baz"));
+	}
+
+	#[test]
+	fn parse_uri_deep_path() {
+		let dep = GitSource::parse("example.com/foo/bar/baz/bat").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.repo, "foo/bar");
+		assert_eq!(gs.subdir, "baz/bat");
+	}
+
+	#[test]
+	fn parse_uri_subgroups() {
+		let dep = GitSource::parse("example.com/group/subgroup/repository.git").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.repo, "group/subgroup/repository.git");
+		assert_eq!(gs.plain_repo_name(), "group/subgroup/repository");
+		assert_eq!(gs.subdir, "");
+		assert_eq!(
+			gs.remote(),
+			"https://example.com/group/subgroup/repository.git"
+		);
+	}
+
+	#[test]
+	fn parse_uri_subgroup_subdir() {
+		let dep = GitSource::parse("example.com/group/subgroup/repository.git/subdir").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.plain_repo_name(), "group/subgroup/repository");
+		assert_eq!(gs.subdir, "subdir");
+	}
+
+	#[test]
+	fn parse_uri_bitbucket_personal() {
+		let dep = GitSource::parse("bitbucket.org/~user/repository.git").unwrap();
+		let Source::Git(gs) = &dep.source else {
+			panic!()
+		};
+		assert_eq!(gs.host, "bitbucket.org");
+		assert_eq!(gs.repo, "~user/repository.git");
+		assert_eq!(gs.remote(), "https://bitbucket.org/~user/repository.git");
+	}
+
+	#[test]
+	fn name_with_subdir() {
+		let gs = GitSource {
+			scheme: GitScheme::Https,
+			host: host("github.com"),
+			repo: sd("ksonnet/ksonnet-lib"),
+			subdir: sd("ksonnet.beta.3"),
+		};
+		assert_eq!(gs.name(), "github.com/ksonnet/ksonnet-lib/ksonnet.beta.3");
+		assert_eq!(gs.legacy_name(), "ksonnet.beta.3");
+	}
+
+	#[test]
+	fn name_without_subdir() {
+		let gs = GitSource {
+			scheme: GitScheme::Https,
+			host: host("github.com"),
+			repo: sd("user/repo"),
+			subdir: SubDir::empty(),
+		};
+		assert_eq!(gs.name(), "github.com/user/repo");
+		assert_eq!(gs.legacy_name(), "repo");
+	}
+
+	#[test]
+	fn defaults() {
+		let input = r#"{ "version": 1 }"#;
+		let jf: JsonnetFile = serde_json::from_str(input).unwrap();
+		assert!(jf.dependencies.is_empty());
+		assert!(jf.legacy_imports);
+	}
+
+	#[test]
+	fn roundtrip() {
+		let jf = JsonnetFile {
+			version: 1,
+			dependencies: vec![Dependency {
+				source: Source::Git(GitSource {
+					scheme: GitScheme::Https,
+					host: host("github.com"),
+					repo: sd("user/repo"),
+					subdir: sd("lib"),
+				}),
+				version: Some("main".into()),
+				sum: None,
+				name: None,
+				single: false,
+			}],
+			legacy_imports: false,
+		};
+		let json = serde_json::to_string_pretty(&jf).unwrap();
+		let parsed: JsonnetFile = serde_json::from_str(&json).unwrap();
+		assert_eq!(parsed.dependencies.len(), 1);
+		let Source::Git(gs) = &parsed.dependencies[0].source else {
+			panic!()
+		};
+		assert_eq!(gs.host, "github.com");
+		assert_eq!(gs.repo, "user/repo");
+		assert_eq!(gs.subdir, "lib");
+	}
+
+	#[test]
+	fn hostname_rejects_slash() {
+		assert!(Hostname::from_str("foo/bar").is_err());
+		assert!(Hostname::from_str("").is_err());
+		assert!(Hostname::from_str(".").is_err());
+		assert!(Hostname::from_str("..").is_err());
+		assert!(Hostname::from_str(".foo").is_err());
+		assert!(Hostname::from_str("foo.").is_err());
+		assert!(Hostname::from_str("foo..bar").is_err());
+		assert!(Hostname::from_str("foo bar").is_err());
+		assert!(Hostname::from_str("foo.bar").is_ok());
+	}
+
+	#[test]
+	fn subdir_rejects_escape() {
+		assert!(SubDir::from_str("../foo").is_err());
+		assert!(SubDir::from_str("/foo").is_err());
+		assert!(SubDir::from_str("foo/../bar").is_err());
+		assert!(SubDir::from_str("foo/bar").is_ok());
+		assert!(SubDir::from_str("").is_ok());
+	}
+
+	#[test]
+	fn local_source_parse() {
+		let l = LocalSource::from_str("../shared-lib").unwrap();
+		assert_eq!(l.ups, 1);
+		assert_eq!(l.dir, "shared-lib");
+
+		let l = LocalSource::from_str("../../foo/bar").unwrap();
+		assert_eq!(l.ups, 2);
+		assert_eq!(l.dir, "foo/bar");
+
+		let l = LocalSource::from_str("./foo").unwrap();
+		assert_eq!(l.ups, 0);
+		assert_eq!(l.dir, "foo");
+
+		let l = LocalSource::from_str(".").unwrap();
+		assert_eq!(l.ups, 0);
+		assert!(l.dir.is_empty());
+
+		let l = LocalSource::from_str("..").unwrap();
+		assert_eq!(l.ups, 1);
+		assert!(l.dir.is_empty());
+
+		// Mid-path `..` is rejected.
+		assert!(LocalSource::from_str("foo/../bar").is_err());
+		// Absolute path is rejected.
+		assert!(LocalSource::from_str("/foo").is_err());
+	}
+
+	#[test]
+	fn local_source_render_roundtrip() {
+		for s in ["../shared-lib", "../../foo/bar", "foo", "."] {
+			assert_eq!(LocalSource::from_str(s).unwrap().to_string(), s);
+		}
+	}
+
+	#[test]
+	fn local_source_resolve_under() {
+		// `../foo` from `pkg/sub` lands at `pkg/foo`.
+		let l = LocalSource::from_str("../foo").unwrap();
+		assert_eq!(l.resolve_under(&sd("pkg/sub")).unwrap(), "pkg/foo");
+
+		// Plain `foo` from `pkg/sub` lands at `pkg/sub/foo`.
+		let l = LocalSource::from_str("foo").unwrap();
+		assert_eq!(l.resolve_under(&sd("pkg/sub")).unwrap(), "pkg/sub/foo");
+
+		// Too many `..` escapes the parent.
+		let l = LocalSource::from_str("../../../foo").unwrap();
+		assert!(l.resolve_under(&sd("pkg")).is_err());
+	}
+}
addedcrates/jrsonnet-pkg/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-pkg/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod install;
+pub mod jsonnet_bundler;