@cuboapp/crdt 1.0.6 → 1.0.8
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 +1 -1
- package/src/client/index.ts +57 -26
- package/src/client/types/index.ts +4 -2
- package/src/constants/index.ts +1 -0
- package/src/server/document/index.ts +14 -21
- package/src/server/index.ts +297 -164
- package/src/server/types/document.ts +3 -1
- package/src/server/types/index.ts +8 -5
- package/src/types/index.ts +1 -1
- package/src/utils/index.ts +54 -24
- package/src/client/old.ts +0 -249
- package/src/server/old/document/index.ts +0 -107
- package/src/server/old/index.ts +0 -49
package/src/utils/index.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { CuboApiEntitiesMap } from '@cuboapp/types'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CuboCrdtClient,
|
|
5
|
+
CuboCrdtClientOptions,
|
|
6
|
+
CuboCrdtServer,
|
|
7
|
+
CuboCrdtServerDocumentOrigin,
|
|
8
|
+
CuboCrdtServerOptions,
|
|
9
|
+
CuboCrdtServerSubscribe
|
|
10
|
+
} from '..'
|
|
4
11
|
|
|
5
|
-
export function createCrdtServer<M extends CuboApiEntitiesMap<M>, A = {}
|
|
6
|
-
|
|
12
|
+
export function createCrdtServer<M extends CuboApiEntitiesMap<M>, A = {}, E extends Extract<keyof M, string> = Extract<keyof M, string>>(
|
|
13
|
+
opts: CuboCrdtServerOptions<M, A, E>
|
|
14
|
+
) {
|
|
15
|
+
return new CuboCrdtServer<M, A, E>(opts)
|
|
7
16
|
}
|
|
8
17
|
|
|
9
18
|
export function createCrdtClient<M extends CuboApiEntitiesMap<M>>(opts: CuboCrdtClientOptions<M>) {
|
|
@@ -30,35 +39,43 @@ export function cleanObject<F>(object: F): any {
|
|
|
30
39
|
return object
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
export function checkRowIsSutable(
|
|
42
|
+
export function checkRowIsSutable(
|
|
43
|
+
row: any,
|
|
44
|
+
filters?: object,
|
|
45
|
+
customKeys?: Record<string, (key: string, row: any, filters?: any) => boolean>
|
|
46
|
+
) {
|
|
34
47
|
let sutable = true
|
|
35
48
|
|
|
36
49
|
if (Object.keys(filters || {}).length) {
|
|
37
50
|
Object.entries(filters || {}).forEach(([key, value]) => {
|
|
38
51
|
if (!['limit', 'with'].includes(key)) {
|
|
39
|
-
|
|
52
|
+
if (customKeys?.[key]) {
|
|
53
|
+
sutable = customKeys[key](key, row, filters)
|
|
54
|
+
} else {
|
|
55
|
+
const [formula, str] = `${value}`.split(':')
|
|
40
56
|
|
|
41
|
-
|
|
57
|
+
// console.log({ formula, str, value: row[key] })
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
sutable = false
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
switch (formula) {
|
|
49
|
-
case 'in':
|
|
50
|
-
if (
|
|
51
|
-
!str
|
|
52
|
-
.split(',')
|
|
53
|
-
.map(Number)
|
|
54
|
-
.includes(row[key as keyof typeof row] as any)
|
|
55
|
-
) {
|
|
56
|
-
sutable = false
|
|
57
|
-
}
|
|
58
|
-
break
|
|
59
|
-
default:
|
|
59
|
+
if (!str) {
|
|
60
|
+
if (row[key as keyof typeof row] !== value) {
|
|
60
61
|
sutable = false
|
|
61
|
-
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
switch (formula) {
|
|
65
|
+
case 'in':
|
|
66
|
+
if (
|
|
67
|
+
!str
|
|
68
|
+
.split(',')
|
|
69
|
+
.map(Number)
|
|
70
|
+
.includes(row[key as keyof typeof row] as any)
|
|
71
|
+
) {
|
|
72
|
+
sutable = false
|
|
73
|
+
}
|
|
74
|
+
break
|
|
75
|
+
default:
|
|
76
|
+
sutable = false
|
|
77
|
+
break
|
|
78
|
+
}
|
|
62
79
|
}
|
|
63
80
|
}
|
|
64
81
|
}
|
|
@@ -67,3 +84,16 @@ export function checkRowIsSutable(row: object, filters?: object) {
|
|
|
67
84
|
|
|
68
85
|
return sutable
|
|
69
86
|
}
|
|
87
|
+
|
|
88
|
+
export function checkSubscribeIsSutable(origin: CuboCrdtServerDocumentOrigin, subscribe?: CuboCrdtServerSubscribe) {
|
|
89
|
+
if (!subscribe) {
|
|
90
|
+
return false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const noStrategyResolved = !origin?.expose || origin?.expose === 'all'
|
|
94
|
+
const strategyOtherResolved = (origin?.expose === 'other' && origin.subscribe_id !== subscribe?.id) || false
|
|
95
|
+
const strategyClientResolved = origin?.expose === 'client' && subscribe?.client_id === origin.client_id
|
|
96
|
+
const strategySubscribeResolved = origin?.expose === 'subscribe' && subscribe?.id === origin.subscribe_id
|
|
97
|
+
|
|
98
|
+
return noStrategyResolved || strategyOtherResolved || strategyClientResolved || strategySubscribeResolved
|
|
99
|
+
}
|
package/src/client/old.ts
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import { uuid } from '@cuboapp/utils'
|
|
2
|
-
import { WsClient } from '@cuboapp/ws'
|
|
3
|
-
import { computed, ComputedRef, Reactive, reactive } from 'vue'
|
|
4
|
-
import { applyUpdate, Doc } from 'yjs'
|
|
5
|
-
|
|
6
|
-
export type CuboCrdtClientStoreElement<M, K> = {
|
|
7
|
-
rows: Reactive<Map<number, K extends keyof M ? M[K] : unknown>>
|
|
8
|
-
docs: Map<number, Doc>
|
|
9
|
-
flags: Reactive<{
|
|
10
|
-
inited: boolean
|
|
11
|
-
}>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export type CuboCrdtClientSubscribeOptions<S, F> = {
|
|
15
|
-
entity_id?: number
|
|
16
|
-
storeKey?: S
|
|
17
|
-
filters?: F
|
|
18
|
-
autoInit?: boolean
|
|
19
|
-
onUpdate?: () => void
|
|
20
|
-
onInit?: () => void
|
|
21
|
-
debug?: boolean
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type CuboCrdtClientSubscribe<M = {}, K = {}, S = {}, F = {}> = {
|
|
25
|
-
id: string
|
|
26
|
-
storeKey: S
|
|
27
|
-
store: CuboCrdtClientStoreElement<M, K>
|
|
28
|
-
init: (filters?: F) => void
|
|
29
|
-
destroy: (opts?: { clear?: boolean }) => void
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type CuboCrdtClientOptions = {
|
|
33
|
-
ws: WsClient
|
|
34
|
-
debug?: boolean
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export class CuboCrdtClient<M> {
|
|
38
|
-
constructor(private opts: CuboCrdtClientOptions) {}
|
|
39
|
-
|
|
40
|
-
public store: Partial<{ [K in Extract<keyof M, string>]: CuboCrdtClientStoreElement<M, K> }> = {}
|
|
41
|
-
|
|
42
|
-
private get ws() {
|
|
43
|
-
return this.opts.ws
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
private getFilters<F>(filters: F) {
|
|
47
|
-
if (Array.isArray(filters)) {
|
|
48
|
-
return filters.map(this.getFilters).filter(Boolean)
|
|
49
|
-
} else if (typeof filters === 'object' && filters !== null) {
|
|
50
|
-
const newObj: any = {}
|
|
51
|
-
|
|
52
|
-
for (const key in filters) {
|
|
53
|
-
const value = this.getFilters(filters[key])
|
|
54
|
-
|
|
55
|
-
if (value !== undefined && value !== null && value !== '' && !(typeof value === 'object' && Object.keys(value).length === 0)) {
|
|
56
|
-
newObj[key] = value
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return newObj
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return filters
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
subscribe<K extends Extract<keyof M, string>, S extends string, F extends Record<string, any>>(
|
|
67
|
-
entity: K,
|
|
68
|
-
opts?: CuboCrdtClientSubscribeOptions<S, F>
|
|
69
|
-
): CuboCrdtClientSubscribe<M, K, S, F> {
|
|
70
|
-
const subscribe_id = uuid()
|
|
71
|
-
const storeKey = opts?.storeKey ?? (entity as any)
|
|
72
|
-
|
|
73
|
-
if (opts?.debug) {
|
|
74
|
-
console.trace('subscribe tasks', subscribe_id, opts)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (this.store[storeKey] === undefined) {
|
|
78
|
-
this.store[storeKey] = {
|
|
79
|
-
flags: reactive({
|
|
80
|
-
inited: false
|
|
81
|
-
}),
|
|
82
|
-
rows: reactive(new Map()),
|
|
83
|
-
docs: new Map()
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const onUpdateDocument = (id: number, data: Uint8Array, fireOnUpdate?: boolean) => {
|
|
88
|
-
let item: any
|
|
89
|
-
|
|
90
|
-
// обновляем документ
|
|
91
|
-
|
|
92
|
-
let exDoc = this.store[storeKey].docs.get(id)
|
|
93
|
-
if (!exDoc) {
|
|
94
|
-
const doc = new Doc()
|
|
95
|
-
applyUpdate(doc, data)
|
|
96
|
-
|
|
97
|
-
doc.on('update', (update, origin) => {
|
|
98
|
-
if (!origin?.no_push) {
|
|
99
|
-
this.ws.request({
|
|
100
|
-
method: 'crdt:update',
|
|
101
|
-
data: {
|
|
102
|
-
subscribe_id,
|
|
103
|
-
name: `${entity}:${id}`,
|
|
104
|
-
update: Array.from(update),
|
|
105
|
-
origin
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
this.store[storeKey].docs.set(id, doc)
|
|
112
|
-
exDoc = doc
|
|
113
|
-
} else {
|
|
114
|
-
applyUpdate(exDoc, data, { no_push: true })
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
item = exDoc.getMap().toJSON()
|
|
118
|
-
|
|
119
|
-
// обновляем строку
|
|
120
|
-
this.store[storeKey].rows.set(id, item as any)
|
|
121
|
-
|
|
122
|
-
if (opts?.debug) {
|
|
123
|
-
console.log('fire onUpdate', { fireOnUpdate })
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (fireOnUpdate !== false) {
|
|
127
|
-
opts?.onUpdate?.()
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const init = (filters?: F) => {
|
|
132
|
-
filters = filters !== undefined ? filters : opts?.filters
|
|
133
|
-
|
|
134
|
-
if (this.store[storeKey].flags.inited) {
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// подписываемся на обновления
|
|
139
|
-
this.ws.registerHandler('crdt:action:' + subscribe_id, (ctx) => {
|
|
140
|
-
const { action, entity_id, data } = ctx.message.data as any
|
|
141
|
-
|
|
142
|
-
switch (action) {
|
|
143
|
-
case 'update':
|
|
144
|
-
onUpdateDocument(entity_id, new Uint8Array(data), true)
|
|
145
|
-
break
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
this.ws
|
|
150
|
-
.request(
|
|
151
|
-
{
|
|
152
|
-
method: 'crdt:subscribe',
|
|
153
|
-
data: this.getFilters({
|
|
154
|
-
id: subscribe_id,
|
|
155
|
-
entity,
|
|
156
|
-
entity_id: opts?.entity_id,
|
|
157
|
-
filters
|
|
158
|
-
})
|
|
159
|
-
},
|
|
160
|
-
true
|
|
161
|
-
)
|
|
162
|
-
.then((data: any) => {
|
|
163
|
-
if (opts?.entity_id) {
|
|
164
|
-
onUpdateDocument(data.id, new Uint8Array(data.row), false)
|
|
165
|
-
} else {
|
|
166
|
-
data.map(({ id, row }: { id: number; row: number[] }) => onUpdateDocument(id, new Uint8Array(row), false))
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
this.store[storeKey].flags.inited = true
|
|
170
|
-
opts?.onInit?.()
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const destroy = ({ clear }: { clear?: boolean } = {}) => {
|
|
175
|
-
if (!this.store[storeKey].flags.inited) {
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.ws.request(
|
|
180
|
-
{
|
|
181
|
-
method: 'crdt:unsubscribe',
|
|
182
|
-
data: this.getFilters({
|
|
183
|
-
id: subscribe_id,
|
|
184
|
-
entity,
|
|
185
|
-
entity_id: opts?.entity_id,
|
|
186
|
-
filters: opts?.filters
|
|
187
|
-
})
|
|
188
|
-
},
|
|
189
|
-
true
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
this.ws.deleteHandler('crdt:update:' + subscribe_id)
|
|
193
|
-
|
|
194
|
-
if (clear !== false) {
|
|
195
|
-
this.store[storeKey] = undefined
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (opts?.autoInit !== false) {
|
|
200
|
-
init()
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
id: subscribe_id,
|
|
205
|
-
storeKey,
|
|
206
|
-
store: this.store[storeKey] as any,
|
|
207
|
-
init,
|
|
208
|
-
destroy
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
useList<K extends string, T = K extends keyof M ? M[K] : unknown>(key: K): ComputedRef<T[]> {
|
|
213
|
-
return computed(() => {
|
|
214
|
-
return Array.from(this.store[key as any].rows || []).map((m) => m[1] as T)
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
change<K extends string = Extract<keyof M, string>, T = K extends keyof M ? M[K] : unknown>(
|
|
219
|
-
entity: K,
|
|
220
|
-
entity_id: number,
|
|
221
|
-
dto: Partial<T> & Record<string, any>,
|
|
222
|
-
opts?: { strategy?: 'other' | 'all' }
|
|
223
|
-
) {
|
|
224
|
-
const row = this.store[entity as any].rows.get(entity_id)
|
|
225
|
-
const doc = this.store[entity as any].docs.get(entity_id)
|
|
226
|
-
|
|
227
|
-
if (doc) {
|
|
228
|
-
const map = doc.getMap()
|
|
229
|
-
|
|
230
|
-
const toUpdate = Object.fromEntries(Object.entries(dto).filter(([key, value]) => row[key] !== value))
|
|
231
|
-
|
|
232
|
-
if (Object.keys(toUpdate).length) {
|
|
233
|
-
doc.transact(
|
|
234
|
-
() => {
|
|
235
|
-
Object.entries(toUpdate).forEach(([key, value]) => {
|
|
236
|
-
map.set(key, value)
|
|
237
|
-
row[key] = value
|
|
238
|
-
})
|
|
239
|
-
},
|
|
240
|
-
{ with_save: true, strategy: opts?.strategy ?? 'other', keys: Object.keys(toUpdate) }
|
|
241
|
-
)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
useRow<K extends Extract<keyof M, string>, T = K extends keyof M ? M[K] : unknown>(entity: K, entity_id: number): ComputedRef<T> {
|
|
247
|
-
return computed(() => this.store[entity].rows.get(entity_id)) as ComputedRef<T>
|
|
248
|
-
}
|
|
249
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'
|
|
2
|
-
|
|
3
|
-
export class CuboCrdtDocument {
|
|
4
|
-
private ydoc: Doc
|
|
5
|
-
private subs = new Map<string, (data: Uint8Array<ArrayBufferLike>, origin?: any) => void>()
|
|
6
|
-
|
|
7
|
-
constructor(
|
|
8
|
-
public opts: {
|
|
9
|
-
name: string
|
|
10
|
-
initialState?: any
|
|
11
|
-
autoRemove?: boolean
|
|
12
|
-
onDelete?: () => void
|
|
13
|
-
yjsOptions?: {
|
|
14
|
-
guid?: string
|
|
15
|
-
collectionid?: string
|
|
16
|
-
gc?: boolean
|
|
17
|
-
gcFilter?: () => true
|
|
18
|
-
meta?: any
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
) {}
|
|
22
|
-
|
|
23
|
-
get stateAsUpdate() {
|
|
24
|
-
return encodeStateAsUpdate(this.ydoc)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async init() {
|
|
28
|
-
this.ydoc = new Doc({
|
|
29
|
-
...(this.opts.yjsOptions || {}),
|
|
30
|
-
autoLoad: false
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const state = this.opts?.initialState ?? {}
|
|
34
|
-
|
|
35
|
-
return new Promise<Uint8Array<ArrayBufferLike>>((resolve) => {
|
|
36
|
-
this.ydoc.once('update', () => {
|
|
37
|
-
this.ydoc.on('update', (data, origin) => {
|
|
38
|
-
this.subs.forEach((sub) => sub(data, origin))
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
resolve(this.stateAsUpdate)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const map = this.getMap()
|
|
45
|
-
|
|
46
|
-
this.ydoc.transact(() => {
|
|
47
|
-
if (Object.keys(state).length) {
|
|
48
|
-
Object.entries(state).forEach(([key, value]) => {
|
|
49
|
-
map.set(key, value)
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
this.getState().setAttribute('inited', 'Y')
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get inited() {
|
|
59
|
-
return this.getState().getAttribute('inited') === 'Y'
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get id() {
|
|
63
|
-
return this.ydoc.guid
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
applyUpdate(update: Uint8Array, origin?: any) {
|
|
67
|
-
return applyUpdate(this.ydoc, update, origin)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
subscribe(id: string, cb: (data: Uint8Array<ArrayBufferLike>, origin?: any) => void) {
|
|
71
|
-
this.subs.set(id, cb)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
unsubscribe(id: string) {
|
|
75
|
-
this.subs.delete(id)
|
|
76
|
-
|
|
77
|
-
if (!this.subs.size && this.opts?.autoRemove !== false) {
|
|
78
|
-
this.opts?.onDelete?.()
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
getState() {
|
|
83
|
-
return this.ydoc.getText('state')
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
getMap() {
|
|
87
|
-
return this.ydoc.getMap()
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
getXmlFragment() {
|
|
91
|
-
return this.ydoc.getXmlFragment()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async writeMap(dto: object, origin?: any) {
|
|
95
|
-
return new Promise<void>(async (resolve) => {
|
|
96
|
-
const map = this.getMap()
|
|
97
|
-
|
|
98
|
-
this.ydoc.transact(() => {
|
|
99
|
-
Object.entries(dto).forEach(([key, value]) => {
|
|
100
|
-
map.set(key, value)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
resolve()
|
|
104
|
-
}, origin)
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
}
|
package/src/server/old/index.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { CuboCrdtDocument } from './document'
|
|
2
|
-
|
|
3
|
-
export * from './document'
|
|
4
|
-
|
|
5
|
-
export class CuboCrdtServer<T> {
|
|
6
|
-
constructor(private options: { debug?: boolean }) {}
|
|
7
|
-
|
|
8
|
-
public documents = new Map<string, CuboCrdtDocument>()
|
|
9
|
-
private loading = new Map<string, Promise<CuboCrdtDocument>>()
|
|
10
|
-
|
|
11
|
-
public async getDocument(name: string, opts?: { autoCreate?: boolean; initialState?: any }) {
|
|
12
|
-
// существующий документ
|
|
13
|
-
if (!this.documents.has(name)) {
|
|
14
|
-
if (!this.loading.has(name) && opts?.autoCreate !== false) {
|
|
15
|
-
if (this.options.debug) {
|
|
16
|
-
console.log('CRDT create document load start', name)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
this.loading.set(
|
|
20
|
-
name,
|
|
21
|
-
new Promise(async (resolve) => {
|
|
22
|
-
const document = new CuboCrdtDocument({
|
|
23
|
-
name,
|
|
24
|
-
initialState: opts?.initialState,
|
|
25
|
-
autoRemove: true,
|
|
26
|
-
onDelete: () => {
|
|
27
|
-
this.documents.delete(name)
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
await document.init()
|
|
32
|
-
|
|
33
|
-
resolve(document)
|
|
34
|
-
})
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const document = await this.loading.get(name)
|
|
39
|
-
|
|
40
|
-
this.loading.delete(name)
|
|
41
|
-
|
|
42
|
-
if (document) {
|
|
43
|
-
this.documents.set(name, document)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return this.documents.get(name)
|
|
48
|
-
}
|
|
49
|
-
}
|