@powersync/nuxt 0.0.0-dev-20260503073249 → 0.0.0-dev-20260630141119
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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/SyncStatusTab.vue +1 -1
- package/dist/runtime/composables/useDiagnosticsLogger.d.ts +4 -4
- package/dist/runtime/composables/useDiagnosticsLogger.js +30 -23
- package/dist/runtime/composables/usePowerSyncInspector.d.ts +0 -3
- package/dist/runtime/composables/usePowerSyncInspector.js +0 -2
- package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.d.ts +3 -3
- package/dist/runtime/composables/usePowerSyncInspectorDiagnostics.js +13 -14
- package/dist/runtime/utils/DynamicSchemaManager.d.ts +3 -2
- package/dist/runtime/utils/DynamicSchemaManager.js +27 -29
- package/dist/runtime/utils/NuxtPowerSyncDatabase.d.ts +16 -14
- package/dist/runtime/utils/NuxtPowerSyncDatabase.js +17 -29
- package/dist/runtime/utils/RustClientInterceptor.d.ts +4 -4
- package/dist/runtime/utils/RustClientInterceptor.js +16 -28
- package/package.json +13 -11
- package/dist/runtime/utils/RecordingStorageAdapter.d.ts +0 -13
- package/dist/runtime/utils/RecordingStorageAdapter.js +0 -58
package/dist/module.json
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<div flex="~ col gap-2">
|
|
55
55
|
<span text="sm gray-500">Upload Progress</span>
|
|
56
56
|
<div flex="~ items-center gap-2">
|
|
57
|
-
<span v-if="syncStatus?.
|
|
57
|
+
<span v-if="syncStatus?.uploading">upload in progress...</span>
|
|
58
58
|
<span v-else text="sm gray-400">No active upload</span>
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type PowerSyncLogger } from '@powersync/common';
|
|
2
2
|
/**
|
|
3
3
|
* Provides a logger configured for PowerSync diagnostics.
|
|
4
4
|
*
|
|
5
5
|
* This composable creates a logger instance that is automatically configured for diagnostics
|
|
6
6
|
* recording. The logger stores logs in session storage and emits events for real-time log monitoring.
|
|
7
7
|
*
|
|
8
|
-
* @param
|
|
8
|
+
* @param additional - Optional logger that messages will also be forwarded to.
|
|
9
9
|
*
|
|
10
10
|
* @returns An object containing:
|
|
11
11
|
* - `logger` - The configured ILogHandler instance
|
|
@@ -20,8 +20,8 @@ import { type ILogHandler } from '@powersync/web';
|
|
|
20
20
|
* // Use it in your PowerSync setup if needed
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare const useDiagnosticsLogger: (
|
|
24
|
-
logger:
|
|
23
|
+
export declare const useDiagnosticsLogger: (additional?: PowerSyncLogger) => {
|
|
24
|
+
logger: PowerSyncLogger;
|
|
25
25
|
logsStorage: import("unstorage").Storage<import("unstorage").StorageValue>;
|
|
26
26
|
emitter: import("mitt").Emitter<Record<import("mitt").EventType, unknown>>;
|
|
27
27
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createConsoleLogger, LogLevels } from "@powersync/common";
|
|
2
2
|
import { createStorage } from "unstorage";
|
|
3
3
|
import localStorageDriver from "unstorage/drivers/session-storage";
|
|
4
4
|
import mitt from "mitt";
|
|
@@ -6,28 +6,35 @@ const emitter = mitt();
|
|
|
6
6
|
const logsStorage = createStorage({
|
|
7
7
|
driver: localStorageDriver({ base: "powersync:" })
|
|
8
8
|
});
|
|
9
|
-
export const useDiagnosticsLogger = (
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
export const useDiagnosticsLogger = (additional) => {
|
|
10
|
+
const consoleLogger = createConsoleLogger({ minLevel: LogLevels.debug });
|
|
11
|
+
const logger = {
|
|
12
|
+
async log(record) {
|
|
13
|
+
consoleLogger.log(record);
|
|
14
|
+
const logObject = {
|
|
15
|
+
date: /* @__PURE__ */ new Date(),
|
|
16
|
+
message: record.message,
|
|
17
|
+
error: record.error,
|
|
18
|
+
level: nameOfLogLevel(record.level)
|
|
19
|
+
};
|
|
20
|
+
const key = `log:${logObject.date.toISOString()}`;
|
|
21
|
+
await logsStorage.set(key, logObject);
|
|
22
|
+
emitter.emit("log", { key, value: logObject });
|
|
23
|
+
additional?.log(record);
|
|
14
24
|
}
|
|
15
|
-
}
|
|
16
|
-
logger.setLevel(LogLevel.DEBUG);
|
|
17
|
-
logger.setHandler(async (messages, context) => {
|
|
18
|
-
consoleHandler(messages, context);
|
|
19
|
-
const messageArray = Array.from(messages);
|
|
20
|
-
const mainMessage = String(messageArray[0] ?? "Empty log message");
|
|
21
|
-
const extra = messageArray.length > 1 ? messageArray.length === 2 ? messageArray[1] : messageArray.slice(1) : void 0;
|
|
22
|
-
const logObject = {
|
|
23
|
-
date: /* @__PURE__ */ new Date(),
|
|
24
|
-
type: context.level.name.toLowerCase(),
|
|
25
|
-
args: [mainMessage, extra, context]
|
|
26
|
-
};
|
|
27
|
-
const key = `log:${logObject.date.toISOString()}`;
|
|
28
|
-
await logsStorage.set(key, logObject);
|
|
29
|
-
emitter.emit("log", { key, value: logObject });
|
|
30
|
-
await customHandler?.(messages, context);
|
|
31
|
-
});
|
|
25
|
+
};
|
|
32
26
|
return { logger, logsStorage, emitter };
|
|
33
27
|
};
|
|
28
|
+
const levelNames = [
|
|
29
|
+
["ERROR", LogLevels.error],
|
|
30
|
+
["WARNING", LogLevels.warn],
|
|
31
|
+
["INFO", LogLevels.info],
|
|
32
|
+
["DEBUG", LogLevels.debug],
|
|
33
|
+
["TRACE", LogLevels.trace]
|
|
34
|
+
];
|
|
35
|
+
function nameOfLogLevel(level) {
|
|
36
|
+
for (const [name, minLevel] of levelNames) {
|
|
37
|
+
if (level >= minLevel) return name;
|
|
38
|
+
}
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RecordingStorageAdapter } from '../utils/RecordingStorageAdapter.js';
|
|
2
1
|
import { DynamicSchemaManager } from '../utils/DynamicSchemaManager.js';
|
|
3
2
|
declare function getCurrentSchemaManager(): DynamicSchemaManager;
|
|
4
3
|
/**
|
|
@@ -9,7 +8,6 @@ declare function getCurrentSchemaManager(): DynamicSchemaManager;
|
|
|
9
8
|
*
|
|
10
9
|
* @returns An object containing:
|
|
11
10
|
* - `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
11
|
* - `getCurrentSchemaManager()` - Used internally. Gets the current schema manager instance for dynamic schema operations.
|
|
14
12
|
*
|
|
15
13
|
* @example
|
|
@@ -36,7 +34,6 @@ export declare function usePowerSyncInspector(): {
|
|
|
36
34
|
data: import("@powersync/common").BaseColumnType<string | null>;
|
|
37
35
|
}>;
|
|
38
36
|
}>;
|
|
39
|
-
RecordingStorageAdapter: typeof RecordingStorageAdapter;
|
|
40
37
|
getCurrentSchemaManager: typeof getCurrentSchemaManager;
|
|
41
38
|
};
|
|
42
39
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DiagnosticsAppSchema } from "../utils/AppSchema.js";
|
|
2
|
-
import { RecordingStorageAdapter } from "../utils/RecordingStorageAdapter.js";
|
|
3
2
|
import { DynamicSchemaManager } from "../utils/DynamicSchemaManager.js";
|
|
4
3
|
let currentSchemaManager = null;
|
|
5
4
|
function getCurrentSchemaManager() {
|
|
@@ -13,7 +12,6 @@ export function usePowerSyncInspector() {
|
|
|
13
12
|
const diagnosticsSchema = DiagnosticsAppSchema;
|
|
14
13
|
return {
|
|
15
14
|
diagnosticsSchema,
|
|
16
|
-
RecordingStorageAdapter,
|
|
17
15
|
getCurrentSchemaManager
|
|
18
16
|
};
|
|
19
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CommonPowerSyncDatabase, PowerSyncBackendConnector, SyncOptions, SyncStatus, UploadQueueStats } from '@powersync/common';
|
|
2
2
|
import type { ComputedRef, Ref } from 'vue';
|
|
3
3
|
export type BucketRow = {
|
|
4
4
|
name: string;
|
|
@@ -33,9 +33,9 @@ type ReadonlyRef<T> = Readonly<Ref<T>>;
|
|
|
33
33
|
* Uses named types so the signature stays readable in docs and IDE.
|
|
34
34
|
*/
|
|
35
35
|
export interface UsePowerSyncInspectorDiagnosticsReturn {
|
|
36
|
-
db: Ref<
|
|
36
|
+
db: Ref<CommonPowerSyncDatabase> | undefined;
|
|
37
37
|
connector: ComputedRef<PowerSyncBackendConnector | null>;
|
|
38
|
-
connectionOptions: ComputedRef<
|
|
38
|
+
connectionOptions: ComputedRef<SyncOptions | null>;
|
|
39
39
|
isDiagnosticSchemaSetup: ReadonlyRef<boolean>;
|
|
40
40
|
/** Current sync status. Typed as SyncStatus for a concise doc signature; at runtime it may be a readonly proxy. */
|
|
41
41
|
syncStatus: ReadonlyRef<SyncStatus>;
|
|
@@ -73,12 +73,12 @@ export function usePowerSyncInspectorDiagnostics() {
|
|
|
73
73
|
const hasSynced = ref(syncStatus.value?.hasSynced || false);
|
|
74
74
|
const isConnected = ref(syncStatus.value?.connected || false);
|
|
75
75
|
const isSyncing = ref(false);
|
|
76
|
-
const isDownloading = ref(syncStatus.value?.
|
|
77
|
-
const isUploading = ref(syncStatus.value?.
|
|
76
|
+
const isDownloading = ref(syncStatus.value?.downloading || false);
|
|
77
|
+
const isUploading = ref(syncStatus.value?.uploading || false);
|
|
78
78
|
const lastSyncedAt = ref(syncStatus.value?.lastSyncedAt || null);
|
|
79
|
-
const uploadError = ref(syncStatus.value?.
|
|
80
|
-
const downloadError = ref(syncStatus.value?.
|
|
81
|
-
const downloadProgressDetails = ref(syncStatus.value?.
|
|
79
|
+
const uploadError = ref(syncStatus.value?.uploadError || null);
|
|
80
|
+
const downloadError = ref(syncStatus.value?.downloadError || null);
|
|
81
|
+
const downloadProgressDetails = ref(syncStatus.value?.downloadProgress || null);
|
|
82
82
|
const bucketRows = ref(null);
|
|
83
83
|
const tableRows = ref(null);
|
|
84
84
|
const uploadQueueStats = ref(null);
|
|
@@ -111,7 +111,7 @@ export function usePowerSyncInspectorDiagnostics() {
|
|
|
111
111
|
const uploadQueueSize = computed(() => formatBytes(uploadQueueStats.value?.size ?? 0));
|
|
112
112
|
const uploadQueueCount = computed(() => uploadQueueStats.value?.count ?? 0);
|
|
113
113
|
const clearData = async () => {
|
|
114
|
-
await db.value?.
|
|
114
|
+
await db.value?.disconnect();
|
|
115
115
|
const connector2 = db.value.connector;
|
|
116
116
|
const connectionOptions2 = db.value.connectionOptions;
|
|
117
117
|
await db.value?.disconnectAndClear();
|
|
@@ -126,7 +126,7 @@ export function usePowerSyncInspectorDiagnostics() {
|
|
|
126
126
|
"SELECT powersync_last_synced_at() as synced_at"
|
|
127
127
|
);
|
|
128
128
|
uploadQueueStats.value = await db.value?.getUploadQueueStats(true);
|
|
129
|
-
if (synced_at != null && !syncStatus.value?.
|
|
129
|
+
if (synced_at != null && !syncStatus.value?.downloading) {
|
|
130
130
|
bucketRows.value = await db.value.getAll(BUCKETS_QUERY);
|
|
131
131
|
tableRows.value = await db.value.getAll(TABLES_QUERY);
|
|
132
132
|
} else if (synced_at != null) {
|
|
@@ -146,16 +146,16 @@ export function usePowerSyncInspectorDiagnostics() {
|
|
|
146
146
|
statusChanged: (newStatus) => {
|
|
147
147
|
hasSynced.value = !!newStatus.hasSynced;
|
|
148
148
|
isConnected.value = !!newStatus.connected;
|
|
149
|
-
isDownloading.value = !!newStatus.
|
|
150
|
-
isUploading.value = !!newStatus.
|
|
149
|
+
isDownloading.value = !!newStatus.downloading;
|
|
150
|
+
isUploading.value = !!newStatus.uploading;
|
|
151
151
|
lastSyncedAt.value = newStatus.lastSyncedAt || null;
|
|
152
|
-
uploadError.value = newStatus.
|
|
153
|
-
downloadError.value = newStatus.
|
|
154
|
-
downloadProgressDetails.value = newStatus.
|
|
152
|
+
uploadError.value = newStatus.uploadError || null;
|
|
153
|
+
downloadError.value = newStatus.downloadError || null;
|
|
154
|
+
downloadProgressDetails.value = newStatus.downloadProgress || null;
|
|
155
155
|
if (newStatus?.hasSynced === void 0 || newStatus?.priorityStatusEntries?.length && newStatus.priorityStatusEntries.length > 0) {
|
|
156
156
|
hasSynced.value = newStatus?.priorityStatusEntries.every((entry) => entry.hasSynced) ?? false;
|
|
157
157
|
}
|
|
158
|
-
if (newStatus?.
|
|
158
|
+
if (newStatus?.downloading || newStatus?.uploading) {
|
|
159
159
|
isSyncing.value = true;
|
|
160
160
|
} else {
|
|
161
161
|
isSyncing.value = false;
|
|
@@ -169,7 +169,6 @@ export function usePowerSyncInspectorDiagnostics() {
|
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
|
-
rawTableNames: true,
|
|
173
172
|
tables: ["ps_oplog", "ps_buckets", "ps_data_local__local_bucket_data", "ps_crud"],
|
|
174
173
|
throttleMs: 500
|
|
175
174
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { DBAdapter
|
|
1
|
+
import type { DBAdapter } from '@powersync/web';
|
|
2
2
|
import { Schema } from '@powersync/web';
|
|
3
|
+
import type { SyncDataBucketJSON } from '@powersync/shared-internals/internal/sync_protocol';
|
|
3
4
|
/**
|
|
4
5
|
* Record fields from downloaded data, then build a schema from it.
|
|
5
6
|
*/
|
|
@@ -8,7 +9,7 @@ export declare class DynamicSchemaManager {
|
|
|
8
9
|
private dirty;
|
|
9
10
|
constructor();
|
|
10
11
|
clear(): Promise<void>;
|
|
11
|
-
updateFromOperations(batch:
|
|
12
|
+
updateFromOperations(batch: SyncDataBucketJSON): Promise<void>;
|
|
12
13
|
refreshSchema(db: DBAdapter): Promise<void>;
|
|
13
14
|
buildSchema(): Schema;
|
|
14
15
|
schemaToString(): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Column, ColumnType,
|
|
1
|
+
import { Column, ColumnType, ResolvedTable, Schema } from "@powersync/web";
|
|
2
2
|
import { DiagnosticsAppSchema as AppSchema } from "./AppSchema.js";
|
|
3
3
|
import { JsSchemaGenerator } from "./JsSchemaGenerator.js";
|
|
4
4
|
export class DynamicSchemaManager {
|
|
@@ -15,34 +15,32 @@ export class DynamicSchemaManager {
|
|
|
15
15
|
}
|
|
16
16
|
async updateFromOperations(batch) {
|
|
17
17
|
let schemaDirty = false;
|
|
18
|
-
for (const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
schemaDirty = true;
|
|
43
|
-
}
|
|
44
|
-
table[key] = ColumnType.TEXT;
|
|
18
|
+
for (const op of batch.data) {
|
|
19
|
+
if (op.op == "PUT" && op.data != null) {
|
|
20
|
+
this.tables[op.object_type] ??= {};
|
|
21
|
+
const table = this.tables[op.object_type];
|
|
22
|
+
const data = JSON.parse(op.data);
|
|
23
|
+
for (const [key, value] of Object.entries(data)) {
|
|
24
|
+
if (key == "id") {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (table) {
|
|
28
|
+
const existing = table[key];
|
|
29
|
+
if (typeof value == "number" && Number.isInteger(value) && existing != ColumnType.REAL && existing != ColumnType.TEXT) {
|
|
30
|
+
if (table[key] != ColumnType.INTEGER) {
|
|
31
|
+
schemaDirty = true;
|
|
32
|
+
}
|
|
33
|
+
table[key] = ColumnType.INTEGER;
|
|
34
|
+
} else if (typeof value == "number" && existing != ColumnType.TEXT) {
|
|
35
|
+
if (table[key] != ColumnType.REAL) {
|
|
36
|
+
schemaDirty = true;
|
|
37
|
+
}
|
|
38
|
+
table[key] = ColumnType.REAL;
|
|
39
|
+
} else if (typeof value == "string") {
|
|
40
|
+
if (table[key] != ColumnType.TEXT) {
|
|
41
|
+
schemaDirty = true;
|
|
45
42
|
}
|
|
43
|
+
table[key] = ColumnType.TEXT;
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
}
|
|
@@ -64,7 +62,7 @@ export class DynamicSchemaManager {
|
|
|
64
62
|
const base = AppSchema;
|
|
65
63
|
const tables = [...base.tables];
|
|
66
64
|
for (const [key, value] of Object.entries(this.tables)) {
|
|
67
|
-
const table = new
|
|
65
|
+
const table = new ResolvedTable({
|
|
68
66
|
name: key,
|
|
69
67
|
columns: Object.entries(value).map(
|
|
70
68
|
([cname, ctype]) => new Column({
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WebPowerSyncDatabase, type DisconnectAndClearOptions, type PowerSyncBackendConnector, type WebPowerSyncDatabaseOptions, type SyncOptions, type CommonPowerSyncDatabase, type PowerSyncDatabaseConstructor } from '@powersync/web';
|
|
2
|
+
import { type StreamingSyncImplementation, type CreateSyncImplementationOptions } from '@powersync/shared-internals';
|
|
3
|
+
export declare class NuxtDatabaseImplementation extends WebPowerSyncDatabase {
|
|
4
|
+
private schemaManager;
|
|
5
|
+
private _connector;
|
|
6
|
+
private useDiagnostics;
|
|
7
|
+
get dbOptions(): WebPowerSyncDatabaseOptions;
|
|
8
|
+
get connector(): PowerSyncBackendConnector | null;
|
|
9
|
+
constructor(options: WebPowerSyncDatabaseOptions);
|
|
10
|
+
protected generateSyncStreamImplementation(connector: PowerSyncBackendConnector, options: CreateSyncImplementationOptions): StreamingSyncImplementation;
|
|
11
|
+
connect(connector: PowerSyncBackendConnector, options?: SyncOptions): Promise<void>;
|
|
12
|
+
disconnect(): Promise<void>;
|
|
13
|
+
disconnectAndClear(options?: DisconnectAndClearOptions): Promise<void>;
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* An extended PowerSync database class that includes diagnostic capabilities for use with the PowerSync Inspector.
|
|
4
17
|
*
|
|
@@ -24,17 +37,6 @@ import { PowerSyncDatabase, type DisconnectAndClearOptions, type PowerSyncBacken
|
|
|
24
37
|
* - Automatically configures logging when diagnostics are enabled
|
|
25
38
|
* - When diagnostics are disabled, behaves like a standard `PowerSyncDatabase`
|
|
26
39
|
*/
|
|
27
|
-
export declare
|
|
28
|
-
|
|
29
|
-
private _connector;
|
|
30
|
-
private _connectionOptions;
|
|
31
|
-
private useDiagnostics;
|
|
32
|
-
get dbOptions(): WebPowerSyncDatabaseOptions;
|
|
33
|
-
get connector(): PowerSyncBackendConnector | null;
|
|
34
|
-
get connectionOptions(): PowerSyncConnectionOptions | null;
|
|
35
|
-
constructor(options: WebPowerSyncDatabaseOptions);
|
|
36
|
-
protected generateSyncStreamImplementation(connector: PowerSyncBackendConnector, options: RequiredAdditionalConnectionOptions): StreamingSyncImplementation;
|
|
37
|
-
connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions): Promise<void>;
|
|
38
|
-
disconnect(): Promise<void>;
|
|
39
|
-
disconnectAndClear(options?: DisconnectAndClearOptions): Promise<void>;
|
|
40
|
+
export declare const NuxtPowerSyncDatabase: PowerSyncDatabaseConstructor<WebPowerSyncDatabaseOptions>;
|
|
41
|
+
export interface NuxtPowerSyncDatabase extends CommonPowerSyncDatabase {
|
|
40
42
|
}
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
PowerSyncDatabase,
|
|
2
|
+
WebPowerSyncDatabase,
|
|
4
3
|
Schema,
|
|
5
4
|
SharedWebStreamingSyncImplementation,
|
|
6
|
-
SyncClientImplementation,
|
|
7
5
|
WebRemote,
|
|
8
|
-
WebStreamingSyncImplementation
|
|
6
|
+
WebStreamingSyncImplementation,
|
|
7
|
+
LogLevels
|
|
9
8
|
} from "@powersync/web";
|
|
10
|
-
import { RecordingStorageAdapter } from "./RecordingStorageAdapter.js";
|
|
11
9
|
import { usePowerSyncInspector } from "../composables/usePowerSyncInspector.js";
|
|
12
10
|
import { useDiagnosticsLogger } from "../composables/useDiagnosticsLogger.js";
|
|
13
11
|
import { shallowRef } from "vue";
|
|
14
12
|
import { useRuntimeConfig } from "#app";
|
|
15
13
|
import { RustClientInterceptor } from "./RustClientInterceptor.js";
|
|
16
|
-
export class
|
|
14
|
+
export class NuxtDatabaseImplementation extends WebPowerSyncDatabase {
|
|
17
15
|
schemaManager;
|
|
18
16
|
_connector = null;
|
|
19
|
-
_connectionOptions = null;
|
|
20
17
|
useDiagnostics = false;
|
|
21
18
|
get dbOptions() {
|
|
22
19
|
return this.options;
|
|
@@ -24,20 +21,16 @@ export class NuxtPowerSyncDatabase extends PowerSyncDatabase {
|
|
|
24
21
|
get connector() {
|
|
25
22
|
return this._connector ?? super.connector;
|
|
26
23
|
}
|
|
27
|
-
get connectionOptions() {
|
|
28
|
-
return this._connectionOptions ?? super.connectionOptions;
|
|
29
|
-
}
|
|
30
24
|
constructor(options) {
|
|
31
25
|
const useDiagnostics = useRuntimeConfig().public.powerSyncModuleOptions.useDiagnostics ?? false;
|
|
32
26
|
if (useDiagnostics) {
|
|
33
27
|
const { logger } = useDiagnosticsLogger();
|
|
34
28
|
const { getCurrentSchemaManager, diagnosticsSchema } = usePowerSyncInspector();
|
|
35
29
|
const currentSchemaManager = getCurrentSchemaManager();
|
|
36
|
-
options
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
30
|
+
if ("database" in options) {
|
|
31
|
+
options.database.enableMultiTabs = true;
|
|
32
|
+
options.broadcastLogs = true;
|
|
33
|
+
}
|
|
41
34
|
options.logger = logger;
|
|
42
35
|
options.schema = new Schema([...options.schema.tables, ...diagnosticsSchema.tables]);
|
|
43
36
|
super(options);
|
|
@@ -54,22 +47,17 @@ export class NuxtPowerSyncDatabase extends PowerSyncDatabase {
|
|
|
54
47
|
const { getCurrentSchemaManager } = usePowerSyncInspector();
|
|
55
48
|
const currentSchemaManager = getCurrentSchemaManager();
|
|
56
49
|
const schemaManager = currentSchemaManager || this.schemaManager;
|
|
57
|
-
const
|
|
58
|
-
const adapter = clientImplementation === SyncClientImplementation.RUST ? new RustClientInterceptor(
|
|
59
|
-
shallowRef(this),
|
|
60
|
-
new WebRemote(connector, logger),
|
|
61
|
-
shallowRef(schemaManager)
|
|
62
|
-
) : new RecordingStorageAdapter(
|
|
50
|
+
const adapter = new RustClientInterceptor(
|
|
63
51
|
shallowRef(this),
|
|
64
52
|
shallowRef(schemaManager)
|
|
65
53
|
);
|
|
66
|
-
if (this.
|
|
67
|
-
if (!this.
|
|
54
|
+
if (this.resolvedOpenOptions.enableMultiTabs) {
|
|
55
|
+
if (!this.enableBroadcastLogs) {
|
|
68
56
|
const warning = `
|
|
69
57
|
Multiple tabs are enabled, but broadcasting of logs is disabled.
|
|
70
58
|
Logs for shared sync worker will only be available in the shared worker context
|
|
71
59
|
`;
|
|
72
|
-
logger ? logger.warn
|
|
60
|
+
logger ? logger.log({ level: LogLevels.warn, message: warning }) : console.warn(warning);
|
|
73
61
|
}
|
|
74
62
|
return new SharedWebStreamingSyncImplementation({
|
|
75
63
|
...options,
|
|
@@ -80,7 +68,9 @@ export class NuxtPowerSyncDatabase extends PowerSyncDatabase {
|
|
|
80
68
|
await connector.uploadData(this);
|
|
81
69
|
},
|
|
82
70
|
logger,
|
|
83
|
-
db: this.database
|
|
71
|
+
db: this.database,
|
|
72
|
+
logLevel: this.resolvedOpenOptions.databaseWorkerLogLevel ?? LogLevels.info,
|
|
73
|
+
enableBroadcastLogs: this.enableBroadcastLogs
|
|
84
74
|
});
|
|
85
75
|
} else {
|
|
86
76
|
return new WebStreamingSyncImplementation({
|
|
@@ -91,7 +81,7 @@ export class NuxtPowerSyncDatabase extends PowerSyncDatabase {
|
|
|
91
81
|
await this.waitForReady();
|
|
92
82
|
await connector.uploadData(this);
|
|
93
83
|
},
|
|
94
|
-
identifier: "
|
|
84
|
+
identifier: "database" in this.options ? this.options.database.dbFilename : "diagnostics-sync",
|
|
95
85
|
logger
|
|
96
86
|
});
|
|
97
87
|
}
|
|
@@ -101,17 +91,15 @@ export class NuxtPowerSyncDatabase extends PowerSyncDatabase {
|
|
|
101
91
|
}
|
|
102
92
|
async connect(connector, options) {
|
|
103
93
|
this._connector = connector;
|
|
104
|
-
this._connectionOptions = options ?? null;
|
|
105
94
|
await super.connect(connector, options);
|
|
106
95
|
}
|
|
107
96
|
async disconnect() {
|
|
108
97
|
this._connector = null;
|
|
109
|
-
this._connectionOptions = null;
|
|
110
98
|
await super.disconnect();
|
|
111
99
|
}
|
|
112
100
|
async disconnectAndClear(options) {
|
|
113
101
|
this._connector = null;
|
|
114
|
-
this._connectionOptions = null;
|
|
115
102
|
await super.disconnectAndClear(options);
|
|
116
103
|
}
|
|
117
104
|
}
|
|
105
|
+
export const NuxtPowerSyncDatabase = NuxtDatabaseImplementation;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { PowerSyncControlCommand, SqliteBucketStorage } from '@powersync/
|
|
1
|
+
import type { ColumnType, PowerSyncDatabase } from '@powersync/web';
|
|
2
|
+
import { PowerSyncControlCommand, SqliteBucketStorage } from '@powersync/shared-internals';
|
|
3
3
|
import type { DynamicSchemaManager } from './DynamicSchemaManager.js';
|
|
4
4
|
import type { ShallowRef } from 'vue';
|
|
5
5
|
/**
|
|
@@ -10,12 +10,12 @@ import type { ShallowRef } from 'vue';
|
|
|
10
10
|
* `powersync_control` calls to decode sync lines and derive progress information.
|
|
11
11
|
*/
|
|
12
12
|
export declare class RustClientInterceptor extends SqliteBucketStorage {
|
|
13
|
-
private remote;
|
|
14
13
|
private schemaManager;
|
|
14
|
+
private bson?;
|
|
15
15
|
private rdb;
|
|
16
16
|
private lastStartedCheckpoint;
|
|
17
17
|
tables: Record<string, Record<string, ColumnType>>;
|
|
18
|
-
constructor(db: ShallowRef<PowerSyncDatabase>,
|
|
18
|
+
constructor(db: ShallowRef<PowerSyncDatabase>, schemaManager: ShallowRef<DynamicSchemaManager>);
|
|
19
19
|
control(op: PowerSyncControlCommand, payload: string | Uint8Array | ArrayBuffer | null): Promise<string>;
|
|
20
20
|
private processTextLine;
|
|
21
21
|
private processBinaryLine;
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AbstractPowerSyncDatabase,
|
|
3
|
-
isStreamingSyncCheckpoint,
|
|
4
|
-
isStreamingSyncCheckpointComplete,
|
|
5
|
-
isStreamingSyncCheckpointDiff,
|
|
6
|
-
isStreamingSyncCheckpointPartiallyComplete,
|
|
7
|
-
isStreamingSyncData,
|
|
8
|
-
PowerSyncControlCommand,
|
|
9
|
-
SqliteBucketStorage,
|
|
10
|
-
SyncDataBucket
|
|
11
|
-
} from "@powersync/web";
|
|
1
|
+
import { BasePowerSyncDatabase, PowerSyncControlCommand, SqliteBucketStorage } from "@powersync/shared-internals";
|
|
12
2
|
export class RustClientInterceptor extends SqliteBucketStorage {
|
|
13
|
-
constructor(db,
|
|
14
|
-
super(db.value.database,
|
|
15
|
-
this.remote = remote;
|
|
3
|
+
constructor(db, schemaManager) {
|
|
4
|
+
super(db.value.database, BasePowerSyncDatabase.transactionMutex);
|
|
16
5
|
this.schemaManager = schemaManager;
|
|
17
6
|
this.rdb = db.value.database;
|
|
18
7
|
}
|
|
8
|
+
bson;
|
|
19
9
|
rdb;
|
|
20
10
|
lastStartedCheckpoint = null;
|
|
21
11
|
tables = {};
|
|
@@ -32,14 +22,14 @@ export class RustClientInterceptor extends SqliteBucketStorage {
|
|
|
32
22
|
return this.processParsedLine(JSON.parse(line));
|
|
33
23
|
}
|
|
34
24
|
async processBinaryLine(line) {
|
|
35
|
-
const bson = await
|
|
25
|
+
const bson = this.bson ??= await import("bson");
|
|
36
26
|
await this.processParsedLine(bson.deserialize(line));
|
|
37
27
|
}
|
|
38
28
|
async processParsedLine(line) {
|
|
39
|
-
if (
|
|
29
|
+
if ("checkpoint" in line) {
|
|
40
30
|
this.lastStartedCheckpoint = line.checkpoint;
|
|
41
31
|
await this.trackCheckpoint(line.checkpoint);
|
|
42
|
-
} else if (
|
|
32
|
+
} else if ("checkpoint_diff" in line && this.lastStartedCheckpoint) {
|
|
43
33
|
const diff = line.checkpoint_diff;
|
|
44
34
|
const newBuckets = /* @__PURE__ */ new Map();
|
|
45
35
|
for (const checksum of this.lastStartedCheckpoint.buckets) {
|
|
@@ -58,24 +48,22 @@ export class RustClientInterceptor extends SqliteBucketStorage {
|
|
|
58
48
|
};
|
|
59
49
|
this.lastStartedCheckpoint = newCheckpoint;
|
|
60
50
|
await this.trackCheckpoint(newCheckpoint);
|
|
61
|
-
} else if (
|
|
62
|
-
const
|
|
51
|
+
} else if ("data" in line) {
|
|
52
|
+
const bucket = line.data;
|
|
63
53
|
await this.rdb.writeTransaction(async (tx) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`UPDATE local_bucket_data SET
|
|
54
|
+
const size = JSON.stringify(bucket.data).length;
|
|
55
|
+
await tx.execute(
|
|
56
|
+
`UPDATE local_bucket_data SET
|
|
68
57
|
download_size = IFNULL(download_size, 0) + ?,
|
|
69
58
|
last_op = ?,
|
|
70
59
|
downloading = ?,
|
|
71
60
|
downloaded_operations = IFNULL(downloaded_operations, 0) + ?
|
|
72
61
|
WHERE id = ?`,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
62
|
+
[size, bucket.next_after, bucket.has_more, bucket.data.length, bucket.bucket]
|
|
63
|
+
);
|
|
76
64
|
});
|
|
77
|
-
await this.schemaManager.value.updateFromOperations(
|
|
78
|
-
} else if (
|
|
65
|
+
await this.schemaManager.value.updateFromOperations(bucket);
|
|
66
|
+
} else if ("partial_checkpoint_complete" in line || "checkpoint_complete" in line) {
|
|
79
67
|
setTimeout(() => {
|
|
80
68
|
this.schemaManager.value.refreshSchema(this.rdb);
|
|
81
69
|
}, 60);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powersync/nuxt",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260630141119",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -48,13 +48,15 @@
|
|
|
48
48
|
"reka-ui": "^2.9.1",
|
|
49
49
|
"shiki": "^3.23.0",
|
|
50
50
|
"unocss": "^66.6.6",
|
|
51
|
-
"unstorage": "^1.17.4"
|
|
51
|
+
"unstorage": "^1.17.4",
|
|
52
|
+
"bson": "^6.10.4"
|
|
52
53
|
},
|
|
53
54
|
"peerDependencies": {
|
|
54
55
|
"@journeyapps/wa-sqlite": "^1.7.0",
|
|
55
|
-
"@powersync/kysely-driver": "
|
|
56
|
-
"@powersync/vue": "0.
|
|
57
|
-
"@powersync/web": "0.0.0-dev-
|
|
56
|
+
"@powersync/kysely-driver": "0.0.0-dev-20260630141119",
|
|
57
|
+
"@powersync/vue": "0.0.0-dev-20260630141119",
|
|
58
|
+
"@powersync/web": "0.0.0-dev-20260630141119",
|
|
59
|
+
"@powersync/shared-internals": "1.0.0"
|
|
58
60
|
},
|
|
59
61
|
"peerDependenciesMeta": {
|
|
60
62
|
"@powersync/kysely-driver": {
|
|
@@ -65,16 +67,16 @@
|
|
|
65
67
|
"@journeyapps/wa-sqlite": "^1.7.0",
|
|
66
68
|
"@nuxt/module-builder": "^1.0.2",
|
|
67
69
|
"@nuxt/schema": "^4.3.1",
|
|
68
|
-
"@nuxt/test-utils": "^4.0.
|
|
69
|
-
"bson": "^6.10.4",
|
|
70
|
+
"@nuxt/test-utils": "^4.0.3",
|
|
70
71
|
"comlink": "^4.4.2",
|
|
71
72
|
"nuxt": "^4.3.1",
|
|
72
|
-
"vitest": "^
|
|
73
|
+
"vitest": "^4.1.8",
|
|
73
74
|
"vue": "^3.5.30",
|
|
74
75
|
"vue-tsc": "^3.2.5",
|
|
75
|
-
"@powersync/
|
|
76
|
-
"@powersync/
|
|
77
|
-
"@powersync/web": "0.0.0-dev-
|
|
76
|
+
"@powersync/kysely-driver": "0.0.0-dev-20260630141119",
|
|
77
|
+
"@powersync/vue": "0.0.0-dev-20260630141119",
|
|
78
|
+
"@powersync/web": "0.0.0-dev-20260630141119",
|
|
79
|
+
"@powersync/common": "0.0.0-dev-20260630141119"
|
|
78
80
|
},
|
|
79
81
|
"scripts": {
|
|
80
82
|
"prebuild": "nuxt-module-build prepare",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Checkpoint, ColumnType, PowerSyncDatabase, SyncDataBatch } from '@powersync/web';
|
|
2
|
-
import { SqliteBucketStorage } from '@powersync/web';
|
|
3
|
-
import type { DynamicSchemaManager } from './DynamicSchemaManager.js';
|
|
4
|
-
import type { ShallowRef } from 'vue';
|
|
5
|
-
export declare class RecordingStorageAdapter extends SqliteBucketStorage {
|
|
6
|
-
private rdb;
|
|
7
|
-
private schemaManager;
|
|
8
|
-
tables: Record<string, Record<string, ColumnType>>;
|
|
9
|
-
constructor(db: ShallowRef<PowerSyncDatabase>, schemaManager: ShallowRef<DynamicSchemaManager>);
|
|
10
|
-
setTargetCheckpoint(checkpoint: Checkpoint): Promise<void>;
|
|
11
|
-
syncLocalDatabase(checkpoint: Checkpoint, priority?: number): Promise<import("@powersync/common").SyncLocalDatabaseResult>;
|
|
12
|
-
saveSyncData(batch: SyncDataBatch): Promise<void>;
|
|
13
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { AbstractPowerSyncDatabase, SqliteBucketStorage } from "@powersync/web";
|
|
2
|
-
export class RecordingStorageAdapter extends SqliteBucketStorage {
|
|
3
|
-
rdb;
|
|
4
|
-
schemaManager;
|
|
5
|
-
tables = {};
|
|
6
|
-
constructor(db, schemaManager) {
|
|
7
|
-
super(db.value.database, AbstractPowerSyncDatabase.transactionMutex);
|
|
8
|
-
this.rdb = db.value.database;
|
|
9
|
-
this.schemaManager = schemaManager.value;
|
|
10
|
-
}
|
|
11
|
-
async setTargetCheckpoint(checkpoint) {
|
|
12
|
-
await super.setTargetCheckpoint(checkpoint);
|
|
13
|
-
await this.rdb.writeTransaction(async (tx) => {
|
|
14
|
-
for (const bucket of checkpoint.buckets) {
|
|
15
|
-
await tx.execute(
|
|
16
|
-
`INSERT OR REPLACE INTO local_bucket_data(id, total_operations, last_op, download_size, downloading, downloaded_operations)
|
|
17
|
-
VALUES (
|
|
18
|
-
?,
|
|
19
|
-
?,
|
|
20
|
-
IFNULL((SELECT last_op FROM local_bucket_data WHERE id = ?), '0'),
|
|
21
|
-
IFNULL((SELECT download_size FROM local_bucket_data WHERE id = ?), 0),
|
|
22
|
-
IFNULL((SELECT downloading FROM local_bucket_data WHERE id = ?), TRUE),
|
|
23
|
-
IFNULL((SELECT downloaded_operations FROM local_bucket_data WHERE id = ?), TRUE)
|
|
24
|
-
)`,
|
|
25
|
-
[bucket.bucket, bucket.count, bucket.bucket, bucket.bucket, bucket.bucket, bucket.bucket]
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
async syncLocalDatabase(checkpoint, priority) {
|
|
31
|
-
const r = await super.syncLocalDatabase(checkpoint, priority);
|
|
32
|
-
setTimeout(() => {
|
|
33
|
-
this.schemaManager.refreshSchema(this.rdb);
|
|
34
|
-
}, 60);
|
|
35
|
-
if (r.checkpointValid) {
|
|
36
|
-
await this.rdb.execute("UPDATE local_bucket_data SET downloading = FALSE");
|
|
37
|
-
}
|
|
38
|
-
return r;
|
|
39
|
-
}
|
|
40
|
-
async saveSyncData(batch) {
|
|
41
|
-
await super.saveSyncData(batch);
|
|
42
|
-
await this.rdb.writeTransaction(async (tx) => {
|
|
43
|
-
for (const bucket of batch.buckets) {
|
|
44
|
-
const size = JSON.stringify(bucket.data).length;
|
|
45
|
-
await tx.execute(
|
|
46
|
-
`UPDATE local_bucket_data SET
|
|
47
|
-
download_size = IFNULL(download_size, 0) + ?,
|
|
48
|
-
last_op = ?,
|
|
49
|
-
downloading = ?,
|
|
50
|
-
downloaded_operations = IFNULL(downloaded_operations, 0) + ?
|
|
51
|
-
WHERE id = ?`,
|
|
52
|
-
[size, bucket.next_after, bucket.has_more, bucket.data.length, bucket.bucket]
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
await this.schemaManager.updateFromOperations(batch);
|
|
57
|
-
}
|
|
58
|
-
}
|