@vue-skuilder/db 0.2.17 → 0.2.18

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.
Files changed (38) hide show
  1. package/dist/{SyncStrategy-CyATpyLQ.d.cts → SyncStrategy-BJMZq3WO.d.cts} +17 -0
  2. package/dist/{SyncStrategy-CyATpyLQ.d.ts → SyncStrategy-BJMZq3WO.d.ts} +17 -0
  3. package/dist/{contentSource-Brz42x7n.d.cts → contentSource-CBqZCoGU.d.cts} +85 -1
  4. package/dist/{contentSource-B1p-vdz7.d.ts → contentSource-CudEz5Tm.d.ts} +85 -1
  5. package/dist/core/index.d.cts +3 -3
  6. package/dist/core/index.d.ts +3 -3
  7. package/dist/core/index.js +146 -1
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/core/index.mjs +145 -1
  10. package/dist/core/index.mjs.map +1 -1
  11. package/dist/{dataLayerProvider-CpwpT1rM.d.cts → dataLayerProvider-BBA8tJNx.d.cts} +1 -1
  12. package/dist/{dataLayerProvider-BWayUIoK.d.ts → dataLayerProvider-C9WgkBzR.d.ts} +1 -1
  13. package/dist/impl/couch/index.d.cts +16 -3
  14. package/dist/impl/couch/index.d.ts +16 -3
  15. package/dist/impl/couch/index.js +178 -3
  16. package/dist/impl/couch/index.js.map +1 -1
  17. package/dist/impl/couch/index.mjs +178 -3
  18. package/dist/impl/couch/index.mjs.map +1 -1
  19. package/dist/impl/static/index.d.cts +3 -3
  20. package/dist/impl/static/index.d.ts +3 -3
  21. package/dist/impl/static/index.js +144 -1
  22. package/dist/impl/static/index.js.map +1 -1
  23. package/dist/impl/static/index.mjs +144 -1
  24. package/dist/impl/static/index.mjs.map +1 -1
  25. package/dist/index.d.cts +3 -3
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.js +180 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +179 -3
  30. package/dist/index.mjs.map +1 -1
  31. package/package.json +4 -3
  32. package/src/core/index.ts +1 -0
  33. package/src/core/interfaces/userDB.ts +11 -0
  34. package/src/core/types/hydration.ts +78 -0
  35. package/src/impl/common/BaseUserDB.ts +202 -2
  36. package/src/impl/common/SyncStrategy.ts +19 -0
  37. package/src/impl/couch/CouchDBSyncStrategy.ts +54 -4
  38. package/tests/impl/hydration.test.ts +281 -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-Brz42x7n.cjs';
2
- export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 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, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z 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, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-Brz42x7n.cjs';
3
- import { D as DataLayerProvider } from './dataLayerProvider-CpwpT1rM.cjs';
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-CBqZCoGU.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-CBqZCoGU.cjs';
3
+ import { D as DataLayerProvider } from './dataLayerProvider-BBA8tJNx.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-B1p-vdz7.js';
2
- export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 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, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z 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, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-B1p-vdz7.js';
3
- import { D as DataLayerProvider } from './dataLayerProvider-BWayUIoK.js';
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-CudEz5Tm.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-CudEz5Tm.js';
3
+ import { D as DataLayerProvider } from './dataLayerProvider-C9WgkBzR.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
@@ -7357,6 +7357,8 @@ var init_CouchDBSyncStrategy = __esm({
7357
7357
  CouchDBSyncStrategy = class {
7358
7358
  syncHandle;
7359
7359
  // Handle to cancel sync if needed
7360
+ /** In-flight one-shot hydration pull, so a timed-out pull can be abandoned. */
7361
+ hydrationHandle;
7360
7362
  setupRemoteDB(username) {
7361
7363
  if (username === GuestUsername || username.startsWith(GuestUsername)) {
7362
7364
  return getLocalUserDB(username);
@@ -7371,8 +7373,26 @@ var init_CouchDBSyncStrategy = __esm({
7371
7373
  return this.getUserDB(username);
7372
7374
  }
7373
7375
  }
7376
+ /**
7377
+ * One-shot remote → local pull. Resolves once the local mirror is caught up.
7378
+ *
7379
+ * Pull only: pushing here would upload whatever the local DB happens to hold
7380
+ * before we know what the remote already has, which is the conflict-leaf
7381
+ * problem hydration exists to avoid. Local changes go up when startSync()
7382
+ * takes over.
7383
+ */
7384
+ async hydrate(localDB, remoteDB) {
7385
+ const replication = pouchdb_setup_default.replicate(remoteDB, localDB, {});
7386
+ this.hydrationHandle = replication;
7387
+ try {
7388
+ const info = await replication;
7389
+ return { docsWritten: info.docs_written };
7390
+ } finally {
7391
+ this.hydrationHandle = void 0;
7392
+ }
7393
+ }
7374
7394
  startSync(localDB, remoteDB) {
7375
- if (localDB !== remoteDB) {
7395
+ if (localDB.name !== remoteDB.name) {
7376
7396
  this.syncHandle = pouchdb_setup_default.sync(localDB, remoteDB, {
7377
7397
  live: true,
7378
7398
  retry: true
@@ -7380,8 +7400,20 @@ var init_CouchDBSyncStrategy = __esm({
7380
7400
  }
7381
7401
  }
7382
7402
  stopSync() {
7403
+ if (this.hydrationHandle) {
7404
+ try {
7405
+ this.hydrationHandle.cancel();
7406
+ } catch (e) {
7407
+ logger.warn(`Failed to cancel hydration pull (continuing anyway): ${e}`);
7408
+ }
7409
+ this.hydrationHandle = void 0;
7410
+ }
7383
7411
  if (this.syncHandle) {
7384
- this.syncHandle.cancel();
7412
+ try {
7413
+ this.syncHandle.cancel();
7414
+ } catch (e) {
7415
+ logger.warn(`Failed to cancel user sync (continuing anyway): ${e}`);
7416
+ }
7385
7417
  this.syncHandle = void 0;
7386
7418
  }
7387
7419
  }
@@ -7650,6 +7682,19 @@ var init_couch = __esm({
7650
7682
  });
7651
7683
 
7652
7684
  // src/impl/common/BaseUserDB.ts
7685
+ function withTimeout(p, ms, label) {
7686
+ let timer;
7687
+ return Promise.race([
7688
+ p,
7689
+ new Promise((_resolve, reject) => {
7690
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
7691
+ })
7692
+ ]).finally(() => {
7693
+ if (timer !== void 0) {
7694
+ clearTimeout(timer);
7695
+ }
7696
+ });
7697
+ }
7653
7698
  function accomodateGuest() {
7654
7699
  logger.log("[funnel] accomodateGuest() called");
7655
7700
  if (typeof localStorage === "undefined") {
@@ -7823,7 +7868,7 @@ async function dropUserFromClassroom(user, classID) {
7823
7868
  async function getUserClassrooms(user) {
7824
7869
  return getOrCreateClassroomRegistrationsDoc(user);
7825
7870
  }
7826
- var import_common12, import_moment6, log4, BaseUser, userCoursesDoc, userClassroomsDoc;
7871
+ var import_common12, import_moment6, log4, HYDRATION_TIMEOUT_MS, BaseUser, userCoursesDoc, userClassroomsDoc;
7827
7872
  var init_BaseUserDB = __esm({
7828
7873
  "src/impl/common/BaseUserDB.ts"() {
7829
7874
  "use strict";
@@ -7840,6 +7885,7 @@ var init_BaseUserDB = __esm({
7840
7885
  log4 = (s) => {
7841
7886
  logger.info(s);
7842
7887
  };
7888
+ HYDRATION_TIMEOUT_MS = 15e3;
7843
7889
  BaseUser = class _BaseUser {
7844
7890
  static _instance;
7845
7891
  static _initialized = false;
@@ -7868,6 +7914,29 @@ var init_BaseUserDB = __esm({
7868
7914
  writeDB;
7869
7915
  // Database to use for write operations (local-first approach)
7870
7916
  updateQueue;
7917
+ _hydration = { state: "not-required" };
7918
+ /**
7919
+ * How far the local mirror can be trusted. See {@link UserHydrationStatus}.
7920
+ *
7921
+ * Consumers that would otherwise read a 404 as "new user" — onboarding
7922
+ * gates, first-run experiences, progress dashboards — should check for
7923
+ * `failed` and present a retry affordance rather than an empty state.
7924
+ */
7925
+ hydrationStatus() {
7926
+ return { ...this._hydration };
7927
+ }
7928
+ /**
7929
+ * Whether a missing document may be treated as genuinely absent, allowing
7930
+ * callers to create it with defaults.
7931
+ *
7932
+ * False only when hydration failed or is still running: a 404 then may just
7933
+ * mean "not pulled down yet", and materializing a default would both lie to
7934
+ * the user and, once live sync starts, push a rev-1 document that loses to
7935
+ * the real one — silently discarding it.
7936
+ */
7937
+ canMaterializeDefaults() {
7938
+ return this._hydration.state !== "failed" && this._hydration.state !== "hydrating";
7939
+ }
7871
7940
  async createAccount(username, password) {
7872
7941
  if (!this.syncStrategy.canCreateAccount()) {
7873
7942
  throw new Error("Account creation not supported by current sync strategy");
@@ -7985,6 +8054,11 @@ Currently logged-in as ${this._username}.`
7985
8054
  } catch (e) {
7986
8055
  const err = e;
7987
8056
  if (err.status === 404) {
8057
+ if (!this.canMaterializeDefaults()) {
8058
+ throw new Error(
8059
+ `Cannot read course registrations for ${this._username}: the local mirror is unavailable (hydration state '${this._hydration.state}'). Refusing to create an empty registration doc \u2014 that would report an existing user as unregistered, and then lose to their real document once sync resumes.`
8060
+ );
8061
+ }
7988
8062
  await this.localDB.put({
7989
8063
  _id: _BaseUser.DOC_IDS.COURSE_REGISTRATIONS,
7990
8064
  courses: [],
@@ -8227,6 +8301,11 @@ Currently logged-in as ${this._username}.`
8227
8301
  } catch (e) {
8228
8302
  const err = e;
8229
8303
  if (err.name && err.name === "not_found") {
8304
+ if (!this.canMaterializeDefaults()) {
8305
+ throw new Error(
8306
+ `Cannot read config for ${this._username}: the local mirror is unavailable (hydration state '${this._hydration.state}'). Refusing to write a default config over what may be an existing one.`
8307
+ );
8308
+ }
8230
8309
  await this.localDB.put(defaultConfig);
8231
8310
  return this.getConfig();
8232
8311
  } else {
@@ -8300,7 +8379,9 @@ Currently logged-in as ${this._username}.`
8300
8379
  _BaseUser._initialized = true;
8301
8380
  return;
8302
8381
  }
8382
+ this.syncStrategy.stopSync?.();
8303
8383
  this.setDBandQ();
8384
+ await this.hydrateLocalMirror();
8304
8385
  this.syncStrategy.startSync(this.localDB, this.remoteDB);
8305
8386
  this.applyDesignDocs().catch((error) => {
8306
8387
  log4(`Error in applyDesignDocs background task: ${error}`);
@@ -8316,6 +8397,85 @@ Currently logged-in as ${this._username}.`
8316
8397
  });
8317
8398
  _BaseUser._initialized = true;
8318
8399
  }
8400
+ /**
8401
+ * Pull this account's documents into the local mirror, if that hasn't
8402
+ * happened on this device before.
8403
+ *
8404
+ * Runs between setDBandQ() and startSync() — after the handles exist, before
8405
+ * anything can observe an empty local DB or replicate one upward.
8406
+ *
8407
+ * Never throws: a failure is recorded as `failed` state and reported through
8408
+ * hydrationStatus(), because throwing here would abort init() and leave
8409
+ * BaseUser._initialized false forever (BaseUser.instance() polls it with no
8410
+ * ceiling). Callers decide what a failure means for them.
8411
+ */
8412
+ async hydrateLocalMirror() {
8413
+ if (this.localDB.name === this.remoteDB.name || !this.syncStrategy.hydrate) {
8414
+ this._hydration = { state: "not-required" };
8415
+ return;
8416
+ }
8417
+ if (await this.hasHydrationMarker()) {
8418
+ this._hydration = { state: "stale" };
8419
+ return;
8420
+ }
8421
+ this._hydration = { state: "hydrating" };
8422
+ const start = Date.now();
8423
+ try {
8424
+ const { docsWritten } = await withTimeout(
8425
+ this.syncStrategy.hydrate(this.localDB, this.remoteDB),
8426
+ HYDRATION_TIMEOUT_MS,
8427
+ `Hydration of local mirror for ${this._username}`
8428
+ );
8429
+ await this.writeHydrationMarker();
8430
+ this._hydration = {
8431
+ state: "hydrated",
8432
+ docsWritten,
8433
+ durationMs: Date.now() - start
8434
+ };
8435
+ log4(
8436
+ `Hydrated local mirror for ${this._username}: ${docsWritten} docs in ${this._hydration.durationMs}ms`
8437
+ );
8438
+ } catch (e) {
8439
+ this.syncStrategy.stopSync?.();
8440
+ this._hydration = {
8441
+ state: "failed",
8442
+ durationMs: Date.now() - start,
8443
+ error: e instanceof Error ? e.message : String(e)
8444
+ };
8445
+ logger.error(`Failed to hydrate local mirror for ${this._username}:`, e);
8446
+ }
8447
+ }
8448
+ /**
8449
+ * Has a full pull completed on this device for the CURRENT account?
8450
+ *
8451
+ * The marker is a `_local/` document, which never replicates — so its
8452
+ * presence describes this device's mirror and cannot arrive from elsewhere.
8453
+ */
8454
+ async hasHydrationMarker() {
8455
+ try {
8456
+ const marker = await this.localDB.get(HYDRATION_MARKER_ID);
8457
+ return marker.username === this._username;
8458
+ } catch {
8459
+ return false;
8460
+ }
8461
+ }
8462
+ async writeHydrationMarker() {
8463
+ try {
8464
+ let existingRev;
8465
+ try {
8466
+ existingRev = (await this.localDB.get(HYDRATION_MARKER_ID))._rev;
8467
+ } catch {
8468
+ }
8469
+ await this.localDB.put({
8470
+ _id: HYDRATION_MARKER_ID,
8471
+ _rev: existingRev,
8472
+ username: this._username,
8473
+ hydratedAt: (/* @__PURE__ */ new Date()).toISOString()
8474
+ });
8475
+ } catch (e) {
8476
+ logger.warn(`Could not write hydration marker for ${this._username}: ${e}`);
8477
+ }
8478
+ }
8319
8479
  static designDocs = [
8320
8480
  {
8321
8481
  _id: "_design/reviewCards",
@@ -8652,6 +8812,11 @@ Currently logged-in as ${this._username}.`
8652
8812
  } catch (e) {
8653
8813
  const err = e;
8654
8814
  if (err.status === 404) {
8815
+ if (!this.canMaterializeDefaults()) {
8816
+ throw new Error(
8817
+ `Cannot read strategy state '${strategyKey}' for ${this._username}: the local mirror is unavailable (hydration state '${this._hydration.state}').`
8818
+ );
8819
+ }
8655
8820
  return null;
8656
8821
  }
8657
8822
  throw e;
@@ -10172,6 +10337,15 @@ var init_userOutcome = __esm({
10172
10337
  }
10173
10338
  });
10174
10339
 
10340
+ // src/core/types/hydration.ts
10341
+ var HYDRATION_MARKER_ID;
10342
+ var init_hydration = __esm({
10343
+ "src/core/types/hydration.ts"() {
10344
+ "use strict";
10345
+ HYDRATION_MARKER_ID = "_local/hydration";
10346
+ }
10347
+ });
10348
+
10175
10349
  // src/core/bulkImport/cardProcessor.ts
10176
10350
  async function importParsedCards(parsedCards, courseDB, config) {
10177
10351
  const results = [];
@@ -10652,6 +10826,7 @@ var init_core = __esm({
10652
10826
  init_user();
10653
10827
  init_strategyState();
10654
10828
  init_userOutcome();
10829
+ init_hydration();
10655
10830
  init_Loggable();
10656
10831
  init_util();
10657
10832
  init_navigators();
@@ -10674,6 +10849,7 @@ __export(index_exports, {
10674
10849
  ENV: () => ENV,
10675
10850
  FileSystemError: () => FileSystemError,
10676
10851
  GuestUsername: () => GuestUsername,
10852
+ HYDRATION_MARKER_ID: () => HYDRATION_MARKER_ID,
10677
10853
  Loggable: () => Loggable,
10678
10854
  NOT_SET: () => NOT_SET,
10679
10855
  NavigatorRole: () => NavigatorRole,
@@ -15454,6 +15630,7 @@ init_userDBHelpers();
15454
15630
  ENV,
15455
15631
  FileSystemError,
15456
15632
  GuestUsername,
15633
+ HYDRATION_MARKER_ID,
15457
15634
  Loggable,
15458
15635
  NOT_SET,
15459
15636
  NavigatorRole,