@hartl-services/medusa-base 0.2.0 → 0.4.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 (97) hide show
  1. package/.medusa/server/src/admin/index.js +741 -36
  2. package/.medusa/server/src/admin/index.mjs +743 -38
  3. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/route.js +31 -0
  4. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/actions/[action_key]/runs/route.js +16 -0
  5. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/route.js +14 -0
  6. package/.medusa/server/src/api/admin/plugin-features/[plugin_key]/settings/route.js +22 -0
  7. package/.medusa/server/src/api/admin/plugin-features/route.js +6 -4
  8. package/.medusa/server/src/api/middlewares.js +22 -8
  9. package/.medusa/server/src/api/plugin-actions.js +221 -0
  10. package/.medusa/server/src/api/plugin-features-http.js +67 -8
  11. package/.medusa/server/src/modules/plugin-features/domain/plugin-action-runs.js +92 -0
  12. package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +213 -0
  13. package/.medusa/server/src/modules/plugin-features/domain/secret-box.js +56 -0
  14. package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928205031.js +16 -0
  15. package/.medusa/server/src/modules/plugin-features/migrations/Migration20260928215428.js +17 -0
  16. package/.medusa/server/src/modules/plugin-features/models/plugin-action-run.js +43 -0
  17. package/.medusa/server/src/modules/plugin-features/models/plugin-setting-state.js +32 -0
  18. package/.medusa/server/src/modules/plugin-features/service.js +419 -17
  19. package/.medusa/server/src/public/action-progress.js +67 -0
  20. package/.medusa/server/src/public/descriptor.js +199 -2
  21. package/.medusa/server/src/public/effective-features.js +9 -5
  22. package/.medusa/server/src/public/enforcement.js +18 -8
  23. package/.medusa/server/src/public/index.js +10 -2
  24. package/.medusa/server/src/public/module-access.js +14 -0
  25. package/.medusa/server/src/public/sentry-logger.js +82 -0
  26. package/.medusa/server/src/public/settings.js +17 -0
  27. package/.medusa/server/src/public/shop-master-data.js +95 -0
  28. package/.medusa/server/src/utils/discover-complete-cart-participants.js +9 -2
  29. package/.medusa/server/src/utils/discover-plugin-features.js +47 -9
  30. package/.medusa/server/src/workflows/hooks/complete-cart-participants.js +28 -16
  31. package/README.md +416 -12
  32. package/dist/admin-components/features/plugin-features/components/PluginActionsSection.d.ts +6 -0
  33. package/dist/admin-components/features/plugin-features/components/PluginActionsSection.js +66 -0
  34. package/dist/admin-components/features/plugin-features/components/PluginDetailPage.d.ts +2 -0
  35. package/dist/admin-components/features/plugin-features/components/PluginDetailPage.js +9 -0
  36. package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.d.ts +22 -0
  37. package/dist/admin-components/features/plugin-features/components/PluginFeatureGate.js +18 -0
  38. package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +11 -0
  39. package/dist/admin-components/features/plugin-features/components/PluginFeatureSections.js +19 -0
  40. package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +12 -0
  41. package/dist/admin-components/features/plugin-features/components/PluginFeaturesCard.js +34 -0
  42. package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +5 -0
  43. package/dist/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +16 -0
  44. package/dist/admin-components/features/plugin-features/components/PluginHubCard.d.ts +7 -0
  45. package/dist/admin-components/features/plugin-features/components/PluginHubCard.js +21 -0
  46. package/dist/admin-components/features/plugin-features/components/PluginPageShell.d.ts +13 -0
  47. package/dist/admin-components/features/plugin-features/components/PluginPageShell.js +20 -0
  48. package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +7 -0
  49. package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.js +39 -0
  50. package/dist/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -0
  51. package/dist/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -0
  52. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +114 -0
  53. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.js +40 -0
  54. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +392 -0
  55. package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.js +128 -0
  56. package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +93 -0
  57. package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.js +44 -0
  58. package/dist/admin-components/features/plugin-features/lib/action-runs.d.ts +19 -0
  59. package/dist/admin-components/features/plugin-features/lib/action-runs.js +74 -0
  60. package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +363 -0
  61. package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.js +9 -0
  62. package/dist/admin-components/features/plugin-features/lib/plugin-paths.d.ts +4 -0
  63. package/dist/admin-components/features/plugin-features/lib/plugin-paths.js +4 -0
  64. package/dist/admin-components/features/plugin-features/lib/settings-draft.d.ts +28 -0
  65. package/dist/admin-components/features/plugin-features/lib/settings-draft.js +86 -0
  66. package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.d.ts +7 -0
  67. package/dist/admin-components/features/plugin-features/queries/pluginFeatureKeys.js +7 -0
  68. package/dist/admin-components/i18n/texts.d.ts +90 -0
  69. package/dist/admin-components/i18n/texts.js +90 -0
  70. package/dist/admin-components/index.d.ts +14 -0
  71. package/dist/admin-components/index.js +14 -0
  72. package/dist/admin-components/lib/errors.d.ts +6 -0
  73. package/dist/admin-components/lib/errors.js +27 -0
  74. package/dist/admin-components/lib/sdk.d.ts +2 -0
  75. package/dist/admin-components/lib/sdk.js +13 -0
  76. package/dist/public/action-progress.d.ts +25 -0
  77. package/dist/public/action-progress.js +66 -0
  78. package/dist/public/descriptor.d.ts +3 -2
  79. package/dist/public/descriptor.js +198 -1
  80. package/dist/public/effective-features.d.ts +2 -1
  81. package/dist/public/effective-features.js +8 -4
  82. package/dist/public/enforcement.d.ts +7 -0
  83. package/dist/public/enforcement.js +17 -7
  84. package/dist/public/index.d.ts +5 -2
  85. package/dist/public/index.js +9 -1
  86. package/dist/public/module-access.d.ts +3 -0
  87. package/dist/public/module-access.js +13 -0
  88. package/dist/public/sentry-logger.d.ts +11 -0
  89. package/dist/public/sentry-logger.js +81 -0
  90. package/dist/public/settings.d.ts +11 -0
  91. package/dist/public/settings.js +16 -0
  92. package/dist/public/shop-master-data.d.ts +25 -0
  93. package/dist/public/shop-master-data.js +94 -0
  94. package/dist/public/types.d.ts +97 -1
  95. package/dist/utils/discover-plugin-features.d.ts +28 -0
  96. package/dist/utils/discover-plugin-features.js +96 -0
  97. package/package.json +20 -4
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveBaseModule = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const constants_1 = require("./constants");
6
+ /** Resolves the base module or fails with a clear `UNEXPECTED_STATE`. */
7
+ const resolveBaseModule = (container) => {
8
+ if (!container.hasRegistration(constants_1.PLUGIN_FEATURES_MODULE)) {
9
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "@hartl-services/medusa-base ist nicht in medusa-config registriert.");
10
+ }
11
+ return container.resolve(constants_1.PLUGIN_FEATURES_MODULE);
12
+ };
13
+ exports.resolveBaseModule = resolveBaseModule;
@@ -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
+ }
@@ -0,0 +1,11 @@
1
+ import type { MedusaContainer } from "@medusajs/framework/types";
2
+ import type { PluginFeatureDescriptor, PluginSettingsFallback, ResolvedPluginSettings } from "./types";
3
+ /**
4
+ * Current settings of a plugin for server-side code (subscribers, workflow
5
+ * steps, jobs, scripts, module services via the global container). Secrets
6
+ * are DECRYPTED; never send the result to a client. Precedence per key:
7
+ * stored in the Admin > `fallback` (medusa-config) > declared default.
8
+ * A missing base plugin fails with `UNEXPECTED_STATE`; read failures
9
+ * propagate.
10
+ */
11
+ export declare function getPluginSettings<K extends string, S extends string, A extends string>(container: MedusaContainer, descriptor: PluginFeatureDescriptor<K, S, A>, fallback?: PluginSettingsFallback<S>): Promise<ResolvedPluginSettings<S>>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPluginSettings = getPluginSettings;
4
+ const module_access_1 = require("./module-access");
5
+ /**
6
+ * Current settings of a plugin for server-side code (subscribers, workflow
7
+ * steps, jobs, scripts, module services via the global container). Secrets
8
+ * are DECRYPTED; never send the result to a client. Precedence per key:
9
+ * stored in the Admin > `fallback` (medusa-config) > declared default.
10
+ * A missing base plugin fails with `UNEXPECTED_STATE`; read failures
11
+ * propagate.
12
+ */
13
+ async function getPluginSettings(container, descriptor, fallback) {
14
+ const resolved = await (0, module_access_1.resolveBaseModule)(container).resolvePluginSettings(descriptor, fallback);
15
+ return resolved;
16
+ }
@@ -0,0 +1,25 @@
1
+ import type { MedusaContainer } from "@medusajs/framework/types";
2
+ import type { PluginSettingsFallback } from "./types";
3
+ /**
4
+ * The base's own descriptor: the shop's master data (Firmenangaben), edited
5
+ * on the generic plugin page like any plugin's settings. It cannot be
6
+ * switched off and declares no features.
7
+ */
8
+ export declare const shopMasterDataDescriptor: import("./types").PluginFeatureDescriptor<string, "legal_name" | "address_line1" | "address_line2" | "postal_code" | "city" | "country_code" | "vat_number" | "email" | "phone" | "website" | "iban" | "bic" | "company_register_number" | "company_register_court", never>;
9
+ /** Keys of the shop master data settings. */
10
+ export type ShopMasterDataKey = "legal_name" | "address_line1" | "address_line2" | "postal_code" | "city" | "country_code" | "vat_number" | "email" | "phone" | "website" | "iban" | "bic" | "company_register_number" | "company_register_court";
11
+ /** Every key that has a value (stored > fallback). All values are strings. */
12
+ export type ShopMasterData = Partial<Record<ShopMasterDataKey, string>>;
13
+ export type ResolvedShopMasterData = {
14
+ values: ShopMasterData;
15
+ /** Every required field has a value. */
16
+ configured: boolean;
17
+ /** Required keys without a value. */
18
+ missing: ShopMasterDataKey[];
19
+ };
20
+ /**
21
+ * The shop's master data for server-side code. Precedence per key: stored in
22
+ * the Admin > `fallback` (e.g. a plugin's own medusa-config option). A
23
+ * missing base plugin fails with `UNEXPECTED_STATE`; read failures propagate.
24
+ */
25
+ export declare function getShopMasterData(container: MedusaContainer, fallback?: PluginSettingsFallback<ShopMasterDataKey>): Promise<ResolvedShopMasterData>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shopMasterDataDescriptor = void 0;
4
+ exports.getShopMasterData = getShopMasterData;
5
+ const descriptor_1 = require("./descriptor");
6
+ const settings_1 = require("./settings");
7
+ /**
8
+ * The base's own descriptor: the shop's master data (Firmenangaben), edited
9
+ * on the generic plugin page like any plugin's settings. It cannot be
10
+ * switched off and declares no features.
11
+ */
12
+ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
13
+ plugin_key: "shop",
14
+ label: "Shop-Stammdaten",
15
+ description: "Firmenangaben des Shops für Rechnungen, Dokumente und Steuerprüfungen.",
16
+ switchable: false,
17
+ features: [],
18
+ settings: [
19
+ { key: "legal_name", label: "Firmenname", type: "string", required: true },
20
+ { key: "address_line1", label: "Adresse", type: "string", required: true },
21
+ { key: "address_line2", label: "Adresszusatz", type: "string" },
22
+ { key: "postal_code", label: "Postleitzahl", type: "string", required: true },
23
+ { key: "city", label: "Ort", type: "string", required: true },
24
+ {
25
+ key: "country_code",
26
+ label: "Land",
27
+ type: "select",
28
+ required: true,
29
+ // EU-27 plus CH, GB, NO, IS, LI (ISO 3166-1 alpha-2, lowercase),
30
+ // sorted by German label.
31
+ options: [
32
+ { value: "be", label: "Belgien" },
33
+ { value: "bg", label: "Bulgarien" },
34
+ { value: "dk", label: "Dänemark" },
35
+ { value: "de", label: "Deutschland" },
36
+ { value: "ee", label: "Estland" },
37
+ { value: "fi", label: "Finnland" },
38
+ { value: "fr", label: "Frankreich" },
39
+ { value: "gr", label: "Griechenland" },
40
+ { value: "ie", label: "Irland" },
41
+ { value: "is", label: "Island" },
42
+ { value: "it", label: "Italien" },
43
+ { value: "hr", label: "Kroatien" },
44
+ { value: "lv", label: "Lettland" },
45
+ { value: "li", label: "Liechtenstein" },
46
+ { value: "lt", label: "Litauen" },
47
+ { value: "lu", label: "Luxemburg" },
48
+ { value: "mt", label: "Malta" },
49
+ { value: "nl", label: "Niederlande" },
50
+ { value: "no", label: "Norwegen" },
51
+ { value: "at", label: "Österreich" },
52
+ { value: "pl", label: "Polen" },
53
+ { value: "pt", label: "Portugal" },
54
+ { value: "ro", label: "Rumänien" },
55
+ { value: "se", label: "Schweden" },
56
+ { value: "ch", label: "Schweiz" },
57
+ { value: "sk", label: "Slowakei" },
58
+ { value: "si", label: "Slowenien" },
59
+ { value: "es", label: "Spanien" },
60
+ { value: "cz", label: "Tschechien" },
61
+ { value: "hu", label: "Ungarn" },
62
+ { value: "gb", label: "Vereinigtes Königreich" },
63
+ { value: "cy", label: "Zypern" },
64
+ ],
65
+ },
66
+ { key: "vat_number", label: "UID-Nummer", type: "string", required: true },
67
+ { key: "email", label: "E-Mail", type: "string" },
68
+ { key: "phone", label: "Telefon", type: "string" },
69
+ { key: "website", label: "Website", type: "url" },
70
+ { key: "iban", label: "IBAN", type: "string" },
71
+ { key: "bic", label: "BIC", type: "string" },
72
+ { key: "company_register_number", label: "Firmenbuchnummer", type: "string" },
73
+ { key: "company_register_court", label: "Firmenbuchgericht", type: "string" },
74
+ ],
75
+ });
76
+ /**
77
+ * The shop's master data for server-side code. Precedence per key: stored in
78
+ * the Admin > `fallback` (e.g. a plugin's own medusa-config option). A
79
+ * missing base plugin fails with `UNEXPECTED_STATE`; read failures propagate.
80
+ */
81
+ async function getShopMasterData(container, fallback) {
82
+ const resolved = await (0, settings_1.getPluginSettings)(container, exports.shopMasterDataDescriptor, fallback);
83
+ const values = {};
84
+ for (const [key, value] of Object.entries(resolved.values)) {
85
+ if (typeof value === "string") {
86
+ values[key] = value;
87
+ }
88
+ }
89
+ return {
90
+ values,
91
+ configured: resolved.configured,
92
+ missing: resolved.missing,
93
+ };
94
+ }
@@ -1,3 +1,12 @@
1
+ /** Options of the base plugin (`plugins: [{ resolve, options }]`). */
2
+ export type BasePluginOptions = {
3
+ /**
4
+ * Key material for stored secret settings; set it from an environment
5
+ * variable of the shop. Defaults to `projectConfig.http.cookieSecret`.
6
+ * Changing it makes stored secrets undecryptable ("nicht gesetzt").
7
+ */
8
+ readonly settingsSecret?: string;
9
+ };
1
10
  export type PluginFeatureDefinition<K extends string = string> = {
2
11
  readonly key: K;
3
12
  readonly label: string;
@@ -5,17 +14,75 @@ export type PluginFeatureDefinition<K extends string = string> = {
5
14
  readonly default_enabled: boolean;
6
15
  readonly parent_key?: K;
7
16
  };
8
- export type PluginFeatureDescriptor<K extends string = string> = {
17
+ export type PluginSettingType = "string" | "url" | "number" | "boolean" | "secret" | "select";
18
+ /** One allowed value of a `"select"` setting. */
19
+ export type PluginSettingOption = {
20
+ readonly value: string;
21
+ readonly label: string;
22
+ };
23
+ export type PluginSettingValue = string | number | boolean;
24
+ export type PluginSettingDefinition<S extends string = string> = {
25
+ readonly key: S;
26
+ readonly label: string;
27
+ readonly description?: string;
28
+ readonly type: PluginSettingType;
29
+ /** `false` when omitted. */
30
+ readonly required?: boolean;
31
+ /** Never for `"secret"`; must match `type` (and `options`/`min`/`max`). */
32
+ readonly default?: PluginSettingValue;
33
+ /** Required for `"select"` (non-empty, unique values); only there. */
34
+ readonly options?: readonly PluginSettingOption[];
35
+ /** Inclusive lower bound; only for `"number"`. */
36
+ readonly min?: number;
37
+ /** Inclusive upper bound; only for `"number"`. */
38
+ readonly max?: number;
39
+ /**
40
+ * Secret settings of the same plugin whose stored values are deleted when
41
+ * this setting's stored value changes (also when it is cleared), e.g. an
42
+ * API key that must not be sent to a new URL.
43
+ */
44
+ readonly invalidates?: readonly S[];
45
+ };
46
+ export type PluginActionMode = "sync" | "background";
47
+ export type PluginActionDefinition<A extends string = string> = {
48
+ readonly key: A;
49
+ readonly label: string;
50
+ readonly description?: string;
51
+ /** Registered workflow the base runs for this action. */
52
+ readonly workflow_id: string;
53
+ readonly mode: PluginActionMode;
54
+ /** Confirmation text shown before running. */
55
+ readonly confirm?: string;
56
+ };
57
+ export type PluginFeatureDescriptor<K extends string = string, S extends string = string, A extends string = string> = {
9
58
  readonly plugin_key: string;
10
59
  readonly label: string;
11
60
  readonly description?: string;
12
61
  /** Plugin switch default, `true` when omitted. */
13
62
  readonly default_enabled?: boolean;
63
+ /**
64
+ * `false`: the plugin cannot be switched off. The Admin shows no plugin
65
+ * switch and `enabled` is always `true`. `true` when omitted.
66
+ */
67
+ readonly switchable?: boolean;
14
68
  readonly features: readonly PluginFeatureDefinition<K>[];
69
+ readonly settings?: readonly PluginSettingDefinition<S>[];
70
+ readonly actions?: readonly PluginActionDefinition<A>[];
71
+ /**
72
+ * The plugin's own Admin page, relative to the Admin root; the hub's
73
+ * "Öffnen" links there. Starts with `/settings/hartl-services/`, never with
74
+ * `/settings/hartl-services/plugin/` (the base's generic page, used when
75
+ * omitted).
76
+ */
77
+ readonly admin_page?: string;
15
78
  };
79
+ /** Values a plugin received from medusa-config; the base shows them as source "config". */
80
+ export type PluginSettingsFallback<S extends string = string> = Partial<Record<S, PluginSettingValue | null | undefined>>;
16
81
  /** Implemented by a plugin's module service so the base can discover it. */
17
82
  export interface PluginFeatureProvider {
18
83
  readonly pluginFeatures: PluginFeatureDescriptor;
84
+ /** Optional medusa-config fallback for the declared settings. */
85
+ readonly pluginSettingsFallback?: PluginSettingsFallback;
19
86
  }
20
87
  export type EffectivePluginFeatures = {
21
88
  plugin_key: string;
@@ -27,3 +94,32 @@ export type StoredPluginFeatures = {
27
94
  enabled: boolean;
28
95
  features: Record<string, boolean>;
29
96
  };
97
+ /**
98
+ * Settings for server-side consumers. Secrets are decrypted. `values` holds
99
+ * every setting that has a value (stored > config > default).
100
+ */
101
+ export type ResolvedPluginSettings<S extends string = string> = {
102
+ values: Partial<Record<S, PluginSettingValue>>;
103
+ /** Every required setting has a value. */
104
+ configured: boolean;
105
+ /** Keys of required settings without a value. */
106
+ missing: S[];
107
+ };
108
+ /** Input the base passes to an action's workflow. */
109
+ export type PluginActionWorkflowInput = {
110
+ plugin_key: string;
111
+ action_key: string;
112
+ /** Admin user id that triggered the action. */
113
+ triggered_by: string;
114
+ /** Recorded run; pass it to `reportPluginActionProgress`. */
115
+ run_id: string;
116
+ };
117
+ /** Progress a running action reports through `reportPluginActionProgress`. */
118
+ export type PluginActionProgressInput = {
119
+ /** Items (or pages) done so far; a non-negative integer. */
120
+ current: number;
121
+ /** Expected total; `null`/omitted = unknown. */
122
+ total?: number | null;
123
+ /** Short human-readable status, e.g. "Produkte: Seite 3". */
124
+ label?: string | null;
125
+ };
@@ -0,0 +1,28 @@
1
+ import type { MedusaContainer } from "@medusajs/framework/types";
2
+ import type { PluginFeatureDescriptor, PluginSettingsFallback } from "../public/types";
3
+ export type RegisteredModule = {
4
+ key: string;
5
+ service: unknown;
6
+ };
7
+ /** A discovered declaring module service. */
8
+ export type PluginFeatureProviderEntry = {
9
+ module_key: string;
10
+ descriptor: PluginFeatureDescriptor;
11
+ service: unknown;
12
+ };
13
+ /** Every module listed in medusa-config that is registered in the container. */
14
+ export declare function listRegisteredModules(container: MedusaContainer): RegisteredModule[];
15
+ /**
16
+ * Finds every registered module whose service declares `pluginFeatures`.
17
+ * Fails closed: an invalid or duplicate declaration is `UNEXPECTED_STATE`.
18
+ */
19
+ export declare function discoverPluginFeatureDescriptors(container: MedusaContainer): PluginFeatureDescriptor[];
20
+ /** Like `discoverPluginFeatureDescriptors`, with the declaring service. */
21
+ export declare function discoverPluginFeatureProviders(container: MedusaContainer): PluginFeatureProviderEntry[];
22
+ export declare function findPluginFeatureDescriptor(container: MedusaContainer, pluginKey: string): PluginFeatureDescriptor | undefined;
23
+ export declare function findPluginFeatureProvider(container: MedusaContainer, pluginKey: string): PluginFeatureProviderEntry | undefined;
24
+ /**
25
+ * The medusa-config fallback a provider exposes as `pluginSettingsFallback`.
26
+ * Read on every call, since a service may compute it lazily.
27
+ */
28
+ export declare function readPluginSettingsFallback(provider: PluginFeatureProviderEntry): PluginSettingsFallback | undefined;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listRegisteredModules = listRegisteredModules;
4
+ exports.discoverPluginFeatureDescriptors = discoverPluginFeatureDescriptors;
5
+ exports.discoverPluginFeatureProviders = discoverPluginFeatureProviders;
6
+ exports.findPluginFeatureDescriptor = findPluginFeatureDescriptor;
7
+ exports.findPluginFeatureProvider = findPluginFeatureProvider;
8
+ exports.readPluginSettingsFallback = readPluginSettingsFallback;
9
+ const utils_1 = require("@medusajs/framework/utils");
10
+ const descriptor_1 = require("../public/descriptor");
11
+ // Keyed by the app's config object: stable for the process lifetime, unlike
12
+ // `req.scope`, which is a new scope per request.
13
+ const discovered = new WeakMap();
14
+ const registeredModules = (container, configModule) => Object.keys(configModule.modules ?? {})
15
+ .filter((moduleKey) => container.hasRegistration(moduleKey))
16
+ .map((moduleKey) => ({
17
+ key: moduleKey,
18
+ service: container.resolve(moduleKey),
19
+ }));
20
+ /** Every module listed in medusa-config that is registered in the container. */
21
+ function listRegisteredModules(container) {
22
+ return registeredModules(container, container.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE));
23
+ }
24
+ const readDescriptor = (service) => service !== null && typeof service === "object" && "pluginFeatures" in service
25
+ ? service.pluginFeatures
26
+ : undefined;
27
+ const discover = (container) => {
28
+ const configModule = container.resolve(utils_1.ContainerRegistrationKeys.CONFIG_MODULE);
29
+ const memo = discovered.get(configModule);
30
+ if (memo) {
31
+ return memo;
32
+ }
33
+ const byPluginKey = new Map();
34
+ for (const { key: moduleKey, service } of registeredModules(container, configModule)) {
35
+ const candidate = readDescriptor(service);
36
+ if (candidate === undefined) {
37
+ continue;
38
+ }
39
+ const descriptor = candidate;
40
+ try {
41
+ (0, descriptor_1.validatePluginFeatureDescriptor)(descriptor);
42
+ }
43
+ catch (error) {
44
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Modul "${moduleKey}" deklariert ungültige Funktionsschalter: ${error instanceof Error ? error.message : String(error)}`);
45
+ }
46
+ if (byPluginKey.has(descriptor.plugin_key)) {
47
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Plugin-Key "${descriptor.plugin_key}" wird von mehreren Modulen deklariert.`);
48
+ }
49
+ byPluginKey.set(descriptor.plugin_key, {
50
+ module_key: moduleKey,
51
+ descriptor,
52
+ service,
53
+ });
54
+ }
55
+ const providers = [...byPluginKey.values()].sort((left, right) => left.descriptor.plugin_key.localeCompare(right.descriptor.plugin_key));
56
+ const result = {
57
+ providers,
58
+ descriptors: providers.map((provider) => provider.descriptor),
59
+ };
60
+ discovered.set(configModule, result);
61
+ return result;
62
+ };
63
+ /**
64
+ * Finds every registered module whose service declares `pluginFeatures`.
65
+ * Fails closed: an invalid or duplicate declaration is `UNEXPECTED_STATE`.
66
+ */
67
+ function discoverPluginFeatureDescriptors(container) {
68
+ return discover(container).descriptors;
69
+ }
70
+ /** Like `discoverPluginFeatureDescriptors`, with the declaring service. */
71
+ function discoverPluginFeatureProviders(container) {
72
+ return discover(container).providers;
73
+ }
74
+ function findPluginFeatureDescriptor(container, pluginKey) {
75
+ return findPluginFeatureProvider(container, pluginKey)?.descriptor;
76
+ }
77
+ function findPluginFeatureProvider(container, pluginKey) {
78
+ return discover(container).providers.find((provider) => provider.descriptor.plugin_key === pluginKey);
79
+ }
80
+ /**
81
+ * The medusa-config fallback a provider exposes as `pluginSettingsFallback`.
82
+ * Read on every call, since a service may compute it lazily.
83
+ */
84
+ function readPluginSettingsFallback(provider) {
85
+ const service = provider.service;
86
+ if (service === null ||
87
+ typeof service !== "object" ||
88
+ !("pluginSettingsFallback" in service)) {
89
+ return undefined;
90
+ }
91
+ const fallback = service
92
+ .pluginSettingsFallback;
93
+ return fallback !== null && typeof fallback === "object"
94
+ ? fallback
95
+ : undefined;
96
+ }
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.4.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,15 @@
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
+ },
36
+ "./admin-components": {
37
+ "types": "./dist/admin-components/index.d.ts",
38
+ "import": "./dist/admin-components/index.js"
39
+ },
31
40
  "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
32
41
  "./.medusa/server/src/*": "./.medusa/server/src/*",
33
42
  "./admin": {
@@ -41,12 +50,13 @@
41
50
  "@medusajs/js-sdk": "^2.19.0",
42
51
  "@medusajs/medusa": "^2.19.0",
43
52
  "@medusajs/ui": "^4.2.1",
53
+ "@sentry/node": "^10.74.0",
44
54
  "@tanstack/react-query": "^5.64.2",
45
55
  "react": "^18.3.1",
46
56
  "react-router-dom": "^7.18.2"
47
57
  },
48
58
  "dependencies": {
49
- "@hartl-services/medusa-food-supplements-contracts": "^0.11.0"
59
+ "@hartl-services/medusa-food-supplements-contracts": "^0.12.0"
50
60
  },
51
61
  "devDependencies": {
52
62
  "@medusajs/admin-sdk": "2.19.0",
@@ -55,6 +65,7 @@
55
65
  "@medusajs/js-sdk": "2.19.0",
56
66
  "@medusajs/medusa": "2.19.0",
57
67
  "@medusajs/ui": "4.2.1",
68
+ "@sentry/node": "^10.74.0",
58
69
  "@swc/core": "^1.7.28",
59
70
  "@swc/jest": "^0.2.39",
60
71
  "@tanstack/react-query": "5.64.2",
@@ -72,8 +83,13 @@
72
83
  "engines": {
73
84
  "node": ">=20"
74
85
  },
86
+ "peerDependenciesMeta": {
87
+ "@sentry/node": {
88
+ "optional": true
89
+ }
90
+ },
75
91
  "scripts": {
76
- "build": "medusa plugin:build && tsc -p tsconfig.public.json",
92
+ "build": "medusa plugin:build && tsc -p tsconfig.public.json && tsc -p tsconfig.admin-components.json",
77
93
  "dev": "medusa plugin:develop",
78
94
  "typecheck": "tsc --noEmit",
79
95
  "pretest:unit": "pnpm --filter @hartl-services/medusa-food-supplements-contracts build",