@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.
- package/agents/browser/embedded.js +3 -3
- package/agents/browser/initialize.js +0 -4
- package/agents/generic/index.js +2 -2
- package/agents/generic/message-queue.js +1 -12
- package/agents/generic/state.js +2 -2
- package/agents/watch.js +5 -1
- package/package.json +2 -1
- package/vue/3/persist/vuex.js +3 -4
- package/persistence/json.js +0 -95
|
@@ -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
|
|
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
|
|
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 = () => {
|
package/agents/generic/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validate as isUUID } from 'uuid'
|
|
2
|
-
import
|
|
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
|
|
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
|
package/agents/generic/state.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuid, validate as isUUID } from 'uuid'
|
|
2
|
-
import
|
|
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
|
|
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.
|
|
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",
|
package/vue/3/persist/vuex.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
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 = () =>
|
|
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 =
|
|
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
|
}
|
package/persistence/json.js
DELETED
|
@@ -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
|
-
}
|