@mblaney/holster 2.0.2 → 2.1.0

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/ts/src/buffer.ts CHANGED
@@ -5,7 +5,14 @@
5
5
 
6
6
  import SeaArray from "./array.ts"
7
7
 
8
- type Encoding = "utf8" | "utf-8" | "hex" | "base64" | "binary" | "latin1" | "ascii"
8
+ type Encoding =
9
+ | "utf8"
10
+ | "utf-8"
11
+ | "hex"
12
+ | "base64"
13
+ | "binary"
14
+ | "latin1"
15
+ | "ascii"
9
16
 
10
17
  // Security constants
11
18
  const SECURITY_LIMITS = {
@@ -44,15 +51,18 @@ SafeBuffer.prototype = Object.create(Array.prototype)
44
51
  const validateStringInput = (input: string, _encoding: Encoding): void => {
45
52
  if (input.length > SECURITY_LIMITS.MAX_STRING_LENGTH) {
46
53
  throw new RangeError(
47
- `String too large: ${input.length} > ${SECURITY_LIMITS.MAX_STRING_LENGTH}`
54
+ `String too large: ${input.length} > ${SECURITY_LIMITS.MAX_STRING_LENGTH}`,
48
55
  )
49
56
  }
50
57
  }
51
58
 
52
- const validateBufferSize = (size: number, operation = "buffer operation"): void => {
59
+ const validateBufferSize = (
60
+ size: number,
61
+ operation = "buffer operation",
62
+ ): void => {
53
63
  if (size > SECURITY_LIMITS.MAX_BUFFER_SIZE) {
54
64
  throw new RangeError(
55
- `Buffer size too large for ${operation}: ${size} > ${SECURITY_LIMITS.MAX_BUFFER_SIZE}`
65
+ `Buffer size too large for ${operation}: ${size} > ${SECURITY_LIMITS.MAX_BUFFER_SIZE}`,
56
66
  )
57
67
  }
58
68
  }
@@ -97,7 +107,9 @@ const encodeUtf8String = (str: string): Uint8Array => {
97
107
  validateBufferSize(bytes.length, "UTF-8 encoding")
98
108
  return bytes
99
109
  } catch (err) {
100
- throw new TypeError(`Failed to encode UTF-8 string: ${(err as Error).message}`)
110
+ throw new TypeError(
111
+ `Failed to encode UTF-8 string: ${(err as Error).message}`,
112
+ )
101
113
  }
102
114
  }
103
115
 
@@ -110,7 +122,7 @@ const encodeBinaryString = (str: string): Uint8Array => {
110
122
  const charCode = str.charCodeAt(i)
111
123
  if (charCode > 255) {
112
124
  throw new TypeError(
113
- `Invalid binary string: character at position ${i} exceeds byte range (${charCode} > 255)`
125
+ `Invalid binary string: character at position ${i} exceeds byte range (${charCode} > 255)`,
114
126
  )
115
127
  }
116
128
  bytes[i] = charCode
@@ -137,7 +149,9 @@ const parseBase64String = (base64String: string): Uint8Array => {
137
149
  }
138
150
  return bytes
139
151
  } catch (err) {
140
- throw new TypeError(`Failed to decode base64 string: ${(err as Error).message}`)
152
+ throw new TypeError(
153
+ `Failed to decode base64 string: ${(err as Error).message}`,
154
+ )
141
155
  }
142
156
  }
143
157
 
@@ -164,7 +178,7 @@ const processArrayLikeInput = (input: unknown): Uint8Array => {
164
178
  !Number.isInteger(val)
165
179
  ) {
166
180
  throw new TypeError(
167
- `Invalid byte value at index ${i}: ${val} (must be integer 0-255)`
181
+ `Invalid byte value at index ${i}: ${val} (must be integer 0-255)`,
168
182
  )
169
183
  }
170
184
  }
@@ -175,9 +189,12 @@ const processArrayLikeInput = (input: unknown): Uint8Array => {
175
189
  typeof input === "object" &&
176
190
  input !== null &&
177
191
  "byteLength" in input &&
178
- typeof (input as { byteLength: unknown }).byteLength === "number"
192
+ typeof (input as {byteLength: unknown}).byteLength === "number"
179
193
  ) {
180
- validateBufferSize((input as { byteLength: number }).byteLength, "ArrayBuffer-like processing")
194
+ validateBufferSize(
195
+ (input as {byteLength: number}).byteLength,
196
+ "ArrayBuffer-like processing",
197
+ )
181
198
  return new Uint8Array(input as ArrayBuffer)
182
199
  }
183
200
 
@@ -185,7 +202,7 @@ const processArrayLikeInput = (input: unknown): Uint8Array => {
185
202
  typeof input === "object" &&
186
203
  input !== null &&
187
204
  "length" in input &&
188
- typeof (input as { length: unknown }).length === "number"
205
+ typeof (input as {length: unknown}).length === "number"
189
206
  ) {
190
207
  const arrayLike = input as ArrayLike<unknown>
191
208
  validateBufferSize(arrayLike.length, "array-like processing")
@@ -200,7 +217,7 @@ const processArrayLikeInput = (input: unknown): Uint8Array => {
200
217
  !Number.isInteger(val)
201
218
  ) {
202
219
  throw new TypeError(
203
- `Invalid byte value at index ${i}: ${val} (must be integer 0-255)`
220
+ `Invalid byte value at index ${i}: ${val} (must be integer 0-255)`,
204
221
  )
205
222
  }
206
223
  }
@@ -257,7 +274,7 @@ Object.assign(SafeBuffer, {
257
274
  for (let i = 0; i < input.length; i++) {
258
275
  if (input.charCodeAt(i) > 127) {
259
276
  throw new TypeError(
260
- `Invalid ASCII character at position ${i}: ${input.charCodeAt(i)} > 127`
277
+ `Invalid ASCII character at position ${i}: ${input.charCodeAt(i)} > 127`,
261
278
  )
262
279
  }
263
280
  }
@@ -342,7 +359,7 @@ Object.assign(SafeBuffer, {
342
359
  bytes = processArrayLikeInput(item)
343
360
  } catch (err) {
344
361
  throw new TypeError(
345
- `Invalid array element at index ${i}: ${(err as Error).message}`
362
+ `Invalid array element at index ${i}: ${(err as Error).message}`,
346
363
  )
347
364
  }
348
365
 
@@ -362,7 +379,9 @@ Object.assign(SafeBuffer, {
362
379
 
363
380
  return SeaArray.from(result)
364
381
  } catch (err) {
365
- throw new Error(`Failed to concatenate buffers: ${(err as Error).message}`)
382
+ throw new Error(
383
+ `Failed to concatenate buffers: ${(err as Error).message}`,
384
+ )
366
385
  }
367
386
  },
368
387
 
@@ -371,7 +390,7 @@ Object.assign(SafeBuffer, {
371
390
  obj instanceof SeaArray ||
372
391
  (obj !== null &&
373
392
  typeof obj === "object" &&
374
- (obj as { constructor?: unknown }).constructor === SafeBuffer)
393
+ (obj as {constructor?: unknown}).constructor === SafeBuffer)
375
394
  )
376
395
  },
377
396
 
@@ -411,7 +430,7 @@ SafeBuffer.prototype.toString = function (
411
430
  this: SeaArray,
412
431
  encoding: Encoding = "utf8",
413
432
  start = 0,
414
- end = this.length
433
+ end = this.length,
415
434
  ): string {
416
435
  if (typeof encoding !== "string") {
417
436
  throw new TypeError("Encoding must be a string")
@@ -430,12 +449,13 @@ SafeBuffer.prototype.toString = function (
430
449
  this,
431
450
  encoding as "utf8" | "hex" | "base64",
432
451
  start,
433
- end
452
+ end,
434
453
  )
435
454
  } catch (err) {
436
- throw new Error(`Failed to convert buffer to string: ${(err as Error).message}`)
455
+ throw new Error(
456
+ `Failed to convert buffer to string: ${(err as Error).message}`,
457
+ )
437
458
  }
438
459
  }
439
460
 
440
461
  export default SafeBuffer
441
-
package/ts/src/get.ts CHANGED
@@ -3,9 +3,9 @@
3
3
  * Extracts nodes and properties based on lex queries
4
4
  */
5
5
 
6
- import { match, userPublicKey } from "./utils.ts"
7
- import type { Lex, Graph } from "./schemas.ts"
8
- import type { GraphNode } from "./schemas.ts"
6
+ import {match, userPublicKey} from "./utils.ts"
7
+ import type {Lex, Graph} from "./schemas.ts"
8
+ import type {GraphNode} from "./schemas.ts"
9
9
 
10
10
  /**
11
11
  * Get data from graph based on lex query
@@ -29,7 +29,7 @@ const Get = (lex: Lex, graph: Graph, fast?: boolean): Graph | undefined => {
29
29
  if (!graph[soul]) return undefined
30
30
 
31
31
  const stateVector: Record<string, number> = {}
32
- const node: GraphNode = { _: { "#": soul, ">": stateVector } }
32
+ const node: GraphNode = {_: {"#": soul, ">": stateVector}}
33
33
  const signatures: Record<string, string> = {}
34
34
 
35
35
  if (typeof lex["."] === "string") {
@@ -76,8 +76,7 @@ const Get = (lex: Lex, graph: Graph, fast?: boolean): Graph | undefined => {
76
76
  if (Object.keys(signatures).length > 0) {
77
77
  node._["s"] = signatures
78
78
  }
79
- return { [soul]: node }
79
+ return {[soul]: node}
80
80
  }
81
81
 
82
82
  export default Get
83
-
package/ts/src/ham.ts CHANGED
@@ -31,17 +31,17 @@ const Ham = (
31
31
  currentState: Timestamp,
32
32
  value: GraphValue,
33
33
  currentValue: GraphValue,
34
- signed = false
34
+ signed = false,
35
35
  ): HamResult => {
36
- if (state < currentState) return { historical: true }
36
+ if (state < currentState) return {historical: true}
37
37
 
38
- if (state > currentState) return { incoming: true }
38
+ if (state > currentState) return {incoming: true}
39
39
 
40
40
  // state is equal to currentState. Old data is expected to propagate through
41
41
  // the network, but conflicting values with same signed timestamp are ignored
42
42
  // since the owner can only create one value per timestamp.
43
43
  if (signed && value !== currentValue) {
44
- return { historical: true }
44
+ return {historical: true}
45
45
  }
46
46
 
47
47
  // Lexically compare to resolve conflict (for unsigned or matching values)
@@ -52,13 +52,13 @@ const Ham = (
52
52
  : currentValue
53
53
 
54
54
  // No update required
55
- if (valueStr === currentValueStr) return { state: true }
55
+ if (valueStr === currentValueStr) return {state: true}
56
56
 
57
57
  // Keep the current value
58
- if (valueStr < currentValueStr) return { current: true }
58
+ if (valueStr < currentValueStr) return {current: true}
59
59
 
60
60
  // Otherwise update using the incoming value
61
- return { incoming: true }
61
+ return {incoming: true}
62
62
  }
63
63
 
64
64
  /**
@@ -69,7 +69,8 @@ Ham.mix = async (
69
69
  graph: Graph,
70
70
  hasNode: Set<string> | undefined,
71
71
  secure: boolean,
72
- listen: ListenMap
72
+ listen: ListenMap,
73
+ maxGraphSize = MAX_GRAPH_SIZE,
73
74
  ): Promise<HamMixResult> => {
74
75
  if (!change || typeof change !== "object") {
75
76
  throw new TypeError("change must be an object")
@@ -137,7 +138,9 @@ Ham.mix = async (
137
138
  propsForTimestamp.length > 0 &&
138
139
  propsForTimestamp.every(([prop]) => {
139
140
  const graphState =
140
- graph[soul] && graph[soul]!._[">"] ? graph[soul]!._[">"][prop] : 0
141
+ graph[soul] && graph[soul]!._[">"]
142
+ ? graph[soul]!._[">"][prop]
143
+ : 0
141
144
  return graphState !== undefined && graphState > asNumber
142
145
  })
143
146
 
@@ -145,7 +148,11 @@ Ham.mix = async (
145
148
  continue
146
149
  }
147
150
 
148
- const isValid = await SEA.verifyTimestamp(asNumber, sig as string, pub)
151
+ const isValid = await SEA.verifyTimestamp(
152
+ asNumber,
153
+ sig as string,
154
+ pub,
155
+ )
149
156
  if (isValid) {
150
157
  signedTimestamps.add(asNumber)
151
158
  for (const [prop, ts] of incomingTimestamps) {
@@ -175,7 +182,7 @@ Ham.mix = async (
175
182
  const value = node[key]!
176
183
  const state = (node._ && node._[">"] ? node._[">"][key] : 0) || 0
177
184
  const currentValue = graph[soul] ? graph[soul][key] : undefined
178
- const currentState = graph[soul] ? (graph[soul]._[">"][key] || 0) : 0
185
+ const currentState = graph[soul] ? graph[soul]._[">"][key] || 0 : 0
179
186
 
180
187
  if (alias && key !== utils.rel.is(value)) {
181
188
  console.log(`error alias ${alias}: ${key} !== ${utils.rel.is(value)}`)
@@ -190,7 +197,9 @@ Ham.mix = async (
190
197
  if (!defer[soul]) {
191
198
  const stateVector: Record<string, number> = {}
192
199
  const signatures: Record<string, string> | undefined = {}
193
- defer[soul] = { _: { "#": soul, ">": stateVector, s: signatures } } as GraphNode
200
+ defer[soul] = {
201
+ _: {"#": soul, ">": stateVector, s: signatures},
202
+ } as GraphNode
194
203
  }
195
204
  defer[soul]![key] = value
196
205
  defer[soul]!._[">"][key] = state
@@ -200,28 +209,39 @@ Ham.mix = async (
200
209
  } else {
201
210
  const isSigned =
202
211
  validTimestamps.has(soul) && validTimestamps.get(soul)!.has(state)
203
- const result = Ham(state, currentState, value, currentValue ?? null, isSigned)
212
+ const result = Ham(
213
+ state,
214
+ currentState,
215
+ value,
216
+ currentValue ?? null,
217
+ isSigned,
218
+ )
204
219
  if (result.incoming) {
205
220
  if (!now[soul]) {
206
221
  const stateVector1: Record<string, number> = {}
207
222
  const signatures1: Record<string, string> | undefined = {}
208
- now[soul] = { _: { "#": soul, ">": stateVector1, s: signatures1 } } as GraphNode
223
+ now[soul] = {
224
+ _: {"#": soul, ">": stateVector1, s: signatures1},
225
+ } as GraphNode
209
226
  }
210
227
  if (!graph[soul]) {
211
228
  const stateVector2: Record<string, number> = {}
212
229
  const signatures2: Record<string, string> | undefined = {}
213
- graph[soul] = { _: { "#": soul, ">": stateVector2, s: signatures2 } } as GraphNode
230
+ graph[soul] = {
231
+ _: {"#": soul, ">": stateVector2, s: signatures2},
232
+ } as GraphNode
214
233
  }
215
234
  graph[soul]![key] = now[soul]![key] = value
216
235
  graph[soul]!._[">"][key] = now[soul]!._[">"][key] = state
217
236
  if (node._["s"] && node._["s"][state]) {
218
- graph[soul]!._["s"]![state] = now[soul]!._["s"]![state] = node._["s"][state]
237
+ graph[soul]!._["s"]![state] = now[soul]!._["s"]![state] =
238
+ node._["s"][state]
219
239
  }
220
240
 
221
241
  if (listen[soul]) {
222
- listen[soul]!
223
- .filter(l => utils.match(l["."], key))
224
- .forEach(l => listeners.push(l.cb as () => void))
242
+ listen[soul]!.filter(l => utils.match(l["."], key)).forEach(l =>
243
+ listeners.push(l.cb as () => void),
244
+ )
225
245
  }
226
246
  updated = true
227
247
  }
@@ -229,30 +249,29 @@ Ham.mix = async (
229
249
  }
230
250
 
231
251
  if (updated && listen[soul]) {
232
- listen[soul]!
233
- .filter(l => utils.match(l["."]))
234
- .forEach(l => listeners.push(l.cb as () => void))
252
+ listen[soul]!.filter(l => utils.match(l["."])).forEach(l =>
253
+ listeners.push(l.cb as () => void),
254
+ )
235
255
  }
236
256
  }
237
257
 
238
258
  const souls = Object.keys(graph)
239
- if (souls.length > MAX_GRAPH_SIZE) {
259
+ if (souls.length > maxGraphSize) {
240
260
  const soulsByAge = souls
241
261
  .map(soul => {
242
262
  const states = graph[soul]!._ && graph[soul]!._[">"]
243
263
  const maxState = states ? Math.max(...Object.values(states)) : 0
244
- return { soul, maxState }
264
+ return {soul, maxState}
245
265
  })
246
266
  .sort((a, b) => a.maxState - b.maxState)
247
- const remove = soulsByAge.slice(0, souls.length - MAX_GRAPH_SIZE)
248
- remove.forEach(({ soul }) => {
267
+ const remove = soulsByAge.slice(0, souls.length - maxGraphSize)
268
+ remove.forEach(({soul}) => {
249
269
  delete graph[soul]
250
270
  if (hasNode) hasNode.delete(soul)
251
271
  })
252
272
  }
253
273
 
254
- return { now, defer, wait, listeners }
274
+ return {now, defer, wait, listeners}
255
275
  }
256
276
 
257
277
  export default Ham
258
-