From 5153e5a023d0a81b1b16b4b389850be29a61f30c Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:18:57 -0700 Subject: [PATCH 01/32] fix(node): preserve JSON field metadata when adding data (#4064) ## Summary - preserve Arrow field metadata when matching record data to a provided schema - retain metadata on partially reconstructed nested struct fields - add a regression test for lance.json metadata through Arrow IPC serialization ## Root cause The TypeScript schema inferrer rebuilt fields selected from a provided schema without copying their metadata. JSON columns therefore kept their LargeBinary physical type but lost the lance.json extension marker before insert, causing the schema mismatch reported in the issue. ## Validation - pnpm lint - pnpm build - pnpm tsc - pnpm run docs - pnpm test --runInBand (18 suites and 798 tests passed; 5 tests skipped) Fixes #4062 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> --- nodejs/__test__/arrow.test.ts | 21 +++++++++++++++++++++ nodejs/__test__/table.test.ts | 21 +++++++++++++++++++++ nodejs/lancedb/schema.ts | 3 ++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/nodejs/__test__/arrow.test.ts b/nodejs/__test__/arrow.test.ts index c5bbbf169..cb56cb5ae 100644 --- a/nodejs/__test__/arrow.test.ts +++ b/nodejs/__test__/arrow.test.ts @@ -6,6 +6,9 @@ import * as arrow17 from "apache-arrow-17"; import * as arrow18 from "apache-arrow-18"; import { + Field as CurrentField, + LargeBinary as CurrentLargeBinary, + Schema as CurrentSchema, Vector as CurrentVector, convertToTable, tableFromIPC as currentTableFromIPC, @@ -36,6 +39,24 @@ function sampleRecords(): Array> { }, ]; } + +it("preserves field metadata from a provided schema", async function () { + const jsonMetadata = new Map([["ARROW:extension:name", "lance.json"]]); + const schema = new CurrentSchema([ + new CurrentField("meta", new CurrentLargeBinary(), true, jsonMetadata), + ]); + + const table = makeArrowTable( + [{ meta: Buffer.from(JSON.stringify({ source: "test" })) }], + { schema }, + ); + + expect(table.schema.fields[0].metadata).toEqual(jsonMetadata); + + const roundTripped = currentTableFromIPC(await fromTableToBuffer(table)); + expect(roundTripped.schema.fields[0].metadata).toEqual(jsonMetadata); +}); + describe.each([arrow15, arrow16, arrow17, arrow18])( "Arrow", ( diff --git a/nodejs/__test__/table.test.ts b/nodejs/__test__/table.test.ts index dae640850..554c7fcd3 100644 --- a/nodejs/__test__/table.test.ts +++ b/nodejs/__test__/table.test.ts @@ -3561,6 +3561,27 @@ describe("when creating an empty table", () => { expect((actualSchema.fields[1].type as Float64).precision).toBe(2); }); + it("can add and query JSON data", async () => { + const schema = new Schema([ + new Field("id", new Int32(), true), + new Field( + "meta", + new Utf8(), + true, + new Map([["ARROW:extension:name", "arrow.json"]]), + ), + ]); + const table = await con.createEmptyTable("json", schema); + const meta = JSON.stringify({ x: 1 }); + + await table.add([{ id: 1, meta }]); + + const rows = await table.query().toArray(); + expect(rows).toHaveLength(1); + expect(rows[0].id).toBe(1); + expect(rows[0].meta).toBe(meta); + }); + it("can create an empty table from schema that specifies field types by name", async () => { const schemaLike = { fields: [ diff --git a/nodejs/lancedb/schema.ts b/nodejs/lancedb/schema.ts index e4749ef37..3a3ee9316 100644 --- a/nodejs/lancedb/schema.ts +++ b/nodejs/lancedb/schema.ts @@ -406,10 +406,11 @@ function matchingFields(fields: Field[], tree: FieldTree): Field[] { field.name, new Struct(matchingFields(struct.children, value)), field.nullable, + field.metadata, ), ); } else { - matches.push(new Field(field.name, value as DataType, field.nullable)); + matches.push(field); } } return matches; From ead4d27bfc60d6be42fbde15471aa55807b8bbbf Mon Sep 17 00:00:00 2001 From: Lance Release Date: Thu, 27 Aug 2026 04:31:22 +0000 Subject: [PATCH 02/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.10=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package-lock.json | 4 ++-- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 1c4aea809..2e0b78bf3 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.10" +current_version = "0.38.0-beta.11" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index e27f9f271..822689a0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,7 +5402,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" dependencies = [ "ahash", "anyhow", @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" dependencies = [ "arrow-array", "arrow-buffer", @@ -5515,7 +5515,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index 06dc267f3..1ce012522 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.10 + 0.38.0-beta.11 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 25e3b10e3..c6acc7dfe 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.10 + 0.38.0-beta.11 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index a2cec19c0..0b85b69df 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.10 + 0.38.0-beta.11 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index fd08e7a5e..6496c6384 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index ff6347c4d..38b3db7d7 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index ed99fee05..a256cb1ee 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 5b215dcc0..567b785b0 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index e0f5a9f26..4443a2748 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index d42541707..5c0710d56 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 496a40720..648c985f1 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index 734013343..1f3bfdeb8 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 732a7c01c..d46f08628 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lancedb/lancedb", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@lancedb/lancedb", - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "cpu": [ "x64", "arm64" diff --git a/nodejs/package.json b/nodejs/package.json index 262d4c4a7..665e2a522 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.10", + "version": "0.38.0-beta.11", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 3a8a05522..b97fad0ed 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index 8276e5bb3..a71e3c948 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.10" +version = "0.38.0-beta.11" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From 2deccf21cf4ec30e915584c44065c2275614653b Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 13:42:30 +0800 Subject: [PATCH 03/32] fix(node): read Python embedding metadata (#3836) ## Summary - normalize Python snake_case and TypeScript camelCase embedding metadata - use the normalized metadata for schema validation and embedding lookup - cover appending through `Table.add()` with a Python-authored schema fixture ## Root cause Python writes embedding source and vector column names as `source_column` and `vector_column`, but the TypeScript SDK only read `sourceColumn` and `vectorColumn`. The missing source name reached the add path as `undefined`, preventing JavaScript rows from being embedded and appended. ## Validation - `pnpm lint` - `pnpm test __test__/embedding.test.ts __test__/arrow.test.ts __test__/registry.test.ts --runInBand` (201 passed, 1 skipped) - `pnpm build` - `pnpm run docs` Fixes #1289 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo --- nodejs/__test__/embedding.test.ts | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/nodejs/__test__/embedding.test.ts b/nodejs/__test__/embedding.test.ts index 2a8494e0f..45d171a3d 100644 --- a/nodejs/__test__/embedding.test.ts +++ b/nodejs/__test__/embedding.test.ts @@ -187,6 +187,58 @@ describe("embedding functions", () => { const vector0 = JSON.parse(JSON.stringify(arr[0].vector)); expect(vector0).toEqual([1, 2, 3]); }); + it("should append multiple Python embeddings with the same alias", async () => { + @register("python-mock") + // biome-ignore lint/correctness/noUnusedVariables: the decorator registers this class + class MockEmbeddingFunction extends EmbeddingFunction { + ndims() { + return 3; + } + embeddingDataType(): Float { + return new Float32(); + } + async computeQueryEmbeddings(_data: string) { + return [1, 2, 3]; + } + async computeSourceEmbeddings(data: string[]) { + return data.map((value) => + value === "hello world" ? [1, 2, 3] : [4, 5, 6], + ); + } + } + + const metadata = new Map([ + [ + "embedding_functions", + '[{"source_column":"text1","vector_column":"vector1","name":"python-mock","model":{}},{"source_column":"text2","vector_column":"vector2","name":"python-mock","model":{}}]', + ], + ]); + const schema = new Schema( + [ + new Field("text1", new Utf8(), true), + new Field("text2", new Utf8(), true), + new Field( + "vector1", + new FixedSizeList(3, new Field("item", new Float32(), true)), + true, + ), + new Field( + "vector2", + new FixedSizeList(3, new Field("item", new Float32(), true)), + true, + ), + ], + metadata, + ); + + const db = await connect(tmpDir.name); + const table = await db.createEmptyTable("test", schema); + await table.add([{ text1: "hello world", text2: "goodbye world" }]); + + const rows = await table.query().toArray(); + expect(JSON.parse(JSON.stringify(rows[0].vector1))).toEqual([1, 2, 3]); + expect(JSON.parse(JSON.stringify(rows[0].vector2))).toEqual([4, 5, 6]); + }); it("should append generated vectors to a non-nullable schema", async () => { @register("non_nullable_schema_test") From d24b2dcacc4a3cbf8b62948941baba38dce5ce9a Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:47:18 +0800 Subject: [PATCH 04/32] fix: show nested fields in query schema errors (#3849) ## Summary - enrich local query field-not-found errors with recursively qualified Arrow struct leaf paths - preserve all other Lance and DataFusion errors unchanged - add a regression test for the Python-visible filter error described in the issue ## Root cause DataFusion builds `FieldNotFound` candidates from the top-level Arrow schema even though Lance supports dotted struct-field filters. As a result, the error listed only the struct container and hid its valid nested leaves. ## Validation - `cargo test --quiet --features remote -p lancedb table::query::tests::test_missing_filter_field_lists_nested_fields -- --exact` - `cargo check --quiet --features remote --tests --examples` - `cargo clippy --quiet --features remote --tests --examples` (passes with pre-existing unrelated warnings) - `cargo fmt --all -- --check` Fixes #951 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo --- rust/lancedb/src/table/query.rs | 263 +++++++++++++++++++++++++++- rust/lancedb/src/table/query/lsm.rs | 24 ++- 2 files changed, 280 insertions(+), 7 deletions(-) diff --git a/rust/lancedb/src/table/query.rs b/rust/lancedb/src/table/query.rs index b413b2e17..6f6bbf372 100644 --- a/rust/lancedb/src/table/query.rs +++ b/rust/lancedb/src/table/query.rs @@ -17,6 +17,7 @@ use arrow::array::{AsArray, FixedSizeListBuilder, Float32Builder}; use arrow::datatypes::{Float32Type, UInt8Type}; use arrow_array::Array; use arrow_schema::{DataType, Schema}; +use datafusion_common::{Column, DataFusionError, SchemaError}; use datafusion_physical_plan::ExecutionPlan; use datafusion_physical_plan::projection::ProjectionExec; use datafusion_physical_plan::repartition::RepartitionExec; @@ -191,7 +192,7 @@ pub async fn create_plan( if query.query_vector.len() > 1 { if column.is_none() { // Infer a vector column with the same dimension of the query vector. - let arrow_schema = Schema::from(ds_ref.schema()); + let arrow_schema = Schema::from(schema); column = Some(default_vector_column( &arrow_schema, Some(query.query_vector[0].len() as i32), @@ -268,7 +269,7 @@ pub async fn create_plan( let column = if let Some(col) = column { col } else { - let arrow_schema = Schema::from(ds_ref.schema()); + let arrow_schema = Schema::from(schema); default_vector_column(&arrow_schema, Some(query_vector.len() as i32))? }; @@ -374,7 +375,97 @@ pub async fn create_plan( scanner.order_by(Some(order_by.clone()))?; } - Ok(scanner.create_plan().await?) + scanner + .create_plan() + .await + .map_err(|error| enrich_lance_field_not_found(error, schema)) +} + +/// Replace DataFusion's top-level field candidates with qualified leaf paths. +/// +/// DataFusion resolves nested fields but its `FieldNotFound` error only lists the +/// top-level Arrow fields. This makes a missing leaf look unavailable even when it +/// exists below a struct. Keep every other Lance/DataFusion error unchanged and +/// enrich only this one schema error at the LanceDB query boundary. +fn enrich_lance_field_not_found( + error: lance::Error, + schema: &lance_core::datatypes::Schema, +) -> Error { + let Some(field) = find_missing_field(&error) else { + return error.into(); + }; + field_not_found_error(field, &Schema::from(schema)) +} + +fn field_not_found_diagnostic( + error: &(dyn std::error::Error + 'static), + schema: &Schema, +) -> Option { + let field = find_missing_field(error)?; + Some(field_not_found_error(field, schema)) +} + +fn field_not_found_error(field: &Column, schema: &Schema) -> Error { + let valid_fields = leaf_field_paths(schema); + let mut message = format!("Schema error: No field named {}", field.quoted_flat_name()); + if !valid_fields.is_empty() { + message.push_str(". Valid fields are "); + message.push_str(&valid_fields.join(", ")); + } + message.push('.'); + + Error::InvalidInput { message } +} + +fn find_missing_field<'a>(error: &'a (dyn std::error::Error + 'static)) -> Option<&'a Column> { + if let Some(DataFusionError::SchemaError(schema_error, _)) = + error.downcast_ref::() + && let SchemaError::FieldNotFound { field, .. } = schema_error.as_ref() + { + return Some(field); + } + + error.source().and_then(find_missing_field) +} + +fn leaf_field_paths(schema: &Schema) -> Vec { + fn format_segment(segment: &str) -> String { + // Quote every segment instead of maintaining a SQL keyword list. Bare + // lowercase names such as `true` can be parsed as expressions rather + // than identifiers, while backticks preserve all field names in both + // local SQL parsers. + format!("`{}`", segment.replace('`', "``")) + } + + fn visit(fields: &arrow_schema::Fields, path: &mut Vec, paths: &mut Vec) { + for field in fields { + // Neither local planner can address an empty field-path segment, + // even when it is backtick-quoted. Do not advertise leaves beneath + // such a segment as valid filter fields. + if field.name().is_empty() { + continue; + } + path.push(field.name().clone()); + match field.data_type() { + DataType::Struct(children) if !children.is_empty() => { + visit(children, path, paths); + } + _ => { + paths.push( + path.iter() + .map(|segment| format_segment(segment)) + .collect::>() + .join("."), + ); + } + } + path.pop(); + } + } + + let mut paths = Vec::new(); + visit(schema.fields(), &mut Vec::new(), &mut paths); + paths } //Helper functions below @@ -734,7 +825,10 @@ async fn parse_arrow_ipc_response(bytes: bytes::Bytes) -> Result, dimension: i32) -> FixedSizeListArray { @@ -884,7 +978,6 @@ mod tests { async fn test_execute_query_local_routing() { use crate::connect; use crate::table::query::execute_query; - use arrow_array::{Int32Array, RecordBatch}; use arrow_schema::{DataType, Field, Schema}; let conn = connect("memory://").execute().await.unwrap(); @@ -924,6 +1017,164 @@ mod tests { assert_eq!(count, 2); // 4 and 5 } + #[tokio::test] + async fn test_missing_filter_field_lists_nested_fields_in_local_planners() { + use crate::connect; + use arrow_schema::{DataType, Field, Schema}; + + let conn = connect("memory://").execute().await.unwrap(); + let metadata = Arc::new(StructArray::from(vec![ + ( + Arc::new(Field::new("year", DataType::Int32, false)), + Arc::new(Int32Array::from(vec![2024])) as ArrayRef, + ), + ( + Arc::new(Field::new("genre", DataType::Utf8, false)), + Arc::new(StringArray::from(vec!["fiction"])) as ArrayRef, + ), + ( + Arc::new(Field::new("Title", DataType::Int32, false)), + Arc::new(Int32Array::from(vec![7])) as ArrayRef, + ), + ( + Arc::new(Field::new("true", DataType::Int32, false)), + Arc::new(Int32Array::from(vec![8])) as ArrayRef, + ), + ( + Arc::new(Field::new("", DataType::Int32, false)), + Arc::new(Int32Array::from(vec![10])) as ArrayRef, + ), + ])); + let vector = Arc::new(fixed_size_list_array(vec![0.0, 1.0], 2)); + let schema = Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("vector", vector.data_type().clone(), false), + Field::new("content", DataType::Utf8, false), + Field::new("metadata", metadata.data_type().clone(), false), + ])); + let batch = RecordBatch::try_new( + schema, + vec![ + Arc::new(Int32Array::from(vec![1])), + vector, + Arc::new(StringArray::from(vec!["example"])), + metadata, + ], + ) + .unwrap(); + let table = conn + .create_table("nested_error", batch) + .execute() + .await + .unwrap(); + + let error = table + .query() + .only_if("year = 2024") + .execute() + .await + .err() + .expect("query should reject the unqualified nested field"); + let case_sensitive_path = "`metadata`.`Title`"; + let keyword_path = "`metadata`.`true`"; + let expected = format!( + "No field named year. Valid fields are `id`, `vector`, `content`, `metadata`.`year`, `metadata`.`genre`, {case_sensitive_path}, {keyword_path}." + ); + + assert!( + error.to_string().contains(&expected), + "unexpected error: {error}" + ); + for (path, value) in [(case_sensitive_path, 7), (keyword_path, 8)] { + table + .query() + .only_if(format!("{path} = {value}")) + .execute() + .await + .expect("the path advertised by the diagnostic should be reusable"); + } + + table.set_unenforced_primary_key(["id"]).await.unwrap(); + table + .set_lsm_write_spec(crate::table::LsmWriteSpec::unsharded()) + .await + .unwrap(); + let lsm_error = table + .query() + .only_if("year = 2024") + .execute() + .await + .err() + .expect("LSM query should reject the unqualified nested field"); + + assert!( + lsm_error.to_string().contains(&expected), + "unexpected LSM error: {lsm_error}" + ); + for (path, value) in [(case_sensitive_path, 7), (keyword_path, 8)] { + table + .query() + .only_if(format!("{path} = {value}")) + .execute() + .await + .expect("the path advertised by the diagnostic should be reusable in LSM queries"); + } + } + + #[test] + fn test_leaf_field_paths_preserve_arbitrary_depth() { + use arrow_schema::{DataType, Field, Schema}; + + fn nested_field(path: &[&str]) -> Field { + let mut segments = path.iter().rev(); + let mut field = Field::new( + *segments.next().expect("path must have a leaf"), + DataType::Int32, + false, + ); + for segment in segments { + field = Field::new(*segment, DataType::Struct(vec![field].into()), false); + } + field + } + + let schema = Schema::new(vec![ + nested_field(&["a", "b", "c", "d", "e"]), + nested_field(&["metadata", "child.with.dot"]), + nested_field(&["metadata", "Title"]), + nested_field(&["metadata", "123child"]), + nested_field(&["metadata", "child`tick"]), + nested_field(&["metadata", ""]), + nested_field(&["", "child"]), + ]); + + assert_eq!( + leaf_field_paths(&schema), + vec![ + "`a`.`b`.`c`.`d`.`e`", + "`metadata`.`child.with.dot`", + "`metadata`.`Title`", + "`metadata`.`123child`", + "`metadata`.`child``tick`", + ] + ); + + let source = DataFusionError::SchemaError( + Box::new(SchemaError::FieldNotFound { + field: Box::new(Column::from_name("missing")), + valid_fields: Vec::new(), + }), + Box::new(None), + ); + let error = field_not_found_diagnostic(&source, &schema).unwrap(); + assert!( + error.to_string().contains( + "Valid fields are `a`.`b`.`c`.`d`.`e`, `metadata`.`child.with.dot`, `metadata`.`Title`, `metadata`.`123child`, `metadata`.`child``tick`" + ), + "unexpected error: {error}" + ); + } + #[derive(Debug, Default)] struct CountingNamespaceClient { query_table_calls: AtomicUsize, diff --git a/rust/lancedb/src/table/query/lsm.rs b/rust/lancedb/src/table/query/lsm.rs index 5c340cc35..86c1fe5f2 100644 --- a/rust/lancedb/src/table/query/lsm.rs +++ b/rust/lancedb/src/table/query/lsm.rs @@ -27,6 +27,8 @@ use std::sync::Arc; use arrow_array::Array; use arrow_schema::{DataType, Schema as ArrowSchema}; +use datafusion::common::{DataFusionError, ToDFSchema}; +use datafusion::prelude::SessionContext; use datafusion_physical_plan::expressions::Column; use datafusion_physical_plan::projection::ProjectionExec; use datafusion_physical_plan::{ExecutionPlan, PhysicalExpr}; @@ -391,7 +393,21 @@ fn base_scanner( } if let Some(filter) = &query.base.filter { scanner = match filter { - QueryFilter::Sql(sql) => scanner.filter(sql)?, + QueryFilter::Sql(sql) => { + // Parse here instead of inside `LsmScanner::filter` so the typed + // DataFusion `FieldNotFound` error is still available for the + // same nested-field enrichment used by the ordinary scanner. + let schema = ArrowSchema::from(dataset.schema()); + let df_schema = schema.clone().to_dfschema().map_err(|error| { + enrich_filter_error(error, &schema, "Failed to create DFSchema") + })?; + let expr = SessionContext::new() + .parse_sql_expr(sql, &df_schema) + .map_err(|error| { + enrich_filter_error(error, &schema, "Failed to parse filter expression") + })?; + scanner.filter_expr(expr) + } QueryFilter::Datafusion(expr) => scanner.filter_expr(expr.clone()), QueryFilter::Substrait(_) => { return Err(Error::NotSupported { @@ -403,6 +419,12 @@ fn base_scanner( Ok(scanner) } +fn enrich_filter_error(error: DataFusionError, schema: &ArrowSchema, context: &str) -> Error { + super::field_not_found_diagnostic(&error, schema).unwrap_or_else(|| Error::InvalidInput { + message: format!("{context}: {error}"), + }) +} + /// Plain scan: filter / projection / limit over base ∪ SSTables ∪ in-memory. /// The plain scan applies limit and offset inside the planner. async fn plain_plan( From 0dd9dfdfc745f002afd24facc918744a8fe1ccfc Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:10:51 +0800 Subject: [PATCH 05/32] test(python): cover arithmetic with distance projections (#3862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - add Python regression coverage for integer and double arithmetic against the generated _distance column - merge the current main base containing Lance v11.0.0-beta.3 from #3896 - verify both expressions retain the generated scoring field Float32 type and compute the expected values ## Root cause Lance parsed dynamic projection expressions before vector search added its generated Float32 _distance field. Without a typed provisional field, expression discovery rejected mixed numeric arithmetic. Lance upstream fixed discovery and final-schema replanning in lance-format/lance#8163, and the current base consumes that fix through Lance v11.0.0-beta.3. ## Validation - uv run --extra tests pytest python/tests/test_query.py::test_select_arithmetic_with_distance -vv --maxfail=2 — 2 passed - python/.venv/bin/ruff format --check python/python/tests/test_query.py - python/.venv/bin/ruff check . Fixes #2618 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo --- python/python/tests/test_query.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/python/tests/test_query.py b/python/python/tests/test_query.py index 4758f0e2d..ff62b2b51 100644 --- a/python/python/tests/test_query.py +++ b/python/python/tests/test_query.py @@ -675,6 +675,21 @@ def test_distance_range(table: lancedb.table.Table): assert res["_distance"].to_pylist() == [min_dist, max_dist] +@pytest.mark.parametrize("expression", ["1 - _distance", "1.0 - _distance"]) +def test_select_arithmetic_with_distance(table, expression): + result = ( + table.search([10, 10]) + .select({"similarity": expression, "_distance": "_distance"}) + .distance_type("cosine") + .to_arrow() + ) + + assert result.schema.field("similarity").type == pa.float32() + assert result["similarity"].to_pylist() == pytest.approx( + [1 - distance for distance in result["_distance"].to_pylist()] + ) + + @pytest.mark.asyncio async def test_distance_range_async(table_async: AsyncTable): q = [0, 0] From 25645d82d42e27fc4db8c386aa3decac7b4f2f97 Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:28:21 +0800 Subject: [PATCH 06/32] feat(python): accept expressions in update filters (#3876) ## Summary - allow Python sync, async, and remote table updates to accept type-safe `Expr` filters - serialize expression filters before invoking the existing update implementation - cover numeric-looking text and apostrophe-containing text in sync and async regression tests ## Root cause `Table.update` was the remaining Python write path that required callers to construct a raw SQL predicate. Dynamic text interpolated without SQL literal encoding could therefore be parsed as an integer, float, or unterminated string instead of Utf8. The expression API already encodes literals safely for query and delete filters. ## Validation - `cd python && .venv/bin/pytest python/tests/test_table.py::test_update_async python/tests/test_table.py::test_update_expr_filter_literals_async python/tests/test_table.py::test_update python/tests/test_table.py::test_update_expr_filter_literals -q` - `cd python && .venv/bin/pytest python/tests/test_expr.py -q` - `cd python && .venv/bin/ruff format --check .` - `cd python && .venv/bin/ruff check .` Fixes #1869 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo --- python/python/lancedb/_blob.py | 6 +- python/python/lancedb/_lancedb.pyi | 2 + python/python/lancedb/expr.py | 6 +- python/python/lancedb/query.py | 16 +- python/python/lancedb/remote/table.py | 11 +- python/python/lancedb/table.py | 42 +++-- python/python/tests/test_blob.py | 73 ++++++- python/python/tests/test_expr.py | 42 ++--- python/python/tests/test_table.py | 158 ++++++++++++++++ python/src/expr.rs | 8 + python/src/query.rs | 23 +++ rust/lancedb/src/expr.rs | 124 +++++++++++- rust/lancedb/src/expr/sql.rs | 262 +++++++++++++++++++++----- 13 files changed, 678 insertions(+), 95 deletions(-) diff --git a/python/python/lancedb/_blob.py b/python/python/lancedb/_blob.py index 926769f48..5b4c0c343 100644 --- a/python/python/lancedb/_blob.py +++ b/python/python/lancedb/_blob.py @@ -270,7 +270,8 @@ def _iter_projection_pairs( if isinstance(expr, str): yield name, expr elif isinstance(expr, Expr): - yield name, expr.to_sql() + source = expr._column_name() + yield name, source if source is not None else expr.to_sql() return for column in projection: if isinstance(column, str): @@ -280,7 +281,8 @@ def _iter_projection_pairs( if isinstance(expr, str): yield name, expr elif isinstance(expr, Expr): - yield name, expr.to_sql() + source = expr._column_name() + yield name, source if source is not None else expr.to_sql() def _set_blob_column(tbl: pa.Table, output_name: str, blobs: pa.Array) -> pa.Table: diff --git a/python/python/lancedb/_lancedb.pyi b/python/python/lancedb/_lancedb.pyi index 593bceffa..7d7ca7f2a 100644 --- a/python/python/lancedb/_lancedb.pyi +++ b/python/python/lancedb/_lancedb.pyi @@ -87,6 +87,7 @@ class PyExpr: def contains(self, substr: "PyExpr") -> "PyExpr": ... def isin(self, values: List["PyExpr"]) -> "PyExpr": ... def cast(self, data_type: pa.DataType) -> "PyExpr": ... + def column_name(self) -> Optional[str]: ... def to_sql(self) -> str: ... def expr_col(name: str) -> PyExpr: ... @@ -608,6 +609,7 @@ class PyQueryRequest: filter: Optional[Union[str, bytes]] full_text_search: Optional[FullTextQuery] select: Optional[Union[str, List[str]]] + select_source_columns: Optional[Dict[str, str]] fast_search: Optional[bool] with_row_id: Optional[bool] use_lsm: Optional[bool] diff --git a/python/python/lancedb/expr.py b/python/python/lancedb/expr.py index d16ba95d7..80d01e29a 100644 --- a/python/python/lancedb/expr.py +++ b/python/python/lancedb/expr.py @@ -249,6 +249,10 @@ class Expr: # ── utilities ──────────────────────────────────────────────────────────── + def _column_name(self) -> str | None: + """Return the source name when this is a bare column expression.""" + return self._inner.column_name() + def to_sql(self) -> str: """Render the expression as a SQL string (useful for debugging).""" return self._inner.to_sql() @@ -312,7 +316,7 @@ def func(name: str, *args: ExprLike) -> Expr: -------- >>> from lancedb.expr import col, func >>> func("lower", col("name")) - Expr(lower(name)) + Expr(lower(`name`)) """ inner_args = [_coerce(a)._inner for a in args] return Expr(expr_func(name, inner_args)) diff --git a/python/python/lancedb/query.py b/python/python/lancedb/query.py index 9301d7df8..451384ad1 100644 --- a/python/python/lancedb/query.py +++ b/python/python/lancedb/query.py @@ -167,6 +167,12 @@ def _projection_to_scanner_kwargs(columns: QueryProjection) -> Dict[str, Any]: return {"columns": projection} +def _query_request_projection(req: "PyQueryRequest") -> QueryProjection: + if req.select_source_columns is not None: + return req.select_source_columns + return req.select + + def _scanner_kwargs_for_query( query: Query, blob_mode: BlobMode, @@ -2799,15 +2805,16 @@ class AsyncQueryBase(object): req = self._inner.to_query_request() schema = await self._table.schema() + projection = _query_request_projection(req) self._blob_auto_row_id = blob_auto_row_id_for_scan( schema, - req.select, + projection, with_row_id=self._with_row_id, ) if not self._blob_auto_row_id: self._blob_paths = () return - self._blob_paths = tuple(blob_v2_projection_sources(schema, req.select).keys()) + self._blob_paths = tuple(blob_v2_projection_sources(schema, projection).keys()) self._inner.with_row_id() def select(self, columns: Union[List[str], dict[str, str]]) -> Self: @@ -3894,14 +3901,15 @@ class AsyncHybridQuery(AsyncStandardQuery, AsyncVectorQueryBase): blob_paths: tuple[str, ...] = () if self._table is not None: schema = await self._table.schema() + projection = _query_request_projection(req) blob_auto_row_id = blob_auto_row_id_for_scan( schema, - req.select, + projection, with_row_id=self._with_row_id, ) if blob_auto_row_id: blob_paths = tuple( - blob_v2_projection_sources(schema, req.select).keys() + blob_v2_projection_sources(schema, projection).keys() ) self._blob_auto_row_id = blob_auto_row_id self._blob_paths = blob_paths diff --git a/python/python/lancedb/remote/table.py b/python/python/lancedb/remote/table.py index d9139396b..02748b9bc 100644 --- a/python/python/lancedb/remote/table.py +++ b/python/python/lancedb/remote/table.py @@ -36,6 +36,7 @@ from lancedb._lancedb import ( UpdateResult, ) from lancedb.embeddings.base import EmbeddingFunctionConfig +from lancedb.expr import Expr from lancedb.index import ( FTS, BTree, @@ -863,7 +864,7 @@ class RemoteTable(Table): def update( self, - where: Optional[str] = None, + where: Optional[Union[str, Expr]] = None, values: Optional[dict] = None, *, values_sql: Optional[Dict[str, str]] = None, @@ -874,9 +875,11 @@ class RemoteTable(Table): Parameters ---------- - where: str, optional - The SQL where clause to use when updating rows. For example, 'x = 2' - or 'x IN (1, 2, 3)'. The filter must not be empty, or it will error. + where: str or [Expr][lancedb.expr.Expr], optional + The filter condition. Can be a SQL string or a type-safe + [Expr][lancedb.expr.Expr] built with [col][lancedb.expr.col] and + [lit][lancedb.expr.lit]. The filter must not be empty, or it will + error. values: dict, optional The values to update. The keys are the column names and the values are the values to set. diff --git a/python/python/lancedb/table.py b/python/python/lancedb/table.py index c354a944e..765b7fa14 100644 --- a/python/python/lancedb/table.py +++ b/python/python/lancedb/table.py @@ -1744,7 +1744,7 @@ class Table(ABC): @abstractmethod def update( self, - where: Optional[str] = None, + where: Optional[Union[str, Expr]] = None, values: Optional[dict] = None, *, values_sql: Optional[Dict[str, str]] = None, @@ -1759,9 +1759,11 @@ class Table(ABC): Parameters ---------- - where: str, optional - The SQL where clause to use when updating rows. For example, 'x = 2' - or 'x IN (1, 2, 3)'. The filter must not be empty, or it will error. + where: str or [Expr][lancedb.expr.Expr], optional + The filter condition. Can be a SQL string or a type-safe + [Expr][lancedb.expr.Expr] built with [col][lancedb.expr.col] and + [lit][lancedb.expr.lit]. The filter must not be empty, or it will + error. values: dict, optional The values to update. The keys are the column names and the values are the values to set. @@ -1779,6 +1781,7 @@ class Table(ABC): Examples -------- >>> import lancedb + >>> from lancedb.expr import col >>> import pandas as pd >>> data = pd.DataFrame({"x": [1, 2, 3], "vector": [[1.0, 2], [3, 4], [5, 6]]}) >>> db = lancedb.connect("./.lancedb") @@ -1788,7 +1791,7 @@ class Table(ABC): 0 1 [1.0, 2.0] 1 2 [3.0, 4.0] 2 3 [5.0, 6.0] - >>> table.update(where="x = 2", values={"vector": [10.0, 10]}) + >>> table.update(where=col("x") == 2, values={"vector": [10.0, 10]}) UpdateResult(rows_updated=1, version=2) >>> table.to_pandas() x vector @@ -3841,7 +3844,7 @@ class LanceTable(Table): def update( self, - where: Optional[str] = None, + where: Optional[Union[str, Expr]] = None, values: Optional[dict] = None, *, values_sql: Optional[Dict[str, str]] = None, @@ -3852,9 +3855,11 @@ class LanceTable(Table): Parameters ---------- - where: str, optional - The SQL where clause to use when updating rows. For example, 'x = 2' - or 'x IN (1, 2, 3)'. The filter must not be empty, or it will error. + where: str or [Expr][lancedb.expr.Expr], optional + The filter condition. Can be a SQL string or a type-safe + [Expr][lancedb.expr.Expr] built with [col][lancedb.expr.col] and + [lit][lancedb.expr.lit]. The filter must not be empty, or it will + error. values: dict, optional The values to update. The keys are the column names and the values are the values to set. @@ -3872,6 +3877,7 @@ class LanceTable(Table): Examples -------- >>> import lancedb + >>> from lancedb.expr import col >>> import pandas as pd >>> data = pd.DataFrame({"x": [1, 2, 3], "vector": [[1.0, 2], [3, 4], [5, 6]]}) >>> db = lancedb.connect("./.lancedb") @@ -3881,7 +3887,7 @@ class LanceTable(Table): 0 1 [1.0, 2.0] 1 2 [3.0, 4.0] 2 3 [5.0, 6.0] - >>> table.update(where="x = 2", values={"vector": [10.0, 10]}) + >>> table.update(where=col("x") == 2, values={"vector": [10.0, 10]}) UpdateResult(rows_updated=1, version=2) >>> table.to_pandas() x vector @@ -5995,7 +6001,7 @@ class AsyncTable: self, updates: Optional[Dict[str, Any]] = None, *, - where: Optional[str] = None, + where: Optional[Union[str, Expr]] = None, updates_sql: Optional[Dict[str, str]] = None, ) -> UpdateResult: """ @@ -6010,9 +6016,11 @@ class AsyncTable: The updates to apply. The keys should be the name of the column to update. The values should be the new values to assign. This is required unless updates_sql is supplied. - where: str, optional - An SQL filter that controls which rows are updated. For example, 'x = 2' - or 'x IN (1, 2, 3)'. Only rows that satisfy this filter will be udpated. + where: str or [Expr][lancedb.expr.Expr], optional + The filter condition. Can be a SQL string or a type-safe + [Expr][lancedb.expr.Expr] built with [col][lancedb.expr.col] and + [lit][lancedb.expr.lit]. Only rows that satisfy this filter will + be updated. updates_sql: dict, optional The updates to apply, expressed as SQL expression strings. The keys should be column names. The values should be SQL expressions. These can be SQL @@ -6030,13 +6038,14 @@ class AsyncTable: -------- >>> import asyncio >>> import lancedb + >>> from lancedb.expr import col >>> import pandas as pd >>> async def demo_update(): ... data = pd.DataFrame({"x": [1, 2], "vector": [[1, 2], [3, 4]]}) ... db = await lancedb.connect_async("./.lancedb") ... table = await db.create_table("my_table", data) ... # x is [1, 2], vector is [[1, 2], [3, 4]] - ... await table.update({"vector": [10, 10]}, where="x = 2") + ... await table.update({"vector": [10, 10]}, where=col("x") == 2) ... # x is [1, 2], vector is [[1, 2], [10, 10]] ... await table.update(updates_sql={"x": "x + 1"}) ... # x is [2, 3], vector is [[1, 2], [10, 10]] @@ -6050,7 +6059,8 @@ class AsyncTable: if updates is not None: updates_sql = {k: value_to_sql(v) for k, v in updates.items()} - return await self._inner.update(updates_sql, where) + predicate = where.to_sql() if isinstance(where, Expr) else where + return await self._inner.update(updates_sql, predicate) async def add_columns( self, diff --git a/python/python/tests/test_blob.py b/python/python/tests/test_blob.py index 5d7682f24..351769ff6 100644 --- a/python/python/tests/test_blob.py +++ b/python/python/tests/test_blob.py @@ -8,7 +8,12 @@ import pyarrow.compute as pc import pytest import lancedb -from lancedb._blob import read_row_ids_from_hits, stash_auto_row_ids +from lancedb._blob import ( + blob_v2_projection_sources, + read_row_ids_from_hits, + stash_auto_row_ids, +) +from lancedb.expr import col from lancedb.index import FTS from lancedb.schema import blob_column_paths, blob_v2_column_paths @@ -70,6 +75,14 @@ def test_blob_v2_column_paths_include_list_children(): ] +def test_blob_v2_projection_sources_use_typed_column_name(): + schema = pa.schema([lancedb.blob("blob")]) + + assert blob_v2_projection_sources(schema, {"blob_alias": col("blob")}) == { + "blob_alias": "blob" + } + + def _legacy_v1_table(name): db = lancedb.connect("memory:///") schema = pa.schema( @@ -166,6 +179,20 @@ async def test_async_table_to_pandas_descriptions_mode_omits_row_id(): assert set(descriptor.keys()) == {"kind", "position", "size", "blob_id", "blob_uri"} +@pytest.mark.asyncio +async def test_async_typed_blob_projection_preserves_source_column(): + db = await lancedb.connect_async("memory:///typed_blob_projection") + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("blob")]) + table = await db.create_table("typed_blob_projection", schema=schema) + await table.add([{"id": 1, "blob": b"alpha"}]) + + hits = await table.query().select({"blob_alias": col("blob")}).to_arrow() + + assert "_lance_row_id" in hits.schema.field("blob_alias").type.names + blobs = await table.fetch_blobs("blob", hits) + assert blobs.to_pylist() == [b"alpha"] + + def test_fetch_blobs_round_trip(): table = _blob_table( "round_trip", @@ -403,6 +430,50 @@ async def test_blob_v2_hybrid_fetch_blobs_async(): assert {blobs[i].as_py() for i in range(len(blobs))} == {b"alpha", b"beta"} +@pytest.mark.asyncio +async def test_async_hybrid_typed_blob_projection_preserves_source_column(): + db = await lancedb.connect_async("memory:///hybrid_typed_blob") + schema = pa.schema( + [ + pa.field("id", pa.int64()), + pa.field("text", pa.utf8()), + pa.field("vector", pa.list_(pa.float32(), list_size=2)), + lancedb.blob("blob"), + ] + ) + table = await db.create_table("hybrid_typed_blob", schema=schema) + await table.add( + [ + { + "id": 1, + "text": "hello alpha", + "vector": [1.0, 0.0], + "blob": b"alpha", + }, + { + "id": 2, + "text": "hello beta", + "vector": [0.9, 0.1], + "blob": b"beta", + }, + ] + ) + await table.create_index("text", config=FTS(with_position=False)) + + hits = await ( + table.query() + .nearest_to([1.0, 0.0]) + .nearest_to_text("hello") + .select({"blob_alias": col("blob")}) + .limit(2) + .to_arrow() + ) + + assert "_lance_row_id" in hits.schema.field("blob_alias").type.names + blobs = await table.fetch_blobs("blob", hits) + assert {blobs[i].as_py() for i in range(len(blobs))} == {b"alpha", b"beta"} + + def test_blob_file_seek_read_and_read_range(): payload = _identifiable_payload(1024) table = _blob_table("seek_read", [{"id": 1, "image": payload}]) diff --git a/python/python/tests/test_expr.py b/python/python/tests/test_expr.py index 0eb6f8929..0f49231f1 100644 --- a/python/python/tests/test_expr.py +++ b/python/python/tests/test_expr.py @@ -52,7 +52,7 @@ class TestExprConstruction: def test_func(self): e = func("lower", col("name")) assert isinstance(e, Expr) - assert e.to_sql() == "lower(name)" + assert e.to_sql() == "lower(`name`)" def test_func_unknown_raises(self): with pytest.raises(Exception): @@ -115,7 +115,7 @@ class TestExprOperators: def test_and_operator(self): e = (col("age") > lit(18)) & (col("status") == lit("active")) assert isinstance(e, Expr) - assert e.to_sql() == "((age > 18) AND (status = 'active'))" + assert e.to_sql() == "((age > 18) AND (`status` = 'active'))" def test_or_operator(self): e = (col("a") == lit(1)) | (col("b") == lit(2)) @@ -166,7 +166,7 @@ class TestExprOperators: def test_coerce_plain_str(self): e = col("name") == "alice" assert isinstance(e, Expr) - assert e.to_sql() == "(name = 'alice')" + assert e.to_sql() == "(`name` = 'alice')" def test_reflexive_comparisons(self): # 10 < col("age") swaps to col("age") > 10 @@ -198,85 +198,85 @@ class TestExprBytesLiteral: def test_bytes_equality_expr_sql(self): e = col("data") == lit(b"\xca\xfe") - assert e.to_sql() == "(data = X'CAFE')" + assert e.to_sql() == "(`data` = X'CAFE')" def test_bytes_ne_expr_sql(self): e = col("data") != lit(b"\xff") - assert e.to_sql() == "(data <> X'FF')" + assert e.to_sql() == "(`data` <> X'FF')" def test_bytes_compound_expr_sql(self): e = (col("data") == lit(b"\x01")) & (col("id") > lit(5)) - assert e.to_sql() == "((data = X'01') AND (id > 5))" + assert e.to_sql() == "((`data` = X'01') AND (id > 5))" def test_bytes_in_function_call(self): # Regression test: binary literals inside scalar function calls # used to fail because DataFusion's unparser does not support Binary # scalars. Now handled via a placeholder-substitution rewrite. e = func("contains", col("data"), lit(b"\xff")) - assert e.to_sql() == "contains(data, X'FF')" + assert e.to_sql() == "contains(`data`, X'FF')" def test_bytes_in_not(self): e = ~(col("data") == lit(b"\xff")) - assert e.to_sql() == "NOT (data = X'FF')" + assert e.to_sql() == "NOT (`data` = X'FF')" class TestExprStringMethods: def test_lower(self): e = col("name").lower() assert isinstance(e, Expr) - assert e.to_sql() == "lower(name)" + assert e.to_sql() == "lower(`name`)" def test_upper(self): e = col("name").upper() assert isinstance(e, Expr) - assert e.to_sql() == "upper(name)" + assert e.to_sql() == "upper(`name`)" def test_contains(self): e = col("text").contains(lit("hello")) assert isinstance(e, Expr) - assert e.to_sql() == "contains(text, 'hello')" + assert e.to_sql() == "contains(`text`, 'hello')" def test_contains_with_str_coerce(self): e = col("text").contains("hello") assert isinstance(e, Expr) - assert e.to_sql() == "contains(text, 'hello')" + assert e.to_sql() == "contains(`text`, 'hello')" def test_chained_lower_eq(self): e = col("name").lower() == lit("alice") assert isinstance(e, Expr) - assert e.to_sql() == "(lower(name) = 'alice')" + assert e.to_sql() == "(lower(`name`) = 'alice')" class TestExprCast: def test_cast_string(self): e = col("id").cast("string") assert isinstance(e, Expr) - assert e.to_sql() == "CAST(id AS VARCHAR)" + assert e.to_sql() == "arrow_cast(id, 'Utf8')" def test_cast_int32(self): e = col("score").cast("int32") assert isinstance(e, Expr) - assert e.to_sql() == "CAST(score AS INTEGER)" + assert e.to_sql() == "arrow_cast(score, 'Int32')" def test_cast_float64(self): e = col("val").cast("float64") assert isinstance(e, Expr) - assert e.to_sql() == "CAST(val AS DOUBLE)" + assert e.to_sql() == "arrow_cast(val, 'Float64')" def test_cast_pyarrow_type(self): e = col("score").cast(pa.int32()) assert isinstance(e, Expr) - assert e.to_sql() == "CAST(score AS INTEGER)" + assert e.to_sql() == "arrow_cast(score, 'Int32')" def test_cast_pyarrow_float64(self): e = col("val").cast(pa.float64()) assert isinstance(e, Expr) - assert e.to_sql() == "CAST(val AS DOUBLE)" + assert e.to_sql() == "arrow_cast(val, 'Float64')" def test_cast_pyarrow_string(self): e = col("id").cast(pa.string()) assert isinstance(e, Expr) - assert e.to_sql() == "CAST(id AS VARCHAR)" + assert e.to_sql() == "arrow_cast(id, 'Utf8')" def test_cast_pyarrow_and_string_equivalent(self): # pa.int32() and "int32" should produce equivalent SQL @@ -597,14 +597,14 @@ class TestExprIsin: def test_isin_strs(self): assert ( col("status").isin(["active", "pending"]).to_sql() - == "status IN ('active', 'pending')" + == "`status` IN ('active', 'pending')" ) def test_isin_coerces_and_mixes(self): assert col("id").isin([lit(1), 2]).to_sql() == "id IN (1, 2)" def test_isin_empty(self): - assert col("id").isin([]).to_sql() == "id IN ()" + assert col("id").isin([]).to_sql() == "false" def test_isin_filter(self, simple_table): result = simple_table.search().where(col("id").isin([1, 3, 5])).to_arrow() diff --git a/python/python/tests/test_table.py b/python/python/tests/test_table.py index 56e0eacfd..0be9e139d 100644 --- a/python/python/tests/test_table.py +++ b/python/python/tests/test_table.py @@ -11,6 +11,7 @@ import warnings import weakref from concurrent.futures import ThreadPoolExecutor from datetime import date, datetime, timedelta +from decimal import Decimal from time import sleep from typing import List from unittest.mock import patch @@ -336,6 +337,21 @@ async def test_update_async(mem_db_async: AsyncConnection): assert await table.count_rows("id == 10") == 1 +@pytest.mark.asyncio +async def test_update_expr_filter_literals_async(mem_db_async: AsyncConnection): + values = ["5", "4.66e-84", "it's"] + table = await mem_db_async.create_table( + "update_expr_literals", + data=[{"field": value, "result": "original"} for value in values], + ) + + for value in values: + update_res = await table.update({"result": value}, where=col("field") == value) + assert update_res.rows_updated == 1 + + assert (await table.to_arrow())["result"].to_pylist() == values + + def test_create_table(mem_db: DBConnection): schema = pa.schema( { @@ -2343,6 +2359,148 @@ def test_update(mem_db: DBConnection): assert np.allclose(v, np.array([[1.2, 1.9], [1.1, 1.1]])) +def test_update_expr_filter_literals(mem_db: DBConnection): + values = ["5", "4.66e-84", "it's"] + table = mem_db.create_table( + "update_expr_literals", + data=[{"field": value, "result": "original"} for value in values], + ) + + for value in values: + update_res = table.update(where=col("field") == value, values={"result": value}) + assert update_res.rows_updated == 1 + + assert table.to_arrow()["result"].to_pylist() == values + + +def test_update_expr_filter_preserves_typed_semantics(mem_db: DBConnection): + low = Decimal("1.234567890123456789") + high = Decimal("1.234567890123456790") + decimal_schema = pa.schema( + [("val", pa.decimal128(19, 18)), ("result", pa.string())] + ) + decimal_table = mem_db.create_table( + "update_expr_decimal", + pa.table( + {"val": [low, high], "result": ["old", "old"]}, + schema=decimal_schema, + ), + ) + predicate = col("val") < lit(high) + assert decimal_table.search().where(predicate).to_arrow().num_rows == 1 + result = decimal_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + keyword_table = mem_db.create_table( + "update_expr_keyword", [{"null": 1, "result": "old"}] + ) + predicate = col("null") == 1 + assert keyword_table.search().where(predicate).to_arrow().num_rows == 1 + result = keyword_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + empty_in_table = mem_db.create_table( + "update_expr_empty_in", [{"id": 1, "result": "old"}] + ) + predicate = col("id").isin([]) + assert empty_in_table.search().where(predicate).to_arrow().num_rows == 0 + result = empty_in_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 0 + + marker = "__lancedb_binary_placeholder_0__" + binary_schema = pa.schema( + [("payload", pa.binary()), ("text", pa.string()), ("result", pa.string())] + ) + binary_table = mem_db.create_table( + "update_expr_binary", + pa.table( + { + "payload": [b"\x01", b"\x02"], + "text": ["other", marker], + "result": ["old", "old"], + }, + schema=binary_schema, + ), + ) + predicate = (col("payload") == lit(b"\x01")) | (col("text") == marker) + assert binary_table.search().where(predicate).to_arrow().num_rows == 2 + result = binary_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 2 + + nonfinite_table = mem_db.create_table( + "update_expr_nonfinite", + [{"x": 1.0, "result": "old"}, {"x": 2.0, "result": "old"}], + ) + predicate = col("x") < float("inf") + assert nonfinite_table.search().where(predicate).to_arrow().num_rows == 2 + result = nonfinite_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 2 + + float16_table = mem_db.create_table( + "update_expr_float16", + [{"x": 1.0, "result": "old"}, {"x": 3.0, "result": "old"}], + ) + predicate = col("x").cast(pa.float16()) < 2.0 + assert float16_table.search().where(predicate).to_arrow().num_rows == 1 + result = float16_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + string_cast_table = mem_db.create_table( + "update_expr_string_cast", + [{"x": 1, "result": "old"}, {"x": 2, "result": "old"}], + ) + predicate = col("x").cast("string") == "1" + assert string_cast_table.search().where(predicate).to_arrow().num_rows == 1 + result = string_cast_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + quoted_identifier_schema = pa.schema( + [("payload", pa.binary()), ("odd'name", pa.int64()), ("result", pa.string())] + ) + quoted_identifier_table = mem_db.create_table( + "update_expr_quoted_identifier", + pa.table( + {"payload": [b"\x01"], "odd'name": [1], "result": ["old"]}, + schema=quoted_identifier_schema, + ), + ) + predicate = (col("payload") == lit(b"\x01")) & (col("odd'name") == 1) + assert quoted_identifier_table.search().where(predicate).to_arrow().num_rows == 1 + result = quoted_identifier_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + decimal256_schema = pa.schema( + [("val", pa.decimal256(40, 2)), ("result", pa.string())] + ) + decimal256_table = mem_db.create_table( + "update_expr_decimal256", + pa.table( + { + "val": [Decimal("1.00"), Decimal("3.00")], + "result": ["old", "old"], + }, + schema=decimal256_schema, + ), + ) + predicate = col("val") < lit(Decimal("2.00")).cast(pa.decimal256(40, 2)) + assert decimal256_table.search().where(predicate).to_arrow().num_rows == 1 + result = decimal256_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 1 + + binary_empty_table = mem_db.create_table( + "update_expr_binary_empty", + pa.table( + {"payload": [b"\x01", b"\x02"], "result": ["old", "old"]}, + schema=pa.schema([("payload", pa.binary()), ("result", pa.string())]), + ), + ) + predicate = (col("payload") == lit(b"\x01")).isin([]) + assert binary_empty_table.search().where(predicate).to_arrow().num_rows == 0 + assert predicate.to_sql() == "false" + result = binary_empty_table.update(where=predicate, values={"result": "new"}) + assert result.rows_updated == 0 + + def test_update_with_arrow_scalar(mem_db: DBConnection): schema = pa.schema({"id": pa.int64(), "vector": pa.list_(pa.float32(), 4)}) table = mem_db.create_table("my_table", schema=schema) diff --git a/python/src/expr.rs b/python/src/expr.rs index eae1d96ec..79b448fdf 100644 --- a/python/src/expr.rs +++ b/python/src/expr.rs @@ -130,6 +130,14 @@ impl PyExpr { // ── utilities ──────────────────────────────────────────────────────────── + /// Return the referenced column name for a bare column expression. + fn column_name(&self) -> Option { + match &self.0 { + DfExpr::Column(column) if column.relation.is_none() => Some(column.name.clone()), + _ => None, + } + } + /// Render the expression as a SQL string (useful for debugging). fn to_sql(&self) -> PyResult { lancedb::expr::expr_to_sql_string(&self.0).map_err(|e| PyValueError::new_err(e.to_string())) diff --git a/python/src/query.rs b/python/src/query.rs index 014e79e2d..38153729f 100644 --- a/python/src/query.rs +++ b/python/src/query.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The LanceDB Authors +use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; @@ -325,6 +326,7 @@ pub struct PyQueryRequest { pub filter: Option, pub full_text_search: Option>, pub select: PySelect, + pub select_source_columns: Option>, pub fast_search: Option, pub with_row_id: Option, pub use_lsm: Option, @@ -355,6 +357,7 @@ impl From for PyQueryRequest { full_text_search: query_request .full_text_search .map(|fts| PyLanceDB(fts.query)), + select_source_columns: PySelect::source_columns(&query_request.select), select: PySelect(query_request.select), fast_search: Some(query_request.fast_search), with_row_id: Some(query_request.with_row_id), @@ -380,6 +383,7 @@ impl From for PyQueryRequest { offset: vector_query.base.offset, filter: vector_query.base.filter.map(PyQueryFilter), full_text_search: None, + select_source_columns: PySelect::source_columns(&vector_query.base.select), select: PySelect(vector_query.base.select), fast_search: Some(vector_query.base.fast_search), with_row_id: Some(vector_query.base.with_row_id), @@ -412,6 +416,25 @@ impl From for PyQueryRequest { #[derive(Clone)] pub struct PySelect(Select); +impl PySelect { + fn source_columns(select: &Select) -> Option> { + match select { + Select::Expr(pairs) => Some( + pairs + .iter() + .filter_map(|(output, expr)| match expr { + lancedb::expr::DfExpr::Column(column) if column.relation.is_none() => { + Some((output.clone(), column.name.clone())) + } + _ => None, + }) + .collect(), + ), + _ => None, + } + } +} + impl<'py> IntoPyObject<'py> for PySelect { type Target = PyAny; type Output = Bound<'py, Self::Target>; diff --git a/rust/lancedb/src/expr.rs b/rust/lancedb/src/expr.rs index da69914e3..1625d9632 100644 --- a/rust/lancedb/src/expr.rs +++ b/rust/lancedb/src/expr.rs @@ -157,7 +157,7 @@ mod tests { use datafusion_common::ScalarValue; let expr = col("data").eq(lit(ScalarValue::Binary(Some(vec![0xca, 0xfe])))); let sql = expr_to_sql_string(&expr).unwrap(); - assert_eq!(sql, "(data = X'CAFE')"); + assert_eq!(sql, "(`data` = X'CAFE')"); } #[test] @@ -167,7 +167,7 @@ mod tests { let int_expr = col("id").gt(lit(5i64)); let combined = bin_expr.and(int_expr); let sql = expr_to_sql_string(&combined).unwrap(); - assert_eq!(sql, "((data = X'01') AND (id > 5))"); + assert_eq!(sql, "((`data` = X'01') AND (id > 5))"); } #[test] @@ -185,7 +185,7 @@ mod tests { // serialized correctly (regression test for placeholder rewrite path). let expr = contains(col("data"), lit(ScalarValue::Binary(Some(vec![0xff])))); let sql = expr_to_sql_string(&expr).unwrap(); - assert_eq!(sql, "contains(data, X'FF')"); + assert_eq!(sql, "contains(`data`, X'FF')"); } #[test] @@ -196,7 +196,7 @@ mod tests { .eq(lit(ScalarValue::Binary(Some(vec![0xab, 0xcd])))) .not(); let sql = expr_to_sql_string(&expr).unwrap(); - assert_eq!(sql, "NOT (data = X'ABCD')"); + assert_eq!(sql, "NOT (`data` = X'ABCD')"); } #[test] @@ -206,6 +206,122 @@ mod tests { assert!(sql.contains("IN"), "expected IN in: {}", sql); } + #[test] + fn test_empty_is_in() { + let expr = is_in(col("id"), vec![]); + assert_eq!(expr_to_sql_string(&expr).unwrap(), "false"); + } + + #[test] + fn test_empty_is_in_discards_binary_children() { + use datafusion_common::ScalarValue; + + let expr = is_in( + col("payload").eq(lit(ScalarValue::Binary(Some(vec![0x01])))), + vec![], + ); + assert_eq!(expr_to_sql_string(&expr).unwrap(), "false"); + } + + #[test] + fn test_keyword_identifier() { + let expr = col("null").eq(lit(1i64)); + assert_eq!(expr_to_sql_string(&expr).unwrap(), "(`null` = 1)"); + } + + #[test] + fn test_decimal_literal_preserves_type() { + use datafusion_common::ScalarValue; + + let expr = col("val").lt(lit(ScalarValue::Decimal128( + Some(1_234_567_890_123_456_790), + 19, + 18, + ))); + let sql = expr_to_sql_string(&expr).unwrap(); + assert_eq!( + sql, + "(val < arrow_cast('1.234567890123456790', 'Decimal128(19, 18)'))" + ); + } + + #[test] + fn test_non_finite_float_literal_preserves_type() { + let expr = col("x").lt(lit(f64::INFINITY)); + assert_eq!( + expr_to_sql_string(&expr).unwrap(), + "(x < arrow_cast('inf', 'Float64'))" + ); + } + + #[test] + fn test_cast_uses_arrow_type_name() { + let string = expr_cast(col("x"), DataType::Utf8); + assert_eq!( + expr_to_sql_string(&string).unwrap(), + "arrow_cast(x, 'Utf8')" + ); + + let int32 = expr_cast(col("x"), DataType::Int32); + assert_eq!( + expr_to_sql_string(&int32).unwrap(), + "arrow_cast(x, 'Int32')" + ); + + let expr = expr_cast(col("x"), DataType::Float16).lt(lit(2.0)); + assert_eq!( + expr_to_sql_string(&expr).unwrap(), + "(arrow_cast(x, 'Float16') < 2.0)" + ); + + let decimal = expr_cast(lit("2.00"), DataType::Decimal256(40, 2)); + assert_eq!( + expr_to_sql_string(&decimal).unwrap(), + "arrow_cast('2.00', 'Decimal256(40, 2)')" + ); + } + + #[test] + fn test_binary_placeholder_does_not_rewrite_user_string() { + use datafusion_common::ScalarValue; + + let marker = "__lancedb_binary_placeholder_0__"; + let expr = col("payload") + .eq(lit(ScalarValue::Binary(Some(vec![0x01])))) + .or(col("text").eq(lit(marker))); + assert_eq!( + expr_to_sql_string(&expr).unwrap(), + "((payload = X'01') OR (`text` = '__lancedb_binary_placeholder_0__'))" + ); + } + + #[test] + fn test_binary_binding_skips_quoted_identifiers() { + use datafusion_common::ScalarValue; + + let expr = col("payload") + .eq(lit(ScalarValue::Binary(Some(vec![0x01])))) + .and(col("odd'name").eq(lit(1i64))) + .and(col("odd`'name").eq(lit(2i64))); + assert_eq!( + expr_to_sql_string(&expr).unwrap(), + "(((payload = X'01') AND (`odd'name` = 1)) AND (`odd``'name` = 2))" + ); + } + + #[test] + fn test_binary_placeholder_collision_search_is_linear() { + use datafusion_common::ScalarValue; + + let collision_shaped = format!("__lancedb_binary_placeholder_0__{}", "_".repeat(64_000)); + let expr = col("payload") + .eq(lit(ScalarValue::Binary(Some(vec![0x01])))) + .and(col("text").eq(lit(collision_shaped.clone()))); + let sql = expr_to_sql_string(&expr).unwrap(); + assert!(sql.contains("X'01'")); + assert!(sql.contains(&format!("'{collision_shaped}'"))); + } + #[test] fn test_multiple_binary_literals() { use datafusion_common::ScalarValue; diff --git a/rust/lancedb/src/expr/sql.rs b/rust/lancedb/src/expr/sql.rs index 24a676485..2a1ca201d 100644 --- a/rust/lancedb/src/expr/sql.rs +++ b/rust/lancedb/src/expr/sql.rs @@ -1,13 +1,24 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The LanceDB Authors -use std::any::TypeId; +use std::{ + any::TypeId, + collections::{HashMap, HashSet}, +}; +use arrow_array::types::{ + Decimal32Type, Decimal64Type, Decimal128Type, Decimal256Type, DecimalType, +}; +use arrow_schema::DataType; use datafusion_common::ScalarValue; use datafusion_common::tree_node::{Transformed, TreeNode, TreeNodeRecursion}; use datafusion_expr::Expr; +use datafusion_functions::core::expr_fn::{ + arrow_cast as datafusion_arrow_cast, arrow_try_cast as datafusion_arrow_try_cast, +}; use datafusion_sql::sqlparser::{ dialect::{Dialect as SqlParserDialect, GenericDialect}, + keywords::ALL_KEYWORDS, tokenizer::{Token, Tokenizer}, }; use datafusion_sql::unparser::{self, dialect::Dialect as UnparserDialect}; @@ -27,11 +38,13 @@ struct LanceSqlDialect; impl UnparserDialect for LanceSqlDialect { fn identifier_quote_style(&self, identifier: &str) -> Option { - let needs_quote = identifier.chars().any(|c| c.is_ascii_uppercase()) - || !identifier - .chars() - .enumerate() - .all(|(i, c)| c == '_' || c.is_ascii_alphabetic() || (i > 0 && c.is_ascii_digit())); + let identifier_upper = identifier.to_ascii_uppercase(); + let needs_quote = + (identifier_upper != "ID" && ALL_KEYWORDS.contains(&identifier_upper.as_str())) + || identifier.chars().any(|c| c.is_ascii_uppercase()) + || !identifier.chars().enumerate().all(|(i, c)| { + c == '_' || c.is_ascii_alphabetic() || (i > 0 && c.is_ascii_digit()) + }); if needs_quote { Some('`') } else { None } } } @@ -100,24 +113,128 @@ fn bytes_to_hex_sql(bytes: &[u8]) -> String { format!("X'{hex}'") } -/// Returns true if *expr* contains a `Binary` or `LargeBinary` scalar literal -/// anywhere in its subtree. DataFusion's SQL unparser cannot serialize those -/// variants, so we route such expressions through a placeholder-substitution -/// path that emits SQL `X'...'` byte-string literals. -fn has_binary_literal(expr: &Expr) -> bool { - let mut found = false; +fn string_literals(expr: &Expr) -> HashSet { + let mut literals = HashSet::new(); let _ = expr.apply(&mut |e: &Expr| { - if matches!( - e, - Expr::Literal(ScalarValue::Binary(_) | ScalarValue::LargeBinary(_), _) - ) { - found = true; - Ok(TreeNodeRecursion::Stop) - } else { - Ok(TreeNodeRecursion::Continue) + if let Expr::Literal( + ScalarValue::Utf8(Some(value)) + | ScalarValue::LargeUtf8(Some(value)) + | ScalarValue::Utf8View(Some(value)), + _, + ) = e + { + literals.insert(value.clone()); } + Ok(TreeNodeRecursion::Continue) }); - found + literals +} + +fn typed_string_literal(value: String, data_type: DataType) -> Expr { + datafusion_arrow_cast( + Expr::Literal(ScalarValue::Utf8(Some(value)), None), + Expr::Literal(ScalarValue::Utf8(Some(data_type.to_string())), None), + ) +} + +fn next_binary_placeholder(user_strings: &HashSet, next_id: &mut usize) -> String { + loop { + let placeholder = format!("{BINARY_PLACEHOLDER_PREFIX}{}__", *next_id); + *next_id += 1; + if !user_strings.contains(&placeholder) { + return placeholder; + } + } +} + +fn bind_binary_literals( + sql: &str, + mut bindings: HashMap>, +) -> crate::Result { + let bytes = sql.as_bytes(); + let mut output = Vec::with_capacity(bytes.len()); + let mut index = 0; + + // Walk SQL string tokens once. Placeholders are plain, unescaped string + // literals, so this remains linear even when user strings are large or + // deliberately resemble the placeholder prefix. + while index < bytes.len() { + if bytes[index] == b'`' { + let identifier_start = index; + index += 1; + let mut identifier_end = None; + while index < bytes.len() { + if bytes[index] == b'`' { + if index + 1 < bytes.len() && bytes[index + 1] == b'`' { + index += 2; + } else { + index += 1; + identifier_end = Some(index); + break; + } + } else { + index += 1; + } + } + + let Some(identifier_end) = identifier_end else { + return Err(crate::Error::InvalidInput { + message: "unterminated identifier while binding binary literal".to_string(), + }); + }; + output.extend_from_slice(&bytes[identifier_start..identifier_end]); + continue; + } + + if bytes[index] != b'\'' { + output.push(bytes[index]); + index += 1; + continue; + } + + let literal_start = index; + index += 1; + let content_start = index; + let mut escaped = false; + let mut content_end = None; + while index < bytes.len() { + if bytes[index] == b'\'' { + if index + 1 < bytes.len() && bytes[index + 1] == b'\'' { + escaped = true; + index += 2; + } else { + content_end = Some(index); + index += 1; + break; + } + } else { + index += 1; + } + } + + let Some(content_end) = content_end else { + return Err(crate::Error::InvalidInput { + message: "unterminated string while binding binary literal".to_string(), + }); + }; + + let placeholder = &sql[content_start..content_end]; + if !escaped && let Some(value) = bindings.remove(placeholder) { + output.extend_from_slice(bytes_to_hex_sql(&value).as_bytes()); + } else { + output.extend_from_slice(&bytes[literal_start..index]); + } + } + + if !bindings.is_empty() { + return Err(crate::Error::InvalidInput { + message: "failed to bind binary literal while serializing expression".to_string(), + }); + } + + String::from_utf8(output).map_err(|e| crate::Error::InvalidInput { + message: format!("failed to bind binary literal: {e}"), + }) } fn run_unparser(expr: &Expr) -> crate::Result { @@ -130,25 +247,37 @@ fn run_unparser(expr: &Expr) -> crate::Result { } pub fn expr_to_sql_string(expr: &Expr) -> crate::Result { - // Fast path: no binary literals — DataFusion's unparser handles everything. - if !has_binary_literal(expr) { - return run_unparser(expr); - } - - // Slow path: DataFusion's unparser cannot serialize `Binary`/`LargeBinary` - // scalars, so we rewrite each one to a unique string-literal placeholder, - // let the unparser do the rest of the work, then substitute the SQL - // `X'...'` byte-string literal back in. This keeps the operator/function - // serialization logic centralized in DataFusion and works for every - // expression node type the unparser supports. - let mut bindings: Vec> = Vec::new(); + // DataFusion's unparser needs a few adaptations before its SQL can be + // reparsed by Lance without changing the typed expression's semantics: + // + // * decimal literals need an explicit cast to preserve precision and scale; + // * casts need exact Arrow type names rather than SQL type aliases; + // * an empty IN list is valid in DataFusion but invalid SQL; + // * binary literals are unsupported by the unparser and need placeholders. + // Eliminate empty membership expressions before visiting their children. + // Otherwise a discarded binary child could leave behind a stale binding. let rewritten = expr .clone() + .transform(|e: Expr| match e { + Expr::InList(in_list) if in_list.list.is_empty() => Ok(Transformed::yes( + Expr::Literal(ScalarValue::Boolean(Some(in_list.negated)), None), + )), + other => Ok(Transformed::no(other)), + }) + .map_err(|e| crate::Error::InvalidInput { + message: format!("failed to rewrite expression: {e}"), + })? + .data; + + let user_strings = string_literals(&rewritten); + let mut next_placeholder_id = 0; + let mut binary_bindings = HashMap::new(); + let rewritten = rewritten .transform(|e: Expr| match e { Expr::Literal(ScalarValue::Binary(Some(bytes)), m) | Expr::Literal(ScalarValue::LargeBinary(Some(bytes)), m) => { - let placeholder = format!("{}{}__", BINARY_PLACEHOLDER_PREFIX, bindings.len()); - bindings.push(bytes); + let placeholder = next_binary_placeholder(&user_strings, &mut next_placeholder_id); + binary_bindings.insert(placeholder.clone(), bytes); Ok(Transformed::yes(Expr::Literal( ScalarValue::Utf8(Some(placeholder)), m, @@ -158,6 +287,57 @@ pub fn expr_to_sql_string(expr: &Expr) -> crate::Result { | Expr::Literal(ScalarValue::LargeBinary(None), m) => { Ok(Transformed::yes(Expr::Literal(ScalarValue::Null, m))) } + Expr::Literal(ScalarValue::Decimal32(Some(value), precision, scale), _m) => { + let value = Decimal32Type::format_decimal(value, precision, scale); + Ok(Transformed::yes(typed_string_literal( + value, + DataType::Decimal32(precision, scale), + ))) + } + Expr::Literal(ScalarValue::Decimal64(Some(value), precision, scale), _m) => { + let value = Decimal64Type::format_decimal(value, precision, scale); + Ok(Transformed::yes(typed_string_literal( + value, + DataType::Decimal64(precision, scale), + ))) + } + Expr::Literal(ScalarValue::Decimal128(Some(value), precision, scale), _m) => { + let value = Decimal128Type::format_decimal(value, precision, scale); + Ok(Transformed::yes(typed_string_literal( + value, + DataType::Decimal128(precision, scale), + ))) + } + Expr::Literal(ScalarValue::Decimal256(Some(value), precision, scale), _m) => { + let value = Decimal256Type::format_decimal(value, precision, scale); + Ok(Transformed::yes(typed_string_literal( + value, + DataType::Decimal256(precision, scale), + ))) + } + Expr::Literal(ScalarValue::Float16(Some(value)), _m) if !value.is_finite() => Ok( + Transformed::yes(typed_string_literal(value.to_string(), DataType::Float16)), + ), + Expr::Literal(ScalarValue::Float32(Some(value)), _m) if !value.is_finite() => Ok( + Transformed::yes(typed_string_literal(value.to_string(), DataType::Float32)), + ), + Expr::Literal(ScalarValue::Float64(Some(value)), _m) if !value.is_finite() => Ok( + Transformed::yes(typed_string_literal(value.to_string(), DataType::Float64)), + ), + Expr::Cast(cast) => Ok(Transformed::yes(datafusion_arrow_cast( + *cast.expr, + Expr::Literal( + ScalarValue::Utf8(Some(cast.field.data_type().to_string())), + None, + ), + ))), + Expr::TryCast(cast) => Ok(Transformed::yes(datafusion_arrow_try_cast( + *cast.expr, + Expr::Literal( + ScalarValue::Utf8(Some(cast.field.data_type().to_string())), + None, + ), + ))), other => Ok(Transformed::no(other)), }) .map_err(|e| crate::Error::InvalidInput { @@ -165,14 +345,12 @@ pub fn expr_to_sql_string(expr: &Expr) -> crate::Result { })? .data; - let mut sql = run_unparser(&rewritten)?; - for (i, bytes) in bindings.iter().enumerate() { - // The unparser quotes string literals with single quotes, so the - // placeholder appears as `'__lancedb_binary_placeholder___'`. - let quoted = format!("'{}{}__'", BINARY_PLACEHOLDER_PREFIX, i); - sql = sql.replace("ed, &bytes_to_hex_sql(bytes)); + let sql = run_unparser(&rewritten)?; + if binary_bindings.is_empty() { + Ok(sql) + } else { + bind_binary_literals(&sql, binary_bindings) } - Ok(sql) } #[cfg(test)] From 9d3962686e847be3e81a48005642b01f7ad9698f Mon Sep 17 00:00:00 2001 From: "lancedb-gatefixer[bot]" <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:35:02 +0800 Subject: [PATCH 07/32] fix(node): accept Arrow metadata across JavaScript realms (#3904) ## Summary - accept genuine Arrow metadata maps created in another JavaScript realm - validate every metadata entry and clone it into a local Map - cover an Arrow 15 VM-realm table through the public fromDataToBuffer boundary - retain structural typing for nested and dictionary Arrow data ## Root cause The sanitizer used a local-realm instanceof Map check for schema and field metadata. A genuine Map created in another JavaScript realm has the required internal Map state but fails that identity check, so fromDataToBuffer rejected the foreign table before serializing its rows. ## Scope This fixes the distinct JavaScript-realm sanitizer failure identified during review. It does not establish the cause of the S3/compaction panic reported in #1525, so that issue remains open. ## Validation - pnpm test --runInBand (707 passed, 5 skipped) - pnpm test --runInBand __test__/arrow.test.ts (189 passed) - pnpm build - pnpm lint - pnpm run docs Related to #1525 --------- Co-authored-by: Gatefixer <313497061+lancedb-gatefixer[bot]@users.noreply.github.com> Co-authored-by: Xuanwo --- nodejs/__test__/arrow.test.ts | 37 +++++++++++++++++++++++++++++++++++ nodejs/lancedb/arrow.ts | 4 ++-- nodejs/lancedb/sanitize.ts | 15 ++++++++++---- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/nodejs/__test__/arrow.test.ts b/nodejs/__test__/arrow.test.ts index cb56cb5ae..83b4fae46 100644 --- a/nodejs/__test__/arrow.test.ts +++ b/nodejs/__test__/arrow.test.ts @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The LanceDB Authors +import * as fs from "node:fs"; +import * as vm from "node:vm"; import * as arrow15 from "apache-arrow-15"; import * as arrow16 from "apache-arrow-16"; import * as arrow17 from "apache-arrow-17"; @@ -40,6 +42,41 @@ function sampleRecords(): Array> { ]; } +it("serializes an Arrow Table created in another JavaScript realm", async () => { + const context = vm.createContext({ + TextDecoder, + TextEncoder, + console, + setTimeout, + clearTimeout, + }); + vm.runInContext( + fs.readFileSync( + require.resolve("apache-arrow-15/Arrow.es2015.min"), + "utf8", + ), + context, + ); + const foreignTable: unknown = vm.runInContext( + "Arrow.tableFromArrays({ id: new Int32Array([1, 2, 3]), text: ['foo', 'bar', 'baz'] })", + context, + ); + + const foreignMetadata = ( + foreignTable as { schema: { metadata: Map } } + ).schema.metadata; + expect(foreignMetadata).not.toBeInstanceOf(Map); + + const buf = await fromDataToBuffer( + foreignTable as Parameters[0], + ); + const actual = currentTableFromIPC(buf); + + expect(actual.numRows).toBe(3); + expect(actual.getChild("id")?.toJSON()).toEqual([1, 2, 3]); + expect(actual.getChild("text")?.toJSON()).toEqual(["foo", "bar", "baz"]); +}); + it("preserves field metadata from a provided schema", async function () { const jsonMetadata = new Map([["ARROW:extension:name", "lance.json"]]); const schema = new CurrentSchema([ diff --git a/nodejs/lancedb/arrow.ts b/nodejs/lancedb/arrow.ts index b52ab50ef..1b6b98cc9 100644 --- a/nodejs/lancedb/arrow.ts +++ b/nodejs/lancedb/arrow.ts @@ -72,8 +72,7 @@ export type FieldLike = }; export type DataLike = - // biome-ignore lint/suspicious/noExplicitAny: - | import("apache-arrow").Data> + | import("apache-arrow").Data | { // biome-ignore lint/suspicious/noExplicitAny: type: any; @@ -82,6 +81,7 @@ export type DataLike = stride: number; nullable: boolean; children: DataLike[]; + dictionary?: { data: readonly DataLike[] }; get nullCount(): number; // biome-ignore lint/suspicious/noExplicitAny: values: Buffers[BufferType.DATA]; diff --git a/nodejs/lancedb/sanitize.ts b/nodejs/lancedb/sanitize.ts index 8fb2f1a0a..454c82247 100644 --- a/nodejs/lancedb/sanitize.ts +++ b/nodejs/lancedb/sanitize.ts @@ -94,17 +94,24 @@ export function sanitizeMetadata( if (metadataLike === undefined || metadataLike === null) { return undefined; } - if (!(metadataLike instanceof Map)) { + + let entries: IterableIterator<[unknown, unknown]>; + try { + entries = Map.prototype.entries.call(metadataLike); + } catch { throw Error("Expected metadata, if present, to be a Map"); } - for (const item of metadataLike) { - if (typeof item[0] !== "string" || typeof item[1] !== "string") { + + const metadata = new Map(); + for (const [key, value] of entries) { + if (typeof key !== "string" || typeof value !== "string") { throw Error( "Expected metadata, if present, to be a Map but it had non-string keys or values", ); } + metadata.set(key, value); } - return metadataLike as Map; + return metadata; } export function sanitizeInt(typeLike: object) { From b85776c22a7605047bf14c2a7f7d036648d501dd Mon Sep 17 00:00:00 2001 From: Will Jones Date: Thu, 27 Aug 2026 15:52:41 -0700 Subject: [PATCH 08/32] fix(listing)!: page table listings from the store's own cursor (#3979) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: list_tables now provides tables in arbitrary order and the page token is now completely opaque. `table_names` retains the old behavior of lexical ordering and `start-after` semantics. Listing the tables in a directory database cost what the database held rather than what the page held. `ListingDatabase::list_tables` enumerated every child directory of the base path, sorted the names, then discarded all but the requested page — on every request, for every page. On object storage that is one full listing per page. This PR pages the store instead. `list_tables` asks for one page at a time through `ObjectStore::read_dir_page`, carrying the store's own continuation token, so a page is one request. Non-table children can leave a page short of its limit, so the walk continues until the page is full or the store runs out. --------- Co-authored-by: Claude Opus 5 (1M context) --- rust/lancedb/src/database/listing.rs | 283 +++++++++++++++++++++++---- 1 file changed, 248 insertions(+), 35 deletions(-) diff --git a/rust/lancedb/src/database/listing.rs b/rust/lancedb/src/database/listing.rs index 064b5d28f..c22b73dd7 100644 --- a/rust/lancedb/src/database/listing.rs +++ b/rust/lancedb/src/database/listing.rs @@ -13,7 +13,7 @@ use lance::dataset::{ReadParams, WriteMode, builder::DatasetBuilder}; use lance::io::{ObjectStore, ObjectStoreParams, WrappingObjectStore}; use lance_datafusion::utils::StreamingWriteSource; use lance_file::version::LanceFileVersion; -use lance_io::object_store::{StorageOptionsAccessor, StorageOptionsProvider}; +use lance_io::object_store::{ReadDirOptions, StorageOptionsAccessor, StorageOptionsProvider}; use lance_table::io::commit::commit_handler_from_url; use object_store::local::LocalFileSystem; use snafu::ResultExt; @@ -281,6 +281,22 @@ impl std::fmt::Display for ListingDatabase { } const LANCE_EXTENSION: &str = "lance"; + +/// The table a listed child of the database names, or `None` if the child is not a table. +/// +/// A table is the directory `.lance`; a loose file or any other directory under the +/// database prefix belongs to something else. `dir_suffix` is `.lance`, built once by the +/// caller rather than per child. +/// The table a listed child directory holds, or `None` if it is not a table at all. +/// +/// Only directories are considered, so a loose object named like a table is not one. +fn table_name(location: &object_store::path::Path, dir_suffix: &str) -> Option { + location + .filename()? + .strip_suffix(dir_suffix) + .map(String::from) + .filter(|name| !name.is_empty()) +} const ENGINE: &str = "engine"; const MIRRORED_STORE: &str = "mirroredStore"; @@ -944,51 +960,72 @@ impl Database for ListingDatabase { Ok(f) } + /// List the tables in the database, a page at a time. + /// + /// The page_token is opaque, unlike the `start_after` parameter of [`Self::table_names()`]. + /// + /// When there are no more results, the returned page_token will be None. + /// + /// `limit` is the maximum number of tables to return in the response. But it is possible + /// for the response to contain fewer than `limit` tables, even when there are more tables + /// to return. Clients should check the returned page_token to determine if there are + /// more results, rather than relying on the number of tables returned. + /// + /// The order that results are returned in not guaranteed to be stable across calls, + /// so clients should not rely on it. async fn list_tables(&self, request: ListTablesRequest) -> Result { if request.id.as_ref().map(|v| !v.is_empty()).unwrap_or(false) { return self.namespace_database().list_tables(request).await; } - let mut f = self - .object_store - .read_dir(self.base_path.clone()) - .await? - .iter() - .map(Path::new) - .filter(|path| { - let is_lance = path - .extension() - .and_then(|e| e.to_str()) - .map(|e| e == LANCE_EXTENSION); - is_lance.unwrap_or(false) - }) - .filter_map(|p| p.file_stem().and_then(|s| s.to_str().map(String::from))) - .collect::>(); - f.sort(); + let limit = request.limit.map(|limit| limit.max(0) as usize); + let dir_suffix = format!(".{LANCE_EXTENSION}"); + let mut tables = Vec::new(); + let mut page_token = request.page_token.filter(|token| !token.is_empty()); - // Handle pagination with page_token - if let Some(ref page_token) = request.page_token { - let index = f - .iter() - .position(|name| name.as_str() > page_token.as_str()) - .unwrap_or(f.len()); - f.drain(0..index); + // A page of nothing: the store rejects a limit of zero, and no table was handed over + // for a token to resume after. + if limit == Some(0) { + return Ok(ListTablesResponse { + context: None, + tables, + page_token: None, + }); } - // Determine if there's a next page. The token is the last name of this page, - // not the first of the next one: the next page resumes strictly after the - // token, so naming the next page's first entry would skip it. - let next_page_token = match request.limit { - Some(limit) if f.len() > limit as usize => { - f.truncate(limit as usize); - f.last().cloned() + loop { + // Ask only for what the page still has room for, so a database holding more + // than one page costs one request per page rather than one per table. + let listing = self + .object_store + .read_dir_page( + self.base_path.clone(), + ReadDirOptions { + page_token: page_token.take(), + limit: limit.map(|limit| limit - tables.len()), + }, + ) + .await?; + page_token = listing.page_token; + // Only child directories can be tables, and the store already separates them + // out, so the objects in the page are not looked at. + tables.extend( + listing + .result + .common_prefixes + .iter() + .filter_map(|location| table_name(location, &dir_suffix)), + ); + // Children that are not tables leave the page short of the limit, so keep + // going until the page is full or the database runs out. + if page_token.is_none() || limit.is_none_or(|limit| tables.len() >= limit) { + break; } - _ => None, - }; + } Ok(ListTablesResponse { context: None, - tables: f, - page_token: next_page_token, + tables, + page_token, }) } @@ -1484,6 +1521,182 @@ mod tests { use tokio::sync::Barrier; use tokio::time::timeout; + async fn create_tables(db: &ListingDatabase, names: &[&str]) { + let schema = Arc::new(Schema::new(vec![Field::new("id", DataType::Int32, false)])); + for name in names { + db.create_table(CreateTableRequest { + name: name.to_string(), + namespace_path: vec![], + data: Box::new(RecordBatch::new_empty(schema.clone())) as Box, + mode: CreateTableMode::Create, + write_options: Default::default(), + location: None, + namespace_client: None, + }) + .await + .unwrap(); + } + } + + /// Every table in the database, taken `limit` at a time, which is how a caller walks a + /// listing: the token ends the walk, never a short page. + async fn walk(db: &ListingDatabase, limit: Option) -> Vec { + let mut seen = Vec::new(); + let mut page_token = None; + loop { + let page = db + .list_tables(ListTablesRequest { + limit, + page_token, + ..Default::default() + }) + .await + .unwrap(); + seen.extend(page.tables); + page_token = page.page_token; + if page_token.is_none() { + return seen; + } + assert!( + seen.len() < 100, + "the walk is serving tables more than once" + ); + } + } + + /// Paging with the returned token has to visit every table exactly once, whatever the + /// page size, with nothing lost or repeated at a boundary. + #[rstest::rstest] + #[tokio::test] + async fn test_list_tables_pages_over_every_table_once(#[values(1, 2, 3, 5, 10)] limit: i32) { + let (_tempdir, db) = setup_database().await; + create_tables(&db, &["a", "b", "c", "d", "e"]).await; + + assert_eq!(walk(&db, Some(limit)).await, vec!["a", "b", "c", "d", "e"]); + } + + /// The token is opaque: it is whatever resumes the store the database sits on, not a + /// table name. Callers hand it back and nothing else. + /// + /// Nothing validates a token, so one invented by a caller is read as a position rather + /// than refused — which is why the token has to come back from a previous page. + #[tokio::test] + async fn test_the_page_token_is_not_a_table_name() { + let (_tempdir, db) = setup_database().await; + create_tables(&db, &["a", "b", "c"]).await; + + let page = db + .list_tables(ListTablesRequest { + limit: Some(1), + ..Default::default() + }) + .await + .unwrap(); + + assert_eq!(page.tables, vec!["a"]); + let token = page.page_token.expect("two tables are still to come"); + assert_ne!(token, "a"); + + // Handing it back is the only thing a caller does with it, and it resumes. + let rest = db + .list_tables(ListTablesRequest { + page_token: Some(token), + ..Default::default() + }) + .await + .unwrap(); + assert_eq!(rest.tables, vec!["b", "c"]); + } + + /// A limit the listing does not fill leaves no token behind, so a caller paging by token + /// stops without asking for an empty page. + #[tokio::test] + async fn test_a_listing_that_runs_out_has_no_token() { + let (_tempdir, db) = setup_database().await; + create_tables(&db, &["a", "b"]).await; + + let page = db + .list_tables(ListTablesRequest { + limit: Some(10), + ..Default::default() + }) + .await + .unwrap(); + + assert_eq!(page.tables, vec!["a", "b"]); + assert_eq!(page.page_token, None); + } + + /// An empty page token means "from the start", which is how a client looping on a token + /// spells its first request. + #[tokio::test] + async fn test_an_empty_page_token_lists_from_the_start() { + let (_tempdir, db) = setup_database().await; + create_tables(&db, &["a", "b"]).await; + + let page = db + .list_tables(ListTablesRequest { + page_token: Some(String::new()), + ..Default::default() + }) + .await + .unwrap(); + + assert_eq!(page.tables, vec!["a", "b"]); + } + + /// Listing follows the order the object store lists directories in, so a name that + /// extends another comes first: the `-` of `users-archive.lance` sorts below the `.` of + /// `users.lance`. Pagination pushes its cursor into the list request, so it cannot report + /// an order other than the one it resumes in. + #[tokio::test] + async fn test_listing_order_follows_the_store_not_the_table_name() { + let (_tempdir, db) = setup_database().await; + create_tables(&db, &["users", "users-archive", "users.old"]).await; + + assert_eq!( + walk(&db, None).await, + vec!["users-archive", "users", "users.old"] + ); + // And paging reports the same order, so a walk sees each table once. + assert_eq!( + walk(&db, Some(1)).await, + vec!["users-archive", "users", "users.old"] + ); + } + + /// Only directories named `.lance` are tables; loose files and other directories + /// under the database prefix are not. A page spent on them is filled from the next one, + /// so a page holding only non-tables does not read as an empty database. + #[tokio::test] + async fn test_listing_ignores_non_table_children() { + let (tempdir, db) = setup_database().await; + create_tables(&db, &["real"]).await; + std::fs::write(tempdir.path().join("aaa-loose.lance"), b"not a table").unwrap(); + create_dir_all(tempdir.path().join("aaa-scratch")).unwrap(); + + let page = db + .list_tables(ListTablesRequest { + limit: Some(1), + ..Default::default() + }) + .await + .unwrap(); + + assert_eq!(page.tables, vec!["real"]); + } + + #[tokio::test] + async fn listing_ignores_empty_table_name() { + let (tempdir, db) = setup_database().await; + create_dir_all(tempdir.path().join(".lance")).unwrap(); + let page = db.list_tables(ListTablesRequest::default()).await.unwrap(); + assert!( + page.tables.is_empty(), + "invalid empty table name was listed" + ); + } + async fn setup_database() -> (tempfile::TempDir, ListingDatabase) { let tempdir = tempdir().unwrap(); let uri = tempdir.path().to_str().unwrap(); From 83cff3ab93c1998e9e546efbc6a62adfdbc00b7b Mon Sep 17 00:00:00 2001 From: Drew Date: Thu, 27 Aug 2026 17:07:29 -0700 Subject: [PATCH 09/32] fix(python): use one blobv2 type and coerce blob writes by metadata (#4065) --- docs/src/python/python.md | 8 +- python/python/lancedb/__init__.py | 17 +- python/python/lancedb/_blob.py | 8 +- python/python/lancedb/schema.py | 135 +++-- python/python/lancedb/table.py | 292 ++++++++--- python/python/tests/test_blob.py | 480 ++++++++++++++++++ python/python/tests/test_util.py | 160 ++++++ .../src/table/datafusion/blob_coerce.rs | 22 +- 8 files changed, 1022 insertions(+), 100 deletions(-) diff --git a/docs/src/python/python.md b/docs/src/python/python.md index 3cb996a15..5f359f4b7 100644 --- a/docs/src/python/python.md +++ b/docs/src/python/python.md @@ -223,9 +223,13 @@ tokens = list( Blob columns store large binary values out of line so they can be read lazily instead of being materialized with the rest of the row. -::: lancedb.blob +`lancedb.BlobType` is `lance.blob.BlobType` when pylance is installed. Without +pylance, LanceDB uses a matching `lance.blob.v2` extension type so blob columns +still work. Queries return descriptors. Call +[`fetch_blob_files`][lancedb.table.Table.fetch_blob_files] for lazy reads or +[`fetch_blobs`][lancedb.table.Table.fetch_blobs] for eager bytes. -::: lancedb.BlobType +::: lancedb.blob ::: lancedb._blob.BlobFile options: diff --git a/python/python/lancedb/__init__.py b/python/python/lancedb/__init__.py index 8cb85a3ed..21ffc8860 100644 --- a/python/python/lancedb/__init__.py +++ b/python/python/lancedb/__init__.py @@ -6,7 +6,7 @@ import importlib.metadata import os from concurrent.futures import ThreadPoolExecutor from datetime import timedelta -from typing import Dict, Optional, Union, Any, List, Iterable +from typing import Dict, Optional, Union, Any, List, Iterable, TYPE_CHECKING __version__ = importlib.metadata.version("lancedb") @@ -20,7 +20,7 @@ from .db import AsyncConnection, DBConnection, LanceDBConnection from .remote import ClientConfig from .remote.db import RemoteDBConnection from .expr import Expr, col, lit, func -from .schema import blob, vector, BlobType +from .schema import blob, vector from .job import AsyncJob, Job from .functions import ( FunctionArtifactRequest as FunctionArtifactRequest, @@ -49,6 +49,19 @@ from .namespace import ( ) +if TYPE_CHECKING: + from lance.blob import BlobType as BlobType + + +def __getattr__(name: str): + if name == "BlobType": + from .schema import BlobType + + globals()["BlobType"] = BlobType + return BlobType + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + def _check_s3_bucket_with_dots( uri: str, storage_options: Optional[Dict[str, str]] ) -> None: diff --git a/python/python/lancedb/_blob.py b/python/python/lancedb/_blob.py index 5b4c0c343..dc4ed37df 100644 --- a/python/python/lancedb/_blob.py +++ b/python/python/lancedb/_blob.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Optional, Union import pyarrow as pa from .expr import Expr -from .schema import blob_v2_column_paths +from .schema import row_addressable_blob_v2_paths from .types import BlobMode, QueryProjection, QueryProjectionSpec if TYPE_CHECKING: @@ -119,7 +119,7 @@ def blob_v2_projection_sources( schema: pa.Schema, projection: QueryProjection, ) -> dict[str, str]: - blob_columns = blob_v2_column_paths(schema) + blob_columns = row_addressable_blob_v2_paths(schema) if not blob_columns: return {} columns = set(blob_columns) @@ -140,7 +140,9 @@ def v2_projection_needs_row_id( ) -> bool: if with_row_id: return False - return projection_includes_blob_column(projection, blob_v2_column_paths(schema)) + return projection_includes_blob_column( + projection, row_addressable_blob_v2_paths(schema) + ) def blob_auto_row_id_for_scan( diff --git a/python/python/lancedb/schema.py b/python/python/lancedb/schema.py index 33adbbae3..4dce09f0f 100644 --- a/python/python/lancedb/schema.py +++ b/python/python/lancedb/schema.py @@ -4,30 +4,34 @@ """Schema helpers for Lance blob columns.""" +import importlib +from typing import TYPE_CHECKING + import pyarrow as pa +import pyarrow.ipc + +if TYPE_CHECKING: + from lance.blob import BlobType as BlobType _BLOB_EXTENSION_NAME = "lance.blob.v2" _BLOB_V1_KEY = "lance-encoding:blob" _ARROW_EXT_NAME_KEY = "ARROW:extension:name" +_BLOB_V2_STORAGE_TYPE = pa.struct( + [ + pa.field("data", pa.large_binary(), nullable=True), + pa.field("uri", pa.utf8(), nullable=True), + pa.field("position", pa.uint64(), nullable=True), + pa.field("size", pa.uint64(), nullable=True), + ] +) +_resolved_blob_type = None -class BlobType(pa.ExtensionType): - """PyArrow extension type for a Lance blob v2 column. - - Queries return descriptors; call :meth:`~lancedb.table.Table.fetch_blob_files` - for lazy reads or :meth:`~lancedb.table.Table.fetch_blobs` for eager bytes. - """ +class _FallbackBlobType(pa.ExtensionType): + """lance.blob.v2 extension type used when pylance is not installed.""" def __init__(self) -> None: - storage_type = pa.struct( - [ - pa.field("data", pa.large_binary(), nullable=True), - pa.field("uri", pa.utf8(), nullable=True), - pa.field("position", pa.uint64(), nullable=True), - pa.field("size", pa.uint64(), nullable=True), - ] - ) - super().__init__(storage_type, _BLOB_EXTENSION_NAME) + pa.ExtensionType.__init__(self, _BLOB_V2_STORAGE_TYPE, _BLOB_EXTENSION_NAME) def __arrow_ext_serialize__(self) -> bytes: return b"" @@ -35,23 +39,16 @@ class BlobType(pa.ExtensionType): @classmethod def __arrow_ext_deserialize__( cls, storage_type: pa.DataType, serialized: bytes - ) -> "BlobType": + ) -> "_FallbackBlobType": return cls() def __reduce__(self): - # Ensure pickle round-trips on older pyarrow (apache/arrow#35599). return type(self).__arrow_ext_deserialize__, ( self.storage_type, self.__arrow_ext_serialize__(), ) -try: - pa.register_extension_type(BlobType()) # type: ignore[arg-type] -except pa.ArrowKeyError: - pass - - def _metadata_value(metadata: dict, key: str): return metadata.get(key.encode()) or metadata.get(key) @@ -92,43 +89,105 @@ def is_blob_like_field(field: pa.Field) -> bool: return is_blob_v2_field(field) or _metadata_marks_legacy_blob(field.metadata or {}) -def _collect_blob_paths(schema: pa.Schema, is_blob) -> list[str]: - paths: list[str] = [] +def _collect_blob_paths(schema: pa.Schema, is_blob) -> list[tuple[str, bool]]: + """Walk the schema and return (path, has_list_ancestor) for each blob field.""" + paths: list[tuple[str, bool]] = [] - def walk(fields, prefix: str) -> None: + def walk(fields, prefix: str, has_list_ancestor: bool) -> None: for field in fields: path = f"{prefix}.{field.name}" if prefix else field.name if is_blob(field): - paths.append(path) + paths.append((path, has_list_ancestor)) elif pa.types.is_struct(field.type): - walk(field.type, path) + walk(field.type, path, has_list_ancestor) elif ( pa.types.is_list(field.type) or pa.types.is_large_list(field.type) or pa.types.is_fixed_size_list(field.type) ): - walk([field.type.value_field], path) + walk([field.type.value_field], path, True) - walk(schema, "") + walk(schema, "", False) return paths def blob_column_paths(schema: pa.Schema) -> list[str]: """Dotted paths of blob-like columns (v2 extension or legacy metadata).""" - return _collect_blob_paths(schema, is_blob_like_field) + return [path for path, _ in _collect_blob_paths(schema, is_blob_like_field)] def blob_v2_column_paths(schema: pa.Schema) -> list[str]: - return _collect_blob_paths(schema, is_blob_v2_field) + return [path for path, _ in _collect_blob_paths(schema, is_blob_v2_field)] + + +def row_addressable_blob_v2_paths(schema: pa.Schema) -> list[str]: + """Blob v2 paths with one blob addressable by table row id. + + ``fetch_blobs`` and the descriptor row-id ride-along address one blob per + row, so a blob inside a list container has no row-id slot and no fetch + path. Those columns still store and query as raw descriptors. + """ + return [ + path + for path, has_list_ancestor in _collect_blob_paths(schema, is_blob_v2_field) + if not has_list_ancestor + ] def schema_has_blob_field(schema: pa.Schema) -> bool: return bool(blob_column_paths(schema)) +def _deserialize_registered_type(extension_type: pa.ExtensionType) -> pa.DataType: + """Return the type Arrow reconstructs for this extension name.""" + schema = pa.schema([pa.field("value", extension_type)]) + restored = pa.ipc.read_schema(schema.serialize()) + return restored.field("value").type + + +def _resolve_blob_type(): + """Return the BlobType class this process should use. + + pylance's class when it owns the lance.blob.v2 registry entry, + otherwise LanceDB's fallback. A different registered class is an error. + """ + global _resolved_blob_type + if _resolved_blob_type is not None: + return _resolved_blob_type + try: + blob_module = importlib.import_module("lance.blob") + except ModuleNotFoundError as err: + if err.name not in ("lance", "lance.blob"): + raise + else: + blob_type = getattr(blob_module, "BlobType", None) + if blob_type is not None: + registered_type = _deserialize_registered_type(blob_type()) + if type(registered_type) is not blob_type: + registered_cls = type(registered_type) + raise ValueError( + "lance.blob.v2 is already registered by " + f"{registered_cls.__module__}.{registered_cls.__qualname__}" + ) + _resolved_blob_type = blob_type + return blob_type + try: + pa.register_extension_type(_FallbackBlobType()) # type: ignore[arg-type] + except pa.ArrowKeyError as err: + raise ValueError( + "lance.blob.v2 is already registered by another extension class" + ) from err + _resolved_blob_type = _FallbackBlobType + return _resolved_blob_type + + def blob(name: str, nullable: bool = True) -> pa.Field: - """Create a Lance blob v2 column field.""" - return pa.field(name, BlobType(), nullable=nullable) + """Create a Lance blob v2 column field. + + When pylance is installed this is ``lance.blob.BlobType``. + """ + blob_type = _resolve_blob_type() + return pa.field(name, blob_type(), nullable=nullable) def vector(dimension: int, value_type: pa.DataType = pa.float32()) -> pa.DataType: @@ -155,3 +214,11 @@ def vector(dimension: int, value_type: pa.DataType = pa.float32()) -> pa.DataTyp ... ]) """ return pa.list_(value_type, dimension) + + +def __getattr__(name: str): + if name == "BlobType": + blob_type = _resolve_blob_type() + globals()["BlobType"] = blob_type + return blob_type + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/python/python/lancedb/table.py b/python/python/lancedb/table.py index 765b7fa14..535ed7c0d 100644 --- a/python/python/lancedb/table.py +++ b/python/python/lancedb/table.py @@ -104,7 +104,12 @@ from .util import ( value_to_sql, ) from .index import lang_mapping -from .schema import blob_v2_column_paths, schema_has_blob_field +from .schema import ( + blob_v2_column_paths, + is_blob_v2_field, + row_addressable_blob_v2_paths, + schema_has_blob_field, +) def _should_push_down_query_table( @@ -426,6 +431,7 @@ def _cast_to_target_schema( def gen(): for batch in reader: + batch = _coerce_blob_write_columns(batch, reordered_schema) # Table but not RecordBatch has cast. cast_batches = ( pa.Table.from_batches([batch]).cast(reordered_schema).to_batches() @@ -438,6 +444,166 @@ def _cast_to_target_schema( return pa.RecordBatchReader.from_batches(reordered_schema, gen()) +def _coerce_blob_write_columns( + batch: pa.RecordBatch, target_schema: pa.Schema +) -> pa.RecordBatch: + """Materialize blob storage structs before the stream leaves Python. + + merge_insert requires its source reader to already match the table's + physical schema. Unlike add and insert, it does not pass through + LanceDB's Rust blob coercion, so preserving binary input here would + reach Lance as binary and fail the schema check. + """ + columns = [] + fields = [] + changed = False + for field, column in zip(batch.schema, batch.columns): + target_field = target_schema.field(field.name) + coerced = _coerce_blob_value(column, target_field) + if coerced is not column: + column = coerced + field = pa.field( + field.name, + coerced.type, + field.nullable, + target_field.metadata, + ) + changed = True + columns.append(column) + fields.append(field) + if not changed: + return batch + return pa.RecordBatch.from_arrays( + columns, schema=pa.schema(fields, metadata=batch.schema.metadata) + ) + + +def _coerce_blob_value(column: pa.Array, target_field: pa.Field) -> pa.Array: + if is_blob_v2_field(target_field) and _can_coerce_to_blob(column.type): + return _coerce_value_to_blob(column, target_field) + + target_type = target_field.type + if pa.types.is_struct(target_type) and pa.types.is_struct(column.type): + children = [] + fields = [] + changed = False + for source_field in column.type: + source_column = column.field(source_field.name) + nested_target = next( + (field for field in target_type if field.name == source_field.name), + None, + ) + if nested_target is None: + children.append(source_column) + fields.append(source_field) + continue + coerced = _coerce_blob_value(source_column, nested_target) + if coerced is not source_column: + changed = True + child_array, child_type = _physical_array_and_type(coerced) + children.append(child_array) + fields.append( + pa.field( + source_field.name, + child_type, + source_field.nullable, + nested_target.metadata, + ) + ) + if not changed: + return column + return pa.StructArray.from_arrays( + children, + fields=fields, + mask=column.is_null() if column.null_count else None, + ) + + if _is_list_like(target_type) and _is_list_like(column.type): + return _coerce_blob_list_values(column, target_type.value_field) + + return column + + +def _coerce_blob_list_values( + column: pa.Array, target_value_field: pa.Field +) -> pa.Array: + """Coerce blob values inside a list column, preserving offsets and nulls. + + Works on the raw child values window instead of ``pc.list_flatten`` because + flatten drops values spanned by null slots, which would misalign offsets. + """ + mask = column.is_null() if column.null_count else None + if pa.types.is_fixed_size_list(column.type): + list_size = column.type.list_size + values = column.values.slice(column.offset * list_size, len(column) * list_size) + coerced = _coerce_blob_value(values, target_value_field) + if coerced is values: + return column + physical_values, _ = _physical_array_and_type(coerced) + return pa.FixedSizeListArray.from_arrays(physical_values, list_size, mask=mask) + offsets = column.offsets + first_offset = offsets[0].as_py() + values = column.values.slice( + first_offset, + offsets[-1].as_py() - first_offset, + ) + coerced = _coerce_blob_value(values, target_value_field) + if coerced is values: + return column + physical_values, _ = _physical_array_and_type(coerced) + if first_offset: + offsets = pc.subtract(offsets, pa.scalar(first_offset, offsets.type)) + if pa.types.is_large_list(column.type): + return pa.LargeListArray.from_arrays(offsets, physical_values, mask=mask) + return pa.ListArray.from_arrays(offsets, physical_values, mask=mask) + + +def _coerce_value_to_blob(values: pa.Array, target_field: pa.Field) -> pa.Array: + if pa.types.is_null(values.type): + data = pa.nulls(len(values), type=pa.large_binary()) + elif pa.types.is_large_binary(values.type): + data = values + else: + data = values.cast(pa.large_binary()) + length = len(values) + storage_type = target_field.type + if isinstance(storage_type, pa.ExtensionType): + storage_type = storage_type.storage_type + storage_fields = list(storage_type) + children = [] + for storage_field in storage_fields: + if storage_field.name == "data": + children.append(data) + else: + children.append(pa.nulls(length, type=storage_field.type)) + storage = pa.StructArray.from_arrays( + children, + fields=storage_fields, + mask=values.is_null() if values.null_count else None, + ) + if isinstance(target_field.type, pa.ExtensionType): + return pa.ExtensionArray.from_storage(target_field.type, storage) + return storage + + +def _physical_array_and_type(array: pa.Array) -> tuple[pa.Array, pa.DataType]: + if isinstance(array.type, pa.ExtensionType): + return array.storage, array.type.storage_type + return array, array.type + + +def _can_coerce_to_blob(data_type: pa.DataType) -> bool: + return _is_binary_like(data_type) or pa.types.is_null(data_type) + + +def _is_binary_like(data_type: pa.DataType) -> bool: + return ( + pa.types.is_binary(data_type) + or pa.types.is_large_binary(data_type) + or pa.types.is_binary_view(data_type) + ) + + def _field_extension_name(field: pa.Field) -> Optional[str]: extension_name = getattr(field.type, "extension_name", None) if extension_name is not None: @@ -464,63 +630,71 @@ def _align_field_types( target_field = next((f for f in target_fields if f.name == field.name), None) if target_field is None: raise ValueError(f"Field '{field.name}' not found in target schema") - # Preserve arrow.json input until it reaches Lance. LanceDB exposes stored - # JSON columns as lance.json (JSONB-backed LargeBinary), but casting the - # input to that storage type here merely relabels the raw JSON bytes as - # JSONB. Lance must see arrow.json so it can perform the JSONB encoding. - if ( - _field_extension_name(field) == "arrow.json" - and _field_extension_name(target_field) == "lance.json" - ): - new_fields.append(field) - continue - if pa.types.is_struct(target_field.type): - if pa.types.is_struct(field.type): - new_type = pa.struct( - _align_field_types( - field.type.fields, - target_field.type.fields, - ) + new_fields.append(_align_field(field, target_field)) + return new_fields + + +def _align_list_value_field( + value_field: pa.Field, target_value_field: pa.Field +) -> pa.Field: + # A list has exactly one child, so the inferred child name ("item") aligns + # positionally and adopts the table's child name; pa.Table.cast renames it. + return _align_field(value_field, target_value_field).with_name( + target_value_field.name + ) + + +def _align_field(field: pa.Field, target_field: pa.Field) -> pa.Field: + # Preserve arrow.json input until it reaches Lance. LanceDB exposes stored + # JSON columns as lance.json (JSONB-backed LargeBinary), but casting the + # input to that storage type here merely relabels the raw JSON bytes as + # JSONB. Lance must see arrow.json so it can perform the JSONB encoding. + if ( + _field_extension_name(field) == "arrow.json" + and _field_extension_name(target_field) == "lance.json" + ): + return field + if pa.types.is_struct(target_field.type): + if pa.types.is_struct(field.type): + new_type = pa.struct( + _align_field_types( + field.type.fields, + target_field.type.fields, ) - else: - new_type = target_field.type - elif pa.types.is_list(target_field.type): - if _is_list_like(field.type): - new_type = pa.list_( - _align_field_types( - [field.type.value_field], - [target_field.type.value_field], - )[0] - ) - else: - new_type = target_field.type - elif pa.types.is_large_list(target_field.type): - if _is_list_like(field.type): - new_type = pa.large_list( - _align_field_types( - [field.type.value_field], - [target_field.type.value_field], - )[0] - ) - else: - new_type = target_field.type - elif pa.types.is_fixed_size_list(target_field.type): - if _is_list_like(field.type): - new_type = pa.list_( - _align_field_types( - [field.type.value_field], - [target_field.type.value_field], - )[0], - target_field.type.list_size, - ) - else: - new_type = target_field.type + ) else: new_type = target_field.type - new_fields.append( - pa.field(field.name, new_type, field.nullable, target_field.metadata) - ) - return new_fields + elif pa.types.is_list(target_field.type): + if _is_list_like(field.type): + new_type = pa.list_( + _align_list_value_field( + field.type.value_field, target_field.type.value_field + ) + ) + else: + new_type = target_field.type + elif pa.types.is_large_list(target_field.type): + if _is_list_like(field.type): + new_type = pa.large_list( + _align_list_value_field( + field.type.value_field, target_field.type.value_field + ) + ) + else: + new_type = target_field.type + elif pa.types.is_fixed_size_list(target_field.type): + if _is_list_like(field.type): + new_type = pa.list_( + _align_list_value_field( + field.type.value_field, target_field.type.value_field + ), + target_field.type.list_size, + ) + else: + new_type = target_field.type + else: + new_type = target_field.type + return pa.field(field.name, new_type, field.nullable, target_field.metadata) def _infer_subschema( @@ -589,7 +763,7 @@ def sanitize_create_table( schema = data.schema else: if schema is not None: - data = pa.Table.from_pylist([], schema) + data = pa.Table.from_batches([], schema=schema) if schema is None: if data is None: raise ValueError("Either data or schema must be provided") @@ -2698,7 +2872,7 @@ class LanceTable(Table): arrow_tbl = self.to_arrow() if blob_mode == "descriptions": arrow_tbl = strip_auto_row_ids( - arrow_tbl, blob_v2_column_paths(self.schema) + arrow_tbl, row_addressable_blob_v2_paths(self.schema) ) return arrow_tbl.to_pandas(**kwargs) @@ -5102,7 +5276,9 @@ class AsyncTable: if blob_mode == "descriptions" or not schema_has_blob_field(schema): arrow_tbl = await self.to_arrow() if blob_mode == "descriptions": - arrow_tbl = strip_auto_row_ids(arrow_tbl, blob_v2_column_paths(schema)) + arrow_tbl = strip_auto_row_ids( + arrow_tbl, row_addressable_blob_v2_paths(schema) + ) return arrow_tbl.to_pandas(**kwargs) if blob_mode == "lazy" and get_uri_scheme(await self.uri()) == "memory": diff --git a/python/python/tests/test_blob.py b/python/python/tests/test_blob.py index 351769ff6..c9694277c 100644 --- a/python/python/tests/test_blob.py +++ b/python/python/tests/test_blob.py @@ -2,10 +2,15 @@ # SPDX-FileCopyrightText: Copyright The LanceDB Authors import io +import subprocess +import sys +import textwrap +import lance import pyarrow as pa import pyarrow.compute as pc import pytest +from lance.blob import BlobType as LanceBlobType import lancedb from lancedb._blob import ( @@ -18,6 +23,20 @@ from lancedb.index import FTS from lancedb.schema import blob_column_paths, blob_v2_column_paths +_HIDE_LANCE_BLOB = """\ +import importlib.abc +import sys + +class _MissingLanceBlob(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path, target=None): + if fullname == "lance.blob" or fullname.startswith("lance.blob."): + raise ModuleNotFoundError(fullname, name="lance.blob") + +sys.modules.pop("lance.blob", None) +sys.meta_path.insert(0, _MissingLanceBlob()) +""" + + def _blob_table(name, rows): db = lancedb.connect("memory:///") schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) @@ -51,6 +70,181 @@ def test_blob_factory_declares_v2_field(): field = lancedb.blob("image") assert isinstance(field.type, pa.ExtensionType) assert field.type.extension_name == "lance.blob.v2" + assert lancedb.BlobType is LanceBlobType + assert type(field.type) is LanceBlobType + + +def test_blob_type_works_without_pylance(): + script = _HIDE_LANCE_BLOB + textwrap.dedent( + """\ + import lancedb + import pyarrow as pa + + field = lancedb.blob("image") + if not isinstance(field.type, pa.ExtensionType): + raise SystemExit("expected an extension type") + if field.type.extension_name != "lance.blob.v2": + raise SystemExit(field.type.extension_name) + if lancedb.BlobType is not type(field.type): + raise SystemExit("BlobType is not the field type class") + if lancedb.BlobType.__module__ != "lancedb.schema": + raise SystemExit(lancedb.BlobType.__module__) + + db = lancedb.connect("memory:///") + table = db.create_table( + "images", + schema=pa.schema([pa.field("id", pa.int64()), field]), + ) + table.add([{"id": 1, "image": b"hello"}]) + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute([{"id": 1, "image": b"updated"}, {"id": 2, "image": b"inserted"}]) + ) + if result.num_updated_rows != 1 or result.num_inserted_rows != 1: + raise SystemExit( + f"merge_insert rows updated={result.num_updated_rows} " + f"inserted={result.num_inserted_rows}" + ) + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_blob_resolves_pylance_type_without_eager_import(): + script = textwrap.dedent( + """\ + import sys + import lancedb + + if "lance.blob" in sys.modules: + raise SystemExit("import lancedb imported lance.blob") + field = lancedb.blob("image") + from lance.blob import BlobType + + if type(field.type) is not BlobType: + raise SystemExit(f"{type(field.type)} is not {BlobType}") + import lance + + image = lance.blob_array([b"x"]) + if type(image.type) is not BlobType: + raise SystemExit("blob_array used a different class") + if type(image.type) is not type(field.type): + raise SystemExit("field and array classes differ") + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_blob_fallback_fails_if_name_already_registered(): + script = _HIDE_LANCE_BLOB + textwrap.dedent( + """\ + import pyarrow as pa + + class OtherBlobType(pa.ExtensionType): + def __init__(self): + super().__init__( + pa.struct([pa.field("data", pa.large_binary())]), + "lance.blob.v2", + ) + + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type, serialized): + return cls() + + pa.register_extension_type(OtherBlobType()) + import lancedb + + try: + lancedb.blob("image") + except ValueError as err: + if "already registered" not in str(err): + raise SystemExit(err) + else: + raise SystemExit("expected ValueError") + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_blob_type_rejects_competing_registration_with_pylance(): + script = textwrap.dedent( + """\ + import pyarrow as pa + import pyarrow.ipc + + class OtherBlobType(pa.ExtensionType): + def __init__(self): + super().__init__( + pa.struct( + [ + pa.field("data", pa.large_binary()), + pa.field("uri", pa.utf8()), + pa.field("position", pa.uint64()), + pa.field("size", pa.uint64()), + ] + ), + "lance.blob.v2", + ) + + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type, serialized): + return cls() + + pa.register_extension_type(OtherBlobType()) + + from lance.blob import BlobType + + if BlobType is OtherBlobType: + raise SystemExit("pylance BlobType was replaced") + schema = pa.schema([pa.field("value", BlobType())]) + restored = pa.ipc.read_schema(schema.serialize()) + if type(restored.field("value").type) is not OtherBlobType: + raise SystemExit(type(restored.field("value").type)) + + import lancedb + + try: + lancedb.blob("image") + except ValueError as err: + if "__main__.OtherBlobType" not in str(err): + raise SystemExit(err) + else: + raise SystemExit("expected ValueError") + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr def test_blob_v2_column_paths_include_list_children(): @@ -203,6 +397,292 @@ def test_fetch_blobs_round_trip(): assert [blobs[0].as_py(), blobs[1].as_py()] == [b"alpha", b"beta"] +def test_merge_insert_writes_python_bytes(): + table = _blob_table("merge_bytes", [{"id": 1, "image": b"before"}]) + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute([{"id": 1, "image": b"updated"}, {"id": 2, "image": b"inserted"}]) + ) + assert result.num_updated_rows == 1 + assert result.num_inserted_rows == 1 + by_id = _row_ids_by_id(table) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + assert blobs.to_pylist() == [b"updated", b"inserted"] + + +def test_merge_insert_bytes_after_reopen_without_touching_blob_type(tmp_path): + db = lancedb.connect(tmp_path) + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) + table = db.create_table("images", schema=schema) + table.add([{"id": 1, "image": b"hello"}]) + + script = textwrap.dedent( + f"""\ + import lancedb + + db = lancedb.connect({str(tmp_path)!r}) + table = db.open_table("images") + image_type = table.schema.field("image").type + if type(image_type).__name__ != "StructType": + raise SystemExit(f"expected StructType, got {{type(image_type)}}") + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute( + [{{"id": 1, "image": b"updated"}}, {{"id": 2, "image": b"inserted"}}] + ) + ) + if result.num_updated_rows != 1 or result.num_inserted_rows != 1: + raise SystemExit( + f"rows updated={{result.num_updated_rows}} " + f"inserted={{result.num_inserted_rows}}" + ) + hits = table.search().with_row_id(True).limit(10).to_arrow() + by_id = dict(zip(hits["id"].to_pylist(), hits["_rowid"].to_pylist())) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + if blobs.to_pylist() != [b"updated", b"inserted"]: + raise SystemExit(blobs.to_pylist()) + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_merge_insert_bytes_after_reopen_without_pylance(tmp_path): + db = lancedb.connect(tmp_path) + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) + table = db.create_table("images", schema=schema) + table.add([{"id": 1, "image": b"hello"}]) + + script = _HIDE_LANCE_BLOB + textwrap.dedent( + f"""\ + import lancedb + + db = lancedb.connect({str(tmp_path)!r}) + table = db.open_table("images") + image_type = table.schema.field("image").type + if type(image_type).__name__ != "StructType": + raise SystemExit(f"expected StructType, got {{type(image_type)}}") + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute( + [{{"id": 1, "image": b"updated"}}, {{"id": 2, "image": b"inserted"}}] + ) + ) + if result.num_updated_rows != 1 or result.num_inserted_rows != 1: + raise SystemExit( + f"rows updated={{result.num_updated_rows}} " + f"inserted={{result.num_inserted_rows}}" + ) + hits = table.search().with_row_id(True).limit(10).to_arrow() + by_id = dict(zip(hits["id"].to_pylist(), hits["_rowid"].to_pylist())) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + if blobs.to_pylist() != [b"updated", b"inserted"]: + raise SystemExit(blobs.to_pylist()) + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_merge_insert_blob_array_into_reopened_unregistered_table(tmp_path): + db = lancedb.connect(tmp_path) + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) + table = db.create_table("images", schema=schema) + table.add([{"id": 1, "image": b"before"}]) + + script = textwrap.dedent( + f"""\ + import pyarrow as pa + import lancedb + + db = lancedb.connect({str(tmp_path)!r}) + table = db.open_table("images") + image_type = table.schema.field("image").type + if type(image_type).__name__ != "StructType": + raise SystemExit( + f"expected StructType before lance import, got {{type(image_type)}}" + ) + + import lance + + updates = pa.Table.from_arrays( + [ + pa.array([1, 2], type=pa.int64()), + lance.blob_array([b"updated", b"inserted"]), + ], + names=["id", "image"], + ) + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute(updates) + ) + if result.num_updated_rows != 1 or result.num_inserted_rows != 1: + raise SystemExit( + f"rows updated={{result.num_updated_rows}} " + f"inserted={{result.num_inserted_rows}}" + ) + hits = table.search().with_row_id(True).limit(10).to_arrow() + by_id = dict(zip(hits["id"].to_pylist(), hits["_rowid"].to_pylist())) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + if blobs.to_pylist() != [b"updated", b"inserted"]: + raise SystemExit(blobs.to_pylist()) + """ + ) + result = subprocess.run( + [sys.executable, "-c", script], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr + + +def test_add_all_null_blob_column(): + db = lancedb.connect("memory:///") + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) + table = db.create_table("all_null", schema=schema) + table.add([{"id": 1, "image": None}, {"id": 2, "image": None}]) + by_id = _row_ids_by_id(table) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + assert blobs.to_pylist() == [None, None] + + +def test_create_table_nested_blob_schema_without_rows(): + db = lancedb.connect("memory:///") + schema = pa.schema( + [ + pa.field("id", pa.int64()), + pa.field("info", pa.struct([lancedb.blob("blob")])), + pa.field("images", pa.list_(lancedb.blob("image"))), + ] + ) + table = db.create_table("nested_empty", schema=schema) + assert table.count_rows() == 0 + + +def test_merge_insert_nested_blob_dicts(): + db = lancedb.connect("memory:///") + info = pa.StructArray.from_arrays( + [ + pa.array(["first"], type=pa.string()), + _blob_array("blob", [b"before"]), + ], + names=["name", "blob"], + ) + data = pa.Table.from_arrays( + [pa.array([1], type=pa.int64()), info], + names=["id", "info"], + ) + table = db.create_table("nested_merge", data=data) + result = ( + table.merge_insert("id") + .when_matched_update_all() + .execute([{"id": 1, "info": {"name": "first", "blob": b"after"}}]) + ) + assert result.num_updated_rows == 1 + by_id = _row_ids_by_id(table) + blobs = table.fetch_blobs("info.blob", [by_id[1]]) + assert blobs.to_pylist() == [b"after"] + + +def _list_blob_table(name): + db = lancedb.connect("memory:///") + blob_field = lancedb.blob("image") + images = pa.ListArray.from_arrays( + pa.array([0, 1], type=pa.int32()), _blob_array("image", [b"before"]) + ) + data = pa.Table.from_arrays( + [pa.array([1], type=pa.int64()), images], + schema=pa.schema( + [pa.field("id", pa.int64()), pa.field("images", pa.list_(blob_field))] + ), + ) + return db.create_table(name, data=data) + + +def test_merge_insert_list_blob_dicts(): + table = _list_blob_table("list_merge") + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute([{"id": 1, "images": [b"one", b"two"]}, {"id": 2, "images": None}]) + ) + assert result.num_updated_rows == 1 + assert result.num_inserted_rows == 1 + hits = table.search().limit(10).to_arrow() + sizes = { + row["id"]: None if row["images"] is None else [d["size"] for d in row["images"]] + for row in hits.to_pylist() + } + assert sizes == {1: [3, 3], 2: None} + + +def test_list_blob_column_queries_as_raw_descriptors(): + table = _list_blob_table("list_query") + hits = table.search().limit(10).to_arrow() + element = hits.schema.field("images").type.value_type + assert pa.types.is_struct(element) + assert "_lance_row_id" not in element.names + with pytest.raises(ValueError, match="expected struct before segment"): + table.fetch_blobs("images.image", [0]) + + +def test_row_addressable_paths_exclude_list_children(): + from lancedb.schema import row_addressable_blob_v2_paths + + schema = pa.schema( + [ + pa.field("id", pa.int64()), + pa.field("info", pa.struct([lancedb.blob("blob")])), + pa.field("images", pa.list_(lancedb.blob("image"))), + ] + ) + assert blob_v2_column_paths(schema) == ["info.blob", "images.image"] + assert row_addressable_blob_v2_paths(schema) == ["info.blob"] + + +def test_merge_insert_writes_pylance_blob_array(): + table = _blob_table("merge_pylance", [{"id": 1, "image": b"before"}]) + image = lance.blob_array([b"updated", b"inserted"]) + assert type(image.type) is LanceBlobType + assert type(image.type) is type(lancedb.BlobType()) + updates = pa.Table.from_arrays( + [pa.array([1, 2], type=pa.int64()), image], names=["id", "image"] + ) + + result = ( + table.merge_insert("id") + .when_matched_update_all() + .when_not_matched_insert_all() + .execute(updates) + ) + + assert result.num_updated_rows == 1 + assert result.num_inserted_rows == 1 + by_id = _row_ids_by_id(table) + blobs = table.fetch_blobs("image", [by_id[1], by_id[2]]) + assert blobs.to_pylist() == [b"updated", b"inserted"] + + def test_fetch_blobs_accepts_query_result(): table = _blob_table("from_result", [{"id": 1, "image": b"gamma"}]) hits = table.search().limit(10).to_arrow() diff --git a/python/python/tests/test_util.py b/python/python/tests/test_util.py index a9b66b2dd..acdef8eac 100644 --- a/python/python/tests/test_util.py +++ b/python/python/tests/test_util.py @@ -7,6 +7,7 @@ import pathlib from typing import Optional import lance +from lance.blob import BlobType as LanceBlobType from lancedb.conftest import MockTextEmbeddingFunction from lancedb.embeddings.base import EmbeddingFunctionConfig from lancedb.embeddings.registry import EmbeddingFunctionRegistry @@ -907,6 +908,165 @@ def test_cast_to_target_schema(): assert output == expected +def test_cast_to_target_schema_coerces_binary_to_blob_v2(): + data = pa.table({"image": pa.array([b"hello", None], type=pa.binary())}) + target = pa.schema([lancedb.blob("image")]) + + output = _cast_to_target_schema(data.to_reader(), target).read_all() + + image = output["image"].chunk(0) + assert type(image.type) is lancedb.BlobType + assert image.storage.to_pylist() == [ + {"data": b"hello", "uri": None, "position": None, "size": None}, + None, + ] + + +def test_cast_to_target_schema_coerces_binary_to_metadata_blob_struct(): + storage = lancedb.blob("image").type.storage_type + target = pa.schema( + [ + pa.field( + "image", + storage, + metadata={ + b"ARROW:extension:name": b"lance.blob.v2", + b"ARROW:extension:metadata": b"", + }, + ) + ] + ) + data = pa.table({"image": pa.array([b"hello", None], type=pa.binary())}) + + output = _cast_to_target_schema(data.to_reader(), target).read_all() + + image = output["image"].chunk(0) + assert not isinstance(image.type, pa.ExtensionType) + assert image.to_pylist() == [ + {"data": b"hello", "uri": None, "position": None, "size": None}, + None, + ] + + +def test_cast_to_target_schema_coerces_nested_binary_blob(): + data = pa.table( + { + "info": pa.array( + [{"blob": b"hello"}, {"blob": None}], + type=pa.struct([pa.field("blob", pa.binary())]), + ) + } + ) + target = pa.schema([pa.field("info", pa.struct([lancedb.blob("blob")]))]) + + output = _cast_to_target_schema(data.to_reader(), target).read_all() + + blob = output["info"].chunk(0).field("blob") + assert type(blob.type) is lancedb.BlobType + assert blob.storage.to_pylist() == [ + {"data": b"hello", "uri": None, "position": None, "size": None}, + None, + ] + + +def test_cast_to_target_schema_coerces_list_binary_blob_with_inferred_child_name(): + data = pa.table( + {"images": pa.array([[b"a", b"b"], None], type=pa.list_(pa.binary()))} + ) + target = pa.schema([pa.field("images", pa.list_(lancedb.blob("image")))]) + + output = _cast_to_target_schema(data.to_reader(), target).read_all() + + images = output["images"].chunk(0) + assert images.type.value_field.name == "image" + assert type(images.type.value_type) is lancedb.BlobType + assert images.to_pylist()[1] is None + assert images.values.storage.to_pylist() == [ + {"data": b"a", "uri": None, "position": None, "size": None}, + {"data": b"b", "uri": None, "position": None, "size": None}, + ] + + +def test_list_blob_coercion_preserves_null_slots_with_nonzero_extent(): + child = pa.field("image", pa.binary()) + source = pa.ListArray.from_arrays( + pa.array([0, 2, 4], type=pa.int32()), + pa.array([b"a", b"b", b"dead", b"beef"], type=pa.binary()), + mask=pa.array([False, True]), + ).cast(pa.list_(child)) + target = pa.schema([pa.field("images", pa.list_(lancedb.blob("image")))]) + + output = _cast_to_target_schema( + pa.table({"images": source}).to_reader(), target + ).read_all() + + images = output["images"].chunk(0) + assert images.to_pylist()[1] is None + assert [b["data"] for b in images.to_pylist()[0]] == [b"a", b"b"] + + +def test_fixed_size_list_blob_coercion_keeps_null_rows(): + child = pa.field("frame", pa.binary()) + source = ( + pa.FixedSizeListArray.from_arrays( + pa.array([b"a", b"b", b"c", b"d"], type=pa.binary()), 2 + ) + .take(pa.array([0, None], type=pa.int32())) + .cast(pa.list_(child, 2)) + ) + target = pa.schema([pa.field("frames", pa.list_(lancedb.blob("frame"), 2))]) + + output = _cast_to_target_schema( + pa.table({"frames": source}).to_reader(), target + ).read_all() + + frames = output["frames"].chunk(0) + assert frames.to_pylist()[1] is None + assert [b["data"] for b in frames.to_pylist()[0]] == [b"a", b"b"] + + +def test_cast_to_target_schema_accepts_pylance_blob_v2(): + target_type = lancedb.BlobType() + source = lance.blob_array([b"hello", None]) + assert type(source.type) is LanceBlobType + assert type(source.type) is type(target_type) + data = pa.table({"image": source}) + target = pa.schema([pa.field("image", target_type)]) + + output = _cast_to_target_schema(data.to_reader(), target).read_all() + + image = output["image"].chunk(0) + assert type(image.type) is LanceBlobType + assert image.type == target_type + assert image.storage.to_pylist() == [ + {"data": b"hello", "uri": None, "position": None, "size": None}, + None, + ] + + +def test_cast_to_target_schema_rejects_different_blob_v2_class(): + class OtherBlobType(pa.ExtensionType): + def __init__(self): + super().__init__(lancedb.BlobType().storage_type, "lance.blob.v2") + + def __arrow_ext_serialize__(self) -> bytes: + return b"" + + @classmethod + def __arrow_ext_deserialize__( + cls, storage_type: pa.DataType, serialized: bytes + ) -> "OtherBlobType": + return cls() + + storage = lance.blob_array([b"hello"]).storage + source = pa.ExtensionArray.from_storage(OtherBlobType(), storage) + data = pa.table({"image": source}) + target = pa.schema([lancedb.blob("image")]) + + with pytest.raises(pa.ArrowTypeError, match="different extension type"): + _cast_to_target_schema(data.to_reader(), target).read_all() + + def test_sanitize_data_stream(): # Make sure we don't collect the whole stream when running sanitize_data schema = pa.schema({"a": pa.int32()}) diff --git a/rust/lancedb/src/table/datafusion/blob_coerce.rs b/rust/lancedb/src/table/datafusion/blob_coerce.rs index cb984f7f4..0596e7a2d 100644 --- a/rust/lancedb/src/table/datafusion/blob_coerce.rs +++ b/rust/lancedb/src/table/datafusion/blob_coerce.rs @@ -36,6 +36,14 @@ pub(super) fn coerce_blob_expr( }; let input_shape = match input_field.data_type() { + DataType::Null => { + let expr: Arc = Arc::new(CastExpr::new( + input_expr, + table_field.data_type().clone(), + None, + )); + return Ok((expr, table_field.clone())); + } DataType::Binary | DataType::LargeBinary | DataType::BinaryView => BlobInputShape::Bytes, DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => BlobInputShape::String, DataType::Struct(children) => { @@ -155,7 +163,7 @@ mod tests { use crate::blob::blob; use arrow_array::{ Array, ArrayRef, BinaryArray, BinaryViewArray, Int32Array, Int64Array, LargeBinaryArray, - RecordBatch, StringArray, StringViewArray, StructArray, UInt8Array, UInt64Array, + NullArray, RecordBatch, StringArray, StringViewArray, StructArray, UInt8Array, UInt64Array, }; use arrow_schema::Schema; use datafusion::prelude::SessionContext; @@ -279,6 +287,18 @@ mod tests { assert_eq!(data.value(0), b"view"); } + #[tokio::test] + async fn null_column_coerces_to_all_null_blob_struct() { + let batch = batch_with_image( + Field::new("image", DataType::Null, true), + Arc::new(NullArray::new(2)), + ); + let coerced = coerce(batch, &blob_table_schema()).await; + let image = image_struct(&coerced); + assert!(image.is_null(0)); + assert!(image.is_null(1)); + } + #[tokio::test] async fn binary_nulls_stay_null_after_coercion() { let batch = batch_with_image( From 84f46df876b988aeb22a05eff6eb671d174b87a6 Mon Sep 17 00:00:00 2001 From: Will Jones Date: Thu, 27 Aug 2026 17:35:06 -0700 Subject: [PATCH 10/32] ci(nodejs): fix nightly OOM on the aarch64 publish legs (#4077) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nightly `NPM Publish` run has failed every night since at least Aug 23, always on the same two legs: `aarch64-unknown-linux-gnu` and `aarch64-unknown-linux-musl`. The other five targets pass. rustc is OOM-killed during the fat-LTO codegen of the cdylib — `signal: 9` with no diagnostic, about 27 minutes in — and on the musl leg that takes the whole runner down with `The runner has received a shutdown signal`. Both legs now pass: | leg | before | peak memory | wall time | | --- | --- | --- | --- | | `aarch64-unknown-linux-gnu` | OOM-killed at ~27 min | 31391 → 22851 MiB | 38m43s → 22m04s | | `aarch64-unknown-linux-musl` | runner killed at ~28 min | >32 GiB → 16516 MiB | ~40 min → 20m50s | **ThinLTO** is most of that. Fat LTO is single-threaded, and its peak is consumed inside rustc's LLVM before any linker process is spawned — which is why it is the whole fix on musl, and why lld alone left the gnu leg still peaking at 31391 MiB against the runner's 32 GiB. Both legs now use the `lto: thin` / `codegen_units: 16` settings that darwin and both Windows legs already use, at a cost of a few percent runtime performance. **lld** covers the rest, on the gnu leg. arm64 Linux otherwise links through GNU `ld` where x86_64 already defaults to `rust-lld`, which is why only the arm64 legs hit this at all; on a comparable arm64 build (`lancedb/sophon#7313`) it cut the largest single linker process from 7.0 to 4.0 GiB and wall time by 35%. The flags live in a small wrapper script used as the linker rather than in `-C link-arg`, because the per-target rustflags variable does not reach every unit that links: dependency crates linking a dylib (`crc-fast`, `lance-arrow`) were invoked as bare `clang`, which targets the x86_64 host and fails with `Relocations in generic ELF (EM: 183)`. Separately, and affecting five legs rather than two: the three ThinLTO targets exported `CARGO_PROFILE_RELEASE_LTO` and `CARGO_PROFILE_RELEASE_CODEGEN_UNITS` from `pre_build`, which runs inside the build step — after the cache step. `Swatinem/rust-cache` computes its key when the action runs, before any step, so step-local values are invisible to it. The result is a loop that never converges: the key never changes, so restores are exact hits, an exact hit makes the post-run save a no-op, and cargo invalidates the restored artifacts anyway because the flags differ. Those legs have been rebuilding cold on every run. Both values move to job-level `env:` ahead of the cache step, driven by new `lto:`/`codegen_units:` matrix fields, and are forwarded into the containers with `-e` since `docker run` inherits nothing. Every leg's cache key shifts once as a result, so expect one cold rebuild. A `Report peak memory` step is added so whether these legs fit is a number rather than an inference from whether the runner survived. It produced the figures above. ## Not included Moving these legs to native arm64 runners. It would retire the zig cross path, the `AT_HWCAP2` workaround and the `TARGET_CC` override, and arm64 runners are billed roughly 37% below x64 at equal core count — but the `lts-debian-aarch64` image exists to link against the manylinux2014 sysroot's glibc 2.17, and building natively on ubuntu-24.04 would raise the minimum glibc for every published aarch64 binary. That is a user-facing decision, not a CI cleanup. Dropping these legs to smaller runners, which is where the real cost saving is — larger runners are billed even on public repos. On these numbers it is not available yet: musl at 16516 MiB is about 130 MiB over what a 16 GB standard runner has. Worth revisiting as a follow-up. ## Testing Cargo's rustflags precedence was checked locally rather than taken from the docs, since getting it wrong would silently change the published binaries. With a throwaway crate carrying both a `target.'cfg(all())'` and a per-target rustflags table: setting `RUSTFLAGS` discards both, and setting it to the empty string discards them too. That rules out routing the linker flag through a job-level `RUSTFLAGS`, because `env:` keys cannot be conditionally omitted and every other leg would then silently lose the `target-cpu`/`target-feature` settings in `.cargo/config.toml` — `+avx2` on x86_64 and `-crt-static` on aarch64-musl. --------- Co-authored-by: Claude Opus 5 (1M context) --- .github/workflows/npm-publish.yml | 102 ++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 72ef5ad13..ee6906e00 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -40,40 +40,31 @@ jobs: - target: aarch64-apple-darwin host: macos-latest features: fp16kernels + # Fat LTO was ~111 of this job's ~113 minutes. + lto: thin + codegen_units: 16 pre_build: |- brew install protobuf - # Fat LTO (the workspace default in .cargo/config.toml) is - # single-threaded and is the peak-memory step of the build. On - # this runner it accounted for ~111 of the job's ~113 minutes, - # making it the critical path of the entire publish pipeline. - # ThinLTO parallelizes it across the runner's cores, for a few - # percent of runtime performance. - export CARGO_PROFILE_RELEASE_LTO=thin - export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 - target: x86_64-pc-windows-msvc host: windows-2025 features: "," + # The lower peak also keeps this on the standard 4-core runner. + lto: thin + codegen_units: 16 pre_build: |- choco install --no-progress protoc ninja nasm tail -n 1000 /c/ProgramData/chocolatey/logs/chocolatey.log # There is an issue where choco doesn't add nasm to the path export PATH="$PATH:/c/Program Files/NASM" nasm -v - # See the ThinLTO note on aarch64-apple-darwin above. Keeping - # peak memory down is also what lets this run on the standard - # 4-core runner: the 8-core larger runner was only needed to - # stop fat LTO from OOMing rustc-LLVM. - export CARGO_PROFILE_RELEASE_LTO=thin - export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 - target: aarch64-pc-windows-msvc host: windows-2025 features: "," + lto: thin + codegen_units: 16 pre_build: |- choco install --no-progress protoc rustup target add aarch64-pc-windows-msvc - # See the ThinLTO note on aarch64-apple-darwin above. - export CARGO_PROFILE_RELEASE_LTO=thin - export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=16 - target: x86_64-unknown-linux-gnu host: ubuntu-latest features: fp16kernels @@ -103,6 +94,14 @@ jobs: # https://github.com/napi-rs/napi-rs/blob/main/debian-aarch64.Dockerfile docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 features: "fp16kernels" + # Fat LTO OOM-killed rustc every nightly; even with lld it peaked + # at 31391 MiB of the runner's 32 GiB. + lto: thin + codegen_units: 16 + # arm64 Linux links through GNU `ld` where x86_64 defaults to + # `rust-lld`, which is why only arm64 OOM'd. lld cut the largest + # linker process 7.0 -> 4.0 GiB (lancedb/sophon#7313). + linker: /tmp/aarch64-lld-clang pre_build: |- set -e && apt-get update && @@ -112,9 +111,30 @@ jobs: # AT_HWCAP2 (added in Linux 3.17). Define it for aws-lc-sys. export CFLAGS="$CFLAGS -DAT_HWCAP2=26" && rustup target add aarch64-unknown-linux-gnu + # Not `&&`-chained: in dash, errexit does not fire for a + # non-final command in an `&&` list, so failures were ignored. + # + # A wrapper rather than `-C link-arg` because the per-target + # rustflags variable does not reach every unit that links, while + # the linker variable does. `clang` because GCC silently ignores + # `-fuse-ld=lld` unless built with lld support. Two echoes + # because printf's newline escape gets rewritten to `;` between + # here and the container. + echo '#!/bin/sh' > /tmp/aarch64-lld-clang + echo 'exec clang --target=aarch64-unknown-linux-gnu --sysroot=/usr/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot --gcc-toolchain=/usr/aarch64-unknown-linux-gnu -fuse-ld=lld "$@"' >> /tmp/aarch64-lld-clang + chmod 0755 /tmp/aarch64-lld-clang + # Fail now, not at the cdylib link ~30 minutes later. Linking at + # all also proves lld resolved; clang errors out when it cannot. + echo 'int main(void){return 0;}' > /tmp/probe.c + /tmp/aarch64-lld-clang /tmp/probe.c -o /tmp/probe + readelf -h /tmp/probe | grep AArch64 - target: aarch64-unknown-linux-musl host: ubuntu-2404-8x-x64 features: "," + # Fat LTO took the whole runner down. lld cannot help: it died + # inside rustc's LLVM, before any linker was spawned. + lto: thin + codegen_units: 16 pre_build: |- set -e && sudo apt-get update && @@ -123,6 +143,19 @@ jobs: export EXTRA_ARGS="-x" name: build - ${{ matrix.settings.target }} runs-on: ${{ matrix.settings.host }} + # On the job, not exported from `pre_build`: `Swatinem/rust-cache` hashes + # `CARGO_*` into its cache key before any step runs, so a step-local export + # leaves the key unchanged while cargo still rebuilds cold. The ThinLTO + # legs had been doing that every run. + # + # Not `RUSTFLAGS`: setting it, even to "", discards every config-file + # rustflag, silently dropping .cargo/config.toml's `target-cpu` and + # `target-feature` from the published binaries. + env: + CARGO_PROFILE_RELEASE_LTO: ${{ matrix.settings.lto || 'fat' }} + CARGO_PROFILE_RELEASE_CODEGEN_UNITS: ${{ matrix.settings.codegen_units || '1' }} + # Empty elsewhere: a per-target variable is only read for that triple. + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.settings.linker }} defaults: run: working-directory: nodejs @@ -169,19 +202,15 @@ jobs: # creating ref). The nightly cadence also keeps entries inside # GitHub's 7-day eviction window, which a tag-only trigger would not. save-if: ${{ github.ref == 'refs/heads/main' }} - # Docker builds can use rust-cache too. `target/` already lives on the - # host because the whole workspace is bind-mounted into the container, and - # rust-cache's prune and save run host-side, so they can manage it -- which - # is what keeps the entry to dependency artifacts rather than a multi-GB - # copy of everything. + # Docker builds can use rust-cache too: the workspace is bind-mounted, so + # `target/` lives on the host and rust-cache's prune keeps the entry + # small. # # Two differences from the native builds. The container's CARGO_HOME is - # bind-mounted from `.cargo-cache` rather than the host's ~/.cargo, so that - # has to be cached explicitly. And the key is derived from the *host* rustc - # version, which is not the compiler that produced these artifacts; that is - # safe because cargo fingerprints the real compiler and rebuilds on a - # mismatch, it just means a base-image toolchain bump costs one cold build - # instead of invalidating the key. + # bind-mounted from `.cargo-cache` rather than ~/.cargo, so that is cached + # explicitly. And the key uses the *host* rustc version, not the compiler + # that built these artifacts -- safe, since cargo fingerprints the real + # one; a base-image bump just costs one cold build. - name: Cache cargo (docker builds) uses: Swatinem/rust-cache@v2 if: ${{ matrix.settings.docker }} @@ -210,9 +239,14 @@ jobs: # cache step above saves. Previously the registry mounts pointed at # `.cargo/...`, a path nothing cached, so the container re-downloaded # the whole crate registry on every run. + # + # `docker run` inherits nothing; `-e NAME` carries the job's `env:` in. options: "--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db \ -v ${{ github.workspace }}/.cargo-cache/registry/cache:/usr/local/cargo/registry/cache \ -v ${{ github.workspace }}/.cargo-cache/registry/index:/usr/local/cargo/registry/index \ + -e CARGO_PROFILE_RELEASE_LTO \ + -e CARGO_PROFILE_RELEASE_CODEGEN_UNITS \ + -e CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER \ -v ${{ github.workspace }}:/build -w /build/nodejs" run: | set -e @@ -256,6 +290,18 @@ jobs: if: always() run: df -h shell: bash + - name: Report peak memory + if: always() && runner.os == 'Linux' + shell: bash + run: | + peak=$(find /sys/fs/cgroup -name memory.peak -readable \ + -exec cat {} + 2>/dev/null | sort -n | tail -1) + if [ -n "$peak" ]; then + echo "peak memory: $((peak / 1024 / 1024)) MiB" + else + echo "peak memory: unavailable (no readable cgroup v2 memory.peak)" + fi + free -g || true - name: Upload artifact uses: actions/upload-artifact@v7 with: From 6c8aa22704690bf9875ef446897c43e864bfc502 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Thu, 27 Aug 2026 17:47:27 -0700 Subject: [PATCH 11/32] feat: let a computed-column batch read its own earlier declarations (#4072) `add_columns().computed()` accepted several columns in one call but bound each against the table's schema as it stood before the call, so `a` and `b = a + 1` had to be two commits. A server staging declarations behind other schema work has no atomic way to do that, and a caller reading the builder's plural signature reasonably expects the batch to be one. Each accepted column now joins the schema the next one resolves against, so the batch is planned and committed as one. Order is the dependency order; reading ahead is still an unknown column. `validate_declarations` exposes the schema-level checks -- the Function-binding guard and the planning -- without a commit, for callers that must reject before earlier work in the same request lands; LSM state is table state and stays a commit-time check. Refresh order matters for a dependent column: `b = coalesce(a, 0)` refreshed before `a` would bake zeros from `a`'s placeholder null, and the fill-once contract keeps them. Refresh now refuses, naming the input, while a computed input still has rows a refresh of it would fill -- the same probe refresh already uses to detect a no-op. Otherwise it is one snapshot and one commit, as before; a concurrent append is not in the commit and waits for the next refresh. Refreshing dependencies on the caller's behalf was considered and rejected: it is not how materialized views or our own backfill scheduler behave, and it needs multi-commit fencing that an explicit per-row fill marker would make unnecessary. --- rust/lancedb/src/table/computed_columns.rs | 105 +++++++++++- rust/lancedb/src/table/refresh.rs | 190 +++++++++++++++++++-- 2 files changed, 270 insertions(+), 25 deletions(-) diff --git a/rust/lancedb/src/table/computed_columns.rs b/rust/lancedb/src/table/computed_columns.rs index 2b95cb34c..0f89ca612 100644 --- a/rust/lancedb/src/table/computed_columns.rs +++ b/rust/lancedb/src/table/computed_columns.rs @@ -22,7 +22,7 @@ use std::collections::{BTreeSet, HashMap}; use std::sync::Arc; -use arrow_schema::{DataType, Field as ArrowField, Schema as ArrowSchema, SchemaRef}; +use arrow_schema::{DataType, Field as ArrowField, Fields, Schema as ArrowSchema, SchemaRef}; use datafusion_common::tree_node::TreeNode; use datafusion_physical_plan::PhysicalExpr; use lance::dataset::NewColumnTransform; @@ -1273,6 +1273,11 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< /// refresh time: that the expression parses, that every column it reads /// exists, and that the target name is free. A declaration that survives this /// is one a refresh can always act on. +/// +/// Each accepted column joins the schema the next one resolves against, so a +/// batch may declare `a` and then `b = a + 1` in one commit. Refresh order +/// then matters, and refresh enforces it: `b` is refused while `a` still has +/// unfilled rows. pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result> { if columns.is_empty() { return Err(Error::InvalidInput { @@ -1280,11 +1285,11 @@ pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result = Vec::with_capacity(columns.len()); for (name, expression) in columns { - if schema.field_with_name(name).is_ok() || declared.contains(&name.as_str()) { + if schema.field_with_name(name).is_ok() { return Err(Error::ColumnAlreadyExists { name: name.clone() }); } @@ -1292,16 +1297,50 @@ pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result(), + schema.metadata().clone(), + )); + fields.push(field); } Ok(fields) } +/// Run the schema-level checks of +/// [`AddColumnsBuilder::computed`](super::AddColumnsBuilder::computed) against +/// `schema` without committing: the Function-binding guard and the planning of +/// every declaration. For callers that stage declarations behind other work +/// and need those rejections before any of it lands. +/// +/// Only the schema is consulted. Declaring also refuses a table with an LSM +/// write spec or retained SSTables; that is table state, checked at commit. +/// +/// ``` +/// # use std::sync::Arc; +/// # use arrow_schema::{DataType, Field, Schema}; +/// use lancedb::table::computed_columns::validate_declarations; +/// +/// let schema = Arc::new(Schema::new(vec![Field::new("x", DataType::Int32, false)])); +/// let declarations = vec![ +/// ("a".to_string(), "x + 1".to_string()), +/// ("b".to_string(), "a * 2".to_string()), +/// ]; +/// assert!(validate_declarations(schema.clone(), &declarations).is_ok()); +/// assert!(validate_declarations(schema, &[("c".into(), "random()".into())]).is_err()); +/// ``` +pub fn validate_declarations(schema: SchemaRef, columns: &[(String, String)]) -> Result<()> { + ensure_no_function_bindings_for_mutation(schema.as_ref(), "schema evolution")?; + plan(schema, columns).map(drop) +} + /// Build the transform that declares `columns` against `schema`. /// /// An all-null column is how a binding with no values yet is carried into a @@ -1340,6 +1379,22 @@ pub(super) async fn add_foreign_kind(table: &crate::Table, name: &str, kind: &st #[cfg(test)] mod tests { + /// The gate's reproducer: the validator applies the same schema-level + /// guard declaring does, so a staging caller is refused before it commits + /// anything else. + #[test] + fn test_validate_declarations_matches_schema_admission_barriers() { + let schema = Arc::new(ArrowSchema::new_with_metadata( + vec![ArrowField::new("x", DataType::Int32, true)], + HashMap::from([( + FUNCTION_BINDINGS_META_KEY.to_string(), + "not valid binding metadata".to_string(), + )]), + )); + let declarations = vec![("a".to_string(), "x + 1".to_string())]; + assert!(super::validate_declarations(schema, &declarations).is_err()); + } + #[test] fn output_arrow_type_grammar_matches_the_shared_golden() { let golden: serde_json::Value = serde_json::from_str(include_str!( @@ -1582,6 +1637,40 @@ mod tests { assert!(declared(&table).await.is_empty()); } + /// A batch may build on itself: one commit, and the later entry's inputs + /// name the earlier one. + #[tokio::test] + async fn test_a_declaration_may_read_one_declared_before_it() { + let table = table_with_ints("chain").await; + let before = table.version().await.unwrap(); + add_computed( + &table, + &[("a".into(), "x + 1".into()), ("b".into(), "a * 2".into())], + ) + .await + .unwrap(); + assert_eq!(table.version().await.unwrap(), before + 1); + let declared = declared(&table).await; + assert_eq!(declared[1].name, "b"); + assert_eq!(declared[1].inputs, vec!["a".to_string()]); + + // Order is the dependency order; reading ahead is still unknown. + let err = add_computed( + &table, + &[("c".into(), "d + 1".into()), ("d".into(), "x + 1".into())], + ) + .await + .unwrap_err(); + assert!(matches!(err, Error::InvalidExpression { column, .. } if column == "c")); + assert!( + validate_declarations( + table.schema().await.unwrap(), + &[("e".into(), "random()".into())] + ) + .is_err() + ); + } + /// A column added by an ordinary transform is materialized, not bound, so /// it carries no declaration to report. #[tokio::test] diff --git a/rust/lancedb/src/table/refresh.rs b/rust/lancedb/src/table/refresh.rs index 35f883411..bc2cc38d1 100644 --- a/rust/lancedb/src/table/refresh.rs +++ b/rust/lancedb/src/table/refresh.rs @@ -7,6 +7,16 @@ //! therefore idempotent and does not observe input mutation -- once a row is //! filled, changing what the expression reads leaves the stored result alone. //! +//! A column's computed inputs are filled first -- the dependency graph is +//! walked once, each reachable column filled once in dependency order, each +//! fill its own commit. Every fill in the pass, the requested column's +//! included, covers only the fragments of the snapshot the pass started +//! from: a commit may rebase over a concurrent append, and the fragment that +//! admits carries placeholder nulls no earlier fill covered, so it waits for +//! a later refresh rather than being read as values. Two concurrent fills of +//! one input collide on its field in lance's conflict check, so a dependent +//! fill can only commit over inputs that were durable when it read them. +//! //! Two passes per fragment. The first scans only the unfilled live rows and //! evaluates the expression over them, which yields the exact fill count and //! decides whether the fragment is staged at all -- a fragment where nothing @@ -41,7 +51,8 @@ use crate::{Error, Result}; /// The result of refreshing a computed column. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct RefreshColumnResult { - /// Rows that had a value computed. + /// Rows that had a value computed, in the requested column only; inputs + /// filled on its behalf are not counted. #[serde(default)] pub rows_filled: u64, /// The commit version associated with the operation. @@ -52,6 +63,7 @@ pub struct RefreshColumnResult { struct RefreshExecution { result: RefreshColumnResult, source_version: u64, + published_version: Option, } /// Internal implementation of the refresh logic. @@ -74,7 +86,12 @@ async fn execute_refresh_column_with_source( let expression = declared_expression(&dataset, column)?; let schema = Arc::new(ArrowSchema::from(dataset.schema())); - let bound = Arc::new(super::computed_columns::bind(schema, column, &expression)?); + let bound = Arc::new(super::computed_columns::bind( + schema.clone(), + column, + &expression, + )?); + ensure_inputs_filled(&dataset, &schema, column, &bound).await?; let field = dataset .schema() .field(column) @@ -100,25 +117,25 @@ async fn execute_refresh_column_with_source( replacements.push(fragment.write_columns(values, &column_schema).await?); } + let source_version = dataset.version().version; if replacements.is_empty() { - let source_version = dataset.version().version; return Ok(RefreshExecution { result: RefreshColumnResult { rows_filled: 0, version: source_version, }, source_version, + published_version: None, }); } - let read_version = dataset.version().version; // The dataset's own session, so registrations and caches survive the // commit being installed on the handle. let session = dataset.session(); let new_dataset = Dataset::commit( WriteDestination::Dataset(dataset.clone()), Operation::DataReplacement { replacements }, - Some(read_version), + Some(source_version), None, None, session, @@ -133,10 +150,52 @@ async fn execute_refresh_column_with_source( rows_filled, version, }, - source_version: read_version, + source_version, + published_version: Some(version), }) } +/// Refuse while a computed input still has rows a refresh of it would fill: +/// read now, its placeholder null would be evaluated as a value and kept. +async fn ensure_inputs_filled( + dataset: &Dataset, + schema: &Arc, + column: &str, + bound: &BoundExpression, +) -> Result<()> { + for input in &bound.roots { + let Some(declaration) = schema + .field_with_name(input) + .ok() + .and_then(computed_column_from_field) + else { + continue; + }; + let ComputedColumnKind::Sql { expression } = &declaration.kind else { + return Err(Error::NotSupported { + message: format!( + "computed column '{column}' reads '{input}', whose fill state this \ + refresh cannot check; refresh '{input}' first" + ), + }); + }; + let input_bound = super::computed_columns::bind(schema.clone(), input, expression)?; + let mut unfilled = 0u64; + for fragment in dataset.get_fragments() { + unfilled += count_fragment_gains(dataset, &fragment, &input_bound, input).await?; + } + if unfilled > 0 { + return Err(Error::InvalidInput { + message: format!( + "computed column '{column}' reads '{input}', which has {unfilled} unfilled \ + rows; refresh '{input}' first" + ), + }); + } + } + Ok(()) +} + /// Run the refresh as a [`Job`] in this process. pub(crate) async fn execute_refresh_column_async( table: &NativeTable, @@ -160,8 +219,7 @@ pub(crate) async fn execute_refresh_column_async( rows_failed: 0, rows_remaining: 0, source_version: execution.source_version, - published_version: (execution.result.rows_filled > 0) - .then_some(execution.result.version), + published_version: execution.published_version, }) }))) } @@ -384,7 +442,8 @@ mod tests { .version) } - async fn read(table: &Table, column: &str) -> Vec> { + async fn read(table: &Table, column: &str) -> Vec> { + use arrow_array::{Array, Int64Array}; let batches = table .query() .select(Select::columns(&[column])) @@ -394,15 +453,19 @@ mod tests { .try_collect::>() .await .unwrap(); - let mut values: Vec> = batches + let mut values: Vec> = batches .iter() .flat_map(|batch| { - batch[column] - .as_any() - .downcast_ref::() - .unwrap() - .iter() - .collect::>() + let array = &batch[column]; + match array.as_any().downcast_ref::() { + Some(ints) => ints.iter().map(|v| v.map(i64::from)).collect::>(), + None => array + .as_any() + .downcast_ref::() + .unwrap() + .iter() + .collect::>(), + } }) .collect(); values.sort(); @@ -414,6 +477,98 @@ mod tests { table.add(batch).execute().await.unwrap(); } + /// The gate's reproducer: `b = coalesce(a, 0)` refreshed before `a` + /// must not bake zeros from `a`'s placeholder null. It is refused, and + /// names the input, until `a` is filled -- after every append too. + #[tokio::test] + async fn test_dependent_refresh_refuses_an_unfilled_input() { + let table = table_with("dependent_refresh_order", vec![1, 2, 3]).await; + table + .add_columns() + .computed("a", "x + 1") + .computed("b", "coalesce(a, 0)") + .execute() + .await + .unwrap(); + + let err = table.refresh_column("b").await.unwrap_err(); + assert!( + matches!(&err, Error::InvalidInput { message } if message.contains("refresh 'a' first")), + "{err}" + ); + assert_eq!(read(&table, "b").await, vec![None, None, None]); + + assert_eq!(table.refresh_column("a").await.unwrap().rows_filled, 3); + assert_eq!(table.refresh_column("b").await.unwrap().rows_filled, 3); + assert_eq!(read(&table, "b").await, vec![Some(2), Some(3), Some(4)]); + + append(&table, vec![10]).await; + assert!(table.refresh_column("b").await.is_err()); + table.refresh_column("a").await.unwrap(); + assert_eq!(table.refresh_column("b").await.unwrap().rows_filled, 1); + assert_eq!( + table.count_rows(Some("b = 0".to_string())).await.unwrap(), + 0 + ); + } + + /// Names that need quoting, and a nested input, survive the trip through + /// declaration metadata and the dependency check: the recorded inputs + /// are matched by name, never re-parsed as SQL. + #[tokio::test] + async fn test_dependent_refresh_handles_awkward_column_names() { + use arrow_array::{Int32Array, StructArray}; + use arrow_schema::{DataType, Field, Fields}; + + let conn = connect("memory://").execute().await.unwrap(); + let age_fields = Fields::from(vec![Field::new("age", DataType::Int32, true)]); + let meta = StructArray::new( + age_fields.clone(), + vec![Arc::new(Int32Array::from(vec![10, 20])) as _], + None, + ); + let schema = Arc::new(arrow_schema::Schema::new(vec![ + Field::new("camelCase", DataType::Int32, true), + Field::new("with-hyphen", DataType::Int32, true), + Field::new("meta", DataType::Struct(age_fields), true), + ])); + let batch = arrow_array::RecordBatch::try_new( + schema, + vec![ + Arc::new(Int32Array::from(vec![1, 2])) as _, + Arc::new(Int32Array::from(vec![100, 200])) as _, + Arc::new(meta) as _, + ], + ) + .unwrap(); + let table = conn + .create_table("awkward_names", batch) + .execute() + .await + .unwrap(); + + table + .add_columns() + .computed("y", "`camelCase` * 2") + .computed("z", "coalesce(y, 0) + `with-hyphen` + meta.age") + .execute() + .await + .unwrap(); + let z = crate::table::computed_columns::computed_columns( + table.schema().await.unwrap().as_ref(), + ) + .into_iter() + .find(|c| c.name == "z") + .unwrap(); + assert_eq!(z.inputs, vec!["meta.age", "with-hyphen", "y"]); + + let err = table.refresh_column("z").await.unwrap_err(); + assert!(err.to_string().contains("refresh 'y' first"), "{err}"); + assert_eq!(table.refresh_column("y").await.unwrap().rows_filled, 2); + assert_eq!(table.refresh_column("z").await.unwrap().rows_filled, 2); + assert_eq!(read(&table, "z").await, vec![Some(112), Some(224)]); + } + #[tokio::test] async fn test_refresh_fills_a_declared_column() { let table = table_with("refresh_fills", vec![1, 2, 3]).await; @@ -651,7 +806,8 @@ mod tests { let read_back = read(&table, "doubled").await; assert_eq!(read_back.len(), 20_000); - let mut expected: Vec> = values.iter().map(|v| Some(v * 2)).collect(); + let mut expected: Vec> = + values.iter().map(|v| Some(i64::from(v * 2))).collect(); expected.sort(); assert_eq!(read_back, expected); } From c94d9a2a166eca9606c8308d6d25946c44f1b6a5 Mon Sep 17 00:00:00 2001 From: Lance Release Date: Fri, 28 Aug 2026 00:51:15 +0000 Subject: [PATCH 12/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.11=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package-lock.json | 4 ++-- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 2e0b78bf3..3d57dc0fe 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.11" +current_version = "0.38.0-beta.12" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index 822689a0c..f12988efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,7 +5402,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" dependencies = [ "ahash", "anyhow", @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" dependencies = [ "arrow-array", "arrow-buffer", @@ -5515,7 +5515,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index 1ce012522..e19880e29 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.11 + 0.38.0-beta.12 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index c6acc7dfe..3864ed127 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.11 + 0.38.0-beta.12 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 0b85b69df..b3521f9c6 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.11 + 0.38.0-beta.12 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index 6496c6384..c3b69424f 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index 38b3db7d7..2b8d43c3d 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index a256cb1ee..6656fdd5c 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 567b785b0..f8f3e151f 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index 4443a2748..20efa860a 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index 5c0710d56..4b735a687 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 648c985f1..35fee5ee0 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index 1f3bfdeb8..925211fbe 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index d46f08628..b996b0810 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lancedb/lancedb", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@lancedb/lancedb", - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "cpu": [ "x64", "arm64" diff --git a/nodejs/package.json b/nodejs/package.json index 665e2a522..19c7f4d32 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.11", + "version": "0.38.0-beta.12", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index b97fad0ed..0ee561977 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index a71e3c948..881a5017e 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.11" +version = "0.38.0-beta.12" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From 6ab3b9eb30d449d61ae8e1ca76744babb9052af5 Mon Sep 17 00:00:00 2001 From: Will Jones Date: Thu, 27 Aug 2026 19:06:07 -0700 Subject: [PATCH 13/32] ci: upgrade chacha20 to 0.10.2 (#4078) The pinned version was yanked due to UB in some SIMD kernels. Upgrading. --- Cargo.lock | 4 ++-- deny.toml | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f12988efa..cff38a304 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1597,9 +1597,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.3.0", diff --git a/deny.toml b/deny.toml index 3672321d0..20510adc2 100644 --- a/deny.toml +++ b/deny.toml @@ -131,18 +131,13 @@ allow = [ "BSD-3-Clause", "ISC", "Unicode-3.0", - "Unicode-DFS-2016", "Zlib", "CC0-1.0", "MPL-2.0", "BSL-1.0", - "OpenSSL", # 0BSD ("BSD Zero Clause") is effectively public domain — no attribution # required. Pulled in by `mock_instant`. "0BSD", - # bzip2-1.0.6 is the permissive upstream bzip2 license (BSD-like). Pulled - # in by `libbz2-rs-sys`, the pure-Rust bzip2 implementation. - "bzip2-1.0.6", # CDLA-Permissive-2.0 is a permissive data license used by `webpki-roots` # for the Mozilla CA root bundle. Data-only, distribution-compatible. "CDLA-Permissive-2.0", @@ -150,12 +145,7 @@ allow = [ confidence-threshold = 0.8 # Per-crate license exceptions: allow a license for a specific crate only, # rather than globally via the `allow` list above. -exceptions = [ - # CDDL-1.0 (copyleft) is pulled in only as a dev/profiling dependency via - # `inferno` -> `pprof` -> `lance-testing`; it is a test dependency that we - # do not distribute, so scope the allowance to `inferno` alone. - { allow = ["CDDL-1.0"], crate = "inferno" }, -] +exceptions = [] # Crates whose license cannot be determined from Cargo metadata but whose # license we've manually confirmed from upstream. Keep this list minimal. [[licenses.clarify]] From 0559108fa94b29cc5db4d390d8b37794b9fcc41c Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 28 Aug 2026 17:23:49 +0800 Subject: [PATCH 14/32] feat: support blob computed column refresh (#4081) Computed-column planning currently sees Blob v2 storage descriptors, so expressions cannot consume payload bytes or preserve Blob semantics in their outputs. A computed declaration now derives its output field from its expression. A direct projection of a Blob v2 field inherits the source field's Blob metadata; other expressions retain their ordinary Arrow-inferred type. Declarations remain ordered, so the same rule applies across chained projections. Refresh materializes referenced Blob inputs as `LargeBinary` payload bytes and publishes inherited Blob outputs through Lance's Blob conversion path. Remote requests remain within the shared namespace contract as `{name, computed}`; the server planner is being updated in tandem to implement the same Blob-aware planning semantics, and remote enablement must be aligned with that server rollout. The existing null-as-unfilled contract remains unchanged. Row-level freshness and cell flags remain follow-up work. --- python/python/lancedb/remote/table.py | 10 +- python/python/lancedb/table.py | 15 +- python/python/tests/test_table.py | 23 + rust/lancedb/src/remote/table.rs | 8 +- rust/lancedb/src/table.rs | 4 +- rust/lancedb/src/table/computed_columns.rs | 289 ++++++++++-- rust/lancedb/src/table/refresh.rs | 522 ++++++++++++++++++++- 7 files changed, 821 insertions(+), 50 deletions(-) diff --git a/python/python/lancedb/remote/table.py b/python/python/lancedb/remote/table.py index 02748b9bc..55014a423 100644 --- a/python/python/lancedb/remote/table.py +++ b/python/python/lancedb/remote/table.py @@ -67,7 +67,15 @@ from ..query import ( LanceTakeQueryBuilder, LanceVectorQueryBuilder, ) -from ..table import AsyncTable, BlobMode, Branches, IndexStatistics, Query, Table, Tags +from ..table import ( + AsyncTable, + BlobMode, + Branches, + IndexStatistics, + Query, + Table, + Tags, +) from ..types import BaseTokenizerType diff --git a/python/python/lancedb/table.py b/python/python/lancedb/table.py index 535ed7c0d..36c5b727d 100644 --- a/python/python/lancedb/table.py +++ b/python/python/lancedb/table.py @@ -2165,9 +2165,11 @@ class Table(ABC): Function columns are supported only on LanceDB Cloud and Enterprise. computed: Dict[str, str], optional - A map of column name to a SQL expression defining the column. The - column's type and inputs are derived from the expression, so no - data type is supplied. + A mapping from output column names to SQL expressions derives each + output field from its expression. A direct projection of a Blob v2 + field inherits Blob v2 semantics; other expressions derive their + ordinary Arrow type. Mapping order is declaration and dependency + order. Unlike ``transforms``, the expression is stored rather than evaluated now: the column is committed with no values, and rows get @@ -6268,8 +6270,11 @@ class AsyncTable: Function columns are supported only on LanceDB Cloud and Enterprise. computed: Dict[str, str], optional - A map of column name to a SQL expression defining the column. The - column's type and inputs are derived from the expression. + A mapping from output column names to SQL expressions derives each + output field from its expression. A direct projection of a Blob v2 + field inherits Blob v2 semantics; other expressions derive their + ordinary Arrow type. Mapping order is declaration and dependency + order. Unlike ``transforms``, the expression is stored rather than evaluated now: the column is committed with no values, and rows get diff --git a/python/python/tests/test_table.py b/python/python/tests/test_table.py index 0be9e139d..fbdfac5d8 100644 --- a/python/python/tests/test_table.py +++ b/python/python/tests/test_table.py @@ -4087,6 +4087,29 @@ def test_computed_column_rejects_transforms_and_computed_together(tmp_path): table.add_columns({"a": "x + 1"}, computed={"b": "x * 2"}) +def test_computed_column_blob_projection_inherits_semantics(tmp_path): + schema = pa.schema([pa.field("id", pa.int64()), lancedb.blob("image")]) + db = lancedb.connect(tmp_path) + table = db.create_table("computed_column_blob", schema=schema) + table.add( + [ + {"id": 1, "image": b"hello"}, + {"id": 2, "image": b""}, + {"id": 3, "image": None}, + ] + ) + + table.add_columns(computed={"image_copy": "image", "second_copy": "image_copy"}) + assert table.refresh_column("image_copy").rows_filled == 2 + assert table.refresh_column("second_copy").rows_filled == 2 + assert table.blob_columns() == ["image", "image_copy", "second_copy"] + + hits = table.search().with_row_id(True).limit(10).to_arrow() + rows = sorted(zip(hits["id"].to_pylist(), hits["_rowid"].to_pylist())) + copied = table.fetch_blobs("second_copy", [row_id for _, row_id in rows]) + assert copied.to_pylist() == [b"hello", b"", None] + + @pytest.mark.asyncio async def test_computed_column_async(tmp_path): db = await lancedb.connect_async(tmp_path) diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index 1afc2615a..57d2dc47d 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -3180,8 +3180,8 @@ impl BaseTable for RemoteTable { self.schema().await?.as_ref(), "schema evolution", )?; - // The server plans the declaration: expression validation, type - // inference and the persisted binding all happen there. + // The server plans the declaration against its table schema, including + // Blob v2 semantics inherited by a direct field projection. let entries = columns .iter() .map( @@ -7388,8 +7388,8 @@ mod tests { assert_eq!(result.version, if old_server { 0 } else { 43 }); } - /// A declaration is sent as `{name, computed}` entries for the server to - /// plan; the client never types the expression itself. + /// A declaration is sent as `{name, computed}` for the server to plan; the + /// client never types the expression itself. #[tokio::test] async fn test_add_computed_columns_sends_the_expression() { let table = Table::new_with_handler("my_table", |request| match request.url().path() { diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index 8436657ca..70b86f3cf 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -750,8 +750,8 @@ pub trait BaseTable: std::fmt::Display + std::fmt::Debug + Send + Sync { /// Declare computed columns, each defined by a SQL expression. /// /// Where the declaration is planned depends on the backend: a local table - /// validates and types the expression itself, a remote one sends the text - /// for the server to plan. + /// validates and types the expression itself, while a remote one sends the + /// expression for the server to plan. async fn add_computed_columns( &self, _columns: &[(String, String)], diff --git a/rust/lancedb/src/table/computed_columns.rs b/rust/lancedb/src/table/computed_columns.rs index 0f89ca612..4e8d8211e 100644 --- a/rust/lancedb/src/table/computed_columns.rs +++ b/rust/lancedb/src/table/computed_columns.rs @@ -9,29 +9,35 @@ //! refresh fills the rows. //! //! The rule is tagged by kind ([`ComputedColumnKind`]) because kinds differ in -//! where the column's type and inputs come from. A SQL expression is -//! self-describing -- both are derived from the expression, so a caller writes -//! neither -- while a kind resolved through a registry cannot be typed without -//! consulting it. Registered Functions use an exact remote version plus a -//! schema-level Function binding; unknown newer kinds remain readable and fail -//! closed before mutation. +//! where the column's type and inputs come from. A SQL expression determines +//! its inputs and physical result type. A direct projection of a Blob v2 field +//! also inherits that field's semantic type while execution continues to use +//! `LargeBinary`. A kind resolved through a registry cannot be typed without +//! consulting it. +//! Registered Functions use an exact remote version plus a schema-level +//! Function binding; unknown newer kinds remain readable and fail closed +//! before mutation. //! //! [`computed_columns`] and [`computed_column_from_field`] read declarations //! back off a schema. -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::sync::Arc; use arrow_schema::{DataType, Field as ArrowField, Fields, Schema as ArrowSchema, SchemaRef}; -use datafusion_common::tree_node::TreeNode; +use datafusion_common::{ScalarValue, tree_node::TreeNode}; +use datafusion_expr::Expr; use datafusion_physical_plan::PhysicalExpr; use lance::dataset::NewColumnTransform; +use lance_arrow::FieldExt; +use lance_core::datatypes::{BLOB_V2_DESC_FIELD, format_field_path_minimal, parse_field_path}; use lance_datafusion::planner::Planner; use lance_namespace::models::{JsonArrowDataType, JsonArrowField, JsonArrowSchema}; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::function::{FunctionApplication, FunctionBinding}; +use crate::utils::resolve_arrow_field_path; use crate::{Error, Result}; /// Field metadata key marking a column as computed. The value is `"true"`. @@ -1106,15 +1112,20 @@ pub(crate) fn ensure_no_foreign_declarations<'a>( fields: impl IntoIterator>, ) -> Result<()> { for field in fields { - if field.metadata().keys().any(|k| is_declaration_key(k)) { - return Err(Error::InvalidInput { - message: format!( - "field '{}' carries computed-column metadata; declare computed columns \ - with add_columns().computed()", - field.name() - ), - }); - } + ensure_no_foreign_declaration(field)?; + } + Ok(()) +} + +fn ensure_no_foreign_declaration(field: &ArrowField) -> Result<()> { + if field.metadata().keys().any(|k| is_declaration_key(k)) { + return Err(Error::InvalidInput { + message: format!( + "field '{}' carries computed-column metadata; declare computed columns \ + with add_columns().computed()", + field.name() + ), + }); } Ok(()) } @@ -1162,15 +1173,154 @@ pub(crate) struct BoundExpression { /// The columns the expression names, as written; nested inputs keep /// their dotted path. pub inputs: Vec, - /// The top-level columns evaluation reads, in [`Self::read_schema`] - /// order. A nested input appears through its root. + /// The top-level columns evaluation reads, in physical-expression order. + /// A nested input appears through its root. pub roots: Vec, - /// The projected schema evaluation runs against. - pub read_schema: SchemaRef, /// The compiled expression. pub physical: Arc, /// The type the expression yields. pub data_type: DataType, + /// Blob v2 leaves the scan must materialize as `LargeBinary`. + pub blob_paths: Vec, + /// A directly projected Blob v2 field whose semantics the output inherits. + projected_blob_field: Option, +} + +fn is_direct_field_projection(expr: &Expr) -> bool { + match expr { + Expr::Column(_) => true, + Expr::ScalarFunction(function) + if function.name() == "get_field" && function.args.len() == 2 => + { + is_direct_field_projection(&function.args[0]) + && matches!( + &function.args[1], + Expr::Literal(ScalarValue::Utf8(Some(_)), _) + ) + } + _ => false, + } +} + +fn projected_blob_field(schema: &ArrowSchema, expr: &Expr) -> Result> { + if !is_direct_field_projection(expr) { + return Ok(None); + } + let paths = Planner::column_names_in_expr(expr); + let [path] = paths.as_slice() else { + return Ok(None); + }; + let (_, field) = resolve_arrow_field_path(schema, path)?; + Ok(field.is_blob_v2().then_some(field)) +} + +fn collect_blob_paths(field: &ArrowField, parent: &[String], paths: &mut Vec>) { + let mut path = parent.to_vec(); + path.push(field.name().clone()); + if field.is_blob_v2() { + paths.push(path); + return; + } + match field.data_type() { + DataType::Struct(children) => { + for child in children { + collect_blob_paths(child, &path, paths); + } + } + DataType::List(child) + | DataType::LargeList(child) + | DataType::FixedSizeList(child, _) + | DataType::Map(child, _) => collect_blob_paths(child, &path, paths), + _ => {} + } +} + +fn schema_blob_paths(schema: &ArrowSchema) -> Vec> { + let mut paths = Vec::new(); + for field in schema.fields() { + collect_blob_paths(field, &[], &mut paths); + } + paths +} + +fn transform_blob_field( + field: &ArrowField, + parent: &[String], + materialized: &HashSet>, +) -> ArrowField { + let mut path = parent.to_vec(); + path.push(field.name().clone()); + if field.is_blob_v2() { + if materialized.contains(&path) { + return ArrowField::new(field.name(), DataType::LargeBinary, field.is_nullable()); + } + return ArrowField::new( + field.name(), + BLOB_V2_DESC_FIELD.data_type().clone(), + field.is_nullable(), + ) + .with_metadata(BLOB_V2_DESC_FIELD.metadata().clone()); + } + + let data_type = match field.data_type() { + DataType::Struct(children) => DataType::Struct( + children + .iter() + .map(|child| Arc::new(transform_blob_field(child, &path, materialized))) + .collect(), + ), + DataType::List(child) => { + DataType::List(Arc::new(transform_blob_field(child, &path, materialized))) + } + DataType::LargeList(child) => { + DataType::LargeList(Arc::new(transform_blob_field(child, &path, materialized))) + } + DataType::FixedSizeList(child, size) => DataType::FixedSizeList( + Arc::new(transform_blob_field(child, &path, materialized)), + *size, + ), + DataType::Map(child, sorted) => DataType::Map( + Arc::new(transform_blob_field(child, &path, materialized)), + *sorted, + ), + _ => return field.clone(), + }; + ArrowField::new(field.name(), data_type, field.is_nullable()) + .with_metadata(field.metadata().clone()) +} + +fn blob_runtime_schema(schema: &ArrowSchema, materialized: &HashSet>) -> SchemaRef { + Arc::new(ArrowSchema::new_with_metadata( + schema + .fields() + .iter() + .map(|field| Arc::new(transform_blob_field(field, &[], materialized))) + .collect::(), + schema.metadata().clone(), + )) +} + +fn referenced_blob_paths(schema: &ArrowSchema, inputs: &[String]) -> Result>> { + let input_paths = inputs + .iter() + .map(|input| { + parse_field_path(input).map_err(|error| Error::InvalidInput { + message: format!("invalid computed-column input path '{input}': {error}"), + }) + }) + .collect::>>()?; + Ok(schema_blob_paths(schema) + .into_iter() + .filter(|blob_path| { + input_paths.iter().any(|input_path| { + input_path.len() <= blob_path.len() + && input_path + .iter() + .zip(blob_path) + .all(|(input, blob)| input == blob) + }) + }) + .collect()) } /// Parse, resolve and compile `expression` against `schema`. @@ -1185,10 +1335,18 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< message, }; - let planner = Planner::new(schema.clone()); + // Blob v2 is a semantic type whose runtime expression ABI is + // `LargeBinary`. Parse against that ABI first so a direct Blob reference + // is not mistaken for its storage descriptor struct. + let all_blob_paths = schema_blob_paths(schema.as_ref()) + .into_iter() + .collect::>(); + let parsing_schema = blob_runtime_schema(schema.as_ref(), &all_blob_paths); + let planner = Planner::new(parsing_schema); let parsed = planner .parse_expr(expression) .map_err(|e| invalid(e.to_string()))?; + let projected_blob_field = projected_blob_field(schema.as_ref(), &parsed)?; // A declaration is evaluated more than once -- staging and writing are // separate passes, and a refresh years later replays the same text -- so @@ -1218,13 +1376,19 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< inputs.sort(); inputs.dedup(); + let blob_paths = referenced_blob_paths(schema.as_ref(), &inputs)?; + let runtime_schema = blob_runtime_schema( + schema.as_ref(), + &blob_paths.iter().cloned().collect::>(), + ); + // A nested input is recorded by its path but read through its root // column; Schema::index_of resolves top-level names only. Resolved here // rather than left to the planner so an unknown column names itself in // the error instead of surfacing as a plan failure. let mut indices = Vec::with_capacity(inputs.len()); for input in &inputs { - let index = schema + let index = runtime_schema .index_of(root(input)) .map_err(|_| invalid(format!("unknown column '{input}'")))?; if !indices.contains(&index) { @@ -1237,7 +1401,7 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< // compiles the expression has to be built on the projected schema // evaluation will actually read. let read_schema = Arc::new( - schema + runtime_schema .project(&indices) .map_err(|e| invalid(e.to_string()))?, ); @@ -1247,7 +1411,8 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< .map(|field| field.name().clone()) .collect(); - let optimized = planner + let runtime_planner = Planner::new(runtime_schema); + let optimized = runtime_planner .optimize_expr(parsed) .map_err(|e| invalid(e.to_string()))?; let physical = Planner::new(read_schema.clone()) @@ -1260,9 +1425,16 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< Ok(BoundExpression { inputs, roots, - read_schema, physical, data_type, + blob_paths: blob_paths + .iter() + .map(|path| { + let segments = path.iter().map(String::as_str).collect::>(); + format_field_path_minimal(&segments) + }) + .collect(), + projected_blob_field, }) } @@ -1278,7 +1450,7 @@ pub(crate) fn bind(schema: SchemaRef, column: &str, expression: &str) -> Result< /// batch may declare `a` and then `b = a + 1` in one commit. Refresh order /// then matters, and refresh enforces it: `b` is refused while `a` still has /// unfilled rows. -pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result> { +fn plan_declarations(schema: SchemaRef, columns: &[(String, String)]) -> Result> { if columns.is_empty() { return Err(Error::InvalidInput { message: "at least one computed column is required".into(), @@ -1290,15 +1462,28 @@ pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result { + let mut metadata = source.metadata().clone(); + metadata.retain(|key, _| !is_declaration_key(key)); + metadata.extend(computed_metadata); + source + .with_name(name) + .with_nullable(true) + .with_metadata(metadata) + } + None => ArrowField::new(name, bound.data_type, true).with_metadata(computed_metadata), + }; schema = Arc::new(ArrowSchema::new_with_metadata( schema .fields() @@ -1314,6 +1499,10 @@ pub(crate) fn plan(schema: SchemaRef, columns: &[(String, String)]) -> Result Result> { + plan_declarations(schema, columns) +} + /// Run the schema-level checks of /// [`AddColumnsBuilder::computed`](super::AddColumnsBuilder::computed) against /// `schema` without committing: the Function-binding guard and the planning of @@ -1352,7 +1541,7 @@ pub(crate) fn declare( schema: SchemaRef, columns: &[(String, String)], ) -> Result { - let fields = plan(schema, columns)?; + let fields = plan_declarations(schema, columns)?; Ok(NewColumnTransform::AllNulls(Arc::new(ArrowSchema::new( fields, )))) @@ -1478,6 +1667,44 @@ mod tests { ); } + #[test] + fn test_direct_blob_projection_inherits_semantics() { + let schema = Arc::new(ArrowSchema::new(vec![crate::blob("image", false)])); + let fields = plan( + schema, + &[ + ("first".to_string(), "image".to_string()), + ("second".to_string(), "first".to_string()), + ], + ) + .unwrap(); + + for field in &fields { + assert!(field.is_blob_v2()); + assert!(field.is_nullable()); + } + assert_eq!( + fields[1] + .metadata() + .get(EXPRESSION_META_KEY) + .map(String::as_str), + Some("first") + ); + } + + #[test] + fn test_blob_expression_transformation_does_not_inherit_semantics() { + let schema = Arc::new(ArrowSchema::new(vec![crate::blob("image", true)])); + let fields = plan( + schema, + &[("payload".to_string(), "coalesce(image, image)".to_string())], + ) + .unwrap(); + + assert!(!fields[0].is_blob_v2()); + assert_eq!(fields[0].data_type(), &DataType::LargeBinary); + } + /// The binding reaches the schema only if `AllNulls` carries per-field /// metadata through the commit. The whole representation rests on it. #[tokio::test] diff --git a/rust/lancedb/src/table/refresh.rs b/rust/lancedb/src/table/refresh.rs index bc2cc38d1..511fce8ff 100644 --- a/rust/lancedb/src/table/refresh.rs +++ b/rust/lancedb/src/table/refresh.rs @@ -29,10 +29,14 @@ //! inputs masked to null first, so a poison value in a row nobody is filling //! cannot fail the refresh. +use std::collections::HashSet; use std::sync::Arc; -use arrow_array::{ArrayRef, BooleanArray, RecordBatch, RecordBatchOptions}; -use arrow_schema::Schema as ArrowSchema; +use arrow_array::{ + Array, ArrayRef, BooleanArray, LargeBinaryArray, RecordBatch, RecordBatchOptions, StructArray, + new_null_array, +}; +use arrow_schema::{DataType, Field as ArrowField, Schema as ArrowSchema}; use datafusion_expr::ColumnarValue; use futures::{Stream, StreamExt, TryStreamExt}; use lance::Dataset; @@ -40,7 +44,7 @@ use lance::dataset::WriteDestination; use lance::dataset::fragment::FileFragment; use lance::dataset::transaction::Operation; use lance_core::ROW_ID; -use lance_core::datatypes::Schema as LanceSchema; +use lance_core::datatypes::{BlobHandling, Schema as LanceSchema}; use serde::{Deserialize, Serialize}; use super::computed_columns::{BoundExpression, ComputedColumnKind, computed_column_from_field}; @@ -104,6 +108,7 @@ async fn execute_refresh_column_with_source( fields: vec![field.clone()], metadata: Default::default(), }; + let output_is_blob = field.is_blob_v2(); let mut rows_filled = 0u64; let mut replacements = Vec::new(); @@ -113,7 +118,8 @@ async fn execute_refresh_column_with_source( continue; } rows_filled += gained; - let values = fill_stream(&dataset, &fragment, bound.clone(), column).await?; + let values = + fill_stream(&dataset, &fragment, bound.clone(), column, output_is_blob).await?; replacements.push(fragment.write_columns(values, &column_schema).await?); } @@ -294,12 +300,15 @@ fn evaluation_batch( mask_out: Option<&BooleanArray>, ) -> lance_core::Result { let mut columns = Vec::with_capacity(bound.roots.len()); + let mut fields = Vec::with_capacity(bound.roots.len()); for name in &bound.roots { - let column = batch.column_by_name(name).ok_or_else(|| { + let index = batch.schema_ref().index_of(name).map_err(|_| { lance_core::Error::invalid_input(format!( "refreshing a computed column read no {name} column" )) })?; + let column = batch.column(index); + fields.push(batch.schema_ref().field(index).clone()); // Rows outside the mask must not reach the expression: a value in a // deleted or already-filled row can be one it would choke on. columns.push(match mask_out { @@ -308,7 +317,7 @@ fn evaluation_batch( }); } Ok(RecordBatch::try_new_with_options( - bound.read_schema.clone(), + Arc::new(ArrowSchema::new(fields)), columns, &RecordBatchOptions::new().with_row_count(Some(batch.num_rows())), )?) @@ -329,6 +338,99 @@ fn evaluate(bound: &BoundExpression, batch: &RecordBatch) -> lance_core::Result< } } +fn materialized_blob_ids(schema: &LanceSchema, paths: &[String]) -> Result> { + paths + .iter() + .map(|path| { + let field = schema + .resolve(path) + .and_then(|fields| fields.last().copied()) + .ok_or_else(|| Error::InvalidInput { + message: format!("computed Blob input '{path}' no longer exists"), + })?; + if !field.is_blob_v2() { + return Err(Error::InvalidInput { + message: format!("computed Blob input '{path}' is no longer Blob v2"), + }); + } + u32::try_from(field.id).map_err(|_| Error::InvalidInput { + message: format!( + "computed Blob input '{path}' has invalid field id {}", + field.id + ), + }) + }) + .collect() +} + +fn configure_blob_inputs( + scanner: &mut lance::dataset::scanner::Scanner, + schema: &LanceSchema, + bound: &BoundExpression, + extra_blob_id: Option, +) -> Result<()> { + let mut ids = materialized_blob_ids(schema, &bound.blob_paths)?; + ids.extend(extra_blob_id); + scanner.blob_handling(BlobHandling::SomeBlobsBinary(ids)); + Ok(()) +} + +fn blob_array_from_binary( + array: &ArrayRef, + target_field: &ArrowField, +) -> lance_core::Result { + let values = array + .as_any() + .downcast_ref::() + .ok_or_else(|| { + lance_core::Error::invalid_input(format!( + "a Blob v2 computed output produced {}, expected LargeBinary", + array.data_type() + )) + })?; + let mut builder = lance::blob::BlobArrayBuilder::new(values.len()); + for index in 0..values.len() { + if values.is_null(index) { + builder.push_null()?; + } else { + builder.push_bytes(values.value(index))?; + } + } + let minimal = builder.finish()?; + let minimal = minimal + .as_any() + .downcast_ref::() + .ok_or_else(|| lance_core::Error::internal("Blob builder returned a non-struct array"))?; + let DataType::Struct(target_fields) = target_field.data_type() else { + return Err(lance_core::Error::invalid_input(format!( + "Blob v2 output field '{}' has non-struct type {}", + target_field.name(), + target_field.data_type() + ))); + }; + let columns = target_fields + .iter() + .map(|field| match field.name().as_str() { + "data" | "uri" => minimal + .column_by_name(field.name()) + .cloned() + .ok_or_else(|| { + lance_core::Error::internal(format!("Blob builder omitted '{}'", field.name())) + }), + "position" | "size" => Ok(new_null_array(field.data_type(), minimal.len())), + name => Err(lance_core::Error::invalid_input(format!( + "Blob v2 output field '{}' has unsupported logical child '{name}'", + target_field.name() + ))), + }) + .collect::>>()?; + Ok(Arc::new(StructArray::try_new( + target_fields.clone(), + columns, + minimal.nulls().cloned(), + )?)) +} + /// How many rows of one fragment would gain a value. /// /// Scans only the unfilled live rows -- deleted rows never reach the @@ -347,6 +449,7 @@ async fn count_fragment_gains( .with_row_id() .filter(&format!("{} IS NULL", quote_identifier(column)))? .project(&bound.roots)?; + configure_blob_inputs(&mut scanner, dataset.schema(), bound, None)?; let mut gained = 0u64; let mut batches = scanner.try_into_stream().await?; @@ -368,6 +471,7 @@ async fn fill_stream( fragment: &FileFragment, bound: Arc, column: &str, + output_is_blob: bool, ) -> Result> + Send + use<>> { let mut projection: Vec = bound.roots.clone(); projection.push(column.to_string()); @@ -377,6 +481,20 @@ async fn fill_stream( .with_row_id() .include_deleted_rows() .project(&projection)?; + let output_blob_id = output_is_blob + .then(|| { + dataset + .schema() + .field(column) + .and_then(|field| u32::try_from(field.id).ok()) + }) + .flatten(); + configure_blob_inputs( + &mut scanner, + dataset.schema(), + bound.as_ref(), + output_blob_id, + )?; let projected = Arc::new(ArrowSchema::new(vec![ ArrowSchema::from(dataset.schema()) @@ -412,6 +530,11 @@ async fn fill_stream( let computed = evaluate(&bound, &evaluation_batch(&batch, &bound, Some(&keep))?)?; let merged = arrow_select::zip::zip(&fill, &computed, existing)?; + let merged = if output_is_blob { + blob_array_from_binary(&merged, projected.field(0))? + } else { + merged + }; Ok(RecordBatch::try_new(projected.clone(), vec![merged])?) })) } @@ -420,8 +543,12 @@ async fn fill_stream( mod tests { use std::sync::Arc; - use arrow_array::{Int32Array, record_batch}; + use arrow_array::{ + Array, ArrayRef, Int32Array, LargeBinaryArray, RecordBatch, StructArray, record_batch, + }; + use arrow_schema::Field as ArrowField; use futures::TryStreamExt; + use lance_core::ROW_ID; use crate::connect; use crate::query::{ExecutableQuery, QueryBase, Select}; @@ -477,6 +604,25 @@ mod tests { table.add(batch).execute().await.unwrap(); } + #[test] + fn test_blob_output_matches_complete_logical_field() { + let values: ArrayRef = Arc::new(LargeBinaryArray::from(vec![ + Some(b"hello".as_slice()), + None, + ])); + let field = ArrowField::new( + "image", + lance_core::datatypes::BLOB_V2_LOGICAL_TYPE.clone(), + true, + ); + + let output = super::blob_array_from_binary(&values, &field).unwrap(); + assert_eq!(output.data_type(), field.data_type()); + let output = output.as_any().downcast_ref::().unwrap(); + assert_eq!(output.column_by_name("position").unwrap().null_count(), 2); + assert_eq!(output.column_by_name("size").unwrap().null_count(), 2); + } + /// The gate's reproducer: `b = coalesce(a, 0)` refreshed before `a` /// must not bake zeros from `a`'s placeholder null. It is refused, and /// names the input, until `a` is filled -- after every append too. @@ -1164,4 +1310,366 @@ mod tests { let err = table.refresh_column("embedding").await.unwrap_err(); assert!(matches!(err, Error::NotSupported { message } if message.contains("udf"))); } + + fn blob_batch(ids: Vec, payloads: Vec>) -> RecordBatch { + use arrow_array::Int32Array; + use arrow_schema::{Field, Schema}; + + let mut builder = lance::blob::BlobArrayBuilder::new(payloads.len()); + for payload in payloads { + match payload { + Some(payload) => builder.push_bytes(payload).unwrap(), + None => builder.push_null().unwrap(), + } + } + RecordBatch::try_new( + Arc::new(Schema::new(vec![ + Field::new("id", arrow_schema::DataType::Int32, false), + crate::blob("image", true), + ])), + vec![Arc::new(Int32Array::from(ids)), builder.finish().unwrap()], + ) + .unwrap() + } + + async fn create_blob_table(path: &std::path::Path, batch: RecordBatch) -> Table { + let conn = connect(path.to_str().unwrap()).execute().await.unwrap(); + conn.create_table("blobs", batch).execute().await.unwrap() + } + + #[tokio::test] + async fn test_refresh_inherits_and_publishes_blob_output() { + use arrow_array::UInt64Array; + use lance_arrow::{ + BLOB_DEDICATED_SIZE_THRESHOLD_META_KEY, BLOB_INLINE_SIZE_THRESHOLD_META_KEY, + }; + use lance_core::datatypes::BlobKind; + + use crate::table::schema_evolution::FieldMetadataUpdate; + + let tmp = tempfile::tempdir().unwrap(); + let table = create_blob_table( + tmp.path(), + blob_batch( + vec![1, 2, 3, 4], + vec![Some(b"hello"), Some(b"ab"), Some(b""), None], + ), + ) + .await; + table + .add_columns() + .computed("image_copy", "image") + .execute() + .await + .unwrap(); + table + .update_field_metadata(&[FieldMetadataUpdate::new("image_copy") + .set(BLOB_INLINE_SIZE_THRESHOLD_META_KEY, "1") + .set(BLOB_DEDICATED_SIZE_THRESHOLD_META_KEY, "4")]) + .await + .unwrap(); + + let first_refresh = table.refresh_column("image_copy").await.unwrap(); + assert_eq!(first_refresh.rows_filled, 3); + assert_eq!( + table.blob_columns().await.unwrap(), + vec!["image".to_string(), "image_copy".to_string()] + ); + + let batches = table + .query() + .with_row_id() + .execute() + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + let batch = arrow_select::concat::concat_batches(&batches[0].schema(), &batches).unwrap(); + assert!( + batch + .column_by_name("image_copy") + .unwrap() + .as_any() + .is::() + ); + let row_ids = batch + .column_by_name(ROW_ID) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .values() + .to_vec(); + let original = table.fetch_blobs("image", &row_ids).await.unwrap(); + let copied = table.fetch_blobs("image_copy", &row_ids).await.unwrap(); + assert_eq!(original, copied); + let ids = batch + .column_by_name("id") + .unwrap() + .as_any() + .downcast_ref::() + .unwrap(); + let files = table + .fetch_blob_files("image_copy", &row_ids) + .await + .unwrap(); + let mut layouts = ids + .values() + .iter() + .copied() + .zip(files) + .map(|(id, file)| (id, file.and_then(|file| file.kind()))) + .collect::>(); + layouts.sort_by_key(|(id, _)| *id); + assert_eq!( + layouts, + vec![ + (1, Some(BlobKind::Dedicated)), + (2, Some(BlobKind::Packed)), + (3, Some(BlobKind::Inline)), + (4, None), + ] + ); + + table + .add(blob_batch(vec![5], vec![Some(b"appended")])) + .execute() + .await + .unwrap(); + table + .optimize(crate::table::OptimizeAction::Compact { + options: crate::table::CompactionOptions::default(), + remap_options: None, + }) + .await + .unwrap(); + assert_eq!( + table + .refresh_column("image_copy") + .await + .unwrap() + .rows_filled, + 1 + ); + assert_eq!( + table + .refresh_column("image_copy") + .await + .unwrap() + .rows_filled, + 0 + ); + + table.checkout(first_refresh.version).await.unwrap(); + assert_eq!(table.count_rows(None).await.unwrap(), 4); + assert_eq!( + table.blob_columns().await.unwrap(), + vec!["image".to_string(), "image_copy".to_string()] + ); + table.checkout_latest().await.unwrap(); + } + + #[tokio::test] + async fn test_refresh_inherits_nested_struct_blob_input() { + use arrow_array::{Int32Array, StructArray, UInt64Array}; + use arrow_schema::{DataType, Field, Fields, Schema}; + + let tmp = tempfile::tempdir().unwrap(); + let mut blob_builder = lance::blob::BlobArrayBuilder::new(2); + blob_builder.push_bytes(b"nested").unwrap(); + blob_builder.push_null().unwrap(); + let blob_field = crate::blob("image", true); + let metadata_fields = Fields::from(vec![blob_field.clone()]); + let metadata = StructArray::new( + metadata_fields.clone(), + vec![blob_builder.finish().unwrap()], + None, + ); + let batch = RecordBatch::try_new( + Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("metadata", DataType::Struct(metadata_fields), true), + ])), + vec![Arc::new(Int32Array::from(vec![1, 2])), Arc::new(metadata)], + ) + .unwrap(); + let table = create_blob_table(tmp.path(), batch).await; + table + .add_columns() + .computed("payload_copy", "metadata.image") + .execute() + .await + .unwrap(); + + assert_eq!( + table + .refresh_column("payload_copy") + .await + .unwrap() + .rows_filled, + 1 + ); + assert_eq!( + table.blob_columns().await.unwrap(), + vec!["metadata.image".to_string(), "payload_copy".to_string()] + ); + let batches = table + .query() + .with_row_id() + .execute() + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + let row_ids = batches[0] + .column_by_name(ROW_ID) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .values(); + let payloads = table.fetch_blobs("payload_copy", row_ids).await.unwrap(); + assert_eq!(payloads.value(0), b"nested"); + assert!(payloads.is_null(1)); + } + + #[tokio::test] + async fn test_refresh_preserves_list_shape_when_materializing_blob_input() { + use arrow_array::{Int32Array, ListArray}; + use arrow_buffer::{OffsetBuffer, ScalarBuffer}; + use arrow_schema::{DataType, Field, Schema}; + + let tmp = tempfile::tempdir().unwrap(); + let mut blob_builder = lance::blob::BlobArrayBuilder::new(3); + blob_builder.push_bytes(b"a").unwrap(); + blob_builder.push_bytes(b"bb").unwrap(); + blob_builder.push_null().unwrap(); + let item = Arc::new(crate::blob("item", true)); + let images = ListArray::new( + item.clone(), + OffsetBuffer::new(ScalarBuffer::from(vec![0, 2, 3])), + blob_builder.finish().unwrap(), + None, + ); + let batch = RecordBatch::try_new( + Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("images", DataType::List(item), true), + ])), + vec![Arc::new(Int32Array::from(vec![1, 2])), Arc::new(images)], + ) + .unwrap(); + let table = create_blob_table(tmp.path(), batch).await; + table + .add_columns() + .computed("image_payloads", "images") + .execute() + .await + .unwrap(); + + assert_eq!( + table + .refresh_column("image_payloads") + .await + .unwrap() + .rows_filled, + 2 + ); + let batches = table + .query() + .select(Select::columns(&["image_payloads"])) + .execute() + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + let output = batches[0] + .column_by_name("image_payloads") + .unwrap() + .as_any() + .downcast_ref::() + .unwrap(); + assert_eq!(output.value_offsets(), &[0, 2, 3]); + assert!(output.values().as_any().is::()); + } + + #[tokio::test] + async fn test_refresh_inherits_external_blob_input() { + use arrow_array::{Int32Array, StringArray, UInt64Array}; + use arrow_schema::{DataType, Field, Schema}; + + let tmp = tempfile::tempdir().unwrap(); + let payload = b"external-payload"; + let path = tmp.path().join("payload.bin"); + std::fs::write(&path, payload).unwrap(); + let uri = url::Url::from_file_path(path).unwrap().to_string(); + let conn = connect(tmp.path().join("db").to_str().unwrap()) + .execute() + .await + .unwrap(); + let table = conn + .create_empty_table( + "external", + Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + crate::blob("image", true), + ])), + ) + .execute() + .await + .unwrap(); + let batch = RecordBatch::try_new( + Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("image", DataType::Utf8, true), + ])), + vec![ + Arc::new(Int32Array::from(vec![1])), + Arc::new(StringArray::from(vec![Some(uri)])), + ], + ) + .unwrap(); + table + .add(batch) + .allow_external_blob_outside_bases(true) + .execute() + .await + .unwrap(); + table + .add_columns() + .computed("payload_copy", "image") + .execute() + .await + .unwrap(); + + assert_eq!( + table + .refresh_column("payload_copy") + .await + .unwrap() + .rows_filled, + 1 + ); + let batches = table + .query() + .with_row_id() + .execute() + .await + .unwrap() + .try_collect::>() + .await + .unwrap(); + let row_ids = batches[0] + .column_by_name(ROW_ID) + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .values(); + let payloads = table.fetch_blobs("payload_copy", row_ids).await.unwrap(); + assert_eq!(payloads.value(0), payload); + } } From a87cada90e0b4a7c7f6bfc5b82ec95f0f57765d2 Mon Sep 17 00:00:00 2001 From: Will Jones Date: Fri, 28 Aug 2026 09:47:47 -0700 Subject: [PATCH 15/32] feat(node)!: require Node >= 22 and drop npm lockfiles (#4074) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bindings are built, installed and published with pnpm everywhere, but a parallel npm dependency graph was still being maintained beside it. This removes it, raises the supported Node floor to the versions we actually test, and gives Dependabot the npm coverage it was missing. ## Dropping npm `nodejs/package-lock.json` was regenerated by `ci/update_lockfiles.sh` on every release commit and read by nothing — no workflow runs `npm ci` or `npm install` in `nodejs/`, and npm never publishes a lockfile in a package tarball. It could not even agree with the real install, since npm does not see pnpm's `overrides`. Because GitHub's dependency graph parses `package-lock.json`, it was also reporting vulnerabilities for a tree we neither install nor ship. `docs/package.json`, `docs/package-lock.json` and `docs/tsconfig.json` go too. They depend on `file:../node` and `file:../node/node_modules/apache-arrow` — the `node/` directory was removed long ago — the tsconfig compiles `src/*.ts` where no TypeScript files exist, and nothing installs any of it. `docs.yml` only referenced the lockfile to configure an npm cache for an install it never ran. Two `workflow_dispatch` workflows for regenerating those lockfiles are removed as well. Both were already broken: they `uses:` composite actions at `.github/workflows/update_package_lock{,_nodejs}` that do not exist, so dispatching either failed immediately. The remaining `npx` calls become direct `node_modules/.bin/...` invocations. These were already running locally installed binaries rather than resolving anything, but naming the binary removes the npm CLI from the loop and does not depend on which Node version is active. `dev.yml`'s commitlint check was the last place doing real npm dependency resolution — an unpinned `npm install @commitlint/config-conventional` that also bypassed the `minimumReleaseAge` hold configured for `nodejs/` — and is now a pinned `pnpm dlx`. ## Node support Node 18 and 20 both reached end-of-life, in April 2025 and April 2026. The matrix moves to 22, 24 and 26, and `engines` rises from `>= 18` to `>= 22` so the declared floor is one the matrix actually covers. Node 22 is LTS until April 2027; 24 is LTS; 26 is Current and becomes LTS in October 2026. This also removes the reason the workflows reached for `npx` in the first place: pnpm 11 requires Node >= 22.13, which every matrix version now satisfies. The prebuilt-binary smoke test in `npm-publish.yml` moves from Node 20 to Node 22 — the floor, where a napi ABI problem would surface first — rather than fanning out across all three, to keep the publish matrix from tripling. ## Dependabot There were no npm-ecosystem entries at all, which is why the advisories behind #4073 went unnoticed. Both pnpm lockfiles are now watched — `nodejs/` and `nodejs/examples/`, which is a separate install — using the same `lockfile-only` strategy as the existing cargo and pip entries, so version ranges in `package.json` are left alone. ## Pre-commit biome The hook ran `npx @biomejs/biome@1.8.3` while `nodejs/package.json` resolved 1.9.4. The two disagree about formatting, so the hook rejected code that `pnpm lint` accepts, and failed on unmodified `main` for anyone touching `nodejs/`. It now uses the pnpm-managed biome, which fixes the drift with no source changes. ## Testing `dev.yml`'s commitlint job does not check out the repo, so it runs in an empty workspace, and I could not verify `pnpm/action-setup` there locally. It triggers on `pull_request_target`, so this PR exercises it directly — worth confirming green before merge. I did verify the `pnpm dlx` invocation itself locally: it accepts a conventional title and rejects a non-conventional one with exit 1. Node 26 is new enough that the examples job may surface gaps in prebuilt native binaries (`onnxruntime-node`, `sharp`) before their maintainers publish for it. ## Not included `nodejs/examples/` still pins `sharp: "0.33.5"` and has its own audit findings. Raising the Node floor unblocks that work — sharp 0.35 requires Node >= 20.9, which the matrix now satisfies — but it is a dependency bump rather than tooling cleanup, so it is left separate. ## Breaking changes `@lancedb/lancedb` now requires Node >= 22; previously >= 18. The `@types/node` peer range moves from `>=18` to `>=22` to match. Users on Node 18 or 20 must upgrade their runtime; both have been end-of-life for some time. Existing installs are unaffected, since `engines` is only checked on install. --------- Co-authored-by: Claude Opus 5 (1M context) --- .github/dependabot.yml | 24 + .github/workflows/dev.yml | 14 +- .github/workflows/docs-link-check.yml | 2 +- .github/workflows/docs.yml | 4 +- .github/workflows/nodejs.yml | 24 +- .github/workflows/npm-publish.yml | 15 +- .github/workflows/update_package_lock_run.yml | 22 - .../update_package_lock_run_nodejs.yml | 22 - .pre-commit-config.yaml | 5 +- AGENTS.md | 6 +- Makefile | 2 +- ci/update_lockfiles.sh | 8 +- docs/README.md | 19 +- docs/package-lock.json | 135 - docs/package.json | 20 - docs/tsconfig.json | 17 - nodejs/__test__/package.test.ts | 4 +- nodejs/__test__/remote.test.ts | 11 +- nodejs/examples/package.json | 3 +- nodejs/package-lock.json | 11106 ---------------- nodejs/package.json | 4 +- 21 files changed, 90 insertions(+), 11377 deletions(-) delete mode 100644 .github/workflows/update_package_lock_run.yml delete mode 100644 .github/workflows/update_package_lock_run_nodejs.yml delete mode 100644 docs/package-lock.json delete mode 100644 docs/package.json delete mode 100644 docs/tsconfig.json delete mode 100644 nodejs/package-lock.json diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eee966f76..d625b0698 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -44,3 +44,27 @@ updates: python-deps: patterns: - "*" + + # The npm ecosystem covers pnpm lockfiles. There are two separate installs: + # the bindings themselves and the examples, which have their own lockfile. + # As with cargo and pip above, only bump the lockfile — the version ranges + # in package.json are our consumers' constraints, not ours. + - package-ecosystem: npm + directory: /nodejs + schedule: + interval: weekly + versioning-strategy: lockfile-only + groups: + nodejs-deps: + patterns: + - "*" + + - package-ecosystem: npm + directory: /nodejs/examples + schedule: + interval: weekly + versioning-strategy: lockfile-only + groups: + nodejs-examples-deps: + patterns: + - "*" diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f77ba5f77..eac4cc4fc 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -29,12 +29,14 @@ jobs: steps: - uses: actions/setup-node@v6 with: - node-version: "18" + node-version: "24" + - uses: pnpm/action-setup@v6 + with: + version: 11.1.1 # These rules are disabled because Github will always ensure there # is a blank line between the title and the body and Github will # word wrap the description field to ensure a reasonable max line # length. - - run: npm install @commitlint/config-conventional - run: > echo 'module.exports = { "rules": { @@ -43,7 +45,11 @@ jobs: "body-leading-blank": [0, "always"] } }' > .commitlintrc.js - - run: npx commitlint --extends @commitlint/config-conventional --verbose <<< $COMMIT_MSG + - run: > + pnpm dlx + --package @commitlint/cli@21.2.2 + --package @commitlint/config-conventional@21.2.2 + commitlint --extends @commitlint/config-conventional --verbose <<< $COMMIT_MSG env: COMMIT_MSG: > ${{ github.event.pull_request.title }} @@ -54,7 +60,7 @@ jobs: with: script: | const message = `**ACTION NEEDED** - + Lance follows the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) for release automation. The PR title and description are used as the merge commit message.\ diff --git a/.github/workflows/docs-link-check.yml b/.github/workflows/docs-link-check.yml index 1286819bc..afa48a14a 100644 --- a/.github/workflows/docs-link-check.yml +++ b/.github/workflows/docs-link-check.yml @@ -56,7 +56,7 @@ jobs: uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0 with: # Restricted to http(s) on purpose. Much of docs/src is generated - # API reference (the js/ tree comes from `npm run docs` in nodejs) + # API reference (the js/ tree comes from `pnpm run docs` in nodejs) # and the hand-written pages use mkdocstrings cross-references and # nav-relative paths that only resolve in the site mkdocs builds, # not in this checkout, so relative links would be reported as diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e787afb7f..d0ec583bf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -55,9 +55,7 @@ jobs: - name: Set up node uses: actions/setup-node@v6 with: - node-version: 20 - cache: 'npm' - cache-dependency-path: docs/package-lock.json + node-version: 24 - name: Install node dependencies working-directory: nodejs run: | diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index e82cf71c9..55c050a7d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -47,9 +47,8 @@ jobs: version: 11.1.1 - uses: actions/setup-node@v6 with: - # pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL - # in October. The library itself still supports Node >= 18 - # (see test matrix below). + # Build on a supported LTS; the matrix job below covers every + # Node version the library claims to support. node-version: 24 cache: 'pnpm' cache-dependency-path: nodejs/pnpm-lock.yaml @@ -84,7 +83,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - node-version: [ "18", "20" ] + node-version: [ "22", "24", "26" ] runs-on: "ubuntu-22.04" defaults: run: @@ -101,9 +100,9 @@ jobs: - uses: actions/setup-node@v6 name: Setup Node.js 24 for build with: - # pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL - # in October. Build/install runs on Node 24; tests run on the - # matrix version below using direct jest invocation. + # Build and install once on a fixed version so the generated docs + # are identical across matrix legs; the tests below then run on each + # supported Node version. node-version: 24 cache: 'pnpm' cache-dependency-path: nodejs/pnpm-lock.yaml @@ -152,9 +151,9 @@ jobs: S3_TEST: "1" # Newer @smithy/core uses dynamic ESM imports. NODE_OPTIONS: "--experimental-vm-modules" - # Invoke jest directly because pnpm 11 itself requires Node 22+ - # while the matrix tests on older Node versions. - run: npx jest --verbose + # Invoke the installed jest binary directly; the pnpm shim is set up + # against the build-phase Node, not the version selected above. + run: node_modules/.bin/jest --verbose - name: Test examples working-directory: ./ env: @@ -164,7 +163,7 @@ jobs: run: | python ci/mock_openai.py & cd nodejs/examples - npx jest --testEnvironment jest-environment-node-single-context --verbose + node_modules/.bin/jest --testEnvironment jest-environment-node-single-context --verbose macos: timeout-minutes: 30 # macos-15 ships a newer linker; the older macos-14 linker fails to insert @@ -185,8 +184,7 @@ jobs: version: 11.1.1 - uses: actions/setup-node@v6 with: - # pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL - # in October. + # pnpm 11 requires Node >= 22.13. node-version: 24 cache: 'pnpm' cache-dependency-path: nodejs/pnpm-lock.yaml diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index ee6906e00..c3724de8d 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -168,8 +168,7 @@ jobs: - name: Setup node uses: actions/setup-node@v6 with: - # pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL - # in October. + # pnpm 11 requires Node >= 22.13. node-version: 24 cache: pnpm cache-dependency-path: nodejs/pnpm-lock.yaml @@ -251,7 +250,7 @@ jobs: run: | set -e ${{ matrix.settings.pre_build }} - npx napi build --platform --release \ + node_modules/.bin/napi build --platform --release \ --features ${{ matrix.settings.features }} \ --target ${{ matrix.settings.target }} \ --dts ../lancedb/native.d.ts \ @@ -271,7 +270,7 @@ jobs: - name: Build run: | ${{ matrix.settings.pre_build }} - npx napi build --platform --release \ + node_modules/.bin/napi build --platform --release \ --features ${{ matrix.settings.features }} \ --target ${{ matrix.settings.target }} \ --dts ../lancedb/native.d.ts \ @@ -339,7 +338,7 @@ jobs: - target: aarch64-unknown-linux-gnu host: ubuntu-2404-8x-arm64 node: - - '20' + - '22' runs-on: ${{ matrix.settings.host }} defaults: run: @@ -385,9 +384,9 @@ jobs: - name: Move built files run: cp dist/native.d.ts dist/native.js dist/*.node lancedb/ - name: Test bindings - # Invoke jest directly because pnpm 11 itself requires Node 22+ - # while the matrix tests on older Node versions. - run: npx jest --verbose + # Invoke the installed jest binary directly; the pnpm shim is set up + # against the install-phase Node, not the version selected above. + run: node_modules/.bin/jest --verbose publish: name: Publish runs-on: ubuntu-latest diff --git a/.github/workflows/update_package_lock_run.yml b/.github/workflows/update_package_lock_run.yml deleted file mode 100644 index 35836a86f..000000000 --- a/.github/workflows/update_package_lock_run.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Update package-lock.json - -on: - workflow_dispatch: - -permissions: - contents: read - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - ref: main - persist-credentials: false - fetch-depth: 0 - lfs: true - - uses: ./.github/workflows/update_package_lock - with: - github_token: ${{ secrets.LANCEDB_RELEASE_TOKEN }} diff --git a/.github/workflows/update_package_lock_run_nodejs.yml b/.github/workflows/update_package_lock_run_nodejs.yml deleted file mode 100644 index 227a94ecc..000000000 --- a/.github/workflows/update_package_lock_run_nodejs.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Update NodeJs package-lock.json - -on: - workflow_dispatch: - -permissions: - contents: read - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - ref: main - persist-credentials: false - fetch-depth: 0 - lfs: true - - uses: ./.github/workflows/update_package_lock_nodejs - with: - github_token: ${{ secrets.LANCEDB_RELEASE_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bef53f90e..7c98a344c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,10 @@ repos: hooks: - id: local-biome-check name: biome check - entry: npx @biomejs/biome@1.8.3 check --config-path nodejs/biome.json nodejs/ + # Use the biome from nodejs/package.json rather than a separately + # pinned one: the two drifted apart and disagreed on formatting, so + # this hook rejected code that `pnpm lint` accepted. + entry: nodejs/node_modules/.bin/biome check --config-path nodejs/biome.json nodejs/ language: system types: [text] files: "nodejs/.*" diff --git a/AGENTS.md b/AGENTS.md index 1e072446a..f6d01db03 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,7 @@ Before committing changes, run formatting for every language you touched. At min * Rust changes: run `cargo fmt --all`. * Python changes: run `ruff format .` and `ruff check .` from the repository root, and run targeted tests through `cd python && uv run ...`. -* TypeScript changes: run the relevant `npm`/`pnpm` lint, format, build, and docs commands in `nodejs`. +* TypeScript changes: run the relevant `pnpm` lint, format, build, and docs commands in `nodejs`. Before creating a PR, the exact value passed to `gh pr create --title` must follow Conventional Commits, such as `fix: support nested field paths in native index creation` @@ -101,12 +101,12 @@ Python bindings changes: TypeScript bindings changes: 1. Add napi-rs method binding on `Table` in `nodejs/src/table.rs`. -2. Run `npm run build` to generate TypeScript definitions. +2. Run `pnpm build` to generate TypeScript definitions. 3. Add typescript method on abstract class `Table` in `nodejs/src/table.ts`. 4. Add concrete method on `LocalTable` class in `nodejs/src/native_table.ts`. * Note: despite the name, this class is also used for remote tables. 5. Add test in `nodejs/__test__/table.test.ts`. -6. Run `npm run docs` to generate TypeScript documentation. +6. Run `pnpm run docs` to generate TypeScript documentation. ## Python API reference diff --git a/Makefile b/Makefile index b558e6ee3..2e665ee28 100644 --- a/Makefile +++ b/Makefile @@ -5,5 +5,5 @@ licenses: cd python && cargo about generate ../about.hbs -o RUST_THIRD_PARTY_LICENSES.html -c ../about.toml cd python && uv sync --all-extras && uv tool run pip-licenses --python .venv/bin/python --format=markdown --with-urls --output-file=PYTHON_THIRD_PARTY_LICENSES.md cd nodejs && cargo about generate ../about.hbs -o RUST_THIRD_PARTY_LICENSES.html -c ../about.toml - cd nodejs && npx license-checker --markdown --out NODEJS_THIRD_PARTY_LICENSES.md + cd nodejs && pnpm dlx license-checker@25 --markdown --out NODEJS_THIRD_PARTY_LICENSES.md cd java && ./mvnw license:aggregate-add-third-party -q diff --git a/ci/update_lockfiles.sh b/ci/update_lockfiles.sh index 9defa6ddc..ddf5fae79 100755 --- a/ci/update_lockfiles.sh +++ b/ci/update_lockfiles.sh @@ -12,16 +12,12 @@ done # This updates the lockfile without building cargo metadata --quiet > /dev/null -pushd nodejs || exit 1 -npm install --package-lock-only --silent -popd - if git diff --quiet --exit-code; then echo "No lockfile changes to commit; skipping amend." elif $AMEND; then - git add Cargo.lock nodejs/package-lock.json + git add Cargo.lock git commit --amend --no-edit else - git add Cargo.lock nodejs/package-lock.json + git add Cargo.lock git commit -m "Update lockfiles" fi diff --git a/docs/README.md b/docs/README.md index c0171f6cb..bce1ec668 100644 --- a/docs/README.md +++ b/docs/README.md @@ -47,22 +47,24 @@ pytest -vv python/tests/docs ### Checking typescript examples -The `@lancedb/lancedb` package must be built before running the tests: +The examples depend on `@lancedb/lancedb` at `file:../dist`, so the package must be +built before running the tests. This uses pnpm; see the +[Typescript contributing guide](../nodejs/CONTRIBUTING.md) for the toolchain setup. ```shell pushd nodejs -npm ci -npm run build +pnpm install +pnpm build popd ``` -Then you can run the examples by going to the `nodejs/examples` directory and -running the tests like a normal npm package: +Then you can run the examples by going to the `nodejs/examples` directory, which is a +separate pnpm package with its own lockfile: ```shell pushd nodejs/examples -npm ci -npm test +pnpm install +pnpm test popd ``` @@ -84,6 +86,7 @@ The new files should be checked into the repository. ```shell pushd nodejs -npm run docs +# `pnpm docs` would invoke pnpm's built-in `docs` command, not the script. +pnpm run docs popd ``` diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index e87f3e0ee..000000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "name": "lancedb-docs-test", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "lancedb-docs-test", - "version": "1.0.0", - "license": "Apache 2", - "dependencies": { - "apache-arrow": "file:../node/node_modules/apache-arrow", - "vectordb": "file:../node" - }, - "devDependencies": { - "@types/node": "^20.11.8", - "typescript": "^5.3.3" - } - }, - "../node": { - "name": "vectordb", - "version": "0.21.2-beta.0", - "cpu": [ - "x64", - "arm64" - ], - "license": "Apache-2.0", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "@neon-rs/load": "^0.0.74", - "axios": "^1.4.0" - }, - "devDependencies": { - "@neon-rs/cli": "^0.0.160", - "@types/chai": "^4.3.4", - "@types/chai-as-promised": "^7.1.5", - "@types/mocha": "^10.0.1", - "@types/node": "^18.16.2", - "@types/sinon": "^10.0.15", - "@types/temp": "^0.9.1", - "@types/uuid": "^9.0.3", - "@typescript-eslint/eslint-plugin": "^5.59.1", - "apache-arrow-old": "npm:apache-arrow@13.0.0", - "cargo-cp-artifact": "^0.1", - "chai": "^4.3.7", - "chai-as-promised": "^7.1.1", - "eslint": "^8.39.0", - "eslint-config-standard-with-typescript": "^34.0.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-n": "^15.7.0", - "eslint-plugin-promise": "^6.1.1", - "mocha": "^10.2.0", - "openai": "^4.24.1", - "sinon": "^15.1.0", - "temp": "^0.9.4", - "ts-node": "^10.9.1", - "ts-node-dev": "^2.0.0", - "typedoc": "^0.24.7", - "typedoc-plugin-markdown": "^3.15.3", - "typescript": "^5.1.0", - "uuid": "^9.0.0" - }, - "optionalDependencies": { - "@lancedb/vectordb-darwin-arm64": "0.21.2-beta.0", - "@lancedb/vectordb-darwin-x64": "0.21.2-beta.0", - "@lancedb/vectordb-linux-arm64-gnu": "0.21.2-beta.0", - "@lancedb/vectordb-linux-x64-gnu": "0.21.2-beta.0", - "@lancedb/vectordb-win32-x64-msvc": "0.21.2-beta.0" - }, - "peerDependencies": { - "@apache-arrow/ts": "^14.0.2", - "apache-arrow": "^14.0.2" - } - }, - "../node/node_modules/apache-arrow": { - "version": "14.0.2", - "license": "Apache-2.0", - "dependencies": { - "@types/command-line-args": "5.2.0", - "@types/command-line-usage": "5.0.2", - "@types/node": "20.3.0", - "@types/pad-left": "2.1.1", - "command-line-args": "5.2.1", - "command-line-usage": "7.0.1", - "flatbuffers": "23.5.26", - "json-bignum": "^0.0.3", - "pad-left": "^2.1.0", - "tslib": "^2.5.3" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.js" - } - }, - "node_modules/@types/node": { - "version": "20.11.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.8.tgz", - "integrity": "sha512-i7omyekpPTNdv4Jb/Rgqg0RU8YqLcNsI12quKSDkRXNfx7Wxdm6HhK1awT3xTgEkgxPn3bvnSpiEAc7a7Lpyow==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/apache-arrow": { - "resolved": "../node/node_modules/apache-arrow", - "link": true - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/vectordb": { - "resolved": "../node", - "link": true - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 041e55247..000000000 --- a/docs/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "lancedb-docs-test", - "version": "1.0.0", - "description": "auto-generated tests from doc", - "author": "dev@lancedb.com", - "license": "Apache 2", - "dependencies": { - "apache-arrow": "file:../node/node_modules/apache-arrow", - "vectordb": "file:../node" - }, - "scripts": { - "build": "tsc -b && cd ../node && npm run build-release", - "example": "npm run build && node", - "test": "npm run build && ls dist/*.js | xargs -n 1 node" - }, - "devDependencies": { - "@types/node": "^20.11.8", - "typescript": "^5.3.3" - } -} diff --git a/docs/tsconfig.json b/docs/tsconfig.json deleted file mode 100644 index 23a30f8b7..000000000 --- a/docs/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "include": [ - "src/*.ts", - ], - "compilerOptions": { - "target": "es2022", - "module": "nodenext", - "declaration": true, - "outDir": "./dist", - "strict": true, - "allowJs": true, - "resolveJsonModule": true, - }, - "exclude": [ - "./dist/*", - ] -} diff --git a/nodejs/__test__/package.test.ts b/nodejs/__test__/package.test.ts index 7743d73d6..90e750321 100644 --- a/nodejs/__test__/package.test.ts +++ b/nodejs/__test__/package.test.ts @@ -5,8 +5,8 @@ import packageJson = require("../package.json"); describe("package metadata", () => { it("requires Node.js type declarations compatible with the runtime", () => { - expect(packageJson.engines.node).toBe(">= 18"); - expect(packageJson.peerDependencies["@types/node"]).toBe(">=18"); + expect(packageJson.engines.node).toBe(">= 22"); + expect(packageJson.peerDependencies["@types/node"]).toBe(">=22"); expect(packageJson.peerDependenciesMeta["@types/node"]).toEqual({ optional: true, }); diff --git a/nodejs/__test__/remote.test.ts b/nodejs/__test__/remote.test.ts index c51cbbbb7..708559b7b 100644 --- a/nodejs/__test__/remote.test.ts +++ b/nodejs/__test__/remote.test.ts @@ -3,6 +3,7 @@ import * as http from "http"; import { RequestListener } from "http"; +import packageJson = require("../package.json"); import { ClientConfig, Connection, @@ -70,7 +71,13 @@ async function withMockDatabase( try { await callback(db); } finally { - server.close(); + // `close()` alone leaves the port bound until keep-alive sockets drain, so + // a single failing test would cascade into EADDRINUSE for every test after + // it. Destroy the connections and wait for the port to actually be free. + await new Promise((resolve) => { + server.closeAllConnections(); + server.close(() => resolve()); + }); } } @@ -131,7 +138,7 @@ describe("remote connection", () => { (req, res) => { expect(req.headers["x-api-key"]).toEqual("fake"); expect(req.headers["user-agent"]).toEqual( - `LanceDB-Node-Client/${process.env.npm_package_version}`, + `LanceDB-Node-Client/${packageJson.version}`, ); const body = JSON.stringify({ tables: [] }); diff --git a/nodejs/examples/package.json b/nodejs/examples/package.json index 0dce03ac0..c3f962b79 100644 --- a/nodejs/examples/package.json +++ b/nodejs/examples/package.json @@ -8,7 +8,8 @@ "//1": "--experimental-vm-modules is needed to run jest with sentence-transformers", "//2": "--testEnvironment is needed to run jest with sentence-transformers", "//3": "See: https://github.com/huggingface/transformers.js/issues/57", - "test": "node --experimental-vm-modules node_modules/.bin/jest --testEnvironment jest-environment-node-single-context --verbose", + "//4": "jest is invoked by its JS entry, not node_modules/.bin/jest: under pnpm that path is a shell shim, which `node` cannot execute", + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --testEnvironment jest-environment-node-single-context --verbose", "lint": "biome check *.ts && biome format *.ts", "lint-ci": "biome ci .", "lint-fix": "biome check --write *.ts && pnpm format", diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json deleted file mode 100644 index b996b0810..000000000 --- a/nodejs/package-lock.json +++ /dev/null @@ -1,11106 +0,0 @@ -{ - "name": "@lancedb/lancedb", - "version": "0.38.0-beta.12", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@lancedb/lancedb", - "version": "0.38.0-beta.12", - "cpu": [ - "x64", - "arm64" - ], - "license": "Apache-2.0", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "@opentelemetry/api": "^1.9.0", - "reflect-metadata": "^0.2.2" - }, - "devDependencies": { - "@aws-sdk/client-dynamodb": "3.1003.0", - "@aws-sdk/client-kms": "3.1003.0", - "@aws-sdk/client-s3": "3.1003.0", - "@biomejs/biome": "^1.7.3", - "@jest/globals": "^29.7.0", - "@napi-rs/cli": "3.7.0", - "@opentelemetry/sdk-metrics": "^1.30.0", - "@types/axios": "^0.14.0", - "@types/jest": "^29.1.2", - "@types/node": "22.7.4", - "@types/tmp": "^0.2.6", - "apache-arrow-15": "npm:apache-arrow@15.0.0", - "apache-arrow-16": "npm:apache-arrow@16.0.0", - "apache-arrow-17": "npm:apache-arrow@17.0.0", - "apache-arrow-18": "npm:apache-arrow@18.0.0", - "eslint": "^8.57.0", - "jest": "^29.7.0", - "shx": "^0.3.4", - "tmp": "^0.2.3", - "ts-jest": "^29.1.2", - "typedoc": "0.26.4", - "typedoc-plugin-markdown": "4.2.1", - "typescript": "5.5.4", - "typescript-eslint": "^7.1.0" - }, - "engines": { - "node": ">= 18" - }, - "optionalDependencies": { - "@huggingface/transformers": "3.0.2", - "openai": "4.29.2" - }, - "peerDependencies": { - "@types/node": ">=18", - "apache-arrow": ">=15.0.0 <=18.1.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-dynamodb": { - "version": "3.1003.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1003.0.tgz", - "integrity": "sha512-tUN5kKCvaXeXnw3nqckhRq9m3bAKsYL2WaNotYEFrKQrFW3WAEu6jxRwsRr+pasSEEYvX4B03J9tlaxfPR8rZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.17", - "@aws-sdk/dynamodb-codec": "^3.972.19", - "@aws-sdk/middleware-endpoint-discovery": "^3.972.7", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.18", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.3", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-base64": "^4.3.2", - "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-utf8": "^4.2.2", - "@smithy/util-waiter": "^4.2.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/client-kms": { - "version": "3.1003.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-kms/-/client-kms-3.1003.0.tgz", - "integrity": "sha512-XO11qsl/p+WTzOTf4o9w6aZZ0lh2QHwwpuv9en2fgtVL4PnibndWC4Ln/5CB9fJpeUsQo8dLAys1PVhTh4lcGQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.17", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.18", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.3", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-base64": "^4.3.2", - "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-utf8": "^4.2.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.1003.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1003.0.tgz", - "integrity": "sha512-on8GvIWeH1pD0l53NuKbPO84bEC1mk/9zskgU+dVKcVoGxOZI94fVddCJb+IwIUN6rfBHCfXPCVbgVyzsHTAVg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.18", - "@aws-sdk/credential-provider-node": "^3.972.17", - "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", - "@aws-sdk/middleware-expect-continue": "^3.972.7", - "@aws-sdk/middleware-flexible-checksums": "^3.973.4", - "@aws-sdk/middleware-host-header": "^3.972.7", - "@aws-sdk/middleware-location-constraint": "^3.972.7", - "@aws-sdk/middleware-logger": "^3.972.7", - "@aws-sdk/middleware-recursion-detection": "^3.972.7", - "@aws-sdk/middleware-sdk-s3": "^3.972.18", - "@aws-sdk/middleware-ssec": "^3.972.7", - "@aws-sdk/middleware-user-agent": "^3.972.18", - "@aws-sdk/region-config-resolver": "^3.972.7", - "@aws-sdk/signature-v4-multi-region": "^3.996.6", - "@aws-sdk/types": "^3.973.5", - "@aws-sdk/util-endpoints": "^3.996.4", - "@aws-sdk/util-user-agent-browser": "^3.972.7", - "@aws-sdk/util-user-agent-node": "^3.973.3", - "@smithy/config-resolver": "^4.4.10", - "@smithy/core": "^3.23.8", - "@smithy/eventstream-serde-browser": "^4.2.11", - "@smithy/eventstream-serde-config-resolver": "^4.3.11", - "@smithy/eventstream-serde-node": "^4.2.11", - "@smithy/fetch-http-handler": "^5.3.13", - "@smithy/hash-blob-browser": "^4.2.12", - "@smithy/hash-node": "^4.2.11", - "@smithy/hash-stream-node": "^4.2.11", - "@smithy/invalid-dependency": "^4.2.11", - "@smithy/md5-js": "^4.2.11", - "@smithy/middleware-content-length": "^4.2.11", - "@smithy/middleware-endpoint": "^4.4.22", - "@smithy/middleware-retry": "^4.4.39", - "@smithy/middleware-serde": "^4.2.12", - "@smithy/middleware-stack": "^4.2.11", - "@smithy/node-config-provider": "^4.3.11", - "@smithy/node-http-handler": "^4.4.14", - "@smithy/protocol-http": "^5.3.11", - "@smithy/smithy-client": "^4.12.2", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.11", - "@smithy/util-base64": "^4.3.2", - "@smithy/util-body-length-browser": "^4.2.2", - "@smithy/util-body-length-node": "^4.2.3", - "@smithy/util-defaults-mode-browser": "^4.3.38", - "@smithy/util-defaults-mode-node": "^4.2.41", - "@smithy/util-endpoints": "^3.3.2", - "@smithy/util-middleware": "^4.2.11", - "@smithy/util-retry": "^4.2.11", - "@smithy/util-stream": "^4.5.17", - "@smithy/util-utf8": "^4.2.2", - "@smithy/util-waiter": "^4.2.11", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.974.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.12.tgz", - "integrity": "sha512-qrqgioqYFjwR6LatVNS1L2Vk++EwRIxqSQXPKNv5Ofux2D8UNgqMQ1znnMyEImXquVPTtbf71fc128pvmU6y9A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@aws-sdk/xml-builder": "^3.972.24", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/crc64-nvme": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.8.tgz", - "integrity": "sha512-fVfUCL/Xh2zINYMPZvj+iBn6XWouQf0DAnjaWCI9MkmqXzL2Iy5FoQB8O7syFe6gN6AH1ecDDU58T51Ou0kFkA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.38", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.38.tgz", - "integrity": "sha512-m3WjZEgPtioMhPmwqUt+DhlTJ2i9ufR6DhfkyXojb9puEvfR+ur2U5shavu5/Cc9WHHsDCvALi6UFHgcqjhQ5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.40", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.40.tgz", - "integrity": "sha512-D78L/m2Dr6cJnnSvWoAudPhQmCwmJ7j6APXsPYmFpPaKfQTfCSu0rdm8j14Np+VmXF9z8Aj8HE3xFpsrwtfgeg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.42.tgz", - "integrity": "sha512-Mu5ESvFXeinafVM8jTIvRqcvK2Ehj4kz3auT39yUcHwu1Vfxo6xRlmUafdKLW4tusjAJukQwK09sCSMgOm7OKg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/credential-provider-env": "^3.972.38", - "@aws-sdk/credential-provider-http": "^3.972.40", - "@aws-sdk/credential-provider-login": "^3.972.42", - "@aws-sdk/credential-provider-process": "^3.972.38", - "@aws-sdk/credential-provider-sso": "^3.972.42", - "@aws-sdk/credential-provider-web-identity": "^3.972.42", - "@aws-sdk/nested-clients": "^3.997.10", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.42.tgz", - "integrity": "sha512-O6WkZga3kf0yqyJYd1dbeJqVhEgJx/x1UaLgtbR+XuL/YP+K5y6QTxQKL7ka9z3jnQASESKGAPnRyt4D5hQrxA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/nested-clients": "^3.997.10", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.43", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.43.tgz", - "integrity": "sha512-D/DJmbrWRP5BXEO3FH+ar4el+2n6OlGofiud7dQun2jES+AQEJjczenp1jBb4MBN7CpGpS8nsWGQLtuzc9tQbA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.38", - "@aws-sdk/credential-provider-http": "^3.972.40", - "@aws-sdk/credential-provider-ini": "^3.972.42", - "@aws-sdk/credential-provider-process": "^3.972.38", - "@aws-sdk/credential-provider-sso": "^3.972.42", - "@aws-sdk/credential-provider-web-identity": "^3.972.42", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.38", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.38.tgz", - "integrity": "sha512-EnbYVajGgbkb24s0K1eo4VNAPV5mHIET7LSvirTaFCwkfrfaOJxtSE+wY/tJdKDS21cEYkZs2ruCaAm+W4iblg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.42.tgz", - "integrity": "sha512-RVV/9NbFwI8ZHEH5dn39lGyFmSbSVj1+orZdr6QsOe1mW9DCglmlen0cFaNZmCcqkqc7erNRHNBduxbeZuHAnw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/nested-clients": "^3.997.10", - "@aws-sdk/token-providers": "3.1049.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.42.tgz", - "integrity": "sha512-/67fXX0ddllD4u2Nujc5PvT4byHgpMUfz6+RxIKi/0nFIckeorm7JvXgzBuDyVKw0s58EbofmETDWUf9vTEuHQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/nested-clients": "^3.997.10", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/dynamodb-codec": { - "version": "3.973.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.12.tgz", - "integrity": "sha512-E+qpJPN1QLzfeVDQe1gVmMiHu9PTJWwXqSQjIt8mH5OQXmds2J/IN+Ar6Oa9ZhhuPZb4fPkcgZg4UEpwJM90NA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/endpoint-cache": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.5.tgz", - "integrity": "sha512-itVdge0NozgtgmtbZ25FVwWU3vGlE7x7feE/aOEJNkQfEpbkrF8Rj1QmnK+2blFfYE1xWt/iU+6/jUp/pv1+MA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "mnemonist": "0.38.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.972.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.14.tgz", - "integrity": "sha512-Aaj0d+xbo1jJquBWJP0/9V/XZRYukO3LWIRp3dOLHmoFrYKb4YZ0aLefgVHfGcNOVBS2ZTq7L/n5JcrE7DaC+Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.13.tgz", - "integrity": "sha512-1r6EkFdSQ4quTP3pW8yWIcYuyDwdwdBxGr+kfuPFYE3DqR+1gBc6NyJneAyoIs+wc/cUfnyJ4ZYC0T2SQTxP9A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/endpoint-cache": "^3.972.5", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.12.tgz", - "integrity": "sha512-dA5pKTom/Ls9mgeyeaRBNQrRIVOLVjv4AmKOB0/e4yaiXEUy0gSz2d3liP8JHtYoCAEWySU1jWnyzwLOREN+4g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.974.20", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.20.tgz", - "integrity": "sha512-NdnMVQCR1YjIcqFAiNLdBiOwr2DyQDB2IiXQrBhzolKOv32ae4d4Ll7IzLMi04eMHiq/o/Y/GjFuVjF9HuG0QA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/crc64-nvme": "^3.972.8", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.13.tgz", - "integrity": "sha512-EA3+u2LD3kGcfRNmCSjyJuzX4XvG4zYv57i4ZksH+1IEciuSyHQGvzivEz7vZ+jbRPdAAe7WWKy/4M8InCKDcw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.972.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.10.tgz", - "integrity": "sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.12.tgz", - "integrity": "sha512-NxB2dS4/mV3380hNkC72TkhMaLLjWGGBeTAEucqlJptVVovTbNmQWZLwaMC74ICo9NZHmFiBVVTHzDfAh/3y6Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.14.tgz", - "integrity": "sha512-bqL+upATpOJ/7px4IVfMVxcM6Lyt9uRizmEx3mNg4N6+IQlnOaYXXOJ4TNX6P0mKPPW0lwn9ZW8QEhXwQuCH9A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.41.tgz", - "integrity": "sha512-M4T2I2WPuH5WQpU8Tsp+u2bcO29zGRkU14ATzuqb9I4xh8tzsLqtp4hzaJM5aO2dhMZnHDzyQwSFVgc3XbnoGg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/signature-v4-multi-region": "^3.996.27", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.972.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.10.tgz", - "integrity": "sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.42.tgz", - "integrity": "sha512-U7jjlJKQnuUlI2swC2umFLFzLAxMLudSRFv+Bqk2F8ORmr5bG25qsFxGm4GEFwoZeGaFFnAFmTY0xReVRfyl2A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.10.tgz", - "integrity": "sha512-FtQ/Bt327peZJuyo4WZSOLVUTw9ujRxntepiC7L65FxA2P82Xlq0g14T22BuqBUeMjDoxa9nvwiMHjLIfP3eUg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/signature-v4-multi-region": "^3.996.27", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.16.tgz", - "integrity": "sha512-/YaivCvKUkEeMN9VTKBSvBn5w/4osAM1YboM58DKaLF/vqFGf/FdJCLmppqiPPJWZaXcASqByVjc3evE7KHKdA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.1049.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1049.0.tgz", - "integrity": "sha512-r7+d0lQMTHKypkmaF5jRTBYLYHCUHzt3gaVoN9SidLhQeWhCmHk3AKrboDTpPF5b7Pt7vKu3+oeMjznM2Eu1ow==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@aws-sdk/nested-clients": "^3.997.10", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.11.tgz", - "integrity": "sha512-BUMJ6VoL54r6Udj/wKy8uKRIndL04rGbaS/wTIV0dM1ewxSrR8yARBHdvZKQsK55ZSW2JrmAPk3KP15kBDxJMw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "@smithy/core": "^3.24.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.13.tgz", - "integrity": "sha512-wfk9ZdVwh187gdGXB1EyAoprwjSMt/bSfVtva+OaZx+LyNdKD7smlZf611yMd42UpfQ9vaS8NOftjSajgpdd+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.973.28", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.28.tgz", - "integrity": "sha512-A2l/PTRzsOS9L8dmZbXtDyJQgeeX+qjqLJ+fr0UU5Dz0AUQMuxgZCPSLKZgUDlHAmLFuk34owdMEvJxmDTBgRg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.12", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@nodable/entities": "2.1.0", - "@smithy/types": "^4.14.1", - "fast-xml-parser": "5.7.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@huggingface/jinja": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.3.4.tgz", - "integrity": "sha512-kFFQWJiWwvxezKQnvH1X7GjsECcMljFx+UZK9hx6P26aVHwwidJVTB0ptLfRVZQvVkOGHoMmTGvo4nT0X9hHOA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@huggingface/transformers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-3.0.2.tgz", - "integrity": "sha512-lTyS81eQazMea5UCehDGFMfdcNRZyei7XQLH5X6j4AhA/18Ka0+5qPgMxUxuZLU4xkv60aY2KNz9Yzthv6WVJg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@huggingface/jinja": "^0.3.0", - "onnxruntime-node": "1.19.2", - "onnxruntime-web": "1.21.0-dev.20241024-d9ca84ef96", - "sharp": "^0.33.5" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@inquirer/ansi": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz", - "integrity": "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.1.5.tgz", - "integrity": "sha512-Jmf9tgBHIEK5SAOB7swYfStqmtkZb00xOTpSQmkoGEpdxOTpJi9RS0A8bkfDPHTTItZRJrRdZrEMu25wyj0VfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.5", - "@inquirer/core": "^11.1.10", - "@inquirer/figures": "^2.0.5", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.13.tgz", - "integrity": "sha512-wkGPC7yJ5WJk1DJ5SX7fzk+gfj4BM8cf5dDDi71B/551xHrdsZVRJOC0WyikXd0pEsb/9cLniuE4atbsMqmFkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "11.1.10", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.10.tgz", - "integrity": "sha512-a4Q5BXHQAHa9eO202sTaFCHFYVB3x5fauDuThEAdZ9gfn76pSxiKU7wWcEH0N1O0XmQvNfQNU6QXpiRxmYQx+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.5", - "@inquirer/figures": "^2.0.5", - "@inquirer/type": "^4.0.5", - "cli-width": "^4.1.0", - "fast-wrap-ansi": "^0.2.0", - "mute-stream": "^3.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/editor": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.1.2.tgz", - "integrity": "sha512-Y3Nor7S/DhIPo+8Ym/dSY4efwKI4BsflKDwXh0jNeXJsSF3dteS/3Yf+z4wkibVZDvYMyCgknSTQlNahfunGHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/external-editor": "^3.0.0", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "5.0.14", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.0.14.tgz", - "integrity": "sha512-qyY9zcIX2eKYwaAUiQo9zORd61Lc3sXeM72fVbeHkYnDkqfr8/armcRbmVAIrExeJhI2puk+uomeKtWrpUVUmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.0.tgz", - "integrity": "sha512-lDSwMgg+M5rq6JKBYaJwSX6T9e/HK2qqZ1oxmOwn4AQoJE5D+7TumsxLGC02PWS//rkIVqbZv3XA3ejsc9FYvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.2" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.5.tgz", - "integrity": "sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - } - }, - "node_modules/@inquirer/input": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.0.13.tgz", - "integrity": "sha512-0l0jCHlJnXIV8CTxwQC0C+5Ziq8WP22edWgmciW2xYvoeoSck4v5FvCS1ctKdqLLR0dUo93uAHgWHywgBSoRyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.0.13.tgz", - "integrity": "sha512-WHmkYnnJAou5gx7RgcvAfUggnHNM1zWfoh0dFPl3dxVssuqt+dK5rIbaOYQXNyOegvFnopbKupjnhw2O8gANNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.0.13.tgz", - "integrity": "sha512-XDGu64ROHZjOOXLAANvJN7iIxWKhOSCG5VakrZ5kaScVR+snVJCFglD/hL3/677awtWcu4pXoWa280CDIYcBeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.5", - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "8.4.3", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.4.3.tgz", - "integrity": "sha512-ai5LseTw9HhegupIgmo4cn7RpnCGznjjXu4OI+7jMR8vu7T1ZCCNMzFFAovUCjL1fl0cceksIN1++yQE59SmZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^5.1.5", - "@inquirer/confirm": "^6.0.13", - "@inquirer/editor": "^5.1.2", - "@inquirer/expand": "^5.0.14", - "@inquirer/input": "^5.0.13", - "@inquirer/number": "^4.0.13", - "@inquirer/password": "^5.0.13", - "@inquirer/rawlist": "^5.2.9", - "@inquirer/search": "^4.1.9", - "@inquirer/select": "^5.1.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "5.2.9", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.2.9.tgz", - "integrity": "sha512-a1ErXEfgjfPYpyQ89dp+7n2IISjH9oQg3ygvF5adz8B7aHn4n2PjEgu1wpVTp69K3bj3lVLxP0qJ2b1clk1Whw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.9.tgz", - "integrity": "sha512-ZlbM28Q9lmLkFPNAIv+ZuY530n5Km8U1WW48oYEvDhe9yc2uL3m3t+JSdRUkQlk5fuIuskgiIVjcb7czFzQpuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^11.1.10", - "@inquirer/figures": "^2.0.5", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.1.5.tgz", - "integrity": "sha512-6SRg6kHfK/sjLXOsuqNebuir+sjwrf/iWuRUnXgB2slzEewppI1WfzeS16XxDcOQmXBruMmmB9Cgrz7wsAxqMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^2.0.5", - "@inquirer/core": "^11.1.10", - "@inquirer/figures": "^2.0.5", - "@inquirer/type": "^4.0.5" - }, - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.5.tgz", - "integrity": "sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/cli": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-3.7.0.tgz", - "integrity": "sha512-3d3+rmxlOIV/G1zPWeX4PCxuYnhcCQM2BvY9rtimC8RO0dFR9gtYP+Grov+WoduZtfWRj5N1XvytWeRxxCk5zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/prompts": "^8.0.0", - "@napi-rs/cross-toolchain": "^1.0.3", - "@napi-rs/wasm-tools": "^1.0.1", - "@octokit/rest": "^22.0.1", - "clipanion": "^4.0.0-rc.4", - "colorette": "^2.0.20", - "emnapi": "^1.10.0", - "es-toolkit": "^1.41.0", - "js-yaml": "^4.1.0", - "obug": "^2.0.0", - "semver": "^7.7.3", - "typanion": "^3.14.0" - }, - "bin": { - "napi": "dist/cli.js", - "napi-raw": "cli.mjs" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/runtime": "^1.7.1" - }, - "peerDependenciesMeta": { - "@emnapi/runtime": { - "optional": true - } - } - }, - "node_modules/@napi-rs/cross-toolchain": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@napi-rs/cross-toolchain/-/cross-toolchain-1.0.3.tgz", - "integrity": "sha512-ENPfLe4937bsKVTDA6zdABx4pq9w0tHqRrJHyaGxgaPq03a2Bd1unD5XSKjXJjebsABJ+MjAv1A2OvCgK9yehg==", - "dev": true, - "license": "MIT", - "workspaces": [ - ".", - "arm64/*", - "x64/*" - ], - "dependencies": { - "@napi-rs/lzma": "^1.4.5", - "@napi-rs/tar": "^1.1.0", - "debug": "^4.4.1" - }, - "peerDependencies": { - "@napi-rs/cross-toolchain-arm64-target-aarch64": "^1.0.3", - "@napi-rs/cross-toolchain-arm64-target-armv7": "^1.0.3", - "@napi-rs/cross-toolchain-arm64-target-ppc64le": "^1.0.3", - "@napi-rs/cross-toolchain-arm64-target-s390x": "^1.0.3", - "@napi-rs/cross-toolchain-arm64-target-x86_64": "^1.0.3", - "@napi-rs/cross-toolchain-x64-target-aarch64": "^1.0.3", - "@napi-rs/cross-toolchain-x64-target-armv7": "^1.0.3", - "@napi-rs/cross-toolchain-x64-target-ppc64le": "^1.0.3", - "@napi-rs/cross-toolchain-x64-target-s390x": "^1.0.3", - "@napi-rs/cross-toolchain-x64-target-x86_64": "^1.0.3" - }, - "peerDependenciesMeta": { - "@napi-rs/cross-toolchain-arm64-target-aarch64": { - "optional": true - }, - "@napi-rs/cross-toolchain-arm64-target-armv7": { - "optional": true - }, - "@napi-rs/cross-toolchain-arm64-target-ppc64le": { - "optional": true - }, - "@napi-rs/cross-toolchain-arm64-target-s390x": { - "optional": true - }, - "@napi-rs/cross-toolchain-arm64-target-x86_64": { - "optional": true - }, - "@napi-rs/cross-toolchain-x64-target-aarch64": { - "optional": true - }, - "@napi-rs/cross-toolchain-x64-target-armv7": { - "optional": true - }, - "@napi-rs/cross-toolchain-x64-target-ppc64le": { - "optional": true - }, - "@napi-rs/cross-toolchain-x64-target-s390x": { - "optional": true - }, - "@napi-rs/cross-toolchain-x64-target-x86_64": { - "optional": true - } - } - }, - "node_modules/@napi-rs/lzma": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma/-/lzma-1.4.5.tgz", - "integrity": "sha512-zS5LuN1OBPAyZpda2ZZgYOEDC+xecUdAGnrvbYzjnLXkrq/OBC3B9qcRvlxbDR3k5H/gVfvef1/jyUqPknqjbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/lzma-android-arm-eabi": "1.4.5", - "@napi-rs/lzma-android-arm64": "1.4.5", - "@napi-rs/lzma-darwin-arm64": "1.4.5", - "@napi-rs/lzma-darwin-x64": "1.4.5", - "@napi-rs/lzma-freebsd-x64": "1.4.5", - "@napi-rs/lzma-linux-arm-gnueabihf": "1.4.5", - "@napi-rs/lzma-linux-arm64-gnu": "1.4.5", - "@napi-rs/lzma-linux-arm64-musl": "1.4.5", - "@napi-rs/lzma-linux-ppc64-gnu": "1.4.5", - "@napi-rs/lzma-linux-riscv64-gnu": "1.4.5", - "@napi-rs/lzma-linux-s390x-gnu": "1.4.5", - "@napi-rs/lzma-linux-x64-gnu": "1.4.5", - "@napi-rs/lzma-linux-x64-musl": "1.4.5", - "@napi-rs/lzma-wasm32-wasi": "1.4.5", - "@napi-rs/lzma-win32-arm64-msvc": "1.4.5", - "@napi-rs/lzma-win32-ia32-msvc": "1.4.5", - "@napi-rs/lzma-win32-x64-msvc": "1.4.5" - } - }, - "node_modules/@napi-rs/lzma-android-arm-eabi": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm-eabi/-/lzma-android-arm-eabi-1.4.5.tgz", - "integrity": "sha512-Up4gpyw2SacmyKWWEib06GhiDdF+H+CCU0LAV8pnM4aJIDqKKd5LHSlBht83Jut6frkB0vwEPmAkv4NjQ5u//Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-android-arm64": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-android-arm64/-/lzma-android-arm64-1.4.5.tgz", - "integrity": "sha512-uwa8sLlWEzkAM0MWyoZJg0JTD3BkPknvejAFG2acUA1raXM8jLrqujWCdOStisXhqQjZ2nDMp3FV6cs//zjfuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-darwin-arm64": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-arm64/-/lzma-darwin-arm64-1.4.5.tgz", - "integrity": "sha512-0Y0TQLQ2xAjVabrMDem1NhIssOZzF/y/dqetc6OT8mD3xMTDtF8u5BqZoX3MyPc9FzpsZw4ksol+w7DsxHrpMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-darwin-x64": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-darwin-x64/-/lzma-darwin-x64-1.4.5.tgz", - "integrity": "sha512-vR2IUyJY3En+V1wJkwmbGWcYiT8pHloTAWdW4pG24+51GIq+intst6Uf6D/r46citObGZrlX0QvMarOkQeHWpw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-freebsd-x64": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-freebsd-x64/-/lzma-freebsd-x64-1.4.5.tgz", - "integrity": "sha512-XpnYQC5SVovO35tF0xGkbHYjsS6kqyNCjuaLQ2dbEblFRr5cAZVvsJ/9h7zj/5FluJPJRDojVNxGyRhTp4z2lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-arm-gnueabihf": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm-gnueabihf/-/lzma-linux-arm-gnueabihf-1.4.5.tgz", - "integrity": "sha512-ic1ZZMoRfRMwtSwxkyw4zIlbDZGC6davC9r+2oX6x9QiF247BRqqT94qGeL5ZP4Vtz0Hyy7TEViWhx5j6Bpzvw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-arm64-gnu": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-gnu/-/lzma-linux-arm64-gnu-1.4.5.tgz", - "integrity": "sha512-asEp7FPd7C1Yi6DQb45a3KPHKOFBSfGuJWXcAd4/bL2Fjetb2n/KK2z14yfW8YC/Fv6x3rBM0VAZKmJuz4tysg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-arm64-musl": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-arm64-musl/-/lzma-linux-arm64-musl-1.4.5.tgz", - "integrity": "sha512-yWjcPDgJ2nIL3KNvi4536dlT/CcCWO0DUyEOlBs/SacG7BeD6IjGh6yYzd3/X1Y3JItCbZoDoLUH8iB1lTXo3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-ppc64-gnu": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-ppc64-gnu/-/lzma-linux-ppc64-gnu-1.4.5.tgz", - "integrity": "sha512-0XRhKuIU/9ZjT4WDIG/qnX7Xz7mSQHYZo9Gb3MP2gcvBgr6BA4zywQ9k3gmQaPn9ECE+CZg2V7DV7kT+x2pUMQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-riscv64-gnu": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-riscv64-gnu/-/lzma-linux-riscv64-gnu-1.4.5.tgz", - "integrity": "sha512-QrqDIPEUUB23GCpyQj/QFyMlr8SGxxyExeZz9OWFnHfb70kXdTLWrHS/hEI1Ru+lSbQ/6xRqeoGyQ4Aqdg+/RA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-s390x-gnu": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-s390x-gnu/-/lzma-linux-s390x-gnu-1.4.5.tgz", - "integrity": "sha512-k8RVM5aMhW86E9H0QXdquwojew4H3SwPxbRVbl49/COJQWCUjGi79X6mYruMnMPEznZinUiT1jgKbFo2A00NdA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.4.5.tgz", - "integrity": "sha512-6rMtBgnIq2Wcl1rQdZsnM+rtCcVCbws1nF8S2NzaUsVaZv8bjrPiAa0lwg4Eqnn1d9lgwqT+cZgm5m+//K08Kw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-musl": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-musl/-/lzma-linux-x64-musl-1.4.5.tgz", - "integrity": "sha512-eiadGBKi7Vd0bCArBUOO/qqRYPHt/VQVvGyYvDFt6C2ZSIjlD+HuOl+2oS1sjf4CFjK4eDIog6EdXnL0NE6iyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-wasm32-wasi": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-wasm32-wasi/-/lzma-wasm32-wasi-1.4.5.tgz", - "integrity": "sha512-+VyHHlr68dvey6fXc2hehw9gHVFIW3TtGF1XkcbAu65qVXsA9D/T+uuoRVqhE+JCyFHFrO0ixRbZDRK1XJt1sA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@napi-rs/lzma-win32-arm64-msvc": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-arm64-msvc/-/lzma-win32-arm64-msvc-1.4.5.tgz", - "integrity": "sha512-eewnqvIyyhHi3KaZtBOJXohLvwwN27gfS2G/YDWdfHlbz1jrmfeHAmzMsP5qv8vGB+T80TMHNkro4kYjeh6Deg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-win32-ia32-msvc": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-ia32-msvc/-/lzma-win32-ia32-msvc-1.4.5.tgz", - "integrity": "sha512-OeacFVRCJOKNU/a0ephUfYZ2Yt+NvaHze/4TgOwJ0J0P4P7X1mHzN+ig9Iyd74aQDXYqc7kaCXA2dpAOcH87Cg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/lzma-win32-x64-msvc": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-win32-x64-msvc/-/lzma-win32-x64-msvc-1.4.5.tgz", - "integrity": "sha512-T4I1SamdSmtyZgDXGAGP+y5LEK5vxHUFwe8mz6D4R7Sa5/WCxTcCIgPJ9BD7RkpO17lzhlaM2vmVvMy96Lvk9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar/-/tar-1.1.0.tgz", - "integrity": "sha512-7cmzIu+Vbupriudo7UudoMRH2OA3cTw67vva8MxeoAe5S7vPFI7z0vp0pMXiA25S8IUJefImQ90FeJjl8fjEaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/tar-android-arm-eabi": "1.1.0", - "@napi-rs/tar-android-arm64": "1.1.0", - "@napi-rs/tar-darwin-arm64": "1.1.0", - "@napi-rs/tar-darwin-x64": "1.1.0", - "@napi-rs/tar-freebsd-x64": "1.1.0", - "@napi-rs/tar-linux-arm-gnueabihf": "1.1.0", - "@napi-rs/tar-linux-arm64-gnu": "1.1.0", - "@napi-rs/tar-linux-arm64-musl": "1.1.0", - "@napi-rs/tar-linux-ppc64-gnu": "1.1.0", - "@napi-rs/tar-linux-s390x-gnu": "1.1.0", - "@napi-rs/tar-linux-x64-gnu": "1.1.0", - "@napi-rs/tar-linux-x64-musl": "1.1.0", - "@napi-rs/tar-wasm32-wasi": "1.1.0", - "@napi-rs/tar-win32-arm64-msvc": "1.1.0", - "@napi-rs/tar-win32-ia32-msvc": "1.1.0", - "@napi-rs/tar-win32-x64-msvc": "1.1.0" - } - }, - "node_modules/@napi-rs/tar-android-arm-eabi": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-android-arm-eabi/-/tar-android-arm-eabi-1.1.0.tgz", - "integrity": "sha512-h2Ryndraj/YiKgMV/r5by1cDusluYIRT0CaE0/PekQ4u+Wpy2iUVqvzVU98ZPnhXaNeYxEvVJHNGafpOfaD0TA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-android-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-android-arm64/-/tar-android-arm64-1.1.0.tgz", - "integrity": "sha512-DJFyQHr1ZxNZorm/gzc1qBNLF/FcKzcH0V0Vwan5P+o0aE2keQIGEjJ09FudkF9v6uOuJjHCVDdK6S6uHtShAw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-darwin-arm64/-/tar-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-Zz2sXRzjIX4e532zD6xm2SjXEym6MkvfCvL2RMpG2+UwNVDVscHNcz3d47Pf3sysP2e2af7fBB3TIoK2f6trPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-darwin-x64/-/tar-darwin-x64-1.1.0.tgz", - "integrity": "sha512-EI+CptIMNweT0ms9S3mkP/q+J6FNZ1Q6pvpJOEcWglRfyfQpLqjlC0O+dptruTPE8VamKYuqdjxfqD8hifZDOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-freebsd-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-freebsd-x64/-/tar-freebsd-x64-1.1.0.tgz", - "integrity": "sha512-J0PIqX+pl6lBIAckL/c87gpodLbjZB1OtIK+RDscKC9NLdpVv6VGOxzUV/fYev/hctcE8EfkLbgFOfpmVQPg2g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-arm-gnueabihf": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm-gnueabihf/-/tar-linux-arm-gnueabihf-1.1.0.tgz", - "integrity": "sha512-SLgIQo3f3EjkZ82ZwvrEgFvMdDAhsxCYjyoSuWfHCz0U16qx3SuGCp8+FYOPYCECHN3ZlGjXnoAIt9ERd0dEUg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-arm64-gnu": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm64-gnu/-/tar-linux-arm64-gnu-1.1.0.tgz", - "integrity": "sha512-d014cdle52EGaH6GpYTQOP9Py7glMO1zz/+ynJPjjzYFSxvdYx0byrjumZk2UQdIyGZiJO2MEFpCkEEKFSgPYA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-arm64-musl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-arm64-musl/-/tar-linux-arm64-musl-1.1.0.tgz", - "integrity": "sha512-L/y1/26q9L/uBqiW/JdOb/Dc94egFvNALUZV2WCGKQXc6UByPBMgdiEyW2dtoYxYYYYc+AKD+jr+wQPcvX2vrQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-ppc64-gnu": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-ppc64-gnu/-/tar-linux-ppc64-gnu-1.1.0.tgz", - "integrity": "sha512-EPE1K/80RQvPbLRJDJs1QmCIcH+7WRi0F73+oTe1582y9RtfGRuzAkzeBuAGRXAQEjRQw/RjtNqr6UTJ+8UuWQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-s390x-gnu": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-s390x-gnu/-/tar-linux-s390x-gnu-1.1.0.tgz", - "integrity": "sha512-B2jhWiB1ffw1nQBqLUP1h4+J1ovAxBOoe5N2IqDMOc63fsPZKNqF1PvO/dIem8z7LL4U4bsfmhy3gBfu547oNQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-x64-gnu": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-x64-gnu/-/tar-linux-x64-gnu-1.1.0.tgz", - "integrity": "sha512-tbZDHnb9617lTnsDMGo/eAMZxnsQFnaRe+MszRqHguKfMwkisc9CCJnks/r1o84u5fECI+J/HOrKXgczq/3Oww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-linux-x64-musl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-linux-x64-musl/-/tar-linux-x64-musl-1.1.0.tgz", - "integrity": "sha512-dV6cODlzbO8u6Anmv2N/ilQHq/AWz0xyltuXoLU3yUyXbZcnWYZuB2rL8OBGPmqNcD+x9NdScBNXh7vWN0naSQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-wasm32-wasi": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-wasm32-wasi/-/tar-wasm32-wasi-1.1.0.tgz", - "integrity": "sha512-jIa9nb2HzOrfH0F8QQ9g3WE4aMH5vSI5/1NYVNm9ysCmNjCCtMXCAhlI3WKCdm/DwHf0zLqdrrtDFXODcNaqMw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@napi-rs/tar-win32-arm64-msvc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-arm64-msvc/-/tar-win32-arm64-msvc-1.1.0.tgz", - "integrity": "sha512-vfpG71OB0ijtjemp3WTdmBKJm9R70KM8vsSExMsIQtV0lVzP07oM1CW6JbNRPXNLhRoue9ofYLiUDk8bE0Hckg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-win32-ia32-msvc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-ia32-msvc/-/tar-win32-ia32-msvc-1.1.0.tgz", - "integrity": "sha512-hGPyPW60YSpOSgzfy68DLBHgi6HxkAM+L59ZZZPMQ0TOXjQg+p2EW87+TjZfJOkSpbYiEkULwa/f4a2hcVjsqQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/tar-win32-x64-msvc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@napi-rs/tar-win32-x64-msvc/-/tar-win32-x64-msvc-1.1.0.tgz", - "integrity": "sha512-L6Ed1DxXK9YSCMyvpR8MiNAyKNkQLjsHsHK9E0qnHa8NzLFqzDKhvs5LfnWxM2kJ+F7m/e5n9zPm24kHb3LsVw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@napi-rs/wasm-tools": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools/-/wasm-tools-1.0.1.tgz", - "integrity": "sha512-enkZYyuCdo+9jneCPE/0fjIta4wWnvVN9hBo2HuiMpRF0q3lzv1J6b/cl7i0mxZUKhBrV3aCKDBQnCOhwKbPmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/wasm-tools-android-arm-eabi": "1.0.1", - "@napi-rs/wasm-tools-android-arm64": "1.0.1", - "@napi-rs/wasm-tools-darwin-arm64": "1.0.1", - "@napi-rs/wasm-tools-darwin-x64": "1.0.1", - "@napi-rs/wasm-tools-freebsd-x64": "1.0.1", - "@napi-rs/wasm-tools-linux-arm64-gnu": "1.0.1", - "@napi-rs/wasm-tools-linux-arm64-musl": "1.0.1", - "@napi-rs/wasm-tools-linux-x64-gnu": "1.0.1", - "@napi-rs/wasm-tools-linux-x64-musl": "1.0.1", - "@napi-rs/wasm-tools-wasm32-wasi": "1.0.1", - "@napi-rs/wasm-tools-win32-arm64-msvc": "1.0.1", - "@napi-rs/wasm-tools-win32-ia32-msvc": "1.0.1", - "@napi-rs/wasm-tools-win32-x64-msvc": "1.0.1" - } - }, - "node_modules/@napi-rs/wasm-tools-android-arm-eabi": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-android-arm-eabi/-/wasm-tools-android-arm-eabi-1.0.1.tgz", - "integrity": "sha512-lr07E/l571Gft5v4aA1dI8koJEmF1F0UigBbsqg9OWNzg80H3lDPO+auv85y3T/NHE3GirDk7x/D3sLO57vayw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-android-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-android-arm64/-/wasm-tools-android-arm64-1.0.1.tgz", - "integrity": "sha512-WDR7S+aRLV6LtBJAg5fmjKkTZIdrEnnQxgdsb7Cf8pYiMWBHLU+LC49OUVppQ2YSPY0+GeYm9yuZWW3kLjJ7Bg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-darwin-arm64/-/wasm-tools-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-qWTI+EEkiN0oIn/N2gQo7+TVYil+AJ20jjuzD2vATS6uIjVz+Updeqmszi7zq7rdFTLp6Ea3/z4kDKIfZwmR9g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-darwin-x64/-/wasm-tools-darwin-x64-1.0.1.tgz", - "integrity": "sha512-bA6hubqtHROR5UI3tToAF/c6TDmaAgF0SWgo4rADHtQ4wdn0JeogvOk50gs2TYVhKPE2ZD2+qqt7oBKB+sxW3A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-freebsd-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-freebsd-x64/-/wasm-tools-freebsd-x64-1.0.1.tgz", - "integrity": "sha512-90+KLBkD9hZEjPQW1MDfwSt5J1L46EUKacpCZWyRuL6iIEO5CgWU0V/JnEgFsDOGyyYtiTvHc5bUdUTWd4I9Vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-linux-arm64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-arm64-gnu/-/wasm-tools-linux-arm64-gnu-1.0.1.tgz", - "integrity": "sha512-rG0QlS65x9K/u3HrKafDf8cFKj5wV2JHGfl8abWgKew0GVPyp6vfsDweOwHbWAjcHtp2LHi6JHoW80/MTHm52Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-linux-arm64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-arm64-musl/-/wasm-tools-linux-arm64-musl-1.0.1.tgz", - "integrity": "sha512-jAasbIvjZXCgX0TCuEFQr+4D6Lla/3AAVx2LmDuMjgG4xoIXzjKWl7c4chuaD+TI+prWT0X6LJcdzFT+ROKGHQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-linux-x64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-x64-gnu/-/wasm-tools-linux-x64-gnu-1.0.1.tgz", - "integrity": "sha512-Plgk5rPqqK2nocBGajkMVbGm010Z7dnUgq0wtnYRZbzWWxwWcXfZMPa8EYxrK4eE8SzpI7VlZP1tdVsdjgGwMw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-linux-x64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-linux-x64-musl/-/wasm-tools-linux-x64-musl-1.0.1.tgz", - "integrity": "sha512-GW7AzGuWxtQkyHknHWYFdR0CHmW6is8rG2Rf4V6GNmMpmwtXt/ItWYWtBe4zqJWycMNazpfZKSw/BpT7/MVCXQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-wasm32-wasi": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-wasm32-wasi/-/wasm-tools-wasm32-wasi-1.0.1.tgz", - "integrity": "sha512-/nQVSTrqSsn7YdAc2R7Ips/tnw5SPUcl3D7QrXCNGPqjbatIspnaexvaOYNyKMU6xPu+pc0BTnKVmqhlJJCPLA==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@napi-rs/wasm-tools-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-arm64-msvc/-/wasm-tools-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-PFi7oJIBu5w7Qzh3dwFea3sHRO3pojMsaEnUIy22QvsW+UJfNQwJCryVrpoUt8m4QyZXI+saEq/0r4GwdoHYFQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-win32-ia32-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-ia32-msvc/-/wasm-tools-win32-ia32-msvc-1.0.1.tgz", - "integrity": "sha512-gXkuYzxQsgkj05Zaq+KQTkHIN83dFAwMcTKa2aQcpYPRImFm2AQzEyLtpXmyCWzJ0F9ZYAOmbSyrNew8/us6bw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/wasm-tools-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-tools-win32-x64-msvc/-/wasm-tools-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-rEAf05nol3e3eei2sRButmgXP+6ATgm0/38MKhz9Isne82T4rPIMYsCIFj0kOisaGeVwoi2fnm7O9oWp5YVnYQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodable/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", - "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/core": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", - "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^6.0.0", - "@octokit/graphql": "^9.0.3", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "before-after-hook": "^4.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/endpoint": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", - "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/graphql": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", - "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", - "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", - "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", - "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", - "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/request": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.9.tgz", - "integrity": "sha512-o8Bi3f608eyM+7BmBiUWxFsdjLb3/ym1cQek5LZOv9KkZcxRrHCPhhRzm6xjO6HVZ85ItD6+sTsjxo821SVa/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/endpoint": "^11.0.3", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "content-type": "^2.0.0", - "fast-content-type-parse": "^3.0.0", - "json-with-bigint": "^3.5.3", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/request-error": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", - "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/rest": { - "version": "22.0.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", - "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/core": "^7.0.6", - "@octokit/plugin-paginate-rest": "^14.0.0", - "@octokit/plugin-request-log": "^6.0.0", - "@octokit/plugin-rest-endpoint-methods": "^17.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", - "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^27.0.0" - } - }, - "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", - "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", - "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", - "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", - "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@shikijs/core": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.29.2.tgz", - "integrity": "sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-javascript": "1.29.2", - "@shikijs/engine-oniguruma": "1.29.2", - "@shikijs/types": "1.29.2", - "@shikijs/vscode-textmate": "^10.0.1", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.4" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz", - "integrity": "sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "1.29.2", - "@shikijs/vscode-textmate": "^10.0.1", - "oniguruma-to-es": "^2.2.0" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz", - "integrity": "sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "1.29.2", - "@shikijs/vscode-textmate": "^10.0.1" - } - }, - "node_modules/@shikijs/langs": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.29.2.tgz", - "integrity": "sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "1.29.2" - } - }, - "node_modules/@shikijs/themes": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.29.2.tgz", - "integrity": "sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "1.29.2" - } - }, - "node_modules/@shikijs/types": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.29.2.tgz", - "integrity": "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.1", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.5.3.tgz", - "integrity": "sha512-TpS6Am5zSEtx3ow7VynThEL7UwRM06zZZcmFaP6Ij9hqKPfsFhTYCLcgU7gjFjw9QAI2kzwXrfS7InH8BivJTA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.3.3.tgz", - "integrity": "sha512-LXg5yYJPYnVSrpa6LOZ+/wqpI2OlIccy7j5F16EFNYDbXWmnhry/PFRRPyM30H+hJeqfVgckFuvNGnAGCt56cA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.4.3.tgz", - "integrity": "sha512-MdQxEX5SFNc3QmpiLXtcZXsWk4imCfGVN7Ikz9I/XvavypvHT4mqxwo5JHdr/LBKCfAv89+8193ZWlUwDp8YXQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.3.3.tgz", - "integrity": "sha512-54RbRsw9eVaVnqYUXi3F6nMAPgUyKsBvAKBY2lf+81mIgM7N+yS9V5LYk7yUGbrM789b2e1qBuyDSjX1/Axxcw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.3.3.tgz", - "integrity": "sha512-TkGfDlYeWOGwYvAunHHHmKgvFtD7DFAl6gWxATI4pv4B6w0Wnx6RK5zCMoXTTqMVd+zPcWm7w8RPTgHytoCDJA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.3.3.tgz", - "integrity": "sha512-tSUA38sM7kzMoLhqQ2aCGTwLXovjurz3jjG+a0sxqD4qT/4FhQr/wxMdhCumT70giM+axC1pPjimAHLlEQCfzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.3.3.tgz", - "integrity": "sha512-ZyDAlpKKc7BKHUp+kDBiTwNhiHrOf3syQdvQadvnwWs0QJhYMHMg6QSarlhpzN6qr+KBFM/oF/xP/bvzR6KI9w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.3.3.tgz", - "integrity": "sha512-wUWowbCm7DGczl6bfLI6wGGtoxwN5Pon8DhF0Q8AA4NvgLwYfLo3h2DWI7sHr33lLcEsyTLQKeUeTHydqSfQ5Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.3.3.tgz", - "integrity": "sha512-pFw8gEMrHw9BbRwNm//UU4WgnVO7+dhfFRaSAkFPfwslWU2LXt0mM+oap3iFwGbdD8kuAWIeOAxqSiamOcM3Dw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.3.3.tgz", - "integrity": "sha512-Up1XAYnj6oxFBypWpkhNpgX+yReQxkKAV/iLaeP0KVLb2oTkmA9X+UJuGBVvEA9uZIN06y0irDi7sBMuTZMVJg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.5.3.tgz", - "integrity": "sha512-p60HGFflWsJC6V9GAYeFgbfORn+9ILx8FqgMa/8PzA0rhIUxF57EKoOR4Irs6oe1oy8RLzhjhcGS8CBtPv/t+Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.6.3.tgz", - "integrity": "sha512-MnfYnJs3cBXK3ZBqbPzXRPHIp+QtgpkX5NogcUOWHPU5GbgTAQSIfPLi91lTcEbkFDcH2YbgjLPQjWeyQ689rA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.3.3.tgz", - "integrity": "sha512-RUVCZgn92izDAARs5OJSM2+KWSfTRvQWwN9t0MmiybT3pquRgDx9vD9t/YZjd/5lwcFbsNuPojJSddYQEZGeWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.3.3.tgz", - "integrity": "sha512-+BPabWluqxo3EfMMvOgnAmPtWnCSzj+gf5mJ27wTZUbvS0hpdUIU1g80R01bEGKZx4JCi8P58jAXD9FUGMjhwA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.4.3.tgz", - "integrity": "sha512-vDtz5OuytrjP4o9GtAOz1JloN003p94utJIQeO0WAjorhpafFFjpbDOrP6btPoCN3UxaU/U84OIEt5dM7ZRRLA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.4.3.tgz", - "integrity": "sha512-P16TBD/d8ZcD9MHQ0ubQ9BbOYSd5HZKbHOLsyFWxKk2oBEoghbRFPfGOoqToZX1yrfLITXRylL16EyPP4IzLPg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.13.3", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.13.3.tgz", - "integrity": "sha512-Z8mQ+YryjP5krDadV6unnp5035L4S1brafXpTiRmjPweKSaQ6X9CYDYWvmEggXjDIa1oufX/2a/bdwu8EIz/lw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.3.3.tgz", - "integrity": "sha512-TsMTAOnjuMOv1zJBw8cfYGWhopyc3og8tZX/KuyCPjg7V3ji3f4YjFOVu843UjBmrfS/+X6kwFv5ZKg7sSm1bQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.4.3.tgz", - "integrity": "sha512-91lxjhFpAktA9yPBxniqVR/NSH9zyjMjLmoa+jbQHQFR9WiJA+n61T7HBrfh5APdEoAledJwGq8l4cS+ZJFUnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.3.3.tgz", - "integrity": "sha512-/M6Ya1Fjq8hg3rYjiwwqTen6s1bAa3U3g/2eicBaBQfaoa4ymLUke/x4T8mwb9dSq/L8TQ4YgndS0MaB9ShgmA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.3.3.tgz", - "integrity": "sha512-M+zdSrevWj0grtZx2RBULPUyjTq1aB+n+13Hrm9owiGpow6DqY/WqiSj6sHVQy/rKp0j7NzV3TNf2LrwDel8JQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.4.3.tgz", - "integrity": "sha512-Q60hxKkMEkmBsOEzxlMWEymBWov0dtWGgoJhOUs6mE8k2FDPjK8NlsRdMkmO80n2pwzreHtrYcX5jiRP7ZkP3w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.3.3.tgz", - "integrity": "sha512-RYj+8gr95WiiBqvVghoRvL12NS9ryvLyufp7FOs7EzKwGX0W5gOVlXdCrFkJScSf8gxdjQMRyIZ3Y82/MvXQ3Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.5.3.tgz", - "integrity": "sha512-2JqSmzQtKDKqBckLl/9NXTL1fY+zQBU5fNGMpud7AT65vql0tVFhb2UEZNZmLSHayLeD+X/Qzn84oXw5KS+KSQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.3.3.tgz", - "integrity": "sha512-8NZwlQ+nyAIWn9YZxH14FC8ca0i6ZGW1aJyPjD+zMZz3k9jOhXXKhdCSRvjmcSYLW42uhbrxavXqMkrTKHyY3A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.4.3.tgz", - "integrity": "sha512-8RJXeU5lEhdNfXm4XAuHlf6VtNzd279Z2FJZSR7VaELYCR46ffgjJBSjc+3UAy7V1YqBOLV0G9gWhLB/nA44nA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.6.3.tgz", - "integrity": "sha512-DSpJpPg0rQwjZk9/CSlOTplD6xSUu+bz8eDJQkq/Fmy9JlSD4ZGhXG/qFl0aRHmouDbBF75tnZ00lPxiL/sgRQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.3.3.tgz", - "integrity": "sha512-c1QpRBn3aMsoqE64dd4Imgjy8Pynfw+eR7GkjElquxUFSnezwYVaOFm8JcYa+Bo/5ssbEyPKcT3+4bmrWYh6eQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.4.3.tgz", - "integrity": "sha512-WSHSF865zDGFGtJdMmYPI2Blq/MbUrn5CB4bLDg4ARbQ9z7oA87ZZ/FSiwNZbQrU/EiVyl9lpINswALgI4lZXA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", - "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/axios": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.4.tgz", - "integrity": "sha512-9JgOaunvQdsQ/qW2OPmE5+hCeUB52lQSolecrFrthct55QekhmXEwT203s20RL+UHtCQc15y3VXpby9E7Kkh/g==", - "deprecated": "This is a stub types definition. axios provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "axios": "*" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/command-line-args": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", - "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==", - "license": "MIT" - }, - "node_modules/@types/command-line-usage": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.4.tgz", - "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==", - "license": "MIT" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "22.7.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", - "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tmp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", - "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "optional": true, - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/apache-arrow": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.1.0.tgz", - "integrity": "sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.11", - "@types/command-line-args": "^5.2.3", - "@types/command-line-usage": "^5.0.4", - "@types/node": "^20.13.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^24.3.25", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.js" - } - }, - "node_modules/apache-arrow-15": { - "name": "apache-arrow", - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-15.0.0.tgz", - "integrity": "sha512-e6aunxNKM+woQf137ny3tp/xbLjFJS2oGQxQhYGqW6dGeIwNV1jOeEAeR6sS2jwAI2qLO83gYIP2MBz02Gw5Xw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.2", - "@types/command-line-args": "^5.2.1", - "@types/command-line-usage": "^5.0.2", - "@types/node": "^20.6.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^23.5.26", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.cjs" - } - }, - "node_modules/apache-arrow-15/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow-15/node_modules/flatbuffers": { - "version": "23.5.26", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-23.5.26.tgz", - "integrity": "sha512-vE+SI9vrJDwi1oETtTIFldC/o9GsVKRM+s6EL0nQgxXlYV1Vc4Tk30hj4xGICftInKQKj1F3up2n8UbIVobISQ==", - "dev": true, - "license": "SEE LICENSE IN LICENSE" - }, - "node_modules/apache-arrow-15/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/apache-arrow-16": { - "name": "apache-arrow", - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-16.0.0.tgz", - "integrity": "sha512-bVyJeV4ahJW4XYjXefSBco0/mSSSElOzzh3Qx7tsKH+94sZaHrRotKKj1xVjON1hMUm7TODi6DnbFE73Q2h2MA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.2", - "@types/command-line-args": "^5.2.1", - "@types/command-line-usage": "^5.0.2", - "@types/node": "^20.6.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^23.5.26", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.cjs" - } - }, - "node_modules/apache-arrow-16/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow-16/node_modules/flatbuffers": { - "version": "23.5.26", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-23.5.26.tgz", - "integrity": "sha512-vE+SI9vrJDwi1oETtTIFldC/o9GsVKRM+s6EL0nQgxXlYV1Vc4Tk30hj4xGICftInKQKj1F3up2n8UbIVobISQ==", - "dev": true, - "license": "SEE LICENSE IN LICENSE" - }, - "node_modules/apache-arrow-16/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/apache-arrow-17": { - "name": "apache-arrow", - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-17.0.0.tgz", - "integrity": "sha512-X0p7auzdnGuhYMVKYINdQssS4EcKec9TCXyez/qtJt32DrIMGbzqiaMiQ0X6fQlQpw8Fl0Qygcv4dfRAr5Gu9Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.11", - "@types/command-line-args": "^5.2.3", - "@types/command-line-usage": "^5.0.4", - "@types/node": "^20.13.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^24.3.25", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.cjs" - } - }, - "node_modules/apache-arrow-17/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow-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/apache-arrow-18": { - "name": "apache-arrow", - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.0.0.tgz", - "integrity": "sha512-gFlPaqN9osetbB83zC29AbbZqGiCuFH1vyyPseJ+B7SIbfBtESV62mMT/CkiIt77W6ykC/nTWFzTXFs0Uldg4g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/helpers": "^0.5.11", - "@types/command-line-args": "^5.2.3", - "@types/command-line-usage": "^5.0.4", - "@types/node": "^20.13.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^24.3.25", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.js" - } - }, - "node_modules/apache-arrow-18/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow-18/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/apache-arrow/node_modules/@types/node": { - "version": "20.19.41", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz", - "integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow/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==", - "license": "MIT", - "peer": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==", - "optional": true - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", - "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/before-after-hook": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", - "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/bowser": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk-template": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", - "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "license": "BlueOak-1.0.0", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/clipanion": { - "version": "4.0.0-rc.4", - "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-4.0.0-rc.4.tgz", - "integrity": "sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q==", - "dev": true, - "license": "MIT", - "workspaces": [ - "website" - ], - "dependencies": { - "typanion": "^3.8.0" - }, - "peerDependencies": { - "typanion": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "license": "MIT", - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.4.tgz", - "integrity": "sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==", - "license": "MIT", - "dependencies": { - "array-back": "^6.2.2", - "chalk-template": "^0.4.0", - "table-layout": "^4.1.1", - "typical": "^7.3.0" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.3.tgz", - "integrity": "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==", - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/digest-fetch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", - "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", - "license": "ISC", - "optional": true, - "dependencies": { - "base-64": "^0.1.0", - "md5": "^2.3.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.357", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.357.tgz", - "integrity": "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emnapi": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/emnapi/-/emnapi-1.10.0.tgz", - "integrity": "sha512-swoyZjupDvLoe/KC3HZ4SY1JUN+tviT6eOZ3Px28TZAYdBHtRIiMWWrIUUH+2/9CYY4fNTID1YhYZ+kdFHszHg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "node-addon-api": ">= 6.1.0" - }, - "peerDependenciesMeta": { - "node-addon-api": { - "optional": true - } - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex-xs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", - "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-toolkit": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", - "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "docs", - "benchmarks" - ] - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", - "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, - "node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^2.1.0", - "fast-xml-builder": "^1.1.7", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.2.3" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "license": "MIT", - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatbuffers": { - "version": "24.12.23", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz", - "integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==", - "license": "Apache-2.0" - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT", - "optional": true - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formdata-node/node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "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/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "devOptional": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/guid-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", - "license": "ISC", - "optional": true - }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "license": "MIT", - "optional": true - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-bignum": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/json-bignum/-/json-bignum-0.0.3.tgz", - "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-with-bigint": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz", - "integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "optional": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mnemonist": { - "version": "0.38.3", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", - "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "obliterator": "^1.6.1" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/mute-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", - "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.44", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", - "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/obliterator": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", - "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", - "dev": true, - "license": "MIT" - }, - "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/oniguruma-to-es": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz", - "integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex-xs": "^1.0.0", - "regex": "^5.1.1", - "regex-recursion": "^5.1.1" - } - }, - "node_modules/onnxruntime-common": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.19.2.tgz", - "integrity": "sha512-a4R7wYEVFbZBlp0BfhpbFWqe4opCor3KM+5Wm22Az3NGDcQMiU2hfG/0MfnBs+1ZrlSGmlgWeMcXQkDk1UFb8Q==", - "license": "MIT", - "optional": true - }, - "node_modules/onnxruntime-node": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.19.2.tgz", - "integrity": "sha512-9eHMP/HKbbeUcqte1JYzaaRC8JPn7ojWeCeoyShO86TOR97OCyIyAIOGX3V95ErjslVhJRXY8Em/caIUc0hm1Q==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "win32", - "darwin", - "linux" - ], - "dependencies": { - "onnxruntime-common": "1.19.2", - "tar": "^7.0.1" - } - }, - "node_modules/onnxruntime-web": { - "version": "1.21.0-dev.20241024-d9ca84ef96", - "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.21.0-dev.20241024-d9ca84ef96.tgz", - "integrity": "sha512-ANSQfMALvCviN3Y4tvTViKofKToV1WUb2r2VjZVCi3uUBPaK15oNJyIxhsNyEckBr/Num3JmSXlkHOD8HfVzSQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "flatbuffers": "^1.12.0", - "guid-typescript": "^1.0.9", - "long": "^5.2.3", - "onnxruntime-common": "1.20.0-dev.20241016-2b8fc5529b", - "platform": "^1.3.6", - "protobufjs": "^7.2.4" - } - }, - "node_modules/onnxruntime-web/node_modules/flatbuffers": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz", - "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==", - "license": "SEE LICENSE IN LICENSE.txt", - "optional": true - }, - "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { - "version": "1.20.0-dev.20241016-2b8fc5529b", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.20.0-dev.20241016-2b8fc5529b.tgz", - "integrity": "sha512-KZK8b6zCYGZFjd4ANze0pqBnqnFTS3GIVeclQpa2qseDpXrCQJfkWBixRcrZShNhm3LpFOZ8qJYFC5/qsJK9WQ==", - "license": "MIT", - "optional": true - }, - "node_modules/openai": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.29.2.tgz", - "integrity": "sha512-cPkT6zjEcE4qU5OW/SoDDuXEsdOLrXlAORhzmaguj5xZSPlgKvLhi27sFWhLKj07Y6WKNWxcwIbzm512FzTBNQ==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7", - "web-streams-polyfill": "^3.2.1" - }, - "bin": { - "openai": "bin/cli" - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", - "optional": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT", - "optional": true - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "license": "MIT", - "optional": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/protobufjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.0.tgz", - "integrity": "sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/regex": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz", - "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz", - "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex": "^5.1.1", - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shiki": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", - "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "1.29.2", - "@shikijs/engine-javascript": "1.29.2", - "@shikijs/engine-oniguruma": "1.29.2", - "@shikijs/langs": "1.29.2", - "@shikijs/themes": "1.29.2", - "@shikijs/types": "1.29.2", - "@shikijs/vscode-textmate": "^10.0.1", - "@types/hast": "^3.0.4" - } - }, - "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT", - "optional": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/table-layout": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", - "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", - "license": "MIT", - "dependencies": { - "array-back": "^6.2.2", - "wordwrapjs": "^5.1.0" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.3.tgz", - "integrity": "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==", - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/tar": { - "version": "7.5.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", - "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "license": "BlueOak-1.0.0", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "optional": true - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.4.9", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", - "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.9", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.4", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <7" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typanion": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", - "integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==", - "dev": true, - "license": "MIT", - "workspaces": [ - "website" - ] - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedoc": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.4.tgz", - "integrity": "sha512-FlW6HpvULDKgc3rK04V+nbFyXogPV88hurarDPOjuuB5HAwuAlrCMQ5NeH7Zt68a/ikOKu6Z/0hFXAeC9xPccQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "shiki": "^1.9.1", - "yaml": "^2.4.5" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x" - } - }, - "node_modules/typedoc-plugin-markdown": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.1.tgz", - "integrity": "sha512-7hQt/1WaW/VI4+x3sxwcCGsEylP1E1GvF6OTTELK5sfTEp6AeK+83jkCOgZGp1pI2DiOammMYQMnxxOny9TKsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "typedoc": "0.26.x" - } - }, - "node_modules/typedoc/node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz", - "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "7.18.0", - "@typescript-eslint/parser": "7.18.0", - "@typescript-eslint/utils": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universal-user-agent": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", - "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", - "dev": true, - "license": "ISC" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wordwrapjs": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", - "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/xml-naming": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/nodejs/package.json b/nodejs/package.json index 19c7f4d32..a4a2286b7 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -67,7 +67,7 @@ "timeout": "3m" }, "engines": { - "node": ">= 18" + "node": ">= 22" }, "packageManager": "pnpm@11.1.1", "cpu": ["x64", "arm64"], @@ -101,7 +101,7 @@ "openai": "4.29.2" }, "peerDependencies": { - "@types/node": ">=18", + "@types/node": ">=22", "apache-arrow": ">=15.0.0 <=18.1.0" }, "peerDependenciesMeta": { From 36c142fa2e82c329513bcba478e0bbc41f32ed08 Mon Sep 17 00:00:00 2001 From: LanceDB Robot Date: Sat, 29 Aug 2026 14:51:41 -0700 Subject: [PATCH 16/32] chore: update lance dependency to v12.0.0-beta.5 (#4089) Updates the Rust workspace and Java lance-core dependency to Lance v12.0.0-beta.5. Includes minimal Rust 1.97 Clippy compatibility fixes required by validation. Lance tag: https://github.com/lance-format/lance/releases/tag/v12.0.0-beta.5 --------- Co-authored-by: Jack Ye --- Cargo.lock | 84 +++++++++++----------- Cargo.toml | 28 ++++---- java/pom.xml | 2 +- rust/lancedb/src/remote/table.rs | 5 +- rust/lancedb/src/table.rs | 7 ++ rust/lancedb/src/table/computed_columns.rs | 4 +- 6 files changed, 67 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cff38a304..6d973b073 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3455,8 +3455,8 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "fsst" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "rand 0.9.5", @@ -4815,8 +4815,8 @@ checksum = "e037a2e1d8d5fdbd49b16a4ea09d5d6401c1f29eca5ff29d03d3824dba16256a" [[package]] name = "lance" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arc-swap", "arrow", @@ -4888,8 +4888,8 @@ dependencies = [ [[package]] name = "lance-arrow" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4911,7 +4911,7 @@ dependencies = [ [[package]] name = "lance-arrow-scalar" version = "58.0.0" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4925,7 +4925,7 @@ dependencies = [ [[package]] name = "lance-arrow-stats" version = "58.0.0" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -4934,8 +4934,8 @@ dependencies = [ [[package]] name = "lance-bitpacking" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrayref", "crunchy", @@ -4945,8 +4945,8 @@ dependencies = [ [[package]] name = "lance-core" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4983,8 +4983,8 @@ dependencies = [ [[package]] name = "lance-datafusion" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5013,8 +5013,8 @@ dependencies = [ [[package]] name = "lance-datagen" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5031,8 +5031,8 @@ dependencies = [ [[package]] name = "lance-derive" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "proc-macro2", "quote", @@ -5041,8 +5041,8 @@ dependencies = [ [[package]] name = "lance-encoding" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-arith", "arrow-array", @@ -5075,8 +5075,8 @@ dependencies = [ [[package]] name = "lance-file" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-arith", "arrow-array", @@ -5107,8 +5107,8 @@ dependencies = [ [[package]] name = "lance-index" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arc-swap", "arrow", @@ -5172,8 +5172,8 @@ dependencies = [ [[package]] name = "lance-index-core" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5195,8 +5195,8 @@ dependencies = [ [[package]] name = "lance-io" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5236,8 +5236,8 @@ dependencies = [ [[package]] name = "lance-linalg" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5251,8 +5251,8 @@ dependencies = [ [[package]] name = "lance-namespace" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "async-trait", @@ -5264,8 +5264,8 @@ dependencies = [ [[package]] name = "lance-namespace-impls" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-ipc", @@ -5318,8 +5318,8 @@ dependencies = [ [[package]] name = "lance-select" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -5333,8 +5333,8 @@ dependencies = [ [[package]] name = "lance-table" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5374,8 +5374,8 @@ dependencies = [ [[package]] name = "lance-testing" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5388,8 +5388,8 @@ dependencies = [ [[package]] name = "lance-tokenizer" -version = "12.0.0-beta.2" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.2#dafa4642658d996b3e31dde91e02f72db7860d7e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "frostem", "icu_segmenter", diff --git a/Cargo.toml b/Cargo.toml index a16f0412c..033da5907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,20 +13,20 @@ categories = ["database-implementations"] rust-version = "1.91.0" [workspace.dependencies] -lance = { "version" = "=12.0.0-beta.2", default-features = false, "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-core = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-datagen = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-file = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-io = { "version" = "=12.0.0-beta.2", default-features = false, "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-index = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-linalg = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-namespace = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-namespace-impls = { "version" = "=12.0.0-beta.2", default-features = false, "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-table = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-testing = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-datafusion = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-encoding = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } -lance-arrow = { "version" = "=12.0.0-beta.2", "tag" = "v12.0.0-beta.2", "git" = "https://github.com/lance-format/lance.git" } +lance = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-core = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-datagen = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-file = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-io = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-index = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-linalg = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-namespace = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-namespace-impls = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-table = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-testing = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-datafusion = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-encoding = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-arrow = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } lancedb = { path = "rust/lancedb", default-features = false } ahash = "0.8" # Note that this one does not include pyarrow diff --git a/java/pom.xml b/java/pom.xml index b3521f9c6..87e6a2bae 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -28,7 +28,7 @@ UTF-8 15.0.0 - 12.0.0-beta.2 + 12.0.0-beta.5 false 2.30.0 1.7 diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index 57d2dc47d..5ce886369 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -5734,9 +5734,8 @@ mod tests { )) .execute() .await; - let err = match result { - Ok(_) => panic!("legacy remote query unexpectedly succeeded"), - Err(err) => err, + let Err(err) = result else { + panic!("legacy remote query unexpectedly succeeded") }; assert!( diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index 70b86f3cf..efc705e3c 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -5763,6 +5763,13 @@ mod tests { assert!(index_bytes > 0); assert_eq!(with_index, data_only + index_bytes); + // Release builds reject unstable overlay datasets unless explicitly opted in. + if !lance_table::feature_flags::can_read_dataset( + lance_table::feature_flags::FLAG_UNSTABLE_DATA_OVERLAY_FILES, + ) { + return; + } + // Commit an overlay file supplying new `foo` values for the first three // rows of fragment 0. There is no high-level API that writes overlays // yet, so write the overlay's data file and commit the `DataOverlay` diff --git a/rust/lancedb/src/table/computed_columns.rs b/rust/lancedb/src/table/computed_columns.rs index 4e8d8211e..b62db3fbb 100644 --- a/rust/lancedb/src/table/computed_columns.rs +++ b/rust/lancedb/src/table/computed_columns.rs @@ -1462,9 +1462,7 @@ fn plan_declarations(schema: SchemaRef, columns: &[(String, String)]) -> Result< for (name, expression) in columns { if schema.field_with_name(name).is_ok() { - return Err(Error::ColumnAlreadyExists { - name: name.to_string(), - }); + return Err(Error::ColumnAlreadyExists { name: name.clone() }); } let bound = bind(schema.clone(), name, expression)?; From 101f524e4786582e5e8a08020df4bd6f5d5ae08f Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sat, 29 Aug 2026 23:07:59 -0700 Subject: [PATCH 17/32] feat(python): support nested Function Arrow types (#4088) Teach Python Function authoring to retain the compact V1 grammar for existing types and emit canonical exact JSON for nested struct signatures. Adds coverage for recursive struct/list schemas and exact field properties. --- python/python/lancedb/functions.py | 110 ++++++++- .../tests/test_first_class_function_slice2.py | 219 +++++++++++++++++- 2 files changed, 312 insertions(+), 17 deletions(-) diff --git a/python/python/lancedb/functions.py b/python/python/lancedb/functions.py index 8a19a9d37..3e1b4f2d6 100644 --- a/python/python/lancedb/functions.py +++ b/python/python/lancedb/functions.py @@ -502,31 +502,107 @@ _GRAMMAR_PRIMITIVES = ( def _canonical_arrow_type(data_type: pa.DataType) -> str: - """The server's V1 Function type grammar. Anything outside it is rejected - here rather than at registration.""" + """The compact Function grammar, or canonical exact JSON for nested types.""" + grammar = _grammar_arrow_type(data_type) + if grammar is not None: + return grammar + exact = _exact_arrow_type(data_type) + return json.dumps(exact, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def _grammar_arrow_type(data_type: pa.DataType) -> Optional[str]: for candidate, name in _GRAMMAR_PRIMITIVES: if data_type == candidate: return name if pa.types.is_list(data_type) or pa.types.is_large_list(data_type): + item = _grammar_list_item(data_type) + if item is None: + return None prefix = "list" if pa.types.is_list(data_type) else "large_list" - return f"{prefix}<{_canonical_list_item(data_type)}>" + return f"{prefix}<{item}>" if pa.types.is_fixed_size_list(data_type) and data_type.list_size > 0: - return ( - f"fixed_size_list<{_canonical_list_item(data_type)}, {data_type.list_size}>" - ) - raise TypeError(f"unsupported Arrow type for Function signature: {data_type}") + item = _grammar_list_item(data_type) + if item is not None: + return f"fixed_size_list<{item}, {data_type.list_size}>" + return None -def _canonical_list_item(data_type: pa.DataType) -> str: +def _grammar_list_item(data_type: pa.DataType) -> Optional[str]: """The grammar names only the item type; it always means a non-nullable - child called `item`, so any other child metadata cannot be represented.""" + child called `item`, so other child properties require exact JSON.""" child = data_type.value_field if child.name != "item" or child.nullable or child.metadata: + return None + return _grammar_arrow_type(child.type) + + +def _validate_exact_arrow_field(field: pa.Field) -> None: + if not field.name: raise TypeError( - "unsupported Arrow type for Function signature: list items must be a " - f"non-nullable field named 'item', got {child}" + "unsupported Arrow type for Function signature: field names " + "must not be empty" ) - return _canonical_arrow_type(child.type) + if field.metadata: + raise TypeError( + "unsupported Arrow type for Function signature: field metadata " + f"is not supported, got {field}" + ) + + +def _exact_arrow_field(field: pa.Field) -> dict[str, Any]: + _validate_exact_arrow_field(field) + return { + "name": field.name, + "nullable": field.nullable, + "type": _exact_arrow_type(field.type), + } + + +def _exact_arrow_type(data_type: pa.DataType) -> dict[str, Any]: + for candidate, name in _GRAMMAR_PRIMITIVES: + if data_type == candidate: + return {"type": name} + if pa.types.is_struct(data_type): + fields = list(data_type) + names = [field.name for field in fields] + if not fields or len(set(names)) != len(names): + raise TypeError( + "unsupported Arrow type for Function signature: structs must have " + "non-empty, uniquely named fields" + ) + return { + "type": "struct", + "fields": [_exact_arrow_field(field) for field in fields], + } + if ( + pa.types.is_list(data_type) + or pa.types.is_large_list(data_type) + or pa.types.is_fixed_size_list(data_type) + ): + if pa.types.is_fixed_size_list(data_type): + if data_type.value_field.name != "item": + raise TypeError( + "unsupported Arrow type for Function signature: fixed-size list " + "items must be named 'item'" + ) + if data_type.list_size <= 0: + raise TypeError( + f"unsupported Arrow type for Function signature: {data_type}" + ) + value: dict[str, Any] = { + "type": ( + "list" + if pa.types.is_list(data_type) + else "large_list" + if pa.types.is_large_list(data_type) + else "fixed_size_list" + ), + "fields": [_exact_arrow_field(data_type.value_field)], + } + if pa.types.is_fixed_size_list(data_type): + value["length"] = data_type.list_size + return value + raise TypeError(f"unsupported Arrow type for Function signature: {data_type}") def _list_of(item: pa.DataType) -> pa.DataType: @@ -600,8 +676,11 @@ def _callable_parameters(function: Callable[..., Any]) -> tuple[inspect.Paramete def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutput: if isinstance(output, pa.Schema): + if output.metadata: + raise TypeError("Function output schema metadata is not supported") fields = tuple(output) elif isinstance(output, pa.Field) and pa.types.is_struct(output.type): + _validate_exact_arrow_field(output) if output.nullable: raise ValueError("Function output must be non-nullable") fields = tuple(output.type) @@ -617,6 +696,7 @@ def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutp raise TypeError( "output_schema must be a PyArrow DataType, Field, or Schema" ) + _validate_exact_arrow_field(field) if field.nullable: raise ValueError("Function output must be non-nullable") return FunctionOutput( @@ -629,6 +709,8 @@ def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutp raise ValueError("named-struct Function output must contain at least one field") if any(field.nullable for field in fields): raise ValueError("Function output fields must be non-nullable") + for field in fields: + _validate_exact_arrow_field(field) names = [field.name for field in fields] if len(set(names)) != len(names): raise ValueError("Function output field names must be unique") @@ -657,6 +739,10 @@ def _infer_signature( if input_schema is not None: if not isinstance(input_schema, pa.Schema): raise TypeError("input_schema must be a PyArrow Schema") + if input_schema.metadata: + raise TypeError("Function input schema metadata is not supported") + for field in input_schema: + _validate_exact_arrow_field(field) expected = tuple(parameter.name for parameter in parameters) actual = tuple(input_schema.names) if actual != expected: diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index 7ce6b6b91..ee14043e4 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -168,7 +168,7 @@ def test_udf_resolves_module_globals_before_builtins(tmp_path): udf(module.uses_callable_shadow) -def test_canonical_arrow_type_is_exactly_the_grammar(): +def test_canonical_arrow_type_prefers_the_compact_grammar(): from lancedb.functions import _GRAMMAR_PRIMITIVES, _canonical_arrow_type golden = json.loads( @@ -181,6 +181,13 @@ def test_canonical_arrow_type_is_exactly_the_grammar(): case["arrow_type"] for case in golden["valid"] if "<" not in case["arrow_type"] ] assert [name for _, name in _GRAMMAR_PRIMITIVES] == primitives + assert _canonical_arrow_type(pa.list_(pa.field("item", pa.float32(), False))) == ( + "list" + ) + assert ( + _canonical_arrow_type(pa.large_list(pa.field("item", pa.float32(), False))) + == "large_list" + ) for outside in [ pa.timestamp("us"), pa.decimal128(10, 2), @@ -188,7 +195,6 @@ def test_canonical_arrow_type_is_exactly_the_grammar(): pa.large_binary(), pa.binary(4), pa.duration("s"), - pa.struct([pa.field("a", pa.int32())]), pa.list_(pa.float32(), 0), pa.list_(pa.timestamp("us")), ]: @@ -378,14 +384,29 @@ def test_udf_recursion_versus_a_rebound_module_name(tmp_path): udf(raw_fact) -def test_canonical_arrow_type_rejects_unrepresentable_list_children(): +def test_canonical_arrow_type_uses_exact_json_for_list_child_properties(): from lancedb.functions import _canonical_arrow_type + nullable = pa.list_(pa.float32()) + assert json.loads(_canonical_arrow_type(nullable)) == { + "type": "list", + "fields": [ + { + "name": "item", + "nullable": True, + "type": {"type": "float32"}, + } + ], + } + named = pa.list_(pa.field("custom", pa.float32(), nullable=False)) + assert json.loads(_canonical_arrow_type(named))["fields"][0]["name"] == "custom" for outside in [ - pa.list_(pa.float32()), # pyarrow default: nullable child - pa.list_(pa.field("custom", pa.float32(), nullable=False)), pa.list_(pa.field("item", pa.float32(), nullable=False, metadata={"k": "v"})), pa.list_(pa.field("item", pa.float32(), nullable=False), 0), + pa.list_( + pa.field("item", pa.float32(), nullable=False, metadata={"k": "v"}), 3 + ), + pa.list_(pa.field("custom", pa.float32(), nullable=False), 3), ]: with pytest.raises(TypeError, match="unsupported Arrow type"): _canonical_arrow_type(outside) @@ -395,6 +416,29 @@ def test_canonical_arrow_type_rejects_unrepresentable_list_children(): ) == "fixed_size_list" ) + fixed = json.loads(_canonical_arrow_type(pa.list_(pa.float32(), 3))) + assert fixed == { + "type": "fixed_size_list", + "fields": [ + { + "name": "item", + "nullable": True, + "type": {"type": "float32"}, + } + ], + "length": 3, + } + large = json.loads(_canonical_arrow_type(pa.large_list(pa.float32()))) + assert large["type"] == "large_list" + assert large["fields"][0]["nullable"] is True + + for invalid_struct in [ + pa.struct([]), + pa.struct([pa.field("a", pa.int32()), pa.field("a", pa.int64())]), + pa.struct([pa.field("", pa.int32())]), + ]: + with pytest.raises(TypeError, match="unsupported Arrow type"): + _canonical_arrow_type(invalid_struct) def _calls_missing(value: int) -> int: @@ -482,6 +526,105 @@ def test_explicit_arrow_schema_is_deterministic(): assert signature.output.nullable is False +def test_nested_struct_output_uses_canonical_exact_json(): + token = pa.struct( + [ + pa.field("position", pa.int32(), nullable=False), + pa.field("value", pa.string(), nullable=False), + pa.field("length", pa.int32(), nullable=False), + ] + ) + analysis = pa.struct( + [ + pa.field("normalized_text", pa.string(), nullable=False), + pa.field("has_content", pa.bool_(), nullable=False), + pa.field( + "metrics", + pa.struct( + [ + pa.field("character_count", pa.int64(), nullable=False), + pa.field("word_count", pa.int32(), nullable=False), + pa.field("average_word_length", pa.float64(), nullable=False), + ] + ), + nullable=False, + ), + pa.field( + "diagnostics", + pa.struct( + [ + pa.field("status", pa.string(), nullable=False), + pa.field( + "normalization", + pa.struct( + [ + pa.field("changed", pa.bool_(), nullable=False), + pa.field( + "original_length", pa.int64(), nullable=False + ), + ] + ), + nullable=False, + ), + ] + ), + nullable=False, + ), + pa.field( + "token_preview", + pa.list_(pa.field("item", token, nullable=False)), + nullable=False, + ), + ] + ) + + @udf( + input_schema=pa.schema([pa.field("text", pa.string(), nullable=False)]), + output_schema=pa.field("analysis", analysis, nullable=False), + ) + def analyze(text): + return {"normalized_text": text} + + output = analyze.registration_request.signature.output + assert output.kind == "named_struct" + assert [field.name for field in output.fields] == [ + "normalized_text", + "has_content", + "metrics", + "diagnostics", + "token_preview", + ] + metrics = json.loads(output.fields[2].arrow_type) + assert metrics == { + "type": "struct", + "fields": [ + { + "name": "character_count", + "nullable": False, + "type": {"type": "int64"}, + }, + { + "name": "word_count", + "nullable": False, + "type": {"type": "int32"}, + }, + { + "name": "average_word_length", + "nullable": False, + "type": {"type": "float64"}, + }, + ], + } + preview = json.loads(output.fields[4].arrow_type) + assert preview["type"] == "list" + assert preview["fields"][0]["type"]["type"] == "struct" + assert [field["name"] for field in preview["fields"][0]["type"]["fields"]] == [ + "position", + "value", + "length", + ] + + def test_annotation_and_explicit_schema_validation_fail_closed(): with pytest.raises(TypeError, match="missing Function annotations"): @@ -525,6 +668,72 @@ def test_annotation_and_explicit_schema_validation_fail_closed(): def nullable_explicit(value): return value + for invalid_field in [ + pa.field("", pa.int32(), nullable=False), + pa.field("result", pa.int32(), nullable=False, metadata={"k": "v"}), + ]: + with pytest.raises(TypeError, match="unsupported Arrow type"): + + @udf( + input_schema=pa.schema([pa.field("value", pa.int64())]), + output_schema=pa.schema([invalid_field]), + ) + def invalid_explicit_field(value): + return value + + with pytest.raises(TypeError, match="unsupported Arrow type"): + + @udf( + input_schema=pa.schema( + [pa.field("value", pa.int64(), metadata={"k": "v"})] + ), + output_schema=pa.int64(), + ) + def input_field_metadata(value): + return value + + with pytest.raises(TypeError, match="unsupported Arrow type"): + + @udf( + input_schema=pa.schema([pa.field("value", pa.int64())]), + output_schema=pa.field( + "result", pa.int64(), nullable=False, metadata={"k": "v"} + ), + ) + def scalar_output_field_metadata(value): + return value + + struct_type = pa.struct([pa.field("value", pa.int64(), nullable=False)]) + with pytest.raises(TypeError, match="unsupported Arrow type"): + + @udf( + input_schema=pa.schema([pa.field("value", pa.int64())]), + output_schema=pa.field( + "result", struct_type, nullable=False, metadata={"k": "v"} + ), + ) + def struct_output_field_metadata(value): + return {"value": value} + + for input_schema, output_schema in [ + ( + pa.schema([pa.field("value", pa.int64())], metadata={"k": "v"}), + pa.int64(), + ), + ( + pa.schema([pa.field("value", pa.int64())]), + pa.schema( + [pa.field("result", pa.int64(), nullable=False)], + metadata={"k": "v"}, + ), + ), + ]: + with pytest.raises(TypeError, match="schema metadata"): + + @udf(input_schema=input_schema, output_schema=output_schema) + def schema_metadata(value): + return value + def test_local_function_catalog_operations_are_not_supported(tmp_path): db = lancedb.connect(tmp_path) From 0c4e0667bca14f00307dc21c31cec9bcf24c2ebe Mon Sep 17 00:00:00 2001 From: Lance Release Date: Sun, 30 Aug 2026 06:08:51 +0000 Subject: [PATCH 18/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.12=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 3d57dc0fe..0afd176a0 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.12" +current_version = "0.38.0-beta.13" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index 6d973b073..a3aaf566d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,7 +5402,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" dependencies = [ "ahash", "anyhow", @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" dependencies = [ "arrow-array", "arrow-buffer", @@ -5515,7 +5515,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index e19880e29..e0d7485af 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.12 + 0.38.0-beta.13 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 3864ed127..7f36371f6 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.12 + 0.38.0-beta.13 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 87e6a2bae..0efb48110 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.12 + 0.38.0-beta.13 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index c3b69424f..0cfcb4f49 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index 2b8d43c3d..1863059de 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index 6656fdd5c..c4c1bc504 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index f8f3e151f..7a491258f 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index 20efa860a..37f6eb3f0 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index 4b735a687..fde8388cf 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 35fee5ee0..7bf23c75d 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index 925211fbe..2cabfcc20 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package.json b/nodejs/package.json index a4a2286b7..2071f4633 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.12", + "version": "0.38.0-beta.13", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 0ee561977..09ff1cc50 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index 881a5017e..688123006 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.12" +version = "0.38.0-beta.13" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From fcdc3f949ee59a791b5facb91bb32eb4c26b2311 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sun, 30 Aug 2026 01:16:57 -0700 Subject: [PATCH 19/32] fix: allow multiple function bindings per table (#4090) Allow a remote Function declaration when the table already contains valid, supported Function binding metadata. Existing bindings remain fully validated, including fail-closed handling for newer or inconsistent contracts, while other schema mutations retain their existing no-binding guard. Add planner and remote request-path regression coverage for a second binding and reject dependent Function inputs, including nested paths. --- rust/lancedb/src/remote/table.rs | 87 ++++++++ rust/lancedb/src/table/computed_columns.rs | 229 +++++++++++++++++++-- 2 files changed, 302 insertions(+), 14 deletions(-) diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index 5ce886369..d372a6f56 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -7464,6 +7464,93 @@ mod tests { assert_eq!(result.version, 8); } + #[tokio::test] + async fn test_add_function_column_allows_an_existing_binding() { + let binding = crate::function::FunctionBinding::from_json(include_str!( + "../../tests/fixtures/first_class_functions/v1/remote_function_binding.json" + )) + .unwrap(); + let binding_metadata = crate::table::computed_columns::function_bindings_metadata( + std::slice::from_ref(&binding), + ) + .unwrap(); + let mut fields = vec![ + Field::new("title", DataType::Utf8, true), + Field::new("body", DataType::Utf8, true), + ]; + fields.extend(binding.outputs().iter().map(|output| { + let data_type = match output.arrow_type.as_str() { + "utf8" => DataType::Utf8, + "int64" => DataType::Int64, + other => panic!("unexpected fixture output type {other}"), + }; + Field::new(&output.output_name, data_type, true).with_metadata( + crate::table::computed_columns::function_computed_column_metadata( + binding.binding_id(), + output.output_ordinal, + &["title".into(), "body".into()], + ), + ) + })); + let schema = Schema::new_with_metadata( + fields, + HashMap::from([( + crate::table::computed_columns::FUNCTION_BINDINGS_META_KEY.to_string(), + binding_metadata, + )]), + ); + let table = + Table::new_with_handler("my_table", move |request| match request.url().path() { + "/v1/table/my_table/describe/" => http::Response::builder() + .status(200) + .body(describe_response(&schema)) + .unwrap(), + "/v1/table/my_table/add_columns/" => { + let actual: serde_json::Value = + serde_json::from_slice(request.body().unwrap().as_bytes().unwrap()) + .unwrap(); + assert_eq!( + actual["new_columns"], + serde_json::json!([ + {"name":"secondary_text","all_null":true}, + {"name":"secondary_token_count","all_null":true} + ]) + ); + http::Response::builder() + .status(200) + .body(r#"{"version":10}"#.to_string()) + .unwrap() + } + path => panic!("Unexpected path: {path}"), + }); + let application = crate::function::FunctionApplication::from_json( + r#"{ + "function":{"name":"text_features","version":"fv_01K3TEXT"}, + "inputs":[ + {"parameter":"title","kind":"column","value":{"path":"title"}}, + {"parameter":"body","kind":"column","value":{"path":"body"}} + ], + "output":{"kind":"named_struct","fields":[ + {"name":"normalized_text","arrow_type":"utf8","nullable":false}, + {"name":"token_count","arrow_type":"int64","nullable":false} + ]}, + "columns":{ + "normalized_text":"secondary_text", + "token_count":"secondary_token_count" + } + }"#, + ) + .unwrap(); + + let result = table + .add_columns() + .function(application) + .execute() + .await + .unwrap(); + assert_eq!(result.version, 10); + } + #[tokio::test] async fn test_add_fixed_size_list_function_column_declares_the_vector_type() { let table = Table::new_with_handler("my_table", |request| { diff --git a/rust/lancedb/src/table/computed_columns.rs b/rust/lancedb/src/table/computed_columns.rs index b62db3fbb..6dc3ffad5 100644 --- a/rust/lancedb/src/table/computed_columns.rs +++ b/rust/lancedb/src/table/computed_columns.rs @@ -547,7 +547,12 @@ fn ensure_known_binding_shape(value: &Value) -> Result<()> { Ok(()) } -fn resolve_field_path<'a>(schema: &'a ArrowSchema, path: &str) -> Result<&'a ArrowField> { +struct ResolvedFieldPath<'a> { + root: &'a ArrowField, + leaf: &'a ArrowField, +} + +fn resolve_field_path<'a>(schema: &'a ArrowSchema, path: &str) -> Result> { let parts = lance_core::datatypes::parse_field_path(path).map_err(|e| { invalid_function(format!("invalid Function input field path '{path}': {e}")) })?; @@ -556,22 +561,23 @@ fn resolve_field_path<'a>(schema: &'a ArrowSchema, path: &str) -> Result<&'a Arr "Function input field path cannot be empty", )); }; - let mut field = schema + let root = schema .field_with_name(root) .map_err(|_| invalid_function(format!("unknown Function input column '{path}'")))?; + let mut leaf = root; for child in children { - let DataType::Struct(fields) = field.data_type() else { + let DataType::Struct(fields) = leaf.data_type() else { return Err(invalid_function(format!( "Function input field path '{path}' traverses a non-struct field" ))); }; - field = fields + leaf = fields .iter() .find(|field| field.name() == child) .map(AsRef::as_ref) .ok_or_else(|| invalid_function(format!("unknown Function input column '{path}'")))?; } - Ok(field) + Ok(ResolvedFieldPath { root, leaf }) } fn canonical_input_arrow_type(field: &JsonArrowField) -> Result { @@ -666,7 +672,8 @@ fn parse_output_arrow_type(raw: &str) -> Result { fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding) -> Result<()> { let mut input_fields = Vec::with_capacity(binding.inputs().len()); for input in binding.inputs() { - let field = resolve_field_path(schema, &input.field_path)?; + let resolved = resolve_field_path(schema, &input.field_path)?; + let field = resolved.leaf; if field .metadata() .get(COMPUTED_COLUMN_META_KEY) @@ -721,6 +728,11 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding ))); } + let expected_inputs = binding + .inputs() + .iter() + .map(|input| input.field_path.clone()) + .collect::>(); let mut output_fields = Vec::with_capacity(binding.outputs().len()); for output in binding.outputs() { let field = schema.field_with_name(&output.output_name).map_err(|_| { @@ -747,6 +759,28 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding binding.binding_id() ))); } + let metadata = field.metadata(); + let declared_inputs = metadata + .get(INPUTS_META_KEY) + .and_then(|raw| serde_json::from_str::>(raw).ok()); + if metadata.get(COMPUTED_COLUMN_META_KEY).map(String::as_str) != Some("true") + || metadata.get(KIND_META_KEY).map(String::as_str) != Some(FUNCTION_KIND) + || metadata + .get(FUNCTION_BINDING_ID_META_KEY) + .map(String::as_str) + != Some(binding.binding_id()) + || metadata + .get(FUNCTION_OUTPUT_ORDINAL_META_KEY) + .and_then(|value| value.parse::().ok()) + != Some(output.output_ordinal) + || declared_inputs.as_deref() != Some(expected_inputs.as_slice()) + { + return Err(invalid_function(format!( + "Function output '{}' declaration metadata does not match binding '{}'", + output.output_name, + binding.binding_id() + ))); + } output_fields.push(ArrowField::new( field.name().clone(), field.data_type().clone(), @@ -778,7 +812,7 @@ pub(crate) fn plan_function_application( application: &FunctionApplication, output_name: Option<&str>, ) -> Result { - ensure_no_function_bindings_for_mutation(schema, "Function binding declaration")?; + ensure_supported_function_metadata(schema)?; if application.has_unknown_fields() { return Err(Error::NotSupported { message: "Function application contains fields from a newer contract".into(), @@ -828,8 +862,9 @@ pub(crate) fn plan_function_application( input.parameter )) })?; - let field = resolve_field_path(schema, path)?; - if field + let resolved = resolve_field_path(schema, path)?; + if resolved + .root .metadata() .get(COMPUTED_COLUMN_META_KEY) .map(String::as_str) @@ -839,6 +874,7 @@ pub(crate) fn plan_function_application( "Function input '{path}' is computed; computed-on-computed bindings are not supported" ))); } + let field = resolved.leaf; let parameter_field = ArrowField::new( input.parameter.clone(), field.data_type().clone(), @@ -2579,6 +2615,37 @@ mod tests { ]) } + fn valid_function_binding_schema( + title_nullable: bool, + body_nullable: bool, + binding: &FunctionBinding, + ) -> ArrowSchema { + let mut fields = function_binding_schema(title_nullable, body_nullable) + .fields() + .iter() + .map(|field| field.as_ref().clone()) + .collect::>(); + let inputs = binding + .inputs() + .iter() + .map(|input| input.field_path.clone()) + .collect::>(); + for output in binding.outputs() { + let index = fields + .iter() + .position(|field| field.name() == &output.output_name) + .unwrap(); + fields[index] = fields[index] + .clone() + .with_metadata(function_computed_column_metadata( + binding.binding_id(), + output.output_ordinal, + &inputs, + )); + } + ArrowSchema::new(fields) + } + #[test] fn test_non_nullable_function_inputs_can_bind_to_nullable_parameters() { let binding = FunctionBinding::from_json(include_str!( @@ -2586,7 +2653,11 @@ mod tests { )) .unwrap(); - ensure_binding_matches_schema(&function_binding_schema(false, false), &binding).unwrap(); + ensure_binding_matches_schema( + &valid_function_binding_schema(false, false, &binding), + &binding, + ) + .unwrap(); } #[test] @@ -2599,8 +2670,11 @@ mod tests { raw_binding["input_schema"]["fields"][0]["nullable"] = Value::Bool(false); let binding: FunctionBinding = serde_json::from_value(raw_binding).unwrap(); - let err = ensure_binding_matches_schema(&function_binding_schema(true, false), &binding) - .unwrap_err(); + let err = ensure_binding_matches_schema( + &valid_function_binding_schema(true, false, &binding), + &binding, + ) + .unwrap_err(); assert!( matches!(&err, Error::InvalidInput { message } if message.contains("input column 'title' is nullable") @@ -2611,6 +2685,73 @@ mod tests { ); } + #[test] + fn test_second_binding_rejects_outputs_without_reciprocal_metadata() { + let binding = FunctionBinding::from_json(include_str!( + "../../tests/fixtures/first_class_functions/v1/remote_function_binding.json" + )) + .unwrap(); + let schema = ArrowSchema::new_with_metadata( + function_binding_schema(true, true).fields().to_vec(), + HashMap::from([( + FUNCTION_BINDINGS_META_KEY.to_string(), + function_bindings_metadata(std::slice::from_ref(&binding)).unwrap(), + )]), + ); + + let err = plan_function_application( + &schema, + &named_struct_application( + r#"{"normalized_text":"secondary_text","token_count":"secondary_token_count"}"#, + ), + None, + ) + .unwrap_err(); + assert!( + matches!(&err, Error::InvalidInput { message } + if message.contains("declaration metadata") + && message.contains("fb_01K3TEXT")), + "{err:?}" + ); + } + + #[test] + fn test_persisted_nested_input_keeps_leaf_level_validation() { + let mut raw_binding: Value = serde_json::from_str(include_str!( + "../../tests/fixtures/first_class_functions/v1/remote_function_binding.json" + )) + .unwrap(); + raw_binding["inputs"][0]["field_path"] = Value::String("title.value".to_string()); + let binding: FunctionBinding = serde_json::from_value(raw_binding).unwrap(); + let title = ArrowField::new( + "title", + DataType::Struct(vec![ArrowField::new("value", DataType::Utf8, true)].into()), + true, + ) + .with_metadata(HashMap::from([ + (COMPUTED_COLUMN_META_KEY.to_string(), "true".to_string()), + (KIND_META_KEY.to_string(), SQL_KIND.to_string()), + (EXPRESSION_META_KEY.to_string(), "title".to_string()), + ])); + let mut fields = vec![title, ArrowField::new("body", DataType::Utf8, true)]; + fields.extend(binding.outputs().iter().map(|output| { + let data_type = match output.arrow_type.as_str() { + "utf8" => DataType::Utf8, + "int64" => DataType::Int64, + other => panic!("unexpected fixture output type {other}"), + }; + ArrowField::new(&output.output_name, data_type, true).with_metadata( + function_computed_column_metadata( + binding.binding_id(), + output.output_ordinal, + &["title.value".into(), "body".into()], + ), + ) + })); + + ensure_binding_matches_schema(&ArrowSchema::new(fields), &binding).unwrap(); + } + #[test] fn test_function_binding_metadata_survives_schema_round_trip() { let binding = FunctionBinding::from_json(include_str!( @@ -2659,9 +2800,36 @@ mod tests { output_ordinal: 1, } if binding_id == "fb_01K3TEXT" )); - let err = plan_function_application(&reopened, &named_struct_application("{}"), None) + let dependent_application = FunctionApplication::from_json( + r#"{ + "function":{"name":"dependent","version":"fv_dependent"}, + "inputs":[ + {"parameter":"text","kind":"column","value":{"path":"search_text"}} + ], + "output":{"kind":"scalar","arrow_type":"int64","nullable":false} + }"#, + ) + .unwrap(); + let err = plan_function_application(&reopened, &dependent_application, Some("dependent")) .unwrap_err(); - assert!(matches!(err, Error::NotSupported { .. })); + assert!( + matches!(&err, Error::InvalidInput { message } if message.contains("computed-on-computed")) + ); + let plan = plan_function_application( + &reopened, + &named_struct_application( + r#"{"normalized_text":"secondary_text","token_count":"secondary_token_count"}"#, + ), + None, + ) + .unwrap(); + assert_eq!( + plan.outputs + .iter() + .map(|output| output.output_name.as_str()) + .collect::>(), + ["secondary_text", "secondary_token_count"] + ); } #[test] @@ -2817,5 +2985,38 @@ mod tests { assert!( matches!(&err, Error::InvalidInput { message } if message.contains("computed-on-computed")) ); + + let nested_title = ArrowField::new( + "title", + DataType::Struct(vec![ArrowField::new("value", DataType::Utf8, true)].into()), + true, + ) + .with_metadata(HashMap::from([ + (COMPUTED_COLUMN_META_KEY.to_string(), "true".to_string()), + (KIND_META_KEY.to_string(), SQL_KIND.to_string()), + ( + EXPRESSION_META_KEY.to_string(), + "struct('value')".to_string(), + ), + ])); + let nested_schema = ArrowSchema::new(vec![nested_title, schema.field(1).as_ref().clone()]); + let nested_application = FunctionApplication::from_json( + r#"{ + "function":{"name":"text_features","version":"fv_exact"}, + "inputs":[ + {"parameter":"title","kind":"column","value":{"path":"title.value"}}, + {"parameter":"body","kind":"column","value":{"path":"body"}} + ], + "output":{"kind":"named_struct","fields":[ + {"name":"normalized_text","arrow_type":"utf8","nullable":false}, + {"name":"token_count","arrow_type":"int64","nullable":false} + ]} + }"#, + ) + .unwrap(); + let err = plan_function_application(&nested_schema, &nested_application, None).unwrap_err(); + assert!( + matches!(&err, Error::InvalidInput { message } if message.contains("computed-on-computed")) + ); } } From a417e46bface04b813f54458b76f7181f4b7bdb7 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Sun, 30 Aug 2026 23:10:08 +0800 Subject: [PATCH 20/32] feat(functions): support GPU resource requirements (#4085) Functions can describe their Python environment today, but cannot declare accelerator requirements. That prevents Sophon from scheduling computed-column UDF refreshes onto GPU workers from the immutable Function definition. Add `num_gpus` to Python `@udf` through a typed `FunctionResourceRequirements` value and represent resource-aware definitions with the `python_v2` runtime discriminator. CPU Functions retain their existing `python` encoding and canonical identity. The new discriminator is intentional for mixed-version safety: deployments that do not understand execution resources reject the runtime instead of accepting a new field and silently running the Function on CPU. Required resources are part of Function version identity; priority, concurrency, and retry policy remain Job concerns. The actual resource scheduling remains owned by Sophon. --- python/python/lancedb/functions.py | 60 +++++- .../tests/test_first_class_function_slice2.py | 54 +++++- rust/lancedb/src/function.rs | 177 +++++++++++++++--- 3 files changed, 260 insertions(+), 31 deletions(-) diff --git a/python/python/lancedb/functions.py b/python/python/lancedb/functions.py index 3e1b4f2d6..9be63a558 100644 --- a/python/python/lancedb/functions.py +++ b/python/python/lancedb/functions.py @@ -54,6 +54,18 @@ _UInt32 = conint(strict=True, ge=0, le=2**32 - 1) _UInt64 = conint(strict=True, ge=0, le=2**64 - 1) +def _validate_gpu_wire_marker(value: Any) -> bool: + if value is not True: + raise ValueError("runtime.gpu must be true") + return True + + +def _normalize_gpu_marker(value: bool) -> Optional[bool]: + if not isinstance(value, bool): + raise ValueError("gpu must be a boolean") + return True if value else None + + class _FrozenDict(dict): def _immutable(self, *args, **kwargs): raise TypeError("remote canonical values are immutable") @@ -239,6 +251,23 @@ class PythonRuntimeSpec(_RemoteValue): python_version: Optional[str] = None environment: Optional[PythonEnvironmentSpec] = None env: Optional[Mapping[str, str]] = None + gpu: Optional[bool] = None + + @model_validator(mode="before") + @classmethod + def _discard_unknown_runtime_payload(cls, value): + if isinstance(value, Mapping): + kind = value.get("kind") + if isinstance(kind, str) and kind not in {"python", "python_v2"}: + return {"kind": kind} + return value + + @field_validator("gpu", mode="before") + @classmethod + def _validate_gpu_marker(cls, value): + if value is None: + return None + return _validate_gpu_wire_marker(value) @model_validator(mode="after") def _validate_runtime_kind(self): @@ -247,18 +276,28 @@ class PythonRuntimeSpec(_RemoteValue): raise ValueError("python runtime requires python_version") if self.environment is None: raise ValueError("python runtime requires environment") + if self.gpu is not None: + raise ValueError("python runtime with gpu requires kind='python_v2'") + elif self.kind == "python_v2": + if self.python_version is None: + raise ValueError("python_v2 runtime requires python_version") + if self.environment is None: + raise ValueError("python_v2 runtime requires environment") + if self.gpu is None: + raise ValueError("python_v2 runtime requires gpu") else: object.__setattr__(self, "python_version", None) object.__setattr__(self, "environment", None) object.__setattr__(self, "env", None) + object.__setattr__(self, "gpu", None) return self class FunctionVersion(_RemoteValue): """An exact immutable Function version returned by Enterprise. - Scheduling resources, priority, concurrency, and retry policy belong to - the submitting Job and are not part of this identity. + The GPU execution requirement is part of this identity. CPU and memory sizing, + priority, concurrency, and retry policy belong to the execution platform. """ name: str @@ -996,6 +1035,7 @@ class UdfDefinition: pip: tuple[str, ...], env: Mapping[str, str], python_version: Optional[str], + gpu: bool = False, conda: tuple[str, ...] = (), conda_channels: tuple[str, ...] = (), ): @@ -1024,12 +1064,14 @@ class UdfDefinition: signature = _infer_signature(function, input_schema, output_schema) source = _package_source(function) digest = f"sha256:{hashlib.sha256(source).hexdigest()}" + gpu_marker = _normalize_gpu_marker(gpu) runtime = PythonRuntimeSpec( - kind="python", + kind="python_v2" if gpu_marker is not None else "python", python_version=python_version or f"{sys.version_info.major}.{sys.version_info.minor}", environment=environment_spec, env=environment, + gpu=gpu_marker, ) self._function = function self._request = FunctionRegistrationRequest( @@ -1075,6 +1117,7 @@ def udf( pip: tuple[str, ...] | list[str] = (), env: Optional[Mapping[str, str]] = None, python_version: Optional[str] = None, + gpu: bool = False, conda: tuple[str, ...] | list[str] = (), conda_channels: tuple[str, ...] | list[str] = (), ) -> Callable[[Callable[..., Any]], UdfDefinition]: ... @@ -1089,6 +1132,7 @@ def udf( pip: tuple[str, ...] | list[str] = (), env: Optional[Mapping[str, str]] = None, python_version: Optional[str] = None, + gpu: bool = False, conda: tuple[str, ...] | list[str] = (), conda_channels: tuple[str, ...] | list[str] = (), ): @@ -1121,6 +1165,10 @@ def udf( Environment variables included in the Function definition. python_version : str, optional Remote Python major/minor version. Defaults to the client version. + gpu : bool, default False + Whether every remote execution requires a GPU. The execution platform + selects one compatible GPU for each worker. The requirement is part of + the immutable Function version. The packaged artifact is a snapshot: the function source plus exactly the module-level names it references (modules as imports, importable @@ -1145,6 +1193,11 @@ def udf( ... return value * 2 >>> score(1.5) 3.0 + >>> @udf(pip=["cupy-cuda12x"], gpu=True) + ... def gpu_score(value: int) -> int: + ... return value * 2 + >>> gpu_score.registration_request.runtime.gpu + True """ def decorate(target: Callable[..., Any]) -> UdfDefinition: @@ -1156,6 +1209,7 @@ def udf( pip=tuple(pip), env={} if env is None else env, python_version=python_version, + gpu=gpu, conda=tuple(conda), conda_channels=tuple(conda_channels), ) diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index ee14043e4..cf1542b55 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -19,7 +19,7 @@ import pyarrow as pa import pytest import lancedb -from lancedb.functions import UdfDefinition, udf +from lancedb.functions import PythonRuntimeSpec, UdfDefinition, udf THRESHOLD = 20 _CACHE = None @@ -89,6 +89,58 @@ def test_udf_conda_environment(): udf(name="channels", conda_channels=["conda-forge"])(lambda value: value) +def test_udf_gpu_marker_uses_gpu_runtime(): + @udf(pip=["cupy-cuda12x"], gpu=True) + def double_on_gpu(value: int) -> int: + return value * 2 + + request = json.loads(double_on_gpu.registration_request.to_canonical_json()) + assert request["runtime"]["kind"] == "python_v2" + assert request["runtime"]["gpu"] is True + + @udf(pip=["pyarrow"]) + def cpu_function(value: int) -> int: + return value + + cpu_runtime = json.loads(cpu_function.registration_request.to_canonical_json())[ + "runtime" + ] + assert cpu_runtime["kind"] == "python" + assert "gpu" not in cpu_runtime + + def identity(value: int) -> int: + return value + + for invalid in [None, 0, 1, -1, 1.5, "", "true", "1", "H100"]: + with pytest.raises(ValueError, match="gpu must be a boolean"): + udf(name="invalid_gpu", gpu=invalid)(identity) + + base_runtime = { + "kind": "python_v2", + "python_version": "3.12", + "environment": {"kind": "pip"}, + } + runtime = PythonRuntimeSpec.model_validate({**base_runtime, "gpu": True}) + assert runtime.gpu is True + for invalid in [False, 1, 0, "", "true", "1", "H100"]: + with pytest.raises(ValueError, match="runtime.gpu must be true"): + PythonRuntimeSpec.model_validate({**base_runtime, "gpu": invalid}) + + +def test_unknown_runtime_discards_payload_before_known_field_validation(): + for payload in [ + {"kind": "python_v3", "gpu": {"model": "H100"}}, + {"kind": "python_v3", "resources": []}, + { + "kind": "python_v3", + "environment": {"kind": []}, + "python_version": 3.15, + }, + ]: + runtime = PythonRuntimeSpec.model_validate(payload) + assert runtime.to_canonical_json() == '{"kind":"python_v3"}' + + def test_udf_packages_attribute_access_and_body_imports(): @udf def word_norm(body: str) -> float: diff --git a/rust/lancedb/src/function.rs b/rust/lancedb/src/function.rs index 5366d984e..4b31a4376 100644 --- a/rust/lancedb/src/function.rs +++ b/rust/lancedb/src/function.rs @@ -207,6 +207,33 @@ pub enum PythonRuntimeSpec { environment: PythonEnvironmentSpec, env: BTreeMap, }, + /// The GPU-enabled Sophon-managed Python runtime. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use lancedb::function::{PythonEnvironmentSpec, PythonRuntimeSpec}; + /// + /// let runtime = PythonRuntimeSpec::PythonV2 { + /// python_version: "3.12".to_string(), + /// environment: PythonEnvironmentSpec { + /// kind: "pip".to_string(), + /// packages: vec!["cupy-cuda12x".to_string()], + /// channels: Vec::new(), + /// path: None, + /// modules: Vec::new(), + /// image: None, + /// }, + /// env: BTreeMap::new(), + /// }; + /// assert!(runtime.requires_gpu()); + /// ``` + PythonV2 { + python_version: String, + environment: PythonEnvironmentSpec, + env: BTreeMap, + }, /// A runtime kind introduced by a newer server. /// /// Unknown payload fields are intentionally not retained because the @@ -219,22 +246,27 @@ impl PythonRuntimeSpec { pub fn kind(&self) -> &str { match self { Self::Python { .. } => "python", + Self::PythonV2 { .. } => "python_v2", Self::Unrecognized { kind } => kind, } } - /// The Python version for the V1 runtime, or `None` for an unknown kind. + /// The Python version for a known Python runtime, or `None` for an unknown kind. pub fn python_version(&self) -> Option<&str> { match self { - Self::Python { python_version, .. } => Some(python_version), + Self::Python { python_version, .. } | Self::PythonV2 { python_version, .. } => { + Some(python_version) + } Self::Unrecognized { .. } => None, } } - /// The Python environment for the V1 runtime, or `None` for an unknown kind. + /// The Python environment for a known Python runtime, or `None` for an unknown kind. pub fn environment(&self) -> Option<&PythonEnvironmentSpec> { match self { - Self::Python { environment, .. } => Some(environment), + Self::Python { environment, .. } | Self::PythonV2 { environment, .. } => { + Some(environment) + } Self::Unrecognized { .. } => None, } } @@ -242,38 +274,73 @@ impl PythonRuntimeSpec { /// Environment variables, or `None` for an unknown kind. pub fn env(&self) -> Option<&BTreeMap> { match self { - Self::Python { env, .. } => Some(env), + Self::Python { env, .. } | Self::PythonV2 { env, .. } => Some(env), Self::Unrecognized { .. } => None, } } + + /// Whether the runtime requires a GPU selected by the execution platform. + pub fn requires_gpu(&self) -> bool { + matches!(self, Self::PythonV2 { .. }) + } } #[derive(Deserialize)] -struct PythonRuntimeWire { - kind: String, - #[serde(default)] - python_version: Option, - #[serde(default)] - environment: Option, +struct PythonRuntimeV1Wire { + python_version: String, + environment: PythonEnvironmentSpec, #[serde(default)] env: BTreeMap, + #[serde(default)] + gpu: Option, +} + +#[derive(Deserialize)] +struct PythonRuntimeV2Wire { + python_version: String, + environment: PythonEnvironmentSpec, + #[serde(default)] + env: BTreeMap, + gpu: bool, } impl<'de> Deserialize<'de> for PythonRuntimeSpec { fn deserialize>(deserializer: D) -> std::result::Result { - let wire = PythonRuntimeWire::deserialize(deserializer)?; - if wire.kind == "python" { - Ok(Self::Python { - python_version: wire - .python_version - .ok_or_else(|| de::Error::missing_field("python_version"))?, - environment: wire - .environment - .ok_or_else(|| de::Error::missing_field("environment"))?, - env: wire.env, - }) - } else { - Ok(Self::Unrecognized { kind: wire.kind }) + let value = Value::deserialize(deserializer)?; + let kind = value + .get("kind") + .ok_or_else(|| de::Error::missing_field("kind"))? + .as_str() + .ok_or_else(|| de::Error::custom("runtime.kind must be a string"))? + .to_string(); + match kind.as_str() { + "python" => { + let wire: PythonRuntimeV1Wire = + serde_json::from_value(value).map_err(de::Error::custom)?; + if wire.gpu.is_some() { + return Err(de::Error::custom( + "python runtime with gpu requires kind='python_v2'", + )); + } + Ok(Self::Python { + python_version: wire.python_version, + environment: wire.environment, + env: wire.env, + }) + } + "python_v2" => { + let wire: PythonRuntimeV2Wire = + serde_json::from_value(value).map_err(de::Error::custom)?; + if !wire.gpu { + return Err(de::Error::custom("runtime.gpu must be true")); + } + Ok(Self::PythonV2 { + python_version: wire.python_version, + environment: wire.environment, + env: wire.env, + }) + } + _ => Ok(Self::Unrecognized { kind }), } } } @@ -287,6 +354,8 @@ impl Serialize for PythonRuntimeSpec { environment: &'a PythonEnvironmentSpec, #[serde(skip_serializing_if = "BTreeMap::is_empty")] env: &'a BTreeMap, + #[serde(skip_serializing_if = "Option::is_none")] + gpu: Option, } #[derive(Serialize)] @@ -304,6 +373,19 @@ impl Serialize for PythonRuntimeSpec { python_version, environment, env, + gpu: None, + } + .serialize(serializer), + Self::PythonV2 { + python_version, + environment, + env, + } => PythonRuntimeRef { + kind: "python_v2", + python_version, + environment, + env, + gpu: Some(true), } .serialize(serializer), Self::Unrecognized { kind } => UnrecognizedRuntimeRef { kind }.serialize(serializer), @@ -313,8 +395,8 @@ impl Serialize for PythonRuntimeSpec { /// Immutable Function version returned by the Enterprise catalog. /// -/// Scheduling resources, priority, concurrency, and retry policy belong to -/// the submitting Job and are not part of this identity. +/// The GPU execution requirement is part of this identity. CPU and memory sizing, +/// priority, concurrency, and retry policy belong to the execution platform. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FunctionVersion { name: String, @@ -589,7 +671,7 @@ impl_json!(RefreshColumnResult); #[cfg(test)] mod conda_environment_tests { - use super::PythonEnvironmentSpec; + use super::{PythonEnvironmentSpec, PythonRuntimeSpec}; #[test] fn conda_channels_round_trip_and_pip_stays_bare() { @@ -608,4 +690,45 @@ mod conda_environment_tests { serde_json::from_str(r#"{"kind":"pip","packages":["numpy"]}"#).unwrap(); assert!(!serde_json::to_string(&pip).unwrap().contains("channels")); } + + #[test] + fn gpu_python_runtime_marker_round_trips_and_validates() { + let runtime: PythonRuntimeSpec = serde_json::from_str( + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"},"gpu":true}"#, + ) + .unwrap(); + assert_eq!(runtime.kind(), "python_v2"); + assert!(runtime.requires_gpu()); + assert_eq!( + super::canonical_json(&runtime).unwrap(), + r#"{"environment":{"kind":"pip"},"gpu":true,"kind":"python_v2","python_version":"3.12"}"# + ); + + for invalid in [ + r#"{"kind":"python","python_version":"3.12","environment":{"kind":"pip"},"gpu":true}"#, + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"}}"#, + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"},"gpu":1}"#, + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"},"gpu":false}"#, + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"},"gpu":"true"}"#, + r#"{"kind":"python_v2","python_version":"3.12","environment":{"kind":"pip"},"gpu":"H100"}"#, + ] { + assert!(serde_json::from_str::(invalid).is_err()); + } + } + + #[test] + fn unknown_runtime_discards_payload_before_known_field_validation() { + for encoded in [ + r#"{"kind":"python_v3","gpu":{"model":"H100"}}"#, + r#"{"kind":"python_v3","resources":[]}"#, + r#"{"kind":"python_v3","python_version":3.15,"environment":{"kind":[]}}"#, + ] { + let runtime: PythonRuntimeSpec = serde_json::from_str(encoded).unwrap(); + assert_eq!(runtime.kind(), "python_v3"); + assert_eq!( + super::canonical_json(&runtime).unwrap(), + r#"{"kind":"python_v3"}"# + ); + } + } } From 1b0fc2c465ea94ca97d322c76acb43e8319d0f2f Mon Sep 17 00:00:00 2001 From: Lance Release Date: Sun, 30 Aug 2026 15:16:00 +0000 Subject: [PATCH 21/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.13=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 0afd176a0..763df001b 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.13" +current_version = "0.38.0-beta.14" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index a3aaf566d..69c33c587 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,7 +5402,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" dependencies = [ "ahash", "anyhow", @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" dependencies = [ "arrow-array", "arrow-buffer", @@ -5515,7 +5515,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index e0d7485af..b66a8db2f 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.13 + 0.38.0-beta.14 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 7f36371f6..6b4f2ea82 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.13 + 0.38.0-beta.14 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 0efb48110..9a752b3ef 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.13 + 0.38.0-beta.14 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index 0cfcb4f49..accd9e3bf 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index 1863059de..8ae5be4ef 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index c4c1bc504..b923bf224 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 7a491258f..2e2bd92c5 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index 37f6eb3f0..e0b918b2f 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index fde8388cf..da6de64d1 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 7bf23c75d..5f7c7d6c2 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index 2cabfcc20..d2a808411 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package.json b/nodejs/package.json index 2071f4633..38536d7f5 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.13", + "version": "0.38.0-beta.14", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 09ff1cc50..972c86ede 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index 688123006..21babc252 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.13" +version = "0.38.0-beta.14" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From d5dac65a21e4fb28ea909388bf49021ac1e4f265 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Sun, 30 Aug 2026 23:33:30 -0700 Subject: [PATCH 22/32] feat: support Blob v2 UDF signatures (#4091) Make Function authoring and declaration planning treat Blob v2 as a scalar semantic type while preserving exact Blob metadata in binding schemas. Covers scalar Blob outputs, expanded named-struct outputs, and whole-result structs with Blob children. --- python/python/lancedb/functions.py | 93 ++++- .../tests/test_first_class_function_slice2.py | 118 ++++++ rust/lancedb/src/function.rs | 3 + rust/lancedb/src/table/computed_columns.rs | 378 ++++++++++++++++-- 4 files changed, 552 insertions(+), 40 deletions(-) diff --git a/python/python/lancedb/functions.py b/python/python/lancedb/functions.py index 9be63a558..bac6a762f 100644 --- a/python/python/lancedb/functions.py +++ b/python/python/lancedb/functions.py @@ -49,6 +49,8 @@ from pydantic import ( model_validator, ) +from .schema import is_blob_v2_field as _is_blob_v2_field + _Int32 = conint(strict=True, ge=-(2**31), le=2**31 - 1) _UInt32 = conint(strict=True, ge=0, le=2**32 - 1) _UInt64 = conint(strict=True, ge=0, le=2**64 - 1) @@ -518,6 +520,7 @@ class RefreshColumnResult(_RemoteValue): _FUNCTION_NAME = re.compile(r"^[A-Za-z_][A-Za-z0-9_.-]*$") +_FUNCTION_BLOB_V2_TYPE = "blob_v2" _GRAMMAR_PRIMITIVES = ( @@ -581,20 +584,90 @@ def _validate_exact_arrow_field(field: pa.Field) -> None: "unsupported Arrow type for Function signature: field names " "must not be empty" ) - if field.metadata: + if _is_blob_v2_field(field): + if not _has_supported_blob_v2_layout(field): + raise TypeError( + "unsupported Arrow type for Function signature: lance.blob.v2 " + f"requires a supported Blob storage layout, got {field}" + ) + elif field.metadata: raise TypeError( "unsupported Arrow type for Function signature: field metadata " f"is not supported, got {field}" ) +def _has_supported_blob_v2_layout(field: pa.Field) -> bool: + data_type = field.type + if isinstance(data_type, pa.ExtensionType): + data_type = data_type.storage_type + if not pa.types.is_struct(data_type): + return False + + fields = tuple(data_type) + + def matches(spec, compare_nullable) -> bool: + return len(fields) == len(spec) and all( + actual.name == name + and actual.type == expected_type + and (not check_nullable or actual.nullable == nullable) + for actual, (name, expected_type, nullable), check_nullable in zip( + fields, spec, compare_nullable + ) + ) + + logical_minimal = ( + ("data", pa.large_binary(), True), + ("uri", pa.utf8(), True), + ) + logical_full = logical_minimal + ( + ("position", pa.uint64(), True), + ("size", pa.uint64(), True), + ) + prepared = ( + ("kind", pa.uint8(), True), + ("data", pa.large_binary(), True), + ("uri", pa.utf8(), True), + ("blob_id", pa.uint32(), True), + ("blob_size", pa.uint64(), True), + ("position", pa.uint64(), True), + ) + descriptor = ( + ("kind", pa.uint8(), False), + ("position", pa.uint64(), False), + ("size", pa.uint64(), False), + ("blob_id", pa.uint32(), False), + ("blob_uri", pa.utf8(), False), + ) + return ( + matches(logical_minimal, (True, True)) + or matches(logical_full, (True, True, False, False)) + or matches(prepared, (True,) * len(prepared)) + or matches(descriptor, (False,) * len(descriptor)) + ) + + +def _canonical_arrow_field(field: pa.Field) -> str: + _validate_exact_arrow_field(field) + if _is_blob_v2_field(field): + return _FUNCTION_BLOB_V2_TYPE + return _canonical_arrow_type(field.type) + + def _exact_arrow_field(field: pa.Field) -> dict[str, Any]: _validate_exact_arrow_field(field) - return { + if _is_blob_v2_field(field): + raise TypeError( + "unsupported Arrow type for Function signature: nested Blob v2 " + "fields are not supported; declare Blob parameters or named result " + "fields directly" + ) + value = { "name": field.name, "nullable": field.nullable, "type": _exact_arrow_type(field.type), } + return value def _exact_arrow_type(data_type: pa.DataType) -> dict[str, Any]: @@ -718,7 +791,11 @@ def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutp if output.metadata: raise TypeError("Function output schema metadata is not supported") fields = tuple(output) - elif isinstance(output, pa.Field) and pa.types.is_struct(output.type): + elif ( + isinstance(output, pa.Field) + and not _is_blob_v2_field(output) + and pa.types.is_struct(output.type) + ): _validate_exact_arrow_field(output) if output.nullable: raise ValueError("Function output must be non-nullable") @@ -740,7 +817,7 @@ def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutp raise ValueError("Function output must be non-nullable") return FunctionOutput( kind="scalar", - arrow_type=_canonical_arrow_type(field.type), + arrow_type=_canonical_arrow_field(field), nullable=False, ) @@ -758,7 +835,7 @@ def _function_output(output: pa.DataType | pa.Field | pa.Schema) -> FunctionOutp fields=tuple( FunctionResultField( name=field.name, - arrow_type=_canonical_arrow_type(field.type), + arrow_type=_canonical_arrow_field(field), nullable=False, ) for field in fields @@ -792,7 +869,7 @@ def _infer_signature( inputs = tuple( FunctionParameter( name=field.name, - arrow_type=_canonical_arrow_type(field.type), + arrow_type=_canonical_arrow_field(field), nullable=field.nullable, ) for field in input_schema @@ -815,7 +892,9 @@ def _infer_signature( inputs.append( FunctionParameter( name=parameter.name, - arrow_type=_canonical_arrow_type(data_type), + arrow_type=_canonical_arrow_field( + pa.field(parameter.name, data_type, nullable=nullable) + ), nullable=nullable, ) ) diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index cf1542b55..518a62bae 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -578,6 +578,124 @@ def test_explicit_arrow_schema_is_deterministic(): assert signature.output.nullable is False +def test_blob_fields_use_the_scalar_function_semantic_type(): + @udf( + input_schema=pa.schema([lancedb.blob("image", nullable=False)]), + output_schema=lancedb.blob("result", nullable=False), + ) + def copy_blob(image): + return image + + signature = copy_blob.registration_request.signature + assert signature.inputs[0].arrow_type == "blob_v2" + assert signature.output.kind == "scalar" + assert signature.output.arrow_type == "blob_v2" + + +def test_named_struct_function_can_include_a_blob_result_field(): + @udf( + input_schema=pa.schema([lancedb.blob("image", nullable=False)]), + output_schema=pa.schema( + [ + lancedb.blob("thumbnail", nullable=False), + pa.field("width", pa.int32(), nullable=False), + ] + ), + ) + def inspect_blob(image): + return {"thumbnail": image, "width": 1} + + output = inspect_blob.registration_request.signature.output + assert output.kind == "named_struct" + assert [(field.name, field.arrow_type) for field in output.fields] == [ + ("thumbnail", "blob_v2"), + ("width", "int32"), + ] + + +def test_metadata_marked_blob_field_uses_the_semantic_type(): + extension = lancedb.blob("image", nullable=False).type + storage = ( + extension.storage_type if isinstance(extension, pa.ExtensionType) else extension + ) + metadata_blob = pa.field( + "image", + storage, + nullable=False, + metadata={"ARROW:extension:name": "lance.blob.v2"}, + ) + + @udf( + input_schema=pa.schema([metadata_blob]), + output_schema=pa.field("size", pa.int64(), nullable=False), + ) + def blob_size(image): + return len(image) + + assert blob_size.registration_request.signature.inputs[0].arrow_type == "blob_v2" + + +def test_blob_marker_rejects_invalid_storage_layout(): + malformed = pa.field( + "image", + pa.int64(), + nullable=False, + metadata={"ARROW:extension:name": "lance.blob.v2"}, + ) + + with pytest.raises(TypeError, match="requires a supported Blob storage layout"): + + @udf( + input_schema=pa.schema([malformed]), + output_schema=pa.field("size", pa.int64(), nullable=False), + ) + def blob_size(image): + return len(image) + + +def test_nested_blob_signature_field_has_a_clear_error(): + nested = pa.field( + "value", + pa.struct([lancedb.blob("image", nullable=False)]), + nullable=False, + ) + with pytest.raises(TypeError, match="nested Blob v2 fields are not supported"): + + @udf( + input_schema=pa.schema([nested]), + output_schema=pa.field("size", pa.int64(), nullable=False), + ) + def blob_size(value): + return len(value["image"]) + + +def test_nested_non_blob_extension_is_not_silently_unwrapped(): + class TestExtension(pa.ExtensionType): + def __init__(self): + super().__init__(pa.int64(), "test.function.extension") + + def __arrow_ext_serialize__(self): + return b"" + + @classmethod + def __arrow_ext_deserialize__(cls, storage_type, serialized): + return cls() + + nested = pa.field( + "value", + pa.struct([pa.field("extended", TestExtension(), nullable=False)]), + nullable=False, + ) + with pytest.raises(TypeError, match="unsupported Arrow type"): + + @udf( + input_schema=pa.schema([nested]), + output_schema=pa.field("result", pa.int64(), nullable=False), + ) + def extension_value(value): + return value["extended"] + + def test_nested_struct_output_uses_canonical_exact_json(): token = pa.struct( [ diff --git a/rust/lancedb/src/function.rs b/rust/lancedb/src/function.rs index 4b31a4376..79693c031 100644 --- a/rust/lancedb/src/function.rs +++ b/rust/lancedb/src/function.rs @@ -15,6 +15,9 @@ use serde_json::Value; use crate::{Error, Result}; +/// Semantic Function type for a Blob v2 value. +pub const FUNCTION_BLOB_V2_TYPE: &str = "blob_v2"; + fn invalid_json(error: impl std::fmt::Display) -> Error { Error::InvalidInput { message: format!("invalid remote Function JSON: {error}"), diff --git a/rust/lancedb/src/table/computed_columns.rs b/rust/lancedb/src/table/computed_columns.rs index 6dc3ffad5..77e3d0a4d 100644 --- a/rust/lancedb/src/table/computed_columns.rs +++ b/rust/lancedb/src/table/computed_columns.rs @@ -29,14 +29,16 @@ use datafusion_common::{ScalarValue, tree_node::TreeNode}; use datafusion_expr::Expr; use datafusion_physical_plan::PhysicalExpr; use lance::dataset::NewColumnTransform; -use lance_arrow::FieldExt; -use lance_core::datatypes::{BLOB_V2_DESC_FIELD, format_field_path_minimal, parse_field_path}; +use lance_arrow::{ARROW_EXT_NAME_KEY, BLOB_V2_EXT_NAME, FieldExt}; +use lance_core::datatypes::{ + BLOB_V2_DESC_FIELD, BlobV2Layout, format_field_path_minimal, parse_field_path, +}; use lance_datafusion::planner::Planner; use lance_namespace::models::{JsonArrowDataType, JsonArrowField, JsonArrowSchema}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::function::{FunctionApplication, FunctionBinding}; +use crate::function::{FUNCTION_BLOB_V2_TYPE, FunctionApplication, FunctionBinding}; use crate::utils::resolve_arrow_field_path; use crate::{Error, Result}; @@ -581,6 +583,23 @@ fn resolve_field_path<'a>(schema: &'a ArrowSchema, path: &str) -> Result Result { + let is_blob_v2 = field + .metadata + .as_ref() + .and_then(|metadata| metadata.get(ARROW_EXT_NAME_KEY)) + .map(String::as_str) + == Some(BLOB_V2_EXT_NAME); + if is_blob_v2 { + let arrow_field = lance_namespace::schema::convert_json_arrow_field(field) + .map_err(|e| invalid_function(format!("invalid Function input field: {e}")))?; + if !has_supported_blob_v2_layout(&arrow_field) { + return Err(invalid_function(format!( + "Function input '{}' has an invalid Blob v2 storage layout", + arrow_field.name() + ))); + } + return Ok(FUNCTION_BLOB_V2_TYPE.to_string()); + } if field.r#type.fields.is_none() && field.r#type.length.is_none() { Ok(field.r#type.r#type.clone()) } else { @@ -590,6 +609,14 @@ fn canonical_input_arrow_type(field: &JsonArrowField) -> Result { } } +fn has_supported_blob_v2_layout(field: &ArrowField) -> bool { + field.is_blob_v2() + && matches!( + field.data_type(), + DataType::Struct(fields) if BlobV2Layout::classify(fields).is_some() + ) +} + /// `fixed_size_list` -> (`item`, `size`); the comma must sit outside /// any nested `<...>`. fn split_fixed_size_list(raw: &str) -> Option<(&str, i32)> { @@ -669,6 +696,76 @@ fn parse_output_arrow_type(raw: &str) -> Result { Ok(data_type) } +fn function_output_field(name: &str, nullable: bool, raw: &str) -> Result { + if raw == FUNCTION_BLOB_V2_TYPE { + return lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![ + crate::blob(name, nullable), + ])) + .map_err(|e| invalid_function(format!("could not encode Blob v2 output field: {e}")))? + .fields + .into_iter() + .next() + .ok_or_else(|| invalid_function("Blob v2 output field is missing")); + } + Ok(JsonArrowField::new( + name.to_string(), + nullable, + parse_output_arrow_type(raw)?, + )) +} + +fn function_output_field_matches(expected: &ArrowField, actual: &ArrowField) -> bool { + expected.name() == actual.name() + && expected.is_nullable() == actual.is_nullable() + && if expected.is_blob_v2() { + has_supported_blob_v2_layout(expected) && has_supported_blob_v2_layout(actual) + } else { + function_output_type_matches(expected.data_type(), actual.data_type()) + } +} + +fn function_output_type_matches(expected: &DataType, actual: &DataType) -> bool { + if expected == actual { + return true; + } + match (expected, actual) { + (DataType::Struct(expected), DataType::Struct(actual)) => { + expected.len() == actual.len() + && expected + .iter() + .zip(actual) + .all(|(expected, actual)| function_output_field_matches(expected, actual)) + } + (DataType::List(expected), DataType::List(actual)) + | (DataType::LargeList(expected), DataType::LargeList(actual)) => { + function_output_field_matches(expected, actual) + } + ( + DataType::FixedSizeList(expected, expected_size), + DataType::FixedSizeList(actual, actual_size), + ) => expected_size == actual_size && function_output_field_matches(expected, actual), + (DataType::Map(expected, expected_sorted), DataType::Map(actual, actual_sorted)) => { + expected_sorted == actual_sorted && function_output_field_matches(expected, actual) + } + _ => false, + } +} + +fn function_output_type_has_blob(data_type: &DataType) -> bool { + match data_type { + DataType::Struct(fields) => fields + .iter() + .any(|field| field.is_blob_v2() || function_output_type_has_blob(field.data_type())), + DataType::List(field) + | DataType::LargeList(field) + | DataType::FixedSizeList(field, _) + | DataType::Map(field, _) => { + field.is_blob_v2() || function_output_type_has_blob(field.data_type()) + } + _ => false, + } +} + fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding) -> Result<()> { let mut input_fields = Vec::with_capacity(binding.inputs().len()); for input in binding.inputs() { @@ -749,10 +846,18 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding binding.binding_id() ))); } - let expected_type = parse_output_arrow_type(&output.arrow_type)?; - let expected_type = lance_namespace::schema::convert_json_arrow_type(&expected_type) - .map_err(|e| invalid_function(format!("invalid Function output type: {e}")))?; - if field.data_type() != &expected_type { + let (type_matches, has_semantic_blob) = if output.arrow_type == FUNCTION_BLOB_V2_TYPE { + (has_supported_blob_v2_layout(field), true) + } else { + let expected_type = parse_output_arrow_type(&output.arrow_type)?; + let expected_type = lance_namespace::schema::convert_json_arrow_type(&expected_type) + .map_err(|e| invalid_function(format!("invalid Function output type: {e}")))?; + ( + function_output_type_matches(&expected_type, field.data_type()), + function_output_type_has_blob(&expected_type), + ) + }; + if !type_matches { return Err(invalid_function(format!( "Function output '{}' type no longer matches binding '{}'", output.output_name, @@ -781,15 +886,21 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding binding.binding_id() ))); } - output_fields.push(ArrowField::new( - field.name().clone(), - field.data_type().clone(), - true, - )); - } - let output_schema = - lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(output_fields)) + if has_semantic_blob { + output_fields.push(function_output_field( + field.name(), + true, + &output.arrow_type, + )?); + } else { + let json = lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![ + ArrowField::new(field.name().clone(), field.data_type().clone(), true), + ])) .map_err(|e| invalid_function(format!("invalid Function output schema: {e}")))?; + output_fields.push(json.fields.into_iter().next().unwrap()); + } + } + let output_schema = JsonArrowSchema::new(output_fields); let output_schema = serde_json::to_value(output_schema).map_err(|e| { invalid_function(format!( "could not encode exact Function output schema: {e}" @@ -918,16 +1029,15 @@ pub(crate) fn plan_function_application( "Function logical outputs must be non-nullable during NULL assignment", )); } - let data_type = - parse_output_arrow_type(output.arrow_type.as_deref().ok_or_else(|| { - invalid_function("scalar Function output is missing its Arrow type") - })?)?; + let arrow_type = output.arrow_type.as_deref().ok_or_else(|| { + invalid_function("scalar Function output is missing its Arrow type") + })?; outputs.push(FunctionOutputTarget { result_field: WHOLE_RESULT_FIELD.to_string(), output_name: name.to_string(), output_ordinal: 0, }); - output_fields.push(JsonArrowField::new(name.to_string(), true, data_type)); + output_fields.push(function_output_field(name, true, arrow_type)?); } "named_struct" => { if output.fields.is_empty() { @@ -971,13 +1081,7 @@ pub(crate) fn plan_function_application( let fields = output .fields .iter() - .map(|field| { - Ok(JsonArrowField::new( - field.name.clone(), - false, - parse_output_arrow_type(&field.arrow_type)?, - )) - }) + .map(|field| function_output_field(&field.name, false, &field.arrow_type)) .collect::>>()?; let mut data_type = JsonArrowDataType::new("struct".to_string()); data_type.fields = Some(fields); @@ -1004,11 +1108,7 @@ pub(crate) fn plan_function_application( output_name: name.clone(), output_ordinal: ordinal as u32, }); - output_fields.push(JsonArrowField::new( - name.clone(), - true, - parse_output_arrow_type(&field.arrow_type)?, - )); + output_fields.push(function_output_field(name, true, &field.arrow_type)?); } } } @@ -1645,7 +1745,7 @@ mod tests { } use arrow_array::record_batch; - use arrow_schema::DataType; + use arrow_schema::{DataType, TimeUnit}; use futures::TryStreamExt; use lance::dataset::ColumnAlteration; @@ -2606,6 +2706,73 @@ mod tests { .unwrap() } + fn blob_application(output: &str) -> FunctionApplication { + FunctionApplication::from_json(&format!( + r#"{{ + "function":{{"name":"blob_features","version":"fv_blob"}}, + "inputs":[ + {{"parameter":"image","kind":"column","value":{{"path":"image"}}}} + ], + "output":{output} + }}"# + )) + .unwrap() + } + + fn binding_from_plan(plan: &FunctionDeclarationPlan) -> FunctionBinding { + let inputs = plan + .input_bindings + .iter() + .enumerate() + .map(|(index, input)| { + serde_json::json!({ + "parameter": input.parameter, + "field_id": index, + "field_path": input.field_path, + "arrow_type": input.arrow_type, + "nullable": input.nullable, + }) + }) + .collect::>(); + let outputs = plan + .outputs + .iter() + .zip(&plan.output_schema.fields) + .enumerate() + .map(|(index, (output, field))| { + serde_json::json!({ + "result_field": output.result_field, + "output_name": output.output_name, + "output_field_id": 100 + index, + "output_ordinal": output.output_ordinal, + "arrow_type": canonical_input_arrow_type(field).unwrap(), + "nullable": false, + }) + }) + .collect::>(); + FunctionBinding::from_json( + &serde_json::json!({ + "binding_id": "fb_blob", + "function": plan.application.function(), + "inputs": inputs, + "outputs": outputs, + "input_schema": plan.input_schema, + "output_schema": plan.output_schema, + }) + .to_string(), + ) + .unwrap() + } + + fn full_blob_field(name: &str, nullable: bool) -> ArrowField { + ArrowField::new( + name, + DataType::Struct(lance_core::datatypes::BLOB_V2_LOGICAL_FIELDS.clone()), + nullable, + ) + .with_metadata(crate::blob(name, nullable).metadata().clone()) + } + fn function_binding_schema(title_nullable: bool, body_nullable: bool) -> ArrowSchema { ArrowSchema::new(vec![ ArrowField::new("title", DataType::Utf8, title_nullable), @@ -2879,6 +3046,151 @@ mod tests { ); } + #[test] + fn test_blob_function_plans_semantic_input_and_scalar_output() { + let schema = ArrowSchema::new(vec![crate::blob("image", false)]); + let application = + blob_application(r#"{"kind":"scalar","arrow_type":"blob_v2","nullable":false}"#); + let plan = plan_function_application(&schema, &application, Some("thumbnail")).unwrap(); + + assert_eq!(plan.input_bindings[0].arrow_type, FUNCTION_BLOB_V2_TYPE); + let input_schema = + lance_namespace::schema::convert_json_arrow_schema(&plan.input_schema).unwrap(); + assert!(input_schema.field(0).is_blob_v2()); + let output_schema = + lance_namespace::schema::convert_json_arrow_schema(&plan.output_schema).unwrap(); + assert!(output_schema.field(0).is_blob_v2()); + } + + #[test] + fn test_blob_scalar_binding_accepts_full_logical_layout() { + let input = crate::blob("image", false); + let application = + blob_application(r#"{"kind":"scalar","arrow_type":"blob_v2","nullable":false}"#); + let plan = plan_function_application( + &ArrowSchema::new(vec![input.clone()]), + &application, + Some("thumbnail"), + ) + .unwrap(); + let binding = binding_from_plan(&plan); + let mut metadata = full_blob_field("thumbnail", true).metadata().clone(); + metadata.extend(function_computed_column_metadata( + binding.binding_id(), + 0, + &["image".into()], + )); + let output = full_blob_field("thumbnail", true).with_metadata(metadata); + + ensure_binding_matches_schema(&ArrowSchema::new(vec![input, output]), &binding).unwrap(); + } + + #[test] + fn test_blob_binding_rejects_marker_on_invalid_storage_layout() { + let input = crate::blob("image", false); + let application = + blob_application(r#"{"kind":"scalar","arrow_type":"blob_v2","nullable":false}"#); + let plan = plan_function_application( + &ArrowSchema::new(vec![input.clone()]), + &application, + Some("thumbnail"), + ) + .unwrap(); + let binding = binding_from_plan(&plan); + let malformed = ArrowField::new("thumbnail", DataType::Int64, true) + .with_metadata(crate::blob("thumbnail", true).metadata().clone()); + + ensure_binding_matches_schema(&ArrowSchema::new(vec![input, malformed]), &binding) + .unwrap_err(); + } + + #[test] + fn test_blob_input_rejects_marker_on_invalid_storage_layout() { + let malformed = ArrowField::new("image", DataType::Int64, false) + .with_metadata(crate::blob("image", false).metadata().clone()); + let application = + blob_application(r#"{"kind":"scalar","arrow_type":"blob_v2","nullable":false}"#); + + plan_function_application( + &ArrowSchema::new(vec![malformed]), + &application, + Some("thumbnail"), + ) + .unwrap_err(); + } + + #[test] + fn test_non_blob_input_does_not_require_json_round_trip() { + let json = lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![ + ArrowField::new("event_time", DataType::Time64(TimeUnit::Microsecond), false), + ])) + .unwrap(); + + assert_eq!( + canonical_input_arrow_type(&json.fields[0]).unwrap(), + "time64" + ); + } + + #[test] + fn test_blob_named_struct_plans_expanded_and_whole_outputs() { + let schema = ArrowSchema::new(vec![crate::blob("image", false)]); + let application = blob_application( + r#"{"kind":"named_struct","fields":[ + {"name":"thumbnail","arrow_type":"blob_v2","nullable":false}, + {"name":"width","arrow_type":"int32","nullable":false} + ]}"#, + ); + + let expanded = plan_function_application(&schema, &application, None).unwrap(); + let expanded_schema = + lance_namespace::schema::convert_json_arrow_schema(&expanded.output_schema).unwrap(); + assert!(expanded_schema.field(0).is_blob_v2()); + assert_eq!(expanded_schema.field(1).data_type(), &DataType::Int32); + + let whole = plan_function_application(&schema, &application, Some("analysis")).unwrap(); + let whole_schema = + lance_namespace::schema::convert_json_arrow_schema(&whole.output_schema).unwrap(); + let DataType::Struct(fields) = whole_schema.field(0).data_type() else { + panic!("whole Function output should be a struct"); + }; + assert!(fields[0].is_blob_v2()); + assert_eq!(fields[1].data_type(), &DataType::Int32); + } + + #[test] + fn test_blob_whole_struct_binding_accepts_full_logical_layout() { + let input = crate::blob("image", false); + let application = blob_application( + r#"{"kind":"named_struct","fields":[ + {"name":"thumbnail","arrow_type":"blob_v2","nullable":false}, + {"name":"width","arrow_type":"int32","nullable":false} + ]}"#, + ); + let plan = plan_function_application( + &ArrowSchema::new(vec![input.clone()]), + &application, + Some("analysis"), + ) + .unwrap(); + let binding = binding_from_plan(&plan); + + let output = ArrowField::new( + "analysis", + DataType::Struct(Fields::from(vec![ + full_blob_field("thumbnail", false), + ArrowField::new("width", DataType::Int32, false), + ])), + true, + ) + .with_metadata(function_computed_column_metadata( + binding.binding_id(), + 0, + &["image".into()], + )); + ensure_binding_matches_schema(&ArrowSchema::new(vec![input, output]), &binding).unwrap(); + } + #[test] fn test_function_mapping_and_sibling_collisions_fail_before_request() { let unknown = named_struct_application(r#"{"missing":"renamed"}"#); From c6dfe830d90857ef930b56aa1d0b3afeffa7772a Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Mon, 31 Aug 2026 00:32:04 -0700 Subject: [PATCH 23/32] feat(python): support large_utf8 function signatures (#4092) Teach the Python Function signature emitter to serialize PyArrow large strings as the canonical Arrow type name `large_utf8`. Extend the shared Function Arrow type fixture and explicit-schema coverage for scalar, nested, list, and large-list compositions. --- python/python/lancedb/functions.py | 1 + .../tests/test_first_class_function_slice2.py | 43 +++++++++++++++---- .../first_class_functions/v1/arrow_types.json | 38 +++++++++++++++- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/python/python/lancedb/functions.py b/python/python/lancedb/functions.py index bac6a762f..3bdd117cb 100644 --- a/python/python/lancedb/functions.py +++ b/python/python/lancedb/functions.py @@ -537,6 +537,7 @@ _GRAMMAR_PRIMITIVES = ( (pa.float32(), "float32"), (pa.float64(), "float64"), (pa.string(), "utf8"), + (pa.large_string(), "large_utf8"), (pa.binary(), "binary"), (pa.date32(), "date32"), (pa.date64(), "date64"), diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index 518a62bae..415ecbe0f 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -19,7 +19,13 @@ import pyarrow as pa import pytest import lancedb -from lancedb.functions import PythonRuntimeSpec, UdfDefinition, udf +from lancedb.functions import ( + PythonRuntimeSpec, + UdfDefinition, + _canonical_arrow_type, + _GRAMMAR_PRIMITIVES, + udf, +) THRESHOLD = 20 _CACHE = None @@ -221,8 +227,6 @@ def test_udf_resolves_module_globals_before_builtins(tmp_path): def test_canonical_arrow_type_prefers_the_compact_grammar(): - from lancedb.functions import _GRAMMAR_PRIMITIVES, _canonical_arrow_type - golden = json.loads( ( Path(__file__).parents[3] @@ -243,7 +247,6 @@ def test_canonical_arrow_type_prefers_the_compact_grammar(): for outside in [ pa.timestamp("us"), pa.decimal128(10, 2), - pa.large_string(), pa.large_binary(), pa.binary(4), pa.duration("s"), @@ -437,8 +440,6 @@ def test_udf_recursion_versus_a_rebound_module_name(tmp_path): def test_canonical_arrow_type_uses_exact_json_for_list_child_properties(): - from lancedb.functions import _canonical_arrow_type - nullable = pa.list_(pa.float32()) assert json.loads(_canonical_arrow_type(nullable)) == { "type": "list", @@ -528,6 +529,7 @@ def _arrow_type_from_golden(spec: dict) -> pa.DataType: "null": pa.null(), "bool": pa.bool_(), "utf8": pa.string(), + "large_utf8": pa.large_string(), "binary": pa.binary(), "float16": pa.float16(), "float32": pa.float32(), @@ -544,8 +546,6 @@ def test_arrow_type_grammar_matches_the_shared_golden(): / "rust/lancedb/tests/fixtures/first_class_functions/v1/arrow_types.json" ).read_text() ) - from lancedb.functions import _canonical_arrow_type - emitted = { case["arrow_type"]: _canonical_arrow_type(_arrow_type_from_golden(case["json"])) for case in golden["valid"] @@ -696,6 +696,33 @@ def test_nested_non_blob_extension_is_not_silently_unwrapped(): return value["extended"] +def test_explicit_large_utf8_schemas_use_the_canonical_function_name(): + input_schema = pa.schema([pa.field("text", pa.large_string(), nullable=True)]) + output_schema = pa.field("result", pa.large_string(), nullable=False) + + @udf(input_schema=input_schema, output_schema=output_schema) + def preserve(text): + return text + + signature = preserve.registration_request.signature + assert signature.inputs[0].arrow_type == "large_utf8" + assert signature.inputs[0].nullable is True + assert signature.output.arrow_type == "large_utf8" + assert signature.output.nullable is False + + nested = pa.struct([pa.field("text", pa.large_string(), nullable=True)]) + assert json.loads(_canonical_arrow_type(nested)) == { + "type": "struct", + "fields": [ + { + "name": "text", + "nullable": True, + "type": {"type": "large_utf8"}, + } + ], + } + + def test_nested_struct_output_uses_canonical_exact_json(): token = pa.struct( [ diff --git a/rust/lancedb/tests/fixtures/first_class_functions/v1/arrow_types.json b/rust/lancedb/tests/fixtures/first_class_functions/v1/arrow_types.json index ff26e4c08..38d17821b 100644 --- a/rust/lancedb/tests/fixtures/first_class_functions/v1/arrow_types.json +++ b/rust/lancedb/tests/fixtures/first_class_functions/v1/arrow_types.json @@ -78,6 +78,12 @@ "type": "utf8" } }, + { + "arrow_type": "large_utf8", + "json": { + "type": "large_utf8" + } + }, { "arrow_type": "binary", "json": { @@ -171,6 +177,21 @@ ] } }, + { + "arrow_type": "list", + "json": { + "type": "list", + "fields": [ + { + "name": "item", + "nullable": false, + "type": { + "type": "large_utf8" + } + } + ] + } + }, { "arrow_type": "large_list", "json": { @@ -186,6 +207,21 @@ ] } }, + { + "arrow_type": "large_list", + "json": { + "type": "large_list", + "fields": [ + { + "name": "item", + "nullable": false, + "type": { + "type": "large_utf8" + } + } + ] + } + }, { "arrow_type": "fixed_size_list", "json": { @@ -330,4 +366,4 @@ "timestamp[us]", "struct" ] -} \ No newline at end of file +} From 57b8d3bf053e839270d81d7af1927655a36f453b Mon Sep 17 00:00:00 2001 From: Lance Release Date: Mon, 31 Aug 2026 07:33:01 +0000 Subject: [PATCH 24/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.14=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 763df001b..287f49768 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.14" +current_version = "0.38.0-beta.15" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index 69c33c587..8172c1b75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5402,7 +5402,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" dependencies = [ "ahash", "anyhow", @@ -5490,7 +5490,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" dependencies = [ "arrow-array", "arrow-buffer", @@ -5515,7 +5515,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index b66a8db2f..1ac67b1c1 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.14 + 0.38.0-beta.15 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 6b4f2ea82..0b39ecc68 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.14 + 0.38.0-beta.15 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 9a752b3ef..c5481e022 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.14 + 0.38.0-beta.15 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index accd9e3bf..cb1281e85 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index 8ae5be4ef..defb684c3 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index b923bf224..b2cb057d6 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 2e2bd92c5..23d9df464 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index e0b918b2f..d12ad53fa 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index da6de64d1..42ad36ab8 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 5f7c7d6c2..6264bb5dc 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index d2a808411..dc21cd79c 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package.json b/nodejs/package.json index 38536d7f5..496faf410 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.14", + "version": "0.38.0-beta.15", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 972c86ede..92126a178 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index 21babc252..e61ebf141 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.14" +version = "0.38.0-beta.15" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From c4ee8ae670807a97258b3dc822cfd43c3c4ae074 Mon Sep 17 00:00:00 2001 From: LanceDB Robot Date: Mon, 31 Aug 2026 00:35:09 -0700 Subject: [PATCH 25/32] feat: update lance dependency to v11.0.0 (#4093) Updates the Rust workspace and Java lance-core dependency to Lance v11.0.0. Includes compatibility adjustments for the Lance 11 object-store, table-listing, and shard-manifest APIs. --- Cargo.lock | 107 +++++++----- Cargo.toml | 28 ++-- java/pom.xml | 2 +- rust/lancedb/src/database/listing.rs | 158 +++++++++++++----- rust/lancedb/src/io/object_store.rs | 10 +- .../src/io/object_store/io_tracking.rs | 10 +- rust/lancedb/src/table.rs | 16 -- rust/lancedb/src/table/query/lsm.rs | 2 +- 8 files changed, 203 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8172c1b75..3a2a4a8a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3455,8 +3455,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "fsst" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f727719438dfdb74f358a347c91ff81b6e7084a6421f34de3e473ce271f10caa" dependencies = [ "arrow-array", "rand 0.9.5", @@ -4815,8 +4816,9 @@ checksum = "e037a2e1d8d5fdbd49b16a4ea09d5d6401c1f29eca5ff29d03d3824dba16256a" [[package]] name = "lance" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be781f40c7a75f9eae2188a2f71174acb7a360dca97163db40b041d0828dea48" dependencies = [ "arc-swap", "arrow", @@ -4888,8 +4890,9 @@ dependencies = [ [[package]] name = "lance-arrow" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb97fd9875f3036d7c2561aa5b16eb87b80ccabaa4eeb5e6099b19cc662f1cd8" dependencies = [ "arrow-array", "arrow-buffer", @@ -4911,7 +4914,8 @@ dependencies = [ [[package]] name = "lance-arrow-scalar" version = "58.0.0" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771f68b04b47f3addf781116f65061808de94b05e1e9411c23c18f32d14ebe79" dependencies = [ "arrow-array", "arrow-buffer", @@ -4925,17 +4929,20 @@ dependencies = [ [[package]] name = "lance-arrow-stats" version = "58.0.0" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd47ec33c90bf29f688fd02118e37d3a5ad5c339caa3163f89e417dc0867001f" dependencies = [ "arrow-array", "arrow-schema", + "half", "lance-arrow-scalar", ] [[package]] name = "lance-bitpacking" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f45658c5b2dc9aada41b66ee44b83af3fa888b7385ae414bae951b12a9f1cd3" dependencies = [ "arrayref", "crunchy", @@ -4945,8 +4952,9 @@ dependencies = [ [[package]] name = "lance-core" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27af3df3a7d08897efccd04461df31cedf0880c4b86a055ddce48e423d27f967" dependencies = [ "arrow-array", "arrow-buffer", @@ -4983,8 +4991,9 @@ dependencies = [ [[package]] name = "lance-datafusion" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c377f837df5296e92f9fad724c83c1bef4e74d5af6e5a9312e9307e1dead8614" dependencies = [ "arrow", "arrow-array", @@ -5013,8 +5022,9 @@ dependencies = [ [[package]] name = "lance-datagen" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e1a5065fa4bc184e36e32681f10f8f4680ad8cedc9377b4c088dce8c5b8da" dependencies = [ "arrow", "arrow-array", @@ -5031,8 +5041,9 @@ dependencies = [ [[package]] name = "lance-derive" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e5e95e0fd3d74f7938f4bee623041421b323b5c61f242c8622a1f48a202527" dependencies = [ "proc-macro2", "quote", @@ -5041,8 +5052,9 @@ dependencies = [ [[package]] name = "lance-encoding" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1625653c55c65f3426e281f6e29b54c603f38a40bd4cebd707bd4f3ea48be6c5" dependencies = [ "arrow-arith", "arrow-array", @@ -5075,8 +5087,9 @@ dependencies = [ [[package]] name = "lance-file" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e13c9266b478fc98f36ee19347c4658f7a6613fed77778b1a455fe1b88552e" dependencies = [ "arrow-arith", "arrow-array", @@ -5107,8 +5120,9 @@ dependencies = [ [[package]] name = "lance-index" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0e0cb95f2c4f341c4dd04ac60f6a89ea26a6f75e09570225cbda4854c8b088e" dependencies = [ "arc-swap", "arrow", @@ -5172,8 +5186,9 @@ dependencies = [ [[package]] name = "lance-index-core" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ccd371977c1f7168da259d66ad37154f23146f093d46136bc7f79559f00f2c" dependencies = [ "arrow-array", "arrow-schema", @@ -5195,8 +5210,9 @@ dependencies = [ [[package]] name = "lance-io" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414d50997391b1ac83dc183c1612fdff88f58b959806078dc4c5e465154566de" dependencies = [ "arrow", "arrow-array", @@ -5236,8 +5252,9 @@ dependencies = [ [[package]] name = "lance-linalg" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55b152ef23a56d7ba7e4d1b2c9cf0cc79aef6ee607c115597557ea4059f41" dependencies = [ "arrow-array", "arrow-schema", @@ -5251,8 +5268,9 @@ dependencies = [ [[package]] name = "lance-namespace" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09991c13ab282b731e323619613914e08da9cc82b312f904e58c128b23f2f0e3" dependencies = [ "arrow", "async-trait", @@ -5264,8 +5282,9 @@ dependencies = [ [[package]] name = "lance-namespace-impls" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec0bc005f6bb8f120774eb4a9ba02e10463d8338167a46cbb1391d46680a174" dependencies = [ "arrow", "arrow-ipc", @@ -5318,8 +5337,9 @@ dependencies = [ [[package]] name = "lance-select" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8f676a2a1837cc85b77feb5326d3296827da964e40d67144f646563302a6ce9" dependencies = [ "arrow-array", "arrow-buffer", @@ -5333,8 +5353,9 @@ dependencies = [ [[package]] name = "lance-table" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd33054347395048b1d842dfb85a13f7801392c2da5f39425db62f00a481744b" dependencies = [ "arrow", "arrow-array", @@ -5374,8 +5395,9 @@ dependencies = [ [[package]] name = "lance-testing" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc9ad9ae24f045dfddd538a39e55a28fa7e1ca6ad9f23e20d4087eaf2bb66f7" dependencies = [ "arrow-array", "arrow-schema", @@ -5388,8 +5410,9 @@ dependencies = [ [[package]] name = "lance-tokenizer" -version = "12.0.0-beta.5" -source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bfa6f0164c8b7056150f5682ce4d415a335b59b04c479873fda04b200117d27" dependencies = [ "frostem", "icu_segmenter", diff --git a/Cargo.toml b/Cargo.toml index 033da5907..276658157 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,20 +13,20 @@ categories = ["database-implementations"] rust-version = "1.91.0" [workspace.dependencies] -lance = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-core = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-datagen = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-file = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-io = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-index = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-linalg = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-namespace = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-namespace-impls = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-table = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-testing = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-datafusion = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-encoding = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } -lance-arrow = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance = { "version" = "=11.0.0", default-features = false } +lance-core = "=11.0.0" +lance-datagen = "=11.0.0" +lance-file = "=11.0.0" +lance-io = { "version" = "=11.0.0", default-features = false } +lance-index = "=11.0.0" +lance-linalg = "=11.0.0" +lance-namespace = "=11.0.0" +lance-namespace-impls = { "version" = "=11.0.0", default-features = false } +lance-table = "=11.0.0" +lance-testing = "=11.0.0" +lance-datafusion = "=11.0.0" +lance-encoding = "=11.0.0" +lance-arrow = "=11.0.0" lancedb = { path = "rust/lancedb", default-features = false } ahash = "0.8" # Note that this one does not include pyarrow diff --git a/java/pom.xml b/java/pom.xml index c5481e022..3b0b84667 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -28,7 +28,7 @@ UTF-8 15.0.0 - 12.0.0-beta.5 + 11.0.0 false 2.30.0 1.7 diff --git a/rust/lancedb/src/database/listing.rs b/rust/lancedb/src/database/listing.rs index c22b73dd7..71e4016dd 100644 --- a/rust/lancedb/src/database/listing.rs +++ b/rust/lancedb/src/database/listing.rs @@ -13,7 +13,7 @@ use lance::dataset::{ReadParams, WriteMode, builder::DatasetBuilder}; use lance::io::{ObjectStore, ObjectStoreParams, WrappingObjectStore}; use lance_datafusion::utils::StreamingWriteSource; use lance_file::version::LanceFileVersion; -use lance_io::object_store::{ReadDirOptions, StorageOptionsAccessor, StorageOptionsProvider}; +use lance_io::object_store::{StorageOptionsAccessor, StorageOptionsProvider}; use lance_table::io::commit::commit_handler_from_url; use object_store::local::LocalFileSystem; use snafu::ResultExt; @@ -282,14 +282,11 @@ impl std::fmt::Display for ListingDatabase { const LANCE_EXTENSION: &str = "lance"; -/// The table a listed child of the database names, or `None` if the child is not a table. +/// The table a listed child directory holds, or `None` if it is not a table at all. /// /// A table is the directory `.lance`; a loose file or any other directory under the /// database prefix belongs to something else. `dir_suffix` is `.lance`, built once by the /// caller rather than per child. -/// The table a listed child directory holds, or `None` if it is not a table at all. -/// -/// Only directories are considered, so a loose object named like a table is not one. fn table_name(location: &object_store::path::Path, dir_suffix: &str) -> Option { location .filename()? @@ -297,6 +294,75 @@ fn table_name(location: &object_store::path::Path, dir_suffix: &str) -> Option, + /// Resumes after this page, or `None` when the page reached the end of the level. + page_token: Option, +} + +/// Where a listed location sits inside the database directory — the space page tokens live +/// in — or `None` if it is not a child of that directory at all. Matching both halves of the +/// prefix drops a location that merely starts with the directory's name (`dbx/y` against +/// `db/`) as well as the marker object some stores keep for the directory itself. +fn relative_key<'a>(prefix: Option<&str>, location: &'a str) -> Option<&'a str> { + let relative = match prefix { + Some(prefix) => location.strip_prefix(prefix)?, + None => location, + }; + (!relative.is_empty()).then_some(relative) +} + +/// One page of the table directories under `base_path`, one directory level deep. +/// +/// Lance 11 exposes no paginated directory listing, so the level is listed in full and paged +/// locally: table directories go into key order (a directory's key keeps its trailing `/`, +/// so a token is never a table name), the page is the smallest `limit` of them past +/// `page_token`, and the token handed back is the key of the last directory the page took — +/// so a page that took nothing ends the listing rather than resuming from a position no page +/// ever reached. Only `.lance/` directories enter the page: loose objects, other +/// directories, and a bare `.lance/` never take a page slot or name a token, which keeps a +/// page to exactly one listing of the level. Correct on every store, at the cost of that one +/// full-level listing per page. +async fn read_dir_page( + object_store: &ObjectStore, + base_path: &object_store::path::Path, + page_token: Option, + limit: Option, +) -> Result { + let listed = object_store.list_with_delimiter(Some(base_path)).await?; + let prefix = { + let base = base_path.as_ref(); + (!base.is_empty()).then(|| format!("{base}/")) + }; + let table_dir_suffix = format!(".{LANCE_EXTENSION}/"); + let mut children: Vec<(String, object_store::path::Path)> = listed + .common_prefixes + .into_iter() + .filter_map(|location| { + let key = format!("{}/", relative_key(prefix.as_deref(), location.as_ref())?); + (key.len() > table_dir_suffix.len() && key.ends_with(&table_dir_suffix)) + .then_some((key, location)) + }) + .collect(); + children.sort_unstable_by(|(left, _), (right, _)| left.cmp(right)); + if let Some(resume) = &page_token { + children.retain(|(key, _)| key > resume); + } + let total = children.len(); + children.truncate(limit.unwrap_or(total).min(total)); + let page_token = match children.last() { + Some((last, _)) if children.len() < total => Some(last.clone()), + _ => None, + }; + Ok(DirPage { + common_prefixes: children.into_iter().map(|(_, location)| location).collect(), + page_token, + }) +} + const ENGINE: &str = "engine"; const MIRRORED_STORE: &str = "mirroredStore"; @@ -982,8 +1048,7 @@ impl Database for ListingDatabase { let mut tables = Vec::new(); let mut page_token = request.page_token.filter(|token| !token.is_empty()); - // A page of nothing: the store rejects a limit of zero, and no table was handed over - // for a token to resume after. + // A page of nothing: no table was handed over for a token to resume after. if limit == Some(0) { return Ok(ListTablesResponse { context: None, @@ -992,35 +1057,21 @@ impl Database for ListingDatabase { }); } - loop { - // Ask only for what the page still has room for, so a database holding more - // than one page costs one request per page rather than one per table. - let listing = self - .object_store - .read_dir_page( - self.base_path.clone(), - ReadDirOptions { - page_token: page_token.take(), - limit: limit.map(|limit| limit - tables.len()), - }, - ) - .await?; - page_token = listing.page_token; - // Only child directories can be tables, and the store already separates them - // out, so the objects in the page are not looked at. - tables.extend( - listing - .result - .common_prefixes - .iter() - .filter_map(|location| table_name(location, &dir_suffix)), - ); - // Children that are not tables leave the page short of the limit, so keep - // going until the page is full or the database runs out. - if page_token.is_none() || limit.is_none_or(|limit| tables.len() >= limit) { - break; - } - } + // The page holds only table directories, so one call — and the one full-level + // listing behind it — fills it. + let page = read_dir_page( + &self.object_store, + &self.base_path, + page_token.take(), + limit, + ) + .await?; + page_token = page.page_token; + tables.extend( + page.common_prefixes + .iter() + .filter_map(|location| table_name(location, &dir_suffix)), + ); Ok(ListTablesResponse { context: None, @@ -1666,8 +1717,8 @@ mod tests { } /// Only directories named `.lance` are tables; loose files and other directories - /// under the database prefix are not. A page spent on them is filled from the next one, - /// so a page holding only non-tables does not read as an empty database. + /// under the database prefix are not. They never take a page slot, so even a `limit` + /// smaller than the clutter ahead of the first table returns that table. #[tokio::test] async fn test_listing_ignores_non_table_children() { let (tempdir, db) = setup_database().await; @@ -1686,6 +1737,37 @@ mod tests { assert_eq!(page.tables, vec!["real"]); } + /// The Lance 11 fallback pages locally over one full-level listing, so a bounded page + /// costs exactly one listing call — clutter ahead of the first table must not buy extra + /// round trips. + #[tokio::test] + async fn test_one_full_listing_per_public_page() { + use crate::io::object_store::io_tracking::IoStatsHolder; + use lance_io::object_store::WrappingObjectStore; + + let (tempdir, mut db) = setup_database().await; + create_tables(&db, &["real"]).await; + std::fs::write(tempdir.path().join("aaa-loose.lance"), b"not a table").unwrap(); + create_dir_all(tempdir.path().join("aaa-scratch")).unwrap(); + + let io_stats = IoStatsHolder::default(); + let mut tracked_store = (*db.object_store).clone(); + tracked_store.inner = + io_stats.wrap(&tracked_store.store_prefix, tracked_store.inner.clone()); + db.object_store = Arc::new(tracked_store); + + let page = db + .list_tables(ListTablesRequest { + limit: Some(1), + ..Default::default() + }) + .await + .unwrap(); + + assert_eq!(page.tables, vec!["real"]); + assert_eq!(io_stats.incremental_stats().read_iops, 1); + } + #[tokio::test] async fn listing_ignores_empty_table_name() { let (tempdir, db) = setup_database().await; diff --git a/rust/lancedb/src/io/object_store.rs b/rust/lancedb/src/io/object_store.rs index c4a9a4f7e..d594bd857 100644 --- a/rust/lancedb/src/io/object_store.rs +++ b/rust/lancedb/src/io/object_store.rs @@ -10,7 +10,7 @@ use lance::io::WrappingObjectStore; use object_store::{ CopyOptions, Error, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore, ObjectStoreExt, PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, - UploadPart, list::PaginatedListStore, path::Path, + UploadPart, path::Path, }; use async_trait::async_trait; @@ -187,14 +187,6 @@ impl WrappingObjectStore for MirroringObjectStoreWrapper { secondary: self.secondary.clone(), }) } - - fn wrap_paginated( - &self, - _store_prefix: &str, - original: Arc, - ) -> Option> { - Some(original) - } } // windows pathing can't be simply concatenated diff --git a/rust/lancedb/src/io/object_store/io_tracking.rs b/rust/lancedb/src/io/object_store/io_tracking.rs index 7f9750216..bd4f8f54a 100644 --- a/rust/lancedb/src/io/object_store/io_tracking.rs +++ b/rust/lancedb/src/io/object_store/io_tracking.rs @@ -12,7 +12,7 @@ use lance::io::WrappingObjectStore; use object_store::{ CopyOptions, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore, PutMultipartOptions, PutOptions, PutPayload, PutResult, RenameOptions, Result as OSResult, - UploadPart, list::PaginatedListStore, path::Path, + UploadPart, path::Path, }; #[derive(Debug, Default)] @@ -57,14 +57,6 @@ impl WrappingObjectStore for IoStatsHolder { stats: self.0.clone(), }) } - - fn wrap_paginated( - &self, - _store_prefix: &str, - original: Arc, - ) -> Option> { - Some(original) - } } impl IoTrackingStore { diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index efc705e3c..d152f3616 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -4183,14 +4183,6 @@ mod tests { parent_list_calls: self.parent_list_calls.clone(), }) } - - fn wrap_paginated( - &self, - _store_prefix: &str, - _original: Arc, - ) -> Option> { - None - } } #[tokio::test] @@ -4294,14 +4286,6 @@ mod tests { self.called.store(true, Ordering::Relaxed); original } - - fn wrap_paginated( - &self, - _store_prefix: &str, - original: Arc, - ) -> Option> { - Some(original) - } } #[tokio::test] diff --git a/rust/lancedb/src/table/query/lsm.rs b/rust/lancedb/src/table/query/lsm.rs index 86c1fe5f2..07ea7fb81 100644 --- a/rust/lancedb/src/table/query/lsm.rs +++ b/rust/lancedb/src/table/query/lsm.rs @@ -300,7 +300,7 @@ async fn build_read_context( for shard_id in shard_ids { let manifest_store = ShardManifestStore::new(store.clone(), &base_path, shard_id, scan_batch_size); - if let Some(manifest) = manifest_store.latest().await? { + if let Some(manifest) = manifest_store.read_latest().await? { snapshots.push(snapshot_from_manifest(shard_id, &manifest, &exclude)); } } From 1a9414c47c9c4e18ef00c89401d871b3363214da Mon Sep 17 00:00:00 2001 From: Lance Release Date: Mon, 31 Aug 2026 07:38:25 +0000 Subject: [PATCH 26/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.15=20?= =?UTF-8?q?=E2=86=92=200.38.0-beta.16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index 287f49768..ee9f48658 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.15" +current_version = "0.38.0-beta.16" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/docs/src/java/java.md b/docs/src/java/java.md index 1ac67b1c1..aba19ac03 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.15 + 0.38.0-beta.16 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 0b39ecc68..113ff633e 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.15 + 0.38.0-beta.16 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 3b0b84667..0974df14a 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.15 + 0.38.0-beta.16 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index cb1281e85..0f83189bc 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.15" +version = "0.38.0-beta.16" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index defb684c3..95578fad5 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index b2cb057d6..be34180bb 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 23d9df464..5a2871e25 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index d12ad53fa..01680fe3b 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index 42ad36ab8..9631a2b94 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 6264bb5dc..8ba1a0038 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index dc21cd79c..ef410b875 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package.json b/nodejs/package.json index 496faf410..3c6506d57 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.15", + "version": "0.38.0-beta.16", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 92126a178..47645b9f0 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.15" +version = "0.38.0-beta.16" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index e61ebf141..d4a97c195 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.15" +version = "0.38.0-beta.16" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From 840e1d7313df25473556e5f29404c58fa51b3a7d Mon Sep 17 00:00:00 2001 From: Lance Release Date: Mon, 31 Aug 2026 07:38:30 +0000 Subject: [PATCH 27/32] =?UTF-8?q?Bump=20version:=200.38.0-beta.16=20?= =?UTF-8?q?=E2=86=92=200.38.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.toml | 2 +- Cargo.lock | 6 +++--- docs/src/java/java.md | 2 +- java/lancedb-core/pom.xml | 2 +- java/pom.xml | 2 +- nodejs/Cargo.toml | 2 +- nodejs/npm/darwin-arm64/package.json | 2 +- nodejs/npm/linux-arm64-gnu/package.json | 2 +- nodejs/npm/linux-arm64-musl/package.json | 2 +- nodejs/npm/linux-x64-gnu/package.json | 2 +- nodejs/npm/linux-x64-musl/package.json | 2 +- nodejs/npm/win32-arm64-msvc/package.json | 2 +- nodejs/npm/win32-x64-msvc/package.json | 2 +- nodejs/package.json | 2 +- python/Cargo.toml | 2 +- rust/lancedb/Cargo.toml | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.bumpversion.toml b/.bumpversion.toml index ee9f48658..b88c14615 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,5 +1,5 @@ [tool.bumpversion] -current_version = "0.38.0-beta.16" +current_version = "0.38.0" parse = """(?x) (?P0|[1-9]\\d*)\\. (?P0|[1-9]\\d*)\\. diff --git a/Cargo.lock b/Cargo.lock index 3a2a4a8a3..7eec5a8e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5425,7 +5425,7 @@ dependencies = [ [[package]] name = "lancedb" -version = "0.38.0-beta.15" +version = "0.38.0" dependencies = [ "ahash", "anyhow", @@ -5513,7 +5513,7 @@ dependencies = [ [[package]] name = "lancedb-nodejs" -version = "0.38.0-beta.15" +version = "0.38.0" dependencies = [ "arrow-array", "arrow-buffer", @@ -5538,7 +5538,7 @@ dependencies = [ [[package]] name = "lancedb-python" -version = "0.38.0-beta.15" +version = "0.38.0" dependencies = [ "arrow", "async-trait", diff --git a/docs/src/java/java.md b/docs/src/java/java.md index aba19ac03..f3e7952f4 100644 --- a/docs/src/java/java.md +++ b/docs/src/java/java.md @@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`: com.lancedb lancedb-core - 0.38.0-beta.16 + 0.38.0 ``` diff --git a/java/lancedb-core/pom.xml b/java/lancedb-core/pom.xml index 113ff633e..6a9059119 100644 --- a/java/lancedb-core/pom.xml +++ b/java/lancedb-core/pom.xml @@ -8,7 +8,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.16 + 0.38.0-final.0 ../pom.xml diff --git a/java/pom.xml b/java/pom.xml index 0974df14a..80cef9716 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.lancedb lancedb-parent - 0.38.0-beta.16 + 0.38.0-final.0 pom ${project.artifactId} LanceDB Java SDK Parent POM diff --git a/nodejs/Cargo.toml b/nodejs/Cargo.toml index 0f83189bc..b6f006327 100644 --- a/nodejs/Cargo.toml +++ b/nodejs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "lancedb-nodejs" edition.workspace = true -version = "0.38.0-beta.16" +version = "0.38.0" publish = false license.workspace = true description.workspace = true diff --git a/nodejs/npm/darwin-arm64/package.json b/nodejs/npm/darwin-arm64/package.json index 95578fad5..68ce67487 100644 --- a/nodejs/npm/darwin-arm64/package.json +++ b/nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-darwin-arm64", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["darwin"], "cpu": ["arm64"], "main": "lancedb.darwin-arm64.node", diff --git a/nodejs/npm/linux-arm64-gnu/package.json b/nodejs/npm/linux-arm64-gnu/package.json index be34180bb..4cb228b9e 100644 --- a/nodejs/npm/linux-arm64-gnu/package.json +++ b/nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-gnu", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-gnu.node", diff --git a/nodejs/npm/linux-arm64-musl/package.json b/nodejs/npm/linux-arm64-musl/package.json index 5a2871e25..ad22eecb7 100644 --- a/nodejs/npm/linux-arm64-musl/package.json +++ b/nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-arm64-musl", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["linux"], "cpu": ["arm64"], "main": "lancedb.linux-arm64-musl.node", diff --git a/nodejs/npm/linux-x64-gnu/package.json b/nodejs/npm/linux-x64-gnu/package.json index 01680fe3b..e6a8c566b 100644 --- a/nodejs/npm/linux-x64-gnu/package.json +++ b/nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-gnu", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-gnu.node", diff --git a/nodejs/npm/linux-x64-musl/package.json b/nodejs/npm/linux-x64-musl/package.json index 9631a2b94..8c33306d3 100644 --- a/nodejs/npm/linux-x64-musl/package.json +++ b/nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-linux-x64-musl", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["linux"], "cpu": ["x64"], "main": "lancedb.linux-x64-musl.node", diff --git a/nodejs/npm/win32-arm64-msvc/package.json b/nodejs/npm/win32-arm64-msvc/package.json index 8ba1a0038..97977353e 100644 --- a/nodejs/npm/win32-arm64-msvc/package.json +++ b/nodejs/npm/win32-arm64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-arm64-msvc", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": [ "win32" ], diff --git a/nodejs/npm/win32-x64-msvc/package.json b/nodejs/npm/win32-x64-msvc/package.json index ef410b875..6a1cb0f41 100644 --- a/nodejs/npm/win32-x64-msvc/package.json +++ b/nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@lancedb/lancedb-win32-x64-msvc", - "version": "0.38.0-beta.16", + "version": "0.38.0", "os": ["win32"], "cpu": ["x64"], "main": "lancedb.win32-x64-msvc.node", diff --git a/nodejs/package.json b/nodejs/package.json index 3c6506d57..857952b5c 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -11,7 +11,7 @@ "ann" ], "private": false, - "version": "0.38.0-beta.16", + "version": "0.38.0", "main": "dist/index.js", "exports": { ".": "./dist/index.js", diff --git a/python/Cargo.toml b/python/Cargo.toml index 47645b9f0..7cbe5d418 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb-python" -version = "0.38.0-beta.16" +version = "0.38.0" publish = false edition.workspace = true description = "Python bindings for LanceDB" diff --git a/rust/lancedb/Cargo.toml b/rust/lancedb/Cargo.toml index d4a97c195..faababd08 100644 --- a/rust/lancedb/Cargo.toml +++ b/rust/lancedb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lancedb" -version = "0.38.0-beta.16" +version = "0.38.0" edition.workspace = true description = "LanceDB: A serverless, low-latency vector database for AI applications" license.workspace = true From 16753b805ae6dd9bd3055b7d61509e59120a46a7 Mon Sep 17 00:00:00 2001 From: Yang Cen Date: Mon, 31 Aug 2026 20:40:46 +0800 Subject: [PATCH 28/32] revert: restore the lance v12.0.0-beta.5 pin on main (#4095) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts #4093 (`c4ee8ae`), restoring main's lance dependency to v12.0.0-beta.5 and the v12 integration surface it carried — the `read_dir_page` paginated-listing pushdown from #3979, the v12 object-store wrapper APIs, and the shard-manifest call sites. Pinning lance v11.0.0 stable belonged on a dedicated release branch for cutting v0.38.0, not on main: main was already on the v12 beta train, so #4093 was a downgrade of the development line. The released **v0.38.0 stands as published** — this only moves main forward again. Verified on this branch: `cargo check --features remote --tests --examples` clean, all 48 `database::listing` tests pass (the restored store-pushdown pagination versions), `cargo fmt --check` and `cargo clippy --features remote --tests --examples` clean. The root `Cargo.lock` is restored by the revert and resolves as-is. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BX7w9fUMQbPAXuxe9YgQKc --- _Generated by [Claude Code](https://claude.ai/code/session_01BX7w9fUMQbPAXuxe9YgQKc)_ Co-authored-by: Claude --- Cargo.lock | 107 +++++------- Cargo.toml | 28 ++-- java/pom.xml | 2 +- rust/lancedb/src/database/listing.rs | 158 +++++------------- rust/lancedb/src/io/object_store.rs | 10 +- .../src/io/object_store/io_tracking.rs | 10 +- rust/lancedb/src/table.rs | 16 ++ rust/lancedb/src/table/query/lsm.rs | 2 +- 8 files changed, 130 insertions(+), 203 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7eec5a8e5..adf15c218 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3455,9 +3455,8 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "fsst" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f727719438dfdb74f358a347c91ff81b6e7084a6421f34de3e473ce271f10caa" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "rand 0.9.5", @@ -4816,9 +4815,8 @@ checksum = "e037a2e1d8d5fdbd49b16a4ea09d5d6401c1f29eca5ff29d03d3824dba16256a" [[package]] name = "lance" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be781f40c7a75f9eae2188a2f71174acb7a360dca97163db40b041d0828dea48" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arc-swap", "arrow", @@ -4890,9 +4888,8 @@ dependencies = [ [[package]] name = "lance-arrow" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb97fd9875f3036d7c2561aa5b16eb87b80ccabaa4eeb5e6099b19cc662f1cd8" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4914,8 +4911,7 @@ dependencies = [ [[package]] name = "lance-arrow-scalar" version = "58.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771f68b04b47f3addf781116f65061808de94b05e1e9411c23c18f32d14ebe79" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4929,20 +4925,17 @@ dependencies = [ [[package]] name = "lance-arrow-stats" version = "58.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd47ec33c90bf29f688fd02118e37d3a5ad5c339caa3163f89e417dc0867001f" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", - "half", "lance-arrow-scalar", ] [[package]] name = "lance-bitpacking" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f45658c5b2dc9aada41b66ee44b83af3fa888b7385ae414bae951b12a9f1cd3" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrayref", "crunchy", @@ -4952,9 +4945,8 @@ dependencies = [ [[package]] name = "lance-core" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27af3df3a7d08897efccd04461df31cedf0880c4b86a055ddce48e423d27f967" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -4991,9 +4983,8 @@ dependencies = [ [[package]] name = "lance-datafusion" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c377f837df5296e92f9fad724c83c1bef4e74d5af6e5a9312e9307e1dead8614" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5022,9 +5013,8 @@ dependencies = [ [[package]] name = "lance-datagen" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e1a5065fa4bc184e36e32681f10f8f4680ad8cedc9377b4c088dce8c5b8da" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5041,9 +5031,8 @@ dependencies = [ [[package]] name = "lance-derive" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e5e95e0fd3d74f7938f4bee623041421b323b5c61f242c8622a1f48a202527" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "proc-macro2", "quote", @@ -5052,9 +5041,8 @@ dependencies = [ [[package]] name = "lance-encoding" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1625653c55c65f3426e281f6e29b54c603f38a40bd4cebd707bd4f3ea48be6c5" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-arith", "arrow-array", @@ -5087,9 +5075,8 @@ dependencies = [ [[package]] name = "lance-file" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e13c9266b478fc98f36ee19347c4658f7a6613fed77778b1a455fe1b88552e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-arith", "arrow-array", @@ -5120,9 +5107,8 @@ dependencies = [ [[package]] name = "lance-index" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0e0cb95f2c4f341c4dd04ac60f6a89ea26a6f75e09570225cbda4854c8b088e" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arc-swap", "arrow", @@ -5186,9 +5172,8 @@ dependencies = [ [[package]] name = "lance-index-core" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ccd371977c1f7168da259d66ad37154f23146f093d46136bc7f79559f00f2c" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5210,9 +5195,8 @@ dependencies = [ [[package]] name = "lance-io" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414d50997391b1ac83dc183c1612fdff88f58b959806078dc4c5e465154566de" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5252,9 +5236,8 @@ dependencies = [ [[package]] name = "lance-linalg" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55b152ef23a56d7ba7e4d1b2c9cf0cc79aef6ee607c115597557ea4059f41" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5268,9 +5251,8 @@ dependencies = [ [[package]] name = "lance-namespace" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09991c13ab282b731e323619613914e08da9cc82b312f904e58c128b23f2f0e3" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "async-trait", @@ -5282,9 +5264,8 @@ dependencies = [ [[package]] name = "lance-namespace-impls" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec0bc005f6bb8f120774eb4a9ba02e10463d8338167a46cbb1391d46680a174" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-ipc", @@ -5337,9 +5318,8 @@ dependencies = [ [[package]] name = "lance-select" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f676a2a1837cc85b77feb5326d3296827da964e40d67144f646563302a6ce9" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-buffer", @@ -5353,9 +5333,8 @@ dependencies = [ [[package]] name = "lance-table" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd33054347395048b1d842dfb85a13f7801392c2da5f39425db62f00a481744b" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow", "arrow-array", @@ -5395,9 +5374,8 @@ dependencies = [ [[package]] name = "lance-testing" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc9ad9ae24f045dfddd538a39e55a28fa7e1ca6ad9f23e20d4087eaf2bb66f7" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "arrow-array", "arrow-schema", @@ -5410,9 +5388,8 @@ dependencies = [ [[package]] name = "lance-tokenizer" -version = "11.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bfa6f0164c8b7056150f5682ce4d415a335b59b04c479873fda04b200117d27" +version = "12.0.0-beta.5" +source = "git+https://github.com/lance-format/lance.git?tag=v12.0.0-beta.5#556637791d0048c2b4f1342dd84b67c8bbd65259" dependencies = [ "frostem", "icu_segmenter", diff --git a/Cargo.toml b/Cargo.toml index 276658157..033da5907 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,20 +13,20 @@ categories = ["database-implementations"] rust-version = "1.91.0" [workspace.dependencies] -lance = { "version" = "=11.0.0", default-features = false } -lance-core = "=11.0.0" -lance-datagen = "=11.0.0" -lance-file = "=11.0.0" -lance-io = { "version" = "=11.0.0", default-features = false } -lance-index = "=11.0.0" -lance-linalg = "=11.0.0" -lance-namespace = "=11.0.0" -lance-namespace-impls = { "version" = "=11.0.0", default-features = false } -lance-table = "=11.0.0" -lance-testing = "=11.0.0" -lance-datafusion = "=11.0.0" -lance-encoding = "=11.0.0" -lance-arrow = "=11.0.0" +lance = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-core = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-datagen = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-file = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-io = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-index = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-linalg = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-namespace = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-namespace-impls = { "version" = "=12.0.0-beta.5", default-features = false, "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-table = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-testing = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-datafusion = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-encoding = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } +lance-arrow = { "version" = "=12.0.0-beta.5", "tag" = "v12.0.0-beta.5", "git" = "https://github.com/lance-format/lance.git" } lancedb = { path = "rust/lancedb", default-features = false } ahash = "0.8" # Note that this one does not include pyarrow diff --git a/java/pom.xml b/java/pom.xml index 80cef9716..91ece16a1 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -28,7 +28,7 @@ UTF-8 15.0.0 - 11.0.0 + 12.0.0-beta.5 false 2.30.0 1.7 diff --git a/rust/lancedb/src/database/listing.rs b/rust/lancedb/src/database/listing.rs index 71e4016dd..c22b73dd7 100644 --- a/rust/lancedb/src/database/listing.rs +++ b/rust/lancedb/src/database/listing.rs @@ -13,7 +13,7 @@ use lance::dataset::{ReadParams, WriteMode, builder::DatasetBuilder}; use lance::io::{ObjectStore, ObjectStoreParams, WrappingObjectStore}; use lance_datafusion::utils::StreamingWriteSource; use lance_file::version::LanceFileVersion; -use lance_io::object_store::{StorageOptionsAccessor, StorageOptionsProvider}; +use lance_io::object_store::{ReadDirOptions, StorageOptionsAccessor, StorageOptionsProvider}; use lance_table::io::commit::commit_handler_from_url; use object_store::local::LocalFileSystem; use snafu::ResultExt; @@ -282,11 +282,14 @@ impl std::fmt::Display for ListingDatabase { const LANCE_EXTENSION: &str = "lance"; -/// The table a listed child directory holds, or `None` if it is not a table at all. +/// The table a listed child of the database names, or `None` if the child is not a table. /// /// A table is the directory `.lance`; a loose file or any other directory under the /// database prefix belongs to something else. `dir_suffix` is `.lance`, built once by the /// caller rather than per child. +/// The table a listed child directory holds, or `None` if it is not a table at all. +/// +/// Only directories are considered, so a loose object named like a table is not one. fn table_name(location: &object_store::path::Path, dir_suffix: &str) -> Option { location .filename()? @@ -294,75 +297,6 @@ fn table_name(location: &object_store::path::Path, dir_suffix: &str) -> Option, - /// Resumes after this page, or `None` when the page reached the end of the level. - page_token: Option, -} - -/// Where a listed location sits inside the database directory — the space page tokens live -/// in — or `None` if it is not a child of that directory at all. Matching both halves of the -/// prefix drops a location that merely starts with the directory's name (`dbx/y` against -/// `db/`) as well as the marker object some stores keep for the directory itself. -fn relative_key<'a>(prefix: Option<&str>, location: &'a str) -> Option<&'a str> { - let relative = match prefix { - Some(prefix) => location.strip_prefix(prefix)?, - None => location, - }; - (!relative.is_empty()).then_some(relative) -} - -/// One page of the table directories under `base_path`, one directory level deep. -/// -/// Lance 11 exposes no paginated directory listing, so the level is listed in full and paged -/// locally: table directories go into key order (a directory's key keeps its trailing `/`, -/// so a token is never a table name), the page is the smallest `limit` of them past -/// `page_token`, and the token handed back is the key of the last directory the page took — -/// so a page that took nothing ends the listing rather than resuming from a position no page -/// ever reached. Only `.lance/` directories enter the page: loose objects, other -/// directories, and a bare `.lance/` never take a page slot or name a token, which keeps a -/// page to exactly one listing of the level. Correct on every store, at the cost of that one -/// full-level listing per page. -async fn read_dir_page( - object_store: &ObjectStore, - base_path: &object_store::path::Path, - page_token: Option, - limit: Option, -) -> Result { - let listed = object_store.list_with_delimiter(Some(base_path)).await?; - let prefix = { - let base = base_path.as_ref(); - (!base.is_empty()).then(|| format!("{base}/")) - }; - let table_dir_suffix = format!(".{LANCE_EXTENSION}/"); - let mut children: Vec<(String, object_store::path::Path)> = listed - .common_prefixes - .into_iter() - .filter_map(|location| { - let key = format!("{}/", relative_key(prefix.as_deref(), location.as_ref())?); - (key.len() > table_dir_suffix.len() && key.ends_with(&table_dir_suffix)) - .then_some((key, location)) - }) - .collect(); - children.sort_unstable_by(|(left, _), (right, _)| left.cmp(right)); - if let Some(resume) = &page_token { - children.retain(|(key, _)| key > resume); - } - let total = children.len(); - children.truncate(limit.unwrap_or(total).min(total)); - let page_token = match children.last() { - Some((last, _)) if children.len() < total => Some(last.clone()), - _ => None, - }; - Ok(DirPage { - common_prefixes: children.into_iter().map(|(_, location)| location).collect(), - page_token, - }) -} - const ENGINE: &str = "engine"; const MIRRORED_STORE: &str = "mirroredStore"; @@ -1048,7 +982,8 @@ impl Database for ListingDatabase { let mut tables = Vec::new(); let mut page_token = request.page_token.filter(|token| !token.is_empty()); - // A page of nothing: no table was handed over for a token to resume after. + // A page of nothing: the store rejects a limit of zero, and no table was handed over + // for a token to resume after. if limit == Some(0) { return Ok(ListTablesResponse { context: None, @@ -1057,21 +992,35 @@ impl Database for ListingDatabase { }); } - // The page holds only table directories, so one call — and the one full-level - // listing behind it — fills it. - let page = read_dir_page( - &self.object_store, - &self.base_path, - page_token.take(), - limit, - ) - .await?; - page_token = page.page_token; - tables.extend( - page.common_prefixes - .iter() - .filter_map(|location| table_name(location, &dir_suffix)), - ); + loop { + // Ask only for what the page still has room for, so a database holding more + // than one page costs one request per page rather than one per table. + let listing = self + .object_store + .read_dir_page( + self.base_path.clone(), + ReadDirOptions { + page_token: page_token.take(), + limit: limit.map(|limit| limit - tables.len()), + }, + ) + .await?; + page_token = listing.page_token; + // Only child directories can be tables, and the store already separates them + // out, so the objects in the page are not looked at. + tables.extend( + listing + .result + .common_prefixes + .iter() + .filter_map(|location| table_name(location, &dir_suffix)), + ); + // Children that are not tables leave the page short of the limit, so keep + // going until the page is full or the database runs out. + if page_token.is_none() || limit.is_none_or(|limit| tables.len() >= limit) { + break; + } + } Ok(ListTablesResponse { context: None, @@ -1717,8 +1666,8 @@ mod tests { } /// Only directories named `.lance` are tables; loose files and other directories - /// under the database prefix are not. They never take a page slot, so even a `limit` - /// smaller than the clutter ahead of the first table returns that table. + /// under the database prefix are not. A page spent on them is filled from the next one, + /// so a page holding only non-tables does not read as an empty database. #[tokio::test] async fn test_listing_ignores_non_table_children() { let (tempdir, db) = setup_database().await; @@ -1737,37 +1686,6 @@ mod tests { assert_eq!(page.tables, vec!["real"]); } - /// The Lance 11 fallback pages locally over one full-level listing, so a bounded page - /// costs exactly one listing call — clutter ahead of the first table must not buy extra - /// round trips. - #[tokio::test] - async fn test_one_full_listing_per_public_page() { - use crate::io::object_store::io_tracking::IoStatsHolder; - use lance_io::object_store::WrappingObjectStore; - - let (tempdir, mut db) = setup_database().await; - create_tables(&db, &["real"]).await; - std::fs::write(tempdir.path().join("aaa-loose.lance"), b"not a table").unwrap(); - create_dir_all(tempdir.path().join("aaa-scratch")).unwrap(); - - let io_stats = IoStatsHolder::default(); - let mut tracked_store = (*db.object_store).clone(); - tracked_store.inner = - io_stats.wrap(&tracked_store.store_prefix, tracked_store.inner.clone()); - db.object_store = Arc::new(tracked_store); - - let page = db - .list_tables(ListTablesRequest { - limit: Some(1), - ..Default::default() - }) - .await - .unwrap(); - - assert_eq!(page.tables, vec!["real"]); - assert_eq!(io_stats.incremental_stats().read_iops, 1); - } - #[tokio::test] async fn listing_ignores_empty_table_name() { let (tempdir, db) = setup_database().await; diff --git a/rust/lancedb/src/io/object_store.rs b/rust/lancedb/src/io/object_store.rs index d594bd857..c4a9a4f7e 100644 --- a/rust/lancedb/src/io/object_store.rs +++ b/rust/lancedb/src/io/object_store.rs @@ -10,7 +10,7 @@ use lance::io::WrappingObjectStore; use object_store::{ CopyOptions, Error, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore, ObjectStoreExt, PutMultipartOptions, PutOptions, PutPayload, PutResult, Result, - UploadPart, path::Path, + UploadPart, list::PaginatedListStore, path::Path, }; use async_trait::async_trait; @@ -187,6 +187,14 @@ impl WrappingObjectStore for MirroringObjectStoreWrapper { secondary: self.secondary.clone(), }) } + + fn wrap_paginated( + &self, + _store_prefix: &str, + original: Arc, + ) -> Option> { + Some(original) + } } // windows pathing can't be simply concatenated diff --git a/rust/lancedb/src/io/object_store/io_tracking.rs b/rust/lancedb/src/io/object_store/io_tracking.rs index bd4f8f54a..7f9750216 100644 --- a/rust/lancedb/src/io/object_store/io_tracking.rs +++ b/rust/lancedb/src/io/object_store/io_tracking.rs @@ -12,7 +12,7 @@ use lance::io::WrappingObjectStore; use object_store::{ CopyOptions, GetOptions, GetResult, ListResult, MultipartUpload, ObjectMeta, ObjectStore, PutMultipartOptions, PutOptions, PutPayload, PutResult, RenameOptions, Result as OSResult, - UploadPart, path::Path, + UploadPart, list::PaginatedListStore, path::Path, }; #[derive(Debug, Default)] @@ -57,6 +57,14 @@ impl WrappingObjectStore for IoStatsHolder { stats: self.0.clone(), }) } + + fn wrap_paginated( + &self, + _store_prefix: &str, + original: Arc, + ) -> Option> { + Some(original) + } } impl IoTrackingStore { diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index d152f3616..efc705e3c 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -4183,6 +4183,14 @@ mod tests { parent_list_calls: self.parent_list_calls.clone(), }) } + + fn wrap_paginated( + &self, + _store_prefix: &str, + _original: Arc, + ) -> Option> { + None + } } #[tokio::test] @@ -4286,6 +4294,14 @@ mod tests { self.called.store(true, Ordering::Relaxed); original } + + fn wrap_paginated( + &self, + _store_prefix: &str, + original: Arc, + ) -> Option> { + Some(original) + } } #[tokio::test] diff --git a/rust/lancedb/src/table/query/lsm.rs b/rust/lancedb/src/table/query/lsm.rs index 07ea7fb81..86c1fe5f2 100644 --- a/rust/lancedb/src/table/query/lsm.rs +++ b/rust/lancedb/src/table/query/lsm.rs @@ -300,7 +300,7 @@ async fn build_read_context( for shard_id in shard_ids { let manifest_store = ShardManifestStore::new(store.clone(), &base_path, shard_id, scan_batch_size); - if let Some(manifest) = manifest_store.read_latest().await? { + if let Some(manifest) = manifest_store.latest().await? { snapshots.push(snapshot_from_manifest(shard_id, &manifest, &exclude)); } } From c196d033e932591bb696772ebb3490cde49011b7 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Mon, 31 Aug 2026 22:17:11 +0800 Subject: [PATCH 29/32] feat: add drop_function client APIs (#4097) --- python/python/lancedb/_lancedb.pyi | 1 + python/python/lancedb/db.py | 18 ++++++++ python/python/lancedb/remote/db.py | 4 ++ .../tests/test_first_class_function_slice2.py | 45 +++++++++++++++++++ python/src/connection.rs | 11 +++++ rust/lancedb/src/connection.rs | 15 +++++++ rust/lancedb/src/database.rs | 4 ++ rust/lancedb/src/remote/db.rs | 38 ++++++++++++++++ .../tests/first_class_function_slice2.rs | 6 ++- 9 files changed, 141 insertions(+), 1 deletion(-) diff --git a/python/python/lancedb/_lancedb.pyi b/python/python/lancedb/_lancedb.pyi index 7d7ca7f2a..05ece3043 100644 --- a/python/python/lancedb/_lancedb.pyi +++ b/python/python/lancedb/_lancedb.pyi @@ -150,6 +150,7 @@ class Connection(object): def job(self, job_id: str) -> Job: ... async def create_function_async(self, request_json: str) -> Job: ... async def get_function(self, name: str, version: str) -> str: ... + async def drop_function(self, name: str, version: str) -> bool: ... async def list_jobs(self) -> List[JobInfo]: ... async def get_job(self, job_id: str) -> Optional[JobDescription]: ... async def cancel_job(self, job_id: str) -> bool: ... diff --git a/python/python/lancedb/db.py b/python/python/lancedb/db.py index 51b8d9993..ecaae42f8 100644 --- a/python/python/lancedb/db.py +++ b/python/python/lancedb/db.py @@ -712,6 +712,16 @@ class DBConnection(EnforceOverrides): "Function catalog operations are not supported for this connection type" ) + def drop_function(self, name: str, *, version: str) -> bool: + """Drop one exact immutable Function version from the remote catalog. + + Returns True when the version changed to Dropped and False for an + idempotent replay. Local connections raise NotImplementedError. + """ + raise NotImplementedError( + "Function catalog operations are not supported for this connection type" + ) + def job(self, job_id: str) -> Job: """A [Job][lancedb.job.Job] handle for a server-side job by id. @@ -1413,6 +1423,10 @@ class LanceDBConnection(DBConnection): def get_function(self, name: str, *, version: str) -> FunctionVersion: return LOOP.run(self._conn.get_function(name, version=version)) + @override + def drop_function(self, name: str, *, version: str) -> bool: + return LOOP.run(self._conn.drop_function(name, version=version)) + @override def list_jobs(self) -> List[JobInfo]: """List server-side jobs across the database's tables.""" @@ -2243,6 +2257,10 @@ class AsyncConnection(object): """Open one exact immutable Function version from the remote catalog.""" return FunctionVersion.from_json(await self._inner.get_function(name, version)) + async def drop_function(self, name: str, *, version: str) -> bool: + """Drop one exact immutable Function version from the remote catalog.""" + return await self._inner.drop_function(name, version) + async def list_jobs(self) -> List[JobInfo]: """List server-side jobs across the database's tables.""" return await self._inner.list_jobs() diff --git a/python/python/lancedb/remote/db.py b/python/python/lancedb/remote/db.py index b228cfb5b..27e21d200 100644 --- a/python/python/lancedb/remote/db.py +++ b/python/python/lancedb/remote/db.py @@ -749,6 +749,10 @@ class RemoteDBConnection(DBConnection): def get_function(self, name: str, *, version: str) -> FunctionVersion: return LOOP.run(self._conn.get_function(name, version=version)) + @override + def drop_function(self, name: str, *, version: str) -> bool: + return LOOP.run(self._conn.drop_function(name, version=version)) + @override def list_jobs(self) -> List["JobInfo"]: """List server-side jobs across the database's tables.""" diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index 415ecbe0f..bab78316c 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -941,6 +941,8 @@ def test_local_function_catalog_operations_are_not_supported(tmp_path): db.create_function_async(normalize_score) with pytest.raises(NotImplementedError, match=message): db.get_function("normalize_score", version="fv_exact") + with pytest.raises(NotImplementedError, match=message): + db.drop_function("normalize_score", version="fv_exact") @contextlib.contextmanager @@ -986,6 +988,12 @@ def _mock_remote_function_catalog(): "version": "fv_exact", } response = state["version"] + elif self.path == "/v1/functions/drop": + assert body == { + "name": "normalize_score", + "version": "fv_exact", + } + response = {"dropped": True} else: status = 404 response = {"error": "not found"} @@ -1044,3 +1052,40 @@ def test_blocking_remote_registration_returns_function_version(): "/v1/functions/create", "/v1/jobs/describe", ] + + +def test_remote_drop_function_sends_exact_version(): + with _mock_remote_function_catalog() as (host, state): + db = lancedb.connect( + "db://dev", + api_key="fake", + host_override=host, + client_config={"retry_config": {"retries": 0}}, + ) + assert db.drop_function("normalize_score", version="fv_exact") is True + + assert state["requests"] == [ + ( + "/v1/functions/drop", + {"name": "normalize_score", "version": "fv_exact"}, + ) + ] + + +@pytest.mark.asyncio +async def test_async_remote_drop_function_sends_exact_version(): + with _mock_remote_function_catalog() as (host, state): + db = await lancedb.connect_async( + "db://dev", + api_key="fake", + host_override=host, + client_config={"retry_config": {"retries": 0}}, + ) + assert await db.drop_function("normalize_score", version="fv_exact") is True + + assert state["requests"] == [ + ( + "/v1/functions/drop", + {"name": "normalize_score", "version": "fv_exact"}, + ) + ] diff --git a/python/src/connection.rs b/python/src/connection.rs index 902489f4f..fc835f805 100644 --- a/python/src/connection.rs +++ b/python/src/connection.rs @@ -629,6 +629,17 @@ impl Connection { }) } + pub fn drop_function( + self_: PyRef<'_, Self>, + name: String, + version: String, + ) -> PyResult> { + let inner = self_.get_inner()?.clone(); + future_into_py(self_.py(), async move { + inner.drop_function(name, version).await.infer_error() + }) + } + pub fn list_jobs(self_: PyRef<'_, Self>) -> PyResult> { let inner = self_.get_inner()?.clone(); future_into_py(self_.py(), async move { diff --git a/rust/lancedb/src/connection.rs b/rust/lancedb/src/connection.rs index 5f66d9dee..943ad51b7 100644 --- a/rust/lancedb/src/connection.rs +++ b/rust/lancedb/src/connection.rs @@ -523,6 +523,21 @@ impl Connection { .await } + /// Drop one exact immutable Function version from the remote catalog. + /// + /// Returns `true` when the server appended a Dropped transition and + /// `false` for an idempotent replay. Local databases return + /// [`Error::NotSupported`]. + pub async fn drop_function( + &self, + name: impl AsRef, + version: impl AsRef, + ) -> Result { + self.internal + .drop_function(name.as_ref(), version.as_ref()) + .await + } + /// Rename a table in the database. /// /// This is only supported in LanceDB Cloud. diff --git a/rust/lancedb/src/database.rs b/rust/lancedb/src/database.rs index 6c4537972..775b0b579 100644 --- a/rust/lancedb/src/database.rs +++ b/rust/lancedb/src/database.rs @@ -307,6 +307,10 @@ pub trait Database: ) -> Result { function_catalog_not_supported() } + /// Drop one exact immutable Function version from the remote catalog. + async fn drop_function(&self, _name: &str, _version: &str) -> Result { + function_catalog_not_supported() + } /// A [`crate::job::Job`] handle for a server-side job by id, suitable for /// waiting on or cancelling the job. The handle is constructed without a /// server round trip; an unknown id surfaces when the handle is used. diff --git a/rust/lancedb/src/remote/db.rs b/rust/lancedb/src/remote/db.rs index da9a4b09b..39b258a63 100644 --- a/rust/lancedb/src/remote/db.rs +++ b/rust/lancedb/src/remote/db.rs @@ -533,6 +533,11 @@ struct RemoteListJobsResponse { page_token: Option, } +#[derive(serde::Deserialize)] +struct RemoteDropFunctionResponse { + dropped: bool, +} + /// Bound on `list_jobs` page walking; a warning is logged when the listing /// is truncated at this many pages. const MAX_LIST_JOBS_PAGES: usize = 100; @@ -583,6 +588,20 @@ impl Database for RemoteDatabase { response.json().await.err_to_http(request_id) } + async fn drop_function(&self, name: &str, version: &str) -> Result { + let req = self + .client + .post("/v1/functions/drop") + .json(&serde_json::json!({ + "name": name, + "version": version, + })); + let (request_id, response) = self.client.send(req).await?; + let response = self.client.check_response(&request_id, response).await?; + let response: RemoteDropFunctionResponse = response.json().await.err_to_http(request_id)?; + Ok(response.dropped) + } + fn job(&self, job_id: &str) -> Result { Ok(crate::job::Job::new(Box::new(super::job::RemoteJob::new( self.client.clone(), @@ -2689,6 +2708,25 @@ mod tests { assert_eq!(version.version(), "fv_01K3EXACT"); } + #[tokio::test] + async fn test_drop_function_sends_exact_version_and_decodes_replay() { + let conn = Connection::new_with_handler(|request| { + assert_eq!(request.method(), &reqwest::Method::POST); + assert_eq!(request.url().path(), "/v1/functions/drop"); + let body: serde_json::Value = + serde_json::from_slice(request.body().unwrap().as_bytes().unwrap()).unwrap(); + assert_eq!( + body, + serde_json::json!({"name": "embed", "version": "fv_01K3EXACT"}) + ); + http::Response::builder() + .status(200) + .body(r#"{"dropped":false}"#) + .unwrap() + }); + assert!(!conn.drop_function("embed", "fv_01K3EXACT").await.unwrap()); + } + #[tokio::test] async fn test_conn_job_waits_to_done() { let polls = Arc::new(AtomicUsize::new(0)); diff --git a/rust/lancedb/tests/first_class_function_slice2.rs b/rust/lancedb/tests/first_class_function_slice2.rs index 93252dde4..6d046d9d1 100644 --- a/rust/lancedb/tests/first_class_function_slice2.rs +++ b/rust/lancedb/tests/first_class_function_slice2.rs @@ -45,7 +45,11 @@ async fn local_function_catalog_operations_return_stable_not_supported() { .get_function("normalize_score", "fv_exact") .await .unwrap_err(); - for error in [create_error, lookup_error] { + let drop_error = connection + .drop_function("normalize_score", "fv_exact") + .await + .unwrap_err(); + for error in [create_error, lookup_error, drop_error] { assert!(matches!( error, Error::NotSupported { message } From c8fd3e97d1bb35ad704ea442def79bf9188e8cbf Mon Sep 17 00:00:00 2001 From: Yang Cen Date: Mon, 31 Aug 2026 22:34:34 +0800 Subject: [PATCH 30/32] test(python): cover stable main udf registration identity (#4094) ## Other changes ### What changed? - Add a subprocess regression harness for an ordinary `@udf` function defined in `__main__`. - Verify the full registration request, artifact digest, and Function signature stay identical across independent Python processes and renamed/moved script paths. - Verify body, referenced-global, and annotation changes still produce distinct artifact identities, with annotation changes also producing a distinct Function signature. ### Why is the change needed? [ENT-2441](https://linear.app/lancedb/issue/ENT-2441/make-sure-function-defined-in-main-gets-stable-signature) tracks the stability guarantee. Investigation on the exact `840e1d73` main base found that LanceDB already packages canonical source instead of cloudpickle bytes, so the unchanged `__main__` function is stable and no production-code fix is needed. This change closes the missing regression-test coverage. [GEN-950](https://linear.app/lancedb/issue/GEN-950/class-based-udfs-defined-in-main-get-a-new-auto-version-on-every-run) remains a separate Geneva checkpoint-version issue for class-based callables. LanceDB's Function API continues to accept synchronous Python functions only. ## Validation - `cd python && uv run --extra tests pytest python/tests/test_first_class_function_slice2.py -q` (`40 passed`) - `uv run --project python --extra dev ruff format .` - `uv run --project python --extra dev ruff check .` (`All checks passed!`) --- .../tests/test_first_class_function_slice2.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/python/python/tests/test_first_class_function_slice2.py b/python/python/tests/test_first_class_function_slice2.py index bab78316c..57b08e18d 100644 --- a/python/python/tests/test_first_class_function_slice2.py +++ b/python/python/tests/test_first_class_function_slice2.py @@ -12,6 +12,8 @@ from datetime import date import http.server import json from pathlib import Path +import subprocess +import sys import threading from typing import Optional @@ -67,6 +69,80 @@ def test_scalar_udf_matches_shared_registration_golden_and_remains_callable(): } +def _main_udf_source( + *, threshold: int = 20, input_annotation: str = "int", comparison: str = ">=" +) -> str: + return ( + "from __future__ import annotations\n" + "from lancedb.functions import udf\n" + f"THRESHOLD = {threshold}\n" + "\n" + "@udf\n" + f"def label(value: {input_annotation}) -> str:\n" + f" return 'big' if value {comparison} THRESHOLD else 'small'\n" + "\n" + "assert label.__module__ == '__main__'\n" + "print(label.registration_request.to_canonical_json())\n" + ) + + +def _run_main_udf(path: Path, source: str) -> dict: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(source) + result = subprocess.run( + [sys.executable, str(path)], + check=True, + capture_output=True, + text=True, + ) + return json.loads(result.stdout) + + +def test_main_udf_registration_identity_is_stable_across_processes_and_paths( + tmp_path, +): + source = _main_udf_source() + original_path = tmp_path / "original" / "job.py" + moved_path = tmp_path / "moved" / "renamed_job.py" + + original_runs = [_run_main_udf(original_path, source) for _ in range(2)] + moved_run = _run_main_udf(moved_path, source) + + assert len({run["artifact"]["digest"] for run in [*original_runs, moved_run]}) == 1 + assert all( + run["signature"] == original_runs[0]["signature"] + for run in [original_runs[1], moved_run] + ) + assert original_runs[0] == original_runs[1] == moved_run + + body_change = _run_main_udf( + tmp_path / "changes" / "body.py", _main_udf_source(comparison=">") + ) + global_change = _run_main_udf( + tmp_path / "changes" / "global.py", _main_udf_source(threshold=21) + ) + annotation_change = _run_main_udf( + tmp_path / "changes" / "annotation.py", + _main_udf_source(input_annotation="float"), + ) + + baseline = original_runs[0] + assert baseline["signature"] == body_change["signature"] + assert baseline["signature"] == global_change["signature"] + assert baseline["signature"] != annotation_change["signature"] + assert ( + len( + { + baseline["artifact"]["digest"], + body_change["artifact"]["digest"], + global_change["artifact"]["digest"], + annotation_change["artifact"]["digest"], + } + ) + == 4 + ) + + def _run_packaged(definition, *args): """Execute the shipped artifact in a fresh namespace, as a worker would.""" source = base64.b64decode(definition.registration_request.artifact.content.data) From e773d1e093a08b775b9ff3ee5386fe310f378443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:40:11 -0700 Subject: [PATCH 31/32] build(deps): bump the rust-minor-patch group across 1 directory with 9 updates (#4084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the rust-minor-patch group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [async-trait](https://github.com/dtolnay/async-trait) | `0.1.91` | `0.1.92` | | [log](https://github.com/rust-lang/log) | `0.4.33` | `0.4.34` | | [moka](https://github.com/moka-rs/moka) | `0.12.15` | `0.12.16` | | [uuid](https://github.com/uuid-rs/uuid) | `1.24.0` | `1.26.0` | | [serde_with](https://github.com/jonasbb/serde_with) | `3.21.0` | `3.22.0` | | [roaring](https://github.com/RoaringBitmap/roaring-rs) | `0.11.4` | `0.11.5` | | [napi](https://github.com/napi-rs/napi-rs) | `3.11.0` | `3.12.0` | | [napi-derive](https://github.com/napi-rs/napi-rs) | `3.6.1` | `3.6.3` | | [napi-build](https://github.com/napi-rs/napi-rs) | `2.4.0` | `2.4.1` | Updates `async-trait` from 0.1.91 to 0.1.92
Release notes

Sourced from async-trait's releases.

0.1.92

  • Resolve double_must_use clippy lint in generated code (#303)
Commits

Updates `log` from 0.4.33 to 0.4.34
Release notes

Sourced from log's releases.

0.4.34

What's Changed

New Contributors

Full Changelog: https://github.com/rust-lang/log/compare/0.4.33...0.4.34

Changelog

Sourced from log's changelog.

[0.4.34] - 2026-08-22

What's Changed

New Contributors

Full Changelog: https://github.com/rust-lang/log/compare/0.4.33...0.4.34

Commits

Updates `moka` from 0.12.15 to 0.12.16
Release notes

Sourced from moka's releases.

v0.12.16

Version 0.12.16

Fixed

  • Fixed a bug where cache eviction could stall permanently when the cache was configured with the non-default LRU eviction policy (EvictionPolicy::lru()) by a race between insert and remove operations on the same key (#592gh-pull-0592 by @​kim-jhyeon, reported in #590gh-issue-0590):
    • This bug was introduced in v0.12.0 and affected sync::Cache, sync::SegmentedCache and future::Cache.
    • A race between applying a write recording for an entry and concurrently removing that entry from the internal concurrent hash table could leave an orphaned node at the front of the LRU queue. Once present, no entry was ever evicted again and the cache grew unboundedly past max_capacity.
    • The same race also affected the default TinyLFU eviction policy, but with a milder symptom: each occurrence permanently leaked one phantom entry slot, causing entry_count and weighted_size to over-report and the usable capacity to shrink by one entry per occurrence. Fixed by the same change.

Changed

  • Worked around a ThreadSanitizer false positive (#602gh-pull-0602):
    • Replaced the standalone fence(Acquire) in the internal MiniArc's drop path with an Acquire load of the reference count, so that downstream projects can now run ThreadSanitizer on code using Moka without hitting this false positive.
    • std::sync::Arc has a similar workaround.
  • Raised the minimum version of the crossbeam-epoch crate from v0.9.18 to v0.9.20 to avoid the following advisory (#603gh-pull-0603):
    • [RUSTSEC-2026-0204] crossbeam-epoch: invalid pointer dereference in fmt::Pointer for Atomic and Shared
    • Moka is not affected by this advisory because it never formats these pointer types. However, raising the minimum version prevents downstream lockfiles from resolving to an affected crossbeam-epoch version via Moka.
Changelog

Sourced from moka's changelog.

Version 0.12.16

Fixed

  • Fixed a bug where cache eviction could stall permanently when the cache was configured with the non-default LRU eviction policy (EvictionPolicy::lru()) by a race between insert and remove operations on the same key (#592[gh-pull-0592] by [@​kim-jhyeon][gh-kim-jhyeon], reported in #590[gh-issue-0590]):
    • This bug was introduced in v0.12.0 and affected sync::Cache, sync::SegmentedCache and future::Cache.
    • A race between applying a write recording for an entry and concurrently removing that entry from the internal concurrent hash table could leave an orphaned node at the front of the LRU queue. Once present, no entry was ever evicted again and the cache grew unboundedly past max_capacity.
    • The same race also affected the default TinyLFU eviction policy, but with a milder symptom: each occurrence permanently leaked one phantom entry slot, causing entry_count and weighted_size to over-report and the usable capacity to shrink by one entry per occurrence. Fixed by the same change.

Changed

  • Worked around a ThreadSanitizer false positive (#602[gh-pull-0602]):
    • Replaced the standalone fence(Acquire) in the internal MiniArc's drop path with an Acquire load of the reference count, so that downstream projects can now run ThreadSanitizer on code using Moka without hitting this false positive.
    • std::sync::Arc has a similar workaround.
  • Raised the minimum version of the crossbeam-epoch crate from v0.9.18 to v0.9.20 to avoid the following advisory (#603[gh-pull-0603]):
    • [RUSTSEC-2026-0204] crossbeam-epoch: invalid pointer dereference in fmt::Pointer for Atomic and Shared
    • Moka is not affected by this advisory because it never formats these pointer types. However, raising the minimum version prevents downstream lockfiles from resolving to an affected crossbeam-epoch version via Moka.
Commits
  • a616ec1 Merge pull request #604 from moka-rs/chore/bump-v0.12.16
  • 3b140a6 Bump the version to v0.12.16
  • 51b802d Merge pull request #603 from moka-rs/bump-crossbeam-epoch-floor
  • 4f90716 Raise the minimum crossbeam-epoch version to 0.9.20
  • 08d0e04 Merge pull request #602 from moka-rs/gh600-tsan-workaround
  • 14447a7 Restructure the v0.12.16 TSan workaround CHANGELOG entry
  • 7b14c37 Avoid a TSan false positive by replacing the fence in MiniArc::drop
  • 05b37c6 Merge pull request #599 from moka-rs/gh590-deterministic-tests
  • fc31858 Replace private doc references in gh590 test comments
  • 5743592 Improve the v0.12.16 CHANGELOG entry
  • Additional commits viewable in compare view

Updates `uuid` from 1.24.0 to 1.26.0
Release notes

Sourced from uuid's releases.

v1.26.0

What's Changed

Full Changelog: https://github.com/uuid-rs/uuid/compare/1.25.0...v1.26.0

1.25.0

What's Changed

New Contributors

Full Changelog: https://github.com/uuid-rs/uuid/compare/v1.24.1...1.25.0

v1.24.1

What's Changed

New Contributors

Full Changelog: https://github.com/uuid-rs/uuid/compare/v1.24.0...v1.24.1

Commits
  • cdc96a8 Merge pull request #905 from uuid-rs/cargo/v1.26.0
  • 34e4f49 don't test macros under miri
  • d9e7242 update nightly used for miri
  • ec16819 prepare for 1.26.0 release
  • 162cd20 Merge pull request #904 from ChrisJr404/v7-additional-precision-bits
  • 97eceff Add ContextV7::with_additional_precision_bits for microsecond clocks
  • 302e0bf Merge pull request #903 from uuid-rs/cargo/1.25.0
  • b7ccde8 prepare for 1.25.0 release
  • c62dffb Merge pull request #902 from ChrisJr404/serde-bytes-module
  • 8c198b2 Add a serde::bytes module that encodes as a byte string
  • Additional commits viewable in compare view

Updates `serde_with` from 3.21.0 to 3.22.0
Release notes

Sourced from serde_with's releases.

serde_with v3.22.0

Added

  • Add support for jiff v0.2 behind the new jiff_0_2 feature flag (#936) jiff::SignedDuration works with DurationSeconds and its variants. jiff::Timestamp, jiff::Zoned, and jiff::civil::DateTime work with TimestampSeconds and its variants. Deserializing a jiff::Zoned uses the system time zone, like chrono::DateTime<Local>.

Fixed

  • Extend the GHSA-7gcf-g7xr-8hxj fix to the duplicate-key-prevention collections. The rust::sets_duplicate_value_is_error, rust::maps_duplicate_key_is_error, rust::sets_last_value_wins, and rust::maps_first_key_wins adapters created their backing sets/maps with with_capacity_and_hasher using the raw deserializer size_hint, bypassing the size_hint_cautious cap added in #966 (the clippy.toml disallowed_methods lint only covers Vec::with_capacity, not with_capacity_and_hasher, so these sites were not flagged). Attacker-controlled input claiming a huge length could panic with Hash table capacity overflow before a single element was read. All such constructions now route through size_hint_cautious.
Commits
  • 88f576a Bump version to 3.22.0 (#991)
  • 931e664 Bump version to 3.22.0
  • e26930e Bump github/codeql-action from 4.37.3 to 4.37.4 in the github-actions group (...
  • 92cd5a0 Bump github/codeql-action in the github-actions group
  • 32be66f Guard with_capacity_and_hasher against untrusted size_hint (DoS) (#971)
  • 33871cd Merge branch 'master' into fix/duplicate-key-impls-capacity-overflow
  • bb1e064 Change function position within impl (#968)
  • 202d3dd Improve the time unit macros to remove unnecessary repetition and make the co...
  • b347efb Move the use_duration_signed_ser/*_de macros utils
  • 6590545 chrono_0_4: Implement the same time unit macro cleanup as jiff_0_2
  • Additional commits viewable in compare view

Updates `roaring` from 0.11.4 to 0.11.5
Release notes

Sourced from roaring's releases.

v0.11.5

What's Changed

New Contributors

Full Changelog: https://github.com/RoaringBitmap/roaring-rs/compare/v0.11.4...v0.11.5

Commits
  • 0ce3fc8 Merge pull request #364 from RoaringBitmap/upgrade-dependencies-bump-version
  • a961a04 Remove the once_cell dependency
  • 5e8445b Merge pull request #363 from youdie006/fix/359-interval-remove-boundary
  • bf2961d Bump version to v0.11.5
  • 048a8b0 Fix off-by-one that corrupts a bitmap in remove_smallest/remove_biggest
  • 27d84f5 Merge pull request #360 from silver-ymz/fix/treemap-iter-advance-across-bitmaps
  • aac2de8 Make clippy happy
  • a3d1d54 Merge pull request #362 from RoaringBitmap/std-error-for-integer-too-small
  • 9a3c33e Implement std Error for IntegerTooSmall
  • f46c0ff fix: invalid treemap iter advance
  • See full diff in compare view

Updates `napi` from 3.11.0 to 3.12.0
Release notes

Sourced from napi's releases.

napi-v3.12.0

Added

  • (cli) support non-threaded WASI targets (#3353)
Commits
  • 58bd87f chore: release (#3414)
  • 9da8723 chore(release): publish
  • 8d22196 chore(deps): update dependency oxc-parser to ^0.142.0 (#3422)
  • abc30fb build(deps): bump postcss from 8.5.17 to 8.5.23 (#3421)
  • 5542139 build(deps): bump fast-xml-parser from 5.9.3 to 5.10.1 (#3418)
  • dc4ee8c build(deps): bump fast-uri from 3.1.3 to 3.1.4 (#3419)
  • 050d985 feat(async-runtime): drain-linger surface + lock-free scheduler internals (#3...
  • e0b8708 chore(deps): update dependency oxc-parser to ^0.141.0 (#3417)
  • fc84940 chore(deps): update actions/setup-node action to v7 (#3413)
  • ee598db build(deps): bump protobufjs from 7.6.4 to 7.6.5 (#3410)
  • Additional commits viewable in compare view

Updates `napi-derive` from 3.6.1 to 3.6.3
Release notes

Sourced from napi-derive's releases.

napi-derive-v3.6.3

Other

  • updated the following local packages: napi-derive-backend

napi-derive-v3.6.2

Other

  • updated the following local packages: napi-derive-backend
Commits
  • 956e452 chore: release (#3448)
  • 73048f5 chore(release): publish
  • 61fae8a fix(napi): stop unloading addons with live native code, preserve non-Error re...
  • 93e86ce chore(release): publish
  • 2c90599 fix(cli): support npm 12 pack output (#3449)
  • 360b1ec fix(wasi): avoid randomness during module registration (#3447)
  • b648c40 build(deps): bump nanoid from 3.3.16 to 3.3.18 (#3446)
  • ffda4ef chore(deps): update dependency js-yaml to v4.3.1 [security] (#3445)
  • 387b0dc feat(cli): size WASI browser worker pools from navigator.hardwareConcurrency ...
  • 61e4346 build(deps): bump fast-uri from 3.1.4 to 3.1.5 (#3440)
  • Additional commits viewable in compare view

Updates `napi-build` from 2.4.0 to 2.4.1
Release notes

Sourced from napi-build's releases.

napi-build-v2.4.1

Fixed

  • (napi) stop unloading addons with live native code, preserve non-Error rejections, and add the wasm teardown barrier (#3423)
Commits
  • 956e452 chore: release (#3448)
  • 73048f5 chore(release): publish
  • 61fae8a fix(napi): stop unloading addons with live native code, preserve non-Error re...
  • 93e86ce chore(release): publish
  • 2c90599 fix(cli): support npm 12 pack output (#3449)
  • 360b1ec fix(wasi): avoid randomness during module registration (#3447)
  • b648c40 build(deps): bump nanoid from 3.3.16 to 3.3.18 (#3446)
  • ffda4ef chore(deps): update dependency js-yaml to v4.3.1 [security] (#3445)
  • 387b0dc feat(cli): size WASI browser worker pools from navigator.hardwareConcurrency ...
  • 61e4346 build(deps): bump fast-uri from 3.1.4 to 3.1.5 (#3440)
  • Additional commits viewable in compare view

--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Will Jones Co-authored-by: Claude Sonnet 5 --- Cargo.lock | 50 +++++++++++++++++++++++---------------------- nodejs/src/query.rs | 6 +++++- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adf15c218..d1f4675a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -535,9 +535,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", @@ -1443,9 +1443,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] @@ -5748,9 +5748,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "loom" @@ -6001,9 +6001,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" +checksum = "4293f18e7567a1caf3c584855554377025c65e0aa445344d04171f5ad63d19b9" dependencies = [ "async-lock", "crossbeam-channel", @@ -6097,14 +6097,15 @@ dependencies = [ [[package]] name = "napi" -version = "3.11.0" +version = "3.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de33522036981030a75c231829566bc63414e08101a6f5ff4ac6cef19c8e0941" +checksum = "58c5f4d5375213fdb7be2655e152386e82f026f9a5ba36a75556e11359aafe09" dependencies = [ "bitflags 2.11.1", "chrono", "ctor 1.0.12", "futures", + "libc", "napi-build", "napi-sys", "nohash-hasher", @@ -6116,15 +6117,15 @@ dependencies = [ [[package]] name = "napi-build" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5282704fbe8d49b0cf8b08e3f33233416a528658f205c7e5ace63b582de0b11c" +checksum = "60fdf9b392c50e7c4170fa633bd909490ed7835cea4c046776d1a4dd8d2ae0ab" [[package]] name = "napi-derive" -version = "3.6.1" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5c9c02556ea6dc99dffd36c1ce60141411657438501a125b675776d011ce92" +checksum = "0fa55ea69990c90b888e9e77044410e304ce7f35de599dc6d0b5c1923d2e59af" dependencies = [ "convert_case", "ctor 1.0.12", @@ -6136,9 +6137,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "6.1.1" +version = "6.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60b5d773ad46c698c8cc2cd9fde0b283d39cbb7f71c04bee633c7bdba4423bd" +checksum = "df4056ac7c18e4438ccf0edaed4340ca0d269278c8ec19284f7b23cb039fd0ae" dependencies = [ "convert_case", "proc-macro2", @@ -8601,9 +8602,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" +checksum = "18bd8a37d17a58532776dcdf6041ce64929adca78e8489d5cacbafe99229d3e1" dependencies = [ "bytemuck", "byteorder", @@ -9063,9 +9064,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", "bs58", @@ -9073,6 +9074,7 @@ dependencies = [ "hex", "indexmap 1.9.3", "indexmap 2.14.0", + "jiff", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -9083,9 +9085,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -10452,9 +10454,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.24.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.2", "js-sys", diff --git a/nodejs/src/query.rs b/nodejs/src/query.rs index 3828023a9..f9fe91d35 100644 --- a/nodejs/src/query.rs +++ b/nodejs/src/query.rs @@ -664,7 +664,11 @@ impl JsFullTextQuery { } fn parse_fts_query(query: Object) -> napi::Result { - if let Ok(Some(query)) = query.get::<&JsFullTextQuery>("query") { + // `&JsFullTextQuery` recovers a native class reference through napi's borrow-tracked + // path, which is only usable from generated `#[napi]` argument conversion. This is a + // manual lookup on a nested `Object` property instead, so use `ClassInstance`, which + // unwraps the class without requiring a borrow scope. + if let Ok(Some(query)) = query.get::>("query") { Ok(FullTextSearchQuery::new_query(query.inner.clone())) } else if let Ok(Some(query_text)) = query.get::("query") { let mut query_text = query_text; From 5cbd979455d792cf6c6d8ed27e13daaeabc20e2f Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Tue, 1 Sep 2026 05:02:40 +0800 Subject: [PATCH 32/32] fix: preserve namespace drop errors (#4099) ## Summary - preserve typed namespace errors returned by `drop_table` - return `TableNotFound` when dropping an absent namespace table - cover repeated drop behavior in the namespace database test ## Validation - `cargo test --quiet --features remote -p lancedb database::namespace::tests::test_namespace_drop_table --lib` - `cargo clippy --quiet --features remote -p lancedb --lib --tests -- -D warnings` ## Context Sophon SQL implements `DROP TABLE IF EXISTS` by matching `lancedb::Error::TableNotFound`. The namespace database previously wrapped this error as `Runtime`, causing cleanup to fail and mask an earlier statement error. --- rust/lancedb/src/database/namespace.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/lancedb/src/database/namespace.rs b/rust/lancedb/src/database/namespace.rs index 250d933f6..5ca720e85 100644 --- a/rust/lancedb/src/database/namespace.rs +++ b/rust/lancedb/src/database/namespace.rs @@ -539,9 +539,7 @@ impl Database for LanceNamespaceDatabase { self.namespace .drop_table(drop_request) .await - .map_err(|e| Error::Runtime { - message: format!("Failed to drop table: {}", e), - })?; + .map_err(|e| map_namespace_lance_error(e, name))?; Ok(()) } @@ -1495,6 +1493,15 @@ mod tests { .expect("Failed to list tables"); assert!(!table_names_after.contains(&"drop_test".to_string())); + let error = conn + .drop_table("drop_test", &["test_ns".into()]) + .await + .expect_err("dropping a missing table should fail"); + assert!( + matches!(error, Error::TableNotFound { ref name, .. } if name == "drop_test"), + "expected TableNotFound, got: {error:?}" + ); + // Verify: Cannot open dropped table let open_result = conn.open_table("drop_test").execute().await; assert!(open_result.is_err());