@hartl-services/medusa-base 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.withSentryErrorReporting = withSentryErrorReporting;
37
+ const Sentry = __importStar(require("@sentry/node"));
38
+ /**
39
+ * Medusa's own HTTP error-handler middleware fully "handles" every thrown
40
+ * route/workflow error via logger.error(...) before it can ever reach an
41
+ * uncaught-exception process hook, so Sentry's default Node integrations
42
+ * never see it. Bridging logger.error(...) itself is the only place that
43
+ * reliably captures every error Medusa's framework already logs internally.
44
+ * Errors that Medusa logs through logger.warn(err) (failing event
45
+ * subscribers) are reported too, at warning level.
46
+ */
47
+ function withSentryErrorReporting(logger) {
48
+ const originalError = logger.error.bind(logger);
49
+ const originalWarn = logger.warn.bind(logger);
50
+ return {
51
+ panic: logger.panic.bind(logger),
52
+ shouldLog: logger.shouldLog.bind(logger),
53
+ setLogLevel: logger.setLogLevel.bind(logger),
54
+ unsetLogLevel: logger.unsetLogLevel.bind(logger),
55
+ activity: logger.activity.bind(logger),
56
+ progress: logger.progress.bind(logger),
57
+ failure: logger.failure.bind(logger),
58
+ success: logger.success.bind(logger),
59
+ silly: logger.silly.bind(logger),
60
+ debug: logger.debug.bind(logger),
61
+ verbose: logger.verbose.bind(logger),
62
+ http: logger.http.bind(logger),
63
+ info: logger.info.bind(logger),
64
+ // Medusa's Redis Event Bus logs a failing subscriber's exception with
65
+ // warn(err), even on the final retry, so an Error passed to warn is a real
66
+ // failure too. Plain warning messages stay out of Sentry.
67
+ warn: (message) => {
68
+ if (message instanceof Error) {
69
+ Sentry.captureException(message, { level: "warning" });
70
+ }
71
+ return originalWarn(message);
72
+ },
73
+ log: logger.log.bind(logger),
74
+ error: (messageOrError, error) => {
75
+ const exception = error ?? (messageOrError instanceof Error ? messageOrError : new Error(messageOrError));
76
+ const loggerMessage = error && typeof messageOrError === "string" ? messageOrError : undefined;
77
+ Sentry.captureException(exception, loggerMessage ? { extra: { loggerMessage } } : undefined);
78
+ return originalError(messageOrError, error);
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VudHJ5LWxvZ2dlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9wdWJsaWMvc2VudHJ5LWxvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQVlBLDREQTBDQztBQXRERCxxREFBc0M7QUFHdEM7Ozs7Ozs7O0dBUUc7QUFDSCxTQUFnQix3QkFBd0IsQ0FBQyxNQUFjO0lBQ3JELE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQy9DLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBRTdDLE9BQU87UUFDTCxLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQ2hDLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDeEMsV0FBVyxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUM1QyxhQUFhLEVBQUUsTUFBTSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQ2hELFFBQVEsRUFBRSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDdEMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUN0QyxPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQ3BDLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDcEMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUNoQyxLQUFLLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQ2hDLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDcEMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUM5QixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQzlCLHNFQUFzRTtRQUN0RSwyRUFBMkU7UUFDM0UsMERBQTBEO1FBQzFELElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQ2hCLElBQUssT0FBbUIsWUFBWSxLQUFLLEVBQUUsQ0FBQztnQkFDMUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFBO1lBQ3hELENBQUM7WUFDRCxPQUFPLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQTtRQUM5QixDQUFDO1FBQ0QsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUM1QixLQUFLLEVBQUUsQ0FBQyxjQUFjLEVBQUUsS0FBSyxFQUFFLEVBQUU7WUFDL0IsTUFBTSxTQUFTLEdBQ2IsS0FBSyxJQUFJLENBQUMsY0FBYyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFBO1lBQ3pGLE1BQU0sYUFBYSxHQUNqQixLQUFLLElBQUksT0FBTyxjQUFjLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtZQUUxRSxNQUFNLENBQUMsZ0JBQWdCLENBQ3JCLFNBQVMsRUFDVCxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUUsYUFBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUN6RCxDQUFBO1lBRUQsT0FBTyxhQUFhLENBQUMsY0FBYyxFQUFFLEtBQUssQ0FBQyxDQUFBO1FBQzdDLENBQUM7S0FDRixDQUFBO0FBQ0gsQ0FBQyJ9
package/README.md CHANGED
@@ -230,3 +230,26 @@ Affiliate 100 (`affiliate-validate-complete-cart`,
230
230
  instead of silently leaving partial state.
231
231
  - The base never swallows a participant error and never reads toggles here.
232
232
 
233
+
234
+ ## Sentry logger wrapper
235
+
236
+ `@hartl-services/medusa-base/sentry-logger` exports `withSentryErrorReporting(logger)`,
237
+ formerly published as `@hartl-services/medusa-sentry-logger`. Medusa's HTTP
238
+ error handler catches thrown route and workflow errors and only logs them, so
239
+ Sentry's default Node integrations never see them; wrapping the logger reports
240
+ every `logger.error(...)` and every `Error` passed to `logger.warn(...)` (at
241
+ level `warning`). It is a plain library import for `medusa-config.ts`, not a
242
+ Medusa plugin feature, and needs `@sentry/node` (an optional peer dependency):
243
+
244
+ ```ts
245
+ import { logger } from "@medusajs/framework/logger";
246
+ import { withSentryErrorReporting } from "@hartl-services/medusa-base/sentry-logger";
247
+
248
+ module.exports = defineConfig({
249
+ logger: withSentryErrorReporting(logger),
250
+ // ...
251
+ });
252
+ ```
253
+
254
+ Initialize Sentry in `instrumentation.ts` as usual; without a `SENTRY_DSN` the
255
+ wrapper is inert.
@@ -0,0 +1,11 @@
1
+ import type { Logger } from "@medusajs/framework/types";
2
+ /**
3
+ * Medusa's own HTTP error-handler middleware fully "handles" every thrown
4
+ * route/workflow error via logger.error(...) before it can ever reach an
5
+ * uncaught-exception process hook, so Sentry's default Node integrations
6
+ * never see it. Bridging logger.error(...) itself is the only place that
7
+ * reliably captures every error Medusa's framework already logs internally.
8
+ * Errors that Medusa logs through logger.warn(err) (failing event
9
+ * subscribers) are reported too, at warning level.
10
+ */
11
+ export declare function withSentryErrorReporting(logger: Logger): Logger;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.withSentryErrorReporting = withSentryErrorReporting;
37
+ const Sentry = __importStar(require("@sentry/node"));
38
+ /**
39
+ * Medusa's own HTTP error-handler middleware fully "handles" every thrown
40
+ * route/workflow error via logger.error(...) before it can ever reach an
41
+ * uncaught-exception process hook, so Sentry's default Node integrations
42
+ * never see it. Bridging logger.error(...) itself is the only place that
43
+ * reliably captures every error Medusa's framework already logs internally.
44
+ * Errors that Medusa logs through logger.warn(err) (failing event
45
+ * subscribers) are reported too, at warning level.
46
+ */
47
+ function withSentryErrorReporting(logger) {
48
+ const originalError = logger.error.bind(logger);
49
+ const originalWarn = logger.warn.bind(logger);
50
+ return {
51
+ panic: logger.panic.bind(logger),
52
+ shouldLog: logger.shouldLog.bind(logger),
53
+ setLogLevel: logger.setLogLevel.bind(logger),
54
+ unsetLogLevel: logger.unsetLogLevel.bind(logger),
55
+ activity: logger.activity.bind(logger),
56
+ progress: logger.progress.bind(logger),
57
+ failure: logger.failure.bind(logger),
58
+ success: logger.success.bind(logger),
59
+ silly: logger.silly.bind(logger),
60
+ debug: logger.debug.bind(logger),
61
+ verbose: logger.verbose.bind(logger),
62
+ http: logger.http.bind(logger),
63
+ info: logger.info.bind(logger),
64
+ // Medusa's Redis Event Bus logs a failing subscriber's exception with
65
+ // warn(err), even on the final retry, so an Error passed to warn is a real
66
+ // failure too. Plain warning messages stay out of Sentry.
67
+ warn: (message) => {
68
+ if (message instanceof Error) {
69
+ Sentry.captureException(message, { level: "warning" });
70
+ }
71
+ return originalWarn(message);
72
+ },
73
+ log: logger.log.bind(logger),
74
+ error: (messageOrError, error) => {
75
+ const exception = error ?? (messageOrError instanceof Error ? messageOrError : new Error(messageOrError));
76
+ const loggerMessage = error && typeof messageOrError === "string" ? messageOrError : undefined;
77
+ Sentry.captureException(exception, loggerMessage ? { extra: { loggerMessage } } : undefined);
78
+ return originalError(messageOrError, error);
79
+ },
80
+ };
81
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hartl-services/medusa-base",
3
- "version": "0.2.0",
4
- "description": "Base plugin for Hartl Services Medusa shops: per-plugin feature toggles with a generic Admin settings page, a Store endpoint for storefronts, and enforcement helpers for plugin routes, subscribers and jobs.",
3
+ "version": "0.3.0",
4
+ "description": "Base plugin for Hartl Services Medusa shops: per-plugin feature toggles with a generic Admin settings page, a Store endpoint for storefronts, enforcement helpers, the complete-cart hook composition, and the Sentry logger wrapper.",
5
5
  "license": "MIT",
6
6
  "files": [
7
7
  ".medusa/server",
@@ -28,6 +28,11 @@
28
28
  "import": "./dist/public/index.js",
29
29
  "require": "./dist/public/index.js"
30
30
  },
31
+ "./sentry-logger": {
32
+ "types": "./dist/public/sentry-logger.d.ts",
33
+ "import": "./dist/public/sentry-logger.js",
34
+ "require": "./dist/public/sentry-logger.js"
35
+ },
31
36
  "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
32
37
  "./.medusa/server/src/*": "./.medusa/server/src/*",
33
38
  "./admin": {
@@ -41,6 +46,7 @@
41
46
  "@medusajs/js-sdk": "^2.19.0",
42
47
  "@medusajs/medusa": "^2.19.0",
43
48
  "@medusajs/ui": "^4.2.1",
49
+ "@sentry/node": "^10.74.0",
44
50
  "@tanstack/react-query": "^5.64.2",
45
51
  "react": "^18.3.1",
46
52
  "react-router-dom": "^7.18.2"
@@ -55,6 +61,7 @@
55
61
  "@medusajs/js-sdk": "2.19.0",
56
62
  "@medusajs/medusa": "2.19.0",
57
63
  "@medusajs/ui": "4.2.1",
64
+ "@sentry/node": "^10.74.0",
58
65
  "@swc/core": "^1.7.28",
59
66
  "@swc/jest": "^0.2.39",
60
67
  "@tanstack/react-query": "5.64.2",
@@ -72,6 +79,11 @@
72
79
  "engines": {
73
80
  "node": ">=20"
74
81
  },
82
+ "peerDependenciesMeta": {
83
+ "@sentry/node": {
84
+ "optional": true
85
+ }
86
+ },
75
87
  "scripts": {
76
88
  "build": "medusa plugin:build && tsc -p tsconfig.public.json",
77
89
  "dev": "medusa plugin:develop",