@powersync/service-core 0.0.0-dev-20241119082750 → 0.0.0-dev-20241219091224
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 +88 -18
- package/dist/auth/KeySpec.d.ts +1 -0
- package/dist/auth/KeySpec.js +10 -8
- package/dist/auth/KeySpec.js.map +1 -1
- package/dist/auth/RemoteJWKSCollector.js +2 -2
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/dist/auth/StaticSupabaseKeyCollector.d.ts +19 -0
- package/dist/auth/StaticSupabaseKeyCollector.js +28 -0
- package/dist/auth/StaticSupabaseKeyCollector.js.map +1 -0
- package/dist/auth/auth-index.d.ts +1 -0
- package/dist/auth/auth-index.js +1 -0
- package/dist/auth/auth-index.js.map +1 -1
- package/dist/entry/commands/compact-action.js +15 -15
- package/dist/entry/commands/compact-action.js.map +1 -1
- package/dist/entry/commands/migrate-action.js +15 -4
- package/dist/entry/commands/migrate-action.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations/PowerSyncMigrationManager.d.ts +17 -0
- package/dist/migrations/PowerSyncMigrationManager.js +22 -0
- package/dist/migrations/PowerSyncMigrationManager.js.map +1 -0
- package/dist/migrations/ensure-automatic-migrations.d.ts +4 -0
- package/dist/migrations/ensure-automatic-migrations.js +14 -0
- package/dist/migrations/ensure-automatic-migrations.js.map +1 -0
- package/dist/migrations/migrations-index.d.ts +2 -3
- package/dist/migrations/migrations-index.js +2 -3
- package/dist/migrations/migrations-index.js.map +1 -1
- package/dist/routes/RouterEngine.js +2 -1
- package/dist/routes/RouterEngine.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +28 -28
- package/dist/routes/endpoints/admin.d.ts +24 -24
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +41 -1
- package/dist/storage/BucketStorage.js +26 -0
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/storage-index.d.ts +2 -14
- package/dist/storage/storage-index.js +2 -14
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/sync/sync.js +12 -3
- package/dist/sync/sync.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +3 -0
- package/dist/system/ServiceContext.js +11 -3
- package/dist/system/ServiceContext.js.map +1 -1
- package/dist/util/config/compound-config-collector.js +16 -0
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/types.d.ts +2 -2
- package/dist/util/utils.d.ts +14 -1
- package/dist/util/utils.js +56 -0
- package/dist/util/utils.js.map +1 -1
- package/package.json +7 -11
- package/src/auth/KeySpec.ts +12 -9
- package/src/auth/RemoteJWKSCollector.ts +2 -2
- package/src/auth/StaticSupabaseKeyCollector.ts +31 -0
- package/src/auth/auth-index.ts +1 -0
- package/src/entry/commands/compact-action.ts +20 -15
- package/src/entry/commands/migrate-action.ts +17 -4
- package/src/index.ts +1 -4
- package/src/migrations/PowerSyncMigrationManager.ts +43 -0
- package/src/migrations/ensure-automatic-migrations.ts +15 -0
- package/src/migrations/migrations-index.ts +2 -3
- package/src/routes/RouterEngine.ts +2 -1
- package/src/routes/endpoints/sync-rules.ts +1 -2
- package/src/storage/BucketStorage.ts +44 -1
- package/src/storage/storage-index.ts +3 -15
- package/src/sync/sync.ts +12 -3
- package/src/system/ServiceContext.ts +17 -4
- package/src/util/config/compound-config-collector.ts +19 -1
- package/src/util/config/types.ts +2 -2
- package/src/util/utils.ts +59 -1
- package/test/src/auth.test.ts +54 -21
- package/test/src/env.ts +0 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/db/db-index.d.ts +0 -1
- package/dist/db/db-index.js +0 -2
- package/dist/db/db-index.js.map +0 -1
- package/dist/db/mongo.d.ts +0 -35
- package/dist/db/mongo.js +0 -73
- package/dist/db/mongo.js.map +0 -1
- package/dist/locks/LockManager.d.ts +0 -10
- package/dist/locks/LockManager.js +0 -7
- package/dist/locks/LockManager.js.map +0 -1
- package/dist/locks/MongoLocks.d.ts +0 -36
- package/dist/locks/MongoLocks.js +0 -81
- package/dist/locks/MongoLocks.js.map +0 -1
- package/dist/locks/locks-index.d.ts +0 -2
- package/dist/locks/locks-index.js +0 -3
- package/dist/locks/locks-index.js.map +0 -1
- package/dist/migrations/db/migrations/1684951997326-init.d.ts +0 -3
- package/dist/migrations/db/migrations/1684951997326-init.js +0 -33
- package/dist/migrations/db/migrations/1684951997326-init.js.map +0 -1
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.d.ts +0 -2
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +0 -5
- package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +0 -1
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +0 -3
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +0 -56
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +0 -1
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +0 -3
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +0 -29
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +0 -1
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.d.ts +0 -3
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js +0 -31
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js.map +0 -1
- package/dist/migrations/definitions.d.ts +0 -18
- package/dist/migrations/definitions.js +0 -6
- package/dist/migrations/definitions.js.map +0 -1
- package/dist/migrations/executor.d.ts +0 -16
- package/dist/migrations/executor.js +0 -64
- package/dist/migrations/executor.js.map +0 -1
- package/dist/migrations/migrations.d.ts +0 -18
- package/dist/migrations/migrations.js +0 -110
- package/dist/migrations/migrations.js.map +0 -1
- package/dist/migrations/store/migration-store.d.ts +0 -11
- package/dist/migrations/store/migration-store.js +0 -46
- package/dist/migrations/store/migration-store.js.map +0 -1
- package/dist/storage/MongoBucketStorage.d.ts +0 -48
- package/dist/storage/MongoBucketStorage.js +0 -426
- package/dist/storage/MongoBucketStorage.js.map +0 -1
- package/dist/storage/mongo/MongoBucketBatch.d.ts +0 -67
- package/dist/storage/mongo/MongoBucketBatch.js +0 -643
- package/dist/storage/mongo/MongoBucketBatch.js.map +0 -1
- package/dist/storage/mongo/MongoCompactor.d.ts +0 -40
- package/dist/storage/mongo/MongoCompactor.js +0 -309
- package/dist/storage/mongo/MongoCompactor.js.map +0 -1
- package/dist/storage/mongo/MongoIdSequence.d.ts +0 -12
- package/dist/storage/mongo/MongoIdSequence.js +0 -21
- package/dist/storage/mongo/MongoIdSequence.js.map +0 -1
- package/dist/storage/mongo/MongoPersistedSyncRules.d.ts +0 -9
- package/dist/storage/mongo/MongoPersistedSyncRules.js +0 -9
- package/dist/storage/mongo/MongoPersistedSyncRules.js.map +0 -1
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +0 -20
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +0 -26
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +0 -1
- package/dist/storage/mongo/MongoStorageProvider.d.ts +0 -5
- package/dist/storage/mongo/MongoStorageProvider.js +0 -26
- package/dist/storage/mongo/MongoStorageProvider.js.map +0 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +0 -38
- package/dist/storage/mongo/MongoSyncBucketStorage.js +0 -531
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +0 -1
- package/dist/storage/mongo/MongoSyncRulesLock.d.ts +0 -16
- package/dist/storage/mongo/MongoSyncRulesLock.js +0 -65
- package/dist/storage/mongo/MongoSyncRulesLock.js.map +0 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +0 -20
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js +0 -103
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +0 -1
- package/dist/storage/mongo/OperationBatch.d.ts +0 -35
- package/dist/storage/mongo/OperationBatch.js +0 -119
- package/dist/storage/mongo/OperationBatch.js.map +0 -1
- package/dist/storage/mongo/PersistedBatch.d.ts +0 -46
- package/dist/storage/mongo/PersistedBatch.js +0 -213
- package/dist/storage/mongo/PersistedBatch.js.map +0 -1
- package/dist/storage/mongo/config.d.ts +0 -19
- package/dist/storage/mongo/config.js +0 -26
- package/dist/storage/mongo/config.js.map +0 -1
- package/dist/storage/mongo/db.d.ts +0 -36
- package/dist/storage/mongo/db.js +0 -47
- package/dist/storage/mongo/db.js.map +0 -1
- package/dist/storage/mongo/models.d.ts +0 -156
- package/dist/storage/mongo/models.js +0 -27
- package/dist/storage/mongo/models.js.map +0 -1
- package/dist/storage/mongo/util.d.ts +0 -40
- package/dist/storage/mongo/util.js +0 -151
- package/dist/storage/mongo/util.js.map +0 -1
- package/src/db/db-index.ts +0 -1
- package/src/db/mongo.ts +0 -81
- package/src/locks/LockManager.ts +0 -16
- package/src/locks/MongoLocks.ts +0 -142
- package/src/locks/locks-index.ts +0 -2
- package/src/migrations/db/migrations/1684951997326-init.ts +0 -38
- package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +0 -5
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +0 -102
- package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +0 -34
- package/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts +0 -37
- package/src/migrations/definitions.ts +0 -21
- package/src/migrations/executor.ts +0 -87
- package/src/migrations/migrations.ts +0 -142
- package/src/migrations/store/migration-store.ts +0 -63
- package/src/storage/MongoBucketStorage.ts +0 -540
- package/src/storage/mongo/MongoBucketBatch.ts +0 -841
- package/src/storage/mongo/MongoCompactor.ts +0 -392
- package/src/storage/mongo/MongoIdSequence.ts +0 -24
- package/src/storage/mongo/MongoPersistedSyncRules.ts +0 -16
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +0 -50
- package/src/storage/mongo/MongoStorageProvider.ts +0 -31
- package/src/storage/mongo/MongoSyncBucketStorage.ts +0 -636
- package/src/storage/mongo/MongoSyncRulesLock.ts +0 -85
- package/src/storage/mongo/MongoWriteCheckpointAPI.ts +0 -151
- package/src/storage/mongo/OperationBatch.ts +0 -131
- package/src/storage/mongo/PersistedBatch.ts +0 -272
- package/src/storage/mongo/config.ts +0 -40
- package/src/storage/mongo/db.ts +0 -88
- package/src/storage/mongo/models.ts +0 -179
- package/src/storage/mongo/util.ts +0 -158
- package/test/src/__snapshots__/sync.test.ts.snap +0 -332
- package/test/src/bucket_validation.test.ts +0 -142
- package/test/src/bucket_validation.ts +0 -116
- package/test/src/compacting.test.ts +0 -295
- package/test/src/data_storage.test.ts +0 -1499
- package/test/src/stream_utils.ts +0 -42
- package/test/src/sync.test.ts +0 -511
- package/test/src/util.ts +0 -148
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
export * from './BucketStorage.js';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './ChecksumCache.js';
|
|
3
3
|
export * from './ReplicationEventPayload.js';
|
|
4
4
|
export * from './SourceEntity.js';
|
|
5
5
|
export * from './SourceTable.js';
|
|
6
6
|
export * from './StorageEngine.js';
|
|
7
|
-
|
|
8
|
-
export * from './mongo/config.js';
|
|
9
|
-
export * from './mongo/db.js';
|
|
10
|
-
export * from './mongo/models.js';
|
|
11
|
-
export * from './mongo/MongoBucketBatch.js';
|
|
12
|
-
export * from './mongo/MongoIdSequence.js';
|
|
13
|
-
export * from './mongo/MongoPersistedSyncRules.js';
|
|
14
|
-
export * from './mongo/MongoPersistedSyncRulesContent.js';
|
|
15
|
-
export * from './mongo/MongoStorageProvider.js';
|
|
16
|
-
export * from './mongo/MongoSyncBucketStorage.js';
|
|
17
|
-
export * from './mongo/MongoSyncRulesLock.js';
|
|
18
|
-
export * from './mongo/OperationBatch.js';
|
|
19
|
-
export * from './mongo/PersistedBatch.js';
|
|
20
|
-
export * from './mongo/util.js';
|
|
7
|
+
export * from './StorageProvider.js';
|
|
21
8
|
export * from './WriteCheckpointAPI.js';
|
|
9
|
+
|
package/src/sync/sync.ts
CHANGED
|
@@ -318,9 +318,18 @@ async function* bucketDataBatch(request: BucketDataRequest): AsyncGenerator<Buck
|
|
|
318
318
|
// Send the object as is, will most likely be encoded as a BSON document
|
|
319
319
|
send_data = { data: r };
|
|
320
320
|
} else if (raw_data) {
|
|
321
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Data is a raw string - we can use the more efficient JSON.stringify.
|
|
323
|
+
* FIXME: ensure that the data is actually a string
|
|
324
|
+
*/
|
|
322
325
|
const response: util.StreamingSyncData = {
|
|
323
|
-
data:
|
|
326
|
+
data: {
|
|
327
|
+
...r,
|
|
328
|
+
data: r.data.map((entry) => ({
|
|
329
|
+
...entry,
|
|
330
|
+
data: typeof entry.data == 'string' ? entry.data : JSONBig.stringify(entry.data)
|
|
331
|
+
}))
|
|
332
|
+
}
|
|
324
333
|
};
|
|
325
334
|
send_data = JSON.stringify(response);
|
|
326
335
|
} else {
|
|
@@ -375,7 +384,7 @@ function transformLegacyResponse(bucketData: util.SyncBucketData): any {
|
|
|
375
384
|
data: bucketData.data.map((entry) => {
|
|
376
385
|
return {
|
|
377
386
|
...entry,
|
|
378
|
-
data: entry.data == null ? null : new JsonContainer(entry.data
|
|
387
|
+
data: entry.data == null ? null : typeof entry.data == 'string' ? new JsonContainer(entry.data) : entry.data,
|
|
379
388
|
checksum: BigInt(entry.checksum)
|
|
380
389
|
};
|
|
381
390
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { LifeCycledSystem, ServiceIdentifier, container } from '@powersync/lib-services-framework';
|
|
1
|
+
import { LifeCycledSystem, MigrationManager, ServiceIdentifier, container } from '@powersync/lib-services-framework';
|
|
2
2
|
|
|
3
|
+
import { framework } from '../index.js';
|
|
3
4
|
import * as metrics from '../metrics/Metrics.js';
|
|
5
|
+
import { PowerSyncMigrationManager } from '../migrations/PowerSyncMigrationManager.js';
|
|
4
6
|
import * as replication from '../replication/replication-index.js';
|
|
5
7
|
import * as routes from '../routes/routes-index.js';
|
|
6
8
|
import * as storage from '../storage/storage-index.js';
|
|
@@ -13,6 +15,7 @@ export interface ServiceContext {
|
|
|
13
15
|
replicationEngine: replication.ReplicationEngine | null;
|
|
14
16
|
routerEngine: routes.RouterEngine | null;
|
|
15
17
|
storageEngine: storage.StorageEngine;
|
|
18
|
+
migrations: PowerSyncMigrationManager;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -30,13 +33,19 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
30
33
|
this.storageEngine = new storage.StorageEngine({
|
|
31
34
|
configuration
|
|
32
35
|
});
|
|
36
|
+
|
|
37
|
+
const migrationManager = new MigrationManager();
|
|
38
|
+
container.register(framework.ContainerImplementation.MIGRATION_MANAGER, migrationManager);
|
|
39
|
+
|
|
40
|
+
this.lifeCycleEngine.withLifecycle(migrationManager, {
|
|
41
|
+
// Migrations should be executed before the system starts
|
|
42
|
+
start: () => migrationManager[Symbol.asyncDispose]()
|
|
43
|
+
});
|
|
44
|
+
|
|
33
45
|
this.lifeCycleEngine.withLifecycle(this.storageEngine, {
|
|
34
46
|
start: (storageEngine) => storageEngine.start(),
|
|
35
47
|
stop: (storageEngine) => storageEngine.shutDown()
|
|
36
48
|
});
|
|
37
|
-
|
|
38
|
-
// Mongo storage is available as an option by default TODO: Consider moving this to a Mongo Storage Module
|
|
39
|
-
this.storageEngine.registerProvider(new storage.MongoStorageProvider());
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
get replicationEngine(): replication.ReplicationEngine | null {
|
|
@@ -51,6 +60,10 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
51
60
|
return container.getOptional(metrics.Metrics);
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
get migrations(): PowerSyncMigrationManager {
|
|
64
|
+
return container.getImplementation(framework.ContainerImplementation.MIGRATION_MANAGER);
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
/**
|
|
55
68
|
* Allows for registering core and generic implementations of services/helpers.
|
|
56
69
|
* This uses the framework container under the hood.
|
|
@@ -65,9 +65,27 @@ export class CompoundConfigCollector {
|
|
|
65
65
|
|
|
66
66
|
const inputKeys = baseConfig.client_auth?.jwks?.keys ?? [];
|
|
67
67
|
const staticCollector = await auth.StaticKeyCollector.importKeys(inputKeys);
|
|
68
|
-
|
|
69
68
|
collectors.add(staticCollector);
|
|
70
69
|
|
|
70
|
+
if (baseConfig.client_auth?.supabase && baseConfig.client_auth?.supabase_jwt_secret != null) {
|
|
71
|
+
// This replaces the old SupabaseKeyCollector, with a statically-configured key.
|
|
72
|
+
// You can get the same effect with manual HS256 key configuration, but this
|
|
73
|
+
// makes the config simpler.
|
|
74
|
+
// We also a custom audience ("authenticated"), increased max lifetime (1 week),
|
|
75
|
+
// and auto base64-url-encode the key.
|
|
76
|
+
collectors.add(
|
|
77
|
+
await auth.StaticSupabaseKeyCollector.importKeys([
|
|
78
|
+
{
|
|
79
|
+
kty: 'oct',
|
|
80
|
+
alg: 'HS256',
|
|
81
|
+
// In this case, the key is not base64-encoded yet.
|
|
82
|
+
k: Buffer.from(baseConfig.client_auth.supabase_jwt_secret, 'utf8').toString('base64url'),
|
|
83
|
+
kid: undefined // Wildcard kid - any kid can match
|
|
84
|
+
}
|
|
85
|
+
])
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
71
89
|
let jwks_uris = baseConfig.client_auth?.jwks_uri ?? [];
|
|
72
90
|
if (typeof jwks_uris == 'string') {
|
|
73
91
|
jwks_uris = [jwks_uris];
|
package/src/util/config/types.ts
CHANGED
|
@@ -30,8 +30,8 @@ export type SyncRulesConfig = {
|
|
|
30
30
|
|
|
31
31
|
export type ResolvedPowerSyncConfig = {
|
|
32
32
|
base_config: PowerSyncConfig;
|
|
33
|
-
connections?: configFile.
|
|
34
|
-
storage: configFile.
|
|
33
|
+
connections?: configFile.GenericDataSourceConfig[];
|
|
34
|
+
storage: configFile.GenericStorageConfig;
|
|
35
35
|
dev: {
|
|
36
36
|
demo_auth: boolean;
|
|
37
37
|
demo_password?: string;
|
package/src/util/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as sync_rules from '@powersync/service-sync-rules';
|
|
|
2
2
|
import * as bson from 'bson';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
import * as uuid from 'uuid';
|
|
5
|
-
import { BucketChecksum, OpId } from './protocol-types.js';
|
|
5
|
+
import { BucketChecksum, OpId, OplogEntry } from './protocol-types.js';
|
|
6
6
|
|
|
7
7
|
import * as storage from '../storage/storage-index.js';
|
|
8
8
|
|
|
@@ -144,3 +144,61 @@ export function checkpointUserId(user_id: string | undefined, client_id: string
|
|
|
144
144
|
}
|
|
145
145
|
return `${user_id}/${client_id}`;
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Reduce a bucket to the final state as stored on the client.
|
|
150
|
+
*
|
|
151
|
+
* This keeps the final state for each row as a PUT operation.
|
|
152
|
+
*
|
|
153
|
+
* All other operations are replaced with a single CLEAR operation,
|
|
154
|
+
* summing their checksums, and using a 0 as an op_id.
|
|
155
|
+
*
|
|
156
|
+
* This is the function $r(B)$, as described in /docs/bucket-properties.md.
|
|
157
|
+
*
|
|
158
|
+
* Used for tests.
|
|
159
|
+
*/
|
|
160
|
+
export function reduceBucket(operations: OplogEntry[]) {
|
|
161
|
+
let rowState = new Map<string, OplogEntry>();
|
|
162
|
+
let otherChecksum = 0;
|
|
163
|
+
|
|
164
|
+
for (let op of operations) {
|
|
165
|
+
const key = rowKey(op);
|
|
166
|
+
if (op.op == 'PUT') {
|
|
167
|
+
const existing = rowState.get(key);
|
|
168
|
+
if (existing) {
|
|
169
|
+
otherChecksum = addChecksums(otherChecksum, existing.checksum as number);
|
|
170
|
+
}
|
|
171
|
+
rowState.set(key, op);
|
|
172
|
+
} else if (op.op == 'REMOVE') {
|
|
173
|
+
const existing = rowState.get(key);
|
|
174
|
+
if (existing) {
|
|
175
|
+
otherChecksum = addChecksums(otherChecksum, existing.checksum as number);
|
|
176
|
+
}
|
|
177
|
+
rowState.delete(key);
|
|
178
|
+
otherChecksum = addChecksums(otherChecksum, op.checksum as number);
|
|
179
|
+
} else if (op.op == 'CLEAR') {
|
|
180
|
+
rowState.clear();
|
|
181
|
+
otherChecksum = op.checksum as number;
|
|
182
|
+
} else if (op.op == 'MOVE') {
|
|
183
|
+
otherChecksum = addChecksums(otherChecksum, op.checksum as number);
|
|
184
|
+
} else {
|
|
185
|
+
throw new Error(`Unknown operation ${op.op}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const puts = [...rowState.values()].sort((a, b) => {
|
|
190
|
+
return Number(BigInt(a.op_id) - BigInt(b.op_id));
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
let finalState: OplogEntry[] = [
|
|
194
|
+
// Special operation to indiciate the checksum remainder
|
|
195
|
+
{ op_id: '0', op: 'CLEAR', checksum: otherChecksum },
|
|
196
|
+
...puts
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
return finalState;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function rowKey(entry: OplogEntry) {
|
|
203
|
+
return `${entry.object_type}/${entry.object_id}/${entry.subkey}`;
|
|
204
|
+
}
|
package/test/src/auth.test.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { CachedKeyCollector } from '@/auth/CachedKeyCollector.js';
|
|
2
|
-
import { KeyResult } from '@/auth/KeyCollector.js';
|
|
3
|
-
import { KeySpec } from '@/auth/KeySpec.js';
|
|
4
|
-
import { KeyStore } from '@/auth/KeyStore.js';
|
|
5
|
-
import { RemoteJWKSCollector } from '@/auth/RemoteJWKSCollector.js';
|
|
6
|
-
import { StaticKeyCollector } from '@/auth/StaticKeyCollector.js';
|
|
7
|
-
import * as jose from 'jose';
|
|
8
1
|
import { describe, expect, test } from 'vitest';
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { StaticKeyCollector } from '../../src/auth/StaticKeyCollector.js';
|
|
3
|
+
import * as jose from 'jose';
|
|
4
|
+
import { KeyStore } from '../../src/auth/KeyStore.js';
|
|
5
|
+
import { KeySpec } from '../../src/auth/KeySpec.js';
|
|
6
|
+
import { RemoteJWKSCollector } from '../../src/auth/RemoteJWKSCollector.js';
|
|
7
|
+
import { KeyResult } from '../../src/auth/KeyCollector.js';
|
|
8
|
+
import { CachedKeyCollector } from '../../src/auth/CachedKeyCollector.js';
|
|
9
|
+
import { JwtPayload } from '@/index.js';
|
|
10
|
+
|
|
11
|
+
const publicKeyRSA: jose.JWK = {
|
|
11
12
|
use: 'sig',
|
|
12
13
|
kty: 'RSA',
|
|
13
14
|
e: 'AQAB',
|
|
@@ -29,6 +30,16 @@ const sharedKey2: jose.JWK = {
|
|
|
29
30
|
k: Buffer.from('mysecret2', 'utf-8').toString('base64url')
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
const privateKeyEdDSA: jose.JWK = {
|
|
34
|
+
use: 'sig',
|
|
35
|
+
kty: 'OKP',
|
|
36
|
+
crv: 'Ed25519',
|
|
37
|
+
kid: 'k2',
|
|
38
|
+
x: 'nfaqgxakPaiiEdAtRGrubgh_SQ1mr6gAUx3--N-ehvo',
|
|
39
|
+
d: 'wweBqMbTrME6oChSEMYAOyYzxsGisQb-C1t0XMjb_Ng',
|
|
40
|
+
alg: 'EdDSA'
|
|
41
|
+
};
|
|
42
|
+
|
|
32
43
|
describe('JWT Auth', () => {
|
|
33
44
|
test('KeyStore basics', async () => {
|
|
34
45
|
const keys = await StaticKeyCollector.importKeys([sharedKey]);
|
|
@@ -86,20 +97,20 @@ describe('JWT Auth', () => {
|
|
|
86
97
|
});
|
|
87
98
|
|
|
88
99
|
test('Algorithm validation', async () => {
|
|
89
|
-
const keys = await StaticKeyCollector.importKeys([
|
|
100
|
+
const keys = await StaticKeyCollector.importKeys([publicKeyRSA]);
|
|
90
101
|
const store = new KeyStore(keys);
|
|
91
102
|
|
|
92
103
|
// Bad attempt at signing token with rsa public key
|
|
93
104
|
const spoofedKey: jose.JWK = {
|
|
94
105
|
kty: 'oct',
|
|
95
|
-
kid:
|
|
106
|
+
kid: publicKeyRSA.kid!,
|
|
96
107
|
alg: 'HS256',
|
|
97
|
-
k:
|
|
108
|
+
k: publicKeyRSA.n!
|
|
98
109
|
};
|
|
99
110
|
const signKey = (await jose.importJWK(spoofedKey)) as jose.KeyLike;
|
|
100
111
|
|
|
101
112
|
const signedJwt = await new jose.SignJWT({})
|
|
102
|
-
.setProtectedHeader({ alg: 'HS256', kid:
|
|
113
|
+
.setProtectedHeader({ alg: 'HS256', kid: publicKeyRSA.kid! })
|
|
103
114
|
.setSubject('f1')
|
|
104
115
|
.setIssuedAt()
|
|
105
116
|
.setIssuer('tester')
|
|
@@ -116,7 +127,7 @@ describe('JWT Auth', () => {
|
|
|
116
127
|
});
|
|
117
128
|
|
|
118
129
|
test('key selection for key with kid', async () => {
|
|
119
|
-
const keys = await StaticKeyCollector.importKeys([
|
|
130
|
+
const keys = await StaticKeyCollector.importKeys([publicKeyRSA, sharedKey, sharedKey2]);
|
|
120
131
|
const store = new KeyStore(keys);
|
|
121
132
|
const signKey = (await jose.importJWK(sharedKey)) as jose.KeyLike;
|
|
122
133
|
const signKey2 = (await jose.importJWK(sharedKey2)) as jose.KeyLike;
|
|
@@ -296,30 +307,30 @@ describe('JWT Auth', () => {
|
|
|
296
307
|
|
|
297
308
|
currentResponse = Promise.resolve({
|
|
298
309
|
errors: [],
|
|
299
|
-
keys: [await KeySpec.importKey(
|
|
310
|
+
keys: [await KeySpec.importKey(publicKeyRSA)]
|
|
300
311
|
});
|
|
301
312
|
|
|
302
313
|
let key = (await cached.getKeys()).keys[0];
|
|
303
|
-
expect(key.kid).toEqual(
|
|
314
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
304
315
|
|
|
305
316
|
currentResponse = undefined as any;
|
|
306
317
|
|
|
307
318
|
key = (await cached.getKeys()).keys[0];
|
|
308
|
-
expect(key.kid).toEqual(
|
|
319
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
309
320
|
|
|
310
321
|
cached.addTimeForTests(301_000);
|
|
311
322
|
currentResponse = Promise.reject('refresh failed');
|
|
312
323
|
|
|
313
324
|
// Uses the promise, refreshes in the background
|
|
314
325
|
let response = await cached.getKeys();
|
|
315
|
-
expect(response.keys[0].kid).toEqual(
|
|
326
|
+
expect(response.keys[0].kid).toEqual(publicKeyRSA.kid!);
|
|
316
327
|
expect(response.errors).toEqual([]);
|
|
317
328
|
|
|
318
329
|
// Wait for refresh to finish
|
|
319
330
|
await cached.addTimeForTests(0);
|
|
320
331
|
response = await cached.getKeys();
|
|
321
332
|
// Still have the cached key, but also have the error
|
|
322
|
-
expect(response.keys[0].kid).toEqual(
|
|
333
|
+
expect(response.keys[0].kid).toEqual(publicKeyRSA.kid!);
|
|
323
334
|
expect(response.errors[0].message).toMatch('Failed to fetch');
|
|
324
335
|
|
|
325
336
|
await cached.addTimeForTests(3601_000);
|
|
@@ -331,12 +342,34 @@ describe('JWT Auth', () => {
|
|
|
331
342
|
|
|
332
343
|
currentResponse = Promise.resolve({
|
|
333
344
|
errors: [],
|
|
334
|
-
keys: [await KeySpec.importKey(
|
|
345
|
+
keys: [await KeySpec.importKey(publicKeyRSA)]
|
|
335
346
|
});
|
|
336
347
|
|
|
337
348
|
// After a delay, we can refresh again
|
|
338
349
|
await cached.addTimeForTests(30_000);
|
|
339
350
|
key = (await cached.getKeys()).keys[0];
|
|
340
|
-
expect(key.kid).toEqual(
|
|
351
|
+
expect(key.kid).toEqual(publicKeyRSA.kid!);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('signing with EdDSA', async () => {
|
|
355
|
+
const keys = await StaticKeyCollector.importKeys([privateKeyEdDSA]);
|
|
356
|
+
const store = new KeyStore(keys);
|
|
357
|
+
const signKey = (await jose.importJWK(privateKeyEdDSA)) as jose.KeyLike;
|
|
358
|
+
|
|
359
|
+
const signedJwt = await new jose.SignJWT({ claim: 'test-claim' })
|
|
360
|
+
.setProtectedHeader({ alg: 'EdDSA', kid: 'k2' })
|
|
361
|
+
.setSubject('f1')
|
|
362
|
+
.setIssuedAt()
|
|
363
|
+
.setIssuer('tester')
|
|
364
|
+
.setAudience('tests')
|
|
365
|
+
.setExpirationTime('5m')
|
|
366
|
+
.sign(signKey);
|
|
367
|
+
|
|
368
|
+
const verified = (await store.verifyJwt(signedJwt, {
|
|
369
|
+
defaultAudiences: ['tests'],
|
|
370
|
+
maxAge: '6m'
|
|
371
|
+
})) as JwtPayload & { claim: string };
|
|
372
|
+
|
|
373
|
+
expect(verified.claim).toEqual('test-claim');
|
|
341
374
|
});
|
|
342
375
|
});
|
package/test/src/env.ts
CHANGED