@bsv/overlay 0.4.3 → 0.4.5

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.
Files changed (26) hide show
  1. package/dist/cjs/package.json +2 -2
  2. package/dist/cjs/src/Engine.js +10 -3
  3. package/dist/cjs/src/Engine.js.map +1 -1
  4. package/dist/cjs/src/storage/knex/all-migrations.js +3 -1
  5. package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
  6. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js +24 -0
  7. package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -0
  8. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  9. package/dist/esm/src/Engine.js +11 -3
  10. package/dist/esm/src/Engine.js.map +1 -1
  11. package/dist/esm/src/storage/knex/all-migrations.js +3 -1
  12. package/dist/esm/src/storage/knex/all-migrations.js.map +1 -1
  13. package/dist/esm/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js +20 -0
  14. package/dist/esm/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -0
  15. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  16. package/dist/types/src/Engine.d.ts +3 -1
  17. package/dist/types/src/Engine.d.ts.map +1 -1
  18. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  19. package/dist/types/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
  20. package/dist/types/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
  21. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  22. package/docs/API.md +619 -356
  23. package/package.json +2 -2
  24. package/src/Engine.ts +32 -24
  25. package/src/storage/knex/all-migrations.ts +3 -1
  26. package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +24 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/overlay",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Overlay Services Engine",
6
6
  "main": "dist/cjs/mod.js",
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "dependencies": {
76
76
  "@bsv/gasp": "^1.2.0",
77
- "@bsv/sdk": "^1.6.16",
77
+ "@bsv/sdk": "^1.6.20",
78
78
  "knex": "^3.1.0"
79
79
  }
80
80
  }
package/src/Engine.ts CHANGED
@@ -47,8 +47,9 @@ export class Engine {
47
47
  * @param {boolean} throwOnBroadcastFailure - Enables / disables throwing an error when a transaction broadcast failure is detected.
48
48
  * @param {OverlayBroadcastFacilitator} overlayBroadcastFacilitator - Facilitator for propagation to other Overlay Services.
49
49
  * @param {typeof console} logger - The place where log entries are written.
50
+ * @param {boolean} suppressDefaultSyncAdvertisements - Whether to suppress the default (SHIP/SLAP) sync advertisements.
50
51
  */
51
- constructor (
52
+ constructor(
52
53
  public managers: { [key: string]: TopicManager },
53
54
  public lookupServices: { [key: string]: LookupService },
54
55
  public storage: Storage,
@@ -63,7 +64,8 @@ export class Engine {
63
64
  public logPrefix = '[OVERLAY_ENGINE] ',
64
65
  public throwOnBroadcastFailure = false,
65
66
  public overlayBroadcastFacilitator: OverlayBroadcastFacilitator = new HTTPSOverlayBroadcastFacilitator(),
66
- public logger: typeof console = console
67
+ public logger: typeof console = console,
68
+ public suppressDefaultSyncAdvertisements = true
67
69
  ) {
68
70
  // To encourage synchronization of overlay services, the SHIP sync strategy is used by default for all overlay topics, except for 'tm_ship' and 'tm_slap'.
69
71
  // For these two topics, any existing trackers are combined with the provided shipTrackers and slapTrackers omitting any duplicates.
@@ -98,13 +100,13 @@ export class Engine {
98
100
  }
99
101
 
100
102
  // Helper functions for logging timings
101
- private startTime (label: string): void {
103
+ private startTime(label: string): void {
102
104
  if (this.logTime) {
103
105
  this.logger.time(`${this.logPrefix} ${label}`)
104
106
  }
105
107
  }
106
108
 
107
- private endTime (label: string): void {
109
+ private endTime(label: string): void {
108
110
  if (this.logTime) {
109
111
  this.logger.timeEnd(`${this.logPrefix} ${label}`)
110
112
  }
@@ -121,7 +123,7 @@ export class Engine {
121
123
  *
122
124
  * @returns {Promise<STEAK>} The submitted transaction execution acknowledgement
123
125
  */
124
- async submit (taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx', offChainValues?: number[]): Promise<STEAK> {
126
+ async submit(taggedBEEF: TaggedBEEF, onSteakReady?: (steak: STEAK) => void, mode: 'historical-tx' | 'current-tx' = 'current-tx', offChainValues?: number[]): Promise<STEAK> {
125
127
  for (const t of taggedBEEF.topics) {
126
128
  if (this.managers[t] === undefined || this.managers[t] === null) {
127
129
  throw new Error(`This server does not support this topic: ${t}`)
@@ -455,7 +457,7 @@ export class Engine {
455
457
  * @param LookupQuestion — The question to ask the Overlay Services Engine
456
458
  * @returns The answer to the question
457
459
  */
458
- async lookup (lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
460
+ async lookup(lookupQuestion: LookupQuestion): Promise<LookupAnswer> {
459
461
  // Validate a lookup service for the provider is found
460
462
  const lookupService = this.lookupServices[lookupQuestion.service]
461
463
  if (lookupService === undefined || lookupService === null) throw new Error(`Lookup service not found for provider: ${lookupQuestion.service} `)
@@ -508,7 +510,7 @@ export class Engine {
508
510
  * @throws Will throw an error if there are issues during the advertisement synchronization process.
509
511
  * @returns {Promise<void>} A promise that resolves when the synchronization process is complete.
510
512
  */
511
- async syncAdvertisements (): Promise<void> {
513
+ async syncAdvertisements(): Promise<void> {
512
514
  if (
513
515
  this.advertiser === undefined ||
514
516
  typeof this.hostingURL !== 'string' ||
@@ -520,8 +522,14 @@ export class Engine {
520
522
  const advertiser = this.advertiser
521
523
 
522
524
  // Step 1: Retrieve Current Configuration
523
- const configuredTopics = Object.keys(this.managers)
524
- const configuredServices = Object.keys(this.lookupServices)
525
+ let configuredTopics = Object.keys(this.managers)
526
+ let configuredServices = Object.keys(this.lookupServices)
527
+
528
+ // Filter out default SHIP/SLAP topics/services if suppressDefaultSyncAdvertisements is true
529
+ if (this.suppressDefaultSyncAdvertisements === true) {
530
+ configuredTopics = configuredTopics.filter(topic => topic !== 'tm_ship' && topic !== 'tm_slap')
531
+ configuredServices = configuredServices.filter(service => service !== 'ls_ship' && service !== 'ls_slap')
532
+ }
525
533
 
526
534
  // Step 2: Fetch Existing Advertisements
527
535
  const currentSHIPAdvertisements = await advertiser.findAllAdvertisements('SHIP')
@@ -574,7 +582,7 @@ export class Engine {
574
582
  *
575
583
  * @throws Error if the overlay service engine is not configured for topical synchronization.
576
584
  */
577
- async startGASPSync (): Promise<void> {
585
+ async startGASPSync(): Promise<void> {
578
586
  if (this.syncConfiguration === undefined) {
579
587
  throw new Error('Overlay Service Engine not configured for topical synchronization!')
580
588
  }
@@ -636,7 +644,7 @@ export class Engine {
636
644
  try {
637
645
  // Read the last interaction score from storage
638
646
  const lastInteraction = await this.storage.getLastInteraction(endpoint, topic)
639
-
647
+
640
648
  const gasp = new GASP(
641
649
  new OverlayGASPStorage(topic, this),
642
650
  new OverlayGASPRemote(endpoint, topic),
@@ -646,7 +654,7 @@ export class Engine {
646
654
  true
647
655
  )
648
656
  await gasp.sync(endpoint, DEFAULT_GASP_SYNC_LIMIT)
649
-
657
+
650
658
  // Save the updated last interaction score
651
659
  if (gasp.lastInteraction > lastInteraction) {
652
660
  await this.storage.updateLastInteraction(endpoint, topic, gasp.lastInteraction)
@@ -676,7 +684,7 @@ export class Engine {
676
684
  * @param topic - The topic for which UTXOs are being requested.
677
685
  * @returns A promise that resolves to a GASPInitialResponse containing the list of UTXOs and the provided min block height.
678
686
  */
679
- async provideForeignSyncResponse (initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
687
+ async provideForeignSyncResponse(initialRequest: GASPInitialRequest, topic: string): Promise<GASPInitialResponse> {
680
688
  const outputs = await this.storage.findUTXOsForTopic(topic, initialRequest.since, initialRequest.limit)
681
689
 
682
690
  return {
@@ -698,7 +706,7 @@ export class Engine {
698
706
  * @returns A promise that resolves to a GASPNode containing the raw transaction and other optional data.
699
707
  * @throws An error if no output is found for the given transaction ID and output index.
700
708
  */
701
- async provideForeignGASPNode (graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
709
+ async provideForeignGASPNode(graphID: string, txid: string, outputIndex: number): Promise<GASPNode> {
702
710
  const hydrator = async (output: Output | null): Promise<GASPNode> => {
703
711
  if (output?.beef === undefined) {
704
712
  throw new Error('No matching output found!')
@@ -776,7 +784,7 @@ export class Engine {
776
784
  *
777
785
  * @returns {Promise<Output | undefined>} - A promise that resolves to the output history if found, or undefined if not.
778
786
  */
779
- async getUTXOHistory (
787
+ async getUTXOHistory(
780
788
  output: Output,
781
789
  historySelector?: ((beef: number[], outputIndex: number, currentDepth: number) => Promise<boolean>) | number,
782
790
  currentDepth = 0
@@ -857,7 +865,7 @@ export class Engine {
857
865
  * @param output - The UTXO to be deleted.
858
866
  * @returns {Promise<void>} - A promise that resolves when the deletion process is complete.
859
867
  */
860
- private async deleteUTXODeep (output: Output): Promise<void> {
868
+ private async deleteUTXODeep(output: Output): Promise<void> {
861
869
  try {
862
870
  // Delete the current output IFF there are no references to it
863
871
  if (output.consumedBy.length === 0) {
@@ -913,7 +921,7 @@ export class Engine {
913
921
  * @param txid BE hex string double hash of transaction proven by proof.
914
922
  * @param proof for txid
915
923
  */
916
- private updateInputProofs (tx: Transaction, txid: string, proof: MerklePath): void {
924
+ private updateInputProofs(tx: Transaction, txid: string, proof: MerklePath): void {
917
925
  if (tx.merklePath !== undefined) {
918
926
  // Update the merkle path to handle potential reorgs
919
927
  tx.merklePath = proof
@@ -938,7 +946,7 @@ export class Engine {
938
946
  * @param txid - The txid for which proof is a valid merkle path.
939
947
  * @param proof - The merklePath proving txid is a mined transaction hash
940
948
  */
941
- private async updateMerkleProof (output: Output, txid: string, proof: MerklePath): Promise<void> {
949
+ private async updateMerkleProof(output: Output, txid: string, proof: MerklePath): Promise<void> {
942
950
  if (output.beef === undefined) {
943
951
  throw new Error('Output must have associated transaction BEEF!')
944
952
  }
@@ -972,7 +980,7 @@ export class Engine {
972
980
  * @param proof - Merkle proof containing the Merkle path and other relevant data to verify the transaction.
973
981
  * @param blockHeight - The block height associated with the incoming merkle proof.
974
982
  */
975
- async handleNewMerkleProof (txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
983
+ async handleNewMerkleProof(txid: string, proof: MerklePath, blockHeight?: number): Promise<void> {
976
984
  const outputs = await this.storage.findOutputsForTransaction(txid, true)
977
985
 
978
986
  if (outputs === undefined || outputs.length === 0) {
@@ -995,7 +1003,7 @@ export class Engine {
995
1003
  * @public
996
1004
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported topic managers and their metadata
997
1005
  */
998
- async listTopicManagers (): Promise<Record<string, {
1006
+ async listTopicManagers(): Promise<Record<string, {
999
1007
  name: string
1000
1008
  shortDescription: string
1001
1009
  iconURL?: string
@@ -1028,7 +1036,7 @@ export class Engine {
1028
1036
  * @public
1029
1037
  * @returns {Promise<Record<string, { name: string; shortDescription: string; iconURL?: string; version?: string; informationURL?: string; }>>} - Supported lookup services and their metadata
1030
1038
  */
1031
- async listLookupServiceProviders (): Promise<Record<string, {
1039
+ async listLookupServiceProviders(): Promise<Record<string, {
1032
1040
  name: string
1033
1041
  shortDescription: string
1034
1042
  iconURL?: string
@@ -1061,7 +1069,7 @@ export class Engine {
1061
1069
  * @public
1062
1070
  * @returns {Promise<string>} - the documentation for the topic manager
1063
1071
  */
1064
- async getDocumentationForTopicManager (manager: any): Promise<string> {
1072
+ async getDocumentationForTopicManager(manager: any): Promise<string> {
1065
1073
  const documentation = await this.managers[manager]?.getDocumentation?.()
1066
1074
  return documentation !== undefined ? documentation : 'No documentation found!'
1067
1075
  }
@@ -1071,7 +1079,7 @@ export class Engine {
1071
1079
  * @public
1072
1080
  * @returns {Promise<string>} - the documentation for the lookup service
1073
1081
  */
1074
- async getDocumentationForLookupServiceProvider (provider: any): Promise<string> {
1082
+ async getDocumentationForLookupServiceProvider(provider: any): Promise<string> {
1075
1083
  const documentation = await this.lookupServices[provider]?.getDocumentation?.()
1076
1084
  return documentation !== undefined ? documentation : 'No documentation found!'
1077
1085
  }
@@ -1086,7 +1094,7 @@ export class Engine {
1086
1094
  * @param url - The URL string to validate
1087
1095
  * @returns {boolean} - Returns `false` if the URL violates any of the conditions `true` otherwise
1088
1096
  */
1089
- private isValidUrl (url: string): boolean {
1097
+ private isValidUrl(url: string): boolean {
1090
1098
  try {
1091
1099
  const parsedUrl = new URL(url)
1092
1100
 
@@ -5,6 +5,7 @@ import { up as addTransactionsTableUp, down as addTransactionsTableDown } from '
5
5
  import { up as addedIndexesUp, down as addedIndexesDown } from './migrations/2024-07-18-001-indexes.js'
6
6
  import { up as enlargeUp, down as enlargeDown } from './migrations/2025-05-28-001-enlarge.js'
7
7
  import { up as gaspPaginationSupportUp, down as gaspPaginationSupportDown } from './migrations/2025-06-25-001-gasp-pagination-support.js'
8
+ import { up as fixScoreColumnTypeUp, down as fixScoreColumnTypeDown } from './migrations/2025-07-22-001-fix-score-column-type.js'
8
9
 
9
10
 
10
11
 
@@ -22,7 +23,8 @@ const allMigrations: Migration[] = [
22
23
  { up: addTransactionsTableUp, down: addTransactionsTableDown },
23
24
  { up: addedIndexesUp, down: addedIndexesDown },
24
25
  { up: enlargeUp, down: enlargeDown },
25
- { up: gaspPaginationSupportUp, down: gaspPaginationSupportDown }
26
+ { up: gaspPaginationSupportUp, down: gaspPaginationSupportDown },
27
+ { up: fixScoreColumnTypeUp, down: fixScoreColumnTypeDown }
26
28
  ]
27
29
 
28
30
  export default allMigrations
@@ -0,0 +1,24 @@
1
+ import type { Knex } from 'knex'
2
+
3
+ export async function up (knex: Knex): Promise<void> {
4
+ // Fix score column type from float to bigint to handle large timestamp values
5
+ const hasScoreColumn = await knex.schema.hasColumn('outputs', 'score')
6
+
7
+ if (hasScoreColumn) {
8
+ // Modify the score column to be bigint instead of float
9
+ await knex.schema.table('outputs', table => {
10
+ table.bigInteger('score').notNullable().defaultTo(0).alter()
11
+ })
12
+ }
13
+ }
14
+
15
+ export async function down (knex: Knex): Promise<void> {
16
+ // Rollback to original float type
17
+ const hasScoreColumn = await knex.schema.hasColumn('outputs', 'score')
18
+
19
+ if (hasScoreColumn) {
20
+ await knex.schema.table('outputs', table => {
21
+ table.float('score', 8, 2).notNullable().defaultTo(0).alter()
22
+ })
23
+ }
24
+ }