solid-logic 1.3.17-9d25ceb7 → 1.3.17-a4538ffc

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.
Files changed (125) hide show
  1. package/lib/acl/aclLogic.d.ts +3 -30
  2. package/lib/acl/aclLogic.d.ts.map +1 -1
  3. package/lib/acl/aclLogic.js +152 -119
  4. package/lib/acl/aclLogic.js.map +1 -1
  5. package/lib/authn/SolidAuthnLogic.d.ts.map +1 -1
  6. package/lib/authn/SolidAuthnLogic.js +2 -2
  7. package/lib/authn/SolidAuthnLogic.js.map +1 -1
  8. package/lib/chat/chatLogic.d.ts +3 -0
  9. package/lib/chat/chatLogic.d.ts.map +1 -0
  10. package/lib/chat/{ChatLogic.js → chatLogic.js} +82 -86
  11. package/lib/chat/chatLogic.js.map +1 -0
  12. package/lib/inbox/inboxLogic.d.ts +3 -0
  13. package/lib/inbox/inboxLogic.d.ts.map +1 -0
  14. package/lib/inbox/{InboxLogic.js → inboxLogic.js} +58 -64
  15. package/lib/inbox/inboxLogic.js.map +1 -0
  16. package/lib/index.d.ts +6 -11
  17. package/lib/index.d.ts.map +1 -1
  18. package/lib/index.js +5 -25
  19. package/lib/index.js.map +1 -1
  20. package/lib/issuer/issuerLogic.js +1 -1
  21. package/lib/issuer/issuerLogic.js.map +1 -1
  22. package/lib/logic/CustomError.d.ts +4 -0
  23. package/lib/logic/CustomError.d.ts.map +1 -1
  24. package/lib/logic/CustomError.js +17 -1
  25. package/lib/logic/CustomError.js.map +1 -1
  26. package/lib/logic/solidLogic.d.ts +6 -0
  27. package/lib/logic/solidLogic.d.ts.map +1 -0
  28. package/lib/logic/solidLogic.js +92 -0
  29. package/lib/logic/solidLogic.js.map +1 -0
  30. package/lib/logic/solidLogicSingleton.d.ts +1 -2
  31. package/lib/logic/solidLogicSingleton.d.ts.map +1 -1
  32. package/lib/logic/solidLogicSingleton.js +3 -3
  33. package/lib/logic/solidLogicSingleton.js.map +1 -1
  34. package/lib/profile/profileLogic.d.ts +3 -0
  35. package/lib/profile/profileLogic.d.ts.map +1 -0
  36. package/lib/profile/profileLogic.js +246 -0
  37. package/lib/profile/profileLogic.js.map +1 -0
  38. package/lib/typeIndex/typeIndexLogic.d.ts +2 -21
  39. package/lib/typeIndex/typeIndexLogic.d.ts.map +1 -1
  40. package/lib/typeIndex/typeIndexLogic.js +304 -296
  41. package/lib/typeIndex/typeIndexLogic.js.map +1 -1
  42. package/lib/types.d.ts +82 -1
  43. package/lib/types.d.ts.map +1 -1
  44. package/lib/util/containerLogic.d.ts +11 -0
  45. package/lib/util/containerLogic.d.ts.map +1 -0
  46. package/lib/{profile/ProfileLogic.js → util/containerLogic.js} +53 -44
  47. package/lib/util/containerLogic.js.map +1 -0
  48. package/lib/util/ns.d.ts +2 -0
  49. package/lib/util/ns.d.ts.map +1 -0
  50. package/lib/util/ns.js +34 -0
  51. package/lib/util/ns.js.map +1 -0
  52. package/lib/util/utilityLogic.d.ts +15 -0
  53. package/lib/util/utilityLogic.d.ts.map +1 -0
  54. package/lib/util/utilityLogic.js +272 -0
  55. package/lib/util/utilityLogic.js.map +1 -0
  56. package/lib/util/utils.d.ts +8 -0
  57. package/lib/util/utils.d.ts.map +1 -0
  58. package/lib/util/utils.js +48 -0
  59. package/lib/util/utils.js.map +1 -0
  60. package/package.json +15 -13
  61. package/src/acl/aclLogic.ts +137 -118
  62. package/src/authn/SolidAuthnLogic.ts +3 -2
  63. package/src/chat/chatLogic.ts +225 -0
  64. package/src/inbox/inboxLogic.ts +58 -0
  65. package/src/index.ts +11 -29
  66. package/src/issuer/issuerLogic.ts +1 -1
  67. package/src/logic/CustomError.ts +5 -1
  68. package/src/logic/solidLogic.ts +75 -0
  69. package/src/logic/solidLogicSingleton.ts +3 -3
  70. package/src/profile/profileLogic.ts +126 -0
  71. package/src/typeIndex/typeIndexLogic.ts +175 -182
  72. package/src/types.ts +83 -4
  73. package/src/util/containerLogic.ts +54 -0
  74. package/src/util/ns.ts +5 -0
  75. package/src/util/utilityLogic.ts +155 -0
  76. package/src/util/utils.ts +52 -0
  77. package/test/aclLogic.test.ts +13 -4
  78. package/test/chatLogic.test.ts +70 -71
  79. package/test/container.test.ts +56 -0
  80. package/test/helpers/dataSetup.ts +134 -0
  81. package/test/helpers/setup.ts +4 -0
  82. package/test/inboxLogic.test.ts +39 -38
  83. package/test/logic.test.ts +10 -9
  84. package/test/profileLogic.test.ts +246 -0
  85. package/test/typeIndexLogic.test.ts +487 -30
  86. package/test/utilityLogic.test.ts +172 -126
  87. package/test/utils.test.ts +32 -0
  88. package/lib/chat/ChatLogic.d.ts +0 -26
  89. package/lib/chat/ChatLogic.d.ts.map +0 -1
  90. package/lib/chat/ChatLogic.js.map +0 -1
  91. package/lib/chat/determineChatContainer.d.ts +0 -3
  92. package/lib/chat/determineChatContainer.d.ts.map +0 -1
  93. package/lib/chat/determineChatContainer.js +0 -12
  94. package/lib/chat/determineChatContainer.js.map +0 -1
  95. package/lib/discovery/discoveryLogic.d.ts +0 -37
  96. package/lib/discovery/discoveryLogic.d.ts.map +0 -1
  97. package/lib/discovery/discoveryLogic.js +0 -507
  98. package/lib/discovery/discoveryLogic.js.map +0 -1
  99. package/lib/inbox/InboxLogic.d.ts +0 -18
  100. package/lib/inbox/InboxLogic.d.ts.map +0 -1
  101. package/lib/inbox/InboxLogic.js.map +0 -1
  102. package/lib/logic/SolidLogic.d.ts +0 -48
  103. package/lib/logic/SolidLogic.d.ts.map +0 -1
  104. package/lib/logic/SolidLogic.js +0 -321
  105. package/lib/logic/SolidLogic.js.map +0 -1
  106. package/lib/profile/ProfileLogic.d.ts +0 -13
  107. package/lib/profile/ProfileLogic.d.ts.map +0 -1
  108. package/lib/profile/ProfileLogic.js.map +0 -1
  109. package/lib/util/UtilityLogic.d.ts +0 -33
  110. package/lib/util/UtilityLogic.d.ts.map +0 -1
  111. package/lib/util/UtilityLogic.js +0 -240
  112. package/lib/util/UtilityLogic.js.map +0 -1
  113. package/lib/util/uri.d.ts +0 -3
  114. package/lib/util/uri.d.ts.map +0 -1
  115. package/lib/util/uri.js +0 -9
  116. package/lib/util/uri.js.map +0 -1
  117. package/src/chat/ChatLogic.ts +0 -244
  118. package/src/chat/determineChatContainer.ts +0 -14
  119. package/src/discovery/discoveryLogic.ts +0 -288
  120. package/src/inbox/InboxLogic.ts +0 -66
  121. package/src/logic/SolidLogic.ts +0 -262
  122. package/src/profile/ProfileLogic.ts +0 -44
  123. package/src/util/UtilityLogic.ts +0 -161
  124. package/src/util/uri.ts +0 -5
  125. 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 { SolidLogic } from "./SolidLogic"
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 ftech should respect crentials omit itself
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 = new SolidLogic({ fetch: _fetch }, authSession)
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 "rdflib"
2
- import * as debug from '../util/debug'
3
- import solidNamespace from 'solid-namespace'
4
- import * as $rdf from 'rdflib'
5
- import { newThing } from "../util/uri"
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
- * Resolves with the same context, outputting
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
- export async function loadTypeIndexes (context: AuthenticationContext) {
45
- try {
46
- await loadPreferences(solidLogicSingleton.store, context.me as NamedNode)
47
- } catch (error) {
48
- debug.warn(error.message) as undefined
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
- * Resolves with the same context, outputting
69
- * @see https://github.com/solidos/solid/blob/main/proposals/data-discovery.md#discoverability
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
- * Load or create ONE type index
82
- * Find one or make one or fail
83
- * Many reasons for failing including script not having permission etc
84
- *
85
- * Adds its output to the context
86
- * @see https://github.com/solidos/solid/blob/main/proposals/data-discovery.md#discoverability
87
- */
88
- async function ensureOneTypeIndex (context: AuthenticationContext, isPublic: boolean, agent?: NamedNode): Promise<AuthenticationContext | void> {
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
- context.index = context.index || {}
107
- context.index[visibility] = context.index[visibility] || []
108
- let newIndex
109
- if (context.index[visibility].length === 0) {
110
- if (!store.updater.editable(relevant)) {
111
- debug.log(`Not adding new type index as ${relevant} is not editable`)
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
- await solidLogicSingleton.updatePromise([], addMe)
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 msg = `Error saving type index link saving back ${newIndex}: ${err}`
127
- //widgets.complain(context, msg)
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
- // officially exists
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
- } // makeIndexIfNecessary
147
-
148
- const context2 = await ensureLoadedPreferences(context)
149
- if (!context2.publicProfile) throw new Error(`@@ type index: no publicProfile`)
150
- if (!context2.preferencesFile) throw new Error(`@@ type index: no preferencesFile for profile ${context2.publicProfile}`)
151
- const relevant = isPublic ? context2.publicProfile : context2.preferencesFile
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
- await makeIndexIfNecessary(context2, isPublic)
161
- } catch (error) {
162
- await makeIndexIfNecessary(context2, isPublic)
163
- // widgets.complain(context2, 'calling loadIndex:' + error)
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
- * Register a new app in a type index
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
- const indexes = isPublic ? context.index.public : context.index.private
183
- if (!indexes.length) {
184
- throw new Error('registerInTypeIndex: What no type index?')
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
- const index = indexes[0]
187
- const registration = newThing(index)
188
- const ins = [
189
- // See https://github.com/solidos/solid/blob/main/proposals/data-discovery.md
190
- st(registration, ns.rdf('type'), ns.solid('TypeRegistration'), index),
191
- st(registration, ns.solid('forClass'), theClass, index),
192
- st(registration, ns.solid('instance'), instance, index)
193
- ]
194
- try {
195
- await solidLogicSingleton.updatePromise([], ins)
196
- } catch (e) {
197
- debug.log(e)
198
- alert(e)
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
  }