teamplay 0.4.0-alpha.22 → 0.4.0-alpha.23
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/orm/Compat/README.md +40 -0
- package/orm/Compat/startStopCompat.js +85 -0
- package/orm/SignalBase.js +17 -0
- package/package.json +2 -2
package/orm/Compat/README.md
CHANGED
|
@@ -174,6 +174,46 @@ $alias.get() === $user.get() // false
|
|
|
174
174
|
- No event emissions specific to refs.
|
|
175
175
|
- No support for racer-style ref meta/options beyond the basic signature.
|
|
176
176
|
|
|
177
|
+
### start(targetPath, ...deps, getter)
|
|
178
|
+
|
|
179
|
+
Legacy computed binding API from Racer/StartupJS.
|
|
180
|
+
Creates a reactive computation and writes its result into `targetPath`.
|
|
181
|
+
Source of truth is root API (`$root.start(...)`), but non-root calls are supported as sugar:
|
|
182
|
+
- `$scope.start('a.b', ...deps, getter)` → `$root.start('<scopePath>.a.b', ...deps, getter)`
|
|
183
|
+
|
|
184
|
+
- `targetPath`: string path where computed value is written.
|
|
185
|
+
- `deps`: dependencies used by `getter`.
|
|
186
|
+
- `getter`: function called as `getter(...resolvedDeps)`.
|
|
187
|
+
|
|
188
|
+
Dependency resolution:
|
|
189
|
+
- Signal-like dep (`$doc`, `$session.user`) → `dep.get()`.
|
|
190
|
+
- String dep (`'settings.theme'`) → `$root.get(dep)`.
|
|
191
|
+
- Any other dep → passed as-is.
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
$root.start('_virtual.lesson', $.lessons[lessonId], '_session.userId', (lesson, userId) => {
|
|
195
|
+
if (!lesson) return undefined
|
|
196
|
+
return { stageIds: lesson.stageIds, userId }
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Behavior:
|
|
201
|
+
- Calling `start()` again for the same `targetPath` replaces previous reaction.
|
|
202
|
+
- `undefined` / `null` result clears target path via normal `set` semantics.
|
|
203
|
+
- Returns target signal.
|
|
204
|
+
|
|
205
|
+
### stop(targetPath)
|
|
206
|
+
|
|
207
|
+
Stops a computation created with `start(targetPath, ...)`.
|
|
208
|
+
No-op if there is no active computation for the path.
|
|
209
|
+
Source of truth is root API (`$root.stop(...)`), but non-root calls are supported as sugar:
|
|
210
|
+
- `$scope.stop('a.b')` → `$root.stop('<scopePath>.a.b')`
|
|
211
|
+
- `$scope.stop()` → `$root.stop('<scopePath>')`
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
$root.stop('_virtual.lesson')
|
|
215
|
+
```
|
|
216
|
+
|
|
177
217
|
### query(collection, query, options?)
|
|
178
218
|
|
|
179
219
|
Creates a query signal **without** subscribing. Supports shorthand params:
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { observe, unobserve } from '@nx-js/observer-util'
|
|
2
|
+
import { getRoot } from '../Root.js'
|
|
3
|
+
|
|
4
|
+
const START_REACTIONS = Symbol('compat start reactions')
|
|
5
|
+
|
|
6
|
+
export function compatStartOnRoot ($root, targetPath, ...depsAndGetter) {
|
|
7
|
+
if (!isRootSignal($root)) throw Error('Signal.start() is only available on root signal')
|
|
8
|
+
if (typeof targetPath !== 'string') throw Error('Signal.start() expects targetPath to be a string')
|
|
9
|
+
if (depsAndGetter.length < 1) {
|
|
10
|
+
throw Error('Signal.start() expects targetPath, dependencies, and a getter function')
|
|
11
|
+
}
|
|
12
|
+
const getter = depsAndGetter[depsAndGetter.length - 1]
|
|
13
|
+
if (typeof getter !== 'function') {
|
|
14
|
+
throw Error('Signal.start() expects the last argument to be a getter function')
|
|
15
|
+
}
|
|
16
|
+
const deps = depsAndGetter.slice(0, -1)
|
|
17
|
+
const targetSegments = parsePathSegments(targetPath)
|
|
18
|
+
const $target = resolveSignal($root, targetSegments)
|
|
19
|
+
const targetKey = $target.path()
|
|
20
|
+
|
|
21
|
+
const store = getStartStore($root)
|
|
22
|
+
const existing = store.get(targetKey)
|
|
23
|
+
if (existing) existing.stop()
|
|
24
|
+
|
|
25
|
+
const reaction = observe(() => {
|
|
26
|
+
const resolvedDeps = deps.map(dep => resolveStartDep(dep, $root))
|
|
27
|
+
const nextValue = getter(...resolvedDeps)
|
|
28
|
+
const maybePromise = $target.set(nextValue)
|
|
29
|
+
if (maybePromise?.catch) maybePromise.catch(ignorePromiseRejection)
|
|
30
|
+
})
|
|
31
|
+
store.set(targetKey, { stop: () => unobserve(reaction) })
|
|
32
|
+
return $target
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function compatStopOnRoot ($root, targetPath) {
|
|
36
|
+
if (!isRootSignal($root)) throw Error('Signal.stop() is only available on root signal')
|
|
37
|
+
if (typeof targetPath !== 'string') throw Error('Signal.stop() expects targetPath to be a string')
|
|
38
|
+
const targetSegments = parsePathSegments(targetPath)
|
|
39
|
+
const $target = resolveSignal($root, targetSegments)
|
|
40
|
+
const targetKey = $target.path()
|
|
41
|
+
const store = getStartStore($root)
|
|
42
|
+
const existing = store.get(targetKey)
|
|
43
|
+
if (!existing) return
|
|
44
|
+
existing.stop()
|
|
45
|
+
store.delete(targetKey)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function joinScopePath (scopePath, relativePath) {
|
|
49
|
+
if (typeof scopePath !== 'string') scopePath = ''
|
|
50
|
+
const segments = []
|
|
51
|
+
if (scopePath) segments.push(...parsePathSegments(scopePath))
|
|
52
|
+
if (relativePath) segments.push(...parsePathSegments(relativePath))
|
|
53
|
+
return segments.join('.')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getStartStore ($root) {
|
|
57
|
+
$root[START_REACTIONS] ??= new Map()
|
|
58
|
+
return $root[START_REACTIONS]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function resolveStartDep (dep, $root) {
|
|
62
|
+
if (isSignalLike(dep)) return dep.get()
|
|
63
|
+
if (typeof dep === 'string') return resolveSignal($root, parsePathSegments(dep)).get()
|
|
64
|
+
return dep
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isSignalLike (value) {
|
|
68
|
+
return value && typeof value.path === 'function' && typeof value.get === 'function'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parsePathSegments (path) {
|
|
72
|
+
return path.split('.').filter(Boolean)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveSignal ($base, segments) {
|
|
76
|
+
let $cursor = $base
|
|
77
|
+
for (const segment of segments) $cursor = $cursor[segment]
|
|
78
|
+
return $cursor
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isRootSignal ($signal) {
|
|
82
|
+
return getRoot($signal) === $signal
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ignorePromiseRejection () {}
|
package/orm/SignalBase.js
CHANGED
|
@@ -48,6 +48,7 @@ import { publicOnly } from './connection.js'
|
|
|
48
48
|
import { DEFAULT_ID_FIELDS, getIdFieldsForSegments, isIdFieldPath, normalizeIdFields } from './idFields.js'
|
|
49
49
|
import { isCompatEnv } from './compatEnv.js'
|
|
50
50
|
import { resolveRefSegmentsSafe, resolveRefSignalSafe } from './Compat/refFallback.js'
|
|
51
|
+
import { compatStartOnRoot, compatStopOnRoot, joinScopePath } from './Compat/startStopCompat.js'
|
|
51
52
|
|
|
52
53
|
export const SEGMENTS = Symbol('path segments targeting the particular node in the data tree')
|
|
53
54
|
export const ARRAY_METHOD = Symbol('run array method on the signal')
|
|
@@ -522,6 +523,22 @@ export const extremelyLateBindings = {
|
|
|
522
523
|
}
|
|
523
524
|
}
|
|
524
525
|
}
|
|
526
|
+
|
|
527
|
+
if (key === 'start') {
|
|
528
|
+
const [relativePath, ...depsAndGetter] = argumentsList
|
|
529
|
+
if (typeof relativePath !== 'string') throw Error('Signal.start() expects targetPath to be a string')
|
|
530
|
+
const absolutePath = joinScopePath($parent.path(), relativePath)
|
|
531
|
+
return compatStartOnRoot(getRoot($parent) || $parent, absolutePath, ...depsAndGetter)
|
|
532
|
+
}
|
|
533
|
+
if (key === 'stop') {
|
|
534
|
+
if (argumentsList.length > 1) throw Error('Signal.stop() expects zero or one argument')
|
|
535
|
+
const relativePath = argumentsList.length === 0 ? '' : argumentsList[0]
|
|
536
|
+
if (relativePath != null && typeof relativePath !== 'string') {
|
|
537
|
+
throw Error('Signal.stop() expects targetPath to be a string')
|
|
538
|
+
}
|
|
539
|
+
const absolutePath = joinScopePath($parent.path(), relativePath || '')
|
|
540
|
+
return compatStopOnRoot(getRoot($parent) || $parent, absolutePath)
|
|
541
|
+
}
|
|
525
542
|
}
|
|
526
543
|
|
|
527
544
|
throw Error(ERRORS.noSignalKey($parent, key))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teamplay",
|
|
3
|
-
"version": "0.4.0-alpha.
|
|
3
|
+
"version": "0.4.0-alpha.23",
|
|
4
4
|
"description": "Full-stack signals ORM with multiplayer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
]
|
|
82
82
|
},
|
|
83
83
|
"license": "MIT",
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "bcf9b46727a5fe9ee3836e582bf25b933fc7d479"
|
|
85
85
|
}
|