@interfere/elysia 0.1.0-canary.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/README.md +92 -0
- package/dist/cors.cjs +1 -0
- package/dist/cors.d.cts +23 -0
- package/dist/cors.d.mts +23 -0
- package/dist/cors.mjs +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +88 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.mjs +3 -0
- package/dist/internal/server-fault.cjs +1 -0
- package/dist/internal/server-fault.d.cts +9 -0
- package/dist/internal/server-fault.d.mts +9 -0
- package/dist/internal/server-fault.mjs +1 -0
- package/dist/package.cjs +1 -0
- package/dist/package.mjs +1 -0
- package/dist/trace-headers.cjs +1 -0
- package/dist/trace-headers.d.cts +17 -0
- package/dist/trace-headers.d.mts +17 -0
- package/dist/trace-headers.mjs +1 -0
- package/dist/version.cjs +1 -0
- package/dist/version.d.cts +4 -0
- package/dist/version.d.mts +4 -0
- package/dist/version.mjs +1 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @interfere/elysia
|
|
2
|
+
|
|
3
|
+
Elysia plugin for [Interfere](https://interfere.com). Creates a span for every request and reports server-fault errors (5xx and uncaught throws) with request context.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add @interfere/elysia
|
|
9
|
+
# or
|
|
10
|
+
npm install @interfere/elysia
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @interfere/elysia
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`elysia` and `@elysia/opentelemetry` are peer dependencies:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
bun add elysia @elysia/opentelemetry
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
Register the plugin once at the root of your app:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Elysia } from "elysia";
|
|
27
|
+
import { interfere } from "@interfere/elysia";
|
|
28
|
+
|
|
29
|
+
new Elysia()
|
|
30
|
+
.use(interfere({ serviceName: "checkout-api" }))
|
|
31
|
+
.get("/", () => "ok")
|
|
32
|
+
.listen(3000);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That's it. The plugin exports per-request spans to Interfere, reports 5xx and uncaught errors with request context, and installs process-level handlers for uncaught exceptions and unhandled rejections. Client faults (validation, not-found) still produce spans but are not reported as errors.
|
|
36
|
+
|
|
37
|
+
When `INTERFERE_PUBLIC_KEY` is unset the plugin is inert, so local runs without the SDK configured don't crash.
|
|
38
|
+
|
|
39
|
+
## Manual instrumentation
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { record, captureError, withSpan } from "@interfere/elysia";
|
|
43
|
+
|
|
44
|
+
app.get("/users", () =>
|
|
45
|
+
record("db.query", () => db.query("SELECT * FROM users"))
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `record(name, fn)` — wrap work in a child span that auto-closes and captures exceptions.
|
|
50
|
+
- `withSpan(name, fn)` — same, framework-agnostic.
|
|
51
|
+
- `captureError(error, request?)` / `captureMessage(message, request?)` — report an error from anywhere.
|
|
52
|
+
- `getCurrentSpan()` / `setAttributes(attrs)` — annotate the active span.
|
|
53
|
+
|
|
54
|
+
## Build & Deploy
|
|
55
|
+
|
|
56
|
+
Install [`@interfere/cli`](../cli) as a dev dependency and add a `postbuild` script so source maps are uploaded and the release is registered. Without this step, the collector rejects spans from production deployments.
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsc",
|
|
62
|
+
"postbuild": "interfere sourcemaps upload ./dist"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The CLI derives the release slug from the same commit SHA the SDK uses at runtime (`INTERFERE_SOURCE_ID`, `GITHUB_SHA`, `VERCEL_GIT_COMMIT_SHA`, or `git rev-parse HEAD`). Both must see the same SHA for the slugs to match.
|
|
68
|
+
|
|
69
|
+
## Environment Variables
|
|
70
|
+
|
|
71
|
+
| Variable | Where | Purpose |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `INTERFERE_PUBLIC_KEY` | Runtime | Routes spans to your surface (`interfere_pub_us_*` or `interfere_pub_eu_*`) |
|
|
74
|
+
| `INTERFERE_API_KEY` | CI / Build | Authenticates source map uploads and release registration (`interfere_secret_us_*` or `interfere_secret_eu_*`) |
|
|
75
|
+
| `INTERFERE_SOURCE_ID` | Both | Override the commit SHA used to derive the release slug. |
|
|
76
|
+
| `INTERFERE_DEBUG` | Runtime | Set to `1` to log lifecycle events and exporter results. Equivalent to `debug: true`. |
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
| Option | Default | Purpose |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `serviceName` | `OTEL_SERVICE_NAME` ?? `"elysia-app"` | The identity spans are grouped by. |
|
|
83
|
+
| `serviceNamespace` | — | Groups several services under one product surface. |
|
|
84
|
+
| `environment` | `INTERFERE_ENVIRONMENT` ?? `VERCEL_ENV` ?? `NODE_ENV` ?? `development` | Deployment environment label. |
|
|
85
|
+
| `captureUncaughtException` | `true` | Report and exit on `uncaughtException`. |
|
|
86
|
+
| `captureUnhandledRejection` | `true` | Report on `unhandledRejection`. |
|
|
87
|
+
| `debug` | `false` | Log lifecycle and exporter results to stdout. |
|
|
88
|
+
|
|
89
|
+
## Compatibility
|
|
90
|
+
|
|
91
|
+
- Elysia 1.4+
|
|
92
|
+
- Bun and Node.js
|
package/dist/cors.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_trace_headers=require("./trace-headers.cjs");let _elysia_cors=require("@elysia/cors");function interfereCors(config={}){let{allowedHeaders}=config;return typeof allowedHeaders==`string`||Array.isArray(allowedHeaders)?(0,_elysia_cors.cors)({...config,allowedHeaders:withTraceHeaders(allowedHeaders)}):(0,_elysia_cors.cors)(config)}function withTraceHeaders(allowedHeaders){let existing=Array.isArray(allowedHeaders)?allowedHeaders:allowedHeaders.split(`,`).map(header=>header.trim()).filter(Boolean),seen=new Set(existing.map(header=>header.toLowerCase()));return[...existing,...require_trace_headers.INTERFERE_TRACE_HEADERS.filter(header=>!seen.has(header))]}exports.interfereCors=interfereCors;
|
package/dist/cors.d.cts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CORSConfig, cors } from "@elysia/cors";
|
|
2
|
+
|
|
3
|
+
//#region src/cors.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Drop-in for `@elysia/cors`'s `cors()` that also permits the W3C
|
|
6
|
+
* `traceparent` / `tracestate` / `baggage` request headers, so a
|
|
7
|
+
* different-origin frontend's traces link to this server. Pass your normal
|
|
8
|
+
* CORS options — origins, methods, credentials — exactly as you would to
|
|
9
|
+
* `cors()`.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { interfereCors } from "@interfere/elysia/cors";
|
|
13
|
+
*
|
|
14
|
+
* new Elysia().use(interfereCors({ origin: "https://app.example.com" }));
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Only an *explicit* `allowedHeaders` allowlist (string or array) is extended;
|
|
18
|
+
* the permissive default (`true`) already reflects the trace headers, so it's
|
|
19
|
+
* passed through untouched — this never tightens your CORS policy.
|
|
20
|
+
*/
|
|
21
|
+
declare function interfereCors(config?: CORSConfig): ReturnType<typeof cors>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { interfereCors };
|
package/dist/cors.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CORSConfig, cors } from "@elysia/cors";
|
|
2
|
+
|
|
3
|
+
//#region src/cors.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Drop-in for `@elysia/cors`'s `cors()` that also permits the W3C
|
|
6
|
+
* `traceparent` / `tracestate` / `baggage` request headers, so a
|
|
7
|
+
* different-origin frontend's traces link to this server. Pass your normal
|
|
8
|
+
* CORS options — origins, methods, credentials — exactly as you would to
|
|
9
|
+
* `cors()`.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { interfereCors } from "@interfere/elysia/cors";
|
|
13
|
+
*
|
|
14
|
+
* new Elysia().use(interfereCors({ origin: "https://app.example.com" }));
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Only an *explicit* `allowedHeaders` allowlist (string or array) is extended;
|
|
18
|
+
* the permissive default (`true`) already reflects the trace headers, so it's
|
|
19
|
+
* passed through untouched — this never tightens your CORS policy.
|
|
20
|
+
*/
|
|
21
|
+
declare function interfereCors(config?: CORSConfig): ReturnType<typeof cors>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { interfereCors };
|
package/dist/cors.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{INTERFERE_TRACE_HEADERS}from"./trace-headers.mjs";import{cors}from"@elysia/cors";function interfereCors(config={}){let{allowedHeaders}=config;return cors(typeof allowedHeaders==`string`||Array.isArray(allowedHeaders)?{...config,allowedHeaders:withTraceHeaders(allowedHeaders)}:config)}function withTraceHeaders(allowedHeaders){let existing=Array.isArray(allowedHeaders)?allowedHeaders:allowedHeaders.split(`,`).map(header=>header.trim()).filter(Boolean),seen=new Set(existing.map(header=>header.toLowerCase()));return[...existing,...INTERFERE_TRACE_HEADERS.filter(header=>!seen.has(header))]}export{interfereCors};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_trace_headers=require("./trace-headers.cjs"),require_internal_server_fault=require("./internal/server-fault.cjs"),require_version=require("./version.cjs");let _interfere_node=require("@interfere/node"),_interfere_node_otel_config=require("@interfere/node/otel-config"),_elysia_opentelemetry=require("@elysia/opentelemetry"),_opentelemetry_api=require("@opentelemetry/api"),elysia=require("elysia");const PLUGIN_NAME=`@interfere/elysia`;function interfere(options={}){let debug=options.debug??process.env.INTERFERE_DEBUG===`1`,serviceName=options.serviceName??process.env.OTEL_SERVICE_NAME??`elysia-app`,config=(0,_interfere_node_otel_config.resolveOtelConfig)({serviceName,serviceNamespace:options.serviceNamespace,environment:options.environment,sdkName:PLUGIN_NAME,producerVersion:require_version.PRODUCER_VERSION,debug});if(!config)return console.warn(`[interfere] INTERFERE_PUBLIC_KEY not set; SDK disabled`),new elysia.Elysia({name:PLUGIN_NAME});config.releaseSlug||console.warn(`[interfere] No release slug resolved — the collector will reject spans. Add a postbuild script to your package.json:
|
|
2
|
+
"postbuild": "interfere sourcemaps upload ./dist"
|
|
3
|
+
and set INTERFERE_API_KEY in your CI environment. See https://interfere.com/docs/elysia#releases`);let flush=async()=>{await Promise.all(config.spanProcessors.map(p=>p.forceFlush()))};return(0,_interfere_node_otel_config.installGlobalHandlers)({captureUncaughtException:options.captureUncaughtException??!0,captureUnhandledRejection:options.captureUnhandledRejection??!0,flush}),debug&&(console.log(`[interfere] init: service=${serviceName}, env=${config.environment}, endpoint=${config.endpoint}`),config.releaseSlug&&console.log(`[interfere] release.slug=${config.releaseSlug}`)),new elysia.Elysia({name:PLUGIN_NAME,seed:options}).use((0,_elysia_opentelemetry.opentelemetry)({resource:config.resource,spanProcessors:config.spanProcessors})).onError({as:`global`},function({error,code,request,set}){require_internal_server_fault.isServerFault(code,set.status)&&(_opentelemetry_api.trace.getActiveSpan()?.setStatus({code:_opentelemetry_api.SpanStatusCode.ERROR}),(0,_interfere_node.captureError)(error,request))}).onStop(async function(){(0,_interfere_node_otel_config.uninstallGlobalHandlers)(),await flush()})}exports.INTERFERE_TRACE_HEADERS=require_trace_headers.INTERFERE_TRACE_HEADERS,Object.defineProperty(exports,"captureError",{enumerable:!0,get:function(){return _interfere_node.captureError}}),Object.defineProperty(exports,"captureMessage",{enumerable:!0,get:function(){return _interfere_node.captureMessage}}),Object.defineProperty(exports,"getCurrentSpan",{enumerable:!0,get:function(){return _elysia_opentelemetry.getCurrentSpan}}),exports.interfere=interfere,Object.defineProperty(exports,"record",{enumerable:!0,get:function(){return _elysia_opentelemetry.record}}),Object.defineProperty(exports,"setAttributes",{enumerable:!0,get:function(){return _elysia_opentelemetry.setAttributes}}),Object.defineProperty(exports,"withSpan",{enumerable:!0,get:function(){return _interfere_node.withSpan}});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { INTERFERE_TRACE_HEADERS } from "./trace-headers.cjs";
|
|
2
|
+
import { Elysia } from "elysia";
|
|
3
|
+
import { CaptureErrorContext, captureError, captureMessage, withSpan } from "@interfere/node";
|
|
4
|
+
import { getCurrentSpan, record, setAttributes } from "@elysia/opentelemetry";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
interface InterfereOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Install a `process.on("uncaughtException")` listener that captures the
|
|
10
|
+
* error, flushes spans, and exits the process. Default `true`. Disable to
|
|
11
|
+
* use your own handler.
|
|
12
|
+
*/
|
|
13
|
+
readonly captureUncaughtException?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Install a `process.on("unhandledRejection")` listener that captures the
|
|
16
|
+
* rejection and flushes spans. Default `true`. Disable to use your own
|
|
17
|
+
* handler.
|
|
18
|
+
*/
|
|
19
|
+
readonly captureUnhandledRejection?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Log lifecycle events and exporter results to `stdout`. Also activatable
|
|
22
|
+
* via `INTERFERE_DEBUG=1`. Default `false`.
|
|
23
|
+
*/
|
|
24
|
+
readonly debug?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Deployment environment label (e.g. `production`, `staging`, `preview`).
|
|
27
|
+
* Falls back to `INTERFERE_ENVIRONMENT`, then `VERCEL_ENV`, then `NODE_ENV`,
|
|
28
|
+
* then `development`.
|
|
29
|
+
*/
|
|
30
|
+
readonly environment?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Sets the OTel `service.name` resource attribute — the identity spans are
|
|
33
|
+
* grouped by in the dashboard. Defaults to `OTEL_SERVICE_NAME`, then
|
|
34
|
+
* `"elysia-app"`.
|
|
35
|
+
*/
|
|
36
|
+
readonly serviceName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Sets the OTel `service.namespace` resource attribute. Optional; groups
|
|
39
|
+
* several services under one logical product surface.
|
|
40
|
+
*/
|
|
41
|
+
readonly serviceNamespace?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Elysia plugin that instruments your server for Interfere: it stands up an
|
|
45
|
+
* OpenTelemetry tracer that exports per-request spans to the Interfere
|
|
46
|
+
* collector, and reports server-fault errors (5xx and uncaught throws) with
|
|
47
|
+
* request context. Client faults (validation, not-found) still produce spans
|
|
48
|
+
* but are not reported as errors.
|
|
49
|
+
*
|
|
50
|
+
* Register once at the root of your app:
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* new Elysia().use(interfere({ serviceName: "checkout-api" })).listen(3000);
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* No-ops when `INTERFERE_PUBLIC_KEY` is unset so local runs without the SDK
|
|
57
|
+
* configured don't crash.
|
|
58
|
+
*/
|
|
59
|
+
declare function interfere(options?: InterfereOptions): Elysia<"", {
|
|
60
|
+
decorator: {};
|
|
61
|
+
store: {};
|
|
62
|
+
derive: {};
|
|
63
|
+
resolve: {};
|
|
64
|
+
}, {
|
|
65
|
+
typebox: {};
|
|
66
|
+
error: {};
|
|
67
|
+
}, {
|
|
68
|
+
schema: {};
|
|
69
|
+
standaloneSchema: {};
|
|
70
|
+
macro: {};
|
|
71
|
+
macroFn: {};
|
|
72
|
+
parser: {};
|
|
73
|
+
response: {};
|
|
74
|
+
}, {}, {
|
|
75
|
+
derive: {};
|
|
76
|
+
resolve: {};
|
|
77
|
+
schema: {};
|
|
78
|
+
standaloneSchema: {};
|
|
79
|
+
response: {};
|
|
80
|
+
}, {
|
|
81
|
+
derive: {};
|
|
82
|
+
resolve: {};
|
|
83
|
+
schema: {};
|
|
84
|
+
standaloneSchema: {};
|
|
85
|
+
response: {};
|
|
86
|
+
}>;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { type CaptureErrorContext, INTERFERE_TRACE_HEADERS, InterfereOptions, captureError, captureMessage, getCurrentSpan, interfere, record, setAttributes, withSpan };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { INTERFERE_TRACE_HEADERS } from "./trace-headers.mjs";
|
|
2
|
+
import { CaptureErrorContext, captureError, captureMessage, withSpan } from "@interfere/node";
|
|
3
|
+
import { getCurrentSpan, record, setAttributes } from "@elysia/opentelemetry";
|
|
4
|
+
import { Elysia } from "elysia";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
interface InterfereOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Install a `process.on("uncaughtException")` listener that captures the
|
|
10
|
+
* error, flushes spans, and exits the process. Default `true`. Disable to
|
|
11
|
+
* use your own handler.
|
|
12
|
+
*/
|
|
13
|
+
readonly captureUncaughtException?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Install a `process.on("unhandledRejection")` listener that captures the
|
|
16
|
+
* rejection and flushes spans. Default `true`. Disable to use your own
|
|
17
|
+
* handler.
|
|
18
|
+
*/
|
|
19
|
+
readonly captureUnhandledRejection?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Log lifecycle events and exporter results to `stdout`. Also activatable
|
|
22
|
+
* via `INTERFERE_DEBUG=1`. Default `false`.
|
|
23
|
+
*/
|
|
24
|
+
readonly debug?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Deployment environment label (e.g. `production`, `staging`, `preview`).
|
|
27
|
+
* Falls back to `INTERFERE_ENVIRONMENT`, then `VERCEL_ENV`, then `NODE_ENV`,
|
|
28
|
+
* then `development`.
|
|
29
|
+
*/
|
|
30
|
+
readonly environment?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Sets the OTel `service.name` resource attribute — the identity spans are
|
|
33
|
+
* grouped by in the dashboard. Defaults to `OTEL_SERVICE_NAME`, then
|
|
34
|
+
* `"elysia-app"`.
|
|
35
|
+
*/
|
|
36
|
+
readonly serviceName?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Sets the OTel `service.namespace` resource attribute. Optional; groups
|
|
39
|
+
* several services under one logical product surface.
|
|
40
|
+
*/
|
|
41
|
+
readonly serviceNamespace?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Elysia plugin that instruments your server for Interfere: it stands up an
|
|
45
|
+
* OpenTelemetry tracer that exports per-request spans to the Interfere
|
|
46
|
+
* collector, and reports server-fault errors (5xx and uncaught throws) with
|
|
47
|
+
* request context. Client faults (validation, not-found) still produce spans
|
|
48
|
+
* but are not reported as errors.
|
|
49
|
+
*
|
|
50
|
+
* Register once at the root of your app:
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* new Elysia().use(interfere({ serviceName: "checkout-api" })).listen(3000);
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* No-ops when `INTERFERE_PUBLIC_KEY` is unset so local runs without the SDK
|
|
57
|
+
* configured don't crash.
|
|
58
|
+
*/
|
|
59
|
+
declare function interfere(options?: InterfereOptions): Elysia<"", {
|
|
60
|
+
decorator: {};
|
|
61
|
+
store: {};
|
|
62
|
+
derive: {};
|
|
63
|
+
resolve: {};
|
|
64
|
+
}, {
|
|
65
|
+
typebox: {};
|
|
66
|
+
error: {};
|
|
67
|
+
}, {
|
|
68
|
+
schema: {};
|
|
69
|
+
standaloneSchema: {};
|
|
70
|
+
macro: {};
|
|
71
|
+
macroFn: {};
|
|
72
|
+
parser: {};
|
|
73
|
+
response: {};
|
|
74
|
+
}, {}, {
|
|
75
|
+
derive: {};
|
|
76
|
+
resolve: {};
|
|
77
|
+
schema: {};
|
|
78
|
+
standaloneSchema: {};
|
|
79
|
+
response: {};
|
|
80
|
+
}, {
|
|
81
|
+
derive: {};
|
|
82
|
+
resolve: {};
|
|
83
|
+
schema: {};
|
|
84
|
+
standaloneSchema: {};
|
|
85
|
+
response: {};
|
|
86
|
+
}>;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { type CaptureErrorContext, INTERFERE_TRACE_HEADERS, InterfereOptions, captureError, captureMessage, getCurrentSpan, interfere, record, setAttributes, withSpan };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{INTERFERE_TRACE_HEADERS}from"./trace-headers.mjs";import{isServerFault}from"./internal/server-fault.mjs";import{PRODUCER_VERSION}from"./version.mjs";import{captureError,captureError as captureError$1,captureMessage,withSpan}from"@interfere/node";import{installGlobalHandlers,resolveOtelConfig,uninstallGlobalHandlers}from"@interfere/node/otel-config";import{getCurrentSpan,opentelemetry,record,setAttributes}from"@elysia/opentelemetry";import{SpanStatusCode,trace}from"@opentelemetry/api";import{Elysia}from"elysia";const PLUGIN_NAME=`@interfere/elysia`;function interfere(options={}){let debug=options.debug??process.env.INTERFERE_DEBUG===`1`,serviceName=options.serviceName??process.env.OTEL_SERVICE_NAME??`elysia-app`,config=resolveOtelConfig({serviceName,serviceNamespace:options.serviceNamespace,environment:options.environment,sdkName:PLUGIN_NAME,producerVersion:PRODUCER_VERSION,debug});if(!config)return console.warn(`[interfere] INTERFERE_PUBLIC_KEY not set; SDK disabled`),new Elysia({name:PLUGIN_NAME});config.releaseSlug||console.warn(`[interfere] No release slug resolved — the collector will reject spans. Add a postbuild script to your package.json:
|
|
2
|
+
"postbuild": "interfere sourcemaps upload ./dist"
|
|
3
|
+
and set INTERFERE_API_KEY in your CI environment. See https://interfere.com/docs/elysia#releases`);let flush=async()=>{await Promise.all(config.spanProcessors.map(p=>p.forceFlush()))};return installGlobalHandlers({captureUncaughtException:options.captureUncaughtException??!0,captureUnhandledRejection:options.captureUnhandledRejection??!0,flush}),debug&&(console.log(`[interfere] init: service=${serviceName}, env=${config.environment}, endpoint=${config.endpoint}`),config.releaseSlug&&console.log(`[interfere] release.slug=${config.releaseSlug}`)),new Elysia({name:PLUGIN_NAME,seed:options}).use(opentelemetry({resource:config.resource,spanProcessors:config.spanProcessors})).onError({as:`global`},function({error,code,request,set}){isServerFault(code,set.status)&&(trace.getActiveSpan()?.setStatus({code:SpanStatusCode.ERROR}),captureError$1(error,request))}).onStop(async function(){uninstallGlobalHandlers(),await flush()})}export{INTERFERE_TRACE_HEADERS,captureError,captureMessage,getCurrentSpan,interfere,record,setAttributes,withSpan};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const CLIENT_FAULT_CODES=new Set([`VALIDATION`,`NOT_FOUND`,`PARSE`,`INVALID_COOKIE_SIGNATURE`,`INVALID_FILE_TYPE`]);function isServerFault(code,status){if(typeof code==`string`&&CLIENT_FAULT_CODES.has(code))return!1;let resolved=resolveStatus(code,status);return typeof resolved==`number`?resolved>=500:!0}function resolveStatus(code,status){if(typeof status==`number`)return status;if(typeof code==`number`)return code}exports.isServerFault=isServerFault;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/internal/server-fault.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Decides whether an Elysia error represents a server fault worth reporting.
|
|
4
|
+
* Skips the framework's client-fault codes and any error whose response
|
|
5
|
+
* status is a 4xx; unknown throws with no status default to reportable.
|
|
6
|
+
*/
|
|
7
|
+
declare function isServerFault(code: string | number, status: number | string | undefined): boolean;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { isServerFault };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/internal/server-fault.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Decides whether an Elysia error represents a server fault worth reporting.
|
|
4
|
+
* Skips the framework's client-fault codes and any error whose response
|
|
5
|
+
* status is a 4xx; unknown throws with no status default to reportable.
|
|
6
|
+
*/
|
|
7
|
+
declare function isServerFault(code: string | number, status: number | string | undefined): boolean;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { isServerFault };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const CLIENT_FAULT_CODES=new Set([`VALIDATION`,`NOT_FOUND`,`PARSE`,`INVALID_COOKIE_SIGNATURE`,`INVALID_FILE_TYPE`]);function isServerFault(code,status){if(typeof code==`string`&&CLIENT_FAULT_CODES.has(code))return!1;let resolved=resolveStatus(code,status);return typeof resolved==`number`?resolved>=500:!0}function resolveStatus(code,status){if(typeof status==`number`)return status;if(typeof code==`number`)return code}export{isServerFault};
|
package/dist/package.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,"name",{enumerable:!0,get:function(){return`@interfere/elysia`}}),Object.defineProperty(exports,"version",{enumerable:!0,get:function(){return`0.1.0-canary.1`}});
|
package/dist/package.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var name=`@interfere/elysia`,version=`0.1.0-canary.1`;export{name,version};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const INTERFERE_TRACE_HEADERS=[`traceparent`,`tracestate`,`baggage`];exports.INTERFERE_TRACE_HEADERS=INTERFERE_TRACE_HEADERS;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/trace-headers.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* W3C trace-context and baggage request headers the browser SDK injects on
|
|
4
|
+
* cross-origin requests. Spread these into your CORS `allowedHeaders` allowlist
|
|
5
|
+
* so preflight requests from a different-origin frontend are accepted and
|
|
6
|
+
* front-to-back traces stay linked:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* cors({ allowedHeaders: ["content-type", ...INTERFERE_TRACE_HEADERS] })
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Unnecessary when the frontend is same-origin, or when your CORS layer
|
|
13
|
+
* reflects requested headers (the `@elysia/cors` default).
|
|
14
|
+
*/
|
|
15
|
+
declare const INTERFERE_TRACE_HEADERS: readonly ["traceparent", "tracestate", "baggage"];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { INTERFERE_TRACE_HEADERS };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/trace-headers.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* W3C trace-context and baggage request headers the browser SDK injects on
|
|
4
|
+
* cross-origin requests. Spread these into your CORS `allowedHeaders` allowlist
|
|
5
|
+
* so preflight requests from a different-origin frontend are accepted and
|
|
6
|
+
* front-to-back traces stay linked:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* cors({ allowedHeaders: ["content-type", ...INTERFERE_TRACE_HEADERS] })
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Unnecessary when the frontend is same-origin, or when your CORS layer
|
|
13
|
+
* reflects requested headers (the `@elysia/cors` default).
|
|
14
|
+
*/
|
|
15
|
+
declare const INTERFERE_TRACE_HEADERS: readonly ["traceparent", "tracestate", "baggage"];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { INTERFERE_TRACE_HEADERS };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const INTERFERE_TRACE_HEADERS=[`traceparent`,`tracestate`,`baggage`];export{INTERFERE_TRACE_HEADERS};
|
package/dist/version.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_package=require("./package.cjs"),PRODUCER_VERSION=`${require_package.name}@${require_package.version}`;exports.PRODUCER_VERSION=PRODUCER_VERSION;
|
package/dist/version.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{name,version}from"./package.mjs";const PRODUCER_VERSION=`${name}@${version}`;export{PRODUCER_VERSION};
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@interfere/elysia",
|
|
3
|
+
"version": "0.1.0-canary.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Elysia plugin for Interfere. Per-request OTel spans and error capture for any Elysia server.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"observability",
|
|
8
|
+
"typescript",
|
|
9
|
+
"elysia",
|
|
10
|
+
"bun",
|
|
11
|
+
"error-tracking",
|
|
12
|
+
"opentelemetry"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://interfere.com",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "mailto:support@interfere.com"
|
|
17
|
+
},
|
|
18
|
+
"author": "Interfere <support@interfere.com> (https://interfere.com)",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://interfere.ghe.com/interfere/interfere.git",
|
|
22
|
+
"directory": "src/packages/public/elysia"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist/**/*.cjs",
|
|
26
|
+
"dist/**/*.mjs",
|
|
27
|
+
"dist/**/*.d.cts",
|
|
28
|
+
"dist/**/*.d.mts",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "./dist/index.cjs",
|
|
33
|
+
"module": "./dist/index.mjs",
|
|
34
|
+
"types": "./dist/index.d.cts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"monorepo": "./src/index.ts",
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/index.d.mts",
|
|
40
|
+
"default": "./dist/index.mjs"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/index.d.cts",
|
|
44
|
+
"default": "./dist/index.cjs"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"./cors": {
|
|
48
|
+
"monorepo": "./src/cors.ts",
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/cors.d.mts",
|
|
51
|
+
"default": "./dist/cors.mjs"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/cors.d.cts",
|
|
55
|
+
"default": "./dist/cors.cjs"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"sideEffects": false,
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsdown",
|
|
65
|
+
"test": "vitest run --passWithNoTests",
|
|
66
|
+
"typecheck": "tsc --noEmit"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@interfere/node": "^0.1.0-canary.10",
|
|
70
|
+
"@opentelemetry/api": "^1.9.1"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"@elysia/cors": "^1.4.1",
|
|
74
|
+
"@elysia/opentelemetry": "^1.4.11",
|
|
75
|
+
"elysia": "^1.4.29"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"@elysia/cors": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"@elysia/cors": "^1.4.1",
|
|
84
|
+
"@elysia/opentelemetry": "^1.4.11",
|
|
85
|
+
"@interfere/test-utils": "^9.0.1-canary.1",
|
|
86
|
+
"@interfere/typescript-config": "^9.0.1-canary.1",
|
|
87
|
+
"@types/node": "^26.1.0",
|
|
88
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
89
|
+
"elysia": "^1.4.29",
|
|
90
|
+
"tsdown": "^0.22.2",
|
|
91
|
+
"typescript": "^6.0.3",
|
|
92
|
+
"vitest": "4.1.9"
|
|
93
|
+
}
|
|
94
|
+
}
|