Skip to content

Commit

Permalink
Update node bindings (#1651)
Browse files Browse the repository at this point in the history
* Add new group create methods

* Add consent states option to sync all

* Update list conversations options

* Move MessageDisappearingSettings into conversations

* Remove auto filtering of group messages

* Add disappearing messages methods to groups

* Update conversations list methods

* Add consent streaming

* Preference streaming WIP

* ConversationListItem + UserPreferences

* taplo fmt

* Fix ListConversationsOptions

* Refactor tests

* Add tests

* Add more tests

* Clippy

* Update stream_preferences callback type

* Update lint command

* clippy

* Add tsx and zx deps

* Add version script

* Add version export and generate command

* Update node release workflow

* lints

* last clippy lints

* Make test more stable

* Update version and changelog

---------

Co-authored-by: Andrew Plaza <[email protected]>
  • Loading branch information
rygine and insipx authored Feb 24, 2025
1 parent 0a80fea commit e7abf9f
Show file tree
Hide file tree
Showing 21 changed files with 1,444 additions and 141 deletions.
11 changes: 9 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ version: 2
updates:
# Maintain dependencies for cargo
- package-ecosystem: "cargo"
directory: "/"
directories:
- "/"
- "/xmtp_*"
- "/bindings_*"
- "/mls_validation_service"
- "/examples/cli"
- "/xtask"
- "/common"
schedule:
interval: "weekly"
groups:
Expand All @@ -13,7 +20,7 @@ updates:
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
# Maintain dependencies for GitHub Actions
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
# Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.)
directory: "/"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release-node-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ jobs:
- name: Enable corepack
run: corepack enable

- name: Generate version
working-directory: bindings_node
run: yarn generate:version

- name: Publish to NPM
uses: JS-DevTools/npm-publish@v3
with:
Expand Down
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions bindings_node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @xmtp/node-bindings

## 0.0.38

- Added `version.json` to package
- Added new methods to create groups by inbox ID
- Added consent states option to `sync_all_conversations`
- Updated list conversations options to include `consent_states` and `include_duplicate_dms`
- Removed automatic message filtering from DM groups
- Added disappearing messages methods to conversations
- Updated conversations list methods to return conversations and their last message
- Added consent streaming
- Added preferences streaming

## 0.0.37

- Removed group pinned frame URL
Expand Down
6 changes: 4 additions & 2 deletions bindings_node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ crate-type = ["cdylib"]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
futures.workspace = true
hex.workspace = true
napi = { version = "2.12.2", default-features = false, features = [
napi = { version = "2.16.16", default-features = false, features = [
"napi4",
"napi6",
"async",
"serde-json",
] }
napi-derive = "2.12.2"
napi-derive = "2.16.6"
prost.workspace = true
serde.workspace = true
tokio = { workspace = true, features = ["sync"] }
tracing.workspace = true
tracing-subscriber = { workspace = true, features = [
Expand Down
14 changes: 9 additions & 5 deletions bindings_node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xmtp/node-bindings",
"version": "0.0.37",
"version": "0.0.38",
"repository": {
"type": "git",
"url": "git+https://[email protected]/xmtp/libxmtp.git",
Expand All @@ -12,7 +12,8 @@
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"./version.json": "./dist/version.json"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -21,16 +22,17 @@
],
"scripts": {
"artifacts": "napi artifacts",
"build": "yarn build:clean && yarn build:release && yarn build:finish",
"build": "yarn build:clean && yarn build:release && yarn build:finish && yarn generate:version",
"build:clean": "rm -rf dist",
"build:debug": "napi build --platform --esm",
"build:finish": "mkdir dist && mv index.js dist && mv index.d.ts dist && mv *.node dist",
"build:release": "napi build --platform --release --esm",
"clean": "yarn build:clean && yarn test:clean",
"format": "prettier -w .",
"format:check": "prettier -c .",
"generate:version": "tsx scripts/version.ts",
"lint": "yarn lint:clippy && yarn lint:fmt",
"lint:clippy": "cargo clippy --all-features --all-targets --no-deps -- -Dwarnings",
"lint:clippy": "cargo clippy --locked --all-features --all-targets --no-deps -- -D warnings",
"lint:fmt": "cargo fmt --check",
"test": "vitest run",
"test:clean": "rm -rf test/*.db3*"
Expand All @@ -43,12 +45,14 @@
"fast-glob": "^3.3.3",
"prettier": "^3.5.0",
"prettier-plugin-packagejson": "^2.5.8",
"tsx": "^4.19.3",
"typescript": "^5.7.3",
"uuid": "^11.0.5",
"viem": "^2.23.2",
"vite": "^6.1.0",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.4"
"vitest": "^3.0.4",
"zx": "^8.3.2"
},
"packageManager": "[email protected]",
"engines": {
Expand Down
20 changes: 20 additions & 0 deletions bindings_node/scripts/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { writeFile } from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { $ } from 'zx'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const distPath = path.resolve(__dirname, '../dist')

const branch = (await $`git rev-parse --abbrev-ref HEAD`.text()).trim()
const version = (await $`git log -1 --pretty=format:"%h"`.text()).trim()
const date = (
await $`TZ=UTC git log -1 --date=iso-local --pretty=format:"%ad"`.text()
).trim()

const json = JSON.stringify({ branch, version, date }, null, 2)

console.log(`Writing version.json to ${distPath}`)

await writeFile(path.resolve(distPath, 'version.json'), json)
23 changes: 23 additions & 0 deletions bindings_node/src/consent_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use xmtp_mls::storage::consent_record::{
use crate::{client::Client, ErrorWrapper};

#[napi]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ConsentState {
Unknown,
Allowed,
Expand Down Expand Up @@ -34,6 +35,7 @@ impl From<ConsentState> for XmtpConsentState {
}

#[napi]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ConsentEntityType {
GroupId,
InboxId,
Expand All @@ -50,7 +52,18 @@ impl From<ConsentEntityType> for XmtpConsentType {
}
}

impl From<XmtpConsentType> for ConsentEntityType {
fn from(entity_type: XmtpConsentType) -> Self {
match entity_type {
XmtpConsentType::ConversationId => ConsentEntityType::GroupId,
XmtpConsentType::InboxId => ConsentEntityType::InboxId,
XmtpConsentType::Address => ConsentEntityType::Address,
}
}
}

#[napi(object)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Consent {
pub entity_type: ConsentEntityType,
pub state: ConsentState,
Expand All @@ -67,6 +80,16 @@ impl From<Consent> for StoredConsentRecord {
}
}

impl From<StoredConsentRecord> for Consent {
fn from(consent: StoredConsentRecord) -> Self {
Self {
entity_type: consent.entity_type.into(),
state: consent.state.into(),
entity: consent.entity,
}
}
}

#[napi]
impl Client {
#[napi]
Expand Down
93 changes: 60 additions & 33 deletions bindings_node/src/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,21 @@ use xmtp_mls::{
intents::PermissionUpdateType as XmtpPermissionUpdateType,
members::PermissionLevel as XmtpPermissionLevel, MlsGroup, UpdateAdminListType,
},
storage::{
group::ConversationType,
group_message::{GroupMessageKind as XmtpGroupMessageKind, MsgQueryArgs},
},
storage::{group::ConversationType, group_message::MsgQueryArgs},
};
use xmtp_proto::xmtp::mls::message_contents::EncodedContent as XmtpEncodedContent;

use crate::{
client::RustXmtpClient,
consent_state::ConsentState,
conversations::MessageDisappearingSettings,
encoded_content::EncodedContent,
message::{ListMessagesOptions, Message},
permissions::{GroupPermissions, MetadataField, PermissionPolicy, PermissionUpdateType},
streams::StreamCloser,
ErrorWrapper,
};
use prost::Message as ProstMessage;
use xmtp_mls::groups::group_mutable_metadata::MessageDisappearingSettings as XmtpConversationMessageDisappearingSettings;

use napi_derive::napi;

Expand All @@ -38,33 +35,6 @@ pub struct GroupMetadata {
inner: XmtpGroupMetadata,
}

/// Settings for disappearing messages in a conversation.
///
/// # Fields
///
/// * `from_ns` - The timestamp (in nanoseconds) from when messages should be tracked for deletion.
/// * `in_ns` - The duration (in nanoseconds) after which tracked messages will be deleted.
#[napi(object)]
#[derive(Clone)]
pub struct MessageDisappearingSettings {
pub from_ns: i64,
pub in_ns: i64,
}

#[napi]
impl MessageDisappearingSettings {
#[napi]
pub fn new(from_ns: i64, in_ns: i64) -> Self {
Self { from_ns, in_ns }
}
}

impl From<MessageDisappearingSettings> for XmtpConversationMessageDisappearingSettings {
fn from(value: MessageDisappearingSettings) -> Self {
XmtpConversationMessageDisappearingSettings::new(value.from_ns, value.in_ns)
}
}

#[napi]
impl GroupMetadata {
#[napi]
Expand Down Expand Up @@ -99,6 +69,7 @@ pub struct GroupMember {
}

#[napi]
#[derive(Clone)]
pub struct Conversation {
inner_client: Arc<RustXmtpClient>,
group_id: Vec<u8>,
Expand Down Expand Up @@ -201,7 +172,7 @@ impl Conversation {
.map_err(ErrorWrapper::from)?;
let kind = match conversation_type {
ConversationType::Group => None,
ConversationType::Dm => Some(XmtpGroupMessageKind::Application),
ConversationType::Dm => None,
ConversationType::Sync => None,
};
let opts = MsgQueryArgs {
Expand Down Expand Up @@ -681,4 +652,60 @@ impl Conversation {

Ok(())
}

#[napi]
pub async fn update_message_disappearing_settings(
&self,
settings: MessageDisappearingSettings,
) -> Result<()> {
let group = MlsGroup::new(
self.inner_client.clone(),
self.group_id.clone(),
self.created_at_ns,
);
group
.update_conversation_message_disappearing_settings(settings.into())
.await
.map_err(ErrorWrapper::from)?;

Ok(())
}

#[napi]
pub async fn remove_message_disappearing_settings(&self) -> Result<()> {
let group = MlsGroup::new(
self.inner_client.clone(),
self.group_id.clone(),
self.created_at_ns,
);

group
.remove_conversation_message_disappearing_settings()
.await
.map_err(ErrorWrapper::from)?;

Ok(())
}

#[napi]
pub fn message_disappearing_settings(&self) -> Result<Option<MessageDisappearingSettings>> {
let settings = self
.inner_client
.group_disappearing_settings(self.group_id.clone())
.map_err(ErrorWrapper::from)?;

match settings {
Some(s) => Ok(Some(s.into())),
None => Ok(None),
}
}

#[napi]
pub fn is_message_disappearing_enabled(&self) -> Result<bool> {
self.message_disappearing_settings().map(|settings| {
settings
.as_ref()
.is_some_and(|s| s.from_ns > 0 && s.in_ns > 0)
})
}
}
Loading

0 comments on commit e7abf9f

Please sign in to comment.