Skip to content

Commit

Permalink
Tyler's Stable SDK API implementation (#102)
Browse files Browse the repository at this point in the history
* 0.12: Remove global (#99)

* Push

* Half baked satuff

* Push tables stuff

* Update the example a bit

* Added tsup to dev dependencies

* Fixed naming and a lot of issues

* Improved the serialization situation

* More cleanup

* Updating the examples with the code gen

* Unscuffed the scuffery

* Unscuffing

* It works

* Whitespace

* Updated the quickstart

* pnpm format

* Fixed errors and manually imported sdk correctly in code gen code

* Updated the typescript sdk with the new websocket proposal format

* Fixes and removed playground

* Updated for 0.12 still not done fixing tests

* Regenerated the quickstart

* fixes and formating

* Fixed tests almost

* Fixed more tests

* Fixed all tests

* Renamed compile to build

* Fixes

* test-app: Expose the files

* ci: Build the code before tests

* build -> compile

* try to fix CR

---------

Co-authored-by: Puru Vijay <[email protected]>
Co-authored-by: Puru Vijay <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2024
1 parent 17227c0 commit b8c944c
Show file tree
Hide file tree
Showing 146 changed files with 5,383 additions and 5,203 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-colts-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clockworklabs/spacetimedb-sdk': minor
---

internal: Remove global instance, allow multiple connections
4 changes: 2 additions & 2 deletions .github/workflows/cr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
version: 9.7
run_install: true

- name: Compile
- name: Build
run: pnpm compile

- name: Release
run: cd packages/sdk && pnpm dlx pkg-pr-new publish --compact
run: cd packages/sdk && pnpm dlx pkg-pr-new publish --compact --pnpm
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@ jobs:
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Build
run: pnpm compile

- name: Run tests
run: pnpm test
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ node_modules
pnpm-lock.yaml
dist
target
module_bindings
1 change: 0 additions & 1 deletion examples/quickstart/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@ dist-ssr
*.sw?

server
module_bindings
17 changes: 3 additions & 14 deletions examples/quickstart/server/Cargo.lock

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

58 changes: 26 additions & 32 deletions examples/quickstart/server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
use anyhow::{anyhow, Result};
use spacetimedb::{spacetimedb, Identity, ReducerContext, Timestamp};
use spacetimedb::{Identity, ReducerContext, Table, Timestamp};

#[spacetimedb(table(public))]
#[spacetimedb::table(name = user, public)]
pub struct User {
#[primarykey]
#[primary_key]
identity: Identity,
name: Option<String>,
online: bool,
}

#[spacetimedb(table(public))]
#[spacetimedb::table(name = message, public)]
pub struct Message {
sender: Identity,
sent: Timestamp,
text: String,
}

#[spacetimedb(init)]
pub fn init() {
#[spacetimedb::reducer(init)]
pub fn init(_ctx: &ReducerContext) {
// Called when the module is initially published
}

#[spacetimedb(connect)]
pub fn identity_connected(ctx: ReducerContext) {
if let Some(user) = User::filter_by_identity(&ctx.sender) {
#[spacetimedb::reducer(client_connected)]
pub fn identity_connected(ctx: &ReducerContext) {
if let Some(user) = ctx.db.user().identity().find(&ctx.sender) {
// If this is a returning user, i.e. we already have a `User` with this `Identity`,
// set `online: true`, but leave `name` and `identity` unchanged.
User::update_by_identity(
&ctx.sender,
User {
online: true,
..user
},
);
ctx.db.user().identity().update(User {
online: true,
..user
});
} else {
// If this is a new user, create a `User` row for the `Identity`,
// which is online, but hasn't set a name.
User::insert(User {
ctx.db.user().insert(User {
name: None,
identity: ctx.sender,
online: true,
})
.unwrap();
});
}
}

#[spacetimedb(disconnect)]
pub fn identity_disconnected(ctx: ReducerContext) {
if let Some(user) = User::filter_by_identity(&ctx.sender) {
User::update_by_identity(
&ctx.sender,
#[spacetimedb::reducer(client_disconnected)]
pub fn identity_disconnected(ctx: &ReducerContext) {
if let Some(user) = ctx.db.user().identity().find(&ctx.sender) {
ctx.db.user().identity().update(
User {
online: false,
..user
Expand All @@ -73,12 +68,11 @@ fn validate_name(name: String) -> Result<String> {
}
}

#[spacetimedb(reducer)]
pub fn set_name(ctx: ReducerContext, name: String) -> Result<()> {
#[spacetimedb::reducer]
pub fn set_name(ctx: &ReducerContext, name: String) -> Result<()> {
let name = validate_name(name)?;
if let Some(user) = User::filter_by_identity(&ctx.sender) {
User::update_by_identity(
&ctx.sender,
if let Some(user) = ctx.db.user().identity().find(&ctx.sender) {
ctx.db.user().identity().update(
User {
name: Some(name),
..user
Expand All @@ -98,13 +92,13 @@ fn validate_message(text: String) -> Result<String> {
}
}

#[spacetimedb(reducer)]
pub fn send_message(ctx: ReducerContext, text: String) -> Result<()> {
#[spacetimedb::reducer]
pub fn send_message(ctx: &ReducerContext, text: String) -> Result<()> {
// Things to consider:
// - Rate-limit messages per-user.
// - Reject messages from unnamed users.
let text = validate_message(text)?;
Message::insert(Message {
ctx.db.message().insert(Message {
sender: ctx.sender,
text,
sent: ctx.timestamp,
Expand Down
84 changes: 43 additions & 41 deletions examples/quickstart/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import './App.css';

// Bindings
import Message from './module_bindings/message';
import SendMessageReducer from './module_bindings/send_message_reducer';
import SetNameReducer from './module_bindings/set_name_reducer';
import User from './module_bindings/user';
import { DbConnection, type RemoteDBContext } from './module_bindings';

import { Identity, SpacetimeDBClient } from '@clockworklabs/spacetimedb-sdk';
import { DBConnectionBuilder, Identity } from '@clockworklabs/spacetimedb-sdk';
import React, { useEffect, useRef, useState } from 'react';

export type MessageType = {
name: string;
message: string;
};

// Register the tables and reducers before creating the SpacetimeDBClient
SpacetimeDBClient.registerTables(Message, User);
SpacetimeDBClient.registerReducers(SendMessageReducer, SetNameReducer);

const token = localStorage.getItem('auth_token') || undefined;
const client = new SpacetimeDBClient('ws://localhost:3000', 'chat', token);
const identity = localStorage.getItem('identity') || undefined;

function App() {
const [newName, setNewName] = useState('');
Expand All @@ -36,47 +28,57 @@ function App() {

const local_identity = useRef<Identity | undefined>(undefined);
const initialized = useRef<boolean>(false);
const conn = useRef<RemoteDBContext>(null!);

useEffect(() => {
if (!initialized.current) {
client.connect();
initialized.current = true;
async function main() {
if (!conn.current) {
conn.current = await DbConnection.builder()
.withUri('ws://localhost:3000')
.withModuleName('chat')
.withCredentials([Identity.fromString(identity!), token!])
.onDisconnect(() => {
console.log('disconnected');
})
.onConnectError(() => {
console.log('client_error');
})
.onConnect((identity, token) => {
console.log('Connected to SpacetimeDB');

localStorage.setItem('auth_token', token);
localStorage.setItem('identity', identity.toHexString());

// conn.current!.subscribe([
// 'SELECT * FROM User',
// 'SELECT * FROM Message',
// ]);
})
.build();
}
}

main();
}, []);

// All the event listeners are set up in the useEffect hook
useEffect(() => {
client.on('disconnected', () => {
console.log('disconnected');
});

client.on('client_error', () => {
console.log('client_error');
});

client.onConnect((token: string, identity: Identity) => {
console.log('Connected to SpacetimeDB');
if (!conn.current) return;

local_identity.current = identity;
// TODO: What do about this?
// conn.on('initialStateSync', () => {
// setAllMessagesInOrder();
// const user = User.findByIdentity(local_identity?.current!);
// setName(userNameOrIdentity(user!));
// });

localStorage.setItem('auth_token', token);

client.subscribe(['SELECT * FROM User', 'SELECT * FROM Message']);
});

client.on('initialStateSync', () => {
setAllMessagesInOrder();
const user = User.findByIdentity(local_identity?.current!);
setName(userNameOrIdentity(user!));
});

User.onInsert(user => {
conn.current.db.user.onInsert(user => {
if (user.online) {
appendToSystemMessage(`${userNameOrIdentity(user)} has connected.`);
}
});

User.onUpdate((oldUser, user) => {
conn.current.db.user.onUpdate((oldUser, user) => {
if (oldUser.online === false && user.online === true) {
appendToSystemMessage(`${userNameOrIdentity(user)} has connected.`);
} else if (oldUser.online === true && user.online === false) {
Expand All @@ -92,14 +94,14 @@ function App() {
}
});

Message.onInsert(() => {
conn.current.db.message.onInsert(() => {
setAllMessagesInOrder();
});

SendMessageReducer.on(reducerEvent => {
conn.current.reducers.onSendMessage((ctx, text) => {
if (
local_identity.current &&
reducerEvent.callerIdentity.isEqual(local_identity.current)
ctx.event.callerIdentity.isEqual(local_identity.current)
) {
if (reducerEvent.status === 'failed') {
appendToSystemMessage(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD.

import {
// @ts-ignore
Address,
// @ts-ignore
AlgebraicType,
// @ts-ignore
AlgebraicValue,
// @ts-ignore
BinaryReader,
// @ts-ignore
BinaryWriter,
// @ts-ignore
DBConnectionBuilder,
// @ts-ignore
DBConnectionImpl,
// @ts-ignore
DBContext,
// @ts-ignore
Event,
// @ts-ignore
EventContextInterface,
// @ts-ignore
Identity,
// @ts-ignore
ProductType,
// @ts-ignore
ProductTypeElement,
// @ts-ignore
SumType,
// @ts-ignore
SumTypeVariant,
// @ts-ignore
TableCache,
} from '@clockworklabs/spacetimedb-sdk';

export type IdentityConnected = {};

/**
* A namespace for generated helper functions.
*/
export namespace IdentityConnected {
/**
* A function which returns this type represented as an AlgebraicType.
* This function is derived from the AlgebraicType used to generate this type.
*/
export function getAlgebraicType(): AlgebraicType {
return AlgebraicType.createProductType([]);
}

export function serialize(
writer: BinaryWriter,
value: IdentityConnected
): void {
const converted = {};
IdentityConnected.getAlgebraicType().serialize(writer, converted);
}

export function deserialize(reader: BinaryReader): IdentityConnected {
const value = IdentityConnected.getAlgebraicType().deserialize(reader);
return {};
}
}
Loading

0 comments on commit b8c944c

Please sign in to comment.