@knowlearning/agents 0.9.188 → 0.9.189

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.
@@ -0,0 +1,56 @@
1
+ import { applyPatch } from 'fast-json-patch'
2
+ import { standardJSONPatch } from '@knowlearning/patch-proxy'
3
+
4
+ // Wraps a state promise with a .synced(callback?) method that applies external
5
+ // patches to the live proxy in-place.
6
+ //
7
+ // Usage:
8
+ // const promise = attachSynced(watchers, (ctx, resolve) => {
9
+ // const p = new Promise(async (...) => {
10
+ // const proxy = new PatchProxy(data, patch => {
11
+ // if (ctx.applyingExternalUpdate) return
12
+ // // ... interact
13
+ // })
14
+ // resolve(proxy, qualifiedKey)
15
+ // resolvePromise(proxy)
16
+ // })
17
+ // return p
18
+ // })
19
+ export default function attachSynced(watchers, init) {
20
+ const ctx = { applyingExternalUpdate: false }
21
+ let shouldSync = false
22
+ let syncCallbacks = []
23
+ let syncRegistered = false
24
+ let resolvedProxy = null
25
+ let resolvedKey = null
26
+
27
+ function registerSyncWatcher() {
28
+ if (syncRegistered) return
29
+ syncRegistered = true
30
+ if (!watchers[resolvedKey]) watchers[resolvedKey] = []
31
+ watchers[resolvedKey].push(({ patch }) => {
32
+ if (!patch) return
33
+ ctx.applyingExternalUpdate = true
34
+ applyPatch(resolvedProxy, standardJSONPatch(patch))
35
+ ctx.applyingExternalUpdate = false
36
+ syncCallbacks.forEach(cb => cb(resolvedProxy, patch))
37
+ })
38
+ }
39
+
40
+ function resolve(proxy, key) {
41
+ resolvedProxy = proxy
42
+ resolvedKey = key
43
+ if (shouldSync) registerSyncWatcher()
44
+ }
45
+
46
+ const promise = init(ctx, resolve)
47
+
48
+ promise.synced = function(callback) {
49
+ shouldSync = true
50
+ if (callback) syncCallbacks.push(callback)
51
+ if (resolvedProxy) registerSyncWatcher()
52
+ return promise
53
+ }
54
+
55
+ return promise
56
+ }
@@ -1,5 +1,6 @@
1
1
  import { validate as isUUID, v1 as uuid } from 'uuid'
2
2
  import PatchProxy from '@knowlearning/patch-proxy'
3
+ import attachSynced from './attach-synced.js'
3
4
  import watchImplementation from './watch.js'
4
5
  import sync from './sync.js'
5
6
 
@@ -96,18 +97,27 @@ export default function EmbeddedAgent(postMessage) {
96
97
  return send({ type: 'patch', root, scopes })
97
98
  }
98
99
 
99
- async function state(scope, user, domain) {
100
- if (scope === undefined) {
101
- const { context } = await environment()
102
- scope = JSON.stringify(context)
103
- }
104
- const startState = await send({ type: 'state', scope, user, domain })
105
- return new PatchProxy(startState, patch => {
106
- // TODO: reject updates if user is not owner
107
- const activePatch = structuredClone(patch)
108
- activePatch.forEach(entry => entry.path.unshift('active'))
109
- interact(scope, activePatch)
110
- })
100
+ function state(scope, user, domain) {
101
+ return attachSynced(watchers, (ctx, resolveSync) => (async () => {
102
+ if (scope === undefined) {
103
+ const { context } = await environment()
104
+ scope = JSON.stringify(context)
105
+ }
106
+ const startState = await send({ type: 'state', scope, user, domain })
107
+ const { auth, domain: rootDomain } = await environment()
108
+ const d = !domain || domain === rootDomain ? '' : domain
109
+ const u = !user || auth.user === user ? '' : user
110
+ const key = isUUID(scope) ? scope : `${d}/${u}/${scope}`
111
+ const proxy = new PatchProxy(startState, patch => {
112
+ // TODO: reject updates if user is not owner
113
+ if (ctx.applyingExternalUpdate) return
114
+ const activePatch = structuredClone(patch)
115
+ activePatch.forEach(entry => entry.path.unshift('active'))
116
+ interact(scope, activePatch)
117
+ })
118
+ resolveSync(proxy, key)
119
+ return proxy
120
+ })())
111
121
  }
112
122
 
113
123
  function reset(scope) {
@@ -1,11 +1,12 @@
1
1
  import { v4 as uuid, validate as isUUID } from 'uuid'
2
2
  import PatchProxy from '@knowlearning/patch-proxy'
3
+ import attachSynced from '../attach-synced.js'
3
4
 
4
5
  export default function(scope='[]', user, domain, { keyToSubscriptionId, watchers, states, create, environment, lastMessageResponse, lastInteractionResponse, interact, log }) {
5
6
  let resolveMetadataPromise
6
7
  let metadataPromise = new Promise(resolve => resolveMetadataPromise = resolve)
7
8
 
8
- const statePromise = new Promise(async (resolveState, rejectState) => {
9
+ const statePromise = attachSynced(watchers, (ctx, resolveSync) => new Promise(async (resolveState, rejectState) => {
9
10
  const { auth: { user: u }, domain: d, session } = await environment()
10
11
 
11
12
  const qualifiedScope = isUUID(scope) ? scope : `${!domain || domain === d ? '' : domain}/${!user || user === u ? '' : user}/${scope}`
@@ -35,17 +36,20 @@ export default function(scope='[]', user, domain, { keyToSubscriptionId, watcher
35
36
  const active = data.active
36
37
  delete data.active
37
38
  resolveMetadataPromise(data)
38
- resolveState(new PatchProxy(active || {}, patch => {
39
+ const proxy = new PatchProxy(active || {}, patch => {
40
+ if (ctx.applyingExternalUpdate) return
39
41
  const activePatch = structuredClone(patch)
40
42
  activePatch.forEach(entry => entry.path.unshift('active'))
41
43
  interact(scope, activePatch)
42
- }))
44
+ })
45
+ resolveSync(proxy, qualifiedScope)
46
+ resolveState(proxy)
43
47
  }
44
48
  catch (error) {
45
49
  rejectState(error)
46
50
  }
47
- })
51
+ }))
48
52
 
49
53
  statePromise.metadata = metadataPromise
50
54
  return statePromise
51
- }
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowlearning/agents",
3
- "version": "0.9.188",
3
+ "version": "0.9.189",
4
4
  "description": "API for embedding applications in KnowLearning systems.",
5
5
  "main": "node.js",
6
6
  "browser": "browser.js",
package/types.d.ts CHANGED
@@ -27,11 +27,16 @@ export interface AgentUploadInfo {
27
27
  accept?: string;
28
28
  }
29
29
 
30
+ export interface SyncedStatePromise extends Promise<object> {
31
+ synced(callback?: (state: object, patch: object[]) => void): SyncedStatePromise;
32
+ metadata: Promise<object>;
33
+ }
34
+
30
35
  export interface Agent {
31
36
  login(provider: string): void;
32
37
  logout(): void;
33
38
  uuid(): string;
34
- state(id: string, user?: string, domain?: string): Promise<object>;
39
+ state(id: string, user?: string, domain?: string): SyncedStatePromise;
35
40
  metadata(id: string, user?: string, domain?: string): Promise<object>;
36
41
  watch(id: string, callback: (update: { state: object }) => void, user?: string, domain?: string): void;
37
42
  upload(info?: AgentUploadInfo): Promise<string>;