braid-blob 0.0.21 → 0.0.22
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/index.js +23 -42
- package/package.json +2 -2
- package/test/tests.js +169 -2
- package/.claude/settings.local.json +0 -20
package/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
var {http_server: braidify, fetch: braid_fetch} = require('braid-http'),
|
|
2
2
|
{url_file_db} = require('url-file-db'),
|
|
3
|
-
fs = require('fs'),
|
|
4
3
|
path = require('path')
|
|
5
4
|
|
|
6
5
|
function create_braid_blob() {
|
|
@@ -9,9 +8,8 @@ function create_braid_blob() {
|
|
|
9
8
|
meta_folder: './braid-blob-meta',
|
|
10
9
|
cache: {},
|
|
11
10
|
key_to_subs: {},
|
|
12
|
-
peer: null, //
|
|
13
|
-
db: null
|
|
14
|
-
meta_db: null // url-file-db instance for meta storage
|
|
11
|
+
peer: null, // will be auto-generated if not set by the user
|
|
12
|
+
db: null // url-file-db instance with integrated meta storage
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
braid_blob.init = async () => {
|
|
@@ -21,25 +19,20 @@ function create_braid_blob() {
|
|
|
21
19
|
await braid_blob.init()
|
|
22
20
|
|
|
23
21
|
async function real_init() {
|
|
24
|
-
// Create url-file-db instance
|
|
25
|
-
braid_blob.db = await url_file_db.create(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
// Create url-file-db instance with integrated meta storage
|
|
23
|
+
braid_blob.db = await url_file_db.create(
|
|
24
|
+
braid_blob.db_folder,
|
|
25
|
+
braid_blob.meta_folder,
|
|
26
|
+
async (key) => {
|
|
27
|
+
// File changed externally, notify subscriptions
|
|
28
|
+
var body = await braid_blob.db.read(key)
|
|
29
|
+
await braid_blob.put(key, body, { skip_write: true })
|
|
30
|
+
}
|
|
31
|
+
)
|
|
34
32
|
|
|
35
|
-
// establish a peer id
|
|
36
|
-
if (!braid_blob.peer)
|
|
37
|
-
try {
|
|
38
|
-
braid_blob.peer = await fs.promises.readFile(`${braid_blob.meta_folder}/peer.txt`, 'utf8')
|
|
39
|
-
} catch (e) {}
|
|
33
|
+
// establish a peer id if not already set
|
|
40
34
|
if (!braid_blob.peer)
|
|
41
35
|
braid_blob.peer = Math.random().toString(36).slice(2)
|
|
42
|
-
await fs.promises.writeFile(`${braid_blob.meta_folder}/peer.txt`, braid_blob.peer)
|
|
43
36
|
}
|
|
44
37
|
}
|
|
45
38
|
|
|
@@ -69,11 +62,8 @@ function create_braid_blob() {
|
|
|
69
62
|
|
|
70
63
|
await braid_blob.init()
|
|
71
64
|
|
|
72
|
-
// Read the meta data
|
|
73
|
-
var meta = {}
|
|
74
|
-
var meta_content = await braid_blob.meta_db.read(key)
|
|
75
|
-
if (meta_content)
|
|
76
|
-
meta = JSON.parse(meta_content.toString('utf8'))
|
|
65
|
+
// Read the meta data using new meta API
|
|
66
|
+
var meta = braid_blob.db.get_meta(key) || {}
|
|
77
67
|
|
|
78
68
|
var their_e =
|
|
79
69
|
!options.version ?
|
|
@@ -93,11 +83,12 @@ function create_braid_blob() {
|
|
|
93
83
|
if (!options.skip_write)
|
|
94
84
|
await braid_blob.db.write(key, body)
|
|
95
85
|
|
|
96
|
-
//
|
|
86
|
+
// Update only the fields we want to change in metadata
|
|
87
|
+
var meta_updates = { event: their_e }
|
|
97
88
|
if (options.content_type)
|
|
98
|
-
|
|
89
|
+
meta_updates.content_type = options.content_type
|
|
99
90
|
|
|
100
|
-
await braid_blob.
|
|
91
|
+
await braid_blob.db.update_meta(key, meta_updates)
|
|
101
92
|
|
|
102
93
|
// Notify all subscriptions of the update
|
|
103
94
|
// (except the peer which made the PUT request itself)
|
|
@@ -153,11 +144,8 @@ function create_braid_blob() {
|
|
|
153
144
|
|
|
154
145
|
await braid_blob.init()
|
|
155
146
|
|
|
156
|
-
// Read the meta data
|
|
157
|
-
var meta = {}
|
|
158
|
-
var meta_content = await braid_blob.meta_db.read(key)
|
|
159
|
-
if (meta_content)
|
|
160
|
-
meta = JSON.parse(meta_content.toString('utf8'))
|
|
147
|
+
// Read the meta data using new meta API
|
|
148
|
+
var meta = braid_blob.db.get_meta(key) || {}
|
|
161
149
|
if (meta.event == null) return null
|
|
162
150
|
|
|
163
151
|
var result = {
|
|
@@ -238,12 +226,6 @@ function create_braid_blob() {
|
|
|
238
226
|
var body = req.method === 'PUT' && await slurp(req)
|
|
239
227
|
|
|
240
228
|
await within_fiber(options.key, async () => {
|
|
241
|
-
// Read the meta data from meta_db
|
|
242
|
-
var meta = {}
|
|
243
|
-
var meta_content = await braid_blob.meta_db.read(options.key)
|
|
244
|
-
if (meta_content)
|
|
245
|
-
meta = JSON.parse(meta_content.toString('utf8'))
|
|
246
|
-
|
|
247
229
|
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
248
230
|
if (!res.hasHeader("editable")) res.setHeader("Editable", "true")
|
|
249
231
|
if (!req.subscribe) res.setHeader("Accept-Subscribe", "true")
|
|
@@ -301,16 +283,15 @@ function create_braid_blob() {
|
|
|
301
283
|
}
|
|
302
284
|
} else if (req.method === 'PUT') {
|
|
303
285
|
// Handle PUT request to update binary files
|
|
304
|
-
|
|
286
|
+
var event = await braid_blob.put(options.key, body, {
|
|
305
287
|
version: req.version,
|
|
306
288
|
content_type: req.headers['content-type'],
|
|
307
289
|
peer: req.peer
|
|
308
290
|
})
|
|
309
|
-
res.setHeader("Version", version_to_header(
|
|
291
|
+
res.setHeader("Version", version_to_header(event != null ? [event] : []))
|
|
310
292
|
res.end('')
|
|
311
293
|
} else if (req.method === 'DELETE') {
|
|
312
294
|
await braid_blob.db.delete(options.key)
|
|
313
|
-
await braid_blob.meta_db.delete(options.key)
|
|
314
295
|
res.statusCode = 204 // No Content
|
|
315
296
|
res.end('')
|
|
316
297
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braid-blob",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "Library for collaborative blobs over http using braid.",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braid-blob",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"braid-http": "~1.3.82",
|
|
14
|
-
"url-file-db": "^0.0.
|
|
14
|
+
"url-file-db": "^0.0.19"
|
|
15
15
|
}
|
|
16
16
|
}
|
package/test/tests.js
CHANGED
|
@@ -34,7 +34,7 @@ runTest(
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
runTest(
|
|
37
|
-
"test that peer is
|
|
37
|
+
"test that peer is different each time we create a new instance",
|
|
38
38
|
async () => {
|
|
39
39
|
var r1 = await braid_fetch(`/eval`, {
|
|
40
40
|
method: 'POST',
|
|
@@ -62,7 +62,7 @@ runTest(
|
|
|
62
62
|
await require('fs').promises.rm(db, { recursive: true, force: true })
|
|
63
63
|
await require('fs').promises.rm(meta, { recursive: true, force: true })
|
|
64
64
|
|
|
65
|
-
res.end('' + (bb1.peer
|
|
65
|
+
res.end('' + (bb1.peer !== bb2.peer))
|
|
66
66
|
})()`
|
|
67
67
|
})
|
|
68
68
|
return await r1.text()
|
|
@@ -100,6 +100,39 @@ runTest(
|
|
|
100
100
|
'test_peer'
|
|
101
101
|
)
|
|
102
102
|
|
|
103
|
+
runTest(
|
|
104
|
+
"test that manually set peer persists through initialization",
|
|
105
|
+
async () => {
|
|
106
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
body: `void (async () => {
|
|
109
|
+
var test_id = 'test-db-' + Math.random().toString(36).slice(2)
|
|
110
|
+
var db = __dirname + '/' + test_id + '-db'
|
|
111
|
+
var meta = __dirname + '/' + test_id + '-meta'
|
|
112
|
+
|
|
113
|
+
// Create instance with manually set peer
|
|
114
|
+
var bb1 = braid_blob.create_braid_blob()
|
|
115
|
+
bb1.db_folder = db
|
|
116
|
+
bb1.meta_folder = meta
|
|
117
|
+
bb1.peer = 'custom-peer-id-123'
|
|
118
|
+
|
|
119
|
+
// Initialize (should keep our custom peer)
|
|
120
|
+
await bb1.init()
|
|
121
|
+
|
|
122
|
+
var peer_after_init = bb1.peer
|
|
123
|
+
|
|
124
|
+
// Clean up
|
|
125
|
+
await require('fs').promises.rm(db, { recursive: true, force: true })
|
|
126
|
+
await require('fs').promises.rm(meta, { recursive: true, force: true })
|
|
127
|
+
|
|
128
|
+
res.end(peer_after_init === 'custom-peer-id-123' ? 'true' : 'false: ' + peer_after_init)
|
|
129
|
+
})()`
|
|
130
|
+
})
|
|
131
|
+
return await r1.text()
|
|
132
|
+
},
|
|
133
|
+
'true'
|
|
134
|
+
)
|
|
135
|
+
|
|
103
136
|
runTest(
|
|
104
137
|
"test that PUTing with shorter event id doesn't do anything.",
|
|
105
138
|
async () => {
|
|
@@ -1142,6 +1175,140 @@ runTest(
|
|
|
1142
1175
|
'309'
|
|
1143
1176
|
)
|
|
1144
1177
|
|
|
1178
|
+
runTest(
|
|
1179
|
+
"test multiple writes preserve correct mtime across restarts",
|
|
1180
|
+
async () => {
|
|
1181
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1182
|
+
method: 'POST',
|
|
1183
|
+
body: `void (async () => {
|
|
1184
|
+
var fs = require('fs').promises
|
|
1185
|
+
var test_id = 'test-multi-write-' + Math.random().toString(36).slice(2)
|
|
1186
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1187
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1188
|
+
var test_key = 'test-file'
|
|
1189
|
+
|
|
1190
|
+
try {
|
|
1191
|
+
// Create first braid_blob instance
|
|
1192
|
+
var bb1 = braid_blob.create_braid_blob()
|
|
1193
|
+
bb1.db_folder = db_folder
|
|
1194
|
+
bb1.meta_folder = meta_folder
|
|
1195
|
+
|
|
1196
|
+
// First write
|
|
1197
|
+
await bb1.put(test_key, Buffer.from('content1'), {
|
|
1198
|
+
version: ['version-1']
|
|
1199
|
+
})
|
|
1200
|
+
|
|
1201
|
+
// Wait a bit to ensure different mtime
|
|
1202
|
+
await new Promise(resolve => setTimeout(resolve, 50))
|
|
1203
|
+
|
|
1204
|
+
// Second write to same file (this is where the bug would occur)
|
|
1205
|
+
await bb1.put(test_key, Buffer.from('content2'), {
|
|
1206
|
+
version: ['version-2']
|
|
1207
|
+
})
|
|
1208
|
+
|
|
1209
|
+
var result1 = await bb1.get(test_key)
|
|
1210
|
+
|
|
1211
|
+
// Now restart and check
|
|
1212
|
+
var bb2 = braid_blob.create_braid_blob()
|
|
1213
|
+
bb2.db_folder = db_folder
|
|
1214
|
+
bb2.meta_folder = meta_folder
|
|
1215
|
+
|
|
1216
|
+
// This should NOT trigger a file change callback
|
|
1217
|
+
var result2 = await bb2.get(test_key)
|
|
1218
|
+
|
|
1219
|
+
// Version should still be version-2, not regenerated
|
|
1220
|
+
var correct_version = (result2.version[0] === 'version-2')
|
|
1221
|
+
var content_correct = (result2.body.toString() === 'content2')
|
|
1222
|
+
|
|
1223
|
+
// Clean up
|
|
1224
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1225
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1226
|
+
|
|
1227
|
+
res.end(correct_version && content_correct ? 'true' :
|
|
1228
|
+
'false: version=' + result2.version[0] + ', content=' + result2.body.toString())
|
|
1229
|
+
} catch (e) {
|
|
1230
|
+
// Clean up even on error
|
|
1231
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1232
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1233
|
+
res.end('error: ' + e.message)
|
|
1234
|
+
}
|
|
1235
|
+
})()`
|
|
1236
|
+
})
|
|
1237
|
+
return await r1.text()
|
|
1238
|
+
},
|
|
1239
|
+
'true'
|
|
1240
|
+
)
|
|
1241
|
+
|
|
1242
|
+
runTest(
|
|
1243
|
+
"test that files keep same event ID across restarts when not edited",
|
|
1244
|
+
async () => {
|
|
1245
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1246
|
+
method: 'POST',
|
|
1247
|
+
body: `void (async () => {
|
|
1248
|
+
var fs = require('fs').promises
|
|
1249
|
+
var test_id = 'test-persist-event-' + Math.random().toString(36).slice(2)
|
|
1250
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1251
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1252
|
+
var test_key = 'test-file'
|
|
1253
|
+
var test_content = 'test content that should not change'
|
|
1254
|
+
|
|
1255
|
+
try {
|
|
1256
|
+
// Create first braid_blob instance
|
|
1257
|
+
var bb1 = braid_blob.create_braid_blob()
|
|
1258
|
+
bb1.db_folder = db_folder
|
|
1259
|
+
bb1.meta_folder = meta_folder
|
|
1260
|
+
|
|
1261
|
+
// Put a file with specific version
|
|
1262
|
+
var version1 = await bb1.put(test_key, Buffer.from(test_content), {
|
|
1263
|
+
version: ['test-peer-123456']
|
|
1264
|
+
})
|
|
1265
|
+
|
|
1266
|
+
// Get the file to verify it has the expected version
|
|
1267
|
+
var result1 = await bb1.get(test_key)
|
|
1268
|
+
|
|
1269
|
+
// Check what metadata was saved
|
|
1270
|
+
var meta1 = bb1.db.get_meta(test_key)
|
|
1271
|
+
var debug_info = 'meta1: ' + JSON.stringify(meta1) + '; '
|
|
1272
|
+
|
|
1273
|
+
// Wait a bit to ensure file system has settled
|
|
1274
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
1275
|
+
|
|
1276
|
+
// Now create a second braid_blob instance with the same folders
|
|
1277
|
+
// This simulates a restart
|
|
1278
|
+
var bb2 = braid_blob.create_braid_blob()
|
|
1279
|
+
bb2.db_folder = db_folder
|
|
1280
|
+
bb2.meta_folder = meta_folder
|
|
1281
|
+
|
|
1282
|
+
// Initialize bb2 by doing a get (this triggers init)
|
|
1283
|
+
var result2 = await bb2.get(test_key)
|
|
1284
|
+
|
|
1285
|
+
// Check what metadata bb2 sees
|
|
1286
|
+
var meta2 = bb2.db.get_meta(test_key)
|
|
1287
|
+
debug_info += 'meta2: ' + JSON.stringify(meta2) + '; '
|
|
1288
|
+
|
|
1289
|
+
// The version should be the same - no new event ID generated
|
|
1290
|
+
var versions_match = (result1.version[0] === result2.version[0])
|
|
1291
|
+
var both_have_expected = (result1.version[0] === 'test-peer-123456')
|
|
1292
|
+
|
|
1293
|
+
// Clean up
|
|
1294
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1295
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1296
|
+
|
|
1297
|
+
res.end(versions_match && both_have_expected ? 'true' :
|
|
1298
|
+
'false: v1=' + result1.version[0] + ', v2=' + result2.version[0] + ' | ' + debug_info)
|
|
1299
|
+
} catch (e) {
|
|
1300
|
+
// Clean up even on error
|
|
1301
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1302
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1303
|
+
res.end('error: ' + e.message)
|
|
1304
|
+
}
|
|
1305
|
+
})()`
|
|
1306
|
+
})
|
|
1307
|
+
return await r1.text()
|
|
1308
|
+
},
|
|
1309
|
+
'true'
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1145
1312
|
}
|
|
1146
1313
|
|
|
1147
1314
|
// Export for Node.js (CommonJS)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(node test/test.js:*)",
|
|
5
|
-
"Bash(lsof:*)",
|
|
6
|
-
"Bash(xargs kill -9)",
|
|
7
|
-
"Bash(curl:*)",
|
|
8
|
-
"Bash(cat:*)",
|
|
9
|
-
"Bash(node test.js:*)",
|
|
10
|
-
"Bash(timeout 5 node:*)",
|
|
11
|
-
"Bash(git checkout:*)",
|
|
12
|
-
"Bash(npm show:*)",
|
|
13
|
-
"Bash(npm install:*)",
|
|
14
|
-
"Bash(node:*)",
|
|
15
|
-
"Bash(git add:*)"
|
|
16
|
-
],
|
|
17
|
-
"deny": [],
|
|
18
|
-
"ask": []
|
|
19
|
-
}
|
|
20
|
-
}
|