@vue-skuilder/db 0.2.18 → 0.2.20
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/{SyncStrategy-BJMZq3WO.d.cts → SyncStrategy-CCU1H81I.d.cts} +8 -2
- package/dist/{SyncStrategy-BJMZq3WO.d.ts → SyncStrategy-CCU1H81I.d.ts} +8 -2
- package/dist/{contentSource-CudEz5Tm.d.ts → contentSource-BDRX_YYJ.d.ts} +13 -1
- package/dist/{contentSource-CBqZCoGU.d.cts → contentSource-D-LQYFyx.d.cts} +13 -1
- package/dist/core/index.d.cts +3 -3
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +38 -7
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +38 -7
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BBA8tJNx.d.cts → dataLayerProvider-Dd2UcCif.d.cts} +1 -1
- package/dist/{dataLayerProvider-C9WgkBzR.d.ts → dataLayerProvider-Dpshuql4.d.ts} +1 -1
- package/dist/impl/couch/index.d.cts +20 -4
- package/dist/impl/couch/index.d.ts +20 -4
- package/dist/impl/couch/index.js +80 -11
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +80 -11
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +3 -3
- package/dist/impl/static/index.d.ts +3 -3
- package/dist/impl/static/index.js +38 -7
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +38 -7
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +80 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/core/interfaces/userDB.ts +14 -1
- package/src/impl/common/BaseUserDB.ts +56 -8
- package/src/impl/common/SyncStrategy.ts +17 -4
- package/src/impl/couch/CouchDBSyncStrategy.ts +52 -4
- package/tests/impl/hydration.test.ts +49 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-
|
|
2
|
-
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a8 as CardGenerator, aa as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ah as DocumentUpdater, a9 as GeneratorContext, P as HYDRATION_MARKER_ID, Q as HydrationMarker, ab as LearnableWeight, V as NavigatorConstructor, a4 as NavigatorRole, a5 as NavigatorRoles, a3 as Navigators, ac as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, a1 as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, N as UserHydrationState, O as UserHydrationStatus, M as UserOutcomeRecord, B as UsrCrsDataInterface, ad as computeDeviation, af as computeEffectiveWeight, ae as computeSpread, ag as createOrchestrationContext, a2 as getCardOrigin, Y as getRegisteredNavigator, $ as getRegisteredNavigatorNames, _ as getRegisteredNavigatorRole, r as getStudySource, Z as hasRegisteredNavigator, a0 as initializeNavigatorRegistry, a7 as isFilter, a6 as isGenerator, p as isReview, ai as newInterval, X as registerNavigator } from './contentSource-
|
|
3
|
-
import { D as DataLayerProvider } from './dataLayerProvider-
|
|
1
|
+
import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-D-LQYFyx.cjs';
|
|
2
|
+
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a8 as CardGenerator, aa as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ah as DocumentUpdater, a9 as GeneratorContext, P as HYDRATION_MARKER_ID, Q as HydrationMarker, ab as LearnableWeight, V as NavigatorConstructor, a4 as NavigatorRole, a5 as NavigatorRoles, a3 as Navigators, ac as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, a1 as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, N as UserHydrationState, O as UserHydrationStatus, M as UserOutcomeRecord, B as UsrCrsDataInterface, ad as computeDeviation, af as computeEffectiveWeight, ae as computeSpread, ag as createOrchestrationContext, a2 as getCardOrigin, Y as getRegisteredNavigator, $ as getRegisteredNavigatorNames, _ as getRegisteredNavigatorRole, r as getStudySource, Z as hasRegisteredNavigator, a0 as initializeNavigatorRegistry, a7 as isFilter, a6 as isGenerator, p as isReview, ai as newInterval, X as registerNavigator } from './contentSource-D-LQYFyx.cjs';
|
|
3
|
+
import { D as DataLayerProvider } from './dataLayerProvider-Dd2UcCif.cjs';
|
|
4
4
|
import { C as CardHistory, c as CardRecord, d as QuestionRecord } from './types-legacy-4tlwHnXo.cjs';
|
|
5
5
|
export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from './types-legacy-4tlwHnXo.cjs';
|
|
6
6
|
import { SrsBacklogDebug, StrategyPressureDebug, Loggable } from './core/index.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-
|
|
2
|
-
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a8 as CardGenerator, aa as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ah as DocumentUpdater, a9 as GeneratorContext, P as HYDRATION_MARKER_ID, Q as HydrationMarker, ab as LearnableWeight, V as NavigatorConstructor, a4 as NavigatorRole, a5 as NavigatorRoles, a3 as Navigators, ac as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, a1 as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, N as UserHydrationState, O as UserHydrationStatus, M as UserOutcomeRecord, B as UsrCrsDataInterface, ad as computeDeviation, af as computeEffectiveWeight, ae as computeSpread, ag as createOrchestrationContext, a2 as getCardOrigin, Y as getRegisteredNavigator, $ as getRegisteredNavigatorNames, _ as getRegisteredNavigatorRole, r as getStudySource, Z as hasRegisteredNavigator, a0 as initializeNavigatorRegistry, a7 as isFilter, a6 as isGenerator, p as isReview, ai as newInterval, X as registerNavigator } from './contentSource-
|
|
3
|
-
import { D as DataLayerProvider } from './dataLayerProvider-
|
|
1
|
+
import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-BDRX_YYJ.js';
|
|
2
|
+
export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a8 as CardGenerator, aa as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ah as DocumentUpdater, a9 as GeneratorContext, P as HYDRATION_MARKER_ID, Q as HydrationMarker, ab as LearnableWeight, V as NavigatorConstructor, a4 as NavigatorRole, a5 as NavigatorRoles, a3 as Navigators, ac as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, a1 as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, N as UserHydrationState, O as UserHydrationStatus, M as UserOutcomeRecord, B as UsrCrsDataInterface, ad as computeDeviation, af as computeEffectiveWeight, ae as computeSpread, ag as createOrchestrationContext, a2 as getCardOrigin, Y as getRegisteredNavigator, $ as getRegisteredNavigatorNames, _ as getRegisteredNavigatorRole, r as getStudySource, Z as hasRegisteredNavigator, a0 as initializeNavigatorRegistry, a7 as isFilter, a6 as isGenerator, p as isReview, ai as newInterval, X as registerNavigator } from './contentSource-BDRX_YYJ.js';
|
|
3
|
+
import { D as DataLayerProvider } from './dataLayerProvider-Dpshuql4.js';
|
|
4
4
|
import { C as CardHistory, c as CardRecord, d as QuestionRecord } from './types-legacy-4tlwHnXo.js';
|
|
5
5
|
export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from './types-legacy-4tlwHnXo.js';
|
|
6
6
|
import { SrsBacklogDebug, StrategyPressureDebug, Loggable } from './core/index.js';
|
package/dist/index.js
CHANGED
|
@@ -7380,22 +7380,60 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
7380
7380
|
* before we know what the remote already has, which is the conflict-leaf
|
|
7381
7381
|
* problem hydration exists to avoid. Local changes go up when startSync()
|
|
7382
7382
|
* takes over.
|
|
7383
|
+
*
|
|
7384
|
+
* Into an EMPTY local DB this skips deleted documents, because a mirror that
|
|
7385
|
+
* never held a document does not need to be told it was removed. That
|
|
7386
|
+
* matters more than it sounds: scheduled reviews (`card_review_*`) are
|
|
7387
|
+
* created and deleted once per review completed, so the changes feed is
|
|
7388
|
+
* dominated by tombstones and grows without bound, while the durable record
|
|
7389
|
+
* lives in card history. One real account measured 385 live documents behind
|
|
7390
|
+
* 8,370 deletions — an unfiltered pull moved 9,520 documents and took ~20s,
|
|
7391
|
+
* blowing the hydration timeout on a phone; filtered, the same pull moves
|
|
7392
|
+
* 404 and takes ~2s, reaching a byte-identical local state.
|
|
7393
|
+
*
|
|
7394
|
+
* The filter runs server-side, so `last_seq` still reports the source's true
|
|
7395
|
+
* sequence. Returning it lets startSync() begin the live pull from there
|
|
7396
|
+
* instead of re-walking the same history in the background — without that,
|
|
7397
|
+
* the cost is merely deferred, not removed.
|
|
7383
7398
|
*/
|
|
7384
7399
|
async hydrate(localDB, remoteDB) {
|
|
7385
|
-
const
|
|
7400
|
+
const pristine = (await localDB.info()).doc_count === 0;
|
|
7401
|
+
const replication = pouchdb_setup_default.replicate(
|
|
7402
|
+
remoteDB,
|
|
7403
|
+
localDB,
|
|
7404
|
+
pristine ? { selector: { _deleted: { $exists: false } } } : {}
|
|
7405
|
+
);
|
|
7386
7406
|
this.hydrationHandle = replication;
|
|
7387
7407
|
try {
|
|
7388
7408
|
const info = await replication;
|
|
7389
|
-
return {
|
|
7409
|
+
return {
|
|
7410
|
+
docsWritten: info.docs_written,
|
|
7411
|
+
// Only a pristine pull is a trustworthy starting point for the live
|
|
7412
|
+
// sync; otherwise leave startSync() to its own checkpoint.
|
|
7413
|
+
lastSeq: pristine ? info.last_seq : void 0
|
|
7414
|
+
};
|
|
7390
7415
|
} finally {
|
|
7391
7416
|
this.hydrationHandle = void 0;
|
|
7392
7417
|
}
|
|
7393
7418
|
}
|
|
7394
|
-
startSync(localDB, remoteDB) {
|
|
7419
|
+
startSync(localDB, remoteDB, since) {
|
|
7395
7420
|
if (localDB.name !== remoteDB.name) {
|
|
7396
7421
|
this.syncHandle = pouchdb_setup_default.sync(localDB, remoteDB, {
|
|
7397
7422
|
live: true,
|
|
7398
|
-
retry: true
|
|
7423
|
+
retry: true,
|
|
7424
|
+
// `since` applies to the pull only, and only when hydrate() just
|
|
7425
|
+
// established that local matches remote at that sequence. Without it,
|
|
7426
|
+
// a filtered hydration leaves the pull with no usable checkpoint (the
|
|
7427
|
+
// filter changes the replication id), so it restarts from zero and
|
|
7428
|
+
// re-walks in the background exactly the tombstone history hydration
|
|
7429
|
+
// just skipped — same work, now competing with the study session.
|
|
7430
|
+
//
|
|
7431
|
+
// Deliberately NOT persisted across launches: on later boots hydration
|
|
7432
|
+
// is skipped and the pull's own checkpoint is the correct resume
|
|
7433
|
+
// point, so a stored sequence could only be stale. If the app dies
|
|
7434
|
+
// before that first checkpoint is written, the next launch simply
|
|
7435
|
+
// walks the full feed once.
|
|
7436
|
+
...since !== void 0 ? { pull: { since } } : {}
|
|
7399
7437
|
});
|
|
7400
7438
|
}
|
|
7401
7439
|
}
|
|
@@ -7915,16 +7953,44 @@ var init_BaseUserDB = __esm({
|
|
|
7915
7953
|
// Database to use for write operations (local-first approach)
|
|
7916
7954
|
updateQueue;
|
|
7917
7955
|
_hydration = { state: "not-required" };
|
|
7956
|
+
/** In-flight hydration, so concurrent callers can wait for a real answer. */
|
|
7957
|
+
_hydrationPromise = null;
|
|
7958
|
+
/**
|
|
7959
|
+
* Sequence the local mirror was filled to by a hydration that succeeded in
|
|
7960
|
+
* THIS init(), handed to startSync() so the live pull can skip history it
|
|
7961
|
+
* would otherwise re-walk. Undefined whenever there is no such guarantee —
|
|
7962
|
+
* hydration skipped, not required, or failed. Never persisted; see the
|
|
7963
|
+
* `since` note in CouchDBSyncStrategy.startSync().
|
|
7964
|
+
*/
|
|
7965
|
+
_hydratedSeq;
|
|
7918
7966
|
/**
|
|
7919
|
-
* How far the local mirror can be trusted. See
|
|
7967
|
+
* How far the local mirror can be trusted, RIGHT NOW. See
|
|
7968
|
+
* {@link UserHydrationStatus}.
|
|
7920
7969
|
*
|
|
7921
|
-
*
|
|
7922
|
-
*
|
|
7923
|
-
*
|
|
7970
|
+
* This is a snapshot, and during login it can legitimately read
|
|
7971
|
+
* `hydrating` — prefer awaitHydration() anywhere a decision hangs on the
|
|
7972
|
+
* answer.
|
|
7924
7973
|
*/
|
|
7925
7974
|
hydrationStatus() {
|
|
7926
7975
|
return { ...this._hydration };
|
|
7927
7976
|
}
|
|
7977
|
+
/**
|
|
7978
|
+
* The hydration status once it has settled — never `hydrating`.
|
|
7979
|
+
*
|
|
7980
|
+
* Resolves immediately unless a pull is in flight. Exists for callers that
|
|
7981
|
+
* must decide something (a route guard, a first-run branch) and would
|
|
7982
|
+
* otherwise sample `hydrating` and guess. Since init() is awaited by both
|
|
7983
|
+
* app startup and login(), that only happens when something navigates
|
|
7984
|
+
* concurrently with a login still in progress — a window that stretches to
|
|
7985
|
+
* HYDRATION_TIMEOUT_MS on a slow connection.
|
|
7986
|
+
*/
|
|
7987
|
+
async awaitHydration() {
|
|
7988
|
+
try {
|
|
7989
|
+
await this._hydrationPromise;
|
|
7990
|
+
} catch {
|
|
7991
|
+
}
|
|
7992
|
+
return this.hydrationStatus();
|
|
7993
|
+
}
|
|
7928
7994
|
/**
|
|
7929
7995
|
* Whether a missing document may be treated as genuinely absent, allowing
|
|
7930
7996
|
* callers to create it with defaults.
|
|
@@ -8381,8 +8447,9 @@ Currently logged-in as ${this._username}.`
|
|
|
8381
8447
|
}
|
|
8382
8448
|
this.syncStrategy.stopSync?.();
|
|
8383
8449
|
this.setDBandQ();
|
|
8384
|
-
|
|
8385
|
-
|
|
8450
|
+
this._hydrationPromise = this.hydrateLocalMirror();
|
|
8451
|
+
await this._hydrationPromise;
|
|
8452
|
+
this.syncStrategy.startSync(this.localDB, this.remoteDB, this._hydratedSeq);
|
|
8386
8453
|
this.applyDesignDocs().catch((error) => {
|
|
8387
8454
|
log4(`Error in applyDesignDocs background task: ${error}`);
|
|
8388
8455
|
if (error && typeof error === "object") {
|
|
@@ -8410,6 +8477,7 @@ Currently logged-in as ${this._username}.`
|
|
|
8410
8477
|
* ceiling). Callers decide what a failure means for them.
|
|
8411
8478
|
*/
|
|
8412
8479
|
async hydrateLocalMirror() {
|
|
8480
|
+
this._hydratedSeq = void 0;
|
|
8413
8481
|
if (this.localDB.name === this.remoteDB.name || !this.syncStrategy.hydrate) {
|
|
8414
8482
|
this._hydration = { state: "not-required" };
|
|
8415
8483
|
return;
|
|
@@ -8421,11 +8489,12 @@ Currently logged-in as ${this._username}.`
|
|
|
8421
8489
|
this._hydration = { state: "hydrating" };
|
|
8422
8490
|
const start = Date.now();
|
|
8423
8491
|
try {
|
|
8424
|
-
const { docsWritten } = await withTimeout(
|
|
8492
|
+
const { docsWritten, lastSeq } = await withTimeout(
|
|
8425
8493
|
this.syncStrategy.hydrate(this.localDB, this.remoteDB),
|
|
8426
8494
|
HYDRATION_TIMEOUT_MS,
|
|
8427
8495
|
`Hydration of local mirror for ${this._username}`
|
|
8428
8496
|
);
|
|
8497
|
+
this._hydratedSeq = lastSeq;
|
|
8429
8498
|
await this.writeHydrationMarker();
|
|
8430
8499
|
this._hydration = {
|
|
8431
8500
|
state: "hydrated",
|