@tanstack/create 0.66.0 → 0.67.0

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.
Files changed (22) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/frameworks/react/add-ons/powersync/README.md.ejs +26 -0
  3. package/dist/frameworks/react/add-ons/powersync/assets/_dot_env.local.append +3 -0
  4. package/dist/frameworks/react/add-ons/powersync/assets/powersync-vite-plugin.ts +17 -0
  5. package/dist/frameworks/react/add-ons/powersync/assets/src/integrations/powersync/provider.tsx +26 -0
  6. package/dist/frameworks/react/add-ons/powersync/assets/src/lib/powersync/AppSchema.ts +17 -0
  7. package/dist/frameworks/react/add-ons/powersync/assets/src/lib/powersync/BackendConnector.ts +52 -0
  8. package/dist/frameworks/react/add-ons/powersync/assets/src/routes/demo/powersync.tsx +129 -0
  9. package/dist/frameworks/react/add-ons/powersync/info.json +46 -0
  10. package/dist/frameworks/react/add-ons/powersync/package.json.ejs +7 -0
  11. package/dist/frameworks/react/add-ons/powersync/small-logo.svg +6 -0
  12. package/package.json +1 -1
  13. package/src/frameworks/react/add-ons/powersync/README.md.ejs +26 -0
  14. package/src/frameworks/react/add-ons/powersync/assets/_dot_env.local.append +3 -0
  15. package/src/frameworks/react/add-ons/powersync/assets/powersync-vite-plugin.ts +17 -0
  16. package/src/frameworks/react/add-ons/powersync/assets/src/integrations/powersync/provider.tsx +26 -0
  17. package/src/frameworks/react/add-ons/powersync/assets/src/lib/powersync/AppSchema.ts +17 -0
  18. package/src/frameworks/react/add-ons/powersync/assets/src/lib/powersync/BackendConnector.ts +52 -0
  19. package/src/frameworks/react/add-ons/powersync/assets/src/routes/demo/powersync.tsx +129 -0
  20. package/src/frameworks/react/add-ons/powersync/info.json +46 -0
  21. package/src/frameworks/react/add-ons/powersync/package.json.ejs +7 -0
  22. package/src/frameworks/react/add-ons/powersync/small-logo.svg +6 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @tanstack/create
2
2
 
3
+ ## 0.67.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Auto-generated changeset from semantic commits on main.
8
+
9
+ - feat(create): add React PowerSync scaffolding add-on (#407) (8f24af5)
10
+
3
11
  ## 0.66.0
4
12
 
5
13
  ### Minor Changes
@@ -0,0 +1,26 @@
1
+ # PowerSync
2
+
3
+ This project includes the PowerSync Web SDK and React hooks.
4
+
5
+ ## Environment
6
+
7
+ Set these variables in `.env.local`:
8
+
9
+ - `VITE_POWERSYNC_URL`
10
+ - `VITE_POWERSYNC_TOKEN` for local development only
11
+
12
+ ## What The Add-on Includes
13
+
14
+ - `src/lib/powersync/AppSchema.ts`
15
+ - `src/lib/powersync/BackendConnector.ts`
16
+ - `src/integrations/powersync/provider.tsx`
17
+ - `src/routes/demo/powersync.tsx`
18
+
19
+ ## Next Steps
20
+
21
+ 1. Replace the development token flow in `src/lib/powersync/BackendConnector.ts` with your real auth flow.
22
+ 2. Update the sample schema in `src/lib/powersync/AppSchema.ts` to match your synced tables.
23
+ 3. Implement the upload logic in `uploadData()` so local mutations are written back to your backend.
24
+
25
+ PowerSync setup guidance:
26
+ https://docs.powersync.com/client-sdk-references/js-web
@@ -0,0 +1,3 @@
1
+
2
+ VITE_POWERSYNC_URL=
3
+ VITE_POWERSYNC_TOKEN=
@@ -0,0 +1,17 @@
1
+ import type { Plugin } from 'vite'
2
+
3
+ export default function powersyncVite(): Plugin {
4
+ return {
5
+ name: 'powersync-vite',
6
+ config() {
7
+ return {
8
+ optimizeDeps: {
9
+ exclude: ['@powersync/web'],
10
+ },
11
+ worker: {
12
+ format: 'es',
13
+ },
14
+ }
15
+ },
16
+ }
17
+ }
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from 'react'
2
+ import { PowerSyncContext } from '@powersync/react'
3
+ import { PowerSyncDatabase, WASQLiteOpenFactory } from '@powersync/web'
4
+
5
+ import { AppSchema } from '#/lib/powersync/AppSchema'
6
+ import { BackendConnector } from '#/lib/powersync/BackendConnector'
7
+
8
+ const db = new PowerSyncDatabase({
9
+ database: new WASQLiteOpenFactory({
10
+ dbFilename: 'powersync.db',
11
+ }),
12
+ schema: AppSchema,
13
+ flags: {
14
+ disableSSRWarning: true,
15
+ },
16
+ })
17
+
18
+ void db.connect(new BackendConnector())
19
+
20
+ export default function PowerSyncProvider({
21
+ children,
22
+ }: {
23
+ children: ReactNode
24
+ }) {
25
+ return <PowerSyncContext.Provider value={db}>{children}</PowerSyncContext.Provider>
26
+ }
@@ -0,0 +1,17 @@
1
+ import { Schema, Table, column } from '@powersync/web'
2
+
3
+ const todos = new Table(
4
+ {
5
+ created_at: column.text,
6
+ description: column.text,
7
+ completed: column.integer,
8
+ },
9
+ { indexes: { created_at: ['created_at'] } },
10
+ )
11
+
12
+ export const AppSchema = new Schema({
13
+ todos,
14
+ })
15
+
16
+ export type Database = (typeof AppSchema)['types']
17
+ export type TodoRecord = Database['todos']
@@ -0,0 +1,52 @@
1
+ import {
2
+ type AbstractPowerSyncDatabase,
3
+ type PowerSyncBackendConnector,
4
+ UpdateType,
5
+ } from '@powersync/web'
6
+
7
+ export class BackendConnector implements PowerSyncBackendConnector {
8
+ private readonly powersyncUrl = import.meta.env.VITE_POWERSYNC_URL
9
+ private readonly powersyncToken = import.meta.env.VITE_POWERSYNC_TOKEN
10
+
11
+ async fetchCredentials() {
12
+ if (!this.powersyncUrl || !this.powersyncToken) {
13
+ return null
14
+ }
15
+
16
+ return {
17
+ endpoint: this.powersyncUrl,
18
+ token: this.powersyncToken,
19
+ }
20
+ }
21
+
22
+ async uploadData(database: AbstractPowerSyncDatabase): Promise<void> {
23
+ const transaction = await database.getNextCrudTransaction()
24
+
25
+ if (!transaction) {
26
+ return
27
+ }
28
+
29
+ try {
30
+ for (const op of transaction.crud) {
31
+ const record = { ...op.opData, id: op.id }
32
+
33
+ switch (op.op) {
34
+ case UpdateType.PUT:
35
+ console.info('TODO: create record remotely', record)
36
+ break
37
+ case UpdateType.PATCH:
38
+ console.info('TODO: patch record remotely', record)
39
+ break
40
+ case UpdateType.DELETE:
41
+ console.info('TODO: delete record remotely', record)
42
+ break
43
+ }
44
+ }
45
+
46
+ await transaction.complete()
47
+ } catch (error) {
48
+ console.error('PowerSync uploadData failed', error)
49
+ throw error
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,129 @@
1
+ import { useState } from 'react'
2
+ import { createFileRoute } from '@tanstack/react-router'
3
+ import { usePowerSync, useQuery, useStatus } from '@powersync/react'
4
+
5
+ export const Route = createFileRoute('/demo/powersync')({
6
+ component: PowerSyncDemo,
7
+ })
8
+
9
+ type TodoRow = {
10
+ id: string
11
+ created_at: string
12
+ description: string
13
+ completed: number
14
+ }
15
+
16
+ function PowerSyncDemo() {
17
+ const powerSync = usePowerSync()
18
+ const status = useStatus()
19
+ const { data } = useQuery(
20
+ 'SELECT id, created_at, description, completed FROM todos ORDER BY created_at DESC',
21
+ )
22
+ const todos = (data ?? []) as Array<TodoRow>
23
+ const [description, setDescription] = useState('')
24
+ const [error, setError] = useState<string | null>(null)
25
+
26
+ async function addTodo(event: React.FormEvent<HTMLFormElement>) {
27
+ event.preventDefault()
28
+
29
+ const nextDescription = description.trim()
30
+ if (!nextDescription) {
31
+ return
32
+ }
33
+
34
+ try {
35
+ setError(null)
36
+ await powerSync.execute(
37
+ 'INSERT INTO todos (id, created_at, description, completed) VALUES (?, ?, ?, ?)',
38
+ [crypto.randomUUID(), new Date().toISOString(), nextDescription, 0],
39
+ )
40
+
41
+ setDescription('')
42
+ } catch (error) {
43
+ console.error('Failed to insert PowerSync todo', error)
44
+ setError('Failed to insert row. Please try again.')
45
+ }
46
+ }
47
+
48
+ return (
49
+ <main className="page-wrap py-10">
50
+ <div className="max-w-3xl space-y-6">
51
+ <header className="space-y-2">
52
+ <p className="text-sm font-semibold uppercase tracking-[0.2em] text-[var(--sea-ink-soft)]">
53
+ Offline Sync
54
+ </p>
55
+ <h1 className="text-3xl font-semibold tracking-tight">PowerSync</h1>
56
+ <p className="text-sm text-[var(--sea-ink-soft)]">
57
+ This demo writes to the local SQLite database immediately. Replace the sample
58
+ schema and backend connector with your real PowerSync configuration.
59
+ </p>
60
+ </header>
61
+
62
+ <section className="rounded-3xl border border-[var(--line)] bg-white/70 p-5 shadow-sm">
63
+ <h2 className="text-sm font-semibold uppercase tracking-[0.16em] text-[var(--sea-ink-soft)]">
64
+ Connection State
65
+ </h2>
66
+ <pre className="mt-3 overflow-auto rounded-2xl bg-[var(--chip-bg)] p-4 text-xs leading-6 text-[var(--sea-ink)]">
67
+ {JSON.stringify(status, null, 2)}
68
+ </pre>
69
+ </section>
70
+
71
+ <section className="rounded-3xl border border-[var(--line)] bg-white/70 p-5 shadow-sm">
72
+ <h2 className="text-sm font-semibold uppercase tracking-[0.16em] text-[var(--sea-ink-soft)]">
73
+ Local Todos
74
+ </h2>
75
+ {error ? (
76
+ <p className="mt-3 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
77
+ {error}
78
+ </p>
79
+ ) : null}
80
+ <form className="mt-4 flex flex-col gap-3 sm:flex-row" onSubmit={addTodo}>
81
+ <label className="sr-only" htmlFor="powersync-todo-description">
82
+ Todo description
83
+ </label>
84
+ <input
85
+ id="powersync-todo-description"
86
+ className="min-w-0 flex-1 rounded-2xl border border-[var(--line)] bg-white px-4 py-3 text-sm text-[var(--sea-ink)] outline-none"
87
+ onChange={(event) => setDescription(event.target.value)}
88
+ placeholder="Write to the local PowerSync database"
89
+ value={description}
90
+ />
91
+ <button
92
+ className="rounded-2xl bg-[var(--sea-ink)] px-4 py-3 text-sm font-semibold text-white"
93
+ type="submit"
94
+ >
95
+ Insert Local Row
96
+ </button>
97
+ </form>
98
+
99
+ <ul className="mt-5 space-y-3">
100
+ {todos.length === 0 ? (
101
+ <li className="rounded-2xl border border-dashed border-[var(--line)] px-4 py-5 text-sm text-[var(--sea-ink-soft)]">
102
+ No rows yet. Insert one locally, then wire `uploadData()` to send it upstream.
103
+ </li>
104
+ ) : (
105
+ todos.map((todo) => (
106
+ <li
107
+ className="rounded-2xl border border-[var(--line)] bg-[var(--chip-bg)] px-4 py-4"
108
+ key={todo.id}
109
+ >
110
+ <div className="flex items-start justify-between gap-4">
111
+ <div>
112
+ <p className="font-medium text-[var(--sea-ink)]">{todo.description}</p>
113
+ <p className="mt-1 text-xs text-[var(--sea-ink-soft)]">
114
+ {todo.created_at}
115
+ </p>
116
+ </div>
117
+ <span className="rounded-full border border-[var(--line)] px-2 py-1 text-xs font-semibold text-[var(--sea-ink-soft)]">
118
+ {todo.completed ? 'done' : 'pending'}
119
+ </span>
120
+ </div>
121
+ </li>
122
+ ))
123
+ )}
124
+ </ul>
125
+ </section>
126
+ </div>
127
+ </main>
128
+ )
129
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "PowerSync",
3
+ "description": "Add PowerSync offline sync to your application.",
4
+ "phase": "add-on",
5
+ "type": "add-on",
6
+ "category": "database",
7
+ "color": "#2563EB",
8
+ "priority": 55,
9
+ "link": "https://docs.powersync.com/client-sdk-references/js-web",
10
+ "modes": ["file-router"],
11
+ "envVars": [
12
+ {
13
+ "name": "VITE_POWERSYNC_URL",
14
+ "description": "PowerSync instance URL",
15
+ "required": true,
16
+ "file": ".env.local"
17
+ },
18
+ {
19
+ "name": "VITE_POWERSYNC_TOKEN",
20
+ "description": "Client-visible development token for local testing; VITE_* env vars are exposed to the browser",
21
+ "required": false,
22
+ "file": ".env.local"
23
+ }
24
+ ],
25
+ "integrations": [
26
+ {
27
+ "type": "vite-plugin",
28
+ "path": "powersync-vite-plugin.ts",
29
+ "jsName": "powersyncVite",
30
+ "code": "powersyncVite()"
31
+ },
32
+ {
33
+ "type": "provider",
34
+ "path": "src/integrations/powersync/provider.tsx",
35
+ "jsName": "PowerSyncProvider"
36
+ }
37
+ ],
38
+ "routes": [
39
+ {
40
+ "url": "/demo/powersync",
41
+ "name": "PowerSync",
42
+ "path": "src/routes/demo/powersync.tsx",
43
+ "jsName": "PowerSyncDemo"
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "dependencies": {
3
+ "@journeyapps/wa-sqlite": "^1.2.6",
4
+ "@powersync/react": "^1.7.4",
5
+ "@powersync/web": "^1.26.1"
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="64" height="64" rx="16" fill="#2563EB"/>
3
+ <path d="M20 21C20 18.7909 21.7909 17 24 17H39.5C45.299 17 50 21.701 50 27.5C50 33.299 45.299 38 39.5 38H29V47H20V21Z" fill="white"/>
4
+ <path d="M29 29H38.5C40.9853 29 43 26.9853 43 24.5C43 22.0147 40.9853 20 38.5 20H29V29Z" fill="#2563EB"/>
5
+ <circle cx="43" cy="44" r="7" fill="#93C5FD"/>
6
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/create",
3
- "version": "0.66.0",
3
+ "version": "0.67.0",
4
4
  "description": "TanStack Application Builder Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,26 @@
1
+ # PowerSync
2
+
3
+ This project includes the PowerSync Web SDK and React hooks.
4
+
5
+ ## Environment
6
+
7
+ Set these variables in `.env.local`:
8
+
9
+ - `VITE_POWERSYNC_URL`
10
+ - `VITE_POWERSYNC_TOKEN` for local development only
11
+
12
+ ## What The Add-on Includes
13
+
14
+ - `src/lib/powersync/AppSchema.ts`
15
+ - `src/lib/powersync/BackendConnector.ts`
16
+ - `src/integrations/powersync/provider.tsx`
17
+ - `src/routes/demo/powersync.tsx`
18
+
19
+ ## Next Steps
20
+
21
+ 1. Replace the development token flow in `src/lib/powersync/BackendConnector.ts` with your real auth flow.
22
+ 2. Update the sample schema in `src/lib/powersync/AppSchema.ts` to match your synced tables.
23
+ 3. Implement the upload logic in `uploadData()` so local mutations are written back to your backend.
24
+
25
+ PowerSync setup guidance:
26
+ https://docs.powersync.com/client-sdk-references/js-web
@@ -0,0 +1,3 @@
1
+
2
+ VITE_POWERSYNC_URL=
3
+ VITE_POWERSYNC_TOKEN=
@@ -0,0 +1,17 @@
1
+ import type { Plugin } from 'vite'
2
+
3
+ export default function powersyncVite(): Plugin {
4
+ return {
5
+ name: 'powersync-vite',
6
+ config() {
7
+ return {
8
+ optimizeDeps: {
9
+ exclude: ['@powersync/web'],
10
+ },
11
+ worker: {
12
+ format: 'es',
13
+ },
14
+ }
15
+ },
16
+ }
17
+ }
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from 'react'
2
+ import { PowerSyncContext } from '@powersync/react'
3
+ import { PowerSyncDatabase, WASQLiteOpenFactory } from '@powersync/web'
4
+
5
+ import { AppSchema } from '#/lib/powersync/AppSchema'
6
+ import { BackendConnector } from '#/lib/powersync/BackendConnector'
7
+
8
+ const db = new PowerSyncDatabase({
9
+ database: new WASQLiteOpenFactory({
10
+ dbFilename: 'powersync.db',
11
+ }),
12
+ schema: AppSchema,
13
+ flags: {
14
+ disableSSRWarning: true,
15
+ },
16
+ })
17
+
18
+ void db.connect(new BackendConnector())
19
+
20
+ export default function PowerSyncProvider({
21
+ children,
22
+ }: {
23
+ children: ReactNode
24
+ }) {
25
+ return <PowerSyncContext.Provider value={db}>{children}</PowerSyncContext.Provider>
26
+ }
@@ -0,0 +1,17 @@
1
+ import { Schema, Table, column } from '@powersync/web'
2
+
3
+ const todos = new Table(
4
+ {
5
+ created_at: column.text,
6
+ description: column.text,
7
+ completed: column.integer,
8
+ },
9
+ { indexes: { created_at: ['created_at'] } },
10
+ )
11
+
12
+ export const AppSchema = new Schema({
13
+ todos,
14
+ })
15
+
16
+ export type Database = (typeof AppSchema)['types']
17
+ export type TodoRecord = Database['todos']
@@ -0,0 +1,52 @@
1
+ import {
2
+ type AbstractPowerSyncDatabase,
3
+ type PowerSyncBackendConnector,
4
+ UpdateType,
5
+ } from '@powersync/web'
6
+
7
+ export class BackendConnector implements PowerSyncBackendConnector {
8
+ private readonly powersyncUrl = import.meta.env.VITE_POWERSYNC_URL
9
+ private readonly powersyncToken = import.meta.env.VITE_POWERSYNC_TOKEN
10
+
11
+ async fetchCredentials() {
12
+ if (!this.powersyncUrl || !this.powersyncToken) {
13
+ return null
14
+ }
15
+
16
+ return {
17
+ endpoint: this.powersyncUrl,
18
+ token: this.powersyncToken,
19
+ }
20
+ }
21
+
22
+ async uploadData(database: AbstractPowerSyncDatabase): Promise<void> {
23
+ const transaction = await database.getNextCrudTransaction()
24
+
25
+ if (!transaction) {
26
+ return
27
+ }
28
+
29
+ try {
30
+ for (const op of transaction.crud) {
31
+ const record = { ...op.opData, id: op.id }
32
+
33
+ switch (op.op) {
34
+ case UpdateType.PUT:
35
+ console.info('TODO: create record remotely', record)
36
+ break
37
+ case UpdateType.PATCH:
38
+ console.info('TODO: patch record remotely', record)
39
+ break
40
+ case UpdateType.DELETE:
41
+ console.info('TODO: delete record remotely', record)
42
+ break
43
+ }
44
+ }
45
+
46
+ await transaction.complete()
47
+ } catch (error) {
48
+ console.error('PowerSync uploadData failed', error)
49
+ throw error
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,129 @@
1
+ import { useState } from 'react'
2
+ import { createFileRoute } from '@tanstack/react-router'
3
+ import { usePowerSync, useQuery, useStatus } from '@powersync/react'
4
+
5
+ export const Route = createFileRoute('/demo/powersync')({
6
+ component: PowerSyncDemo,
7
+ })
8
+
9
+ type TodoRow = {
10
+ id: string
11
+ created_at: string
12
+ description: string
13
+ completed: number
14
+ }
15
+
16
+ function PowerSyncDemo() {
17
+ const powerSync = usePowerSync()
18
+ const status = useStatus()
19
+ const { data } = useQuery(
20
+ 'SELECT id, created_at, description, completed FROM todos ORDER BY created_at DESC',
21
+ )
22
+ const todos = (data ?? []) as Array<TodoRow>
23
+ const [description, setDescription] = useState('')
24
+ const [error, setError] = useState<string | null>(null)
25
+
26
+ async function addTodo(event: React.FormEvent<HTMLFormElement>) {
27
+ event.preventDefault()
28
+
29
+ const nextDescription = description.trim()
30
+ if (!nextDescription) {
31
+ return
32
+ }
33
+
34
+ try {
35
+ setError(null)
36
+ await powerSync.execute(
37
+ 'INSERT INTO todos (id, created_at, description, completed) VALUES (?, ?, ?, ?)',
38
+ [crypto.randomUUID(), new Date().toISOString(), nextDescription, 0],
39
+ )
40
+
41
+ setDescription('')
42
+ } catch (error) {
43
+ console.error('Failed to insert PowerSync todo', error)
44
+ setError('Failed to insert row. Please try again.')
45
+ }
46
+ }
47
+
48
+ return (
49
+ <main className="page-wrap py-10">
50
+ <div className="max-w-3xl space-y-6">
51
+ <header className="space-y-2">
52
+ <p className="text-sm font-semibold uppercase tracking-[0.2em] text-[var(--sea-ink-soft)]">
53
+ Offline Sync
54
+ </p>
55
+ <h1 className="text-3xl font-semibold tracking-tight">PowerSync</h1>
56
+ <p className="text-sm text-[var(--sea-ink-soft)]">
57
+ This demo writes to the local SQLite database immediately. Replace the sample
58
+ schema and backend connector with your real PowerSync configuration.
59
+ </p>
60
+ </header>
61
+
62
+ <section className="rounded-3xl border border-[var(--line)] bg-white/70 p-5 shadow-sm">
63
+ <h2 className="text-sm font-semibold uppercase tracking-[0.16em] text-[var(--sea-ink-soft)]">
64
+ Connection State
65
+ </h2>
66
+ <pre className="mt-3 overflow-auto rounded-2xl bg-[var(--chip-bg)] p-4 text-xs leading-6 text-[var(--sea-ink)]">
67
+ {JSON.stringify(status, null, 2)}
68
+ </pre>
69
+ </section>
70
+
71
+ <section className="rounded-3xl border border-[var(--line)] bg-white/70 p-5 shadow-sm">
72
+ <h2 className="text-sm font-semibold uppercase tracking-[0.16em] text-[var(--sea-ink-soft)]">
73
+ Local Todos
74
+ </h2>
75
+ {error ? (
76
+ <p className="mt-3 rounded-2xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
77
+ {error}
78
+ </p>
79
+ ) : null}
80
+ <form className="mt-4 flex flex-col gap-3 sm:flex-row" onSubmit={addTodo}>
81
+ <label className="sr-only" htmlFor="powersync-todo-description">
82
+ Todo description
83
+ </label>
84
+ <input
85
+ id="powersync-todo-description"
86
+ className="min-w-0 flex-1 rounded-2xl border border-[var(--line)] bg-white px-4 py-3 text-sm text-[var(--sea-ink)] outline-none"
87
+ onChange={(event) => setDescription(event.target.value)}
88
+ placeholder="Write to the local PowerSync database"
89
+ value={description}
90
+ />
91
+ <button
92
+ className="rounded-2xl bg-[var(--sea-ink)] px-4 py-3 text-sm font-semibold text-white"
93
+ type="submit"
94
+ >
95
+ Insert Local Row
96
+ </button>
97
+ </form>
98
+
99
+ <ul className="mt-5 space-y-3">
100
+ {todos.length === 0 ? (
101
+ <li className="rounded-2xl border border-dashed border-[var(--line)] px-4 py-5 text-sm text-[var(--sea-ink-soft)]">
102
+ No rows yet. Insert one locally, then wire `uploadData()` to send it upstream.
103
+ </li>
104
+ ) : (
105
+ todos.map((todo) => (
106
+ <li
107
+ className="rounded-2xl border border-[var(--line)] bg-[var(--chip-bg)] px-4 py-4"
108
+ key={todo.id}
109
+ >
110
+ <div className="flex items-start justify-between gap-4">
111
+ <div>
112
+ <p className="font-medium text-[var(--sea-ink)]">{todo.description}</p>
113
+ <p className="mt-1 text-xs text-[var(--sea-ink-soft)]">
114
+ {todo.created_at}
115
+ </p>
116
+ </div>
117
+ <span className="rounded-full border border-[var(--line)] px-2 py-1 text-xs font-semibold text-[var(--sea-ink-soft)]">
118
+ {todo.completed ? 'done' : 'pending'}
119
+ </span>
120
+ </div>
121
+ </li>
122
+ ))
123
+ )}
124
+ </ul>
125
+ </section>
126
+ </div>
127
+ </main>
128
+ )
129
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "PowerSync",
3
+ "description": "Add PowerSync offline sync to your application.",
4
+ "phase": "add-on",
5
+ "type": "add-on",
6
+ "category": "database",
7
+ "color": "#2563EB",
8
+ "priority": 55,
9
+ "link": "https://docs.powersync.com/client-sdk-references/js-web",
10
+ "modes": ["file-router"],
11
+ "envVars": [
12
+ {
13
+ "name": "VITE_POWERSYNC_URL",
14
+ "description": "PowerSync instance URL",
15
+ "required": true,
16
+ "file": ".env.local"
17
+ },
18
+ {
19
+ "name": "VITE_POWERSYNC_TOKEN",
20
+ "description": "Client-visible development token for local testing; VITE_* env vars are exposed to the browser",
21
+ "required": false,
22
+ "file": ".env.local"
23
+ }
24
+ ],
25
+ "integrations": [
26
+ {
27
+ "type": "vite-plugin",
28
+ "path": "powersync-vite-plugin.ts",
29
+ "jsName": "powersyncVite",
30
+ "code": "powersyncVite()"
31
+ },
32
+ {
33
+ "type": "provider",
34
+ "path": "src/integrations/powersync/provider.tsx",
35
+ "jsName": "PowerSyncProvider"
36
+ }
37
+ ],
38
+ "routes": [
39
+ {
40
+ "url": "/demo/powersync",
41
+ "name": "PowerSync",
42
+ "path": "src/routes/demo/powersync.tsx",
43
+ "jsName": "PowerSyncDemo"
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "dependencies": {
3
+ "@journeyapps/wa-sqlite": "^1.2.6",
4
+ "@powersync/react": "^1.7.4",
5
+ "@powersync/web": "^1.26.1"
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="64" height="64" rx="16" fill="#2563EB"/>
3
+ <path d="M20 21C20 18.7909 21.7909 17 24 17H39.5C45.299 17 50 21.701 50 27.5C50 33.299 45.299 38 39.5 38H29V47H20V21Z" fill="white"/>
4
+ <path d="M29 29H38.5C40.9853 29 43 26.9853 43 24.5C43 22.0147 40.9853 20 38.5 20H29V29Z" fill="#2563EB"/>
5
+ <circle cx="43" cy="44" r="7" fill="#93C5FD"/>
6
+ </svg>