jazz-tools 0.19.8 → 0.19.11

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 (154) hide show
  1. package/.turbo/turbo-build.log +56 -50
  2. package/CHANGELOG.md +30 -3
  3. package/dist/{chunk-2S3Z2CN6.js → chunk-HX5S6W5E.js} +372 -103
  4. package/dist/chunk-HX5S6W5E.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/inspector/account-switcher.d.ts +4 -0
  7. package/dist/inspector/account-switcher.d.ts.map +1 -0
  8. package/dist/inspector/chunk-C6BJPHBQ.js +4096 -0
  9. package/dist/inspector/chunk-C6BJPHBQ.js.map +1 -0
  10. package/dist/inspector/contexts/node.d.ts +19 -0
  11. package/dist/inspector/contexts/node.d.ts.map +1 -0
  12. package/dist/inspector/{custom-element-P76EIWEV.js → custom-element-GJVBPZES.js} +1011 -884
  13. package/dist/inspector/custom-element-GJVBPZES.js.map +1 -0
  14. package/dist/inspector/{viewer/new-app.d.ts → in-app.d.ts} +3 -3
  15. package/dist/inspector/in-app.d.ts.map +1 -0
  16. package/dist/inspector/index.d.ts +0 -11
  17. package/dist/inspector/index.d.ts.map +1 -1
  18. package/dist/inspector/index.js +56 -3910
  19. package/dist/inspector/index.js.map +1 -1
  20. package/dist/inspector/pages/home.d.ts +2 -0
  21. package/dist/inspector/pages/home.d.ts.map +1 -0
  22. package/dist/inspector/register-custom-element.js +1 -1
  23. package/dist/inspector/router/context.d.ts +12 -0
  24. package/dist/inspector/router/context.d.ts.map +1 -0
  25. package/dist/inspector/router/hash-router.d.ts +7 -0
  26. package/dist/inspector/router/hash-router.d.ts.map +1 -0
  27. package/dist/inspector/router/in-memory-router.d.ts +7 -0
  28. package/dist/inspector/router/in-memory-router.d.ts.map +1 -0
  29. package/dist/inspector/router/index.d.ts +5 -0
  30. package/dist/inspector/router/index.d.ts.map +1 -0
  31. package/dist/inspector/standalone.d.ts +6 -0
  32. package/dist/inspector/standalone.d.ts.map +1 -0
  33. package/dist/inspector/standalone.js +420 -0
  34. package/dist/inspector/standalone.js.map +1 -0
  35. package/dist/inspector/tests/router/hash-router.test.d.ts +2 -0
  36. package/dist/inspector/tests/router/hash-router.test.d.ts.map +1 -0
  37. package/dist/inspector/tests/router/in-memory-router.test.d.ts +2 -0
  38. package/dist/inspector/tests/router/in-memory-router.test.d.ts.map +1 -0
  39. package/dist/inspector/ui/modal.d.ts +1 -0
  40. package/dist/inspector/ui/modal.d.ts.map +1 -1
  41. package/dist/inspector/viewer/breadcrumbs.d.ts +1 -7
  42. package/dist/inspector/viewer/breadcrumbs.d.ts.map +1 -1
  43. package/dist/inspector/viewer/header.d.ts +7 -0
  44. package/dist/inspector/viewer/header.d.ts.map +1 -0
  45. package/dist/inspector/viewer/page-stack.d.ts +4 -13
  46. package/dist/inspector/viewer/page-stack.d.ts.map +1 -1
  47. package/dist/inspector/viewer/page.d.ts.map +1 -1
  48. package/dist/react/hooks.d.ts +1 -1
  49. package/dist/react/hooks.d.ts.map +1 -1
  50. package/dist/react/index.d.ts +1 -1
  51. package/dist/react/index.d.ts.map +1 -1
  52. package/dist/react/index.js +5 -1
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/react-core/hooks.d.ts +59 -0
  55. package/dist/react-core/hooks.d.ts.map +1 -1
  56. package/dist/react-core/index.js +124 -36
  57. package/dist/react-core/index.js.map +1 -1
  58. package/dist/react-core/tests/testUtils.d.ts +1 -0
  59. package/dist/react-core/tests/testUtils.d.ts.map +1 -1
  60. package/dist/react-core/tests/useSuspenseAccount.test.d.ts +2 -0
  61. package/dist/react-core/tests/useSuspenseAccount.test.d.ts.map +1 -0
  62. package/dist/react-core/tests/useSuspenseCoState.test.d.ts +2 -0
  63. package/dist/react-core/tests/useSuspenseCoState.test.d.ts.map +1 -0
  64. package/dist/react-core/use.d.ts +3 -0
  65. package/dist/react-core/use.d.ts.map +1 -0
  66. package/dist/react-native/index.js +5 -1
  67. package/dist/react-native/index.js.map +1 -1
  68. package/dist/react-native-core/crypto/RNCrypto.d.ts +2 -0
  69. package/dist/react-native-core/crypto/RNCrypto.d.ts.map +1 -0
  70. package/dist/react-native-core/crypto/RNCrypto.js +3 -0
  71. package/dist/react-native-core/crypto/RNCrypto.js.map +1 -0
  72. package/dist/react-native-core/hooks.d.ts +1 -1
  73. package/dist/react-native-core/hooks.d.ts.map +1 -1
  74. package/dist/react-native-core/index.js +5 -1
  75. package/dist/react-native-core/index.js.map +1 -1
  76. package/dist/react-native-core/platform.d.ts +2 -1
  77. package/dist/react-native-core/platform.d.ts.map +1 -1
  78. package/dist/testing.js +1 -1
  79. package/dist/testing.js.map +1 -1
  80. package/dist/tools/coValues/account.d.ts +7 -1
  81. package/dist/tools/coValues/account.d.ts.map +1 -1
  82. package/dist/tools/coValues/interfaces.d.ts +1 -1
  83. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  84. package/dist/tools/implementation/ContextManager.d.ts +3 -0
  85. package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
  86. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +8 -1
  87. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  88. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  89. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +8 -22
  90. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  91. package/dist/tools/subscribe/SubscriptionCache.d.ts +51 -0
  92. package/dist/tools/subscribe/SubscriptionCache.d.ts.map +1 -0
  93. package/dist/tools/subscribe/SubscriptionScope.d.ts +17 -1
  94. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  95. package/dist/tools/subscribe/utils.d.ts +9 -1
  96. package/dist/tools/subscribe/utils.d.ts.map +1 -1
  97. package/dist/tools/testing.d.ts +2 -2
  98. package/dist/tools/testing.d.ts.map +1 -1
  99. package/dist/tools/tests/SubscriptionCache.test.d.ts +2 -0
  100. package/dist/tools/tests/SubscriptionCache.test.d.ts.map +1 -0
  101. package/package.json +18 -6
  102. package/src/inspector/account-switcher.tsx +440 -0
  103. package/src/inspector/contexts/node.tsx +129 -0
  104. package/src/inspector/custom-element.tsx +2 -2
  105. package/src/inspector/in-app.tsx +61 -0
  106. package/src/inspector/index.tsx +2 -22
  107. package/src/inspector/pages/home.tsx +77 -0
  108. package/src/inspector/router/context.ts +21 -0
  109. package/src/inspector/router/hash-router.tsx +128 -0
  110. package/src/inspector/{viewer/use-page-path.ts → router/in-memory-router.tsx} +31 -29
  111. package/src/inspector/router/index.ts +4 -0
  112. package/src/inspector/standalone.tsx +60 -0
  113. package/src/inspector/tests/router/hash-router.test.tsx +847 -0
  114. package/src/inspector/tests/router/in-memory-router.test.tsx +724 -0
  115. package/src/inspector/ui/modal.tsx +5 -2
  116. package/src/inspector/viewer/breadcrumbs.tsx +5 -11
  117. package/src/inspector/viewer/header.tsx +67 -0
  118. package/src/inspector/viewer/page-stack.tsx +18 -26
  119. package/src/inspector/viewer/page.tsx +0 -1
  120. package/src/react/hooks.tsx +2 -0
  121. package/src/react/index.ts +1 -14
  122. package/src/react-core/hooks.ts +167 -18
  123. package/src/react-core/tests/createCoValueSubscriptionContext.test.tsx +18 -8
  124. package/src/react-core/tests/testUtils.tsx +67 -5
  125. package/src/react-core/tests/useCoState.test.ts +3 -7
  126. package/src/react-core/tests/useSubscriptionSelector.test.ts +3 -7
  127. package/src/react-core/tests/useSuspenseAccount.test.tsx +343 -0
  128. package/src/react-core/tests/useSuspenseCoState.test.tsx +1182 -0
  129. package/src/react-core/use.ts +46 -0
  130. package/src/react-native-core/crypto/RNCrypto.ts +1 -0
  131. package/src/react-native-core/hooks.tsx +2 -0
  132. package/src/react-native-core/platform.ts +2 -1
  133. package/src/tools/coValues/account.ts +13 -2
  134. package/src/tools/coValues/interfaces.ts +2 -3
  135. package/src/tools/implementation/ContextManager.ts +13 -0
  136. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +8 -1
  137. package/src/tools/subscribe/CoValueCoreSubscription.ts +71 -100
  138. package/src/tools/subscribe/SubscriptionCache.ts +272 -0
  139. package/src/tools/subscribe/SubscriptionScope.ts +113 -7
  140. package/src/tools/subscribe/utils.ts +77 -0
  141. package/src/tools/testing.ts +0 -3
  142. package/src/tools/tests/CoValueCoreSubscription.test.ts +46 -12
  143. package/src/tools/tests/ContextManager.test.ts +85 -0
  144. package/src/tools/tests/SubscriptionCache.test.ts +237 -0
  145. package/src/tools/tests/account.test.ts +11 -4
  146. package/src/tools/tests/coMap.test.ts +5 -7
  147. package/src/tools/tests/schema.resolved.test.ts +3 -3
  148. package/tsup.config.ts +2 -0
  149. package/dist/chunk-2S3Z2CN6.js.map +0 -1
  150. package/dist/inspector/custom-element-P76EIWEV.js.map +0 -1
  151. package/dist/inspector/viewer/new-app.d.ts.map +0 -1
  152. package/dist/inspector/viewer/use-page-path.d.ts +0 -10
  153. package/dist/inspector/viewer/use-page-path.d.ts.map +0 -1
  154. package/src/inspector/viewer/new-app.tsx +0 -156
@@ -2308,6 +2308,10 @@ var _Account = class _Account extends CoValueBase {
2308
2308
  crypto,
2309
2309
  peers: [connectedPeers[0]]
2310
2310
  });
2311
+ const credentials = {
2312
+ accountID: account.$jazz.id,
2313
+ accountSecret: account.$jazz.localNode.getCurrentAgent().agentSecret
2314
+ };
2311
2315
  const loadedWorker = await _Account.load(worker.$jazz.id, {
2312
2316
  loadAs: account
2313
2317
  });
@@ -2321,7 +2325,7 @@ var _Account = class _Account extends CoValueBase {
2321
2325
  if (!createdAccount.$isLoaded)
2322
2326
  throw new Error("Unable to load the created account");
2323
2327
  account.$jazz.localNode.gracefulShutdown();
2324
- return createdAccount;
2328
+ return { credentials, account: createdAccount };
2325
2329
  }
2326
2330
  static fromNode(node) {
2327
2331
  return new this({
@@ -3930,7 +3934,8 @@ function applyCoValueMigrations(instance) {
3930
3934
 
3931
3935
  // src/tools/subscribe/CoValueCoreSubscription.ts
3932
3936
  import {
3933
- cojsonInternals as cojsonInternals4
3937
+ cojsonInternals as cojsonInternals4,
3938
+ isRawCoID
3934
3939
  } from "cojson";
3935
3940
 
3936
3941
  // src/tools/subscribe/types.ts
@@ -3984,101 +3989,62 @@ var CoValueCoreSubscription = class {
3984
3989
  */
3985
3990
  initializeSubscription() {
3986
3991
  const source = this.source;
3987
- if (source.isAvailable()) {
3988
- this.handleAvailableSource();
3992
+ if (!isRawCoID(source.id)) {
3993
+ this.emit(CoValueLoadingState.UNAVAILABLE);
3989
3994
  return;
3990
3995
  }
3991
3996
  if (this.branchName) {
3992
- this.handleBranchCheckout();
3997
+ this.handleBranching(this.branchName, this.branchOwnerId);
3993
3998
  return;
3994
3999
  }
3995
- this.loadCoValue();
4000
+ this.subscribe(this.source);
3996
4001
  }
3997
- /**
3998
- * Handles the case where the CoValue source is immediately available.
3999
- * Either subscribes directly or attempts to get the requested branch.
4000
- */
4001
- handleAvailableSource() {
4002
- if (!this.branchName || !cojsonInternals4.canBeBranched(this.source)) {
4003
- this.subscribe(this.source.getCurrentContent());
4002
+ handleBranching(branchName, branchOwnerId) {
4003
+ const source = this.source;
4004
+ if (!source.isAvailable()) {
4005
+ this.waitForSourceToBecomeAvailable(branchName, branchOwnerId);
4004
4006
  return;
4005
4007
  }
4006
- const branch = this.source.getBranch(this.branchName, this.branchOwnerId);
4007
- if (branch.isAvailable()) {
4008
- this.subscribe(branch.getCurrentContent());
4008
+ if (!cojsonInternals4.canBeBranched(source)) {
4009
+ this.subscribe(source);
4009
4010
  return;
4010
- } else if (!this.source.hasBranch(this.branchName, this.branchOwnerId)) {
4011
- this.source.createBranch(this.branchName, this.branchOwnerId);
4012
- this.subscribe(branch.getCurrentContent());
4013
- } else {
4014
- this.handleBranchCheckout();
4015
4011
  }
4016
- }
4017
- /**
4018
- * Attempts to checkout a specific branch of the CoValue.
4019
- * This is called when the source isn't available but a branch is requested.
4020
- */
4021
- handleBranchCheckout() {
4022
- this.localNode.checkoutBranch(this.source.id, this.branchName, this.branchOwnerId).then((value) => {
4023
- if (this.unsubscribed) return;
4024
- if (value !== CoValueLoadingState.UNAVAILABLE) {
4025
- this.subscribe(value);
4026
- } else {
4027
- this.handleUnavailableBranch();
4012
+ const branch = source.getBranch(branchName, branchOwnerId);
4013
+ if (!branch.isAvailable() && !source.hasBranch(branchName, branchOwnerId)) {
4014
+ try {
4015
+ source.createBranch(branchName, branchOwnerId);
4016
+ } catch (error) {
4017
+ console.error("error creating branch", error);
4018
+ this.emit(CoValueLoadingState.UNAVAILABLE);
4019
+ return;
4028
4020
  }
4029
- }).catch((error) => {
4030
- console.error(error);
4031
- this.emit(CoValueLoadingState.UNAVAILABLE);
4032
- });
4033
- }
4034
- /**
4035
- * Handles the case where a branch checkout fails.
4036
- * Determines whether to retry or report unavailability.
4037
- */
4038
- handleUnavailableBranch() {
4039
- const source = this.source;
4040
- if (source.isAvailable()) {
4041
- throw new Error("Branch is unavailable");
4042
4021
  }
4043
- this.subscribeToUnavailableSource();
4044
- this.emit(CoValueLoadingState.UNAVAILABLE);
4022
+ this.subscribe(branch);
4045
4023
  }
4046
4024
  /**
4047
- * Loads the CoValue core from the network/storage.
4048
- * This is the fallback strategy when immediate availability fails.
4025
+ * Loads a CoValue core and emits an unavailable event if it is still unavailable after the retries.
4049
4026
  */
4050
- loadCoValue() {
4051
- this.localNode.loadCoValueCore(this.source.id, void 0, this.skipRetry).then((value) => {
4052
- if (this.unsubscribed) return;
4053
- if (value.isAvailable()) {
4054
- this.subscribe(value.getCurrentContent());
4055
- } else {
4056
- this.subscribeToUnavailableSource();
4027
+ load(value) {
4028
+ this.localNode.loadCoValueCore(value.id, void 0, this.skipRetry).then(() => {
4029
+ if (!value.isAvailable()) {
4057
4030
  this.emit(CoValueLoadingState.UNAVAILABLE);
4058
4031
  }
4059
- }).catch((error) => {
4060
- console.error(error);
4061
- this.emit(CoValueLoadingState.UNAVAILABLE);
4062
4032
  });
4063
4033
  }
4064
4034
  /**
4065
- * Subscribes to state changes of an unavailable CoValue source.
4066
- * This allows the subscription to become active when the source becomes available after a first loading attempt.
4035
+ * Waits for the source to become available and then tries to branch.
4067
4036
  */
4068
- subscribeToUnavailableSource() {
4037
+ waitForSourceToBecomeAvailable(branchName, branchOwnerId) {
4069
4038
  const source = this.source;
4070
4039
  const handleStateChange = (_, unsubFromStateChange) => {
4071
4040
  if (!source.isAvailable()) {
4072
4041
  return;
4073
4042
  }
4074
4043
  unsubFromStateChange();
4075
- if (this.branchName) {
4076
- this.handleBranchCheckout();
4077
- } else {
4078
- this.subscribe(source.getCurrentContent());
4079
- }
4044
+ this.handleBranching(branchName, branchOwnerId);
4080
4045
  };
4081
4046
  this._unsubscribe = source.subscribe(handleStateChange);
4047
+ this.load(source);
4082
4048
  }
4083
4049
  /**
4084
4050
  * Subscribes to a specific CoValue and notifies the listener.
@@ -4087,16 +4053,30 @@ var CoValueCoreSubscription = class {
4087
4053
  subscribe(value) {
4088
4054
  if (this.unsubscribed) return;
4089
4055
  this._unsubscribe = value.subscribe((value2) => {
4090
- this.emit(value2);
4056
+ if (value2.isAvailable()) {
4057
+ this.emit(value2.getCurrentContent());
4058
+ }
4091
4059
  });
4060
+ if (!value.isAvailable()) {
4061
+ this.load(value);
4062
+ }
4092
4063
  }
4093
4064
  emit(value) {
4094
4065
  if (this.unsubscribed) return;
4095
- if (!isReadyForEmit(value)) {
4066
+ if (!this.isReadyForEmit(value)) {
4096
4067
  return;
4097
4068
  }
4098
4069
  this.listener(value);
4099
4070
  }
4071
+ isReadyForEmit(value) {
4072
+ if (value === CoValueLoadingState.UNAVAILABLE) {
4073
+ return true;
4074
+ }
4075
+ if (!isCompletelyDownloaded(value)) {
4076
+ return false;
4077
+ }
4078
+ return true;
4079
+ }
4100
4080
  /**
4101
4081
  * Unsubscribes from all active subscriptions and marks the instance as unsubscribed.
4102
4082
  * This prevents any further operations and ensures proper cleanup.
@@ -4107,10 +4087,7 @@ var CoValueCoreSubscription = class {
4107
4087
  this._unsubscribe();
4108
4088
  }
4109
4089
  };
4110
- function isReadyForEmit(value) {
4111
- if (value === "unavailable") {
4112
- return true;
4113
- }
4090
+ function isCompletelyDownloaded(value) {
4114
4091
  return value.core.verified?.header.meta?.type === "binary" || value.core.isCompletelyDownloaded();
4115
4092
  }
4116
4093
 
@@ -4148,6 +4125,34 @@ var JazzError = class _JazzError {
4148
4125
  }
4149
4126
  };
4150
4127
 
4128
+ // src/tools/subscribe/errorReporting.ts
4129
+ var isDev = function() {
4130
+ try {
4131
+ return process.env.NODE_ENV === "development";
4132
+ } catch {
4133
+ return false;
4134
+ }
4135
+ }();
4136
+ var customErrorReporter;
4137
+ var captureErrorCause = isDev;
4138
+ function enableCaptureErrorCause(capture) {
4139
+ captureErrorCause = capture;
4140
+ }
4141
+ function setCustomErrorReporter(reporter) {
4142
+ customErrorReporter = reporter;
4143
+ }
4144
+ function isCustomErrorReportingEnabled() {
4145
+ return customErrorReporter !== void 0;
4146
+ }
4147
+ function captureStack() {
4148
+ return captureErrorCause ? new Error() : void 0;
4149
+ }
4150
+ function captureError(error, props) {
4151
+ if (customErrorReporter) {
4152
+ customErrorReporter(error, props);
4153
+ }
4154
+ }
4155
+
4151
4156
  // src/tools/subscribe/utils.ts
4152
4157
  import { RawAccount as RawAccount4 } from "cojson";
4153
4158
 
@@ -4188,33 +4193,47 @@ function createCoValue(ref2, raw, subscriptionScope) {
4188
4193
  id: subscriptionScope.id
4189
4194
  };
4190
4195
  }
4191
-
4192
- // src/tools/subscribe/errorReporting.ts
4193
- var isDev = function() {
4194
- try {
4195
- return process.env.NODE_ENV === "development";
4196
- } catch {
4197
- return false;
4198
- }
4199
- }();
4200
- var customErrorReporter;
4201
- var captureErrorCause = isDev;
4202
- function enableCaptureErrorCause(capture) {
4203
- captureErrorCause = capture;
4204
- }
4205
- function setCustomErrorReporter(reporter) {
4206
- customErrorReporter = reporter;
4207
- }
4208
- function isCustomErrorReportingEnabled() {
4209
- return customErrorReporter !== void 0;
4196
+ function resolvedPromise(value) {
4197
+ const promise = Promise.resolve(value);
4198
+ promise.status = "fulfilled";
4199
+ promise.value = value;
4200
+ return promise;
4210
4201
  }
4211
- function captureStack() {
4212
- return captureErrorCause ? new Error() : void 0;
4202
+ function rejectedPromise(reason) {
4203
+ const promise = Promise.reject(reason);
4204
+ promise.status = "rejected";
4205
+ promise.reason = reason;
4206
+ return promise;
4213
4207
  }
4214
- function captureError(error, props) {
4215
- if (customErrorReporter) {
4216
- customErrorReporter(error, props);
4208
+ function isEqualRefsToResolve(a, b) {
4209
+ if (a === b) {
4210
+ return true;
4211
+ }
4212
+ if (typeof a === "boolean" && typeof b === "boolean") {
4213
+ return a === b;
4214
+ }
4215
+ if (typeof a === "boolean" || typeof b === "boolean") {
4216
+ return false;
4217
+ }
4218
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
4219
+ return false;
4220
+ }
4221
+ const keysA = Object.keys(a);
4222
+ const keysB = Object.keys(b);
4223
+ if (keysA.length !== keysB.length) {
4224
+ return false;
4225
+ }
4226
+ for (const key of keysA) {
4227
+ if (!(key in b)) {
4228
+ return false;
4229
+ }
4230
+ const valueA = a[key];
4231
+ const valueB = b[key];
4232
+ if (!isEqualRefsToResolve(valueA, valueB)) {
4233
+ return false;
4234
+ }
4217
4235
  }
4236
+ return true;
4218
4237
  }
4219
4238
 
4220
4239
  // src/tools/subscribe/SubscriptionScope.ts
@@ -4273,6 +4292,7 @@ var SubscriptionScope = class _SubscriptionScope {
4273
4292
  this.triggerUpdate();
4274
4293
  };
4275
4294
  this.subscribers = /* @__PURE__ */ new Set();
4295
+ this.subscriberChangeCallbacks = /* @__PURE__ */ new Set();
4276
4296
  this.callerStack = callerStack;
4277
4297
  this.resolve = resolve;
4278
4298
  this.value = { type: CoValueLoadingState.LOADING, id };
@@ -4401,6 +4421,48 @@ var SubscriptionScope = class _SubscriptionScope {
4401
4421
  if (this.value.type !== CoValueLoadingState.LOADED) return true;
4402
4422
  return this.pendingLoadedChildren.size === 0;
4403
4423
  }
4424
+ cachePromise(value, callback) {
4425
+ if (this.lastPromise?.value === value) {
4426
+ return this.lastPromise.promise;
4427
+ }
4428
+ const promise = callback();
4429
+ this.lastPromise = { value, promise };
4430
+ return promise;
4431
+ }
4432
+ getPromise() {
4433
+ const currentValue = this.getCurrentValue();
4434
+ if (currentValue.$isLoaded) {
4435
+ return resolvedPromise(currentValue);
4436
+ }
4437
+ if (currentValue.$jazz.loadingState !== CoValueLoadingState.LOADING) {
4438
+ const error = this.getError();
4439
+ return rejectedPromise(
4440
+ new Error(error?.toString() ?? "Unknown error", {
4441
+ cause: this.callerStack
4442
+ })
4443
+ );
4444
+ }
4445
+ return this.cachePromise(currentValue, () => {
4446
+ return new Promise((resolve, reject) => {
4447
+ const unsubscribe = this.subscribe(() => {
4448
+ const currentValue2 = this.getCurrentValue();
4449
+ if (currentValue2.$jazz.loadingState === CoValueLoadingState.LOADING) {
4450
+ return;
4451
+ }
4452
+ if (currentValue2.$isLoaded) {
4453
+ resolve(currentValue2);
4454
+ } else {
4455
+ reject(
4456
+ new Error(this.getError()?.toString() ?? "Unknown error", {
4457
+ cause: this.callerStack
4458
+ })
4459
+ );
4460
+ }
4461
+ unsubscribe();
4462
+ });
4463
+ });
4464
+ });
4465
+ }
4404
4466
  getUnloadedValue(reason) {
4405
4467
  if (this.unloadedValue?.$jazz.loadingState === reason) {
4406
4468
  return this.unloadedValue;
@@ -4451,14 +4513,16 @@ var SubscriptionScope = class _SubscriptionScope {
4451
4513
  }
4452
4514
  return result;
4453
4515
  }
4454
- logError() {
4455
- let error;
4516
+ getError() {
4456
4517
  if (this.value.type === CoValueLoadingState.UNAUTHORIZED || this.value.type === CoValueLoadingState.UNAVAILABLE) {
4457
- error = this.value;
4518
+ return this.value;
4458
4519
  }
4459
4520
  if (this.errorFromChildren) {
4460
- error = this.errorFromChildren;
4521
+ return this.errorFromChildren;
4461
4522
  }
4523
+ }
4524
+ logError() {
4525
+ const error = this.getError();
4462
4526
  if (!error || this.lastErrorLogged === error) {
4463
4527
  return;
4464
4528
  }
@@ -4489,14 +4553,37 @@ var SubscriptionScope = class _SubscriptionScope {
4489
4553
  }
4490
4554
  this.dirty = false;
4491
4555
  }
4556
+ /**
4557
+ * Subscribe to subscriber count changes
4558
+ * Callback receives the total number of subscribers
4559
+ * Returns an unsubscribe function
4560
+ */
4561
+ onSubscriberChange(callback) {
4562
+ this.subscriberChangeCallbacks.add(callback);
4563
+ return () => {
4564
+ this.subscriberChangeCallbacks.delete(callback);
4565
+ };
4566
+ }
4567
+ notifySubscriberChange() {
4568
+ const count = this.subscribers.size;
4569
+ this.subscriberChangeCallbacks.forEach((callback) => {
4570
+ callback(count);
4571
+ });
4572
+ }
4492
4573
  subscribe(listener) {
4493
4574
  this.subscribers.add(listener);
4575
+ this.notifySubscriberChange();
4494
4576
  return () => {
4495
4577
  this.subscribers.delete(listener);
4578
+ this.notifySubscriberChange();
4496
4579
  };
4497
4580
  }
4498
4581
  setListener(listener) {
4582
+ const hadListener = this.subscribers.has(listener);
4499
4583
  this.subscribers.add(listener);
4584
+ if (!hadListener) {
4585
+ this.notifySubscriberChange();
4586
+ }
4500
4587
  this.triggerUpdate();
4501
4588
  }
4502
4589
  subscribeToKey(key) {
@@ -4765,7 +4852,12 @@ var SubscriptionScope = class _SubscriptionScope {
4765
4852
  destroy() {
4766
4853
  this.closed = true;
4767
4854
  this.subscription.unsubscribe();
4855
+ const hadSubscribers = this.subscribers.size > 0;
4768
4856
  this.subscribers.clear();
4857
+ if (hadSubscribers) {
4858
+ this.notifySubscriberChange();
4859
+ }
4860
+ this.subscriberChangeCallbacks.clear();
4769
4861
  this.childNodes.forEach((child) => child.destroy());
4770
4862
  }
4771
4863
  };
@@ -6381,6 +6473,177 @@ var InMemoryKVStore = class {
6381
6473
  }
6382
6474
  };
6383
6475
 
6476
+ // src/tools/subscribe/SubscriptionCache.ts
6477
+ var SubscriptionCache = class {
6478
+ constructor(cleanupTimeout = 5e3) {
6479
+ this.cache = /* @__PURE__ */ new Map();
6480
+ this.cleanupTimeout = cleanupTimeout;
6481
+ }
6482
+ /**
6483
+ * Get the inner set for a given id (read-only access)
6484
+ */
6485
+ getIdSet(id) {
6486
+ return this.cache.get(id);
6487
+ }
6488
+ /**
6489
+ * Get the inner set for a given id, creating it if it doesn't exist
6490
+ */
6491
+ getIdSetOrCreate(id) {
6492
+ let idSet = this.cache.get(id);
6493
+ if (!idSet) {
6494
+ idSet = /* @__PURE__ */ new Set();
6495
+ this.cache.set(id, idSet);
6496
+ }
6497
+ return idSet;
6498
+ }
6499
+ /**
6500
+ * Check if an entry matches the provided parameters
6501
+ */
6502
+ matchesEntry(entry, schema, resolve, branch) {
6503
+ if (entry.schema !== schema) {
6504
+ return false;
6505
+ }
6506
+ if (!isEqualRefsToResolve(entry.resolve, resolve)) {
6507
+ return false;
6508
+ }
6509
+ const branchName = branch?.name;
6510
+ if (entry.branch?.name !== branchName) {
6511
+ return false;
6512
+ }
6513
+ const branchOwnerId = branch?.owner?.$jazz.id;
6514
+ if (entry.branch?.owner?.$jazz.id !== branchOwnerId) {
6515
+ return false;
6516
+ }
6517
+ return true;
6518
+ }
6519
+ /**
6520
+ * Find a matching cache entry by comparing against entry properties
6521
+ * Uses id-based nesting to quickly filter candidates
6522
+ */
6523
+ findMatchingEntry(schema, id, resolve, branch) {
6524
+ const idSet = this.getIdSet(id);
6525
+ if (!idSet) {
6526
+ return void 0;
6527
+ }
6528
+ for (const entry of idSet) {
6529
+ if (this.matchesEntry(entry, schema, resolve, branch)) {
6530
+ return entry;
6531
+ }
6532
+ }
6533
+ return void 0;
6534
+ }
6535
+ /**
6536
+ * Handle subscriber count changes from SubscriptionScope
6537
+ */
6538
+ handleSubscriberChange(entry, count) {
6539
+ entry.subscriberCount = count;
6540
+ if (count === 0) {
6541
+ this.scheduleCleanup(entry);
6542
+ } else {
6543
+ this.cancelCleanup(entry);
6544
+ }
6545
+ }
6546
+ /**
6547
+ * Schedule cleanup timeout for an entry
6548
+ */
6549
+ scheduleCleanup(entry) {
6550
+ this.cancelCleanup(entry);
6551
+ entry.cleanupTimeoutId = setTimeout(() => {
6552
+ this.destroyEntry(entry);
6553
+ }, this.cleanupTimeout);
6554
+ }
6555
+ /**
6556
+ * Cancel pending cleanup timeout for an entry
6557
+ */
6558
+ cancelCleanup(entry) {
6559
+ if (entry.cleanupTimeoutId !== void 0) {
6560
+ clearTimeout(entry.cleanupTimeoutId);
6561
+ entry.cleanupTimeoutId = void 0;
6562
+ }
6563
+ }
6564
+ /**
6565
+ * Destroy a cache entry and its SubscriptionScope
6566
+ */
6567
+ destroyEntry(entry) {
6568
+ this.cancelCleanup(entry);
6569
+ entry.unsubscribeFromScope();
6570
+ try {
6571
+ entry.subscriptionScope.destroy();
6572
+ } catch (error) {
6573
+ console.error("Error destroying SubscriptionScope:", error);
6574
+ }
6575
+ const id = entry.subscriptionScope.id;
6576
+ const idSet = this.getIdSet(id);
6577
+ if (idSet) {
6578
+ idSet.delete(entry);
6579
+ if (idSet.size === 0) {
6580
+ this.cache.delete(id);
6581
+ }
6582
+ }
6583
+ }
6584
+ /**
6585
+ * Get or create a SubscriptionScope from the cache
6586
+ */
6587
+ getOrCreate(node, schema, id, resolve, skipRetry, bestEffortResolution, branch) {
6588
+ if (!id) {
6589
+ throw new Error("Cannot create subscription with undefined or null id");
6590
+ }
6591
+ const matchingEntry = this.findMatchingEntry(schema, id, resolve, branch);
6592
+ if (matchingEntry) {
6593
+ this.cancelCleanup(matchingEntry);
6594
+ return matchingEntry.subscriptionScope;
6595
+ }
6596
+ const refEncoded = {
6597
+ ref: coValueClassFromCoValueClassOrSchema(schema),
6598
+ optional: true
6599
+ };
6600
+ const subscriptionScope = new SubscriptionScope(
6601
+ node,
6602
+ // @ts-expect-error the SubscriptionScope is too generic for TS to infer its instances are CoValues
6603
+ resolve,
6604
+ id,
6605
+ refEncoded,
6606
+ skipRetry ?? false,
6607
+ bestEffortResolution ?? false,
6608
+ branch
6609
+ );
6610
+ const handleSubscriberChange = (count) => {
6611
+ const idSet2 = this.getIdSet(id);
6612
+ if (idSet2 && idSet2.has(entry)) {
6613
+ this.handleSubscriberChange(entry, count);
6614
+ }
6615
+ };
6616
+ const entry = {
6617
+ subscriptionScope,
6618
+ schema,
6619
+ resolve,
6620
+ branch,
6621
+ subscriberCount: subscriptionScope.subscribers.size,
6622
+ unsubscribeFromScope: subscriptionScope.onSubscriberChange(
6623
+ handleSubscriberChange
6624
+ )
6625
+ };
6626
+ const idSet = this.getIdSetOrCreate(id);
6627
+ idSet.add(entry);
6628
+ return subscriptionScope;
6629
+ }
6630
+ /**
6631
+ * Clear all cache entries and destroy all SubscriptionScope instances
6632
+ */
6633
+ clear() {
6634
+ const entriesToDestroy = [];
6635
+ for (const idSet of this.cache.values()) {
6636
+ for (const entry of idSet) {
6637
+ entriesToDestroy.push(entry);
6638
+ }
6639
+ }
6640
+ for (const entry of entriesToDestroy) {
6641
+ this.destroyEntry(entry);
6642
+ }
6643
+ this.cache.clear();
6644
+ }
6645
+ };
6646
+
6384
6647
  // src/tools/implementation/ContextManager.ts
6385
6648
  function getAnonymousFallback() {
6386
6649
  const context = createAnonymousJazzContext({
@@ -6414,6 +6677,7 @@ var JazzContextManager = class {
6414
6677
  return;
6415
6678
  }
6416
6679
  this.authenticatingAccountID = null;
6680
+ this.subscriptionCache.clear();
6417
6681
  await this.props.onLogOut?.();
6418
6682
  if (this.props.logOutReplacement) {
6419
6683
  await this.props.logOutReplacement();
@@ -6511,6 +6775,7 @@ var JazzContextManager = class {
6511
6775
  };
6512
6776
  KvStoreContext.getInstance().initialize(this.getKvStore());
6513
6777
  this.authSecretStorage = new AuthSecretStorage(opts?.authSecretStorageKey);
6778
+ this.subscriptionCache = new SubscriptionCache();
6514
6779
  if (opts?.useAnonymousFallback) {
6515
6780
  this.value = getAnonymousFallback();
6516
6781
  }
@@ -6539,6 +6804,7 @@ var JazzContextManager = class {
6539
6804
  throw new Error("Not implemented");
6540
6805
  }
6541
6806
  async updateContext(props, context, authProps) {
6807
+ this.subscriptionCache.clear();
6542
6808
  if (!this.keepContextOpen) {
6543
6809
  this.context?.done();
6544
6810
  }
@@ -6576,6 +6842,9 @@ var JazzContextManager = class {
6576
6842
  getAuthenticatingAccountID() {
6577
6843
  return this.authenticatingAccountID;
6578
6844
  }
6845
+ getSubscriptionScopeCache() {
6846
+ return this.subscriptionCache;
6847
+ }
6579
6848
  async handleAnonymousAccountMigration(prevContext) {
6580
6849
  if (!this.props) {
6581
6850
  throw new Error("Props required");
@@ -7129,4 +7398,4 @@ export {
7129
7398
  JazzContextManager
7130
7399
  };
7131
7400
  /* istanbul ignore file -- @preserve */
7132
- //# sourceMappingURL=chunk-2S3Z2CN6.js.map
7401
+ //# sourceMappingURL=chunk-HX5S6W5E.js.map