@live-change/prosemirror-service 0.2.51 → 0.3.0

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.
Files changed (3) hide show
  1. package/index.js +123 -62
  2. package/model.js +3 -2
  3. 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 { Document, StepsBucket, schemas, getDocument } = require("./model.js")
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
- document: {
16
- type: Document,
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
- document: {
33
- type: Document,
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 path = StepsBucket.rangePath([props.document], App.extractRange(props))
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
- document: {
56
- type: Document,
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({ document, version }, { client, context }) {
64
- return Snapshot.path( App.encodeIdentifier([props.document,version.toFixed().padStart(10, '0')]) )
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
- document: {
72
- type: Document,
104
+ targetType: {
105
+ type: String,
73
106
  validation: ['nonEmpty']
74
107
  },
75
- ...App.rangeProperties
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
- version: {
90
- type: Number
91
- }
112
+ ...App.rangeProperties
92
113
  },
93
- async daoPath({ document, version }, { client, context }) {
94
- Snapshot.limitedRangePath([props.document], { limit: 10 })
95
- return Snapshot.path( App.encodeIdentifier([props.document,version.toFixed().padStart(10, '0')]) )
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
- document: {
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
- async execute({ document, type, purpose, content }, { client, service }, emit) {
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: 0
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
- document: {
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
- async execute({ document, type, purpose, content }, { client, service }, emit) {
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: 0
205
+ type, purpose, content, version: 1
169
206
  }
170
207
  }
171
208
  })
172
209
 
173
210
  definition.action({
174
- name: 'doSteps',
211
+ name: 'edit',
212
+ accessControl: writerAccessControl,
175
213
  waitForEvents: true,
214
+ queuedBy: ['targetType', 'target'],
176
215
  properties: {
177
- document: {
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.client.document,
201
- async execute({ document, type, version, steps, window, continuation }, { client, service }, emit) {
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 documentData = await getDocument(document, type)
205
- if(!documentData) throw new Error('document not found')
206
- if(documentData.version != version) return 'ignored'
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(!documentData.lastStepsBucket
213
- || documentData.lastStepsBucket.sessionOrUserType != sessionOrUserType
214
- || documentData.lastStepsBucket.sessionOrUser != sessionOrUser
215
- || documentData.lastStepsBucket.window != window) {
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 [] // ignore, client will rebase
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 'processed'
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
- document: {
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.client.document,
249
- async execute({ document, type, version }, { client, service }, emit) {
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
- document: {
323
+ targetType: {
269
324
  type: String,
270
325
  validation: ['nonEmpty']
271
326
  },
272
- type: {
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.client.document,
281
- async execute({ document, type, version }, { client, service }, emit) {
282
- if(!schemas[type]) throw new Error(`schema not found for document type ${type}`)
283
- const documentData = await getDocument(document, type)
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: type, version
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 = 0
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.2.51",
3
+ "version": "0.3.0",
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.6.14",
25
- "@live-change/relations-plugin": "0.6.14",
24
+ "@live-change/framework": "0.7.2",
25
+ "@live-change/relations-plugin": "0.7.2",
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": "0f25467a8e4d5050355b407aca0060b08824f024"
31
+ "gitHead": "9cb5f8b01ecbcf4d841274ae112872370ea9d927"
32
32
  }