spacetime-studio 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # 🛸 SpacetimeDB Studio
2
+
3
+ A local web-based database studio for [SpacetimeDB](https://spacetimedb.com) — like Drizzle Studio, but for SpacetimeDB.
4
+
5
+ Run one command and get a full UI to browse your tables, write and execute SQL queries, and inspect your schema.
6
+
7
+ ## Requirements
8
+
9
+ - [SpacetimeDB CLI](https://spacetimedb.com/install) installed and on your PATH
10
+ - A running SpacetimeDB instance with at least one database
11
+
12
+ ## Usage
13
+
14
+ ```bash
15
+ npx spacetime-studio <database>
16
+ bunx spacetime-studio <database> # or w/ bun (faster)
17
+ ```
18
+
19
+ This opens the studio at `http://localhost:5555` pointed at your database.
20
+
21
+ ### Options
22
+
23
+ ```
24
+ spacetime-studio [options] [database]
25
+
26
+ Arguments:
27
+ database Database name to connect to
28
+
29
+ Options:
30
+ --db <database> Database name (alternative to positional argument)
31
+ --port <port> Port to run the studio on (default: 5555)
32
+ --host <host> SpacetimeDB host (default: localhost)
33
+ --debug Show debug path information
34
+ -h, --help Show help
35
+ -V, --version Show version
36
+ ```
37
+
38
+ ### Examples
39
+
40
+ ```bash
41
+ # Connect to a database
42
+ spacetime-studio my-database
43
+
44
+ # Custom port
45
+ spacetime-studio my-database --port 3000 # WIP
46
+
47
+ # Custom host
48
+ spacetime-studio my-database --host 127.0.0.1 # WIP
49
+
50
+ # Using the --db flag
51
+ spacetime-studio --db my-database
52
+ ```
53
+
54
+ ## ✨ Features
55
+
56
+ - 📝 **SQL Editor** — Write and run SQL queries with syntax highlighting and autocompletion
57
+ - ⌨️ **Vim mode** — Full Vim keybindings in the editor (toggle in settings)
58
+ - 📊 **Results table** — View query results in a sortable, interactive table
59
+ - ✏️ **Inline editing** — Click a cell to edit it; generates and runs the UPDATE statement automatically
60
+ - 🔍 **Schema inspector** — Browse your tables, columns, and reducers
61
+ - ⚡ **Keyboard shortcut** — `Cmd+Enter` / `Ctrl+Enter` to run a query (or just the selection)
62
+
63
+ ## 🛠️ Development
64
+
65
+ This is a Bun monorepo. To get started:
66
+
67
+ ```bash
68
+ bun install
69
+
70
+ # Run the studio app (hot reload)
71
+ bun run dev:studio
72
+
73
+ # Run the CLI in dev mode
74
+ bun run dev:cli
75
+
76
+ # Build everything
77
+ bun run build
78
+ ```
79
+
80
+ ### Project Structure
81
+
82
+ ```
83
+ spacetimedb-studio/
84
+ ├── apps/
85
+ │ └── studio/ # SolidJS + Vike web app (the UI)
86
+ └── packages/
87
+ └── cli/ # CLI binary published to npm as spacetime-studio
88
+ ```
89
+
90
+ ### 📦 Publishing
91
+
92
+ This project uses [Changesets](https://github.com/changesets/changesets) for versioning.
93
+
94
+ ```bash
95
+ # Create a changeset
96
+ bun run changeset
97
+
98
+ # Bump versions
99
+ bun run changeset version
100
+
101
+ # Publish (done automatically via GitHub Actions on merge to main)
102
+ bun run publish-ci
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "spacetime-studio",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
+ "files": [
6
+ "dist"
7
+ ],
5
8
  "bin": {
6
9
  "spacetime-studio": "./dist/spacetime-studio"
7
10
  },
8
11
  "scripts": {
9
12
  "dev": "bun run src/cli.ts",
10
- "build:cli": "bun build ./src/cli.ts --compile --outfile ./dist/spacetime-studio --minify",
11
- "build:copy-studio": "cp -r ../../apps/studio/dist ./dist/studio/dist",
12
- "build": "bun run build:cli && bun run build:copy-studio",
13
+ "build:cli": "bun run build.ts",
14
+ "build:copy-studio": "mkdir -p ./dist/studio && cp ../../apps/studio/dist/server/bundled.mjs ./dist/studio/server.mjs && cp -r ../../apps/studio/dist/client ./dist/studio/client",
15
+ "build": "rm -rf ./dist && bun run build:cli && bun run build:copy-studio",
13
16
  "check": "tsc --noEmit"
14
17
  },
15
18
  "dependencies": {
package/CHANGELOG.md DELETED
@@ -1,7 +0,0 @@
1
- # spacetime-studio
2
-
3
- ## 0.0.1
4
-
5
- ### Patch Changes
6
-
7
- - [`a6acc7d`](https://github.com/Blankeos/spacetimedb-studio/commit/a6acc7deb92dc148eaaf79294892ee1cf3b6dcf5) Thanks [@Blankeos](https://github.com/Blankeos)! - feat: first release!
package/src/cli.ts DELETED
@@ -1,313 +0,0 @@
1
- #!/usr/bin/env bun
2
- import process from "node:process"
3
- import chalk from "chalk"
4
- import { spawn } from "child_process"
5
- import { program } from "commander"
6
- import { existsSync } from "fs"
7
- import { join, resolve } from "path"
8
-
9
- const DEFAULT_PORT = 5555
10
- const DEFAULT_HOST = "localhost"
11
-
12
- function findStudioPath(): string | null {
13
- // 1. Check environment variable override
14
- if (process.env.SPACETIME_STUDIO_PATH) {
15
- if (existsSync(process.env.SPACETIME_STUDIO_PATH)) {
16
- return process.env.SPACETIME_STUDIO_PATH
17
- }
18
- }
19
-
20
- // 2. Use import.meta.dir which works in both dev and compiled mode
21
- const baseDir = import.meta.dir
22
-
23
- // Check if we're in compiled mode - bun compile puts us in /$bunfs/root
24
- const isCompiled = baseDir === "/$bunfs/root" || baseDir === "/$bunfs"
25
-
26
- // For compiled binaries: prioritize paths relative to cwd
27
- if (isCompiled) {
28
- // Check packages/cli/dist/studio (for running from packages/cli)
29
- const cliDistPath = join(process.cwd(), "dist/studio")
30
- if (existsSync(join(cliDistPath, "dist/server/index.mjs"))) {
31
- // Check if apps/studio exists (for node_modules)
32
- const appsStudioPath = join(process.cwd(), "../../apps/studio")
33
- if (existsSync(join(appsStudioPath, "dist/server/index.mjs"))) {
34
- return appsStudioPath // Prefer original location for node_modules access
35
- }
36
- return cliDistPath
37
- }
38
-
39
- // Check current working directory for studio folder
40
- const cwdStudioPath = join(process.cwd(), "studio")
41
- if (existsSync(join(cwdStudioPath, "dist/server/index.mjs"))) {
42
- return cwdStudioPath
43
- }
44
-
45
- // Check apps/studio from cwd (for monorepo root)
46
- const cwdAppsPath = join(process.cwd(), "apps/studio")
47
- if (existsSync(join(cwdAppsPath, "dist/server/index.mjs"))) {
48
- return cwdAppsPath
49
- }
50
-
51
- return null
52
- }
53
-
54
- // Development mode: apps/studio relative to packages/cli/src
55
- const devPath = resolve(baseDir, "../../../apps/studio")
56
- const devServerPath = join(devPath, "dist/server/index.mjs")
57
- if (existsSync(devServerPath)) {
58
- return devPath
59
- }
60
-
61
- // Fallback: check cwd-based paths
62
- const cwdStudioPath = join(process.cwd(), "studio")
63
- if (existsSync(join(cwdStudioPath, "dist/server/index.mjs"))) {
64
- return cwdStudioPath
65
- }
66
-
67
- const cwdAppsPath = join(process.cwd(), "apps/studio")
68
- if (existsSync(join(cwdAppsPath, "dist/server/index.mjs"))) {
69
- return cwdAppsPath
70
- }
71
-
72
- return null
73
- }
74
-
75
- function debugPaths(): void {
76
- const baseDir = import.meta.dir
77
- const isCompiled = baseDir === "/$bunfs/root" || baseDir === "/$bunfs"
78
-
79
- console.log("Debug: Path resolution")
80
- console.log(chalk.gray(` import.meta.dir: ${baseDir}`))
81
- console.log(chalk.gray(` isCompiled: ${isCompiled}`))
82
- console.log(chalk.gray(` cwd: ${process.cwd()}`))
83
-
84
- if (isCompiled) {
85
- const cliDistPath = join(process.cwd(), "dist/studio")
86
- const appsStudioPath = join(process.cwd(), "../../apps/studio")
87
-
88
- console.log(chalk.gray(` cliDistPath: ${cliDistPath}`))
89
- console.log(
90
- chalk.gray(
91
- ` cliDistPath server exists: ${existsSync(join(cliDistPath, "dist/server/index.mjs"))}`
92
- )
93
- )
94
- console.log(chalk.gray(` appsStudioPath: ${appsStudioPath}`))
95
- console.log(
96
- chalk.gray(
97
- ` appsStudioPath server exists: ${existsSync(join(appsStudioPath, "dist/server/index.mjs"))}`
98
- )
99
- )
100
- } else {
101
- const devPath = resolve(baseDir, "../../../apps/studio")
102
- console.log(chalk.gray(` devPath: ${devPath}`))
103
- console.log(
104
- chalk.gray(` devServerPath exists: ${existsSync(join(devPath, "dist/server/index.mjs"))}`)
105
- )
106
- }
107
- }
108
-
109
- program
110
- .name("spacetime-studio")
111
- .description("A local web-based database studio for SpacetimeDB databases")
112
- .version("0.0.1")
113
- .argument("[database]", "Database name to connect to")
114
- .option("-d, --db <database>", "Database name to connect to")
115
- .option("-p, --port <port>", "Port to run studio on", String(DEFAULT_PORT))
116
- .option("-h, --host <host>", "Host to bind to", DEFAULT_HOST)
117
- .option("--dev", "Run in development mode with hot reload")
118
- .option("--debug", "Show debug information about paths")
119
- .action(
120
- async (
121
- database: string | undefined,
122
- options: { db?: string; port: string; host: string; dev?: boolean; debug?: boolean }
123
- ) => {
124
- const dbName = database || options.db
125
-
126
- if (options.debug) {
127
- debugPaths()
128
- process.exit(0)
129
- }
130
-
131
- if (!dbName) {
132
- console.error(chalk.red("Error: Database name is required"))
133
- console.log(chalk.gray("Usage: spacetime-studio <database>"))
134
- console.log(chalk.gray(" spacetime-studio --db <database>"))
135
- process.exit(1)
136
- }
137
-
138
- const port = parseInt(options.port, 10)
139
- const host = options.host
140
- const devMode = options.dev
141
-
142
- console.log(chalk.cyan(`\n SpacetimeDB Studio\n`))
143
- console.log(chalk.gray(` Database: ${chalk.white(dbName)}`))
144
- console.log(chalk.gray(` Host: ${host}`))
145
- console.log(chalk.gray(` Port: ${port}`))
146
-
147
- // Validate database exists
148
- console.log(chalk.dim("\n Validating database..."))
149
- const validation = await validateDatabase(dbName)
150
-
151
- if (!validation.valid) {
152
- console.error(chalk.red(`\n Error: ${validation.error}`))
153
- console.log(chalk.gray("\n Make sure:"))
154
- console.log(chalk.gray(" 1. SpacetimeDB is running: spacetime start"))
155
- console.log(chalk.cyan(` 2. Database exists: spacetime publish ${dbName}`))
156
- process.exit(1)
157
- }
158
-
159
- console.log(chalk.green(` ✓ Database "${dbName}" found`))
160
-
161
- if (validation.tables !== undefined) {
162
- console.log(
163
- chalk.dim(` ${validation.tables} table(s), ${validation.reducers} reducer(s)`)
164
- )
165
- }
166
-
167
- // Set environment for the studio app
168
- process.env.SPACETIME_DB = dbName
169
- process.env.PORT = String(port)
170
- process.env.HOST = host
171
-
172
- // Find studio path
173
- const studioPath = findStudioPath()
174
-
175
- if (!studioPath) {
176
- console.error(chalk.red("\n Error: Studio app not found."))
177
- console.log(chalk.gray("\n The studio assets are not bundled."))
178
- console.log(chalk.gray(" For development, run from the project root:"))
179
- console.log(chalk.cyan(" cd packages/cli && bun run src/cli.ts <database>"))
180
- console.log(chalk.gray("\n For production, build first:"))
181
- console.log(chalk.cyan(" cd packages/cli && bun run build"))
182
- process.exit(1)
183
- }
184
-
185
- const studioUrl = `http://${host}:${port}`
186
- console.log(chalk.green(`\n Studio running at: ${chalk.bold(studioUrl)}`))
187
- if (devMode) {
188
- console.log(chalk.dim(` Mode: development (hot reload enabled)`))
189
- }
190
- console.log(chalk.gray(` Press Ctrl+C to stop\n`))
191
-
192
- // Run the studio - always use bun to run the server script
193
- const serverPath = join(studioPath, "dist/server/index.mjs")
194
-
195
- if (!existsSync(serverPath)) {
196
- console.error(chalk.red("\n Error: Studio not built."))
197
- console.log(chalk.gray(" Run: cd apps/studio && bun run build"))
198
- process.exit(1)
199
- }
200
-
201
- if (devMode) {
202
- // Development: spawn vike dev server
203
- const serverProcess = spawn("bun", ["run", "dev"], {
204
- cwd: studioPath,
205
- env: {
206
- ...process.env,
207
- SPACETIME_DB: dbName,
208
- PORT: String(port),
209
- },
210
- stdio: "inherit",
211
- shell: true,
212
- })
213
-
214
- serverProcess.on("error", (err: Error) => {
215
- console.error(chalk.red(`Server error: ${err.message}`))
216
- process.exit(1)
217
- })
218
-
219
- process.on("SIGINT", () => {
220
- console.log(chalk.yellow("\n Shutting down..."))
221
- serverProcess.kill()
222
- process.exit(0)
223
- })
224
- } else {
225
- // Production: run the built server from studio path
226
- // Note: studioPath should point to apps/studio which has node_modules
227
- const serverProcess = spawn("bun", ["run", serverPath], {
228
- cwd: studioPath,
229
- env: {
230
- ...process.env,
231
- SPACETIME_DB: dbName,
232
- PORT: String(port),
233
- NODE_ENV: "production",
234
- },
235
- stdio: "inherit",
236
- })
237
-
238
- serverProcess.on("error", (err: Error) => {
239
- console.error(chalk.red(`\n Server error: ${err.message}`))
240
- process.exit(1)
241
- })
242
-
243
- process.on("SIGINT", () => {
244
- console.log(chalk.yellow("\n Shutting down..."))
245
- serverProcess.kill()
246
- process.exit(0)
247
- })
248
- }
249
- }
250
- )
251
-
252
- async function validateDatabase(dbName: string): Promise<{
253
- valid: boolean
254
- error?: string
255
- tables?: number
256
- reducers?: number
257
- }> {
258
- return new Promise((resolve) => {
259
- const child = spawn("spacetime", ["describe", dbName, "--json"], {
260
- stdio: ["ignore", "pipe", "pipe"],
261
- })
262
-
263
- let stdout = ""
264
- let stderr = ""
265
-
266
- child.stdout.on("data", (data) => {
267
- stdout += data.toString()
268
- })
269
-
270
- child.stderr.on("data", (data) => {
271
- stderr += data.toString()
272
- })
273
-
274
- child.on("close", (code) => {
275
- if (code !== 0) {
276
- const errorMsg = stderr.trim() || `Database "${dbName}" not found`
277
- resolve({
278
- valid: false,
279
- error:
280
- errorMsg.includes("not find") ||
281
- errorMsg.includes("failed to find") ||
282
- errorMsg.includes("Connection refused")
283
- ? `"${dbName}" - Check if SpacetimeDB is running (spacetime start) and database exists (spacetime publish ${dbName})`
284
- : errorMsg,
285
- })
286
- return
287
- }
288
-
289
- try {
290
- const schema = JSON.parse(stdout) as {
291
- tables?: unknown[]
292
- reducers?: unknown[]
293
- }
294
- resolve({
295
- valid: true,
296
- tables: schema.tables?.length ?? 0,
297
- reducers: schema.reducers?.length ?? 0,
298
- })
299
- } catch {
300
- resolve({ valid: true })
301
- }
302
- })
303
-
304
- child.on("error", (err) => {
305
- resolve({
306
- valid: false,
307
- error: `Failed to run spacetime CLI: ${err.message}. Is SpacetimeDB installed?`,
308
- })
309
- })
310
- })
311
- }
312
-
313
- program.parse()
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "allowJs": true,
5
- "esModuleInterop": true,
6
- "forceConsistentCasingInFileNames": true,
7
- "resolveJsonModule": true,
8
- "skipLibCheck": true,
9
- "module": "ESNext",
10
- "noEmit": true,
11
- "moduleResolution": "bundler",
12
- "target": "ES2022",
13
- "lib": ["ESNext"],
14
- "types": ["@types/bun"]
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist"]
18
- }