feat: conformance tests pass for first time
Change implementation to exponentially increase search space at each level.
This commit is contained in:
parent
31c454a78c
commit
546d6deb69
13 changed files with 1852 additions and 102 deletions
28
conformance-tests/README.md
Normal file
28
conformance-tests/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Conformance Tests
|
||||
|
||||
I'm using the Rust implementation as the source of truth. It was pretty easy to write unit tests and measure performance in Rust. I'm most familiar with testing tools in Rust so I did all the validation work there. Now I just want to check that all the other language implementations behave exactly the same as the Rust version, which I poured all this energy into validating for correctness.
|
||||
|
||||
## Running the tests
|
||||
|
||||
| Language | Directory | Command |
|
||||
|----------|-----------|---------|
|
||||
| TypeScript | `runners/typescript` | `npm run test` |
|
||||
|
||||
## Generating test data
|
||||
|
||||
To generate the test data, run:
|
||||
|
||||
```
|
||||
cd generator
|
||||
cargo run
|
||||
```
|
||||
|
||||
This produces `.scenario.json` files in `genfiles/`. Each file contains a sequence of random numbers and the expected outputs from the Rust implementation.
|
||||
|
||||
The idea is that we replay the same sequence of random numbers for each implementation. All of the different language libraries allow you to inject the source of randomness, so the tests just inject a mock random generator that reads from the recorded sequence. In theory, every implementation makes the exact same calls to random in the same order, and they all produce identical output.
|
||||
|
||||
## Scenario format
|
||||
|
||||
You can test the entire public API by assuming you have a sorted list of sort keys. A sort key is just an LSEQ identifier. Each scenario has an `initialState` array of sort keys in sorted order. Then there's a list of operations: pick an array index to insert before or after, which tells you which two boundary keys to use for the `alloc` function from the paper. You call alloc, insert the result into the list, and repeat.
|
||||
|
||||
This setup can test everything you'd want to test, even if it's a bit indirect for very basic cases. You could still write basic unit tests in each language for the simple stuff.
|
||||
204
conformance-tests/generator/Cargo.lock
generated
Normal file
204
conformance-tests/generator/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "conformance-test-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"peoplesgrocers-lseq",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.178"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "peoplesgrocers-lseq"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
10
conformance-tests/generator/Cargo.toml
Normal file
10
conformance-tests/generator/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "conformance-test-generator"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
peoplesgrocers-lseq = { path = "../../rust" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
rand = "0.8"
|
||||
254
conformance-tests/generator/src/main.rs
Normal file
254
conformance-tests/generator/src/main.rs
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
mod model;
|
||||
|
||||
use model::{Insert, Scenario};
|
||||
use peoplesgrocers_lseq::{LseqRng, SortKey, LSEQ};
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use std::fs;
|
||||
|
||||
/// A recording RNG wrapper that records random values as floats
|
||||
/// compatible with TypeScript's Math.random() consumption pattern.
|
||||
struct RecordingRng {
|
||||
inner: StdRng,
|
||||
recorded: Vec<f64>,
|
||||
}
|
||||
|
||||
impl RecordingRng {
|
||||
fn new(seed: u64) -> Self {
|
||||
RecordingRng {
|
||||
inner: StdRng::seed_from_u64(seed),
|
||||
recorded: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn take_recorded(&mut self) -> Vec<f64> {
|
||||
std::mem::take(&mut self.recorded)
|
||||
}
|
||||
}
|
||||
|
||||
impl LseqRng for RecordingRng {
|
||||
fn gen_bool(&mut self, p: f64) -> bool {
|
||||
// Use fully qualified syntax to call rand::Rng method
|
||||
let result = Rng::gen_bool(&mut self.inner, p);
|
||||
// Record a float that TypeScript's `f < p` would produce the same result
|
||||
// If result is true, we need f < p, so use p/2
|
||||
// If result is false, we need f >= p, so use (1 + p) / 2
|
||||
let recorded_value = if result { p / 2.0 } else { (1.0 + p) / 2.0 };
|
||||
self.recorded.push(recorded_value);
|
||||
result
|
||||
}
|
||||
|
||||
fn gen_range(&mut self, range: std::ops::Range<u64>) -> u64 {
|
||||
// Use fully qualified syntax to call rand::Rng method
|
||||
let result: u64 = Rng::gen_range(&mut self.inner, range.clone());
|
||||
// Record a float that TypeScript's `Math.floor(f * range_size)` would produce the same result
|
||||
// For result r in [0, range_size), we need floor(f * range_size) = r
|
||||
// So f should be in [r/range_size, (r+1)/range_size)
|
||||
// Use midpoint: (r + 0.5) / range_size
|
||||
let range_size = (range.end - range.start) as f64;
|
||||
let relative_result = (result - range.start) as f64;
|
||||
let recorded_value = (relative_result + 0.5) / range_size;
|
||||
self.recorded.push(recorded_value);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn generate_scenario(
|
||||
name: &str,
|
||||
description: &str,
|
||||
seed: u64,
|
||||
init: Vec<&str>,
|
||||
ops: impl FnOnce(&mut LSEQ<RecordingRng>, &mut Vec<String>) -> Vec<Insert>,
|
||||
) -> Scenario {
|
||||
let mut lseq = LSEQ::new(RecordingRng::new(seed));
|
||||
let mut state: Vec<String> = init.iter().map(|s| s.to_string()).collect();
|
||||
let operations = ops(&mut lseq, &mut state);
|
||||
|
||||
Scenario {
|
||||
name: name.to_string(),
|
||||
description: description.to_string(),
|
||||
seed,
|
||||
init: init.iter().map(|s| s.to_string()).collect(),
|
||||
rng: lseq.take_rng().take_recorded(),
|
||||
operations,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let scenarios = vec![
|
||||
// 01 - Sequential append
|
||||
generate_scenario(
|
||||
"sequential-append",
|
||||
"20 inserts, each after the last",
|
||||
42,
|
||||
vec![],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
for _ in 0..20 {
|
||||
let before_key = state.last().map(|s| s.parse::<SortKey>().unwrap());
|
||||
let result = lseq.alloc(before_key.as_ref(), None);
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::After {
|
||||
index: -1, // after last element (or beginning if empty)
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.push(result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
// 02 - Sequential prepend
|
||||
generate_scenario(
|
||||
"sequential-prepend",
|
||||
"20 inserts, each before the first",
|
||||
43,
|
||||
vec![],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
for _ in 0..20 {
|
||||
let after_key = state.first().map(|s| s.parse::<SortKey>().unwrap());
|
||||
let result = lseq.alloc(None, after_key.as_ref());
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::Before {
|
||||
index: 0, // before first element (prepend)
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.insert(0, result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
// 03 - Random insert 100 items
|
||||
generate_scenario(
|
||||
"random-insert-100-items",
|
||||
"100 inserts at random positions",
|
||||
44,
|
||||
vec![],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
let mut position_rng = StdRng::seed_from_u64(100); // Separate RNG for positions
|
||||
|
||||
for _ in 0..100 {
|
||||
// Pick a random insertion point (0..=state.len())
|
||||
let idx = if state.is_empty() {
|
||||
0
|
||||
} else {
|
||||
Rng::gen_range(&mut position_rng, 0..=state.len())
|
||||
};
|
||||
|
||||
// Derive beforeKey and afterKey from the insertion index
|
||||
let before_key = if idx > 0 {
|
||||
Some(state[idx - 1].parse::<SortKey>().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let after_key = if idx < state.len() {
|
||||
Some(state[idx].parse::<SortKey>().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let result = lseq.alloc(before_key.as_ref(), after_key.as_ref());
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::Before {
|
||||
index: idx as i32,
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.insert(idx, result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
// 04 - Dense packing
|
||||
generate_scenario(
|
||||
"dense-packing",
|
||||
"20 inserts between adjacent keys '0' and '1'",
|
||||
45,
|
||||
vec!["0", "1"],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
// Always insert between first and second element
|
||||
for _ in 0..20 {
|
||||
let before_key = state[0].parse::<SortKey>().unwrap();
|
||||
let after_key = state[1].parse::<SortKey>().unwrap();
|
||||
let result = lseq.alloc(Some(&before_key), Some(&after_key));
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::Before {
|
||||
index: 1, // between state[0] and state[1]
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.insert(1, result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
// 05 - Deep nesting
|
||||
generate_scenario(
|
||||
"deep-nesting",
|
||||
"Force 5+ level deep keys by inserting between adjacent keys",
|
||||
46,
|
||||
vec!["M", "N"],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
// Keep inserting between first two to force depth
|
||||
for _ in 0..30 {
|
||||
let before_key = state[0].parse::<SortKey>().unwrap();
|
||||
let after_key = state[1].parse::<SortKey>().unwrap();
|
||||
let result = lseq.alloc(Some(&before_key), Some(&after_key));
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::Before {
|
||||
index: 1, // between state[0] and state[1]
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.insert(1, result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
// 06 - Edge min interval
|
||||
generate_scenario(
|
||||
"edge-min-interval",
|
||||
"Insert between adjacent keys (A, B) which have interval=1",
|
||||
47,
|
||||
vec!["A", "B"],
|
||||
|lseq, state| {
|
||||
let mut ops = Vec::new();
|
||||
for _ in 0..10 {
|
||||
let before_key = state[0].parse::<SortKey>().unwrap();
|
||||
let after_key = state[1].parse::<SortKey>().unwrap();
|
||||
let result = lseq.alloc(Some(&before_key), Some(&after_key));
|
||||
let result_str = result.to_string();
|
||||
|
||||
ops.push(Insert::Before {
|
||||
index: 1, // between state[0] and state[1]
|
||||
outcome: result_str.clone(),
|
||||
});
|
||||
|
||||
state.insert(1, result_str);
|
||||
}
|
||||
ops
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
// Write each scenario to a file
|
||||
let output_dir = "../genfiles";
|
||||
fs::create_dir_all(output_dir).expect("Failed to create genfiles directory");
|
||||
|
||||
for (i, scenario) in scenarios.iter().enumerate() {
|
||||
let filename = format!("{}/{:02}-{}.scenario.json", output_dir, i + 1, scenario.name);
|
||||
let json = serde_json::to_string_pretty(scenario).expect("Failed to serialize scenario");
|
||||
fs::write(&filename, json).expect("Failed to write scenario file");
|
||||
println!("Generated: {}", filename);
|
||||
}
|
||||
}
|
||||
46
conformance-tests/generator/src/model.rs
Normal file
46
conformance-tests/generator/src/model.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use serde::ser::{SerializeMap, Serializer};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Scenario {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub seed: u64,
|
||||
pub init: Vec<String>,
|
||||
pub rng: Vec<f64>,
|
||||
pub operations: Vec<Insert>,
|
||||
}
|
||||
|
||||
/// Specifies an insertion point in the sequence.
|
||||
///
|
||||
/// - `Insert::Before { index: 3, .. }` → insert before state[3]
|
||||
/// - `Insert::Before { index: 0, .. }` → insert at start
|
||||
/// - `Insert::After { index: 2, .. }` → insert after state[2]
|
||||
/// - `Insert::After { index: -1, .. }` → insert at end (Python-style negative index)
|
||||
///
|
||||
/// Serializes to `{ "before": n, "expected": "..." }` or `{ "after": n, "expected": "..." }`.
|
||||
#[derive(Debug)]
|
||||
pub enum Insert {
|
||||
Before { index: i32, outcome: String },
|
||||
After { index: i32, outcome: String },
|
||||
}
|
||||
|
||||
impl Serialize for Insert {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(2))?;
|
||||
match self {
|
||||
Insert::Before { index, outcome } => {
|
||||
map.serialize_entry("before", index)?;
|
||||
map.serialize_entry("expected", outcome)?;
|
||||
}
|
||||
Insert::After { index, outcome } => {
|
||||
map.serialize_entry("after", index)?;
|
||||
map.serialize_entry("expected", outcome)?;
|
||||
}
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
0
conformance-tests/genfiles/.gitkeep
Normal file
0
conformance-tests/genfiles/.gitkeep
Normal file
674
conformance-tests/runners/typescript/package-lock.json
generated
Normal file
674
conformance-tests/runners/typescript/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
{
|
||||
"name": "lseq-conformance-runner",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "lseq-conformance-runner",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@peoplesgrocers/lseq": "file:../../../typescript",
|
||||
"uvu": "^0.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"../../../typescript": {
|
||||
"name": "@peoplesgrocers/lseq",
|
||||
"version": "1.0.0",
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"devDependencies": {
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.0.0",
|
||||
"uvu": "^0.5.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
|
||||
"integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
|
||||
"integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
|
||||
"integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
|
||||
"integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
|
||||
"integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
|
||||
"integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
|
||||
"integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
|
||||
"integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
|
||||
"integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
|
||||
"integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
|
||||
"integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
|
||||
"integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@peoplesgrocers/lseq": {
|
||||
"resolved": "../../../typescript",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.26",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz",
|
||||
"integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
|
||||
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
|
||||
"integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.1",
|
||||
"@esbuild/android-arm": "0.27.1",
|
||||
"@esbuild/android-arm64": "0.27.1",
|
||||
"@esbuild/android-x64": "0.27.1",
|
||||
"@esbuild/darwin-arm64": "0.27.1",
|
||||
"@esbuild/darwin-x64": "0.27.1",
|
||||
"@esbuild/freebsd-arm64": "0.27.1",
|
||||
"@esbuild/freebsd-x64": "0.27.1",
|
||||
"@esbuild/linux-arm": "0.27.1",
|
||||
"@esbuild/linux-arm64": "0.27.1",
|
||||
"@esbuild/linux-ia32": "0.27.1",
|
||||
"@esbuild/linux-loong64": "0.27.1",
|
||||
"@esbuild/linux-mips64el": "0.27.1",
|
||||
"@esbuild/linux-ppc64": "0.27.1",
|
||||
"@esbuild/linux-riscv64": "0.27.1",
|
||||
"@esbuild/linux-s390x": "0.27.1",
|
||||
"@esbuild/linux-x64": "0.27.1",
|
||||
"@esbuild/netbsd-arm64": "0.27.1",
|
||||
"@esbuild/netbsd-x64": "0.27.1",
|
||||
"@esbuild/openbsd-arm64": "0.27.1",
|
||||
"@esbuild/openbsd-x64": "0.27.1",
|
||||
"@esbuild/openharmony-arm64": "0.27.1",
|
||||
"@esbuild/sunos-x64": "0.27.1",
|
||||
"@esbuild/win32-arm64": "0.27.1",
|
||||
"@esbuild/win32-ia32": "0.27.1",
|
||||
"@esbuild/win32-x64": "0.27.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
|
||||
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/kleur": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
||||
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mri": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.27.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uvu": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
|
||||
"integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.0",
|
||||
"diff": "^5.0.0",
|
||||
"kleur": "^4.0.3",
|
||||
"sade": "^1.7.3"
|
||||
},
|
||||
"bin": {
|
||||
"uvu": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
conformance-tests/runners/typescript/package.json
Normal file
18
conformance-tests/runners/typescript/package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "lseq-conformance-runner",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "tsx src/runner.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@peoplesgrocers/lseq": "file:../../../typescript",
|
||||
"uvu": "^0.5.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"tsx": "^4.7.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
89
conformance-tests/runners/typescript/src/runner.ts
Normal file
89
conformance-tests/runners/typescript/src/runner.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { LSEQ } from "@peoplesgrocers/lseq";
|
||||
import { test } from "uvu";
|
||||
import * as assert from "uvu/assert";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
interface Operation {
|
||||
before?: number;
|
||||
after?: number;
|
||||
expected: string;
|
||||
}
|
||||
|
||||
interface Scenario {
|
||||
name: string;
|
||||
description: string;
|
||||
seed: number;
|
||||
init: string[];
|
||||
rng: number[];
|
||||
operations: Operation[];
|
||||
}
|
||||
|
||||
function createMockRandom(values: number[]): () => number {
|
||||
let index = 0;
|
||||
return () => {
|
||||
if (index >= values.length) {
|
||||
throw new Error(`Ran out of random values at index ${index}`);
|
||||
}
|
||||
return values[index++];
|
||||
};
|
||||
}
|
||||
|
||||
const testDataDir = path.join(import.meta.dirname!, "../../../genfiles");
|
||||
const files = fs
|
||||
.readdirSync(testDataDir)
|
||||
.filter((f) => f.endsWith(".scenario.json"))
|
||||
.sort();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(testDataDir, file);
|
||||
const scenario: Scenario = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
||||
|
||||
test(`${scenario.name}`, () => {
|
||||
const mockRandom = createMockRandom(scenario.rng);
|
||||
const lseq = new LSEQ(mockRandom);
|
||||
const state: string[] = [...scenario.init];
|
||||
|
||||
for (let i = 0; i < scenario.operations.length; i++) {
|
||||
const op = scenario.operations[i];
|
||||
|
||||
// Derive beforeKey and afterKey from the insertion point
|
||||
// - `before: X` means insert before index X → beforeKey=state[X-1], afterKey=state[X]
|
||||
// - `after: X` means insert after index X → beforeKey=state[X], afterKey=state[X+1]
|
||||
let beforeKey: string | null = null;
|
||||
let afterKey: string | null = null;
|
||||
let insertIdx: number;
|
||||
|
||||
if (op.before !== undefined) {
|
||||
// Insert before index X
|
||||
const idx = op.before < 0 ? state.length + op.before : op.before;
|
||||
beforeKey = idx > 0 ? state[idx - 1] : null;
|
||||
afterKey = idx < state.length ? state[idx] : null;
|
||||
insertIdx = idx;
|
||||
} else if (op.after !== undefined) {
|
||||
// Insert after index X
|
||||
const idx = op.after < 0 ? state.length + op.after : op.after;
|
||||
beforeKey = idx >= 0 && idx < state.length ? state[idx] : null;
|
||||
afterKey = idx + 1 < state.length ? state[idx + 1] : null;
|
||||
insertIdx = idx + 1;
|
||||
} else {
|
||||
// Neither specified - insert at end
|
||||
beforeKey = state.length > 0 ? state[state.length - 1] : null;
|
||||
afterKey = null;
|
||||
insertIdx = state.length;
|
||||
}
|
||||
|
||||
const result = lseq.alloc(beforeKey, afterKey);
|
||||
|
||||
assert.is(
|
||||
result,
|
||||
op.expected,
|
||||
`op ${i}: alloc(${beforeKey}, ${afterKey})`
|
||||
);
|
||||
|
||||
state.splice(insertIdx, 0, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test.run();
|
||||
12
conformance-tests/runners/typescript/tsconfig.json
Normal file
12
conformance-tests/runners/typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue