solid-logic 1.3.17-1274f5aa → 1.3.17-1aa535b3

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 (115) hide show
  1. package/jest.config.js +1 -1
  2. package/lib/acl/aclLogic.d.ts +12 -30
  3. package/lib/acl/aclLogic.d.ts.map +1 -1
  4. package/lib/acl/aclLogic.js +151 -116
  5. package/lib/acl/aclLogic.js.map +1 -1
  6. package/lib/authn/SolidAuthnLogic.d.ts.map +1 -1
  7. package/lib/authn/SolidAuthnLogic.js +2 -2
  8. package/lib/authn/SolidAuthnLogic.js.map +1 -1
  9. package/lib/chat/chatLogic.d.ts +16 -0
  10. package/lib/chat/chatLogic.d.ts.map +1 -0
  11. package/lib/chat/{ChatLogic.js → chatLogic.js} +82 -87
  12. package/lib/chat/chatLogic.js.map +1 -0
  13. package/lib/discovery/discoveryLogic.d.ts +4 -8
  14. package/lib/discovery/discoveryLogic.d.ts.map +1 -1
  15. package/lib/discovery/discoveryLogic.js +37 -22
  16. package/lib/discovery/discoveryLogic.js.map +1 -1
  17. package/lib/inbox/inboxLogic.d.ts +7 -0
  18. package/lib/inbox/inboxLogic.d.ts.map +1 -0
  19. package/lib/inbox/{InboxLogic.js → inboxLogic.js} +58 -64
  20. package/lib/inbox/inboxLogic.js.map +1 -0
  21. package/lib/index.d.ts +10 -13
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +76 -45
  24. package/lib/index.js.map +1 -1
  25. package/lib/logic/CustomError.d.ts +4 -0
  26. package/lib/logic/CustomError.d.ts.map +1 -1
  27. package/lib/logic/CustomError.js +17 -1
  28. package/lib/logic/CustomError.js.map +1 -1
  29. package/lib/logic/solidLogicSingleton.d.ts +35 -3
  30. package/lib/logic/solidLogicSingleton.d.ts.map +1 -1
  31. package/lib/logic/solidLogicSingleton.js +87 -8
  32. package/lib/logic/solidLogicSingleton.js.map +1 -1
  33. package/lib/profile/profileLogic.d.ts +13 -0
  34. package/lib/profile/profileLogic.d.ts.map +1 -0
  35. package/lib/profile/profileLogic.js +268 -0
  36. package/lib/profile/profileLogic.js.map +1 -0
  37. package/lib/typeIndex/typeIndexLogic.d.ts +31 -21
  38. package/lib/typeIndex/typeIndexLogic.d.ts.map +1 -1
  39. package/lib/typeIndex/typeIndexLogic.js +650 -295
  40. package/lib/typeIndex/typeIndexLogic.js.map +1 -1
  41. package/lib/types.d.ts +17 -0
  42. package/lib/types.d.ts.map +1 -1
  43. package/lib/util/containerLogic.d.ts +11 -0
  44. package/lib/util/containerLogic.d.ts.map +1 -0
  45. package/lib/{profile/ProfileLogic.js → util/containerLogic.js} +53 -44
  46. package/lib/util/containerLogic.js.map +1 -0
  47. package/lib/util/utilityLogic.d.ts +15 -0
  48. package/lib/util/utilityLogic.d.ts.map +1 -0
  49. package/lib/util/utilityLogic.js +272 -0
  50. package/lib/util/utilityLogic.js.map +1 -0
  51. package/lib/util/utils.d.ts +8 -0
  52. package/lib/util/utils.d.ts.map +1 -0
  53. package/lib/util/utils.js +48 -0
  54. package/lib/util/utils.js.map +1 -0
  55. package/package.json +1 -1
  56. package/src/acl/aclLogic.ts +135 -119
  57. package/src/authn/SolidAuthnLogic.ts +3 -2
  58. package/src/chat/chatLogic.ts +225 -0
  59. package/src/discovery/discoveryLogic.ts +19 -32
  60. package/src/inbox/inboxLogic.ts +57 -0
  61. package/src/index.ts +71 -31
  62. package/src/logic/CustomError.ts +5 -1
  63. package/src/logic/solidLogicSingleton.ts +160 -7
  64. package/src/profile/profileLogic.ts +134 -0
  65. package/src/typeIndex/typeIndexLogic.ts +417 -153
  66. package/src/types.ts +7 -3
  67. package/src/util/containerLogic.ts +54 -0
  68. package/src/util/ns.js +5 -0
  69. package/src/util/utilityLogic.ts +155 -0
  70. package/src/util/utils.ts +52 -0
  71. package/test/aclLogic.test.ts +13 -4
  72. package/test/chatLogic.test.ts +70 -71
  73. package/test/container.test.ts +56 -0
  74. package/test/discoveryLogic.test.ts +502 -530
  75. package/test/helpers/dataSetup.ts +134 -0
  76. package/test/helpers/setup.ts +1 -0
  77. package/test/inboxLogic.test.ts +39 -38
  78. package/test/logic.test.ts +11 -9
  79. package/test/profileLogic.test.ts +246 -0
  80. package/test/typeIndexLogic.test.ts +49 -22
  81. package/test/typeIndexLogicPart2.test.ts +485 -0
  82. package/test/utilityLogic.test.ts +172 -126
  83. package/test/utils.test.ts +32 -0
  84. package/lib/chat/ChatLogic.d.ts +0 -26
  85. package/lib/chat/ChatLogic.d.ts.map +0 -1
  86. package/lib/chat/ChatLogic.js.map +0 -1
  87. package/lib/chat/determineChatContainer.d.ts +0 -3
  88. package/lib/chat/determineChatContainer.d.ts.map +0 -1
  89. package/lib/chat/determineChatContainer.js +0 -12
  90. package/lib/chat/determineChatContainer.js.map +0 -1
  91. package/lib/inbox/InboxLogic.d.ts +0 -18
  92. package/lib/inbox/InboxLogic.d.ts.map +0 -1
  93. package/lib/inbox/InboxLogic.js.map +0 -1
  94. package/lib/logic/SolidLogic.d.ts +0 -48
  95. package/lib/logic/SolidLogic.d.ts.map +0 -1
  96. package/lib/logic/SolidLogic.js +0 -321
  97. package/lib/logic/SolidLogic.js.map +0 -1
  98. package/lib/profile/ProfileLogic.d.ts +0 -13
  99. package/lib/profile/ProfileLogic.d.ts.map +0 -1
  100. package/lib/profile/ProfileLogic.js.map +0 -1
  101. package/lib/util/UtilityLogic.d.ts +0 -33
  102. package/lib/util/UtilityLogic.d.ts.map +0 -1
  103. package/lib/util/UtilityLogic.js +0 -240
  104. package/lib/util/UtilityLogic.js.map +0 -1
  105. package/lib/util/uri.d.ts +0 -3
  106. package/lib/util/uri.d.ts.map +0 -1
  107. package/lib/util/uri.js +0 -9
  108. package/lib/util/uri.js.map +0 -1
  109. package/src/chat/ChatLogic.ts +0 -244
  110. package/src/chat/determineChatContainer.ts +0 -14
  111. package/src/inbox/InboxLogic.ts +0 -66
  112. package/src/logic/SolidLogic.ts +0 -262
  113. package/src/profile/ProfileLogic.ts +0 -44
  114. package/src/util/UtilityLogic.ts +0 -161
  115. package/src/util/uri.ts +0 -5
@@ -1,137 +1,153 @@
1
- /**
2
- * Simple Access Control
3
- *
4
- * This function sets up a simple default ACL for a resource, with
5
- * RWC for the owner, and a specified access (default none) for the public.
6
- * In all cases owner has read write control.
7
- * Parameter lists modes allowed to public
8
- *
9
- * @param options
10
- * @param options.public eg ['Read', 'Write']
11
- *
12
- * @returns Resolves with aclDoc uri on successful write
13
- */
1
+ import { graph, NamedNode, Namespace, serialize, sym } from "rdflib"
2
+ import ns from '../util/ns'
14
3
 
15
- import { graph, NamedNode, Namespace, serialize } from "rdflib"
16
- import solidNamespace from 'solid-namespace'
17
- import * as $rdf from 'rdflib'
18
- import { solidLogicSingleton } from "../logic/solidLogicSingleton"
19
- import { ACL_LINK } from "../util/UtilityLogic"
20
4
 
21
- export const ns = solidNamespace($rdf)
5
+ export const ACL_LINK = sym(
6
+ "http://www.iana.org/assignments/link-relations/acl"
7
+ );
22
8
 
23
- export function setACLUserPublic (
24
- docURI: string,
25
- me: NamedNode,
26
- options: {
27
- defaultForNew?: boolean,
28
- public?: []
29
- }
30
- ): Promise<NamedNode> {
31
- const aclDoc = solidLogicSingleton.store.any(
32
- solidLogicSingleton.store.sym(docURI),
33
- ACL_LINK
34
- )
35
-
36
- return Promise.resolve()
37
- .then(() => {
38
- if (aclDoc) {
39
- return aclDoc as NamedNode
9
+ export function createAclLogic(store) {
10
+
11
+ async function findAclDocUrl(url: string) {
12
+ const doc = store.sym(url);
13
+ await store.fetcher.load(doc);
14
+ const docNode = store.any(doc, ACL_LINK);
15
+ if (!docNode) {
16
+ throw new Error(`No ACL link discovered for ${url}`);
17
+ }
18
+ return docNode.value;
40
19
  }
41
-
42
- return fetchACLRel(docURI).catch(err => {
43
- throw new Error(`Error fetching rel=ACL header for ${docURI}: ${err}`)
44
- })
45
- })
46
- .then(aclDoc => {
47
- const aclText = genACLText(docURI, me, aclDoc.uri, options)
48
- if (!solidLogicSingleton.store.fetcher) {
49
- throw new Error('Cannot PUT this, store has no fetcher')
20
+ /**
21
+ * Simple Access Control
22
+ *
23
+ * This function sets up a simple default ACL for a resource, with
24
+ * RWC for the owner, and a specified access (default none) for the public.
25
+ * In all cases owner has read write control.
26
+ * Parameter lists modes allowed to public
27
+ *
28
+ * @param options
29
+ * @param options.public eg ['Read', 'Write']
30
+ *
31
+ * @returns Resolves with aclDoc uri on successful write
32
+ */
33
+ function setACLUserPublic (
34
+ docURI: string,
35
+ me: NamedNode,
36
+ options: {
37
+ defaultForNew?: boolean,
38
+ public?: []
50
39
  }
51
- return solidLogicSingleton.store.fetcher
52
- .webOperation('PUT', aclDoc.uri, {
53
- data: aclText,
54
- contentType: 'text/turtle'
55
- })
56
- .then(result => {
57
- if (!result.ok) {
58
- throw new Error('Error writing ACL text: ' + result.error)
40
+ ): Promise<NamedNode> {
41
+ const aclDoc = store.any(
42
+ store.sym(docURI),
43
+ ACL_LINK
44
+ )
45
+
46
+ return Promise.resolve()
47
+ .then(() => {
48
+ if (aclDoc) {
49
+ return aclDoc as NamedNode
59
50
  }
60
51
 
61
- return aclDoc
52
+ return fetchACLRel(docURI).catch(err => {
53
+ throw new Error(`Error fetching rel=ACL header for ${docURI}: ${err}`)
54
+ })
62
55
  })
63
- })
64
- }
56
+ .then(aclDoc => {
57
+ const aclText = genACLText(docURI, me, aclDoc.uri, options)
58
+ if (!store.fetcher) {
59
+ throw new Error('Cannot PUT this, store has no fetcher')
60
+ }
61
+ return store.fetcher
62
+ .webOperation('PUT', aclDoc.uri, {
63
+ data: aclText,
64
+ contentType: 'text/turtle'
65
+ })
66
+ .then(result => {
67
+ if (!result.ok) {
68
+ throw new Error('Error writing ACL text: ' + result.error)
69
+ }
65
70
 
66
- /**
67
- * @param docURI
68
- * @returns
69
- */
70
- function fetchACLRel (docURI: string): Promise<NamedNode> {
71
- const fetcher = solidLogicSingleton.store.fetcher
72
- if (!fetcher) {
73
- throw new Error('Cannot fetch ACL rel, store has no fetcher')
71
+ return aclDoc
72
+ })
73
+ })
74
74
  }
75
75
 
76
- return fetcher.load(docURI).then(result => {
77
- if (!result.ok) {
78
- throw new Error('fetchACLRel: While loading:' + (result as any).error)
76
+ /**
77
+ * @param docURI
78
+ * @returns
79
+ */
80
+ function fetchACLRel (docURI: string): Promise<NamedNode> {
81
+ const fetcher = store.fetcher
82
+ if (!fetcher) {
83
+ throw new Error('Cannot fetch ACL rel, store has no fetcher')
79
84
  }
80
85
 
81
- const aclDoc = solidLogicSingleton.store.any(
82
- solidLogicSingleton.store.sym(docURI),
83
- ACL_LINK
84
- )
86
+ return fetcher.load(docURI).then(result => {
87
+ if (!result.ok) {
88
+ throw new Error('fetchACLRel: While loading:' + (result as any).error)
89
+ }
85
90
 
86
- if (!aclDoc) {
87
- throw new Error('fetchACLRel: No Link rel=ACL header for ' + docURI)
88
- }
91
+ const aclDoc = store.any(
92
+ store.sym(docURI),
93
+ ACL_LINK
94
+ )
89
95
 
90
- return aclDoc as NamedNode
91
- })
92
- }
96
+ if (!aclDoc) {
97
+ throw new Error('fetchACLRel: No Link rel=ACL header for ' + docURI)
98
+ }
99
+
100
+ return aclDoc as NamedNode
101
+ })
102
+ }
93
103
 
94
- /**
95
- * @param docURI
96
- * @param me
97
- * @param aclURI
98
- * @param options
99
- *
100
- * @returns Serialized ACL
101
- */
102
- export function genACLText (
103
- docURI: string,
104
- me: NamedNode,
105
- aclURI: string,
106
- options: {
107
- defaultForNew?: boolean,
108
- public?: []
109
- } = {}
110
- ): string | undefined {
111
- const optPublic = options.public || []
112
- const g = graph()
113
- const auth = Namespace('http://www.w3.org/ns/auth/acl#')
114
- let a = g.sym(`${aclURI}#a1`)
115
- const acl = g.sym(aclURI)
116
- const doc = g.sym(docURI)
117
- g.add(a, ns.rdf('type'), auth('Authorization'), acl)
118
- g.add(a, auth('accessTo'), doc, acl)
119
- if (options.defaultForNew) {
120
- g.add(a, auth('default'), doc, acl)
121
- }
122
- g.add(a, auth('agent'), me, acl)
123
- g.add(a, auth('mode'), auth('Read'), acl)
124
- g.add(a, auth('mode'), auth('Write'), acl)
125
- g.add(a, auth('mode'), auth('Control'), acl)
104
+ /**
105
+ * @param docURI
106
+ * @param me
107
+ * @param aclURI
108
+ * @param options
109
+ *
110
+ * @returns Serialized ACL
111
+ */
112
+ function genACLText (
113
+ docURI: string,
114
+ me: NamedNode,
115
+ aclURI: string,
116
+ options: {
117
+ defaultForNew?: boolean,
118
+ public?: []
119
+ } = {}
120
+ ): string | undefined {
121
+ const optPublic = options.public || []
122
+ const g = graph()
123
+ const auth = Namespace('http://www.w3.org/ns/auth/acl#')
124
+ let a = g.sym(`${aclURI}#a1`)
125
+ const acl = g.sym(aclURI)
126
+ const doc = g.sym(docURI)
127
+ g.add(a, ns.rdf('type'), auth('Authorization'), acl)
128
+ g.add(a, auth('accessTo'), doc, acl)
129
+ if (options.defaultForNew) {
130
+ g.add(a, auth('default'), doc, acl)
131
+ }
132
+ g.add(a, auth('agent'), me, acl)
133
+ g.add(a, auth('mode'), auth('Read'), acl)
134
+ g.add(a, auth('mode'), auth('Write'), acl)
135
+ g.add(a, auth('mode'), auth('Control'), acl)
126
136
 
127
- if (optPublic.length) {
128
- a = g.sym(`${aclURI}#a2`)
129
- g.add(a, ns.rdf('type'), auth('Authorization'), acl)
130
- g.add(a, auth('accessTo'), doc, acl)
131
- g.add(a, auth('agentClass'), ns.foaf('Agent'), acl)
132
- for (let p = 0; p < optPublic.length; p++) {
133
- g.add(a, auth('mode'), auth(optPublic[p]), acl) // Like 'Read' etc
137
+ if (optPublic.length) {
138
+ a = g.sym(`${aclURI}#a2`)
139
+ g.add(a, ns.rdf('type'), auth('Authorization'), acl)
140
+ g.add(a, auth('accessTo'), doc, acl)
141
+ g.add(a, auth('agentClass'), ns.foaf('Agent'), acl)
142
+ for (let p = 0; p < optPublic.length; p++) {
143
+ g.add(a, auth('mode'), auth(optPublic[p]), acl) // Like 'Read' etc
144
+ }
145
+ }
146
+ return serialize(acl, g, aclURI)
147
+ }
148
+ return {
149
+ findAclDocUrl,
150
+ setACLUserPublic,
151
+ genACLText
134
152
  }
135
- }
136
- return serialize(acl, g, aclURI)
137
- }
153
+ }
@@ -3,6 +3,7 @@ import { appContext, offlineTestID } from "./authUtil";
3
3
  import * as debug from '../util/debug'
4
4
  import { Session } from "@inrupt/solid-client-authn-browser";
5
5
  import { AuthenticationContext, AuthnLogic } from "../types";
6
+
6
7
  export class SolidAuthnLogic implements AuthnLogic {
7
8
  private session: Session;
8
9
 
@@ -58,10 +59,10 @@ export class SolidAuthnLogic implements AuthnLogic {
58
59
  const curUrl = new URL(window.location.href)
59
60
  if (curUrl.hash !== postLoginRedirectHash) {
60
61
  if (history.pushState) {
61
- // console.log('Setting window.location.has using pushState')
62
+ // debug.log('Setting window.location.has using pushState')
62
63
  history.pushState(null, document.title, postLoginRedirectHash)
63
64
  } else {
64
- // console.warn('Setting window.location.has using location.hash')
65
+ // debug.warn('Setting window.location.has using location.hash')
65
66
  location.hash = postLoginRedirectHash
66
67
  }
67
68
  curUrl.hash = postLoginRedirectHash
@@ -0,0 +1,225 @@
1
+ import { NamedNode, Node, st, term } from "rdflib"
2
+ import { CreatedPaneOptions, NewPaneOptions } from "../types"
3
+ import { determineChatContainer, newThing } from "../util/utils"
4
+ import { ns as namespace } from '../util/ns'
5
+
6
+ const CHAT_LOCATION_IN_CONTAINER = "index.ttl#this";
7
+
8
+ export function createChatLogic(store, profileLogic) {
9
+ const ns = namespace
10
+
11
+ async function setAcl(
12
+ chatContainer: NamedNode,
13
+ me: NamedNode,
14
+ invitee: NamedNode
15
+ ): Promise<void> {
16
+ // Some servers don't present a Link http response header
17
+ // if the container doesn't exist yet, so refetch the container
18
+ // now that it has been created:
19
+ await store.fetcher.load(chatContainer);
20
+
21
+ // FIXME: check the Why value on this quad:
22
+ const chatAclDoc = store.any(
23
+ chatContainer,
24
+ new NamedNode("http://www.iana.org/assignments/link-relations/acl")
25
+ );
26
+ if (!chatAclDoc) {
27
+ throw new Error("Chat ACL doc not found!");
28
+ }
29
+
30
+ const aclBody = `
31
+ @prefix acl: <http://www.w3.org/ns/auth/acl#>.
32
+ <#owner>
33
+ a acl:Authorization;
34
+ acl:agent <${me.value}>;
35
+ acl:accessTo <.>;
36
+ acl:default <.>;
37
+ acl:mode
38
+ acl:Read, acl:Write, acl:Control.
39
+ <#invitee>
40
+ a acl:Authorization;
41
+ acl:agent <${invitee.value}>;
42
+ acl:accessTo <.>;
43
+ acl:default <.>;
44
+ acl:mode
45
+ acl:Read, acl:Append.
46
+ `;
47
+ await store.fetcher.webOperation("PUT", chatAclDoc.value, {
48
+ data: aclBody,
49
+ contentType: "text/turtle",
50
+ });
51
+ }
52
+
53
+ async function addToPrivateTypeIndex(chatThing, me) {
54
+ // Add to private type index
55
+ const privateTypeIndex = store.any(
56
+ me,
57
+ ns.solid("privateTypeIndex")
58
+ ) as NamedNode | null;
59
+ if (!privateTypeIndex) {
60
+ throw new Error("Private type index not found!");
61
+ }
62
+ await store.fetcher.load(privateTypeIndex);
63
+ const reg = newThing(privateTypeIndex);
64
+ const ins = [
65
+ st(
66
+ reg,
67
+ ns.rdf("type"),
68
+ ns.solid("TypeRegistration"),
69
+ privateTypeIndex.doc()
70
+ ),
71
+ st(
72
+ reg,
73
+ ns.solid("forClass"),
74
+ ns.meeting("LongChat"),
75
+ privateTypeIndex.doc()
76
+ ),
77
+ st(reg, ns.solid("instance"), chatThing, privateTypeIndex.doc()),
78
+ ];
79
+ await new Promise((resolve, reject) => {
80
+ store.updater.update([], ins, function (_uri, ok, errm) {
81
+ if (!ok) {
82
+ reject(new Error(errm));
83
+ } else {
84
+ resolve(null);
85
+ }
86
+ });
87
+ });
88
+ }
89
+
90
+ async function findChat(invitee: NamedNode) {
91
+ const me = await profileLogic.loadMe();
92
+ const podRoot = await profileLogic.getPodRoot(me);
93
+ const chatContainer = determineChatContainer(invitee, podRoot);
94
+ let exists = true;
95
+ try {
96
+ await store.fetcher.load(
97
+ new NamedNode(chatContainer.value + "index.ttl#this")
98
+ );
99
+ } catch (e) {
100
+ exists = false;
101
+ }
102
+ return { me, chatContainer, exists };
103
+ }
104
+
105
+ async function createChatThing(
106
+ chatContainer: NamedNode,
107
+ me: NamedNode
108
+ ): Promise<NamedNode> {
109
+ const created = await mintNew({
110
+ me,
111
+ newBase: chatContainer.value,
112
+ });
113
+ return created.newInstance;
114
+ }
115
+
116
+ function mintNew(newPaneOptions: NewPaneOptions): Promise<CreatedPaneOptions> {
117
+ const kb = store;
118
+ const updater = kb.updater;
119
+ if (newPaneOptions.me && !newPaneOptions.me.uri) {
120
+ throw new Error("chat mintNew: Invalid userid " + newPaneOptions.me);
121
+ }
122
+
123
+ const newInstance = (newPaneOptions.newInstance =
124
+ newPaneOptions.newInstance ||
125
+ kb.sym(newPaneOptions.newBase + CHAT_LOCATION_IN_CONTAINER));
126
+ const newChatDoc = newInstance.doc();
127
+
128
+ kb.add(
129
+ newInstance,
130
+ ns.rdf("type"),
131
+ ns.meeting("LongChat"),
132
+ newChatDoc
133
+ );
134
+ kb.add(newInstance, ns.dc("title"), "Chat channel", newChatDoc);
135
+ kb.add(
136
+ newInstance,
137
+ ns.dc("created"),
138
+ term<Node>(new Date(Date.now())),
139
+ newChatDoc
140
+ );
141
+ if (newPaneOptions.me) {
142
+ kb.add(newInstance, ns.dc("author"), newPaneOptions.me, newChatDoc);
143
+ }
144
+
145
+ return new Promise(function (resolve, reject) {
146
+ updater?.put(
147
+ newChatDoc,
148
+ kb.statementsMatching(undefined, undefined, undefined, newChatDoc),
149
+ "text/turtle",
150
+ function (uri2, ok, message) {
151
+ if (ok) {
152
+ resolve({
153
+ ...newPaneOptions,
154
+ newInstance,
155
+ });
156
+ } else {
157
+ reject(
158
+ new Error(
159
+ "FAILED to save new chat channel at: " + uri2 + " : " + message
160
+ )
161
+ );
162
+ }
163
+ }
164
+ );
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Find (and optionally create) an individual chat between the current user and the given invitee
170
+ * @param invitee - The person to chat with
171
+ * @param createIfMissing - Whether the chat should be created, if missing
172
+ * @returns null if missing, or a node referring to an already existing chat, or the newly created chat
173
+ */
174
+ async function getChat(
175
+ invitee: NamedNode,
176
+ createIfMissing = true
177
+ ): Promise<NamedNode | null> {
178
+ const { me, chatContainer, exists } = await findChat(invitee);
179
+ if (exists) {
180
+ return new NamedNode(chatContainer.value + CHAT_LOCATION_IN_CONTAINER);
181
+ }
182
+
183
+ if (createIfMissing) {
184
+ const chatThing = await createChatThing(chatContainer, me);
185
+ await sendInvite(invitee, chatThing);
186
+ await setAcl(chatContainer, me, invitee);
187
+ await addToPrivateTypeIndex(chatThing, me);
188
+ return chatThing;
189
+ }
190
+ return null;
191
+ }
192
+
193
+ async function sendInvite(invitee: NamedNode, chatThing: NamedNode) {
194
+ await store.fetcher.load(invitee.doc());
195
+ const inviteeInbox = store.any(
196
+ invitee,
197
+ ns.ldp("inbox"),
198
+ undefined,
199
+ invitee.doc()
200
+ );
201
+ if (!inviteeInbox) {
202
+ throw new Error(`Invitee inbox not found! ${invitee.value}`);
203
+ }
204
+ const inviteBody = `
205
+ <> a <http://www.w3.org/ns/pim/meeting#LongChatInvite> ;
206
+ ${ns.rdf("seeAlso")} <${chatThing.value}> .
207
+ `;
208
+
209
+ const inviteResponse = await store.fetcher.webOperation(
210
+ "POST",
211
+ inviteeInbox.value,
212
+ {
213
+ data: inviteBody,
214
+ contentType: "text/turtle",
215
+ }
216
+ );
217
+ const locationStr = inviteResponse?.headers.get("location");
218
+ if (!locationStr) {
219
+ throw new Error(`Invite sending returned a ${inviteResponse?.status}`);
220
+ }
221
+ }
222
+ return {
223
+ setAcl, addToPrivateTypeIndex, findChat, createChatThing, getChat, sendInvite, mintNew
224
+ }
225
+ }
@@ -1,29 +1,17 @@
1
- import { NamedNode, Namespace, LiveStore, sym, st } from "rdflib";
2
- import { solidLogicSingleton } from "../logic/solidLogicSingleton"
3
- import { newThing } from "../util/uri"
4
- const { authn } = solidLogicSingleton
5
- const { currentUser } = authn
1
+ import { NamedNode, LiveStore, sym, st } from 'rdflib'
2
+ import * as $rdf from 'rdflib'
3
+ import { newThing } from "../util/utils"
4
+ import solidNamespace from 'solid-namespace'
5
+ import { authn } from '../logic/solidLogicSingleton'
6
6
 
7
- type TypeIndexScope = { label: string, index: NamedNode, agent: NamedNode } ;
8
- type ScopedApp = { instance: NamedNode, scope: TypeIndexScope }
9
-
10
- const ns = {
11
- dct: Namespace('http://purl.org/dc/terms/'),
12
- ldp: Namespace('http://www.w3.org/ns/ldp#'),
13
- meeting: Namespace('http://www.w3.org/ns/pim/meeting#'),
14
- rdf: Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#'),
15
- schema: Namespace('http://schema.org/'),
16
- solid: Namespace('http://www.w3.org/ns/solid/terms#'),
17
- space: Namespace('http://www.w3.org/ns/pim/space#'),
18
- stat: Namespace('http://www.w3.org/ns/posix/stat#'),
19
- vcard: Namespace('http://www.w3.org/2006/vcard/ns#'),
20
- wf: Namespace('http://www.w3.org/2005/01/wf/flow#'),
21
- xsd: Namespace('http://www.w3.org/2001/XMLSchema#')
7
+ const ns = solidNamespace($rdf)
22
8
 
23
- }
9
+ type TypeIndexScope = { label: string, index: NamedNode, agent: NamedNode }
10
+ type ScopedApp = { instance: NamedNode, scope: TypeIndexScope }
24
11
 
25
- /** Create a resource if it really does not exist
26
- * Be absolutely sure something does not exist before creating a new empty file
12
+ /**
13
+ * Create a resource if it really does not exist
14
+ * Be absolutely sure something does not exist before creating a new empty file
27
15
  * as otherwise existing could be deleted.
28
16
  * @param doc {NamedNode} - The resource
29
17
  */
@@ -64,9 +52,10 @@ export function suggestPublicTypeIndex (me:NamedNode) {
64
52
  export function suggestPrivateTypeIndex (preferencesFile:NamedNode) {
65
53
  return sym(preferencesFile.doc().dir()?.uri + 'privateTypeIndex.ttl')
66
54
  }
55
+
67
56
  /* Follow link from this doc to another thing, or else make a new link
68
57
  **
69
- ** return: null no ld one and failed to make a new one
58
+ ** return: null no ld one and failed to make a new one
70
59
  */
71
60
  export async function followOrCreateLink (store: LiveStore, subject: NamedNode, predicate: NamedNode,
72
61
  object: NamedNode, doc:NamedNode):Promise<NamedNode | null> {
@@ -119,7 +108,7 @@ export async function loadPreferences (store: LiveStore, user: NamedNode): Promi
119
108
  }
120
109
  try {
121
110
  await store.fetcher.load(preferencesFile as NamedNode)
122
- } catch (err) { // Mabeb a permission propblem or origin problem
111
+ } catch (err) { // Maybe a permission propblem or origin problem
123
112
  return undefined
124
113
  }
125
114
  return preferencesFile as NamedNode
@@ -150,7 +139,7 @@ export async function loadTypeIndexesFor (store: LiveStore, user:NamedNode): Pro
150
139
 
151
140
  const privateTypeIndex = store.any(user, ns.solid('privateTypeIndex'), undefined, profile) ||
152
141
 
153
- await followOrCreateLink(store, user, ns.solid('privateTypeIndex') as NamedNode, suggestedPrivateTypeIndex, preferencesFile);
142
+ await followOrCreateLink(store, user, ns.solid('privateTypeIndex') as NamedNode, suggestedPrivateTypeIndex, preferencesFile);
154
143
 
155
144
  privateScopes = privateTypeIndex ? [ { label: 'private', index: privateTypeIndex as NamedNode, agent: user } ] : []
156
145
  } else {
@@ -187,7 +176,6 @@ export async function loadAllTypeIndexes (store:LiveStore, user:NamedNode) {
187
176
  }
188
177
 
189
178
  // Utility: remove duplicates from Array of NamedNodes
190
-
191
179
  export function uniqueNodes (arr: NamedNode[]): NamedNode[] {
192
180
  const uris = arr.map(x => x.uri)
193
181
  const set = new Set(uris)
@@ -210,7 +198,6 @@ export async function getScopedAppsFromIndex (store, scope, theClass: NamedNode
210
198
  reg => store.each(reg as NamedNode, ns.solid('instanceContainer'), null, index)).flat()
211
199
 
212
200
  // instanceContainers may be deprocatable if no one has used them
213
-
214
201
  const containers = uniqueNodes(instanceContainers)
215
202
  if (containers.length > 0) { console.log('@@ getScopedAppsFromIndex containers ', containers)}
216
203
  for (let i = 0; i < containers.length; i++) {
@@ -222,7 +209,6 @@ export async function getScopedAppsFromIndex (store, scope, theClass: NamedNode
222
209
  return instances.map(instance => { return {instance, scope}})
223
210
  }
224
211
 
225
-
226
212
  export async function getScopedAppInstances (store:LiveStore, klass: NamedNode, user: NamedNode):Promise<ScopedApp[]> {
227
213
  const scopes = await loadAllTypeIndexes(store, user)
228
214
  let scopedApps = []
@@ -232,16 +218,18 @@ export async function getScopedAppInstances (store:LiveStore, klass: NamedNode,
232
218
  }
233
219
  return scopedApps
234
220
  }
221
+
235
222
  // This is the function signature which used to be in solid-ui/logic
236
223
  // Recommended to use getScopedAppInstances instead as it provides more information.
237
224
  //
238
225
  export async function getAppInstances (store:LiveStore, klass: NamedNode): Promise<NamedNode[]> {
239
- const user = currentUser()
226
+ const user = authn.currentUser()
240
227
  if (!user) throw new Error('getAppInstances: Must be logged in to find apps.')
241
228
  const scopedAppInstances = await getScopedAppInstances(store, klass, user)
242
229
  return scopedAppInstances.map(scoped => scoped.instance)
243
230
  }
244
- /**
231
+
232
+ /*
245
233
  * Register a new app in a type index
246
234
  * used in chat in bookmark.js (solid-ui)
247
235
  * Returns the registration object if successful else null
@@ -276,5 +264,4 @@ export async function deleteTypeIndexRegistration (store: LiveStore, item) {
276
264
  const statements = store.statementsMatching(reg, null, null, item.scope.index)
277
265
  await store.updater.update(statements, [])
278
266
  }
279
-
280
267
  // ENDS