@powersync/nuxt 0.0.0-dev-20260128023420

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 (64) hide show
  1. package/LICENSE +201 -0
  2. package/README +374 -0
  3. package/dist/module.d.mts +39 -0
  4. package/dist/module.json +9 -0
  5. package/dist/module.mjs +160 -0
  6. package/dist/runtime/assets/powersync-icon.svg +14 -0
  7. package/dist/runtime/components/BucketsInspectorTab.d.vue.ts +3 -0
  8. package/dist/runtime/components/BucketsInspectorTab.vue +646 -0
  9. package/dist/runtime/components/BucketsInspectorTab.vue.d.ts +3 -0
  10. package/dist/runtime/components/ConfigInspectorTab.d.vue.ts +3 -0
  11. package/dist/runtime/components/ConfigInspectorTab.vue +121 -0
  12. package/dist/runtime/components/ConfigInspectorTab.vue.d.ts +3 -0
  13. package/dist/runtime/components/DataInspectorTab.d.vue.ts +3 -0
  14. package/dist/runtime/components/DataInspectorTab.vue +678 -0
  15. package/dist/runtime/components/DataInspectorTab.vue.d.ts +3 -0
  16. package/dist/runtime/components/LoadingSpinner.d.vue.ts +3 -0
  17. package/dist/runtime/components/LoadingSpinner.vue +12 -0
  18. package/dist/runtime/components/LoadingSpinner.vue.d.ts +3 -0
  19. package/dist/runtime/components/LogsTab.d.vue.ts +3 -0
  20. package/dist/runtime/components/LogsTab.vue +325 -0
  21. package/dist/runtime/components/LogsTab.vue.d.ts +3 -0
  22. package/dist/runtime/components/PowerSyncInstanceTab.d.vue.ts +3 -0
  23. package/dist/runtime/components/PowerSyncInstanceTab.vue +9 -0
  24. package/dist/runtime/components/PowerSyncInstanceTab.vue.d.ts +3 -0
  25. package/dist/runtime/components/SyncStatusTab.d.vue.ts +3 -0
  26. package/dist/runtime/components/SyncStatusTab.vue +272 -0
  27. package/dist/runtime/components/SyncStatusTab.vue.d.ts +3 -0
  28. package/dist/runtime/composables/useDiagnosticsLogger.d.ts +27 -0
  29. package/dist/runtime/composables/useDiagnosticsLogger.js +41 -0
  30. package/dist/runtime/composables/usePowerSyncInspector.d.ts +42 -0
  31. package/dist/runtime/composables/usePowerSyncInspector.js +19 -0
  32. package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.d.ts +153 -0
  33. package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.js +254 -0
  34. package/dist/runtime/composables/usePowerSyncKysely.d.ts +23 -0
  35. package/dist/runtime/composables/usePowerSyncKysely.js +7 -0
  36. package/dist/runtime/index.d.ts +9 -0
  37. package/dist/runtime/layouts/powersync-inspector-layout.d.vue.ts +13 -0
  38. package/dist/runtime/layouts/powersync-inspector-layout.vue +90 -0
  39. package/dist/runtime/layouts/powersync-inspector-layout.vue.d.ts +13 -0
  40. package/dist/runtime/pages/__powersync-inspector.d.vue.ts +3 -0
  41. package/dist/runtime/pages/__powersync-inspector.vue +153 -0
  42. package/dist/runtime/pages/__powersync-inspector.vue.d.ts +3 -0
  43. package/dist/runtime/plugin.client.d.ts +2 -0
  44. package/dist/runtime/plugin.client.js +11 -0
  45. package/dist/runtime/utils/AppSchema.d.ts +27 -0
  46. package/dist/runtime/utils/AppSchema.js +23 -0
  47. package/dist/runtime/utils/DynamicSchemaManager.d.ts +15 -0
  48. package/dist/runtime/utils/DynamicSchemaManager.js +91 -0
  49. package/dist/runtime/utils/JsSchemaGenerator.d.ts +8 -0
  50. package/dist/runtime/utils/JsSchemaGenerator.js +28 -0
  51. package/dist/runtime/utils/NuxtPowerSyncDatabase.d.ts +40 -0
  52. package/dist/runtime/utils/NuxtPowerSyncDatabase.js +117 -0
  53. package/dist/runtime/utils/RecordingStorageAdapter.d.ts +13 -0
  54. package/dist/runtime/utils/RecordingStorageAdapter.js +76 -0
  55. package/dist/runtime/utils/RustClientInterceptor.d.ts +24 -0
  56. package/dist/runtime/utils/RustClientInterceptor.js +102 -0
  57. package/dist/runtime/utils/TokenConnector.d.ts +14 -0
  58. package/dist/runtime/utils/TokenConnector.js +62 -0
  59. package/dist/runtime/utils/addImportsFrom.d.ts +1 -0
  60. package/dist/runtime/utils/addImportsFrom.js +4 -0
  61. package/dist/runtime/utils/index.d.ts +1 -0
  62. package/dist/runtime/utils/index.js +1 -0
  63. package/dist/types.d.mts +9 -0
  64. package/package.json +90 -0
@@ -0,0 +1,41 @@
1
+ import { createBaseLogger, LogLevel } from "@powersync/web";
2
+ import { createConsola } from "consola";
3
+ import { createStorage } from "unstorage";
4
+ import localStorageDriver from "unstorage/drivers/session-storage";
5
+ import mitt from "mitt";
6
+ const emitter = mitt();
7
+ const logsStorage = createStorage({
8
+ driver: localStorageDriver({ base: "powersync:" })
9
+ });
10
+ const consola = createConsola({
11
+ level: 5,
12
+ // trace
13
+ fancy: true,
14
+ formatOptions: {
15
+ columns: 80,
16
+ colors: true,
17
+ compact: false,
18
+ date: true
19
+ }
20
+ });
21
+ consola.addReporter({
22
+ log: async (logObject) => {
23
+ const key = `log:${logObject.date.toISOString()}`;
24
+ await logsStorage.set(key, logObject);
25
+ emitter.emit("log", { key, value: logObject });
26
+ }
27
+ });
28
+ export const useDiagnosticsLogger = (customHandler) => {
29
+ const logger = createBaseLogger();
30
+ logger.useDefaults();
31
+ logger.setLevel(LogLevel.DEBUG);
32
+ logger.setHandler(async (messages, context) => {
33
+ const level = context.level.name;
34
+ const messageArray = Array.from(messages);
35
+ const mainMessage = String(messageArray[0] || "Empty log message");
36
+ const extraData = messageArray.slice(1).map((item) => item.toString()).join(" ");
37
+ consola[level.toLowerCase()](`[PowerSync] ${context.name ? `[${context.name}]` : ""} ${mainMessage}`, extraData, context);
38
+ await customHandler?.(messages, context);
39
+ });
40
+ return { logger, logsStorage, emitter };
41
+ };
@@ -0,0 +1,42 @@
1
+ import { RecordingStorageAdapter } from '../utils/RecordingStorageAdapter.js';
2
+ import { DynamicSchemaManager } from '../utils/DynamicSchemaManager.js';
3
+ declare function getCurrentSchemaManager(): DynamicSchemaManager;
4
+ /**
5
+ * A composable for setting up PowerSync Inspector functionality.
6
+ *
7
+ * This composable provides utilities for schema management and diagnostics setup.
8
+ * It exposes the diagnostics schema and internal utilities needed for the inspector.
9
+ *
10
+ * @returns An object containing:
11
+ * - `diagnosticsSchema` - The schema for diagnostics data collection. Use this to extend your app schema with diagnostic tables.
12
+ * - `RecordingStorageAdapter` - Used internally. Storage adapter class that records operations for diagnostic purposes.
13
+ * - `getCurrentSchemaManager()` - Used internally. Gets the current schema manager instance for dynamic schema operations.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const { diagnosticsSchema } = usePowerSyncInspector()
18
+ *
19
+ * // Combine with your app schema
20
+ * const combinedSchema = new Schema([
21
+ * ...yourAppSchema.tables,
22
+ * ...diagnosticsSchema.tables,
23
+ * ])
24
+ * ```
25
+ */
26
+ export declare function usePowerSyncInspector(): {
27
+ diagnosticsSchema: import("@powersync/common").Schema<{
28
+ local_bucket_data: import("@powersync/common").Table<{
29
+ total_operations: import("@powersync/common").BaseColumnType<number | null>;
30
+ last_op: import("@powersync/common").BaseColumnType<string | null>;
31
+ download_size: import("@powersync/common").BaseColumnType<number | null>;
32
+ downloaded_operations: import("@powersync/common").BaseColumnType<number | null>;
33
+ downloading: import("@powersync/common").BaseColumnType<number | null>;
34
+ }>;
35
+ local_schema: import("@powersync/common").Table<{
36
+ data: import("@powersync/common").BaseColumnType<string | null>;
37
+ }>;
38
+ }>;
39
+ RecordingStorageAdapter: typeof RecordingStorageAdapter;
40
+ getCurrentSchemaManager: typeof getCurrentSchemaManager;
41
+ };
42
+ export {};
@@ -0,0 +1,19 @@
1
+ import { DiagnosticsAppSchema } from "../utils/AppSchema.js";
2
+ import { RecordingStorageAdapter } from "../utils/RecordingStorageAdapter.js";
3
+ import { DynamicSchemaManager } from "../utils/DynamicSchemaManager.js";
4
+ let currentSchemaManager = null;
5
+ function getCurrentSchemaManager() {
6
+ if (currentSchemaManager) {
7
+ return currentSchemaManager;
8
+ }
9
+ currentSchemaManager = new DynamicSchemaManager();
10
+ return currentSchemaManager;
11
+ }
12
+ export function usePowerSyncInspector() {
13
+ const diagnosticsSchema = DiagnosticsAppSchema;
14
+ return {
15
+ diagnosticsSchema,
16
+ RecordingStorageAdapter,
17
+ getCurrentSchemaManager
18
+ };
19
+ }
@@ -0,0 +1,153 @@
1
+ import type { AbstractPowerSyncDatabase, PowerSyncBackendConnector, PowerSyncConnectionOptions, SyncStatus, UploadQueueStats } from '@powersync/web';
2
+ import type { ComputedRef, Ref } from 'vue';
3
+ export type BucketRow = {
4
+ name: string;
5
+ id: number;
6
+ tables: string[];
7
+ data_size: number;
8
+ metadata_size: number;
9
+ row_count: number;
10
+ download_size: number;
11
+ downloaded_operations: number;
12
+ total_operations: number;
13
+ downloading: number;
14
+ };
15
+ export type TableRow = {
16
+ name: string;
17
+ count: number;
18
+ size: number;
19
+ };
20
+ /** Aggregated statistics from the diagnostics composable. */
21
+ export type UsePowerSyncInspectorDiagnosticsTotals = {
22
+ buckets: number;
23
+ row_count: number;
24
+ downloaded_operations: number | undefined;
25
+ total_operations: number;
26
+ data_size: string;
27
+ metadata_size: string;
28
+ download_size: string;
29
+ };
30
+ type ReadonlyRef<T> = Readonly<Ref<T>>;
31
+ /**
32
+ * Return type of {@link usePowerSyncInspectorDiagnostics}.
33
+ * Uses named types so the signature stays readable in docs and IDE.
34
+ */
35
+ export interface UsePowerSyncInspectorDiagnosticsReturn {
36
+ db: Ref<AbstractPowerSyncDatabase> | undefined;
37
+ connector: ComputedRef<PowerSyncBackendConnector | null>;
38
+ connectionOptions: ComputedRef<PowerSyncConnectionOptions | null>;
39
+ isDiagnosticSchemaSetup: ReadonlyRef<boolean>;
40
+ /** Current sync status. Typed as SyncStatus for a concise doc signature; at runtime it may be a readonly proxy. */
41
+ syncStatus: ReadonlyRef<SyncStatus>;
42
+ hasSynced: ReadonlyRef<boolean>;
43
+ isConnected: ReadonlyRef<boolean>;
44
+ isSyncing: ReadonlyRef<boolean>;
45
+ isDownloading: ReadonlyRef<boolean>;
46
+ isUploading: ReadonlyRef<boolean>;
47
+ downloadError: ReadonlyRef<unknown>;
48
+ uploadError: ReadonlyRef<unknown>;
49
+ downloadProgressDetails: ReadonlyRef<unknown>;
50
+ lastSyncedAt: ReadonlyRef<Date | null>;
51
+ totalDownloadProgress: ReadonlyRef<string>;
52
+ uploadQueueStats: ReadonlyRef<UploadQueueStats | null>;
53
+ uploadQueueCount: ReadonlyRef<number>;
54
+ uploadQueueSize: ReadonlyRef<string>;
55
+ userID: ReadonlyRef<string | null>;
56
+ bucketRows: ReadonlyRef<BucketRow[] | null>;
57
+ tableRows: ReadonlyRef<TableRow[] | null>;
58
+ totals: ReadonlyRef<UsePowerSyncInspectorDiagnosticsTotals>;
59
+ clearData: () => Promise<void>;
60
+ formatBytes: (bytes: number, decimals?: number) => string;
61
+ }
62
+ /**
63
+ * A comprehensive composable that provides real-time diagnostics data and sync status monitoring
64
+ * for your PowerSync client and local database.
65
+ *
66
+ * This composable can be used to create your own inspector or to monitor sync status in your application.
67
+ * It provides reactive properties that update automatically as the sync state changes.
68
+ *
69
+ * @returns An object containing:
70
+ *
71
+ * **Database & Connection:**
72
+ * - `db` - The PowerSync database instance
73
+ * - `connector` - The current PowerSync backend connector (computed)
74
+ * - `connectionOptions` - The current connection options (computed)
75
+ * - `isDiagnosticSchemaSetup` - Whether the diagnostic schema is properly set up
76
+ *
77
+ * **Sync Status:**
78
+ * - `syncStatus` - The current sync status object
79
+ * - `hasSynced` - Whether the database has completed at least one sync
80
+ * - `isConnected` - Whether the database is currently connected
81
+ * - `isSyncing` - Whether a sync operation is in progress
82
+ * - `isDownloading` - Whether data is currently being downloaded
83
+ * - `isUploading` - Whether data is currently being uploaded
84
+ * - `lastSyncedAt` - The timestamp of the last successful sync
85
+ *
86
+ * **Progress & Statistics:**
87
+ * - `totalDownloadProgress` - Total download progress as a percentage string (computed)
88
+ * - `uploadQueueStats` - Statistics about the upload queue
89
+ * - `uploadQueueCount` - Number of items in the upload queue (computed)
90
+ * - `uploadQueueSize` - Size of the upload queue in human-readable format (computed)
91
+ * - `bucketRows` - Array of bucket data rows
92
+ * - `tableRows` - Array of table statistics rows
93
+ * - `totals` - Aggregated statistics including bucket count, row count, and data sizes (computed)
94
+ *
95
+ * **Error Handling:**
96
+ * - `downloadError` - Any error that occurred during download
97
+ * - `uploadError` - Any error that occurred during upload
98
+ * - `downloadProgressDetails` - Detailed download progress information
99
+ *
100
+ * **User Info:**
101
+ * - `userID` - The current user ID extracted from the authentication token (computed)
102
+ *
103
+ * **Utilities:**
104
+ * - `clearData()` - Disconnects and clears all local PowerSync data, then reconnects. Useful for resetting the sync state during development or troubleshooting.
105
+ * - `formatBytes(bytes, decimals?)` - Formats byte counts into readable file sizes (e.g., "1.5 MiB"). Default decimals is 2.
106
+ *
107
+ * @example
108
+ * ```vue
109
+ * <script setup lang="ts">
110
+ * const {
111
+ * isConnected,
112
+ * hasSynced,
113
+ * isSyncing,
114
+ * totalDownloadProgress,
115
+ * uploadQueueStats,
116
+ * bucketRows,
117
+ * totals,
118
+ * clearData,
119
+ * } = usePowerSyncInspectorDiagnostics()
120
+ * </script>
121
+ *
122
+ * <template>
123
+ * <div>
124
+ * <!-- Connection Status -->
125
+ * <div v-if="isConnected" class="status-connected">
126
+ * Connected {{ hasSynced ? '✅' : '⏳' }}
127
+ * </div>
128
+ *
129
+ * <!-- Sync Progress -->
130
+ * <div v-if="isSyncing">
131
+ * Syncing... {{ totalDownloadProgress }}%
132
+ * </div>
133
+ *
134
+ * <!-- Statistics -->
135
+ * <div v-if="totals">
136
+ * <p>Buckets: {{ totals.buckets }}</p>
137
+ * <p>Total Rows: {{ totals.row_count }}</p>
138
+ * <p>Data Size: {{ totals.data_size }}</p>
139
+ * </div>
140
+ *
141
+ * <!-- Upload Queue -->
142
+ * <div v-if="uploadQueueStats">
143
+ * Pending uploads: {{ uploadQueueStats.count }}
144
+ * </div>
145
+ *
146
+ * <!-- Debug Actions -->
147
+ * <button @click="clearData">Clear Data</button>
148
+ * </div>
149
+ * </template>
150
+ * ```
151
+ */
152
+ export declare function usePowerSyncInspectorDiagnostics(): UsePowerSyncInspectorDiagnosticsReturn;
153
+ export {};
@@ -0,0 +1,254 @@
1
+ import { usePowerSync, useStatus } from "@powersync/vue";
2
+ import { ref, computed, readonly, onMounted, onUnmounted } from "vue";
3
+ import { computedAsync } from "@vueuse/core";
4
+ import { usePowerSyncInspector } from "./usePowerSyncInspector.js";
5
+ const BUCKETS_QUERY = `
6
+ WITH
7
+ oplog_by_table AS
8
+ (SELECT
9
+ bucket,
10
+ row_type,
11
+ sum(length(ifnull(data, ''))) as data_size,
12
+ sum(length(row_type) + length(row_id) + length(key) + 44) as metadata_size,
13
+ count() as row_count
14
+ FROM ps_oplog
15
+ GROUP BY bucket, row_type),
16
+
17
+ oplog_stats AS
18
+ (SELECT
19
+ bucket as bucket_id,
20
+ sum(data_size) as data_size,
21
+ sum(metadata_size) as metadata_size,
22
+ sum(row_count) as row_count,
23
+ json_group_array(row_type) tables
24
+ FROM oplog_by_table
25
+ GROUP BY bucket)
26
+
27
+ SELECT
28
+ local.id as name,
29
+ ps_buckets.id as id,
30
+ stats.tables,
31
+ stats.data_size,
32
+ stats.metadata_size,
33
+ IFNULL(stats.row_count, 0) as row_count,
34
+ local.download_size,
35
+ local.downloaded_operations,
36
+ local.total_operations,
37
+ local.downloading
38
+ FROM local_bucket_data local
39
+ LEFT JOIN ps_buckets ON ps_buckets.name = local.id
40
+ LEFT JOIN oplog_stats stats ON stats.bucket_id = ps_buckets.id`;
41
+ const BUCKETS_QUERY_FAST = `
42
+ SELECT
43
+ local.id as name,
44
+ ps_buckets.id as id,
45
+ '[]' as tables,
46
+ 0 as data_size,
47
+ 0 as metadata_size,
48
+ 0 as row_count,
49
+ local.download_size,
50
+ local.downloaded_operations,
51
+ local.total_operations,
52
+ local.downloading
53
+ FROM local_bucket_data local
54
+ LEFT JOIN ps_buckets ON ps_buckets.name = local.id`;
55
+ const TABLES_QUERY = `
56
+ SELECT row_type as name, count() as count, sum(length(data)) as size FROM ps_oplog GROUP BY row_type
57
+ `;
58
+ function formatBytes(bytes, decimals = 2) {
59
+ if (!+bytes) return "0 Bytes";
60
+ const k = 1024;
61
+ const dm = decimals < 0 ? 0 : decimals;
62
+ const sizes = [
63
+ "Bytes",
64
+ "KiB",
65
+ "MiB",
66
+ "GiB",
67
+ "TiB",
68
+ "PiB",
69
+ "EiB",
70
+ "ZiB",
71
+ "YiB"
72
+ ];
73
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
74
+ return `${Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
75
+ }
76
+ export function usePowerSyncInspectorDiagnostics() {
77
+ const db = usePowerSync();
78
+ const syncStatus = useStatus();
79
+ const { getCurrentSchemaManager } = usePowerSyncInspector();
80
+ const isDiagnosticSchemaSetup = ref(true);
81
+ const connectionOptions = computed(() => db.value.connectionOptions || null);
82
+ const connector = computed(() => db.value.connector || null);
83
+ const hasSynced = ref(syncStatus.value?.hasSynced || false);
84
+ const isConnected = ref(syncStatus.value?.connected || false);
85
+ const isSyncing = ref(false);
86
+ const isDownloading = ref(
87
+ syncStatus.value?.dataFlowStatus.downloading || false
88
+ );
89
+ const isUploading = ref(syncStatus.value?.dataFlowStatus.uploading || false);
90
+ const lastSyncedAt = ref(syncStatus.value?.lastSyncedAt || null);
91
+ const uploadError = ref(syncStatus.value?.dataFlowStatus.uploadError || null);
92
+ const downloadError = ref(
93
+ syncStatus.value?.dataFlowStatus.downloadError || null
94
+ );
95
+ const downloadProgressDetails = ref(
96
+ syncStatus.value?.dataFlowStatus.downloadProgress || null
97
+ );
98
+ const bucketRows = ref(null);
99
+ const tableRows = ref(null);
100
+ const uploadQueueStats = ref(null);
101
+ const totals = computed(() => ({
102
+ buckets: bucketRows.value?.length ?? 0,
103
+ row_count: bucketRows.value?.reduce((total, row) => total + row.row_count, 0) ?? 0,
104
+ downloaded_operations: bucketRows.value?.reduce(
105
+ (total, row) => total + row.downloaded_operations,
106
+ 0
107
+ ),
108
+ total_operations: bucketRows.value?.reduce(
109
+ (total, row) => total + row.total_operations,
110
+ 0
111
+ ) ?? 0,
112
+ data_size: formatBytes(
113
+ bucketRows.value?.reduce((total, row) => total + row.data_size, 0) ?? 0
114
+ ),
115
+ metadata_size: formatBytes(
116
+ bucketRows.value?.reduce((total, row) => total + row.metadata_size, 0) ?? 0
117
+ ),
118
+ download_size: formatBytes(
119
+ bucketRows.value?.reduce((total, row) => total + row.download_size, 0) ?? 0
120
+ )
121
+ }));
122
+ const userID = computedAsync(async () => {
123
+ try {
124
+ const token = (await connector.value.fetchCredentials())?.token;
125
+ if (!token) return null;
126
+ const [_head, body, _signature] = token.split(".");
127
+ const payload = JSON.parse(atob(body ?? ""));
128
+ return payload.sub;
129
+ } catch {
130
+ return null;
131
+ }
132
+ });
133
+ const totalDownloadProgress = computed(() => {
134
+ if (!hasSynced.value || isSyncing.value) {
135
+ return ((syncStatus.value?.downloadProgress?.downloadedFraction ?? 0) * 100).toFixed(1);
136
+ }
137
+ return "100";
138
+ });
139
+ const uploadQueueSize = computed(
140
+ () => formatBytes(uploadQueueStats.value?.size ?? 0)
141
+ );
142
+ const uploadQueueCount = computed(() => uploadQueueStats.value?.count ?? 0);
143
+ const clearData = async () => {
144
+ await db.value?.syncStreamImplementation?.disconnect();
145
+ const connector2 = db.value.connector;
146
+ const connectionOptions2 = db.value.connectionOptions;
147
+ await db.value?.disconnectAndClear();
148
+ const schemaManager = getCurrentSchemaManager();
149
+ await schemaManager.clear();
150
+ await schemaManager.refreshSchema(db.value.database);
151
+ await db.value.connect(
152
+ connector2,
153
+ connectionOptions2
154
+ );
155
+ };
156
+ async function refreshState() {
157
+ if (db.value) {
158
+ const { synced_at } = await db.value.get(
159
+ "SELECT powersync_last_synced_at() as synced_at"
160
+ );
161
+ uploadQueueStats.value = await db.value?.getUploadQueueStats(true);
162
+ if (synced_at != null && !syncStatus.value?.dataFlowStatus.downloading) {
163
+ bucketRows.value = await db.value.getAll(BUCKETS_QUERY);
164
+ tableRows.value = await db.value.getAll(TABLES_QUERY);
165
+ } else if (synced_at != null) {
166
+ bucketRows.value = await db.value.getAll(BUCKETS_QUERY_FAST);
167
+ if (tableRows.value == null) {
168
+ tableRows.value = await db.value.getAll(TABLES_QUERY);
169
+ }
170
+ } else {
171
+ bucketRows.value = await db.value.getAll(BUCKETS_QUERY_FAST);
172
+ tableRows.value = null;
173
+ }
174
+ }
175
+ }
176
+ onMounted(async () => {
177
+ if (!db.value) return;
178
+ const unregisterListener = db.value.registerListener({
179
+ statusChanged: (newStatus) => {
180
+ hasSynced.value = !!newStatus.hasSynced;
181
+ isConnected.value = !!newStatus.connected;
182
+ isDownloading.value = !!newStatus.dataFlowStatus.downloading;
183
+ isUploading.value = !!newStatus.dataFlowStatus.uploading;
184
+ lastSyncedAt.value = newStatus.lastSyncedAt || null;
185
+ uploadError.value = newStatus.dataFlowStatus.uploadError || null;
186
+ downloadError.value = newStatus.dataFlowStatus.downloadError || null;
187
+ downloadProgressDetails.value = newStatus.dataFlowStatus.downloadProgress || null;
188
+ if (newStatus?.hasSynced === void 0 || newStatus?.priorityStatusEntries?.length && newStatus.priorityStatusEntries.length > 0) {
189
+ hasSynced.value = newStatus?.priorityStatusEntries.every(
190
+ (entry) => entry.hasSynced
191
+ ) ?? false;
192
+ }
193
+ if (newStatus?.dataFlowStatus.downloading || newStatus?.dataFlowStatus.uploading) {
194
+ isSyncing.value = true;
195
+ } else {
196
+ isSyncing.value = false;
197
+ }
198
+ }
199
+ });
200
+ db.value.onChangeWithCallback(
201
+ {
202
+ async onChange(_event) {
203
+ await refreshState();
204
+ }
205
+ },
206
+ {
207
+ rawTableNames: true,
208
+ tables: [
209
+ "ps_oplog",
210
+ "ps_buckets",
211
+ "ps_data_local__local_bucket_data",
212
+ "ps_crud"
213
+ ],
214
+ throttleMs: 500
215
+ }
216
+ );
217
+ try {
218
+ await refreshState();
219
+ } catch (error) {
220
+ if (error.message === "no such table: local_bucket_data") {
221
+ isDiagnosticSchemaSetup.value = false;
222
+ }
223
+ }
224
+ onUnmounted(() => {
225
+ unregisterListener();
226
+ });
227
+ });
228
+ return {
229
+ db,
230
+ connector,
231
+ connectionOptions,
232
+ isDiagnosticSchemaSetup: readonly(isDiagnosticSchemaSetup),
233
+ syncStatus: readonly(syncStatus),
234
+ hasSynced: readonly(hasSynced),
235
+ isConnected: readonly(isConnected),
236
+ isSyncing: readonly(isSyncing),
237
+ isDownloading: readonly(isDownloading),
238
+ isUploading: readonly(isUploading),
239
+ downloadError: readonly(downloadError),
240
+ uploadError: readonly(uploadError),
241
+ downloadProgressDetails: readonly(downloadProgressDetails),
242
+ lastSyncedAt: readonly(lastSyncedAt),
243
+ totalDownloadProgress: readonly(totalDownloadProgress),
244
+ uploadQueueStats: readonly(uploadQueueStats),
245
+ uploadQueueCount: readonly(uploadQueueCount),
246
+ uploadQueueSize: readonly(uploadQueueSize),
247
+ userID: readonly(userID),
248
+ bucketRows: readonly(bucketRows),
249
+ tableRows: readonly(tableRows),
250
+ totals: readonly(totals),
251
+ clearData,
252
+ formatBytes
253
+ };
254
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Provides a Kysely-wrapped PowerSync database for type-safe database queries.
3
+ *
4
+ * This composable wraps the PowerSync database instance with Kysely's type-safe query builder,
5
+ * allowing you to write type-safe SQL queries with full TypeScript support.
6
+ *
7
+ * @typeParam T - Your database type (from your schema)
8
+ *
9
+ * @returns Kysely database instance (not `{ db }`)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { usePowerSyncKysely } from '@powersync/nuxt'
14
+ * import { type Database } from '../powersync/AppSchema'
15
+ *
16
+ * // Returns db directly, not { db }
17
+ * const db = usePowerSyncKysely<Database>()
18
+ *
19
+ * // Use Kysely query builder
20
+ * const users = await db.selectFrom('users').selectAll().execute()
21
+ * ```
22
+ */
23
+ export declare const usePowerSyncKysely: <T>() => import("@powersync/kysely-driver").PowerSyncKyselyDatabase<T>;
@@ -0,0 +1,7 @@
1
+ import { wrapPowerSyncWithKysely } from "@powersync/kysely-driver";
2
+ import { usePowerSync } from "@powersync/vue";
3
+ export const usePowerSyncKysely = () => {
4
+ const powerSync = usePowerSync();
5
+ const db = wrapPowerSyncWithKysely(powerSync.value);
6
+ return db;
7
+ };
@@ -0,0 +1,9 @@
1
+ import type { PowerSyncNuxtModuleOptions } from '../module'
2
+
3
+ declare module 'nuxt/schema' {
4
+ interface PublicRuntimeConfig {
5
+ powerSyncModuleOptions: PowerSyncNuxtModuleOptions
6
+ }
7
+ }
8
+ // It is always important to ensure you import/export something when augmenting a type
9
+ export {}
@@ -0,0 +1,13 @@
1
+ declare var __VLS_20: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_20) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,90 @@
1
+ <template>
2
+ <div>
3
+
4
+ <LoadingSpinner v-if="isCssLoading" />
5
+
6
+ <div
7
+ v-show="!isCssLoading"
8
+ flex="~ relative col"
9
+ p="6"
10
+ h="screen"
11
+ n-bg="base"
12
+ class="ps-inspector-ui"
13
+ >
14
+ <!-- Header with title and dark toggle -->
15
+ <div
16
+ flex="~ items-center justify-between"
17
+ mb="3"
18
+ >
19
+ <h1
20
+ flex="~ gap-2 items-center"
21
+ text="3xl"
22
+ font="bold"
23
+ >
24
+ <img
25
+ :src="iconUrl"
26
+ alt="Powersync"
27
+ w="10"
28
+ h="10"
29
+ >
30
+ PowerSync Inspector
31
+ </h1>
32
+
33
+ <!-- Dark Mode Toggle -->
34
+ <NDarkToggle v-slot="{ isDark, toggle }">
35
+ <NButton
36
+ n="sm"
37
+ :icon="isDark.value ? 'carbon:moon' : 'carbon:sun'"
38
+ @click="toggle"
39
+ >
40
+ {{ isDark.value ? "Dark" : "Light" }}
41
+ </NButton>
42
+ </NDarkToggle>
43
+ </div>
44
+
45
+ <slot v-if="useDiagnostics" />
46
+ <div v-else>
47
+ <NTip
48
+ n="red6 dark:red5"
49
+ icon="carbon:warning-alt"
50
+ >
51
+ Enable diagnostics in your Nuxt config to use the inspector.
52
+ </NTip>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </template>
57
+
58
+ <script setup>
59
+ import { useRuntimeConfig } from "#imports";
60
+ import { onMounted, ref } from "vue";
61
+ import iconUrl from "./assets/powersync-icon.svg?url";
62
+ const useDiagnostics = useRuntimeConfig().public.powerSyncModuleOptions.useDiagnostics ?? false;
63
+ const isCssLoading = ref(true);
64
+ onMounted(async () => {
65
+ await waitForUnoCSS();
66
+ isCssLoading.value = false;
67
+ });
68
+ const waitForUnoCSS = () => {
69
+ return new Promise((resolve) => {
70
+ const testEl = document.createElement("div");
71
+ testEl.className = "w-10 h-10";
72
+ testEl.style.position = "absolute";
73
+ testEl.style.visibility = "hidden";
74
+ testEl.style.pointerEvents = "none";
75
+ document.body.appendChild(testEl);
76
+ const checkStyles = () => {
77
+ const computed = window.getComputedStyle(testEl);
78
+ const width = computed.getPropertyValue("width");
79
+ const height = computed.getPropertyValue("height");
80
+ if (width === "40px" && height === "40px") {
81
+ document.body.removeChild(testEl);
82
+ resolve(void 0);
83
+ } else {
84
+ requestAnimationFrame(checkStyles);
85
+ }
86
+ };
87
+ requestAnimationFrame(checkStyles);
88
+ });
89
+ };
90
+ </script>
@@ -0,0 +1,13 @@
1
+ declare var __VLS_20: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_20) => any;
4
+ };
5
+ declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
6
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
+ declare const _default: typeof __VLS_export;
8
+ export default _default;
9
+ type __VLS_WithSlots<T, S> = T & {
10
+ new (): {
11
+ $slots: S;
12
+ };
13
+ };
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;