teamplay 0.4.0-alpha.1 → 0.4.0-alpha.2
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/index.d.ts +11 -0
- package/index.js +41 -0
- package/orm/Compat/hooksCompat.js +12 -0
- package/package.json +2 -2
- package/react/helpers.js +47 -1
- package/react/useApi.js +63 -0
package/index.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export function useBatchQueryDoc (collection: string, query: any, options?: any)
|
|
|
73
73
|
export function useBatchQueryDoc$ (collection: string, query: any, options?: any): any
|
|
74
74
|
export function useAsyncQueryDoc (collection: string, query: any, options?: any): [any, any]
|
|
75
75
|
export function useAsyncQueryDoc$ (collection: string, query: any, options?: any): any
|
|
76
|
+
export function useLocalDoc (collection: string, id: any): [any, any]
|
|
77
|
+
export function useLocalDoc$ (collection: string, id: any): any
|
|
76
78
|
export function emit (eventName: string, ...args: any[]): void
|
|
77
79
|
export function useOn (
|
|
78
80
|
eventName: 'change' | 'all',
|
|
@@ -82,6 +84,15 @@ export function useOn (
|
|
|
82
84
|
): void
|
|
83
85
|
export function useOn (eventName: string, handler: (...args: any[]) => void, deps?: any[]): void
|
|
84
86
|
export function useEmit (): (eventName: string, ...args: any[]) => void
|
|
87
|
+
export function batch<T = any> (fn?: () => T): T | undefined
|
|
88
|
+
export function batchModel<T = any> (fn?: () => T): T | undefined
|
|
89
|
+
export function clone<T = any> (value: T): T
|
|
90
|
+
export function initLocalCollection (name: string): any
|
|
91
|
+
export function useApi (api: (...args: any[]) => any, args?: any[], options?: { debounce?: number }): [any, boolean, any]
|
|
92
|
+
type EffectCleanup = (() => void) | undefined
|
|
93
|
+
export function useDidUpdate (fn: () => EffectCleanup, deps?: any[]): void
|
|
94
|
+
export function useOnce (condition: any, fn: () => EffectCleanup): void
|
|
95
|
+
export function useSyncEffect (fn: () => EffectCleanup, deps?: any[]): void
|
|
85
96
|
export { connection, setConnection, getConnection, fetchOnly, setFetchOnly, publicOnly, setPublicOnly } from './orm/connection.js'
|
|
86
97
|
export { useId, useNow, useScheduleUpdate, useTriggerUpdate } from './react/helpers.js'
|
|
87
98
|
export { GUID_PATTERN, hasMany, hasOne, hasManyFlags, belongsTo, pickFormFields } from '@teamplay/schema'
|
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// In future, we might want to separate the plain JS and React APIs
|
|
6
6
|
import { getRootSignal as _getRootSignal, GLOBAL_ROOT_ID } from './orm/Root.js'
|
|
7
7
|
import universal$ from './react/universal$.js'
|
|
8
|
+
import useApi from './react/useApi.js'
|
|
8
9
|
|
|
9
10
|
export { default as Signal, SEGMENTS } from './orm/Signal.js'
|
|
10
11
|
export { __DEBUG_SIGNALS_CACHE__, rawSignal, getSignalClass } from './orm/getSignal.js'
|
|
@@ -29,6 +30,8 @@ export {
|
|
|
29
30
|
useModel,
|
|
30
31
|
useLocal,
|
|
31
32
|
useLocal$,
|
|
33
|
+
useLocalDoc,
|
|
34
|
+
useLocalDoc$,
|
|
32
35
|
useSession,
|
|
33
36
|
useSession$,
|
|
34
37
|
usePage,
|
|
@@ -57,12 +60,50 @@ export {
|
|
|
57
60
|
useAsyncQueryDoc$
|
|
58
61
|
} from './orm/Compat/hooksCompat.js'
|
|
59
62
|
export { emit, useOn, useEmit } from './orm/Compat/eventsCompat.js'
|
|
63
|
+
export {
|
|
64
|
+
useDidUpdate,
|
|
65
|
+
useOnce,
|
|
66
|
+
useSyncEffect
|
|
67
|
+
} from './react/helpers.js'
|
|
60
68
|
export { connection, setConnection, getConnection, fetchOnly, setFetchOnly, publicOnly, setPublicOnly } from './orm/connection.js'
|
|
61
69
|
export { useId, useNow, useScheduleUpdate, useTriggerUpdate } from './react/helpers.js'
|
|
62
70
|
export { GUID_PATTERN, hasMany, hasOne, hasManyFlags, belongsTo, pickFormFields } from '@teamplay/schema'
|
|
63
71
|
export { aggregation, aggregationHeader as __aggregationHeader } from '@teamplay/utils/aggregation'
|
|
64
72
|
export { accessControl } from '@teamplay/utils/accessControl'
|
|
65
73
|
|
|
74
|
+
export function batch (fn) {
|
|
75
|
+
return $.batch(fn)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function batchModel (fn) {
|
|
79
|
+
return $.batch(fn)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function clone (value) {
|
|
83
|
+
if (typeof globalThis.structuredClone === 'function') {
|
|
84
|
+
try {
|
|
85
|
+
return globalThis.structuredClone(value)
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
if (value == null) return value
|
|
89
|
+
return JSON.parse(JSON.stringify(value))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function initLocalCollection (name) {
|
|
93
|
+
if (typeof name !== 'string') throw Error('initLocalCollection() expects a collection name')
|
|
94
|
+
if (!name) return
|
|
95
|
+
const segments = name.split('.').filter(Boolean)
|
|
96
|
+
if (!segments.length) return
|
|
97
|
+
let $cursor = $
|
|
98
|
+
for (const segment of segments) {
|
|
99
|
+
$cursor = $cursor[segment]
|
|
100
|
+
}
|
|
101
|
+
if ($cursor.get() == null) $cursor.set({})
|
|
102
|
+
return $cursor
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { useApi }
|
|
106
|
+
|
|
66
107
|
export function getRootSignal (options) {
|
|
67
108
|
return _getRootSignal({
|
|
68
109
|
rootFunction: universal$,
|
|
@@ -44,6 +44,18 @@ export function useLocal (path) {
|
|
|
44
44
|
return [$sig.get(), $sig]
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export function useLocalDoc$ (collection, id) {
|
|
48
|
+
if (collection == null) throw Error('useLocalDoc() expects a collection name')
|
|
49
|
+
if (id == null) return undefined
|
|
50
|
+
return $root[collection][id]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useLocalDoc (collection, id) {
|
|
54
|
+
const $doc = useLocalDoc$(collection, id)
|
|
55
|
+
if (!$doc) return [undefined, undefined]
|
|
56
|
+
return [$doc.get(), $doc]
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
export function useSession$ (path) {
|
|
48
60
|
return useLocal$(prefixLocalPath('_session', path))
|
|
49
61
|
}
|
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.2",
|
|
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": "ad790a31ac628c9ea5ac851572533620ac0b7602"
|
|
85
85
|
}
|
package/react/helpers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext, createContext, useRef, useEffect } from 'react'
|
|
1
|
+
import { useContext, createContext, useRef, useEffect, useLayoutEffect } from 'react'
|
|
2
2
|
|
|
3
3
|
export const ComponentMetaContext = createContext({})
|
|
4
4
|
|
|
@@ -69,6 +69,52 @@ export function useUnmount (fn) {
|
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
export function useDidUpdate (fn, deps) {
|
|
73
|
+
const isFirst = useRef(true)
|
|
74
|
+
const stableDeps = useStableDeps(deps)
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (isFirst.current) {
|
|
77
|
+
isFirst.current = false
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
return fn()
|
|
81
|
+
}, [fn, stableDeps])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function useOnce (condition, fn) {
|
|
85
|
+
const fired = useRef(false)
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (fired.current) return
|
|
88
|
+
if (!condition) return
|
|
89
|
+
fired.current = true
|
|
90
|
+
return fn()
|
|
91
|
+
}, [condition, fn])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function useSyncEffect (fn, deps) {
|
|
95
|
+
const stableDeps = useStableDeps(deps)
|
|
96
|
+
useLayoutEffect(fn, [fn, stableDeps])
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function useStableDeps (deps) {
|
|
100
|
+
const depsRef = useRef([])
|
|
101
|
+
const nextDeps = Array.isArray(deps) ? deps : []
|
|
102
|
+
if (!shallowEqualArrays(depsRef.current, nextDeps)) {
|
|
103
|
+
depsRef.current = nextDeps
|
|
104
|
+
}
|
|
105
|
+
return depsRef.current
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function shallowEqualArrays (a, b) {
|
|
109
|
+
if (a === b) return true
|
|
110
|
+
if (!Array.isArray(a) || !Array.isArray(b)) return false
|
|
111
|
+
if (a.length !== b.length) return false
|
|
112
|
+
for (let i = 0; i < a.length; i++) {
|
|
113
|
+
if (!Object.is(a[i], b[i])) return false
|
|
114
|
+
}
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
|
|
72
118
|
const ERRORS = {
|
|
73
119
|
useTriggerUpdate: `
|
|
74
120
|
useTriggerUpdate() can only be used inside a component wrapped with observer().
|
package/react/useApi.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function useApi (api, args = [], options = {}) {
|
|
4
|
+
const { debounce = 0 } = options || {}
|
|
5
|
+
const [data, setData] = useState()
|
|
6
|
+
const [error, setError] = useState()
|
|
7
|
+
const [loading, setLoading] = useState(false)
|
|
8
|
+
const requestIdRef = useRef(0)
|
|
9
|
+
const stableArgs = useStableDeps(Array.isArray(args) ? args : [args])
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (typeof api !== 'function') return
|
|
13
|
+
let cancelled = false
|
|
14
|
+
const requestId = ++requestIdRef.current
|
|
15
|
+
let timer
|
|
16
|
+
|
|
17
|
+
const run = async () => {
|
|
18
|
+
try {
|
|
19
|
+
setLoading(true)
|
|
20
|
+
const result = await api(...stableArgs)
|
|
21
|
+
if (cancelled || requestId !== requestIdRef.current) return
|
|
22
|
+
setData(result)
|
|
23
|
+
setError(undefined)
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (cancelled || requestId !== requestIdRef.current) return
|
|
26
|
+
setError(err)
|
|
27
|
+
} finally {
|
|
28
|
+
if (!cancelled && requestId === requestIdRef.current) setLoading(false)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (debounce > 0) {
|
|
33
|
+
timer = setTimeout(run, debounce)
|
|
34
|
+
} else {
|
|
35
|
+
run()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
cancelled = true
|
|
40
|
+
if (timer) clearTimeout(timer)
|
|
41
|
+
}
|
|
42
|
+
}, [api, debounce, stableArgs])
|
|
43
|
+
|
|
44
|
+
return [data, loading, error]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function useStableDeps (deps) {
|
|
48
|
+
const depsRef = useRef([])
|
|
49
|
+
if (!shallowEqualArrays(depsRef.current, deps)) {
|
|
50
|
+
depsRef.current = deps
|
|
51
|
+
}
|
|
52
|
+
return depsRef.current
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function shallowEqualArrays (a, b) {
|
|
56
|
+
if (a === b) return true
|
|
57
|
+
if (!Array.isArray(a) || !Array.isArray(b)) return false
|
|
58
|
+
if (a.length !== b.length) return false
|
|
59
|
+
for (let i = 0; i < a.length; i++) {
|
|
60
|
+
if (!Object.is(a[i], b[i])) return false
|
|
61
|
+
}
|
|
62
|
+
return true
|
|
63
|
+
}
|