@bsv/overlay-discovery-services 1.4.0 → 1.4.2
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/dist/cjs/package.json +4 -4
- package/dist/cjs/src/LegacyNinjaAdvertiser.js +235 -0
- package/dist/cjs/src/LegacyNinjaAdvertiser.js.map +1 -0
- package/dist/cjs/src/SHIP/SHIPLookupService.js +43 -11
- package/dist/cjs/src/SHIP/SHIPLookupService.js.map +1 -1
- package/dist/cjs/src/SHIP/SHIPStorage.js +33 -10
- package/dist/cjs/src/SHIP/SHIPStorage.js.map +1 -1
- package/dist/cjs/src/SHIP/SHIPTopicManager.js.map +1 -1
- package/dist/cjs/src/SLAP/SLAPLookupService.js +57 -22
- package/dist/cjs/src/SLAP/SLAPLookupService.js.map +1 -1
- package/dist/cjs/src/SLAP/SLAPStorage.js +29 -6
- package/dist/cjs/src/SLAP/SLAPStorage.js.map +1 -1
- package/dist/cjs/src/utils/generateDocs.js +81 -0
- package/dist/cjs/src/utils/generateDocs.js.map +1 -0
- package/dist/cjs/src/utils/getDocumentation.js +22 -0
- package/dist/cjs/src/utils/getDocumentation.js.map +1 -0
- package/dist/cjs/src/utils/isAdvertisableURI.js +21 -21
- package/dist/cjs/src/utils/isAdvertisableURI.js.map +1 -1
- package/dist/cjs/src/utils/isValidDomain.js +15 -0
- package/dist/cjs/src/utils/isValidDomain.js.map +1 -0
- package/dist/cjs/src/utils/isValidServiceName.js +14 -0
- package/dist/cjs/src/utils/isValidServiceName.js.map +1 -0
- package/dist/cjs/src/utils/verifyToken.js +22 -0
- package/dist/cjs/src/utils/verifyToken.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/LegacyNinjaAdvertiser.js +233 -0
- package/dist/esm/src/LegacyNinjaAdvertiser.js.map +1 -0
- package/dist/esm/src/SHIP/SHIPLookupService.js +43 -11
- package/dist/esm/src/SHIP/SHIPLookupService.js.map +1 -1
- package/dist/esm/src/SHIP/SHIPStorage.js +32 -10
- package/dist/esm/src/SHIP/SHIPStorage.js.map +1 -1
- package/dist/esm/src/SHIP/SHIPTopicManager.js.map +1 -1
- package/dist/esm/src/SLAP/SLAPLookupService.js +57 -22
- package/dist/esm/src/SLAP/SLAPLookupService.js.map +1 -1
- package/dist/esm/src/SLAP/SLAPStorage.js +28 -6
- package/dist/esm/src/SLAP/SLAPStorage.js.map +1 -1
- package/dist/esm/src/utils/generateDocs.js +46 -0
- package/dist/esm/src/utils/generateDocs.js.map +1 -0
- package/dist/esm/src/utils/getDocumentation.js +20 -0
- package/dist/esm/src/utils/getDocumentation.js.map +1 -0
- package/dist/esm/src/utils/isAdvertisableURI.js +21 -21
- package/dist/esm/src/utils/isAdvertisableURI.js.map +1 -1
- package/dist/esm/src/utils/isValidDomain.js +11 -0
- package/dist/esm/src/utils/isValidDomain.js.map +1 -0
- package/dist/esm/src/utils/isValidServiceName.js +10 -0
- package/dist/esm/src/utils/isValidServiceName.js.map +1 -0
- package/dist/esm/src/utils/verifyToken.js +18 -0
- package/dist/esm/src/utils/verifyToken.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/LegacyNinjaAdvertiser.d.ts +60 -0
- package/dist/types/src/LegacyNinjaAdvertiser.d.ts.map +1 -0
- package/dist/types/src/SHIP/SHIPLookupService.d.ts.map +1 -1
- package/dist/types/src/SHIP/SHIPStorage.d.ts +8 -5
- package/dist/types/src/SHIP/SHIPStorage.d.ts.map +1 -1
- package/dist/types/src/SHIP/SHIPTopicManager.d.ts.map +1 -1
- package/dist/types/src/SLAP/SLAPLookupService.d.ts.map +1 -1
- package/dist/types/src/SLAP/SLAPStorage.d.ts +4 -1
- package/dist/types/src/SLAP/SLAPStorage.d.ts.map +1 -1
- package/dist/types/src/SLAP/SLAPTopicManager.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +8 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/src/utils/generateDocs.d.ts +2 -0
- package/dist/types/src/utils/generateDocs.d.ts.map +1 -0
- package/dist/types/src/utils/getDocumentation.d.ts +8 -0
- package/dist/types/src/utils/getDocumentation.d.ts.map +1 -0
- package/dist/types/src/utils/isAdvertisableURI.d.ts.map +1 -1
- package/dist/types/src/utils/isValidDomain.d.ts +7 -0
- package/dist/types/src/utils/isValidDomain.d.ts.map +1 -0
- package/dist/types/src/utils/isValidServiceName.d.ts +7 -0
- package/dist/types/src/utils/isValidServiceName.d.ts.map +1 -0
- package/dist/types/src/utils/verifyToken.d.ts +12 -0
- package/dist/types/src/utils/verifyToken.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/SHIP/SHIPLookupService.ts +57 -17
- package/src/SHIP/SHIPStorage.ts +45 -15
- package/src/SHIP/SHIPTopicManager.ts +3 -3
- package/src/SLAP/SLAPLookup.docs.ts +1 -1
- package/src/SLAP/SLAPLookupService.ts +64 -26
- package/src/SLAP/SLAPStorage.ts +41 -11
- package/src/SLAP/SLAPTopicManager.ts +3 -3
- package/src/types.ts +8 -0
- package/src/utils/isAdvertisableURI.ts +50 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/overlay-discovery-services",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "BSV Blockchain Overlay Services Engine",
|
|
6
6
|
"main": "dist/cjs/mod.js",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"typescript": "^5.2.2"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@bsv/overlay": "^0.4.
|
|
66
|
-
"@bsv/sdk": "^1.6.
|
|
67
|
-
"@bsv/wallet-toolbox-client": "^1.5.
|
|
65
|
+
"@bsv/overlay": "^0.4.4",
|
|
66
|
+
"@bsv/sdk": "^1.6.19",
|
|
67
|
+
"@bsv/wallet-toolbox-client": "^1.5.18",
|
|
68
68
|
"mongodb": "^6.11.0"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -13,9 +13,9 @@ import SHIPLookupDocs from './SHIPLookup.docs.js'
|
|
|
13
13
|
export class SHIPLookupService implements LookupService {
|
|
14
14
|
admissionMode: AdmissionMode = 'locking-script'
|
|
15
15
|
spendNotificationMode: SpendNotificationMode = 'none'
|
|
16
|
-
constructor(public storage: SHIPStorage) { }
|
|
16
|
+
constructor (public storage: SHIPStorage) { }
|
|
17
17
|
|
|
18
|
-
async outputAdmittedByTopic(payload: OutputAdmittedByTopic): Promise<void> {
|
|
18
|
+
async outputAdmittedByTopic (payload: OutputAdmittedByTopic): Promise<void> {
|
|
19
19
|
if (payload.mode !== 'locking-script') throw new Error('Invalid payload')
|
|
20
20
|
const { topic, lockingScript, txid, outputIndex } = payload
|
|
21
21
|
if (topic !== 'tm_ship') return
|
|
@@ -28,7 +28,7 @@ export class SHIPLookupService implements LookupService {
|
|
|
28
28
|
await this.storage.storeSHIPRecord(txid, outputIndex, identityKey, domain, topicSupported)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async outputSpent(payload: OutputSpent): Promise<void> {
|
|
31
|
+
async outputSpent (payload: OutputSpent): Promise<void> {
|
|
32
32
|
if (payload.mode !== 'none') throw new Error('Invalid payload')
|
|
33
33
|
const { topic, txid, outputIndex } = payload
|
|
34
34
|
if (topic !== 'tm_ship') return
|
|
@@ -39,7 +39,7 @@ export class SHIPLookupService implements LookupService {
|
|
|
39
39
|
await this.storage.deleteSHIPRecord(txid, outputIndex)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
async lookup(question: LookupQuestion): Promise<LookupFormula> {
|
|
42
|
+
async lookup (question: LookupQuestion): Promise<LookupFormula> {
|
|
43
43
|
if (question.query === undefined || question.query === null) {
|
|
44
44
|
throw new Error('A valid query must be provided!')
|
|
45
45
|
}
|
|
@@ -47,29 +47,69 @@ export class SHIPLookupService implements LookupService {
|
|
|
47
47
|
throw new Error('Lookup service not supported!')
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// Handle legacy "findAll" string query
|
|
50
51
|
if (question.query === 'findAll') {
|
|
51
52
|
return await this.storage.findAll()
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
// Handle object-based query
|
|
56
|
+
if (typeof question.query === 'object') {
|
|
57
|
+
const query = question.query as SHIPQuery
|
|
58
|
+
|
|
59
|
+
// Handle new findAll mode with pagination
|
|
60
|
+
if (query.findAll) {
|
|
61
|
+
const { limit, skip, sortOrder } = query
|
|
62
|
+
|
|
63
|
+
// Validate pagination parameters
|
|
64
|
+
if (typeof limit !== 'undefined' && (typeof limit !== 'number' || limit < 0)) {
|
|
65
|
+
throw new Error('query.limit must be a positive number if provided')
|
|
66
|
+
}
|
|
67
|
+
if (typeof skip !== 'undefined' && (typeof skip !== 'number' || skip < 0)) {
|
|
68
|
+
throw new Error('query.skip must be a non-negative number if provided')
|
|
69
|
+
}
|
|
70
|
+
if (typeof sortOrder !== 'undefined' && sortOrder !== 'asc' && sortOrder !== 'desc') {
|
|
71
|
+
throw new Error('query.sortOrder must be "asc" or "desc" if provided')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return await this.storage.findAll(limit, skip, sortOrder)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle specific query with domain, topics, identityKey
|
|
78
|
+
const { domain, topics, identityKey, limit, skip, sortOrder } = query
|
|
79
|
+
|
|
80
|
+
// Validate query parameters
|
|
81
|
+
if (typeof domain !== 'string' && typeof domain !== 'undefined') {
|
|
82
|
+
throw new Error('query.domain must be a string if provided')
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(topics) && typeof topics !== 'undefined') {
|
|
85
|
+
throw new Error('query.topics must be an array of strings if provided')
|
|
86
|
+
}
|
|
87
|
+
if (typeof identityKey !== 'string' && typeof identityKey !== 'undefined') {
|
|
88
|
+
throw new Error('query.identityKey must be a string if provided')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Validate pagination parameters
|
|
92
|
+
if (typeof limit !== 'undefined' && (typeof limit !== 'number' || limit < 0)) {
|
|
93
|
+
throw new Error('query.limit must be a positive number if provided')
|
|
94
|
+
}
|
|
95
|
+
if (typeof skip !== 'undefined' && (typeof skip !== 'number' || skip < 0)) {
|
|
96
|
+
throw new Error('query.skip must be a non-negative number if provided')
|
|
97
|
+
}
|
|
98
|
+
if (typeof sortOrder !== 'undefined' && sortOrder !== 'asc' && sortOrder !== 'desc') {
|
|
99
|
+
throw new Error('query.sortOrder must be "asc" or "desc" if provided')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return await this.storage.findRecord({ domain, topics, identityKey, limit, skip, sortOrder })
|
|
64
103
|
}
|
|
65
|
-
|
|
104
|
+
|
|
105
|
+
throw new Error('Invalid query format. Query must be "findAll" string or an object with valid parameters.')
|
|
66
106
|
}
|
|
67
107
|
|
|
68
|
-
async getDocumentation(): Promise<string> {
|
|
108
|
+
async getDocumentation (): Promise<string> {
|
|
69
109
|
return SHIPLookupDocs
|
|
70
110
|
}
|
|
71
111
|
|
|
72
|
-
async getMetaData(): Promise<{
|
|
112
|
+
async getMetaData (): Promise<{
|
|
73
113
|
name: string
|
|
74
114
|
shortDescription: string
|
|
75
115
|
iconURL?: string
|
package/src/SHIP/SHIPStorage.ts
CHANGED
|
@@ -11,14 +11,14 @@ export class SHIPStorage {
|
|
|
11
11
|
* Constructs a new SHIPStorage instance
|
|
12
12
|
* @param {Db} db - connected mongo database instance
|
|
13
13
|
*/
|
|
14
|
-
constructor(private readonly db: Db) {
|
|
14
|
+
constructor (private readonly db: Db) {
|
|
15
15
|
this.shipRecords = db.collection<SHIPRecord>('shipRecords')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Ensures the necessary indexes are created for the collections.
|
|
20
20
|
*/
|
|
21
|
-
async ensureIndexes(): Promise<void> {
|
|
21
|
+
async ensureIndexes (): Promise<void> {
|
|
22
22
|
await this.shipRecords.createIndex({ domain: 1, topic: 1 })
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ export class SHIPStorage {
|
|
|
30
30
|
* @param {string} domain domain name
|
|
31
31
|
* @param {string} topic topic name
|
|
32
32
|
*/
|
|
33
|
-
async storeSHIPRecord(txid: string, outputIndex: number, identityKey: string, domain: string, topic: string): Promise<void> {
|
|
33
|
+
async storeSHIPRecord (txid: string, outputIndex: number, identityKey: string, domain: string, topic: string): Promise<void> {
|
|
34
34
|
await this.shipRecords.insertOne({
|
|
35
35
|
txid,
|
|
36
36
|
outputIndex,
|
|
@@ -46,16 +46,16 @@ export class SHIPStorage {
|
|
|
46
46
|
* @param {string} txid transaction id
|
|
47
47
|
* @param {number} outputIndex index of the UTXO
|
|
48
48
|
*/
|
|
49
|
-
async deleteSHIPRecord(txid: string, outputIndex: number): Promise<void> {
|
|
49
|
+
async deleteSHIPRecord (txid: string, outputIndex: number): Promise<void> {
|
|
50
50
|
await this.shipRecords.deleteOne({ txid, outputIndex })
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Finds SHIP records based on a given query object.
|
|
55
|
-
* @param {Object} query The query object which may contain properties for domain, topics, and
|
|
55
|
+
* @param {Object} query The query object which may contain properties for domain, topics, identityKey, limit, and skip.
|
|
56
56
|
* @returns {Promise<UTXOReference[]>} Returns matching UTXO references.
|
|
57
57
|
*/
|
|
58
|
-
async findRecord(query: SHIPQuery): Promise<UTXOReference[]> {
|
|
58
|
+
async findRecord (query: SHIPQuery): Promise<UTXOReference[]> {
|
|
59
59
|
const mongoQuery: any = {}
|
|
60
60
|
|
|
61
61
|
// Add domain to the query if provided
|
|
@@ -73,25 +73,55 @@ export class SHIPStorage {
|
|
|
73
73
|
mongoQuery.identityKey = query.identityKey
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
// Build the query with pagination
|
|
77
|
+
let cursor = this.shipRecords
|
|
77
78
|
.find(mongoQuery)
|
|
78
|
-
.project<UTXOReference>({ txid: 1, outputIndex: 1 })
|
|
79
|
+
.project<UTXOReference>({ txid: 1, outputIndex: 1, createdAt: 1 })
|
|
80
|
+
|
|
81
|
+
cursor.sort({ createdAt: query.sortOrder ?? -1 })
|
|
82
|
+
|
|
83
|
+
// Apply pagination if provided
|
|
84
|
+
if (typeof query.skip === 'number' && query.skip > 0) {
|
|
85
|
+
cursor = cursor.skip(query.skip)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof query.limit === 'number' && query.limit > 0) {
|
|
89
|
+
cursor = cursor.limit(query.limit)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return await cursor
|
|
79
93
|
.toArray()
|
|
80
94
|
.then((results) =>
|
|
81
95
|
results.map((record) => ({
|
|
82
96
|
txid: record.txid,
|
|
83
|
-
outputIndex: record.outputIndex
|
|
97
|
+
outputIndex: record.outputIndex
|
|
84
98
|
}))
|
|
85
99
|
)
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
* Returns all results tracked by the overlay
|
|
104
|
+
* @param {number} limit Optional limit for pagination
|
|
105
|
+
* @param {number} skip Optional skip for pagination
|
|
106
|
+
* @param {string} sortOrder Optional sort order
|
|
107
|
+
* @returns {Promise<UTXOReference[]>} returns matching UTXO references
|
|
108
|
+
*/
|
|
109
|
+
async findAll (limit?: number, skip?: number, sortOrder?: 'asc' | 'desc'): Promise<UTXOReference[]> {
|
|
110
|
+
let cursor = this.shipRecords.find({})
|
|
111
|
+
.project<UTXOReference>({ txid: 1, outputIndex: 1, createdAt: 1 })
|
|
112
|
+
|
|
113
|
+
// Apply pagination if provided
|
|
114
|
+
cursor.sort({ createdAt: sortOrder ?? -1 })
|
|
115
|
+
|
|
116
|
+
if (typeof skip === 'number' && skip > 0) {
|
|
117
|
+
cursor = cursor.skip(skip)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (typeof limit === 'number' && limit > 0) {
|
|
121
|
+
cursor = cursor.limit(limit)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return await cursor
|
|
95
125
|
.toArray()
|
|
96
126
|
.then(results => results.map(shipRecords => ({
|
|
97
127
|
txid: shipRecords.txid,
|
|
@@ -19,7 +19,7 @@ export class SHIPTopicManager implements TopicManager {
|
|
|
19
19
|
* @param previousCoins - The previous coins to consider.
|
|
20
20
|
* @returns A promise that resolves with the admittance instructions.
|
|
21
21
|
*/
|
|
22
|
-
async identifyAdmissibleOutputs(beef: number[], previousCoins: number[]): Promise<AdmittanceInstructions> {
|
|
22
|
+
async identifyAdmissibleOutputs (beef: number[], previousCoins: number[]): Promise<AdmittanceInstructions> {
|
|
23
23
|
const outputsToAdmit: number[] = []
|
|
24
24
|
try {
|
|
25
25
|
const parsedTransaction = Transaction.fromBEEF(beef)
|
|
@@ -75,7 +75,7 @@ export class SHIPTopicManager implements TopicManager {
|
|
|
75
75
|
* Returns documentation specific to the SHIP topic manager.
|
|
76
76
|
* @returns A promise that resolves to the documentation string.
|
|
77
77
|
*/
|
|
78
|
-
async getDocumentation(): Promise<string> {
|
|
78
|
+
async getDocumentation (): Promise<string> {
|
|
79
79
|
return SHIPTopicDocs
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -83,7 +83,7 @@ export class SHIPTopicManager implements TopicManager {
|
|
|
83
83
|
* Returns metadata associated with this topic manager.
|
|
84
84
|
* @returns A promise that resolves to an object containing metadata.
|
|
85
85
|
*/
|
|
86
|
-
async getMetaData(): Promise<{
|
|
86
|
+
async getMetaData (): Promise<{
|
|
87
87
|
name: string
|
|
88
88
|
shortDescription: string
|
|
89
89
|
iconURL?: string
|
|
@@ -91,4 +91,4 @@ You will typically provide a [LookupQuestion](https://www.npmjs.com/package/@bsv
|
|
|
91
91
|
|
|
92
92
|
- **SLAPTopicManager**: Learn how SLAP outputs are detected and admitted.
|
|
93
93
|
- **BRC-101 Overlays**: The higher-level specification for modular overlay availability schemes.
|
|
94
|
-
`
|
|
94
|
+
`
|
|
@@ -14,9 +14,9 @@ import SLAPLookupDocs from './SLAPLookup.docs.js'
|
|
|
14
14
|
export class SLAPLookupService implements LookupService {
|
|
15
15
|
admissionMode: AdmissionMode = 'locking-script'
|
|
16
16
|
spendNotificationMode: SpendNotificationMode = 'none'
|
|
17
|
-
constructor(public storage: SLAPStorage) { }
|
|
17
|
+
constructor (public storage: SLAPStorage) { }
|
|
18
18
|
|
|
19
|
-
async outputAdmittedByTopic(payload: OutputAdmittedByTopic): Promise<void> {
|
|
19
|
+
async outputAdmittedByTopic (payload: OutputAdmittedByTopic): Promise<void> {
|
|
20
20
|
if (payload.mode !== 'locking-script') throw new Error('Invalid mode')
|
|
21
21
|
const { txid, outputIndex, lockingScript, topic } = payload
|
|
22
22
|
if (topic !== 'tm_slap') return
|
|
@@ -29,7 +29,7 @@ export class SLAPLookupService implements LookupService {
|
|
|
29
29
|
await this.storage.storeSLAPRecord(txid, outputIndex, identityKey, domain, service)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async outputSpent(payload: OutputSpent): Promise<void> {
|
|
32
|
+
async outputSpent (payload: OutputSpent): Promise<void> {
|
|
33
33
|
if (payload.mode !== 'none') throw new Error('Invalid payload')
|
|
34
34
|
const { topic, txid, outputIndex } = payload
|
|
35
35
|
if (topic !== 'tm_slap') return
|
|
@@ -40,7 +40,7 @@ export class SLAPLookupService implements LookupService {
|
|
|
40
40
|
await this.storage.deleteSLAPRecord(txid, outputIndex)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
async lookup(question: LookupQuestion): Promise<LookupFormula> {
|
|
43
|
+
async lookup (question: LookupQuestion): Promise<LookupFormula> {
|
|
44
44
|
if (question.query === undefined || question.query === null) {
|
|
45
45
|
throw new Error('A valid query must be provided!')
|
|
46
46
|
}
|
|
@@ -48,40 +48,78 @@ export class SLAPLookupService implements LookupService {
|
|
|
48
48
|
throw new Error('Lookup service not supported!')
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
// Handle legacy "findAll" string query
|
|
51
52
|
if (question.query === 'findAll') {
|
|
52
53
|
return await this.storage.findAll()
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
//
|
|
56
|
-
|
|
56
|
+
// Handle object-based query
|
|
57
|
+
if (typeof question.query === 'object') {
|
|
58
|
+
const query = question.query as SLAPQuery
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
// Handle new findAll mode with pagination
|
|
61
|
+
if (query.findAll) {
|
|
62
|
+
const { limit, skip, sortOrder } = query
|
|
63
|
+
|
|
64
|
+
// Validate pagination parameters
|
|
65
|
+
if (typeof limit !== 'undefined' && (typeof limit !== 'number' || limit < 0)) {
|
|
66
|
+
throw new Error('query.limit must be a positive number if provided')
|
|
67
|
+
}
|
|
68
|
+
if (typeof skip !== 'undefined' && (typeof skip !== 'number' || skip < 0)) {
|
|
69
|
+
throw new Error('query.skip must be a non-negative number if provided')
|
|
70
|
+
}
|
|
71
|
+
if (typeof sortOrder !== 'undefined' && sortOrder !== 'asc' && sortOrder !== 'desc') {
|
|
72
|
+
throw new Error('query.sortOrder must be "asc" or "desc" if provided')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return await this.storage.findAll(limit, skip, sortOrder)
|
|
76
|
+
}
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
// Handle specific query with domain, service, identityKey
|
|
79
|
+
const { domain, service, identityKey, limit, skip, sortOrder } = query
|
|
80
|
+
|
|
81
|
+
// Validate query parameters
|
|
82
|
+
if (typeof domain !== 'undefined' && typeof domain !== 'string') {
|
|
83
|
+
throw new Error('query.domain must be a string if provided')
|
|
84
|
+
}
|
|
85
|
+
if (typeof service !== 'undefined' && typeof service !== 'string') {
|
|
86
|
+
throw new Error('query.service must be a string if provided')
|
|
87
|
+
}
|
|
88
|
+
if (typeof identityKey !== 'undefined' && typeof identityKey !== 'string') {
|
|
89
|
+
throw new Error('query.identityKey must be a string if provided')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Validate pagination parameters
|
|
93
|
+
if (typeof limit !== 'undefined' && (typeof limit !== 'number' || limit < 0)) {
|
|
94
|
+
throw new Error('query.limit must be a positive number if provided')
|
|
95
|
+
}
|
|
96
|
+
if (typeof skip !== 'undefined' && (typeof skip !== 'number' || skip < 0)) {
|
|
97
|
+
throw new Error('query.skip must be a non-negative number if provided')
|
|
98
|
+
}
|
|
99
|
+
if (typeof sortOrder !== 'undefined' && sortOrder !== 'asc' && sortOrder !== 'desc') {
|
|
100
|
+
throw new Error('query.sortOrder must be "asc" or "desc" if provided')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Build the query object dynamically to omit any undefined values
|
|
104
|
+
const queryParams: Partial<SLAPQuery> = {}
|
|
105
|
+
if (domain !== undefined) queryParams.domain = domain
|
|
106
|
+
if (service !== undefined) queryParams.service = service
|
|
107
|
+
if (identityKey !== undefined) queryParams.identityKey = identityKey
|
|
108
|
+
if (limit !== undefined) queryParams.limit = limit
|
|
109
|
+
if (skip !== undefined) queryParams.skip = skip
|
|
110
|
+
if (sortOrder !== undefined) queryParams.sortOrder = sortOrder
|
|
111
|
+
|
|
112
|
+
return await this.storage.findRecord(queryParams)
|
|
113
|
+
}
|
|
74
114
|
|
|
75
|
-
|
|
76
|
-
console.log('LOOKUP RESULT', result)
|
|
77
|
-
return result
|
|
115
|
+
throw new Error('Invalid query format. Query must be "findAll" string or an object with valid parameters.')
|
|
78
116
|
}
|
|
79
117
|
|
|
80
|
-
async getDocumentation(): Promise<string> {
|
|
118
|
+
async getDocumentation (): Promise<string> {
|
|
81
119
|
return SLAPLookupDocs
|
|
82
120
|
}
|
|
83
121
|
|
|
84
|
-
async getMetaData(): Promise<{
|
|
122
|
+
async getMetaData (): Promise<{
|
|
85
123
|
name: string
|
|
86
124
|
shortDescription: string
|
|
87
125
|
iconURL?: string
|
package/src/SLAP/SLAPStorage.ts
CHANGED
|
@@ -11,14 +11,14 @@ export class SLAPStorage {
|
|
|
11
11
|
* Constructs a new SLAPStorage instance
|
|
12
12
|
* @param {Db} db - connected mongo database instance
|
|
13
13
|
*/
|
|
14
|
-
constructor(private readonly db: Db) {
|
|
14
|
+
constructor (private readonly db: Db) {
|
|
15
15
|
this.slapRecords = db.collection<SLAPRecord>('slapRecords')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Ensures the necessary indexes are created for the collections.
|
|
20
20
|
*/
|
|
21
|
-
async ensureIndexes(): Promise<void> {
|
|
21
|
+
async ensureIndexes (): Promise<void> {
|
|
22
22
|
await this.slapRecords.createIndex({ domain: 1, service: 1 })
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ export class SLAPStorage {
|
|
|
30
30
|
* @param {string} domain domain name
|
|
31
31
|
* @param {string} service service name
|
|
32
32
|
*/
|
|
33
|
-
async storeSLAPRecord(txid: string, outputIndex: number, identityKey: string, domain: string, service: string): Promise<void> {
|
|
33
|
+
async storeSLAPRecord (txid: string, outputIndex: number, identityKey: string, domain: string, service: string): Promise<void> {
|
|
34
34
|
await this.slapRecords.insertOne({
|
|
35
35
|
txid,
|
|
36
36
|
outputIndex,
|
|
@@ -46,7 +46,7 @@ export class SLAPStorage {
|
|
|
46
46
|
* @param {string} txid transaction id
|
|
47
47
|
* @param {number} outputIndex index of the UTXO
|
|
48
48
|
*/
|
|
49
|
-
async deleteSLAPRecord(txid: string, outputIndex: number): Promise<void> {
|
|
49
|
+
async deleteSLAPRecord (txid: string, outputIndex: number): Promise<void> {
|
|
50
50
|
await this.slapRecords.deleteOne({ txid, outputIndex })
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -55,7 +55,7 @@ export class SLAPStorage {
|
|
|
55
55
|
* @param {Object} query The query object which may contain properties for domain, service, and/or identityKey.
|
|
56
56
|
* @returns {Promise<UTXOReference[]>} returns matching UTXO references
|
|
57
57
|
*/
|
|
58
|
-
async findRecord(query: SLAPQuery): Promise<UTXOReference[]> {
|
|
58
|
+
async findRecord (query: SLAPQuery): Promise<UTXOReference[]> {
|
|
59
59
|
const mongoQuery: any = {}
|
|
60
60
|
|
|
61
61
|
// Add domain to the query if provided
|
|
@@ -72,10 +72,24 @@ export class SLAPStorage {
|
|
|
72
72
|
if (typeof query.identityKey === 'string') {
|
|
73
73
|
mongoQuery.identityKey = query.identityKey
|
|
74
74
|
}
|
|
75
|
-
console.log(mongoQuery)
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// Build the query with pagination
|
|
77
|
+
let cursor = this.slapRecords
|
|
78
|
+
.find(mongoQuery)
|
|
79
|
+
.project<UTXOReference>({ txid: 1, outputIndex: 1, createdAt: 1 })
|
|
80
|
+
|
|
81
|
+
cursor.sort({ createdAt: query.sortOrder ?? -1 })
|
|
82
|
+
|
|
83
|
+
// Apply pagination if provided
|
|
84
|
+
if (typeof query.skip === 'number' && query.skip > 0) {
|
|
85
|
+
cursor = cursor.skip(query.skip)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof query.limit === 'number' && query.limit > 0) {
|
|
89
|
+
cursor = cursor.limit(query.limit)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return await cursor
|
|
79
93
|
.toArray()
|
|
80
94
|
.then(results => results.map(record => ({
|
|
81
95
|
txid: record.txid,
|
|
@@ -85,11 +99,27 @@ export class SLAPStorage {
|
|
|
85
99
|
|
|
86
100
|
/**
|
|
87
101
|
* Returns all results tracked by the overlay
|
|
102
|
+
* @param {number} limit Optional limit for pagination
|
|
103
|
+
* @param {number} skip Optional skip for pagination
|
|
104
|
+
* @param {string} sortOrder Optional sort order
|
|
88
105
|
* @returns {Promise<UTXOReference[]>} returns matching UTXO references
|
|
89
106
|
*/
|
|
90
|
-
async findAll(): Promise<UTXOReference[]> {
|
|
91
|
-
|
|
92
|
-
.project<UTXOReference>({ txid: 1, outputIndex: 1 })
|
|
107
|
+
async findAll (limit?: number, skip?: number, sortOrder?: 'asc' | 'desc'): Promise<UTXOReference[]> {
|
|
108
|
+
let cursor = this.slapRecords.find({})
|
|
109
|
+
.project<UTXOReference>({ txid: 1, outputIndex: 1, createdAt: 1 })
|
|
110
|
+
|
|
111
|
+
// Apply pagination if provided
|
|
112
|
+
cursor.sort({ createdAt: sortOrder ?? -1 })
|
|
113
|
+
|
|
114
|
+
if (typeof skip === 'number' && skip > 0) {
|
|
115
|
+
cursor = cursor.skip(skip)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (typeof limit === 'number' && limit > 0) {
|
|
119
|
+
cursor = cursor.limit(limit)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return await cursor
|
|
93
123
|
.toArray()
|
|
94
124
|
.then(results => results.map(slapRecords => ({
|
|
95
125
|
txid: slapRecords.txid,
|
|
@@ -19,7 +19,7 @@ export class SLAPTopicManager implements TopicManager {
|
|
|
19
19
|
* @param previousCoins - The previous coins to consider.
|
|
20
20
|
* @returns A promise that resolves with the admittance instructions.
|
|
21
21
|
*/
|
|
22
|
-
async identifyAdmissibleOutputs(
|
|
22
|
+
async identifyAdmissibleOutputs (
|
|
23
23
|
beef: number[],
|
|
24
24
|
previousCoins: number[]
|
|
25
25
|
): Promise<AdmittanceInstructions> {
|
|
@@ -79,7 +79,7 @@ export class SLAPTopicManager implements TopicManager {
|
|
|
79
79
|
* Returns documentation specific to the SLAP topic manager.
|
|
80
80
|
* @returns A promise that resolves to the documentation string.
|
|
81
81
|
*/
|
|
82
|
-
async getDocumentation(): Promise<string> {
|
|
82
|
+
async getDocumentation (): Promise<string> {
|
|
83
83
|
return SLAPTopicDocs
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -87,7 +87,7 @@ export class SLAPTopicManager implements TopicManager {
|
|
|
87
87
|
* Returns metadata associated with this topic manager.
|
|
88
88
|
* @returns A promise that resolves to an object containing metadata.
|
|
89
89
|
*/
|
|
90
|
-
async getMetaData(): Promise<{
|
|
90
|
+
async getMetaData (): Promise<{
|
|
91
91
|
name: string
|
|
92
92
|
shortDescription: string
|
|
93
93
|
iconURL?: string
|
package/src/types.ts
CHANGED
|
@@ -22,13 +22,21 @@ export interface SLAPRecord {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface SHIPQuery {
|
|
25
|
+
findAll?: boolean
|
|
25
26
|
domain?: string
|
|
26
27
|
topics?: string[]
|
|
27
28
|
identityKey?: string
|
|
29
|
+
limit?: number
|
|
30
|
+
skip?: number
|
|
31
|
+
sortOrder?: 'asc' | 'desc'
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
export interface SLAPQuery {
|
|
35
|
+
findAll?: boolean
|
|
31
36
|
domain?: string
|
|
32
37
|
service?: string
|
|
33
38
|
identityKey?: string
|
|
39
|
+
limit?: number
|
|
40
|
+
skip?: number
|
|
41
|
+
sortOrder?: 'asc' | 'desc'
|
|
34
42
|
}
|