sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit 4bb5a24bf196b6f30f78ec969a4677a85a97b49c
parent a85410e02913454ff7e4bfcabf66b3828228f694
Author: triesap <tyson@radroots.org>
Date:   Sun, 28 Jun 2026 00:23:51 +0000

ts: own wasm declarations with dto_bindgen

- Stop generating package src/index.ts barrels from the SDK xtask.

- Render WASM package declaration files through dto_bindgen TypeScript modules.

- Overwrite wasm-pack declaration output after every WASM generation run.

- Replace WASM declaration existence checks with TypeScript typechecks and stale-output guards.

Diffstat:
Mpackages/events-codec-wasm/package.json | 2+-
Mpackages/replica-db-wasm/package.json | 2+-
Mpackages/replica-sync-wasm/package.json | 2+-
Mtools/xtask/src/check.rs | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtools/xtask/src/main.rs | 1+
Mtools/xtask/src/output.rs | 47++++++++++++++++++++---------------------------
Mtools/xtask/src/wasm.rs | 2++
Atools/xtask/src/wasm_declarations.rs | 949+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1052 insertions(+), 35 deletions(-)

diff --git a/packages/events-codec-wasm/package.json b/packages/events-codec-wasm/package.json @@ -17,7 +17,7 @@ }, "scripts": { "build": "cd ../.. && cargo xtask generate wasm --package events_codec", - "typecheck": "node -e \"require('fs').accessSync('dist/radroots_events_codec_wasm.d.ts')\"" + "typecheck": "tsc --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 dist/radroots_events_codec_wasm.d.ts dist/radroots_events_codec_wasm_bg.wasm.d.ts" }, "sideEffects": false } diff --git a/packages/replica-db-wasm/package.json b/packages/replica-db-wasm/package.json @@ -17,7 +17,7 @@ }, "scripts": { "build": "cd ../.. && cargo xtask generate wasm --package replica_db", - "typecheck": "node -e \"require('fs').accessSync('dist/radroots_replica_db_wasm.d.ts')\"" + "typecheck": "tsc --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 dist/radroots_replica_db_wasm.d.ts dist/radroots_replica_db_wasm_bg.wasm.d.ts" }, "sideEffects": false } diff --git a/packages/replica-sync-wasm/package.json b/packages/replica-sync-wasm/package.json @@ -17,7 +17,7 @@ }, "scripts": { "build": "cd ../.. && cargo xtask generate wasm --package replica_sync", - "typecheck": "node -e \"require('fs').accessSync('dist/radroots_replica_sync_wasm.d.ts')\"" + "typecheck": "tsc --noEmit --strict --module NodeNext --moduleResolution NodeNext --target ES2022 dist/radroots_replica_sync_wasm.d.ts dist/radroots_replica_sync_wasm_bg.wasm.d.ts" }, "sideEffects": false } diff --git a/tools/xtask/src/check.rs b/tools/xtask/src/check.rs @@ -8,6 +8,8 @@ use crate::{ FORBIDDEN_PACKAGE_NAMES, WasmPackageSpec, package_specs, validate_package_matrix, wasm_package_specs, }, + ts::generated_header, + wasm_declarations::declaration_files, }; pub fn check() -> Result<(), String> { @@ -24,6 +26,7 @@ pub fn check() -> Result<(), String> { if !index_path.is_file() { return Err(format!("missing package index: {}", index_path.display())); } + check_package_index(&index_path)?; } for spec in wasm_package_specs() { check_wasm_package_surface(&root, *spec)?; @@ -43,6 +46,18 @@ pub fn check() -> Result<(), String> { Ok(()) } +fn check_package_index(path: &Path) -> Result<(), String> { + let raw = fs::read_to_string(path) + .map_err(|error| format!("failed to read {}: {error}", path.display()))?; + if raw.starts_with(generated_header()) { + return Err(format!( + "package index must be handwritten source: {}", + path.display() + )); + } + Ok(()) +} + fn check_binding_crate_sources(root: &Path) -> Result<(), String> { for spec in package_specs() { let crate_src_dir = root.join(spec.crate_dir).join("src"); @@ -150,6 +165,22 @@ pub(crate) fn check_wasm_package_surface(root: &Path, spec: WasmPackageSpec) -> )); } } + check_wasm_declaration_files(&package_dir, spec)?; + Ok(()) +} + +fn check_wasm_declaration_files(package_dir: &Path, spec: WasmPackageSpec) -> Result<(), String> { + for expected in declaration_files(spec)? { + let path = package_dir.join(&expected.relative_path); + let actual = fs::read_to_string(&path) + .map_err(|error| format!("failed to read {}: {error}", path.display()))?; + if actual != expected.contents { + return Err(format!( + "stale dto_bindgen wasm declaration: {}", + path.display() + )); + } + } Ok(()) } @@ -271,7 +302,8 @@ mod tests { use crate::package_matrix::{WasmPackageSpec, validate_package_matrix}; use super::{ - check_binding_crate_sources, check_no_typescript_files, check_wasm_package_surface, + check_binding_crate_sources, check_no_typescript_files, check_package_index, + check_wasm_package_surface, }; #[test] @@ -310,6 +342,23 @@ mod tests { } #[test] + fn rejects_generated_package_index_source() { + let root = test_root("generated_index"); + let path = root.join("src/index.ts"); + fs::create_dir_all(path.parent().expect("parent")).expect("create source directory"); + fs::write( + &path, + "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nexport {};\n", + ) + .expect("write generated index"); + + let error = check_package_index(&path).expect_err("generated index rejected"); + + assert!(error.contains("package index must be handwritten source")); + let _ = fs::remove_dir_all(root); + } + + #[test] fn wasm_package_surface_requires_exported_dist_files() { let root = test_root("wasm_surface"); let package_dir = root.join("packages").join("example-wasm"); @@ -347,10 +396,17 @@ mod tests { assert!(missing.contains("example.d.ts")); fs::write( package_dir.join("dist").join("example.d.ts"), - "export {};\n", + dto_declaration("example.d.ts"), + ) + .expect("write d.ts"); + fs::write( + package_dir.join("dist").join("example_bg.wasm.d.ts"), + dto_declaration("example_bg.wasm.d.ts"), ) .expect("write d.ts"); - check_wasm_package_surface(&root, spec).expect("surface is complete"); + let error = check_wasm_package_surface(&root, spec) + .expect_err("unknown declaration inventory rejected"); + assert!(error.contains("missing wasm declaration inventory")); let _ = fs::remove_dir_all(root); } @@ -374,9 +430,14 @@ mod tests { fs::write(package_dir.join("dist").join("example.js"), "export {};\n").expect("write js"); fs::write( package_dir.join("dist").join("example.d.ts"), - "export {};\n", + dto_declaration("example.d.ts"), ) .expect("write d.ts"); + fs::write( + package_dir.join("dist").join("example_bg.wasm.d.ts"), + dto_declaration("example_bg.wasm.d.ts"), + ) + .expect("write wasm d.ts"); fs::write(package_dir.join("dist").join("package.json"), "{}\n") .expect("write forbidden manifest"); let spec = WasmPackageSpec { @@ -418,9 +479,14 @@ mod tests { fs::write(package_dir.join("dist").join("example.js"), "export {};\n").expect("write js"); fs::write( package_dir.join("dist").join("example.d.ts"), - "export {};\n", + dto_declaration("example.d.ts"), ) .expect("write d.ts"); + fs::write( + package_dir.join("dist").join("example_bg.wasm.d.ts"), + dto_declaration("example_bg.wasm.d.ts"), + ) + .expect("write wasm d.ts"); let spec = WasmPackageSpec { key: "example", crate_name: "radroots_example_wasm", @@ -450,4 +516,10 @@ mod tests { let _ = fs::remove_dir_all(&root); root } + + fn dto_declaration(name: &str) -> String { + format!( + "// @generated by cargo xtask generate wasm via dto_bindgen\n// Do not edit by hand.\nexport type Generated = \"{name}\";\n" + ) + } } diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -11,6 +11,7 @@ mod output; mod package_matrix; mod ts; mod wasm; +mod wasm_declarations; enum CommandAction<'a> { GenerateTs, diff --git a/tools/xtask/src/output.rs b/tools/xtask/src/output.rs @@ -71,10 +71,6 @@ impl PackageOutput { relative_path: format!("src/generated/{}", manifest_file_name()), contents: render_manifest(self.spec), }); - files.push(GeneratedFile { - relative_path: "src/index.ts".to_owned(), - contents: render_index(self), - }); files } } @@ -195,23 +191,6 @@ fn render_manifest(spec: PackageSpec) -> String { ) } -fn render_index(output: &PackageOutput) -> String { - let mut lines = Vec::new(); - if output.types_ts.is_some() { - lines.push("export * from \"./generated/types.js\";"); - } - if output.constants_ts.is_some() { - lines.push("export * from \"./generated/constants.js\";"); - } - if output.kinds_ts.is_some() { - lines.push("export * from \"./generated/kinds.js\";"); - } - if lines.is_empty() { - lines.push("export {};"); - } - format!("{}\n", lines.join("\n")) -} - #[cfg(test)] mod tests { use super::{PackageOutput, TsSource, package_outputs, render_ts}; @@ -273,7 +252,7 @@ mod tests { } #[test] - fn dto_registry_source_uses_package_shell() { + fn dto_registry_source_uses_generated_package_files() { let output = PackageOutput { spec: package_specs()[0], types_ts: Some(TsSource::DtoRegistry(DtoTypesModule::new( @@ -296,17 +275,31 @@ mod tests { .iter() .find(|file| file.relative_path == "src/generated/sdk-manifest.json") .expect("manifest file"); - let index = files - .iter() - .find(|file| file.relative_path == "src/index.ts") - .expect("index file"); assert_eq!( types.contents, "// @generated by cargo xtask generate ts\n// Do not edit by hand.\nimport type { LocalPrelude } from \"@radroots/local\";\nimport type { ExternalThing } from \"@radroots/external-bindings\";\n\nexport type SyntheticThing = { external: ExternalThing, };\n" ); assert!(manifest.contents.contains("\"generated\": true")); - assert_eq!(index.contents, "export * from \"./generated/types.js\";\n"); + assert!( + !files + .iter() + .any(|file| file.relative_path == "src/index.ts") + ); + } + + #[test] + fn package_outputs_do_not_generate_package_indices() { + for output in package_outputs().expect("package outputs") { + assert!( + !output + .files() + .iter() + .any(|file| file.relative_path == "src/index.ts"), + "{} index must remain handwritten source", + output.spec.package_name + ); + } } #[test] diff --git a/tools/xtask/src/wasm.rs b/tools/xtask/src/wasm.rs @@ -9,6 +9,7 @@ use crate::{ check::check_wasm_package_surface, fs::workspace_root, package_matrix::{WasmPackageSpec, validate_package_matrix, wasm_package_specs}, + wasm_declarations::write_declaration_files, }; const WASM_TARGET: &str = "wasm32-unknown-unknown"; @@ -46,6 +47,7 @@ pub fn generate(args: &[String]) -> Result<(), String> { spec.key, spec.package_name, spec.key )); } + write_declaration_files(&root, spec)?; check_wasm_package_surface(&root, spec)?; println!("generated wasm package {}", spec.package_name); } diff --git a/tools/xtask/src/wasm_declarations.rs b/tools/xtask/src/wasm_declarations.rs @@ -0,0 +1,949 @@ +use std::path::Path; + +use dto_bindgen_backend_ts::{ + TypeScriptDeclaration, TypeScriptModule, TypeScriptParameter, TypeScriptProperty, + TypeScriptType, TypeScriptValue, +}; + +use crate::package_matrix::WasmPackageSpec; + +const WASM_DECLARATION_HEADER: &str = + "// @generated by cargo xtask generate wasm via dto_bindgen\n// Do not edit by hand.\n"; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct WasmDeclarationFile { + pub relative_path: String, + pub contents: String, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct WasmDeclarationInventory { + public_functions: &'static [WasmPublicFunction], + support_exports: &'static [WasmSupportExport], +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct WasmPublicFunction { + name: &'static str, + parameter: Option<&'static str>, + return_kind: WasmReturnKind, + low_level: LowLevelSignature, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum WasmReturnKind { + String, + Any, + Void, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct LowLevelSignature { + parameters: usize, + returns: usize, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct WasmSupportExport { + name: &'static str, + export_type: WasmSupportExportType, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum WasmSupportExportType { + Memory, + Table, + Function(LowLevelSignature), + NumberFunction(usize), +} + +macro_rules! public_function { + ($name:literal, $parameter:expr, $return_kind:ident, $low_parameters:literal, $low_returns:literal) => { + WasmPublicFunction { + name: $name, + parameter: $parameter, + return_kind: WasmReturnKind::$return_kind, + low_level: LowLevelSignature { + parameters: $low_parameters, + returns: $low_returns, + }, + } + }; +} + +macro_rules! memory_export { + () => { + WasmSupportExport { + name: "memory", + export_type: WasmSupportExportType::Memory, + } + }; +} + +macro_rules! table_export { + ($name:literal) => { + WasmSupportExport { + name: $name, + export_type: WasmSupportExportType::Table, + } + }; +} + +macro_rules! void_function_export { + ($name:literal, $parameters:literal) => { + WasmSupportExport { + name: $name, + export_type: WasmSupportExportType::Function(LowLevelSignature { + parameters: $parameters, + returns: 0, + }), + } + }; +} + +macro_rules! number_function_export { + ($name:literal, $parameters:literal) => { + WasmSupportExport { + name: $name, + export_type: WasmSupportExportType::NumberFunction($parameters), + } + }; +} + +pub fn write_declaration_files(root: &Path, spec: WasmPackageSpec) -> Result<(), String> { + let package_dir = root.join(spec.package_dir); + for file in declaration_files(spec)? { + crate::fs::write_if_changed(&package_dir.join(&file.relative_path), &file.contents)?; + } + Ok(()) +} + +pub fn declaration_files(spec: WasmPackageSpec) -> Result<Vec<WasmDeclarationFile>, String> { + let inventory = inventory(spec)?; + Ok(vec![ + WasmDeclarationFile { + relative_path: format!("dist/{}.d.ts", spec.out_name), + contents: render_declaration_module(primary_module(spec, inventory)), + }, + WasmDeclarationFile { + relative_path: format!("dist/{}_bg.wasm.d.ts", spec.out_name), + contents: render_declaration_module(sidecar_module(spec, inventory)), + }, + ]) +} + +fn inventory(spec: WasmPackageSpec) -> Result<WasmDeclarationInventory, String> { + match spec.key { + "events_codec" => Ok(WasmDeclarationInventory { + public_functions: EVENTS_CODEC_FUNCTIONS, + support_exports: EVENTS_CODEC_SUPPORT_EXPORTS, + }), + "replica_db" => Ok(WasmDeclarationInventory { + public_functions: REPLICA_DB_FUNCTIONS, + support_exports: SQL_WASM_SUPPORT_EXPORTS, + }), + "replica_sync" => Ok(WasmDeclarationInventory { + public_functions: REPLICA_SYNC_FUNCTIONS, + support_exports: SQL_WASM_SUPPORT_EXPORTS, + }), + _ => Err(format!( + "missing wasm declaration inventory for {}", + spec.key + )), + } +} + +fn primary_module(spec: WasmPackageSpec, inventory: WasmDeclarationInventory) -> TypeScriptModule { + let mut module = TypeScriptModule::new(format!("dist/{}.d.ts", spec.out_name)); + for function in inventory.public_functions { + module.push_declaration(TypeScriptDeclaration::function( + function.name, + public_parameters(function.parameter), + public_return_type(function.return_kind), + )); + } + module.push_declaration(TypeScriptDeclaration::type_alias( + "InitInput", + init_input_type(), + )); + module.push_declaration(TypeScriptDeclaration::type_alias( + "InitOutput", + init_output_type(inventory), + )); + module.push_declaration(TypeScriptDeclaration::type_alias( + "SyncInitInput", + sync_init_input_type(), + )); + module.push_declaration(TypeScriptDeclaration::function( + "initSync", + vec![TypeScriptParameter::new( + "module", + TypeScriptType::union(vec![ + TypeScriptType::object(vec![TypeScriptProperty::new( + "module", + TypeScriptType::named("SyncInitInput"), + )]), + TypeScriptType::named("SyncInitInput"), + ]), + )], + TypeScriptType::named("InitOutput"), + )); + module.push_declaration(TypeScriptDeclaration::default_function( + "__wbg_init", + vec![ + TypeScriptParameter::new( + "module_or_path", + TypeScriptType::union(vec![ + TypeScriptType::object(vec![TypeScriptProperty::new( + "module_or_path", + TypeScriptType::union(vec![ + TypeScriptType::named("InitInput"), + promise_of(TypeScriptType::named("InitInput")), + ]), + )]), + TypeScriptType::named("InitInput"), + promise_of(TypeScriptType::named("InitInput")), + ]), + ) + .optional(), + ], + promise_of(TypeScriptType::named("InitOutput")), + )); + module +} + +fn sidecar_module(spec: WasmPackageSpec, inventory: WasmDeclarationInventory) -> TypeScriptModule { + let mut module = TypeScriptModule::new(format!("dist/{}_bg.wasm.d.ts", spec.out_name)); + for export in inventory.support_exports { + if matches!(export.export_type, WasmSupportExportType::Memory) { + module.push_declaration(support_export_declaration(*export)); + } + } + for function in inventory.public_functions { + module.push_declaration(TypeScriptDeclaration::constant( + function.name, + Some(low_level_function_type(function.low_level)), + TypeScriptValue::string(""), + )); + } + for export in inventory.support_exports { + if !matches!(export.export_type, WasmSupportExportType::Memory) { + module.push_declaration(support_export_declaration(*export)); + } + } + module +} + +fn render_declaration_module(module: TypeScriptModule) -> String { + let mut rendered = String::from(WASM_DECLARATION_HEADER); + rendered.push_str(&module.render_declaration()); + rendered +} + +fn public_parameters(parameter: Option<&'static str>) -> Vec<TypeScriptParameter> { + parameter + .map(|name| vec![TypeScriptParameter::new(name, TypeScriptType::String)]) + .unwrap_or_default() +} + +fn public_return_type(kind: WasmReturnKind) -> TypeScriptType { + match kind { + WasmReturnKind::String => TypeScriptType::String, + WasmReturnKind::Any => TypeScriptType::Any, + WasmReturnKind::Void => TypeScriptType::Void, + } +} + +fn init_input_type() -> TypeScriptType { + TypeScriptType::union(vec![ + TypeScriptType::named("RequestInfo"), + TypeScriptType::named("URL"), + TypeScriptType::named("Response"), + TypeScriptType::named("BufferSource"), + TypeScriptType::named("WebAssembly.Module"), + ]) +} + +fn sync_init_input_type() -> TypeScriptType { + TypeScriptType::union(vec![ + TypeScriptType::named("BufferSource"), + TypeScriptType::named("WebAssembly.Module"), + ]) +} + +fn init_output_type(inventory: WasmDeclarationInventory) -> TypeScriptType { + let mut properties = vec![TypeScriptProperty::new("memory", memory_type()).readonly()]; + properties.extend(inventory.public_functions.iter().map(|function| { + TypeScriptProperty::new(function.name, low_level_function_type(function.low_level)) + .readonly() + })); + properties.extend( + inventory + .support_exports + .iter() + .filter(|export| !matches!(export.export_type, WasmSupportExportType::Memory)) + .map(|export| { + TypeScriptProperty::new(export.name, support_export_type(*export)).readonly() + }), + ); + TypeScriptType::object(properties) +} + +fn support_export_declaration(export: WasmSupportExport) -> TypeScriptDeclaration { + TypeScriptDeclaration::constant( + export.name, + Some(support_export_type(export)), + TypeScriptValue::string(""), + ) +} + +fn support_export_type(export: WasmSupportExport) -> TypeScriptType { + match export.export_type { + WasmSupportExportType::Memory => memory_type(), + WasmSupportExportType::Table => TypeScriptType::named("WebAssembly.Table"), + WasmSupportExportType::Function(signature) => low_level_function_type(signature), + WasmSupportExportType::NumberFunction(parameters) => { + low_level_number_function_type(parameters) + } + } +} + +fn memory_type() -> TypeScriptType { + TypeScriptType::named("WebAssembly.Memory") +} + +fn low_level_number_function_type(parameters: usize) -> TypeScriptType { + TypeScriptType::function(number_parameters(parameters), TypeScriptType::Number) +} + +fn low_level_function_type(signature: LowLevelSignature) -> TypeScriptType { + TypeScriptType::function( + number_parameters(signature.parameters), + low_level_return_type(signature.returns), + ) +} + +fn number_parameters(count: usize) -> Vec<TypeScriptParameter> { + (0..count) + .map(|index| { + TypeScriptParameter::new(low_level_parameter_name(index), TypeScriptType::Number) + }) + .collect() +} + +fn low_level_parameter_name(index: usize) -> String { + match index { + 0 => "a".to_owned(), + 1 => "b".to_owned(), + 2 => "c".to_owned(), + 3 => "d".to_owned(), + _ => format!("arg{index}"), + } +} + +fn low_level_return_type(count: usize) -> TypeScriptType { + match count { + 0 => TypeScriptType::Void, + 1 => TypeScriptType::Number, + _ => TypeScriptType::tuple(vec![TypeScriptType::Number; count]), + } +} + +fn promise_of(type_expr: TypeScriptType) -> TypeScriptType { + TypeScriptType::generic("Promise", vec![type_expr]) +} + +const EVENTS_CODEC_FUNCTIONS: &[WasmPublicFunction] = &[ + public_function!("article_tags", Some("article_json"), String, 2, 4), + public_function!("calendar_date_event_tags", Some("event_json"), String, 2, 4), + public_function!("calendar_event_rsvp_tags", Some("rsvp_json"), String, 2, 4), + public_function!("calendar_tags", Some("calendar_json"), String, 2, 4), + public_function!("calendar_time_event_tags", Some("event_json"), String, 2, 4), + public_function!("comment_tags", Some("comment_json"), String, 2, 4), + public_function!("coop_tags", Some("coop_json"), String, 2, 4), + public_function!("document_tags", Some("document_json"), String, 2, 4), + public_function!("farm_crdt_change_tags", Some("input_json"), String, 2, 4), + public_function!("farm_file_metadata_tags", Some("file_json"), String, 2, 4), + public_function!("farm_tags", Some("farm_json"), String, 2, 4), + public_function!( + "farm_workspace_manifest_tags", + Some("workspace_json"), + String, + 2, + 4 + ), + public_function!("file_metadata_tags", Some("metadata_json"), String, 2, 4), + public_function!("follow_tags", Some("follow_json"), String, 2, 4), + public_function!("generic_repost_tags", Some("repost_json"), String, 2, 4), + public_function!("gift_wrap_tags", Some("gift_wrap_json"), String, 2, 4), + public_function!("group_admins_tags", Some("group_json"), String, 2, 4), + public_function!("group_create_group_tags", Some("group_json"), String, 2, 4), + public_function!("group_create_invite_tags", Some("group_json"), String, 2, 4), + public_function!("group_delete_event_tags", Some("group_json"), String, 2, 4), + public_function!("group_delete_group_tags", Some("group_json"), String, 2, 4), + public_function!("group_edit_metadata_tags", Some("group_json"), String, 2, 4), + public_function!("group_join_request_tags", Some("group_json"), String, 2, 4), + public_function!("group_leave_request_tags", Some("group_json"), String, 2, 4), + public_function!("group_members_tags", Some("group_json"), String, 2, 4), + public_function!("group_metadata_tags", Some("group_json"), String, 2, 4), + public_function!("group_put_user_tags", Some("group_json"), String, 2, 4), + public_function!("group_remove_user_tags", Some("group_json"), String, 2, 4), + public_function!("group_roles_tags", Some("group_json"), String, 2, 4), + public_function!("http_auth_tags", Some("auth_json"), String, 2, 4), + public_function!("job_feedback_tags", Some("job_json"), String, 2, 4), + public_function!("job_request_tags", Some("job_json"), String, 2, 4), + public_function!("job_result_tags", Some("job_json"), String, 2, 4), + public_function!("list_set_tags", Some("list_json"), String, 2, 4), + public_function!("list_tags", Some("list_json"), String, 2, 4), + public_function!("listing_tags", Some("listing_json"), String, 2, 4), + public_function!("listing_tags_full", Some("listing_json"), String, 2, 4), + public_function!("message_file_tags", Some("message_json"), String, 2, 4), + public_function!("message_tags", Some("message_json"), String, 2, 4), + public_function!("plot_tags", Some("plot_json"), String, 2, 4), + public_function!("post_tags", Some("post_json"), String, 2, 4), + public_function!("reaction_tags", Some("reaction_json"), String, 2, 4), + public_function!("relay_auth_tags", Some("auth_json"), String, 2, 4), + public_function!("report_tags", Some("report_json"), String, 2, 4), + public_function!("repost_tags", Some("repost_json"), String, 2, 4), + public_function!("seal_tags", Some("seal_json"), String, 2, 4), +]; + +const EVENTS_CODEC_SUPPORT_EXPORTS: &[WasmSupportExport] = &[ + memory_export!(), + table_export!("__wbindgen_externrefs"), + number_function_export!("__wbindgen_malloc", 2), + number_function_export!("__wbindgen_realloc", 4), + void_function_export!("__externref_table_dealloc", 1), + void_function_export!("__wbindgen_free", 3), + void_function_export!("__wbindgen_start", 0), +]; + +const REPLICA_DB_FUNCTIONS: &[WasmPublicFunction] = &[ + public_function!("replica_db_export_begin", None, Any, 0, 3), + public_function!("replica_db_export_finish", None, Void, 0, 2), + public_function!("replica_db_export_json", None, Any, 0, 3), + public_function!("replica_db_farm_create", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_farm_delete", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_farm_find_many", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_farm_find_one", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_farm_gcs_location_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_gcs_location_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_gcs_location_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_gcs_location_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_gcs_location_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_claim_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_claim_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_claim_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_claim_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_claim_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_farm_member_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_farm_tag_create", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_farm_tag_delete", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_farm_tag_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_farm_tag_find_one", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_farm_tag_update", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_gcs_location_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_gcs_location_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_gcs_location_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_gcs_location_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_gcs_location_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_import_json", Some("dump_json"), Void, 2, 2), + public_function!("replica_db_log_error_create", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_log_error_delete", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_log_error_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_log_error_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_log_error_update", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_media_image_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_media_image_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_media_image_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_media_image_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_media_image_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_event_head_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_event_head_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_event_head_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_event_head_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_event_head_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_relay_set", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_relay_unset", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_profile_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_relay_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_relay_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_relay_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_relay_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_nostr_relay_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_plot_create", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_plot_delete", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_plot_find_many", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_plot_find_one", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_plot_gcs_location_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_plot_gcs_location_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_plot_gcs_location_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_plot_gcs_location_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_plot_gcs_location_update", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_plot_tag_create", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_plot_tag_delete", Some("opts_json"), Any, 2, 3), + public_function!( + "replica_db_plot_tag_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!("replica_db_plot_tag_find_one", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_plot_tag_update", Some("opts_json"), Any, 2, 3), + public_function!("replica_db_reset_database", None, Void, 0, 2), + public_function!("replica_db_run_migrations", None, Void, 0, 2), + public_function!( + "replica_db_trade_product_create", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_delete", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_find_many", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_find_one", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_location_set", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_location_unset", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_media_set", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_media_unset", + Some("opts_json"), + Any, + 2, + 3 + ), + public_function!( + "replica_db_trade_product_update", + Some("opts_json"), + Any, + 2, + 3 + ), +]; + +const REPLICA_SYNC_FUNCTIONS: &[WasmPublicFunction] = &[ + public_function!("replica_sync_ingest_event", Some("event_json"), Any, 2, 3), + public_function!("replica_sync_sync_all", Some("request_json"), Any, 2, 3), +]; + +const SQL_WASM_SUPPORT_EXPORTS: &[WasmSupportExport] = &[ + memory_export!(), + number_function_export!("__wbindgen_malloc", 2), + number_function_export!("__wbindgen_realloc", 4), + void_function_export!("__wbindgen_exn_store", 1), + number_function_export!("__externref_table_alloc", 0), + table_export!("__wbindgen_externrefs"), + void_function_export!("__externref_table_dealloc", 1), + void_function_export!("__wbindgen_start", 0), +]; + +#[cfg(test)] +mod tests { + use crate::package_matrix::wasm_package_specs; + + use super::{WASM_DECLARATION_HEADER, declaration_files}; + + #[test] + fn declaration_files_are_dto_owned_and_cover_primary_and_sidecar_paths() { + for spec in wasm_package_specs() { + let files = declaration_files(*spec).expect("declaration files"); + assert_eq!(files.len(), 2); + assert!( + files + .iter() + .any(|file| file.relative_path == format!("dist/{}.d.ts", spec.out_name)) + ); + assert!( + files.iter().any( + |file| file.relative_path == format!("dist/{}_bg.wasm.d.ts", spec.out_name) + ) + ); + for file in files { + assert!(file.contents.starts_with(WASM_DECLARATION_HEADER)); + assert!(!file.contents.contains("tslint:disable")); + assert!(!file.contents.contains("eslint-disable")); + } + } + } + + #[test] + fn generated_declarations_include_public_wasm_entrypoints() { + let specs = wasm_package_specs(); + let events = declaration_files(specs[0]).expect("events declarations"); + assert!(events[0].contents.contains("export function listing_tags")); + assert!( + events[1] + .contents + .contains("export declare const listing_tags") + ); + + let replica_db = declaration_files(specs[1]).expect("replica db declarations"); + assert!( + replica_db[0] + .contents + .contains("export function replica_db_farm_create") + ); + assert!( + replica_db[1] + .contents + .contains("export declare const replica_db_farm_create") + ); + + let replica_sync = declaration_files(specs[2]).expect("replica sync declarations"); + assert!( + replica_sync[0] + .contents + .contains("export function replica_sync_sync_all") + ); + assert!( + replica_sync[1] + .contents + .contains("export declare const replica_sync_sync_all") + ); + } +}