@bsv/sdk 2.0.15 → 2.0.16

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.
@@ -249,7 +249,8 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
249
249
  ### Interface: KVStoreQuery
250
250
 
251
251
  Query parameters for KVStore lookups from overlay services.
252
- Used when searching for existing key-value pairs in the network.
252
+ Must include at least one selector: key, controller, protocolID, or non-empty tags.
253
+ Pagination and ordering fields only refine selector-based lookups.
253
254
 
254
255
  ```ts
255
256
  export interface KVStoreQuery {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -106,9 +106,7 @@ export class GlobalKVStore {
106
106
  * @returns {Promise<KVStoreEntry | KVStoreEntry[] | undefined>} Single entry for key+controller queries, array for all other queries
107
107
  */
108
108
  async get (query: KVStoreQuery, options: KVStoreGetOptions = {}): Promise<KVStoreEntry | KVStoreEntry[] | undefined> {
109
- if (Object.keys(query).length === 0) {
110
- throw new Error('Must specify either key, controller, or protocolID')
111
- }
109
+ this.validateQuerySelectors(query)
112
110
  if (query.key != null && query.controller != null) {
113
111
  // Specific key+controller query - return single entry
114
112
  const entries = await this.queryOverlay(query, options)
@@ -117,6 +115,24 @@ export class GlobalKVStore {
117
115
  return await this.queryOverlay(query, options)
118
116
  }
119
117
 
118
+ /**
119
+ * Ensures lookup pagination and ordering options are only used with a real KV selector.
120
+ *
121
+ * @param {KVStoreQuery} query - Query parameters sent to overlay.
122
+ * @throws {Error} If the query does not include a valid selector.
123
+ */
124
+ private validateQuerySelectors (query: KVStoreQuery): void {
125
+ const hasSelector =
126
+ (typeof query.key === 'string' && query.key.length > 0) ||
127
+ (typeof query.controller === 'string' && query.controller.length > 0) ||
128
+ (Array.isArray(query.protocolID) && query.protocolID.length === 2) ||
129
+ (Array.isArray(query.tags) && query.tags.length > 0)
130
+
131
+ if (!hasSelector) {
132
+ throw new Error('Must specify at least one selector: key, controller, protocolID, or tags')
133
+ }
134
+ }
135
+
120
136
  /**
121
137
  * Sets a key-value pair. The current user (wallet identity) becomes the controller.
122
138
  *
@@ -577,7 +577,30 @@ describe('GlobalKVStore', () => {
577
577
 
578
578
  describe('sad paths', () => {
579
579
  it('rejects when no query parameters provided', async () => {
580
- await expect(kvStore.get({})).rejects.toThrow('Must specify either key, controller, or protocolID')
580
+ await expect(kvStore.get({})).rejects.toThrow(
581
+ 'Must specify at least one selector: key, controller, protocolID, or tags'
582
+ )
583
+ })
584
+
585
+ it('rejects pagination-only queries before resolving overlay hosts', async () => {
586
+ await expect(kvStore.get({ limit: 10 })).rejects.toThrow(
587
+ 'Must specify at least one selector: key, controller, protocolID, or tags'
588
+ )
589
+ expect(mockResolver.query).not.toHaveBeenCalled()
590
+ })
591
+
592
+ it('rejects ordering-only queries before resolving overlay hosts', async () => {
593
+ await expect(kvStore.get({ sortOrder: 'desc' })).rejects.toThrow(
594
+ 'Must specify at least one selector: key, controller, protocolID, or tags'
595
+ )
596
+ expect(mockResolver.query).not.toHaveBeenCalled()
597
+ })
598
+
599
+ it('rejects empty tag selector queries before resolving overlay hosts', async () => {
600
+ await expect(kvStore.get({ tags: [] })).rejects.toThrow(
601
+ 'Must specify at least one selector: key, controller, protocolID, or tags'
602
+ )
603
+ expect(mockResolver.query).not.toHaveBeenCalled()
581
604
  })
582
605
 
583
606
  it('propagates overlay errors', async () => {
@@ -37,7 +37,8 @@ export interface KVStoreConfig {
37
37
 
38
38
  /**
39
39
  * Query parameters for KVStore lookups from overlay services.
40
- * Used when searching for existing key-value pairs in the network.
40
+ * Must include at least one selector: key, controller, protocolID, or non-empty tags.
41
+ * Pagination and ordering fields only refine selector-based lookups.
41
42
  */
42
43
  export interface KVStoreQuery {
43
44
  key?: string