braid-blob 0.0.20 → 0.0.21
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/.claude/settings.local.json +10 -1
- package/AI-README.md +466 -0
- package/index.js +4 -1
- package/package.json +2 -2
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(node test/test.js:*)",
|
|
5
5
|
"Bash(lsof:*)",
|
|
6
|
-
"Bash(xargs kill -9)"
|
|
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:*)"
|
|
7
16
|
],
|
|
8
17
|
"deny": [],
|
|
9
18
|
"ask": []
|
package/AI-README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# AI-README.md
|
|
2
|
+
|
|
3
|
+
Machine-optimized documentation for braid-blob library.
|
|
4
|
+
|
|
5
|
+
## DEVELOPMENT_BEST_PRACTICES
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
REPOSITORY: braid-org/braid-blob
|
|
9
|
+
MAIN_BRANCH: master
|
|
10
|
+
NPM_PACKAGE: braid-blob
|
|
11
|
+
|
|
12
|
+
VERSION_BUMP_WORKFLOW:
|
|
13
|
+
1. Make code changes
|
|
14
|
+
2. Update package.json version (increment patch: 0.0.X -> 0.0.X+1)
|
|
15
|
+
3. Git commit with formatted message (see below)
|
|
16
|
+
4. Git push to origin/master
|
|
17
|
+
5. npm publish
|
|
18
|
+
|
|
19
|
+
COMMIT_MESSAGE_FORMAT:
|
|
20
|
+
Pattern: "{version} - {brief description of changes}"
|
|
21
|
+
Style: concise, lowercase, describes what was added/fixed/changed
|
|
22
|
+
Examples:
|
|
23
|
+
- "0.0.20 - adds test.js test runner"
|
|
24
|
+
- "0.0.19 - adds URL support for get/put operations and sync function for bidirectional synchronization"
|
|
25
|
+
- "0.0.18 - fixes meta filename case collision issue on case-insensitive filesystems, updates to url-file-db 0.0.8"
|
|
26
|
+
- "0.0.17 - refactors to use url-file-db, separates meta and blob storage, adds new test infrastructure"
|
|
27
|
+
Footer:
|
|
28
|
+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
|
29
|
+
|
|
30
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
31
|
+
|
|
32
|
+
GIT_WORKFLOW:
|
|
33
|
+
- Always stage all changes: git add -A
|
|
34
|
+
- Commit message uses heredoc for proper formatting
|
|
35
|
+
- Push immediately after commit
|
|
36
|
+
- No feature branches (direct to master)
|
|
37
|
+
- Force-add ignored files if explicitly requested: git add -f
|
|
38
|
+
|
|
39
|
+
NPM_PUBLISH_WORKFLOW:
|
|
40
|
+
- Run after git push completes
|
|
41
|
+
- Command: npm publish
|
|
42
|
+
- No additional flags needed
|
|
43
|
+
- Warnings about repository field normalization are expected and safe
|
|
44
|
+
|
|
45
|
+
TESTING_BEFORE_PUBLISH:
|
|
46
|
+
- Run: npm test (node test/test.js)
|
|
47
|
+
- Or: npm run test:browser (browser-based tests)
|
|
48
|
+
- Tests use local server on port 8889
|
|
49
|
+
- All tests must pass before publishing
|
|
50
|
+
|
|
51
|
+
DEPENDENCY_UPDATES:
|
|
52
|
+
- Update package.json dependency version
|
|
53
|
+
- Mention dependency update in commit message
|
|
54
|
+
- Format: "updates to {package}@{version}"
|
|
55
|
+
- Example: "0.0.18 - ... updates to url-file-db 0.0.8"
|
|
56
|
+
|
|
57
|
+
RESOLVED_ISSUES:
|
|
58
|
+
- url-file-db < 0.0.13: Bug where reading non-existent files returned "index" file contents
|
|
59
|
+
- Fixed in url-file-db 0.0.13+ (properly returns null for non-existent files)
|
|
60
|
+
- Caused "test sync local to remote" to fail with unexpected "shared content"
|
|
61
|
+
- url-file-db 0.0.15+ also relaxed path requirements (no longer requires leading "/")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## MODULE_STRUCTURE
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
EXPORT: create_braid_blob() -> braid_blob_instance
|
|
68
|
+
MODULE_TYPE: CommonJS
|
|
69
|
+
MAIN_ENTRY: index.js
|
|
70
|
+
DEPENDENCIES: [braid-http, url-file-db, fs, path]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## DATA_MODEL
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
braid_blob_instance = {
|
|
77
|
+
// Configuration
|
|
78
|
+
db_folder: string = './braid-blob-db' // blob storage location
|
|
79
|
+
meta_folder: string = './braid-blob-meta' // metadata storage location
|
|
80
|
+
peer: string | null // peer identifier (auto-generated if null)
|
|
81
|
+
|
|
82
|
+
// Runtime state
|
|
83
|
+
cache: object // internal cache
|
|
84
|
+
key_to_subs: Map<key, Map<peer, subscription>> // subscription tracking
|
|
85
|
+
db: url_file_db_instance // blob storage backend
|
|
86
|
+
meta_db: url_file_db_instance // metadata storage backend
|
|
87
|
+
|
|
88
|
+
// Methods
|
|
89
|
+
init: async () -> void
|
|
90
|
+
put: async (key, body, options) -> version_string
|
|
91
|
+
get: async (key, options) -> result_object | null
|
|
92
|
+
serve: async (req, res, options) -> void
|
|
93
|
+
sync: async (a, b, options) -> void
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## VERSION_SYSTEM
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
VERSION_FORMAT: "{peer_id}-{timestamp}"
|
|
101
|
+
EXAMPLE: "abc123xyz-1699564820000"
|
|
102
|
+
COMPARISON_RULES:
|
|
103
|
+
1. Compare numeric length of timestamp
|
|
104
|
+
2. If equal, lexicographic compare timestamp
|
|
105
|
+
3. If equal, lexicographic compare full version string
|
|
106
|
+
MERGE_TYPE: last-write-wins (lww)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## METADATA_SCHEMA
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"event": "peer_id-timestamp",
|
|
114
|
+
"content_type": "mime/type"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## API_METHODS
|
|
119
|
+
|
|
120
|
+
### put(key, body, options)
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
INPUT:
|
|
124
|
+
key: string | URL
|
|
125
|
+
- string: local key for storage
|
|
126
|
+
- URL: remote endpoint for HTTP PUT
|
|
127
|
+
body: Buffer | string
|
|
128
|
+
options: {
|
|
129
|
+
version?: [string] // explicit version (default: auto-generated)
|
|
130
|
+
content_type?: string // MIME type
|
|
131
|
+
peer?: string // peer identifier
|
|
132
|
+
skip_write?: boolean // skip disk write (for external changes)
|
|
133
|
+
signal?: AbortSignal // for URL mode
|
|
134
|
+
headers?: object // for URL mode
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
OUTPUT: version_string
|
|
138
|
+
|
|
139
|
+
SIDE_EFFECTS:
|
|
140
|
+
- Writes blob to db_folder via url-file-db
|
|
141
|
+
- Writes metadata to meta_folder/db
|
|
142
|
+
- Notifies active subscriptions (except originating peer)
|
|
143
|
+
- If key instanceof URL: makes remote HTTP PUT via braid_fetch
|
|
144
|
+
|
|
145
|
+
VERSION_LOGIC:
|
|
146
|
+
- If options.version provided: use options.version[0]
|
|
147
|
+
- Else: generate "{peer}-{max(Date.now(), last_version_seq+1)}"
|
|
148
|
+
- Only write if new version > existing version (lww)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### get(key, options)
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
INPUT:
|
|
155
|
+
key: string | URL
|
|
156
|
+
- string: local key to retrieve
|
|
157
|
+
- URL: remote endpoint for HTTP GET
|
|
158
|
+
options: {
|
|
159
|
+
subscribe?: callback(update) // enable subscription mode
|
|
160
|
+
header_cb?: callback(result) // called before body read
|
|
161
|
+
before_send_cb?: callback(result) // called before subscription starts
|
|
162
|
+
head?: boolean // HEAD mode (no body)
|
|
163
|
+
parents?: [version] // fork-point for subscriptions
|
|
164
|
+
version?: [version] // request specific version
|
|
165
|
+
peer?: string // peer identifier
|
|
166
|
+
signal?: AbortSignal // for URL mode
|
|
167
|
+
dont_retry?: boolean // for URL mode subscriptions
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
OUTPUT:
|
|
171
|
+
- If subscribe: result_object with unsubscribe callback
|
|
172
|
+
- If URL + subscribe: Response object or Promise rejection
|
|
173
|
+
- If URL + !subscribe: ArrayBuffer
|
|
174
|
+
- If local + !subscribe: {body: Buffer, version: [string], content_type: string}
|
|
175
|
+
- If not found: null
|
|
176
|
+
|
|
177
|
+
result_object: {
|
|
178
|
+
version: [string]
|
|
179
|
+
content_type?: string
|
|
180
|
+
body?: Buffer // only if !subscribe
|
|
181
|
+
unsubscribe?: function // only if subscribe
|
|
182
|
+
sent?: boolean // true if immediate update sent
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
SUBSCRIPTION_BEHAVIOR:
|
|
186
|
+
- Immediate update sent if: no parents OR local_version > parents
|
|
187
|
+
- Future updates sent when put() called
|
|
188
|
+
- Subscription callback receives: {body: Buffer, version: [string], content_type: string}
|
|
189
|
+
|
|
190
|
+
ERROR_HANDLING:
|
|
191
|
+
- Throws "unkown version: {version}" if requested version > local version
|
|
192
|
+
- Returns 309 status code via serve() when version unknown
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### serve(req, res, options)
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
INPUT:
|
|
199
|
+
req: http.IncomingMessage (braidified)
|
|
200
|
+
res: http.ServerResponse (braidified)
|
|
201
|
+
options: {
|
|
202
|
+
key?: string // override URL-based key extraction
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
HTTP_METHOD_MAPPING:
|
|
206
|
+
GET:
|
|
207
|
+
- Calls get() with req.subscribe, req.parents, req.version, req.peer
|
|
208
|
+
- Sets headers: Editable, Accept-Subscribe, Merge-Type, Version/Current-Version
|
|
209
|
+
- Returns 404 if not found
|
|
210
|
+
- Returns 406 if Content-Type not in Accept header
|
|
211
|
+
- Returns 309 if requested version > server version
|
|
212
|
+
|
|
213
|
+
HEAD:
|
|
214
|
+
- Same as GET but no body
|
|
215
|
+
|
|
216
|
+
PUT:
|
|
217
|
+
- Calls put() with body, req.version, req.headers['content-type'], req.peer
|
|
218
|
+
- Returns Version header with new version
|
|
219
|
+
|
|
220
|
+
DELETE:
|
|
221
|
+
- Deletes from db and meta_db
|
|
222
|
+
- Returns 204 No Content
|
|
223
|
+
|
|
224
|
+
OPTIONS:
|
|
225
|
+
- Returns empty response
|
|
226
|
+
|
|
227
|
+
CONCURRENCY:
|
|
228
|
+
- All operations on same key serialized via within_fiber()
|
|
229
|
+
- Different keys process in parallel
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### sync(a, b, options)
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
INPUT:
|
|
236
|
+
a: string | URL // local key or remote endpoint
|
|
237
|
+
b: string | URL // local key or remote endpoint
|
|
238
|
+
options: {
|
|
239
|
+
// options.my_unsync set by function, call to stop sync
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
OUTPUT: void (async, runs indefinitely)
|
|
243
|
+
|
|
244
|
+
BEHAVIOR_MATRIX:
|
|
245
|
+
a=local, b=local:
|
|
246
|
+
- Bidirectional subscription
|
|
247
|
+
- Updates to a -> put(b), updates to b -> put(a)
|
|
248
|
+
|
|
249
|
+
a=local, b=URL (or a=URL, b=local - swapped internally):
|
|
250
|
+
- Fork-point detection via HEAD request with local version
|
|
251
|
+
- If server has local version: subscribe with parents=local_version
|
|
252
|
+
- If server lacks local version: subscribe without parents (full sync)
|
|
253
|
+
- Local changes pushed to remote via PUT
|
|
254
|
+
- Remote changes pulled to local via subscription
|
|
255
|
+
- Auto-reconnect on error with 1 second delay
|
|
256
|
+
- Respects options.my_unsync() for clean shutdown
|
|
257
|
+
|
|
258
|
+
a=URL, b=URL:
|
|
259
|
+
- Bidirectional subscription
|
|
260
|
+
- Updates from a -> put(b), updates from b -> put(a)
|
|
261
|
+
|
|
262
|
+
ERROR_RECOVERY:
|
|
263
|
+
- Remote sync: catches errors, disconnects, retries after 1s
|
|
264
|
+
- Checks 'closed' flag before retry
|
|
265
|
+
- AbortController cleanup on disconnect
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## UTILITY_FUNCTIONS
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
compare_events(a, b) -> -1 | 0 | 1
|
|
272
|
+
Compares version strings by timestamp length, then timestamp value, then full string
|
|
273
|
+
|
|
274
|
+
get_event_seq(event) -> string
|
|
275
|
+
Extracts timestamp portion after last '-'
|
|
276
|
+
|
|
277
|
+
ascii_ify(string) -> string
|
|
278
|
+
Escapes non-ASCII characters as \uXXXX for HTTP headers
|
|
279
|
+
|
|
280
|
+
version_to_header(version_array) -> string
|
|
281
|
+
Converts ["v1", "v2"] -> '"v1", "v2"' for HTTP headers
|
|
282
|
+
|
|
283
|
+
within_fiber(id, func) -> Promise
|
|
284
|
+
Serializes async operations per ID to prevent race conditions
|
|
285
|
+
|
|
286
|
+
slurp(req) -> Promise<Buffer>
|
|
287
|
+
Reads entire HTTP request body
|
|
288
|
+
|
|
289
|
+
isAcceptable(contentType, acceptHeader) -> boolean
|
|
290
|
+
Checks if contentType matches Accept header patterns
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## STORAGE_LAYOUT
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
db_folder/
|
|
297
|
+
{url_file_db structure}
|
|
298
|
+
- Blob data stored via url-file-db
|
|
299
|
+
- Key mapping: URL-safe encoding of keys
|
|
300
|
+
|
|
301
|
+
meta_folder/
|
|
302
|
+
peer.txt # Peer ID (auto-generated if missing)
|
|
303
|
+
db/ # url-file-db for metadata
|
|
304
|
+
{encoded_key}.txt # JSON: {event: version, content_type: mime}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## PROTOCOL_DETAILS
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
HTTP_HEADERS:
|
|
311
|
+
Request:
|
|
312
|
+
Subscribe: true # enable subscription mode
|
|
313
|
+
Version: "v1", "v2" # specific version (for GET)
|
|
314
|
+
Parents: "v1", "v2" # fork-point for subscriptions
|
|
315
|
+
Content-Type: mime/type # for PUT
|
|
316
|
+
Accept: mime/type # for GET (406 if mismatch)
|
|
317
|
+
|
|
318
|
+
Response:
|
|
319
|
+
Version: "v1" # for PUT response
|
|
320
|
+
Current-Version: "v1" # for subscribed GET
|
|
321
|
+
Editable: true
|
|
322
|
+
Accept-Subscribe: true
|
|
323
|
+
Merge-Type: lww
|
|
324
|
+
Content-Type: mime/type
|
|
325
|
+
|
|
326
|
+
STATUS_CODES:
|
|
327
|
+
200 OK # successful GET/PUT
|
|
328
|
+
204 No Content # successful DELETE
|
|
329
|
+
309 Custom # "Version Unknown Here" - requested version not found
|
|
330
|
+
404 Not Found # resource doesn't exist
|
|
331
|
+
406 Not Acceptable # Content-Type not in Accept header
|
|
332
|
+
|
|
333
|
+
BRAID_UPDATE_FORMAT:
|
|
334
|
+
Version: "v1"\r\n
|
|
335
|
+
Merge-Type: lww\r\n
|
|
336
|
+
Content-Length: N\r\n
|
|
337
|
+
\r\n
|
|
338
|
+
{body}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## KEY_BEHAVIORS
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
INITIALIZATION:
|
|
345
|
+
- init() called lazily by put/get/serve
|
|
346
|
+
- init() runs once (subsequent calls return same promise)
|
|
347
|
+
- Creates db and meta_db url-file-db instances
|
|
348
|
+
- Loads or generates peer ID
|
|
349
|
+
|
|
350
|
+
SUBSCRIPTION_MANAGEMENT:
|
|
351
|
+
- key_to_subs: Map<string, Map<string, {sendUpdate}>>
|
|
352
|
+
- Outer key: resource key
|
|
353
|
+
- Inner key: peer identifier
|
|
354
|
+
- Prevents echo: put() doesn't notify originating peer
|
|
355
|
+
- Serialized updates: subscribe_chain ensures sequential callback execution
|
|
356
|
+
|
|
357
|
+
FILE_WATCHING:
|
|
358
|
+
- url-file-db monitors db_folder for external changes
|
|
359
|
+
- External changes trigger put() with skip_write: true
|
|
360
|
+
- Subscriptions notified of external changes
|
|
361
|
+
|
|
362
|
+
CONCURRENCY_CONTROL:
|
|
363
|
+
- within_fiber(key, fn) serializes operations per key
|
|
364
|
+
- Uses promise chain stored in within_fiber.chains[key]
|
|
365
|
+
- Prevents concurrent modification of same resource
|
|
366
|
+
- Automatic cleanup when chain completes
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## INTEGRATION_PATTERNS
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
BASIC_SERVER:
|
|
373
|
+
require('http').createServer((req, res) => {
|
|
374
|
+
braid_blob.serve(req, res)
|
|
375
|
+
}).listen(PORT)
|
|
376
|
+
|
|
377
|
+
CUSTOM_KEY_MAPPING:
|
|
378
|
+
braid_blob.serve(req, res, {
|
|
379
|
+
key: extract_key_from_url(req.url)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
REMOTE_REPLICATION:
|
|
383
|
+
await braid_blob.sync('local-key', new URL('http://remote/path'))
|
|
384
|
+
|
|
385
|
+
BIDIRECTIONAL_SYNC:
|
|
386
|
+
await braid_blob.sync(
|
|
387
|
+
new URL('http://server1/key'),
|
|
388
|
+
new URL('http://server2/key')
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
PROGRAMMATIC_ACCESS:
|
|
392
|
+
// Local storage
|
|
393
|
+
await braid_blob.put('key', Buffer.from('data'), {version: ['v1']})
|
|
394
|
+
const result = await braid_blob.get('key')
|
|
395
|
+
|
|
396
|
+
// Remote storage
|
|
397
|
+
await braid_blob.put(new URL('http://remote/key'), Buffer.from('data'))
|
|
398
|
+
const data = await braid_blob.get(new URL('http://remote/key'))
|
|
399
|
+
|
|
400
|
+
SUBSCRIPTION_EXAMPLE:
|
|
401
|
+
await braid_blob.get('key', {
|
|
402
|
+
subscribe: async (update) => {
|
|
403
|
+
console.log('Version:', update.version)
|
|
404
|
+
console.log('Body:', update.body.toString())
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## DEPENDENCIES_DETAIL
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
braid-http:
|
|
413
|
+
- http_server (braidify): Adds Braid protocol support to Node.js HTTP
|
|
414
|
+
- fetch (braid_fetch): Braid-aware fetch implementation
|
|
415
|
+
- Handles: Subscribe headers, Version headers, streaming updates
|
|
416
|
+
|
|
417
|
+
url-file-db (^0.0.15):
|
|
418
|
+
- Bidirectional URL ↔ filesystem mapping
|
|
419
|
+
- Collision-resistant encoding (case-insensitive filesystem safe)
|
|
420
|
+
- File watching for external changes
|
|
421
|
+
- Separate instances for blobs (db) and metadata (meta_db)
|
|
422
|
+
- API change in 0.0.15: use get_canonical_path() instead of url_path_to_canonical_path()
|
|
423
|
+
- Fixed in 0.0.13+: properly returns null for non-existent files (not "index" content)
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## ERROR_CONDITIONS
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
"unkown version: {version}"
|
|
430
|
+
- GET with version/parents newer than local version
|
|
431
|
+
- Results in 309 status via serve()
|
|
432
|
+
|
|
433
|
+
AbortError:
|
|
434
|
+
- sync() handles when AbortController triggered
|
|
435
|
+
- Ignored silently in local→remote sync
|
|
436
|
+
|
|
437
|
+
Connection errors (sync):
|
|
438
|
+
- Caught and trigger reconnect after 1s delay
|
|
439
|
+
- Stops retry if closed flag set
|
|
440
|
+
|
|
441
|
+
File not found:
|
|
442
|
+
- get() returns null for non-existent keys
|
|
443
|
+
- serve() returns 404
|
|
444
|
+
|
|
445
|
+
Content-Type mismatch:
|
|
446
|
+
- serve() returns 406 if Content-Type not in Accept header
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## TESTING_INFRASTRUCTURE
|
|
450
|
+
|
|
451
|
+
```
|
|
452
|
+
TEST_RUNNER: test/test.js
|
|
453
|
+
- Runs in Node.js or browser
|
|
454
|
+
- Uses /eval endpoint for isolated test execution
|
|
455
|
+
- Browser mode: Opens puppeteer, loads test.html
|
|
456
|
+
|
|
457
|
+
TEST_SUITE: test/tests.js
|
|
458
|
+
- 40+ test cases covering:
|
|
459
|
+
- Basic put/get operations
|
|
460
|
+
- Subscriptions and updates
|
|
461
|
+
- Version conflict resolution
|
|
462
|
+
- Remote operations (URL mode)
|
|
463
|
+
- Sync functionality
|
|
464
|
+
- Error handling
|
|
465
|
+
- Edge cases (version unknown, fork-points)
|
|
466
|
+
```
|
package/index.js
CHANGED
|
@@ -223,7 +223,10 @@ function create_braid_blob() {
|
|
|
223
223
|
braid_blob.serve = async (req, res, options = {}) => {
|
|
224
224
|
await braid_blob.init()
|
|
225
225
|
|
|
226
|
-
if (!options.key)
|
|
226
|
+
if (!options.key) {
|
|
227
|
+
var url = new URL(req.url, 'http://localhost')
|
|
228
|
+
options.key = url_file_db.get_canonical_path(url.pathname)
|
|
229
|
+
}
|
|
227
230
|
|
|
228
231
|
braidify(req, res)
|
|
229
232
|
if (res.is_multiplexer) return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braid-blob",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
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": "
|
|
14
|
+
"url-file-db": "^0.0.15"
|
|
15
15
|
}
|
|
16
16
|
}
|