Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 160 additions & 151 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ members = [
"tools/upgrade-version",
"tools/license-check",
"crates/bindings-typescript/test-app/server",
"crates/bindings-typescript/test-react-router-app/server",
]
default-members = ["crates/cli", "crates/standalone", "crates/update"]
# cargo feature graph resolver. v3 is default in edition2024 but workspace
Expand Down
25 changes: 2 additions & 23 deletions crates/bindings-typescript/src/react/useTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ export function useTable<
| undefined;
}
const [subscribeApplied, setSubscribeApplied] = useState(false);
const [isActive, setIsActive] = useState(false);
let spacetime: DbConnection | undefined;
try {
spacetime = useSpacetimeDB<DbConnection>();
Expand Down Expand Up @@ -329,27 +328,7 @@ export function useTable<
}, [client, tableName, whereKey, subscribeApplied]);

useEffect(() => {
const onConnect = () => {
setIsActive(client.isActive);
};
const onDisconnect = () => {
setIsActive(client.isActive);
};
const onConnectError = () => {
setIsActive(client.isActive);
};
client['on']('connect', onConnect);
client['on']('disconnect', onDisconnect);
client['on']('connectError', onConnectError);
return () => {
client['off']('connect', onConnect);
client['off']('disconnect', onDisconnect);
client['off']('connectError', onConnectError);
};
}, [client]);

useEffect(() => {
if (isActive) {
if (client.isActive) {
const cancel = client
.subscriptionBuilder()
.onApplied(() => {
Expand All @@ -360,7 +339,7 @@ export function useTable<
cancel.unsubscribe();
};
}
}, [query, isActive, client]);
}, [query, client.isActive, client]);

const subscribe = useCallback(
(onStoreChange: () => void) => {
Expand Down
2 changes: 1 addition & 1 deletion crates/bindings-typescript/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"format": "prettier . --write --ignore-path ../../../.prettierignore",
"lint": "eslint . && prettier . --check --ignore-path ../../../.prettierignore",
"preview": "vite preview",
"generate": "cargo build -p spacetimedb-standalone && cargo run -p spacetimedb-cli generate --lang typescript --out-dir src/module_bindings --project-path server && prettier --write src/module_bindings && find src/module_bindings -type f -exec perl -pi -e 's#spacetimedb#../../../src/index#g' {} + && prettier --write src/module_bindings",
"generate": "cargo build -p spacetimedb-standalone && cargo run -p spacetimedb-cli generate --lang typescript --out-dir src/module_bindings --project-path server && prettier --write src/module_bindings && node ./replace-spacetimedb.js && prettier --write src/module_bindings",
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path server",
"spacetime:start": "spacetime start server",
"spacetime:publish:local": "spacetime publish game --project-path server --server local",
Expand Down
38 changes: 38 additions & 0 deletions crates/bindings-typescript/test-app/replace-spacetimedb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env node

/**
* Cross-platform replacement script
* Equivalent to:
* find src/module_bindings -type f -exec perl -pi -e 's#spacetimedb#../../../src/index#g' {}
*/

import fs from 'fs';
import path from 'path';

const ROOT = path.resolve('src/module_bindings');
const SEARCH = /spacetimedb/g;
const REPLACEMENT = '../../../src/index';

function replaceInFile(filePath) {
try {
let content = fs.readFileSync(filePath, 'utf8');
if (SEARCH.test(content)) {
const updated = content.replace(SEARCH, REPLACEMENT);
fs.writeFileSync(filePath, updated, 'utf8');
console.log(`✔ Updated: ${filePath}`);
}
} catch (err) {
console.error(`✖ Error processing ${filePath}:`, err);
}
}

function walkDir(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) walkDir(fullPath);
else if (entry.isFile()) replaceInFile(fullPath);
}
}

walkDir(ROOT);
console.log('✅ Replacement complete.');
24 changes: 24 additions & 0 deletions crates/bindings-typescript/test-react-router-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
29 changes: 29 additions & 0 deletions crates/bindings-typescript/test-react-router-app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @clockworklabs/test-app

## 0.0.1

### Patch Changes

- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
- @clockworklabs/[email protected]

## 0.0.3-rc1.0

### Patch Changes

- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
- @clockworklabs/[email protected]

## 0.0.2

### Patch Changes

- Updated dependencies [[`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d), [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf), [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912)]:
- @clockworklabs/[email protected]

## 0.0.1

### Patch Changes

- Updated dependencies [[`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8), [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1), [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213), [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a)]:
- @clockworklabs/[email protected]
30 changes: 30 additions & 0 deletions crates/bindings-typescript/test-react-router-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: __dirname,
},
};
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
13 changes: 13 additions & 0 deletions crates/bindings-typescript/test-react-router-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
33 changes: 33 additions & 0 deletions crates/bindings-typescript/test-react-router-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@clockworklabs/test-app",
"private": true,
"version": "0.0.1",
"type": "module",
"files": [
"src"
],
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"format": "prettier . --write --ignore-path ../../../.prettierignore",
"lint": "eslint . && prettier . --check --ignore-path ../../../.prettierignore",
"preview": "vite preview",
"generate": "cargo build -p spacetimedb-standalone && cargo run -p spacetimedb-cli generate --lang typescript --out-dir src/module_bindings --project-path server && prettier --write src/module_bindings && node ./replace-spacetimedb.js && prettier --write src/module_bindings",
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path server",
"spacetime:start": "spacetime start server",
"spacetime:publish:local": "spacetime publish game --project-path server --server local",
"spacetime:publish": "spacetime publish game --project-path server --server testnet"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.9.4"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"typescript": "^5.2.2",
"vite": "^7.1.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env node

/**
* Cross-platform replacement script
* Equivalent to:
* find src/module_bindings -type f -exec perl -pi -e 's#spacetimedb#../../../src/index#g' {}
*/

import fs from 'fs';
import path from 'path';

const ROOT = path.resolve('src/module_bindings');
const SEARCH = /spacetimedb/g;
const REPLACEMENT = '../../../src/index';

function replaceInFile(filePath) {
try {
let content = fs.readFileSync(filePath, 'utf8');
if (SEARCH.test(content)) {
const updated = content.replace(SEARCH, REPLACEMENT);
fs.writeFileSync(filePath, updated, 'utf8');
console.log(`✔ Updated: ${filePath}`);
}
} catch (err) {
console.error(`✖ Error processing ${filePath}:`, err);
}
}

function walkDir(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) walkDir(fullPath);
else if (entry.isFile()) replaceInFile(fullPath);
}
}

walkDir(ROOT);
console.log('✅ Replacement complete.');
17 changes: 17 additions & 0 deletions crates/bindings-typescript/test-react-router-app/server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Spacetime ignore
/.spacetime
14 changes: 14 additions & 0 deletions crates/bindings-typescript/test-react-router-app/server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "typescript-test-react-router-app"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["cdylib"]

[dependencies]
spacetimedb = "1.2.0"
log = "0.4"
anyhow = "1.0"
79 changes: 79 additions & 0 deletions crates/bindings-typescript/test-react-router-app/server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use spacetimedb::{reducer, table, Identity, ReducerContext, Table};

#[table(public, name = counter)]
struct Counter {
#[primary_key]
id: u32,
count: u32,
}

#[table(public, name = user)]
#[table(public, name = offline_user)]
struct User {
#[primary_key]
identity: Identity,
has_incremented_count: u32,
}

#[reducer(init)]
fn init(ctx: &ReducerContext) {
ctx.db.counter().insert(Counter { id: 0, count: 0 });
}

#[reducer(client_connected)]
fn client_connected(ctx: &ReducerContext) {
let existing_user = ctx.db.offline_user().identity().find(ctx.sender);
if let Some(user) = existing_user {
ctx.db.user().insert(user);
ctx.db.offline_user().identity().delete(ctx.sender);
return;
}
ctx.db.offline_user().insert(User {
identity: ctx.sender,
has_incremented_count: 0,
});
}

#[reducer(client_disconnected)]
fn client_disconnected(ctx: &ReducerContext) -> Result<(), String> {
let existing_user = ctx.db.user().identity().find(ctx.sender).ok_or("User not found")?;
ctx.db.offline_user().insert(existing_user);
ctx.db.user().identity().delete(ctx.sender);
Ok(())
}

#[reducer]
fn increment_counter(ctx: &ReducerContext) -> Result<(), String> {
let mut counter = ctx.db.counter().id().find(0).ok_or("Counter not found")?;
counter.count += 1;
ctx.db.counter().id().update(counter);

let mut user = ctx.db.user().identity().find(ctx.sender).ok_or("User not found")?;
user.has_incremented_count += 1;
ctx.db.user().identity().update(user);

Ok(())
}

#[reducer]
fn clear_counter(ctx: &ReducerContext) {
for row in ctx.db.counter().iter() {
ctx.db.counter().id().delete(row.id);
}

for row in ctx.db.user().iter() {
let user = User {
identity: row.identity,
has_incremented_count: 0,
};
ctx.db.user().identity().update(user);
}

for row in ctx.db.offline_user().iter() {
let user = User {
identity: row.identity,
has_incremented_count: 0,
};
ctx.db.offline_user().identity().update(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/module_bindings/** linguist-generated=true
Loading
Loading