@decentrys/dri-sdk 0.1.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.
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Digital Recovery Intelligence: the model.
3
+ *
4
+ * DRI answers three questions in order — where did the value go, who is at
5
+ * that destination, and is there anyone with the standing and the ability to
6
+ * do something about it. This package makes those answers available to the
7
+ * organisation whose value it was.
8
+ *
9
+ * Four constraints shape every type here. They are product and legal
10
+ * positions, not preferences, and the types are built so that code consuming
11
+ * them cannot quietly drop any of them:
12
+ *
13
+ * 1. **Decentrys never takes custody, and never freezes, seizes or returns
14
+ * anything.** Nothing in this package can be called to move value, and
15
+ * nothing it returns says value was moved by Decentrys. Every timeline
16
+ * entry names the party that acted, which is why `actorParty` is required
17
+ * rather than optional.
18
+ *
19
+ * 2. **Decentrys is not a law firm, a law-enforcement agency or a licensed
20
+ * recovery agent.** Nothing returned here is legal advice, and the
21
+ * statement travels in the payload — see `notes` on every response.
22
+ *
23
+ * 3. **The Recovery Index is an analytical estimate, never a prediction.**
24
+ * `RecoveryIndex` requires both `disclaimer` and `analyticalOnly`, and
25
+ * `assertAnalyticalDisclosure` refuses a payload missing either. A score
26
+ * handed onward without its caveat gets read back later as a promised
27
+ * recovery rate.
28
+ *
29
+ * 4. **Attribution beyond a mixer is inference.** `observedPath` is on every
30
+ * intervention point for that reason: false means the link to this case
31
+ * passes through mixing or privacy infrastructure, and the entry is an
32
+ * observation about a counterparty rather than a finding about anyone.
33
+ *
34
+ * DRI is offered to organisations — protocols, exchanges, funds, custodians
35
+ * and their counsel. It is not a consumer service.
36
+ */
37
+ /**
38
+ * What kind of thing sits at an address.
39
+ *
40
+ * Two of these carry the operational weight. `EXCHANGE` and `CUSTODIAN` mean a
41
+ * party holds the value and can be approached; `MIXER` means provable
42
+ * attribution stops. Anything unlabelled stays `UNKNOWN` rather than being
43
+ * guessed into a category — a wrong guess here names an innocent business.
44
+ */
45
+ export type FlowNodeType = 'WALLET' | 'ENTITY' | 'EXCHANGE' | 'CUSTODIAN' | 'DEX' | 'BRIDGE' | 'MIXER' | 'CONTRACT' | 'PROTOCOL' | 'UNKNOWN';
46
+ export interface FlowNode {
47
+ id: string;
48
+ chain: string;
49
+ address: string;
50
+ type: FlowNodeType;
51
+ /** The attribution recorded for this address, when there is one. */
52
+ label: string | null;
53
+ entityName: string | null;
54
+ riskLevel: string | null;
55
+ /** Graph distance from the seed. 0 is the address the case started from. */
56
+ hop: number;
57
+ isSeed: boolean;
58
+ /** False means nobody has looked past this address yet. */
59
+ expanded: boolean;
60
+ /**
61
+ * Confidence of the signal that put this address on the graph. Kept with the
62
+ * node so a four-hop inference is never rendered like a directly-labelled
63
+ * counterparty.
64
+ */
65
+ confidence: number | null;
66
+ explanation: string | null;
67
+ discoveredAt: string;
68
+ }
69
+ export interface FlowEdge {
70
+ id: string;
71
+ from: string;
72
+ to: string;
73
+ type: string;
74
+ chain: string;
75
+ txHash: string | null;
76
+ asset: string | null;
77
+ amountUsd: number | null;
78
+ txCount: number;
79
+ discoveredVia: string | null;
80
+ }
81
+ export interface FlowGraph {
82
+ investigationId: string;
83
+ caseRef: string;
84
+ title: string;
85
+ status: string;
86
+ chain: string;
87
+ seedAddress: string;
88
+ incidentId: string | null;
89
+ /**
90
+ * The recovery case behind this investigation, when the incident it belongs
91
+ * to has one. `getRecoveryIndex` and `generateEvidencePackage` need it, and
92
+ * `null` here is how a caller learns there is no tranche ledger to compute
93
+ * an outlook from — rather than by getting a 404 from those methods.
94
+ */
95
+ recoveryCaseId: string | null;
96
+ maxHops: number;
97
+ createdAt: string;
98
+ nodes: FlowNode[];
99
+ edges: FlowEdge[];
100
+ /** Caveats that ship with the result and cannot be suppressed. */
101
+ notes: string[];
102
+ }
103
+ export interface InvestigationSummary {
104
+ investigationId: string;
105
+ caseRef: string;
106
+ title: string;
107
+ status: string;
108
+ chain: string;
109
+ seedAddress: string;
110
+ incidentId: string | null;
111
+ maxHops: number;
112
+ addressCount: number;
113
+ transferCount: number;
114
+ createdAt: string;
115
+ }
116
+ export interface TraceCoverage {
117
+ addressesVisited?: number;
118
+ hopsCompleted?: number;
119
+ truncated?: boolean;
120
+ truncationReason?: string | null;
121
+ }
122
+ export interface TraceResult {
123
+ /** The address that was expanded. */
124
+ fromNodeId: string;
125
+ addressesDiscovered: number;
126
+ transfersRecorded: number;
127
+ /** Null when the trace reported nothing about its own completeness. */
128
+ coverage: TraceCoverage | null;
129
+ graph: FlowGraph;
130
+ notes: string[];
131
+ }
132
+ /**
133
+ * `CUSTODIAL` means a party holds the value now. `CROSS_CHAIN` means a bridge
134
+ * operator holds records of value that has already moved on. They are separate
135
+ * because treating the second as the first sends a client's legal budget after
136
+ * a paper trail rather than a pot of money.
137
+ */
138
+ export type InterventionKind = 'CUSTODIAL' | 'CROSS_CHAIN';
139
+ export interface InterventionPoint {
140
+ nodeId: string;
141
+ chain: string;
142
+ address: string;
143
+ nodeType: FlowNodeType;
144
+ kind: InterventionKind;
145
+ label: string | null;
146
+ entityName: string | null;
147
+ hop: number;
148
+ /**
149
+ * True when at least one path from the seed reaches this address without
150
+ * passing through mixing or privacy infrastructure.
151
+ *
152
+ * **False means inference.** The address is connected to this case by a
153
+ * chain of reasoning rather than by an observed transfer, and presenting it
154
+ * otherwise turns an observation into an allegation about a business that,
155
+ * so far as the record shows, has done nothing.
156
+ */
157
+ observedPath: boolean;
158
+ expanded: boolean;
159
+ /** Why this address is on the list, in the terms it is allowed to be stated. */
160
+ basis: string;
161
+ }
162
+ export interface AttributionLimit {
163
+ nodeId: string;
164
+ chain: string;
165
+ address: string;
166
+ nodeType: FlowNodeType;
167
+ hop: number;
168
+ reason: string;
169
+ }
170
+ export interface UnexploredAddress {
171
+ nodeId: string;
172
+ chain: string;
173
+ address: string;
174
+ nodeType: FlowNodeType;
175
+ hop: number;
176
+ }
177
+ export interface InterventionAnalysis {
178
+ investigationId: string;
179
+ caseRef: string;
180
+ interventionPoints: InterventionPoint[];
181
+ /** Where provable attribution stops. Mixing and privacy infrastructure. */
182
+ attributionLimits: AttributionLimit[];
183
+ /** Frontier addresses nobody has looked past yet. */
184
+ unexplored: UnexploredAddress[];
185
+ notes: string[];
186
+ }
187
+ export declare const RECOVERY_BANDS: readonly ["HIGH", "MODERATE", "LOW", "VERY_LOW"];
188
+ export type RecoveryBand = (typeof RECOVERY_BANDS)[number];
189
+ export interface RecoveryFunnel {
190
+ totalLossUsd: number;
191
+ byState: Record<string, number>;
192
+ locatedUsd: number;
193
+ atInterventionPointUsd: number;
194
+ freezeRequestedUsd: number;
195
+ frozenUsd: number;
196
+ recoveredUsd: number;
197
+ obfuscatedUsd: number;
198
+ lostUsd: number;
199
+ untracedUsd: number;
200
+ /** Value whose attribution rests on inference rather than a direct link. */
201
+ probabilisticUsd: number;
202
+ accountedUsd: number;
203
+ unaccountedUsd: number;
204
+ reconciles: boolean;
205
+ }
206
+ export interface RecoveryFactor {
207
+ factor: string;
208
+ contribution: number;
209
+ note: string;
210
+ }
211
+ /**
212
+ * The Recovery Index — the Recovery Outlook, as the case file names it.
213
+ *
214
+ * An analytical estimate of the current evidentiary and custodial position,
215
+ * produced by a versioned model from the evidence recorded in the case. It is
216
+ * **not a probability, not a forecast, and not a representation that any value
217
+ * will be recovered.**
218
+ *
219
+ * `disclaimer` and `analyticalOnly` are required fields rather than optional
220
+ * ones, so a consumer destructuring the score cannot end up with a type that
221
+ * says the caveat might not be there. `assertAnalyticalDisclosure` enforces
222
+ * the same thing at runtime, because a wire payload is not bound by a type.
223
+ */
224
+ export interface RecoveryIndex {
225
+ recoveryCaseId: string;
226
+ caseRef: string;
227
+ incidentRef: string;
228
+ /** 0-100. Higher means the record currently supports a stronger position. */
229
+ score: number;
230
+ band: RecoveryBand;
231
+ /** What the band means, stated as a situation rather than a likelihood. */
232
+ bandMeaning: string;
233
+ /** Every factor and what it contributed, so the number can be explained. */
234
+ factors: RecoveryFactor[];
235
+ /** Together these make any figure ever shown to a client reproducible. */
236
+ modelVersion: string;
237
+ inputsHash: string;
238
+ computedAt: string;
239
+ funnel: RecoveryFunnel;
240
+ /** Machine-readable form of `disclaimer`. Never false. */
241
+ analyticalOnly: true;
242
+ disclaimer: string;
243
+ notes: string[];
244
+ }
245
+ /**
246
+ * Thrown when a Recovery Index arrives without its analytical-only disclosure.
247
+ *
248
+ * Its own class, because it is not a transport failure and a caller that
249
+ * blanket-catches network errors must not swallow it.
250
+ */
251
+ export declare class MissingDisclosureError extends Error {
252
+ constructor(message: string);
253
+ }
254
+ /**
255
+ * Refuse a Recovery Index that is not marked analytical-only.
256
+ *
257
+ * This runs on the client, on a payload the server has already checked, and
258
+ * that duplication is the point. The score's whole legal safety rests on the
259
+ * caveat travelling with it: an integrator's dashboard renders whatever
260
+ * arrives, and a bare "58 — MODERATE" on a screen beside the word "recovery"
261
+ * is read as a rate. If the caveat ever stops arriving — a proxy stripping
262
+ * fields, an older deployment, a hand-rolled response — the correct outcome is
263
+ * a loud failure at the boundary rather than a number the caller cannot tell
264
+ * is unlabelled.
265
+ *
266
+ * Returns the value, so it can wrap a call site.
267
+ */
268
+ export declare function assertAnalyticalDisclosure(index: RecoveryIndex): RecoveryIndex;
269
+ /**
270
+ * Points where a party actually holds the value.
271
+ *
272
+ * Offered as a helper because it is the filter every caller reaches for, and
273
+ * writing it by hand is where a bridge quietly gets treated as a custodian.
274
+ */
275
+ export declare function custodialPoints(analysis: InterventionAnalysis): InterventionPoint[];
276
+ /**
277
+ * Intervention points whose connection to the case is inference.
278
+ *
279
+ * Separated so a caller has to decide what to do with them rather than
280
+ * discovering later that a list they forwarded to counsel mixed observed
281
+ * destinations with inferred ones.
282
+ */
283
+ export declare function inferredPoints(analysis: InterventionAnalysis): InterventionPoint[];
284
+ export type EvidenceRecipientType = 'EXCHANGE' | 'ISSUER' | 'LAW_ENFORCEMENT' | 'COUNSEL';
285
+ /**
286
+ * A prepared evidence package.
287
+ *
288
+ * **Prepared, not sent.** Decentrys does not contact counterparties on your
289
+ * behalf unless instructed, and `transmitted` is always false on a package
290
+ * this SDK created — transmission is recorded separately, with the party who
291
+ * sent it named, and that is deliberately not something a customer credential
292
+ * can write.
293
+ *
294
+ * The document is frozen and hashed at generation. A later version is issued
295
+ * as a new document rather than as a revision of this one, so a reader months
296
+ * afterwards can confirm they hold what was produced.
297
+ */
298
+ export interface EvidencePackage {
299
+ packageId: string;
300
+ recoveryCaseId: string;
301
+ caseRef: string;
302
+ version: number;
303
+ title: string;
304
+ recipientType: EvidenceRecipientType;
305
+ recipientName: string;
306
+ /** The document itself, as plain text. */
307
+ content: string;
308
+ /** SHA-256 of `content`. Quote it back to verify what you hold. */
309
+ contentHash: string;
310
+ /** The standing non-authority statement carried on the face of the document. */
311
+ authorityNotice: string;
312
+ preparedAt: string;
313
+ transmitted: false;
314
+ transmittedAt: null;
315
+ notes: string[];
316
+ }
317
+ export interface TimelineEntry {
318
+ at: string;
319
+ type: string;
320
+ /**
321
+ * Who performed this. Required, never optional: an entry saying value
322
+ * "became frozen" with no actor is an implied claim of standing that
323
+ * Decentrys does not have.
324
+ */
325
+ actorParty: string;
326
+ actorName: string | null;
327
+ title: string;
328
+ detail: string | null;
329
+ amountUsd: number | null;
330
+ /** The legal basis a third party cited, where one was given. */
331
+ authority: string | null;
332
+ }
333
+ export interface InvestigationTimeline {
334
+ investigationId: string;
335
+ caseRef: string;
336
+ recoveryCaseId: string | null;
337
+ entries: TimelineEntry[];
338
+ notes: string[];
339
+ }
340
+ export interface RecoveryCaseSummary {
341
+ recoveryCaseId: string;
342
+ caseRef: string;
343
+ status: string;
344
+ totalLossUsd: number;
345
+ openedAt: string;
346
+ watchActive: boolean;
347
+ incident: {
348
+ incidentId: string;
349
+ incidentRef: string;
350
+ title: string;
351
+ severity: string;
352
+ };
353
+ }
354
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
package/dist/model.js ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Digital Recovery Intelligence: the model.
4
+ *
5
+ * DRI answers three questions in order — where did the value go, who is at
6
+ * that destination, and is there anyone with the standing and the ability to
7
+ * do something about it. This package makes those answers available to the
8
+ * organisation whose value it was.
9
+ *
10
+ * Four constraints shape every type here. They are product and legal
11
+ * positions, not preferences, and the types are built so that code consuming
12
+ * them cannot quietly drop any of them:
13
+ *
14
+ * 1. **Decentrys never takes custody, and never freezes, seizes or returns
15
+ * anything.** Nothing in this package can be called to move value, and
16
+ * nothing it returns says value was moved by Decentrys. Every timeline
17
+ * entry names the party that acted, which is why `actorParty` is required
18
+ * rather than optional.
19
+ *
20
+ * 2. **Decentrys is not a law firm, a law-enforcement agency or a licensed
21
+ * recovery agent.** Nothing returned here is legal advice, and the
22
+ * statement travels in the payload — see `notes` on every response.
23
+ *
24
+ * 3. **The Recovery Index is an analytical estimate, never a prediction.**
25
+ * `RecoveryIndex` requires both `disclaimer` and `analyticalOnly`, and
26
+ * `assertAnalyticalDisclosure` refuses a payload missing either. A score
27
+ * handed onward without its caveat gets read back later as a promised
28
+ * recovery rate.
29
+ *
30
+ * 4. **Attribution beyond a mixer is inference.** `observedPath` is on every
31
+ * intervention point for that reason: false means the link to this case
32
+ * passes through mixing or privacy infrastructure, and the entry is an
33
+ * observation about a counterparty rather than a finding about anyone.
34
+ *
35
+ * DRI is offered to organisations — protocols, exchanges, funds, custodians
36
+ * and their counsel. It is not a consumer service.
37
+ */
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.MissingDisclosureError = exports.RECOVERY_BANDS = void 0;
40
+ exports.assertAnalyticalDisclosure = assertAnalyticalDisclosure;
41
+ exports.custodialPoints = custodialPoints;
42
+ exports.inferredPoints = inferredPoints;
43
+ // ---------------------------------------------------------------------------
44
+ // Recovery Index
45
+ // ---------------------------------------------------------------------------
46
+ exports.RECOVERY_BANDS = ['HIGH', 'MODERATE', 'LOW', 'VERY_LOW'];
47
+ /**
48
+ * Thrown when a Recovery Index arrives without its analytical-only disclosure.
49
+ *
50
+ * Its own class, because it is not a transport failure and a caller that
51
+ * blanket-catches network errors must not swallow it.
52
+ */
53
+ class MissingDisclosureError extends Error {
54
+ constructor(message) {
55
+ super(message);
56
+ this.name = 'MissingDisclosureError';
57
+ }
58
+ }
59
+ exports.MissingDisclosureError = MissingDisclosureError;
60
+ /**
61
+ * Refuse a Recovery Index that is not marked analytical-only.
62
+ *
63
+ * This runs on the client, on a payload the server has already checked, and
64
+ * that duplication is the point. The score's whole legal safety rests on the
65
+ * caveat travelling with it: an integrator's dashboard renders whatever
66
+ * arrives, and a bare "58 — MODERATE" on a screen beside the word "recovery"
67
+ * is read as a rate. If the caveat ever stops arriving — a proxy stripping
68
+ * fields, an older deployment, a hand-rolled response — the correct outcome is
69
+ * a loud failure at the boundary rather than a number the caller cannot tell
70
+ * is unlabelled.
71
+ *
72
+ * Returns the value, so it can wrap a call site.
73
+ */
74
+ function assertAnalyticalDisclosure(index) {
75
+ if (index.analyticalOnly !== true) {
76
+ throw new MissingDisclosureError('This Recovery Index is not marked analytical-only. The Recovery Index is an analytical estimate and '
77
+ + 'never a predicted recovery rate; refusing to hand back a score that does not say so.');
78
+ }
79
+ if (typeof index.disclaimer !== 'string' || index.disclaimer.trim().length === 0) {
80
+ throw new MissingDisclosureError('This Recovery Index arrived without its disclaimer. The disclaimer travels with the score by design, '
81
+ + 'because a score shown without it is read as a promise of recovery.');
82
+ }
83
+ return index;
84
+ }
85
+ /**
86
+ * Points where a party actually holds the value.
87
+ *
88
+ * Offered as a helper because it is the filter every caller reaches for, and
89
+ * writing it by hand is where a bridge quietly gets treated as a custodian.
90
+ */
91
+ function custodialPoints(analysis) {
92
+ return analysis.interventionPoints.filter((point) => point.kind === 'CUSTODIAL');
93
+ }
94
+ /**
95
+ * Intervention points whose connection to the case is inference.
96
+ *
97
+ * Separated so a caller has to decide what to do with them rather than
98
+ * discovering later that a list they forwarded to counsel mixed observed
99
+ * destinations with inferred ones.
100
+ */
101
+ function inferredPoints(analysis) {
102
+ return analysis.interventionPoints.filter((point) => !point.observedPath);
103
+ }
104
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;;;AA+QH,gEAcC;AAQD,0CAEC;AASD,wCAEC;AApID,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAEjE,QAAA,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAU,CAAC;AAkE/E;;;;;GAKG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,0BAA0B,CAAC,KAAoB;IAC7D,IAAI,KAAK,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,sBAAsB,CAC9B,sGAAsG;cACpG,sFAAsF,CACzF,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjF,MAAM,IAAI,sBAAsB,CAC9B,uGAAuG;cACrG,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,QAA8B;IAC5D,OAAO,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,QAA8B;IAC3D,OAAO,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;AAC5E,CAAC"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@decentrys/dri-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Digital Recovery Intelligence: fund tracing, attribution, intervention points and evidence packages. Analytical only — Decentrys never takes custody of assets.",
5
+ "license": "MIT",
6
+ "author": "Decentrys Labs",
7
+ "homepage": "https://decentrys.com",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/teamdecentrys-byte/Decentrys.git",
11
+ "directory": "sdk/dri"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/teamdecentrys-byte/Decentrys/issues"
15
+ },
16
+ "keywords": [
17
+ "decentrys",
18
+ "blockchain",
19
+ "web3",
20
+ "security",
21
+ "crypto",
22
+ "forensics",
23
+ "fund-tracing",
24
+ "investigation",
25
+ "incident-response"
26
+ ],
27
+ "sideEffects": false,
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "main": "dist/index.js",
35
+ "types": "dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/browser/decentrys-dri.mjs",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./browser": "./dist/browser/decentrys-dri.mjs"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "!dist/.tsbuildinfo*",
47
+ "src",
48
+ "LICENSE",
49
+ "README.md"
50
+ ],
51
+ "devDependencies": {
52
+ "typescript": "^5.7.2",
53
+ "vitest": "^3.2.7"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc -p tsconfig.json && npm run build:browser",
57
+ "build:browser": "../../node_modules/.bin/esbuild src/index.ts --bundle --format=esm --target=es2022 --outfile=dist/browser/decentrys-dri.mjs && ../../node_modules/.bin/esbuild src/index.ts --bundle --format=iife --global-name=DecentrysDri --target=es2022 --outfile=dist/browser/decentrys-dri.js",
58
+ "typecheck": "tsc -p tsconfig.spec.json",
59
+ "test": "vitest run"
60
+ }
61
+ }