solid-logic 1.3.17-0abe2b84 → 1.3.17-3f057286
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/jest.config.js +1 -1
- package/lib/discovery/discoveryLogic.d.ts +6 -9
- package/lib/discovery/discoveryLogic.d.ts.map +1 -1
- package/lib/discovery/discoveryLogic.js +95 -87
- package/lib/discovery/discoveryLogic.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +17 -4
- package/lib/index.js.map +1 -1
- package/package.json +3 -1
- package/src/discovery/discoveryLogic.ts +44 -86
- package/src/index.ts +16 -3
- package/test/discoveryLogic.test.ts +503 -531
- package/test/helpers/setup.ts +0 -3
|
@@ -1,59 +1,40 @@
|
|
|
1
|
-
import { NamedNode,
|
|
2
|
-
|
|
3
|
-
// import { getContainerMembers } from '../util/UtilityLogic'
|
|
1
|
+
import { NamedNode, LiveStore, sym, st } from 'rdflib'
|
|
2
|
+
import * as $rdf from 'rdflib'
|
|
4
3
|
import { solidLogicSingleton } from "../logic/solidLogicSingleton"
|
|
5
4
|
import { newThing } from "../util/uri"
|
|
5
|
+
import solidNamespace from 'solid-namespace'
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { authn } = solidLogicSingleton
|
|
8
8
|
const { currentUser } = authn
|
|
9
|
+
const ns = solidNamespace($rdf)
|
|
9
10
|
|
|
10
|
-
type TypeIndexScope = { label: string, index: NamedNode, agent: NamedNode }
|
|
11
|
+
type TypeIndexScope = { label: string, index: NamedNode, agent: NamedNode }
|
|
11
12
|
type ScopedApp = { instance: NamedNode, scope: TypeIndexScope }
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
meeting: Namespace('http://www.w3.org/ns/pim/meeting#'),
|
|
17
|
-
rdf: Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'),
|
|
18
|
-
schema: Namespace('http://schema.org/'),
|
|
19
|
-
solid: Namespace('http://www.w3.org/ns/solid/terms#'),
|
|
20
|
-
space: Namespace('http://www.w3.org/ns/pim/space#'),
|
|
21
|
-
stat: Namespace('http://www.w3.org/ns/posix/stat#'),
|
|
22
|
-
vcard: Namespace('http://www.w3.org/2006/vcard/ns#'),
|
|
23
|
-
wf: Namespace('http://www.w3.org/2005/01/wf/flow#'),
|
|
24
|
-
xsd: Namespace('http://www.w3.org/2001/XMLSchema#')
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Create a resource if it really does not exist
|
|
29
|
-
* Be absolutely sure something does not exist before creating a new empty file
|
|
14
|
+
/**
|
|
15
|
+
* Create a resource if it really does not exist
|
|
16
|
+
* Be absolutely sure something does not exist before creating a new empty file
|
|
30
17
|
* as otherwise existing could be deleted.
|
|
31
18
|
* @param doc {NamedNode} - The resource
|
|
32
19
|
*/
|
|
33
20
|
export async function loadOrCreateIfNotExists (store: LiveStore, doc: NamedNode) {
|
|
34
21
|
let response
|
|
35
|
-
// console.log('@@ loadOrCreateIfNotExists doc ', doc)
|
|
36
22
|
try {
|
|
37
23
|
response = await store.fetcher.load(doc)
|
|
38
24
|
} catch (err) {
|
|
39
25
|
if (err.response.status === 404) {
|
|
40
|
-
// console.log('createIfNotExists doc does NOT exist, will create: ' + doc)
|
|
41
26
|
try {
|
|
42
27
|
store.fetcher.webOperation('PUT', doc, {data: '', contentType: 'text/turtle'})
|
|
43
28
|
} catch (err) {
|
|
44
29
|
const msg = 'createIfNotExists: PUT FAILED: ' + doc + ': ' + err
|
|
45
|
-
// console.log(msg)
|
|
46
30
|
throw new Error(msg)
|
|
47
31
|
}
|
|
48
32
|
delete store.fetcher.requested[doc.uri] // delete cached 404 error
|
|
49
|
-
// console.log('createIfNotExists doc created ok ' + doc)
|
|
50
33
|
} else {
|
|
51
34
|
const msg = 'createIfNotExists doc load error NOT 404: ' + doc + ': ' + err
|
|
52
|
-
// console.log(msg)
|
|
53
35
|
throw new Error(msg) // @@ add nested errors
|
|
54
36
|
}
|
|
55
37
|
}
|
|
56
|
-
// console.log('createIfNotExists doc exists, all good ' + doc)
|
|
57
38
|
return response
|
|
58
39
|
}
|
|
59
40
|
|
|
@@ -73,20 +54,18 @@ export function suggestPublicTypeIndex (me:NamedNode) {
|
|
|
73
54
|
export function suggestPrivateTypeIndex (preferencesFile:NamedNode) {
|
|
74
55
|
return sym(preferencesFile.doc().dir()?.uri + 'privateTypeIndex.ttl')
|
|
75
56
|
}
|
|
57
|
+
|
|
76
58
|
/* Follow link from this doc to another thing, or else make a new link
|
|
77
59
|
**
|
|
78
|
-
**
|
|
60
|
+
** return: null no ld one and failed to make a new one
|
|
79
61
|
*/
|
|
80
|
-
export async function followOrCreateLink(store: LiveStore, subject: NamedNode, predicate: NamedNode,
|
|
62
|
+
export async function followOrCreateLink (store: LiveStore, subject: NamedNode, predicate: NamedNode,
|
|
81
63
|
object: NamedNode, doc:NamedNode):Promise<NamedNode | null> {
|
|
82
64
|
await store.fetcher.load(doc)
|
|
83
65
|
const result = store.any(subject, predicate, null, doc)
|
|
84
|
-
// console.log('@@ followOrCreateLink result ', result)
|
|
85
66
|
|
|
86
67
|
if (result) return result as NamedNode
|
|
87
68
|
if (!store.updater.editable(doc)) {
|
|
88
|
-
// console.log(`followOrCreateLink: Can't modify ${doc} so can't make new link to ${object}.`)
|
|
89
|
-
// console.log('followOrCreateLink @@ connectedStatements', store.connectedStatements(subject))
|
|
90
69
|
return null
|
|
91
70
|
}
|
|
92
71
|
try {
|
|
@@ -96,58 +75,48 @@ export async function followOrCreateLink(store: LiveStore, subject: NamedNode, p
|
|
|
96
75
|
return null
|
|
97
76
|
}
|
|
98
77
|
|
|
99
|
-
// console.log(`Success making link in ${doc} to ${object}` )
|
|
100
|
-
|
|
101
78
|
try {
|
|
102
79
|
await loadOrCreateIfNotExists(store, object)
|
|
103
80
|
// store.fetcher.webOperation('PUT', object, { data: '', contentType: 'text/turtle'})
|
|
104
81
|
} catch (err) {
|
|
105
82
|
console.warn(`followOrCreateLink: Error loading or saving new linked document: ${object}: ${err}`)
|
|
106
83
|
}
|
|
107
|
-
// console.log(`followOrCreateLink: Success loading or saving new linked document: ${object}.`)
|
|
108
84
|
return object
|
|
109
85
|
}
|
|
110
86
|
|
|
111
|
-
export async function loadProfile(store: LiveStore, user: NamedNode) {
|
|
112
|
-
// console.log(' @@ loadProfile: user', user)
|
|
87
|
+
export async function loadProfile (store: LiveStore, user: NamedNode) {
|
|
113
88
|
if (!user) {
|
|
114
89
|
throw new Error(`loadProfile: no user given.`)
|
|
115
90
|
}
|
|
116
|
-
|
|
91
|
+
try {
|
|
117
92
|
await store.fetcher.load(user.doc())
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
93
|
+
} catch (err) {
|
|
94
|
+
throw new Error(`Unable to load profile of user ${user}: ${err}`)
|
|
95
|
+
}
|
|
121
96
|
return user.doc()
|
|
122
97
|
}
|
|
123
98
|
|
|
124
|
-
export async function loadPreferences(store: LiveStore, user: NamedNode): Promise <NamedNode | undefined > {
|
|
125
|
-
// console.log('loadPreferences @@ user', user)
|
|
99
|
+
export async function loadPreferences (store: LiveStore, user: NamedNode): Promise <NamedNode | undefined > {
|
|
126
100
|
await loadProfile(store as LiveStore, user)
|
|
127
101
|
|
|
128
102
|
const possiblePreferencesFile = suggestPreferencesFile(user)
|
|
129
103
|
|
|
130
104
|
const preferencesFile = await followOrCreateLink(store, user, ns.space('preferencesFile') as NamedNode, possiblePreferencesFile, user.doc())
|
|
131
|
-
// const preferencesFile = store.any(user, ns.space('preferencesFile'), undefined, profile)
|
|
132
105
|
|
|
133
|
-
// console.log('loadPreferences @@ pref file', preferencesFile)
|
|
134
106
|
if (!preferencesFile) {
|
|
135
107
|
const message = `User ${user} has no pointer in profile to preferences file.`
|
|
136
108
|
console.warn(message)
|
|
137
|
-
// throw new Error()
|
|
138
109
|
return undefined
|
|
139
110
|
}
|
|
140
111
|
try {
|
|
141
112
|
await store.fetcher.load(preferencesFile as NamedNode)
|
|
142
|
-
} catch (err) { //
|
|
113
|
+
} catch (err) { // Maybe a permission propblem or origin problem
|
|
143
114
|
return undefined
|
|
144
|
-
// throw new Error(`Unable to load preferences file ${preferencesFile} of user <${user}>: ${err}`)
|
|
145
115
|
}
|
|
146
116
|
return preferencesFile as NamedNode
|
|
147
117
|
}
|
|
148
118
|
|
|
149
|
-
export async function loadTypeIndexesFor(store: LiveStore, user:NamedNode): Promise<Array<TypeIndexScope>> {
|
|
150
|
-
// console.log('@@ loadTypeIndexesFor user', user)
|
|
119
|
+
export async function loadTypeIndexesFor (store: LiveStore, user:NamedNode): Promise<Array<TypeIndexScope>> {
|
|
151
120
|
if (!user) throw new Error(`loadTypeIndexesFor: No user given`)
|
|
152
121
|
const profile = await loadProfile(store, user)
|
|
153
122
|
|
|
@@ -155,9 +124,6 @@ export async function loadTypeIndexesFor(store: LiveStore, user:NamedNode): Prom
|
|
|
155
124
|
|
|
156
125
|
const publicTypeIndex = await followOrCreateLink(store, user, ns.solid('publicTypeIndex') as NamedNode, suggestion, profile)
|
|
157
126
|
|
|
158
|
-
// const publicTypeIndex = store.any(user, ns.solid('publicTypeIndex'), undefined, profile)
|
|
159
|
-
// console.log('@@ loadTypeIndexesFor publicTypeIndex', publicTypeIndex)
|
|
160
|
-
|
|
161
127
|
const publicScopes = publicTypeIndex ? [ { label: 'public', index: publicTypeIndex as NamedNode, agent: user } ] : []
|
|
162
128
|
|
|
163
129
|
let preferencesFile
|
|
@@ -175,7 +141,7 @@ export async function loadTypeIndexesFor(store: LiveStore, user:NamedNode): Prom
|
|
|
175
141
|
|
|
176
142
|
const privateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), undefined, profile) ||
|
|
177
143
|
|
|
178
|
-
|
|
144
|
+
await followOrCreateLink(store, user, ns.solid('privateTypeIndex') as NamedNode, suggestedPrivateTypeIndex, preferencesFile);
|
|
179
145
|
|
|
180
146
|
privateScopes = privateTypeIndex ? [ { label: 'private', index: privateTypeIndex as NamedNode, agent: user } ] : []
|
|
181
147
|
} else {
|
|
@@ -184,7 +150,6 @@ export async function loadTypeIndexesFor(store: LiveStore, user:NamedNode): Prom
|
|
|
184
150
|
const scopes = publicScopes.concat(privateScopes)
|
|
185
151
|
if (scopes.length === 0) return scopes
|
|
186
152
|
const files = scopes.map(scope => scope.index)
|
|
187
|
-
// console.log('@@ loadTypeIndexesFor files ', files)
|
|
188
153
|
try {
|
|
189
154
|
await store.fetcher.load(files)
|
|
190
155
|
} catch (err) {
|
|
@@ -196,14 +161,13 @@ export async function loadTypeIndexesFor(store: LiveStore, user:NamedNode): Prom
|
|
|
196
161
|
export async function loadCommunityTypeIndexes (store:LiveStore, user:NamedNode): Promise<TypeIndexScope[][]> {
|
|
197
162
|
const preferencesFile = await loadPreferences(store, user)
|
|
198
163
|
if (preferencesFile) { // For now, pick up communities as simple links from the preferences file.
|
|
199
|
-
const communities = store.each(user, ns.solid('community'), undefined, preferencesFile as NamedNode)
|
|
200
|
-
|
|
164
|
+
const communities = store.each(user, ns.solid('community'), undefined, preferencesFile as NamedNode).concat(
|
|
165
|
+
store.each(user, ns.solid('community'), undefined, user.doc() as NamedNode)
|
|
166
|
+
)
|
|
201
167
|
let result = []
|
|
202
168
|
for (const org of communities) {
|
|
203
169
|
result = result.concat(await loadTypeIndexesFor(store, org as NamedNode) as any)
|
|
204
170
|
}
|
|
205
|
-
// const communityTypeIndexesPromises = communities.map(async community => await loadTypeIndexesFor(store, community as NamedNode))
|
|
206
|
-
// const result1 = Promise.all(communityTypeIndexesPromises)
|
|
207
171
|
return result
|
|
208
172
|
}
|
|
209
173
|
return [] // No communities
|
|
@@ -214,7 +178,6 @@ export async function loadAllTypeIndexes (store:LiveStore, user:NamedNode) {
|
|
|
214
178
|
}
|
|
215
179
|
|
|
216
180
|
// Utility: remove duplicates from Array of NamedNodes
|
|
217
|
-
|
|
218
181
|
export function uniqueNodes (arr: NamedNode[]): NamedNode[] {
|
|
219
182
|
const uris = arr.map(x => x.uri)
|
|
220
183
|
const set = new Set(uris)
|
|
@@ -223,53 +186,41 @@ export function uniqueNodes (arr: NamedNode[]): NamedNode[] {
|
|
|
223
186
|
return arr2 // Array.from(new Set(arr.map(x => x.uri))).map(u => sym(u))
|
|
224
187
|
}
|
|
225
188
|
|
|
226
|
-
export async function
|
|
227
|
-
// console.log(`getScopedAppsfromIndex agent ${scope.agent} index: ${scope.index}` )
|
|
189
|
+
export async function getScopedAppsFromIndex (store, scope, theClass: NamedNode | null) {
|
|
228
190
|
const index = scope.index
|
|
229
|
-
const registrations = store.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
191
|
+
const registrations = store.statementsMatching(null, ns.solid('instance'), null, index)
|
|
192
|
+
.concat(store.statementsMatching(null, ns.solid('instanceContainer'), null, index))
|
|
193
|
+
.map(st => st.subject)
|
|
194
|
+
const relevant = theClass ? registrations.filter(reg => store.any(reg, ns.solid('forClass'), null, index).sameTerm(theClass))
|
|
195
|
+
: registrations
|
|
196
|
+
const directInstances = relevant.map(reg => store.each(reg as NamedNode, ns.solid('instance'), null, index)).flat()
|
|
234
197
|
let instances = uniqueNodes(directInstances)
|
|
235
198
|
|
|
236
|
-
|
|
237
|
-
let instanceContainers = []
|
|
238
|
-
for (const reg of registrations) {
|
|
239
|
-
const cont = store.any(reg as NamedNode, ns.solid('instanceContainer'), null, index)
|
|
240
|
-
if (cont) {
|
|
241
|
-
// console.log(' @@ getScopedAppsfromIndex got one: ', cont)
|
|
242
|
-
instanceContainers.push(cont)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
*/
|
|
246
|
-
const instanceContainers = registrations.map(
|
|
199
|
+
const instanceContainers = relevant.map(
|
|
247
200
|
reg => store.each(reg as NamedNode, ns.solid('instanceContainer'), null, index)).flat()
|
|
248
201
|
|
|
249
202
|
// instanceContainers may be deprocatable if no one has used them
|
|
250
|
-
|
|
251
203
|
const containers = uniqueNodes(instanceContainers)
|
|
204
|
+
if (containers.length > 0) { console.log('@@ getScopedAppsFromIndex containers ', containers)}
|
|
252
205
|
for (let i = 0; i < containers.length; i++) {
|
|
253
206
|
const cont = containers[i]
|
|
254
207
|
await store.fetcher.load(cont)
|
|
255
208
|
const contents = store.each(cont, ns.ldp('contains'), null, cont)
|
|
256
|
-
// if (contents.length) console.log('getScopedAppsfromIndex @@ instanceContainer contents:', contents)
|
|
257
209
|
instances = instances.concat(contents)
|
|
258
210
|
}
|
|
259
211
|
return instances.map(instance => { return {instance, scope}})
|
|
260
212
|
}
|
|
261
213
|
|
|
262
|
-
|
|
263
214
|
export async function getScopedAppInstances (store:LiveStore, klass: NamedNode, user: NamedNode):Promise<ScopedApp[]> {
|
|
264
|
-
// console.log('getScopedAppInstances @@ ' + user)
|
|
265
215
|
const scopes = await loadAllTypeIndexes(store, user)
|
|
266
216
|
let scopedApps = []
|
|
267
217
|
for (const scope of scopes) {
|
|
268
|
-
const scopedApps0 = await
|
|
218
|
+
const scopedApps0 = await getScopedAppsFromIndex(store, scope, klass) as any
|
|
269
219
|
scopedApps = scopedApps.concat(scopedApps0)
|
|
270
220
|
}
|
|
271
221
|
return scopedApps
|
|
272
222
|
}
|
|
223
|
+
|
|
273
224
|
// This is the function signature which used to be in solid-ui/logic
|
|
274
225
|
// Recommended to use getScopedAppInstances instead as it provides more information.
|
|
275
226
|
//
|
|
@@ -279,7 +230,8 @@ export async function getAppInstances (store:LiveStore, klass: NamedNode): Promi
|
|
|
279
230
|
const scopedAppInstances = await getScopedAppInstances(store, klass, user)
|
|
280
231
|
return scopedAppInstances.map(scoped => scoped.instance)
|
|
281
232
|
}
|
|
282
|
-
|
|
233
|
+
|
|
234
|
+
/*
|
|
283
235
|
* Register a new app in a type index
|
|
284
236
|
* used in chat in bookmark.js (solid-ui)
|
|
285
237
|
* Returns the registration object if successful else null
|
|
@@ -299,7 +251,6 @@ export async function registerInstanceInTypeIndex (
|
|
|
299
251
|
st(registration, ns.solid('instance'), instance, index)
|
|
300
252
|
]
|
|
301
253
|
try {
|
|
302
|
-
console.log('patching index', ins)
|
|
303
254
|
await store.updater.update([], ins)
|
|
304
255
|
} catch (err) {
|
|
305
256
|
const msg = `Unable to register ${instance} in index ${index}: ${err}`
|
|
@@ -308,4 +259,11 @@ export async function registerInstanceInTypeIndex (
|
|
|
308
259
|
}
|
|
309
260
|
return registration
|
|
310
261
|
}
|
|
262
|
+
|
|
263
|
+
export async function deleteTypeIndexRegistration (store: LiveStore, item) {
|
|
264
|
+
const reg = store.the(null, ns.solid('instance'), item.instance, item.scope.index) as NamedNode
|
|
265
|
+
if (!reg) throw new Error(`deleteTypeIndexRegistration: No registration found for ${item.instance}`)
|
|
266
|
+
const statements = store.statementsMatching(reg, null, null, item.scope.index)
|
|
267
|
+
await store.updater.update(statements, [])
|
|
268
|
+
}
|
|
311
269
|
// ENDS
|
package/src/index.ts
CHANGED
|
@@ -20,12 +20,25 @@ export {
|
|
|
20
20
|
loadIndex
|
|
21
21
|
} from './typeIndex/typeIndexLogic'
|
|
22
22
|
|
|
23
|
+
// Generate by
|
|
24
|
+
// grep export src/discovery/discoveryLogic.ts | sed -e 's/export //g' | sed -e 's/async //g'| sed -e 's/function //g' | sed -e 's/ .*/,/g' | sort
|
|
23
25
|
export {
|
|
24
|
-
|
|
26
|
+
deleteTypeIndexRegistration,
|
|
27
|
+
followOrCreateLink,
|
|
28
|
+
getAppInstances,
|
|
29
|
+
getScopedAppInstances,
|
|
30
|
+
getScopedAppsFromIndex,
|
|
31
|
+
loadAllTypeIndexes,
|
|
32
|
+
loadCommunityTypeIndexes,
|
|
33
|
+
loadOrCreateIfNotExists,
|
|
25
34
|
loadPreferences,
|
|
35
|
+
loadProfile,
|
|
26
36
|
loadTypeIndexesFor,
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
registerInstanceInTypeIndex,
|
|
38
|
+
suggestPreferencesFile,
|
|
39
|
+
suggestPrivateTypeIndex,
|
|
40
|
+
suggestPublicTypeIndex,
|
|
41
|
+
uniqueNodes
|
|
29
42
|
} from './discovery/discoveryLogic'
|
|
30
43
|
|
|
31
44
|
export { SolidLogic } from './logic/SolidLogic'
|