From a2cc33b1de416bef592ecf25816f17017e62fe8e Mon Sep 17 00:00:00 2001
From: anonymous
Date: Wed, 1 Feb 2023 19:59:23 -0500
Subject: minor changes
---
README.md | 8 +--
auth.js | 125 -----------------------------------------
graffiti.js | 13 +++--
index.html | 84 +++++++++++++++++++++++++++
plugins/vue/plugin.js | 2 +-
src/auth.js | 125 +++++++++++++++++++++++++++++++++++++++++
src/logoot.js | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++
test.html | 84 ---------------------------
utils/logoot.js | 153 --------------------------------------------------
9 files changed, 374 insertions(+), 373 deletions(-)
delete mode 100644 auth.js
create mode 100644 index.html
create mode 100644 src/auth.js
create mode 100644 src/logoot.js
delete mode 100644 test.html
delete mode 100644 utils/logoot.js
diff --git a/README.md b/README.md
index cf97d02..8c06ad0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,4 @@
-# Graffiti for Vanilla Javascript
+# Graffiti Javascript Library
-This is the base Javascript library that interfaces with the [Graffiti server](https://github.com/graffiti-garden/server).
-We recommend not using this vanilla library itself but instead using framework plugins that are built on top of it like the [Graffiti plugin for Vue.JS](https://github.com/graffiti-garden/graffiti-x-vue).
-
-If you create a [local Graffiti instance](https://github.com/graffiti-garden/server#local-usage) and a local webserver in this directory (*e.g.* `python3 -m http.server`) and navigate to `test.html` (*e.g.* [http://localhost:8000/test.html](http://localhost:8000/test.html)) you should be able to log in and test each of the Graffiti primitives: `subscribe`, `unsubscribe`, `update`, `remove`. These primitives will be robust to spamming and network interruptions.
+This is the base Javascript library that interfaces with the [Graffiti server](https://github.com/graffiti-garden/graffiti-server).
+It also includes a plugin that extends it to operate with the [Vue.js framework](https://vuejs.org/).
diff --git a/auth.js b/auth.js
deleted file mode 100644
index 8ee803d..0000000
--- a/auth.js
+++ /dev/null
@@ -1,125 +0,0 @@
-export default {
-
- async logIn(graffitiURL) {
- // Generate a random client secret and state
- const clientSecret = crypto.randomUUID()
- const state = crypto.randomUUID()
-
- // The client ID is the secret's hex hash
- const clientID = await this.sha256(clientSecret)
-
- // Store the client secret as a local variable
- window.localStorage.setItem('graffitiClientSecret', clientSecret)
- window.localStorage.setItem('graffitiClientID', clientID)
- window.localStorage.setItem('graffitiAuthState', state)
-
- // Redirect to the login window
- const loginURL = this.authURL(graffitiURL)
- loginURL.searchParams.set('client_id', clientID)
- loginURL.searchParams.set('redirect_uri', window.location.href)
- loginURL.searchParams.set('state', state)
- window.location.href = loginURL
- },
-
- async connect(graffitiURL) {
-
- // Check to see if we are already logged in
- let token = window.localStorage.getItem('graffitiToken')
- let myID = window.localStorage.getItem('graffitiID')
-
- if (!token || !myID) {
- // Remove them both in case one exists
- // and the other does not
- token = myID = null
-
- // Check to see if we are redirecting back
- const url = new URL(window.location)
- if (url.searchParams.has('code')) {
-
- // Extract the code and state from the URL and strip it from the history
- const code = url.searchParams.get('code')
- const state = url.searchParams.get('state')
- url.searchParams.delete('code')
- url.searchParams.delete('state')
- window.history.replaceState({}, '', url)
-
- // Get stored variables and remove them
- const clientSecret = window.localStorage.getItem('graffitiClientSecret')
- const clientID = window.localStorage.getItem('graffitiClientID')
- const storedState = window.localStorage.getItem('graffitiAuthState')
- window.localStorage.removeItem('graffitiClientSecret')
- window.localStorage.removeItem('graffitiClientID')
- window.localStorage.removeItem('graffitiAuthState')
-
- // Make sure state has been preserved
- if (state != storedState) {
- throw new Error("The state in local storage does not match the state sent by the server")
- }
-
- // Construct the body of the POST
- let form = new FormData()
- form.append('client_id', clientID)
- form.append('client_secret', clientSecret)
- form.append('code', code)
-
- // Ask to exchange the code for a token
- const tokenURL = this.authURL(graffitiURL)
- tokenURL.pathname = '/token'
- const response = await fetch(tokenURL, {
- method: 'post',
- body: form
- })
-
- // Make sure the response is OK
- if (!response.ok) {
- let reason = response.status + ": "
- try {
- reason += (await response.json()).detail
- } catch (e) {
- reason += response.statusText
- }
-
- throw new Error(`The authorization code could not be exchanged for a token.\n\n${reason}`)
- }
-
- // Parse out the token
- const data = await response.json()
- token = data.access_token
- myID = data.owner_id
-
- // And make sure that the token is valid
- if (!token || !myID) {
- throw new Error(`The authorization token could not be parsed from the response.\n\n${data}`)
- }
-
- // Store the token and ID
- window.localStorage.setItem('graffitiToken', token)
- window.localStorage.setItem('graffitiID', myID)
- }
- }
-
- return { myID, token }
-
- },
-
- logOut() {
- window.localStorage.removeItem('graffitiToken')
- window.localStorage.removeItem('graffitiID')
- window.location.reload()
- },
-
- authURL(graffitiURL) {
- const url = new URL(graffitiURL)
- url.host = "auth." + url.host
- return url
- },
-
- async sha256(input) {
- const encoder = new TextEncoder()
- const inputBytes = encoder.encode(input)
- const outputBuffer = await crypto.subtle.digest('SHA-256', inputBytes)
- const outputArray = Array.from(new Uint8Array(outputBuffer))
- return outputArray.map(b => b.toString(16).padStart(2, '0')).join('')
- }
-
-}
diff --git a/graffiti.js b/graffiti.js
index d28b64d..9b92b4f 100644
--- a/graffiti.js
+++ b/graffiti.js
@@ -1,4 +1,5 @@
-import Auth from './auth.js'
+import Auth from './src/auth.js'
+//import Collection from './src/collection.js'
export default class {
@@ -36,10 +37,12 @@ export default class {
this.#connect()
}
- async waitTilOpen() {
- await new Promise(resolve => {
- this.eventTarget.addEventListener("graffitiOpen", () => resolve() )
- })
+ async opened() {
+ if (!this.open) {
+ await new Promise(resolve => {
+ this.eventTarget.addEventListener("graffitiOpen", () => resolve() )
+ })
+ }
}
#connect() {
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..e3c2dc0
--- /dev/null
+++ b/index.html
@@ -0,0 +1,84 @@
+
+
+
+
Graffiti x JS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/vue/plugin.js b/plugins/vue/plugin.js
index e5483c6..2c61f40 100644
--- a/plugins/vue/plugin.js
+++ b/plugins/vue/plugin.js
@@ -12,7 +12,7 @@ export default {
// These ID need to change after opening
app.config.globalProperties.$graffitiID = ref(null)
- graffiti.waitTilOpen().then(()=> {
+ graffiti.opened().then(()=> {
app.config.globalProperties.$graffitiID.value = graffiti.myID
})
diff --git a/src/auth.js b/src/auth.js
new file mode 100644
index 0000000..8ee803d
--- /dev/null
+++ b/src/auth.js
@@ -0,0 +1,125 @@
+export default {
+
+ async logIn(graffitiURL) {
+ // Generate a random client secret and state
+ const clientSecret = crypto.randomUUID()
+ const state = crypto.randomUUID()
+
+ // The client ID is the secret's hex hash
+ const clientID = await this.sha256(clientSecret)
+
+ // Store the client secret as a local variable
+ window.localStorage.setItem('graffitiClientSecret', clientSecret)
+ window.localStorage.setItem('graffitiClientID', clientID)
+ window.localStorage.setItem('graffitiAuthState', state)
+
+ // Redirect to the login window
+ const loginURL = this.authURL(graffitiURL)
+ loginURL.searchParams.set('client_id', clientID)
+ loginURL.searchParams.set('redirect_uri', window.location.href)
+ loginURL.searchParams.set('state', state)
+ window.location.href = loginURL
+ },
+
+ async connect(graffitiURL) {
+
+ // Check to see if we are already logged in
+ let token = window.localStorage.getItem('graffitiToken')
+ let myID = window.localStorage.getItem('graffitiID')
+
+ if (!token || !myID) {
+ // Remove them both in case one exists
+ // and the other does not
+ token = myID = null
+
+ // Check to see if we are redirecting back
+ const url = new URL(window.location)
+ if (url.searchParams.has('code')) {
+
+ // Extract the code and state from the URL and strip it from the history
+ const code = url.searchParams.get('code')
+ const state = url.searchParams.get('state')
+ url.searchParams.delete('code')
+ url.searchParams.delete('state')
+ window.history.replaceState({}, '', url)
+
+ // Get stored variables and remove them
+ const clientSecret = window.localStorage.getItem('graffitiClientSecret')
+ const clientID = window.localStorage.getItem('graffitiClientID')
+ const storedState = window.localStorage.getItem('graffitiAuthState')
+ window.localStorage.removeItem('graffitiClientSecret')
+ window.localStorage.removeItem('graffitiClientID')
+ window.localStorage.removeItem('graffitiAuthState')
+
+ // Make sure state has been preserved
+ if (state != storedState) {
+ throw new Error("The state in local storage does not match the state sent by the server")
+ }
+
+ // Construct the body of the POST
+ let form = new FormData()
+ form.append('client_id', clientID)
+ form.append('client_secret', clientSecret)
+ form.append('code', code)
+
+ // Ask to exchange the code for a token
+ const tokenURL = this.authURL(graffitiURL)
+ tokenURL.pathname = '/token'
+ const response = await fetch(tokenURL, {
+ method: 'post',
+ body: form
+ })
+
+ // Make sure the response is OK
+ if (!response.ok) {
+ let reason = response.status + ": "
+ try {
+ reason += (await response.json()).detail
+ } catch (e) {
+ reason += response.statusText
+ }
+
+ throw new Error(`The authorization code could not be exchanged for a token.\n\n${reason}`)
+ }
+
+ // Parse out the token
+ const data = await response.json()
+ token = data.access_token
+ myID = data.owner_id
+
+ // And make sure that the token is valid
+ if (!token || !myID) {
+ throw new Error(`The authorization token could not be parsed from the response.\n\n${data}`)
+ }
+
+ // Store the token and ID
+ window.localStorage.setItem('graffitiToken', token)
+ window.localStorage.setItem('graffitiID', myID)
+ }
+ }
+
+ return { myID, token }
+
+ },
+
+ logOut() {
+ window.localStorage.removeItem('graffitiToken')
+ window.localStorage.removeItem('graffitiID')
+ window.location.reload()
+ },
+
+ authURL(graffitiURL) {
+ const url = new URL(graffitiURL)
+ url.host = "auth." + url.host
+ return url
+ },
+
+ async sha256(input) {
+ const encoder = new TextEncoder()
+ const inputBytes = encoder.encode(input)
+ const outputBuffer = await crypto.subtle.digest('SHA-256', inputBytes)
+ const outputArray = Array.from(new Uint8Array(outputBuffer))
+ return outputArray.map(b => b.toString(16).padStart(2, '0')).join('')
+ }
+
+}
diff --git a/src/logoot.js b/src/logoot.js
new file mode 100644
index 0000000..c8c7c02
--- /dev/null
+++ b/src/logoot.js
@@ -0,0 +1,153 @@
+export default {
+
+ query(property) {
+ return {
+ [property]: {
+ $type: 'array',
+ $type: ['int', 'long'],
+ },
+ $nor: [
+ { [property]: { $gt: this.maxInt } },
+ { [property]: { $lt: 0 } },
+ ]
+ }
+ },
+
+ get before() {
+ return []
+ },
+
+ get after() {
+ return [this.maxInt+1]
+ },
+
+ between(a, b, scale=100) {
+ // Strip zeros and find common length
+ const aLength = this.lengthWithoutZeros(a)
+ const bLength = this.lengthWithoutZeros(b)
+ const minLength = Math.min(aLength, bLength)
+
+ // Initialize output
+ const out = []
+
+ // Find the break point where a[i] != b[i]
+ let i = 0
+ while (i < minLength && a[i] == b[i]) {
+ out.push(a[i])
+ i++
+ }
+
+ // Initialize upper and lower bounds for
+ // sampling the last digit
+ let lowerBound = 1
+ let upperBound = this.maxInt
+
+ if (i < minLength) {
+ // If the break happened before we hit
+ // the end of one of the arrays
+
+ if (Math.abs(a[i] - b[i]) > 1) {
+ // If a[i] and b[i] are more than one
+ // away from each other, just sample
+ // between them
+ lowerBound = Math.min(a[i], b[i]) + 1
+ upperBound = Math.max(a[i], b[i]) - 1
+ } else {
+ // If they are one away no integers
+ // will fit in between, so add new layer
+ const lesser = (a[i] < b[i])? a : b
+ out.push(lesser[i])
+ i++
+
+ while (i < lesser.length && lesser[i] >= this.maxInt) {
+ // If the lesser is at it's limit,
+ // we will need to add even more layers
+ out.push(lesser[i])
+ i++
+ }
+
+ if (i < lesser.length) {
+ // Sample something greater than
+ // the lesser digit
+ lowerBound = lesser[i] + 1
+ }
+ }
+ } else {
+ // The break happened because we hit
+ // the end of one of the arrays.
+
+ if (aLength == bLength) {
+ // If they are entirely equal,
+ // there is nothing in between
+ // just return what we have
+ return out
+ }
+
+ const longerLength = Math.max(aLength, bLength)
+ const longer = (a.length == longerLength)? a : b
+ while (i < longerLength && longer[i] == 0) {
+ // Skip past the zeros because we can't sample
+ // for digits less than zero
+ out.push(0)
+ i++
+ }
+
+ if (i < longerLength) {
+ if (longer[i] == 1) {
+ // If longer is at it's limit,
+ // we still need to add another layer
+ out.push(0)
+ } else {
+ upperBound = longer[i] - 1
+ }
+ }
+ }
+
+ // Create a random number in [0,1] but bias it to be small,
+ // so that numbers tend to increase by a small amount.
+ let random = Math.random()
+ random = -Math.log(1-random)/scale
+ random = Math.min(random, 1)
+
+ // Finally, sample between the upper and lower bounds
+ out.push(Math.floor(random * (upperBound + 1 - lowerBound)) + lowerBound)
+ return out
+ },
+
+ compare(a, b) {
+ // Strip zeros and find common length
+ const aLength = this.lengthWithoutZeros(a)
+ const bLength = this.lengthWithoutZeros(b)
+ const minLength = Math.min(aLength, bLength)
+
+ // See if there are any differences
+ for (let i = 0; i < minLength; i++) {
+ if (a[i] > b[i]) {
+ return 1
+ } else if (a[i] < b[i]) {
+ return -1
+ }
+ }
+
+ // If they are all the same up til now,
+ // the longer one is bigger
+ if (aLength > bLength) {
+ return 1
+ } else if (aLength < bLength) {
+ return -1
+ } else {
+ return 0
+ }
+ },
+
+
+ lengthWithoutZeros(a) {
+ let length = a.length
+ while (length > 0 && a[length - 1] == 0) {
+ length--
+ }
+ return length
+ },
+
+ maxInt: 9007199254740991,
+}
diff --git a/test.html b/test.html
deleted file mode 100644
index 3fb639f..0000000
--- a/test.html
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
Graffiti x JS
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/utils/logoot.js b/utils/logoot.js
deleted file mode 100644
index c8c7c02..0000000
--- a/utils/logoot.js
+++ /dev/null
@@ -1,153 +0,0 @@
-export default {
-
- query(property) {
- return {
- [property]: {
- $type: 'array',
- $type: ['int', 'long'],
- },
- $nor: [
- { [property]: { $gt: this.maxInt } },
- { [property]: { $lt: 0 } },
- ]
- }
- },
-
- get before() {
- return []
- },
-
- get after() {
- return [this.maxInt+1]
- },
-
- between(a, b, scale=100) {
- // Strip zeros and find common length
- const aLength = this.lengthWithoutZeros(a)
- const bLength = this.lengthWithoutZeros(b)
- const minLength = Math.min(aLength, bLength)
-
- // Initialize output
- const out = []
-
- // Find the break point where a[i] != b[i]
- let i = 0
- while (i < minLength && a[i] == b[i]) {
- out.push(a[i])
- i++
- }
-
- // Initialize upper and lower bounds for
- // sampling the last digit
- let lowerBound = 1
- let upperBound = this.maxInt
-
- if (i < minLength) {
- // If the break happened before we hit
- // the end of one of the arrays
-
- if (Math.abs(a[i] - b[i]) > 1) {
- // If a[i] and b[i] are more than one
- // away from each other, just sample
- // between them
- lowerBound = Math.min(a[i], b[i]) + 1
- upperBound = Math.max(a[i], b[i]) - 1
- } else {
- // If they are one away no integers
- // will fit in between, so add new layer
- const lesser = (a[i] < b[i])? a : b
- out.push(lesser[i])
- i++
-
- while (i < lesser.length && lesser[i] >= this.maxInt) {
- // If the lesser is at it's limit,
- // we will need to add even more layers
- out.push(lesser[i])
- i++
- }
-
- if (i < lesser.length) {
- // Sample something greater than
- // the lesser digit
- lowerBound = lesser[i] + 1
- }
- }
- } else {
- // The break happened because we hit
- // the end of one of the arrays.
-
- if (aLength == bLength) {
- // If they are entirely equal,
- // there is nothing in between
- // just return what we have
- return out
- }
-
- const longerLength = Math.max(aLength, bLength)
- const longer = (a.length == longerLength)? a : b
- while (i < longerLength && longer[i] == 0) {
- // Skip past the zeros because we can't sample
- // for digits less than zero
- out.push(0)
- i++
- }
-
- if (i < longerLength) {
- if (longer[i] == 1) {
- // If longer is at it's limit,
- // we still need to add another layer
- out.push(0)
- } else {
- upperBound = longer[i] - 1
- }
- }
- }
-
- // Create a random number in [0,1] but bias it to be small,
- // so that numbers tend to increase by a small amount.
- let random = Math.random()
- random = -Math.log(1-random)/scale
- random = Math.min(random, 1)
-
- // Finally, sample between the upper and lower bounds
- out.push(Math.floor(random * (upperBound + 1 - lowerBound)) + lowerBound)
- return out
- },
-
- compare(a, b) {
- // Strip zeros and find common length
- const aLength = this.lengthWithoutZeros(a)
- const bLength = this.lengthWithoutZeros(b)
- const minLength = Math.min(aLength, bLength)
-
- // See if there are any differences
- for (let i = 0; i < minLength; i++) {
- if (a[i] > b[i]) {
- return 1
- } else if (a[i] < b[i]) {
- return -1
- }
- }
-
- // If they are all the same up til now,
- // the longer one is bigger
- if (aLength > bLength) {
- return 1
- } else if (aLength < bLength) {
- return -1
- } else {
- return 0
- }
- },
-
-
- lengthWithoutZeros(a) {
- let length = a.length
- while (length > 0 && a[length - 1] == 0) {
- length--
- }
- return length
- },
-
- maxInt: 9007199254740991,
-}
--
cgit v1.2.3-70-g09d2