@powersync/service-core 0.0.0-dev-20250117095455 → 0.0.0-dev-20250214100224
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 +48 -8
- package/dist/api/RouteAPI.d.ts +8 -0
- package/dist/auth/CachedKeyCollector.js +26 -25
- package/dist/auth/CachedKeyCollector.js.map +1 -1
- package/dist/auth/CompoundKeyCollector.js +1 -0
- package/dist/auth/CompoundKeyCollector.js.map +1 -1
- package/dist/auth/KeySpec.js +3 -0
- package/dist/auth/KeySpec.js.map +1 -1
- package/dist/auth/KeyStore.js +4 -0
- package/dist/auth/KeyStore.js.map +1 -1
- package/dist/auth/LeakyBucket.js +5 -0
- package/dist/auth/LeakyBucket.js.map +1 -1
- package/dist/auth/RemoteJWKSCollector.d.ts +3 -0
- package/dist/auth/RemoteJWKSCollector.js +12 -5
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/dist/auth/StaticKeyCollector.js +1 -0
- package/dist/auth/StaticKeyCollector.js.map +1 -1
- package/dist/auth/StaticSupabaseKeyCollector.js +1 -0
- package/dist/auth/StaticSupabaseKeyCollector.js.map +1 -1
- package/dist/entry/cli-entry.js +2 -0
- package/dist/entry/cli-entry.js.map +1 -1
- package/dist/entry/commands/teardown-action.js +2 -1
- package/dist/entry/commands/teardown-action.js.map +1 -1
- package/dist/entry/commands/test-connection-action.d.ts +2 -0
- package/dist/entry/commands/test-connection-action.js +32 -0
- package/dist/entry/commands/test-connection-action.js.map +1 -0
- package/dist/metrics/Metrics.js +37 -3
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/modules/AbstractModule.js +2 -0
- package/dist/modules/AbstractModule.js.map +1 -1
- package/dist/modules/ModuleManager.js +1 -3
- package/dist/modules/ModuleManager.js.map +1 -1
- package/dist/replication/AbstractReplicationJob.js +4 -2
- package/dist/replication/AbstractReplicationJob.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +2 -0
- package/dist/replication/AbstractReplicator.js +18 -12
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/replication/ReplicationEngine.d.ts +2 -0
- package/dist/replication/ReplicationEngine.js +4 -3
- package/dist/replication/ReplicationEngine.js.map +1 -1
- package/dist/replication/ReplicationModule.d.ts +8 -2
- package/dist/replication/ReplicationModule.js +9 -11
- package/dist/replication/ReplicationModule.js.map +1 -1
- package/dist/routes/RouterEngine.js +8 -0
- package/dist/routes/RouterEngine.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +3 -3
- package/dist/routes/endpoints/admin.d.ts +6 -6
- package/dist/routes/endpoints/admin.js +7 -4
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.js +14 -84
- package/dist/routes/endpoints/checkpointing.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +5 -5
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +16 -11
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +5 -5
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/route-register.js +4 -4
- package/dist/routes/route-register.js.map +1 -1
- package/dist/runner/teardown.js +2 -2
- package/dist/runner/teardown.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +32 -5
- package/dist/storage/BucketStorage.js +3 -0
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/ChecksumCache.js +12 -7
- package/dist/storage/ChecksumCache.js.map +1 -1
- package/dist/storage/SourceTable.js +32 -25
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/StorageEngine.js +4 -3
- package/dist/storage/StorageEngine.js.map +1 -1
- package/dist/storage/bson.d.ts +5 -3
- package/dist/storage/bson.js.map +1 -1
- package/dist/sync/BroadcastIterable.js +4 -3
- package/dist/sync/BroadcastIterable.js.map +1 -1
- package/dist/sync/LastValueSink.js +2 -0
- package/dist/sync/LastValueSink.js.map +1 -1
- package/dist/sync/RequestTracker.js +2 -4
- package/dist/sync/RequestTracker.js.map +1 -1
- package/dist/sync/merge.js +4 -0
- package/dist/sync/merge.js.map +1 -1
- package/dist/sync/sync.js +2 -2
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.js +2 -2
- package/dist/sync/util.js.map +1 -1
- package/dist/system/ServiceContext.js +3 -0
- package/dist/system/ServiceContext.js.map +1 -1
- package/dist/util/Mutex.js +5 -0
- package/dist/util/Mutex.js.map +1 -1
- package/dist/util/checkpointing.d.ts +13 -0
- package/dist/util/checkpointing.js +92 -0
- package/dist/util/checkpointing.js.map +1 -0
- package/dist/util/config/compound-config-collector.js +3 -1
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +1 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js +1 -0
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/sync-rules-provider.d.ts +2 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js +4 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
- package/dist/util/config/types.d.ts +1 -0
- package/dist/util/memory-tracking.js +1 -1
- package/dist/util/memory-tracking.js.map +1 -1
- package/dist/util/util-index.d.ts +1 -0
- package/dist/util/util-index.js +1 -0
- package/dist/util/util-index.js.map +1 -1
- package/dist/util/utils.d.ts +0 -1
- package/dist/util/utils.js +2 -10
- package/dist/util/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/api/RouteAPI.ts +10 -0
- package/src/auth/RemoteJWKSCollector.ts +18 -5
- package/src/entry/cli-entry.ts +2 -0
- package/src/entry/commands/teardown-action.ts +2 -1
- package/src/entry/commands/test-connection-action.ts +41 -0
- package/src/metrics/Metrics.ts +2 -2
- package/src/replication/AbstractReplicator.ts +12 -3
- package/src/replication/ReplicationEngine.ts +5 -0
- package/src/replication/ReplicationModule.ts +15 -13
- package/src/routes/endpoints/admin.ts +7 -4
- package/src/routes/endpoints/checkpointing.ts +8 -19
- package/src/routes/endpoints/socket-route.ts +5 -5
- package/src/routes/endpoints/sync-rules.ts +16 -11
- package/src/routes/endpoints/sync-stream.ts +5 -5
- package/src/routes/route-register.ts +4 -4
- package/src/storage/BucketStorage.ts +39 -4
- package/src/storage/bson.ts +9 -7
- package/src/util/checkpointing.ts +43 -0
- package/src/util/config/compound-config-collector.ts +2 -1
- package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +1 -0
- package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -0
- package/src/util/config/sync-rules/impl/inline-sync-rules-collector.ts +1 -0
- package/src/util/config/sync-rules/sync-rules-provider.ts +6 -0
- package/src/util/config/types.ts +1 -0
- package/src/util/memory-tracking.ts +2 -2
- package/src/util/util-index.ts +1 -0
- package/src/util/utils.ts +2 -11
- package/tsconfig.tsbuildinfo +1 -1
package/src/storage/bson.ts
CHANGED
|
@@ -3,6 +3,8 @@ import * as bson from 'bson';
|
|
|
3
3
|
import { SqliteJsonValue } from '@powersync/service-sync-rules';
|
|
4
4
|
import { ReplicaId } from './BucketStorage.js';
|
|
5
5
|
|
|
6
|
+
type NodeBuffer = Buffer<ArrayBuffer>;
|
|
7
|
+
|
|
6
8
|
export const BSON_DESERIALIZE_OPTIONS: bson.DeserializeOptions = {
|
|
7
9
|
// use bigint instead of Long
|
|
8
10
|
useBigInt64: true
|
|
@@ -12,7 +14,7 @@ export const BSON_DESERIALIZE_OPTIONS: bson.DeserializeOptions = {
|
|
|
12
14
|
* Lookup serialization must be number-agnostic. I.e. normalize numbers, instead of preserving numbers.
|
|
13
15
|
* @param lookup
|
|
14
16
|
*/
|
|
15
|
-
export const serializeLookupBuffer = (lookup: SqliteJsonValue[]):
|
|
17
|
+
export const serializeLookupBuffer = (lookup: SqliteJsonValue[]): NodeBuffer => {
|
|
16
18
|
const normalized = lookup.map((value) => {
|
|
17
19
|
if (typeof value == 'number' && Number.isInteger(value)) {
|
|
18
20
|
return BigInt(value);
|
|
@@ -20,7 +22,7 @@ export const serializeLookupBuffer = (lookup: SqliteJsonValue[]): Buffer => {
|
|
|
20
22
|
return value;
|
|
21
23
|
}
|
|
22
24
|
});
|
|
23
|
-
return bson.serialize({ l: normalized }) as
|
|
25
|
+
return bson.serialize({ l: normalized }) as NodeBuffer;
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export const serializeLookup = (lookup: SqliteJsonValue[]) => {
|
|
@@ -40,8 +42,8 @@ export const isUUID = (value: any): value is bson.UUID => {
|
|
|
40
42
|
return uuid._bsontype == 'Binary' && uuid.sub_type == bson.Binary.SUBTYPE_UUID;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
export const serializeReplicaId = (id: ReplicaId):
|
|
44
|
-
return bson.serialize({ id }) as
|
|
45
|
+
export const serializeReplicaId = (id: ReplicaId): NodeBuffer => {
|
|
46
|
+
return bson.serialize({ id }) as NodeBuffer;
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
export const deserializeReplicaId = (id: Buffer): ReplicaId => {
|
|
@@ -53,8 +55,8 @@ export const deserializeBson = (buffer: Buffer) => {
|
|
|
53
55
|
return bson.deserialize(buffer, BSON_DESERIALIZE_OPTIONS);
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
export const serializeBson = (document: any):
|
|
57
|
-
return bson.serialize(document) as
|
|
58
|
+
export const serializeBson = (document: any): NodeBuffer => {
|
|
59
|
+
return bson.serialize(document) as NodeBuffer;
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
/**
|
|
@@ -73,6 +75,6 @@ export const replicaIdEquals = (a: ReplicaId, b: ReplicaId) => {
|
|
|
73
75
|
return false;
|
|
74
76
|
} else {
|
|
75
77
|
// There are many possible primitive values, this covers them all
|
|
76
|
-
return serializeReplicaId(a).equals(serializeReplicaId(b)
|
|
78
|
+
return serializeReplicaId(a).equals(serializeReplicaId(b));
|
|
77
79
|
}
|
|
78
80
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ErrorCode, logger, ServiceError } from '@powersync/lib-services-framework';
|
|
2
|
+
import { RouteAPI } from '../api/RouteAPI.js';
|
|
3
|
+
import { BucketStorageFactory } from '../storage/BucketStorage.js';
|
|
4
|
+
|
|
5
|
+
export interface CreateWriteCheckpointOptions {
|
|
6
|
+
userId: string | undefined;
|
|
7
|
+
clientId: string | undefined;
|
|
8
|
+
api: RouteAPI;
|
|
9
|
+
storage: BucketStorageFactory;
|
|
10
|
+
}
|
|
11
|
+
export async function createWriteCheckpoint(options: CreateWriteCheckpointOptions) {
|
|
12
|
+
const full_user_id = checkpointUserId(options.userId, options.clientId);
|
|
13
|
+
|
|
14
|
+
const activeSyncRules = await options.storage.getActiveSyncRulesContent();
|
|
15
|
+
if (!activeSyncRules) {
|
|
16
|
+
throw new ServiceError(ErrorCode.PSYNC_S2302, `Cannot create Write Checkpoint since no sync rules are active.`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
using syncBucketStorage = options.storage.getInstance(activeSyncRules);
|
|
20
|
+
|
|
21
|
+
const { writeCheckpoint, currentCheckpoint } = await options.api.createReplicationHead(async (currentCheckpoint) => {
|
|
22
|
+
const writeCheckpoint = await syncBucketStorage.createManagedWriteCheckpoint({
|
|
23
|
+
user_id: full_user_id,
|
|
24
|
+
heads: { '1': currentCheckpoint }
|
|
25
|
+
});
|
|
26
|
+
return { writeCheckpoint, currentCheckpoint };
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
writeCheckpoint: String(writeCheckpoint),
|
|
31
|
+
replicationHead: currentCheckpoint
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function checkpointUserId(user_id: string | undefined, client_id: string | undefined) {
|
|
36
|
+
if (user_id == null) {
|
|
37
|
+
throw new Error('user_id is required');
|
|
38
|
+
}
|
|
39
|
+
if (client_id == null) {
|
|
40
|
+
return user_id;
|
|
41
|
+
}
|
|
42
|
+
return `${user_id}/${client_id}`;
|
|
43
|
+
}
|
|
@@ -20,6 +20,7 @@ export class FileSystemSyncRulesCollector extends SyncRulesCollector {
|
|
|
20
20
|
// Only persist the path here, and load on demand using `loadSyncRules()`.
|
|
21
21
|
return {
|
|
22
22
|
present: true,
|
|
23
|
+
exit_on_error: baseConfig.sync_rules?.exit_on_error ?? true,
|
|
23
24
|
path: config_path ? path.resolve(path.dirname(config_path), sync_path) : sync_path
|
|
24
25
|
};
|
|
25
26
|
}
|
|
@@ -3,6 +3,8 @@ import fs from 'fs/promises';
|
|
|
3
3
|
|
|
4
4
|
export interface SyncRulesProvider {
|
|
5
5
|
get(): Promise<string | undefined>;
|
|
6
|
+
|
|
7
|
+
readonly exitOnError: boolean;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
export class ConfigurationFileSyncRulesProvider implements SyncRulesProvider {
|
|
@@ -15,4 +17,8 @@ export class ConfigurationFileSyncRulesProvider implements SyncRulesProvider {
|
|
|
15
17
|
return await fs.readFile(this.config.path, 'utf-8');
|
|
16
18
|
}
|
|
17
19
|
}
|
|
20
|
+
|
|
21
|
+
get exitOnError() {
|
|
22
|
+
return this.config.exit_on_error;
|
|
23
|
+
}
|
|
18
24
|
}
|
package/src/util/config/types.ts
CHANGED
|
@@ -25,8 +25,8 @@ export function trackMemoryUsage() {
|
|
|
25
25
|
for (let key of Object.keys(bufferMemory)) {
|
|
26
26
|
const typedKey = key as keyof typeof bufferMemory;
|
|
27
27
|
const originalFunction = Buffer[typedKey] as (...args: any[]) => Buffer;
|
|
28
|
-
Buffer[typedKey] = function (...args: any[]) {
|
|
29
|
-
const buffer = originalFunction.apply(this, args)
|
|
28
|
+
Buffer[typedKey] = function <TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>(...args: any[]) {
|
|
29
|
+
const buffer = originalFunction.apply(this, args) as Buffer<TArrayBuffer>;
|
|
30
30
|
bufferMemory[typedKey] += buffer.byteLength;
|
|
31
31
|
bufferRegistry.register(buffer, [typedKey, buffer.byteLength]);
|
|
32
32
|
return buffer;
|
package/src/util/util-index.ts
CHANGED
package/src/util/utils.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { BucketChecksum, OpId, OplogEntry } from './protocol-types.js';
|
|
|
7
7
|
import * as storage from '../storage/storage-index.js';
|
|
8
8
|
|
|
9
9
|
import { PartialChecksum } from '../storage/ChecksumCache.js';
|
|
10
|
+
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
10
11
|
|
|
11
12
|
export type ChecksumMap = Map<string, BucketChecksum>;
|
|
12
13
|
|
|
@@ -34,7 +35,7 @@ export function timestampToOpId(ts: bigint): OpId {
|
|
|
34
35
|
// Dynamic values are passed in in some cases, so we make extra sure that the
|
|
35
36
|
// number is a bigint and not number or Long.
|
|
36
37
|
if (typeof ts != 'bigint') {
|
|
37
|
-
throw new
|
|
38
|
+
throw new ServiceAssertionError(`bigint expected, got: ${ts} (${typeof ts})`);
|
|
38
39
|
}
|
|
39
40
|
return ts.toString(10);
|
|
40
41
|
}
|
|
@@ -144,16 +145,6 @@ export function isCompleteRow(storeData: boolean, row: sync_rules.ToastableSqlit
|
|
|
144
145
|
return !hasToastedValues(row);
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
export function checkpointUserId(user_id: string | undefined, client_id: string | undefined) {
|
|
148
|
-
if (user_id == null) {
|
|
149
|
-
throw new Error('user_id is required');
|
|
150
|
-
}
|
|
151
|
-
if (client_id == null) {
|
|
152
|
-
return user_id;
|
|
153
|
-
}
|
|
154
|
-
return `${user_id}/${client_id}`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
148
|
/**
|
|
158
149
|
* Reduce a bucket to the final state as stored on the client.
|
|
159
150
|
*
|