braid-text 0.0.10 → 0.0.13
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 +2 -0
- package/index.js +32 -36
- package/package.json +1 -1
- package/server-demo.js +23 -3
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ This library provides a simple http route handler, along with client code, enabl
|
|
|
16
16
|
|
|
17
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
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
|
+
|
|
19
21
|
### Demo: a Wiki!
|
|
20
22
|
|
|
21
23
|
This will run a collaboratively-editable wiki:
|
package/index.js
CHANGED
|
@@ -118,20 +118,28 @@ braid_text.serve = async (req, res, options = {}) => {
|
|
|
118
118
|
done()
|
|
119
119
|
})
|
|
120
120
|
)
|
|
121
|
-
let patches =
|
|
121
|
+
let patches = null
|
|
122
|
+
let ee = null
|
|
123
|
+
try {
|
|
124
|
+
patches = await req.patches()
|
|
125
|
+
} catch (e) { ee = e }
|
|
122
126
|
await my_prev_put_p
|
|
123
127
|
|
|
124
|
-
let body = null
|
|
125
|
-
if (patches[0]?.unit === 'everything') {
|
|
126
|
-
body = patches[0].content
|
|
127
|
-
patches = null
|
|
128
|
-
}
|
|
129
|
-
|
|
130
128
|
try {
|
|
129
|
+
if (ee) throw ee
|
|
130
|
+
|
|
131
|
+
let body = null
|
|
132
|
+
if (patches[0]?.unit === 'everything') {
|
|
133
|
+
body = patches[0].content
|
|
134
|
+
patches = null
|
|
135
|
+
}
|
|
136
|
+
|
|
131
137
|
await braid_text.put(resource, { peer, version: req.version, parents: req.parents, patches, body, merge_type: req.headers["merge-type"] })
|
|
138
|
+
|
|
139
|
+
options.put_cb(options.key, resource.doc.get())
|
|
132
140
|
} catch (e) {
|
|
133
141
|
console.log(`EEE= ${e}:${e.stack}`)
|
|
134
|
-
// we couldn't apply the version,
|
|
142
|
+
// we couldn't apply the version, possibly because we're missing its parents,
|
|
135
143
|
// we want to send a 4XX error, so the client will resend this request later,
|
|
136
144
|
// hopefully after we've received the necessary parents.
|
|
137
145
|
|
|
@@ -155,11 +163,9 @@ braid_text.serve = async (req, res, options = {}) => {
|
|
|
155
163
|
// - 428 Precondition Required
|
|
156
164
|
// - pros: the name sounds right
|
|
157
165
|
// - cons: typically implies that the request was missing an http conditional field like If-Match. that is to say, it implies that the request is missing a precondition, not that the server is missing a precondition
|
|
158
|
-
return done_my_turn(425, "The server
|
|
166
|
+
return done_my_turn(425, "The server failed to apply this version.")
|
|
159
167
|
}
|
|
160
168
|
|
|
161
|
-
options.put_cb(options.key, resource.doc.get())
|
|
162
|
-
|
|
163
169
|
return done_my_turn(200)
|
|
164
170
|
}
|
|
165
171
|
|
|
@@ -544,11 +550,7 @@ async function file_sync(key, process_delta, get_init) {
|
|
|
544
550
|
let threshold = 0
|
|
545
551
|
|
|
546
552
|
// Ensure the existence of db_folder
|
|
547
|
-
|
|
548
|
-
await fs.promises.access(braid_text.db_folder);
|
|
549
|
-
} catch (err) {
|
|
550
|
-
await fs.promises.mkdir(braid_text.db_folder, { recursive: true });
|
|
551
|
-
}
|
|
553
|
+
await fs.promises.mkdir(braid_text.db_folder, { recursive: true });
|
|
552
554
|
|
|
553
555
|
// Read existing files and sort by numbers.
|
|
554
556
|
const files = (await get_files_for_key(key))
|
|
@@ -1273,29 +1275,23 @@ function codePoints_to_index(str, codePoints) {
|
|
|
1273
1275
|
}
|
|
1274
1276
|
|
|
1275
1277
|
function encode_filename(filename) {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
encoded = encoded.replace(/%2F/g, '!');
|
|
1284
|
-
|
|
1285
|
-
return encoded;
|
|
1278
|
+
// Swap all "!" and "/" characters
|
|
1279
|
+
let swapped = filename.replace(/[!/]/g, (match) => (match === "!" ? "/" : "!"))
|
|
1280
|
+
|
|
1281
|
+
// Encode the filename using encodeURIComponent()
|
|
1282
|
+
let encoded = encodeURIComponent(swapped)
|
|
1283
|
+
|
|
1284
|
+
return encoded
|
|
1286
1285
|
}
|
|
1287
1286
|
|
|
1288
1287
|
function decode_filename(encodedFilename) {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
decoded = decoded.replace(/%21/g, '!');
|
|
1297
|
-
|
|
1298
|
-
return decoded;
|
|
1288
|
+
// Decode the filename using decodeURIComponent()
|
|
1289
|
+
let decoded = decodeURIComponent(encodedFilename)
|
|
1290
|
+
|
|
1291
|
+
// Swap all "/" and "!" characters
|
|
1292
|
+
decoded = decoded.replace(/[!/]/g, (match) => (match === "/" ? "!" : "/"))
|
|
1293
|
+
|
|
1294
|
+
return decoded
|
|
1299
1295
|
}
|
|
1300
1296
|
|
|
1301
1297
|
module.exports = braid_text
|
package/package.json
CHANGED
package/server-demo.js
CHANGED
|
@@ -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
|
+
}
|