teamplay 0.4.0-alpha.95 → 0.4.0-alpha.96

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamplay",
3
- "version": "0.4.0-alpha.95",
3
+ "version": "0.4.0-alpha.96",
4
4
  "description": "Full-stack signals ORM with multiplayer",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -83,5 +83,5 @@
83
83
  ]
84
84
  },
85
85
  "license": "MIT",
86
- "gitHead": "43c734951b848a2da50848e9930e50cdf7fdd9d1"
86
+ "gitHead": "78f33dc002740b425f3a056fad5b7e780a34219c"
87
87
  }
@@ -1,37 +1,46 @@
1
1
  class RenderAttemptDestroyer {
2
2
  constructor () {
3
3
  this.fns = []
4
- this.compatArmed = false
4
+ this.compatAttemptCleanupArmed = false
5
+ this.suspenseGateArmed = false
5
6
  }
6
7
 
7
8
  add (fn, { compat = false } = {}) {
8
9
  if (typeof fn !== 'function') return
9
10
  this.fns.push(fn)
10
- if (compat) this.compatArmed = true
11
+ if (compat) this.compatAttemptCleanupArmed = true
11
12
  }
12
13
 
13
- armCompat () {
14
- this.compatArmed = true
14
+ armCompatAttemptCleanup () {
15
+ this.compatAttemptCleanupArmed = true
15
16
  }
16
17
 
17
- getDestructor () {
18
- if (!this.compatArmed) {
19
- this.reset()
20
- return undefined
21
- }
18
+ armSuspenseGate () {
19
+ this.suspenseGateArmed = true
20
+ }
22
21
 
23
- const fns = [...this.fns]
22
+ consumeThenableHandling () {
23
+ const shouldRunAttemptCleanup = this.compatAttemptCleanupArmed && this.fns.length > 0
24
+ const shouldKeepShellAlive = this.suspenseGateArmed || shouldRunAttemptCleanup
25
+ let destroyAttempt
26
+ if (shouldRunAttemptCleanup) {
27
+ const fns = [...this.fns]
28
+ destroyAttempt = async () => {
29
+ await Promise.allSettled(fns.map(fn => fn()))
30
+ fns.length = 0
31
+ }
32
+ }
24
33
  this.reset()
25
- return async () => {
26
- if (fns.length === 0) return
27
- await Promise.allSettled(fns.map(fn => fn()))
28
- fns.length = 0
34
+ return {
35
+ shouldKeepShellAlive,
36
+ destroyAttempt
29
37
  }
30
38
  }
31
39
 
32
40
  reset () {
33
41
  this.fns.length = 0
34
- this.compatArmed = false
42
+ this.compatAttemptCleanupArmed = false
43
+ this.suspenseGateArmed = false
35
44
  }
36
45
  }
37
46
 
@@ -24,8 +24,11 @@ export default function trapRender ({ render, cache, destroy, componentId }) {
24
24
  destroyed = true
25
25
  throw err
26
26
  }
27
- const destroyAttempt = renderAttemptDestroyer.getDestructor()
28
- if (destroyAttempt) {
27
+ const {
28
+ shouldKeepShellAlive,
29
+ destroyAttempt
30
+ } = renderAttemptDestroyer.consumeThenableHandling()
31
+ if (shouldKeepShellAlive) {
29
32
  throw Promise.resolve(err).then(() => destroyAttempt?.())
30
33
  }
31
34
 
package/react/useSub.js CHANGED
@@ -158,6 +158,7 @@ function maybeThrottle (promise) {
158
158
  function registerCompatAttemptCleanup (signal, params) {
159
159
  // Compat hooks don't build per-hook init objects like Racer.
160
160
  // We still need a marker so trapRender can defer observer-shell cleanup
161
- // to Suspense resolution instead of tearing the whole shell down immediately.
162
- renderAttemptDestroyer.armCompat()
161
+ // only when a real attempt cleanup exists.
162
+ // This path must not arm suspense-gate keep-alive by itself.
163
+ renderAttemptDestroyer.armCompatAttemptCleanup()
163
164
  }
@@ -28,7 +28,7 @@ export default function useSuspendMemo (factory, deps) {
28
28
  if (entry.status === 'done') return entry.value
29
29
  if (entry.status === 'pending') {
30
30
  markCompatComponent(componentId)
31
- renderAttemptDestroyer.armCompat()
31
+ renderAttemptDestroyer.armSuspenseGate()
32
32
  throw entry.promise
33
33
  }
34
34
 
@@ -47,7 +47,7 @@ export default function useSuspendMemo (factory, deps) {
47
47
  entry.status = 'pending'
48
48
  entry.promise = promise
49
49
  markCompatComponent(componentId)
50
- renderAttemptDestroyer.armCompat()
50
+ renderAttemptDestroyer.armSuspenseGate()
51
51
  throw promise
52
52
  }
53
53
  }