@syncular/cli 0.0.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 (85) hide show
  1. package/dist/args.d.ts +6 -0
  2. package/dist/args.d.ts.map +1 -0
  3. package/dist/args.js +92 -0
  4. package/dist/args.js.map +1 -0
  5. package/dist/commands/create.d.ts +8 -0
  6. package/dist/commands/create.d.ts.map +1 -0
  7. package/dist/commands/create.js +423 -0
  8. package/dist/commands/create.js.map +1 -0
  9. package/dist/commands/init.d.ts +3 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +15 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/migrate.d.ts +4 -0
  14. package/dist/commands/migrate.d.ts.map +1 -0
  15. package/dist/commands/migrate.js +114 -0
  16. package/dist/commands/migrate.js.map +1 -0
  17. package/dist/config.d.ts +10 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +118 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/constants.d.ts +18 -0
  22. package/dist/constants.d.ts.map +1 -0
  23. package/dist/constants.js +108 -0
  24. package/dist/constants.js.map +1 -0
  25. package/dist/create-libraries-wizard.d.ts +8 -0
  26. package/dist/create-libraries-wizard.d.ts.map +1 -0
  27. package/dist/create-libraries-wizard.js +428 -0
  28. package/dist/create-libraries-wizard.js.map +1 -0
  29. package/dist/doctor.d.ts +3 -0
  30. package/dist/doctor.d.ts.map +1 -0
  31. package/dist/doctor.js +53 -0
  32. package/dist/doctor.js.map +1 -0
  33. package/dist/help.d.ts +2 -0
  34. package/dist/help.d.ts.map +1 -0
  35. package/dist/help.js +52 -0
  36. package/dist/help.js.map +1 -0
  37. package/dist/index.d.ts +3 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/interactive.d.ts +5 -0
  42. package/dist/interactive.d.ts.map +1 -0
  43. package/dist/interactive.js +180 -0
  44. package/dist/interactive.js.map +1 -0
  45. package/dist/main.d.ts +2 -0
  46. package/dist/main.d.ts.map +1 -0
  47. package/dist/main.js +118 -0
  48. package/dist/main.js.map +1 -0
  49. package/dist/output.d.ts +3 -0
  50. package/dist/output.d.ts.map +1 -0
  51. package/dist/output.js +8 -0
  52. package/dist/output.js.map +1 -0
  53. package/dist/template.d.ts +8 -0
  54. package/dist/template.d.ts.map +1 -0
  55. package/dist/template.js +26 -0
  56. package/dist/template.js.map +1 -0
  57. package/dist/types.d.ts +101 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +1 -0
  60. package/dist/types.js.map +1 -0
  61. package/package.json +52 -0
  62. package/templates/demo/index.html.tpl +12 -0
  63. package/templates/demo/package.json.tpl +40 -0
  64. package/templates/demo/src/client/App.tsx.tpl +137 -0
  65. package/templates/demo/src/client/main.tsx.tpl +15 -0
  66. package/templates/demo/src/client/styles.css.tpl +93 -0
  67. package/templates/demo/src/client/syncular.ts.tpl +109 -0
  68. package/templates/demo/src/server/index.ts.tpl +58 -0
  69. package/templates/demo/src/shared/db.ts.tpl +40 -0
  70. package/templates/demo/syncular-migrate.ts.tpl +79 -0
  71. package/templates/demo/syncular-typegen.ts.tpl +23 -0
  72. package/templates/demo/syncular.config.json.tpl +7 -0
  73. package/templates/demo/tsconfig.json.tpl +12 -0
  74. package/templates/demo/vite.config.ts.tpl +15 -0
  75. package/templates/libraries/syncular-electron.ts.tpl +36 -0
  76. package/templates/libraries/syncular-expo.ts.tpl +32 -0
  77. package/templates/libraries/syncular-migrate.ts.tpl +81 -0
  78. package/templates/libraries/syncular-proxy-api.ts.tpl +32 -0
  79. package/templates/libraries/syncular-react-native.ts.tpl +32 -0
  80. package/templates/libraries/syncular-react.ts.tpl +27 -0
  81. package/templates/libraries/syncular-server.postgres.ts.tpl +40 -0
  82. package/templates/libraries/syncular-server.sqlite.ts.tpl +34 -0
  83. package/templates/libraries/syncular-typegen.ts.tpl +29 -0
  84. package/templates/libraries/syncular-vanilla.ts.tpl +38 -0
  85. package/templates/libraries/syncular.config.json.tpl +9 -0
@@ -0,0 +1,137 @@
1
+ import { useEffect, useState } from 'react';
2
+ import type { TasksTable } from '../shared/db';
3
+ import {
4
+ addTask,
5
+ initializeDemoSync,
6
+ listTasks,
7
+ removeTask,
8
+ subscribeToTaskChanges,
9
+ toggleTask,
10
+ } from './syncular';
11
+
12
+ export default function App() {
13
+ const [loading, setLoading] = useState(true);
14
+ const [error, setError] = useState<string | null>(null);
15
+ const [tasks, setTasks] = useState<TasksTable[]>([]);
16
+ const [title, setTitle] = useState('');
17
+ const [saving, setSaving] = useState(false);
18
+
19
+ useEffect(() => {
20
+ let disposed = false;
21
+ let unsubscribe: (() => void) | null = null;
22
+
23
+ async function refresh() {
24
+ const rows = await listTasks();
25
+ if (!disposed) {
26
+ setTasks(rows);
27
+ }
28
+ }
29
+
30
+ async function boot() {
31
+ try {
32
+ await initializeDemoSync();
33
+ await refresh();
34
+ unsubscribe = await subscribeToTaskChanges(() => {
35
+ void refresh();
36
+ });
37
+ } catch (nextError) {
38
+ if (!disposed) {
39
+ setError(nextError instanceof Error ? nextError.message : String(nextError));
40
+ }
41
+ } finally {
42
+ if (!disposed) {
43
+ setLoading(false);
44
+ }
45
+ }
46
+ }
47
+
48
+ void boot();
49
+
50
+ return () => {
51
+ disposed = true;
52
+ unsubscribe?.();
53
+ };
54
+ }, []);
55
+
56
+ const onAdd = async () => {
57
+ const value = title.trim();
58
+ if (value.length === 0) return;
59
+
60
+ setSaving(true);
61
+ setError(null);
62
+ try {
63
+ await addTask(value);
64
+ setTitle('');
65
+ } catch (nextError) {
66
+ setError(nextError instanceof Error ? nextError.message : String(nextError));
67
+ } finally {
68
+ setSaving(false);
69
+ }
70
+ };
71
+
72
+ const onToggle = async (task: TasksTable) => {
73
+ try {
74
+ await toggleTask(task);
75
+ } catch (nextError) {
76
+ setError(nextError instanceof Error ? nextError.message : String(nextError));
77
+ }
78
+ };
79
+
80
+ const onRemove = async (taskId: string) => {
81
+ try {
82
+ await removeTask(taskId);
83
+ } catch (nextError) {
84
+ setError(nextError instanceof Error ? nextError.message : String(nextError));
85
+ }
86
+ };
87
+
88
+ return (
89
+ <main className="page">
90
+ <header>
91
+ <h1>Syncular Demo</h1>
92
+ <p>Hono + Vite React + WA-SQLite</p>
93
+ </header>
94
+
95
+ <section className="card">
96
+ <h2>Tasks</h2>
97
+
98
+ <div className="input-row">
99
+ <input
100
+ value={title}
101
+ onChange={(event) => setTitle(event.target.value)}
102
+ onKeyDown={(event) => {
103
+ if (event.key === 'Enter') {
104
+ void onAdd();
105
+ }
106
+ }}
107
+ placeholder="What should be synced?"
108
+ />
109
+ <button type="button" onClick={() => void onAdd()} disabled={saving}>
110
+ {saving ? 'Saving...' : 'Add'}
111
+ </button>
112
+ </div>
113
+
114
+ {loading ? <p>Connecting local DB + sync engine...</p> : null}
115
+ {error ? <p className="error">{error}</p> : null}
116
+
117
+ <ul className="task-list">
118
+ {tasks.map((task) => (
119
+ <li key={task.id}>
120
+ <label>
121
+ <input
122
+ type="checkbox"
123
+ checked={task.completed === 1}
124
+ onChange={() => void onToggle(task)}
125
+ />
126
+ <span>{task.title}</span>
127
+ </label>
128
+ <button type="button" onClick={() => void onRemove(task.id)}>
129
+ Delete
130
+ </button>
131
+ </li>
132
+ ))}
133
+ </ul>
134
+ </section>
135
+ </main>
136
+ );
137
+ }
@@ -0,0 +1,15 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+ import './styles.css';
5
+
6
+ const root = document.getElementById('root');
7
+ if (!root) {
8
+ throw new Error('Missing #root element');
9
+ }
10
+
11
+ createRoot(root).render(
12
+ <StrictMode>
13
+ <App />
14
+ </StrictMode>
15
+ );
@@ -0,0 +1,93 @@
1
+ :root {
2
+ font-family: 'SF Pro Text', 'Segoe UI', Helvetica, Arial, sans-serif;
3
+ color: #0f172a;
4
+ background: linear-gradient(180deg, #f8fafc 0%, #eef2ff 100%);
5
+ }
6
+
7
+ * {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ body {
12
+ margin: 0;
13
+ min-height: 100vh;
14
+ }
15
+
16
+ .page {
17
+ max-width: 760px;
18
+ margin: 0 auto;
19
+ padding: 48px 20px;
20
+ }
21
+
22
+ header h1 {
23
+ margin: 0;
24
+ font-size: 2rem;
25
+ }
26
+
27
+ header p {
28
+ margin: 6px 0 0;
29
+ color: #334155;
30
+ }
31
+
32
+ .card {
33
+ margin-top: 24px;
34
+ padding: 20px;
35
+ border-radius: 14px;
36
+ background: #ffffff;
37
+ box-shadow: 0 12px 30px rgba(15, 23, 42, 0.08);
38
+ }
39
+
40
+ .input-row {
41
+ display: flex;
42
+ gap: 10px;
43
+ }
44
+
45
+ .input-row input {
46
+ flex: 1;
47
+ padding: 10px 12px;
48
+ border: 1px solid #cbd5e1;
49
+ border-radius: 8px;
50
+ }
51
+
52
+ button {
53
+ padding: 10px 14px;
54
+ border: 0;
55
+ border-radius: 8px;
56
+ background: #2563eb;
57
+ color: #ffffff;
58
+ cursor: pointer;
59
+ }
60
+
61
+ button:disabled {
62
+ opacity: 0.7;
63
+ cursor: default;
64
+ }
65
+
66
+ .task-list {
67
+ margin: 16px 0 0;
68
+ padding: 0;
69
+ list-style: none;
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 10px;
73
+ }
74
+
75
+ .task-list li {
76
+ display: flex;
77
+ justify-content: space-between;
78
+ align-items: center;
79
+ padding: 10px 12px;
80
+ border-radius: 8px;
81
+ border: 1px solid #e2e8f0;
82
+ }
83
+
84
+ .task-list label {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 10px;
88
+ }
89
+
90
+ .error {
91
+ margin: 12px 0;
92
+ color: #dc2626;
93
+ }
@@ -0,0 +1,109 @@
1
+ import { createClient, type Client } from '@syncular/client';
2
+ import { createWaSqliteDb } from '@syncular/dialect-wa-sqlite';
3
+ import { createHttpTransport } from '@syncular/transport-http';
4
+ import type { Kysely } from 'kysely';
5
+ import type { AppClientDb, TasksTable } from '../shared/db';
6
+
7
+ const ACTOR_ID = 'demo-user';
8
+ const CLIENT_DATABASE_FILE = 'syncular-demo-client.sqlite';
9
+
10
+ interface DemoRuntime {
11
+ db: Kysely<AppClientDb>;
12
+ client: Client<AppClientDb>;
13
+ }
14
+
15
+ let runtimePromise: Promise<DemoRuntime> | null = null;
16
+
17
+ async function ensureClientAppTables(db: Kysely<AppClientDb>): Promise<void> {
18
+ await db.schema
19
+ .createTable('tasks')
20
+ .ifNotExists()
21
+ .addColumn('id', 'text', (col) => col.primaryKey())
22
+ .addColumn('title', 'text', (col) => col.notNull())
23
+ .addColumn('completed', 'integer', (col) => col.notNull().defaultTo(0))
24
+ .addColumn('user_id', 'text', (col) => col.notNull())
25
+ .addColumn('server_version', 'integer', (col) => col.notNull().defaultTo(0))
26
+ .execute();
27
+ }
28
+
29
+ async function createRuntime(): Promise<DemoRuntime> {
30
+ const db = createWaSqliteDb<AppClientDb>({ fileName: CLIENT_DATABASE_FILE });
31
+ await ensureClientAppTables(db);
32
+
33
+ const { client } = await createClient<AppClientDb>({
34
+ db,
35
+ actorId: ACTOR_ID,
36
+ transport: createHttpTransport({
37
+ baseUrl: '/api/sync',
38
+ getHeaders: () => ({ 'x-user-id': ACTOR_ID }),
39
+ }),
40
+ tables: ['tasks'],
41
+ scopes: ['user:{user_id}'],
42
+ sync: {
43
+ realtime: false,
44
+ pollIntervalMs: 3000,
45
+ },
46
+ });
47
+
48
+ await client.sync();
49
+ return { db, client };
50
+ }
51
+
52
+ async function getRuntime(): Promise<DemoRuntime> {
53
+ if (!runtimePromise) {
54
+ runtimePromise = createRuntime();
55
+ }
56
+ return runtimePromise;
57
+ }
58
+
59
+ export async function initializeDemoSync(): Promise<void> {
60
+ await getRuntime();
61
+ }
62
+
63
+ export async function listTasks(): Promise<TasksTable[]> {
64
+ const runtime = await getRuntime();
65
+ return runtime.db
66
+ .selectFrom('tasks')
67
+ .selectAll()
68
+ .orderBy('server_version desc')
69
+ .execute();
70
+ }
71
+
72
+ export async function addTask(title: string): Promise<void> {
73
+ const runtime = await getRuntime();
74
+ await runtime.client.mutations.tasks.insert({
75
+ id: crypto.randomUUID(),
76
+ title,
77
+ completed: 0,
78
+ user_id: ACTOR_ID,
79
+ server_version: 0,
80
+ });
81
+ await runtime.client.sync();
82
+ }
83
+
84
+ export async function toggleTask(task: TasksTable): Promise<void> {
85
+ const runtime = await getRuntime();
86
+ await runtime.client.mutations.tasks.update(task.id, {
87
+ completed: task.completed === 1 ? 0 : 1,
88
+ });
89
+ await runtime.client.sync();
90
+ }
91
+
92
+ export async function removeTask(taskId: string): Promise<void> {
93
+ const runtime = await getRuntime();
94
+ await runtime.client.mutations.tasks.delete(taskId);
95
+ await runtime.client.sync();
96
+ }
97
+
98
+ export async function subscribeToTaskChanges(
99
+ callback: () => void
100
+ ): Promise<() => void> {
101
+ const runtime = await getRuntime();
102
+ const unsubscribeData = runtime.client.on('data:change', callback);
103
+ const unsubscribeSync = runtime.client.on('sync:complete', callback);
104
+
105
+ return () => {
106
+ unsubscribeData();
107
+ unsubscribeSync();
108
+ };
109
+ }
@@ -0,0 +1,58 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { createBunSqliteDb } from '@syncular/dialect-bun-sqlite';
4
+ import { runMigrations } from '@syncular/migrations';
5
+ import { createServerHandler, ensureSyncSchema } from '@syncular/server';
6
+ import { createSqliteServerDialect } from '@syncular/server-dialect-sqlite';
7
+ import { createSyncServer } from '@syncular/server-hono';
8
+ import { Hono } from 'hono';
9
+ import { demoMigrations, type AppServerDb } from '../shared/db';
10
+
11
+ const DATABASE_PATH = './data/server.sqlite';
12
+ const PORT = Number(process.env.PORT ?? 8787);
13
+
14
+ async function ensureDemoSchema() {
15
+ await mkdir(dirname(DATABASE_PATH), { recursive: true });
16
+
17
+ const db = createBunSqliteDb<AppServerDb>({ path: DATABASE_PATH });
18
+ const dialect = createSqliteServerDialect();
19
+ await ensureSyncSchema(db, dialect);
20
+ await runMigrations({
21
+ db,
22
+ migrations: demoMigrations,
23
+ trackingTable: 'sync_server_migration_state',
24
+ });
25
+
26
+ const tasksHandler = createServerHandler({
27
+ table: 'tasks',
28
+ scopes: ['user:{user_id}'],
29
+ resolveScopes: async (ctx) => ({ user_id: [ctx.actorId] }),
30
+ });
31
+
32
+ const { syncRoutes } = createSyncServer({
33
+ db,
34
+ dialect,
35
+ handlers: [tasksHandler],
36
+ authenticate: async (c) => {
37
+ const actorId = c.req.header('x-user-id') ?? 'demo-user';
38
+ return { actorId };
39
+ },
40
+ });
41
+
42
+ const app = new Hono();
43
+ app.get('/api/health', (c) =>
44
+ c.json({ ok: true, message: 'Syncular demo server running' })
45
+ );
46
+ app.route('/api/sync', syncRoutes);
47
+
48
+ return app;
49
+ }
50
+
51
+ const app = await ensureDemoSchema();
52
+
53
+ Bun.serve({
54
+ port: PORT,
55
+ fetch: app.fetch,
56
+ });
57
+
58
+ console.log(`Syncular demo server listening on http://localhost:${PORT}`);
@@ -0,0 +1,40 @@
1
+ import type { SyncClientDb } from '@syncular/client';
2
+ import { defineMigrations } from '@syncular/migrations';
3
+ import type { SyncCoreDb } from '@syncular/server';
4
+
5
+ export interface TasksTable {
6
+ id: string;
7
+ title: string;
8
+ completed: number;
9
+ user_id: string;
10
+ server_version: number;
11
+ }
12
+
13
+ export interface AppClientDb extends SyncClientDb {
14
+ tasks: TasksTable;
15
+ }
16
+
17
+ export interface AppServerDb extends SyncCoreDb {
18
+ tasks: TasksTable;
19
+ }
20
+
21
+ export const demoMigrations = defineMigrations<AppServerDb>({
22
+ v1: async (db) => {
23
+ await db.schema
24
+ .createTable('tasks')
25
+ .ifNotExists()
26
+ .addColumn('id', 'text', (col) => col.primaryKey())
27
+ .addColumn('title', 'text', (col) => col.notNull())
28
+ .addColumn('completed', 'integer', (col) => col.notNull().defaultTo(0))
29
+ .addColumn('user_id', 'text', (col) => col.notNull())
30
+ .addColumn('server_version', 'integer', (col) => col.notNull().defaultTo(1))
31
+ .execute();
32
+
33
+ await db.schema
34
+ .createIndex('idx_tasks_user_id')
35
+ .ifNotExists()
36
+ .on('tasks')
37
+ .columns(['user_id'])
38
+ .execute();
39
+ },
40
+ });
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Migration adapter for the generated Syncular demo.
3
+ */
4
+
5
+ import { createBunSqliteDb } from '@syncular/dialect-bun-sqlite';
6
+ import { getSchemaVersion, runMigrations } from '@syncular/migrations';
7
+ import type { Kysely } from 'kysely';
8
+ import { demoMigrations, type AppServerDb } from '../src/shared/db';
9
+
10
+ function createDb(): Kysely<AppServerDb> {
11
+ return createBunSqliteDb<AppServerDb>({ path: './data/server.sqlite' });
12
+ }
13
+
14
+ export const <%= it.ADAPTER_EXPORT %> = {
15
+ async status() {
16
+ const db = createDb();
17
+ try {
18
+ const currentVersion = await getSchemaVersion(
19
+ db,
20
+ 'sync_server_migration_state'
21
+ );
22
+ const targetVersion = demoMigrations.currentVersion;
23
+ const pendingVersions = demoMigrations.migrations
24
+ .map((migration) => migration.version)
25
+ .filter((version) => version > currentVersion);
26
+
27
+ return {
28
+ currentVersion,
29
+ targetVersion,
30
+ pendingVersions,
31
+ trackingTable: 'sync_server_migration_state',
32
+ };
33
+ } finally {
34
+ await db.destroy();
35
+ }
36
+ },
37
+
38
+ async up(options: {
39
+ onChecksumMismatch: 'error' | 'reset';
40
+ dryRun: boolean;
41
+ }) {
42
+ const db = createDb();
43
+ try {
44
+ const currentVersion = await getSchemaVersion(
45
+ db,
46
+ 'sync_server_migration_state'
47
+ );
48
+
49
+ if (options.dryRun) {
50
+ const pendingVersions = demoMigrations.migrations
51
+ .map((migration) => migration.version)
52
+ .filter((version) => version > currentVersion);
53
+
54
+ return {
55
+ appliedVersions: pendingVersions,
56
+ currentVersion,
57
+ wasReset: false,
58
+ dryRun: true,
59
+ };
60
+ }
61
+
62
+ const result = await runMigrations({
63
+ db,
64
+ migrations: demoMigrations,
65
+ trackingTable: 'sync_server_migration_state',
66
+ onChecksumMismatch: options.onChecksumMismatch,
67
+ });
68
+
69
+ return {
70
+ appliedVersions: result.applied,
71
+ currentVersion: result.currentVersion,
72
+ wasReset: result.wasReset,
73
+ dryRun: false,
74
+ };
75
+ } finally {
76
+ await db.destroy();
77
+ }
78
+ },
79
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Generate database types from demo migrations.
3
+ */
4
+
5
+ import { generateTypes } from '@syncular/typegen';
6
+ import { demoMigrations } from '../src/shared/db';
7
+
8
+ async function main() {
9
+ const result = await generateTypes({
10
+ migrations: demoMigrations,
11
+ output: './src/shared/types.generated.ts',
12
+ extendsSyncClientDb: true,
13
+ });
14
+
15
+ console.log(`Generated ${result.outputPath}`);
16
+ console.log(`Schema version: ${result.currentVersion}`);
17
+ console.log(`Tables: ${result.tableCount}`);
18
+ }
19
+
20
+ main().catch((error) => {
21
+ console.error('Type generation failed:', error);
22
+ process.exit(1);
23
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "mode": "demo",
3
+ "migrate": {
4
+ "adapter": "<%= it.ADAPTER_PATH %>",
5
+ "export": "<%= it.ADAPTER_EXPORT %>"
6
+ }
7
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "types": ["bun", "vite/client"]
10
+ },
11
+ "include": ["src", "scripts", "vite.config.ts"]
12
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ worker: {
7
+ format: 'es',
8
+ },
9
+ server: {
10
+ port: 5173,
11
+ proxy: {
12
+ '/api': 'http://localhost:8787',
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Syncular Electron module scaffold.
3
+ *
4
+ * Dialect: <%= it.ELECTRON_DIALECT_LABEL %>
5
+ */
6
+
7
+ import { createClient } from '@syncular/client';
8
+ import type { SyncClientDb } from '@syncular/client';
9
+ <%= it.ELECTRON_DIALECT_IMPORT %>
10
+ import { createHttpTransport } from '@syncular/transport-http';
11
+
12
+ export const clientDialect = '<%= it.ELECTRON_DIALECT %>';
13
+
14
+ export function createClientDb<DB extends SyncClientDb>() {
15
+ <%= it.ELECTRON_DB_FACTORY_LINE %>
16
+ }
17
+
18
+ export async function createSyncularElectronClient<DB extends SyncClientDb>(args: {
19
+ actorId: string;
20
+ token: string;
21
+ baseUrl: string;
22
+ tables: string[];
23
+ scopes: string[];
24
+ }) {
25
+ const db = createClientDb<DB>();
26
+ return createClient({
27
+ db,
28
+ actorId: args.actorId,
29
+ transport: createHttpTransport({
30
+ baseUrl: args.baseUrl,
31
+ getHeaders: () => ({ Authorization: `Bearer ${args.token}` }),
32
+ }),
33
+ tables: args.tables,
34
+ scopes: args.scopes,
35
+ });
36
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Syncular Expo module scaffold.
3
+ */
4
+
5
+ import { createClient } from '@syncular/client';
6
+ import type { SyncClientDb } from '@syncular/client';
7
+ import { createExpoSqliteDb } from '@syncular/dialect-expo-sqlite';
8
+ import { createHttpTransport } from '@syncular/transport-http';
9
+
10
+ export function createClientDb<DB extends SyncClientDb>() {
11
+ return createExpoSqliteDb<DB>({ databaseName: 'app.sqlite' });
12
+ }
13
+
14
+ export async function createSyncularExpoClient<DB extends SyncClientDb>(args: {
15
+ actorId: string;
16
+ token: string;
17
+ baseUrl: string;
18
+ tables: string[];
19
+ scopes: string[];
20
+ }) {
21
+ const db = createClientDb<DB>();
22
+ return createClient({
23
+ db,
24
+ actorId: args.actorId,
25
+ transport: createHttpTransport({
26
+ baseUrl: args.baseUrl,
27
+ getHeaders: () => ({ Authorization: `Bearer ${args.token}` }),
28
+ }),
29
+ tables: args.tables,
30
+ scopes: args.scopes,
31
+ });
32
+ }