braid-blob 0.0.70 → 0.0.72
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 +23 -23
- package/client.js +51 -60
- package/index.js +62 -66
- package/package.json +7 -2
- package/AI-README.md +0 -447
- package/blob.png +0 -0
- package/client-demo.html +0 -171
- package/img-live-demo.html +0 -35
- package/plop.png +0 -0
- package/relative-wallclock-version-type.md +0 -102
- package/server-demo.js +0 -61
- package/spec-for-merge-types.txt +0 -367
- package/spec-for-version-types.txt +0 -1379
- package/test/test.html +0 -107
- package/test/tests.js +0 -2101
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ node server-demo.js
|
|
|
46
46
|
|
|
47
47
|
Now open up http://localhost:8888 in your browser, to see the client. Open two windows. You can drag and drop images between them, and they will always stay synchronized.
|
|
48
48
|
|
|
49
|
-
<video src="https://github.com/user-attachments/assets/
|
|
49
|
+
<video src="https://github.com/user-attachments/assets/24f7be18-87ec-417d-bab5-beee48c5c442" controls width="600"></video>
|
|
50
50
|
|
|
51
51
|
## Network API
|
|
52
52
|
|
|
@@ -56,8 +56,8 @@ Braid-blob speaks [Braid-HTTP](https://github.com/braid-org/braid-spec), an exte
|
|
|
56
56
|
|
|
57
57
|
| Header | Description |
|
|
58
58
|
|--------|-------------|
|
|
59
|
-
| `Version` | Unique identifier for this version of the blob (e.g., `"
|
|
60
|
-
| `Version-Type` | How to interpret the structure of version strings (e.g., [`
|
|
59
|
+
| `Version` | Unique identifier for this version of the blob (e.g., `"1768467700.000"`) |
|
|
60
|
+
| `Version-Type` | How to interpret the structure of version strings (e.g., [`wallclockish`](https://braid.org/protocol/version-types/wallclockish)); see [Version-Type spec](https://github.com/braid-org/braid-spec/blob/master/draft-toomim-httpbis-versions-03.txt) |
|
|
61
61
|
| `Parents` | The previous version |
|
|
62
62
|
| `Merge-Type` | How conflicts resolve consistently (*e.g.* `aww` for [arbitrary-writer-wins](https://braid.org/protocol/merge-types/aww)) |
|
|
63
63
|
| `Subscribe` | In GET, subscribes client to all future changes |
|
|
@@ -74,8 +74,8 @@ Response:
|
|
|
74
74
|
|
|
75
75
|
```http
|
|
76
76
|
HTTP/1.1 200 OK
|
|
77
|
-
Version: "
|
|
78
|
-
Version-Type:
|
|
77
|
+
Version: "1768467700.000"
|
|
78
|
+
Version-Type: wallclockish
|
|
79
79
|
Content-Type: image/png
|
|
80
80
|
Merge-Type: aww
|
|
81
81
|
Accept-Subscribe: true
|
|
@@ -100,12 +100,12 @@ Response (keeps connection open, streams updates):
|
|
|
100
100
|
```http
|
|
101
101
|
HTTP/1.1 209 Multiresponse
|
|
102
102
|
Subscribe: true
|
|
103
|
-
Current-Version: "
|
|
104
|
-
Version-Type:
|
|
103
|
+
Current-Version: "1768467700.000"
|
|
104
|
+
Version-Type: wallclockish
|
|
105
105
|
|
|
106
106
|
HTTP 200 OK
|
|
107
|
-
Version: "
|
|
108
|
-
Version-Type:
|
|
107
|
+
Version: "1768467700.000"
|
|
108
|
+
Version-Type: wallclockish
|
|
109
109
|
Content-Type: image/png
|
|
110
110
|
Merge-Type: aww
|
|
111
111
|
Content-Length: 12345
|
|
@@ -113,8 +113,8 @@ Content-Length: 12345
|
|
|
113
113
|
<binary data>
|
|
114
114
|
|
|
115
115
|
HTTP 200 OK
|
|
116
|
-
Version: "
|
|
117
|
-
Version-Type:
|
|
116
|
+
Version: "1768467700.100"
|
|
117
|
+
Version-Type: wallclockish
|
|
118
118
|
Content-Type: image/png
|
|
119
119
|
Merge-Type: aww
|
|
120
120
|
Content-Length: 23456
|
|
@@ -129,8 +129,8 @@ If the blob doesn't exist yet, `Current-Version` will be blank and no initial up
|
|
|
129
129
|
|
|
130
130
|
```http
|
|
131
131
|
PUT /blob.png HTTP/1.1
|
|
132
|
-
Version: "
|
|
133
|
-
Version-Type:
|
|
132
|
+
Version: "1768467700.200"
|
|
133
|
+
Version-Type: wallclockish
|
|
134
134
|
Content-Type: image/png
|
|
135
135
|
Merge-Type: aww
|
|
136
136
|
Content-Length: 34567
|
|
@@ -142,8 +142,8 @@ Response:
|
|
|
142
142
|
|
|
143
143
|
```http
|
|
144
144
|
HTTP/1.1 200 OK
|
|
145
|
-
Current-Version: "
|
|
146
|
-
Version-Type:
|
|
145
|
+
Current-Version: "1768467700.200"
|
|
146
|
+
Version-Type: wallclockish
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
If the sent version is older or eclipsed by the server's current version, the returned `Current-Version` will be the server's version (not the one you sent).
|
|
@@ -168,9 +168,9 @@ Returns `200 OK` even if the blob didn't exist.
|
|
|
168
168
|
|
|
169
169
|
Braid-blob uses two complementary mechanisms for distributed consistency:
|
|
170
170
|
|
|
171
|
-
**Version-Type: [`
|
|
172
|
-
- Versions are
|
|
173
|
-
- If the current time is behind the latest known version, a small random
|
|
171
|
+
**Version-Type: [`wallclockish`](https://braid.org/protocol/version-types/wallclockish)** defines the format and interpretation of version identifiers:
|
|
172
|
+
- Versions are seconds since the epoch with decimal precision (e.g., `"1768467700.000"`)
|
|
173
|
+
- If the current time is behind the latest known version, a small random decimal is added to the current version, providing entropy when multiple peers write simultaneously
|
|
174
174
|
- Versions are compared numerically—larger timestamps are newer
|
|
175
175
|
|
|
176
176
|
**Merge-Type: [`aww`](https://braid.org/protocol/merge-types/aww)** (arbitrary-writer-wins) defines how conflicts are resolved:
|
|
@@ -205,7 +205,7 @@ var {body, version, content_type} = await braid_blob.get(new URL('https://foo.ba
|
|
|
205
205
|
// Get a specific version of a remote blob:
|
|
206
206
|
var {body} = await braid_blob.get(
|
|
207
207
|
new URL('https://foo.bar/baz'),
|
|
208
|
-
{version: ['
|
|
208
|
+
{version: ['1768467700.000']}
|
|
209
209
|
)
|
|
210
210
|
|
|
211
211
|
// To subscribe to a remote blob, without storing updates locally:
|
|
@@ -258,8 +258,8 @@ Retrieves a blob from local storage or a remote URL.
|
|
|
258
258
|
Parameters:
|
|
259
259
|
- `key` - The local blob (if string) or remote URL (if [URL object](https://nodejs.org/api/url.html#class-url)) to read from
|
|
260
260
|
- `params` - Optional configuration object
|
|
261
|
-
- `version` - Retrieve a specific version instead of the latest (e.g., `['
|
|
262
|
-
- `parents` - When subscribing, only receive updates newer than this version (e.g., `['
|
|
261
|
+
- `version` - Retrieve a specific version instead of the latest (e.g., `['1768467700.000']`)
|
|
262
|
+
- `parents` - When subscribing, only receive updates newer than this version (e.g., `['1768467700.000']`)
|
|
263
263
|
- `subscribe` - Callback `(update) => {}` called with each update; `update` has `{body, version, content_type}`
|
|
264
264
|
- `head` - If `true`, returns only metadata (`{version, content_type}`) without the body—useful for checking if a blob exists or getting its current version
|
|
265
265
|
- `content_type` - Expected content type (sent as Accept header for remote URLs)
|
|
@@ -275,7 +275,7 @@ Parameters:
|
|
|
275
275
|
- `key` - The local blob (if string) or remote URL (if [URL object](https://nodejs.org/api/url.html#class-url)) to write to
|
|
276
276
|
- `body` - The data to store (Buffer, ArrayBuffer, or Uint8Array)
|
|
277
277
|
- `params` - Optional configuration object
|
|
278
|
-
- `version` - Specify a version ID for this write (e.g., `['
|
|
278
|
+
- `version` - Specify a version ID for this write (e.g., `['1768467700.000']`); if omitted, one is generated automatically
|
|
279
279
|
- `content_type` - MIME type of the blob (e.g., `'image/png'`, `'application/json'`)
|
|
280
280
|
- `signal` - AbortSignal to cancel the request
|
|
281
281
|
|
|
@@ -311,7 +311,7 @@ Parameters:
|
|
|
311
311
|
|
|
312
312
|
## Browser Client API
|
|
313
313
|
|
|
314
|
-
A simple browser client is included for subscribing to blob updates.
|
|
314
|
+
A simple browser client (`client.js`) is included for subscribing to blob updates. Here's how to use it:
|
|
315
315
|
|
|
316
316
|
```html
|
|
317
317
|
<script src="https://unpkg.com/braid-http@~1.3/braid-http-client.js"></script>
|
package/client.js
CHANGED
|
@@ -70,78 +70,69 @@ function braid_blob_client(url, params = {}) {
|
|
|
70
70
|
if (!a) a = ''
|
|
71
71
|
if (!b) b = ''
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
// Check if values match wallclockish format
|
|
74
|
+
var re = compare_events.re || (compare_events.re = /^-?[0-9]*\.[0-9]*$/)
|
|
75
|
+
var a_match = re.test(a)
|
|
76
|
+
var b_match = re.test(b)
|
|
77
|
+
|
|
78
|
+
// If only one matches, it wins
|
|
79
|
+
if (a_match && !b_match) return 1
|
|
80
|
+
if (b_match && !a_match) return -1
|
|
81
|
+
|
|
82
|
+
// If neither matches, compare lexicographically
|
|
83
|
+
if (!a_match && !b_match) {
|
|
84
|
+
if (a < b) return -1
|
|
85
|
+
if (a > b) return 1
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
// Both match - compare as decimals using BigInt
|
|
90
|
+
// Add decimal point if missing
|
|
91
|
+
if (a.indexOf('.') === -1) a += '.'
|
|
92
|
+
if (b.indexOf('.') === -1) b += '.'
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
var
|
|
94
|
+
// Pad the shorter fractional part with zeros
|
|
95
|
+
var diff = (a.length - a.indexOf('.')) - (b.length - b.indexOf('.'))
|
|
96
|
+
if (diff < 0) a += '0'.repeat(-diff)
|
|
97
|
+
else if (diff > 0) b += '0'.repeat(diff)
|
|
83
98
|
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
// Remove decimal and parse as BigInt
|
|
100
|
+
var a_big = BigInt(a.replace('.', ''))
|
|
101
|
+
var b_big = BigInt(b.replace('.', ''))
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return
|
|
103
|
+
if (a_big < b_big) return -1
|
|
104
|
+
if (a_big > b_big) return 1
|
|
105
|
+
return 0
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
function
|
|
93
|
-
|
|
108
|
+
function create_event(current_event, decimal_places=3, entropy_digits=4) {
|
|
109
|
+
var now = '' + Date.now() / 1000
|
|
110
|
+
if (compare_events(now, current_event) > 0)
|
|
111
|
+
return now
|
|
94
112
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
113
|
+
// Add smallest increment to current_event using BigInt
|
|
114
|
+
var e = current_event || '0'
|
|
115
|
+
if (e.indexOf('.') === -1) e += '.'
|
|
99
116
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
// Truncate or pad to exactly decimal_places decimal places
|
|
118
|
+
var dot = e.indexOf('.')
|
|
119
|
+
var frac = e.slice(dot + 1)
|
|
120
|
+
if (frac.length > decimal_places) e = e.slice(0, dot + 1 + decimal_places)
|
|
121
|
+
else if (frac.length < decimal_places) e += '0'.repeat(decimal_places - frac.length)
|
|
103
122
|
|
|
104
|
-
var
|
|
105
|
-
var
|
|
106
|
-
if (a_neg !== b_neg) return a_neg ? -1 : 1
|
|
123
|
+
var big = BigInt(e.replace('.', '')) + 1n
|
|
124
|
+
var str = String(big)
|
|
107
125
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
var swap = a.slice(1); a = b.slice(1); b = swap
|
|
111
|
-
}
|
|
126
|
+
// Reinsert decimal point
|
|
127
|
+
var result = str.slice(0, -decimal_places) + '.' + str.slice(-decimal_places)
|
|
112
128
|
|
|
113
|
-
|
|
114
|
-
if (a < b) return -1
|
|
115
|
-
if (a > b) return 1
|
|
116
|
-
return 0
|
|
129
|
+
return result + random_digits(entropy_digits)
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
var min_of_len = Math.pow(10, len - 1) // e.g., len=3 -> 100
|
|
125
|
-
var max_of_len = Math.pow(10, len) - 1 // e.g., len=3 -> 999
|
|
126
|
-
|
|
127
|
-
if (s < String(min_of_len)) return min_of_len
|
|
128
|
-
if (s > String(max_of_len)) return max_of_len + 1
|
|
129
|
-
|
|
130
|
-
// s is in the base-10 range for this length
|
|
131
|
-
// scan for first non-digit > '9', increment prefix and pad zeros
|
|
132
|
-
var n = 0
|
|
133
|
-
for (var i = 0; i < len; i++) {
|
|
134
|
-
var c = s.charCodeAt(i)
|
|
135
|
-
if (c >= 48 && c <= 57) {
|
|
136
|
-
n = n * 10 + (c - 48)
|
|
137
|
-
} else if (c > 57) {
|
|
138
|
-
// non-digit > '9': increment prefix, pad rest with zeros
|
|
139
|
-
return (n + 1) * Math.pow(10, len - i)
|
|
140
|
-
} else {
|
|
141
|
-
// non-digit < '0': just pad rest with zeros
|
|
142
|
-
return n * Math.pow(10, len - i)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return n
|
|
132
|
+
function random_digits(n) {
|
|
133
|
+
if (!n) return ''
|
|
134
|
+
var s = ''
|
|
135
|
+
for (var i = 0; i < n; i++) s += Math.floor(Math.random() * 10)
|
|
136
|
+
return s
|
|
146
137
|
}
|
|
147
138
|
}
|
package/index.js
CHANGED
|
@@ -152,7 +152,7 @@ function create_braid_blob() {
|
|
|
152
152
|
header_cb: (result) => {
|
|
153
153
|
res.setHeader((req.subscribe ? "Current-" : "") +
|
|
154
154
|
"Version", version_to_header(result.version))
|
|
155
|
-
res.setHeader("Version-Type", "
|
|
155
|
+
res.setHeader("Version-Type", "wallclockish")
|
|
156
156
|
if (result.content_type)
|
|
157
157
|
res.setHeader('Content-Type', result.content_type)
|
|
158
158
|
},
|
|
@@ -167,7 +167,7 @@ function create_braid_blob() {
|
|
|
167
167
|
delete update.content_type
|
|
168
168
|
}
|
|
169
169
|
update['Merge-Type'] = 'aww'
|
|
170
|
-
update['Version-Type'] = '
|
|
170
|
+
update['Version-Type'] = 'wallclockish'
|
|
171
171
|
res.sendUpdate(update)
|
|
172
172
|
} : null
|
|
173
173
|
})
|
|
@@ -206,7 +206,7 @@ function create_braid_blob() {
|
|
|
206
206
|
peer: req.peer
|
|
207
207
|
})
|
|
208
208
|
res.setHeader("Current-Version", version_to_header(event != null ? [event] : []))
|
|
209
|
-
res.setHeader("Version-Type", "
|
|
209
|
+
res.setHeader("Version-Type", "wallclockish")
|
|
210
210
|
res.end('')
|
|
211
211
|
} else if (req.method === 'DELETE') {
|
|
212
212
|
await braid_blob.delete(params.key, {
|
|
@@ -239,7 +239,7 @@ function create_braid_blob() {
|
|
|
239
239
|
'Accept': params.content_type }
|
|
240
240
|
if (params.version || params.parents)
|
|
241
241
|
fetch_params.headers = { ...fetch_params.headers,
|
|
242
|
-
'Version-Type': '
|
|
242
|
+
'Version-Type': 'wallclockish' }
|
|
243
243
|
|
|
244
244
|
var res = await braid_fetch(key.href, fetch_params)
|
|
245
245
|
|
|
@@ -353,7 +353,7 @@ function create_braid_blob() {
|
|
|
353
353
|
'Content-Type': params.content_type }
|
|
354
354
|
if (params.version)
|
|
355
355
|
fetch_params.headers = { ...fetch_params.headers,
|
|
356
|
-
'Version-Type': '
|
|
356
|
+
'Version-Type': 'wallclockish' }
|
|
357
357
|
|
|
358
358
|
return await braid_fetch(key.href, fetch_params)
|
|
359
359
|
}
|
|
@@ -591,79 +591,70 @@ function create_braid_blob() {
|
|
|
591
591
|
if (!a) a = ''
|
|
592
592
|
if (!b) b = ''
|
|
593
593
|
|
|
594
|
-
|
|
595
|
-
|
|
594
|
+
// Check if values match wallclockish format
|
|
595
|
+
var re = compare_events.re || (compare_events.re = /^-?[0-9]*\.[0-9]*$/)
|
|
596
|
+
var a_match = re.test(a)
|
|
597
|
+
var b_match = re.test(b)
|
|
596
598
|
|
|
597
|
-
|
|
598
|
-
if (
|
|
599
|
-
return
|
|
600
|
-
}
|
|
599
|
+
// If only one matches, it wins
|
|
600
|
+
if (a_match && !b_match) return 1
|
|
601
|
+
if (b_match && !a_match) return -1
|
|
601
602
|
|
|
602
|
-
|
|
603
|
-
|
|
603
|
+
// If neither matches, compare lexicographically
|
|
604
|
+
if (!a_match && !b_match) {
|
|
605
|
+
if (a < b) return -1
|
|
606
|
+
if (a > b) return 1
|
|
607
|
+
return 0
|
|
608
|
+
}
|
|
604
609
|
|
|
605
|
-
|
|
606
|
-
|
|
610
|
+
// Both match - compare as decimals using BigInt
|
|
611
|
+
// Add decimal point if missing
|
|
612
|
+
if (a.indexOf('.') === -1) a += '.'
|
|
613
|
+
if (b.indexOf('.') === -1) b += '.'
|
|
607
614
|
|
|
608
|
-
//
|
|
609
|
-
var
|
|
610
|
-
|
|
611
|
-
|
|
615
|
+
// Pad the shorter fractional part with zeros
|
|
616
|
+
var diff = (a.length - a.indexOf('.')) - (b.length - b.indexOf('.'))
|
|
617
|
+
if (diff < 0) a += '0'.repeat(-diff)
|
|
618
|
+
else if (diff > 0) b += '0'.repeat(diff)
|
|
612
619
|
|
|
613
|
-
|
|
614
|
-
|
|
620
|
+
// Remove decimal and parse as BigInt
|
|
621
|
+
var a_big = BigInt(a.replace('.', ''))
|
|
622
|
+
var b_big = BigInt(b.replace('.', ''))
|
|
615
623
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
return
|
|
624
|
+
if (a_big < b_big) return -1
|
|
625
|
+
if (a_big > b_big) return 1
|
|
626
|
+
return 0
|
|
619
627
|
}
|
|
620
628
|
|
|
621
|
-
function
|
|
622
|
-
|
|
623
|
-
if (
|
|
629
|
+
function create_event(current_event, decimal_places=3, entropy_digits=4) {
|
|
630
|
+
var now = '' + Date.now() / 1000
|
|
631
|
+
if (compare_events(now, current_event) > 0)
|
|
632
|
+
return now
|
|
624
633
|
|
|
625
|
-
|
|
626
|
-
var
|
|
627
|
-
if (
|
|
634
|
+
// Add smallest increment to current_event using BigInt
|
|
635
|
+
var e = current_event || '0'
|
|
636
|
+
if (e.indexOf('.') === -1) e += '.'
|
|
628
637
|
|
|
629
|
-
//
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
638
|
+
// Truncate or pad to exactly decimal_places decimal places
|
|
639
|
+
var dot = e.indexOf('.')
|
|
640
|
+
var frac = e.slice(dot + 1)
|
|
641
|
+
if (frac.length > decimal_places) e = e.slice(0, dot + 1 + decimal_places)
|
|
642
|
+
else if (frac.length < decimal_places) e += '0'.repeat(decimal_places - frac.length)
|
|
633
643
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
644
|
+
var big = BigInt(e.replace('.', '')) + 1n
|
|
645
|
+
var str = String(big)
|
|
646
|
+
|
|
647
|
+
// Reinsert decimal point
|
|
648
|
+
var result = str.slice(0, -decimal_places) + '.' + str.slice(-decimal_places)
|
|
649
|
+
|
|
650
|
+
return result + random_digits(entropy_digits)
|
|
638
651
|
}
|
|
639
652
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
var min_of_len = Math.pow(10, len - 1) // e.g., len=3 -> 100
|
|
646
|
-
var max_of_len = Math.pow(10, len) - 1 // e.g., len=3 -> 999
|
|
647
|
-
|
|
648
|
-
if (s < String(min_of_len)) return min_of_len
|
|
649
|
-
if (s > String(max_of_len)) return max_of_len + 1
|
|
650
|
-
|
|
651
|
-
// s is in the base-10 range for this length
|
|
652
|
-
// scan for first non-digit > '9', increment prefix and pad zeros
|
|
653
|
-
var n = 0
|
|
654
|
-
for (var i = 0; i < len; i++) {
|
|
655
|
-
var c = s.charCodeAt(i)
|
|
656
|
-
if (c >= 48 && c <= 57) {
|
|
657
|
-
n = n * 10 + (c - 48)
|
|
658
|
-
} else if (c > 57) {
|
|
659
|
-
// non-digit > '9': increment prefix, pad rest with zeros
|
|
660
|
-
return (n + 1) * Math.pow(10, len - i)
|
|
661
|
-
} else {
|
|
662
|
-
// non-digit < '0': just pad rest with zeros
|
|
663
|
-
return n * Math.pow(10, len - i)
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
return n
|
|
653
|
+
function random_digits(n) {
|
|
654
|
+
if (!n) return ''
|
|
655
|
+
var s = ''
|
|
656
|
+
for (var i = 0; i < n; i++) s += Math.floor(Math.random() * 10)
|
|
657
|
+
return s
|
|
667
658
|
}
|
|
668
659
|
|
|
669
660
|
function ascii_ify(s) {
|
|
@@ -791,7 +782,12 @@ function create_braid_blob() {
|
|
|
791
782
|
params.headers.entries() :
|
|
792
783
|
Object.entries(params.headers))) {
|
|
793
784
|
var s = normalize_params.special[k.toLowerCase()]
|
|
794
|
-
if (s)
|
|
785
|
+
if (s) {
|
|
786
|
+
// Parse JSON-encoded header values for version/parents
|
|
787
|
+
if ((s === 'version' || s === 'parents') && typeof v === 'string')
|
|
788
|
+
try { v = JSON.parse(v) } catch (e) {}
|
|
789
|
+
normalized[s] = v
|
|
790
|
+
}
|
|
795
791
|
else normalized.headers[k] = v
|
|
796
792
|
}
|
|
797
793
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braid-blob",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.72",
|
|
4
4
|
"description": "Library for collaborative blobs over http using braid.",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braid-blob",
|
|
7
7
|
"homepage": "https://braid.org",
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js",
|
|
10
|
+
"client.js",
|
|
11
|
+
"img-live.js"
|
|
12
|
+
],
|
|
8
13
|
"scripts": {
|
|
9
14
|
"test": "node test/test.js",
|
|
10
15
|
"test:browser": "node test/test.js --browser"
|
|
11
16
|
},
|
|
12
17
|
"dependencies": {
|
|
13
|
-
"braid-http": "~1.3.
|
|
18
|
+
"braid-http": "~1.3.89",
|
|
14
19
|
"better-sqlite3": "^11.7.0"
|
|
15
20
|
}
|
|
16
21
|
}
|