@powersync/service-core 1.15.7 → 1.16.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.
- package/CHANGELOG.md +33 -0
- package/dist/entry/commands/compact-action.js +1 -1
- package/dist/entry/commands/compact-action.js.map +1 -1
- package/dist/events/EventsEngine.d.ts +14 -0
- package/dist/events/EventsEngine.js +33 -0
- package/dist/events/EventsEngine.js.map +1 -0
- package/dist/routes/configure-fastify.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +15 -2
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +19 -2
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/router.d.ts +3 -3
- package/dist/storage/BucketStorageFactory.d.ts +2 -0
- package/dist/storage/ReportStorage.d.ts +36 -0
- package/dist/storage/ReportStorage.js +2 -0
- package/dist/storage/ReportStorage.js.map +1 -0
- package/dist/storage/StorageEngine.d.ts +2 -2
- package/dist/storage/StorageEngine.js.map +1 -1
- package/dist/storage/StorageProvider.d.ts +3 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +12 -1
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/storage/storage-index.d.ts +1 -0
- package/dist/storage/storage-index.js +1 -0
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +3 -0
- package/dist/system/ServiceContext.js +10 -1
- package/dist/system/ServiceContext.js.map +1 -1
- package/package.json +7 -7
- package/src/entry/commands/compact-action.ts +1 -1
- package/src/events/EventsEngine.ts +38 -0
- package/src/routes/configure-fastify.ts +0 -1
- package/src/routes/endpoints/socket-route.ts +16 -3
- package/src/routes/endpoints/sync-stream.ts +19 -2
- package/src/routes/router.ts +3 -3
- package/src/storage/BucketStorageFactory.ts +2 -0
- package/src/storage/ReportStorage.ts +39 -0
- package/src/storage/StorageEngine.ts +3 -3
- package/src/storage/StorageProvider.ts +3 -1
- package/src/storage/SyncRulesBucketStorage.ts +14 -1
- package/src/storage/storage-index.ts +1 -0
- package/src/system/ServiceContext.ts +13 -1
- package/test/src/routes/mocks.ts +2 -0
- package/test/src/routes/stream.test.ts +14 -6
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
3
|
+
import { event_types } from '@powersync/service-types';
|
|
4
|
+
|
|
5
|
+
export class EventsEngine {
|
|
6
|
+
private emitter: EventEmitter;
|
|
7
|
+
private events: Set<event_types.EventsEngineEventType> = new Set();
|
|
8
|
+
constructor() {
|
|
9
|
+
this.emitter = new EventEmitter({ captureRejections: true });
|
|
10
|
+
this.emitter.on('error', (error: Error) => {
|
|
11
|
+
logger.error(error.message);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* All new events added need to be subscribed to be used.
|
|
17
|
+
* @example engine.subscribe(new MyNewEvent(storageEngine));
|
|
18
|
+
*/
|
|
19
|
+
subscribe<K extends event_types.EventsEngineEventType>(event: event_types.EmitterEvent<K>): void {
|
|
20
|
+
if (!this.events.has(event.event)) {
|
|
21
|
+
this.events.add(event.event);
|
|
22
|
+
}
|
|
23
|
+
this.emitter.on(event.event, event.handler.bind(event));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get listEvents(): event_types.EventsEngineEventType[] {
|
|
27
|
+
return Array.from(this.events.values());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
emit<K extends keyof event_types.SubscribeEvents>(event: K, data: event_types.SubscribeEvents[K]): void {
|
|
31
|
+
this.emitter.emit(event, data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
shutDown(): void {
|
|
35
|
+
logger.info(`Shutting down EmitterEngine and removing all listeners for ${this.listEvents.join(', ')}.`);
|
|
36
|
+
this.emitter.removeAllListeners();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ErrorCode, errors, schema } from '@powersync/lib-services-framework';
|
|
2
|
-
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
3
2
|
|
|
4
3
|
import * as sync from '../../sync/sync-index.js';
|
|
5
4
|
import * as util from '../../util/util-index.js';
|
|
6
5
|
import { SocketRouteGenerator } from '../router-socket.js';
|
|
7
6
|
import { SyncRoutes } from './sync-stream.js';
|
|
8
7
|
|
|
9
|
-
import { APIMetric } from '@powersync/service-types';
|
|
8
|
+
import { APIMetric, event_types } from '@powersync/service-types';
|
|
10
9
|
|
|
11
10
|
export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
12
11
|
router.reactiveStream<util.StreamingSyncRequest, any>(SyncRoutes.STREAM, {
|
|
@@ -14,6 +13,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
14
13
|
handler: async ({ context, params, responder, observer, initialN, signal: upstreamSignal, connection }) => {
|
|
15
14
|
const { service_context, logger } = context;
|
|
16
15
|
const { routerEngine, metricsEngine, syncContext } = service_context;
|
|
16
|
+
const streamStart = Date.now();
|
|
17
17
|
|
|
18
18
|
logger.defaultMeta = {
|
|
19
19
|
...logger.defaultMeta,
|
|
@@ -21,7 +21,15 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
21
21
|
client_id: params.client_id,
|
|
22
22
|
user_agent: context.user_agent
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
const sdkData: event_types.ConnectedUserData & event_types.ClientConnectionEventData = {
|
|
26
|
+
client_id: params.client_id ?? '',
|
|
27
|
+
user_id: context.user_id!,
|
|
28
|
+
user_agent: context.user_agent,
|
|
29
|
+
// At this point the token_payload is guaranteed to be present
|
|
30
|
+
jwt_exp: new Date(context.token_payload!.exp * 1000),
|
|
31
|
+
connected_at: new Date(streamStart)
|
|
32
|
+
};
|
|
25
33
|
|
|
26
34
|
// Best effort guess on why the stream was closed.
|
|
27
35
|
// We use the `??=` operator everywhere, so that we catch the first relevant
|
|
@@ -83,6 +91,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
83
91
|
});
|
|
84
92
|
|
|
85
93
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(1);
|
|
94
|
+
service_context.eventsEngine.emit(event_types.EventsEngineEventType.SDK_CONNECT_EVENT, sdkData);
|
|
86
95
|
const tracker = new sync.RequestTracker(metricsEngine);
|
|
87
96
|
if (connection.tracker.encoding) {
|
|
88
97
|
// Must be set before we start the stream
|
|
@@ -174,6 +183,10 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
174
183
|
close_reason: closeReason ?? 'unknown'
|
|
175
184
|
});
|
|
176
185
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
|
|
186
|
+
service_context.eventsEngine.emit(event_types.EventsEngineEventType.SDK_DISCONNECT_EVENT, {
|
|
187
|
+
...sdkData,
|
|
188
|
+
disconnected_at: new Date()
|
|
189
|
+
});
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
192
|
});
|
|
@@ -7,8 +7,8 @@ import * as util from '../../util/util-index.js';
|
|
|
7
7
|
|
|
8
8
|
import { authUser } from '../auth.js';
|
|
9
9
|
import { routeDefinition } from '../router.js';
|
|
10
|
+
import { APIMetric, event_types } from '@powersync/service-types';
|
|
10
11
|
|
|
11
|
-
import { APIMetric } from '@powersync/service-types';
|
|
12
12
|
import { maybeCompressResponseStream } from '../compression.js';
|
|
13
13
|
|
|
14
14
|
export enum SyncRoutes {
|
|
@@ -25,7 +25,7 @@ export const syncStreamed = routeDefinition({
|
|
|
25
25
|
authorize: authUser,
|
|
26
26
|
validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
|
|
27
27
|
handler: async (payload) => {
|
|
28
|
-
const { service_context, logger } = payload.context;
|
|
28
|
+
const { service_context, logger, token_payload } = payload.context;
|
|
29
29
|
const { routerEngine, storageEngine, metricsEngine, syncContext } = service_context;
|
|
30
30
|
const headers = payload.request.headers;
|
|
31
31
|
const userAgent = headers['x-user-agent'] ?? headers['user-agent'];
|
|
@@ -44,6 +44,14 @@ export const syncStreamed = routeDefinition({
|
|
|
44
44
|
user_id: payload.context.user_id,
|
|
45
45
|
bson: useBson
|
|
46
46
|
};
|
|
47
|
+
const sdkData: event_types.ConnectedUserData & event_types.ClientConnectionEventData = {
|
|
48
|
+
client_id: clientId ?? '',
|
|
49
|
+
user_id: payload.context.user_id!,
|
|
50
|
+
user_agent: userAgent as string,
|
|
51
|
+
// At this point the token_payload is guaranteed to be present
|
|
52
|
+
jwt_exp: new Date(token_payload!.exp * 1000),
|
|
53
|
+
connected_at: new Date(streamStart)
|
|
54
|
+
};
|
|
47
55
|
|
|
48
56
|
if (routerEngine.closed) {
|
|
49
57
|
throw new errors.ServiceError({
|
|
@@ -69,6 +77,7 @@ export const syncStreamed = routeDefinition({
|
|
|
69
77
|
const tracker = new sync.RequestTracker(metricsEngine);
|
|
70
78
|
try {
|
|
71
79
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(1);
|
|
80
|
+
service_context.eventsEngine.emit(event_types.EventsEngineEventType.SDK_CONNECT_EVENT, sdkData);
|
|
72
81
|
const syncLines = sync.streamResponse({
|
|
73
82
|
syncContext: syncContext,
|
|
74
83
|
bucketStorage,
|
|
@@ -134,6 +143,10 @@ export const syncStreamed = routeDefinition({
|
|
|
134
143
|
}
|
|
135
144
|
controller.abort();
|
|
136
145
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
|
|
146
|
+
service_context.eventsEngine.emit(event_types.EventsEngineEventType.SDK_DISCONNECT_EVENT, {
|
|
147
|
+
...sdkData,
|
|
148
|
+
disconnected_at: new Date()
|
|
149
|
+
});
|
|
137
150
|
logger.info(`Sync stream complete`, {
|
|
138
151
|
...tracker.getLogMeta(),
|
|
139
152
|
stream_ms: Date.now() - streamStart,
|
|
@@ -144,6 +157,10 @@ export const syncStreamed = routeDefinition({
|
|
|
144
157
|
} catch (ex) {
|
|
145
158
|
controller.abort();
|
|
146
159
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
|
|
160
|
+
service_context.eventsEngine.emit(event_types.EventsEngineEventType.SDK_DISCONNECT_EVENT, {
|
|
161
|
+
...sdkData,
|
|
162
|
+
disconnected_at: new Date()
|
|
163
|
+
});
|
|
147
164
|
}
|
|
148
165
|
}
|
|
149
166
|
});
|
package/src/routes/router.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Logger, router } from '@powersync/lib-services-framework';
|
|
2
2
|
import type { JwtPayload } from '../auth/auth-index.js';
|
|
3
3
|
import { ServiceContext } from '../system/ServiceContext.js';
|
|
4
4
|
import { RouterEngine } from './RouterEngine.js';
|
|
@@ -31,11 +31,11 @@ export type BasicRouterRequest = {
|
|
|
31
31
|
hostname: string;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
export type
|
|
34
|
+
export type ContextProviderOptions = {
|
|
35
35
|
logger: Logger;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export type ContextProvider = (request: BasicRouterRequest, options:
|
|
38
|
+
export type ContextProvider = (request: BasicRouterRequest, options: ContextProviderOptions) => Promise<Context>;
|
|
39
39
|
|
|
40
40
|
export type RequestEndpoint<
|
|
41
41
|
I,
|
|
@@ -3,6 +3,7 @@ import { ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent }
|
|
|
3
3
|
import { ReplicationEventPayload } from './ReplicationEventPayload.js';
|
|
4
4
|
import { ReplicationLock } from './ReplicationLock.js';
|
|
5
5
|
import { SyncRulesBucketStorage } from './SyncRulesBucketStorage.js';
|
|
6
|
+
import { ReportStorage } from './ReportStorage.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Represents a configured storage provider.
|
|
@@ -164,3 +165,4 @@ export interface TestStorageOptions {
|
|
|
164
165
|
doNotClear?: boolean;
|
|
165
166
|
}
|
|
166
167
|
export type TestStorageFactory = (options?: TestStorageOptions) => Promise<BucketStorageFactory>;
|
|
168
|
+
export type TestReportStorageFactory = (options?: TestStorageOptions) => Promise<ReportStorage>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { event_types } from '@powersync/service-types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a configured report storage.
|
|
5
|
+
*
|
|
6
|
+
* Report storage is used for storing localized data for the instance.
|
|
7
|
+
* Data can then be used for reporting purposes.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export interface ReportStorage extends AsyncDisposable {
|
|
11
|
+
/**
|
|
12
|
+
* Report a client connection.
|
|
13
|
+
*/
|
|
14
|
+
reportClientConnection(data: event_types.ClientConnectionBucketData): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Report a client disconnection.
|
|
17
|
+
*/
|
|
18
|
+
reportClientDisconnection(data: event_types.ClientDisconnectionEventData): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Get currently connected clients.
|
|
21
|
+
* This will return any short or long term connected clients.
|
|
22
|
+
* Clients that have no disconnected_at timestamp and that have a valid jwt_exp timestamp are considered connected.
|
|
23
|
+
*/
|
|
24
|
+
getConnectedClients(): Promise<event_types.ClientConnectionReportResponse>;
|
|
25
|
+
/**
|
|
26
|
+
* Get a report of client connections over a day, week or month.
|
|
27
|
+
* This is internally used to generate reports over it always returns the previous day, week or month.
|
|
28
|
+
* Usually this is call on the start of the new day, week or month. It will return all unique completed connections
|
|
29
|
+
* as well as uniques currently connected clients.
|
|
30
|
+
*/
|
|
31
|
+
getClientConnectionReports(
|
|
32
|
+
data: event_types.ClientConnectionReportRequest
|
|
33
|
+
): Promise<event_types.ClientConnectionReportResponse>;
|
|
34
|
+
/**
|
|
35
|
+
* Delete old connection data based on a specific date.
|
|
36
|
+
* This is used to clean up old connection data that is no longer needed.
|
|
37
|
+
*/
|
|
38
|
+
deleteOldConnectionData(data: event_types.DeleteOldConnectionData): Promise<void>;
|
|
39
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BaseObserver, logger, ServiceError } from '@powersync/lib-services-framework';
|
|
2
2
|
import { ResolvedPowerSyncConfig } from '../util/util-index.js';
|
|
3
3
|
import { BucketStorageFactory } from './BucketStorageFactory.js';
|
|
4
|
-
import { ActiveStorage,
|
|
4
|
+
import { ActiveStorage, StorageProvider } from './StorageProvider.js';
|
|
5
5
|
|
|
6
6
|
export type StorageEngineOptions = {
|
|
7
7
|
configuration: ResolvedPowerSyncConfig;
|
|
@@ -14,7 +14,7 @@ export interface StorageEngineListener {
|
|
|
14
14
|
|
|
15
15
|
export class StorageEngine extends BaseObserver<StorageEngineListener> {
|
|
16
16
|
// TODO: This will need to revisited when we actually support multiple storage providers.
|
|
17
|
-
private storageProviders: Map<string,
|
|
17
|
+
private storageProviders: Map<string, StorageProvider> = new Map();
|
|
18
18
|
private currentActiveStorage: ActiveStorage | null = null;
|
|
19
19
|
|
|
20
20
|
constructor(private options: StorageEngineOptions) {
|
|
@@ -37,7 +37,7 @@ export class StorageEngine extends BaseObserver<StorageEngineListener> {
|
|
|
37
37
|
* Register a provider which generates a {@link BucketStorageFactory}
|
|
38
38
|
* given the matching config specified in the loaded {@link ResolvedPowerSyncConfig}
|
|
39
39
|
*/
|
|
40
|
-
registerProvider(provider:
|
|
40
|
+
registerProvider(provider: StorageProvider) {
|
|
41
41
|
this.storageProviders.set(provider.type, provider);
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ServiceError } from '@powersync/lib-services-framework';
|
|
2
2
|
import * as util from '../util/util-index.js';
|
|
3
3
|
import { BucketStorageFactory } from './BucketStorageFactory.js';
|
|
4
|
+
import { ReportStorage } from './ReportStorage.js';
|
|
4
5
|
|
|
5
6
|
export interface ActiveStorage {
|
|
6
7
|
storage: BucketStorageFactory;
|
|
8
|
+
reportStorage: ReportStorage;
|
|
7
9
|
shutDown(): Promise<void>;
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -22,7 +24,7 @@ export interface GetStorageOptions {
|
|
|
22
24
|
/**
|
|
23
25
|
* Represents a provider that can create a storage instance for a specific storage type from configuration.
|
|
24
26
|
*/
|
|
25
|
-
export interface
|
|
27
|
+
export interface StorageProvider {
|
|
26
28
|
/**
|
|
27
29
|
* The storage type that this provider provides.
|
|
28
30
|
* The type should match the `type` field in the config.
|
|
@@ -65,7 +65,7 @@ export interface SyncRulesBucketStorage
|
|
|
65
65
|
/**
|
|
66
66
|
* Lightweight "compact" process to populate the checksum cache, if any.
|
|
67
67
|
*/
|
|
68
|
-
populatePersistentChecksumCache(options
|
|
68
|
+
populatePersistentChecksumCache(options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults>;
|
|
69
69
|
|
|
70
70
|
// ## Read operations
|
|
71
71
|
|
|
@@ -225,6 +225,19 @@ export interface CompactOptions {
|
|
|
225
225
|
signal?: AbortSignal;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
export interface PopulateChecksumCacheOptions {
|
|
229
|
+
maxOpId: util.InternalOpId;
|
|
230
|
+
minBucketChanges?: number;
|
|
231
|
+
signal?: AbortSignal;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface PopulateChecksumCacheResults {
|
|
235
|
+
/**
|
|
236
|
+
* Number of buckets we have calculated checksums for.
|
|
237
|
+
*/
|
|
238
|
+
buckets: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
228
241
|
export interface ClearStorageOptions {
|
|
229
242
|
signal?: AbortSignal;
|
|
230
243
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LifeCycledSystem, MigrationManager, ServiceIdentifier
|
|
1
|
+
import { container, LifeCycledSystem, MigrationManager, ServiceIdentifier } from '@powersync/lib-services-framework';
|
|
2
2
|
|
|
3
3
|
import { framework } from '../index.js';
|
|
4
4
|
import * as metrics from '../metrics/MetricsEngine.js';
|
|
@@ -8,6 +8,7 @@ import * as routes from '../routes/routes-index.js';
|
|
|
8
8
|
import * as storage from '../storage/storage-index.js';
|
|
9
9
|
import { SyncContext } from '../sync/SyncContext.js';
|
|
10
10
|
import * as utils from '../util/util-index.js';
|
|
11
|
+
import { EventsEngine } from '../events/EventsEngine.js';
|
|
11
12
|
|
|
12
13
|
export interface ServiceContext {
|
|
13
14
|
configuration: utils.ResolvedPowerSyncConfig;
|
|
@@ -19,6 +20,7 @@ export interface ServiceContext {
|
|
|
19
20
|
migrations: PowerSyncMigrationManager;
|
|
20
21
|
syncContext: SyncContext;
|
|
21
22
|
serviceMode: ServiceContextMode;
|
|
23
|
+
eventsEngine: EventsEngine;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export enum ServiceContextMode {
|
|
@@ -45,6 +47,7 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
45
47
|
configuration: utils.ResolvedPowerSyncConfig;
|
|
46
48
|
lifeCycleEngine: LifeCycledSystem;
|
|
47
49
|
storageEngine: storage.StorageEngine;
|
|
50
|
+
eventsEngine: EventsEngine;
|
|
48
51
|
syncContext: SyncContext;
|
|
49
52
|
routerEngine: routes.RouterEngine;
|
|
50
53
|
serviceMode: ServiceContextMode;
|
|
@@ -66,6 +69,11 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
66
69
|
}
|
|
67
70
|
});
|
|
68
71
|
|
|
72
|
+
this.eventsEngine = new EventsEngine();
|
|
73
|
+
this.lifeCycleEngine.withLifecycle(this.eventsEngine, {
|
|
74
|
+
stop: (emitterEngine) => emitterEngine.shutDown()
|
|
75
|
+
});
|
|
76
|
+
|
|
69
77
|
this.lifeCycleEngine.withLifecycle(this.storageEngine, {
|
|
70
78
|
start: (storageEngine) => storageEngine.start(),
|
|
71
79
|
stop: (storageEngine) => storageEngine.shutDown()
|
|
@@ -89,6 +97,10 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
89
97
|
// Migrations should be executed before the system starts
|
|
90
98
|
start: () => migrationManager[Symbol.asyncDispose]()
|
|
91
99
|
});
|
|
100
|
+
|
|
101
|
+
this.lifeCycleEngine.withLifecycle(this.eventsEngine, {
|
|
102
|
+
stop: (emitterEngine) => emitterEngine.shutDown()
|
|
103
|
+
});
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
get replicationEngine(): replication.ReplicationEngine | null {
|
package/test/src/routes/mocks.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
SyncRulesBucketStorage
|
|
12
12
|
} from '@/index.js';
|
|
13
13
|
import { MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
14
|
+
import { EventsEngine } from '@/events/EventsEngine.js';
|
|
14
15
|
|
|
15
16
|
export function mockServiceContext(storage: Partial<SyncRulesBucketStorage> | null) {
|
|
16
17
|
// This is very incomplete - just enough to get the current tests passing.
|
|
@@ -34,6 +35,7 @@ export function mockServiceContext(storage: Partial<SyncRulesBucketStorage> | nu
|
|
|
34
35
|
createCoreAPIMetrics(metricsEngine);
|
|
35
36
|
const service_context: Partial<ServiceContext> = {
|
|
36
37
|
syncContext: new SyncContext({ maxBuckets: 1, maxDataFetchConcurrency: 1, maxParameterQueryResults: 1 }),
|
|
38
|
+
eventsEngine: new EventsEngine(),
|
|
37
39
|
routerEngine: {
|
|
38
40
|
getAPI() {
|
|
39
41
|
return {
|
|
@@ -3,7 +3,7 @@ import { logger, RouterResponse, ServiceError } from '@powersync/lib-services-fr
|
|
|
3
3
|
import { SqlSyncRules } from '@powersync/service-sync-rules';
|
|
4
4
|
import { Readable, Writable } from 'stream';
|
|
5
5
|
import { pipeline } from 'stream/promises';
|
|
6
|
-
import {
|
|
6
|
+
import { describe, expect, it } from 'vitest';
|
|
7
7
|
import { syncStreamed } from '../../../src/routes/endpoints/sync-stream.js';
|
|
8
8
|
import { mockServiceContext } from './mocks.js';
|
|
9
9
|
|
|
@@ -12,7 +12,12 @@ describe('Stream Route', () => {
|
|
|
12
12
|
it('handles missing sync rules', async () => {
|
|
13
13
|
const context: Context = {
|
|
14
14
|
logger: logger,
|
|
15
|
-
service_context: mockServiceContext(null)
|
|
15
|
+
service_context: mockServiceContext(null),
|
|
16
|
+
token_payload: {
|
|
17
|
+
sub: '',
|
|
18
|
+
exp: 0,
|
|
19
|
+
iat: 0
|
|
20
|
+
}
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
const request: BasicRouterRequest = {
|
|
@@ -21,10 +26,13 @@ describe('Stream Route', () => {
|
|
|
21
26
|
protocol: 'http'
|
|
22
27
|
};
|
|
23
28
|
|
|
24
|
-
const error = (await (
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
const error = (await (
|
|
30
|
+
syncStreamed.handler({
|
|
31
|
+
context,
|
|
32
|
+
params: {},
|
|
33
|
+
request
|
|
34
|
+
}) as Promise<RouterResponse>
|
|
35
|
+
).catch((e) => e)) as ServiceError;
|
|
28
36
|
expect(error.errorData.status).toEqual(500);
|
|
29
37
|
expect(error.errorData.code).toEqual('PSYNC_S2302');
|
|
30
38
|
});
|