+38
-1
src/pds.js
+38
-1
src/pds.js
···
105
105
return result
106
106
}
107
107
108
+
// === TID GENERATION ===
109
+
// Timestamp-based IDs: base32-sort encoded microseconds + clock ID
110
+
111
+
const TID_CHARS = '234567abcdefghijklmnopqrstuvwxyz'
112
+
let lastTimestamp = 0
113
+
let clockId = Math.floor(Math.random() * 1024)
114
+
115
+
function createTid() {
116
+
let timestamp = Date.now() * 1000 // microseconds
117
+
118
+
// Ensure monotonic
119
+
if (timestamp <= lastTimestamp) {
120
+
timestamp = lastTimestamp + 1
121
+
}
122
+
lastTimestamp = timestamp
123
+
124
+
// 13 chars: 11 for timestamp (64 bits but only ~53 used), 2 for clock ID
125
+
let tid = ''
126
+
127
+
// Encode timestamp (high bits first for sortability)
128
+
let ts = timestamp
129
+
for (let i = 0; i < 11; i++) {
130
+
tid = TID_CHARS[ts & 31] + tid
131
+
ts = Math.floor(ts / 32)
132
+
}
133
+
134
+
// Append clock ID (2 chars)
135
+
tid += TID_CHARS[(clockId >> 5) & 31]
136
+
tid += TID_CHARS[clockId & 31]
137
+
138
+
return tid
139
+
}
140
+
108
141
export class PersonalDataServer {
109
142
constructor(state, env) {
110
143
this.state = state
···
124
157
const cid = await createCid(data)
125
158
return Response.json({ cid: cidToString(cid) })
126
159
}
160
+
if (url.pathname === '/test/tid') {
161
+
const tids = [createTid(), createTid(), createTid()]
162
+
return Response.json({ tids })
163
+
}
127
164
return new Response('pds running', { status: 200 })
128
165
}
129
166
}
···
144
181
}
145
182
146
183
// Export utilities for testing
147
-
export { cborEncode, createCid, cidToString, base32Encode }
184
+
export { cborEncode, createCid, cidToString, base32Encode, createTid }
+30
-1
test/pds.test.js
+30
-1
test/pds.test.js
···
1
1
import { test, describe } from 'node:test'
2
2
import assert from 'node:assert'
3
-
import { cborEncode, createCid, cidToString, base32Encode } from '../src/pds.js'
3
+
import { cborEncode, createCid, cidToString, base32Encode, createTid } from '../src/pds.js'
4
4
5
5
describe('CBOR Encoding', () => {
6
6
test('encodes simple map', () => {
···
113
113
assert.notStrictEqual(cid1, cid2)
114
114
})
115
115
})
116
+
117
+
describe('TID Generation', () => {
118
+
test('creates 13-character TIDs', () => {
119
+
const tid = createTid()
120
+
assert.strictEqual(tid.length, 13)
121
+
})
122
+
123
+
test('uses valid base32-sort characters', () => {
124
+
const tid = createTid()
125
+
assert.match(tid, /^[234567abcdefghijklmnopqrstuvwxyz]+$/)
126
+
})
127
+
128
+
test('generates monotonically increasing TIDs', () => {
129
+
const tid1 = createTid()
130
+
const tid2 = createTid()
131
+
const tid3 = createTid()
132
+
133
+
assert.ok(tid1 < tid2, `${tid1} should be less than ${tid2}`)
134
+
assert.ok(tid2 < tid3, `${tid2} should be less than ${tid3}`)
135
+
})
136
+
137
+
test('generates unique TIDs', () => {
138
+
const tids = new Set()
139
+
for (let i = 0; i < 100; i++) {
140
+
tids.add(createTid())
141
+
}
142
+
assert.strictEqual(tids.size, 100)
143
+
})
144
+
})