braid-text 0.2.71 → 0.2.73
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/AI-README.md +81 -0
- package/index.js +18 -4
- package/package.json +1 -1
- package/test/tests.js +53 -0
package/AI-README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# AI-README for braid-text
|
|
2
|
+
|
|
3
|
+
This document provides AI assistants with key information about the braid-text project, including development procedures and architecture notes.
|
|
4
|
+
|
|
5
|
+
## Release Procedure
|
|
6
|
+
|
|
7
|
+
Follow these steps to create a new release:
|
|
8
|
+
|
|
9
|
+
1. **Version Bump**
|
|
10
|
+
- Update the version in `package.json` (use smallest version bump: patch level)
|
|
11
|
+
- Current version format: `0.2.x`
|
|
12
|
+
|
|
13
|
+
2. **Run Tests**
|
|
14
|
+
- Run `node test/test.js` - should show all 74 tests passing
|
|
15
|
+
- Run `node test/fuzz-test.js` - should run without errors (best_n = Infinity @ NaN indicates success)
|
|
16
|
+
- Both tests must pass before proceeding
|
|
17
|
+
|
|
18
|
+
3. **Commit Changes**
|
|
19
|
+
- Use commit message format: `VERSION - description`
|
|
20
|
+
- Example: `0.2.73 - adds automatic test cleanup`
|
|
21
|
+
- Keep description concise and descriptive
|
|
22
|
+
|
|
23
|
+
4. **Push to Remote**
|
|
24
|
+
- Run `git push` to push to the remote repository
|
|
25
|
+
|
|
26
|
+
5. **Publish to npm**
|
|
27
|
+
- Run `npm publish` to publish the new version
|
|
28
|
+
|
|
29
|
+
## Test Suite
|
|
30
|
+
|
|
31
|
+
### test/test.js
|
|
32
|
+
- Main test suite with 74 tests
|
|
33
|
+
- Tests Braid protocol, version control, syncing, and collaboration features
|
|
34
|
+
- Automatically cleans up `test_db_folder` after completion
|
|
35
|
+
- Run with: `node test/test.js`
|
|
36
|
+
- Filter tests with: `node test/test.js --filter="sync"`
|
|
37
|
+
|
|
38
|
+
### test/fuzz-test.js
|
|
39
|
+
- Fuzz testing suite that generates random edits and verifies correctness
|
|
40
|
+
- Tests diamond-types integration and merge operations
|
|
41
|
+
- Runs 10,000 iterations by default
|
|
42
|
+
- Success indicated by `best_n = Infinity @ NaN` (no failures found)
|
|
43
|
+
- Run with: `node test/fuzz-test.js`
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
- **index.js** - Main library file implementing Braid protocol for collaborative text
|
|
48
|
+
- **package.json** - Package configuration and dependencies
|
|
49
|
+
- **test/** - Test suite directory
|
|
50
|
+
- **test.js** - Main test runner (supports both console and browser modes)
|
|
51
|
+
- **tests.js** - Test definitions (shared between console and browser)
|
|
52
|
+
- **fuzz-test.js** - Fuzz testing suite
|
|
53
|
+
- **test.html** - Browser test interface
|
|
54
|
+
|
|
55
|
+
## Key Dependencies
|
|
56
|
+
|
|
57
|
+
- **@braid.org/diamond-types-node** - CRDT implementation for conflict-free text editing
|
|
58
|
+
- **braid-http** - Braid protocol HTTP implementation
|
|
59
|
+
- **url-file-db** - File-based database for persistent storage
|
|
60
|
+
|
|
61
|
+
## Architecture Notes
|
|
62
|
+
|
|
63
|
+
- Implements the Braid protocol for synchronizing collaborative text over HTTP
|
|
64
|
+
- Uses diamond-types CRDT for conflict-free merging
|
|
65
|
+
- Supports both simpleton and dt (diamond-types) merge strategies
|
|
66
|
+
- Provides version control with parents tracking and version history
|
|
67
|
+
- File-based persistence with case-insensitive filesystem support
|
|
68
|
+
|
|
69
|
+
## Common Operations
|
|
70
|
+
|
|
71
|
+
### Running Tests Locally
|
|
72
|
+
```bash
|
|
73
|
+
node test/test.js # Run all tests
|
|
74
|
+
node test/test.js --filter="sync" # Run tests matching "sync"
|
|
75
|
+
node test/fuzz-test.js # Run fuzz tests
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Test Cleanup
|
|
79
|
+
The test suite automatically cleans up temporary files and databases. If manual cleanup is needed:
|
|
80
|
+
- Test database is created at `test/test_db_folder` during tests
|
|
81
|
+
- Automatically removed after test completion
|
package/index.js
CHANGED
|
@@ -304,6 +304,17 @@ function create_braid_text() {
|
|
|
304
304
|
if (!req.subscribe) {
|
|
305
305
|
res.setHeader("Accept-Subscribe", "true")
|
|
306
306
|
|
|
307
|
+
// Set headers based on request type
|
|
308
|
+
// Current-Version: always for dt encoding, or when version/parents present
|
|
309
|
+
if (req.headers['accept-transfer-encoding'] === 'dt' || req.version || req.parents) {
|
|
310
|
+
res.setHeader("Current-Version", get_current_version())
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Merge-Type: only when version/parents present
|
|
314
|
+
if (req.version || req.parents) {
|
|
315
|
+
res.setHeader("Merge-Type", merge_type)
|
|
316
|
+
}
|
|
317
|
+
|
|
307
318
|
// special case for HEAD asking for version/parents,
|
|
308
319
|
// to be faster by not reconstructing body
|
|
309
320
|
if (req.method === "HEAD" && (req.version || req.parents))
|
|
@@ -321,13 +332,10 @@ function create_braid_text() {
|
|
|
321
332
|
}
|
|
322
333
|
|
|
323
334
|
if (req.headers['accept-transfer-encoding'] === 'dt') {
|
|
324
|
-
res.setHeader("Current-Version", get_current_version())
|
|
325
335
|
res.setHeader("X-Transfer-Encoding", 'dt')
|
|
326
336
|
res.setHeader("Content-Length", x.body.length)
|
|
327
337
|
return my_end(209, req.method === "HEAD" ? null : x.body, 'Multiresponse')
|
|
328
338
|
} else {
|
|
329
|
-
if (req.version || req.parents)
|
|
330
|
-
res.setHeader("Current-Version", get_current_version())
|
|
331
339
|
res.setHeader("Version", ascii_ify(x.version.map((x) => JSON.stringify(x)).join(", ")))
|
|
332
340
|
var buffer = Buffer.from(x.body, "utf8")
|
|
333
341
|
res.setHeader("Repr-Digest", get_digest(buffer))
|
|
@@ -2258,7 +2266,13 @@ function create_braid_text() {
|
|
|
2258
2266
|
if (!key_to_filename.has(key)) {
|
|
2259
2267
|
key_to_filename.set(key, encoded)
|
|
2260
2268
|
ifilenames.add(encoded.toLowerCase())
|
|
2261
|
-
} else
|
|
2269
|
+
} else {
|
|
2270
|
+
// Already have this key mapped - verify it maps to the same encoding
|
|
2271
|
+
if (key_to_filename.get(key) !== encoded) {
|
|
2272
|
+
throw new Error(`filename conflict detected: key "${key}" maps to both "${key_to_filename.get(key)}" and "${encoded}"`)
|
|
2273
|
+
}
|
|
2274
|
+
// Otherwise it's just a re-initialization, skip it
|
|
2275
|
+
}
|
|
2262
2276
|
}
|
|
2263
2277
|
}
|
|
2264
2278
|
|
package/package.json
CHANGED
package/test/tests.js
CHANGED
|
@@ -2146,6 +2146,59 @@ runTest(
|
|
|
2146
2146
|
'ok'
|
|
2147
2147
|
)
|
|
2148
2148
|
|
|
2149
|
+
runTest(
|
|
2150
|
+
"test Merge-Type header per Braid spec",
|
|
2151
|
+
async () => {
|
|
2152
|
+
let key = 'test-merge-type-' + Math.random().toString(36).slice(2)
|
|
2153
|
+
|
|
2154
|
+
// First PUT some content (15 characters, so version is alice-14)
|
|
2155
|
+
await braid_fetch(`/${key}`, {
|
|
2156
|
+
method: 'PUT',
|
|
2157
|
+
version: ['alice-14'],
|
|
2158
|
+
parents: [],
|
|
2159
|
+
body: 'initial content'
|
|
2160
|
+
})
|
|
2161
|
+
|
|
2162
|
+
// Test 1: GET with Version header should include Merge-Type
|
|
2163
|
+
let r1 = await braid_fetch(`/${key}`, {
|
|
2164
|
+
version: ['alice-14']
|
|
2165
|
+
})
|
|
2166
|
+
if (!r1.headers.get('merge-type')) return 'Missing Merge-Type with Version header'
|
|
2167
|
+
|
|
2168
|
+
// Test 2: GET with empty Version header should still include Merge-Type
|
|
2169
|
+
let r2 = await braid_fetch(`/${key}`, {
|
|
2170
|
+
headers: {
|
|
2171
|
+
'Version': ''
|
|
2172
|
+
}
|
|
2173
|
+
})
|
|
2174
|
+
if (!r2.headers.get('merge-type')) return 'Missing Merge-Type with empty Version header'
|
|
2175
|
+
|
|
2176
|
+
// Test 3: GET with Parents header should include Merge-Type
|
|
2177
|
+
let r3 = await braid_fetch(`/${key}`, {
|
|
2178
|
+
parents: []
|
|
2179
|
+
})
|
|
2180
|
+
if (!r3.headers.get('merge-type')) return 'Missing Merge-Type with Parents header'
|
|
2181
|
+
|
|
2182
|
+
// Test 4: Regular GET without Version/Parents should NOT include Merge-Type
|
|
2183
|
+
let r4 = await braid_fetch(`/${key}`)
|
|
2184
|
+
if (r4.headers.get('merge-type')) return 'Unexpected Merge-Type without Version/Parents'
|
|
2185
|
+
|
|
2186
|
+
// Test 5: GET with Subscribe should include Merge-Type
|
|
2187
|
+
let a = new AbortController()
|
|
2188
|
+
let r5 = await braid_fetch(`/${key}`, {
|
|
2189
|
+
signal: a.signal,
|
|
2190
|
+
subscribe: true
|
|
2191
|
+
})
|
|
2192
|
+
if (!r5.headers.get('merge-type')) return 'Missing Merge-Type with Subscribe'
|
|
2193
|
+
|
|
2194
|
+
// Close subscription
|
|
2195
|
+
a.abort()
|
|
2196
|
+
|
|
2197
|
+
return 'ok'
|
|
2198
|
+
},
|
|
2199
|
+
'ok'
|
|
2200
|
+
)
|
|
2201
|
+
|
|
2149
2202
|
runTest(
|
|
2150
2203
|
"test filename conflict detection (different encodings of same key)",
|
|
2151
2204
|
async () => {
|