@powersync/service-core 1.11.2 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/auth/CachedKeyCollector.js +2 -7
  3. package/dist/auth/CachedKeyCollector.js.map +1 -1
  4. package/dist/auth/CompoundKeyCollector.js.map +1 -1
  5. package/dist/auth/KeyCollector.d.ts +2 -2
  6. package/dist/auth/KeyStore.js +32 -14
  7. package/dist/auth/KeyStore.js.map +1 -1
  8. package/dist/auth/RemoteJWKSCollector.d.ts +1 -0
  9. package/dist/auth/RemoteJWKSCollector.js +39 -16
  10. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  11. package/dist/auth/auth-index.d.ts +1 -0
  12. package/dist/auth/auth-index.js +1 -0
  13. package/dist/auth/auth-index.js.map +1 -1
  14. package/dist/auth/utils.d.ts +6 -0
  15. package/dist/auth/utils.js +97 -0
  16. package/dist/auth/utils.js.map +1 -0
  17. package/dist/entry/commands/compact-action.js +4 -1
  18. package/dist/entry/commands/compact-action.js.map +1 -1
  19. package/dist/entry/commands/migrate-action.js +4 -1
  20. package/dist/entry/commands/migrate-action.js.map +1 -1
  21. package/dist/entry/commands/test-connection-action.js +4 -1
  22. package/dist/entry/commands/test-connection-action.js.map +1 -1
  23. package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js +1 -1
  24. package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js.map +1 -1
  25. package/dist/routes/RouterEngine.d.ts +2 -0
  26. package/dist/routes/RouterEngine.js +15 -10
  27. package/dist/routes/RouterEngine.js.map +1 -1
  28. package/dist/routes/auth.d.ts +5 -16
  29. package/dist/routes/auth.js +6 -4
  30. package/dist/routes/auth.js.map +1 -1
  31. package/dist/routes/configure-fastify.d.ts +3 -21
  32. package/dist/routes/configure-fastify.js +3 -6
  33. package/dist/routes/configure-fastify.js.map +1 -1
  34. package/dist/routes/configure-rsocket.js +28 -14
  35. package/dist/routes/configure-rsocket.js.map +1 -1
  36. package/dist/routes/endpoints/admin.js.map +1 -1
  37. package/dist/routes/endpoints/checkpointing.d.ts +4 -28
  38. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  39. package/dist/routes/endpoints/route-endpoints-index.d.ts +1 -0
  40. package/dist/routes/endpoints/route-endpoints-index.js +1 -0
  41. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -1
  42. package/dist/routes/endpoints/socket-route.js +22 -8
  43. package/dist/routes/endpoints/socket-route.js.map +1 -1
  44. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  45. package/dist/routes/endpoints/sync-stream.d.ts +2 -14
  46. package/dist/routes/endpoints/sync-stream.js +28 -9
  47. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  48. package/dist/routes/route-register.js +10 -6
  49. package/dist/routes/route-register.js.map +1 -1
  50. package/dist/routes/router.d.ts +8 -7
  51. package/dist/routes/router.js.map +1 -1
  52. package/dist/runner/teardown.js +4 -1
  53. package/dist/runner/teardown.js.map +1 -1
  54. package/dist/storage/SyncRulesBucketStorage.d.ts +3 -3
  55. package/dist/storage/storage-metrics.js +1 -1
  56. package/dist/storage/storage-metrics.js.map +1 -1
  57. package/dist/sync/BucketChecksumState.d.ts +40 -18
  58. package/dist/sync/BucketChecksumState.js +120 -72
  59. package/dist/sync/BucketChecksumState.js.map +1 -1
  60. package/dist/sync/RequestTracker.d.ts +22 -1
  61. package/dist/sync/RequestTracker.js +51 -2
  62. package/dist/sync/RequestTracker.js.map +1 -1
  63. package/dist/sync/sync.d.ts +3 -5
  64. package/dist/sync/sync.js +48 -33
  65. package/dist/sync/sync.js.map +1 -1
  66. package/dist/system/ServiceContext.d.ts +19 -4
  67. package/dist/system/ServiceContext.js +20 -8
  68. package/dist/system/ServiceContext.js.map +1 -1
  69. package/dist/util/config/collectors/config-collector.js +4 -33
  70. package/dist/util/config/collectors/config-collector.js.map +1 -1
  71. package/dist/util/config/collectors/impl/yaml-env.d.ts +7 -0
  72. package/dist/util/config/collectors/impl/yaml-env.js +59 -0
  73. package/dist/util/config/collectors/impl/yaml-env.js.map +1 -0
  74. package/dist/util/config/compound-config-collector.js +18 -1
  75. package/dist/util/config/compound-config-collector.js.map +1 -1
  76. package/dist/util/config/types.d.ts +11 -0
  77. package/dist/util/protocol-types.d.ts +2 -2
  78. package/package.json +6 -7
  79. package/src/auth/CachedKeyCollector.ts +4 -6
  80. package/src/auth/CompoundKeyCollector.ts +2 -1
  81. package/src/auth/KeyCollector.ts +2 -2
  82. package/src/auth/KeyStore.ts +45 -20
  83. package/src/auth/RemoteJWKSCollector.ts +39 -16
  84. package/src/auth/auth-index.ts +1 -0
  85. package/src/auth/utils.ts +102 -0
  86. package/src/entry/commands/compact-action.ts +4 -1
  87. package/src/entry/commands/migrate-action.ts +4 -1
  88. package/src/entry/commands/test-connection-action.ts +4 -1
  89. package/src/metrics/open-telemetry/OpenTelemetryMetricsFactory.ts +1 -1
  90. package/src/routes/RouterEngine.ts +21 -11
  91. package/src/routes/auth.ts +7 -6
  92. package/src/routes/configure-fastify.ts +6 -8
  93. package/src/routes/configure-rsocket.ts +33 -18
  94. package/src/routes/endpoints/admin.ts +5 -5
  95. package/src/routes/endpoints/checkpointing.ts +2 -2
  96. package/src/routes/endpoints/route-endpoints-index.ts +1 -0
  97. package/src/routes/endpoints/socket-route.ts +27 -11
  98. package/src/routes/endpoints/sync-rules.ts +4 -4
  99. package/src/routes/endpoints/sync-stream.ts +34 -11
  100. package/src/routes/route-register.ts +10 -7
  101. package/src/routes/router.ts +11 -4
  102. package/src/runner/teardown.ts +5 -1
  103. package/src/storage/SyncRulesBucketStorage.ts +3 -3
  104. package/src/storage/storage-metrics.ts +1 -1
  105. package/src/sync/BucketChecksumState.ts +160 -75
  106. package/src/sync/RequestTracker.ts +70 -3
  107. package/src/sync/sync.ts +70 -47
  108. package/src/system/ServiceContext.ts +31 -12
  109. package/src/util/config/collectors/config-collector.ts +4 -40
  110. package/src/util/config/collectors/impl/yaml-env.ts +67 -0
  111. package/src/util/config/compound-config-collector.ts +22 -5
  112. package/src/util/config/types.ts +13 -0
  113. package/src/util/protocol-types.ts +2 -2
  114. package/test/src/auth.test.ts +29 -11
  115. package/test/src/config.test.ts +72 -0
  116. package/test/src/sync/BucketChecksumState.test.ts +32 -18
  117. package/tsconfig.tsbuildinfo +1 -1
@@ -19,7 +19,7 @@ export const executeSql = routeDefinition({
19
19
  }
20
20
  } = payload;
21
21
 
22
- const apiHandler = payload.context.service_context.routerEngine!.getAPI();
22
+ const apiHandler = payload.context.service_context.routerEngine.getAPI();
23
23
 
24
24
  const sourceConfig = await apiHandler.getSourceConfig();
25
25
  if (!sourceConfig.debug_api) {
@@ -47,7 +47,7 @@ export const diagnostics = routeDefinition({
47
47
  const { service_context } = context;
48
48
  const include_content = payload.params.sync_rules_content ?? false;
49
49
 
50
- const apiHandler = service_context.routerEngine!.getAPI();
50
+ const apiHandler = service_context.routerEngine.getAPI();
51
51
 
52
52
  const status = await apiHandler.getConnectionStatus();
53
53
  if (!status) {
@@ -94,7 +94,7 @@ export const getSchema = routeDefinition({
94
94
  authorize: authApi,
95
95
  validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }),
96
96
  handler: async (payload) => {
97
- const apiHandler = payload.context.service_context.routerEngine!.getAPI();
97
+ const apiHandler = payload.context.service_context.routerEngine.getAPI();
98
98
 
99
99
  return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(apiHandler));
100
100
  }
@@ -112,7 +112,7 @@ export const reprocess = routeDefinition({
112
112
  const {
113
113
  storageEngine: { activeBucketStorage }
114
114
  } = service_context;
115
- const apiHandler = service_context.routerEngine!.getAPI();
115
+ const apiHandler = service_context.routerEngine.getAPI();
116
116
  const next = await activeBucketStorage.getNextSyncRules(apiHandler.getParseSyncRulesOptions());
117
117
  if (next != null) {
118
118
  throw new Error(`Busy processing sync rules - cannot reprocess`);
@@ -159,7 +159,7 @@ export const validate = routeDefinition({
159
159
  context: { service_context }
160
160
  } = payload;
161
161
  const content = payload.params.sync_rules;
162
- const apiHandler = service_context.routerEngine!.getAPI();
162
+ const apiHandler = service_context.routerEngine.getAPI();
163
163
 
164
164
  const schemaData = await api.getConnectionsSchema(apiHandler);
165
165
  const schema = new StaticSchema(schemaData.connections);
@@ -18,7 +18,7 @@ export const writeCheckpoint = routeDefinition({
18
18
  const {
19
19
  context: { service_context }
20
20
  } = payload;
21
- const apiHandler = service_context.routerEngine!.getAPI();
21
+ const apiHandler = service_context.routerEngine.getAPI();
22
22
 
23
23
  // This old API needs a persisted checkpoint id.
24
24
  // Since we don't use LSNs anymore, the only way to get that is to wait.
@@ -54,7 +54,7 @@ export const writeCheckpoint2 = routeDefinition({
54
54
  handler: async (payload) => {
55
55
  const { user_id, service_context } = payload.context;
56
56
 
57
- const apiHandler = service_context.routerEngine!.getAPI();
57
+ const apiHandler = service_context.routerEngine.getAPI();
58
58
 
59
59
  const { replicationHead, writeCheckpoint } = await util.createWriteCheckpoint({
60
60
  userId: user_id,
@@ -1,5 +1,6 @@
1
1
  export * from './admin.js';
2
2
  export * from './checkpointing.js';
3
+ export * from './probes.js';
3
4
  export * from './socket-route.js';
4
5
  export * from './sync-rules.js';
5
6
  export * from './sync-stream.js';
@@ -1,4 +1,4 @@
1
- import { ErrorCode, errors, logger, schema } from '@powersync/lib-services-framework';
1
+ import { ErrorCode, errors, schema } from '@powersync/lib-services-framework';
2
2
  import { RequestParameters } from '@powersync/service-sync-rules';
3
3
  import { serialize } from 'bson';
4
4
 
@@ -13,12 +13,26 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
13
13
  router.reactiveStream<util.StreamingSyncRequest, any>(SyncRoutes.STREAM, {
14
14
  validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
15
15
  handler: async ({ context, params, responder, observer, initialN, signal: upstreamSignal }) => {
16
- const { service_context } = context;
16
+ const { service_context, logger } = context;
17
17
  const { routerEngine, metricsEngine, syncContext } = service_context;
18
18
 
19
+ logger.defaultMeta = {
20
+ ...logger.defaultMeta,
21
+ user_id: context.token_payload?.sub,
22
+ client_id: params.client_id,
23
+ user_agent: context.user_agent
24
+ };
25
+ const streamStart = Date.now();
26
+
27
+ // Best effort guess on why the stream was closed.
28
+ // We use the `??=` operator everywhere, so that we catch the first relevant
29
+ // event, which is usually the most specific.
30
+ let closeReason: string | undefined = undefined;
31
+
19
32
  // Create our own controller that we can abort directly
20
33
  const controller = new AbortController();
21
34
  upstreamSignal.addEventListener('abort', () => {
35
+ closeReason ??= 'client closing stream';
22
36
  controller.abort();
23
37
  });
24
38
  if (upstreamSignal.aborted) {
@@ -33,7 +47,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
33
47
  }
34
48
  });
35
49
 
36
- if (routerEngine!.closed) {
50
+ if (routerEngine.closed) {
37
51
  responder.onError(
38
52
  new errors.ServiceError({
39
53
  status: 503,
@@ -64,9 +78,10 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
64
78
  return;
65
79
  }
66
80
 
67
- const syncRules = bucketStorage.getParsedSyncRules(routerEngine!.getAPI().getParseSyncRulesOptions());
81
+ const syncRules = bucketStorage.getParsedSyncRules(routerEngine.getAPI().getParseSyncRulesOptions());
68
82
 
69
- const removeStopHandler = routerEngine!.addStopHandler(() => {
83
+ const removeStopHandler = routerEngine.addStopHandler(() => {
84
+ closeReason ??= 'process shutdown';
70
85
  controller.abort();
71
86
  });
72
87
 
@@ -88,7 +103,8 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
88
103
  keep_alive: false
89
104
  },
90
105
  tracker,
91
- signal
106
+ signal,
107
+ logger
92
108
  })) {
93
109
  if (signal.aborted) {
94
110
  break;
@@ -131,22 +147,22 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
131
147
  });
132
148
  }
133
149
  }
150
+ closeReason ??= 'service closing stream';
134
151
  } catch (ex) {
135
152
  // Convert to our standard form before responding.
136
153
  // This ensures the error can be serialized.
137
154
  const error = new errors.InternalServerError(ex);
138
155
  logger.error('Sync stream error', error);
156
+ closeReason ??= 'stream error';
139
157
  responder.onError(error);
140
158
  } finally {
141
159
  responder.onComplete();
142
160
  removeStopHandler();
143
161
  disposer();
144
162
  logger.info(`Sync stream complete`, {
145
- user_id: syncParams.user_id,
146
- client_id: params.client_id,
147
- user_agent: context.user_agent,
148
- operations_synced: tracker.operationsSynced,
149
- data_synced_bytes: tracker.dataSyncedBytes
163
+ ...tracker.getLogMeta(),
164
+ stream_ms: Date.now() - streamStart,
165
+ close_reason: closeReason ?? 'unknown'
150
166
  });
151
167
  metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
152
168
  }
@@ -53,7 +53,7 @@ export const deploySyncRules = routeDefinition({
53
53
  const content = payload.params.content;
54
54
 
55
55
  try {
56
- const apiHandler = service_context.routerEngine!.getAPI();
56
+ const apiHandler = service_context.routerEngine.getAPI();
57
57
  SqlSyncRules.fromYaml(payload.params.content, {
58
58
  ...apiHandler.getParseSyncRulesOptions(),
59
59
  // We don't do any schema-level validation at this point
@@ -94,7 +94,7 @@ export const validateSyncRules = routeDefinition({
94
94
  handler: async (payload) => {
95
95
  const content = payload.params.content;
96
96
  const { service_context } = payload.context;
97
- const apiHandler = service_context.routerEngine!.getAPI();
97
+ const apiHandler = service_context.routerEngine.getAPI();
98
98
 
99
99
  const info = await debugSyncRules(apiHandler, content);
100
100
 
@@ -121,7 +121,7 @@ export const currentSyncRules = routeDefinition({
121
121
  });
122
122
  }
123
123
 
124
- const apiHandler = service_context.routerEngine!.getAPI();
124
+ const apiHandler = service_context.routerEngine.getAPI();
125
125
  const info = await debugSyncRules(apiHandler, sync_rules.sync_rules_content);
126
126
  const next = await activeBucketStorage.getNextSyncRulesContent();
127
127
 
@@ -158,7 +158,7 @@ export const reprocessSyncRules = routeDefinition({
158
158
  const {
159
159
  storageEngine: { activeBucketStorage }
160
160
  } = payload.context.service_context;
161
- const apiHandler = payload.context.service_context.routerEngine!.getAPI();
161
+ const apiHandler = payload.context.service_context.routerEngine.getAPI();
162
162
  const sync_rules = await activeBucketStorage.getActiveSyncRules(apiHandler.getParseSyncRulesOptions());
163
163
  if (sync_rules == null) {
164
164
  throw new errors.ServiceError({
@@ -20,13 +20,21 @@ export const syncStreamed = routeDefinition({
20
20
  authorize: authUser,
21
21
  validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
22
22
  handler: async (payload) => {
23
- const { service_context } = payload.context;
23
+ const { service_context, logger } = payload.context;
24
24
  const { routerEngine, storageEngine, metricsEngine, syncContext } = service_context;
25
25
  const headers = payload.request.headers;
26
26
  const userAgent = headers['x-user-agent'] ?? headers['user-agent'];
27
27
  const clientId = payload.params.client_id;
28
+ const streamStart = Date.now();
28
29
 
29
- if (routerEngine!.closed) {
30
+ logger.defaultMeta = {
31
+ ...logger.defaultMeta,
32
+ user_agent: userAgent,
33
+ client_id: clientId,
34
+ user_id: payload.context.user_id
35
+ };
36
+
37
+ if (routerEngine.closed) {
30
38
  throw new errors.ServiceError({
31
39
  status: 503,
32
40
  code: ErrorCode.PSYNC_S2003,
@@ -47,7 +55,7 @@ export const syncStreamed = routeDefinition({
47
55
  });
48
56
  }
49
57
 
50
- const syncRules = bucketStorage.getParsedSyncRules(routerEngine!.getAPI().getParseSyncRulesOptions());
58
+ const syncRules = bucketStorage.getParsedSyncRules(routerEngine.getAPI().getParseSyncRulesOptions());
51
59
 
52
60
  const controller = new AbortController();
53
61
  const tracker = new sync.RequestTracker(metricsEngine);
@@ -64,7 +72,8 @@ export const syncStreamed = routeDefinition({
64
72
  syncParams,
65
73
  token: payload.context.token_payload!,
66
74
  tracker,
67
- signal: controller.signal
75
+ signal: controller.signal,
76
+ logger
68
77
  })
69
78
  ),
70
79
  tracker
@@ -72,16 +81,29 @@ export const syncStreamed = routeDefinition({
72
81
  { objectMode: false, highWaterMark: 16 * 1024 }
73
82
  );
74
83
 
75
- const deregister = routerEngine!.addStopHandler(() => {
84
+ // Best effort guess on why the stream was closed.
85
+ // We use the `??=` operator everywhere, so that we catch the first relevant
86
+ // event, which is usually the most specific.
87
+ let closeReason: string | undefined = undefined;
88
+
89
+ const deregister = routerEngine.addStopHandler(() => {
76
90
  // This error is not currently propagated to the client
77
91
  controller.abort();
92
+ closeReason ??= 'process shutdown';
78
93
  stream.destroy(new Error('Shutting down system'));
79
94
  });
95
+
96
+ stream.on('end', () => {
97
+ // Auth failure or switch to new sync rules
98
+ closeReason ??= 'service closing stream';
99
+ });
100
+
80
101
  stream.on('close', () => {
81
102
  deregister();
82
103
  });
83
104
 
84
105
  stream.on('error', (error) => {
106
+ closeReason ??= 'stream error';
85
107
  controller.abort();
86
108
  // Note: This appears as a 200 response in the logs.
87
109
  if (error.message != 'Shutting down system') {
@@ -95,15 +117,16 @@ export const syncStreamed = routeDefinition({
95
117
  'Content-Type': 'application/x-ndjson'
96
118
  },
97
119
  data: stream,
98
- afterSend: async () => {
120
+ afterSend: async (details) => {
121
+ if (details.clientClosed) {
122
+ closeReason ??= 'client closing stream';
123
+ }
99
124
  controller.abort();
100
125
  metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
101
126
  logger.info(`Sync stream complete`, {
102
- user_id: syncParams.user_id,
103
- client_id: clientId,
104
- user_agent: userAgent,
105
- operations_synced: tracker.operationsSynced,
106
- data_synced_bytes: tracker.dataSyncedBytes
127
+ ...tracker.getLogMeta(),
128
+ stream_ms: Date.now() - streamStart,
129
+ close_reason: closeReason ?? 'unknown'
107
130
  });
108
131
  }
109
132
  });
@@ -1,4 +1,5 @@
1
1
  import type fastify from 'fastify';
2
+ import * as uuid from 'uuid';
2
3
 
3
4
  import { errors, HTTPMethod, logger, router } from '@powersync/lib-services-framework';
4
5
  import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
@@ -25,9 +26,12 @@ export function registerFastifyRoutes(
25
26
  handler: async (request, reply) => {
26
27
  const startTime = new Date();
27
28
  let response: router.RouterResponse;
29
+ const requestLogger = logger.child({
30
+ route: e.path,
31
+ rid: `h/${uuid.v7()}`
32
+ });
28
33
  try {
29
- const context = await contextProvider(request);
30
-
34
+ const context = await contextProvider(request, { logger: requestLogger });
31
35
  let combined = {
32
36
  ...(request.params as any),
33
37
  ...(request.query as any)
@@ -63,7 +67,7 @@ export function registerFastifyRoutes(
63
67
  }
64
68
  } catch (ex) {
65
69
  const serviceError = errors.asServiceError(ex);
66
- logger.error(`Request failed`, serviceError);
70
+ requestLogger.error(`Request failed`, serviceError);
67
71
 
68
72
  response = new router.RouterResponse({
69
73
  status: serviceError.errorData.status || 500,
@@ -83,13 +87,12 @@ export function registerFastifyRoutes(
83
87
  try {
84
88
  await reply.send(response.data);
85
89
  } finally {
86
- await response.afterSend?.();
87
- logger.info(`${e.method} ${request.url}`, {
90
+ await response.afterSend?.({ clientClosed: request.socket.closed });
91
+ requestLogger.info(`${e.method} ${request.url}`, {
88
92
  duration_ms: Math.round(new Date().valueOf() - startTime.valueOf() + Number.EPSILON),
89
93
  status: response.status,
90
94
  method: e.method,
91
- path: request.url,
92
- route: e.path
95
+ path: request.url
93
96
  });
94
97
  }
95
98
  }
@@ -1,4 +1,4 @@
1
- import { router } from '@powersync/lib-services-framework';
1
+ import { router, ServiceError, Logger } 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';
@@ -6,7 +6,8 @@ import { RouterEngine } from './RouterEngine.js';
6
6
  /**
7
7
  * The {@link RouterEngine} must be provided for these routes
8
8
  */
9
- export type RouterServiceContext = ServiceContext & { routerEngine: RouterEngine };
9
+ export type RouterServiceContext = ServiceContext;
10
+
10
11
  /**
11
12
  * Common context for routes
12
13
  */
@@ -16,11 +17,13 @@ export type Context = {
16
17
  service_context: RouterServiceContext;
17
18
 
18
19
  token_payload?: JwtPayload;
19
- token_errors?: string[];
20
+ token_error?: ServiceError;
20
21
  /**
21
22
  * Only on websocket endpoints.
22
23
  */
23
24
  user_agent?: string;
25
+
26
+ logger: Logger;
24
27
  };
25
28
 
26
29
  export type BasicRouterRequest = {
@@ -29,7 +32,11 @@ export type BasicRouterRequest = {
29
32
  hostname: string;
30
33
  };
31
34
 
32
- export type ContextProvider = (request: BasicRouterRequest) => Promise<Context>;
35
+ export type ConextProviderOptions = {
36
+ logger: Logger;
37
+ };
38
+
39
+ export type ContextProvider = (request: BasicRouterRequest, options: ConextProviderOptions) => Promise<Context>;
33
40
 
34
41
  export type RequestEndpoint<
35
42
  I,
@@ -14,7 +14,11 @@ export async function teardown(runnerConfig: utils.RunnerConfig) {
14
14
  try {
15
15
  logger.info(`Tearing down PowerSync instance...`);
16
16
  const config = await utils.loadConfig(runnerConfig);
17
- const serviceContext = new system.ServiceContextContainer(config);
17
+ const serviceContext = new system.ServiceContextContainer({
18
+ serviceMode: system.ServiceContextMode.TEARDOWN,
19
+ configuration: config
20
+ });
21
+
18
22
  const moduleManager = container.getImplementation(modules.ModuleManager);
19
23
  await moduleManager.initialize(serviceContext);
20
24
  // This is mostly done to ensure that the storage is ready
@@ -107,7 +107,7 @@ export interface SyncRulesBucketStorage
107
107
  checkpoint: util.InternalOpId,
108
108
  dataBuckets: Map<string, util.InternalOpId>,
109
109
  options?: BucketDataBatchOptions
110
- ): AsyncIterable<SyncBucketDataBatch>;
110
+ ): AsyncIterable<SyncBucketDataChunk>;
111
111
 
112
112
  /**
113
113
  * Compute checksums for a given list of buckets.
@@ -223,8 +223,8 @@ export interface BucketDataBatchOptions {
223
223
  chunkLimitBytes?: number;
224
224
  }
225
225
 
226
- export interface SyncBucketDataBatch {
227
- batch: util.SyncBucketData;
226
+ export interface SyncBucketDataChunk {
227
+ chunkData: util.SyncBucketData;
228
228
  targetOp: util.InternalOpId | null;
229
229
  }
230
230
 
@@ -34,7 +34,7 @@ export function initializeCoreStorageMetrics(engine: MetricsEngine, storage: Buc
34
34
  let cacheTimestamp = 0;
35
35
 
36
36
  const getMetrics = () => {
37
- if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
37
+ if (!cachedRequest || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
38
38
  cachedRequest = storage.getStorageMetrics().catch((e) => {
39
39
  logger.error(`Failed to get storage metrics`, e);
40
40
  return null;