diff --git a/demos/diagnostics-app/.gitignore b/demos/diagnostics-app/.gitignore deleted file mode 100644 index 0cc2f139..00000000 --- a/demos/diagnostics-app/.gitignore +++ /dev/null @@ -1,47 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts - -# ide -.idea -.fleet -.vscode - -# PWA -**/public/workbox-*.js -**/public/sw.js -**/public/swe-worker-* -**/public/worker-*.js -**/public/fallback-*.js diff --git a/demos/diagnostics-app/CHANGELOG.md b/demos/diagnostics-app/CHANGELOG.md deleted file mode 100644 index 16c77018..00000000 --- a/demos/diagnostics-app/CHANGELOG.md +++ /dev/null @@ -1,11 +0,0 @@ -# diagnostics-app - -## 0.1.1 - -### Patch Changes - -- Updated dependencies [9bf5a76] - - @journeyapps/powersync-react@1.1.0 - - @journeyapps/powersync-sdk-web@0.3.3 - -## 0.0.1 diff --git a/demos/diagnostics-app/README.md b/demos/diagnostics-app/README.md deleted file mode 100644 index 7013485d..00000000 --- a/demos/diagnostics-app/README.md +++ /dev/null @@ -1 +0,0 @@ -# Diagnostics app diff --git a/demos/diagnostics-app/package.json b/demos/diagnostics-app/package.json deleted file mode 100644 index 0ff986ce..00000000 --- a/demos/diagnostics-app/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@journeyapps/powersync-diagnostics-app", - "version": "0.1.1", - "private": true, - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "preview": "vite preview", - "start": "pnpm build && pnpm preview" - }, - "dependencies": { - "@journeyapps/powersync-react": "workspace:*", - "@journeyapps/powersync-sdk-web": "workspace:*", - "@journeyapps/wa-sqlite": "~0.1.1", - "@mui/material": "^5.15.12", - "@mui/x-data-grid": "^6.19.6", - "js-logger": "^1.6.1", - "lodash": "^4.17.21", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.22.3" - }, - "devDependencies": { - "@types/lodash": "^4.14.202", - "@types/node": "^20.11.25", - "@types/react": "^18.2.64", - "@types/react-dom": "^18.2.21", - "@vitejs/plugin-react": "^4.2.1", - "autoprefixer": "^10.4.18", - "babel-loader": "^9.1.3", - "typescript": "^5.4.2", - "vite": "^5.1.5", - "vite-plugin-pwa": "^0.19.2", - "vite-plugin-top-level-await": "^1.4.1", - "vite-plugin-wasm": "^3.3.0" - } -} diff --git a/demos/diagnostics-app/public/favicon.ico b/demos/diagnostics-app/public/favicon.ico deleted file mode 100644 index 96be8f92..00000000 Binary files a/demos/diagnostics-app/public/favicon.ico and /dev/null differ diff --git a/demos/diagnostics-app/public/icons/icon-192x192.png b/demos/diagnostics-app/public/icons/icon-192x192.png deleted file mode 100644 index 3cd46ff8..00000000 Binary files a/demos/diagnostics-app/public/icons/icon-192x192.png and /dev/null differ diff --git a/demos/diagnostics-app/public/icons/icon-256x256.png b/demos/diagnostics-app/public/icons/icon-256x256.png deleted file mode 100644 index 4798983e..00000000 Binary files a/demos/diagnostics-app/public/icons/icon-256x256.png and /dev/null differ diff --git a/demos/diagnostics-app/public/icons/icon-384x384.png b/demos/diagnostics-app/public/icons/icon-384x384.png deleted file mode 100644 index bc1e64e8..00000000 Binary files a/demos/diagnostics-app/public/icons/icon-384x384.png and /dev/null differ diff --git a/demos/diagnostics-app/public/icons/icon-512x512.png b/demos/diagnostics-app/public/icons/icon-512x512.png deleted file mode 100644 index c7d0a3a8..00000000 Binary files a/demos/diagnostics-app/public/icons/icon-512x512.png and /dev/null differ diff --git a/demos/diagnostics-app/public/icons/icon.png b/demos/diagnostics-app/public/icons/icon.png deleted file mode 100644 index eab59883..00000000 Binary files a/demos/diagnostics-app/public/icons/icon.png and /dev/null differ diff --git a/demos/diagnostics-app/public/powersync-logo.svg b/demos/diagnostics-app/public/powersync-logo.svg deleted file mode 100644 index 32c83dd1..00000000 --- a/demos/diagnostics-app/public/powersync-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/demos/diagnostics-app/src/app/globals.css b/demos/diagnostics-app/src/app/globals.css deleted file mode 100644 index 5ceb2604..00000000 --- a/demos/diagnostics-app/src/app/globals.css +++ /dev/null @@ -1,12 +0,0 @@ -:root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; -} - -body { - color: rgb(var(--foreground-rgb)); - min-height: 100vh; - margin: 0; - background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); -} diff --git a/demos/diagnostics-app/src/app/index.tsx b/demos/diagnostics-app/src/app/index.tsx deleted file mode 100644 index 49a9ccd4..00000000 --- a/demos/diagnostics-app/src/app/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import { RouterProvider } from 'react-router-dom'; -import { SystemProvider } from '../components/providers/SystemProvider'; -import { ThemeProviderContainer } from '../components/providers/ThemeProviderContainer'; -import { router } from './router'; - -const root = createRoot(document.getElementById('app')!); -root.render(); - -export function App() { - return ( - - - - - - ); -} diff --git a/demos/diagnostics-app/src/app/login.tsx b/demos/diagnostics-app/src/app/login.tsx deleted file mode 100644 index 638fea35..00000000 --- a/demos/diagnostics-app/src/app/login.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { LoginDetailsWidget } from '@/components/widgets/LoginDetailsWidget'; -import { useNavigate } from 'react-router-dom'; -import { DEFAULT_ENTRY_ROUTE } from '@/app/router'; -import { connector } from '@/components/providers/SystemProvider'; - -export default function LoginPage() { - const navigate = useNavigate(); - - return ( - { - await connector.signIn(values); - - navigate(DEFAULT_ENTRY_ROUTE); - }} - /> - ); -} diff --git a/demos/diagnostics-app/src/app/page.tsx b/demos/diagnostics-app/src/app/page.tsx deleted file mode 100644 index 59c3d3b2..00000000 --- a/demos/diagnostics-app/src/app/page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import { CircularProgress, Grid, styled } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; -import { DEFAULT_ENTRY_ROUTE, LOGIN_ROUTE } from './router'; -import { connector } from '@/components/providers/SystemProvider'; - -export type LoginFormParams = { - email: string; - password: string; -}; - -/** - * This page shows a loading spinner we detect a session - * and redirect either to the app or auth flow. - */ -export default function EntryPage() { - const navigate = useNavigate(); - - React.useEffect(() => { - if (connector.hasCredentials()) { - navigate(DEFAULT_ENTRY_ROUTE); - } else { - navigate(LOGIN_ROUTE); - } - }, []); - - return ( - - - - - - ); -} - -namespace S { - export const CenteredGrid = styled(Grid)` - display: flex; - justify-content: center; - align-items: center; - `; - - export const MainGrid = styled(CenteredGrid)` - min-height: 100vh; - `; -} diff --git a/demos/diagnostics-app/src/app/router.tsx b/demos/diagnostics-app/src/app/router.tsx deleted file mode 100644 index 0cacc549..00000000 --- a/demos/diagnostics-app/src/app/router.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Outlet, createBrowserRouter } from 'react-router-dom'; -import LoginPage from './login'; -import EntryPage from './page'; -import ViewsLayout from './views/layout'; -import SQLConsolePage from './views/sql-console/page'; - -export const LOGIN_ROUTE = '/login'; -export const SQL_CONSOLE_ROUTE = '/sql-console'; - -/** - * Navigate to this route after authentication - */ -export const DEFAULT_ENTRY_ROUTE = SQL_CONSOLE_ROUTE; - -export const router = createBrowserRouter([ - { - path: '/', - element: - }, - { - path: LOGIN_ROUTE, - element: - }, - { - element: ( - - - - ), - children: [ - { - path: SQL_CONSOLE_ROUTE, - element: - } - ] - } -]); diff --git a/demos/diagnostics-app/src/app/views/layout.tsx b/demos/diagnostics-app/src/app/views/layout.tsx deleted file mode 100644 index bf74eb79..00000000 --- a/demos/diagnostics-app/src/app/views/layout.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import ChecklistRtlIcon from '@mui/icons-material/ChecklistRtl'; -import ExitToAppIcon from '@mui/icons-material/ExitToApp'; -import MenuIcon from '@mui/icons-material/Menu'; -import NorthIcon from '@mui/icons-material/North'; -import SignalWifiOffIcon from '@mui/icons-material/SignalWifiOff'; -import SouthIcon from '@mui/icons-material/South'; -import TerminalIcon from '@mui/icons-material/Terminal'; -import WifiIcon from '@mui/icons-material/Wifi'; -import { - AppBar, - Box, - Divider, - Drawer, - IconButton, - List, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, - Toolbar, - Typography, - styled -} from '@mui/material'; -import React from 'react'; - -import { useNavigationPanel } from '@/components/navigation/NavigationPanelContext'; -import { usePowerSync } from '@journeyapps/powersync-react'; -import { useNavigate } from 'react-router-dom'; -import { LOGIN_ROUTE, SQL_CONSOLE_ROUTE } from '@/app/router'; - -export default function ViewsLayout({ children }: { children: React.ReactNode }) { - const powerSync = usePowerSync(); - const navigate = useNavigate(); - - const [syncStatus, setSyncStatus] = React.useState(powerSync.currentStatus); - const [openDrawer, setOpenDrawer] = React.useState(false); - const { title } = useNavigationPanel(); - - const NAVIGATION_ITEMS = React.useMemo( - () => [ - { - path: SQL_CONSOLE_ROUTE, - title: 'SQL Console', - icon: () => - }, - { - path: LOGIN_ROUTE, - title: 'Sign Out', - beforeNavigate: async () => { - await powerSync.disconnectAndClear(); - }, - icon: () => - } - ], - [powerSync] - ); - - React.useEffect(() => { - const l = powerSync.registerListener({ - statusChanged: (status) => { - setSyncStatus(status); - } - }); - return () => l?.(); - }, [powerSync]); - - return ( - - - - setOpenDrawer(!openDrawer)} - > - - - - {title} - - - - {syncStatus?.connected ? : } - - - setOpenDrawer(false)}> - - - - {NAVIGATION_ITEMS.map((item) => ( - - { - await item.beforeNavigate?.(); - navigate(item.path); - setOpenDrawer(false); - }} - > - {item.icon()} - - - - ))} - - - {children} - - ); -} - -namespace S { - export const MainBox = styled(Box)` - flex-grow: 1; - `; - - export const TopBar = styled(AppBar)` - margin-bottom: 20px; - `; - - export const PowerSyncLogo = styled('img')` - max-width: 250px; - max-height: 250px; - object-fit: contain; - padding: 20px; - `; -} diff --git a/demos/diagnostics-app/src/app/views/sql-console/page.tsx b/demos/diagnostics-app/src/app/views/sql-console/page.tsx deleted file mode 100644 index 011966f2..00000000 --- a/demos/diagnostics-app/src/app/views/sql-console/page.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React from 'react'; -import { usePowerSyncWatchedQuery } from '@journeyapps/powersync-react'; -import { Box, Button, Grid, TextField, styled } from '@mui/material'; -import { DataGrid } from '@mui/x-data-grid'; -import { NavigationPage } from '@/components/navigation/NavigationPage'; - -export type LoginFormParams = { - email: string; - password: string; -}; - -const DEFAULT_QUERY = ` -WITH oplog_stats AS (SELECT bucket, sum(length(data) + length(row_type) + length(row_id) + length(bucket) + length(key) + 50) as data_size, count() as operation_count FROM ps_oplog GROUP BY bucket) -SELECT local.id as name, stats.data_size, stats.operation_count, local.download_size, local.total_operations, local.downloading FROM local_bucket_data local JOIN oplog_stats stats ON stats.bucket = local.id`; - -export default function SQLConsolePage() { - const inputRef = React.useRef(); - const [query, setQuery] = React.useState(DEFAULT_QUERY); - const querySQLResult = usePowerSyncWatchedQuery(query); - - const queryDataGridResult = React.useMemo(() => { - const firstItem = querySQLResult?.[0]; - - return { - columns: firstItem - ? Object.keys(firstItem).map((field) => ({ - field, - flex: 1 - })) - : [], - rows: querySQLResult - }; - }, [querySQLResult]); - - return ( - - - - - { - const inputValue = inputRef.current?.value; - if (e.key == 'Enter' && inputValue) { - setQuery(inputValue); - } - }} - /> - - - - - - - {queryDataGridResult ? ( - - {queryDataGridResult.columns ? ( - ({ ...r, id: r.id ?? index })) ?? []} - columns={queryDataGridResult.columns} - initialState={{ - pagination: { - paginationModel: { - pageSize: 20 - } - } - }} - pageSizeOptions={[20]} - disableRowSelectionOnClick - /> - ) : null} - - ) : null} - - - ); -} - -namespace S { - export const MainContainer = styled(Box)` - padding: 20px; - `; - - export const QueryResultContainer = styled(Box)` - margin-top: 40px; - `; - - export const CenteredGrid = styled(Grid)` - display: flex; - justify-content: center; - align-items: center; - `; -} diff --git a/demos/diagnostics-app/src/components/navigation/NavigationPage.tsx b/demos/diagnostics-app/src/components/navigation/NavigationPage.tsx deleted file mode 100644 index ac0cc307..00000000 --- a/demos/diagnostics-app/src/components/navigation/NavigationPage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { useNavigationPanel } from './NavigationPanelContext'; -import { Box, styled } from '@mui/material'; - -/** - * Wraps a component with automatic navigation panel title management - */ -export const NavigationPage: React.FC> = ({ title, children }) => { - const navigationPanel = useNavigationPanel(); - - React.useEffect(() => { - navigationPanel.setTitle(title); - - return () => navigationPanel.setTitle(''); - }, [title, navigationPanel]); - - return {children}; -}; - -namespace S { - export const Container = styled(Box)` - margin: 10px; - `; -} diff --git a/demos/diagnostics-app/src/components/navigation/NavigationPanelContext.tsx b/demos/diagnostics-app/src/components/navigation/NavigationPanelContext.tsx deleted file mode 100644 index 4745475d..00000000 --- a/demos/diagnostics-app/src/components/navigation/NavigationPanelContext.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -export type NavigationPanelController = { - setTitle: (title: string) => void; - title: string; -}; - -export const NavigationPanelContext = React.createContext({ - setTitle: () => { - throw new Error(`No NavigationPanelContext has been provided`); - }, - title: '' -}); - -export const NavigationPanelContextProvider = ({ children }: { children: React.ReactNode }) => { - const [title, setTitle] = React.useState(''); - - return {children}; -}; - -export const useNavigationPanel = () => React.useContext(NavigationPanelContext); diff --git a/demos/diagnostics-app/src/components/providers/SystemProvider.tsx b/demos/diagnostics-app/src/components/providers/SystemProvider.tsx deleted file mode 100644 index 172fb919..00000000 --- a/demos/diagnostics-app/src/components/providers/SystemProvider.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { NavigationPanelContextProvider } from '@/components/navigation/NavigationPanelContext'; -import { AppSchema } from '@/library/powersync/AppSchema'; -import { TokenConnector } from '@/library/powersync/TokenConnector'; -import { PowerSyncContext } from '@journeyapps/powersync-react'; -import { WASQLitePowerSyncDatabaseOpenFactory } from '@journeyapps/powersync-sdk-web'; -import { CircularProgress } from '@mui/material'; -import Logger from 'js-logger'; -import React, { Suspense } from 'react'; - -Logger.useDefaults(); -Logger.setLevel(Logger.DEBUG); - -export const db = new WASQLitePowerSyncDatabaseOpenFactory({ - dbFilename: 'example.db', - schema: AppSchema -}).getInstance(); -export const connector = new TokenConnector(); - -if (connector.hasCredentials()) { - db.connect(connector); - connector.loadCheckpoint(); -} - -(window as any).db = db; - -export const SystemProvider = ({ children }: { children: React.ReactNode }) => { - return ( - }> - - {children} - - - ); -}; - -export default SystemProvider; diff --git a/demos/diagnostics-app/src/components/providers/ThemeProviderContainer.tsx b/demos/diagnostics-app/src/components/providers/ThemeProviderContainer.tsx deleted file mode 100644 index 9b9c1d61..00000000 --- a/demos/diagnostics-app/src/components/providers/ThemeProviderContainer.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; - -export const ThemeProviderContainer: React.FC> = ({ children }) => { - const theme = React.useMemo(() => { - return createTheme({ - palette: { - mode: 'dark', - primary: { - main: '#c44eff' - } - }, - typography: { - fontFamily: 'Rubik, sans-serif' - } - }); - }, []); - - return {children}; -}; diff --git a/demos/diagnostics-app/src/components/widgets/LoginDetailsWidget.tsx b/demos/diagnostics-app/src/components/widgets/LoginDetailsWidget.tsx deleted file mode 100644 index f5a3b5af..00000000 --- a/demos/diagnostics-app/src/components/widgets/LoginDetailsWidget.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import React from 'react'; -import { Box, Button, ButtonGroup, FormGroup, Paper, TextField, Typography, styled } from '@mui/material'; -import { Formik, FormikErrors } from 'formik'; - -export type LoginDetailsFormValues = { - token: string; - endpoint: string; -}; - -export type LoginAction = { - title: string; - onClick: (values: LoginDetailsFormValues) => any; -}; - -export type LoginDetailsWidgetProps = { - onSubmit: (values: LoginDetailsFormValues) => any; -}; - -export const LoginDetailsWidget: React.FC = (props) => { - return ( - - - Diagnostics Config - - - - - initialValues={{ token: '', endpoint: '' }} - validateOnChange={false} - validateOnBlur={false} - validate={(values) => { - const errors: FormikErrors = {}; - if (!values.token) { - errors.token = 'Required'; - } - - return errors; - }} - onSubmit={async (values, { setSubmitting, setFieldError }) => { - try { - const endpoint = values.endpoint || getTokenEndpoint(values.token); - if (endpoint == null) { - throw new Error('endpoint is required'); - } - await props.onSubmit({ - token: values.token, - endpoint - }); - } catch (ex: any) { - console.error(ex); - setSubmitting(false); - setFieldError('endpoint', ex.message); - } - }} - > - {({ values, errors, handleChange, handleBlur, isSubmitting, handleSubmit }) => ( -
- - - - - - - -
- )} - -
-
- ); -}; - -namespace S { - export const MainContainer = styled(Box)` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - min-height: 100vh; - `; - - export const LoginContainer = styled(Paper)` - width: 100%; - padding: 20px; - display: flex; - flex-grow: 1; - flex-direction: column; - justify-content: center; - - ${(props) => props.theme.breakpoints.up('sm')} { - flex-grow: 0; - max-width: 600px; - } - `; - - export const LoginHeader = styled(Typography)` - margin-bottom: 20px; - `; - - export const LogoBox = styled(Box)` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin: 40px; - `; - - export const Logo = styled('img')` - width: auto; - height: auto; - max-width: ${(props) => props.width}px; - max-height: ${(props) => props.height}px; - margin-bottom: 10px; - `; - - export const ActionButtonGroup = styled(ButtonGroup)` - margin-top: 20px; - width: 100%; - display: flex; - justify-content: end; - `; - - export const TextInput = styled(TextField)` - margin-bottom: 20px; - `; -} - -function getTokenEndpoint(token: string) { - try { - const [head, body, signature] = token.split('.'); - const payload = JSON.parse(atob(body)); - const aud = payload.aud as string | undefined; - if (aud?.endsWith('.journeyapps.com')) { - return aud; - } - return null; - } catch (e) { - return null; - } -} diff --git a/demos/diagnostics-app/src/index.html b/demos/diagnostics-app/src/index.html deleted file mode 100644 index de7a97c2..00000000 --- a/demos/diagnostics-app/src/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - -
- - diff --git a/demos/diagnostics-app/src/library/powersync/AppSchema.ts b/demos/diagnostics-app/src/library/powersync/AppSchema.ts deleted file mode 100644 index f6f61f73..00000000 --- a/demos/diagnostics-app/src/library/powersync/AppSchema.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { column, Schema, TableV2 } from '@journeyapps/powersync-sdk-web'; - -export const local_bucket_data = new TableV2( - { - total_operations: column.integer, - last_op: column.text, - download_size: column.integer, - downloading: column.integer - }, - { localOnly: true } -); - -export const AppSchema = new Schema({ - local_bucket_data -}); - -export type Database = (typeof AppSchema)['types']; -export type LocalBucketData = Database['local_bucket_data']; diff --git a/demos/diagnostics-app/src/library/powersync/TokenConnector.ts b/demos/diagnostics-app/src/library/powersync/TokenConnector.ts deleted file mode 100644 index 116159f6..00000000 --- a/demos/diagnostics-app/src/library/powersync/TokenConnector.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { db } from '@/components/providers/SystemProvider'; -import { - AbstractPowerSyncDatabase, - BaseObserver, - BucketState, - BucketStorageAdapter, - BucketStorageListener, - Checkpoint, - PowerSyncBackendConnector, - SyncDataBatch, - WebRemote, - WebStreamingSyncImplementation, - WebStreamingSyncImplementationOptions -} from '@journeyapps/powersync-sdk-web'; -import { LocalBucketData } from './AppSchema'; - -export interface Credentials { - token: string; - endpoint: string; -} - -export class TokenConnector implements PowerSyncBackendConnector { - async fetchCredentials() { - const value = localStorage.getItem('powersync_credentials'); - if (value == null) { - return null; - } - return JSON.parse(value); - } - - async uploadData(database: AbstractPowerSyncDatabase) { - // Discard any data - const tx = await database.getNextCrudTransaction(); - await tx?.complete(); - } - - async signIn(credentials: Credentials) { - localStorage.setItem('powersync_credentials', JSON.stringify(credentials)); - await db.connect(this); - } - - hasCredentials() { - return localStorage.getItem('powersync_credentials') != null; - } - - async loadCheckpoint(): Promise { - const remote = new WebRemote(this); - - let resolveCheckpoint: any = null; - let checkpointPromise: Promise = new Promise((resolve, reject) => { - resolveCheckpoint = resolve; - }); - - const adapter = new RecordingStorageAdapter(db); - adapter.registerListener({ - checkpointAvailable(checkpoint) { - resolveCheckpoint(checkpoint); - } - }); - - const syncOptions: WebStreamingSyncImplementationOptions = { - adapter, - remote, - uploadCrud: async () => { - // No-op - }, - identifier: 'diagnostics' - }; - const sync = new WebStreamingSyncImplementation(syncOptions); - try { - await sync.connect(); - - const checkpoint = await checkpointPromise; - console.log({ checkpoint }); - return checkpoint; - } finally { - // await sync.disconnect(); - } - } -} - -interface MockStorageListener extends BucketStorageListener { - checkpointAvailable(checkpoint: Checkpoint): void; -} - -class RecordingStorageAdapter extends BaseObserver implements BucketStorageAdapter { - private db: AbstractPowerSyncDatabase; - - constructor(db: AbstractPowerSyncDatabase) { - super(); - this.db = db; - } - - async getBucketStates(): Promise { - const buckets = await this.db.getAll('SELECT * FROM local_bucket_data'); - return buckets.map((bucket) => { - return { - bucket: bucket.id, - op_id: bucket.last_op ?? '0' - }; - }); - } - - startSession() {} - async removeBuckets(buckets: string[]) { - if (buckets.length == 0) { - return; - } - await this.db.execute('DELETE FROM local_bucket_data WHERE id IN (SELECT e.value FROM json_each(?) e)', [ - JSON.stringify(buckets) - ]); - } - async setTargetCheckpoint(checkpoint: Checkpoint) { - await this.db.writeTransaction(async (tx) => { - for (let bucket of checkpoint.buckets) { - console.log('save', bucket); - await tx.execute( - `INSERT OR REPLACE INTO local_bucket_data(id, total_operations, last_op, download_size, downloading) - VALUES ( - ?, - ?, - IFNULL((SELECT last_op FROM local_bucket_data WHERE id = ?), '0'), - IFNULL((SELECT download_size FROM local_bucket_data WHERE id = ?), 0), - IFNULL((SELECT downloading FROM local_bucket_data WHERE id = ?), TRUE) - )`, - [bucket.bucket, bucket.count, bucket.bucket, bucket.bucket, bucket.bucket] - ); - console.log('saved', bucket); - } - }); - this.iterateListeners((l) => { - l.checkpointAvailable?.(checkpoint); - }); - } - async syncLocalDatabase(checkpoint: Checkpoint) { - await this.db.execute('UPDATE local_bucket_data SET downloading = FALSE'); - return { checkpointValid: true, ready: true }; - } - async hasCompletedSync() { - return false; - } - async hasCrud() { - return false; - } - getMaxOpId() { - return '9223372036854775807'; - } - async init() {} - async saveSyncData(batch: SyncDataBatch) { - await this.db.writeTransaction(async (tx) => { - for (let bucket of batch.buckets) { - const size = JSON.stringify(bucket.data).length; - await tx.execute( - 'UPDATE local_bucket_data SET download_size = IFNULL(download_size, 0) + ?, last_op = ?, downloading = ? WHERE id = ?', - [size, bucket.next_after, bucket.has_more, bucket.bucket] - ); - } - }); - } - async dispose() {} - async updateLocalTarget(cb: any) { - return false; - } - async getCrudBatch(limit: any) { - return null; - } - async forceCompact() {} - async autoCompact() {} -} diff --git a/demos/diagnostics-app/tsconfig.json b/demos/diagnostics-app/tsconfig.json deleted file mode 100644 index 7f92a0bc..00000000 --- a/demos/diagnostics-app/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "paths": { - "@/*": ["./src/*"] - } - }, - "exclude": ["node_modules"], - "references": [ - { - "path": "../../packages/powersync-sdk-web" - } - ] -} diff --git a/demos/diagnostics-app/vite.config.mts b/demos/diagnostics-app/vite.config.mts deleted file mode 100644 index e5d223f5..00000000 --- a/demos/diagnostics-app/vite.config.mts +++ /dev/null @@ -1,74 +0,0 @@ -import wasm from 'vite-plugin-wasm'; -import topLevelAwait from 'vite-plugin-top-level-await'; -import { fileURLToPath, URL } from 'url'; - -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import { VitePWA } from 'vite-plugin-pwa'; - -// https://vitejs.dev/config/ -export default defineConfig({ - root: 'src', - build: { - outDir: '../dist', - rollupOptions: { - input: 'src/index.html' - }, - emptyOutDir: true - }, - resolve: { - alias: [{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }] - }, - publicDir: '../public', - envDir: '..', // Use this dir for env vars, not 'src'. - optimizeDeps: { - // Don't optimize these packages as they contain web workers and WASM files. - // https://github.com/vitejs/vite/issues/11672#issuecomment-1415820673 - exclude: ['@journeyapps/wa-sqlite', '@journeyapps/powersync-sdk-web'], - include: ['object-hash', 'uuid', 'event-iterator', 'js-logger', 'lodash', 'can-ndjson-stream'] - }, - plugins: [ - wasm(), - topLevelAwait(), - react(), - VitePWA({ - registerType: 'autoUpdate', - includeAssets: ['powersync-logo.svg', 'supabase-logo.png', 'favicon.ico'], - manifest: { - theme_color: '#c44eff', - background_color: '#c44eff', - display: 'standalone', - scope: '/', - start_url: '/', - name: 'PowerSync React Demo', - short_name: 'PowerSync React', - icons: [ - { - src: '/icons/icon-192x192.png', - sizes: '192x192', - type: 'image/png' - }, - { - src: '/icons/icon-256x256.png', - sizes: '256x256', - type: 'image/png' - }, - { - src: '/icons/icon-384x384.png', - sizes: '384x384', - type: 'image/png' - }, - { - src: '/icons/icon-512x512.png', - sizes: '512x512', - type: 'image/png' - } - ] - } - }) - ], - worker: { - format: 'es', - plugins: () => [wasm(), topLevelAwait()] - } -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 350dd160..72db7516 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,76 +91,6 @@ importers: specifier: ~5.2.2 version: 5.2.2 - demos/diagnostics-app: - dependencies: - '@journeyapps/powersync-react': - specifier: workspace:* - version: link:../../packages/powersync-react - '@journeyapps/powersync-sdk-web': - specifier: workspace:* - version: link:../../packages/powersync-sdk-web - '@journeyapps/wa-sqlite': - specifier: ~0.1.1 - version: 0.1.1 - '@mui/material': - specifier: ^5.15.12 - version: 5.15.12(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - '@mui/x-data-grid': - specifier: ^6.19.6 - version: 6.19.6(@mui/material@5.15.12)(@mui/system@5.15.12)(@types/react@18.2.65)(react-dom@18.2.0)(react@18.2.0) - js-logger: - specifier: ^1.6.1 - version: 1.6.1 - lodash: - specifier: ^4.17.21 - version: 4.17.21 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - react-router-dom: - specifier: ^6.22.3 - version: 6.22.3(react-dom@18.2.0)(react@18.2.0) - devDependencies: - '@types/lodash': - specifier: ^4.14.202 - version: 4.17.0 - '@types/node': - specifier: ^20.11.25 - version: 20.11.26 - '@types/react': - specifier: ^18.2.64 - version: 18.2.65 - '@types/react-dom': - specifier: ^18.2.21 - version: 18.2.21 - '@vitejs/plugin-react': - specifier: ^4.2.1 - version: 4.2.1(vite@5.1.6) - autoprefixer: - specifier: ^10.4.18 - version: 10.4.18(postcss@8.4.35) - babel-loader: - specifier: ^9.1.3 - version: 9.1.3(@babel/core@7.24.0)(webpack@5.90.3) - typescript: - specifier: ^5.4.2 - version: 5.4.2 - vite: - specifier: ^5.1.5 - version: 5.1.6(@types/node@20.11.26) - vite-plugin-pwa: - specifier: ^0.19.2 - version: 0.19.2(vite@5.1.6)(workbox-build@7.0.0)(workbox-window@7.0.0) - vite-plugin-top-level-await: - specifier: ^1.4.1 - version: 1.4.1(rollup@2.79.1)(vite@5.1.6) - vite-plugin-wasm: - specifier: ^3.3.0 - version: 3.3.0(vite@5.1.6) - demos/example-nextjs: dependencies: '@emotion/react': @@ -14911,22 +14841,6 @@ packages: - supports-color dev: true - /@vitejs/plugin-react@4.2.1(vite@5.1.6): - resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 - dependencies: - '@babel/core': 7.24.0 - '@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.24.0) - '@babel/plugin-transform-react-jsx-source': 7.23.3(@babel/core@7.24.0) - '@types/babel__core': 7.20.5 - react-refresh: 0.14.0 - vite: 5.1.6(@types/node@20.11.26) - transitivePeerDependencies: - - supports-color - dev: true - /@vitest/browser@1.3.1(vitest@1.3.1)(webdriverio@8.32.3): resolution: {integrity: sha512-pRof8G8nqRWwg3ouyIctyhfIVk5jXgF056uF//sqdi37+pVtDz9kBI/RMu0xlc8tgCyJ2aEMfbgJZPUydlEVaQ==} peerDependencies: @@ -22712,6 +22626,8 @@ packages: peerDependenciesMeta: webpack: optional: true + webpack-sources: + optional: true dependencies: webpack: 5.89.0(esbuild@0.19.11) webpack-sources: 3.2.3 @@ -31040,7 +30956,7 @@ packages: vite: '>=2.8' dependencies: '@rollup/plugin-virtual': 3.0.2(rollup@2.79.1) - '@swc/core': 1.4.2 + '@swc/core': 1.4.6 uuid: 9.0.1 vite: 5.1.5(@types/node@20.11.25) transitivePeerDependencies: @@ -31068,7 +30984,7 @@ packages: vite: '>=2.8' dependencies: '@rollup/plugin-virtual': 3.0.2(rollup@2.79.1) - '@swc/core': 1.4.2 + '@swc/core': 1.4.6 uuid: 9.0.1 vite: 5.1.1(@types/node@20.11.17) transitivePeerDependencies: