Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (toolbar): Adding new top level package for web developer toolbar #446

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
49 changes: 49 additions & 0 deletions .github/workflows/debug_toolbar_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Components / Debug toolbar

on:
push:
branches:
- main
pull_request:
paths:
- 'pnpm-lock.yaml'
- 'toolbar/**'
- '!toolbar/**README.md'

defaults:
run:
working-directory: toolbar

concurrency:
group: clients-typescript-${{ github.ref }}
cancel-in-progress: true

jobs:
verify_formatting:
name: Check formatting & linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run check-styleguide
check_types:
name: Check types
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm run typecheck
5 changes: 4 additions & 1 deletion examples/linearlite/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ const App = () => {
const init = async () => {
try {
const client = await initElectric()

setElectric(client)

const { synced } = await client.db.issue.sync({
include: {
comment: true,
Expand All @@ -67,6 +69,7 @@ const App = () => {
}

init()

}, [])

if (electric === undefined) {
Expand All @@ -81,7 +84,7 @@ const App = () => {
<Route path="/issue/:id" element={<Issue />} />
</Routes>
)

return (
<ElectricProvider db={electric}>
<MenuContext.Provider value={{ showMenu, setShowMenu }}>
Expand Down
1 change: 1 addition & 0 deletions examples/web-wa-sqlite/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ let buildParams = {
plugins: [inlineImage()],
};

paulharter marked this conversation as resolved.
Show resolved Hide resolved

(async () => {
fs.removeSync("dist");
fs.copySync("public", "dist");
Expand Down
2 changes: 1 addition & 1 deletion examples/web-wa-sqlite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
"shelljs": "^0.8.5",
"typescript": "^4.4.3"
}
}
}
203 changes: 101 additions & 102 deletions examples/web-wa-sqlite/public/wa-sqlite-async.mjs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions examples/web-wa-sqlite/src/Example.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState} from 'react'

import { LIB_VERSION } from 'electric-sql/version'
import { makeElectricContext, useLiveQuery } from 'electric-sql/react'
Expand Down Expand Up @@ -37,7 +37,6 @@ export const Example = () => {
if (!isMounted) {
return
}

setElectric(electric)
}

Expand Down
1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ packages:
- 'examples/starter'
- 'generator'
- 'components/electric'
- 'toolbar'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a wider question but looking at the monorepo structure, I wonder why generator and toolbar are not in components.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree btw, I feel like toolbar definitely belongs in components

10 changes: 10 additions & 0 deletions toolbar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
dist
node_modules
# local dev app
dev-app
package.dev.json

# We're ignoring the lockfiles here because this example is meant to act like `npx create-electric-app` in that they use latest deps
pnpm-lock.yaml
package-lock.json
yarn.lock
1 change: 1 addition & 0 deletions toolbar/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
12 changes: 12 additions & 0 deletions toolbar/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*.log
*.md
.DS_Store
dist/
dev-app/dist/
dev-app/public/
dev-app/db/
dev-app/backend/
node_modules/
.gitignore
.github/
dev-app/src/generated/
4 changes: 4 additions & 0 deletions toolbar/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"semi": false,
"singleQuote": true
}
92 changes: 92 additions & 0 deletions toolbar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<a href="https://electric-sql.com">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/electric-sql/meta/main/identity/ElectricSQL-logo-light-trans.svg"
/>
<source media="(prefers-color-scheme: light)"
srcset="https://raw.githubusercontent.com/electric-sql/meta/main/identity/ElectricSQL-logo-black.svg"
/>
<img alt="ElectricSQL logo"
src="https://raw.githubusercontent.com/electric-sql/meta/main/identity/ElectricSQL-logo-black.svg"
/>
</picture>
</a>

# ElectricSQL - Developer Toolbar

These are a collection of tools that can be used by developers to help them debug their ElectricSQL apps

## Adding this toolbar to your project

Add the toolbar to your project's `devDependencies` in `package.json`

```sh
"devDependencies": {
...
"@electric-sql/debug-toolbar": "latest",
...
}
```

In your code add the toolbar after initialising the electric database.

First some imports:

```typescript
// toolbar imports
import { globalRegistry } from "electric-sql/satellite";
import addToolbar, { typescriptApi } from '@electric-sql/debug-toolbar'
import '@electric-sql/debug-toolbar/dist/index.cjs.css'
```

Then add the toolbar after calling `electrify`:

```typescript
// Add the debug toolbar
addToolbar(typescriptApi(globalRegistry))
```

This will add the toolbar to the bottom of your window

## Development

To develop with the toolbar you will need a project with an ElectricSQL database added.
You can use one of the example projects such as `examples/web-wa-sqlite`,
but you will have to make a few changes to the project's configuration:

Add web-wa-sqlite to the pnpm workspace by adding this line to `pnpm-workspace.yaml`

```yaml
- 'examples/web-wa-sqlite
```

Change the `web-wa-sqlite` dev dependencies in `Package.json` to use the local version of the
toolbar rather than the published one and add a dependency on `zod`:

```json
"devDependencies": {
...
"@electric-sql/debug-toolbar": "workspace:*",
...
"zod": "^3.21.1"
},
```
Run `pnpm install` in the root of this repo to install everthing.

Build the toolbar:

```json
cd toolbar
yarn build
```
Run the example:

```
cd ../examples/web-wa-sqlite
yarn build
yarn start
```

You sould now see the local version of the toolbar being used by the app.
When you mofify the toolbar you will have to rebuild it for the changes to appear in the app.

31 changes: 31 additions & 0 deletions toolbar/builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { build } = require('esbuild')
const { dependencies } = require('./package.json')

const inlineImage = require('esbuild-plugin-inline-image')

const entryFile = 'src/index.tsx'
const shared = {
bundle: true,
entryPoints: [entryFile],
// Treat all dependencies in package.json as externals to keep bundle size to a minimum
external: Object.keys(dependencies),
logLevel: 'info',
minify: true,
sourcemap: true,
}

build({
...shared,
format: 'esm',
outfile: './dist/index.esm.js',
target: ['esnext', 'node12.22.0'],
plugins: [inlineImage()],
})

build({
...shared,
format: 'cjs',
outfile: './dist/index.cjs.js',
target: ['esnext', 'node12.22.0'],
plugins: [inlineImage()],
})
6 changes: 6 additions & 0 deletions toolbar/custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module '*.svg' {
import React = require('react')
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>
const src: string
export default src
}
39 changes: 39 additions & 0 deletions toolbar/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@electric-sql/debug-toolbar",
"version": "0.1.0",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"author": "ElectricSQL",
"license": "Apache-2.0",
"scripts": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have the backend and demo-app related scripts in the package.json here. The package.json is copied into the dist. Could the library package.json be clean and the demo-app package.json have the db:migrate client:generate scripts etc.?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I would even argue that we don't want to package a demo-app with the toolbar. Demo apps should be found in examples.

"build": "node builder.js && tsc --emitDeclarationOnly --outDir dist",
"check-styleguide": "prettier --check --loglevel warn .",
"typecheck": "tsc --noEmit"
},
"engines": {
"node": ">=16.11.0"
},
"dependencies": {
"codemirror": "^5.65.15",
"react": "^18.2.0",
"react-codemirror2": "^7.3.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
paulharter marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per above on scripts, if you cut out the dev dependencies to the demo-app package.json then this would maybe allow you to remove from here? I.e.: are there dev dependencies to build and test the toolbar that live here and dev dependencies to run the demo app that belong in the demo app?

"@types/node": ">=16.11.0",
"@types/react": "^18.0.18",
"@types/react-dom": "^18.0.11",
"electric-sql": "latest",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a real dev dependency?
It's probably a remnant of the local dev app you use.
But since we don't checkout the dev app we probably don't need this dev dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it need them all - at least it doesn't build without them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in src/api/toolbar-typescript.ts the global registry is being imported:

import { GlobalRegistry } from 'electric-sql/satellite'

So electric-sql needs to be a dependency rather than a dev dependency.
Perhaps it should even be a peer dependency.

Copy link
Contributor

@kevin-dp kevin-dp Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticing now that it is listed as a peer-dependency. Why does it need to be a dev dependency also then? Is it being used in some test somewhere? I don't think there are any tests.

"esbuild": "^0.16.17",
"esbuild-plugin-inline-image": "^0.0.9",
"prettier": "3.0.3",
"typescript": "^4.4.3"
},
"peerDependencies": {
"electric-sql": "latest"
},
"files": [
"dist"
]
}
8 changes: 8 additions & 0 deletions toolbar/src/api/toolbar-interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Row, Statement } from 'electric-sql/dist/util'

export interface ToolbarInterface {
getSatelliteNames(): string[]
getSatelliteStatus(name: string): string
resetDB(dbName: string): void
queryDB(dbName: string, statement: Statement): Promise<Row[]>
}
36 changes: 36 additions & 0 deletions toolbar/src/api/toolbar-typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ToolbarInterface } from './toolbar-interface'
import { Row, Statement, ConnectivityState } from 'electric-sql/dist/util'
import { GlobalRegistry } from 'electric-sql/satellite'

export class ToolbarTypescript implements ToolbarInterface {
private globalRegistry: GlobalRegistry

constructor(globalRegistry: GlobalRegistry) {
this.globalRegistry = globalRegistry
}

getSatelliteNames(): string[] {
return Object.keys(this.globalRegistry.satellites)
}

getSatelliteStatus(name: string): ConnectivityState | 'Not found' {
const sat = this.globalRegistry.satellites[name]
return sat?.connectivityState ?? 'Not found'
}

resetDB(dbName: string): void {
const DBDeleteRequest = window.indexedDB.deleteDatabase(dbName)
DBDeleteRequest.onsuccess = function () {
console.log('Database deleted successfully')
}
// the indexedDB cannot be deleted if the database connection is still open,
// so we need to reload the page to close any open connections.
// On reload, the database will be recreated.
window.location.reload()
}

queryDB(dbName: string, statement: Statement): Promise<Row[]> {
const sat = this.globalRegistry.satellites[dbName]
return sat?.adapter.query(statement) ?? Promise.resolve([])
}
}
Loading
Loading