@bsv/dpp-overlay-topics 0.4.0-beta.1
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 +58 -0
- package/README.md +169 -0
- package/dist/anchorStorage.d.ts +58 -0
- package/dist/anchorStorage.d.ts.map +1 -0
- package/dist/anchorStorage.js +72 -0
- package/dist/anchorStorage.js.map +1 -0
- package/dist/attestationAnchor.d.ts +10 -0
- package/dist/attestationAnchor.d.ts.map +1 -0
- package/dist/attestationAnchor.js +10 -0
- package/dist/attestationAnchor.js.map +1 -0
- package/dist/attestationStorage.d.ts +49 -0
- package/dist/attestationStorage.d.ts.map +1 -0
- package/dist/attestationStorage.js +78 -0
- package/dist/attestationStorage.js.map +1 -0
- package/dist/capabilities.d.ts +127 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +210 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/engineStorage.d.ts +106 -0
- package/dist/engineStorage.d.ts.map +1 -0
- package/dist/engineStorage.js +253 -0
- package/dist/engineStorage.js.map +1 -0
- package/dist/evidenceExport.d.ts +189 -0
- package/dist/evidenceExport.d.ts.map +1 -0
- package/dist/evidenceExport.js +517 -0
- package/dist/evidenceExport.js.map +1 -0
- package/dist/history.d.ts +114 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +215 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1223 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +31 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +31 -0
- package/dist/lib.js.map +1 -0
- package/dist/limits.d.ts +44 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +44 -0
- package/dist/limits.js.map +1 -0
- package/dist/lsAttestation.d.ts +21 -0
- package/dist/lsAttestation.d.ts.map +1 -0
- package/dist/lsAttestation.js +45 -0
- package/dist/lsAttestation.js.map +1 -0
- package/dist/lsDpp.d.ts +53 -0
- package/dist/lsDpp.d.ts.map +1 -0
- package/dist/lsDpp.js +148 -0
- package/dist/lsDpp.js.map +1 -0
- package/dist/lsUoraDpp.d.ts +55 -0
- package/dist/lsUoraDpp.d.ts.map +1 -0
- package/dist/lsUoraDpp.js +152 -0
- package/dist/lsUoraDpp.js.map +1 -0
- package/dist/policyConfig.d.ts +31 -0
- package/dist/policyConfig.d.ts.map +1 -0
- package/dist/policyConfig.js +112 -0
- package/dist/policyConfig.js.map +1 -0
- package/dist/retraction.d.ts +41 -0
- package/dist/retraction.d.ts.map +1 -0
- package/dist/retraction.js +147 -0
- package/dist/retraction.js.map +1 -0
- package/dist/storage.d.ts +117 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +158 -0
- package/dist/storage.js.map +1 -0
- package/dist/sync.d.ts +75 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +118 -0
- package/dist/sync.js.map +1 -0
- package/dist/tmAttestation.d.ts +36 -0
- package/dist/tmAttestation.d.ts.map +1 -0
- package/dist/tmAttestation.js +50 -0
- package/dist/tmAttestation.js.map +1 -0
- package/dist/tmDpp.d.ts +154 -0
- package/dist/tmDpp.d.ts.map +1 -0
- package/dist/tmDpp.js +397 -0
- package/dist/tmDpp.js.map +1 -0
- package/dist/tmUoraDpp.d.ts +73 -0
- package/dist/tmUoraDpp.d.ts.map +1 -0
- package/dist/tmUoraDpp.js +150 -0
- package/dist/tmUoraDpp.js.map +1 -0
- package/dist/uoraAnchor.d.ts +209 -0
- package/dist/uoraAnchor.d.ts.map +1 -0
- package/dist/uoraAnchor.js +433 -0
- package/dist/uoraAnchor.js.map +1 -0
- package/package.json +51 -0
package/dist/storage.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one accepted spelling of a selector: exactly one key, a non-empty string.
|
|
3
|
+
* Checked in the store and not only at the HTTP handler, because the Mongo
|
|
4
|
+
* store would run whatever object it is handed as a filter.
|
|
5
|
+
*/
|
|
6
|
+
export function selectorFilter(selector) {
|
|
7
|
+
const passportId = typeof selector.passportId === 'string' && selector.passportId !== '' ? selector.passportId : undefined;
|
|
8
|
+
const uid = typeof selector.uid === 'string' && selector.uid !== '' ? selector.uid : undefined;
|
|
9
|
+
if (passportId != null && uid == null)
|
|
10
|
+
return { passportId };
|
|
11
|
+
if (uid != null && passportId == null)
|
|
12
|
+
return { uid };
|
|
13
|
+
throw new Error('selector must name exactly one of passportId or uid, non-empty');
|
|
14
|
+
}
|
|
15
|
+
const COUNTER_ID = 'dppRecords';
|
|
16
|
+
export class MongoDppStorage {
|
|
17
|
+
records;
|
|
18
|
+
counters;
|
|
19
|
+
ready;
|
|
20
|
+
constructor(db) {
|
|
21
|
+
this.records = db.collection('dppRecords');
|
|
22
|
+
this.counters = db.collection('dppCounters');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Indexes, and the one migration this store has ever needed: rows written
|
|
26
|
+
* before `sequence` existed are numbered in createdAt, txid, outputIndex
|
|
27
|
+
* order, so every row has a position before the first page is served and the
|
|
28
|
+
* pages of an old deployment read in the order its lookups always answered.
|
|
29
|
+
* Runs once per process on first use, and `main()` awaits it at boot so the
|
|
30
|
+
* numbering never races a request. Idempotent: a second boot finds nothing
|
|
31
|
+
* to number.
|
|
32
|
+
*/
|
|
33
|
+
async ensureReady() {
|
|
34
|
+
this.ready ??= (async () => {
|
|
35
|
+
await this.records.createIndex({ passportId: 1, sequence: 1 });
|
|
36
|
+
await this.records.createIndex({ uid: 1, sequence: 1 });
|
|
37
|
+
await this.records.createIndex({ txid: 1, outputIndex: 1 }, { unique: true });
|
|
38
|
+
const legacy = this.records
|
|
39
|
+
.find({ sequence: { $exists: false } })
|
|
40
|
+
.sort({ createdAt: 1, txid: 1, outputIndex: 1 });
|
|
41
|
+
for await (const row of legacy) {
|
|
42
|
+
const sequence = await this.nextSequence();
|
|
43
|
+
// The filter repeats the existence test so a row a concurrent insert
|
|
44
|
+
// numbered in the meantime keeps the number it was given.
|
|
45
|
+
await this.records.updateOne({ txid: row.txid, outputIndex: row.outputIndex, sequence: { $exists: false } }, { $set: { sequence } });
|
|
46
|
+
}
|
|
47
|
+
})().catch((error) => {
|
|
48
|
+
this.ready = undefined;
|
|
49
|
+
throw error;
|
|
50
|
+
});
|
|
51
|
+
await this.ready;
|
|
52
|
+
}
|
|
53
|
+
/** One atomic increment per insert: the counter is the order, gaps included. */
|
|
54
|
+
async nextSequence() {
|
|
55
|
+
const counter = await this.counters.findOneAndUpdate({ _id: COUNTER_ID }, { $inc: { value: 1 } }, { upsert: true, returnDocument: 'after' });
|
|
56
|
+
if (counter == null)
|
|
57
|
+
throw new Error('the sequence counter did not answer');
|
|
58
|
+
return counter.value;
|
|
59
|
+
}
|
|
60
|
+
async insert(record) {
|
|
61
|
+
await this.ensureReady();
|
|
62
|
+
// The sequence is allotted before the row is written, so a snapshot taken
|
|
63
|
+
// between the two may pin a number whose row lands a moment later. The
|
|
64
|
+
// Engine admits one transaction at a time per request, so the window is
|
|
65
|
+
// the width of one write; a page that has already passed that position
|
|
66
|
+
// does not go back for it, and the next snapshot has it.
|
|
67
|
+
const sequence = await this.nextSequence();
|
|
68
|
+
await this.records.updateOne({ txid: record.txid, outputIndex: record.outputIndex },
|
|
69
|
+
// $setOnInsert: a re-admitted output keeps the position it was first
|
|
70
|
+
// given, and only its other fields are refreshed.
|
|
71
|
+
{ $set: record, $setOnInsert: { sequence } }, { upsert: true });
|
|
72
|
+
}
|
|
73
|
+
async markSpent(txid, outputIndex, spendingTxid) {
|
|
74
|
+
await this.ensureReady();
|
|
75
|
+
await this.records.updateOne({ txid, outputIndex }, { $set: { spent: true, spendingTxid } });
|
|
76
|
+
}
|
|
77
|
+
async markUnspent(txid, outputIndex) {
|
|
78
|
+
await this.ensureReady();
|
|
79
|
+
await this.records.updateOne({ txid, outputIndex }, { $set: { spent: false, spendingTxid: '' } });
|
|
80
|
+
}
|
|
81
|
+
async delete(txid, outputIndex) {
|
|
82
|
+
await this.ensureReady();
|
|
83
|
+
await this.records.deleteOne({ txid, outputIndex });
|
|
84
|
+
}
|
|
85
|
+
async findByPassport(passportId) {
|
|
86
|
+
await this.ensureReady();
|
|
87
|
+
return await this.records.find({ passportId }).sort({ sequence: 1 }).toArray();
|
|
88
|
+
}
|
|
89
|
+
async findByUid(uid) {
|
|
90
|
+
await this.ensureReady();
|
|
91
|
+
return await this.records.find({ uid }).sort({ sequence: 1 }).toArray();
|
|
92
|
+
}
|
|
93
|
+
async highestSequence() {
|
|
94
|
+
await this.ensureReady();
|
|
95
|
+
const counter = await this.counters.findOne({ _id: COUNTER_ID });
|
|
96
|
+
return counter?.value ?? 0;
|
|
97
|
+
}
|
|
98
|
+
async findPage(selector, range, limit) {
|
|
99
|
+
await this.ensureReady();
|
|
100
|
+
const filter = {
|
|
101
|
+
...selectorFilter(selector),
|
|
102
|
+
sequence: { $gt: range.after, $lte: range.upTo },
|
|
103
|
+
};
|
|
104
|
+
return await this.records.find(filter).sort({ sequence: 1 }).limit(limit).toArray();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export class InMemoryDppStorage {
|
|
108
|
+
records = new Map();
|
|
109
|
+
assigned = 0;
|
|
110
|
+
key(txid, outputIndex) {
|
|
111
|
+
return `${txid}.${outputIndex}`;
|
|
112
|
+
}
|
|
113
|
+
async insert(record) {
|
|
114
|
+
const key = this.key(record.txid, record.outputIndex);
|
|
115
|
+
// As the Mongo store: a re-admitted output keeps its first position.
|
|
116
|
+
const sequence = this.records.get(key)?.sequence ?? ++this.assigned;
|
|
117
|
+
this.records.set(key, { ...record, sequence });
|
|
118
|
+
}
|
|
119
|
+
async markSpent(txid, outputIndex, spendingTxid) {
|
|
120
|
+
const record = this.records.get(this.key(txid, outputIndex));
|
|
121
|
+
if (record != null) {
|
|
122
|
+
record.spent = true;
|
|
123
|
+
record.spendingTxid = spendingTxid;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async markUnspent(txid, outputIndex) {
|
|
127
|
+
const record = this.records.get(this.key(txid, outputIndex));
|
|
128
|
+
if (record != null) {
|
|
129
|
+
record.spent = false;
|
|
130
|
+
record.spendingTxid = '';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async delete(txid, outputIndex) {
|
|
134
|
+
this.records.delete(this.key(txid, outputIndex));
|
|
135
|
+
}
|
|
136
|
+
async findByPassport(passportId) {
|
|
137
|
+
return [...this.records.values()]
|
|
138
|
+
.filter((r) => r.passportId === passportId)
|
|
139
|
+
.sort((a, b) => a.sequence - b.sequence);
|
|
140
|
+
}
|
|
141
|
+
async findByUid(uid) {
|
|
142
|
+
return [...this.records.values()]
|
|
143
|
+
.filter((r) => r.uid === uid)
|
|
144
|
+
.sort((a, b) => a.sequence - b.sequence);
|
|
145
|
+
}
|
|
146
|
+
async highestSequence() {
|
|
147
|
+
return this.assigned;
|
|
148
|
+
}
|
|
149
|
+
async findPage(selector, range, limit) {
|
|
150
|
+
const filter = selectorFilter(selector);
|
|
151
|
+
const matches = (r) => 'passportId' in filter ? r.passportId === filter.passportId : r.uid === filter.uid;
|
|
152
|
+
return [...this.records.values()]
|
|
153
|
+
.filter((r) => matches(r) && r.sequence > range.after && r.sequence <= range.upTo)
|
|
154
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
155
|
+
.slice(0, limit);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Peer synchronisation (`spec/services.md` section 1, operator profile
|
|
3
|
+
* `federated-operators@1`): pulling the states and anchors another operator
|
|
4
|
+
* holds, through the overlay SDK's GASP, from a declared list of peers.
|
|
5
|
+
*
|
|
6
|
+
* The SDK does the protocol: `Engine.startGASPSync` walks every topic the
|
|
7
|
+
* `syncConfiguration` names, asks each peer for the outputs it holds since
|
|
8
|
+
* the last checkpoint, fetches each unknown output's graph, checks it against
|
|
9
|
+
* Bitcoin's rules and this node's own topic managers, and admits it through
|
|
10
|
+
* `Engine.submit` as a historical transaction. Admission during
|
|
11
|
+
* synchronisation is therefore the same admission as `/submit`: a state a
|
|
12
|
+
* peer offers is admitted only under the publisher policy and the chain
|
|
13
|
+
* rules, and a peer offering anything else is refused by name in the log.
|
|
14
|
+
*
|
|
15
|
+
* This module adds what the SDK leaves to the host: reading the peers from
|
|
16
|
+
* the environment, building the configuration for the two current topics
|
|
17
|
+
* (the legacy topic only when asked), running rounds on an interval, keeping
|
|
18
|
+
* a failing peer from stopping the node or blocking a request, and logging
|
|
19
|
+
* what each round did. Discovery is static: the peers are the ones named,
|
|
20
|
+
* never ones a lookup found, so the capability document says
|
|
21
|
+
* `static-peers` and nothing here authorises a publisher.
|
|
22
|
+
*/
|
|
23
|
+
import type { Engine } from '@bsv/overlay';
|
|
24
|
+
/** The Engine's shape for `syncConfiguration`: peers per topic, 'SHIP' for discovered peers, or false. */
|
|
25
|
+
export type SyncConfiguration = Record<string, string[] | 'SHIP' | false>;
|
|
26
|
+
export declare const DEFAULT_SYNC_INTERVAL_MS = 60000;
|
|
27
|
+
export interface SyncSettings {
|
|
28
|
+
/** Peer base URLs, normalised: trimmed, no trailing slash, de-duplicated. */
|
|
29
|
+
peers: string[];
|
|
30
|
+
/** Milliseconds between rounds; 0 runs the round after startup and no other. */
|
|
31
|
+
intervalMs: number;
|
|
32
|
+
/** Whether the historical UORA topic synchronises too. */
|
|
33
|
+
legacy: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function parseSyncPeers(value: string | undefined): string[];
|
|
36
|
+
export declare function syncSettingsFromEnvironment(): SyncSettings;
|
|
37
|
+
/**
|
|
38
|
+
* The Engine's configuration for the named topics: the peers for the passport
|
|
39
|
+
* and attestation topics, the legacy topic only when asked, false everywhere
|
|
40
|
+
* without peers, which is exactly the configuration the node has always run.
|
|
41
|
+
*/
|
|
42
|
+
export declare function syncConfigurationFor(settings: Pick<SyncSettings, 'peers' | 'legacy'>, topics: {
|
|
43
|
+
passport: string;
|
|
44
|
+
attestation: string;
|
|
45
|
+
legacy: string;
|
|
46
|
+
}): SyncConfiguration;
|
|
47
|
+
export interface SyncRound {
|
|
48
|
+
round: number;
|
|
49
|
+
startedAt: string;
|
|
50
|
+
durationMs: number;
|
|
51
|
+
ok: boolean;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PeerSynchronisation {
|
|
55
|
+
/** Run one round now, or join the round in flight; never rejects. */
|
|
56
|
+
runOnce: () => Promise<SyncRound>;
|
|
57
|
+
/** Stop the interval; a round in flight finishes. */
|
|
58
|
+
stop: () => void;
|
|
59
|
+
/** The rounds run so far, newest last, at most the last hundred. */
|
|
60
|
+
readonly rounds: SyncRound[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Rounds on an interval, each logged, none overlapping, none able to fail the
|
|
64
|
+
* node: `startGASPSync` already isolates one peer's failure from the next,
|
|
65
|
+
* and whatever escapes it is caught here and named. Requests are never
|
|
66
|
+
* blocked, because a round is ordinary asynchronous work on the same event
|
|
67
|
+
* loop and nothing awaits it but the round itself.
|
|
68
|
+
*/
|
|
69
|
+
export declare function startPeerSynchronisation(engine: Pick<Engine, 'startGASPSync'>, options: {
|
|
70
|
+
intervalMs: number;
|
|
71
|
+
peers: string[];
|
|
72
|
+
log?: (line: string) => void;
|
|
73
|
+
warn?: (line: string) => void;
|
|
74
|
+
}): PeerSynchronisation;
|
|
75
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,0GAA0G;AAC1G,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;AAEzE,eAAO,MAAM,wBAAwB,QAAS,CAAA;AAE9C,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAA;IAClB,0DAA0D;IAC1D,MAAM,EAAE,OAAO,CAAA;CAChB;AAUD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAkBlE;AAED,wBAAgB,2BAA2B,IAAI,YAAY,CAa1D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,QAAQ,CAAC,EAChD,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChE,iBAAiB,CAOnB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,OAAO,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,OAAO,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAA;IACjC,qDAAqD;IACrD,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,EACrC,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC5G,mBAAmB,CAmDrB"}
|
package/dist/sync.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const DEFAULT_SYNC_INTERVAL_MS = 60_000;
|
|
2
|
+
/** Comma-separated base URLs. Anything that is not an http or https URL stops the boot; a peer is not a thing to guess. */
|
|
3
|
+
/** A peer URL without its trailing slashes, scanned rather than matched so a long run of slashes costs linear time. */
|
|
4
|
+
function stripTrailingSlashes(value) {
|
|
5
|
+
let end = value.length;
|
|
6
|
+
while (end > 0 && value[end - 1] === '/')
|
|
7
|
+
end--;
|
|
8
|
+
return value.slice(0, end);
|
|
9
|
+
}
|
|
10
|
+
export function parseSyncPeers(value) {
|
|
11
|
+
const peers = [];
|
|
12
|
+
for (const raw of (value ?? '').split(',')) {
|
|
13
|
+
const candidate = raw.trim();
|
|
14
|
+
if (candidate === '')
|
|
15
|
+
continue;
|
|
16
|
+
let url;
|
|
17
|
+
try {
|
|
18
|
+
url = new URL(candidate);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new Error(`SYNC_PEERS entry "${candidate}" is not a URL`);
|
|
22
|
+
}
|
|
23
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
24
|
+
throw new Error(`SYNC_PEERS entry "${candidate}" must be an http or https URL`);
|
|
25
|
+
}
|
|
26
|
+
const normalised = stripTrailingSlashes(candidate);
|
|
27
|
+
if (!peers.includes(normalised))
|
|
28
|
+
peers.push(normalised);
|
|
29
|
+
}
|
|
30
|
+
return peers;
|
|
31
|
+
}
|
|
32
|
+
export function syncSettingsFromEnvironment() {
|
|
33
|
+
const peers = parseSyncPeers(process.env.SYNC_PEERS);
|
|
34
|
+
const rawInterval = (process.env.SYNC_INTERVAL_MS ?? '').trim();
|
|
35
|
+
let intervalMs = DEFAULT_SYNC_INTERVAL_MS;
|
|
36
|
+
if (rawInterval !== '') {
|
|
37
|
+
if (!/^\d+$/.test(rawInterval))
|
|
38
|
+
throw new Error('SYNC_INTERVAL_MS must be a non-negative integer of milliseconds');
|
|
39
|
+
intervalMs = Number(rawInterval);
|
|
40
|
+
}
|
|
41
|
+
const legacy = (process.env.SYNC_LEGACY ?? '').trim() === '1';
|
|
42
|
+
if (peers.length === 0 && (rawInterval !== '' || legacy)) {
|
|
43
|
+
console.warn('SYNC_INTERVAL_MS or SYNC_LEGACY is set but SYNC_PEERS is not: nothing synchronises');
|
|
44
|
+
}
|
|
45
|
+
return { peers, intervalMs, legacy };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The Engine's configuration for the named topics: the peers for the passport
|
|
49
|
+
* and attestation topics, the legacy topic only when asked, false everywhere
|
|
50
|
+
* without peers, which is exactly the configuration the node has always run.
|
|
51
|
+
*/
|
|
52
|
+
export function syncConfigurationFor(settings, topics) {
|
|
53
|
+
const peers = settings.peers.length === 0 ? false : [...settings.peers];
|
|
54
|
+
return {
|
|
55
|
+
[topics.passport]: peers,
|
|
56
|
+
[topics.attestation]: peers,
|
|
57
|
+
[topics.legacy]: settings.legacy ? peers : false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Rounds on an interval, each logged, none overlapping, none able to fail the
|
|
62
|
+
* node: `startGASPSync` already isolates one peer's failure from the next,
|
|
63
|
+
* and whatever escapes it is caught here and named. Requests are never
|
|
64
|
+
* blocked, because a round is ordinary asynchronous work on the same event
|
|
65
|
+
* loop and nothing awaits it but the round itself.
|
|
66
|
+
*/
|
|
67
|
+
export function startPeerSynchronisation(engine, options) {
|
|
68
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
69
|
+
const warn = options.warn ?? ((line) => console.warn(line));
|
|
70
|
+
const rounds = [];
|
|
71
|
+
let count = 0;
|
|
72
|
+
let inFlight;
|
|
73
|
+
const run = async () => {
|
|
74
|
+
const round = ++count;
|
|
75
|
+
const started = Date.now();
|
|
76
|
+
log(`peer synchronisation round ${round} started with ${options.peers.length} peer${options.peers.length === 1 ? '' : 's'}: ${options.peers.join(', ')}`);
|
|
77
|
+
let result;
|
|
78
|
+
try {
|
|
79
|
+
await engine.startGASPSync();
|
|
80
|
+
result = { round, startedAt: new Date(started).toISOString(), durationMs: Date.now() - started, ok: true };
|
|
81
|
+
log(`peer synchronisation round ${round} finished in ${result.durationMs} ms`);
|
|
82
|
+
}
|
|
83
|
+
catch (cause) {
|
|
84
|
+
const error = cause instanceof Error ? cause.message : String(cause);
|
|
85
|
+
result = { round, startedAt: new Date(started).toISOString(), durationMs: Date.now() - started, ok: false, error };
|
|
86
|
+
warn(`peer synchronisation round ${round} failed after ${result.durationMs} ms: ${error}`);
|
|
87
|
+
}
|
|
88
|
+
rounds.push(result);
|
|
89
|
+
if (rounds.length > 100)
|
|
90
|
+
rounds.shift();
|
|
91
|
+
return result;
|
|
92
|
+
};
|
|
93
|
+
const runOnce = () => {
|
|
94
|
+
if (inFlight == null) {
|
|
95
|
+
inFlight = run().finally(() => {
|
|
96
|
+
inFlight = undefined;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return inFlight;
|
|
100
|
+
};
|
|
101
|
+
let timer;
|
|
102
|
+
if (options.intervalMs > 0) {
|
|
103
|
+
timer = setInterval(() => {
|
|
104
|
+
void runOnce();
|
|
105
|
+
}, options.intervalMs);
|
|
106
|
+
timer.unref();
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
runOnce,
|
|
110
|
+
stop: () => {
|
|
111
|
+
if (timer != null)
|
|
112
|
+
clearInterval(timer);
|
|
113
|
+
timer = undefined;
|
|
114
|
+
},
|
|
115
|
+
rounds,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AdmittanceInstructions, TopicManager } from '@bsv/overlay';
|
|
2
|
+
import type { PublisherPolicy } from '@bsv/dpp-core';
|
|
3
|
+
export declare const ATTESTATION_TOPIC = "tm_attestation";
|
|
4
|
+
export interface AttestationAdmissionOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The publisher key policy chain (`spec/services.md` section 1), verified by
|
|
7
|
+
* the caller. When present and in force for this topic, an anchor is
|
|
8
|
+
* admitted only when its anchoring service is an anchor-publisher key active
|
|
9
|
+
* at admission time, and the static list is not consulted. Admission time,
|
|
10
|
+
* not the anchor's time: an anchor carries no timestamp, so the instant a
|
|
11
|
+
* key's window is held against is the only instant the index has. A late
|
|
12
|
+
* announcement of an anchor written under a since-retired key is therefore
|
|
13
|
+
* refused, which the operator resolves by re-announcing before retiring the
|
|
14
|
+
* key or by keeping the key active until the backlog is announced.
|
|
15
|
+
*/
|
|
16
|
+
publisherPolicy?: PublisherPolicy[];
|
|
17
|
+
/** The clock admission time is read from; injectable for tests. */
|
|
18
|
+
now?: () => Date;
|
|
19
|
+
}
|
|
20
|
+
/** Independent anchor admission. Credential proof, authority and status require the document. */
|
|
21
|
+
export declare class AttestationTopicManager implements TopicManager {
|
|
22
|
+
private readonly acceptedAnchoringServices;
|
|
23
|
+
private readonly policy?;
|
|
24
|
+
private readonly now;
|
|
25
|
+
constructor(acceptedAnchoringServices?: string[], options?: AttestationAdmissionOptions);
|
|
26
|
+
/** The anchoring services admitted at this instant; undefined means any well-formed anchor. */
|
|
27
|
+
private acceptedAt;
|
|
28
|
+
identifyAdmissibleOutputs(beef: number[]): Promise<AdmittanceInstructions>;
|
|
29
|
+
getDocumentation(): Promise<string>;
|
|
30
|
+
getMetaData(): Promise<{
|
|
31
|
+
name: string;
|
|
32
|
+
shortDescription: string;
|
|
33
|
+
version: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=tmAttestation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmAttestation.d.ts","sourceRoot":"","sources":["../src/tmAttestation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAIpD,eAAO,MAAM,iBAAiB,mBAAmB,CAAA;AAEjD,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,eAAe,EAAE,CAAA;IACnC,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,iGAAiG;AACjG,qBAAa,uBAAwB,YAAW,YAAY;IAKxD,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAJ5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;gBAGb,yBAAyB,GAAE,MAAM,EAAO,EACzD,OAAO,GAAE,2BAAgC;IAM3C,+FAA+F;IAC/F,OAAO,CAAC,UAAU;IAQZ,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAa1E,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAMnC,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAG1F"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Transaction } from '@bsv/sdk';
|
|
2
|
+
import { decodeAttestationAnchor, ATTESTATION_ANCHOR_PREFIX } from './attestationAnchor.js';
|
|
3
|
+
import { policyKeysFor } from './policyConfig.js';
|
|
4
|
+
export const ATTESTATION_TOPIC = 'tm_attestation';
|
|
5
|
+
/** Independent anchor admission. Credential proof, authority and status require the document. */
|
|
6
|
+
export class AttestationTopicManager {
|
|
7
|
+
acceptedAnchoringServices;
|
|
8
|
+
policy;
|
|
9
|
+
now;
|
|
10
|
+
constructor(acceptedAnchoringServices = [], options = {}) {
|
|
11
|
+
this.acceptedAnchoringServices = acceptedAnchoringServices;
|
|
12
|
+
this.policy = options.publisherPolicy != null && options.publisherPolicy.length > 0 ? options.publisherPolicy : undefined;
|
|
13
|
+
this.now = options.now ?? (() => new Date());
|
|
14
|
+
}
|
|
15
|
+
/** The anchoring services admitted at this instant; undefined means any well-formed anchor. */
|
|
16
|
+
acceptedAt(at) {
|
|
17
|
+
if (this.policy != null) {
|
|
18
|
+
const keys = policyKeysFor(this.policy, at, 'anchor-publisher', ATTESTATION_TOPIC);
|
|
19
|
+
if (keys != null)
|
|
20
|
+
return keys;
|
|
21
|
+
}
|
|
22
|
+
return this.acceptedAnchoringServices.length === 0 ? undefined : this.acceptedAnchoringServices;
|
|
23
|
+
}
|
|
24
|
+
async identifyAdmissibleOutputs(beef) {
|
|
25
|
+
const outputsToAdmit = [];
|
|
26
|
+
try {
|
|
27
|
+
const transaction = Transaction.fromBEEF(beef);
|
|
28
|
+
if (!Array.isArray(transaction.inputs) || transaction.inputs.length === 0 || !Array.isArray(transaction.outputs) || transaction.outputs.length === 0)
|
|
29
|
+
return { outputsToAdmit: [], coinsToRetain: [] };
|
|
30
|
+
const accepted = this.acceptedAt(this.now());
|
|
31
|
+
for (let index = 0; index < transaction.outputs.length; index++) {
|
|
32
|
+
const anchor = decodeAttestationAnchor(transaction.outputs[index].lockingScript);
|
|
33
|
+
if (anchor && (accepted == null || accepted.includes(anchor.anchoredBy)))
|
|
34
|
+
outputsToAdmit.push(index);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch { /* Invalid transaction input admits no outputs. */ }
|
|
38
|
+
return { outputsToAdmit, coinsToRetain: [] };
|
|
39
|
+
}
|
|
40
|
+
async getDocumentation() {
|
|
41
|
+
const policy = this.policy == null
|
|
42
|
+
? ''
|
|
43
|
+
: ' This index admits anchors only from anchor-publisher keys active at admission time under its publisher key policy (spec/services.md section 1); an anchor carries no timestamp, so admission time is the instant held against the key window. GET /capabilities lists the keys active now.';
|
|
44
|
+
return `${ATTESTATION_TOPIC} admits ${ATTESTATION_ANCHOR_PREFIX} outputs with ten fields and an exact five-OP_2DROP tail. The anchoring service signs nine length-prefixed fields. The digest commits to the complete secured representation. Carried issuer/subject/type/representation/media metadata must be checked against that document. Admission proves anchoring-service attribution, not credential validity, issuer authority, status, availability or VSC conformance. Legacy UORA layouts belong to their separately named historical topic.${policy}`;
|
|
45
|
+
}
|
|
46
|
+
async getMetaData() {
|
|
47
|
+
return { name: ATTESTATION_TOPIC, shortDescription: 'BSV attestation commitments with explicit representation formats', version: '1.1.0' };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tmAttestation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmAttestation.js","sourceRoot":"","sources":["../src/tmAttestation.ts"],"names":[],"mappings":"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"}
|
package/dist/tmDpp.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { AdmittanceInstructions, Storage, TopicManager } from '@bsv/overlay';
|
|
2
|
+
import { type PublisherPolicy } from '@bsv/dpp-core';
|
|
3
|
+
export declare const DPP_TOPIC = "tm_dpp";
|
|
4
|
+
/**
|
|
5
|
+
* tm_dpp - DPP Token Standard admission, versions 1 and 2
|
|
6
|
+
* (`spec/record-model.md` §6 and §8, `spec/record-model-v2.md` §6 and §8).
|
|
7
|
+
*
|
|
8
|
+
* Admits a transaction's single DPP output when:
|
|
9
|
+
* - exactly one well-formed DPP output exists (invariant 1; field rules are
|
|
10
|
+
* enforced by the dpp-core codec at parse time),
|
|
11
|
+
* - user_signature verifies against actor_identity_key + actor_keyID,
|
|
12
|
+
* - server_signature verifies against the configured service key
|
|
13
|
+
* (admission policy, not token validity - §5),
|
|
14
|
+
* - genesis states satisfy the genesis rules. Non-genesis states spend the
|
|
15
|
+
* previously admitted tip (invariant 4, via previousCoins) and satisfy the
|
|
16
|
+
* per-link transition rules (invariants 5–7).
|
|
17
|
+
*
|
|
18
|
+
* Spent tips are retained (coinsToRetain) so the lifecycle history stays
|
|
19
|
+
* resolvable (§1: the spend history is the passport's lifecycle).
|
|
20
|
+
*
|
|
21
|
+
* One rule is a profile's choice rather than the record model's, and this
|
|
22
|
+
* manager enforces it only when told to: the owner-signed transfer of
|
|
23
|
+
* `spec/custody.md` §4, under which a TRANSFER is admitted only when its actor
|
|
24
|
+
* proves it is the previous owner, by equality or by owner_linkage, or is a
|
|
25
|
+
* named transfer authority. It is admission policy of the same kind as the
|
|
26
|
+
* service signature, and the topic documentation says whether this instance
|
|
27
|
+
* enforces it, so a writer learns the rule from the index and not from a
|
|
28
|
+
* refusal (`spec/services.md` §6).
|
|
29
|
+
*/
|
|
30
|
+
export interface DppAdmissionOptions {
|
|
31
|
+
/**
|
|
32
|
+
* The owner-signed transfer (`spec/custody.md` §4). `true` runs the predicate
|
|
33
|
+
* on every TRANSFER; an object also names the transfer authorities, identity
|
|
34
|
+
* keys whose TRANSFER is admitted without proof (recovery). Off by default:
|
|
35
|
+
* the record model's baseline admits any signed TRANSFER that spends the tip.
|
|
36
|
+
* A malformed authority throws at construction, so a misconfigured
|
|
37
|
+
* deployment fails to boot rather than silently never matching.
|
|
38
|
+
*/
|
|
39
|
+
ownerConsent?: boolean | {
|
|
40
|
+
authorities: string[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The publisher key policy chain (`spec/services.md` §1), already verified
|
|
44
|
+
* by the caller with `verifyPolicyChain`. When present and in force for this
|
|
45
|
+
* topic, a state's countersignature is accepted only from a state-publisher
|
|
46
|
+
* key active at the state's own timestamp, and the positional identity key
|
|
47
|
+
* is not consulted: a retired key still admits the states it signed while
|
|
48
|
+
* active, and a key admits nothing timestamped before its activation.
|
|
49
|
+
* Absent, the identity key is the whole policy, as it always was.
|
|
50
|
+
*/
|
|
51
|
+
publisherPolicy?: PublisherPolicy[];
|
|
52
|
+
/**
|
|
53
|
+
* Authorities for version 2 control proofs (`spec/record-model-v2.md` §6):
|
|
54
|
+
* identity keys whose UPDATE, TRANSFER or RETIRE is admitted without a
|
|
55
|
+
* control proof. Defaults to the `ownerConsent` authorities, so one list
|
|
56
|
+
* serves both versions unless a deployment names another. Malformed keys
|
|
57
|
+
* throw at construction.
|
|
58
|
+
*/
|
|
59
|
+
controlAuthorities?: string[];
|
|
60
|
+
/**
|
|
61
|
+
* The managed-custody profile (`spec/managed-custody.md`): when selected, a
|
|
62
|
+
* version 2 TRANSFER is admitted only when it carries an
|
|
63
|
+
* `authorisation_commitment`. Off by default: the record model admits a
|
|
64
|
+
* TRANSFER with an empty commitment, and the topic documentation says which
|
|
65
|
+
* this instance runs.
|
|
66
|
+
*/
|
|
67
|
+
managedAcceptance?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The engine's own storage, so the manager can read an admitted
|
|
70
|
+
* predecessor's locking script when the BEEF it is handed does not carry
|
|
71
|
+
* the predecessor's bytes. Two callers hand it such a BEEF: a writer that
|
|
72
|
+
* announces a proven state alone, and the SDK's peer synchronisation, whose
|
|
73
|
+
* per-node BEEF stops at the node's proof and whose admission emulation
|
|
74
|
+
* lists as previous coins only what the temporary graph holds. Absent, the
|
|
75
|
+
* manager sees only what the BEEF and its own recent memory show, as it
|
|
76
|
+
* always did.
|
|
77
|
+
*/
|
|
78
|
+
admittedOutputs?: Pick<Storage, 'findOutput'> & Partial<Pick<Storage, 'findOutputsForTransaction'>>;
|
|
79
|
+
}
|
|
80
|
+
export declare class DppTopicManager implements TopicManager {
|
|
81
|
+
private readonly serverIdentityKey;
|
|
82
|
+
private readonly consentSelected;
|
|
83
|
+
private readonly authorities;
|
|
84
|
+
private readonly controlAuthorities;
|
|
85
|
+
private readonly managedAcceptance;
|
|
86
|
+
private readonly policy?;
|
|
87
|
+
private readonly admitted?;
|
|
88
|
+
/** States inspected recently, by txid: the predecessor a synchronisation pass showed one call ago. */
|
|
89
|
+
private readonly recent;
|
|
90
|
+
constructor(serverIdentityKey: string, options?: DppAdmissionOptions);
|
|
91
|
+
/**
|
|
92
|
+
* The keys this state's countersignature may come from: under a policy, the
|
|
93
|
+
* state-publisher keys active at the state's timestamp; otherwise the one
|
|
94
|
+
* configured identity key. The state's own timestamp is the instant that
|
|
95
|
+
* matters, not the admission time, because a state announced late is still
|
|
96
|
+
* the state it was when it was signed.
|
|
97
|
+
*/
|
|
98
|
+
private publisherKeysFor;
|
|
99
|
+
private remember;
|
|
100
|
+
/**
|
|
101
|
+
* The predecessor's locking script for one input, from the nearest source
|
|
102
|
+
* that has it: the hydrated input, the submitted BEEF, the engine's storage,
|
|
103
|
+
* and, on the historical paths only, the manager's recent memory. The txid
|
|
104
|
+
* the input names commits to the bytes each source yields, so none of them
|
|
105
|
+
* is trusted more than another; they differ only in who happened to hand
|
|
106
|
+
* the bytes over. Memory is kept for the synchronisation pass, where the SDK
|
|
107
|
+
* validates a graph one state at a time before any of it reaches storage;
|
|
108
|
+
* on the live path a writer's proven state announced without its parent is
|
|
109
|
+
* judged against what the index holds, never against what it happened to
|
|
110
|
+
* see, so the contract a submitter is held to stays the one it always was.
|
|
111
|
+
*/
|
|
112
|
+
private predecessorScript;
|
|
113
|
+
/**
|
|
114
|
+
* The genesis outpoint of the lineage a version 2 successor must name
|
|
115
|
+
* (`spec/record-model-v2.md` §6). A version 2 predecessor carries it; the
|
|
116
|
+
* ISSUE is it. A version 1 predecessor carries none, so the manager walks
|
|
117
|
+
* the admitted history back to the genesis through the same sources the
|
|
118
|
+
* predecessor's own bytes came from: the submitted BEEF, the engine's
|
|
119
|
+
* storage, and on the historical paths its recent memory. Undefined when
|
|
120
|
+
* the walk cannot be completed, which refuses the state: a lineage this
|
|
121
|
+
* index cannot trace is not one it can vouch for.
|
|
122
|
+
*/
|
|
123
|
+
private lineageGenesisFor;
|
|
124
|
+
/** The admitted DPP state a transaction carries, by txid, from the BEEF, the engine's storage or recent memory. */
|
|
125
|
+
private stateAt;
|
|
126
|
+
/**
|
|
127
|
+
* The inputs to treat as previously admitted coins. The Engine names them
|
|
128
|
+
* on the live path; the synchronisation emulation names only what its
|
|
129
|
+
* temporary graph holds, which for a state on top of a lineage this index
|
|
130
|
+
* already has is nothing. Then, and only when the storage is at hand, the
|
|
131
|
+
* inputs that spend an output this index has admitted for the topic stand
|
|
132
|
+
* in, which is the same question the Engine answers on the live path.
|
|
133
|
+
*/
|
|
134
|
+
private previousCoinsFor;
|
|
135
|
+
identifyAdmissibleOutputs(beef: number[], previousCoins: number[], _offChainValues?: number[], mode?: 'historical-tx' | 'current-tx' | 'historical-tx-no-spv'): Promise<AdmittanceInstructions>;
|
|
136
|
+
/**
|
|
137
|
+
* What a synchronising peer must fetch before this state can be judged: the
|
|
138
|
+
* input that spends the predecessor, when the state is not a genesis. The
|
|
139
|
+
* SDK asks this only for a proven state it could not admit alone, and drops
|
|
140
|
+
* any input this index already holds, so the answer is the predecessor and
|
|
141
|
+
* nothing else. Anything that does not decode as one DPP state needs nothing.
|
|
142
|
+
*/
|
|
143
|
+
identifyNeededInputs(beef: number[]): Promise<Array<{
|
|
144
|
+
txid: string;
|
|
145
|
+
outputIndex: number;
|
|
146
|
+
}>>;
|
|
147
|
+
getDocumentation(): Promise<string>;
|
|
148
|
+
getMetaData(): Promise<{
|
|
149
|
+
name: string;
|
|
150
|
+
shortDescription: string;
|
|
151
|
+
version?: string;
|
|
152
|
+
}>;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=tmDpp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmDpp.d.ts","sourceRoot":"","sources":["../src/tmDpp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACjF,OAAO,EAaL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAA;AAGtB,eAAO,MAAM,SAAS,WAAW,CAAA;AAqBjC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG;QAAE,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAClD;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,eAAe,EAAE,CAAA;IACnC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAA;CACpG;AAED,qBAAa,eAAgB,YAAW,YAAY;IAWhD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAVpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAmF;IAC7G,sGAAsG;IACtG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;gBAGrC,iBAAiB,EAAE,MAAM,EAC1C,OAAO,GAAE,mBAAwB;IAanC;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,QAAQ;IAUhB;;;;;;;;;;;OAWG;YACW,iBAAiB;IAsB/B;;;;;;;;;OASG;YACW,iBAAiB;IAuB/B,mHAAmH;YACrG,OAAO;IA2BrB;;;;;;;OAOG;YACW,gBAAgB;IAWxB,yBAAyB,CAC7B,IAAI,EAAE,MAAM,EAAE,EACd,aAAa,EAAE,MAAM,EAAE,EACvB,eAAe,CAAC,EAAE,MAAM,EAAE,EAC1B,IAAI,CAAC,EAAE,eAAe,GAAG,YAAY,GAAG,sBAAsB,GAC7D,OAAO,CAAC,sBAAsB,CAAC;IA4IlC;;;;;;OAMG;IACG,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAe3F,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IA6DnC,WAAW,IAAI,OAAO,CAAC;QAC3B,IAAI,EAAE,MAAM,CAAA;QACZ,gBAAgB,EAAE,MAAM,CAAA;QACxB,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAC;CAOH"}
|