@bsv/overlay 2.2.1 → 2.3.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/LICENSE.txt +57 -27
- package/README.md +102 -269
- package/dist/cjs/mod.d.ts +15 -0
- package/dist/cjs/mod.d.ts.map +1 -0
- package/dist/cjs/mod.js +4 -1
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +41 -13
- package/dist/cjs/src/Advertisement.d.ts +9 -0
- package/dist/cjs/src/Advertisement.d.ts.map +1 -0
- package/dist/cjs/src/Advertiser.d.ts +36 -0
- package/dist/cjs/src/Advertiser.d.ts.map +1 -0
- package/dist/cjs/src/BASM.d.ts +110 -0
- package/dist/cjs/src/BASM.d.ts.map +1 -0
- package/dist/cjs/src/BASM.js.map +1 -1
- package/dist/cjs/src/BASMRemote.d.ts +14 -0
- package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
- package/dist/cjs/src/BASMRemote.js.map +1 -1
- package/dist/cjs/src/Engine.d.ts +379 -0
- package/dist/cjs/src/Engine.d.ts.map +1 -0
- package/dist/cjs/src/Engine.js +425 -418
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
- package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.js +32 -30
- package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/cjs/src/LookupFormula.d.ts +29 -0
- package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
- package/dist/cjs/src/LookupService.d.ts +85 -0
- package/dist/cjs/src/LookupService.d.ts.map +1 -0
- package/dist/cjs/src/Output.d.ts +32 -0
- package/dist/cjs/src/Output.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.d.ts +14 -0
- package/dist/cjs/src/SafeLog.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.js +44 -0
- package/dist/cjs/src/SafeLog.js.map +1 -0
- package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
- package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
- package/dist/cjs/src/TopicManager.d.ts +35 -0
- package/dist/cjs/src/TopicManager.d.ts.map +1 -0
- package/dist/cjs/src/storage/Storage.d.ts +237 -0
- package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
- package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
- package/dist/esm/mod.js +2 -1
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/BASMRemote.js.map +1 -1
- package/dist/esm/src/Engine.js +404 -396
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
- package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPStorage.js +21 -18
- package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/esm/src/SafeLog.js +40 -0
- package/dist/esm/src/SafeLog.js.map +1 -0
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/types/mod.d.ts +5 -4
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/BASMRemote.d.ts.map +1 -1
- package/dist/types/src/Engine.d.ts +18 -1
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
- package/dist/types/src/SafeLog.d.ts +14 -0
- package/dist/types/src/SafeLog.d.ts.map +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
- package/package.json +85 -28
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
- package/dist/types/tsconfig.types.tsbuildinfo +0 -1
- package/docs/API.md +0 -944
- package/docs/BRC-136-BASM.md +0 -327
- package/docs/README.md +0 -16
- package/docs/Synchronization.md +0 -212
- package/docs/concepts/00-overview.md +0 -85
- package/docs/concepts/01-best-practices.md +0 -202
- package/docs/concepts/02-query-performance.md +0 -345
- package/docs/concepts/03-database-monitoring.md +0 -211
- package/docs/concepts/04-pagination-example.md +0 -186
- package/docs/concepts/05-recommendations-summary.md +0 -158
- package/docs/concepts/README.md +0 -20
- package/docs/examples/README.md +0 -15
- package/docs/examples/gs-wip.md +0 -111
- package/docs/internal/README.md +0 -11
- package/mod.ts +0 -40
- package/src/Advertisement.ts +0 -8
- package/src/Advertiser.ts +0 -41
- package/src/BASM.ts +0 -208
- package/src/BASMRemote.ts +0 -54
- package/src/Engine.ts +0 -2194
- package/src/GASP/OverlayGASPRemote.ts +0 -108
- package/src/GASP/OverlayGASPStorage.ts +0 -384
- package/src/LookupFormula.ts +0 -31
- package/src/LookupService.ts +0 -136
- package/src/Output.ts +0 -31
- package/src/TopicManager.ts +0 -40
- package/src/__tests/BASM.test.ts +0 -44
- package/src/__tests/BASMChain.test.ts +0 -295
- package/src/__tests/Engine.test.ts +0 -1381
- package/src/__tests/OverlayGASPRemote.test.ts +0 -135
- package/src/__tests/OverlayGASPStorage.test.ts +0 -205
- package/src/storage/Storage.ts +0 -260
- package/src/storage/knex/KnexStorage.ts +0 -651
- package/src/storage/knex/all-migrations.ts +0 -32
- package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
- package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
- package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -64
- package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
- package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
- package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
- package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
- package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
- package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +0 -123
package/LICENSE.txt
CHANGED
|
@@ -1,28 +1,58 @@
|
|
|
1
|
-
Open BSV License
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
that contains the longest persistent chain of blocks
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Open BSV License Version 6 – granted by BSV Association, Alpenstrasse 15, 6300
|
|
2
|
+
Zug, Switzerland (CHE-427.008.338) ("Licensor"), to you as a user (henceforth
|
|
3
|
+
"You", "User" or "Licensee").
|
|
4
|
+
|
|
5
|
+
For the purposes of this license, the definitions below have the following
|
|
6
|
+
meanings:
|
|
7
|
+
|
|
8
|
+
"Bitcoin Protocol" means the protocol implementation, cryptographic rules,
|
|
9
|
+
network protocols, and consensus mechanisms in the Bitcoin White Paper as
|
|
10
|
+
described here https://protocol.bsvblockchain.org.
|
|
11
|
+
|
|
12
|
+
"Bitcoin White Paper" means the paper entitled 'Bitcoin: A Peer-to-Peer
|
|
13
|
+
Electronic Cash System' published by 'Satoshi Nakamoto' in October 2008.
|
|
14
|
+
|
|
15
|
+
"BSV Blockchain" means:
|
|
16
|
+
|
|
17
|
+
(a) the Bitcoin blockchain containing block height #556767 with the hash
|
|
18
|
+
"000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b" and
|
|
19
|
+
that contains the longest honest persistent chain of blocks which has been
|
|
20
|
+
produced in a manner which is consistent with the rules set forth in the
|
|
21
|
+
Network Access Rules; and
|
|
22
|
+
(b) the test blockchains that contain the longest honest persistent chains of
|
|
23
|
+
blocks which has been produced in a manner which is consistent with the
|
|
24
|
+
rules set forth in the Network Access Rules.
|
|
25
|
+
|
|
26
|
+
"Network Access Rules" or "Rules" means the set of rules regulating the
|
|
27
|
+
relationship between BSV Association and the nodes on BSV based on the Bitcoin
|
|
28
|
+
Protocol rules and those set out in the Bitcoin White Paper, and available here
|
|
29
|
+
https://bsvblockchain.org/network-access-rules.
|
|
30
|
+
|
|
31
|
+
"Software" means the software the subject of this license, including any/all
|
|
32
|
+
intellectual property rights therein and associated documentation files.
|
|
33
|
+
|
|
34
|
+
BSV Association grants permission, free of charge and on a non-exclusive basis
|
|
35
|
+
to any person obtaining a copy of the Software to deal in the Software, including
|
|
36
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
37
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
|
38
|
+
Software is furnished to do so, subject to and conditioned upon the following
|
|
39
|
+
conditions:
|
|
40
|
+
|
|
41
|
+
1 - The text "© BSV Association", and this license shall be included in all
|
|
42
|
+
copies or substantial portions of the Software.
|
|
43
|
+
|
|
44
|
+
2 - The Software, and any software that is derived from the Software or parts
|
|
45
|
+
thereof, may only be used exclusively on the BSV Blockchain.
|
|
46
|
+
|
|
47
|
+
For the avoidance of doubt, this license is granted subject to and conditioned
|
|
48
|
+
upon your compliance with these terms only and is limited to uses on the BSV
|
|
49
|
+
Blockchain. Any exercise of rights not compliant with these terms including
|
|
50
|
+
use not for the BSV Blockchain is deemed outside the scope of the license.
|
|
51
|
+
|
|
22
52
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
24
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
25
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
26
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
27
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
28
|
-
THE SOFTWARE.
|
|
53
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES REGARDING ENTITLEMENT,
|
|
54
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
55
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS THEREOF BE LIABLE FOR ANY CLAIM,
|
|
56
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
57
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
58
|
+
DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,308 +1,141 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bsv/overlay
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The core engine and storage contracts for BSV Overlay Services. The engine admits
|
|
4
|
+
transactions through topic managers, maintains UTXO state, serves lookup
|
|
5
|
+
services, and supports SHIP, SLAP, GASP, and BASM synchronization.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Use [`@bsv/overlay-express`](../overlay-express/README.md) when you want the
|
|
8
|
+
standard HTTP server, operational endpoints, edge policy, and health checks.
|
|
9
|
+
Use this package directly when you are embedding the engine in another runtime
|
|
10
|
+
or implementing a custom transport.
|
|
6
11
|
|
|
7
|
-
##
|
|
12
|
+
## Requirements
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
5. [Contribution Guidelines](#contribution-guidelines)
|
|
14
|
-
6. [Support & Contacts](#support--contacts)
|
|
14
|
+
- Node.js 22 or newer
|
|
15
|
+
- `@bsv/sdk` installed as a peer dependency
|
|
16
|
+
- A `Storage` implementation
|
|
17
|
+
- A `ChainTracker`, or the explicit `'scripts only'` validation mode
|
|
15
18
|
|
|
16
|
-
##
|
|
19
|
+
## Install
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- Let each lookup service dynamically respond to different types of queries
|
|
22
|
-
- Let each lookup service have its own, specialized storage engine for its unique needs
|
|
23
|
-
|
|
24
|
-
## Getting Started
|
|
25
|
-
|
|
26
|
-
### Installation
|
|
21
|
+
```bash
|
|
22
|
+
npm install @bsv/overlay @bsv/sdk
|
|
23
|
+
```
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
## Create an engine
|
|
29
26
|
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
```ts
|
|
28
|
+
import { Engine, KnexStorage } from '@bsv/overlay'
|
|
29
|
+
import type { LookupService, TopicManager } from '@bsv/overlay'
|
|
30
|
+
import knex from 'knex'
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
const database = knex({
|
|
33
|
+
client: 'pg',
|
|
34
|
+
connection: process.env.DATABASE_URL
|
|
35
|
+
})
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
const topicManagers: Record<string, TopicManager> = {
|
|
38
|
+
tm_example: exampleTopicManager
|
|
39
|
+
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const { Engine, KnexStorage, HelloTopicManager, HelloLookupService, HelloStorageEngine } = require('@bsv/overlay')
|
|
42
|
-
import { WhatsOnChain, NodejsHttpClient, ARC, ArcConfig, MerklePath } from '@bsv/sdk'
|
|
43
|
-
// Populate a Knexfile with your database credentials
|
|
44
|
-
const knex = require('knex')(require('../knexfile.js'))
|
|
45
|
-
const app = express()
|
|
46
|
-
app.use(bodyparser.json({ limit: '1gb', type: 'application/json' }))
|
|
41
|
+
const lookupServices: Record<string, LookupService> = {
|
|
42
|
+
ls_example: exampleLookupService
|
|
43
|
+
}
|
|
47
44
|
|
|
48
45
|
const engine = new Engine(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
new KnexStorageEngine({
|
|
60
|
-
knex
|
|
61
|
-
}),
|
|
62
|
-
new CombinatorialChainTracker([
|
|
63
|
-
new WhatsOnChain(
|
|
64
|
-
NODE_ENV === 'production' ? 'main' : 'test',
|
|
65
|
-
{
|
|
66
|
-
httpClient: new NodejsHttpClient(https)
|
|
67
|
-
})
|
|
68
|
-
]),
|
|
69
|
-
HOSTING_DOMAIN as string,
|
|
70
|
-
SHIP_TRACKERS,
|
|
71
|
-
SLAP_TRACKERS,
|
|
72
|
-
new ARC('https://arc.taal.com', arcConfig)
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
// This allows the API to be used everywhere when CORS is enforced
|
|
76
|
-
app.use((req, res, next) => {
|
|
77
|
-
res.header('Access-Control-Allow-Origin', '*')
|
|
78
|
-
res.header('Access-Control-Allow-Headers', '*')
|
|
79
|
-
res.header('Access-Control-Allow-Methods', '*')
|
|
80
|
-
res.header('Access-Control-Expose-Headers', '*')
|
|
81
|
-
res.header('Access-Control-Allow-Private-Network', 'true')
|
|
82
|
-
if (req.method === 'OPTIONS') {
|
|
83
|
-
res.sendStatus(200)
|
|
84
|
-
} else {
|
|
85
|
-
next()
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
// Serve a static documentstion site, if you have one.
|
|
90
|
-
app.use(express.static('public'))
|
|
91
|
-
|
|
92
|
-
// List hosted topic managers and lookup services
|
|
93
|
-
app.get(`/listTopicManagers`, async (req, res) => {
|
|
94
|
-
try {
|
|
95
|
-
const result = await engine.listTopicManagers()
|
|
96
|
-
return res.status(200).json(result)
|
|
97
|
-
} catch (error) {
|
|
98
|
-
return res.status(400).json({
|
|
99
|
-
status: 'error',
|
|
100
|
-
code: error.code,
|
|
101
|
-
description: error.message
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
app.get(`/listLookupServiceProviders`, async (req, res) => {
|
|
106
|
-
try {
|
|
107
|
-
const result = await engine.listLookupServiceProviders()
|
|
108
|
-
return res.status(200).json(result)
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return res.status(400).json({
|
|
111
|
-
status: 'error',
|
|
112
|
-
code: error.code,
|
|
113
|
-
description: error.message
|
|
114
|
-
})
|
|
115
|
-
}
|
|
46
|
+
topicManagers,
|
|
47
|
+
lookupServices,
|
|
48
|
+
new KnexStorage(database),
|
|
49
|
+
chainTracker,
|
|
50
|
+
'https://overlay.example'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
await engine.submit({
|
|
54
|
+
beef: transaction.toBEEF(),
|
|
55
|
+
topics: ['tm_example']
|
|
116
56
|
})
|
|
117
57
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const result = await engine.getDocumentationForTopicManger(req.query.manager)
|
|
122
|
-
return res.status(200).json(result)
|
|
123
|
-
} catch (error) {
|
|
124
|
-
return res.status(400).json({
|
|
125
|
-
status: 'error',
|
|
126
|
-
code: error.code,
|
|
127
|
-
description: error.message
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
app.get(`/getDocumentationForLookupServiceProvider`, async (req, res) => {
|
|
132
|
-
try {
|
|
133
|
-
const result = await engine.getDocumentationForLookupServiceProvider(req.query.lookupServices)
|
|
134
|
-
return res.status(200).json(result)
|
|
135
|
-
} catch (error) {
|
|
136
|
-
return res.status(400).json({
|
|
137
|
-
status: 'error',
|
|
138
|
-
code: error.code,
|
|
139
|
-
description: error.message
|
|
140
|
-
})
|
|
141
|
-
}
|
|
58
|
+
const answer = await engine.lookup({
|
|
59
|
+
service: 'ls_example',
|
|
60
|
+
query: { txid }
|
|
142
61
|
})
|
|
62
|
+
```
|
|
143
63
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Parse out the topics and construct the tagged BEEF
|
|
148
|
-
const topicsHeader = req.headers['x-topics'] as string
|
|
149
|
-
const topics = topicsHeader.trim().startsWith('[')
|
|
150
|
-
? JSON.parse(topicsHeader)
|
|
151
|
-
: topicsHeader.split(',').map(topic => topic.trim())
|
|
152
|
-
const taggedBEEF: TaggedBEEF = {
|
|
153
|
-
beef: Array.from(req.body as number[]),
|
|
154
|
-
topics
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Using a callback function, we can just return once our steak is ready
|
|
158
|
-
// instead of having to wait for all the broadcasts to occur.
|
|
159
|
-
await engine.submit(taggedBEEF, (steak: STEAK) => {
|
|
160
|
-
return res.status(200).json(steak)
|
|
161
|
-
})
|
|
162
|
-
} catch (error) {
|
|
163
|
-
return res.status(400).json({
|
|
164
|
-
status: 'error',
|
|
165
|
-
code: error.code,
|
|
166
|
-
description: error.message
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
app.post(`/lookup`, async (req, res) => {
|
|
171
|
-
try {
|
|
172
|
-
const result = await engine.lookup(req.body)
|
|
173
|
-
return res.status(200).json(result)
|
|
174
|
-
} catch (error) {
|
|
175
|
-
return res.status(400).json({
|
|
176
|
-
status: 'error',
|
|
177
|
-
code: error.code,
|
|
178
|
-
description: error.message
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
})
|
|
64
|
+
The constructor also accepts SHIP/SLAP trackers, broadcasters, an advertiser,
|
|
65
|
+
sync configuration, a logger, a topic-anchor header resolver, and BASM/unproven
|
|
66
|
+
state controls. Type declarations document the complete configuration surface.
|
|
182
67
|
|
|
183
|
-
|
|
184
|
-
(async () => {
|
|
185
|
-
try {
|
|
186
|
-
const merklePath = MerklePath.fromHex(req.body.merklePath)
|
|
187
|
-
await engine.handleNewMerkleProof(req.body.txid, merklePath, req.body.blockHeight)
|
|
188
|
-
return res.status(200).json({ status: 'success', message: 'transaction status updated' })
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error(error)
|
|
191
|
-
return res.status(400).json({
|
|
192
|
-
status: 'error',
|
|
193
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
})().catch(() => {
|
|
197
|
-
res.status(500).json({
|
|
198
|
-
status: 'error',
|
|
199
|
-
message: 'Unexpected error'
|
|
200
|
-
})
|
|
201
|
-
})
|
|
202
|
-
})
|
|
68
|
+
## Public API
|
|
203
69
|
|
|
204
|
-
|
|
205
|
-
(async () => {
|
|
206
|
-
try {
|
|
207
|
-
const topic = req.headers['x-bsv-topic'] as string
|
|
208
|
-
const response = await engine.provideForeignSyncResponse(req.body, topic)
|
|
209
|
-
return res.status(200).json(response)
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error(error)
|
|
212
|
-
return res.status(400).json({
|
|
213
|
-
status: 'error',
|
|
214
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
})().catch(() => {
|
|
218
|
-
res.status(500).json({
|
|
219
|
-
status: 'error',
|
|
220
|
-
message: 'Unexpected error'
|
|
221
|
-
})
|
|
222
|
-
})
|
|
223
|
-
})
|
|
70
|
+
The root entry point exports:
|
|
224
71
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const response = await engine.provideForeignGASPNode(graphID, txid, outputIndex)
|
|
231
|
-
return res.status(200).json(response)
|
|
232
|
-
} catch (error) {
|
|
233
|
-
console.error(error)
|
|
234
|
-
return res.status(400).json({
|
|
235
|
-
status: 'error',
|
|
236
|
-
message: error instanceof Error ? error.message : 'An unknown error occurred'
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
})().catch(() => {
|
|
240
|
-
res.status(500).json({
|
|
241
|
-
status: 'error',
|
|
242
|
-
message: 'Unexpected error'
|
|
243
|
-
})
|
|
244
|
-
})
|
|
245
|
-
})
|
|
72
|
+
- `Engine`
|
|
73
|
+
- `KnexStorage` and `KnexStorageMigrations`
|
|
74
|
+
- the topic-manager, lookup-service, storage, advertisement, and sync contracts
|
|
75
|
+
- BASM utilities and types
|
|
76
|
+
- safe structured-log serializers
|
|
246
77
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
res.status(404).json({
|
|
251
|
-
status: 'error',
|
|
252
|
-
code: 'ERR_ROUTE_NOT_FOUND',
|
|
253
|
-
description: 'Route not found.'
|
|
254
|
-
})
|
|
255
|
-
})
|
|
78
|
+
`@bsv/overlay/storage` exports the `Storage` contract. Existing supported deep
|
|
79
|
+
imports remain available through the documented package export map, but new
|
|
80
|
+
applications should prefer the root entry point wherever possible.
|
|
256
81
|
|
|
257
|
-
|
|
258
|
-
app.listen(8080, () => {
|
|
259
|
-
console.log('BSV Overlay Services Engine is listening on port', 8080)
|
|
260
|
-
})
|
|
261
|
-
```
|
|
82
|
+
## Runtime and package formats
|
|
262
83
|
|
|
263
|
-
|
|
84
|
+
The package supports both module systems:
|
|
264
85
|
|
|
265
|
-
|
|
86
|
+
```ts
|
|
87
|
+
import { Engine } from '@bsv/overlay'
|
|
88
|
+
```
|
|
266
89
|
|
|
267
|
-
|
|
90
|
+
```js
|
|
91
|
+
const { Engine } = require('@bsv/overlay')
|
|
92
|
+
```
|
|
268
93
|
|
|
269
|
-
|
|
94
|
+
ES modules load from `dist/esm`; CommonJS loads from `dist/cjs`. Each condition
|
|
95
|
+
has matching declarations. Published artifacts contain compiled output, the
|
|
96
|
+
README, and the license only—tests, compiler caches, workspace source, and lock
|
|
97
|
+
files are excluded.
|
|
270
98
|
|
|
271
|
-
##
|
|
99
|
+
## Security and operations
|
|
272
100
|
|
|
273
|
-
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
- Storage engine abstractions
|
|
277
|
-
- [WIP] Examples, HTTP wrapper and Docs
|
|
278
|
-
- [WIP] Arc Proof Acquisition
|
|
279
|
-
- [WIP] Distributed Overlay Availability Advertisements
|
|
280
|
-
- [WIP] Federated Transaction Synchronization
|
|
101
|
+
The engine is transport-neutral. Authentication, CORS, CSP, body limits,
|
|
102
|
+
timeouts, rate or concurrency controls, and administrative authorization belong
|
|
103
|
+
at the HTTP or application boundary.
|
|
281
104
|
|
|
282
|
-
|
|
105
|
+
Overlay endpoints are commonly public protocol services used by browsers,
|
|
106
|
+
mobile wallets, WUI, and applications on previously unknown origins. A wrapper
|
|
107
|
+
should therefore remain public-by-default unless an operator deliberately
|
|
108
|
+
configures an exact-origin allowlist. CORS is not an authentication mechanism,
|
|
109
|
+
and CSP for a hosted UI should be configured independently.
|
|
283
110
|
|
|
284
|
-
|
|
111
|
+
For production deployments:
|
|
285
112
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
For more details, check the [contribution guidelines](./CONTRIBUTING.md).
|
|
113
|
+
- validate all untrusted request data before invoking the engine;
|
|
114
|
+
- use a durable storage implementation and tested database migrations;
|
|
115
|
+
- configure transaction broadcast and proof providers;
|
|
116
|
+
- protect administrative and callback routes with explicit credentials;
|
|
117
|
+
- avoid logging raw secrets, authorization headers, or unbounded payloads;
|
|
118
|
+
- monitor readiness, proof acquisition, synchronization, and unproven state.
|
|
293
119
|
|
|
294
|
-
|
|
120
|
+
`@bsv/overlay-express` supplies these standard HTTP controls while preserving
|
|
121
|
+
public protocol access by default.
|
|
295
122
|
|
|
296
|
-
##
|
|
123
|
+
## Development
|
|
297
124
|
|
|
298
|
-
|
|
125
|
+
From the repository root:
|
|
299
126
|
|
|
300
|
-
|
|
127
|
+
```bash
|
|
128
|
+
pnpm --filter @bsv/overlay format:check
|
|
129
|
+
pnpm --filter @bsv/overlay lint
|
|
130
|
+
pnpm --filter @bsv/overlay typecheck
|
|
131
|
+
pnpm --filter @bsv/overlay test
|
|
132
|
+
pnpm --filter @bsv/overlay test:coverage
|
|
133
|
+
pnpm --filter @bsv/overlay pack:check
|
|
134
|
+
```
|
|
301
135
|
|
|
302
|
-
|
|
136
|
+
`pack:check` verifies the actual npm tarball with publint, strict type
|
|
137
|
+
resolution, and clean ESM/CommonJS consumer projects.
|
|
303
138
|
|
|
304
139
|
## License
|
|
305
140
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
Thank you for being a part of the BSV Blockchain Overlay Services Project. Let's build the future of BSV Blockchain together!
|
|
141
|
+
Open BSV License. See [LICENSE.txt](./LICENSE.txt).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { Engine } from './src/Engine.js';
|
|
2
|
+
export { serializeErrorForLog, serializeLogValue } from './src/SafeLog.js';
|
|
3
|
+
export type * from './src/LookupService.js';
|
|
4
|
+
export type { TopicManager } from './src/TopicManager.js';
|
|
5
|
+
export type { Storage, AppliedTransaction } from './src/storage/Storage.js';
|
|
6
|
+
export type { Output } from './src/Output.js';
|
|
7
|
+
export type { TaggedBEEF, STEAK, LookupQuestion, LookupAnswer, AdmittanceInstructions } from '@bsv/sdk';
|
|
8
|
+
export type { LookupFormula } from './src/LookupFormula.js';
|
|
9
|
+
export type { Advertisement } from './src/Advertisement.js';
|
|
10
|
+
export type { AdvertisementData, Advertiser } from './src/Advertiser.js';
|
|
11
|
+
export { BASM_ZERO_HASH, computeBasmRoot, computeTac, extractMerkleProofMetadata } from './src/BASM.js';
|
|
12
|
+
export type { AdmittedListRequest, AdmittedListResponse, AdmittedTxRef, BASMPeerSyncReport, CompoundMerklePathRequest, CompoundMerklePathResponse, MerkleProofMetadata, RawTransactionRecord, RawTransactionRequest, RawTransactionResponse, TopicAnchorHeader, TopicAnchorHeaderResolver, TopicAnchorRangeRequest, TopicAnchorRangeResponse, TopicAnchorTip, TopicBlockAnchor } from './src/BASM.js';
|
|
13
|
+
export { KnexStorage } from './src/storage/knex/KnexStorage.js';
|
|
14
|
+
export * as KnexStorageMigrations from './src/storage/knex/all-migrations.js';
|
|
15
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../mod.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAC1E,mBAAmB,wBAAwB,CAAA;AAC3C,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAGzD,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC3E,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,YAAY,EACV,UAAU,EACV,KAAK,EACL,cAAc,EACd,YAAY,EACZ,sBAAsB,EACvB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,EACL,cAAc,EACd,eAAe,EACf,UAAU,EACV,0BAA0B,EAC3B,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,yBAAyB,EACzB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,gBAAgB,EACjB,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAA;AAC/D,OAAO,KAAK,qBAAqB,MAAM,sCAAsC,CAAA"}
|
package/dist/cjs/mod.js
CHANGED
|
@@ -33,10 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.KnexStorageMigrations = exports.KnexStorage = exports.extractMerkleProofMetadata = exports.computeTac = exports.computeBasmRoot = exports.BASM_ZERO_HASH = exports.Engine = void 0;
|
|
36
|
+
exports.KnexStorageMigrations = exports.KnexStorage = exports.extractMerkleProofMetadata = exports.computeTac = exports.computeBasmRoot = exports.BASM_ZERO_HASH = exports.serializeLogValue = exports.serializeErrorForLog = exports.Engine = void 0;
|
|
37
37
|
// Fundamentals
|
|
38
38
|
var Engine_js_1 = require("./src/Engine.js");
|
|
39
39
|
Object.defineProperty(exports, "Engine", { enumerable: true, get: function () { return Engine_js_1.Engine; } });
|
|
40
|
+
var SafeLog_js_1 = require("./src/SafeLog.js");
|
|
41
|
+
Object.defineProperty(exports, "serializeErrorForLog", { enumerable: true, get: function () { return SafeLog_js_1.serializeErrorForLog; } });
|
|
42
|
+
Object.defineProperty(exports, "serializeLogValue", { enumerable: true, get: function () { return SafeLog_js_1.serializeLogValue; } });
|
|
40
43
|
var BASM_js_1 = require("./src/BASM.js");
|
|
41
44
|
Object.defineProperty(exports, "BASM_ZERO_HASH", { enumerable: true, get: function () { return BASM_js_1.BASM_ZERO_HASH; } });
|
|
42
45
|
Object.defineProperty(exports, "computeBasmRoot", { enumerable: true, get: function () { return BASM_js_1.computeBasmRoot; } });
|
package/dist/cjs/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../mod.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,6CAAwC;AAA/B,mGAAA,MAAM,OAAA;
|
|
1
|
+
{"version":3,"file":"mod.js","sourceRoot":"","sources":["../../mod.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,6CAAwC;AAA/B,mGAAA,MAAM,OAAA;AACf,+CAA0E;AAAjE,kHAAA,oBAAoB,OAAA;AAAE,+GAAA,iBAAiB,OAAA;AAiBhD,yCAKsB;AAJpB,yGAAA,cAAc,OAAA;AACd,0GAAA,eAAe,OAAA;AACf,qGAAA,UAAU,OAAA;AACV,qHAAA,0BAA0B,OAAA;AAqB5B,0BAA0B;AAC1B,oEAA+D;AAAtD,6GAAA,WAAW,OAAA;AACpB,QAAY,qBAAqB,iEAA4C"}
|
package/dist/cjs/package.json
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsv/overlay",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
4
11
|
"type": "commonjs",
|
|
5
12
|
"description": "BSV Blockchain Overlay Services Engine",
|
|
6
13
|
"files": [
|
|
7
14
|
"dist",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"mod.ts",
|
|
15
|
+
"!dist/**/*.tsbuildinfo",
|
|
16
|
+
"README.md",
|
|
11
17
|
"LICENSE.txt"
|
|
12
18
|
],
|
|
13
19
|
"scripts": {
|
|
14
|
-
"test": "
|
|
15
|
-
"test:watch": "
|
|
16
|
-
"test:coverage": "
|
|
17
|
-
"
|
|
18
|
-
"
|
|
20
|
+
"test": "pnpm build && jest --runInBand --watchman=false",
|
|
21
|
+
"test:watch": "pnpm build && jest --watch",
|
|
22
|
+
"test:coverage": "pnpm build && jest --coverage --runInBand --watchman=false",
|
|
23
|
+
"test:property": "jest --runInBand --watchman=false --runTestsByPath src/__tests/BASM.property.test.ts",
|
|
24
|
+
"format:check": "pnpm --workspace-root exec prettier --check \"packages/overlays/overlay/{README.md,jest.config.js,mod.ts,package.json,tsconfig*.json}\"",
|
|
25
|
+
"lint": "oxlint mod.ts src jest.config.js --deny-warnings",
|
|
26
|
+
"lint:ci": "pnpm lint",
|
|
27
|
+
"pack:check": "pnpm build && node ../../../scripts/check-package-artifact.mjs . --exports BASM_ZERO_HASH,Engine,KnexStorage,KnexStorageMigrations,computeBasmRoot,computeTac,extractMerkleProofMetadata,serializeErrorForLog,serializeLogValue --entry-exports \"./storage=;./storage/knex/KnexStorage=KnexStorage\"",
|
|
28
|
+
"typecheck": "tsc --project tsconfig.base.json --noEmit --incremental false",
|
|
19
29
|
"build": "tsc -b && tsconfig-to-dual-package tsconfig.cjs.json",
|
|
20
30
|
"dev": "tsc -b -w",
|
|
21
|
-
"
|
|
31
|
+
"prepublishOnly": "pnpm build",
|
|
22
32
|
"doc": "ts2md --inputFilename=mod.ts --outputFilename=docs/API.md --filenameSubstring=API --firstHeadingLevel=1"
|
|
23
33
|
},
|
|
24
34
|
"repository": {
|
|
@@ -42,13 +52,15 @@
|
|
|
42
52
|
"devDependencies": {
|
|
43
53
|
"@bsv/sdk": "workspace:^",
|
|
44
54
|
"@types/jest": "^30.0.0",
|
|
45
|
-
"@types/node": "^26.1.
|
|
55
|
+
"@types/node": "^26.1.2",
|
|
56
|
+
"@typescript/native": "npm:typescript@7.0.2",
|
|
57
|
+
"fast-check": "^4.9.0",
|
|
46
58
|
"jest": "^30.4.2",
|
|
59
|
+
"oxlint": "^1.76.0",
|
|
47
60
|
"ts-jest": "^29.4.12",
|
|
48
61
|
"ts2md": "^0.2.8",
|
|
49
62
|
"tsconfig-to-dual-package": "^1.2.0",
|
|
50
|
-
"typescript": "
|
|
51
|
-
"oxlint": "^1.75.0"
|
|
63
|
+
"typescript": "npm:@typescript/typescript6@6.0.2"
|
|
52
64
|
},
|
|
53
65
|
"dependencies": {
|
|
54
66
|
"@bsv/gasp": "workspace:^",
|
|
@@ -61,5 +73,21 @@
|
|
|
61
73
|
"@bsv/sdk": {
|
|
62
74
|
"optional": false
|
|
63
75
|
}
|
|
76
|
+
},
|
|
77
|
+
"typesVersions": {
|
|
78
|
+
"*": {
|
|
79
|
+
"storage": [
|
|
80
|
+
"dist/types/src/storage/Storage.d.ts"
|
|
81
|
+
],
|
|
82
|
+
"storage/*": [
|
|
83
|
+
"dist/types/src/storage/*"
|
|
84
|
+
],
|
|
85
|
+
"*.ts": [
|
|
86
|
+
"dist/types/src/*.d.ts"
|
|
87
|
+
],
|
|
88
|
+
"*": [
|
|
89
|
+
"dist/types/mod.d.ts"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
64
92
|
}
|
|
65
93
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Advertisement.d.ts","sourceRoot":"","sources":["../../../src/Advertisement.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB"}
|