@knowlearning/agents 0.9.15 → 0.9.17

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.
@@ -209,7 +209,7 @@ export default function EmbeddedAgent() {
209
209
  function disconnect() { return send({ type: 'disconnect' }) }
210
210
  function reconnect() { return send({ type: 'reconnect' }) }
211
211
  function synced() { return send({ type: 'synced' }) }
212
- function close() { return send({ type: 'close' }) }
212
+ function close(info) { return send({ type: 'close', info }) }
213
213
 
214
214
  return {
215
215
  embedded: true,
@@ -54,8 +54,7 @@ function embed(environment, iframe) {
54
54
  sendDown({})
55
55
  }
56
56
  else if (type === 'close') {
57
- console.log('closing?', listeners)
58
- if (listeners.close) listeners.close()
57
+ if (listeners.close) listeners.close(message.info)
59
58
  }
60
59
  else if (type === 'environment') {
61
60
  const env = await Agent.environment()
package/agents/generic.js CHANGED
@@ -56,6 +56,8 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
56
56
  const environmentPromise = new Promise(r => resolveEnvironment = r)
57
57
  let lastSentSI = -1
58
58
  let lastHeartbeat
59
+ let lastSynchronousScopePatched = null
60
+ let lastSynchronousScopePatchPromise = null
59
61
  const syncedPromiseResolutions = []
60
62
 
61
63
  const patches = state('patches')
@@ -77,12 +79,20 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
77
79
  if (watcherIndex > -1) watchers[key].splice(watcherIndex, 1)
78
80
  else console.warn('TRIED TO REMOVE WATCHER THAT DOES NOT EXIST')
79
81
  }
80
- function flushMessageQueue() {
81
- // TODO: probably want to make this loop async so we don't try and push more to
82
- // a closed connection
82
+
83
+ // TODO: clear acknowledged messages
84
+ async function flushMessageQueue() {
85
+ // this makes flushing async, giving time for queue message to combine synchronous updates
86
+ await new Promise(resolve => resolve())
87
+ lastSynchronousScopePatched = null
88
+
83
89
  while (authed && ws.readyState === WebSocket.OPEN && lastSentSI+1 < messageQueue.length) {
90
+ lastSynchronousScopePatched = null
84
91
  lastSentSI += 1
85
92
  ws.send(JSON.stringify(messageQueue[lastSentSI]))
93
+
94
+ // async so we don't try and push more to a closed connection
95
+ await new Promise(resolve => resolve())
86
96
  }
87
97
  }
88
98
 
@@ -90,13 +100,21 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
90
100
  return new Promise((resolve, reject) => responses[si].push([resolve, reject]))
91
101
  }
92
102
 
93
- function queueMessage(message) {
103
+ function queueMessage({ scope, patch }) {
94
104
  isSynced = false
95
- si += 1
96
- const promise = new Promise((resolve, reject) => responses[si] = [[resolve, reject]])
97
- messageQueue.push({...message, si, ts: Date.now()})
98
- flushMessageQueue()
99
- return promise
105
+ if (lastSynchronousScopePatched === scope) {
106
+ const i = messageQueue.length - 1
107
+ messageQueue[i].patch = [...messageQueue[i].patch, ...patch]
108
+ }
109
+ else {
110
+ si += 1
111
+ lastSynchronousScopePatchPromise = new Promise((resolve, reject) => responses[si] = [[resolve, reject]])
112
+ messageQueue.push({ scope, patch, si, ts: Date.now()})
113
+ lastSynchronousScopePatched = scope
114
+ flushMessageQueue()
115
+ }
116
+
117
+ return lastSynchronousScopePatchPromise
100
118
  }
101
119
 
102
120
  function checkHeartbeat() {
@@ -114,11 +132,11 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
114
132
  async function restartConnection() {
115
133
  if (restarting) return
116
134
 
117
- restarting = true
118
135
  authed = false
119
- ws.onmessage = () => {} // needs to be a no-op since a closing ws can still get messages
120
136
  if (!disconnected) {
121
137
  await new Promise(r => setTimeout(r, Math.min(1000, failedConnections * 100)))
138
+ ws.onmessage = () => {} // needs to be a no-op since a closing ws can still get messages
139
+ restarting = true
122
140
  failedConnections += 1
123
141
  initWS() // TODO: don't do this if we are purposefully unloading...
124
142
  restarting = false
@@ -231,8 +249,12 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
231
249
 
232
250
  function create({ id=uuid(), active_type, active, name }) {
233
251
  // TODO: collapse into 1 patch and 1 interact call
234
- interact(id, [{ op: 'add', path: ['active_type'], value: active_type }], false)
235
- interact(id, [{ op: 'add', path: ['active'], value: active }], false)
252
+ // (requires updating side effects)
253
+ const patch = [
254
+ { op: 'add', path: ['active_type'], value: active_type },
255
+ { op: 'add', path: ['active'], value: active }
256
+ ]
257
+ interact(id, patch, false)
236
258
  return id
237
259
  }
238
260
 
@@ -300,7 +322,45 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
300
322
  })
301
323
  }
302
324
 
325
+ function watchResolution(path, callback) {
326
+ const id = path[0]
327
+ const references = path.slice(1)
328
+ let unwatchDeeper = () => {}
329
+
330
+ const unwatch = watch(id, ({ state }) => {
331
+ if (references.length === 0) {
332
+ callback(value)
333
+ return
334
+ }
335
+
336
+ // TODO: check if value we care about actually changed
337
+ // and ignore this update if it has not.
338
+ unwatchDeeper()
339
+
340
+ let value = state
341
+ for (let index = 0; index < references.length; index += 1) {
342
+ value = value[references[index]]
343
+ if (
344
+ value === null ||
345
+ value === undefined ||
346
+ index === references.length - 1
347
+ ) callback(value)
348
+ else if (isUUID(value)) {
349
+ unwatchDeeper = watchResolution([value, ...references.slice(index + 1)], callback)
350
+ return
351
+ }
352
+ }
353
+ })
354
+
355
+ return () => {
356
+ unwatch()
357
+ unwatchDeeper()
358
+ }
359
+ }
360
+
303
361
  function watch(scope=DEFAULT_SCOPE_NAME, fn) {
362
+ if (Array.isArray(scope)) return watchResolution(scope, fn)
363
+
304
364
  let initialSent = false
305
365
  const queue = []
306
366
  function cb(update) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowlearning/agents",
3
- "version": "0.9.15",
3
+ "version": "0.9.17",
4
4
  "description": "API for embedding applications in KnowLearning systems.",
5
5
  "main": "node.js",
6
6
  "browser": "browser.js",
@@ -28,29 +28,7 @@ export default function (module, scope) {
28
28
  }
29
29
  }
30
30
  else if (component.setup) {
31
- const origSetupFn = component.setup
32
- const isReactiveRef = r => r && r.__v_isRef
33
- component.setup = function setupProxy(a, b) { // need to specifically add this here, otherwise second argument not passed in arguments array (probably because of webpack optimization...)
34
- let refs = origSetupFn.call(this, a, b)
35
-
36
- Object
37
- .entries(state)
38
- .filter(([k]) => isReactiveRef(refs[k]))
39
- .forEach(([k, v]) => refs[k].value = v)
40
-
41
- Object
42
- .entries(refs)
43
- .filter(([_,r]) => isReactiveRef(r) && isScopeSerializable(r.value))
44
- .forEach(([key, ref]) => {
45
- watchEffect(() => {
46
- if (state[key] !== ref.value) {
47
- state[key] = ref.value
48
- }
49
- })
50
- })
51
-
52
- return refs
53
- }
31
+ throw new Error('vuePersistantCompoent is for components using the Options API. To use the composition API see https://docs.knowlearning.systems/frameworks/vue/')
54
32
  }
55
33
  }
56
34