velocious 1.0.512 → 1.0.514
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/README.md +3 -2
- package/build/environment-handlers/node.js +24 -1
- package/build/src/environment-handlers/node.d.ts +12 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +21 -2
- package/build/src/sync/sync-api-client-types.d.ts +22 -0
- package/build/src/sync/sync-api-client-types.d.ts.map +1 -1
- package/build/src/sync/sync-api-client-types.js +1 -1
- package/build/src/sync/sync-api-client.d.ts +6 -11
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +21 -7
- package/build/src/sync/sync-client.d.ts +10 -2
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +22 -5
- package/build/src/sync/sync-model-change-feed-service.d.ts +34 -2
- package/build/src/sync/sync-model-change-feed-service.d.ts.map +1 -1
- package/build/src/sync/sync-model-change-feed-service.js +35 -8
- package/build/sync/sync-api-client-types.js +9 -0
- package/build/sync/sync-api-client.js +22 -6
- package/build/sync/sync-client.js +21 -4
- package/build/sync/sync-model-change-feed-service.js +37 -7
- package/package.json +1 -1
- package/src/environment-handlers/node.js +24 -1
- package/src/sync/sync-api-client-types.js +9 -0
- package/src/sync/sync-api-client.js +22 -6
- package/src/sync/sync-client.js +21 -4
- package/src/sync/sync-model-change-feed-service.js +37 -7
package/src/sync/sync-client.js
CHANGED
|
@@ -356,9 +356,11 @@ export default class SyncClient {
|
|
|
356
356
|
/**
|
|
357
357
|
* Declares (or re-activates) a sync scope from a model query and pulls it when online.
|
|
358
358
|
* @param {import("../database/query/model-class-query.js").default<?>} query - Query declaring the sync scope.
|
|
359
|
+
* @param {object} [options] - Sync options.
|
|
360
|
+
* @param {(progress: import("./sync-api-client-types.js").SyncPullProgress) => void} [options.onProgress] - Called per applied page of the pull this declaration triggers, so the initial import of a newly declared scope can drive a "syncedCount of total" progress bar. See `pull()`.
|
|
359
361
|
* @returns {Promise<{scope: import("./sync-client-types.js").SerializedSyncScope, pulled: import("./sync-api-client-types.js").SyncChangesResult | null}>} Declared scope and pull result (null while offline).
|
|
360
362
|
*/
|
|
361
|
-
async sync(query) {
|
|
363
|
+
async sync(query, {onProgress} = {}) {
|
|
362
364
|
const scope = serializedScopeFromQuery(query)
|
|
363
365
|
const scopeStore = this.scopeStore()
|
|
364
366
|
const scopeRow = await scopeStore.findOrCreateScope(scope)
|
|
@@ -370,7 +372,7 @@ export default class SyncClient {
|
|
|
370
372
|
if (legacyCursor) await scopeStore.saveCursor(scopeRow, legacyCursor)
|
|
371
373
|
}
|
|
372
374
|
|
|
373
|
-
return {pulled: await this.pull(), scope}
|
|
375
|
+
return {pulled: await this.pull({onProgress}), scope}
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
/**
|
|
@@ -384,9 +386,11 @@ export default class SyncClient {
|
|
|
384
386
|
|
|
385
387
|
/**
|
|
386
388
|
* Pulls changes for every active scope with per-scope cursors (single-flighted, online-gated).
|
|
389
|
+
* @param {object} [options] - Pull options.
|
|
390
|
+
* @param {(progress: import("./sync-api-client-types.js").SyncPullProgress) => void} [options.onProgress] - Called per applied page with cumulative `{pages, syncedCount, total}` across the pulled scopes, for rendering a "syncedCount of total" progress bar (e.g. a full-import screen). Optional; omitting it keeps the existing behavior.
|
|
387
391
|
* @returns {Promise<import("./sync-api-client-types.js").SyncChangesResult | null>} Combined pull result, or null while offline.
|
|
388
392
|
*/
|
|
389
|
-
async pull() {
|
|
393
|
+
async pull({onProgress} = {}) {
|
|
390
394
|
if (!(await this.isOnline())) return null
|
|
391
395
|
|
|
392
396
|
/** @type {import("./sync-api-client-types.js").SyncChangesResult | null} */
|
|
@@ -401,15 +405,27 @@ export default class SyncClient {
|
|
|
401
405
|
pages: 0,
|
|
402
406
|
resourceChanged: /** @type {Record<string, boolean>} */ ({}),
|
|
403
407
|
resourceCounts: /** @type {Record<string, number>} */ ({}),
|
|
404
|
-
syncedCount: 0
|
|
408
|
+
syncedCount: 0,
|
|
409
|
+
total: 0
|
|
405
410
|
}
|
|
406
411
|
|
|
407
412
|
for (const scopeRow of await scopeStore.activeScopes()) {
|
|
413
|
+
// Cumulate scope progress onto the counts of the scopes already pulled so a single
|
|
414
|
+
// scope's per-page progress reads exactly its own counts (base 0), and multi-scope
|
|
415
|
+
// pulls report a running cumulative total across every scope.
|
|
416
|
+
const basePages = result.pages
|
|
417
|
+
const baseSyncedCount = result.syncedCount
|
|
418
|
+
const baseTotal = result.total
|
|
408
419
|
const scopeResult = await SyncApiClient.pullChanges({
|
|
409
420
|
applySync,
|
|
410
421
|
authenticationToken,
|
|
411
422
|
batchSize: this.config.batchSize,
|
|
412
423
|
loadCursor: async () => await scopeStore.loadCursor(scopeRow),
|
|
424
|
+
onProgress: onProgress ? (progress) => onProgress({
|
|
425
|
+
pages: basePages + progress.pages,
|
|
426
|
+
syncedCount: baseSyncedCount + progress.syncedCount,
|
|
427
|
+
total: baseTotal + progress.total
|
|
428
|
+
}) : undefined,
|
|
413
429
|
postChanges: async (payload) => await this.config.postChanges({
|
|
414
430
|
...payload,
|
|
415
431
|
scope: {conditions: scopeRow.conditions, resourceType: scopeRow.resourceType}
|
|
@@ -420,6 +436,7 @@ export default class SyncClient {
|
|
|
420
436
|
result.changed ||= scopeResult.changed
|
|
421
437
|
result.pages += scopeResult.pages
|
|
422
438
|
result.syncedCount += scopeResult.syncedCount
|
|
439
|
+
result.total += scopeResult.total
|
|
423
440
|
|
|
424
441
|
for (const [resourceType, count] of Object.entries(scopeResult.resourceCounts)) {
|
|
425
442
|
result.resourceCounts[resourceType] = (result.resourceCounts[resourceType] || 0) + count
|
|
@@ -32,15 +32,19 @@ export default class SyncModelChangeFeedService {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Builds a stable change-feed page.
|
|
36
|
-
*
|
|
35
|
+
* Builds a stable change-feed page. The additive `total` is the scope's pending
|
|
36
|
+
* change count from the request cursor to the snapshot bound (a COUNT, not a
|
|
37
|
+
* materialized read), so a client can render a "synced of total" progress bar;
|
|
38
|
+
* older clients ignore it.
|
|
39
|
+
* @returns {Promise<{status: string, nextCursor: {id: string, serverSequence: number, updatedAt: string} | null, syncs: Array<Record<string, unknown>>, total: number, upToCursor: {id: string, serverSequence: number, updatedAt: string} | null}>} Change-feed page result.
|
|
37
40
|
*/
|
|
38
41
|
async changes() {
|
|
39
42
|
const limit = this.normalizedLimit(this.params.limit)
|
|
40
43
|
const upToCursor = await this.resolveUpToCursor()
|
|
41
44
|
|
|
42
|
-
if (!upToCursor) return {status: "success", nextCursor: null, syncs: [], upToCursor: null}
|
|
45
|
+
if (!upToCursor) return {status: "success", nextCursor: null, syncs: [], total: 0, upToCursor: null}
|
|
43
46
|
|
|
47
|
+
const total = await this.totalPendingChanges({upToCursor})
|
|
44
48
|
const query = this.pageQuery({limit, upToCursor})
|
|
45
49
|
const records = await query.toArray()
|
|
46
50
|
const nextCursor = records.length > 0 ? this.cursorForRecord(records[records.length - 1]) : upToCursor
|
|
@@ -49,6 +53,7 @@ export default class SyncModelChangeFeedService {
|
|
|
49
53
|
status: "success",
|
|
50
54
|
nextCursor,
|
|
51
55
|
syncs: records.map((record) => this.serializeRecord(record)),
|
|
56
|
+
total,
|
|
52
57
|
upToCursor
|
|
53
58
|
}
|
|
54
59
|
}
|
|
@@ -124,6 +129,34 @@ export default class SyncModelChangeFeedService {
|
|
|
124
129
|
* @returns {import("../database/query/model-class-query.js").default} Page query.
|
|
125
130
|
*/
|
|
126
131
|
pageQuery({limit, upToCursor}) {
|
|
132
|
+
const query = this.cursorFilteredQuery({upToCursor})
|
|
133
|
+
const driver = query.driver
|
|
134
|
+
const serverSequenceColumn = `${driver.quoteTable(this.modelClass.tableName())}.${driver.quoteColumn("server_sequence")}`
|
|
135
|
+
|
|
136
|
+
return query
|
|
137
|
+
.order(`${serverSequenceColumn} ASC`)
|
|
138
|
+
.limit(limit)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Counts the scope's pending change rows from the request cursor to the snapshot
|
|
143
|
+
* bound without materializing them, so the client can render a stable "of Y"
|
|
144
|
+
* progress denominator.
|
|
145
|
+
* @param {{upToCursor: {id: string, serverSequence: number, updatedAt: string}}} args - Count args.
|
|
146
|
+
* @returns {Promise<number>} Pending change count from the request cursor.
|
|
147
|
+
*/
|
|
148
|
+
async totalPendingChanges({upToCursor}) {
|
|
149
|
+
return await this.cursorFilteredQuery({upToCursor}).count()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Builds a scoped query with the snapshot upper bound and after-cursor filters
|
|
154
|
+
* applied, but without ordering or a page limit. Shared by the page read and the
|
|
155
|
+
* total-count so both count the same set of rows.
|
|
156
|
+
* @param {{upToCursor: {id: string, serverSequence: number, updatedAt: string}}} args - Filter args.
|
|
157
|
+
* @returns {import("../database/query/model-class-query.js").default} Cursor-filtered query.
|
|
158
|
+
*/
|
|
159
|
+
cursorFilteredQuery({upToCursor}) {
|
|
127
160
|
const query = this.scopedQuery()
|
|
128
161
|
const driver = query.driver
|
|
129
162
|
const table = driver.quoteTable(this.modelClass.tableName())
|
|
@@ -131,10 +164,7 @@ export default class SyncModelChangeFeedService {
|
|
|
131
164
|
const updatedAtColumn = `${table}.${driver.quoteColumn("updated_at")}`
|
|
132
165
|
const idColumn = `${table}.${driver.quoteColumn("id")}`
|
|
133
166
|
|
|
134
|
-
query
|
|
135
|
-
.where(`${serverSequenceColumn} <= ${driver.quote(upToCursor.serverSequence)}`)
|
|
136
|
-
.order(`${serverSequenceColumn} ASC`)
|
|
137
|
-
.limit(limit)
|
|
167
|
+
query.where(`${serverSequenceColumn} <= ${driver.quote(upToCursor.serverSequence)}`)
|
|
138
168
|
|
|
139
169
|
const afterServerSequence = this.optionalPositiveIntegerParam(this.params.afterServerSequence, "afterServerSequence")
|
|
140
170
|
|