@hartl-services/medusa-base 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +3 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginActionsSection.js +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.d.ts +16 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.js +25 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginDetailPage.js +1 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +5 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.js +8 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +4 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.js +24 -27
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +2 -2
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +9 -9
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.js +11 -15
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.d.ts +10 -4
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.js +10 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +0 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.js +73 -26
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -1
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -6
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.d.ts +11 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.js +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +36 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +144 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +36 -5
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.js +66 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +144 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.d.ts +49 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.js +72 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.d.ts +1 -12
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.js +4 -20
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.d.ts +43 -0
- package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +66 -0
- package/.medusa/server/src/admin/admin-components/i18n/texts.d.ts +22 -2
- package/.medusa/server/src/admin/admin-components/i18n/texts.js +29 -7
- package/.medusa/server/src/admin/admin-components/index.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/index.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.d.ts +26 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.js +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.d.ts +23 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.js +20 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.d.ts +21 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.js +33 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.js +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.d.ts +22 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.js +14 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PdfPreviewModal.js +1 -1
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.d.ts +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.js +10 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.d.ts +27 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.js +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.d.ts +15 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.js +8 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.js +16 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.d.ts +13 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.js +11 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.d.ts +12 -0
- package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.js +29 -0
- package/.medusa/server/src/admin/index.js +3123 -2019
- package/.medusa/server/src/admin/index.mjs +3126 -2022
- package/.medusa/server/src/api/admin-first-login-password-policy.js +56 -0
- package/.medusa/server/src/api/answer-actor-type-casing-not-found.js +26 -0
- package/.medusa/server/src/api/answer-code-provider-route-not-found.js +34 -0
- package/.medusa/server/src/api/code-account-password-routes.js +92 -0
- package/.medusa/server/src/api/middlewares.js +82 -17
- package/.medusa/server/src/api/password-policy.js +55 -10
- package/.medusa/server/src/api/require-login-code-customer-email.js +40 -0
- package/.medusa/server/src/api/store/customer-auth/login-code/route.js +17 -0
- package/.medusa/server/src/api/store/customer-auth/password-policy/route.js +19 -0
- package/.medusa/server/src/customer-auth/application/prepare-code-account-password-reset.js +111 -0
- package/.medusa/server/src/customer-auth/application/request-login-code.js +145 -0
- package/.medusa/server/src/customer-auth/customer-auth-methods-snapshot.js +55 -0
- package/.medusa/server/src/customer-auth/customer-password-policy.js +40 -0
- package/.medusa/server/src/customer-auth/domain/account-linking.js +53 -0
- package/.medusa/server/src/customer-auth/domain/auth-methods.js +32 -0
- package/.medusa/server/src/customer-auth/domain/login-code.js +39 -0
- package/.medusa/server/src/customer-auth/login-code-accounts.js +146 -0
- package/.medusa/server/src/customer-auth/login-code-state.js +139 -0
- package/.medusa/server/src/customer-auth/plugin-features.js +80 -0
- package/.medusa/server/src/customer-auth/remove-provider-identity.js +18 -0
- package/.medusa/server/src/customer-auth/start-customer-auth-methods.js +20 -0
- package/.medusa/server/src/documents/plugin-features.js +8 -2
- package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +186 -0
- package/.medusa/server/src/email/application/deliver-login-code.js +89 -0
- package/.medusa/server/src/email/application/handle-customer-verification-event.js +29 -168
- package/.medusa/server/src/email/application/handle-password-reset-event.js +27 -166
- package/.medusa/server/src/email/customer-verification-snapshot.js +20 -108
- package/.medusa/server/src/email/domain/business-keys.js +15 -2
- package/.medusa/server/src/email/plugin-features.js +8 -3
- package/.medusa/server/src/email/rendering/default-templates.js +2 -1
- package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +19 -1
- package/.medusa/server/src/email/templates/defaults/documents.js +7 -1
- package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
- package/.medusa/server/src/email/templates/defaults/texts.js +5 -2
- package/.medusa/server/src/email/templates/render.js +2 -1
- package/.medusa/server/src/email/templates/sample-data.js +2 -1
- package/.medusa/server/src/email/templates/validate.js +8 -3
- package/.medusa/server/src/email/types/email-kinds.js +4 -2
- package/.medusa/server/src/email/types/template-data.js +9 -1
- package/.medusa/server/src/modules/customer-auth/index.js +14 -0
- package/.medusa/server/src/modules/customer-auth/service.js +35 -0
- package/.medusa/server/src/modules/email/migrations/Migration20261010090000.js +20 -0
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +4 -1
- package/.medusa/server/src/modules/plugin-features/service.js +26 -1
- package/.medusa/server/src/providers/email-otp/index.js +8 -0
- package/.medusa/server/src/providers/email-otp/service.js +96 -0
- package/.medusa/server/src/public/descriptor.js +117 -5
- package/.medusa/server/src/public/effective-features.js +22 -1
- package/.medusa/server/src/public/index.js +4 -2
- package/.medusa/server/src/public/plugin-icons.js +28 -0
- package/.medusa/server/src/public/shop-master-data.js +33 -14
- package/.medusa/server/src/shared/lib/index.js +2 -1
- package/.medusa/server/src/shared/lib/refreshing-snapshot.js +122 -0
- package/.medusa/server/src/shop-config/build-food-shop-config.js +38 -1
- package/README.md +436 -87
- package/dist/customer-auth/customer-auth-methods-snapshot.d.ts +25 -0
- package/dist/customer-auth/customer-auth-methods-snapshot.js +54 -0
- package/dist/customer-auth/domain/auth-methods.d.ts +16 -0
- package/dist/customer-auth/domain/auth-methods.js +31 -0
- package/dist/customer-auth/plugin-features.d.ts +2 -0
- package/dist/customer-auth/plugin-features.js +79 -0
- package/dist/email/customer-verification-snapshot.d.ts +3 -15
- package/dist/email/customer-verification-snapshot.js +19 -107
- package/dist/email/types/email-kinds.d.ts +4 -3
- package/dist/email/types/email-kinds.js +3 -1
- package/dist/email/types/template-data.d.ts +8 -0
- package/dist/email/types/template-data.js +8 -0
- package/dist/public/descriptor.js +116 -4
- package/dist/public/effective-features.d.ts +10 -0
- package/dist/public/effective-features.js +21 -0
- package/dist/public/index.d.ts +2 -1
- package/dist/public/index.js +3 -1
- package/dist/public/plugin-icons.d.ts +7 -0
- package/dist/public/plugin-icons.js +27 -0
- package/dist/public/shop-master-data.js +32 -13
- package/dist/public/types.d.ts +63 -1
- package/dist/shared/lib/index.d.ts +1 -0
- package/dist/shared/lib/index.js +1 -0
- package/dist/shared/lib/refreshing-snapshot.d.ts +52 -0
- package/dist/shared/lib/refreshing-snapshot.js +121 -0
- package/dist/shop-config/build-food-shop-config.js +37 -0
- package/package.json +8 -3
|
@@ -4,6 +4,7 @@ exports.validatePluginFeatureDescriptor = validatePluginFeatureDescriptor;
|
|
|
4
4
|
exports.definePluginFeatures = definePluginFeatures;
|
|
5
5
|
const utils_1 = require("@medusajs/framework/utils");
|
|
6
6
|
const lib_1 = require("../shared/lib");
|
|
7
|
+
const plugin_icons_1 = require("./plugin-icons");
|
|
7
8
|
const KEY_PATTERN = /^[a-z][a-z0-9_]{0,62}$/;
|
|
8
9
|
const SETTING_TYPES = new Set([
|
|
9
10
|
"string",
|
|
@@ -20,6 +21,10 @@ const ACTION_MODES = new Set(["sync", "background"]);
|
|
|
20
21
|
const ADMIN_PAGE_PREFIX = "/settings/hartl-services/";
|
|
21
22
|
const BASE_PAGE_PREFIX = "/settings/hartl-services/plugin/";
|
|
22
23
|
const ADMIN_PAGE_PATTERN = /^\/settings\/hartl-services(\/[a-z0-9][a-z0-9_-]*)+$/;
|
|
24
|
+
const ICON_NAMES = new Set(plugin_icons_1.PLUGIN_ICON_NAMES);
|
|
25
|
+
const SETTING_WIDTHS = new Set(["full", "half"]);
|
|
26
|
+
/** Types an input placeholder makes no sense for. */
|
|
27
|
+
const NO_PLACEHOLDER_TYPES = new Set(["boolean", "select", "image", "color"]);
|
|
23
28
|
const invalid = (pluginKey, message) => new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, `Ungültige Funktionsschalter für Plugin "${pluginKey}": ${message}`);
|
|
24
29
|
const defaultMatchesType = (setting) => {
|
|
25
30
|
const value = setting.default;
|
|
@@ -32,7 +37,9 @@ const defaultMatchesType = (setting) => {
|
|
|
32
37
|
case "color":
|
|
33
38
|
return typeof value === "string" && HEX_COLOR_PATTERN.test(value);
|
|
34
39
|
case "number":
|
|
35
|
-
return typeof value === "number" &&
|
|
40
|
+
return (typeof value === "number" &&
|
|
41
|
+
Number.isFinite(value) &&
|
|
42
|
+
(setting.integer !== true || Number.isInteger(value)));
|
|
36
43
|
case "boolean":
|
|
37
44
|
return typeof value === "boolean";
|
|
38
45
|
case "select":
|
|
@@ -46,7 +53,10 @@ const withinBounds = (setting) => typeof setting.default !== "number" ||
|
|
|
46
53
|
((setting.min === undefined || setting.default >= setting.min) &&
|
|
47
54
|
(setting.max === undefined || setting.default <= setting.max));
|
|
48
55
|
const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
49
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* `options` only (and required) for select, `min`/`max`/`integer` only for
|
|
58
|
+
* number.
|
|
59
|
+
*/
|
|
50
60
|
const validateTypeExtras = (pluginKey, setting) => {
|
|
51
61
|
if (setting.type === "select") {
|
|
52
62
|
const options = setting.options;
|
|
@@ -68,11 +78,16 @@ const validateTypeExtras = (pluginKey, setting) => {
|
|
|
68
78
|
throw invalid(pluginKey, `Einstellung "${setting.key}": options gibt es nur bei "select".`);
|
|
69
79
|
}
|
|
70
80
|
if (setting.type !== "number") {
|
|
71
|
-
if (setting.min !== undefined ||
|
|
72
|
-
|
|
81
|
+
if (setting.min !== undefined ||
|
|
82
|
+
setting.max !== undefined ||
|
|
83
|
+
setting.integer !== undefined) {
|
|
84
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": min/max/integer gibt es nur bei "number".`);
|
|
73
85
|
}
|
|
74
86
|
return;
|
|
75
87
|
}
|
|
88
|
+
if (setting.integer !== undefined && typeof setting.integer !== "boolean") {
|
|
89
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": integer muss boolean sein.`);
|
|
90
|
+
}
|
|
76
91
|
for (const [name, bound] of [
|
|
77
92
|
["min", setting.min],
|
|
78
93
|
["max", setting.max],
|
|
@@ -111,6 +126,28 @@ const validateInvalidates = (pluginKey, setting, settings) => {
|
|
|
111
126
|
seen.add(key);
|
|
112
127
|
}
|
|
113
128
|
};
|
|
129
|
+
/** `hint`, `placeholder`, `section` and `width` of one setting. */
|
|
130
|
+
const validateDisplayFields = (pluginKey, setting) => {
|
|
131
|
+
for (const name of ["hint", "placeholder", "section"]) {
|
|
132
|
+
const value = setting[name];
|
|
133
|
+
if (value !== undefined && !(0, lib_1.isNonEmptyString)(value)) {
|
|
134
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": ${name} muss ein nicht-leerer Text sein.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (setting.placeholder !== undefined && NO_PLACEHOLDER_TYPES.has(setting.type)) {
|
|
138
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": placeholder gibt es nicht bei "${setting.type}".`);
|
|
139
|
+
}
|
|
140
|
+
const width = setting.width;
|
|
141
|
+
if (width === undefined) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (typeof width !== "string" || !SETTING_WIDTHS.has(width)) {
|
|
145
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": width muss "full" oder "half" sein.`);
|
|
146
|
+
}
|
|
147
|
+
if (setting.type === "boolean") {
|
|
148
|
+
throw invalid(pluginKey, `Einstellung "${setting.key}": width gibt es nicht bei "boolean".`);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
114
151
|
const validateSettings = (pluginKey, settings) => {
|
|
115
152
|
const seen = new Set();
|
|
116
153
|
for (const setting of settings) {
|
|
@@ -134,6 +171,7 @@ const validateSettings = (pluginKey, settings) => {
|
|
|
134
171
|
throw invalid(pluginKey, `Einstellung "${setting.key}": required muss boolean sein.`);
|
|
135
172
|
}
|
|
136
173
|
validateTypeExtras(pluginKey, setting);
|
|
174
|
+
validateDisplayFields(pluginKey, setting);
|
|
137
175
|
validateInvalidates(pluginKey, setting, settings);
|
|
138
176
|
if (setting.default === undefined) {
|
|
139
177
|
continue;
|
|
@@ -184,6 +222,78 @@ const validateAdminPage = (pluginKey, path) => {
|
|
|
184
222
|
throw invalid(pluginKey, `admin_page "${String(path)}" muss unter "${ADMIN_PAGE_PREFIX}<plugin>" liegen und darf nicht mit "${BASE_PAGE_PREFIX}" beginnen.`);
|
|
185
223
|
}
|
|
186
224
|
};
|
|
225
|
+
/**
|
|
226
|
+
* Metadata per settings group: unique names, each used by a setting's
|
|
227
|
+
* `group`, `requires_feature` a declared feature key.
|
|
228
|
+
*/
|
|
229
|
+
const validateSettingGroups = (pluginKey, groups, settings, featureKeys) => {
|
|
230
|
+
if (groups === undefined) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!Array.isArray(groups)) {
|
|
234
|
+
throw invalid(pluginKey, "setting_groups muss eine Liste sein.");
|
|
235
|
+
}
|
|
236
|
+
const usedGroups = new Set(settings.map((setting) => setting.group));
|
|
237
|
+
const seen = new Set();
|
|
238
|
+
for (const group of groups) {
|
|
239
|
+
if (!(0, lib_1.isNonEmptyString)(group?.name)) {
|
|
240
|
+
throw invalid(pluginKey, "Eine Einstellungsgruppe hat keinen name.");
|
|
241
|
+
}
|
|
242
|
+
if (seen.has(group.name)) {
|
|
243
|
+
throw invalid(pluginKey, `Einstellungsgruppe "${group.name}" ist doppelt.`);
|
|
244
|
+
}
|
|
245
|
+
seen.add(group.name);
|
|
246
|
+
if (!usedGroups.has(group.name)) {
|
|
247
|
+
throw invalid(pluginKey, `Einstellungsgruppe "${group.name}" wird von keiner Einstellung verwendet.`);
|
|
248
|
+
}
|
|
249
|
+
for (const name of ["description", "note"]) {
|
|
250
|
+
if (group[name] !== undefined && !(0, lib_1.isNonEmptyString)(group[name])) {
|
|
251
|
+
throw invalid(pluginKey, `Einstellungsgruppe "${group.name}": ${name} muss ein nicht-leerer Text sein.`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (group.requires_feature !== undefined &&
|
|
255
|
+
!featureKeys.has(group.requires_feature)) {
|
|
256
|
+
throw invalid(pluginKey, `Einstellungsgruppe "${group.name}": requires_feature "${String(group.requires_feature)}" ist kein Feature dieses Plugins.`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
/** Optional texts and icon of the plugin's Admin page. */
|
|
261
|
+
const validatePresentation = (pluginKey, descriptor) => {
|
|
262
|
+
const icon = descriptor.icon;
|
|
263
|
+
if (icon !== undefined && (typeof icon !== "string" || !ICON_NAMES.has(icon))) {
|
|
264
|
+
throw invalid(pluginKey, `icon "${String(icon)}" ist kein bekannter Icon-Name.`);
|
|
265
|
+
}
|
|
266
|
+
for (const name of [
|
|
267
|
+
"summary",
|
|
268
|
+
"disable_hint",
|
|
269
|
+
"disable_confirm",
|
|
270
|
+
"features_heading",
|
|
271
|
+
"features_note",
|
|
272
|
+
"all_off_warning",
|
|
273
|
+
]) {
|
|
274
|
+
const value = descriptor[name];
|
|
275
|
+
if (value !== undefined && !(0, lib_1.isNonEmptyString)(value)) {
|
|
276
|
+
throw invalid(pluginKey, `${name} muss ein nicht-leerer Text sein.`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (descriptor.switchable === false &&
|
|
280
|
+
(descriptor.disable_hint !== undefined ||
|
|
281
|
+
descriptor.disable_confirm !== undefined)) {
|
|
282
|
+
throw invalid(pluginKey, "disable_hint und disable_confirm gibt es nur bei abschaltbaren Plugins.");
|
|
283
|
+
}
|
|
284
|
+
if (descriptor.all_off_warning !== undefined && descriptor.features.length === 0) {
|
|
285
|
+
throw invalid(pluginKey, "all_off_warning braucht mindestens ein Feature.");
|
|
286
|
+
}
|
|
287
|
+
const requireEnabled = descriptor.require_enabled_feature;
|
|
288
|
+
if (requireEnabled !== undefined) {
|
|
289
|
+
if (requireEnabled !== true) {
|
|
290
|
+
throw invalid(pluginKey, "require_enabled_feature muss true sein.");
|
|
291
|
+
}
|
|
292
|
+
if (!descriptor.features.some((feature) => feature.parent_key === undefined && feature.default_enabled)) {
|
|
293
|
+
throw invalid(pluginKey, "require_enabled_feature braucht mindestens ein Feature ohne parent_key mit default_enabled: true.");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
187
297
|
/**
|
|
188
298
|
* Checks key format, unique feature keys, existing parents and the single
|
|
189
299
|
* allowed nesting level, plus the optional settings, actions and Admin page.
|
|
@@ -236,7 +346,9 @@ function validatePluginFeatureDescriptor(descriptor) {
|
|
|
236
346
|
throw invalid(pluginKey, `Feature "${feature.key}" ist zu tief verschachtelt; erlaubt ist eine Ebene.`);
|
|
237
347
|
}
|
|
238
348
|
}
|
|
349
|
+
validatePresentation(pluginKey, descriptor);
|
|
239
350
|
validateSettings(pluginKey, descriptor.settings ?? []);
|
|
351
|
+
validateSettingGroups(pluginKey, descriptor.setting_groups, descriptor.settings ?? [], new Set(byKey.keys()));
|
|
240
352
|
validateActions(pluginKey, descriptor.actions ?? []);
|
|
241
353
|
validateAdminPage(pluginKey, descriptor.admin_page);
|
|
242
354
|
}
|
|
@@ -27,3 +27,13 @@ export declare function resolveEffectivePluginFeatures(descriptor: PluginFeature
|
|
|
27
27
|
* default).
|
|
28
28
|
*/
|
|
29
29
|
export declare function validateFeatureUpdate(descriptor: PluginFeatureDescriptor, features: Record<string, boolean>): Record<string, boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* For a descriptor with `require_enabled_feature`: rejects (`INVALID_DATA`)
|
|
32
|
+
* an update that switches off the last enabled top-level feature. `features`
|
|
33
|
+
* are the values to persist (`validateFeatureUpdate`; a key without a value
|
|
34
|
+
* counts by its default), `current` the configured values before the update
|
|
35
|
+
* (`resolvePluginFeatureStates`). An update that does not reduce the enabled
|
|
36
|
+
* set is always allowed, so an already stored all-off state can be left and
|
|
37
|
+
* the plugin switch can be flipped with unchanged features.
|
|
38
|
+
*/
|
|
39
|
+
export declare function assertRequiredFeatureEnabled(descriptor: PluginFeatureDescriptor, features: Record<string, boolean>, current: Record<string, boolean>): void;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.resolvePluginFeatureStates = resolvePluginFeatureStates;
|
|
4
4
|
exports.resolveEffectivePluginFeatures = resolveEffectivePluginFeatures;
|
|
5
5
|
exports.validateFeatureUpdate = validateFeatureUpdate;
|
|
6
|
+
exports.assertRequiredFeatureEnabled = assertRequiredFeatureEnabled;
|
|
6
7
|
const utils_1 = require("@medusajs/framework/utils");
|
|
7
8
|
/**
|
|
8
9
|
* Configured and effective values of every declared feature. A descriptor
|
|
@@ -75,3 +76,23 @@ function validateFeatureUpdate(descriptor, features) {
|
|
|
75
76
|
}
|
|
76
77
|
return result;
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* For a descriptor with `require_enabled_feature`: rejects (`INVALID_DATA`)
|
|
81
|
+
* an update that switches off the last enabled top-level feature. `features`
|
|
82
|
+
* are the values to persist (`validateFeatureUpdate`; a key without a value
|
|
83
|
+
* counts by its default), `current` the configured values before the update
|
|
84
|
+
* (`resolvePluginFeatureStates`). An update that does not reduce the enabled
|
|
85
|
+
* set is always allowed, so an already stored all-off state can be left and
|
|
86
|
+
* the plugin switch can be flipped with unchanged features.
|
|
87
|
+
*/
|
|
88
|
+
function assertRequiredFeatureEnabled(descriptor, features, current) {
|
|
89
|
+
if (descriptor.require_enabled_feature !== true) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const topLevel = descriptor.features.filter((feature) => feature.parent_key === undefined);
|
|
93
|
+
const anyOnAfter = topLevel.some((feature) => (features[feature.key] ?? feature.default_enabled) === true);
|
|
94
|
+
const anyOnBefore = topLevel.some((feature) => current[feature.key] === true);
|
|
95
|
+
if (!anyOnAfter && anyOnBefore) {
|
|
96
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Mindestens eine Funktion muss eingeschaltet bleiben.");
|
|
97
|
+
}
|
|
98
|
+
}
|
package/dist/public/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@ export { definePluginFeatures } from "./descriptor";
|
|
|
3
3
|
export { resolveEffectivePluginFeatures } from "./effective-features";
|
|
4
4
|
export { isOwnPluginEvent, isPluginFeatureEnabled, isPluginFeatureEnabledByKey, pluginFeatureGate, requirePluginFeature, } from "./enforcement";
|
|
5
5
|
export { isBasePluginRegistered, resolveBaseModule } from "./module-access";
|
|
6
|
+
export { PLUGIN_ICON_NAMES } from "./plugin-icons";
|
|
6
7
|
export { getPluginSettings } from "./settings";
|
|
7
8
|
export { getShopAppearance, getShopMasterData, shopMasterDataDescriptor, type ResolvedShopMasterData, type ShopAppearance, type ShopAppearanceColors, type ShopMasterData, type ShopMasterDataKey, } from "./shop-master-data";
|
|
8
9
|
export { reportPluginActionProgress, type PluginActionProgress, } from "./action-progress";
|
|
9
|
-
export type { BasePluginOptions, EffectivePluginFeatures, PluginActionDefinition, PluginActionMode, PluginActionProgressInput, PluginActionWorkflowInput, PluginFeatureDefinition, PluginFeatureDescriptor, PluginFeatureProvider, PluginSettingDefinition, PluginSettingOption, PluginSettingsFallback, PluginSettingType, PluginSettingValue, ResolvedPluginSettings, } from "./types";
|
|
10
|
+
export type { BasePluginOptions, EffectivePluginFeatures, PluginActionDefinition, PluginActionMode, PluginActionProgressInput, PluginActionWorkflowInput, PluginFeatureDefinition, PluginFeatureDescriptor, PluginFeatureProvider, PluginIconName, PluginSettingDefinition, PluginSettingGroupDefinition, PluginSettingOption, PluginSettingsFallback, PluginSettingType, PluginSettingValue, ResolvedPluginSettings, } from "./types";
|
|
10
11
|
export type { CompleteCartHookDeclarations, CompleteCartHookName, CompleteCartOrderCreatedInput, CompleteCartParticipant, CompleteCartParticipantStep, CompleteCartValidateInput, } from "./complete-cart";
|
package/dist/public/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.reportPluginActionProgress = exports.shopMasterDataDescriptor = exports.getShopMasterData = exports.getShopAppearance = exports.getPluginSettings = exports.resolveBaseModule = exports.isBasePluginRegistered = exports.requirePluginFeature = exports.pluginFeatureGate = exports.isPluginFeatureEnabledByKey = exports.isPluginFeatureEnabled = exports.isOwnPluginEvent = exports.resolveEffectivePluginFeatures = exports.definePluginFeatures = exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
3
|
+
exports.reportPluginActionProgress = exports.shopMasterDataDescriptor = exports.getShopMasterData = exports.getShopAppearance = exports.getPluginSettings = exports.PLUGIN_ICON_NAMES = exports.resolveBaseModule = exports.isBasePluginRegistered = exports.requirePluginFeature = exports.pluginFeatureGate = exports.isPluginFeatureEnabledByKey = exports.isPluginFeatureEnabled = exports.isOwnPluginEvent = exports.resolveEffectivePluginFeatures = exports.definePluginFeatures = exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
4
4
|
var constants_1 = require("./constants");
|
|
5
5
|
Object.defineProperty(exports, "PLUGIN_FEATURES_MODULE", { enumerable: true, get: function () { return constants_1.PLUGIN_FEATURES_MODULE; } });
|
|
6
6
|
var descriptor_1 = require("./descriptor");
|
|
@@ -16,6 +16,8 @@ Object.defineProperty(exports, "requirePluginFeature", { enumerable: true, get:
|
|
|
16
16
|
var module_access_1 = require("./module-access");
|
|
17
17
|
Object.defineProperty(exports, "isBasePluginRegistered", { enumerable: true, get: function () { return module_access_1.isBasePluginRegistered; } });
|
|
18
18
|
Object.defineProperty(exports, "resolveBaseModule", { enumerable: true, get: function () { return module_access_1.resolveBaseModule; } });
|
|
19
|
+
var plugin_icons_1 = require("./plugin-icons");
|
|
20
|
+
Object.defineProperty(exports, "PLUGIN_ICON_NAMES", { enumerable: true, get: function () { return plugin_icons_1.PLUGIN_ICON_NAMES; } });
|
|
19
21
|
var settings_1 = require("./settings");
|
|
20
22
|
Object.defineProperty(exports, "getPluginSettings", { enumerable: true, get: function () { return settings_1.getPluginSettings; } });
|
|
21
23
|
var shop_master_data_1 = require("./shop-master-data");
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names of `@medusajs/icons` components a plugin descriptor may use as `icon`.
|
|
3
|
+
* A plain list so the server needs no icon package; the Admin maps the name to
|
|
4
|
+
* the component and falls back to a default icon for an unknown one. Every
|
|
5
|
+
* name is an export of `@medusajs/icons`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PLUGIN_ICON_NAMES: readonly ["ArchiveBox", "ArrowPath", "BuildingStorefront", "Buildings", "ClockSolid", "CogSixTooth", "CreditCard", "DocumentText", "Envelope", "ReceiptPercent", "ShieldCheck", "ShoppingCart", "SquaresPlus", "Tag", "TruckFast", "Users"];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLUGIN_ICON_NAMES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Names of `@medusajs/icons` components a plugin descriptor may use as `icon`.
|
|
6
|
+
* A plain list so the server needs no icon package; the Admin maps the name to
|
|
7
|
+
* the component and falls back to a default icon for an unknown one. Every
|
|
8
|
+
* name is an export of `@medusajs/icons`.
|
|
9
|
+
*/
|
|
10
|
+
exports.PLUGIN_ICON_NAMES = [
|
|
11
|
+
"ArchiveBox",
|
|
12
|
+
"ArrowPath",
|
|
13
|
+
"BuildingStorefront",
|
|
14
|
+
"Buildings",
|
|
15
|
+
"ClockSolid",
|
|
16
|
+
"CogSixTooth",
|
|
17
|
+
"CreditCard",
|
|
18
|
+
"DocumentText",
|
|
19
|
+
"Envelope",
|
|
20
|
+
"ReceiptPercent",
|
|
21
|
+
"ShieldCheck",
|
|
22
|
+
"ShoppingCart",
|
|
23
|
+
"SquaresPlus",
|
|
24
|
+
"Tag",
|
|
25
|
+
"TruckFast",
|
|
26
|
+
"Users",
|
|
27
|
+
];
|
|
@@ -7,6 +7,10 @@ const descriptor_1 = require("./descriptor");
|
|
|
7
7
|
const settings_1 = require("./settings");
|
|
8
8
|
const COMPANY = "Firmendaten";
|
|
9
9
|
const APPEARANCE = "Erscheinungsbild";
|
|
10
|
+
const ADDRESS = "Adresse";
|
|
11
|
+
const TAX_CONTACT = "Steuer und Kontakt";
|
|
12
|
+
const BANK_REGISTER = "Bank und Firmenbuch";
|
|
13
|
+
const COLORS = "Farben";
|
|
10
14
|
const DEFAULT_COLOR_HINT = "Leer lassen für die Standardfarbe von E-Mails und Dokumenten.";
|
|
11
15
|
/**
|
|
12
16
|
* The base's own descriptor: the shop's master data (Firmenangaben) and its
|
|
@@ -18,18 +22,22 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
18
22
|
plugin_key: "shop",
|
|
19
23
|
label: "Shop-Stammdaten",
|
|
20
24
|
description: "Firmenangaben des Shops für E-Mails, Rechnungen, Dokumente und Steuerprüfungen sowie Logo und Farben von E-Mails und Dokumenten.",
|
|
25
|
+
summary: "Firmenangaben, Logo und Farben für E-Mails, Rechnungen, Dokumente und Steuerprüfungen.",
|
|
26
|
+
icon: "Buildings",
|
|
21
27
|
switchable: false,
|
|
22
28
|
features: [],
|
|
23
29
|
settings: [
|
|
24
|
-
{ key: "legal_name", group: COMPANY, label: "Firmenname", type: "string", required: true },
|
|
25
|
-
{ key: "address_line1", group: COMPANY, label: "Adresse", type: "string", required: true },
|
|
26
|
-
{ key: "address_line2", group: COMPANY, label: "Adresszusatz", type: "string" },
|
|
27
|
-
{ key: "postal_code", group: COMPANY, label: "Postleitzahl", type: "string", required: true },
|
|
28
|
-
{ key: "city", group: COMPANY, label: "Ort", type: "string", required: true },
|
|
30
|
+
{ key: "legal_name", group: COMPANY, section: ADDRESS, label: "Firmenname", type: "string", required: true },
|
|
31
|
+
{ key: "address_line1", group: COMPANY, section: ADDRESS, width: "half", label: "Adresse", type: "string", required: true },
|
|
32
|
+
{ key: "address_line2", group: COMPANY, section: ADDRESS, width: "half", label: "Adresszusatz", type: "string" },
|
|
33
|
+
{ key: "postal_code", group: COMPANY, section: ADDRESS, width: "half", label: "Postleitzahl", type: "string", required: true },
|
|
34
|
+
{ key: "city", group: COMPANY, section: ADDRESS, width: "half", label: "Ort", type: "string", required: true },
|
|
29
35
|
{
|
|
30
36
|
key: "country_code",
|
|
31
37
|
group: COMPANY,
|
|
32
38
|
label: "Land",
|
|
39
|
+
section: ADDRESS,
|
|
40
|
+
width: "half",
|
|
33
41
|
type: "select",
|
|
34
42
|
required: true,
|
|
35
43
|
// EU-27 plus CH, GB, NO, IS, LI (ISO 3166-1 alpha-2, lowercase),
|
|
@@ -69,24 +77,27 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
69
77
|
{ value: "cy", label: "Zypern" },
|
|
70
78
|
],
|
|
71
79
|
},
|
|
72
|
-
{ key: "vat_number", group: COMPANY, label: "UID-Nummer", type: "string", required: true },
|
|
73
|
-
{ key: "email", group: COMPANY, label: "E-Mail", type: "string" },
|
|
74
|
-
{ key: "phone", group: COMPANY, label: "Telefon", type: "string" },
|
|
75
|
-
{ key: "website", group: COMPANY, label: "Website", type: "url" },
|
|
76
|
-
{ key: "iban", group: COMPANY, label: "IBAN", type: "string" },
|
|
77
|
-
{ key: "bic", group: COMPANY, label: "BIC", type: "string" },
|
|
78
|
-
{ key: "company_register_number", group: COMPANY, label: "Firmenbuchnummer", type: "string" },
|
|
79
|
-
{ key: "company_register_court", group: COMPANY, label: "Firmenbuchgericht", type: "string" },
|
|
80
|
+
{ key: "vat_number", group: COMPANY, section: TAX_CONTACT, width: "half", label: "UID-Nummer", type: "string", required: true },
|
|
81
|
+
{ key: "email", group: COMPANY, section: TAX_CONTACT, width: "half", label: "E-Mail", type: "string" },
|
|
82
|
+
{ key: "phone", group: COMPANY, section: TAX_CONTACT, width: "half", label: "Telefon", type: "string" },
|
|
83
|
+
{ key: "website", group: COMPANY, section: TAX_CONTACT, width: "half", label: "Website", type: "url" },
|
|
84
|
+
{ key: "iban", group: COMPANY, section: BANK_REGISTER, width: "half", label: "IBAN", type: "string" },
|
|
85
|
+
{ key: "bic", group: COMPANY, section: BANK_REGISTER, width: "half", label: "BIC", type: "string" },
|
|
86
|
+
{ key: "company_register_number", group: COMPANY, section: BANK_REGISTER, width: "half", label: "Firmenbuchnummer", type: "string" },
|
|
87
|
+
{ key: "company_register_court", group: COMPANY, section: BANK_REGISTER, width: "half", label: "Firmenbuchgericht", type: "string" },
|
|
80
88
|
{
|
|
81
89
|
key: "logo",
|
|
82
90
|
group: APPEARANCE,
|
|
83
91
|
label: "Logo",
|
|
84
92
|
description: "PNG oder JPG, erscheint oben in den E-Mails und auf Rechnungen und Lieferscheinen. Ohne Logo steht dort der Firmenname bzw. Shopname.",
|
|
93
|
+
hint: "PNG oder JPG, ohne Logo erscheint der Firmenname.",
|
|
85
94
|
type: "image",
|
|
86
95
|
},
|
|
87
96
|
{
|
|
88
97
|
key: "color_accent",
|
|
89
98
|
group: APPEARANCE,
|
|
99
|
+
section: COLORS,
|
|
100
|
+
width: "half",
|
|
90
101
|
label: "Akzentfarbe",
|
|
91
102
|
description: `Buttons, Überschriften und Links. ${DEFAULT_COLOR_HINT}`,
|
|
92
103
|
type: "color",
|
|
@@ -94,6 +105,8 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
94
105
|
{
|
|
95
106
|
key: "color_text",
|
|
96
107
|
group: APPEARANCE,
|
|
108
|
+
section: COLORS,
|
|
109
|
+
width: "half",
|
|
97
110
|
label: "Textfarbe",
|
|
98
111
|
description: `Fließtext und Beträge. ${DEFAULT_COLOR_HINT}`,
|
|
99
112
|
type: "color",
|
|
@@ -101,6 +114,8 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
101
114
|
{
|
|
102
115
|
key: "color_muted",
|
|
103
116
|
group: APPEARANCE,
|
|
117
|
+
section: COLORS,
|
|
118
|
+
width: "half",
|
|
104
119
|
label: "Nebentextfarbe",
|
|
105
120
|
description: `Beschriftungen, Hinweise und Fußzeilen. ${DEFAULT_COLOR_HINT}`,
|
|
106
121
|
type: "color",
|
|
@@ -108,6 +123,8 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
108
123
|
{
|
|
109
124
|
key: "color_line",
|
|
110
125
|
group: APPEARANCE,
|
|
126
|
+
section: COLORS,
|
|
127
|
+
width: "half",
|
|
111
128
|
label: "Linienfarbe",
|
|
112
129
|
description: `Rahmen und Trennlinien. ${DEFAULT_COLOR_HINT}`,
|
|
113
130
|
type: "color",
|
|
@@ -115,6 +132,8 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
115
132
|
{
|
|
116
133
|
key: "color_surface",
|
|
117
134
|
group: APPEARANCE,
|
|
135
|
+
section: COLORS,
|
|
136
|
+
width: "half",
|
|
118
137
|
label: "Flächenfarbe",
|
|
119
138
|
description: `Hinterlegte Flächen wie Infoboxen und Tabellenköpfe. ${DEFAULT_COLOR_HINT}`,
|
|
120
139
|
type: "color",
|
package/dist/public/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DocumentPluginOptions } from "../modules/documents/domain/options";
|
|
2
2
|
import type { EmailPluginOptions } from "../email/types";
|
|
3
3
|
import type { FilesPluginOptions } from "../files/types";
|
|
4
|
+
import type { PLUGIN_ICON_NAMES } from "./plugin-icons";
|
|
4
5
|
/**
|
|
5
6
|
* Options of the base plugin (`plugins: [{ resolve, options }]`). Medusa hands
|
|
6
7
|
* the same options object to every module of the plugin; each module service
|
|
@@ -23,6 +24,8 @@ export type BasePluginOptions = {
|
|
|
23
24
|
/** `/files` gateway signing; set from `createS3CacheStorageConfig`. */
|
|
24
25
|
readonly files?: FilesPluginOptions;
|
|
25
26
|
};
|
|
27
|
+
/** `@medusajs/icons` component name a plugin may use as its icon. */
|
|
28
|
+
export type PluginIconName = (typeof PLUGIN_ICON_NAMES)[number];
|
|
26
29
|
export type PluginFeatureDefinition<K extends string = string> = {
|
|
27
30
|
readonly key: K;
|
|
28
31
|
readonly label: string;
|
|
@@ -53,7 +56,7 @@ export type PluginSettingDefinition<S extends string = string> = {
|
|
|
53
56
|
readonly type: PluginSettingType;
|
|
54
57
|
/** `false` when omitted. */
|
|
55
58
|
readonly required?: boolean;
|
|
56
|
-
/** Never for `"secret"`; must match `type` (and `options`/`min`/`max`). */
|
|
59
|
+
/** Never for `"secret"`; must match `type` (and `options`/`min`/`max`/`integer`). */
|
|
57
60
|
readonly default?: PluginSettingValue;
|
|
58
61
|
/** Required for `"select"` (non-empty, unique values); only there. */
|
|
59
62
|
readonly options?: readonly PluginSettingOption[];
|
|
@@ -61,12 +64,44 @@ export type PluginSettingDefinition<S extends string = string> = {
|
|
|
61
64
|
readonly min?: number;
|
|
62
65
|
/** Inclusive upper bound; only for `"number"`. */
|
|
63
66
|
readonly max?: number;
|
|
67
|
+
/** Only whole numbers are accepted; only for `"number"`. */
|
|
68
|
+
readonly integer?: boolean;
|
|
64
69
|
/**
|
|
65
70
|
* Secret settings of the same plugin whose stored values are deleted when
|
|
66
71
|
* this setting's stored value changes (also when it is cleared), e.g. an
|
|
67
72
|
* API key that must not be sent to a new URL.
|
|
68
73
|
*/
|
|
69
74
|
readonly invalidates?: readonly S[];
|
|
75
|
+
/**
|
|
76
|
+
* Short text under the field; the Admin shows `hint ?? description`.
|
|
77
|
+
*/
|
|
78
|
+
readonly hint?: string;
|
|
79
|
+
/** Input placeholder; not for boolean, select, image and color. */
|
|
80
|
+
readonly placeholder?: string;
|
|
81
|
+
/**
|
|
82
|
+
* `"half"`: consecutive half-width settings share one two-column row.
|
|
83
|
+
* `"full"` when omitted. Not for boolean (always a full-width switch row).
|
|
84
|
+
*/
|
|
85
|
+
readonly width?: "full" | "half";
|
|
86
|
+
/**
|
|
87
|
+
* Sub-heading inside the group; consecutive settings with the same section
|
|
88
|
+
* share one sub-heading.
|
|
89
|
+
*/
|
|
90
|
+
readonly section?: string;
|
|
91
|
+
};
|
|
92
|
+
/** Metadata of one settings group, keyed by the `group` name of its settings. */
|
|
93
|
+
export type PluginSettingGroupDefinition<K extends string = string> = {
|
|
94
|
+
/** Equals the `group` of at least one setting; unique per plugin. */
|
|
95
|
+
readonly name: string;
|
|
96
|
+
/** Explanatory text under the group heading. */
|
|
97
|
+
readonly description?: string;
|
|
98
|
+
/** Short note shown on the right of the group heading. */
|
|
99
|
+
readonly note?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Feature that must be on for the group to apply; the Admin dims the group
|
|
102
|
+
* (still editable) while the feature is not effectively enabled.
|
|
103
|
+
*/
|
|
104
|
+
readonly requires_feature?: K;
|
|
70
105
|
};
|
|
71
106
|
export type PluginActionMode = "sync" | "background";
|
|
72
107
|
export type PluginActionDefinition<A extends string = string> = {
|
|
@@ -100,6 +135,33 @@ export type PluginFeatureDescriptor<K extends string = string, S extends string
|
|
|
100
135
|
* omitted).
|
|
101
136
|
*/
|
|
102
137
|
readonly admin_page?: string;
|
|
138
|
+
/** `@medusajs/icons` component name for the hub card and the page header. */
|
|
139
|
+
readonly icon?: PluginIconName;
|
|
140
|
+
/** One short sentence: hub card text and page subtitle (Admin falls back to `description`). */
|
|
141
|
+
readonly summary?: string;
|
|
142
|
+
/** Line under the plugin switch, e.g. what stops working when it is off. Only when switchable. */
|
|
143
|
+
readonly disable_hint?: string;
|
|
144
|
+
/** Body of the confirmation shown before the plugin is switched off. Only when switchable. */
|
|
145
|
+
readonly disable_confirm?: string;
|
|
146
|
+
/** Heading of the features card; "Funktionen" when omitted. */
|
|
147
|
+
readonly features_heading?: string;
|
|
148
|
+
/** Note on the right of the features card header. */
|
|
149
|
+
readonly features_note?: string;
|
|
150
|
+
/** Warning shown while every top-level feature is off. Needs at least one feature. */
|
|
151
|
+
readonly all_off_warning?: string;
|
|
152
|
+
/**
|
|
153
|
+
* `true`: at least one top-level feature (without `parent_key`) must stay
|
|
154
|
+
* enabled; an update that would switch all of them off is rejected with
|
|
155
|
+
* `INVALID_DATA` and the Admin locks the last enabled switch. Needs at least
|
|
156
|
+
* one top-level feature with `default_enabled: true`. Only an update that
|
|
157
|
+
* switches off the last enabled one is rejected: a stored state that already
|
|
158
|
+
* violates the rule (everything off) can still be read and left, and an
|
|
159
|
+
* update that does not reduce the enabled set (e.g. the plugin switch) is
|
|
160
|
+
* always allowed.
|
|
161
|
+
*/
|
|
162
|
+
readonly require_enabled_feature?: true;
|
|
163
|
+
/** Metadata for the groups used in `settings[].group`. */
|
|
164
|
+
readonly setting_groups?: readonly PluginSettingGroupDefinition<K>[];
|
|
103
165
|
};
|
|
104
166
|
/** Values a plugin received from medusa-config; the base shows them as source "config". */
|
|
105
167
|
export type PluginSettingsFallback<S extends string = string> = Partial<Record<S, PluginSettingValue | null | undefined>>;
|
package/dist/shared/lib/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./errors"), exports);
|
|
|
23
23
|
__exportStar(require("./hash"), exports);
|
|
24
24
|
__exportStar(require("./logging"), exports);
|
|
25
25
|
__exportStar(require("./money"), exports);
|
|
26
|
+
__exportStar(require("./refreshing-snapshot"), exports);
|
|
26
27
|
__exportStar(require("./text"), exports);
|
|
27
28
|
__exportStar(require("./transaction"), exports);
|
|
28
29
|
__exportStar(require("./wire-response"), exports);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory copy of one value for the places that cannot await it: Medusa
|
|
3
|
+
* reads some `projectConfig.http` settings synchronously on every login, and
|
|
4
|
+
* the shop preset puts a getter backed by such a copy there, so a change in
|
|
5
|
+
* the Admin takes effect without a restart.
|
|
6
|
+
*
|
|
7
|
+
* - Boot: `start` loads the value once.
|
|
8
|
+
* - Afterwards an unref'd timer refreshes every 30 s, so a change reaches an
|
|
9
|
+
* idle instance too. A read that finds the copy older than 30 s (a blocked
|
|
10
|
+
* timer, say) also starts one refresh in the background
|
|
11
|
+
* (stale-while-revalidate) and still answers from the copy. A change thus
|
|
12
|
+
* reaches a running instance within about 30 s plus the duration of the
|
|
13
|
+
* read.
|
|
14
|
+
* - A failed read keeps the last value (initially `initial`) and is logged;
|
|
15
|
+
* the next attempt waits another 30 s. Neither a throwing reader nor a
|
|
16
|
+
* throwing logger escapes.
|
|
17
|
+
*
|
|
18
|
+
* The state lives on `globalThis` under a `Symbol.for(key)`: the preset
|
|
19
|
+
* (`dist`) and the Medusa-loaded plugin code (`.medusa/server`) are compiled
|
|
20
|
+
* separately and would otherwise each hold their own copy.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SNAPSHOT_REFRESH_MS = 30000;
|
|
23
|
+
export type RefreshingSnapshot<T> = Readonly<{
|
|
24
|
+
/**
|
|
25
|
+
* Registers the reader, loads the value once and keeps it fresh every 30 s
|
|
26
|
+
* (application start). `warn` is the container logger. Starting again
|
|
27
|
+
* replaces the reader and the timer. Never rejects.
|
|
28
|
+
*/
|
|
29
|
+
start: (input: Readonly<{
|
|
30
|
+
read: () => Promise<T>;
|
|
31
|
+
warn: (message: string) => void;
|
|
32
|
+
}>) => Promise<void>;
|
|
33
|
+
/** Reads the value once; never rejects. Concurrent calls share one read. */
|
|
34
|
+
refresh: () => Promise<void>;
|
|
35
|
+
/** Stops the periodic refresh (application shutdown). The value stays. */
|
|
36
|
+
stop: () => void;
|
|
37
|
+
/** Synchronous; starts a background refresh when the copy is stale. Never throws. */
|
|
38
|
+
current: () => T;
|
|
39
|
+
/**
|
|
40
|
+
* Test hook: sets the value and counts as just refreshed, so it holds for
|
|
41
|
+
* 30 s without a reader; `null` forgets reader and value (back to `initial`).
|
|
42
|
+
*/
|
|
43
|
+
setForTests: (value: T | null) => void;
|
|
44
|
+
}>;
|
|
45
|
+
export declare const createRefreshingSnapshot: <T>(options: Readonly<{
|
|
46
|
+
/** Global registry key; one key = one shared state per process. */
|
|
47
|
+
key: string;
|
|
48
|
+
/** Start and fallback value: used until the first read and after a failed one. */
|
|
49
|
+
initial: T;
|
|
50
|
+
/** What the value is, for the failure log. */
|
|
51
|
+
describe: (value: T) => string;
|
|
52
|
+
}>) => RefreshingSnapshot<T>;
|