@bywaretech/bysentinel-aws-lambda 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.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +184 -0
  3. package/dist/capture.d.ts +16 -0
  4. package/dist/capture.d.ts.map +1 -0
  5. package/dist/capture.js +77 -0
  6. package/dist/capture.js.map +1 -0
  7. package/dist/context.d.ts +27 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +57 -0
  10. package/dist/context.js.map +1 -0
  11. package/dist/dedupe.d.ts +3 -0
  12. package/dist/dedupe.d.ts.map +1 -0
  13. package/dist/dedupe.js +25 -0
  14. package/dist/dedupe.js.map +1 -0
  15. package/dist/event.d.ts +21 -0
  16. package/dist/event.d.ts.map +1 -0
  17. package/dist/event.js +72 -0
  18. package/dist/event.js.map +1 -0
  19. package/dist/git.d.ts +8 -0
  20. package/dist/git.d.ts.map +1 -0
  21. package/dist/git.js +31 -0
  22. package/dist/git.js.map +1 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +6 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/options.d.ts +4 -0
  28. package/dist/options.d.ts.map +1 -0
  29. package/dist/options.js +48 -0
  30. package/dist/options.js.map +1 -0
  31. package/dist/request.d.ts +12 -0
  32. package/dist/request.d.ts.map +1 -0
  33. package/dist/request.js +67 -0
  34. package/dist/request.js.map +1 -0
  35. package/dist/runtime.d.ts +22 -0
  36. package/dist/runtime.d.ts.map +1 -0
  37. package/dist/runtime.js +29 -0
  38. package/dist/runtime.js.map +1 -0
  39. package/dist/scope.d.ts +14 -0
  40. package/dist/scope.d.ts.map +1 -0
  41. package/dist/scope.js +9 -0
  42. package/dist/scope.js.map +1 -0
  43. package/dist/transport.d.ts +19 -0
  44. package/dist/transport.d.ts.map +1 -0
  45. package/dist/transport.js +101 -0
  46. package/dist/transport.js.map +1 -0
  47. package/dist/types.d.ts +62 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/withBySentinel.d.ts +13 -0
  52. package/dist/withBySentinel.d.ts.map +1 -0
  53. package/dist/withBySentinel.js +78 -0
  54. package/dist/withBySentinel.js.map +1 -0
  55. package/package.json +60 -0
package/dist/event.js ADDED
@@ -0,0 +1,72 @@
1
+ import { detectSecuritySignals, newEventId, nowIso, redact, } from "@bywaretech/bysentinel-core";
2
+ import { extractLambdaContext } from "./context.js";
3
+ import { extractRequest } from "./request.js";
4
+ const RUNTIME_VERSION = process.version;
5
+ export function errorToInfo(error, includeStack) {
6
+ if (error instanceof Error) {
7
+ return {
8
+ type: error.name || "Error",
9
+ message: error.message,
10
+ stack: includeStack ? error.stack : undefined,
11
+ };
12
+ }
13
+ return { type: "NonError", message: String(error) };
14
+ }
15
+ /**
16
+ * Assemble a fully-sanitized BySentinelEvent. Security signals are detected on
17
+ * the RAW request (before redaction) so patterns are visible, then the request
18
+ * and custom context are redacted. `sanitized` is always true on the result.
19
+ */
20
+ export function buildEvent(params) {
21
+ const { options, coldStart } = params;
22
+ const security = options.security;
23
+ const rawRequest = extractRequest(params.lambdaEvent, options);
24
+ const error = params.error !== undefined ? errorToInfo(params.error, options.capture.stackTrace) : undefined;
25
+ // Detect signals on raw data before it is redacted away.
26
+ const securitySignals = detectSecuritySignals({ request: rawRequest, error });
27
+ // Second: redact the request + custom context.
28
+ const request = security.sanitize && rawRequest ? redact(rawRequest, security) : rawRequest;
29
+ const customContext = params.customContext && security.sanitize
30
+ ? redact(params.customContext, security)
31
+ : params.customContext;
32
+ const lambda = extractLambdaContext(params.lambdaContext, coldStart);
33
+ const performance = params.performance;
34
+ // Timeline step metadata is developer-supplied and may contain secrets/PII.
35
+ const timeline = params.timeline && security.sanitize ? redact(params.timeline, security) : params.timeline;
36
+ return {
37
+ id: newEventId(),
38
+ timestamp: nowIso(),
39
+ project: options.project,
40
+ environment: options.environment,
41
+ release: options.release,
42
+ runtime: { provider: "aws", service: "lambda", language: "nodejs", version: RUNTIME_VERSION },
43
+ lambda,
44
+ git: options.git,
45
+ error,
46
+ performance,
47
+ timeline,
48
+ request,
49
+ customContext: mergeAiMeta(customContext, options),
50
+ securitySignals: securitySignals.length ? securitySignals : undefined,
51
+ sanitized: true,
52
+ };
53
+ }
54
+ /** Forward the SDK's AI preferences as metadata the collector/worker can honor. */
55
+ function mergeAiMeta(customContext, options) {
56
+ if (!options.ai)
57
+ return customContext;
58
+ return {
59
+ ...customContext,
60
+ __bysentinel: {
61
+ ai: {
62
+ enabled: options.ai.enabled,
63
+ mode: options.ai.mode,
64
+ provider: options.ai.provider,
65
+ model: options.ai.model,
66
+ sendBodyToAI: options.ai.sendBodyToAI ?? false,
67
+ sendHeadersToAI: options.ai.sendHeadersToAI ?? false,
68
+ },
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { GitContext } from "@bywaretech/bysentinel-core";
2
+ /**
3
+ * Resolve Git/release correlation metadata (roadmap #4). Explicit `overrides`
4
+ * win; otherwise we read BySentinel vars first, then common CI providers
5
+ * (GitHub Actions, GitLab CI, Vercel). Returns undefined when nothing is known.
6
+ */
7
+ export declare function resolveGitContext(overrides?: GitContext): GitContext | undefined;
8
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAU9D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAsBhF"}
package/dist/git.js ADDED
@@ -0,0 +1,31 @@
1
+ const pick = (...keys) => {
2
+ for (const k of keys) {
3
+ const v = process.env[k];
4
+ if (v && v.length > 0)
5
+ return v;
6
+ }
7
+ return undefined;
8
+ };
9
+ /**
10
+ * Resolve Git/release correlation metadata (roadmap #4). Explicit `overrides`
11
+ * win; otherwise we read BySentinel vars first, then common CI providers
12
+ * (GitHub Actions, GitLab CI, Vercel). Returns undefined when nothing is known.
13
+ */
14
+ export function resolveGitContext(overrides) {
15
+ const repoFromGithub = pick("GITHUB_SERVER_URL") && pick("GITHUB_REPOSITORY")
16
+ ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`
17
+ : undefined;
18
+ const git = {
19
+ commitSha: overrides?.commitSha ??
20
+ pick("BYSENTINEL_GIT_SHA", "GIT_SHA", "GITHUB_SHA", "CI_COMMIT_SHA", "VERCEL_GIT_COMMIT_SHA"),
21
+ branch: overrides?.branch ??
22
+ pick("BYSENTINEL_GIT_BRANCH", "GITHUB_REF_NAME", "CI_COMMIT_BRANCH", "VERCEL_GIT_COMMIT_REF"),
23
+ version: overrides?.version ?? pick("BYSENTINEL_VERSION", "npm_package_version"),
24
+ release: overrides?.release ?? pick("BYSENTINEL_RELEASE"),
25
+ buildTimestamp: overrides?.buildTimestamp ?? pick("BYSENTINEL_BUILD_TIME"),
26
+ repositoryUrl: overrides?.repositoryUrl ?? pick("BYSENTINEL_GIT_REPO_URL", "CI_PROJECT_URL") ?? repoFromGithub,
27
+ };
28
+ // Drop the object entirely if every field is empty.
29
+ return Object.values(git).some((v) => v != null) ? git : undefined;
30
+ }
31
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAEA,MAAM,IAAI,GAAG,CAAC,GAAG,IAAc,EAAsB,EAAE;IACrD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAsB;IACtD,MAAM,cAAc,GAClB,IAAI,CAAC,mBAAmB,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC;QACpD,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE;QACrE,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,GAAG,GAAe;QACtB,SAAS,EACP,SAAS,EAAE,SAAS;YACpB,IAAI,CAAC,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,uBAAuB,CAAC;QAC/F,MAAM,EACJ,SAAS,EAAE,MAAM;YACjB,IAAI,CAAC,uBAAuB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,uBAAuB,CAAC;QAC/F,OAAO,EAAE,SAAS,EAAE,OAAO,IAAI,IAAI,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;QAChF,OAAO,EAAE,SAAS,EAAE,OAAO,IAAI,IAAI,CAAC,oBAAoB,CAAC;QACzD,cAAc,EAAE,SAAS,EAAE,cAAc,IAAI,IAAI,CAAC,uBAAuB,CAAC;QAC1E,aAAa,EACX,SAAS,EAAE,aAAa,IAAI,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,IAAI,cAAc;KAClG,CAAC;IAEF,oDAAoD;IACpD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACrE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { withBySentinel, type LambdaHandler } from "./withBySentinel.js";
2
+ export { captureException, captureMessage } from "./capture.js";
3
+ export { startRuntime, BySentinel } from "./runtime.js";
4
+ export { resolveOptions } from "./options.js";
5
+ export { resolveGitContext } from "./git.js";
6
+ export type { AwsLambdaContextLike } from "./context.js";
7
+ export type { BySentinelOptions, ResolvedOptions, DeliveryOptions } from "./types.js";
8
+ export type { BySentinelEvent, CaptureContext, CaptureOptions, SecurityOptions, AIOptions, GitContext, Timeline, ExecutionTimeline, TimelineStep, } from "@bywaretech/bysentinel-core";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGtF,YAAY,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,eAAe,EACf,SAAS,EACT,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,YAAY,GACb,MAAM,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { withBySentinel } from "./withBySentinel.js";
2
+ export { captureException, captureMessage } from "./capture.js";
3
+ export { startRuntime, BySentinel } from "./runtime.js";
4
+ export { resolveOptions } from "./options.js";
5
+ export { resolveGitContext } from "./git.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsB,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ResolvedOptions, BySentinelOptions } from "./types.js";
2
+ /** Merge user options with environment variables and secure defaults. */
3
+ export declare function resolveOptions(options: BySentinelOptions): ResolvedOptions;
4
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAerE,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,CAmC1E"}
@@ -0,0 +1,48 @@
1
+ import { DEFAULT_CAPTURE_OPTIONS, DEFAULT_SECURITY_OPTIONS } from "@bywaretech/bysentinel-core";
2
+ import { resolveGitContext } from "./git.js";
3
+ const env = (key) => {
4
+ const v = process.env[key];
5
+ return v && v.length > 0 ? v : undefined;
6
+ };
7
+ const splitList = (value) => value
8
+ ? value
9
+ .split(",")
10
+ .map((item) => item.trim())
11
+ .filter(Boolean)
12
+ : [];
13
+ /** Merge user options with environment variables and secure defaults. */
14
+ export function resolveOptions(options) {
15
+ const security = { ...DEFAULT_SECURITY_OPTIONS, ...options.security };
16
+ const capture = { ...DEFAULT_CAPTURE_OPTIONS, ...options.capture };
17
+ // Strict mode overrides capture flags: bodies/headers never leave the process.
18
+ if (security.strictMode) {
19
+ capture.requestBody = false;
20
+ capture.headers = false;
21
+ }
22
+ return {
23
+ project: options.project ?? env("BYSENTINEL_PROJECT") ?? "unknown",
24
+ environment: options.environment ?? env("BYSENTINEL_ENVIRONMENT") ?? "development",
25
+ release: options.release ?? env("BYSENTINEL_RELEASE"),
26
+ collectorUrl: options.collectorUrl ?? env("BYSENTINEL_COLLECTOR_URL"),
27
+ apiKey: options.apiKey ?? env("BYSENTINEL_API_KEY"),
28
+ capture,
29
+ security,
30
+ ai: options.ai,
31
+ git: resolveGitContext(options.git),
32
+ delivery: {
33
+ mode: options.delivery?.mode ?? "background",
34
+ timeoutMs: options.delivery?.timeoutMs ?? 2000,
35
+ retries: options.delivery?.retries ?? 0,
36
+ maxEventBytes: options.delivery?.maxEventBytes ?? 262_144,
37
+ endpointPath: options.delivery?.endpointPath ?? "/v1/events",
38
+ webhooks: options.delivery?.webhooks ?? splitList(env("BYSENTINEL_DIRECT_WEBHOOK_URLS")),
39
+ },
40
+ onError: options.onError ??
41
+ ((error) => {
42
+ if (options.debug)
43
+ console.debug("[bysentinel] internal error:", error);
44
+ }),
45
+ debug: options.debug ?? false,
46
+ };
47
+ }
48
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAChG,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAsB,EAAE;IAC9C,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,KAAyB,EAAY,EAAE,CACxD,KAAK;IACH,CAAC,CAAC,KAAK;SACF,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC;IACpB,CAAC,CAAC,EAAE,CAAC;AAET,yEAAyE;AACzE,MAAM,UAAU,cAAc,CAAC,OAA0B;IACvD,MAAM,QAAQ,GAAG,EAAE,GAAG,wBAAwB,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACtE,MAAM,OAAO,GAAG,EAAE,GAAG,uBAAuB,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAEnE,+EAA+E;IAC/E,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;QAC5B,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,oBAAoB,CAAC,IAAI,SAAS;QAClE,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC,wBAAwB,CAAC,IAAI,aAAa;QAClF,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,oBAAoB,CAAC;QACrD,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,0BAA0B,CAAC;QACrE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,oBAAoB,CAAC;QACnD,OAAO;QACP,QAAQ;QACR,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,GAAG,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC;QACnC,QAAQ,EAAE;YACR,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,YAAY;YAC5C,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,SAAS,IAAI,IAAI;YAC9C,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,CAAC;YACvC,aAAa,EAAE,OAAO,CAAC,QAAQ,EAAE,aAAa,IAAI,OAAO;YACzD,YAAY,EAAE,OAAO,CAAC,QAAQ,EAAE,YAAY,IAAI,YAAY;YAC5D,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;SACzF;QACD,OAAO,EACL,OAAO,CAAC,OAAO;YACf,CAAC,CAAC,KAAK,EAAE,EAAE;gBACT,IAAI,OAAO,CAAC,KAAK;oBAAE,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YAC1E,CAAC,CAAC;QACJ,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;KAC9B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { RequestInfo } from "@bywaretech/bysentinel-core";
2
+ import type { ResolvedOptions } from "./types.js";
3
+ /**
4
+ * Best-effort extraction of HTTP request context from common Lambda event
5
+ * shapes: API Gateway REST (v1), HTTP API (v2), and ALB. Non-HTTP events yield
6
+ * an empty object.
7
+ *
8
+ * Capture flags decide which parts are collected; strict mode already forced
9
+ * `requestBody`/`headers` to false in `resolveOptions`.
10
+ */
11
+ export declare function extractRequest(event: unknown, options: ResolvedOptions): RequestInfo | undefined;
12
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,GAAG,WAAW,GAAG,SAAS,CAiChG"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Best-effort extraction of HTTP request context from common Lambda event
3
+ * shapes: API Gateway REST (v1), HTTP API (v2), and ALB. Non-HTTP events yield
4
+ * an empty object.
5
+ *
6
+ * Capture flags decide which parts are collected; strict mode already forced
7
+ * `requestBody`/`headers` to false in `resolveOptions`.
8
+ */
9
+ export function extractRequest(event, options) {
10
+ if (!event || typeof event !== "object")
11
+ return undefined;
12
+ const e = event;
13
+ const method = e.httpMethod ?? e.requestContext?.http?.method ?? e.requestContext?.httpMethod;
14
+ const path = e.path ?? e.rawPath ?? e.requestContext?.http?.path;
15
+ const hasHeaders = e.headers && typeof e.headers === "object";
16
+ const hasBody = e.body != null;
17
+ // If it doesn't look like an HTTP-ish event, skip request context entirely.
18
+ // Some local Lambda invocations provide only `{ body }`, so keep body-only
19
+ // events when explicit body capture is enabled.
20
+ if (!method && !path && !hasHeaders && !(options.capture.requestBody && hasBody)) {
21
+ return undefined;
22
+ }
23
+ const info = {};
24
+ if (method)
25
+ info.method = method;
26
+ if (path)
27
+ info.path = path;
28
+ if (options.capture.query) {
29
+ info.query = normalizeQuery(e);
30
+ }
31
+ if (options.capture.headers && hasHeaders) {
32
+ info.headers = { ...e.headers };
33
+ }
34
+ if (options.capture.requestBody && hasBody) {
35
+ info.body = parseBody(e.body, e.isBase64Encoded);
36
+ }
37
+ return info;
38
+ }
39
+ function normalizeQuery(e) {
40
+ if (e.multiValueQueryStringParameters && typeof e.multiValueQueryStringParameters === "object") {
41
+ return { ...e.multiValueQueryStringParameters };
42
+ }
43
+ if (e.queryStringParameters && typeof e.queryStringParameters === "object") {
44
+ return { ...e.queryStringParameters };
45
+ }
46
+ return undefined;
47
+ }
48
+ function parseBody(body, isBase64) {
49
+ if (typeof body !== "string")
50
+ return body;
51
+ let raw = body;
52
+ if (isBase64) {
53
+ try {
54
+ raw = Buffer.from(body, "base64").toString("utf8");
55
+ }
56
+ catch {
57
+ return "[unparseable-base64-body]";
58
+ }
59
+ }
60
+ try {
61
+ return JSON.parse(raw);
62
+ }
63
+ catch {
64
+ return raw;
65
+ }
66
+ }
67
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,OAAwB;IACrE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC1D,MAAM,CAAC,GAAG,KAA4B,CAAC;IAEvC,MAAM,MAAM,GACV,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,cAAc,EAAE,UAAU,CAAC;IACjF,MAAM,IAAI,GAAuB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC;IAErF,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC;IAC9D,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC;IAE/B,4EAA4E;IAC5E,2EAA2E;IAC3E,gDAAgD;IAChD,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,EAAE,CAAC;QACjF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,IAAI,MAAM;QAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACjC,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAE3B,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,UAAU,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,CAAsB;IAC5C,IAAI,CAAC,CAAC,+BAA+B,IAAI,OAAO,CAAC,CAAC,+BAA+B,KAAK,QAAQ,EAAE,CAAC;QAC/F,OAAO,EAAE,GAAG,CAAC,CAAC,+BAA+B,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,CAAC,qBAAqB,IAAI,OAAO,CAAC,CAAC,qBAAqB,KAAK,QAAQ,EAAE,CAAC;QAC3E,OAAO,EAAE,GAAG,CAAC,CAAC,qBAAqB,EAAE,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,SAAS,CAAC,IAAa,EAAE,QAAkB;IAClD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,2BAA2B,CAAC;QACrC,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Timeline } from "@bywaretech/bysentinel-core";
2
+ /**
3
+ * Start an execution timeline for the current invocation (roadmap #1).
4
+ *
5
+ * ```ts
6
+ * const runtime = BySentinel.start();
7
+ * runtime.step("Validate Request");
8
+ * runtime.step("Create Payment");
9
+ * runtime.finish();
10
+ * ```
11
+ *
12
+ * When called inside a `withBySentinel` handler the timeline is attached to the
13
+ * active scope, so it is automatically included in any captured event (and, on
14
+ * an unhandled error, finalized as aborted). Outside a handler it still works as
15
+ * a standalone timer.
16
+ */
17
+ export declare function startRuntime(): Timeline;
18
+ /** Spec-facing namespace: `BySentinel.start()`. */
19
+ export declare const BySentinel: {
20
+ start: typeof startRuntime;
21
+ };
22
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAGvD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,IAAI,QAAQ,CAKvC;AAED,mDAAmD;AACnD,eAAO,MAAM,UAAU;;CAEtB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Timeline } from "@bywaretech/bysentinel-core";
2
+ import { currentScope } from "./scope.js";
3
+ /**
4
+ * Start an execution timeline for the current invocation (roadmap #1).
5
+ *
6
+ * ```ts
7
+ * const runtime = BySentinel.start();
8
+ * runtime.step("Validate Request");
9
+ * runtime.step("Create Payment");
10
+ * runtime.finish();
11
+ * ```
12
+ *
13
+ * When called inside a `withBySentinel` handler the timeline is attached to the
14
+ * active scope, so it is automatically included in any captured event (and, on
15
+ * an unhandled error, finalized as aborted). Outside a handler it still works as
16
+ * a standalone timer.
17
+ */
18
+ export function startRuntime() {
19
+ const timeline = new Timeline();
20
+ const scope = currentScope();
21
+ if (scope)
22
+ scope.timeline = timeline;
23
+ return timeline;
24
+ }
25
+ /** Spec-facing namespace: `BySentinel.start()`. */
26
+ export const BySentinel = {
27
+ start: startRuntime,
28
+ };
29
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,IAAI,KAAK;QAAE,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACrC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,YAAY;CACpB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { Timeline } from "@bywaretech/bysentinel-core";
2
+ import type { AwsLambdaContextLike } from "./context.js";
3
+ import type { ResolvedOptions } from "./types.js";
4
+ export interface ActiveScope {
5
+ options: ResolvedOptions;
6
+ lambdaEvent?: unknown;
7
+ lambdaContext?: AwsLambdaContextLike;
8
+ coldStart: boolean;
9
+ /** Optional execution timeline started via `startRuntime()`. */
10
+ timeline?: Timeline;
11
+ }
12
+ export declare function runWithScope<T>(scope: ActiveScope, fn: () => Promise<T>): Promise<T>;
13
+ export declare function currentScope(): ActiveScope | undefined;
14
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAID,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEpF;AAED,wBAAgB,YAAY,IAAI,WAAW,GAAG,SAAS,CAEtD"}
package/dist/scope.js ADDED
@@ -0,0 +1,9 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const storage = new AsyncLocalStorage();
3
+ export function runWithScope(scope, fn) {
4
+ return storage.run(scope, fn);
5
+ }
6
+ export function currentScope() {
7
+ return storage.getStore();
8
+ }
9
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAcrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAe,CAAC;AAErD,MAAM,UAAU,YAAY,CAAI,KAAkB,EAAE,EAAoB;IACtE,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { BySentinelEvent } from "@bywaretech/bysentinel-core";
2
+ import type { ResolvedOptions } from "./types.js";
3
+ /**
4
+ * Deliver an event to the collector. Always fails silently — a broken
5
+ * collector must never break the Lambda. Delivery is bounded by
6
+ * `delivery.timeoutMs` regardless of mode.
7
+ */
8
+ export declare function sendEvent(options: ResolvedOptions, event: BySentinelEvent): Promise<{
9
+ delivered: boolean;
10
+ }>;
11
+ export declare function buildDeliveryUrl(collectorUrl: string, endpointPath: string): string;
12
+ /**
13
+ * Run delivery according to `delivery.mode`. In "blocking" mode we await the
14
+ * send; in "background" mode we still await but the send itself is bounded by
15
+ * `timeoutMs`, so the wrapper is never blocked longer than that. Both swallow
16
+ * errors.
17
+ */
18
+ export declare function deliver(options: ResolvedOptions, event: BySentinelEvent): Promise<void>;
19
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAmBjC;AA2ED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAKnF;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7F"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Deliver an event to the collector. Always fails silently — a broken
3
+ * collector must never break the Lambda. Delivery is bounded by
4
+ * `delivery.timeoutMs` regardless of mode.
5
+ */
6
+ export async function sendEvent(options, event) {
7
+ const body = JSON.stringify(event);
8
+ if (Buffer.byteLength(body, "utf8") > options.delivery.maxEventBytes) {
9
+ options.onError(new Error(`event exceeds maxEventBytes (${options.delivery.maxEventBytes}); event dropped`));
10
+ return { delivered: false };
11
+ }
12
+ const targets = deliveryTargets(options, event);
13
+ if (targets.length === 0) {
14
+ options.onError(new Error("collectorUrl and delivery.webhooks are not configured; event dropped"));
15
+ return { delivered: false };
16
+ }
17
+ const results = await Promise.all(targets.map((target) => sendToTarget(options, target, body)));
18
+ return { delivered: results.some(Boolean) };
19
+ }
20
+ function deliveryTargets(options, event) {
21
+ const targets = [];
22
+ if (options.collectorUrl) {
23
+ targets.push({
24
+ url: buildDeliveryUrl(options.collectorUrl, options.delivery.endpointPath),
25
+ headers: {
26
+ "content-type": "application/json",
27
+ "x-api-key": options.apiKey ?? "",
28
+ "x-bysentinel-event-id": event.id,
29
+ },
30
+ });
31
+ }
32
+ for (const url of options.delivery.webhooks) {
33
+ targets.push({
34
+ url,
35
+ headers: {
36
+ "content-type": "application/json",
37
+ "x-bysentinel-event-id": event.id,
38
+ "x-bysentinel-delivery": "sdk-webhook",
39
+ },
40
+ });
41
+ }
42
+ return targets;
43
+ }
44
+ async function sendToTarget(options, target, body) {
45
+ const { retries, timeoutMs } = options.delivery;
46
+ for (let attempt = 0; attempt <= retries; attempt++) {
47
+ const controller = new AbortController();
48
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
49
+ try {
50
+ const res = await fetch(target.url, {
51
+ method: "POST",
52
+ headers: target.headers,
53
+ body,
54
+ signal: controller.signal,
55
+ });
56
+ clearTimeout(timer);
57
+ if (res.ok)
58
+ return true;
59
+ // 4xx are not retryable; 5xx may be.
60
+ if (res.status < 500 || attempt === retries) {
61
+ options.onError(new Error(`delivery target responded ${res.status} for ${target.url}`));
62
+ return false;
63
+ }
64
+ }
65
+ catch (error) {
66
+ clearTimeout(timer);
67
+ if (attempt === retries) {
68
+ options.onError(error);
69
+ return false;
70
+ }
71
+ }
72
+ // Exponential backoff before the next retry.
73
+ await delay(Math.min(1000, 100 * 2 ** attempt));
74
+ }
75
+ return false;
76
+ }
77
+ export function buildDeliveryUrl(collectorUrl, endpointPath) {
78
+ if (!endpointPath)
79
+ return collectorUrl;
80
+ const base = collectorUrl.replace(/\/$/, "");
81
+ const path = endpointPath.startsWith("/") ? endpointPath : `/${endpointPath}`;
82
+ return `${base}${path}`;
83
+ }
84
+ /**
85
+ * Run delivery according to `delivery.mode`. In "blocking" mode we await the
86
+ * send; in "background" mode we still await but the send itself is bounded by
87
+ * `timeoutMs`, so the wrapper is never blocked longer than that. Both swallow
88
+ * errors.
89
+ */
90
+ export async function deliver(options, event) {
91
+ try {
92
+ await sendEvent(options, event);
93
+ }
94
+ catch (error) {
95
+ options.onError(error);
96
+ }
97
+ }
98
+ function delay(ms) {
99
+ return new Promise((resolve) => setTimeout(resolve, ms));
100
+ }
101
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { AIOptions, CaptureOptions, GitContext, SecurityOptions } from "@bywaretech/bysentinel-core";
2
+ export interface DeliveryOptions {
3
+ /**
4
+ * "background" (default): the event send is fire-and-forget, bounded by
5
+ * `timeoutMs`, and the wrapper does not block on it beyond that bound.
6
+ * "blocking": await delivery fully before returning (still bounded).
7
+ */
8
+ mode?: "background" | "blocking";
9
+ /** Hard cap on how long delivery may take. Default 2000ms. */
10
+ timeoutMs?: number;
11
+ /** Retries on network failure (exponential backoff). Default 0 in Lambda. */
12
+ retries?: number;
13
+ /** Drop events larger than this serialized size. Default 256 KiB. */
14
+ maxEventBytes?: number;
15
+ /**
16
+ * Path appended to `collectorUrl`. Defaults to `/v1/events` for the
17
+ * BySentinel collector. Use an empty string for exact webhook URLs.
18
+ */
19
+ endpointPath?: string;
20
+ /**
21
+ * Optional direct webhooks that receive the same sanitized event from the SDK.
22
+ * Useful when you want BySentinel plus an external raw notification, or when
23
+ * you are not running the dashboard/collector yet.
24
+ */
25
+ webhooks?: string[];
26
+ }
27
+ export interface BySentinelOptions {
28
+ project: string;
29
+ environment: string;
30
+ release?: string;
31
+ /** Collector base URL. Falls back to BYSENTINEL_COLLECTOR_URL. */
32
+ collectorUrl?: string;
33
+ /** Project API key. Falls back to BYSENTINEL_API_KEY. */
34
+ apiKey?: string;
35
+ capture?: CaptureOptions;
36
+ security?: SecurityOptions;
37
+ /** AI preferences forwarded to the collector as event metadata. */
38
+ ai?: AIOptions;
39
+ /** Explicit Git/release correlation. Falls back to env/CI detection. */
40
+ git?: GitContext;
41
+ delivery?: DeliveryOptions;
42
+ /** Called with internal errors instead of throwing. Off by default. */
43
+ onError?: (error: unknown) => void;
44
+ /** When true, internal diagnostics are logged with console.debug. */
45
+ debug?: boolean;
46
+ }
47
+ /** Fully-resolved options with env fallbacks + defaults applied. */
48
+ export interface ResolvedOptions {
49
+ project: string;
50
+ environment: string;
51
+ release?: string;
52
+ collectorUrl?: string;
53
+ apiKey?: string;
54
+ capture: Required<CaptureOptions>;
55
+ security: Required<SecurityOptions>;
56
+ ai?: AIOptions;
57
+ git?: GitContext;
58
+ delivery: Required<DeliveryOptions>;
59
+ onError: (error: unknown) => void;
60
+ debug: boolean;
61
+ }
62
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE1G,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,IAAI,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACjC,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,mEAAmE;IACnE,EAAE,CAAC,EAAE,SAAS,CAAC;IAEf,wEAAwE;IACxE,GAAG,CAAC,EAAE,UAAU,CAAC;IAEjB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,uEAAuE;IACvE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAClC,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,OAAO,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { type AwsLambdaContextLike } from "./context.js";
2
+ import type { BySentinelOptions } from "./types.js";
3
+ export type LambdaHandler<TEvent = any, TResult = any, TContext extends AwsLambdaContextLike = AwsLambdaContextLike> = (event: TEvent, context: TContext) => Promise<TResult> | TResult;
4
+ /**
5
+ * Wrap a Lambda handler with BySentinel. On an unhandled exception it captures
6
+ * a sanitized event, then re-throws so the Lambda still fails as expected. On
7
+ * success it optionally reports performance metadata (timeout risk / memory).
8
+ *
9
+ * The wrapper never changes the handler's return value and never swallows the
10
+ * business error.
11
+ */
12
+ export declare function withBySentinel<TEvent = any, TResult = any, TContext extends AwsLambdaContextLike = AwsLambdaContextLike>(handler: LambdaHandler<TEvent, TResult, TContext>, options: BySentinelOptions): LambdaHandler<TEvent, TResult, TContext>;
13
+ //# sourceMappingURL=withBySentinel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withBySentinel.d.ts","sourceRoot":"","sources":["../src/withBySentinel.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAMlG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,MAAM,aAAa,CACvB,MAAM,GAAG,GAAG,EACZ,OAAO,GAAG,GAAG,EACb,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,IAC1D,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAErE;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,GAAG,GAAG,EACZ,OAAO,GAAG,GAAG,EACb,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,EAE5D,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EACjD,OAAO,EAAE,iBAAiB,GACzB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAsE1C"}