solid-logic 1.3.14-fd51b121 → 1.3.15-b1f4b69f

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.
@@ -9,7 +9,12 @@ import { AuthnLogic, SolidNamespace } from "../types";
9
9
  import * as debug from "../util/debug";
10
10
  import { UtilityLogic } from "../util/UtilityLogic";
11
11
  import { CrossOriginForbiddenError, FetchError, NotFoundError, SameOriginForbiddenError, UnauthorizedError } from "./CustomError";
12
-
12
+ /*
13
+ ** It is important to distinquish `fetch`, a function provided by the browser
14
+ ** and `Fetcher`, a helper object for the rdflib Store which turns it
15
+ ** into a `ConnectedStore` or a `LiveStore`. A Fetcher object is
16
+ ** available at store.fetcher, and `fetch` function at `store.fetcher._fetch`,
17
+ */
13
18
 
14
19
  const ns: SolidNamespace = solidNamespace(rdf);
15
20
 
@@ -25,60 +30,64 @@ export class SolidLogic {
25
30
 
26
31
  store: LiveStore;
27
32
  me: string | undefined;
28
- fetcher: { fetch: (url: string, options?: any) => any };
33
+ underlyingFetch: { fetch: (url: string, options?: any) => any };
29
34
 
30
35
  chat: ChatLogic;
31
36
  profile: ProfileLogic;
32
37
  authn: AuthnLogic;
33
38
  util: UtilityLogic;
34
39
 
35
- constructor(fetcher: { fetch: (url: any, requestInit: any) => any }, session: Session) {
40
+ constructor(specialFetch: { fetch: (url: any, requestInit: any) => any }, session: Session) {
41
+ // would xpect to be able to do it this way: but get TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation status: 999
42
+ // this.store = new rdf.LiveStore({})
43
+ // this.store.fetcher._fetch = fetch
44
+ console.log("SolidLogic: Unique instance created. There should only be one of these.")
36
45
  this.store = rdf.graph() as LiveStore; // Make a Quad store
37
- rdf.fetcher(this.store, fetcher); // Attach a web I/O module, store.fetcher
46
+ rdf.fetcher(this.store, { fetch: specialFetch.fetch}); // Attach a web I/O module, store.fetcher
38
47
  this.store.updater = new rdf.UpdateManager(this.store); // Add real-time live updates store.updater
48
+
39
49
  this.store.features = [] // disable automatic node merging on store load
40
50
  this.cache = {
41
51
  profileDocument: {},
42
52
  preferencesFile: {},
43
53
  };
44
- this.fetcher = fetcher;
54
+ this.underlyingFetch = { fetch: fetch }; // Note global one not the one passed
45
55
  this.authn = new SolidAuthnLogic(session);
46
56
  debug.log('SolidAuthnLogic initialized')
47
57
  this.profile = new ProfileLogic(this.store, ns, this.authn);
48
58
  this.chat = new ChatLogic(this.store, ns, this.profile);
49
- this.util = new UtilityLogic(this.store, ns, this.fetcher);
59
+ this.util = new UtilityLogic(this.store, ns, this.underlyingFetch);
50
60
  }
51
61
 
52
62
  findAclDocUrl(url: string) {
53
63
  return this.util.findAclDocUrl(url);
54
64
  }
55
65
 
56
- loadDoc(doc: NamedNode): Promise<void> {
57
- return this.util.loadDoc(doc);
58
- }
59
-
60
66
  async loadProfile(me: NamedNode): Promise<NamedNode> {
61
- // console.log('loadProfile', me)
67
+ /*
68
+ // console.log('loadProfile cache ', this.cache)
62
69
  if (this.cache.profileDocument[me.value]) {
63
70
  return this.cache.profileDocument[me.value];
64
- }
65
- let profileDocument;
71
+ } @@ just use the cache in the store
72
+ */
73
+ console.log('loadProfile me ', me)
74
+ const profileDocument = me.doc()
66
75
  try {
67
- profileDocument = me.doc();
68
- await this.loadDoc(profileDocument);
69
- return profileDocument;
76
+ await this.store.fetcher.load(profileDocument);
77
+ return profileDocument;
70
78
  } catch (err) {
71
- const message = `Logged in but cannot load profile ${profileDocument} : ${err}`;
79
+ const message = `Cannot load profile ${profileDocument} : ${err}`;
72
80
  throw new Error(message);
73
81
  }
74
82
  }
75
83
 
76
84
  async loadPreferences(me: NamedNode): Promise<NamedNode> {
77
- // console.log('loadPreferences', me)
85
+ console.log('loadPreferences cache ', this.cache)
78
86
  if (this.cache.preferencesFile[me.value]) {
79
87
  return this.cache.preferencesFile[me.value];
80
88
  }
81
- const preferencesFile = this.store.any(me, ns.space("preferencesFile"));
89
+ await this.loadProfile(me) // Load pointer to pref file
90
+ const preferencesFile = this.store.any(me, ns.space('preferencesFile'), null, me.doc());
82
91
 
83
92
  // console.log('this.store.any()', this.store.any())
84
93
  /**
@@ -100,9 +109,6 @@ export class SolidLogic {
100
109
  );
101
110
  }
102
111
 
103
- if (!this.store.fetcher) {
104
- throw new Error("Cannot load doc, have no fetcher");
105
- }
106
112
  // //// Load preference file
107
113
  try {
108
114
  await this.store.fetcher.load(preferencesFile as NamedNode, {
@@ -152,9 +158,6 @@ export class SolidLogic {
152
158
  }
153
159
 
154
160
  load(doc: NamedNode | NamedNode[] | string) {
155
- if (!this.store.fetcher) {
156
- throw new Error("Cannot load doc(s), have no fetcher");
157
- }
158
161
  return this.store.fetcher.load(doc);
159
162
  }
160
163
 
@@ -210,9 +213,6 @@ export class SolidLogic {
210
213
  }
211
214
 
212
215
  async createEmptyRdfDoc(doc: NamedNode, comment: string) {
213
- if (!this.store.fetcher) {
214
- throw new Error("Cannot create empty rdf doc, have no fetcher");
215
- }
216
216
  await this.store.fetcher.webOperation("PUT", doc.uri, {
217
217
  data: `# ${new Date()} ${comment}
218
218
  `,
@@ -226,9 +226,6 @@ export class SolidLogic {
226
226
  ins: Array<Statement> = []
227
227
  ): Promise<void> {
228
228
  return new Promise((resolve, reject) => {
229
- if (!this.store.updater) {
230
- throw new Error("Cannot updatePromise, have no updater");
231
- }
232
229
  this.store.updater.update(del, ins, function (_uri, ok, errorBody) {
233
230
  if (!ok) {
234
231
  reject(new Error(errorBody));
@@ -260,6 +257,6 @@ export class SolidLogic {
260
257
  }
261
258
 
262
259
  async fetch(url: string, options?: any) {
263
- return this.fetcher.fetch(url, options);
260
+ return this.underlyingFetch.fetch(url, options);
264
261
  }
265
262
  }
@@ -5,13 +5,23 @@ import * as $rdf from 'rdflib'
5
5
  import { newThing } from "../util/uri"
6
6
  import { AuthenticationContext } from "../types"
7
7
  import { solidLogicSingleton } from "../logic/solidLogicSingleton"
8
-
8
+ // import { ensureLoadedPreferences } from '../logic/logic'
9
+ import { loadPreferences, loadProfile } from '../discovery/discoveryLogic'
9
10
  export const ns = solidNamespace($rdf)
10
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
+ }
20
+
11
21
  /**
12
22
  * Resolves with the same context, outputting
13
23
  * output: index.public, index.private
14
- *
24
+ * @@ This is a very bizare function
15
25
  * @see https://github.com/solid/solid/blob/main/proposals/data-discovery.md#discoverability
16
26
  */
17
27
  export async function loadIndex (
@@ -26,32 +36,46 @@ const indexes = await solidLogicSingleton.loadIndexes(
26
36
  async (err: Error) => debug.error(err.message) as undefined
27
37
  )
28
38
  context.index = context.index || {}
29
- context.index.private = indexes.private || context.index.private
30
- context.index.public = indexes.public || context.index.public
39
+ context.index.private = context.index.private || [] // otherwise concat will wrongly add 'undefined' as a private index
40
+ context.index.private = indexes.private.concat(context.index.private)
41
+ context.index.public = context.index.public || [] // otherwise concat will wrongly add 'undefined' as a public index
42
+ context.index.public = indexes.public.concat(context.index.public)
31
43
  return context
32
44
  }
33
45
 
34
46
  export async function loadTypeIndexes (context: AuthenticationContext) {
35
- const indexes = await solidLogicSingleton.loadIndexes(
36
- context.me as NamedNode,
37
- context.publicProfile || null,
38
- context.preferencesFile || null,
39
- // async (err: Error) => widgets.complain(context, err.message)
40
- async (err: Error) => debug.warn(err.message) as undefined
41
- )
42
- context.index = context.index || {}
43
- context.index.private = indexes.private || context.index.private
44
- context.index.public = indexes.public || context.index.public
45
- return context
47
+ try {
48
+ await loadPreferences(solidLogicSingleton.store, context.me)
49
+ } catch (error) {
50
+ debug.warn(error.message) as undefined
51
+ }
52
+ try {
53
+ const indexes = await solidLogicSingleton.loadIndexes(
54
+ context.me as NamedNode,
55
+ context.publicProfile || null,
56
+ context.preferencesFile || null,
57
+ // async (err: Error) => widgets.complain(context, err.message)
58
+ // async (err: Error) => debug.warn(err.message) as undefined
59
+ )
60
+ context.index = context.index || {}
61
+ context.index.private = indexes.private || context.index.private
62
+ context.index.public = indexes.public || context.index.public
63
+ return context
64
+ } catch (error) {
65
+ async (error: Error) => debug.warn(error.message) as undefined
66
+ }
46
67
  }
47
68
 
48
69
  /**
49
70
  * Resolves with the same context, outputting
50
71
  * @see https://github.com/solid/solid/blob/main/proposals/data-discovery.md#discoverability
51
72
  */
52
- export async function ensureTypeIndexes (context: AuthenticationContext): Promise<AuthenticationContext> {
53
- await ensureOneTypeIndex(context, true)
54
- await ensureOneTypeIndex(context, false)
73
+ export async function ensureTypeIndexes (context: AuthenticationContext, agent?: NamedNode): Promise<AuthenticationContext> {
74
+ if (!context.me) {
75
+ throw new Error(`ensureTypeIndexes: @@ no user`)
76
+ }
77
+ await ensureOneTypeIndex(context, true, agent)
78
+ await ensureOneTypeIndex(context, false, agent)
55
79
  return context
56
80
  }
57
81
 
@@ -63,9 +87,10 @@ return context
63
87
  * Adds its output to the context
64
88
  * @see https://github.com/solid/solid/blob/main/proposals/data-discovery.md#discoverability
65
89
  */
66
- async function ensureOneTypeIndex (context: AuthenticationContext, isPublic: boolean): Promise<AuthenticationContext | void> {
90
+ async function ensureOneTypeIndex (context: AuthenticationContext, isPublic: boolean, agent?: NamedNode): Promise<AuthenticationContext | void> {
67
91
  async function makeIndexIfNecessary (context, isPublic) {
68
92
  const relevant = isPublic ? context.publicProfile : context.preferencesFile
93
+ if (!relevant) alert ('@@@@ relevent null')
69
94
  const visibility = isPublic ? 'public' : 'private'
70
95
 
71
96
  async function putIndex (newIndex) {
@@ -79,58 +104,65 @@ async function ensureOneTypeIndex (context: AuthenticationContext, isPublic: boo
79
104
  }
80
105
  } // putIndex
81
106
 
107
+
82
108
  context.index = context.index || {}
83
109
  context.index[visibility] = context.index[visibility] || []
84
110
  let newIndex
85
111
  if (context.index[visibility].length === 0) {
86
- newIndex = sym(`${relevant.dir().uri + visibility}TypeIndex.ttl`)
87
- debug.log(`Linking to new fresh type index ${newIndex}`)
88
- if (!confirm(`OK to create a new empty index file at ${newIndex}, overwriting anything that is now there?`)) {
89
- throw new Error('cancelled by user')
90
- }
91
- debug.log(`Linking to new fresh type index ${newIndex}`)
92
- const addMe = [
93
- st(context.me, ns.solid(`${visibility}TypeIndex`), newIndex, relevant)
94
- ]
95
- try {
96
- await solidLogicSingleton.updatePromise([], addMe)
97
- } catch (err) {
98
- const msg = `Error saving type index link saving back ${newIndex}: ${err}`
99
- //widgets.complain(context, msg)
100
- debug.warn(msg)
101
- return context
102
- }
112
+ if (!store.updater.editable(relevant)) {
113
+ debug.log(`Not adding new type index as ${relevant} is not editable`)
114
+ return
115
+ }
116
+ newIndex = sym(`${relevant.dir().uri + visibility}TypeIndex.ttl`)
117
+ debug.log(`Linking to new fresh type index ${newIndex}`)
118
+ if (!confirm(`OK to create a new empty index file at ${newIndex}, overwriting anything that is now there?`)) {
119
+ throw new Error('cancelled by user')
120
+ }
121
+ debug.log(`Linking to new fresh type index ${newIndex}`)
122
+ const addMe = [
123
+ st(context.me, ns.solid(`${visibility}TypeIndex`), newIndex, relevant)
124
+ ]
125
+ try {
126
+ await solidLogicSingleton.updatePromise([], addMe)
127
+ } catch (err) {
128
+ const msg = `Error saving type index link saving back ${newIndex}: ${err}`
129
+ //widgets.complain(context, msg)
130
+ debug.warn(msg)
131
+ return context
132
+ }
103
133
 
104
- debug.log(`Creating new fresh type index file${newIndex}`)
105
- await putIndex(newIndex)
106
- context.index[visibility].push(newIndex) // @@ wait
134
+ debug.log(`Creating new fresh type index file${newIndex}`)
135
+ await putIndex(newIndex)
136
+ context.index[visibility].push(newIndex) // @@ wait
107
137
  } else {
108
138
  // officially exists
109
- const ixs = context.index[visibility]
110
- try {
111
- await solidLogicSingleton.load(ixs)
112
- } catch (err) {
113
- const msg = `ensureOneTypeIndex: loading indexes ${err}`
114
- debug.warn(msg)
115
- // widgets.complain(context, `ensureOneTypeIndex: loading indexes ${err}`)
116
- }
139
+ const ixs = context.index[visibility]
140
+ try {
141
+ await solidLogicSingleton.load(ixs)
142
+ } catch (err) {
143
+ const msg = `ensureOneTypeIndex: loading indexes ${err}`
144
+ debug.warn(msg)
145
+ // widgets.complain(context, `ensureOneTypeIndex: loading indexes ${err}`)
146
+ }
117
147
  }
118
148
  } // makeIndexIfNecessary
119
149
 
150
+ const context2 = await ensureLoadedPreferences(context)
151
+ if (!context2.publicProfile) throw new Error(`@@ type index: no publicProfile`)
152
+ if (!context2.preferencesFile) throw new Error(`@@ type index: no preferencesFile for profile ${context2.publicProfile}`)
153
+ const relevant = isPublic ? context2.publicProfile : context2.preferencesFile
154
+
120
155
  try {
121
- await loadIndex(context, isPublic)
122
- if (context.index) {
123
- debug.log(
124
- `ensureOneTypeIndex: Type index exists already ${isPublic
125
- ? context.index.public[0]
126
- : context.index.private[0]
127
- }`
128
- )
156
+ await loadIndex(context2, isPublic)
157
+ const pp = isPublic ? 'public' : 'private'
158
+ if (context2.index && context2.index[pp]&& context2.index[pp].length > 0) {
159
+ debug.log(`ensureOneTypeIndex: Type index exists already ${context2.index[pp]}`)
160
+ return context2
129
161
  }
130
- return context
162
+ await makeIndexIfNecessary(context2, isPublic)
131
163
  } catch (error) {
132
- await makeIndexIfNecessary(context, isPublic)
133
- // widgets.complain(context, 'calling loadIndex:' + error)
164
+ await makeIndexIfNecessary(context2, isPublic)
165
+ // widgets.complain(context2, 'calling loadIndex:' + error)
134
166
  }
135
167
  }
136
168
 
@@ -142,9 +174,10 @@ export async function registerInTypeIndex (
142
174
  context: AuthenticationContext,
143
175
  instance: NamedNode,
144
176
  theClass: NamedNode,
145
- isPublic: boolean
177
+ isPublic: boolean,
178
+ agent?: NamedNode // Defaults to current user
146
179
  ): Promise<AuthenticationContext> {
147
- await ensureOneTypeIndex(context, isPublic)
180
+ await ensureOneTypeIndex(context, isPublic, agent)
148
181
  if (!context.index) {
149
182
  throw new Error('registerInTypeIndex: No type index found')
150
183
  }
@@ -167,4 +200,4 @@ isPublic: boolean
167
200
  alert(e)
168
201
  }
169
202
  return context
170
- }
203
+ }
@@ -11,12 +11,12 @@ export const ACL_LINK = sym(
11
11
  export class UtilityLogic {
12
12
  store: LiveStore;
13
13
  ns: SolidNamespace;
14
- fetcher: { fetch: (url: string, options?: any) => any };
14
+ underlyingFetch: { fetch: (url: string, options?: any) => any };
15
15
 
16
- constructor(store: LiveStore, ns: SolidNamespace, fetcher: { fetch: (url: string, options?: any) => any }) {
16
+ constructor(store: LiveStore, ns: SolidNamespace, underlyingFetch: { fetch: (url: string, options?: any) => any }) {
17
17
  this.store = store;
18
18
  this.ns = ns;
19
- this.fetcher = fetcher;
19
+ this.underlyingFetch = underlyingFetch;
20
20
  }
21
21
 
22
22
  async findAclDocUrl(url: string) {
@@ -65,7 +65,7 @@ export class UtilityLogic {
65
65
  ].join('\n')
66
66
  }
67
67
  const aclDocUrl = await this.findAclDocUrl(options.target);
68
- return this.fetcher.fetch(aclDocUrl, {
68
+ return this.underlyingFetch.fetch(aclDocUrl, {
69
69
  method: 'PUT',
70
70
  body: str,
71
71
  headers: [
@@ -83,7 +83,7 @@ export class UtilityLogic {
83
83
  throw new Error("Cannot load doc, have no fetcher");
84
84
  }
85
85
  await this.store.fetcher.load(doc, {
86
- withCredentials: false,
86
+ withCredentials: false, // @@ BUT this won't work when logged in an accessing private stuff!
87
87
  cache: "reload",
88
88
  });
89
89
  // console.log('loaded', profileDocument, this.store)
@@ -98,7 +98,7 @@ export class UtilityLogic {
98
98
  throw new Error(`Not a container URL ${url}`);
99
99
  }
100
100
  // Copied from https://github.com/solid/solid-crud-tests/blob/v3.1.0/test/surface/create-container.test.ts#L56-L64
101
- const result = await this.fetcher.fetch(url, {
101
+ const result = await this.underlyingFetch.fetch(url, {
102
102
  method: "PUT",
103
103
  headers: {
104
104
  "Content-Type": "text/turtle",
@@ -134,13 +134,13 @@ export class UtilityLogic {
134
134
  try {
135
135
  if (this.isContainer(url)) {
136
136
  const aclDocUrl = await this.findAclDocUrl(url);
137
- await this.fetcher.fetch(aclDocUrl, { method: "DELETE" });
137
+ await this.underlyingFetch.fetch(aclDocUrl, { method: "DELETE" });
138
138
  const containerMembers = await this.getContainerMembers(url);
139
139
  await Promise.all(
140
140
  containerMembers.map((url) => this.recursiveDelete(url))
141
141
  );
142
142
  }
143
- return this.fetcher.fetch(url, { method: "DELETE" });
143
+ return this.underlyingFetch.fetch(url, { method: "DELETE" });
144
144
  } catch (e) {
145
145
  // console.log(`Please manually remove ${url} from your system under test.`, e);
146
146
  }
@@ -20,7 +20,7 @@ describe("Chat logic", () => {
20
20
  status: 404,
21
21
  });
22
22
  store = rdf.graph();
23
- store.fetcher = rdf.fetcher(store, { fetch: fetchMock });
23
+ store.fetcher = rdf.fetcher(store, { fetch: fetchMock, timeout: 5 });
24
24
  store.updater = new UpdateManager(store);
25
25
  const authn = {
26
26
  currentUser: () => {
@@ -2,6 +2,20 @@ import { sym } from 'rdflib'
2
2
  import { AuthenticationContext } from '../src/types'
3
3
  import * as typeIndexLogic from '../src/typeIndex/typeIndexLogic'
4
4
 
5
+ it('loadIndex', async () => {
6
+ const context = {
7
+ index: {}
8
+ }
9
+
10
+ const result = await typeIndexLogic.loadIndex(context, true)
11
+ expect(result).toEqual({
12
+ index: {
13
+ private: [],
14
+ public: []
15
+ },
16
+ })
17
+ })
18
+
5
19
  describe('loadTypeIndexes', () => {
6
20
  it('exists', () => {
7
21
  expect(typeIndexLogic.loadTypeIndexes).toBeInstanceOf(Function)
@@ -15,12 +29,13 @@ describe('registerInTypeIndex', () => {
15
29
  it('exists', () => {
16
30
  expect(typeIndexLogic.registerInTypeIndex).toBeInstanceOf(Function)
17
31
  })
18
- it.skip('runs', async () => {
19
- expect(await typeIndexLogic.registerInTypeIndex(
32
+ it('throws error', async () => {
33
+ expect(typeIndexLogic.registerInTypeIndex(
20
34
  {} as AuthenticationContext,
21
35
  sym('https://test.test#'),
22
36
  sym('https://test.test#'),
23
37
  false
24
- )).toEqual(undefined)
38
+ )).rejects
39
+ .toThrow('@@ ensureLoadedPreferences: no user specified')
25
40
  })
26
41
  })