@knowlearning/agents 0.9.127 → 0.9.129

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.
@@ -1,6 +1,6 @@
1
1
  import { validate as isUUID, v1 as uuid } from 'uuid'
2
+ import PatchProxy from '@knowlearning/patch-proxy'
2
3
  import watchImplementation from '../watch.js'
3
- import MutableProxy from '../../persistence/json.js'
4
4
 
5
5
  export default function EmbeddedAgent() {
6
6
  let messageIndex = 0
@@ -113,7 +113,7 @@ export default function EmbeddedAgent() {
113
113
  }
114
114
  tagIfNotYetTaggedInSession('subscribed', scope)
115
115
  const startState = await send({ type: 'state', scope, user, domain })
116
- return new MutableProxy(startState, patch => {
116
+ return new PatchProxy(startState, patch => {
117
117
  // TODO: reject updates if user is not owner
118
118
  const activePatch = structuredClone(patch)
119
119
  activePatch.forEach(entry => entry.path.unshift('active'))
@@ -199,7 +199,7 @@ export default function EmbeddedAgent() {
199
199
 
200
200
  async function metadata(scope, user, domain) {
201
201
  const md = await send({ type: 'metadata', scope, user, domain })
202
- return new MutableProxy(md, patch => {
202
+ return new PatchProxy(md, patch => {
203
203
  const activePatch = structuredClone(patch)
204
204
  activePatch.forEach(entry => {
205
205
  if (!isValidMetadataMutation(entry)) throw new Error('You may only modify the type or name for a scope\'s metadata')
@@ -151,10 +151,6 @@ function embed(environment, iframe) {
151
151
  }
152
152
  })
153
153
 
154
- // write in a temporary loading notification while frame loads
155
- const cw = iframe.contentWindow
156
- if (cw) cw.document.body.innerHTML = 'Loading...'
157
-
158
154
  // TODO: make sure content security policy headers for embedded domain always restrict iframe
159
155
  // src to only self for embedded domain
160
156
  iframe.onload = () => {
@@ -1,5 +1,5 @@
1
1
  import { validate as isUUID } from 'uuid'
2
- import MutableProxy from '../../persistence/json.js'
2
+ import PatchProxy from '@knowlearning/patch-proxy'
3
3
  import messageQueue from './message-queue.js'
4
4
  import stateImplementation from './state.js'
5
5
  import watchImplementation from '../watch.js'
@@ -154,7 +154,7 @@ export default function Agent({ Connection, domain, token, uuid, fetch, applyPat
154
154
  async function metadata(id=DEFAULT_SCOPE_NAME, user, domain) {
155
155
  const md = structuredClone(await state(id, user).metadata)
156
156
  delete md.active
157
- return new MutableProxy(md, patch => {
157
+ return new PatchProxy(md, patch => {
158
158
  const activePatch = structuredClone(patch)
159
159
  if (!activePatch.every(isValidMetadataMutation)) {
160
160
  throw new Error("You may only modify the type or name for a scope's metadata")
@@ -1,24 +1,13 @@
1
1
  import { validate as isUUID } from 'uuid'
2
+ import { standardJSONPatch } from '@knowlearning/patch-proxy'
2
3
 
3
4
  const HEARTBEAT_TIMEOUT = 10000
4
5
  const DOMAIN_MESSAGES = { open: true, mutate: true, close: true }
5
6
 
6
- // transform our custom path implementation to the standard JSONPatch path
7
- function standardJSONPatch(patch) {
8
- return patch.map(p => {
9
- return {...p, path: '/' + p.path.map(sanitizeJSONPatchPathSegment).join('/')}
10
- })
11
- }
12
-
13
7
  function activePatch(patch) {
14
8
  return structuredClone(patch).filter(({ path }) => 'active' === path.shift())
15
9
  }
16
10
 
17
- function sanitizeJSONPatchPathSegment(s) {
18
- if (typeof s === "string") return s.replaceAll('~', '~0').replaceAll('/', '~1')
19
- else return s
20
- }
21
-
22
11
  export default function messageQueue({ token, domain, Connection, watchers, states, applyPatch, log, login, reboot, handleDomainMessage, trigger }) {
23
12
  let connection
24
13
  let user
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuid, validate as isUUID } from 'uuid'
2
- import MutableProxy from '../../persistence/json.js'
2
+ import PatchProxy from '@knowlearning/patch-proxy'
3
3
 
4
4
  export default function(scope='[]', user, domain, { keyToSubscriptionId, watchers, states, create, environment, lastMessageResponse, lastInteractionResponse, tagIfNotYetTaggedInSession, interact, log }) {
5
5
  let resolveMetadataPromise
@@ -34,7 +34,7 @@ export default function(scope='[]', user, domain, { keyToSubscriptionId, watcher
34
34
  const active = data.active
35
35
  delete data.active
36
36
  resolveMetadataPromise(data)
37
- resolveState(new MutableProxy(active || {}, patch => {
37
+ resolveState(new PatchProxy(active || {}, patch => {
38
38
  const activePatch = structuredClone(patch)
39
39
  activePatch.forEach(entry => entry.path.unshift('active'))
40
40
  interact(scope, activePatch)
package/agents/watch.js CHANGED
@@ -80,9 +80,13 @@ export default function({ metadata, environment, state, watchers, synced, sentUp
80
80
  }
81
81
 
82
82
  function removeWatcher(key, fn) {
83
+ if (!watchers[key]) {
84
+ console.warn('NO WATCHERS FOR KEY', key, fn)
85
+ return
86
+ }
83
87
  const watcherIndex = watchers[key].findIndex(x => x === fn)
84
88
  if (watcherIndex > -1) watchers[key].splice(watcherIndex, 1)
85
- else console.warn('TRIED TO REMOVE WATCHER THAT DOES NOT EXIST')
89
+ else console.warn('TRIED TO REMOVE WATCHER THAT DOES NOT EXIST', key, fn)
86
90
  }
87
91
 
88
92
  return [ watch, removeWatcher ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowlearning/agents",
3
- "version": "0.9.127",
3
+ "version": "0.9.129",
4
4
  "description": "API for embedding applications in KnowLearning systems.",
5
5
  "main": "node.js",
6
6
  "browser": "browser.js",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "homepage": "https://github.com/knowlearning/platform#readme",
25
25
  "dependencies": {
26
+ "@knowlearning/patch-proxy": "^1.3.1",
26
27
  "fast-json-patch": "^3.1.1",
27
28
  "uuid": "^8.3.2",
28
29
  "vue": "^3.3.4",
@@ -1,5 +1,4 @@
1
- import MutableProxy from '../../../persistence/json.js'
2
-
1
+ import PatchProxy from '@knowlearning/patch-proxy'
3
2
  // TODO: consider path serialization approach. Also consider just using a mutable proxy from agent.state...
4
3
 
5
4
  const copy = x => JSON.parse(JSON.stringify(x))
@@ -29,7 +28,7 @@ export default async function (storeDefinition, scope) {
29
28
  const ephemeralPaths = {}
30
29
  Object.keys(scopedPaths).forEach(key => ephemeralPaths[key] = true)
31
30
 
32
- stateAttachedStore.state = () => MutableProxy(state, handlePatch, ephemeralPaths)
31
+ stateAttachedStore.state = () => PatchProxy(state, handlePatch, ephemeralPaths)
33
32
  return stateAttachedStore
34
33
  }
35
34
 
@@ -70,7 +69,7 @@ async function attachModuleState(state, module, scopedPaths, path='') {
70
69
  }
71
70
  const initState = await Agent.state(scope)
72
71
  const ephemeralPaths = descendantPaths(path, scopedPaths)
73
- state = MutableProxy(copy(initState), handlePatch, ephemeralPaths)
72
+ state = PatchProxy(copy(initState), handlePatch, ephemeralPaths)
74
73
  if (await scopeIsUninitialized(scope)) {
75
74
  Object.assign(state, module.state instanceof Function ? module.state() : module.state)
76
75
  }
@@ -1,95 +0,0 @@
1
- const proxies = new Set()
2
-
3
- // TODO make serialization more reliable... basically: handle if prop includes "
4
- function serializePath(path) {
5
- return `/${path.join('/')}`
6
- }
7
-
8
- // TODO: add extra argument for "ignorePrefixes" to ignore interactions at certain paths
9
- export default function MutableProxy(state, interact, ephemeralPaths={}, parentPath=[], rootState) {
10
- if (ephemeralPaths[serializePath(parentPath)]) return state
11
- if (!rootState) rootState = state
12
- if (proxies.has(state)) {
13
- // TODO: simply return with no effect if redundantly setting state like a[x] = a[x]
14
- throw new Error(`Cannot add mutable state to multiple mutable parents. Attempted Path: ${serializePath(parentPath)}`)
15
- }
16
-
17
- const isArray = Array.isArray(state)
18
-
19
- const childMutableProxy = (prop, value) => MutableProxy(
20
- value,
21
- interact,
22
- ephemeralPaths,
23
- [...parentPath, prop],
24
- rootState
25
- )
26
-
27
- // recursively ensure child objects are converted to proxies (unless they are in an ephemeral path)
28
- Object
29
- .entries(state)
30
- .filter(([, value]) => value instanceof Object)
31
- .forEach(([key, value]) => {
32
- if (isArray && /^\d+$/.test(key)) key = parseInt(key)
33
- state[key] = childMutableProxy(key, value)
34
- })
35
-
36
- const traps = {
37
- set(target, prop, value) {
38
- if (isArray) {
39
- if (!/^\d+$/.test(prop)) {
40
- target[prop] = value
41
- return true
42
- }
43
- else prop = parseInt(prop)
44
- }
45
-
46
- const path = [...parentPath, prop]
47
- const serializedPath = serializePath(path)
48
-
49
- if (ephemeralPaths[serializedPath]) {
50
- target[prop] = value
51
- return true
52
- }
53
-
54
- if (value === undefined) {
55
- console.log('EXTRA ERROR INFO. target, prop, value', target, prop, value)
56
- throw new Error(`Setting properties to undefined is not supported. Please use a delete statement. Attempted to set ${serializedPath}`)
57
- }
58
-
59
- // TODO: probably want to batch interactions that represent array mutations... and/or do
60
- // something smart around insertions/deletions
61
- // TODO: if is array and prop is last element, consider passing -1 as prop
62
- interact([{
63
- op: target[prop] === undefined ? 'add' : 'replace',
64
- value: JSON.parse(JSON.stringify(value)), // TODO: more efficient sanitization
65
- path
66
- }])
67
-
68
- if (value instanceof Object) target[prop] = childMutableProxy(prop, value)
69
- else target[prop] = value
70
-
71
- return true
72
- },
73
- deleteProperty(target, prop) {
74
- if (prop in target) {
75
- proxies.delete(proxy)
76
- delete target[prop]
77
-
78
- if (isArray) {
79
- if (!/^\d+$/.test(prop)) return true
80
- else prop = parseInt(prop)
81
- }
82
-
83
- // TODO: if is array and prop is last element, consider passing -1 as prop
84
- const path = [...parentPath, prop]
85
- if (!ephemeralPaths[serializePath(path)]) interact([{ op: 'remove', path }])
86
- }
87
- return true
88
- }
89
- }
90
-
91
- const proxy = new Proxy(state, traps)
92
- proxies.add(proxy)
93
-
94
- return proxy
95
- }