@syncular/client 0.0.6-185 → 0.0.6-188
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/dist/client.d.ts +14 -71
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +81 -406
- package/dist/client.js.map +1 -1
- package/dist/create-client.d.ts +0 -2
- package/dist/create-client.d.ts.map +1 -1
- package/dist/create-client.js +2 -3
- package/dist/create-client.js.map +1 -1
- package/dist/engine/SyncEngine.d.ts +1 -0
- package/dist/engine/SyncEngine.d.ts.map +1 -1
- package/dist/engine/SyncEngine.js +21 -6
- package/dist/engine/SyncEngine.js.map +1 -1
- package/dist/handlers/create-handler.d.ts +5 -0
- package/dist/handlers/create-handler.d.ts.map +1 -1
- package/dist/handlers/create-handler.js +123 -4
- package/dist/handlers/create-handler.js.map +1 -1
- package/dist/handlers/types.d.ts +7 -0
- package/dist/handlers/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/blob-schema.d.ts +32 -0
- package/dist/internal/blob-schema.d.ts.map +1 -0
- package/dist/internal/blob-schema.js +2 -0
- package/dist/internal/blob-schema.js.map +1 -0
- package/dist/mutations.d.ts.map +1 -1
- package/dist/mutations.js +15 -6
- package/dist/mutations.js.map +1 -1
- package/dist/plugins/incrementing-version.d.ts.map +1 -1
- package/dist/plugins/incrementing-version.js +20 -8
- package/dist/plugins/incrementing-version.js.map +1 -1
- package/dist/plugins/types.d.ts +26 -1
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js.map +1 -1
- package/dist/pull-engine.d.ts +8 -2
- package/dist/pull-engine.d.ts.map +1 -1
- package/dist/pull-engine.js +150 -26
- package/dist/pull-engine.js.map +1 -1
- package/dist/push-engine.d.ts.map +1 -1
- package/dist/push-engine.js +21 -5
- package/dist/push-engine.js.map +1 -1
- package/dist/schema.d.ts +2 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/sync-loop.d.ts +3 -1
- package/dist/sync-loop.d.ts.map +1 -1
- package/dist/sync-loop.js +382 -139
- package/dist/sync-loop.js.map +1 -1
- package/package.json +76 -3
- package/src/client.test.ts +72 -155
- package/src/client.ts +113 -572
- package/src/create-client.ts +1 -6
- package/src/engine/SyncEngine.test.ts +90 -0
- package/src/engine/SyncEngine.ts +29 -9
- package/src/handlers/create-handler.ts +197 -4
- package/src/handlers/types.ts +11 -0
- package/src/index.ts +1 -2
- package/src/internal/blob-schema.ts +40 -0
- package/src/mutations.ts +17 -6
- package/src/plugins/incrementing-version.ts +36 -7
- package/src/plugins/types.ts +42 -0
- package/src/pull-engine.test.ts +494 -0
- package/src/pull-engine.ts +193 -29
- package/src/push-engine.ts +31 -5
- package/src/schema.ts +2 -2
- package/src/sync-loop.ts +538 -145
- package/dist/blobs/index.d.ts +0 -6
- package/dist/blobs/index.d.ts.map +0 -1
- package/dist/blobs/index.js +0 -6
- package/dist/blobs/index.js.map +0 -1
- package/dist/blobs/migrate.d.ts +0 -14
- package/dist/blobs/migrate.d.ts.map +0 -1
- package/dist/blobs/migrate.js +0 -59
- package/dist/blobs/migrate.js.map +0 -1
- package/dist/blobs/types.d.ts +0 -62
- package/dist/blobs/types.d.ts.map +0 -1
- package/dist/blobs/types.js +0 -5
- package/dist/blobs/types.js.map +0 -1
- package/src/blobs/index.ts +0 -6
- package/src/blobs/migrate.ts +0 -67
- package/src/blobs/types.ts +0 -84
package/src/pull-engine.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { gzipSync } from 'node:zlib';
|
|
|
3
3
|
import {
|
|
4
4
|
createDatabase,
|
|
5
5
|
encodeSnapshotRows,
|
|
6
|
+
type ScopeValues,
|
|
6
7
|
type SyncPullResponse,
|
|
7
8
|
type SyncTransport,
|
|
8
9
|
} from '@syncular/core';
|
|
@@ -19,8 +20,15 @@ interface ItemsTable {
|
|
|
19
20
|
name: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
interface ScopedItemsTable {
|
|
24
|
+
id: string;
|
|
25
|
+
project_id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
interface TestDb extends SyncClientDb {
|
|
23
30
|
items: ItemsTable;
|
|
31
|
+
scoped_items: ScopedItemsTable;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
function createStreamFromBytes(
|
|
@@ -37,6 +45,11 @@ function createStreamFromBytes(
|
|
|
37
45
|
});
|
|
38
46
|
}
|
|
39
47
|
|
|
48
|
+
function toScopeValueArray(value: string | string[] | undefined): string[] {
|
|
49
|
+
if (!value) return [];
|
|
50
|
+
return Array.isArray(value) ? value : [value];
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
describe('applyPullResponse chunk streaming', () => {
|
|
41
54
|
let db: Kysely<TestDb>;
|
|
42
55
|
|
|
@@ -1048,4 +1061,485 @@ describe('applyPullResponse chunk streaming', () => {
|
|
|
1048
1061
|
},
|
|
1049
1062
|
]);
|
|
1050
1063
|
});
|
|
1064
|
+
|
|
1065
|
+
it('uses applyChanges for contiguous same-table incremental changes', async () => {
|
|
1066
|
+
const transport: SyncTransport = {
|
|
1067
|
+
async sync() {
|
|
1068
|
+
return {};
|
|
1069
|
+
},
|
|
1070
|
+
async fetchSnapshotChunk() {
|
|
1071
|
+
return new Uint8Array();
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
let applyChangeCalls = 0;
|
|
1076
|
+
let applyChangesCalls = 0;
|
|
1077
|
+
const baseHandler = createClientHandler<TestDb, 'items'>({
|
|
1078
|
+
table: 'items',
|
|
1079
|
+
scopes: ['items:{id}'],
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
const handlers: ClientHandlerCollection<TestDb> = [
|
|
1083
|
+
{
|
|
1084
|
+
...baseHandler,
|
|
1085
|
+
async applyChange(ctx, change) {
|
|
1086
|
+
applyChangeCalls += 1;
|
|
1087
|
+
await baseHandler.applyChange(ctx, change);
|
|
1088
|
+
},
|
|
1089
|
+
async applyChanges(ctx, changes) {
|
|
1090
|
+
applyChangesCalls += 1;
|
|
1091
|
+
if (!baseHandler.applyChanges) {
|
|
1092
|
+
throw new Error('Expected applyChanges to be available');
|
|
1093
|
+
}
|
|
1094
|
+
await baseHandler.applyChanges(ctx, changes);
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
];
|
|
1098
|
+
|
|
1099
|
+
const options = {
|
|
1100
|
+
clientId: 'client-1',
|
|
1101
|
+
subscriptions: [
|
|
1102
|
+
{
|
|
1103
|
+
id: 'items-sub',
|
|
1104
|
+
table: 'items',
|
|
1105
|
+
scopes: {},
|
|
1106
|
+
},
|
|
1107
|
+
],
|
|
1108
|
+
stateId: 'default',
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
const pullState = await buildPullRequest(db, options);
|
|
1112
|
+
const response: SyncPullResponse = {
|
|
1113
|
+
ok: true,
|
|
1114
|
+
subscriptions: [
|
|
1115
|
+
{
|
|
1116
|
+
id: 'items-sub',
|
|
1117
|
+
status: 'active',
|
|
1118
|
+
scopes: {},
|
|
1119
|
+
bootstrap: false,
|
|
1120
|
+
bootstrapState: null,
|
|
1121
|
+
nextCursor: 9,
|
|
1122
|
+
commits: [
|
|
1123
|
+
{
|
|
1124
|
+
commitSeq: 9,
|
|
1125
|
+
actorId: 'remote-user',
|
|
1126
|
+
createdAt: '2026-03-01T12:00:00.000Z',
|
|
1127
|
+
changes: [
|
|
1128
|
+
{
|
|
1129
|
+
table: 'items',
|
|
1130
|
+
row_id: 'item-1',
|
|
1131
|
+
op: 'upsert',
|
|
1132
|
+
row_version: 1,
|
|
1133
|
+
row_json: { id: 'item-1', name: 'One' },
|
|
1134
|
+
scopes: {},
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
table: 'items',
|
|
1138
|
+
row_id: 'item-2',
|
|
1139
|
+
op: 'upsert',
|
|
1140
|
+
row_version: 1,
|
|
1141
|
+
row_json: { id: 'item-2', name: 'Two' },
|
|
1142
|
+
scopes: {},
|
|
1143
|
+
},
|
|
1144
|
+
],
|
|
1145
|
+
},
|
|
1146
|
+
],
|
|
1147
|
+
snapshots: [],
|
|
1148
|
+
},
|
|
1149
|
+
],
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
await applyPullResponse(
|
|
1153
|
+
db,
|
|
1154
|
+
transport,
|
|
1155
|
+
handlers,
|
|
1156
|
+
options,
|
|
1157
|
+
pullState,
|
|
1158
|
+
response
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
expect(applyChangesCalls).toBe(1);
|
|
1162
|
+
expect(applyChangeCalls).toBe(0);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
it('flushes batched upserts when the same row appears twice in one commit', async () => {
|
|
1166
|
+
const transport: SyncTransport = {
|
|
1167
|
+
async sync() {
|
|
1168
|
+
return {};
|
|
1169
|
+
},
|
|
1170
|
+
async fetchSnapshotChunk() {
|
|
1171
|
+
return new Uint8Array();
|
|
1172
|
+
},
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
const handlers: ClientHandlerCollection<TestDb> = [
|
|
1176
|
+
createClientHandler({
|
|
1177
|
+
table: 'items',
|
|
1178
|
+
scopes: ['items:{id}'],
|
|
1179
|
+
}),
|
|
1180
|
+
];
|
|
1181
|
+
|
|
1182
|
+
const options = {
|
|
1183
|
+
clientId: 'client-1',
|
|
1184
|
+
subscriptions: [
|
|
1185
|
+
{
|
|
1186
|
+
id: 'items-sub',
|
|
1187
|
+
table: 'items',
|
|
1188
|
+
scopes: {},
|
|
1189
|
+
},
|
|
1190
|
+
],
|
|
1191
|
+
stateId: 'default',
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
const pullState = await buildPullRequest(db, options);
|
|
1195
|
+
const response: SyncPullResponse = {
|
|
1196
|
+
ok: true,
|
|
1197
|
+
subscriptions: [
|
|
1198
|
+
{
|
|
1199
|
+
id: 'items-sub',
|
|
1200
|
+
status: 'active',
|
|
1201
|
+
scopes: {},
|
|
1202
|
+
bootstrap: false,
|
|
1203
|
+
bootstrapState: null,
|
|
1204
|
+
nextCursor: 10,
|
|
1205
|
+
commits: [
|
|
1206
|
+
{
|
|
1207
|
+
commitSeq: 10,
|
|
1208
|
+
actorId: 'remote-user',
|
|
1209
|
+
createdAt: '2026-03-01T12:00:00.000Z',
|
|
1210
|
+
changes: [
|
|
1211
|
+
{
|
|
1212
|
+
table: 'items',
|
|
1213
|
+
row_id: 'item-1',
|
|
1214
|
+
op: 'upsert',
|
|
1215
|
+
row_version: 1,
|
|
1216
|
+
row_json: { id: 'item-1', name: 'One' },
|
|
1217
|
+
scopes: {},
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
table: 'items',
|
|
1221
|
+
row_id: 'item-1',
|
|
1222
|
+
op: 'upsert',
|
|
1223
|
+
row_version: 2,
|
|
1224
|
+
row_json: { id: 'item-1', name: 'Two' },
|
|
1225
|
+
scopes: {},
|
|
1226
|
+
},
|
|
1227
|
+
],
|
|
1228
|
+
},
|
|
1229
|
+
],
|
|
1230
|
+
snapshots: [],
|
|
1231
|
+
},
|
|
1232
|
+
],
|
|
1233
|
+
};
|
|
1234
|
+
|
|
1235
|
+
await applyPullResponse(
|
|
1236
|
+
db,
|
|
1237
|
+
transport,
|
|
1238
|
+
handlers,
|
|
1239
|
+
options,
|
|
1240
|
+
pullState,
|
|
1241
|
+
response
|
|
1242
|
+
);
|
|
1243
|
+
|
|
1244
|
+
const row = await db
|
|
1245
|
+
.selectFrom('items')
|
|
1246
|
+
.select(['id', 'name'])
|
|
1247
|
+
.where('id', '=', 'item-1')
|
|
1248
|
+
.executeTakeFirstOrThrow();
|
|
1249
|
+
|
|
1250
|
+
expect(row.name).toBe('Two');
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
it('clears stale rows during same-scope bootstrap by default', async () => {
|
|
1254
|
+
const transport: SyncTransport = {
|
|
1255
|
+
async sync() {
|
|
1256
|
+
return {};
|
|
1257
|
+
},
|
|
1258
|
+
async fetchSnapshotChunk() {
|
|
1259
|
+
throw new Error('fetchSnapshotChunk should not be used');
|
|
1260
|
+
},
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
await db.schema
|
|
1264
|
+
.createTable('scoped_items')
|
|
1265
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
1266
|
+
.addColumn('project_id', 'text', (col) => col.notNull())
|
|
1267
|
+
.addColumn('name', 'text', (col) => col.notNull())
|
|
1268
|
+
.execute();
|
|
1269
|
+
|
|
1270
|
+
const handlers: ClientHandlerCollection<TestDb> = [
|
|
1271
|
+
createClientHandler({
|
|
1272
|
+
table: 'scoped_items',
|
|
1273
|
+
scopes: ['project:{project_id}'],
|
|
1274
|
+
}),
|
|
1275
|
+
];
|
|
1276
|
+
|
|
1277
|
+
const options = {
|
|
1278
|
+
clientId: 'client-1',
|
|
1279
|
+
subscriptions: [
|
|
1280
|
+
{
|
|
1281
|
+
id: 'scoped-sub',
|
|
1282
|
+
table: 'scoped_items',
|
|
1283
|
+
scopes: { project_id: 'p1' },
|
|
1284
|
+
},
|
|
1285
|
+
],
|
|
1286
|
+
stateId: 'default',
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
const firstState = await buildPullRequest(db, options);
|
|
1290
|
+
await applyPullResponse(db, transport, handlers, options, firstState, {
|
|
1291
|
+
ok: true,
|
|
1292
|
+
subscriptions: [
|
|
1293
|
+
{
|
|
1294
|
+
id: 'scoped-sub',
|
|
1295
|
+
status: 'active',
|
|
1296
|
+
scopes: { project_id: 'p1' },
|
|
1297
|
+
bootstrap: true,
|
|
1298
|
+
bootstrapState: null,
|
|
1299
|
+
nextCursor: 1,
|
|
1300
|
+
commits: [],
|
|
1301
|
+
snapshots: [
|
|
1302
|
+
{
|
|
1303
|
+
table: 'scoped_items',
|
|
1304
|
+
rows: [
|
|
1305
|
+
{ id: 'p1-a', project_id: 'p1', name: 'A' },
|
|
1306
|
+
{ id: 'p1-b', project_id: 'p1', name: 'B' },
|
|
1307
|
+
],
|
|
1308
|
+
isFirstPage: true,
|
|
1309
|
+
isLastPage: true,
|
|
1310
|
+
},
|
|
1311
|
+
],
|
|
1312
|
+
},
|
|
1313
|
+
],
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
const secondState = await buildPullRequest(db, options);
|
|
1317
|
+
await applyPullResponse(db, transport, handlers, options, secondState, {
|
|
1318
|
+
ok: true,
|
|
1319
|
+
subscriptions: [
|
|
1320
|
+
{
|
|
1321
|
+
id: 'scoped-sub',
|
|
1322
|
+
status: 'active',
|
|
1323
|
+
scopes: { project_id: 'p1' },
|
|
1324
|
+
bootstrap: true,
|
|
1325
|
+
bootstrapState: null,
|
|
1326
|
+
nextCursor: 2,
|
|
1327
|
+
commits: [],
|
|
1328
|
+
snapshots: [
|
|
1329
|
+
{
|
|
1330
|
+
table: 'scoped_items',
|
|
1331
|
+
rows: [{ id: 'p1-a', project_id: 'p1', name: 'A' }],
|
|
1332
|
+
isFirstPage: true,
|
|
1333
|
+
isLastPage: true,
|
|
1334
|
+
},
|
|
1335
|
+
],
|
|
1336
|
+
},
|
|
1337
|
+
],
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
const rows = await db
|
|
1341
|
+
.selectFrom('scoped_items')
|
|
1342
|
+
.select(['id'])
|
|
1343
|
+
.orderBy('id', 'asc')
|
|
1344
|
+
.execute();
|
|
1345
|
+
expect(rows.map((row) => row.id)).toEqual(['p1-a']);
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
it('clears previously authorized rows when bootstrap scopes narrow', async () => {
|
|
1349
|
+
const transport: SyncTransport = {
|
|
1350
|
+
async sync() {
|
|
1351
|
+
return {};
|
|
1352
|
+
},
|
|
1353
|
+
async fetchSnapshotChunk() {
|
|
1354
|
+
throw new Error('fetchSnapshotChunk should not be used');
|
|
1355
|
+
},
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
await db.schema
|
|
1359
|
+
.createTable('scoped_items')
|
|
1360
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
1361
|
+
.addColumn('project_id', 'text', (col) => col.notNull())
|
|
1362
|
+
.addColumn('name', 'text', (col) => col.notNull())
|
|
1363
|
+
.execute();
|
|
1364
|
+
|
|
1365
|
+
const handlers: ClientHandlerCollection<TestDb> = [
|
|
1366
|
+
createClientHandler({
|
|
1367
|
+
table: 'scoped_items',
|
|
1368
|
+
scopes: ['project:{project_id}'],
|
|
1369
|
+
}),
|
|
1370
|
+
];
|
|
1371
|
+
|
|
1372
|
+
const options = {
|
|
1373
|
+
clientId: 'client-1',
|
|
1374
|
+
subscriptions: [
|
|
1375
|
+
{
|
|
1376
|
+
id: 'scoped-sub',
|
|
1377
|
+
table: 'scoped_items',
|
|
1378
|
+
scopes: { project_id: ['p1', 'p2'] },
|
|
1379
|
+
},
|
|
1380
|
+
],
|
|
1381
|
+
stateId: 'default',
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
const firstState = await buildPullRequest(db, options);
|
|
1385
|
+
await applyPullResponse(db, transport, handlers, options, firstState, {
|
|
1386
|
+
ok: true,
|
|
1387
|
+
subscriptions: [
|
|
1388
|
+
{
|
|
1389
|
+
id: 'scoped-sub',
|
|
1390
|
+
status: 'active',
|
|
1391
|
+
scopes: { project_id: ['p1', 'p2'] },
|
|
1392
|
+
bootstrap: true,
|
|
1393
|
+
bootstrapState: null,
|
|
1394
|
+
nextCursor: 1,
|
|
1395
|
+
commits: [],
|
|
1396
|
+
snapshots: [
|
|
1397
|
+
{
|
|
1398
|
+
table: 'scoped_items',
|
|
1399
|
+
rows: [
|
|
1400
|
+
{ id: 'p1-a', project_id: 'p1', name: 'A' },
|
|
1401
|
+
{ id: 'p2-a', project_id: 'p2', name: 'B' },
|
|
1402
|
+
],
|
|
1403
|
+
isFirstPage: true,
|
|
1404
|
+
isLastPage: true,
|
|
1405
|
+
},
|
|
1406
|
+
],
|
|
1407
|
+
},
|
|
1408
|
+
],
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
const secondState = await buildPullRequest(db, options);
|
|
1412
|
+
await applyPullResponse(db, transport, handlers, options, secondState, {
|
|
1413
|
+
ok: true,
|
|
1414
|
+
subscriptions: [
|
|
1415
|
+
{
|
|
1416
|
+
id: 'scoped-sub',
|
|
1417
|
+
status: 'active',
|
|
1418
|
+
scopes: { project_id: 'p2' },
|
|
1419
|
+
bootstrap: true,
|
|
1420
|
+
bootstrapState: null,
|
|
1421
|
+
nextCursor: 2,
|
|
1422
|
+
commits: [],
|
|
1423
|
+
snapshots: [
|
|
1424
|
+
{
|
|
1425
|
+
table: 'scoped_items',
|
|
1426
|
+
rows: [{ id: 'p2-a', project_id: 'p2', name: 'B' }],
|
|
1427
|
+
isFirstPage: true,
|
|
1428
|
+
isLastPage: true,
|
|
1429
|
+
},
|
|
1430
|
+
],
|
|
1431
|
+
},
|
|
1432
|
+
],
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
const rows = await db
|
|
1436
|
+
.selectFrom('scoped_items')
|
|
1437
|
+
.select(['id'])
|
|
1438
|
+
.orderBy('id', 'asc')
|
|
1439
|
+
.execute();
|
|
1440
|
+
expect(rows.map((row) => row.id)).toEqual(['p2-a']);
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
it('clears only the removed scope slice when bootstrap scopes narrow on one key', async () => {
|
|
1444
|
+
const transport: SyncTransport = {
|
|
1445
|
+
async sync() {
|
|
1446
|
+
return {};
|
|
1447
|
+
},
|
|
1448
|
+
async fetchSnapshotChunk() {
|
|
1449
|
+
throw new Error('fetchSnapshotChunk should not be used');
|
|
1450
|
+
},
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
await db.schema
|
|
1454
|
+
.createTable('tracked_items')
|
|
1455
|
+
.addColumn('id', 'text', (col) => col.primaryKey())
|
|
1456
|
+
.addColumn('project_id', 'text', (col) => col.notNull())
|
|
1457
|
+
.addColumn('name', 'text', (col) => col.notNull())
|
|
1458
|
+
.execute();
|
|
1459
|
+
|
|
1460
|
+
const clearedScopes: ScopeValues[] = [];
|
|
1461
|
+
|
|
1462
|
+
const handlers: ClientHandlerCollection<TestDb> = [
|
|
1463
|
+
createClientHandler({
|
|
1464
|
+
table: 'tracked_items',
|
|
1465
|
+
scopes: ['project:{project_id}'],
|
|
1466
|
+
clearAll: async (ctx) => {
|
|
1467
|
+
clearedScopes.push(ctx.scopes);
|
|
1468
|
+
await sql`
|
|
1469
|
+
delete from ${sql.table('tracked_items')}
|
|
1470
|
+
where ${sql.ref('project_id')} in ${sql`(${sql.join(
|
|
1471
|
+
toScopeValueArray(ctx.scopes.project_id).map((value) =>
|
|
1472
|
+
sql.val(value)
|
|
1473
|
+
)
|
|
1474
|
+
)})`}
|
|
1475
|
+
`.execute(ctx.trx);
|
|
1476
|
+
},
|
|
1477
|
+
}),
|
|
1478
|
+
];
|
|
1479
|
+
|
|
1480
|
+
const options = {
|
|
1481
|
+
clientId: 'client-1',
|
|
1482
|
+
subscriptions: [
|
|
1483
|
+
{
|
|
1484
|
+
id: 'tracked-sub',
|
|
1485
|
+
table: 'tracked_items',
|
|
1486
|
+
scopes: { project_id: ['p1', 'p2'] },
|
|
1487
|
+
},
|
|
1488
|
+
],
|
|
1489
|
+
stateId: 'default',
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
const firstState = await buildPullRequest(db, options);
|
|
1493
|
+
await applyPullResponse(db, transport, handlers, options, firstState, {
|
|
1494
|
+
ok: true,
|
|
1495
|
+
subscriptions: [
|
|
1496
|
+
{
|
|
1497
|
+
id: 'tracked-sub',
|
|
1498
|
+
status: 'active',
|
|
1499
|
+
scopes: { project_id: ['p1', 'p2'] },
|
|
1500
|
+
bootstrap: true,
|
|
1501
|
+
bootstrapState: null,
|
|
1502
|
+
nextCursor: 1,
|
|
1503
|
+
commits: [],
|
|
1504
|
+
snapshots: [
|
|
1505
|
+
{
|
|
1506
|
+
table: 'tracked_items',
|
|
1507
|
+
rows: [
|
|
1508
|
+
{ id: 'p1-a', project_id: 'p1', name: 'A' },
|
|
1509
|
+
{ id: 'p2-a', project_id: 'p2', name: 'B' },
|
|
1510
|
+
],
|
|
1511
|
+
isFirstPage: true,
|
|
1512
|
+
isLastPage: true,
|
|
1513
|
+
},
|
|
1514
|
+
],
|
|
1515
|
+
},
|
|
1516
|
+
],
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
const secondState = await buildPullRequest(db, options);
|
|
1520
|
+
await applyPullResponse(db, transport, handlers, options, secondState, {
|
|
1521
|
+
ok: true,
|
|
1522
|
+
subscriptions: [
|
|
1523
|
+
{
|
|
1524
|
+
id: 'tracked-sub',
|
|
1525
|
+
status: 'active',
|
|
1526
|
+
scopes: { project_id: 'p2' },
|
|
1527
|
+
bootstrap: true,
|
|
1528
|
+
bootstrapState: null,
|
|
1529
|
+
nextCursor: 2,
|
|
1530
|
+
commits: [],
|
|
1531
|
+
snapshots: [
|
|
1532
|
+
{
|
|
1533
|
+
table: 'tracked_items',
|
|
1534
|
+
rows: [{ id: 'p2-a', project_id: 'p2', name: 'B' }],
|
|
1535
|
+
isFirstPage: true,
|
|
1536
|
+
isLastPage: true,
|
|
1537
|
+
},
|
|
1538
|
+
],
|
|
1539
|
+
},
|
|
1540
|
+
],
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1543
|
+
expect(clearedScopes).toEqual([{ project_id: 'p1' }]);
|
|
1544
|
+
});
|
|
1051
1545
|
});
|