@live-change/prosemirror-service 0.2.52 → 0.3.1
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.js +123 -62
- package/model.js +3 -2
- package/package.json +4 -4
package/index.js
CHANGED
|
@@ -3,19 +3,38 @@ const app = App.app()
|
|
|
3
3
|
|
|
4
4
|
const definition = require('./definition.js')
|
|
5
5
|
const config = definition.config
|
|
6
|
+
const {
|
|
7
|
+
readerRoles = ['writer'],
|
|
8
|
+
writerRoles = ['writer'],
|
|
9
|
+
testLatency
|
|
10
|
+
} = config
|
|
6
11
|
|
|
7
|
-
const
|
|
12
|
+
const writerAccessControl = {
|
|
13
|
+
roles: writerRoles,
|
|
14
|
+
objects: p => [{ objectType: p.targetType, object: p.target }]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const readerAccessControl = {
|
|
18
|
+
roles: readerRoles,
|
|
19
|
+
objects: p => [{ objectType: p.targetType, object: p.target }]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { Document, StepsBucket, Snapshot, schemas, getDocument } = require("./model.js")
|
|
8
23
|
|
|
9
|
-
const { testLatency } = config
|
|
10
24
|
const sleep = ms => new Promise(r => setTimeout(r, ms))
|
|
11
25
|
|
|
12
26
|
definition.view({
|
|
13
27
|
name: 'document',
|
|
28
|
+
accessControl: readerAccessControl,
|
|
14
29
|
properties: {
|
|
15
|
-
|
|
16
|
-
type:
|
|
30
|
+
targetType: {
|
|
31
|
+
type: String,
|
|
17
32
|
validation: ['nonEmpty']
|
|
18
|
-
}
|
|
33
|
+
},
|
|
34
|
+
target: {
|
|
35
|
+
type: String,
|
|
36
|
+
validation: ['nonEmpty']
|
|
37
|
+
},
|
|
19
38
|
},
|
|
20
39
|
returns: {
|
|
21
40
|
type: Document
|
|
@@ -28,9 +47,14 @@ definition.view({
|
|
|
28
47
|
|
|
29
48
|
definition.view({
|
|
30
49
|
name: 'steps',
|
|
50
|
+
accessControl: readerAccessControl,
|
|
31
51
|
properties: {
|
|
32
|
-
|
|
33
|
-
type:
|
|
52
|
+
targetType: {
|
|
53
|
+
type: String,
|
|
54
|
+
validation: ['nonEmpty']
|
|
55
|
+
},
|
|
56
|
+
target: {
|
|
57
|
+
type: String,
|
|
34
58
|
validation: ['nonEmpty']
|
|
35
59
|
},
|
|
36
60
|
...App.rangeProperties
|
|
@@ -43,7 +67,9 @@ definition.view({
|
|
|
43
67
|
},
|
|
44
68
|
async daoPath(props, { client, context }) {
|
|
45
69
|
if(testLatency) await sleep(testLatency)
|
|
46
|
-
const
|
|
70
|
+
const { targetType, target } = props
|
|
71
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
72
|
+
const path = StepsBucket.rangePath([document], App.extractRange(props))
|
|
47
73
|
console.log("PATH", path)
|
|
48
74
|
return path
|
|
49
75
|
}
|
|
@@ -51,56 +77,57 @@ definition.view({
|
|
|
51
77
|
|
|
52
78
|
definition.view({
|
|
53
79
|
name: 'snapshot',
|
|
80
|
+
accessControl: readerAccessControl,
|
|
54
81
|
properties: {
|
|
55
|
-
|
|
56
|
-
type:
|
|
82
|
+
targetType: {
|
|
83
|
+
type: String,
|
|
84
|
+
validation: ['nonEmpty']
|
|
85
|
+
},
|
|
86
|
+
target: {
|
|
87
|
+
type: String,
|
|
57
88
|
validation: ['nonEmpty']
|
|
58
89
|
},
|
|
59
90
|
version: {
|
|
60
91
|
type: Number
|
|
61
92
|
}
|
|
62
93
|
},
|
|
63
|
-
async daoPath({
|
|
64
|
-
|
|
94
|
+
async daoPath({ targetType, target, version }, { client, context }) {
|
|
95
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
96
|
+
return Snapshot.path( App.encodeIdentifier([document,version.toFixed().padStart(10, '0')]) )
|
|
65
97
|
}
|
|
66
98
|
})
|
|
67
99
|
|
|
68
100
|
definition.view({
|
|
69
101
|
name: 'snapshots',
|
|
102
|
+
accessControl: readerAccessControl,
|
|
70
103
|
properties: {
|
|
71
|
-
|
|
72
|
-
type:
|
|
104
|
+
targetType: {
|
|
105
|
+
type: String,
|
|
73
106
|
validation: ['nonEmpty']
|
|
74
107
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
async daoPath({ document, version }, { client, context }) {
|
|
78
|
-
return Snapshot.indexRangePath( [document], App.extractRange(props) )
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
definition.view({
|
|
83
|
-
name: 'snapshots',
|
|
84
|
-
properties: {
|
|
85
|
-
document: {
|
|
86
|
-
type: Document,
|
|
108
|
+
target: {
|
|
109
|
+
type: String,
|
|
87
110
|
validation: ['nonEmpty']
|
|
88
111
|
},
|
|
89
|
-
|
|
90
|
-
type: Number
|
|
91
|
-
}
|
|
112
|
+
...App.rangeProperties
|
|
92
113
|
},
|
|
93
|
-
async daoPath({
|
|
94
|
-
|
|
95
|
-
return Snapshot.
|
|
114
|
+
async daoPath({ targetType, target, version }, { client, context }) {
|
|
115
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
116
|
+
return Snapshot.indexRangePath( [document], App.extractRange(props) )
|
|
96
117
|
}
|
|
97
118
|
})
|
|
98
119
|
|
|
99
120
|
definition.action({
|
|
100
121
|
name: 'createDocument',
|
|
122
|
+
accessControl: writerAccessControl,
|
|
101
123
|
waitForEvents: true,
|
|
124
|
+
queuedBy: ['targetType', 'target'],
|
|
102
125
|
properties: {
|
|
103
|
-
|
|
126
|
+
targetType: {
|
|
127
|
+
type: String,
|
|
128
|
+
validation: ['nonEmpty']
|
|
129
|
+
},
|
|
130
|
+
target: {
|
|
104
131
|
type: String,
|
|
105
132
|
validation: ['nonEmpty']
|
|
106
133
|
},
|
|
@@ -116,9 +143,11 @@ definition.action({
|
|
|
116
143
|
type: Object
|
|
117
144
|
}
|
|
118
145
|
},
|
|
119
|
-
|
|
146
|
+
queuedBy: (command) => [command.targetType, command.target],
|
|
147
|
+
async execute({ targetType, target, type, purpose, content }, { client, service }, emit) {
|
|
120
148
|
if(testLatency) await sleep(testLatency)
|
|
121
149
|
if(!schemas[type]) throw new Error(`schema not found for document type ${type}`)
|
|
150
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
122
151
|
const documentData = await Document.get(document)
|
|
123
152
|
if(documentData) throw new Error('document already exists')
|
|
124
153
|
emit({
|
|
@@ -127,16 +156,22 @@ definition.action({
|
|
|
127
156
|
})
|
|
128
157
|
return {
|
|
129
158
|
id: document,
|
|
130
|
-
type, purpose, content, version:
|
|
159
|
+
type, purpose, content, version: 1
|
|
131
160
|
}
|
|
132
161
|
}
|
|
133
162
|
})
|
|
134
163
|
|
|
135
164
|
definition.action({
|
|
136
165
|
name: 'createDocumentIfNotExists',
|
|
166
|
+
accessControl: writerAccessControl,
|
|
137
167
|
waitForEvents: true,
|
|
168
|
+
queuedBy: ['targetType', 'target'],
|
|
138
169
|
properties: {
|
|
139
|
-
|
|
170
|
+
targetType: {
|
|
171
|
+
type: String,
|
|
172
|
+
validation: ['nonEmpty']
|
|
173
|
+
},
|
|
174
|
+
target: {
|
|
140
175
|
type: String,
|
|
141
176
|
validation: ['nonEmpty']
|
|
142
177
|
},
|
|
@@ -152,9 +187,11 @@ definition.action({
|
|
|
152
187
|
type: Object
|
|
153
188
|
}
|
|
154
189
|
},
|
|
155
|
-
|
|
190
|
+
queuedBy: (command) => [command.targetType, command.target],
|
|
191
|
+
async execute({ targetType, target, type, purpose, content }, { client, service }, emit) {
|
|
156
192
|
if(testLatency) await sleep(testLatency)
|
|
157
193
|
if(!schemas[type]) throw new Error(`schema not found for document type ${type}`)
|
|
194
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
158
195
|
const documentData = await Document.get(document)
|
|
159
196
|
if(documentData) {
|
|
160
197
|
return documentData
|
|
@@ -165,19 +202,25 @@ definition.action({
|
|
|
165
202
|
})
|
|
166
203
|
return {
|
|
167
204
|
id: document,
|
|
168
|
-
type, purpose, content, version:
|
|
205
|
+
type, purpose, content, version: 1
|
|
169
206
|
}
|
|
170
207
|
}
|
|
171
208
|
})
|
|
172
209
|
|
|
173
210
|
definition.action({
|
|
174
|
-
name: '
|
|
211
|
+
name: 'edit',
|
|
212
|
+
accessControl: writerAccessControl,
|
|
175
213
|
waitForEvents: true,
|
|
214
|
+
queuedBy: ['targetType', 'target'],
|
|
176
215
|
properties: {
|
|
177
|
-
|
|
216
|
+
targetType: {
|
|
178
217
|
type: String,
|
|
179
218
|
validation: ['nonEmpty']
|
|
180
219
|
},
|
|
220
|
+
target: {
|
|
221
|
+
type: String,
|
|
222
|
+
validation: ['nonEmpty']
|
|
223
|
+
},
|
|
181
224
|
type: {
|
|
182
225
|
type: String,
|
|
183
226
|
validation: ['nonEmpty']
|
|
@@ -197,27 +240,31 @@ definition.action({
|
|
|
197
240
|
type: Boolean
|
|
198
241
|
}
|
|
199
242
|
},
|
|
200
|
-
queuedBy: (command) => command.
|
|
201
|
-
async execute({
|
|
243
|
+
queuedBy: (command) => [command.targetType, command.target],
|
|
244
|
+
async execute({ targetType, target, type, version, steps, window, continuation }, { client, service }, emit) {
|
|
202
245
|
if(testLatency) await sleep(testLatency)
|
|
203
246
|
if(!schemas[type]) throw new Error(`schema not found for document type ${type}`)
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
if(
|
|
247
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
248
|
+
const openDocument = await getDocument(document, type)
|
|
249
|
+
if(!openDocument) throw new Error('document not found')
|
|
250
|
+
console.log("EDIT DOCUMENT", openDocument.version, 'WITH', version)
|
|
251
|
+
if(openDocument.version != version) {
|
|
252
|
+
console.error("WRONG VERSION", openDocument.version, '!=', version)
|
|
253
|
+
return 'rejected'
|
|
254
|
+
}
|
|
207
255
|
const [sessionOrUserType, sessionOrUser] =
|
|
208
256
|
client.user ? ['user_User', client.user] : ['session_Session', client.session]
|
|
209
257
|
if(continuation) {
|
|
210
258
|
//console.log("DOC DATA", documentData)
|
|
211
259
|
//console.log("CONTINUATION", documentData.lastStepsBucket, sessionOrUserType, sessionOrUser)
|
|
212
|
-
if(!
|
|
213
|
-
||
|
|
214
|
-
||
|
|
215
|
-
||
|
|
260
|
+
if(!openDocument.lastStepsBucket
|
|
261
|
+
|| openDocument.lastStepsBucket.sessionOrUserType != sessionOrUserType
|
|
262
|
+
|| openDocument.lastStepsBucket.sessionOrUser != sessionOrUser
|
|
263
|
+
|| openDocument.lastStepsBucket.window != window) {
|
|
216
264
|
console.log("CONTINUATION IGNORED!!")
|
|
217
|
-
return
|
|
265
|
+
return 'rejected' // ignore, client will rebase
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
|
-
|
|
221
268
|
emit({
|
|
222
269
|
type: 'documentEdited',
|
|
223
270
|
document, documentType: type, version, steps, window,
|
|
@@ -225,15 +272,21 @@ definition.action({
|
|
|
225
272
|
sessionOrUser,
|
|
226
273
|
timestamp: new Date()
|
|
227
274
|
})
|
|
228
|
-
return '
|
|
275
|
+
return 'saved'
|
|
229
276
|
}
|
|
230
277
|
})
|
|
231
278
|
|
|
232
279
|
definition.action({
|
|
233
280
|
name: 'takeSnapshot',
|
|
281
|
+
accessControl: writerAccessControl,
|
|
234
282
|
waitForEvents: true,
|
|
283
|
+
queuedBy: ['targetType', 'target'],
|
|
235
284
|
properties: {
|
|
236
|
-
|
|
285
|
+
targetType: {
|
|
286
|
+
type: String,
|
|
287
|
+
validation: ['nonEmpty']
|
|
288
|
+
},
|
|
289
|
+
target: {
|
|
237
290
|
type: String,
|
|
238
291
|
validation: ['nonEmpty']
|
|
239
292
|
},
|
|
@@ -245,9 +298,10 @@ definition.action({
|
|
|
245
298
|
type: Number
|
|
246
299
|
},
|
|
247
300
|
},
|
|
248
|
-
queuedBy: (command) => command.
|
|
249
|
-
async execute({
|
|
301
|
+
queuedBy: (command) => [command.targetType, command.target],
|
|
302
|
+
async execute({ targetType, target, type, version }, { client, service }, emit) {
|
|
250
303
|
if(!schemas[type]) throw new Error(`schema not found for document type ${type}`)
|
|
304
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
251
305
|
const documentData = await getDocument(document, type)
|
|
252
306
|
if(!documentData) throw new Error('document not found')
|
|
253
307
|
if(typeof version != 'number') version = documentData.version
|
|
@@ -264,12 +318,17 @@ definition.action({
|
|
|
264
318
|
definition.trigger({
|
|
265
319
|
name: 'takeSnapshot',
|
|
266
320
|
waitForEvents: true,
|
|
321
|
+
queuedBy: ['targetType', 'target'],
|
|
267
322
|
properties: {
|
|
268
|
-
|
|
323
|
+
targetType: {
|
|
269
324
|
type: String,
|
|
270
325
|
validation: ['nonEmpty']
|
|
271
326
|
},
|
|
272
|
-
|
|
327
|
+
target: {
|
|
328
|
+
type: String,
|
|
329
|
+
validation: ['nonEmpty']
|
|
330
|
+
},
|
|
331
|
+
documentType: {
|
|
273
332
|
type: String,
|
|
274
333
|
validation: ['nonEmpty']
|
|
275
334
|
},
|
|
@@ -277,17 +336,19 @@ definition.trigger({
|
|
|
277
336
|
type: Number
|
|
278
337
|
},
|
|
279
338
|
},
|
|
280
|
-
queuedBy: (command) => command.
|
|
281
|
-
async execute({
|
|
282
|
-
if(!schemas[
|
|
283
|
-
const
|
|
339
|
+
queuedBy: (command) => [command.targetType, command.target],
|
|
340
|
+
async execute({ targetType, target, documentType, version }, { client, service }, emit) {
|
|
341
|
+
if(!schemas[documentType]) throw new Error(`schema not found for document type ${documentType}`)
|
|
342
|
+
const document = App.encodeIdentifier([targetType, target])
|
|
343
|
+
const documentData = await getDocument(document, documentType)
|
|
284
344
|
if(!documentData) throw new Error('document not found')
|
|
285
345
|
if(typeof version != 'number') version = documentData.version
|
|
346
|
+
if(version > documentData.version) throw new Error("version not found")
|
|
286
347
|
const snapshot = App.encodeIdentifier([document, version.toFixed().padStart(10, '0')])
|
|
287
348
|
emit({
|
|
288
349
|
type: 'snapshotTaken',
|
|
289
350
|
snapshot,
|
|
290
|
-
document, documentType
|
|
351
|
+
document, documentType, version
|
|
291
352
|
})
|
|
292
353
|
return snapshot
|
|
293
354
|
}
|
package/model.js
CHANGED
|
@@ -121,7 +121,7 @@ async function getDocument(documentId, documentType) {
|
|
|
121
121
|
definition.event({
|
|
122
122
|
name: "documentCreated",
|
|
123
123
|
async execute({ document, documentType, purpose, content, created, lastModified }) {
|
|
124
|
-
const version =
|
|
124
|
+
const version = 1
|
|
125
125
|
await Document.create({ id: document, type: documentType, purpose, content, created, lastModified, version })
|
|
126
126
|
await Snapshot.create({ id: App.encodeIdentifier([document, version.toFixed().padStart(10, '0')]),
|
|
127
127
|
document, version, content, timestamp: lastModified })
|
|
@@ -199,7 +199,8 @@ definition.event({
|
|
|
199
199
|
if(!openDocument) throw new Error('critical error - document not found') /// impossible
|
|
200
200
|
if(openDocument.version < version)
|
|
201
201
|
throw new Error('critical error - document version is lower than snapshot version') /// impossible
|
|
202
|
-
const existing = Snapshot.get(id)
|
|
202
|
+
const existing = await Snapshot.get(id)
|
|
203
|
+
console.log("SNAP", existing, openDocument.version, version)
|
|
203
204
|
if(existing) return id
|
|
204
205
|
if(openDocument.version == version) {
|
|
205
206
|
const content = openDocument.content
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/prosemirror-service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
"url": "https://www.viamage.com/"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@live-change/framework": "0.
|
|
25
|
-
"@live-change/relations-plugin": "0.
|
|
24
|
+
"@live-change/framework": "0.7.3",
|
|
25
|
+
"@live-change/relations-plugin": "0.7.3",
|
|
26
26
|
"lru-cache": "^7.12.0",
|
|
27
27
|
"pluralize": "8.0.0",
|
|
28
28
|
"progress-stream": "^2.0.0",
|
|
29
29
|
"prosemirror-model": "^1.18.1"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "0217e49963ae913ab28c1e07dcbea72ceb588bc1"
|
|
32
32
|
}
|