braid-text 0.0.9 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -16
- package/index.js +14 -20
- package/package.json +1 -1
- package/server-demo.js +24 -4
package/README.md
CHANGED
|
@@ -8,11 +8,16 @@ This library provides a simple http route handler, along with client code, enabl
|
|
|
8
8
|
- As little as 50 lines of code!
|
|
9
9
|
- With zero history overhead on client
|
|
10
10
|
- Supports [backpressure](https://braid.org/meeting-81/simpleton) to run smoothly on constrained servers
|
|
11
|
-
-
|
|
11
|
+
- Server merges with Diamond-Types
|
|
12
12
|
- Supports [Diamond Types](https://github.com/josephg/diamond-types) merge-type
|
|
13
|
-
-
|
|
13
|
+
- Fully peer-to-peer CRDT
|
|
14
|
+
- Fast / Robust / Extensively fuzz-tested
|
|
14
15
|
- Developed in [braid.org](https://braid.org)
|
|
15
16
|
|
|
17
|
+
This library makes it safe, easy & efficient to add collaborative text editing to every user-editable string in your web app. Make your app multiplayer!
|
|
18
|
+
|
|
19
|
+
Check out the [**demo video**](https://braid.org/video/https://invisiblecollege.s3.us-west-1.amazonaws.com/braid-meeting-86.mp4#4755) 📺 from the Braid 86 release!
|
|
20
|
+
|
|
16
21
|
### Demo: a Wiki!
|
|
17
22
|
|
|
18
23
|
This will run a collaboratively-editable wiki:
|
|
@@ -26,8 +31,9 @@ Now open these URLs in your browser:
|
|
|
26
31
|
- http://localhost:8888/demo (to see the demo text)
|
|
27
32
|
- http://localhost:8888/demo?editor (to edit the text)
|
|
28
33
|
- http://localhost:8888/demo?markdown-editor (to edit it as markdown)
|
|
34
|
+
- http://localhost:8888/any-other-path?editor (to create a new page, just go to its URL, and then start editing)
|
|
29
35
|
|
|
30
|
-
Or try opening the URL in [Braid-Chrome](https://github.com/braid-org/braid-chrome), or another Braid client, to edit it directly!
|
|
36
|
+
Or try opening the URL in [Braid-Chrome](https://github.com/braid-org/braid-chrome), or [another Braid client](https://bloop.monster/simpleditor), to edit it directly!
|
|
31
37
|
|
|
32
38
|
Check out the `server-demo.js` file to see examples for how to add access control, and a `/pages` endpoint to show all the edited pages.
|
|
33
39
|
|
|
@@ -59,8 +65,8 @@ http_server.on("request", (req, res) => {
|
|
|
59
65
|
- The files for a resource will all be prefixed with a url-encoding of `key` within this folder.
|
|
60
66
|
|
|
61
67
|
`braid_text.serve(req, res, options)`
|
|
62
|
-
- `req`:
|
|
63
|
-
- `res`:
|
|
68
|
+
- `req`: Incoming HTTP request object.
|
|
69
|
+
- `res`: Outgoing HTTP response object.
|
|
64
70
|
- `options`: <small style="color:lightgrey">[optional]</small> An object containing additional options:
|
|
65
71
|
- `key`: <small style="color:lightgrey">[optional]</small> ID of text resource to sync with. Defaults to `req.url`.
|
|
66
72
|
- This is the main method of this library, and does all the work to handle Braid-HTTP `GET` and `PUT` requests concerned with a specific text resource.
|
|
@@ -72,21 +78,20 @@ http_server.on("request", (req, res) => {
|
|
|
72
78
|
`await braid_text.get(key, options)`
|
|
73
79
|
- `key`: ID of text resource.
|
|
74
80
|
- `options`: An object containing additional options, like http headers:
|
|
75
|
-
- `version`: <small style="color:lightgrey">[optional]</small> The version to get.
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
78
|
-
- `merge_type`: <small style="color:lightgrey">[optional]</small>
|
|
79
|
-
- `peer`: <small style="color:lightgrey">[optional]</small>
|
|
80
|
-
|
|
81
|
-
- If we are NOT subscribing, returns `{version, body}`, with the `version` being returned, and the text as `body`. If we are subscribing, this returns nothing.
|
|
81
|
+
- `version`: <small style="color:lightgrey">[optional]</small> The [version](https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-braid-http#section-2) to get, as an array of strings. (The array is typically length 1.)
|
|
82
|
+
- `parents`: <small style="color:lightgrey">[optional]</small> The version to start the subscription at, as an array of strings.
|
|
83
|
+
- `subscribe: cb`: <small style="color:lightgrey">[optional]</small> Instead of returning the state; [subscribes](https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-braid-http#section-4) to the state, and calls `cb` with the initial state and each update. The function `cb` will be called with a Braid [update](https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-braid-http#section-3) of the form `cb({version, parents, body, patches})`.
|
|
84
|
+
- `merge_type`: <small style="color:lightgrey">[optional]</small> The CRDT/OT [merge-type](https://raw.githubusercontent.com/braid-org/braid-spec/master/draft-toomim-httpbis-merge-types-00.txt) algorithm to emulate. Currently supports `"simpleton"` (default) and `"dt"`.
|
|
85
|
+
- `peer`: <small style="color:lightgrey">[optional]</small> Unique string ID that identifies the peer making the subscription. Mutations will not be echoed back to the same peer that `PUT`s them, for any `PUT` setting the same `peer` header.
|
|
86
|
+
- If NOT subscribing, returns `{version: <current_version>, body: <current-text>}`. If subscribing, returns nothing.
|
|
82
87
|
|
|
83
88
|
`await braid_text.put(key, options)`
|
|
84
89
|
- `key`: ID of text resource.
|
|
85
90
|
- `options`: An object containing additional options, like http headers:
|
|
86
|
-
- `version`: <small style="color:lightgrey">[optional]</small> The version being
|
|
87
|
-
- `parents`: <small style="color:lightgrey">[optional]</small>
|
|
88
|
-
- `body`: <small style="color:lightgrey">[optional]</small> Use this to completely replace the existing text with this new text.
|
|
89
|
-
- `patches`: <small style="color:lightgrey">[optional]</small> Array of patches, each of the form `{unit: 'text', range: '[1:3]', content: 'hi'}`, which would replace the second and third unicode code-points in the text with `hi`.
|
|
91
|
+
- `version`: <small style="color:lightgrey">[optional]</small> The [version](https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-braid-http#section-2) being `PUT`, as an array of strings. Will be generated if not provided.
|
|
92
|
+
- `parents`: <small style="color:lightgrey">[optional]</small> The previous version being updated, as array of strings. Defaults to the server’s current version.
|
|
93
|
+
- `body`: <small style="color:lightgrey">[optional]</small> Use this to completely replace the existing text with this new text. See Braid [updates](https://datatracker.ietf.org/doc/html/draft-toomim-httpbis-braid-http#section-3).
|
|
94
|
+
- `patches`: <small style="color:lightgrey">[optional]</small> Array of patches, each of the form `{unit: 'text', range: '[1:3]', content: 'hi'}`, which would replace the second and third unicode code-points in the text with `hi`. See Braid [Range-Patches](https://github.com/braid-org/braid-spec/blob/master/draft-toomim-httpbis-range-patch-01.txt).
|
|
90
95
|
- `peer`: <small style="color:lightgrey">[optional]</small> Identifies this peer. This mutation will not be echoed back to `get` subscriptions that use this same `peer` header.
|
|
91
96
|
|
|
92
97
|
## General Use on Client
|
package/index.js
CHANGED
|
@@ -1273,29 +1273,23 @@ function codePoints_to_index(str, codePoints) {
|
|
|
1273
1273
|
}
|
|
1274
1274
|
|
|
1275
1275
|
function encode_filename(filename) {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
encoded = encoded.replace(/%2F/g, '!');
|
|
1284
|
-
|
|
1285
|
-
return encoded;
|
|
1276
|
+
// Swap all "!" and "/" characters
|
|
1277
|
+
let swapped = filename.replace(/[!/]/g, (match) => (match === "!" ? "/" : "!"))
|
|
1278
|
+
|
|
1279
|
+
// Encode the filename using encodeURIComponent()
|
|
1280
|
+
let encoded = encodeURIComponent(swapped)
|
|
1281
|
+
|
|
1282
|
+
return encoded
|
|
1286
1283
|
}
|
|
1287
1284
|
|
|
1288
1285
|
function decode_filename(encodedFilename) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
decoded = decoded.replace(/%21/g, '!');
|
|
1297
|
-
|
|
1298
|
-
return decoded;
|
|
1286
|
+
// Decode the filename using decodeURIComponent()
|
|
1287
|
+
let decoded = decodeURIComponent(encodedFilename)
|
|
1288
|
+
|
|
1289
|
+
// Swap all "/" and "!" characters
|
|
1290
|
+
decoded = decoded.replace(/[!/]/g, (match) => (match === "/" ? "!" : "/"))
|
|
1291
|
+
|
|
1292
|
+
return decoded
|
|
1299
1293
|
}
|
|
1300
1294
|
|
|
1301
1295
|
module.exports = braid_text
|
package/package.json
CHANGED
package/server-demo.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
var port = 8888
|
|
2
|
+
var port = process.argv[2] || 8888
|
|
3
3
|
|
|
4
4
|
var braid_text = require("./index.js")
|
|
5
5
|
|
|
@@ -11,6 +11,11 @@ var braid_text = require("./index.js")
|
|
|
11
11
|
var server = require("http").createServer(async (req, res) => {
|
|
12
12
|
console.log(`${req.method} ${req.url}`)
|
|
13
13
|
|
|
14
|
+
// Free the CORS
|
|
15
|
+
free_the_cors(req, res)
|
|
16
|
+
if (req.method === 'OPTIONS') return
|
|
17
|
+
|
|
18
|
+
|
|
14
19
|
if (req.url.endsWith("?editor")) {
|
|
15
20
|
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": "no-cache" })
|
|
16
21
|
require("fs").createReadStream("./editor.html").pipe(res)
|
|
@@ -30,9 +35,6 @@ var server = require("http").createServer(async (req, res) => {
|
|
|
30
35
|
// var pages = await braid_text.list()
|
|
31
36
|
// res.writeHead(200, {
|
|
32
37
|
// "Content-Type": "application/json",
|
|
33
|
-
// "Access-Control-Allow-Origin": "*",
|
|
34
|
-
// "Access-Control-Allow-Methods": "*",
|
|
35
|
-
// "Access-Control-Allow-Headers": "*",
|
|
36
38
|
// "Access-Control-Expose-Headers": "*"
|
|
37
39
|
// })
|
|
38
40
|
// res.end(JSON.stringify(pages))
|
|
@@ -73,3 +75,21 @@ var server = require("http").createServer(async (req, res) => {
|
|
|
73
75
|
server.listen(port, () => {
|
|
74
76
|
console.log(`server started on port ${port}`)
|
|
75
77
|
})
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
// Free the CORS!
|
|
81
|
+
function free_the_cors (req, res) {
|
|
82
|
+
res.setHeader('Range-Request-Allow-Methods', 'PATCH, PUT')
|
|
83
|
+
res.setHeader('Range-Request-Allow-Units', 'json')
|
|
84
|
+
res.setHeader("Patches", "OK")
|
|
85
|
+
var free_the_cors = {
|
|
86
|
+
"Access-Control-Allow-Origin": "*",
|
|
87
|
+
"Access-Control-Allow-Methods": "OPTIONS, HEAD, GET, PUT, UNSUBSCRIBE",
|
|
88
|
+
"Access-Control-Allow-Headers": "subscribe, client, version, parents, merge-type, content-type, content-range, patches, cache-control, peer"
|
|
89
|
+
}
|
|
90
|
+
Object.entries(free_the_cors).forEach(x => res.setHeader(x[0], x[1]))
|
|
91
|
+
if (req.method === 'OPTIONS') {
|
|
92
|
+
res.writeHead(200)
|
|
93
|
+
res.end()
|
|
94
|
+
}
|
|
95
|
+
}
|