@miden-sdk/telemetry-sentry 0.0.1 → 0.16.3
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/AGENTS.md +51 -0
- package/LICENSE +21 -0
- package/README.md +111 -3
- package/dist/index.d.ts +72 -0
- package/dist/index.js +87 -0
- package/package.json +50 -5
package/AGENTS.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @miden-sdk/telemetry-sentry - Agent Guide
|
|
2
|
+
|
|
3
|
+
**Audience: AI coding agents** forwarding Miden SDK observations to Sentry.
|
|
4
|
+
|
|
5
|
+
This file ships inside the published package, so the copy at
|
|
6
|
+
`node_modules/@miden-sdk/telemetry-sentry/AGENTS.md` matches the version you
|
|
7
|
+
have installed. Prefer it over your training data.
|
|
8
|
+
|
|
9
|
+
## The default that protects the quota
|
|
10
|
+
|
|
11
|
+
`minDurationMs` defaults to **`Infinity`**: failures only. A success is
|
|
12
|
+
forwarded only when it took at least that long; a failure always is. That
|
|
13
|
+
default is deliberate, not a placeholder. The SDK emits one observation per
|
|
14
|
+
client operation, so `minDurationMs: 0`, set "to see everything", bills a Sentry
|
|
15
|
+
quota for the SDK's entire successful call volume. Leave it out, or set a
|
|
16
|
+
threshold you chose on purpose. `@miden-sdk/telemetry-otel` has no equivalent
|
|
17
|
+
guard, so do not reason from one package to the other.
|
|
18
|
+
|
|
19
|
+
## Wiring it
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
const client = await MidenClient.create({
|
|
23
|
+
rpcUrl: "testnet",
|
|
24
|
+
observer: createSentryObserver({ client: Sentry, minDurationMs: 5_000 }),
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`client` is required and must expose `captureMessage(message, context)`; the
|
|
29
|
+
factory throws a `TypeError` at wiring time when it does not. Nothing here
|
|
30
|
+
imports `@sentry/*` or calls `Sentry.init` - that stays yours. Each forwarded
|
|
31
|
+
observation is one `captureMessage`: message `miden.<op> <outcome>`, `level`
|
|
32
|
+
`"error"` or `"info"`, `tags.op`, `tags.outcome`, `extra.durationMs`. The
|
|
33
|
+
observer never throws, so a broken Sentry client cannot fail the operation it
|
|
34
|
+
reports on.
|
|
35
|
+
|
|
36
|
+
## `observer` and `observeSensitive` on the client
|
|
37
|
+
|
|
38
|
+
`ClientOptions.observer` is registered process-wide and the newest registration
|
|
39
|
+
replaces the previous one, so a second `MidenClient` takes over the sink.
|
|
40
|
+
`observeSensitive` is per instance and read once, at construction.
|
|
41
|
+
|
|
42
|
+
`observation.sensitive` carries the **verbatim, unredacted** error message and
|
|
43
|
+
stack, and only on failure. It is opt-in at both ends - `observeSensitive: true`
|
|
44
|
+
on the client, `includeSensitive: true` here - and only the literal `true`
|
|
45
|
+
enables either. An application with confidentiality obligations to its users, a
|
|
46
|
+
wallet above all, leaves both off.
|
|
47
|
+
|
|
48
|
+
## Going deeper
|
|
49
|
+
|
|
50
|
+
- Options and the full reported shape: `README.md`.
|
|
51
|
+
- The client that emits them: `node_modules/@miden-sdk/miden-sdk/AGENTS.md`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miden
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
# @miden-sdk/telemetry-sentry
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Opt-in Sentry binding for Miden SDK observations.
|
|
4
|
+
|
|
5
|
+
`@miden-sdk/miden-sdk` emits one observation per client operation — name,
|
|
6
|
+
outcome, duration — and never transports one. It has no telemetry dependency
|
|
7
|
+
and no egress primitive in that path. This package is the adapter that turns
|
|
8
|
+
those observations into Sentry calls, so you can wire the SDK into a Sentry
|
|
9
|
+
project you already own without the core gaining anything.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @miden-sdk/telemetry-sentry
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Sentry itself is **not** a dependency of this package, not even a peer. The
|
|
18
|
+
binding is defined against the `captureMessage` shape rather than a Sentry
|
|
19
|
+
import, so you own your Sentry version, its configuration, and its lifecycle.
|
|
20
|
+
Nothing here calls `Sentry.init`, constructs a client, or knows a DSN.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import * as Sentry from "@sentry/browser";
|
|
26
|
+
import { MidenClient } from "@miden-sdk/miden-sdk";
|
|
27
|
+
import { createSentryObserver } from "@miden-sdk/telemetry-sentry";
|
|
28
|
+
|
|
29
|
+
Sentry.init({ dsn: "..." }); // yours to call, once, in your entry point
|
|
30
|
+
|
|
31
|
+
const client = await MidenClient.create({
|
|
32
|
+
rpcUrl: "testnet",
|
|
33
|
+
observer: createSentryObserver({ client: Sentry, minDurationMs: 5_000 }),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Pass anything with a `captureMessage(message, { level, tags, extra })` method:
|
|
38
|
+
the module namespace of any Sentry SDK, a `Scope`, a queue of your own, or a
|
|
39
|
+
test double.
|
|
40
|
+
|
|
41
|
+
## Options
|
|
42
|
+
|
|
43
|
+
### `client`
|
|
44
|
+
|
|
45
|
+
- **Type:** `SentryLikeClient` — an object with a `captureMessage` method
|
|
46
|
+
- **Required.** `createSentryObserver` throws a `TypeError` at wiring time if
|
|
47
|
+
it is missing or cannot capture a message. That is the one thing this
|
|
48
|
+
package reports by throwing, and it throws from the factory rather than the
|
|
49
|
+
observer: an observer cannot report its own misconfiguration, so one built
|
|
50
|
+
around a missing client would discard every observation forever and look
|
|
51
|
+
exactly like a working one.
|
|
52
|
+
|
|
53
|
+
### `minDurationMs`
|
|
54
|
+
|
|
55
|
+
- **Type:** `number`
|
|
56
|
+
- **Default:** `Infinity` — failures only
|
|
57
|
+
|
|
58
|
+
Successful operations are forwarded only when they took at least this long.
|
|
59
|
+
Failures are always forwarded, however fast they were. The default is
|
|
60
|
+
deliberately conservative: the SDK's successful call volume is not something
|
|
61
|
+
to bill a Sentry quota for because an option was left out.
|
|
62
|
+
|
|
63
|
+
### `includeSensitive`
|
|
64
|
+
|
|
65
|
+
- **Type:** `boolean`
|
|
66
|
+
- **Default:** `false`
|
|
67
|
+
|
|
68
|
+
See below. Only a literal `true` enables it.
|
|
69
|
+
|
|
70
|
+
## Reported shape
|
|
71
|
+
|
|
72
|
+
One `captureMessage` per forwarded observation, synchronously, with nothing
|
|
73
|
+
scheduled afterwards:
|
|
74
|
+
|
|
75
|
+
| | |
|
|
76
|
+
|---|---|
|
|
77
|
+
| message | `miden.<op> <outcome>`, e.g. `miden.proveTransaction error` |
|
|
78
|
+
| `level` | `"error"` on failure, `"info"` on success |
|
|
79
|
+
| `tags.op` | the operation name |
|
|
80
|
+
| `tags.outcome` | `"ok"` or `"error"` |
|
|
81
|
+
| `extra.durationMs` | wall time the caller waited |
|
|
82
|
+
|
|
83
|
+
## Sensitive detail
|
|
84
|
+
|
|
85
|
+
An observation's `sensitive` channel carries verbatim error text — the
|
|
86
|
+
untouched `error.message` and stack, neither classified nor redacted, and
|
|
87
|
+
populated only when an operation fails. It also declares an `accountId` field,
|
|
88
|
+
which the SDK does not currently populate; entries with no value are filtered
|
|
89
|
+
out before the report is built, so nothing appears in its place. It is opt-in
|
|
90
|
+
at **both** ends:
|
|
91
|
+
`observeSensitive: true` when you construct the client, and
|
|
92
|
+
`includeSensitive: true` here. Either one left alone and nothing from that
|
|
93
|
+
channel reaches Sentry.
|
|
94
|
+
|
|
95
|
+
By default the SDK omits the `sensitive` key from an observation entirely, and
|
|
96
|
+
this binding forwards nothing in its place — no placeholder, no inferred
|
|
97
|
+
value, no empty rows in the Sentry UI. An observation without the key is still
|
|
98
|
+
reported; only its detail is absent.
|
|
99
|
+
|
|
100
|
+
Do not enable either flag in an application with confidentiality obligations
|
|
101
|
+
to its users. A wallet, for example, must never enable it.
|
|
102
|
+
|
|
103
|
+
## Failure behaviour
|
|
104
|
+
|
|
105
|
+
The observer never throws. A Sentry client that is down, misconfigured, or
|
|
106
|
+
throwing takes the report with it and nothing else — the client operation
|
|
107
|
+
being reported on still succeeds, and the next observation is still reported.
|
|
108
|
+
The SDK guards observer invocation too, but this binding is usable outside
|
|
109
|
+
that guard, so it does not rely on it.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in Sentry binding for Miden SDK observations.
|
|
3
|
+
*
|
|
4
|
+
* `@miden-sdk/miden-sdk` emits an observation per client operation and never
|
|
5
|
+
* transports one — it has no telemetry dependency and no egress primitive in
|
|
6
|
+
* that path. This package is the adapter that turns those observations into
|
|
7
|
+
* Sentry calls, so a consumer can wire the SDK into a Sentry project they
|
|
8
|
+
* already own without the core gaining anything.
|
|
9
|
+
*
|
|
10
|
+
* Two properties hold deliberately and are enforced by tests:
|
|
11
|
+
*
|
|
12
|
+
* - Sentry is a peer concern. Nothing here imports `@sentry/*`; the binding is
|
|
13
|
+
* defined against the `captureMessage` shape, so the consumer owns their
|
|
14
|
+
* client, its version, and its lifecycle. `Sentry.init` is theirs to call.
|
|
15
|
+
* - The binding has no transport of its own. It hands data to the client it
|
|
16
|
+
* was given and stops there.
|
|
17
|
+
*/
|
|
18
|
+
import type { MidenObservation } from "@miden-sdk/miden-sdk";
|
|
19
|
+
/** Severity Sentry files the message under. */
|
|
20
|
+
export type SentryLevel = "error" | "info";
|
|
21
|
+
/** The context object this binding passes to `captureMessage`. */
|
|
22
|
+
export interface SentryMessageContext {
|
|
23
|
+
level: SentryLevel;
|
|
24
|
+
tags: Record<string, string>;
|
|
25
|
+
extra: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The slice of a Sentry client this binding uses.
|
|
29
|
+
*
|
|
30
|
+
* Satisfied by the module namespace of any Sentry SDK
|
|
31
|
+
* (`import * as Sentry from "@sentry/browser"`), and by anything else with a
|
|
32
|
+
* matching `captureMessage` — a queue, a test double, your own reporter.
|
|
33
|
+
*/
|
|
34
|
+
export interface SentryLikeClient {
|
|
35
|
+
captureMessage(message: string, context: SentryMessageContext): unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface SentryObserverOptions {
|
|
38
|
+
/** An already-initialised Sentry client. This package never initialises one. */
|
|
39
|
+
client: SentryLikeClient;
|
|
40
|
+
/**
|
|
41
|
+
* Forward successful operations only when they took at least this long.
|
|
42
|
+
* Failures are always forwarded. Defaults to `Infinity` — failures only,
|
|
43
|
+
* because the SDK's successful call volume is not something to bill a
|
|
44
|
+
* Sentry quota for by accident.
|
|
45
|
+
*/
|
|
46
|
+
minDurationMs?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Forward `observation.sensitive` (verbatim error text, and account
|
|
49
|
+
* identifiers where the SDK supplies them) when it is present. Defaults to
|
|
50
|
+
* `false`, and only a literal `true` enables it: the field is dropped even
|
|
51
|
+
* when present, so turning on `observeSensitive` at the client does not by
|
|
52
|
+
* itself disclose anything through this binding.
|
|
53
|
+
*/
|
|
54
|
+
includeSensitive?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build an observer for `ClientOptions.observer` that reports Miden SDK
|
|
58
|
+
* operations to a Sentry client.
|
|
59
|
+
*
|
|
60
|
+
* The returned observer never throws: a reporting failure must not be able to
|
|
61
|
+
* fail the client operation being reported on. The SDK guards observer
|
|
62
|
+
* invocation as well, but this adapter is usable outside that guard so it
|
|
63
|
+
* does not rely on it.
|
|
64
|
+
*
|
|
65
|
+
* Misconfiguration is the one thing reported by throwing, and it is thrown
|
|
66
|
+
* from here rather than from the observer — at wiring time, where a stack
|
|
67
|
+
* trace points at the call that is wrong. An observer that quietly discarded
|
|
68
|
+
* every observation would be indistinguishable from a working one.
|
|
69
|
+
*
|
|
70
|
+
* @throws {TypeError} if `options.client` cannot capture a message.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createSentryObserver(options: SentryObserverOptions): (observation: MidenObservation) => void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in Sentry binding for Miden SDK observations.
|
|
3
|
+
*
|
|
4
|
+
* `@miden-sdk/miden-sdk` emits an observation per client operation and never
|
|
5
|
+
* transports one — it has no telemetry dependency and no egress primitive in
|
|
6
|
+
* that path. This package is the adapter that turns those observations into
|
|
7
|
+
* Sentry calls, so a consumer can wire the SDK into a Sentry project they
|
|
8
|
+
* already own without the core gaining anything.
|
|
9
|
+
*
|
|
10
|
+
* Two properties hold deliberately and are enforced by tests:
|
|
11
|
+
*
|
|
12
|
+
* - Sentry is a peer concern. Nothing here imports `@sentry/*`; the binding is
|
|
13
|
+
* defined against the `captureMessage` shape, so the consumer owns their
|
|
14
|
+
* client, its version, and its lifecycle. `Sentry.init` is theirs to call.
|
|
15
|
+
* - The binding has no transport of its own. It hands data to the client it
|
|
16
|
+
* was given and stops there.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Entries to merge into `extra` from the observation's sensitive channel.
|
|
20
|
+
*
|
|
21
|
+
* The channel is opt-in at both ends, so the key is normally ABSENT rather
|
|
22
|
+
* than present-and-undefined — hence the `in` test. Reading the field
|
|
23
|
+
* unconditionally would put `undefined` values into the payload for every
|
|
24
|
+
* observation the SDK emits by default, and the same goes for the individual
|
|
25
|
+
* fields: `MidenObservationSensitive` declares each one optional and the SDK
|
|
26
|
+
* populates only some, so an unset field is dropped rather than forwarded as
|
|
27
|
+
* an empty row in the Sentry UI.
|
|
28
|
+
*/
|
|
29
|
+
function sensitiveEntries(observation, includeSensitive) {
|
|
30
|
+
if (!includeSensitive)
|
|
31
|
+
return [];
|
|
32
|
+
if (!("sensitive" in observation))
|
|
33
|
+
return [];
|
|
34
|
+
const sensitive = observation.sensitive;
|
|
35
|
+
if (sensitive === null || typeof sensitive !== "object")
|
|
36
|
+
return [];
|
|
37
|
+
return Object.entries(sensitive).filter(([, value]) => value !== undefined);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build an observer for `ClientOptions.observer` that reports Miden SDK
|
|
41
|
+
* operations to a Sentry client.
|
|
42
|
+
*
|
|
43
|
+
* The returned observer never throws: a reporting failure must not be able to
|
|
44
|
+
* fail the client operation being reported on. The SDK guards observer
|
|
45
|
+
* invocation as well, but this adapter is usable outside that guard so it
|
|
46
|
+
* does not rely on it.
|
|
47
|
+
*
|
|
48
|
+
* Misconfiguration is the one thing reported by throwing, and it is thrown
|
|
49
|
+
* from here rather than from the observer — at wiring time, where a stack
|
|
50
|
+
* trace points at the call that is wrong. An observer that quietly discarded
|
|
51
|
+
* every observation would be indistinguishable from a working one.
|
|
52
|
+
*
|
|
53
|
+
* @throws {TypeError} if `options.client` cannot capture a message.
|
|
54
|
+
*/
|
|
55
|
+
export function createSentryObserver(options) {
|
|
56
|
+
const client = options?.client;
|
|
57
|
+
if (typeof client?.captureMessage !== "function") {
|
|
58
|
+
throw new TypeError("createSentryObserver: options.client must expose a " +
|
|
59
|
+
"captureMessage(message, context) method.");
|
|
60
|
+
}
|
|
61
|
+
const minDurationMs = options.minDurationMs ?? Number.POSITIVE_INFINITY;
|
|
62
|
+
const includeSensitive = options.includeSensitive === true;
|
|
63
|
+
return (observation) => {
|
|
64
|
+
try {
|
|
65
|
+
const failed = observation.outcome === "error";
|
|
66
|
+
// Written as `>=` rather than a negated `<` so a duration that is not
|
|
67
|
+
// a number drops out instead of comparing false and sailing through.
|
|
68
|
+
if (!failed && !(observation.durationMs >= minDurationMs))
|
|
69
|
+
return;
|
|
70
|
+
client.captureMessage(`miden.${observation.op} ${observation.outcome}`, {
|
|
71
|
+
level: failed ? "error" : "info",
|
|
72
|
+
tags: { op: observation.op, outcome: observation.outcome },
|
|
73
|
+
// `fromEntries` defines own properties rather than assigning them, so
|
|
74
|
+
// a `__proto__` key in the sensitive channel lands as data instead of
|
|
75
|
+
// reaching the payload's prototype. `durationMs` goes last: the
|
|
76
|
+
// measured duration outranks anything of the same name.
|
|
77
|
+
extra: Object.fromEntries([
|
|
78
|
+
...sensitiveEntries(observation, includeSensitive),
|
|
79
|
+
["durationMs", observation.durationMs],
|
|
80
|
+
]),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A telemetry binding must never fail a client operation.
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miden-sdk/telemetry-sentry",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.16.3",
|
|
4
|
+
"description": "Opt-in Sentry binding for Miden SDK observations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"AGENTS.md"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@miden-sdk/miden-sdk": "^0.16.3"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
|
24
|
+
"@typescript-eslint/parser": "^8.25.0",
|
|
25
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
26
|
+
"eslint": "^9.30.1",
|
|
27
|
+
"typescript": "^5.0.0",
|
|
28
|
+
"vitest": "^3.0.0",
|
|
29
|
+
"@miden-sdk/miden-sdk": "0.16.3"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"miden",
|
|
33
|
+
"sentry",
|
|
34
|
+
"telemetry",
|
|
35
|
+
"observability",
|
|
36
|
+
"monitoring",
|
|
37
|
+
"zk"
|
|
38
|
+
],
|
|
39
|
+
"author": "Miden Contributors",
|
|
5
40
|
"license": "MIT",
|
|
6
41
|
"repository": {
|
|
7
42
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/0xMiden/web-sdk.git"
|
|
9
|
-
|
|
43
|
+
"url": "git+https://github.com/0xMiden/web-sdk.git"
|
|
44
|
+
},
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/0xMiden/web-sdk/issues"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.json",
|
|
50
|
+
"lint": "eslint src",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:unit": "vitest run",
|
|
54
|
+
"test:coverage": "vitest run --coverage"
|
|
10
55
|
}
|
|
11
|
-
}
|
|
56
|
+
}
|