Implement auto-download feature for syncback command

- Enhanced the `syncback` command to automatically download Roblox place files from the asset delivery API, allowing for zero-argument usage.
- Updated `Cargo.toml` to include compression features for `reqwest` and moved `tempfile` to dependencies.
- Introduced a new `download` argument in `SyncbackCommand` to specify place IDs for downloading.
- Implemented logic to resolve input file paths, prioritizing downloads when necessary.
- Added sourcemap generation after successful syncback and refreshed the git index if in a repository.
- Updated tests to validate the new download functionality and sourcemap creation.
This commit is contained in:
Joe 2026-02-01 09:46:34 -06:00
parent 8fe908740f
commit 2b2ada219e
32 changed files with 1180 additions and 125 deletions

View file

@ -5,7 +5,7 @@ todos: []
isProject: false isProject: false
--- ---
# Destructive Syncback Mode # Destructive Syncback Mode (Clean by Default)
## Root Cause Analysis ## Root Cause Analysis
@ -50,41 +50,46 @@ if let Some(old_child) = old_child_map.remove(new_child.name.as_str()) {
} }
``` ```
## Solution: Add `--clean` Flag ## Solution: Clean Mode by Default with `--incremental` Flag
Add a `--clean` flag that makes syncback treat all instances as "new" by setting `old` references to `None`, ensuring consistent output regardless of existing filesystem state. Make clean (destructive) mode the default behavior, with an `--incremental` flag to opt into the legacy structure-preserving behavior.
### Ensuring No Residue Files
The key challenge: if we set `old = None`, the `removed_children` logic won't work (nothing to compare against). We need a two-phase approach:
1. **Phase 1**: Collect ALL existing paths from the old tree before syncback
2. **Phase 2**: After computing new paths, remove any old paths that aren't in the new set
This guarantees no orphaned files remain.
### Changes Required ### Changes Required
#### 1. Add CLI Flag (`[src/cli/syncback.rs](src/cli/syncback.rs)`) #### 1. Add CLI Flag (`[src/cli/syncback.rs](src/cli/syncback.rs)`)
Add `--clean` flag to the command definition: Add `--incremental` flag (clean mode is now default):
```rust ```rust
/// If provided, syncback will ignore existing file structure and create /// If provided, syncback will preserve existing file structure and middleware
/// a fresh project layout. This ensures consistent output regardless of /// formats when possible. Without this flag, syncback creates a fresh project
/// existing filesystem state. /// layout that exactly matches the input file.
#[clap(long)] #[clap(long, short = 'n')]
pub clean: bool, pub incremental: bool,
``` ```
#### 2. Pass Flag to `syncback_loop` (`[src/cli/syncback.rs](src/cli/syncback.rs)`) #### 2. Pass Flag to `syncback_loop` (`[src/cli/syncback.rs](src/cli/syncback.rs)`)
Modify the `syncback_loop` call to pass the clean flag:
```rust ```rust
let snapshot = syncback_loop( let snapshot = syncback_loop(
session_old.vfs(), session_old.vfs(),
&mut dom_old, &mut dom_old,
dom_new, dom_new,
session_old.root_project(), session_old.root_project(),
self.clean, // New parameter self.incremental, // false = clean mode (default)
)?; )?;
``` ```
#### 3. Modify `syncback_loop` (`[src/syncback/mod.rs](src/syncback/mod.rs)`) #### 3. Modify `syncback_loop` Signature (`[src/syncback/mod.rs](src/syncback/mod.rs)`)
Update function signature and initial snapshot:
```rust ```rust
pub fn syncback_loop( pub fn syncback_loop(
@ -92,67 +97,120 @@ pub fn syncback_loop(
old_tree: &mut RojoTree, old_tree: &mut RojoTree,
mut new_tree: WeakDom, mut new_tree: WeakDom,
project: &Project, project: &Project,
clean_mode: bool, // New parameter incremental: bool, // false = clean mode (default)
) -> anyhow::Result<FsSnapshot> ) -> anyhow::Result<FsSnapshot>
``` ```
When `clean_mode` is true, set the initial `old` reference to `None`: #### 4. Collect Existing Paths Before Syncback (`[src/syncback/mod.rs](src/syncback/mod.rs)`)
Before the main loop, collect all existing filesystem paths (respecting ignore patterns):
```rust
// Collect all existing paths from old tree for cleanup in clean mode
let existing_paths: HashSet<PathBuf> = if !incremental {
let mut paths = HashSet::new();
for ref_id in descendants(old_tree.inner(), old_tree.get_root_id()) {
if let Some(inst) = old_tree.get_instance(ref_id) {
for path in inst.metadata().relevant_paths.iter() {
// Skip paths matching ignore patterns
if is_valid_path(&ignore_patterns, project_path, path) {
paths.insert(path.clone());
}
}
}
}
paths
} else {
HashSet::new()
};
```
#### 5. Set `old = None` in Clean Mode (`[src/syncback/mod.rs](src/syncback/mod.rs)`)
```rust ```rust
let mut snapshots = vec![SyncbackSnapshot { let mut snapshots = vec![SyncbackSnapshot {
data: syncback_data, data: syncback_data,
old: if clean_mode { None } else { Some(old_tree.get_root_id()) }, old: if incremental { Some(old_tree.get_root_id()) } else { None },
new: new_tree.root_ref(), new: new_tree.root_ref(),
path: project.file_location.clone(), path: project.file_location.clone(),
middleware: Some(Middleware::Project), middleware: Some(Middleware::Project),
}]; }];
``` ```
#### 4. Track Removed Files in Clean Mode (`[src/syncback/mod.rs](src/syncback/mod.rs)`) #### 6. Remove Orphaned Files After Syncback (`[src/syncback/mod.rs](src/syncback/mod.rs)`)
In clean mode, we need to remove all existing files in the target paths. Add logic to collect existing paths for removal: After the main loop, remove any existing paths that weren't written to:
```rust ```rust
if clean_mode { // In clean mode, remove any existing paths that weren't overwritten
// Collect all existing paths under the project tree for removal if !incremental {
for ref_id in descendants(old_tree.inner(), old_tree.get_root_id()) { let added_paths: HashSet<&Path> = fs_snapshot.added_paths().iter().map(|p| p.as_path()).collect();
if let Some(inst) = old_tree.get_instance(ref_id) {
for path in inst.metadata().relevant_paths.iter() { for old_path in &existing_paths {
if path.is_dir() { // Skip if this path (or a parent) is being written to
fs_snapshot.remove_dir(path); if added_paths.contains(old_path.as_path()) {
} else { continue;
fs_snapshot.remove_file(path); }
} // Skip if a parent directory is being added (the new structure will replace it)
} if added_paths.iter().any(|p| old_path.starts_with(p)) {
continue;
}
if old_path.is_dir() {
fs_snapshot.remove_dir(old_path);
} else {
fs_snapshot.remove_file(old_path);
} }
} }
} }
``` ```
#### 5. Update Child Snapshot Creation (`[src/snapshot_middleware/dir.rs](src/snapshot_middleware/dir.rs)`) #### 7. Skip Hash Comparison in Clean Mode (`[src/syncback/mod.rs](src/syncback/mod.rs)`)
The `SyncbackSnapshot::with_joined_path` method already handles `old = None` correctly - it will create a fresh child snapshot without old references. The hash-based skipping should be disabled in clean mode since we want to rewrite everything:
```rust
// Skip hash comparison in clean mode - we want to rewrite everything
if incremental {
if let Some(old_ref) = snapshot.old {
match (old_hashes.get(&old_ref), new_hashes.get(&snapshot.new)) {
(Some(old), Some(new)) => {
if old == new {
log::trace!(
"Skipping {inst_path} due to it being identically hashed as {old:?}"
);
continue;
}
}
_ => unreachable!("All Instances in both DOMs should have hashes"),
}
}
}
```
### Data Flow Diagram ### Data Flow Diagram
```mermaid ```mermaid
flowchart TD flowchart TD
subgraph currentBehavior [Current Behavior] subgraph cleanMode [Default: Clean Mode]
A1[Syncback on Existing Files] --> B1{Old Instance Exists?} A1[rojo syncback -i game.rbxl] --> B1[Collect existing paths]
B1 -->|Yes| C1[Preserve Filename] B1 --> C1[Set old = None for all snapshots]
B1 -->|Yes| D1[Preserve Middleware] C1 --> D1[Generate fresh filenames]
B1 -->|Yes| E1[Match Children by Name] C1 --> E1[Choose best middleware]
C1 --> F1[Inconsistent Structure] C1 --> F1[All children treated as new]
D1 --> F1 D1 --> G1[Compute new paths]
E1 --> F1 E1 --> G1
F1 --> G1
G1 --> H1[Remove orphaned files]
H1 --> I1[Consistent 1:1 Structure]
end end
subgraph cleanMode [Clean Mode] subgraph incrementalMode [With --incremental Flag]
A2[Syncback with --clean] --> B2[Set old = None] A2[rojo syncback -i game.rbxl --incremental] --> B2{Old Instance Exists?}
B2 --> C2[Generate Fresh Filename] B2 -->|Yes| C2[Preserve Filename]
B2 --> D2[Choose Best Middleware] B2 -->|Yes| D2[Preserve Middleware]
B2 --> E2[All Children Are New] B2 -->|Yes| E2[Match Children by Name]
C2 --> F2[Consistent 1:1 Structure] C2 --> F2[Legacy Behavior]
D2 --> F2 D2 --> F2
E2 --> F2 E2 --> F2
end end
@ -163,16 +221,30 @@ flowchart TD
### Usage ### Usage
```bash ```bash
# Current behavior (preserves existing structure) # Default: Clean mode (creates fresh, consistent layout)
rojo syncback -i game.rbxl rojo syncback -i game.rbxl
# Destructive mode (ignores existing structure, creates fresh layout) # Preview what clean mode would do
rojo syncback -i game.rbxl --clean rojo syncback -i game.rbxl --list --dry-run
# Combine with other flags # Legacy behavior (preserves existing structure)
rojo syncback -i game.rbxl --clean --list --dry-run rojo syncback -i game.rbxl --incremental
rojo syncback -i game.rbxl -n
# Combine flags
rojo syncback -i game.rbxl --incremental --list
``` ```
### Alternative Consideration ### Project-Level Configuration (Optional Enhancement)
If you also want to support project-level configuration (so you don't need to pass `--clean` every time), we could add a `syncbackRules.cleanMode: true` option in the project file. Let me know if you'd prefer that approach. If you want to make incremental mode the default for a specific project without passing the flag, we could also add:
```json5
{
"syncbackRules": {
"incremental": true // Opt into legacy behavior per-project
}
}
```
This would be read in `syncback_loop` and combined with the CLI flag (CLI takes precedence).

View file

@ -0,0 +1,276 @@
---
name: Syncback Auto-Downloader
overview: Add an auto-download feature to the syncback command that fetches the .rbxl file from Roblox's asset delivery API, with sensible defaults to make `rojo syncback` work with zero arguments.
todos:
- id: update-cargo
content: Add gzip/brotli/deflate features to reqwest, move tempfile to dependencies
status: completed
- id: download-function
content: Implement download_place() function with no-cache headers and cookie auth
status: completed
- id: update-args
content: "Update SyncbackCommand args: --download, new defaults, --interactive flip"
status: completed
- id: input-resolution
content: Implement input file resolution logic (download vs existing file)
status: completed
- id: sourcemap-gen
content: Add automatic sourcemap.json generation after syncback
status: completed
- id: git-refresh
content: Add git update-index --refresh for git repos
status: completed
- id: testing
content: Test the complete flow with various argument combinations
status: completed
isProject: false
---
# Syncback Auto-Downloader Feature
## Overview
Enhance the `rojo syncback` command to automatically download place files from Roblox's asset delivery API, generate sourcemaps after completion, and refresh the git index. The goal is to make `rojo syncback` work with zero arguments.
## Key Files to Modify
- `[src/cli/syncback.rs](src/cli/syncback.rs)` - Main syncback command implementation
- `[Cargo.toml](Cargo.toml)` - Add compression features to reqwest, move tempfile to dependencies
## Implementation Details
### 1. Update Dependencies in `Cargo.toml`
```toml
# Add compression features to reqwest
reqwest = { version = "0.11.27", default-features = false, features = [
"blocking",
"json",
"rustls-tls",
"gzip",
"brotli",
"deflate",
] }
# Move tempfile from dev-dependencies to dependencies
tempfile = "3.24.0"
```
### 2. Add Download Arguments to `SyncbackCommand`
In `src/cli/syncback.rs`, modify the struct:
```rust
pub struct SyncbackCommand {
/// Path to the project to sync back to.
#[clap(default_value = "default.project.json5")]
pub project: PathBuf,
/// Path to the Roblox file to pull Instances from.
#[clap(long, short, default_value = "Project.rbxl")]
pub input: PathBuf,
/// Download the place file from Roblox. If no place ID is provided,
/// uses the first ID from servePlaceIds in the project file.
#[clap(long, short = 'd')]
pub download: Option<Option<u64>>,
/// Interactive mode - prompts before writing. Disabled by default.
#[clap(long, short = 'i')]
pub interactive: bool,
// ... rest of existing fields
}
```
Note: Flip `--non-interactive` to `--interactive` (new default is non-interactive).
### 3. Download Function Implementation
Create the download logic with these characteristics:
- **URL**: `https://assetdelivery.roblox.com/v1/asset/?id={PLACEID}`
- **Auth**: `ROBLOSECURITY` cookie from environment variable
- **No-cache headers**:
- `Cache-Control: no-cache, no-store, must-revalidate`
- `Pragma: no-cache`
- `Expires: 0`
- **Compression**: Request `Accept-Encoding: gzip, deflate, br` (reqwest handles decompression)
- **Temp file**: Use `tempfile::NamedTempFile` with `.rbxl` extension, keep it alive during processing
```rust
fn download_place(place_id: u64) -> anyhow::Result<tempfile::NamedTempFile> {
let cookie = std::env::var("ROBLOSECURITY")
.context("ROBLOSECURITY environment variable not set")?;
let url = format!("https://assetdelivery.roblox.com/v1/asset/?id={}", place_id);
let client = reqwest::blocking::Client::builder()
.gzip(true)
.brotli(true)
.deflate(true)
.no_proxy() // Avoid proxy caching
.build()?;
let response = client
.get(&url)
.header(COOKIE, format!(".ROBLOSECURITY={}", cookie))
.header(CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.header(PRAGMA, "no-cache")
.header("Expires", "0")
.header(USER_AGENT, "Rojo")
.send()?;
// Create temp file with .rbxl extension
let mut temp_file = tempfile::Builder::new()
.suffix(".rbxl")
.tempfile()?;
// Stream response to file
let bytes = response.bytes()?;
std::io::copy(&mut bytes.as_ref(), &mut temp_file)?;
Ok(temp_file)
}
```
### 4. Resolve Place ID Logic
When `--download` is provided without a value:
1. Load the project file first
2. Extract `serve_place_ids` from the project
3. Take the first place ID from the set (convert HashSet to Vec, sort for determinism, take first)
4. Error if `servePlaceIds` is empty or not set
### 5. Input File Resolution Logic
In the `run()` method:
```rust
// Determine the actual input path
let input_path = if let Some(download_opt) = &self.download {
// --download was specified
if !self.input_exists() || download_opt.is_some() {
// Download needed: either file doesn't exist or explicit place ID
let place_id = match download_opt {
Some(id) => *id,
None => {
// Load project to get servePlaceIds
let project = Project::load_fuzzy(&path_old)?;
project.serve_place_ids
.as_ref()
.and_then(|ids| ids.iter().min().copied())
.context("No servePlaceIds in project and no place ID provided")?
}
};
eprintln!("Downloading place {}...", place_id);
let temp_file = download_place(place_id)?;
// Keep temp_file alive during processing
DownloadedInput::Temp(temp_file)
} else {
DownloadedInput::Existing(resolve_path(&self.input).into_owned())
}
} else if self.input.exists() {
DownloadedInput::Existing(resolve_path(&self.input).into_owned())
} else {
anyhow::bail!("Input file {} not found. Use --download to fetch from Roblox.", self.input.display())
};
```
### 6. Sourcemap Generation After Syncback
After successful syncback, generate `sourcemap.json` in the project root:
```rust
// After snapshot.write_to_vfs()
let sourcemap_path = session_old.root_project().folder_location().join("sourcemap.json");
write_sourcemap(&session_old, Some(&sourcemap_path), filter_nothing, false)?;
eprintln!("Generated sourcemap at {}", sourcemap_path.display());
```
This requires extracting or sharing the `write_sourcemap` function from `sourcemap.rs` (likely needs to be made `pub(crate)`).
### 7. Git Index Refresh
After syncback completes, check if in a git repo and refresh the index:
```rust
fn refresh_git_index(project_dir: &Path) {
// Check if .git exists in project dir or any parent
let mut check_dir = Some(project_dir);
let mut is_git_repo = false;
while let Some(dir) = check_dir {
if dir.join(".git").exists() {
is_git_repo = true;
break;
}
check_dir = dir.parent();
}
if is_git_repo {
let result = std::process::Command::new("git")
.arg("update-index")
.arg("--refresh")
.current_dir(project_dir)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status();
if let Err(e) = result {
log::warn!("Failed to refresh git index: {}", e);
}
}
}
```
### 8. Summary of Default Changes
| Argument | Old Default | New Default |
| ------------------- | ------------------ | ----------------------------------------------------------------- |
| `project` | `""` (current dir) | `default.project.json5` |
| `--input` | Required | `Project.rbxl` |
| `--non-interactive` | `false` | Removed, replaced with `--interactive` (default: non-interactive) |
## Flow Diagram
```mermaid
flowchart TD
Start[rojo syncback] --> CheckDownload{--download flag?}
CheckDownload -->|No| CheckInput{Input file exists?}
CheckDownload -->|Yes| CheckPlaceId{Place ID provided?}
CheckPlaceId -->|Yes| Download[Download from Roblox]
CheckPlaceId -->|No| LoadProject[Load project.json5]
LoadProject --> GetPlaceId[Get first servePlaceIds]
GetPlaceId --> Download
Download --> TempFile[Save to temp file]
TempFile --> ProcessDom[Process DOM]
CheckInput -->|Yes| UseExisting[Use existing file]
CheckInput -->|No| Error[Error: file not found]
UseExisting --> ProcessDom
ProcessDom --> Syncback[Run syncback]
Syncback --> WriteFiles[Write to filesystem]
WriteFiles --> GenSourcemap[Generate sourcemap.json]
GenSourcemap --> GitRefresh[git update-index --refresh]
GitRefresh --> Cleanup[Cleanup temp file]
Cleanup --> End[Done]
```
## Edge Cases
- If `--input` file exists and `--download` not specified: use existing file (no download)
- If `--input` file exists but `--download <placeid>` explicitly given: download anyway (override)
- If `--download` with no arg and no `servePlaceIds` in project: error with helpful message
- If `ROBLOSECURITY` not in environment: error with helpful message
- If download fails: error with status code and helpful message
- If not a git repo: silently skip git refresh

556
Cargo.lock generated
View file

@ -39,12 +39,36 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]] [[package]]
name = "anes" name = "anes"
version = "0.1.6" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.21" version = "0.6.21"
@ -113,12 +137,30 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-compression"
version = "0.4.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40"
dependencies = [
"compression-codecs",
"compression-core",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -151,6 +193,18 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "base-x"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.7" version = "0.21.7"
@ -184,6 +238,17 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec 0.5.2",
"constant_time_eq 0.1.5",
]
[[package]] [[package]]
name = "blake3" name = "blake3"
version = "1.8.3" version = "1.8.3"
@ -191,10 +256,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"arrayvec", "arrayvec 0.7.6",
"cc", "cc",
"cfg-if 1.0.4", "cfg-if 1.0.4",
"constant_time_eq", "constant_time_eq 0.4.2",
"cpufeatures", "cpufeatures",
] ]
@ -207,6 +272,27 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "brotli"
version = "8.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.12.1" version = "1.12.1"
@ -224,6 +310,12 @@ version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "byteorder"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
@ -299,9 +391,13 @@ version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [ dependencies = [
"ansi_term",
"atty",
"bitflags 1.3.2", "bitflags 1.3.2",
"strsim 0.8.0",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
"vec_map",
] ]
[[package]] [[package]]
@ -323,7 +419,7 @@ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim 0.11.1",
] ]
[[package]] [[package]]
@ -335,7 +431,7 @@ dependencies = [
"heck", "heck",
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -350,6 +446,24 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "compression-codecs"
version = "0.4.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a"
dependencies = [
"brotli",
"compression-core",
"flate2",
"memchr",
]
[[package]]
name = "compression-core"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d"
[[package]] [[package]]
name = "console" name = "console"
version = "0.15.11" version = "0.15.11"
@ -362,12 +476,34 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "const_fn"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.4.2" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "cookie"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6e25dfc584d06a3dbf775d207ff00d7de98d824c952dd2233dfbb261889a42"
dependencies = [
"time 0.2.27",
"version_check",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -393,6 +529,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if 1.0.4",
]
[[package]] [[package]]
name = "criterion" name = "criterion"
version = "0.3.6" version = "0.3.6"
@ -542,6 +687,15 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "deranged"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@ -558,6 +712,17 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users 0.3.5",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "2.0.2" version = "2.0.2"
@ -584,7 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [ dependencies = [
"libc", "libc",
"redox_users", "redox_users 0.4.6",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -596,10 +761,16 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users", "redox_users 0.4.6",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.5"
@ -608,7 +779,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -624,7 +795,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e" checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e"
dependencies = [ dependencies = [
"cc", "cc",
"rustc_version", "rustc_version 0.4.1",
"toml 0.5.11", "toml 0.5.11",
"vswhom", "vswhom",
"winreg 0.10.1", "winreg 0.10.1",
@ -655,6 +826,19 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.11.8" version = "0.11.8"
@ -707,6 +891,16 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "float-cmp" name = "float-cmp"
version = "0.9.0" version = "0.9.0"
@ -837,7 +1031,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -895,6 +1089,17 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.4",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.17" version = "0.2.17"
@ -903,7 +1108,7 @@ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [ dependencies = [
"cfg-if 1.0.4", "cfg-if 1.0.4",
"libc", "libc",
"wasi", "wasi 0.11.1+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -1348,7 +1553,7 @@ checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -1527,6 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [ dependencies = [
"adler2", "adler2",
"simd-adler32",
] ]
[[package]] [[package]]
@ -1555,7 +1761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
@ -1621,6 +1827,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -1748,7 +1960,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -1779,6 +1991,19 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "plist"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
dependencies = [
"base64 0.22.1",
"indexmap 2.13.0",
"quick-xml",
"serde",
"time 0.3.46",
]
[[package]] [[package]]
name = "plotters" name = "plotters"
version = "0.3.7" version = "0.3.7"
@ -1831,6 +2056,12 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@ -1897,7 +2128,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [ dependencies = [
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
]
[[package]]
name = "quick-xml"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
] ]
[[package]] [[package]]
@ -1980,7 +2220,7 @@ version = "2.0.1"
dependencies = [ dependencies = [
"ahash", "ahash",
"criterion 0.5.1", "criterion 0.5.1",
"env_logger", "env_logger 0.11.8",
"heck", "heck",
"insta", "insta",
"log", "log",
@ -1994,6 +2234,23 @@ dependencies = [
"zstd", "zstd",
] ]
[[package]]
name = "rbx_cookie"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62d81c3d7cd53ac75f48a7b526e145d0b72c19d695364edb343e9248192d3f17"
dependencies = [
"byteorder 0.5.3",
"clap 2.34.0",
"cookie",
"dirs 1.0.5",
"env_logger 0.9.3",
"log",
"plist",
"winapi 0.3.9",
"winreg 0.10.1",
]
[[package]] [[package]]
name = "rbx_dom_weak" name = "rbx_dom_weak"
version = "4.1.0" version = "4.1.0"
@ -2047,7 +2304,7 @@ version = "2.0.1"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.22.1", "base64 0.22.1",
"env_logger", "env_logger 0.11.8",
"heck", "heck",
"insta", "insta",
"log", "log",
@ -2057,6 +2314,12 @@ dependencies = [
"xml-rs", "xml-rs",
] ]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.18" version = "0.5.18"
@ -2075,6 +2338,17 @@ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
] ]
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.16",
"redox_syscall 0.1.57",
"rust-argon2",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.6" version = "0.4.6"
@ -2121,6 +2395,7 @@ version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [ dependencies = [
"async-compression",
"base64 0.21.7", "base64 0.21.7",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
@ -2147,6 +2422,7 @@ dependencies = [
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tokio-util",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
@ -2238,7 +2514,7 @@ dependencies = [
"csv", "csv",
"data-encoding", "data-encoding",
"embed-resource", "embed-resource",
"env_logger", "env_logger 0.11.8",
"float-cmp", "float-cmp",
"fs-err", "fs-err",
"futures", "futures",
@ -2260,6 +2536,7 @@ dependencies = [
"profiling", "profiling",
"rayon", "rayon",
"rbx_binary", "rbx_binary",
"rbx_cookie",
"rbx_dom_weak", "rbx_dom_weak",
"rbx_reflection", "rbx_reflection",
"rbx_reflection_database", "rbx_reflection_database",
@ -2270,7 +2547,7 @@ dependencies = [
"rmp-serde", "rmp-serde",
"roblox_install", "roblox_install",
"rojo-insta-ext", "rojo-insta-ext",
"semver", "semver 1.0.27",
"serde", "serde",
"serde_bytes", "serde_bytes",
"serde_json", "serde_json",
@ -2295,19 +2572,40 @@ dependencies = [
"serde_yaml", "serde_yaml",
] ]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64 0.13.1",
"blake2b_simd",
"constant_time_eq 0.1.5",
"crossbeam-utils",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.27" version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver 0.9.0",
]
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [ dependencies = [
"semver", "semver 1.0.27",
] ]
[[package]] [[package]]
@ -2397,12 +2695,27 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.27" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@ -2450,7 +2763,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -2499,6 +2812,15 @@ dependencies = [
"yaml-rust", "yaml-rust",
] ]
[[package]]
name = "sha1"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
dependencies = [
"sha1_smol",
]
[[package]] [[package]]
name = "sha1" name = "sha1"
version = "0.10.6" version = "0.10.6"
@ -2510,6 +2832,12 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "sha1_smol"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.9" version = "0.10.9"
@ -2536,6 +2864,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]] [[package]]
name = "similar" name = "similar"
version = "2.7.0" version = "2.7.0"
@ -2589,6 +2923,70 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "standback"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
dependencies = [
"version_check",
]
[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
dependencies = [
"discard",
"rustc_version 0.2.3",
"stdweb-derive",
"stdweb-internal-macros",
"stdweb-internal-runtime",
"wasm-bindgen",
]
[[package]]
name = "stdweb-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
dependencies = [
"proc-macro2 1.0.106",
"quote 1.0.44",
"serde",
"serde_derive",
"syn 1.0.109",
]
[[package]]
name = "stdweb-internal-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
dependencies = [
"base-x",
"proc-macro2 1.0.106",
"quote 1.0.44",
"serde",
"serde_derive",
"serde_json",
"sha1 0.6.1",
"syn 1.0.109",
]
[[package]]
name = "stdweb-internal-runtime"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -2613,7 +3011,18 @@ dependencies = [
"heck", "heck",
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2 1.0.106",
"quote 1.0.44",
"unicode-ident",
] ]
[[package]] [[package]]
@ -2641,7 +3050,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -2722,7 +3131,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -2733,7 +3142,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -2745,6 +3154,75 @@ dependencies = [
"cfg-if 1.0.4", "cfg-if 1.0.4",
] ]
[[package]]
name = "time"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
dependencies = [
"const_fn",
"libc",
"standback",
"stdweb",
"time-macros 0.1.1",
"version_check",
"winapi 0.3.9",
]
[[package]]
name = "time"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros 0.2.26",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
dependencies = [
"proc-macro-hack",
"time-macros-impl",
]
[[package]]
name = "time-macros"
version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "time-macros-impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.106",
"quote 1.0.44",
"standback",
"syn 1.0.109",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.2" version = "0.8.2"
@ -2788,7 +3266,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -2964,14 +3442,14 @@ version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
dependencies = [ dependencies = [
"byteorder", "byteorder 1.5.0",
"bytes", "bytes",
"data-encoding", "data-encoding",
"http", "http",
"httparse", "httparse",
"log", "log",
"rand", "rand",
"sha1", "sha1 0.10.6",
"thiserror 1.0.69", "thiserror 1.0.69",
"url", "url",
"utf-8", "utf-8",
@ -3038,7 +3516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208" checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208"
dependencies = [ dependencies = [
"ahash", "ahash",
"byteorder", "byteorder 1.5.0",
"lazy_static", "lazy_static",
"parking_lot", "parking_lot",
"serde", "serde",
@ -3080,6 +3558,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -3125,6 +3609,12 @@ dependencies = [
"try-lock", "try-lock",
] ]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.1+wasi-snapshot-preview1" version = "0.11.1+wasi-snapshot-preview1"
@ -3186,7 +3676,7 @@ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3614,7 +4104,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
"synstructure", "synstructure",
] ]
@ -3635,7 +4125,7 @@ checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]
@ -3655,7 +4145,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
"synstructure", "synstructure",
] ]
@ -3689,7 +4179,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [ dependencies = [
"proc-macro2 1.0.106", "proc-macro2 1.0.106",
"quote 1.0.44", "quote 1.0.44",
"syn", "syn 2.0.114",
] ]
[[package]] [[package]]

View file

@ -72,11 +72,16 @@ log = "0.4.29"
num_cpus = "1.17.0" num_cpus = "1.17.0"
opener = "0.5.2" opener = "0.5.2"
rayon = "1.11.0" rayon = "1.11.0"
rbx_cookie = "0.1.5"
reqwest = { version = "0.11.27", default-features = false, features = [ reqwest = { version = "0.11.27", default-features = false, features = [
"blocking", "blocking",
"json", "json",
"rustls-tls", "rustls-tls",
"gzip",
"brotli",
"deflate",
] } ] }
tempfile = "3.24.0"
ritz = "0.1.0" ritz = "0.1.0"
roblox_install = "1.0.0" roblox_install = "1.0.0"
serde = { version = "1.0.228", features = ["derive", "rc"] } serde = { version = "1.0.228", features = ["derive", "rc"] }
@ -121,5 +126,4 @@ paste = "1.0.15"
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
regex = "1" regex = "1"
serde_yaml = "0.8.26" serde_yaml = "0.8.26"
tempfile = "3.24.0"
walkdir = "2.5.0" walkdir = "2.5.0"

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing OnlyOneCopy/child_of_one.luau Writing OnlyOneCopy/child_of_one.luau
Writing ReplicatedStorage/child_replicated_storage.luau Writing ReplicatedStorage/child_replicated_storage.luau
Created sourcemap at <TEMP>/child_but_not\sourcemap.json

View file

@ -1,7 +1,8 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/csv.csv Writing src/csv.csv
Writing src/csv_init/init.csv Writing src/csv_init/init.csv
Writing src/csv_init Writing src/csv_init
Created sourcemap at <TEMP>/csv\sourcemap.json

View file

@ -1,5 +1,6 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing container.model.json5 Writing container.model.json5
Created sourcemap at <TEMP>/duplicate_rojo_id\sourcemap.json

View file

@ -1,8 +1,9 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/ignored_part.model.json5 Writing src/ignored_part.model.json5
Writing src/int_value.model.json5 Writing src/int_value.model.json5
Writing src/subfolder/string_value.txt Writing src/subfolder/string_value.txt
Writing src/subfolder Writing src/subfolder
Created sourcemap at <TEMP>/ignore_paths_adding\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/non-init.luau Writing src/non-init.luau
Writing src/init-file Writing src/init-file
Created sourcemap at <TEMP>/ignore_paths_init\sourcemap.json

View file

@ -1,5 +1,6 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/Message.model.json5 Writing src/Message.model.json5
Created sourcemap at <TEMP>/ignore_paths_removing\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/IncludeMe/.gitkeep Writing src/IncludeMe/.gitkeep
Writing src/IncludeMe Writing src/IncludeMe
Created sourcemap at <TEMP>/ignore_trees_adding\sourcemap.json

View file

@ -3,3 +3,4 @@ source: tests/rojo_test/syncback_util.rs
expression: normalized_stdout expression: normalized_stdout
--- ---
Removing src Removing src
Created sourcemap at <TEMP>/ignore_trees_removing\sourcemap.json

View file

@ -1,8 +1,9 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/dir_with_meta/init.meta.json5 Writing src/dir_with_meta/init.meta.json5
Writing src/model_json.model.json5 Writing src/model_json.model.json5
Writing src/project_json.project.json5 Writing src/project_json.project.json5
Writing src/dir_with_meta Writing src/dir_with_meta
Created sourcemap at <TEMP>/json_middlewares\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing nested.project.json5 Writing nested.project.json5
Writing string_value.txt Writing string_value.txt
Created sourcemap at <TEMP>/nested_projects\sourcemap.json

View file

@ -6,3 +6,4 @@ Writing src/modules/ClientModule.luau
Writing src/modules/ServerModule.luau Writing src/modules/ServerModule.luau
Removing client-only.project.json5 Removing client-only.project.json5
Removing server-only.project.json5 Removing server-only.project.json5
Created sourcemap at <TEMP>/nested_projects_weird\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/init.luau Writing src/init.luau
Writing src Writing src
Created sourcemap at <TEMP>/project_init\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing attribute_mismatch.luau Writing attribute_mismatch.luau
Writing property_mismatch.project.json5 Writing property_mismatch.project.json5
Created sourcemap at <TEMP>/project_reserialize\sourcemap.json

View file

@ -1,8 +1,9 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/ChildWithoutDuplicates/Child/.gitkeep Writing src/ChildWithoutDuplicates/Child/.gitkeep
Writing src/ChildWithDuplicates Writing src/ChildWithDuplicates
Writing src/ChildWithoutDuplicates Writing src/ChildWithoutDuplicates
Writing src/ChildWithoutDuplicates/Child Writing src/ChildWithoutDuplicates/Child
Created sourcemap at <TEMP>/rbxm_fallback\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/pointer.model.json5 Writing src/pointer.model.json5
Writing src/target.model.json5 Writing src/target.model.json5
Created sourcemap at <TEMP>/ref_properties\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/pointer.model.json5 Writing src/pointer.model.json5
Writing src/target.txt Writing src/target.txt
Created sourcemap at <TEMP>/ref_properties_blank\sourcemap.json

View file

@ -1,8 +1,9 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/Pointer_1.model.json5 Writing src/Pointer_1.model.json5
Writing src/Pointer_2.model.json5 Writing src/Pointer_2.model.json5
Writing src/Target_1.model.json5 Writing src/Target_1.model.json5
Writing src/Target_2.model.json5 Writing src/Target_2.model.json5
Created sourcemap at <TEMP>/ref_properties_conflict\sourcemap.json

View file

@ -7,3 +7,4 @@ Writing src/Pointer_2.model.json5
Writing src/Target/init.meta.json5 Writing src/Target/init.meta.json5
Writing src/Target Writing src/Target
Removing src/Target.model.json5 Removing src/Target.model.json5
Created sourcemap at <TEMP>/ref_properties_duplicate\sourcemap.json

View file

@ -1,7 +1,8 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/Pointer1.model.json5 Writing src/Pointer1.model.json5
Writing src/Pointer2.model.json5 Writing src/Pointer2.model.json5
Writing src/Pointer3.model.json5 Writing src/Pointer3.model.json5
Created sourcemap at <TEMP>/ref_properties_pruned\sourcemap.json

View file

@ -1,8 +1,9 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing default.project.json5 Writing default.project.json5
Writing src/model_json.model.json5 Writing src/model_json.model.json5
Writing src/rbxm.rbxm Writing src/rbxm.rbxm
Writing src/rbxmx.rbxmx Writing src/rbxmx.rbxmx
Created sourcemap at <TEMP>/respect_old_middleware\sourcemap.json

View file

@ -1,6 +1,6 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing default.project.json5 Writing default.project.json5
Writing src/adjacent.luau Writing src/adjacent.luau
@ -8,3 +8,4 @@ Writing src/adjacent.meta.json5
Writing src/init/init.meta.json5 Writing src/init/init.meta.json5
Writing src/model.model.json5 Writing src/model.model.json5
Writing src/init Writing src/init
Created sourcemap at <TEMP>/schema_roundtrip\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing default.project.json5 Writing default.project.json5
Writing string_value.txt Writing string_value.txt
Created sourcemap at <TEMP>/string_value_project\sourcemap.json

View file

@ -1,6 +1,7 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing src/module.modulescript Writing src/module.modulescript
Writing src/text.text Writing src/text.text
Created sourcemap at <TEMP>/sync_rules\sourcemap.json

View file

@ -1,5 +1,6 @@
--- ---
source: tests/rojo_test/syncback_util.rs source: tests/rojo_test/syncback_util.rs
expression: "String::from_utf8_lossy(&output.stdout)" expression: normalized_stdout
--- ---
Writing default.project.json5 Writing default.project.json5
Created sourcemap at <TEMP>/unscriptable_properties\sourcemap.json

View file

@ -115,7 +115,7 @@ impl SourcemapCommand {
} }
} }
fn filter_nothing(_instance: &InstanceWithMeta) -> bool { pub(crate) fn filter_nothing(_instance: &InstanceWithMeta) -> bool {
true true
} }
@ -229,7 +229,7 @@ fn recurse_create_node<'a>(
}) })
} }
fn write_sourcemap( pub(crate) fn write_sourcemap(
session: &ServeSession, session: &ServeSession,
output: Option<&Path>, output: Option<&Path>,
filter: fn(&InstanceWithMeta) -> bool, filter: fn(&InstanceWithMeta) -> bool,

View file

@ -2,23 +2,31 @@ use std::{
io::{self, BufReader, Write as _}, io::{self, BufReader, Write as _},
mem::forget, mem::forget,
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Stdio},
time::Instant, time::Instant,
}; };
use anyhow::Context; use anyhow::{bail, Context};
use clap::Parser; use clap::Parser;
use fs_err::File; use fs_err::File;
use memofs::Vfs; use memofs::Vfs;
use rbx_dom_weak::{InstanceBuilder, WeakDom}; use rbx_dom_weak::{InstanceBuilder, WeakDom};
use reqwest::header::{CACHE_CONTROL, COOKIE, PRAGMA, USER_AGENT};
use tempfile::NamedTempFile;
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
use crate::{ use crate::{
path_serializer::display_absolute, path_serializer::display_absolute,
project::Project,
serve_session::ServeSession, serve_session::ServeSession,
syncback::{syncback_loop, FsSnapshot}, syncback::{syncback_loop, FsSnapshot},
}; };
use super::{resolve_path, GlobalOptions}; use super::{
resolve_path,
sourcemap::{filter_nothing, write_sourcemap},
GlobalOptions,
};
const UNKNOWN_INPUT_KIND_ERR: &str = "Could not detect what kind of file was inputted. \ const UNKNOWN_INPUT_KIND_ERR: &str = "Could not detect what kind of file was inputted. \
Expected input file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx."; Expected input file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.";
@ -34,16 +42,21 @@ const UNKNOWN_INPUT_KIND_ERR: &str = "Could not detect what kind of file was inp
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct SyncbackCommand { pub struct SyncbackCommand {
/// Path to the project to sync back to. /// Path to the project to sync back to.
#[clap(default_value = "")] #[clap(default_value = "default.project.json5")]
pub project: PathBuf, pub project: PathBuf,
/// Path to the Roblox file to pull Instances from. /// Path to the Roblox file to pull Instances from.
#[clap(long, short)] #[clap(long, short = 'f', default_value = "Project.rbxl")]
pub input: PathBuf, pub input: PathBuf,
/// Download the place file from Roblox with the specified place ID,
/// ignoring any existing input file.
#[clap(long, short = 'd')]
pub download: Option<u64>,
/// If provided, a list all of the files and directories that will be /// If provided, a list all of the files and directories that will be
/// added or removed is emitted into stdout. /// added or removed is emitted into stdout.
#[clap(long, short)] #[clap(long, short = 'l')]
pub list: bool, pub list: bool,
/// If provided, syncback will not actually write anything to the file /// If provided, syncback will not actually write anything to the file
@ -51,9 +64,10 @@ pub struct SyncbackCommand {
#[clap(long)] #[clap(long)]
pub dry_run: bool, pub dry_run: bool,
/// If provided, the prompt for writing to the file system is skipped. /// If provided, prompts before writing to the file system.
#[clap(long, short = 'y')] /// By default, syncback runs non-interactively.
pub non_interactive: bool, #[clap(long, short = 'i')]
pub interactive: bool,
/// If provided, syncback will preserve existing file structure and middleware /// If provided, syncback will preserve existing file structure and middleware
/// formats when possible. Without this flag (default), syncback creates a fresh /// formats when possible. Without this flag (default), syncback creates a fresh
@ -65,7 +79,53 @@ pub struct SyncbackCommand {
impl SyncbackCommand { impl SyncbackCommand {
pub fn run(&self, global: GlobalOptions) -> anyhow::Result<()> { pub fn run(&self, global: GlobalOptions) -> anyhow::Result<()> {
let path_old = resolve_path(&self.project); let path_old = resolve_path(&self.project);
let path_new = resolve_path(&self.input);
// Determine if we need to download the input file
let resolved_input = resolve_path(&self.input);
let _temp_file: Option<NamedTempFile>;
// Logic:
// - If --download=PLACEID: always download that specific place
// - If input file exists: use it
// - If input file doesn't exist: auto-download using servePlaceIds
let path_new = match &self.download {
Some(place_id) => {
// --download=PLACEID: always download this specific place
eprintln!("Downloading place {}...", place_id);
let download_timer = Instant::now();
let temp = download_place(*place_id)?;
eprintln!(
"Downloaded in {:.02}s",
download_timer.elapsed().as_secs_f32()
);
let temp_path = temp.path().to_path_buf();
_temp_file = Some(temp);
temp_path
}
None if resolved_input.exists() => {
// No --download flag, input file exists: use it
_temp_file = None;
resolved_input.into_owned()
}
None => {
// No --download flag, input file doesn't exist: auto-download
let place_id = get_place_id_from_project(&path_old)?;
eprintln!(
"Input file '{}' not found, downloading place {}...",
resolved_input.display(),
place_id
);
let download_timer = Instant::now();
let temp = download_place(place_id)?;
eprintln!(
"Downloaded in {:.02}s",
download_timer.elapsed().as_secs_f32()
);
let temp_path = temp.path().to_path_buf();
_temp_file = Some(temp);
temp_path
}
};
let input_kind = FileKind::from_path(&path_new).context(UNKNOWN_INPUT_KIND_ERR)?; let input_kind = FileKind::from_path(&path_new).context(UNKNOWN_INPUT_KIND_ERR)?;
let dom_start_timer = Instant::now(); let dom_start_timer = Instant::now();
@ -127,8 +187,12 @@ impl SyncbackCommand {
list_files(&snapshot, global.color.into(), base_path)?; list_files(&snapshot, global.color.into(), base_path)?;
} }
// Drop dom_old early to release the mutex - we don't need it anymore
// and write_sourcemap needs to acquire the same lock
drop(dom_old);
if !self.dry_run { if !self.dry_run {
if !self.non_interactive { if self.interactive {
eprintln!( eprintln!(
"Would write {} files/folders and remove {} files/folders.", "Would write {} files/folders and remove {} files/folders.",
snapshot.added_paths().len(), snapshot.added_paths().len(),
@ -146,7 +210,14 @@ impl SyncbackCommand {
} }
eprintln!("Writing to the file system..."); eprintln!("Writing to the file system...");
snapshot.write_to_vfs(base_path, session_old.vfs())?; snapshot.write_to_vfs(base_path, session_old.vfs())?;
eprintln!("Finished syncback.") eprintln!("Finished syncback.");
// Generate sourcemap after successful syncback
let sourcemap_path = base_path.join("sourcemap.json");
write_sourcemap(&session_old, Some(&sourcemap_path), filter_nothing, false)?;
// Refresh git index if in a git repository
refresh_git_index(base_path);
} else { } else {
eprintln!( eprintln!(
"Would write {} files/folders and remove {} files/folders.", "Would write {} files/folders and remove {} files/folders.",
@ -159,13 +230,124 @@ impl SyncbackCommand {
// It is potentially prohibitively expensive to drop a ServeSession, // It is potentially prohibitively expensive to drop a ServeSession,
// and the program is about to exit anyway so we're just going to forget // and the program is about to exit anyway so we're just going to forget
// about it. // about it.
drop(dom_old);
forget(session_old); forget(session_old);
// Temp file is automatically cleaned up when _temp_file is dropped
Ok(()) Ok(())
} }
} }
/// Gets the first place ID from the project's servePlaceIds field.
fn get_place_id_from_project(project_path: &Path) -> anyhow::Result<u64> {
// Use oneshot Vfs to avoid file watching issues
let temp_vfs = Vfs::new_oneshot();
let project =
Project::load_fuzzy(&temp_vfs, project_path)?.context("Could not find project file")?;
let serve_place_ids = project.serve_place_ids.as_ref().context(
"No servePlaceIds in project file. Add servePlaceIds to your project or use --download=PLACEID",
)?;
// Get the smallest ID for deterministic behavior
serve_place_ids
.iter()
.min()
.copied()
.context("servePlaceIds is empty in project file")
}
/// Downloads a place file from Roblox's asset delivery API.
///
/// Uses rbx_cookie to get the authentication cookie from the system.
fn download_place(place_id: u64) -> anyhow::Result<NamedTempFile> {
let cookie = rbx_cookie::get_value()
.context("Could not find Roblox authentication cookie. Please log into Roblox Studio.")?;
let url = format!("https://assetdelivery.roblox.com/v1/asset/?id={}", place_id);
let client = reqwest::blocking::Client::builder()
.gzip(true)
.brotli(true)
.deflate(true)
.build()?;
let response = client
.get(&url)
.header(COOKIE, format!(".ROBLOSECURITY={}", cookie))
.header(CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.header(PRAGMA, "no-cache")
.header("Expires", "0")
.header(USER_AGENT, "Rojo")
.send()?;
let status = response.status();
if !status.is_success() {
bail!(
"Failed to download place {}: HTTP {} - {}",
place_id,
status,
response.text().unwrap_or_default()
);
}
// Create temp file with .rbxl extension
let mut temp_file = tempfile::Builder::new()
.prefix("rojo-syncback-")
.suffix(".rbxl")
.tempfile()
.context("Failed to create temporary file")?;
// Write response body to temp file
let bytes = response.bytes()?;
io::copy(&mut bytes.as_ref(), &mut temp_file)?;
temp_file.flush()?;
log::debug!(
"Downloaded {} bytes to {}",
bytes.len(),
temp_file.path().display()
);
Ok(temp_file)
}
/// Refreshes the git index if the project is in a git repository.
///
/// This is useful because syncback may rewrite files with identical content,
/// which can cause git to report them as modified due to timestamp changes.
fn refresh_git_index(project_dir: &Path) {
// Check if .git exists in project dir or any parent
let mut check_dir = Some(project_dir);
let mut is_git_repo = false;
while let Some(dir) = check_dir {
if dir.join(".git").exists() {
is_git_repo = true;
break;
}
check_dir = dir.parent();
}
if is_git_repo {
log::debug!("Refreshing git index...");
let result = Command::new("git")
.args(["update-index", "--refresh"])
.current_dir(project_dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
match result {
Ok(status) => {
if !status.success() {
log::debug!("git update-index --refresh exited with: {}", status);
}
}
Err(e) => {
log::warn!("Failed to run git update-index --refresh: {}", e);
}
}
}
}
fn read_dom(path: &Path, file_kind: FileKind) -> anyhow::Result<WeakDom> { fn read_dom(path: &Path, file_kind: FileKind) -> anyhow::Result<WeakDom> {
let content = BufReader::new(File::open(path)?); let content = BufReader::new(File::open(path)?);
match file_kind { match file_kind {

View file

@ -75,7 +75,6 @@ fn run_syncback_test_impl(name: &str, incremental: bool, callback: impl FnOnce(&
project_path.to_str().unwrap(), project_path.to_str().unwrap(),
"--input", "--input",
input_file.to_str().unwrap(), input_file.to_str().unwrap(),
"--non-interactive",
"--list", "--list",
]; ];
@ -118,11 +117,20 @@ fn run_syncback_test_impl(name: &str, incremental: bool, callback: impl FnOnce(&
// Normalize temp directory paths in the output to make snapshots deterministic. // Normalize temp directory paths in the output to make snapshots deterministic.
// Paths like "C:/Users/Joe/AppData/Local/Temp/.tmpXXXXXX/test_name/..." // Paths like "C:/Users/Joe/AppData/Local/Temp/.tmpXXXXXX/test_name/..."
// or "\\?\C:\Users\Joe\AppData\Local\Temp\.tmpXXXXXX\test_name\..."
// become "<TEMP>/test_name/..." // become "<TEMP>/test_name/..."
let stdout = String::from_utf8_lossy(&output.stdout); let stdout = String::from_utf8_lossy(&output.stdout);
// Handle forward slash paths (Unix-style)
let temp_path_regex = Regex::new(r"[A-Za-z]:/[^\s]*/\.tmp[^/]*/").expect("Invalid regex"); let temp_path_regex = Regex::new(r"[A-Za-z]:/[^\s]*/\.tmp[^/]*/").expect("Invalid regex");
let normalized_stdout = temp_path_regex.replace_all(&stdout, "<TEMP>/"); let normalized_stdout = temp_path_regex.replace_all(&stdout, "<TEMP>/");
// Handle backslash paths with optional UNC prefix (Windows-style)
// Matches: \\?\C:\...\temp dir\ or C:\...\temp dir\
let windows_temp_regex =
Regex::new(r"(?:\\\\?\?\\)?[A-Za-z]:\\[^\s]*\\\.tmp[^\\]*\\").expect("Invalid regex");
let normalized_stdout = windows_temp_regex.replace_all(&normalized_stdout, "<TEMP>/");
settings.bind(|| assert_snapshot!(format!("{name}-stdout"), normalized_stdout)); settings.bind(|| assert_snapshot!(format!("{name}-stdout"), normalized_stdout));
settings.bind(|| callback(project_path.as_path())) settings.bind(|| callback(project_path.as_path()))

View file

@ -24,7 +24,6 @@ fn run_syncback(project_path: &Path, input_path: &Path, incremental: bool) -> bo
project_path.to_str().unwrap(), project_path.to_str().unwrap(),
"--input", "--input",
input_path.to_str().unwrap(), input_path.to_str().unwrap(),
"--non-interactive",
]; ];
if incremental { if incremental {