solid-logic 1.3.17-f7bdc345 → 2.0.0-30dbc72f
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/lib/acl/aclLogic.d.ts +3 -30
- package/lib/acl/aclLogic.d.ts.map +1 -1
- package/lib/acl/aclLogic.js +152 -119
- package/lib/acl/aclLogic.js.map +1 -1
- package/lib/authn/SolidAuthnLogic.d.ts.map +1 -1
- package/lib/authn/SolidAuthnLogic.js +2 -2
- package/lib/authn/SolidAuthnLogic.js.map +1 -1
- package/lib/chat/chatLogic.d.ts +3 -0
- package/lib/chat/chatLogic.d.ts.map +1 -0
- package/lib/chat/{ChatLogic.js → chatLogic.js} +82 -86
- package/lib/chat/chatLogic.js.map +1 -0
- package/lib/inbox/inboxLogic.d.ts +3 -0
- package/lib/inbox/inboxLogic.d.ts.map +1 -0
- package/lib/inbox/{InboxLogic.js → inboxLogic.js} +58 -64
- package/lib/inbox/inboxLogic.js.map +1 -0
- package/lib/index.d.ts +6 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -38
- package/lib/index.js.map +1 -1
- package/lib/issuer/issuerLogic.js +1 -1
- package/lib/issuer/issuerLogic.js.map +1 -1
- package/lib/logic/CustomError.d.ts +4 -0
- package/lib/logic/CustomError.d.ts.map +1 -1
- package/lib/logic/CustomError.js +17 -1
- package/lib/logic/CustomError.js.map +1 -1
- package/lib/logic/solidLogic.d.ts +6 -0
- package/lib/logic/solidLogic.d.ts.map +1 -0
- package/lib/logic/solidLogic.js +92 -0
- package/lib/logic/solidLogic.js.map +1 -0
- package/lib/logic/solidLogicSingleton.d.ts +1 -2
- package/lib/logic/solidLogicSingleton.d.ts.map +1 -1
- package/lib/logic/solidLogicSingleton.js +3 -3
- package/lib/logic/solidLogicSingleton.js.map +1 -1
- package/lib/profile/profileLogic.d.ts +3 -0
- package/lib/profile/profileLogic.d.ts.map +1 -0
- package/lib/profile/profileLogic.js +246 -0
- package/lib/profile/profileLogic.js.map +1 -0
- package/lib/typeIndex/typeIndexLogic.d.ts +2 -21
- package/lib/typeIndex/typeIndexLogic.d.ts.map +1 -1
- package/lib/typeIndex/typeIndexLogic.js +304 -296
- package/lib/typeIndex/typeIndexLogic.js.map +1 -1
- package/lib/types.d.ts +82 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/util/containerLogic.d.ts +11 -0
- package/lib/util/containerLogic.d.ts.map +1 -0
- package/lib/{profile/ProfileLogic.js → util/containerLogic.js} +53 -44
- package/lib/util/containerLogic.js.map +1 -0
- package/lib/util/ns.d.ts +2 -0
- package/lib/util/ns.d.ts.map +1 -0
- package/lib/util/ns.js +34 -0
- package/lib/util/ns.js.map +1 -0
- package/lib/util/utilityLogic.d.ts +15 -0
- package/lib/util/utilityLogic.d.ts.map +1 -0
- package/lib/util/utilityLogic.js +272 -0
- package/lib/util/utilityLogic.js.map +1 -0
- package/lib/util/utils.d.ts +8 -0
- package/lib/util/utils.d.ts.map +1 -0
- package/lib/util/utils.js +48 -0
- package/lib/util/utils.js.map +1 -0
- package/package.json +14 -14
- package/src/acl/aclLogic.ts +137 -118
- package/src/authn/SolidAuthnLogic.ts +3 -2
- package/src/chat/chatLogic.ts +225 -0
- package/src/inbox/inboxLogic.ts +58 -0
- package/src/index.ts +11 -42
- package/src/issuer/issuerLogic.ts +1 -1
- package/src/logic/CustomError.ts +5 -1
- package/src/logic/solidLogic.ts +75 -0
- package/src/logic/solidLogicSingleton.ts +3 -3
- package/src/profile/profileLogic.ts +126 -0
- package/src/typeIndex/typeIndexLogic.ts +175 -182
- package/src/types.ts +83 -4
- package/src/util/containerLogic.ts +54 -0
- package/src/util/ns.ts +5 -0
- package/src/util/utilityLogic.ts +155 -0
- package/src/util/utils.ts +52 -0
- package/test/aclLogic.test.ts +13 -4
- package/test/chatLogic.test.ts +70 -71
- package/test/container.test.ts +56 -0
- package/test/helpers/dataSetup.ts +134 -0
- package/test/helpers/setup.ts +4 -0
- package/test/inboxLogic.test.ts +39 -38
- package/test/logic.test.ts +10 -9
- package/test/profileLogic.test.ts +246 -0
- package/test/typeIndexLogic.test.ts +487 -30
- package/test/utilityLogic.test.ts +172 -126
- package/test/utils.test.ts +32 -0
- package/lib/chat/ChatLogic.d.ts +0 -26
- package/lib/chat/ChatLogic.d.ts.map +0 -1
- package/lib/chat/ChatLogic.js.map +0 -1
- package/lib/chat/determineChatContainer.d.ts +0 -3
- package/lib/chat/determineChatContainer.d.ts.map +0 -1
- package/lib/chat/determineChatContainer.js +0 -12
- package/lib/chat/determineChatContainer.js.map +0 -1
- package/lib/discovery/discoveryLogic.d.ts +0 -37
- package/lib/discovery/discoveryLogic.d.ts.map +0 -1
- package/lib/discovery/discoveryLogic.js +0 -502
- package/lib/discovery/discoveryLogic.js.map +0 -1
- package/lib/inbox/InboxLogic.d.ts +0 -18
- package/lib/inbox/InboxLogic.d.ts.map +0 -1
- package/lib/inbox/InboxLogic.js.map +0 -1
- package/lib/logic/SolidLogic.d.ts +0 -45
- package/lib/logic/SolidLogic.d.ts.map +0 -1
- package/lib/logic/SolidLogic.js +0 -320
- package/lib/logic/SolidLogic.js.map +0 -1
- package/lib/profile/ProfileLogic.d.ts +0 -13
- package/lib/profile/ProfileLogic.d.ts.map +0 -1
- package/lib/profile/ProfileLogic.js.map +0 -1
- package/lib/util/UtilityLogic.d.ts +0 -27
- package/lib/util/UtilityLogic.d.ts.map +0 -1
- package/lib/util/UtilityLogic.js +0 -216
- package/lib/util/UtilityLogic.js.map +0 -1
- package/lib/util/uri.d.ts +0 -3
- package/lib/util/uri.d.ts.map +0 -1
- package/lib/util/uri.js +0 -9
- package/lib/util/uri.js.map +0 -1
- package/src/chat/ChatLogic.ts +0 -244
- package/src/chat/determineChatContainer.ts +0 -14
- package/src/discovery/discoveryLogic.ts +0 -269
- package/src/inbox/InboxLogic.ts +0 -66
- package/src/logic/SolidLogic.ts +0 -259
- package/src/profile/ProfileLogic.ts +0 -44
- package/src/util/UtilityLogic.ts +0 -144
- package/src/util/uri.ts +0 -5
- package/test/discoveryLogic.test.ts +0 -712
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Session } from "@inrupt/solid-client-authn-browser";
|
|
2
|
+
import * as rdf from "rdflib";
|
|
3
|
+
import { LiveStore, NamedNode, Statement } from "rdflib";
|
|
4
|
+
import { createAclLogic } from "../acl/aclLogic";
|
|
5
|
+
import { SolidAuthnLogic } from "../authn/SolidAuthnLogic";
|
|
6
|
+
import { createChatLogic } from "../chat/chatLogic";
|
|
7
|
+
import { createInboxLogic } from "../inbox/inboxLogic";
|
|
8
|
+
import { createProfileLogic } from "../profile/profileLogic";
|
|
9
|
+
import { createTypeIndexLogic } from "../typeIndex/typeIndexLogic";
|
|
10
|
+
import { createContainerLogic } from "../util/containerLogic";
|
|
11
|
+
import { createUtilityLogic } from "../util/utilityLogic";
|
|
12
|
+
import { AuthnLogic, SolidLogic } from "../types";
|
|
13
|
+
import * as debug from "../util/debug";
|
|
14
|
+
/*
|
|
15
|
+
** It is important to distinquish `fetch`, a function provided by the browser
|
|
16
|
+
** and `Fetcher`, a helper object for the rdflib Store which turns it
|
|
17
|
+
** into a `ConnectedStore` or a `LiveStore`. A Fetcher object is
|
|
18
|
+
** available at store.fetcher, and `fetch` function at `store.fetcher._fetch`,
|
|
19
|
+
*/
|
|
20
|
+
export function createSolidLogic(specialFetch: { fetch: (url: any, requestInit: any) => any }, session: Session): SolidLogic {
|
|
21
|
+
|
|
22
|
+
debug.log("SolidLogic: Unique instance created. There should only be one of these.")
|
|
23
|
+
const store: LiveStore = rdf.graph() as LiveStore
|
|
24
|
+
rdf.fetcher(store, {fetch: specialFetch.fetch}); // Attach a web I/O module, store.fetcher
|
|
25
|
+
store.updater = new rdf.UpdateManager(store); // Add real-time live updates store.updater
|
|
26
|
+
store.features = [] // disable automatic node merging on store load
|
|
27
|
+
|
|
28
|
+
const authn: AuthnLogic = new SolidAuthnLogic(session)
|
|
29
|
+
|
|
30
|
+
const acl = createAclLogic(store)
|
|
31
|
+
const containerLogic = createContainerLogic(store)
|
|
32
|
+
const utilityLogic = createUtilityLogic(store, acl, containerLogic)
|
|
33
|
+
const profile = createProfileLogic(store, authn, utilityLogic)
|
|
34
|
+
const chat = createChatLogic(store, profile)
|
|
35
|
+
const inbox = createInboxLogic(store, profile, utilityLogic, containerLogic, acl)
|
|
36
|
+
const typeIndex = createTypeIndexLogic(store, authn, profile, utilityLogic)
|
|
37
|
+
debug.log('SolidAuthnLogic initialized')
|
|
38
|
+
|
|
39
|
+
function load(doc: NamedNode | NamedNode[] | string) {
|
|
40
|
+
return store.fetcher.load(doc);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// @@@@ use the one in rdflib.js when it is available and delete this
|
|
44
|
+
function updatePromise(
|
|
45
|
+
del: Array<Statement>,
|
|
46
|
+
ins: Array<Statement> = []
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
store.updater.update(del, ins, function (_uri, ok, errorBody) {
|
|
50
|
+
if (!ok) {
|
|
51
|
+
reject(new Error(errorBody));
|
|
52
|
+
} else {
|
|
53
|
+
resolve();
|
|
54
|
+
}
|
|
55
|
+
}); // callback
|
|
56
|
+
}); // promise
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function clearStore() {
|
|
60
|
+
store.statements.slice().forEach(store.remove.bind(store));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
store,
|
|
65
|
+
authn,
|
|
66
|
+
acl,
|
|
67
|
+
inbox,
|
|
68
|
+
chat,
|
|
69
|
+
profile,
|
|
70
|
+
typeIndex,
|
|
71
|
+
load,
|
|
72
|
+
updatePromise,
|
|
73
|
+
clearStore
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as debug from "../util/debug"
|
|
2
2
|
import { authSession } from "../authSession/authSession"
|
|
3
|
-
import {
|
|
3
|
+
import { createSolidLogic } from "./solidLogic"
|
|
4
4
|
|
|
5
5
|
const _fetch = async (url, requestInit) => {
|
|
6
6
|
const omitCreds = requestInit && requestInit.credentials && requestInit.credentials == 'omit'
|
|
7
7
|
if (authSession.info.webId && !omitCreds) { // see https://github.com/solidos/solidos/issues/114
|
|
8
|
-
// In fact
|
|
8
|
+
// In fact fetch should respect credentials omit itself
|
|
9
9
|
return authSession.fetch(url, requestInit)
|
|
10
10
|
} else {
|
|
11
11
|
return window.fetch(url, requestInit)
|
|
@@ -13,7 +13,7 @@ const _fetch = async (url, requestInit) => {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
//this const makes solidLogicSingleton global accessible in mashlib
|
|
16
|
-
const solidLogicSingleton =
|
|
16
|
+
const solidLogicSingleton = createSolidLogic({ fetch: _fetch }, authSession)
|
|
17
17
|
|
|
18
18
|
debug.log('Unique quadstore initialized.')
|
|
19
19
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { NamedNode } from "rdflib";
|
|
2
|
+
import { CrossOriginForbiddenError, FetchError, NotEditableError, SameOriginForbiddenError, UnauthorizedError, WebOperationError } from "../logic/CustomError";
|
|
3
|
+
import * as debug from "../util/debug";
|
|
4
|
+
import { ns as namespace } from '../util/ns';
|
|
5
|
+
import { differentOrigin, suggestPreferencesFile } from "../util/utils"
|
|
6
|
+
import { ProfileLogic } from "../types"
|
|
7
|
+
|
|
8
|
+
export function createProfileLogic(store, authn, utilityLogic): ProfileLogic {
|
|
9
|
+
const ns = namespace
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* loads the preference without throwing errors - if it can create it it does so.
|
|
13
|
+
* remark: it still throws error if it cannot load profile.
|
|
14
|
+
* @param user
|
|
15
|
+
* @returns undefined if preferenceFile cannot be returned or NamedNode if it can find it or create it
|
|
16
|
+
*/
|
|
17
|
+
async function silencedLoadPreferences(user: NamedNode): Promise <NamedNode | undefined> {
|
|
18
|
+
try {
|
|
19
|
+
return await loadPreferences(user)
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* loads the preference without returning different errors if it cannot create or load it.
|
|
27
|
+
* remark: it also throws error if it cannot load profile.
|
|
28
|
+
* @param user
|
|
29
|
+
* @returns undefined if preferenceFile cannot be an Error or NamedNode if it can find it or create it
|
|
30
|
+
*/
|
|
31
|
+
async function loadPreferences (user: NamedNode): Promise <NamedNode> {
|
|
32
|
+
await loadProfile(user)
|
|
33
|
+
|
|
34
|
+
const possiblePreferencesFile = suggestPreferencesFile(user)
|
|
35
|
+
let preferencesFile
|
|
36
|
+
try {
|
|
37
|
+
preferencesFile = await utilityLogic.followOrCreateLink(user, ns.space('preferencesFile') as NamedNode, possiblePreferencesFile, user.doc())
|
|
38
|
+
} catch (err) {
|
|
39
|
+
const message = `User ${user} has no pointer in profile to preferences file.`
|
|
40
|
+
debug.warn(message)
|
|
41
|
+
// we are listing the possible errors
|
|
42
|
+
if (err instanceof NotEditableError) { throw err }
|
|
43
|
+
if (err instanceof WebOperationError) { throw err }
|
|
44
|
+
if (err instanceof UnauthorizedError) { throw err }
|
|
45
|
+
if (err instanceof CrossOriginForbiddenError) { throw err }
|
|
46
|
+
if (err instanceof SameOriginForbiddenError) { throw err }
|
|
47
|
+
if (err instanceof FetchError) { throw err }
|
|
48
|
+
throw err
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let response
|
|
52
|
+
try {
|
|
53
|
+
response = await store.fetcher.load(preferencesFile as NamedNode)
|
|
54
|
+
} catch (err) { // Maybe a permission problem or origin problem
|
|
55
|
+
const msg = `Unable to load preference of user ${user}: ${err}`
|
|
56
|
+
debug.warn(msg)
|
|
57
|
+
if (err.response.status === 401) {
|
|
58
|
+
throw new UnauthorizedError();
|
|
59
|
+
}
|
|
60
|
+
if (err.response.status === 403) {
|
|
61
|
+
if (differentOrigin(preferencesFile)) {
|
|
62
|
+
throw new CrossOriginForbiddenError();
|
|
63
|
+
}
|
|
64
|
+
throw new SameOriginForbiddenError();
|
|
65
|
+
}
|
|
66
|
+
/*if (err.response.status === 404) {
|
|
67
|
+
throw new NotFoundError();
|
|
68
|
+
}*/
|
|
69
|
+
throw new Error(msg)
|
|
70
|
+
}
|
|
71
|
+
return preferencesFile as NamedNode
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function loadProfile (user: NamedNode):Promise <NamedNode> {
|
|
75
|
+
if (!user) {
|
|
76
|
+
throw new Error(`loadProfile: no user given.`)
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await store.fetcher.load(user.doc())
|
|
80
|
+
} catch (err) {
|
|
81
|
+
throw new Error(`Unable to load profile of user ${user}: ${err}`)
|
|
82
|
+
}
|
|
83
|
+
return user.doc()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function loadMe(): Promise<NamedNode> {
|
|
87
|
+
const me = authn.currentUser();
|
|
88
|
+
if (me === null) {
|
|
89
|
+
throw new Error("Current user not found! Not logged in?");
|
|
90
|
+
}
|
|
91
|
+
await store.fetcher.load(me.doc());
|
|
92
|
+
return me;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getPodRoot(user: NamedNode): NamedNode {
|
|
96
|
+
const podRoot = findStorage(user);
|
|
97
|
+
if (!podRoot) {
|
|
98
|
+
throw new Error("User pod root not found!");
|
|
99
|
+
}
|
|
100
|
+
return podRoot as NamedNode;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function getMainInbox(user: NamedNode): Promise<NamedNode> {
|
|
104
|
+
await store.fetcher.load(user);
|
|
105
|
+
const mainInbox = store.any(user, ns.ldp("inbox"), undefined, user.doc());
|
|
106
|
+
if (!mainInbox) {
|
|
107
|
+
throw new Error("User main inbox not found!");
|
|
108
|
+
}
|
|
109
|
+
return mainInbox as NamedNode;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function findStorage(me: NamedNode) {
|
|
113
|
+
return store.any(me, ns.space("storage"), undefined, me.doc());
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
loadMe,
|
|
118
|
+
getPodRoot,
|
|
119
|
+
getMainInbox,
|
|
120
|
+
findStorage,
|
|
121
|
+
loadPreferences,
|
|
122
|
+
loadProfile,
|
|
123
|
+
silencedLoadPreferences
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
@@ -1,201 +1,194 @@
|
|
|
1
|
-
import { NamedNode, st, sym } from
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { newThing } from "../util/
|
|
6
|
-
import { AuthenticationContext } from "../types"
|
|
7
|
-
import { solidLogicSingleton } from "../logic/solidLogicSingleton"
|
|
8
|
-
// import { ensureLoadedPreferences } from '../logic/logic'
|
|
9
|
-
import { loadPreferences, loadProfile } from '../discovery/discoveryLogic'
|
|
10
|
-
export const ns = solidNamespace($rdf)
|
|
11
|
-
|
|
12
|
-
const store = solidLogicSingleton.store
|
|
13
|
-
|
|
14
|
-
async function ensureLoadedPreferences (context:AuthenticationContext) {
|
|
15
|
-
if (!context.me) throw new Error('@@ ensureLoadedPreferences: no user specified')
|
|
16
|
-
context.publicProfile = await loadProfile(store, context.me)
|
|
17
|
-
context.preferencesFile = await loadPreferences(store, context.me)
|
|
18
|
-
return context
|
|
19
|
-
}
|
|
1
|
+
import { NamedNode, st, sym } from 'rdflib'
|
|
2
|
+
import { ScopedApp, TypeIndexLogic, TypeIndexScope } from '../types'
|
|
3
|
+
import * as debug from "../util/debug"
|
|
4
|
+
import { ns as namespace } from '../util/ns'
|
|
5
|
+
import { newThing, uniqueNodes } from "../util/utils"
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* output: index.public, index.private
|
|
24
|
-
* @@ This is a very bizare function
|
|
25
|
-
* @see https://github.com/solidos/solid/blob/main/proposals/data-discovery.md#discoverability
|
|
26
|
-
*/
|
|
27
|
-
export async function loadIndex (
|
|
28
|
-
context: AuthenticationContext,
|
|
29
|
-
isPublic: boolean
|
|
30
|
-
): Promise<AuthenticationContext> {
|
|
31
|
-
const indexes = await solidLogicSingleton.loadIndexes(
|
|
32
|
-
context.me as NamedNode,
|
|
33
|
-
(isPublic ? context.publicProfile || null : null),
|
|
34
|
-
(isPublic ? null : context.preferencesFile || null),
|
|
35
|
-
// async (err: Error) => widgets.complain(context, err.message)
|
|
36
|
-
async (err: Error) => debug.error(err.message) as undefined
|
|
37
|
-
)
|
|
38
|
-
context.index = context.index || {}
|
|
39
|
-
context.index.private = indexes.private.concat(context.index.private || []) // otherwise concat will wrongly add 'undefined' as a private index
|
|
40
|
-
context.index.public = indexes.public.concat(context.index.public || []) // otherwise concat will wrongly add 'undefined' as a public index
|
|
41
|
-
return context
|
|
42
|
-
}
|
|
7
|
+
export function createTypeIndexLogic(store, authn, profileLogic, utilityLogic): TypeIndexLogic {
|
|
8
|
+
const ns = namespace
|
|
43
9
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const indexes = await solidLogicSingleton.loadIndexes(
|
|
52
|
-
context.me as NamedNode,
|
|
53
|
-
context.publicProfile || null,
|
|
54
|
-
context.preferencesFile || null,
|
|
55
|
-
// async (err: Error) => widgets.complain(context, err.message)
|
|
56
|
-
// async (err: Error) => debug.warn(err.message) as undefined
|
|
57
|
-
)
|
|
58
|
-
context.index = context.index || {}
|
|
59
|
-
context.index.private = indexes.private || context.index.private
|
|
60
|
-
context.index.public = indexes.public || context.index.public
|
|
61
|
-
return context
|
|
62
|
-
} catch (error) {
|
|
63
|
-
async (error: Error) => debug.warn(error.message) as undefined
|
|
10
|
+
function getRegistrations(instance, theClass) {
|
|
11
|
+
return store
|
|
12
|
+
.each(undefined, ns.solid("instance"), instance)
|
|
13
|
+
.filter((r) => {
|
|
14
|
+
return store.holds(r, ns.solid("forClass"), theClass);
|
|
15
|
+
});
|
|
64
16
|
}
|
|
65
|
-
}
|
|
66
17
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*/
|
|
71
|
-
export async function ensureTypeIndexes (context: AuthenticationContext, agent?: NamedNode): Promise<AuthenticationContext> {
|
|
72
|
-
if (!context.me) {
|
|
73
|
-
throw new Error(`ensureTypeIndexes: @@ no user`)
|
|
74
|
-
}
|
|
75
|
-
await ensureOneTypeIndex(context, true, agent)
|
|
76
|
-
await ensureOneTypeIndex(context, false, agent)
|
|
77
|
-
return context
|
|
78
|
-
}
|
|
18
|
+
async function loadTypeIndexesFor(user: NamedNode): Promise<Array<TypeIndexScope>> {
|
|
19
|
+
if (!user) throw new Error(`loadTypeIndexesFor: No user given`)
|
|
20
|
+
const profile = await profileLogic.loadProfile(user)
|
|
79
21
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
async function makeIndexIfNecessary (context, isPublic) {
|
|
90
|
-
const relevant = isPublic ? context.publicProfile : context.preferencesFile
|
|
91
|
-
if (!relevant) alert ('@@@@ relevent null')
|
|
92
|
-
const visibility = isPublic ? 'public' : 'private'
|
|
93
|
-
|
|
94
|
-
async function putIndex (newIndex) {
|
|
95
|
-
try {
|
|
96
|
-
await solidLogicSingleton.createEmptyRdfDoc(newIndex, 'Blank initial Type index')
|
|
97
|
-
return context
|
|
98
|
-
} catch (e) {
|
|
99
|
-
const msg = `Error creating new index ${e}`
|
|
100
|
-
// widgets.complain(context, msg)
|
|
101
|
-
debug.warn(msg)
|
|
102
|
-
}
|
|
103
|
-
} // putIndex
|
|
22
|
+
const suggestion = suggestPublicTypeIndex(user)
|
|
23
|
+
let publicTypeIndex
|
|
24
|
+
try {
|
|
25
|
+
publicTypeIndex = await utilityLogic.followOrCreateLink(user, ns.solid('publicTypeIndex') as NamedNode, suggestion, profile)
|
|
26
|
+
} catch (err) {
|
|
27
|
+
const message = `User ${user} has no pointer in profile to publicTypeIndex file.`
|
|
28
|
+
debug.warn(message)
|
|
29
|
+
}
|
|
30
|
+
const publicScopes = publicTypeIndex ? [{ label: 'public', index: publicTypeIndex as NamedNode, agent: user }] : []
|
|
104
31
|
|
|
32
|
+
let preferencesFile
|
|
33
|
+
try {
|
|
34
|
+
preferencesFile = await profileLogic.silencedLoadPreferences(user)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
preferencesFile = null
|
|
37
|
+
}
|
|
105
38
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
newIndex = sym(`${relevant.dir().uri + visibility}TypeIndex.ttl`)
|
|
115
|
-
debug.log(`Linking to new fresh type index ${newIndex}`)
|
|
116
|
-
if (!confirm(`OK to create a new empty index file at ${newIndex}, overwriting anything that is now there?`)) {
|
|
117
|
-
throw new Error('cancelled by user')
|
|
118
|
-
}
|
|
119
|
-
debug.log(`Linking to new fresh type index ${newIndex}`)
|
|
120
|
-
const addMe = [
|
|
121
|
-
st(context.me, ns.solid(`${visibility}TypeIndex`), newIndex, relevant)
|
|
122
|
-
]
|
|
39
|
+
let privateScopes
|
|
40
|
+
if (preferencesFile) { // watch out - can be in either as spec was not clear. Legacy is profile.
|
|
41
|
+
// If there is a legacy one linked from the profile, use that.
|
|
42
|
+
// Otherwiae use or make one linked from Preferences
|
|
43
|
+
const suggestedPrivateTypeIndex = suggestPrivateTypeIndex(preferencesFile)
|
|
44
|
+
let privateTypeIndex
|
|
123
45
|
try {
|
|
124
|
-
|
|
46
|
+
privateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), undefined, profile) ||
|
|
47
|
+
await utilityLogic.followOrCreateLink(user, ns.solid('privateTypeIndex') as NamedNode, suggestedPrivateTypeIndex, preferencesFile);
|
|
125
48
|
} catch (err) {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
debug.warn(msg)
|
|
129
|
-
return context
|
|
49
|
+
const message = `User ${user} has no pointer in preference file to privateTypeIndex file.`
|
|
50
|
+
debug.warn(message)
|
|
130
51
|
}
|
|
131
|
-
|
|
132
|
-
debug.log(`Creating new fresh type index file${newIndex}`)
|
|
133
|
-
await putIndex(newIndex)
|
|
134
|
-
context.index[visibility].push(newIndex) // @@ wait
|
|
52
|
+
privateScopes = privateTypeIndex ? [{ label: 'private', index: privateTypeIndex as NamedNode, agent: user }] : []
|
|
135
53
|
} else {
|
|
136
|
-
|
|
137
|
-
const ixs = context.index[visibility]
|
|
138
|
-
try {
|
|
139
|
-
await solidLogicSingleton.load(ixs)
|
|
140
|
-
} catch (err) {
|
|
141
|
-
const msg = `ensureOneTypeIndex: loading indexes ${err}`
|
|
142
|
-
debug.warn(msg)
|
|
143
|
-
// widgets.complain(context, `ensureOneTypeIndex: loading indexes ${err}`)
|
|
144
|
-
}
|
|
54
|
+
privateScopes = []
|
|
145
55
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
await loadIndex(context2, isPublic)
|
|
155
|
-
const pp = isPublic ? 'public' : 'private'
|
|
156
|
-
if (context2.index && context2.index[pp]&& context2.index[pp].length > 0) {
|
|
157
|
-
debug.log(`ensureOneTypeIndex: Type index exists already ${context2.index[pp]}`)
|
|
158
|
-
return context2
|
|
56
|
+
const scopes = publicScopes.concat(privateScopes)
|
|
57
|
+
if (scopes.length === 0) return scopes
|
|
58
|
+
const files = scopes.map(scope => scope.index)
|
|
59
|
+
try {
|
|
60
|
+
await store.fetcher.load(files)
|
|
61
|
+
} catch (err) {
|
|
62
|
+
debug.warn('Problems loading type index: ', err)
|
|
159
63
|
}
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
64
|
+
return scopes
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function loadCommunityTypeIndexes(user: NamedNode): Promise<TypeIndexScope[][]> {
|
|
68
|
+
let preferencesFile
|
|
69
|
+
try {
|
|
70
|
+
preferencesFile = await profileLogic.silencedLoadPreferences(user)
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const message = `User ${user} has no pointer in profile to preferences file.`
|
|
73
|
+
debug.warn(message)
|
|
74
|
+
}
|
|
75
|
+
if (preferencesFile) { // For now, pick up communities as simple links from the preferences file.
|
|
76
|
+
const communities = store.each(user, ns.solid('community'), undefined, preferencesFile as NamedNode).concat(
|
|
77
|
+
store.each(user, ns.solid('community'), undefined, user.doc() as NamedNode)
|
|
78
|
+
)
|
|
79
|
+
let result = []
|
|
80
|
+
for (const org of communities) {
|
|
81
|
+
result = result.concat(await loadTypeIndexesFor(org as NamedNode) as any)
|
|
82
|
+
}
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
return [] // No communities
|
|
164
86
|
}
|
|
165
|
-
}
|
|
166
87
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
* used in chat in bookmark.js (solid-ui)
|
|
170
|
-
*/
|
|
171
|
-
export async function registerInTypeIndex (
|
|
172
|
-
context: AuthenticationContext,
|
|
173
|
-
instance: NamedNode,
|
|
174
|
-
theClass: NamedNode,
|
|
175
|
-
isPublic: boolean,
|
|
176
|
-
agent?: NamedNode // Defaults to current user
|
|
177
|
-
): Promise<AuthenticationContext> {
|
|
178
|
-
await ensureOneTypeIndex(context, isPublic, agent)
|
|
179
|
-
if (!context.index) {
|
|
180
|
-
throw new Error('registerInTypeIndex: No type index found')
|
|
88
|
+
async function loadAllTypeIndexes(user: NamedNode) {
|
|
89
|
+
return (await loadTypeIndexesFor(user)).concat((await loadCommunityTypeIndexes(user)).flat())
|
|
181
90
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
91
|
+
|
|
92
|
+
async function getScopedAppInstances(klass: NamedNode, user: NamedNode): Promise<ScopedApp[]> {
|
|
93
|
+
const scopes = await loadAllTypeIndexes(user)
|
|
94
|
+
let scopedApps = []
|
|
95
|
+
for (const scope of scopes) {
|
|
96
|
+
const scopedApps0 = await getScopedAppsFromIndex(scope, klass) as any
|
|
97
|
+
scopedApps = scopedApps.concat(scopedApps0)
|
|
98
|
+
}
|
|
99
|
+
return scopedApps
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// This is the function signature which used to be in solid-ui/logic
|
|
103
|
+
// Recommended to use getScopedAppInstances instead as it provides more information.
|
|
104
|
+
//
|
|
105
|
+
async function getAppInstances(klass: NamedNode): Promise<NamedNode[]> {
|
|
106
|
+
const user = authn.currentUser()
|
|
107
|
+
if (!user) throw new Error('getAppInstances: Must be logged in to find apps.')
|
|
108
|
+
const scopedAppInstances = await getScopedAppInstances(klass, user)
|
|
109
|
+
return scopedAppInstances.map(scoped => scoped.instance)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function suggestPublicTypeIndex(me: NamedNode) {
|
|
113
|
+
return sym(me.doc().dir()?.uri + 'publicTypeIndex.ttl')
|
|
114
|
+
}
|
|
115
|
+
// Note this one is based off the pref file not the profile
|
|
116
|
+
|
|
117
|
+
function suggestPrivateTypeIndex(preferencesFile: NamedNode) {
|
|
118
|
+
return sym(preferencesFile.doc().dir()?.uri + 'privateTypeIndex.ttl')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* Register a new app in a type index
|
|
123
|
+
* used in chat in bookmark.js (solid-ui)
|
|
124
|
+
* Returns the registration object if successful else null
|
|
125
|
+
*/
|
|
126
|
+
async function registerInTypeIndex(
|
|
127
|
+
instance: NamedNode,
|
|
128
|
+
index: NamedNode,
|
|
129
|
+
theClass: NamedNode,
|
|
130
|
+
// agent: NamedNode
|
|
131
|
+
): Promise<NamedNode | null> {
|
|
132
|
+
const registration = newThing(index)
|
|
133
|
+
const ins = [
|
|
134
|
+
// See https://github.com/solid/solid/blob/main/proposals/data-discovery.md
|
|
135
|
+
st(registration, ns.rdf('type'), ns.solid('TypeRegistration'), index),
|
|
136
|
+
st(registration, ns.solid('forClass'), theClass, index),
|
|
137
|
+
st(registration, ns.solid('instance'), instance, index)
|
|
138
|
+
]
|
|
139
|
+
try {
|
|
140
|
+
await store.updater.update([], ins)
|
|
141
|
+
} catch (err) {
|
|
142
|
+
const msg = `Unable to register ${instance} in index ${index}: ${err}`
|
|
143
|
+
console.warn(msg)
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
return registration
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function deleteTypeIndexRegistration(item) {
|
|
150
|
+
const reg = store.the(null, ns.solid('instance'), item.instance, item.scope.index) as NamedNode
|
|
151
|
+
if (!reg) throw new Error(`deleteTypeIndexRegistration: No registration found for ${item.instance}`)
|
|
152
|
+
const statements = store.statementsMatching(reg, null, null, item.scope.index)
|
|
153
|
+
await store.updater.update(statements, [])
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function getScopedAppsFromIndex(scope: TypeIndexScope, theClass: NamedNode | null): Promise<ScopedApp[]> {
|
|
157
|
+
const index = scope.index
|
|
158
|
+
const registrations = store.statementsMatching(null, ns.solid('instance'), null, index)
|
|
159
|
+
.concat(store.statementsMatching(null, ns.solid('instanceContainer'), null, index))
|
|
160
|
+
.map(st => st.subject)
|
|
161
|
+
const relevant = theClass ? registrations.filter(reg => store.any(reg, ns.solid('forClass'), null, index)?.sameTerm(theClass))
|
|
162
|
+
: registrations
|
|
163
|
+
const directInstances = relevant.map(reg => store.each(reg, ns.solid('instance'), null, index).map(one => sym(one.value))).flat()
|
|
164
|
+
let instances = uniqueNodes(directInstances)
|
|
165
|
+
|
|
166
|
+
const instanceContainers = relevant.map(
|
|
167
|
+
reg => store.each(reg, ns.solid('instanceContainer'), null, index).map(one => sym(one.value))).flat()
|
|
168
|
+
|
|
169
|
+
// instanceContainers may be deprocatable if no one has used them
|
|
170
|
+
const containers = uniqueNodes(instanceContainers)
|
|
171
|
+
if (containers.length > 0) { console.log('@@ getScopedAppsFromIndex containers ', containers) }
|
|
172
|
+
for (let i = 0; i < containers.length; i++) {
|
|
173
|
+
const cont = containers[i]
|
|
174
|
+
await store.fetcher.load(cont)
|
|
175
|
+
const contents = store.each(cont, ns.ldp('contains'), null, cont).map(one => sym(one.value))
|
|
176
|
+
instances = instances.concat(contents)
|
|
177
|
+
}
|
|
178
|
+
return instances.map(instance => { return { instance, scope } })
|
|
185
179
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
registerInTypeIndex,
|
|
183
|
+
getRegistrations,
|
|
184
|
+
loadTypeIndexesFor,
|
|
185
|
+
loadCommunityTypeIndexes,
|
|
186
|
+
loadAllTypeIndexes,
|
|
187
|
+
getScopedAppInstances,
|
|
188
|
+
getAppInstances,
|
|
189
|
+
suggestPublicTypeIndex,
|
|
190
|
+
suggestPrivateTypeIndex,
|
|
191
|
+
deleteTypeIndexRegistration,
|
|
192
|
+
getScopedAppsFromIndex
|
|
199
193
|
}
|
|
200
|
-
return context
|
|
201
194
|
}
|