From 2b2ada219ecc34e4bdd0306951b56aec9e72953f Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 1 Feb 2026 09:46:34 -0600 Subject: [PATCH] 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. --- ...destructive_syncback_mode_acb8cb54.plan.md | 180 ++++-- .../syncback_auto-downloader_0508b4bb.plan.md | 276 +++++++++ Cargo.lock | 556 ++++++++++++++++-- Cargo.toml | 6 +- ...__syncback_util__child_but_not-stdout.snap | 3 +- ..._rojo_test__syncback_util__csv-stdout.snap | 3 +- ...ncback_util__duplicate_rojo_id-stdout.snap | 3 +- ...back_util__ignore_paths_adding-stdout.snap | 3 +- ...ncback_util__ignore_paths_init-stdout.snap | 3 +- ...ck_util__ignore_paths_removing-stdout.snap | 3 +- ...back_util__ignore_trees_adding-stdout.snap | 3 +- ...ck_util__ignore_trees_removing-stdout.snap | 1 + ...yncback_util__json_middlewares-stdout.snap | 3 +- ...syncback_util__nested_projects-stdout.snap | 3 +- ...ck_util__nested_projects_weird-stdout.snap | 1 + ...t__syncback_util__project_init-stdout.snap | 3 +- ...back_util__project_reserialize-stdout.snap | 3 +- ...__syncback_util__rbxm_fallback-stdout.snap | 3 +- ..._syncback_util__ref_properties-stdout.snap | 3 +- ...ack_util__ref_properties_blank-stdout.snap | 3 +- ..._util__ref_properties_conflict-stdout.snap | 3 +- ...util__ref_properties_duplicate-stdout.snap | 1 + ...ck_util__ref_properties_pruned-stdout.snap | 3 +- ...k_util__respect_old_middleware-stdout.snap | 3 +- ...yncback_util__schema_roundtrip-stdout.snap | 3 +- ...ack_util__string_value_project-stdout.snap | 3 +- ...est__syncback_util__sync_rules-stdout.snap | 3 +- ..._util__unscriptable_properties-stdout.snap | 3 +- src/cli/sourcemap.rs | 4 +- src/cli/syncback.rs | 206 ++++++- tests/rojo_test/syncback_util.rs | 10 +- tests/tests/clean_mode.rs | 1 - 32 files changed, 1180 insertions(+), 125 deletions(-) create mode 100644 .cursor/plans/syncback_auto-downloader_0508b4bb.plan.md diff --git a/.cursor/plans/destructive_syncback_mode_acb8cb54.plan.md b/.cursor/plans/destructive_syncback_mode_acb8cb54.plan.md index 392cee5c..d61074a4 100644 --- a/.cursor/plans/destructive_syncback_mode_acb8cb54.plan.md +++ b/.cursor/plans/destructive_syncback_mode_acb8cb54.plan.md @@ -5,7 +5,7 @@ todos: [] isProject: false --- -# Destructive Syncback Mode +# Destructive Syncback Mode (Clean by Default) ## 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 #### 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 -/// If provided, syncback will ignore existing file structure and create -/// a fresh project layout. This ensures consistent output regardless of -/// existing filesystem state. -#[clap(long)] -pub clean: bool, +/// If provided, syncback will preserve existing file structure and middleware +/// formats when possible. Without this flag, syncback creates a fresh project +/// layout that exactly matches the input file. +#[clap(long, short = 'n')] +pub incremental: bool, ``` #### 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 let snapshot = syncback_loop( session_old.vfs(), &mut dom_old, dom_new, 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)`) - -Update function signature and initial snapshot: +#### 3. Modify `syncback_loop` Signature (`[src/syncback/mod.rs](src/syncback/mod.rs)`) ```rust pub fn syncback_loop( @@ -92,67 +97,120 @@ pub fn syncback_loop( old_tree: &mut RojoTree, mut new_tree: WeakDom, project: &Project, - clean_mode: bool, // New parameter + incremental: bool, // false = clean mode (default) ) -> anyhow::Result ``` -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 = 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 let mut snapshots = vec![SyncbackSnapshot { 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(), path: project.file_location.clone(), 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 -if clean_mode { - // Collect all existing paths under the project tree for removal - 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() { - if path.is_dir() { - fs_snapshot.remove_dir(path); - } else { - fs_snapshot.remove_file(path); - } - } +// In clean mode, remove any existing paths that weren't overwritten +if !incremental { + let added_paths: HashSet<&Path> = fs_snapshot.added_paths().iter().map(|p| p.as_path()).collect(); + + for old_path in &existing_paths { + // Skip if this path (or a parent) is being written to + if added_paths.contains(old_path.as_path()) { + continue; + } + // 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 ```mermaid flowchart TD - subgraph currentBehavior [Current Behavior] - A1[Syncback on Existing Files] --> B1{Old Instance Exists?} - B1 -->|Yes| C1[Preserve Filename] - B1 -->|Yes| D1[Preserve Middleware] - B1 -->|Yes| E1[Match Children by Name] - C1 --> F1[Inconsistent Structure] - D1 --> F1 - E1 --> F1 + subgraph cleanMode [Default: Clean Mode] + A1[rojo syncback -i game.rbxl] --> B1[Collect existing paths] + B1 --> C1[Set old = None for all snapshots] + C1 --> D1[Generate fresh filenames] + C1 --> E1[Choose best middleware] + C1 --> F1[All children treated as new] + D1 --> G1[Compute new paths] + E1 --> G1 + F1 --> G1 + G1 --> H1[Remove orphaned files] + H1 --> I1[Consistent 1:1 Structure] end - subgraph cleanMode [Clean Mode] - A2[Syncback with --clean] --> B2[Set old = None] - B2 --> C2[Generate Fresh Filename] - B2 --> D2[Choose Best Middleware] - B2 --> E2[All Children Are New] - C2 --> F2[Consistent 1:1 Structure] + subgraph incrementalMode [With --incremental Flag] + A2[rojo syncback -i game.rbxl --incremental] --> B2{Old Instance Exists?} + B2 -->|Yes| C2[Preserve Filename] + B2 -->|Yes| D2[Preserve Middleware] + B2 -->|Yes| E2[Match Children by Name] + C2 --> F2[Legacy Behavior] D2 --> F2 E2 --> F2 end @@ -163,16 +221,30 @@ flowchart TD ### Usage ```bash -# Current behavior (preserves existing structure) +# Default: Clean mode (creates fresh, consistent layout) rojo syncback -i game.rbxl -# Destructive mode (ignores existing structure, creates fresh layout) -rojo syncback -i game.rbxl --clean +# Preview what clean mode would do +rojo syncback -i game.rbxl --list --dry-run -# Combine with other flags -rojo syncback -i game.rbxl --clean --list --dry-run +# Legacy behavior (preserves existing structure) +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. \ No newline at end of file +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). \ No newline at end of file diff --git a/.cursor/plans/syncback_auto-downloader_0508b4bb.plan.md b/.cursor/plans/syncback_auto-downloader_0508b4bb.plan.md new file mode 100644 index 00000000..aa264741 --- /dev/null +++ b/.cursor/plans/syncback_auto-downloader_0508b4bb.plan.md @@ -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>, + + /// 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 { + 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 ` 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 + diff --git a/Cargo.lock b/Cargo.lock index 4c5160dd..f06aacf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,36 @@ dependencies = [ "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]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "anstream" version = "0.6.21" @@ -113,12 +137,30 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "atty" version = "0.2.14" @@ -151,6 +193,18 @@ dependencies = [ "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]] name = "base64" version = "0.21.7" @@ -184,6 +238,17 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "blake3" version = "1.8.3" @@ -191,10 +256,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.6", "cc", "cfg-if 1.0.4", - "constant_time_eq", + "constant_time_eq 0.4.2", "cpufeatures", ] @@ -207,6 +272,27 @@ dependencies = [ "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]] name = "bstr" version = "1.12.1" @@ -224,6 +310,12 @@ version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.5.0" @@ -299,9 +391,13 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ + "ansi_term", + "atty", "bitflags 1.3.2", + "strsim 0.8.0", "textwrap", "unicode-width", + "vec_map", ] [[package]] @@ -323,7 +419,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -335,7 +431,7 @@ dependencies = [ "heck", "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -350,6 +446,24 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "console" version = "0.15.11" @@ -362,12 +476,34 @@ dependencies = [ "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]] name = "constant_time_eq" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "core-foundation" version = "0.9.4" @@ -393,6 +529,15 @@ dependencies = [ "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]] name = "criterion" version = "0.3.6" @@ -542,6 +687,15 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "diff" version = "0.1.13" @@ -558,6 +712,17 @@ dependencies = [ "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]] name = "dirs" version = "2.0.2" @@ -584,7 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi 0.3.9", ] @@ -596,10 +761,16 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "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]] name = "displaydoc" version = "0.2.5" @@ -608,7 +779,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -624,7 +795,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e" dependencies = [ "cc", - "rustc_version", + "rustc_version 0.4.1", "toml 0.5.11", "vswhom", "winreg 0.10.1", @@ -655,6 +826,19 @@ dependencies = [ "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]] name = "env_logger" version = "0.11.8" @@ -707,6 +891,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "float-cmp" version = "0.9.0" @@ -837,7 +1031,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -895,6 +1089,17 @@ dependencies = [ "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]] name = "getrandom" version = "0.2.17" @@ -903,7 +1108,7 @@ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if 1.0.4", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -1348,7 +1553,7 @@ checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -1527,6 +1732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1555,7 +1761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -1621,6 +1827,12 @@ dependencies = [ "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]] name = "num-traits" version = "0.2.19" @@ -1748,7 +1960,7 @@ dependencies = [ "pest_meta", "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -1779,6 +1991,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "plotters" version = "0.3.7" @@ -1831,6 +2056,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1897,7 +2128,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "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]] @@ -1980,7 +2220,7 @@ version = "2.0.1" dependencies = [ "ahash", "criterion 0.5.1", - "env_logger", + "env_logger 0.11.8", "heck", "insta", "log", @@ -1994,6 +2234,23 @@ dependencies = [ "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]] name = "rbx_dom_weak" version = "4.1.0" @@ -2047,7 +2304,7 @@ version = "2.0.1" dependencies = [ "ahash", "base64 0.22.1", - "env_logger", + "env_logger 0.11.8", "heck", "insta", "log", @@ -2057,6 +2314,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2075,6 +2338,17 @@ dependencies = [ "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]] name = "redox_users" version = "0.4.6" @@ -2121,6 +2395,7 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ + "async-compression", "base64 0.21.7", "bytes", "encoding_rs", @@ -2147,6 +2422,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-rustls", + "tokio-util", "tower-service", "url", "wasm-bindgen", @@ -2238,7 +2514,7 @@ dependencies = [ "csv", "data-encoding", "embed-resource", - "env_logger", + "env_logger 0.11.8", "float-cmp", "fs-err", "futures", @@ -2260,6 +2536,7 @@ dependencies = [ "profiling", "rayon", "rbx_binary", + "rbx_cookie", "rbx_dom_weak", "rbx_reflection", "rbx_reflection_database", @@ -2270,7 +2547,7 @@ dependencies = [ "rmp-serde", "roblox_install", "rojo-insta-ext", - "semver", + "semver 1.0.27", "serde", "serde_bytes", "serde_json", @@ -2295,19 +2572,40 @@ dependencies = [ "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]] name = "rustc-demangle" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.27", ] [[package]] @@ -2397,12 +2695,27 @@ dependencies = [ "untrusted", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.228" @@ -2450,7 +2763,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -2499,6 +2812,15 @@ dependencies = [ "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]] name = "sha1" version = "0.10.6" @@ -2510,6 +2832,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.9" @@ -2536,6 +2864,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "similar" version = "2.7.0" @@ -2589,6 +2923,70 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "strsim" version = "0.11.1" @@ -2613,7 +3011,18 @@ dependencies = [ "heck", "proc-macro2 1.0.106", "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]] @@ -2641,7 +3050,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -2722,7 +3131,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -2733,7 +3142,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -2745,6 +3154,75 @@ dependencies = [ "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]] name = "tinystr" version = "0.8.2" @@ -2788,7 +3266,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -2964,14 +3442,14 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "byteorder", + "byteorder 1.5.0", "bytes", "data-encoding", "http", "httparse", "log", "rand", - "sha1", + "sha1 0.10.6", "thiserror 1.0.69", "url", "utf-8", @@ -3038,7 +3516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208" dependencies = [ "ahash", - "byteorder", + "byteorder 1.5.0", "lazy_static", "parking_lot", "serde", @@ -3080,6 +3558,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.5" @@ -3125,6 +3609,12 @@ dependencies = [ "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]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -3186,7 +3676,7 @@ dependencies = [ "bumpalo", "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", "wasm-bindgen-shared", ] @@ -3614,7 +4104,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", "synstructure", ] @@ -3635,7 +4125,7 @@ checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] @@ -3655,7 +4145,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", "synstructure", ] @@ -3689,7 +4179,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2 1.0.106", "quote 1.0.44", - "syn", + "syn 2.0.114", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cb51866a..7339b9b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,11 +72,16 @@ log = "0.4.29" num_cpus = "1.17.0" opener = "0.5.2" rayon = "1.11.0" +rbx_cookie = "0.1.5" reqwest = { version = "0.11.27", default-features = false, features = [ "blocking", "json", "rustls-tls", + "gzip", + "brotli", + "deflate", ] } +tempfile = "3.24.0" ritz = "0.1.0" roblox_install = "1.0.0" serde = { version = "1.0.228", features = ["derive", "rc"] } @@ -121,5 +126,4 @@ paste = "1.0.15" pretty_assertions = "1.4.1" regex = "1" serde_yaml = "0.8.26" -tempfile = "3.24.0" walkdir = "2.5.0" diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__child_but_not-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__child_but_not-stdout.snap index 7495dc1f..a08b212b 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__child_but_not-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__child_but_not-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing OnlyOneCopy/child_of_one.luau Writing ReplicatedStorage/child_replicated_storage.luau +Created sourcemap at /child_but_not\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__csv-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__csv-stdout.snap index 61fc2288..f6868221 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__csv-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__csv-stdout.snap @@ -1,7 +1,8 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/csv.csv Writing src/csv_init/init.csv Writing src/csv_init +Created sourcemap at /csv\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__duplicate_rojo_id-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__duplicate_rojo_id-stdout.snap index b30a753c..ce881a26 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__duplicate_rojo_id-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__duplicate_rojo_id-stdout.snap @@ -1,5 +1,6 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing container.model.json5 +Created sourcemap at /duplicate_rojo_id\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_adding-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_adding-stdout.snap index 3bb43c0f..cc513262 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_adding-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_adding-stdout.snap @@ -1,8 +1,9 @@ --- 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/int_value.model.json5 Writing src/subfolder/string_value.txt Writing src/subfolder +Created sourcemap at /ignore_paths_adding\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_init-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_init-stdout.snap index 6222321f..54a0cd8f 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_init-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_init-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/non-init.luau Writing src/init-file +Created sourcemap at /ignore_paths_init\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_removing-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_removing-stdout.snap index 906d6317..c78b4282 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_removing-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_paths_removing-stdout.snap @@ -1,5 +1,6 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/Message.model.json5 +Created sourcemap at /ignore_paths_removing\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_adding-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_adding-stdout.snap index f1f8c02c..571584b8 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_adding-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_adding-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/IncludeMe/.gitkeep Writing src/IncludeMe +Created sourcemap at /ignore_trees_adding\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_removing-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_removing-stdout.snap index 36f02997..e1d0ff7e 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_removing-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ignore_trees_removing-stdout.snap @@ -3,3 +3,4 @@ source: tests/rojo_test/syncback_util.rs expression: normalized_stdout --- Removing src +Created sourcemap at /ignore_trees_removing\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__json_middlewares-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__json_middlewares-stdout.snap index d017a9ae..857453a0 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__json_middlewares-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__json_middlewares-stdout.snap @@ -1,8 +1,9 @@ --- 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/model_json.model.json5 Writing src/project_json.project.json5 Writing src/dir_with_meta +Created sourcemap at /json_middlewares\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects-stdout.snap index 581968e9..335318b1 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing nested.project.json5 Writing string_value.txt +Created sourcemap at /nested_projects\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects_weird-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects_weird-stdout.snap index 370ed871..e335455d 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects_weird-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__nested_projects_weird-stdout.snap @@ -6,3 +6,4 @@ Writing src/modules/ClientModule.luau Writing src/modules/ServerModule.luau Removing client-only.project.json5 Removing server-only.project.json5 +Created sourcemap at /nested_projects_weird\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_init-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_init-stdout.snap index 409b4d57..ce1567a0 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_init-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_init-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/init.luau Writing src +Created sourcemap at /project_init\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_reserialize-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_reserialize-stdout.snap index c441e44b..676368df 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_reserialize-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__project_reserialize-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing attribute_mismatch.luau Writing property_mismatch.project.json5 +Created sourcemap at /project_reserialize\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__rbxm_fallback-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__rbxm_fallback-stdout.snap index a787ab3e..e6b6cb76 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__rbxm_fallback-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__rbxm_fallback-stdout.snap @@ -1,8 +1,9 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/ChildWithoutDuplicates/Child/.gitkeep Writing src/ChildWithDuplicates Writing src/ChildWithoutDuplicates Writing src/ChildWithoutDuplicates/Child +Created sourcemap at /rbxm_fallback\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties-stdout.snap index c8e9ec4d..08304ec2 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/pointer.model.json5 Writing src/target.model.json5 +Created sourcemap at /ref_properties\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_blank-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_blank-stdout.snap index e7ae28ae..bf94cc0c 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_blank-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_blank-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/pointer.model.json5 Writing src/target.txt +Created sourcemap at /ref_properties_blank\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_conflict-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_conflict-stdout.snap index d5db8835..e83e4e49 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_conflict-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_conflict-stdout.snap @@ -1,8 +1,9 @@ --- 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_2.model.json5 Writing src/Target_1.model.json5 Writing src/Target_2.model.json5 +Created sourcemap at /ref_properties_conflict\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_duplicate-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_duplicate-stdout.snap index e9d435da..32749835 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_duplicate-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_duplicate-stdout.snap @@ -7,3 +7,4 @@ Writing src/Pointer_2.model.json5 Writing src/Target/init.meta.json5 Writing src/Target Removing src/Target.model.json5 +Created sourcemap at /ref_properties_duplicate\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_pruned-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_pruned-stdout.snap index 8cf3014b..94c9b8cc 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_pruned-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__ref_properties_pruned-stdout.snap @@ -1,7 +1,8 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/Pointer1.model.json5 Writing src/Pointer2.model.json5 Writing src/Pointer3.model.json5 +Created sourcemap at /ref_properties_pruned\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__respect_old_middleware-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__respect_old_middleware-stdout.snap index baaff4cc..fdc17624 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__respect_old_middleware-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__respect_old_middleware-stdout.snap @@ -1,8 +1,9 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing default.project.json5 Writing src/model_json.model.json5 Writing src/rbxm.rbxm Writing src/rbxmx.rbxmx +Created sourcemap at /respect_old_middleware\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__schema_roundtrip-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__schema_roundtrip-stdout.snap index a414f22f..237ee011 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__schema_roundtrip-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__schema_roundtrip-stdout.snap @@ -1,6 +1,6 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing default.project.json5 Writing src/adjacent.luau @@ -8,3 +8,4 @@ Writing src/adjacent.meta.json5 Writing src/init/init.meta.json5 Writing src/model.model.json5 Writing src/init +Created sourcemap at /schema_roundtrip\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__string_value_project-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__string_value_project-stdout.snap index fd7f3aaa..6b1a5d64 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__string_value_project-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__string_value_project-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing default.project.json5 Writing string_value.txt +Created sourcemap at /string_value_project\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__sync_rules-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__sync_rules-stdout.snap index 00045825..155becb6 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__sync_rules-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__sync_rules-stdout.snap @@ -1,6 +1,7 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing src/module.modulescript Writing src/text.text +Created sourcemap at /sync_rules\sourcemap.json diff --git a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__unscriptable_properties-stdout.snap b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__unscriptable_properties-stdout.snap index eabf9b6c..ab2cee3d 100644 --- a/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__unscriptable_properties-stdout.snap +++ b/rojo-test/syncback-test-snapshots/end_to_end__rojo_test__syncback_util__unscriptable_properties-stdout.snap @@ -1,5 +1,6 @@ --- source: tests/rojo_test/syncback_util.rs -expression: "String::from_utf8_lossy(&output.stdout)" +expression: normalized_stdout --- Writing default.project.json5 +Created sourcemap at /unscriptable_properties\sourcemap.json diff --git a/src/cli/sourcemap.rs b/src/cli/sourcemap.rs index a6dae664..782cf7e8 100644 --- a/src/cli/sourcemap.rs +++ b/src/cli/sourcemap.rs @@ -115,7 +115,7 @@ impl SourcemapCommand { } } -fn filter_nothing(_instance: &InstanceWithMeta) -> bool { +pub(crate) fn filter_nothing(_instance: &InstanceWithMeta) -> bool { true } @@ -229,7 +229,7 @@ fn recurse_create_node<'a>( }) } -fn write_sourcemap( +pub(crate) fn write_sourcemap( session: &ServeSession, output: Option<&Path>, filter: fn(&InstanceWithMeta) -> bool, diff --git a/src/cli/syncback.rs b/src/cli/syncback.rs index 134a5428..32b6bbba 100644 --- a/src/cli/syncback.rs +++ b/src/cli/syncback.rs @@ -2,23 +2,31 @@ use std::{ io::{self, BufReader, Write as _}, mem::forget, path::{Path, PathBuf}, + process::{Command, Stdio}, time::Instant, }; -use anyhow::Context; +use anyhow::{bail, Context}; use clap::Parser; use fs_err::File; use memofs::Vfs; 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 crate::{ path_serializer::display_absolute, + project::Project, serve_session::ServeSession, 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. \ 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)] pub struct SyncbackCommand { /// Path to the project to sync back to. - #[clap(default_value = "")] + #[clap(default_value = "default.project.json5")] pub project: PathBuf, /// Path to the Roblox file to pull Instances from. - #[clap(long, short)] + #[clap(long, short = 'f', default_value = "Project.rbxl")] 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, + /// If provided, a list all of the files and directories that will be /// added or removed is emitted into stdout. - #[clap(long, short)] + #[clap(long, short = 'l')] pub list: bool, /// If provided, syncback will not actually write anything to the file @@ -51,9 +64,10 @@ pub struct SyncbackCommand { #[clap(long)] pub dry_run: bool, - /// If provided, the prompt for writing to the file system is skipped. - #[clap(long, short = 'y')] - pub non_interactive: bool, + /// If provided, prompts before writing to the file system. + /// By default, syncback runs non-interactively. + #[clap(long, short = 'i')] + pub interactive: bool, /// If provided, syncback will preserve existing file structure and middleware /// formats when possible. Without this flag (default), syncback creates a fresh @@ -65,7 +79,53 @@ pub struct SyncbackCommand { impl SyncbackCommand { pub fn run(&self, global: GlobalOptions) -> anyhow::Result<()> { 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; + + // 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 dom_start_timer = Instant::now(); @@ -127,8 +187,12 @@ impl SyncbackCommand { 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.non_interactive { + if self.interactive { eprintln!( "Would write {} files/folders and remove {} files/folders.", snapshot.added_paths().len(), @@ -146,7 +210,14 @@ impl SyncbackCommand { } eprintln!("Writing to the file system..."); 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 { eprintln!( "Would write {} files/folders and remove {} files/folders.", @@ -159,13 +230,124 @@ impl SyncbackCommand { // It is potentially prohibitively expensive to drop a ServeSession, // and the program is about to exit anyway so we're just going to forget // about it. - drop(dom_old); forget(session_old); + // Temp file is automatically cleaned up when _temp_file is dropped + Ok(()) } } +/// Gets the first place ID from the project's servePlaceIds field. +fn get_place_id_from_project(project_path: &Path) -> anyhow::Result { + // 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 { + 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 { let content = BufReader::new(File::open(path)?); match file_kind { diff --git a/tests/rojo_test/syncback_util.rs b/tests/rojo_test/syncback_util.rs index 6b5f1f77..73905d66 100644 --- a/tests/rojo_test/syncback_util.rs +++ b/tests/rojo_test/syncback_util.rs @@ -75,7 +75,6 @@ fn run_syncback_test_impl(name: &str, incremental: bool, callback: impl FnOnce(& project_path.to_str().unwrap(), "--input", input_file.to_str().unwrap(), - "--non-interactive", "--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. // Paths like "C:/Users/Joe/AppData/Local/Temp/.tmpXXXXXX/test_name/..." + // or "\\?\C:\Users\Joe\AppData\Local\Temp\.tmpXXXXXX\test_name\..." // become "/test_name/..." 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 normalized_stdout = temp_path_regex.replace_all(&stdout, "/"); + // 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, "/"); + settings.bind(|| assert_snapshot!(format!("{name}-stdout"), normalized_stdout)); settings.bind(|| callback(project_path.as_path())) diff --git a/tests/tests/clean_mode.rs b/tests/tests/clean_mode.rs index c1793358..0edb4cae 100644 --- a/tests/tests/clean_mode.rs +++ b/tests/tests/clean_mode.rs @@ -24,7 +24,6 @@ fn run_syncback(project_path: &Path, input_path: &Path, incremental: bool) -> bo project_path.to_str().unwrap(), "--input", input_path.to_str().unwrap(), - "--non-interactive", ]; if incremental {