Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/quiet-owls-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@powersync/react-native': minor
'@powersync/common': minor
'@powersync/node': minor
'@powersync/web': minor
'@powersync/capacitor': minor
---

Added ability to specify `appMetadata` for sync/stream requests

```javascript
powerSync.connect(connector, {
// This will be included in PowerSync service logs
appMetadata: {
app_version: MY_APP_VERSION
}
});
```
9 changes: 5 additions & 4 deletions demos/example-node/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import {
SyncStreamConnectionMethod
} from '@powersync/node';
import { exit } from 'node:process';
import { WorkerOpener } from 'node_modules/@powersync/node/src/db/options.js';
import { AppSchema, DemoConnector } from './powersync.js';
import { enableUncidiDiagnostics } from './UndiciDiagnostics.js';
import { WorkerOpener } from 'node_modules/@powersync/node/src/db/options.js';
import { LockContext } from 'node_modules/@powersync/node/dist/bundle.cjs';

const main = async () => {
const baseLogger = createBaseLogger();
Expand Down Expand Up @@ -59,10 +58,12 @@ const main = async () => {
logger
});
console.log(await db.get('SELECT powersync_rs_version();'));

await db.connect(new DemoConnector(), {
connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
clientImplementation: SyncClientImplementation.RUST
clientImplementation: SyncClientImplementation.RUST,
appMetadata: {
app_version: process.env.npm_package_version || 'unknown'
}
});
// Example using a proxy agent for more control over the connection:
// const proxyAgent = new (await import('undici')).ProxyAgent({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { createBaseLogger, DifferentialWatchedQuery, LogLevel, PowerSyncDatabase
import React, { Suspense } from 'react';
import { NavigationPanelContextProvider } from '../navigation/NavigationPanelContext';

declare const APP_VERSION: string;

const SupabaseContext = React.createContext<SupabaseConnector | null>(null);
export const useSupabase = () => React.useContext(SupabaseContext);

Expand Down Expand Up @@ -68,7 +70,11 @@ export const SystemProvider = ({ children }: { children: React.ReactNode }) => {
const l = connector.registerListener({
initialized: () => {},
sessionStarted: () => {
powerSync.connect(connector);
powerSync.connect(connector, {
appMetadata: {
app_version: APP_VERSION
}
});
}
});

Expand Down
9 changes: 6 additions & 3 deletions demos/react-supabase-todolist/vite.config.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';
import { fileURLToPath, URL } from 'url';
import topLevelAwait from 'vite-plugin-top-level-await';
import wasm from 'vite-plugin-wasm';

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';

// https://vitejs.dev/config/
Expand All @@ -19,6 +19,9 @@ export default defineConfig({
resolve: {
alias: [{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }]
},
define: {
APP_VERSION: JSON.stringify(process.env.npm_package_version)
},
publicDir: '../public',
envDir: '..', // Use this dir for env vars, not 'src'.
optimizeDeps: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { CrudEntry } from '../bucket/CrudEntry.js';
import { SyncDataBucket } from '../bucket/SyncDataBucket.js';
import { AbstractRemote, FetchStrategy, SyncStreamOptions } from './AbstractRemote.js';
import { coreStatusToJs, EstablishSyncStream, Instruction, SyncPriorityStatus } from './core-instruction.js';
import { EstablishSyncStream, Instruction, coreStatusToJs } from './core-instruction.js';
import {
BucketRequest,
CrudUploadNotification,
Expand Down Expand Up @@ -129,6 +129,11 @@ export interface InternalConnectionOptions extends BaseConnectionOptions, Additi

/** @internal */
export interface BaseConnectionOptions {
/**
* A set of metadata to be included in service logs.
*/
appMetadata?: Record<string, string>;

/**
* Whether to use a JavaScript implementation to handle received sync lines from the sync
* service, or whether this work should be offloaded to the PowerSync core extension.
Expand Down Expand Up @@ -223,6 +228,7 @@ export const DEFAULT_STREAMING_SYNC_OPTIONS = {
export type RequiredPowerSyncConnectionOptions = Required<BaseConnectionOptions>;

export const DEFAULT_STREAM_CONNECTION_OPTIONS: RequiredPowerSyncConnectionOptions = {
appMetadata: {},
connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
clientImplementation: DEFAULT_SYNC_CLIENT_IMPLEMENTATION,
fetchStrategy: FetchStrategy.Buffered,
Expand Down Expand Up @@ -658,6 +664,16 @@ The next upload iteration will be delayed.`);
...DEFAULT_STREAM_CONNECTION_OPTIONS,
...(options ?? {})
};

// Validate app metadata
const invalidMetadata = Object.entries(resolvedOptions.appMetadata).filter(
([_, value]) => typeof value != 'string'
);
if (invalidMetadata.length > 0) {
throw new Error(
`Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`
);
}
const clientImplementation = resolvedOptions.clientImplementation;
this.updateSyncStatus({ clientImplementation });

Expand Down Expand Up @@ -699,6 +715,7 @@ The next upload iteration will be delayed.`);
include_checksum: true,
raw_data: true,
parameters: resolvedOptions.params,
app_metadata: resolvedOptions.appMetadata,
client_id: clientId
}
};
Expand Down Expand Up @@ -947,7 +964,14 @@ The next upload iteration will be delayed.`);
const syncOptions: SyncStreamOptions = {
path: '/sync/stream',
abortSignal: abortController.signal,
data: instr.request
data: {
...instr.request,
// FIXME the Rust core does not currently pass metadata through
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app_metadata: {
...resolvedOptions.appMetadata,
...instr.request.app_metadata
}
}
};

if (resolvedOptions.connectionMethod == SyncStreamConnectionMethod.HTTP) {
Expand Down Expand Up @@ -1088,6 +1112,7 @@ The next upload iteration will be delayed.`);
try {
const options: any = {
parameters: resolvedOptions.params,
app_metadata: resolvedOptions.appMetadata,
active_streams: this.activeStreams,
include_defaults: resolvedOptions.includeDefaultStreams
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ export interface StreamingSyncRequest {
*/
parameters?: Record<string, StreamingSyncRequestParameterType>;

/**
* Application metadata to be included in service logs.
*/
app_metadata?: Record<string, string>;

client_id?: string;
}

Expand Down