@hartl-services/medusa-base 2.6.1 → 2.8.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 (151) hide show
  1. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +3 -2
  2. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ImageSettingInput.js +4 -4
  3. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginActionsSection.js +4 -4
  4. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.d.ts +16 -0
  5. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginBreadcrumb.js +25 -0
  6. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginDetailPage.js +1 -1
  7. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.d.ts +5 -4
  8. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeatureSections.js +8 -5
  9. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.d.ts +4 -4
  10. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesCard.js +24 -27
  11. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.d.ts +2 -2
  12. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginFeaturesSettingsPage.js +9 -9
  13. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.d.ts +5 -1
  14. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginHubCard.js +11 -15
  15. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.d.ts +10 -4
  16. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginPageShell.js +10 -5
  17. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.d.ts +0 -1
  18. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.js +77 -26
  19. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.d.ts +5 -1
  20. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginStatusChips.js +8 -6
  21. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.d.ts +11 -0
  22. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/ShopMasterDataBanner.js +15 -0
  23. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +40 -5
  24. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +160 -20
  25. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +40 -5
  26. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.d.ts +15 -0
  27. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSwitch.js +66 -0
  28. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +160 -20
  29. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.d.ts +49 -0
  30. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.js +72 -0
  31. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.d.ts +1 -12
  32. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-draft.js +4 -20
  33. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.d.ts +47 -0
  34. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +70 -0
  35. package/.medusa/server/src/admin/admin-components/i18n/texts.d.ts +22 -2
  36. package/.medusa/server/src/admin/admin-components/i18n/texts.js +29 -7
  37. package/.medusa/server/src/admin/admin-components/index.d.ts +12 -0
  38. package/.medusa/server/src/admin/admin-components/index.js +11 -0
  39. package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.d.ts +22 -0
  40. package/.medusa/server/src/admin/admin-components/shared/ui/AdminPageHeader.js +8 -0
  41. package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.d.ts +26 -0
  42. package/.medusa/server/src/admin/admin-components/shared/ui/AdminSection.js +12 -0
  43. package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.d.ts +23 -0
  44. package/.medusa/server/src/admin/admin-components/shared/ui/BreadcrumbTrail.js +20 -0
  45. package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.d.ts +21 -0
  46. package/.medusa/server/src/admin/admin-components/shared/ui/FileDropField.js +33 -0
  47. package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.d.ts +27 -0
  48. package/.medusa/server/src/admin/admin-components/shared/ui/FocusModalShell.js +13 -0
  49. package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.d.ts +13 -0
  50. package/.medusa/server/src/admin/admin-components/shared/ui/InfoTooltip.js +8 -0
  51. package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.d.ts +22 -0
  52. package/.medusa/server/src/admin/admin-components/shared/ui/MultiSelectDropdown.js +14 -0
  53. package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.d.ts +10 -0
  54. package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.js +10 -0
  55. package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.d.ts +27 -0
  56. package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.js +15 -0
  57. package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.d.ts +15 -0
  58. package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.js +8 -0
  59. package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.d.ts +13 -0
  60. package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.js +16 -0
  61. package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.d.ts +13 -0
  62. package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.js +11 -0
  63. package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.d.ts +12 -0
  64. package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.js +29 -0
  65. package/.medusa/server/src/admin/index.js +3155 -2039
  66. package/.medusa/server/src/admin/index.mjs +3158 -2042
  67. package/.medusa/server/src/api/admin-first-login-password-policy.js +56 -0
  68. package/.medusa/server/src/api/answer-actor-type-casing-not-found.js +26 -0
  69. package/.medusa/server/src/api/answer-code-provider-route-not-found.js +34 -0
  70. package/.medusa/server/src/api/code-account-password-routes.js +92 -0
  71. package/.medusa/server/src/api/middlewares.js +82 -17
  72. package/.medusa/server/src/api/password-policy.js +55 -10
  73. package/.medusa/server/src/api/require-login-code-customer-email.js +40 -0
  74. package/.medusa/server/src/api/store/customer-auth/login-code/route.js +17 -0
  75. package/.medusa/server/src/api/store/customer-auth/password-policy/route.js +19 -0
  76. package/.medusa/server/src/customer-auth/application/prepare-code-account-password-reset.js +111 -0
  77. package/.medusa/server/src/customer-auth/application/request-login-code.js +145 -0
  78. package/.medusa/server/src/customer-auth/customer-auth-methods-snapshot.js +55 -0
  79. package/.medusa/server/src/customer-auth/customer-password-policy.js +40 -0
  80. package/.medusa/server/src/customer-auth/domain/account-linking.js +53 -0
  81. package/.medusa/server/src/customer-auth/domain/auth-methods.js +32 -0
  82. package/.medusa/server/src/customer-auth/domain/login-code.js +39 -0
  83. package/.medusa/server/src/customer-auth/login-code-accounts.js +146 -0
  84. package/.medusa/server/src/customer-auth/login-code-state.js +139 -0
  85. package/.medusa/server/src/customer-auth/plugin-features.js +80 -0
  86. package/.medusa/server/src/customer-auth/remove-provider-identity.js +18 -0
  87. package/.medusa/server/src/customer-auth/start-customer-auth-methods.js +20 -0
  88. package/.medusa/server/src/documents/plugin-features.js +8 -2
  89. package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +186 -0
  90. package/.medusa/server/src/email/application/deliver-login-code.js +89 -0
  91. package/.medusa/server/src/email/application/handle-customer-verification-event.js +29 -168
  92. package/.medusa/server/src/email/application/handle-password-reset-event.js +27 -166
  93. package/.medusa/server/src/email/customer-verification-snapshot.js +20 -108
  94. package/.medusa/server/src/email/domain/business-keys.js +15 -2
  95. package/.medusa/server/src/email/plugin-features.js +8 -3
  96. package/.medusa/server/src/email/rendering/default-templates.js +2 -1
  97. package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +19 -1
  98. package/.medusa/server/src/email/templates/defaults/documents.js +7 -1
  99. package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
  100. package/.medusa/server/src/email/templates/defaults/texts.js +5 -2
  101. package/.medusa/server/src/email/templates/render.js +2 -1
  102. package/.medusa/server/src/email/templates/sample-data.js +2 -1
  103. package/.medusa/server/src/email/templates/validate.js +8 -3
  104. package/.medusa/server/src/email/types/email-kinds.js +4 -2
  105. package/.medusa/server/src/email/types/template-data.js +9 -1
  106. package/.medusa/server/src/modules/customer-auth/index.js +14 -0
  107. package/.medusa/server/src/modules/customer-auth/service.js +35 -0
  108. package/.medusa/server/src/modules/email/migrations/Migration20261010090000.js +20 -0
  109. package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +30 -6
  110. package/.medusa/server/src/modules/plugin-features/service.js +29 -3
  111. package/.medusa/server/src/providers/email-otp/index.js +8 -0
  112. package/.medusa/server/src/providers/email-otp/service.js +96 -0
  113. package/.medusa/server/src/public/descriptor.js +138 -5
  114. package/.medusa/server/src/public/effective-features.js +22 -1
  115. package/.medusa/server/src/public/index.js +4 -2
  116. package/.medusa/server/src/public/plugin-icons.js +28 -0
  117. package/.medusa/server/src/public/shop-master-data.js +33 -14
  118. package/.medusa/server/src/shared/lib/dates.js +8 -2
  119. package/.medusa/server/src/shared/lib/index.js +2 -1
  120. package/.medusa/server/src/shared/lib/refreshing-snapshot.js +122 -0
  121. package/.medusa/server/src/shop-config/build-food-shop-config.js +38 -1
  122. package/README.md +436 -87
  123. package/dist/customer-auth/customer-auth-methods-snapshot.d.ts +25 -0
  124. package/dist/customer-auth/customer-auth-methods-snapshot.js +54 -0
  125. package/dist/customer-auth/domain/auth-methods.d.ts +16 -0
  126. package/dist/customer-auth/domain/auth-methods.js +31 -0
  127. package/dist/customer-auth/plugin-features.d.ts +2 -0
  128. package/dist/customer-auth/plugin-features.js +79 -0
  129. package/dist/email/customer-verification-snapshot.d.ts +3 -15
  130. package/dist/email/customer-verification-snapshot.js +19 -107
  131. package/dist/email/types/email-kinds.d.ts +4 -3
  132. package/dist/email/types/email-kinds.js +3 -1
  133. package/dist/email/types/template-data.d.ts +8 -0
  134. package/dist/email/types/template-data.js +8 -0
  135. package/dist/public/descriptor.js +137 -4
  136. package/dist/public/effective-features.d.ts +10 -0
  137. package/dist/public/effective-features.js +21 -0
  138. package/dist/public/index.d.ts +2 -1
  139. package/dist/public/index.js +3 -1
  140. package/dist/public/plugin-icons.d.ts +7 -0
  141. package/dist/public/plugin-icons.js +27 -0
  142. package/dist/public/shop-master-data.js +32 -13
  143. package/dist/public/types.d.ts +74 -2
  144. package/dist/shared/lib/dates.d.ts +2 -0
  145. package/dist/shared/lib/dates.js +7 -1
  146. package/dist/shared/lib/index.d.ts +1 -0
  147. package/dist/shared/lib/index.js +1 -0
  148. package/dist/shared/lib/refreshing-snapshot.d.ts +52 -0
  149. package/dist/shared/lib/refreshing-snapshot.js +121 -0
  150. package/dist/shop-config/build-food-shop-config.js +37 -0
  151. package/package.json +8 -3
@@ -0,0 +1,25 @@
1
+ /** Reads the switches once; never rejects. Concurrent calls share one read. */
2
+ export declare const refreshCustomerAuthMethodsSnapshot: () => Promise<void>;
3
+ /** Stops the periodic refresh (application shutdown). The value stays. */
4
+ export declare const stopCustomerAuthMethodsSnapshot: () => void;
5
+ /**
6
+ * Registers the reader, loads the switches once and keeps them fresh every
7
+ * 30 s (application start). `read` returns the effective feature map of
8
+ * `customer_auth`; `warn` is the container logger.
9
+ */
10
+ export declare const startCustomerAuthMethodsSnapshot: (input: Readonly<{
11
+ read: () => Promise<Readonly<Record<string, boolean>>>;
12
+ warn: (message: string) => void;
13
+ }>) => Promise<void>;
14
+ /**
15
+ * The value of `authMethodsPerActor.customer`: the provider ids a customer
16
+ * may use. Synchronous; starts a background refresh when the copy is stale.
17
+ * A fresh array per call.
18
+ */
19
+ export declare const customerAuthMethods: () => string[];
20
+ /**
21
+ * Test hook: sets the methods and counts as just refreshed, so they hold for
22
+ * 30 s without a reader; `null` forgets reader and value (back to the
23
+ * defaults).
24
+ */
25
+ export declare const setCustomerAuthMethodsForTests: (methods: readonly string[] | null) => void;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setCustomerAuthMethodsForTests = exports.customerAuthMethods = exports.startCustomerAuthMethodsSnapshot = exports.stopCustomerAuthMethodsSnapshot = exports.refreshCustomerAuthMethodsSnapshot = void 0;
4
+ const refreshing_snapshot_1 = require("../shared/lib/refreshing-snapshot");
5
+ const auth_methods_1 = require("./domain/auth-methods");
6
+ const plugin_features_1 = require("./plugin-features");
7
+ /**
8
+ * In-memory copy of the login methods customers may use, for the one place
9
+ * that cannot await them: Medusa reads
10
+ * `projectConfig.http.authMethodsPerActor.customer` synchronously on every
11
+ * request to an `/auth/customer/<provider>` route. The shop preset puts
12
+ * `customerAuthMethods()` behind a getter there, so a switch in the Admin
13
+ * takes effect without a restart. Loading, the 30 s refresh and the failure
14
+ * behavior are those of `createRefreshingSnapshot`.
15
+ *
16
+ * Start and fallback value are the descriptor defaults (`["emailpass"]`),
17
+ * never an empty list: an empty allowlist blocks every customer login, so a
18
+ * failed read must not produce one.
19
+ */
20
+ const snapshot = (0, refreshing_snapshot_1.createRefreshingSnapshot)({
21
+ key: "@hartl-services/medusa-base/customer-auth-methods-snapshot",
22
+ initial: Object.freeze((0, auth_methods_1.defaultCustomerAuthMethods)(plugin_features_1.customerAuthPluginFeatures)),
23
+ describe: (methods) => `Customer login methods could not be read, keeping [${methods.join(", ")}]`,
24
+ });
25
+ /** Reads the switches once; never rejects. Concurrent calls share one read. */
26
+ const refreshCustomerAuthMethodsSnapshot = () => snapshot.refresh();
27
+ exports.refreshCustomerAuthMethodsSnapshot = refreshCustomerAuthMethodsSnapshot;
28
+ /** Stops the periodic refresh (application shutdown). The value stays. */
29
+ const stopCustomerAuthMethodsSnapshot = () => snapshot.stop();
30
+ exports.stopCustomerAuthMethodsSnapshot = stopCustomerAuthMethodsSnapshot;
31
+ /**
32
+ * Registers the reader, loads the switches once and keeps them fresh every
33
+ * 30 s (application start). `read` returns the effective feature map of
34
+ * `customer_auth`; `warn` is the container logger.
35
+ */
36
+ const startCustomerAuthMethodsSnapshot = (input) => snapshot.start({
37
+ read: async () => Object.freeze((0, auth_methods_1.deriveCustomerAuthMethods)(await input.read())),
38
+ warn: input.warn,
39
+ });
40
+ exports.startCustomerAuthMethodsSnapshot = startCustomerAuthMethodsSnapshot;
41
+ /**
42
+ * The value of `authMethodsPerActor.customer`: the provider ids a customer
43
+ * may use. Synchronous; starts a background refresh when the copy is stale.
44
+ * A fresh array per call.
45
+ */
46
+ const customerAuthMethods = () => [...snapshot.current()];
47
+ exports.customerAuthMethods = customerAuthMethods;
48
+ /**
49
+ * Test hook: sets the methods and counts as just refreshed, so they hold for
50
+ * 30 s without a reader; `null` forgets reader and value (back to the
51
+ * defaults).
52
+ */
53
+ const setCustomerAuthMethodsForTests = (methods) => snapshot.setForTests(methods === null ? null : Object.freeze([...methods]));
54
+ exports.setCustomerAuthMethodsForTests = setCustomerAuthMethodsForTests;
@@ -0,0 +1,16 @@
1
+ import type { PluginFeatureDescriptor } from "../../public/types";
2
+ /** Medusa's built-in e-mail + password provider. */
3
+ export declare const EMAIL_PASSWORD_AUTH_PROVIDER = "emailpass";
4
+ /**
5
+ * The auth providers a customer may use for the given effective switches:
6
+ * `emailpass` while `email_password` is on, `email-otp` while `email_otp` is
7
+ * on. Empty only when both are off, which blocks every customer login (Medusa
8
+ * treats an empty allowlist as "nothing allowed"). A fresh array per call.
9
+ */
10
+ export declare const deriveCustomerAuthMethods: (features: Readonly<Record<string, boolean | undefined>>) => string[];
11
+ /**
12
+ * The methods of a shop whose switches were never stored: the defaults of
13
+ * the descriptor. This is the start value and the fallback of the snapshot,
14
+ * so an unreadable database never locks every customer out.
15
+ */
16
+ export declare const defaultCustomerAuthMethods: (descriptor: PluginFeatureDescriptor) => string[];
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultCustomerAuthMethods = exports.deriveCustomerAuthMethods = exports.EMAIL_PASSWORD_AUTH_PROVIDER = void 0;
4
+ const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
5
+ /** Medusa's built-in e-mail + password provider. */
6
+ exports.EMAIL_PASSWORD_AUTH_PROVIDER = "emailpass";
7
+ /**
8
+ * The auth providers a customer may use for the given effective switches:
9
+ * `emailpass` while `email_password` is on, `email-otp` while `email_otp` is
10
+ * on. Empty only when both are off, which blocks every customer login (Medusa
11
+ * treats an empty allowlist as "nothing allowed"). A fresh array per call.
12
+ */
13
+ const deriveCustomerAuthMethods = (features) => [
14
+ ...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword] === true
15
+ ? [exports.EMAIL_PASSWORD_AUTH_PROVIDER]
16
+ : []),
17
+ ...(features[customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp] === true
18
+ ? [customer_auth_1.EMAIL_OTP_AUTH_PROVIDER]
19
+ : []),
20
+ ];
21
+ exports.deriveCustomerAuthMethods = deriveCustomerAuthMethods;
22
+ /**
23
+ * The methods of a shop whose switches were never stored: the defaults of
24
+ * the descriptor. This is the start value and the fallback of the snapshot,
25
+ * so an unreadable database never locks every customer out.
26
+ */
27
+ const defaultCustomerAuthMethods = (descriptor) => (0, exports.deriveCustomerAuthMethods)(Object.fromEntries(descriptor.features.map((feature) => [
28
+ feature.key,
29
+ feature.default_enabled,
30
+ ])));
31
+ exports.defaultCustomerAuthMethods = defaultCustomerAuthMethods;
@@ -0,0 +1,2 @@
1
+ export declare const customerAuthPluginFeatures: import("../public").PluginFeatureDescriptor<"email_password" | "email_otp", "password_min_length" | "password_require_mixed_case" | "password_require_digit" | "password_require_symbol", never>;
2
+ export type CustomerAuthFeatureKey = (typeof customerAuthPluginFeatures)["features"][number]["key"];
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customerAuthPluginFeatures = void 0;
4
+ const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
5
+ const descriptor_1 = require("../public/descriptor");
6
+ const PASSWORD_RULES = "Passwort-Regeln";
7
+ exports.customerAuthPluginFeatures = (0, descriptor_1.definePluginFeatures)({
8
+ plugin_key: customer_auth_1.CUSTOMER_AUTH_PLUGIN_KEY,
9
+ label: "Kunden-Login",
10
+ description: "Wie sich Kunden anmelden. Mindestens eine Methode bleibt eingeschaltet; Admin-Benutzer melden sich immer mit E-Mail und Passwort an.",
11
+ summary: "Anmeldemethoden und Passwort-Regeln für Kunden.",
12
+ icon: "ShieldCheck",
13
+ switchable: false,
14
+ features_heading: "Anmeldemethoden",
15
+ features_note: "Wirkt nach ca. 1 Minute.",
16
+ all_off_warning: "Keine Methode eingeschaltet: Kunden können sich nicht mehr anmelden.",
17
+ require_enabled_feature: true,
18
+ features: [
19
+ {
20
+ key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword,
21
+ label: "E-Mail + Passwort",
22
+ description: "Anmeldung, Registrierung und Passwort zurücksetzen.",
23
+ default_enabled: true,
24
+ },
25
+ {
26
+ key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp,
27
+ label: "Anmeldecode per E-Mail",
28
+ description: "Anmeldung mit einem 6-stelligen Code per E-Mail; für neue Adressen entsteht ein Konto.",
29
+ default_enabled: false,
30
+ },
31
+ ],
32
+ setting_groups: [
33
+ {
34
+ name: PASSWORD_RULES,
35
+ description: "Für Kunden mit „E-Mail + Passwort“. Admin-Benutzer: mindestens 15 Zeichen.",
36
+ requires_feature: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailPassword,
37
+ },
38
+ ],
39
+ settings: [
40
+ {
41
+ key: "password_min_length",
42
+ label: "Mindestlänge",
43
+ description: "Gilt bei Registrierung und Passwort-Zurücksetzen; bestehende Passwörter bleiben gültig.",
44
+ hint: "8 bis 128 Zeichen. Empfohlen: 15 (NIST SP 800-63B).",
45
+ group: PASSWORD_RULES,
46
+ width: "half",
47
+ type: "number",
48
+ min: 8,
49
+ max: 128,
50
+ integer: true,
51
+ default: 15,
52
+ },
53
+ {
54
+ key: "password_require_mixed_case",
55
+ label: "Groß- und Kleinbuchstaben verlangen",
56
+ description: "Mindestens ein Groß- und ein Kleinbuchstabe.",
57
+ hint: "NIST rät von Zeichenregeln ab, Länge schützt besser.",
58
+ group: PASSWORD_RULES,
59
+ type: "boolean",
60
+ default: false,
61
+ },
62
+ {
63
+ key: "password_require_digit",
64
+ label: "Ziffer verlangen",
65
+ description: "Mindestens eine Ziffer.",
66
+ group: PASSWORD_RULES,
67
+ type: "boolean",
68
+ default: false,
69
+ },
70
+ {
71
+ key: "password_require_symbol",
72
+ label: "Sonderzeichen verlangen",
73
+ description: "Mindestens ein Sonderzeichen.",
74
+ group: PASSWORD_RULES,
75
+ type: "boolean",
76
+ default: false,
77
+ },
78
+ ],
79
+ });
@@ -4,21 +4,9 @@
4
4
  * `projectConfig.http.authVerificationsPerActor.customer` synchronously on
5
5
  * every login. The shop preset puts `customerVerificationActors()` behind a
6
6
  * getter there, so a toggle change in the Admin takes effect without a
7
- * restart.
8
- *
9
- * - Boot: `startCustomerVerificationSnapshot` loads the toggle once.
10
- * - Afterwards an unref'd timer refreshes every 30 s, so a change reaches an
11
- * idle instance too. A read that finds the copy older than 30 s (a blocked
12
- * timer, say) also starts one refresh in the background
13
- * (stale-while-revalidate) and still answers from the copy. A change thus
14
- * reaches a running instance within about 30 s plus the duration of the
15
- * read.
16
- * - A failed read keeps the last value (initially "off") and is logged; the
17
- * next attempt waits another 30 s.
18
- *
19
- * The state lives on `globalThis`: the preset (`dist`) and the Medusa-loaded
20
- * plugin code (`.medusa/server`) are compiled separately and would otherwise
21
- * each hold their own copy. Login is not on the cart path.
7
+ * restart. Loading, the 30 s refresh and the failure behavior (keep the last
8
+ * value, initially "off") are those of `createRefreshingSnapshot`. Login is
9
+ * not on the cart path.
22
10
  */
23
11
  export declare const CUSTOMER_VERIFICATION_REFRESH_MS = 30000;
24
12
  export type CustomerVerificationActor = Readonly<{
@@ -1,117 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setCustomerVerificationEnabledForTests = exports.customerVerificationActors = exports.isCustomerVerificationEnabled = exports.startCustomerVerificationSnapshot = exports.stopCustomerVerificationSnapshot = exports.refreshCustomerVerificationSnapshot = exports.CUSTOMER_VERIFICATION_REFRESH_MS = void 0;
4
+ const refreshing_snapshot_1 = require("../shared/lib/refreshing-snapshot");
4
5
  /**
5
6
  * In-memory copy of the effective `customer_verification` toggle for the one
6
7
  * place that cannot await it: Medusa reads
7
8
  * `projectConfig.http.authVerificationsPerActor.customer` synchronously on
8
9
  * every login. The shop preset puts `customerVerificationActors()` behind a
9
10
  * getter there, so a toggle change in the Admin takes effect without a
10
- * restart.
11
- *
12
- * - Boot: `startCustomerVerificationSnapshot` loads the toggle once.
13
- * - Afterwards an unref'd timer refreshes every 30 s, so a change reaches an
14
- * idle instance too. A read that finds the copy older than 30 s (a blocked
15
- * timer, say) also starts one refresh in the background
16
- * (stale-while-revalidate) and still answers from the copy. A change thus
17
- * reaches a running instance within about 30 s plus the duration of the
18
- * read.
19
- * - A failed read keeps the last value (initially "off") and is logged; the
20
- * next attempt waits another 30 s.
21
- *
22
- * The state lives on `globalThis`: the preset (`dist`) and the Medusa-loaded
23
- * plugin code (`.medusa/server`) are compiled separately and would otherwise
24
- * each hold their own copy. Login is not on the cart path.
11
+ * restart. Loading, the 30 s refresh and the failure behavior (keep the last
12
+ * value, initially "off") are those of `createRefreshingSnapshot`. Login is
13
+ * not on the cart path.
25
14
  */
26
- exports.CUSTOMER_VERIFICATION_REFRESH_MS = 30_000;
27
- const STATE_KEY = Symbol.for("@hartl-services/medusa-base/customer-verification-snapshot");
28
- const state = () => {
29
- const holder = globalThis;
30
- holder[STATE_KEY] ??= {
31
- enabled: false,
32
- refreshedAt: null,
33
- refreshing: null,
34
- timer: null,
35
- read: null,
36
- warn: null,
37
- };
38
- return holder[STATE_KEY];
39
- };
40
- const logFailure = (current, error) => {
41
- try {
42
- current.warn?.(`Customer verification toggle could not be read, keeping "${current.enabled ? "on" : "off"}": ${error instanceof Error ? error.message : "unknown error"}`);
43
- }
44
- catch {
45
- // A broken logger must not turn a failed refresh into a throw.
46
- }
47
- };
15
+ exports.CUSTOMER_VERIFICATION_REFRESH_MS = refreshing_snapshot_1.SNAPSHOT_REFRESH_MS;
16
+ // The initial value is "off" (no verification) until the toggle has been read.
17
+ const snapshot = (0, refreshing_snapshot_1.createRefreshingSnapshot)({
18
+ key: "@hartl-services/medusa-base/customer-verification-snapshot",
19
+ initial: false,
20
+ describe: (enabled) => `Customer verification toggle could not be read, keeping "${enabled ? "on" : "off"}"`,
21
+ });
48
22
  /** Reads the toggle once; never rejects. Concurrent calls share one read. */
49
- const refreshCustomerVerificationSnapshot = () => {
50
- const current = state();
51
- if (!current.read) {
52
- return Promise.resolve();
53
- }
54
- if (current.refreshing) {
55
- return current.refreshing;
56
- }
57
- const read = current.read;
58
- // Started from a microtask so the assignment below always precedes `finally`.
59
- const refreshing = Promise.resolve().then(async () => {
60
- try {
61
- current.enabled = (await read()) === true;
62
- }
63
- catch (error) {
64
- logFailure(current, error);
65
- }
66
- finally {
67
- current.refreshedAt = Date.now();
68
- current.refreshing = null;
69
- }
70
- });
71
- current.refreshing = refreshing;
72
- return refreshing;
73
- };
23
+ const refreshCustomerVerificationSnapshot = () => snapshot.refresh();
74
24
  exports.refreshCustomerVerificationSnapshot = refreshCustomerVerificationSnapshot;
75
- const stopTimer = (current) => {
76
- if (current.timer) {
77
- clearInterval(current.timer);
78
- current.timer = null;
79
- }
80
- };
81
25
  /** Stops the periodic refresh (application shutdown). The value stays. */
82
- const stopCustomerVerificationSnapshot = () => {
83
- stopTimer(state());
84
- };
26
+ const stopCustomerVerificationSnapshot = () => snapshot.stop();
85
27
  exports.stopCustomerVerificationSnapshot = stopCustomerVerificationSnapshot;
86
28
  /**
87
29
  * Registers the reader, loads the toggle once and keeps it fresh every 30 s
88
30
  * (application start). `read` returns the effective toggle; `warn` is the
89
31
  * container logger. Starting again replaces the reader and the timer.
90
32
  */
91
- const startCustomerVerificationSnapshot = (input) => {
92
- const current = state();
93
- current.read = input.read;
94
- current.warn = input.warn;
95
- stopTimer(current);
96
- current.timer = setInterval(() => {
97
- void (0, exports.refreshCustomerVerificationSnapshot)();
98
- }, exports.CUSTOMER_VERIFICATION_REFRESH_MS);
99
- // An idle timer must not keep the process (or a test run) alive.
100
- current.timer.unref();
101
- return (0, exports.refreshCustomerVerificationSnapshot)();
102
- };
33
+ const startCustomerVerificationSnapshot = (input) => snapshot.start({
34
+ read: async () => (await input.read()) === true,
35
+ warn: input.warn,
36
+ });
103
37
  exports.startCustomerVerificationSnapshot = startCustomerVerificationSnapshot;
104
38
  /** Synchronous; starts a background refresh when the copy is stale. Never throws. */
105
- const isCustomerVerificationEnabled = () => {
106
- const current = state();
107
- if (current.read &&
108
- !current.refreshing &&
109
- (current.refreshedAt === null ||
110
- Date.now() - current.refreshedAt >= exports.CUSTOMER_VERIFICATION_REFRESH_MS)) {
111
- void (0, exports.refreshCustomerVerificationSnapshot)();
112
- }
113
- return current.enabled;
114
- };
39
+ const isCustomerVerificationEnabled = () => snapshot.current();
115
40
  exports.isCustomerVerificationEnabled = isCustomerVerificationEnabled;
116
41
  /**
117
42
  * The value of `authVerificationsPerActor.customer`: Medusa's e-mail
@@ -126,18 +51,5 @@ exports.customerVerificationActors = customerVerificationActors;
126
51
  * Test hook: sets the value and counts as just refreshed, so it holds for
127
52
  * 30 s without a reader; `null` forgets reader and value.
128
53
  */
129
- const setCustomerVerificationEnabledForTests = (enabled) => {
130
- const current = state();
131
- if (enabled === null) {
132
- stopTimer(current);
133
- current.enabled = false;
134
- current.refreshedAt = null;
135
- current.refreshing = null;
136
- current.read = null;
137
- current.warn = null;
138
- return;
139
- }
140
- current.enabled = enabled;
141
- current.refreshedAt = Date.now();
142
- };
54
+ const setCustomerVerificationEnabledForTests = (enabled) => snapshot.setForTests(enabled);
143
55
  exports.setCustomerVerificationEnabledForTests = setCustomerVerificationEnabledForTests;
@@ -1,5 +1,5 @@
1
1
  import { z } from "@medusajs/framework/zod";
2
- export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification"];
2
+ export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification", "auth.login_code"];
3
3
  export declare const EmailKindSchema: z.ZodEnum<{
4
4
  "order.received": "order.received";
5
5
  "order.received.admin": "order.received.admin";
@@ -13,12 +13,13 @@ export declare const EmailKindSchema: z.ZodEnum<{
13
13
  "affiliate.application_submitted.admin": "affiliate.application_submitted.admin";
14
14
  "user.invite": "user.invite";
15
15
  "auth.verification": "auth.verification";
16
+ "auth.login_code": "auth.login_code";
16
17
  }>;
17
18
  export type EmailKind = (typeof EMAIL_KINDS)[number];
18
19
  /**
19
- * Kinds whose link carries a one-time token that is never persisted: the
20
+ * Kinds whose link or code carries a one-time secret that is never persisted: the
20
21
  * subscriber builds the mail from the event, so neither the generic retry
21
22
  * job, the automatic dispatch nor an Admin resend may touch them.
22
23
  */
23
- export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.verification"];
24
+ export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.verification", "auth.login_code"];
24
25
  export declare const isEphemeralEmailKind: (kind: EmailKind) => boolean;
@@ -15,16 +15,18 @@ exports.EMAIL_KINDS = [
15
15
  "affiliate.application_submitted.admin",
16
16
  "user.invite",
17
17
  "auth.verification",
18
+ "auth.login_code",
18
19
  ];
19
20
  exports.EmailKindSchema = zod_1.z.enum(exports.EMAIL_KINDS);
20
21
  /**
21
- * Kinds whose link carries a one-time token that is never persisted: the
22
+ * Kinds whose link or code carries a one-time secret that is never persisted: the
22
23
  * subscriber builds the mail from the event, so neither the generic retry
23
24
  * job, the automatic dispatch nor an Admin resend may touch them.
24
25
  */
25
26
  exports.EPHEMERAL_EMAIL_KINDS = [
26
27
  "auth.password_reset",
27
28
  "auth.verification",
29
+ "auth.login_code",
28
30
  ];
29
31
  const isEphemeralEmailKind = (kind) => exports.EPHEMERAL_EMAIL_KINDS.includes(kind);
30
32
  exports.isEphemeralEmailKind = isEphemeralEmailKind;
@@ -375,6 +375,7 @@ export declare const EmailPersistedDataSchemas: {
375
375
  readonly "auth.verification": z.ZodObject<{
376
376
  locale: z.ZodNullable<z.ZodString>;
377
377
  }, z.core.$strict>;
378
+ readonly "auth.login_code": z.ZodObject<{}, z.core.$strict>;
378
379
  };
379
380
  export declare const EmailRenderDataSchemas: {
380
381
  readonly "auth.password_reset": z.ZodObject<{
@@ -390,6 +391,10 @@ export declare const EmailRenderDataSchemas: {
390
391
  locale: z.ZodNullable<z.ZodString>;
391
392
  verify_url: z.ZodString;
392
393
  }, z.core.$strict>;
394
+ readonly "auth.login_code": z.ZodObject<{
395
+ code: z.ZodString;
396
+ valid_minutes: z.ZodNumber;
397
+ }, z.core.$strict>;
393
398
  readonly "order.received": z.ZodObject<{
394
399
  order_id: z.ZodString;
395
400
  summary: z.ZodOptional<z.ZodObject<{
@@ -628,6 +633,9 @@ export type EmailEphemeralRenderContextMap = {
628
633
  expires_at?: string;
629
634
  }> : Kind extends "auth.verification" ? Readonly<{
630
635
  verify_url: string;
636
+ }> : Kind extends "auth.login_code" ? Readonly<{
637
+ code: string;
638
+ valid_minutes: number;
631
639
  }> : EmptyRenderContext;
632
640
  };
633
641
  export type EmailRenderDataMap = {
@@ -231,6 +231,8 @@ exports.EmailPersistedDataSchemas = {
231
231
  locale: NullableLocaleSchema,
232
232
  })
233
233
  .strict(),
234
+ // The code itself is never stored: it only exists in the render context.
235
+ "auth.login_code": zod_1.z.object({}).strict(),
234
236
  };
235
237
  exports.EmailRenderDataSchemas = {
236
238
  ...exports.EmailPersistedDataSchemas,
@@ -247,4 +249,10 @@ exports.EmailRenderDataSchemas = {
247
249
  "auth.verification": exports.EmailPersistedDataSchemas["auth.verification"].extend({
248
250
  verify_url: zod_1.z.string().url(),
249
251
  }),
252
+ "auth.login_code": exports.EmailPersistedDataSchemas["auth.login_code"].extend({
253
+ /** The six digits; never persisted, only passed to rendering. */
254
+ code: zod_1.z.string().regex(/^[0-9]{6}$/),
255
+ /** How long the code is valid, in minutes. */
256
+ valid_minutes: zod_1.z.number().int().positive(),
257
+ }),
250
258
  };