@statly/observe 0.1.1 → 1.0.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/README.md +31 -5
- package/dist/{chunk-PETWPVHU.mjs → chunk-UNDSALI5.mjs} +25 -1
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +248 -0
- package/dist/cli.mjs +225 -0
- package/dist/index.d.mts +21 -4
- package/dist/index.d.ts +21 -4
- package/dist/index.js +25 -1
- package/dist/index.mjs +1 -1
- package/dist/integrations/express.js +25 -1
- package/dist/integrations/express.mjs +1 -1
- package/dist/integrations/fastify.js +25 -1
- package/dist/integrations/fastify.mjs +1 -1
- package/dist/integrations/nextjs.js +25 -1
- package/dist/integrations/nextjs.mjs +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -33,16 +33,30 @@ pnpm add @statly/observe
|
|
|
33
33
|
|
|
34
34
|
1. Go to [statly.live/dashboard/observe/setup](https://statly.live/dashboard/observe/setup)
|
|
35
35
|
2. Create an API key for Observe
|
|
36
|
-
3. Copy your DSN (format: `https://<
|
|
36
|
+
3. Copy your DSN (format: `https://<key-prefix>@statly.live/<org-slug>`)
|
|
37
|
+
4. Add to your `.env` file: `STATLY_DSN=https://...`
|
|
38
|
+
|
|
39
|
+
**Note**: The DSN contains only a 16-character key prefix (e.g., `sk_live_a1b2c3d4`) which is safe to embed in client-side code. For server-side operations requiring full permissions, use the complete API key.
|
|
37
40
|
|
|
38
41
|
## Quick Start
|
|
39
42
|
|
|
43
|
+
The SDK automatically loads DSN from environment variables, so you can simply:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Statly } from '@statly/observe';
|
|
47
|
+
|
|
48
|
+
// Auto-loads STATLY_DSN from environment
|
|
49
|
+
Statly.init();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or pass it explicitly:
|
|
53
|
+
|
|
40
54
|
```typescript
|
|
41
55
|
import { Statly } from '@statly/observe';
|
|
42
56
|
|
|
43
57
|
// Initialize the SDK
|
|
44
58
|
Statly.init({
|
|
45
|
-
dsn: 'https://
|
|
59
|
+
dsn: 'https://sk_live_a1b2c3d4@statly.live/your-org',
|
|
46
60
|
release: '1.0.0',
|
|
47
61
|
environment: 'production',
|
|
48
62
|
});
|
|
@@ -88,7 +102,7 @@ const app = express();
|
|
|
88
102
|
|
|
89
103
|
// Initialize first
|
|
90
104
|
Statly.init({
|
|
91
|
-
dsn: 'https://
|
|
105
|
+
dsn: 'https://sk_live_a1b2c3d4@statly.live/your-org',
|
|
92
106
|
environment: process.env.NODE_ENV,
|
|
93
107
|
});
|
|
94
108
|
|
|
@@ -180,7 +194,7 @@ import { Statly, statlyFastifyPlugin } from '@statly/observe';
|
|
|
180
194
|
const fastify = Fastify();
|
|
181
195
|
|
|
182
196
|
Statly.init({
|
|
183
|
-
dsn: 'https://
|
|
197
|
+
dsn: 'https://sk_live_a1b2c3d4@statly.live/your-org',
|
|
184
198
|
});
|
|
185
199
|
|
|
186
200
|
// Register the plugin
|
|
@@ -196,11 +210,23 @@ fastify.get('/', async () => {
|
|
|
196
210
|
fastify.listen({ port: 3000 });
|
|
197
211
|
```
|
|
198
212
|
|
|
213
|
+
## Environment Variables
|
|
214
|
+
|
|
215
|
+
The SDK automatically loads configuration from environment variables:
|
|
216
|
+
|
|
217
|
+
| Variable | Description |
|
|
218
|
+
|----------|-------------|
|
|
219
|
+
| `STATLY_DSN` | Your project's DSN (primary) |
|
|
220
|
+
| `NEXT_PUBLIC_STATLY_DSN` | DSN for Next.js client-side |
|
|
221
|
+
| `STATLY_OBSERVE_DSN` | Alternative DSN variable |
|
|
222
|
+
| `STATLY_ENVIRONMENT` | Environment name |
|
|
223
|
+
| `NODE_ENV` | Fallback for environment |
|
|
224
|
+
|
|
199
225
|
## Configuration Options
|
|
200
226
|
|
|
201
227
|
| Option | Type | Default | Description |
|
|
202
228
|
|--------|------|---------|-------------|
|
|
203
|
-
| `dsn` | `string` |
|
|
229
|
+
| `dsn` | `string` | `process.env.STATLY_DSN` | Your project's Data Source Name |
|
|
204
230
|
| `environment` | `string` | `undefined` | Environment name (production, staging, development) |
|
|
205
231
|
| `release` | `string` | `undefined` | Release/version identifier for tracking |
|
|
206
232
|
| `debug` | `boolean` | `false` | Enable debug logging to console |
|
|
@@ -886,12 +886,36 @@ function sanitizeHeaders2(headers) {
|
|
|
886
886
|
|
|
887
887
|
// src/index.ts
|
|
888
888
|
var client = null;
|
|
889
|
+
function loadDsnFromEnv() {
|
|
890
|
+
if (typeof process !== "undefined" && process.env) {
|
|
891
|
+
return process.env.STATLY_DSN || process.env.NEXT_PUBLIC_STATLY_DSN || process.env.STATLY_OBSERVE_DSN;
|
|
892
|
+
}
|
|
893
|
+
return void 0;
|
|
894
|
+
}
|
|
895
|
+
function loadEnvironmentFromEnv() {
|
|
896
|
+
if (typeof process !== "undefined" && process.env) {
|
|
897
|
+
return process.env.STATLY_ENVIRONMENT || process.env.NODE_ENV;
|
|
898
|
+
}
|
|
899
|
+
return void 0;
|
|
900
|
+
}
|
|
889
901
|
function init(options) {
|
|
890
902
|
if (client) {
|
|
891
903
|
console.warn("[Statly] SDK already initialized. Call close() first to reinitialize.");
|
|
892
904
|
return;
|
|
893
905
|
}
|
|
894
|
-
|
|
906
|
+
const dsn = options?.dsn || loadDsnFromEnv();
|
|
907
|
+
if (!dsn) {
|
|
908
|
+
console.error("[Statly] No DSN provided. Set STATLY_DSN in your environment or pass dsn to init().");
|
|
909
|
+
console.error("[Statly] Get your DSN at https://statly.live/dashboard/observe/setup");
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
const environment = options?.environment || loadEnvironmentFromEnv();
|
|
913
|
+
const finalOptions = {
|
|
914
|
+
...options,
|
|
915
|
+
dsn,
|
|
916
|
+
environment
|
|
917
|
+
};
|
|
918
|
+
client = new StatlyClient(finalOptions);
|
|
895
919
|
client.init();
|
|
896
920
|
}
|
|
897
921
|
function captureException(error, context) {
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var readline = __toESM(require("readline"));
|
|
28
|
+
var fs = __toESM(require("fs"));
|
|
29
|
+
var path = __toESM(require("path"));
|
|
30
|
+
var COLORS = {
|
|
31
|
+
reset: "\x1B[0m",
|
|
32
|
+
bright: "\x1B[1m",
|
|
33
|
+
dim: "\x1B[2m",
|
|
34
|
+
green: "\x1B[32m",
|
|
35
|
+
yellow: "\x1B[33m",
|
|
36
|
+
blue: "\x1B[34m",
|
|
37
|
+
cyan: "\x1B[36m"
|
|
38
|
+
};
|
|
39
|
+
function print(msg) {
|
|
40
|
+
console.log(msg);
|
|
41
|
+
}
|
|
42
|
+
function printHeader() {
|
|
43
|
+
print("");
|
|
44
|
+
print(`${COLORS.cyan}${COLORS.bright} _____ _ _ _ `);
|
|
45
|
+
print(`${COLORS.cyan} / ____| | | | | | `);
|
|
46
|
+
print(`${COLORS.cyan}| (___ | |_ __ _| |_| |_ _ `);
|
|
47
|
+
print(`${COLORS.cyan} \\___ \\| __/ _\` | __| | | | |`);
|
|
48
|
+
print(`${COLORS.cyan} ____) | || (_| | |_| | |_| |`);
|
|
49
|
+
print(`${COLORS.cyan}|_____/ \\__\\__,_|\\__|_|\\__, |`);
|
|
50
|
+
print(`${COLORS.cyan} __/ |`);
|
|
51
|
+
print(`${COLORS.cyan} |___/ ${COLORS.reset}`);
|
|
52
|
+
print("");
|
|
53
|
+
print(`${COLORS.bright}Statly Observe SDK Setup${COLORS.reset}`);
|
|
54
|
+
print(`${COLORS.dim}Error tracking for JavaScript applications${COLORS.reset}`);
|
|
55
|
+
print("");
|
|
56
|
+
}
|
|
57
|
+
function printStep(num, msg) {
|
|
58
|
+
print(`${COLORS.green}[${num}]${COLORS.reset} ${msg}`);
|
|
59
|
+
}
|
|
60
|
+
async function prompt(question) {
|
|
61
|
+
const rl = readline.createInterface({
|
|
62
|
+
input: process.stdin,
|
|
63
|
+
output: process.stdout
|
|
64
|
+
});
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
rl.question(`${COLORS.yellow}?${COLORS.reset} ${question} `, (answer) => {
|
|
67
|
+
rl.close();
|
|
68
|
+
resolve(answer.trim());
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function detectFramework() {
|
|
73
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
74
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
75
|
+
try {
|
|
76
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
77
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
78
|
+
if (deps["next"]) return "nextjs";
|
|
79
|
+
if (deps["express"]) return "express";
|
|
80
|
+
if (deps["fastify"]) return "fastify";
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function generateCode(dsn, framework) {
|
|
87
|
+
if (framework === "express") {
|
|
88
|
+
return `// Add to your main server file (e.g., app.js or index.js)
|
|
89
|
+
import express from 'express';
|
|
90
|
+
import { Statly, requestHandler, expressErrorHandler } from '@statly/observe';
|
|
91
|
+
|
|
92
|
+
const app = express();
|
|
93
|
+
|
|
94
|
+
// Initialize Statly FIRST
|
|
95
|
+
Statly.init({
|
|
96
|
+
dsn: '${dsn}',
|
|
97
|
+
environment: process.env.NODE_ENV || 'development',
|
|
98
|
+
release: process.env.npm_package_version,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Add request handler before routes
|
|
102
|
+
app.use(requestHandler());
|
|
103
|
+
|
|
104
|
+
// Your routes here...
|
|
105
|
+
|
|
106
|
+
// Add error handler LAST (after all routes)
|
|
107
|
+
app.use(expressErrorHandler());
|
|
108
|
+
|
|
109
|
+
app.listen(3000);
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
if (framework === "nextjs") {
|
|
113
|
+
return `// Create instrumentation.ts in your project root
|
|
114
|
+
// instrumentation.ts
|
|
115
|
+
import { Statly } from '@statly/observe';
|
|
116
|
+
|
|
117
|
+
export function register() {
|
|
118
|
+
Statly.init({
|
|
119
|
+
dsn: '${dsn}',
|
|
120
|
+
environment: process.env.NODE_ENV || 'development',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// For API routes, wrap handlers:
|
|
125
|
+
// app/api/example/route.ts
|
|
126
|
+
import { withStatly } from '@statly/observe';
|
|
127
|
+
|
|
128
|
+
export const GET = withStatly(async (request) => {
|
|
129
|
+
return Response.json({ ok: true });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// For error boundaries:
|
|
133
|
+
// app/error.tsx
|
|
134
|
+
'use client';
|
|
135
|
+
import { useEffect } from 'react';
|
|
136
|
+
import { captureNextJsError } from '@statly/observe';
|
|
137
|
+
|
|
138
|
+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
139
|
+
useEffect(() => { captureNextJsError(error); }, [error]);
|
|
140
|
+
return <button onClick={reset}>Try again</button>;
|
|
141
|
+
}
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
if (framework === "fastify") {
|
|
145
|
+
return `// Add to your Fastify server file
|
|
146
|
+
import Fastify from 'fastify';
|
|
147
|
+
import { Statly, statlyFastifyPlugin } from '@statly/observe';
|
|
148
|
+
|
|
149
|
+
const fastify = Fastify();
|
|
150
|
+
|
|
151
|
+
// Initialize Statly
|
|
152
|
+
Statly.init({
|
|
153
|
+
dsn: '${dsn}',
|
|
154
|
+
environment: process.env.NODE_ENV || 'development',
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Register the plugin
|
|
158
|
+
await fastify.register(statlyFastifyPlugin, {
|
|
159
|
+
captureValidationErrors: true,
|
|
160
|
+
skipStatusCodes: [400, 401, 403, 404],
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
fastify.get('/', async () => {
|
|
164
|
+
return { hello: 'world' };
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await fastify.listen({ port: 3000 });
|
|
168
|
+
`;
|
|
169
|
+
}
|
|
170
|
+
return `// Add to your application entry point
|
|
171
|
+
import { Statly } from '@statly/observe';
|
|
172
|
+
|
|
173
|
+
// Initialize the SDK
|
|
174
|
+
Statly.init({
|
|
175
|
+
dsn: '${dsn}',
|
|
176
|
+
environment: process.env.NODE_ENV || 'development',
|
|
177
|
+
release: '1.0.0', // Your app version
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Errors are captured automatically
|
|
181
|
+
|
|
182
|
+
// Manual capture example
|
|
183
|
+
try {
|
|
184
|
+
riskyOperation();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
Statly.captureException(error);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Set user context (after login)
|
|
190
|
+
Statly.setUser({
|
|
191
|
+
id: 'user-123',
|
|
192
|
+
email: 'user@example.com',
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Flush before exit (important for serverless)
|
|
196
|
+
await Statly.close();
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
199
|
+
async function main() {
|
|
200
|
+
const args = process.argv.slice(2);
|
|
201
|
+
const command = args[0];
|
|
202
|
+
if (command === "init") {
|
|
203
|
+
printHeader();
|
|
204
|
+
print(`${COLORS.dim}Get your DSN from: https://statly.live/dashboard/observe/setup${COLORS.reset}`);
|
|
205
|
+
print("");
|
|
206
|
+
const dsn = await prompt("Enter your DSN:");
|
|
207
|
+
if (!dsn) {
|
|
208
|
+
print(`${COLORS.yellow}No DSN provided. Get one at https://statly.live${COLORS.reset}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
if (!dsn.match(/^https:\/\/[^@]+@statly\.live\/.+$/)) {
|
|
212
|
+
print(`${COLORS.yellow}Warning: DSN format looks incorrect.${COLORS.reset}`);
|
|
213
|
+
print(`${COLORS.dim}Expected format: https://<api-key>@statly.live/<org-slug>${COLORS.reset}`);
|
|
214
|
+
}
|
|
215
|
+
print("");
|
|
216
|
+
printStep(1, "Detecting your framework...");
|
|
217
|
+
const framework = detectFramework();
|
|
218
|
+
if (framework) {
|
|
219
|
+
print(` ${COLORS.green}\u2713${COLORS.reset} Detected: ${COLORS.bright}${framework}${COLORS.reset}`);
|
|
220
|
+
} else {
|
|
221
|
+
print(` ${COLORS.dim}No framework detected, using generic setup${COLORS.reset}`);
|
|
222
|
+
}
|
|
223
|
+
print("");
|
|
224
|
+
printStep(2, "Generated setup code:");
|
|
225
|
+
print("");
|
|
226
|
+
print(`${COLORS.dim}${"\u2500".repeat(50)}${COLORS.reset}`);
|
|
227
|
+
print(generateCode(dsn, framework));
|
|
228
|
+
print(`${COLORS.dim}${"\u2500".repeat(50)}${COLORS.reset}`);
|
|
229
|
+
print("");
|
|
230
|
+
printStep(3, "Next steps:");
|
|
231
|
+
print(` ${COLORS.dim}1.${COLORS.reset} Copy the code above into your application`);
|
|
232
|
+
print(` ${COLORS.dim}2.${COLORS.reset} Set your environment variables`);
|
|
233
|
+
print(` ${COLORS.dim}3.${COLORS.reset} Trigger a test error to verify`);
|
|
234
|
+
print("");
|
|
235
|
+
print(`${COLORS.green}\u2713${COLORS.reset} Setup complete! View errors at ${COLORS.cyan}https://statly.live/dashboard/observe${COLORS.reset}`);
|
|
236
|
+
print("");
|
|
237
|
+
print(`${COLORS.dim}Documentation: https://docs.statly.live/sdk/javascript/installation${COLORS.reset}`);
|
|
238
|
+
print("");
|
|
239
|
+
} else {
|
|
240
|
+
print(`${COLORS.bright}Statly Observe CLI${COLORS.reset}`);
|
|
241
|
+
print("");
|
|
242
|
+
print("Usage:");
|
|
243
|
+
print(` npx @statly/observe init ${COLORS.dim}Setup Statly in your project${COLORS.reset}`);
|
|
244
|
+
print("");
|
|
245
|
+
print("Get started at https://statly.live");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
main().catch(console.error);
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import * as readline from "readline";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
var COLORS = {
|
|
8
|
+
reset: "\x1B[0m",
|
|
9
|
+
bright: "\x1B[1m",
|
|
10
|
+
dim: "\x1B[2m",
|
|
11
|
+
green: "\x1B[32m",
|
|
12
|
+
yellow: "\x1B[33m",
|
|
13
|
+
blue: "\x1B[34m",
|
|
14
|
+
cyan: "\x1B[36m"
|
|
15
|
+
};
|
|
16
|
+
function print(msg) {
|
|
17
|
+
console.log(msg);
|
|
18
|
+
}
|
|
19
|
+
function printHeader() {
|
|
20
|
+
print("");
|
|
21
|
+
print(`${COLORS.cyan}${COLORS.bright} _____ _ _ _ `);
|
|
22
|
+
print(`${COLORS.cyan} / ____| | | | | | `);
|
|
23
|
+
print(`${COLORS.cyan}| (___ | |_ __ _| |_| |_ _ `);
|
|
24
|
+
print(`${COLORS.cyan} \\___ \\| __/ _\` | __| | | | |`);
|
|
25
|
+
print(`${COLORS.cyan} ____) | || (_| | |_| | |_| |`);
|
|
26
|
+
print(`${COLORS.cyan}|_____/ \\__\\__,_|\\__|_|\\__, |`);
|
|
27
|
+
print(`${COLORS.cyan} __/ |`);
|
|
28
|
+
print(`${COLORS.cyan} |___/ ${COLORS.reset}`);
|
|
29
|
+
print("");
|
|
30
|
+
print(`${COLORS.bright}Statly Observe SDK Setup${COLORS.reset}`);
|
|
31
|
+
print(`${COLORS.dim}Error tracking for JavaScript applications${COLORS.reset}`);
|
|
32
|
+
print("");
|
|
33
|
+
}
|
|
34
|
+
function printStep(num, msg) {
|
|
35
|
+
print(`${COLORS.green}[${num}]${COLORS.reset} ${msg}`);
|
|
36
|
+
}
|
|
37
|
+
async function prompt(question) {
|
|
38
|
+
const rl = readline.createInterface({
|
|
39
|
+
input: process.stdin,
|
|
40
|
+
output: process.stdout
|
|
41
|
+
});
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
rl.question(`${COLORS.yellow}?${COLORS.reset} ${question} `, (answer) => {
|
|
44
|
+
rl.close();
|
|
45
|
+
resolve(answer.trim());
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function detectFramework() {
|
|
50
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
51
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
52
|
+
try {
|
|
53
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
54
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
55
|
+
if (deps["next"]) return "nextjs";
|
|
56
|
+
if (deps["express"]) return "express";
|
|
57
|
+
if (deps["fastify"]) return "fastify";
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function generateCode(dsn, framework) {
|
|
64
|
+
if (framework === "express") {
|
|
65
|
+
return `// Add to your main server file (e.g., app.js or index.js)
|
|
66
|
+
import express from 'express';
|
|
67
|
+
import { Statly, requestHandler, expressErrorHandler } from '@statly/observe';
|
|
68
|
+
|
|
69
|
+
const app = express();
|
|
70
|
+
|
|
71
|
+
// Initialize Statly FIRST
|
|
72
|
+
Statly.init({
|
|
73
|
+
dsn: '${dsn}',
|
|
74
|
+
environment: process.env.NODE_ENV || 'development',
|
|
75
|
+
release: process.env.npm_package_version,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Add request handler before routes
|
|
79
|
+
app.use(requestHandler());
|
|
80
|
+
|
|
81
|
+
// Your routes here...
|
|
82
|
+
|
|
83
|
+
// Add error handler LAST (after all routes)
|
|
84
|
+
app.use(expressErrorHandler());
|
|
85
|
+
|
|
86
|
+
app.listen(3000);
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
if (framework === "nextjs") {
|
|
90
|
+
return `// Create instrumentation.ts in your project root
|
|
91
|
+
// instrumentation.ts
|
|
92
|
+
import { Statly } from '@statly/observe';
|
|
93
|
+
|
|
94
|
+
export function register() {
|
|
95
|
+
Statly.init({
|
|
96
|
+
dsn: '${dsn}',
|
|
97
|
+
environment: process.env.NODE_ENV || 'development',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// For API routes, wrap handlers:
|
|
102
|
+
// app/api/example/route.ts
|
|
103
|
+
import { withStatly } from '@statly/observe';
|
|
104
|
+
|
|
105
|
+
export const GET = withStatly(async (request) => {
|
|
106
|
+
return Response.json({ ok: true });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// For error boundaries:
|
|
110
|
+
// app/error.tsx
|
|
111
|
+
'use client';
|
|
112
|
+
import { useEffect } from 'react';
|
|
113
|
+
import { captureNextJsError } from '@statly/observe';
|
|
114
|
+
|
|
115
|
+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
116
|
+
useEffect(() => { captureNextJsError(error); }, [error]);
|
|
117
|
+
return <button onClick={reset}>Try again</button>;
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
if (framework === "fastify") {
|
|
122
|
+
return `// Add to your Fastify server file
|
|
123
|
+
import Fastify from 'fastify';
|
|
124
|
+
import { Statly, statlyFastifyPlugin } from '@statly/observe';
|
|
125
|
+
|
|
126
|
+
const fastify = Fastify();
|
|
127
|
+
|
|
128
|
+
// Initialize Statly
|
|
129
|
+
Statly.init({
|
|
130
|
+
dsn: '${dsn}',
|
|
131
|
+
environment: process.env.NODE_ENV || 'development',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Register the plugin
|
|
135
|
+
await fastify.register(statlyFastifyPlugin, {
|
|
136
|
+
captureValidationErrors: true,
|
|
137
|
+
skipStatusCodes: [400, 401, 403, 404],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
fastify.get('/', async () => {
|
|
141
|
+
return { hello: 'world' };
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await fastify.listen({ port: 3000 });
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
return `// Add to your application entry point
|
|
148
|
+
import { Statly } from '@statly/observe';
|
|
149
|
+
|
|
150
|
+
// Initialize the SDK
|
|
151
|
+
Statly.init({
|
|
152
|
+
dsn: '${dsn}',
|
|
153
|
+
environment: process.env.NODE_ENV || 'development',
|
|
154
|
+
release: '1.0.0', // Your app version
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Errors are captured automatically
|
|
158
|
+
|
|
159
|
+
// Manual capture example
|
|
160
|
+
try {
|
|
161
|
+
riskyOperation();
|
|
162
|
+
} catch (error) {
|
|
163
|
+
Statly.captureException(error);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Set user context (after login)
|
|
167
|
+
Statly.setUser({
|
|
168
|
+
id: 'user-123',
|
|
169
|
+
email: 'user@example.com',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Flush before exit (important for serverless)
|
|
173
|
+
await Statly.close();
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
async function main() {
|
|
177
|
+
const args = process.argv.slice(2);
|
|
178
|
+
const command = args[0];
|
|
179
|
+
if (command === "init") {
|
|
180
|
+
printHeader();
|
|
181
|
+
print(`${COLORS.dim}Get your DSN from: https://statly.live/dashboard/observe/setup${COLORS.reset}`);
|
|
182
|
+
print("");
|
|
183
|
+
const dsn = await prompt("Enter your DSN:");
|
|
184
|
+
if (!dsn) {
|
|
185
|
+
print(`${COLORS.yellow}No DSN provided. Get one at https://statly.live${COLORS.reset}`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
if (!dsn.match(/^https:\/\/[^@]+@statly\.live\/.+$/)) {
|
|
189
|
+
print(`${COLORS.yellow}Warning: DSN format looks incorrect.${COLORS.reset}`);
|
|
190
|
+
print(`${COLORS.dim}Expected format: https://<api-key>@statly.live/<org-slug>${COLORS.reset}`);
|
|
191
|
+
}
|
|
192
|
+
print("");
|
|
193
|
+
printStep(1, "Detecting your framework...");
|
|
194
|
+
const framework = detectFramework();
|
|
195
|
+
if (framework) {
|
|
196
|
+
print(` ${COLORS.green}\u2713${COLORS.reset} Detected: ${COLORS.bright}${framework}${COLORS.reset}`);
|
|
197
|
+
} else {
|
|
198
|
+
print(` ${COLORS.dim}No framework detected, using generic setup${COLORS.reset}`);
|
|
199
|
+
}
|
|
200
|
+
print("");
|
|
201
|
+
printStep(2, "Generated setup code:");
|
|
202
|
+
print("");
|
|
203
|
+
print(`${COLORS.dim}${"\u2500".repeat(50)}${COLORS.reset}`);
|
|
204
|
+
print(generateCode(dsn, framework));
|
|
205
|
+
print(`${COLORS.dim}${"\u2500".repeat(50)}${COLORS.reset}`);
|
|
206
|
+
print("");
|
|
207
|
+
printStep(3, "Next steps:");
|
|
208
|
+
print(` ${COLORS.dim}1.${COLORS.reset} Copy the code above into your application`);
|
|
209
|
+
print(` ${COLORS.dim}2.${COLORS.reset} Set your environment variables`);
|
|
210
|
+
print(` ${COLORS.dim}3.${COLORS.reset} Trigger a test error to verify`);
|
|
211
|
+
print("");
|
|
212
|
+
print(`${COLORS.green}\u2713${COLORS.reset} Setup complete! View errors at ${COLORS.cyan}https://statly.live/dashboard/observe${COLORS.reset}`);
|
|
213
|
+
print("");
|
|
214
|
+
print(`${COLORS.dim}Documentation: https://docs.statly.live/sdk/javascript/installation${COLORS.reset}`);
|
|
215
|
+
print("");
|
|
216
|
+
} else {
|
|
217
|
+
print(`${COLORS.bright}Statly Observe CLI${COLORS.reset}`);
|
|
218
|
+
print("");
|
|
219
|
+
print("Usage:");
|
|
220
|
+
print(` npx @statly/observe init ${COLORS.dim}Setup Statly in your project${COLORS.reset}`);
|
|
221
|
+
print("");
|
|
222
|
+
print("Get started at https://statly.live");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
main().catch(console.error);
|
package/dist/index.d.mts
CHANGED
|
@@ -6,8 +6,11 @@ export { createRequestCapture, statlyFastifyPlugin, statlyPlugin } from './integ
|
|
|
6
6
|
* Statly Observe SDK Types
|
|
7
7
|
*/
|
|
8
8
|
interface StatlyOptions {
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* DSN for your organization: https://<api-key>@statly.live/<org-slug>
|
|
11
|
+
* Can be omitted if STATLY_DSN, NEXT_PUBLIC_STATLY_DSN, or STATLY_OBSERVE_DSN is set in environment
|
|
12
|
+
*/
|
|
13
|
+
dsn?: string;
|
|
11
14
|
/** Release/version identifier */
|
|
12
15
|
release?: string;
|
|
13
16
|
/** Environment (e.g., 'production', 'staging', 'development') */
|
|
@@ -110,7 +113,9 @@ declare class StatlyClient {
|
|
|
110
113
|
private consoleIntegration;
|
|
111
114
|
private user;
|
|
112
115
|
private initialized;
|
|
113
|
-
constructor(options: StatlyOptions
|
|
116
|
+
constructor(options: StatlyOptions & {
|
|
117
|
+
dsn: string;
|
|
118
|
+
});
|
|
114
119
|
private mergeOptions;
|
|
115
120
|
private detectEnvironment;
|
|
116
121
|
/**
|
|
@@ -219,8 +224,20 @@ declare class StatlyClient {
|
|
|
219
224
|
|
|
220
225
|
/**
|
|
221
226
|
* Initialize the Statly SDK
|
|
227
|
+
*
|
|
228
|
+
* DSN can be passed explicitly or loaded from environment variables:
|
|
229
|
+
* - STATLY_DSN
|
|
230
|
+
* - NEXT_PUBLIC_STATLY_DSN (for Next.js)
|
|
231
|
+
* - STATLY_OBSERVE_DSN
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* // Explicit DSN
|
|
235
|
+
* Statly.init({ dsn: 'https://sk_live_xxx@statly.live/your-org' });
|
|
236
|
+
*
|
|
237
|
+
* // Auto-load from .env
|
|
238
|
+
* Statly.init(); // Uses STATLY_DSN from environment
|
|
222
239
|
*/
|
|
223
|
-
declare function init(options
|
|
240
|
+
declare function init(options?: Partial<StatlyOptions>): void;
|
|
224
241
|
/**
|
|
225
242
|
* Capture an exception
|
|
226
243
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,11 @@ export { createRequestCapture, statlyFastifyPlugin, statlyPlugin } from './integ
|
|
|
6
6
|
* Statly Observe SDK Types
|
|
7
7
|
*/
|
|
8
8
|
interface StatlyOptions {
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* DSN for your organization: https://<api-key>@statly.live/<org-slug>
|
|
11
|
+
* Can be omitted if STATLY_DSN, NEXT_PUBLIC_STATLY_DSN, or STATLY_OBSERVE_DSN is set in environment
|
|
12
|
+
*/
|
|
13
|
+
dsn?: string;
|
|
11
14
|
/** Release/version identifier */
|
|
12
15
|
release?: string;
|
|
13
16
|
/** Environment (e.g., 'production', 'staging', 'development') */
|
|
@@ -110,7 +113,9 @@ declare class StatlyClient {
|
|
|
110
113
|
private consoleIntegration;
|
|
111
114
|
private user;
|
|
112
115
|
private initialized;
|
|
113
|
-
constructor(options: StatlyOptions
|
|
116
|
+
constructor(options: StatlyOptions & {
|
|
117
|
+
dsn: string;
|
|
118
|
+
});
|
|
114
119
|
private mergeOptions;
|
|
115
120
|
private detectEnvironment;
|
|
116
121
|
/**
|
|
@@ -219,8 +224,20 @@ declare class StatlyClient {
|
|
|
219
224
|
|
|
220
225
|
/**
|
|
221
226
|
* Initialize the Statly SDK
|
|
227
|
+
*
|
|
228
|
+
* DSN can be passed explicitly or loaded from environment variables:
|
|
229
|
+
* - STATLY_DSN
|
|
230
|
+
* - NEXT_PUBLIC_STATLY_DSN (for Next.js)
|
|
231
|
+
* - STATLY_OBSERVE_DSN
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* // Explicit DSN
|
|
235
|
+
* Statly.init({ dsn: 'https://sk_live_xxx@statly.live/your-org' });
|
|
236
|
+
*
|
|
237
|
+
* // Auto-load from .env
|
|
238
|
+
* Statly.init(); // Uses STATLY_DSN from environment
|
|
222
239
|
*/
|
|
223
|
-
declare function init(options
|
|
240
|
+
declare function init(options?: Partial<StatlyOptions>): void;
|
|
224
241
|
/**
|
|
225
242
|
* Capture an exception
|
|
226
243
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1032,12 +1032,36 @@ function sanitizeHeaders3(headers) {
|
|
|
1032
1032
|
|
|
1033
1033
|
// src/index.ts
|
|
1034
1034
|
var client = null;
|
|
1035
|
+
function loadDsnFromEnv() {
|
|
1036
|
+
if (typeof process !== "undefined" && process.env) {
|
|
1037
|
+
return process.env.STATLY_DSN || process.env.NEXT_PUBLIC_STATLY_DSN || process.env.STATLY_OBSERVE_DSN;
|
|
1038
|
+
}
|
|
1039
|
+
return void 0;
|
|
1040
|
+
}
|
|
1041
|
+
function loadEnvironmentFromEnv() {
|
|
1042
|
+
if (typeof process !== "undefined" && process.env) {
|
|
1043
|
+
return process.env.STATLY_ENVIRONMENT || process.env.NODE_ENV;
|
|
1044
|
+
}
|
|
1045
|
+
return void 0;
|
|
1046
|
+
}
|
|
1035
1047
|
function init(options) {
|
|
1036
1048
|
if (client) {
|
|
1037
1049
|
console.warn("[Statly] SDK already initialized. Call close() first to reinitialize.");
|
|
1038
1050
|
return;
|
|
1039
1051
|
}
|
|
1040
|
-
|
|
1052
|
+
const dsn = options?.dsn || loadDsnFromEnv();
|
|
1053
|
+
if (!dsn) {
|
|
1054
|
+
console.error("[Statly] No DSN provided. Set STATLY_DSN in your environment or pass dsn to init().");
|
|
1055
|
+
console.error("[Statly] Get your DSN at https://statly.live/dashboard/observe/setup");
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const environment = options?.environment || loadEnvironmentFromEnv();
|
|
1059
|
+
const finalOptions = {
|
|
1060
|
+
...options,
|
|
1061
|
+
dsn,
|
|
1062
|
+
environment
|
|
1063
|
+
};
|
|
1064
|
+
client = new StatlyClient(finalOptions);
|
|
1041
1065
|
client.init();
|
|
1042
1066
|
}
|
|
1043
1067
|
function captureException(error, context) {
|
package/dist/index.mjs
CHANGED
|
@@ -678,12 +678,36 @@ var StatlyClient = class {
|
|
|
678
678
|
|
|
679
679
|
// src/index.ts
|
|
680
680
|
var client = null;
|
|
681
|
+
function loadDsnFromEnv() {
|
|
682
|
+
if (typeof process !== "undefined" && process.env) {
|
|
683
|
+
return process.env.STATLY_DSN || process.env.NEXT_PUBLIC_STATLY_DSN || process.env.STATLY_OBSERVE_DSN;
|
|
684
|
+
}
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
function loadEnvironmentFromEnv() {
|
|
688
|
+
if (typeof process !== "undefined" && process.env) {
|
|
689
|
+
return process.env.STATLY_ENVIRONMENT || process.env.NODE_ENV;
|
|
690
|
+
}
|
|
691
|
+
return void 0;
|
|
692
|
+
}
|
|
681
693
|
function init(options) {
|
|
682
694
|
if (client) {
|
|
683
695
|
console.warn("[Statly] SDK already initialized. Call close() first to reinitialize.");
|
|
684
696
|
return;
|
|
685
697
|
}
|
|
686
|
-
|
|
698
|
+
const dsn = options?.dsn || loadDsnFromEnv();
|
|
699
|
+
if (!dsn) {
|
|
700
|
+
console.error("[Statly] No DSN provided. Set STATLY_DSN in your environment or pass dsn to init().");
|
|
701
|
+
console.error("[Statly] Get your DSN at https://statly.live/dashboard/observe/setup");
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const environment = options?.environment || loadEnvironmentFromEnv();
|
|
705
|
+
const finalOptions = {
|
|
706
|
+
...options,
|
|
707
|
+
dsn,
|
|
708
|
+
environment
|
|
709
|
+
};
|
|
710
|
+
client = new StatlyClient(finalOptions);
|
|
687
711
|
client.init();
|
|
688
712
|
}
|
|
689
713
|
function captureException(error, context) {
|
|
@@ -679,12 +679,36 @@ var StatlyClient = class {
|
|
|
679
679
|
|
|
680
680
|
// src/index.ts
|
|
681
681
|
var client = null;
|
|
682
|
+
function loadDsnFromEnv() {
|
|
683
|
+
if (typeof process !== "undefined" && process.env) {
|
|
684
|
+
return process.env.STATLY_DSN || process.env.NEXT_PUBLIC_STATLY_DSN || process.env.STATLY_OBSERVE_DSN;
|
|
685
|
+
}
|
|
686
|
+
return void 0;
|
|
687
|
+
}
|
|
688
|
+
function loadEnvironmentFromEnv() {
|
|
689
|
+
if (typeof process !== "undefined" && process.env) {
|
|
690
|
+
return process.env.STATLY_ENVIRONMENT || process.env.NODE_ENV;
|
|
691
|
+
}
|
|
692
|
+
return void 0;
|
|
693
|
+
}
|
|
682
694
|
function init(options) {
|
|
683
695
|
if (client) {
|
|
684
696
|
console.warn("[Statly] SDK already initialized. Call close() first to reinitialize.");
|
|
685
697
|
return;
|
|
686
698
|
}
|
|
687
|
-
|
|
699
|
+
const dsn = options?.dsn || loadDsnFromEnv();
|
|
700
|
+
if (!dsn) {
|
|
701
|
+
console.error("[Statly] No DSN provided. Set STATLY_DSN in your environment or pass dsn to init().");
|
|
702
|
+
console.error("[Statly] Get your DSN at https://statly.live/dashboard/observe/setup");
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const environment = options?.environment || loadEnvironmentFromEnv();
|
|
706
|
+
const finalOptions = {
|
|
707
|
+
...options,
|
|
708
|
+
dsn,
|
|
709
|
+
environment
|
|
710
|
+
};
|
|
711
|
+
client = new StatlyClient(finalOptions);
|
|
688
712
|
client.init();
|
|
689
713
|
}
|
|
690
714
|
function captureException(error, context) {
|
|
@@ -682,12 +682,36 @@ var StatlyClient = class {
|
|
|
682
682
|
|
|
683
683
|
// src/index.ts
|
|
684
684
|
var client = null;
|
|
685
|
+
function loadDsnFromEnv() {
|
|
686
|
+
if (typeof process !== "undefined" && process.env) {
|
|
687
|
+
return process.env.STATLY_DSN || process.env.NEXT_PUBLIC_STATLY_DSN || process.env.STATLY_OBSERVE_DSN;
|
|
688
|
+
}
|
|
689
|
+
return void 0;
|
|
690
|
+
}
|
|
691
|
+
function loadEnvironmentFromEnv() {
|
|
692
|
+
if (typeof process !== "undefined" && process.env) {
|
|
693
|
+
return process.env.STATLY_ENVIRONMENT || process.env.NODE_ENV;
|
|
694
|
+
}
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
685
697
|
function init(options) {
|
|
686
698
|
if (client) {
|
|
687
699
|
console.warn("[Statly] SDK already initialized. Call close() first to reinitialize.");
|
|
688
700
|
return;
|
|
689
701
|
}
|
|
690
|
-
|
|
702
|
+
const dsn = options?.dsn || loadDsnFromEnv();
|
|
703
|
+
if (!dsn) {
|
|
704
|
+
console.error("[Statly] No DSN provided. Set STATLY_DSN in your environment or pass dsn to init().");
|
|
705
|
+
console.error("[Statly] Get your DSN at https://statly.live/dashboard/observe/setup");
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const environment = options?.environment || loadEnvironmentFromEnv();
|
|
709
|
+
const finalOptions = {
|
|
710
|
+
...options,
|
|
711
|
+
dsn,
|
|
712
|
+
environment
|
|
713
|
+
};
|
|
714
|
+
client = new StatlyClient(finalOptions);
|
|
691
715
|
client.init();
|
|
692
716
|
}
|
|
693
717
|
function captureException(error, context) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statly/observe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "JavaScript SDK for Statly Observe - Error tracking and monitoring",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"statly-observe": "./dist/cli.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -30,10 +33,11 @@
|
|
|
30
33
|
"files": [
|
|
31
34
|
"dist",
|
|
32
35
|
"README.md",
|
|
33
|
-
"LICENSE"
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"bin"
|
|
34
38
|
],
|
|
35
39
|
"scripts": {
|
|
36
|
-
"build": "tsup src/index.ts src/integrations/express.ts src/integrations/nextjs.ts src/integrations/fastify.ts --format cjs,esm --dts --clean",
|
|
40
|
+
"build": "tsup src/index.ts src/integrations/express.ts src/integrations/nextjs.ts src/integrations/fastify.ts src/cli.ts --format cjs,esm --dts --clean",
|
|
37
41
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
38
42
|
"lint": "eslint src --ext .ts",
|
|
39
43
|
"test": "vitest run",
|
|
@@ -60,6 +64,7 @@
|
|
|
60
64
|
"url": "https://github.com/KodyDennon/statly-node/issues"
|
|
61
65
|
},
|
|
62
66
|
"devDependencies": {
|
|
67
|
+
"@types/node": "^20.0.0",
|
|
63
68
|
"tsup": "^8.0.0",
|
|
64
69
|
"typescript": "^5.0.0",
|
|
65
70
|
"vitest": "^1.0.0"
|
|
@@ -71,4 +76,4 @@
|
|
|
71
76
|
"publishConfig": {
|
|
72
77
|
"access": "public"
|
|
73
78
|
}
|
|
74
|
-
}
|
|
79
|
+
}
|