@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.
Files changed (149) 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 +73 -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 +36 -5
  24. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +144 -20
  25. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +36 -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 +144 -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 +43 -0
  34. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +66 -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/PdfPreviewModal.js +1 -1
  54. package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.d.ts +10 -0
  55. package/.medusa/server/src/admin/admin-components/shared/ui/PluginIcon.js +10 -0
  56. package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.d.ts +27 -0
  57. package/.medusa/server/src/admin/admin-components/shared/ui/SwitchRow.js +15 -0
  58. package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.d.ts +15 -0
  59. package/.medusa/server/src/admin/admin-components/shared/ui/WidgetHeader.js +8 -0
  60. package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.d.ts +13 -0
  61. package/.medusa/server/src/admin/admin-components/shared/ui/file-drop.js +16 -0
  62. package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.d.ts +13 -0
  63. package/.medusa/server/src/admin/admin-components/shared/ui/multi-select.js +11 -0
  64. package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.d.ts +12 -0
  65. package/.medusa/server/src/admin/admin-components/shared/ui/plugin-icon.js +29 -0
  66. package/.medusa/server/src/admin/index.js +3123 -2019
  67. package/.medusa/server/src/admin/index.mjs +3126 -2022
  68. package/.medusa/server/src/api/admin-first-login-password-policy.js +56 -0
  69. package/.medusa/server/src/api/answer-actor-type-casing-not-found.js +26 -0
  70. package/.medusa/server/src/api/answer-code-provider-route-not-found.js +34 -0
  71. package/.medusa/server/src/api/code-account-password-routes.js +92 -0
  72. package/.medusa/server/src/api/middlewares.js +82 -17
  73. package/.medusa/server/src/api/password-policy.js +55 -10
  74. package/.medusa/server/src/api/require-login-code-customer-email.js +40 -0
  75. package/.medusa/server/src/api/store/customer-auth/login-code/route.js +17 -0
  76. package/.medusa/server/src/api/store/customer-auth/password-policy/route.js +19 -0
  77. package/.medusa/server/src/customer-auth/application/prepare-code-account-password-reset.js +111 -0
  78. package/.medusa/server/src/customer-auth/application/request-login-code.js +145 -0
  79. package/.medusa/server/src/customer-auth/customer-auth-methods-snapshot.js +55 -0
  80. package/.medusa/server/src/customer-auth/customer-password-policy.js +40 -0
  81. package/.medusa/server/src/customer-auth/domain/account-linking.js +53 -0
  82. package/.medusa/server/src/customer-auth/domain/auth-methods.js +32 -0
  83. package/.medusa/server/src/customer-auth/domain/login-code.js +39 -0
  84. package/.medusa/server/src/customer-auth/login-code-accounts.js +146 -0
  85. package/.medusa/server/src/customer-auth/login-code-state.js +139 -0
  86. package/.medusa/server/src/customer-auth/plugin-features.js +80 -0
  87. package/.medusa/server/src/customer-auth/remove-provider-identity.js +18 -0
  88. package/.medusa/server/src/customer-auth/start-customer-auth-methods.js +20 -0
  89. package/.medusa/server/src/documents/plugin-features.js +8 -2
  90. package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +186 -0
  91. package/.medusa/server/src/email/application/deliver-login-code.js +89 -0
  92. package/.medusa/server/src/email/application/handle-customer-verification-event.js +29 -168
  93. package/.medusa/server/src/email/application/handle-password-reset-event.js +27 -166
  94. package/.medusa/server/src/email/customer-verification-snapshot.js +20 -108
  95. package/.medusa/server/src/email/domain/business-keys.js +15 -2
  96. package/.medusa/server/src/email/plugin-features.js +8 -3
  97. package/.medusa/server/src/email/rendering/default-templates.js +2 -1
  98. package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +19 -1
  99. package/.medusa/server/src/email/templates/defaults/documents.js +7 -1
  100. package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
  101. package/.medusa/server/src/email/templates/defaults/texts.js +5 -2
  102. package/.medusa/server/src/email/templates/render.js +2 -1
  103. package/.medusa/server/src/email/templates/sample-data.js +2 -1
  104. package/.medusa/server/src/email/templates/validate.js +8 -3
  105. package/.medusa/server/src/email/types/email-kinds.js +4 -2
  106. package/.medusa/server/src/email/types/template-data.js +9 -1
  107. package/.medusa/server/src/modules/customer-auth/index.js +14 -0
  108. package/.medusa/server/src/modules/customer-auth/service.js +35 -0
  109. package/.medusa/server/src/modules/email/migrations/Migration20261010090000.js +20 -0
  110. package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +4 -1
  111. package/.medusa/server/src/modules/plugin-features/service.js +26 -1
  112. package/.medusa/server/src/providers/email-otp/index.js +8 -0
  113. package/.medusa/server/src/providers/email-otp/service.js +96 -0
  114. package/.medusa/server/src/public/descriptor.js +117 -5
  115. package/.medusa/server/src/public/effective-features.js +22 -1
  116. package/.medusa/server/src/public/index.js +4 -2
  117. package/.medusa/server/src/public/plugin-icons.js +28 -0
  118. package/.medusa/server/src/public/shop-master-data.js +33 -14
  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 +116 -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 +63 -1
  144. package/dist/shared/lib/index.d.ts +1 -0
  145. package/dist/shared/lib/index.js +1 -0
  146. package/dist/shared/lib/refreshing-snapshot.d.ts +52 -0
  147. package/dist/shared/lib/refreshing-snapshot.js +121 -0
  148. package/dist/shop-config/build-food-shop-config.js +37 -0
  149. package/package.json +8 -3
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasCustomerAccountIdentity = exports.findCodeAccountAuthIdentityId = exports.findPasswordIdentities = exports.deleteUnconfirmedPasswordIdentities = exports.findEmailPasswordAccounts = exports.findOwnLoginIdentity = void 0;
4
+ /** The address's own code-login identity (provider `email-otp`), or null. */
5
+ const findOwnLoginIdentity = async (sql, entityId) => {
6
+ const result = await sql.raw(`SELECT pi.id AS provider_identity_id,
7
+ pi.auth_identity_id AS auth_identity_id,
8
+ NULLIF(ai.app_metadata->>'customer_id', '') AS customer_id
9
+ FROM provider_identity pi
10
+ JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
11
+ WHERE pi.provider = 'email-otp'
12
+ AND pi.entity_id = ?
13
+ AND pi.deleted_at IS NULL`, [entityId]);
14
+ const row = result.rows[0];
15
+ return row
16
+ ? {
17
+ providerIdentityId: String(row.provider_identity_id),
18
+ authIdentityId: String(row.auth_identity_id),
19
+ customerId: typeof row.customer_id === "string" && row.customer_id
20
+ ? row.customer_id
21
+ : null,
22
+ }
23
+ : null;
24
+ };
25
+ exports.findOwnLoginIdentity = findOwnLoginIdentity;
26
+ /**
27
+ * A verified Medusa auth verification of the provider identity `pi`'s very
28
+ * address (`entity_type` "email"): the one confirmation check of the linking
29
+ * rule and of the clean-up after a code login.
30
+ */
31
+ const ADDRESS_CONFIRMED_SQL = `EXISTS (
32
+ SELECT 1 FROM auth_verification v
33
+ WHERE v.auth_identity_id = pi.auth_identity_id
34
+ AND v.entity_id = pi.entity_id
35
+ AND v.entity_type = 'email'
36
+ AND v.verified_at IS NOT NULL
37
+ AND v.deleted_at IS NULL
38
+ )`;
39
+ /**
40
+ * Customer accounts that registered with this address through `emailpass`.
41
+ * `emailpass` stores the address exactly as typed, so the match ignores
42
+ * letter case. Only identities whose auth identity carries a customer count
43
+ * (`app_metadata.customer_id`); `confirmed` is a verified Medusa auth
44
+ * verification of that very address. `customerEmailMatches` says the customer
45
+ * record itself (not deleted) has the same address, so a password identity
46
+ * pointing at a customer of another address is never linked.
47
+ */
48
+ const findEmailPasswordAccounts = async (sql, normalizedEmail) => {
49
+ const result = await sql.raw(`SELECT pi.auth_identity_id AS auth_identity_id,
50
+ pi.entity_id AS entity_id,
51
+ ${ADDRESS_CONFIRMED_SQL} AS confirmed,
52
+ (c.id IS NOT NULL AND lower(c.email) = ?) AS customer_email_matches
53
+ FROM provider_identity pi
54
+ JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
55
+ LEFT JOIN customer c
56
+ ON c.id = ai.app_metadata->>'customer_id' AND c.deleted_at IS NULL
57
+ WHERE pi.provider = 'emailpass'
58
+ AND lower(pi.entity_id) = ?
59
+ AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
60
+ AND pi.deleted_at IS NULL
61
+ AND NULLIF(ai.app_metadata->>'customer_id', '') IS NOT NULL`, [normalizedEmail, normalizedEmail]);
62
+ return result.rows.map((row) => ({
63
+ authIdentityId: String(row.auth_identity_id),
64
+ entityId: String(row.entity_id),
65
+ confirmed: row.confirmed === true,
66
+ customerEmailMatches: row.customer_email_matches === true,
67
+ }));
68
+ };
69
+ exports.findEmailPasswordAccounts = findEmailPasswordAccounts;
70
+ /**
71
+ * Deletes the `emailpass` identities of an auth identity whose address was
72
+ * never confirmed, for good (the unique index on `(entity_id, provider)` has no
73
+ * `deleted_at` filter, so a soft delete would block a new registration). A
74
+ * password somebody else may have planted for the address before the code
75
+ * login took over can then never be used. Returns how many were deleted.
76
+ */
77
+ const deleteUnconfirmedPasswordIdentities = async (sql, authIdentityId) => {
78
+ const result = await sql.raw(`DELETE FROM provider_identity pi
79
+ WHERE pi.auth_identity_id = ?
80
+ AND pi.provider = 'emailpass'
81
+ AND NOT ${ADDRESS_CONFIRMED_SQL}
82
+ RETURNING pi.id`, [authIdentityId]);
83
+ return result.rows.length;
84
+ };
85
+ exports.deleteUnconfirmedPasswordIdentities = deleteUnconfirmedPasswordIdentities;
86
+ /**
87
+ * Every `emailpass` identity of the address, letter case ignored (`emailpass`
88
+ * stores the address as typed), with or without a customer.
89
+ */
90
+ const findPasswordIdentities = async (sql, normalizedEmail) => {
91
+ const result = await sql.raw(`SELECT pi.id AS provider_identity_id,
92
+ pi.auth_identity_id AS auth_identity_id,
93
+ pi.entity_id AS entity_id,
94
+ NULLIF(ai.app_metadata->>'customer_id', '') AS customer_id
95
+ FROM provider_identity pi
96
+ JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
97
+ WHERE pi.provider = 'emailpass'
98
+ AND lower(pi.entity_id) = ?
99
+ AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
100
+ AND pi.deleted_at IS NULL`, [normalizedEmail]);
101
+ return result.rows.map((row) => ({
102
+ providerIdentityId: String(row.provider_identity_id),
103
+ authIdentityId: String(row.auth_identity_id),
104
+ entityId: String(row.entity_id),
105
+ customerId: typeof row.customer_id === "string" && row.customer_id
106
+ ? row.customer_id
107
+ : null,
108
+ }));
109
+ };
110
+ exports.findPasswordIdentities = findPasswordIdentities;
111
+ /**
112
+ * The auth identity of the address's code login (`email-otp`) when it carries a
113
+ * customer (not deleted) with that very address: the account the address was
114
+ * proven for. Null otherwise.
115
+ */
116
+ const findCodeAccountAuthIdentityId = async (sql, normalizedEmail) => {
117
+ const result = await sql.raw(`SELECT pi.auth_identity_id AS auth_identity_id
118
+ FROM provider_identity pi
119
+ JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
120
+ JOIN customer c
121
+ ON c.id = ai.app_metadata->>'customer_id' AND c.deleted_at IS NULL
122
+ WHERE pi.provider = 'email-otp'
123
+ AND pi.entity_id = ?
124
+ AND pi.deleted_at IS NULL
125
+ AND lower(c.email) = ?`, [normalizedEmail, normalizedEmail]);
126
+ const id = result.rows[0]?.auth_identity_id;
127
+ return typeof id === "string" && id ? id : null;
128
+ };
129
+ exports.findCodeAccountAuthIdentityId = findCodeAccountAuthIdentityId;
130
+ /**
131
+ * Whether an identity of any provider for the address (letter case ignored)
132
+ * belongs to an auth identity that carries a customer.
133
+ */
134
+ const hasCustomerAccountIdentity = async (sql, normalizedEmail) => {
135
+ const result = await sql.raw(`SELECT 1 AS found
136
+ FROM provider_identity pi
137
+ JOIN auth_identity ai ON ai.id = pi.auth_identity_id AND ai.deleted_at IS NULL
138
+ WHERE lower(pi.entity_id) = ?
139
+ AND pi.entity_id ~ '^[\\x21-\\x7e]+$'
140
+ AND pi.deleted_at IS NULL
141
+ AND NULLIF(ai.app_metadata->>'customer_id', '') IS NOT NULL
142
+ LIMIT 1`, [normalizedEmail]);
143
+ return result.rows.length > 0;
144
+ };
145
+ exports.hasCustomerAccountIdentity = hasCustomerAccountIdentity;
146
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9naW4tY29kZS1hY2NvdW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jdXN0b21lci1hdXRoL2xvZ2luLWNvZGUtYWNjb3VudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBTUEsNkVBQTZFO0FBQ3RFLE1BQU0sb0JBQW9CLEdBQUcsS0FBSyxFQUN2QyxHQUFpQixFQUNqQixRQUFnQixFQUNrQixFQUFFO0lBQ3BDLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7Ozs7Ozs7aUNBTzZCLEVBQzdCLENBQUMsUUFBUSxDQUFDLENBQ1gsQ0FBQTtJQUNELE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDMUIsT0FBTyxHQUFHO1FBQ1IsQ0FBQyxDQUFDO1lBQ0Usa0JBQWtCLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsQ0FBQztZQUNwRCxjQUFjLEVBQUUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQztZQUM1QyxVQUFVLEVBQ1IsT0FBTyxHQUFHLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsV0FBVztnQkFDcEQsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxXQUFXO2dCQUNqQixDQUFDLENBQUMsSUFBSTtTQUNYO1FBQ0gsQ0FBQyxDQUFDLElBQUksQ0FBQTtBQUNWLENBQUMsQ0FBQTtBQTFCWSxRQUFBLG9CQUFvQix3QkEwQmhDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0scUJBQXFCLEdBQUc7Ozs7Ozs7RUFPNUIsQ0FBQTtBQUVGOzs7Ozs7OztHQVFHO0FBQ0ksTUFBTSx5QkFBeUIsR0FBRyxLQUFLLEVBQzVDLEdBQWlCLEVBQ2pCLGVBQXVCLEVBQ2MsRUFBRTtJQUN2QyxNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQzFCOztjQUVVLHFCQUFxQjs7Ozs7Ozs7OzttRUFVZ0MsRUFDL0QsQ0FBQyxlQUFlLEVBQUUsZUFBZSxDQUFDLENBQ25DLENBQUE7SUFDRCxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQy9CLGNBQWMsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQzVDLFFBQVEsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQztRQUMvQixTQUFTLEVBQUUsR0FBRyxDQUFDLFNBQVMsS0FBSyxJQUFJO1FBQ2pDLG9CQUFvQixFQUFFLEdBQUcsQ0FBQyxzQkFBc0IsS0FBSyxJQUFJO0tBQzFELENBQUMsQ0FBQyxDQUFBO0FBQ0wsQ0FBQyxDQUFBO0FBMUJZLFFBQUEseUJBQXlCLDZCQTBCckM7QUFFRDs7Ozs7O0dBTUc7QUFDSSxNQUFNLG1DQUFtQyxHQUFHLEtBQUssRUFDdEQsR0FBaUIsRUFDakIsY0FBc0IsRUFDTCxFQUFFO0lBQ25CLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7OztpQkFHYSxxQkFBcUI7cUJBQ2pCLEVBQ2pCLENBQUMsY0FBYyxDQUFDLENBQ2pCLENBQUE7SUFDRCxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFBO0FBQzNCLENBQUMsQ0FBQTtBQWJZLFFBQUEsbUNBQW1DLHVDQWEvQztBQVlEOzs7R0FHRztBQUNJLE1BQU0sc0JBQXNCLEdBQUcsS0FBSyxFQUN6QyxHQUFpQixFQUNqQixlQUF1QixFQUNNLEVBQUU7SUFDL0IsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7O2lDQVM2QixFQUM3QixDQUFDLGVBQWUsQ0FBQyxDQUNsQixDQUFBO0lBQ0QsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUMvQixrQkFBa0IsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLG9CQUFvQixDQUFDO1FBQ3BELGNBQWMsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDO1FBQzVDLFFBQVEsRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQztRQUMvQixVQUFVLEVBQ1IsT0FBTyxHQUFHLENBQUMsV0FBVyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsV0FBVztZQUNwRCxDQUFDLENBQUMsR0FBRyxDQUFDLFdBQVc7WUFDakIsQ0FBQyxDQUFDLElBQUk7S0FDWCxDQUFDLENBQUMsQ0FBQTtBQUNMLENBQUMsQ0FBQTtBQTFCWSxRQUFBLHNCQUFzQiwwQkEwQmxDO0FBRUQ7Ozs7R0FJRztBQUNJLE1BQU0sNkJBQTZCLEdBQUcsS0FBSyxFQUNoRCxHQUFpQixFQUNqQixlQUF1QixFQUNDLEVBQUU7SUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7OEJBUTBCLEVBQzFCLENBQUMsZUFBZSxFQUFFLGVBQWUsQ0FBQyxDQUNuQyxDQUFBO0lBQ0QsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQTtJQUMzQyxPQUFPLE9BQU8sRUFBRSxLQUFLLFFBQVEsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBO0FBQ2pELENBQUMsQ0FBQTtBQWxCWSxRQUFBLDZCQUE2QixpQ0FrQnpDO0FBRUQ7OztHQUdHO0FBQ0ksTUFBTSwwQkFBMEIsR0FBRyxLQUFLLEVBQzdDLEdBQWlCLEVBQ2pCLGVBQXVCLEVBQ0wsRUFBRTtJQUNwQixNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQzFCOzs7Ozs7O2FBT1MsRUFDVCxDQUFDLGVBQWUsQ0FBQyxDQUNsQixDQUFBO0lBQ0QsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUE7QUFDL0IsQ0FBQyxDQUFBO0FBaEJZLFFBQUEsMEJBQTBCLDhCQWdCdEMifQ==
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.consumeLoginCode = exports.countLoginCodeAttempt = exports.storeLoginCode = exports.LOGIN_CODE_RESEND_SLACK_SECONDS = void 0;
4
+ const customer_auth_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/customer-auth");
5
+ /**
6
+ * The code state lives in `provider_identity.provider_metadata` of the
7
+ * `email-otp` provider identity: `login_code` is `{ hash, expires_at,
8
+ * attempts, sent_at }`, `login_code_budget` is `{ window_start, issued }`
9
+ * (codes issued in the current 24 hour window; issuing a new code does not
10
+ * reset it, a successful login does). Every transition below is one SQL
11
+ * statement whose WHERE clause carries the rule, so concurrent requests cannot
12
+ * both pass it. Time is the database's `now()`, never the application's clock.
13
+ */
14
+ /**
15
+ * The storefront starts its resend countdown when it receives the 202, which
16
+ * is before `sent_at` is written; the spacing check tolerates that head start.
17
+ */
18
+ exports.LOGIN_CODE_RESEND_SLACK_SECONDS = 5;
19
+ /** Length of the issuance window (fixed, counted from its first code). */
20
+ const LOGIN_CODE_BUDGET_WINDOW_HOURS = 24;
21
+ /** The budget window of the row is still running (`window_start` < 24 h ago). */
22
+ const BUDGET_WINDOW_ACTIVE_SQL = `COALESCE(
23
+ jsonb_typeof(provider_metadata->'login_code_budget') = 'object'
24
+ AND (provider_metadata->'login_code_budget'->>'window_start')::timestamptz
25
+ > now() - make_interval(hours => ${LOGIN_CODE_BUDGET_WINDOW_HOURS}),
26
+ false
27
+ )`;
28
+ /**
29
+ * Stores a new code unless one was sent within the resend interval or the
30
+ * address already got `LOGIN_CODE_MAX_PER_DAY` codes in the running 24 hour
31
+ * window. Both rules are checked and the code and the budget are written by
32
+ * one conditional UPDATE. An expired window starts a new one with this code.
33
+ */
34
+ const storeLoginCode = async (sql, providerIdentityId, hash) => {
35
+ const result = await sql.raw(`UPDATE provider_identity
36
+ SET provider_metadata = jsonb_set(
37
+ jsonb_set(
38
+ COALESCE(provider_metadata, '{}'::jsonb),
39
+ '{login_code}',
40
+ jsonb_build_object(
41
+ 'hash', ?::text,
42
+ 'expires_at', to_jsonb(now() + make_interval(mins => ?)),
43
+ 'attempts', 0,
44
+ 'sent_at', to_jsonb(now())
45
+ )
46
+ ),
47
+ '{login_code_budget}',
48
+ jsonb_build_object(
49
+ 'window_start', CASE
50
+ WHEN ${BUDGET_WINDOW_ACTIVE_SQL}
51
+ THEN provider_metadata->'login_code_budget'->'window_start'
52
+ ELSE to_jsonb(now())
53
+ END,
54
+ 'issued', CASE
55
+ WHEN ${BUDGET_WINDOW_ACTIVE_SQL}
56
+ THEN to_jsonb((provider_metadata->'login_code_budget'->>'issued')::int + 1)
57
+ ELSE to_jsonb(1)
58
+ END
59
+ )
60
+ ),
61
+ updated_at = now()
62
+ WHERE id = ?
63
+ AND provider = 'email-otp'
64
+ AND deleted_at IS NULL
65
+ AND (
66
+ jsonb_typeof(provider_metadata->'login_code') IS DISTINCT FROM 'object'
67
+ OR (provider_metadata->'login_code'->>'sent_at')::timestamptz
68
+ < now() - make_interval(secs => ?)
69
+ )
70
+ AND (
71
+ NOT ${BUDGET_WINDOW_ACTIVE_SQL}
72
+ OR (provider_metadata->'login_code_budget'->>'issued')::int < ?
73
+ )
74
+ RETURNING id`, [
75
+ hash,
76
+ customer_auth_1.LOGIN_CODE_TTL_MINUTES,
77
+ providerIdentityId,
78
+ customer_auth_1.LOGIN_CODE_RESEND_INTERVAL_SECONDS - exports.LOGIN_CODE_RESEND_SLACK_SECONDS,
79
+ customer_auth_1.LOGIN_CODE_MAX_PER_DAY,
80
+ ]);
81
+ if (result.rows.length === 1) {
82
+ return "stored";
83
+ }
84
+ // Only to tell the reason apart for the caller's log; the decision above
85
+ // was already made atomically.
86
+ const capped = await sql.raw(`SELECT id FROM provider_identity
87
+ WHERE id = ?
88
+ AND provider = 'email-otp'
89
+ AND deleted_at IS NULL
90
+ AND ${BUDGET_WINDOW_ACTIVE_SQL}
91
+ AND (provider_metadata->'login_code_budget'->>'issued')::int >= ?`, [providerIdentityId, customer_auth_1.LOGIN_CODE_MAX_PER_DAY]);
92
+ return capped.rows.length === 1 ? "capped" : "throttled";
93
+ };
94
+ exports.storeLoginCode = storeLoginCode;
95
+ /**
96
+ * Counts one attempt against the address' live code and returns the stored
97
+ * hash; null when there is no code, it is expired or its attempts are used
98
+ * up. The count happens before the comparison, so parallel guesses share the
99
+ * limit instead of each seeing the same remaining budget.
100
+ */
101
+ const countLoginCodeAttempt = async (sql, entityId) => {
102
+ const result = await sql.raw(`UPDATE provider_identity
103
+ SET provider_metadata = jsonb_set(
104
+ provider_metadata,
105
+ '{login_code,attempts}',
106
+ to_jsonb((provider_metadata->'login_code'->>'attempts')::int + 1)
107
+ ),
108
+ updated_at = now()
109
+ WHERE provider = 'email-otp'
110
+ AND entity_id = ?
111
+ AND deleted_at IS NULL
112
+ AND jsonb_typeof(provider_metadata->'login_code') = 'object'
113
+ AND (provider_metadata->'login_code'->>'attempts')::int < ?
114
+ AND (provider_metadata->'login_code'->>'expires_at')::timestamptz > now()
115
+ RETURNING provider_metadata->'login_code'->>'hash' AS hash`, [entityId, customer_auth_1.LOGIN_CODE_MAX_ATTEMPTS]);
116
+ const hash = result.rows[0]?.hash;
117
+ return typeof hash === "string" && hash ? hash : null;
118
+ };
119
+ exports.countLoginCodeAttempt = countLoginCodeAttempt;
120
+ /**
121
+ * Uses the code up: removes it (and the issuance budget, a successful login
122
+ * proves the address' owner holds the mailbox) if it is still the one with
123
+ * `hash` and not expired. Exactly one of several parallel correct logins gets
124
+ * true.
125
+ */
126
+ const consumeLoginCode = async (sql, entityId, hash) => {
127
+ const result = await sql.raw(`UPDATE provider_identity
128
+ SET provider_metadata = provider_metadata - 'login_code' - 'login_code_budget',
129
+ updated_at = now()
130
+ WHERE provider = 'email-otp'
131
+ AND entity_id = ?
132
+ AND deleted_at IS NULL
133
+ AND provider_metadata->'login_code'->>'hash' = ?
134
+ AND (provider_metadata->'login_code'->>'expires_at')::timestamptz > now()
135
+ RETURNING id`, [entityId, hash]);
136
+ return result.rows.length === 1;
137
+ };
138
+ exports.consumeLoginCode = consumeLoginCode;
139
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9naW4tY29kZS1zdGF0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jdXN0b21lci1hdXRoL2xvZ2luLWNvZGUtc3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkdBS2dGO0FBVWhGOzs7Ozs7OztHQVFHO0FBRUg7OztHQUdHO0FBQ1UsUUFBQSwrQkFBK0IsR0FBRyxDQUFDLENBQUE7QUFFaEQsMEVBQTBFO0FBQzFFLE1BQU0sOEJBQThCLEdBQUcsRUFBRSxDQUFBO0FBRXpDLGlGQUFpRjtBQUNqRixNQUFNLHdCQUF3QixHQUFHOzs7MkNBR1UsOEJBQThCOztFQUV2RSxDQUFBO0FBVUY7Ozs7O0dBS0c7QUFDSSxNQUFNLGNBQWMsR0FBRyxLQUFLLEVBQ2pDLEdBQWlCLEVBQ2pCLGtCQUEwQixFQUMxQixJQUFZLEVBQ21CLEVBQUU7SUFDakMsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7Ozs7Ozs7O3NCQWVrQix3QkFBd0I7Ozs7O3NCQUt4Qix3QkFBd0I7Ozs7Ozs7Ozs7Ozs7Ozs7ZUFnQi9CLHdCQUF3Qjs7O2tCQUdyQixFQUNkO1FBQ0UsSUFBSTtRQUNKLHNDQUFzQjtRQUN0QixrQkFBa0I7UUFDbEIsa0RBQWtDLEdBQUcsdUNBQStCO1FBQ3BFLHNDQUFzQjtLQUN2QixDQUNGLENBQUE7SUFDRCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzdCLE9BQU8sUUFBUSxDQUFBO0lBQ2pCLENBQUM7SUFDRCx5RUFBeUU7SUFDekUsK0JBQStCO0lBQy9CLE1BQU0sTUFBTSxHQUFHLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FDMUI7Ozs7YUFJUyx3QkFBd0I7eUVBQ29DLEVBQ3JFLENBQUMsa0JBQWtCLEVBQUUsc0NBQXNCLENBQUMsQ0FDN0MsQ0FBQTtJQUNELE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQTtBQUMxRCxDQUFDLENBQUE7QUFyRVksUUFBQSxjQUFjLGtCQXFFMUI7QUFFRDs7Ozs7R0FLRztBQUNJLE1BQU0scUJBQXFCLEdBQUcsS0FBSyxFQUN4QyxHQUFpQixFQUNqQixRQUFnQixFQUNRLEVBQUU7SUFDMUIsTUFBTSxNQUFNLEdBQUcsTUFBTSxHQUFHLENBQUMsR0FBRyxDQUMxQjs7Ozs7Ozs7Ozs7OztnRUFhNEQsRUFDNUQsQ0FBQyxRQUFRLEVBQUUsdUNBQXVCLENBQUMsQ0FDcEMsQ0FBQTtJQUNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFBO0lBQ2pDLE9BQU8sT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUE7QUFDdkQsQ0FBQyxDQUFBO0FBdkJZLFFBQUEscUJBQXFCLHlCQXVCakM7QUFFRDs7Ozs7R0FLRztBQUNJLE1BQU0sZ0JBQWdCLEdBQUcsS0FBSyxFQUNuQyxHQUFpQixFQUNqQixRQUFnQixFQUNoQixJQUFZLEVBQ00sRUFBRTtJQUNwQixNQUFNLE1BQU0sR0FBRyxNQUFNLEdBQUcsQ0FBQyxHQUFHLENBQzFCOzs7Ozs7OztrQkFRYyxFQUNkLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUNqQixDQUFBO0lBQ0QsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUE7QUFDakMsQ0FBQyxDQUFBO0FBbEJZLFFBQUEsZ0JBQWdCLG9CQWtCNUIifQ==
@@ -0,0 +1,80 @@
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: "Welche Anmeldemethoden Kunden im Shop nutzen können. Mindestens eine Methode muss eingeschaltet bleiben, sonst kann sich niemand mehr anmelden; Admin-Benutzer melden sich unabhängig davon mit E-Mail und Passwort an. Eine Änderung wirkt innerhalb von etwa 30 Sekunden im Backend, die Storefront übernimmt sie nach ihrem Zwischenspeicher (etwa eine Minute). Die Passwort-Regeln gelten nur für Kunden, solange „E-Mail + Passwort“ eingeschaltet ist; Admin-Benutzer behalten mindestens 15 Zeichen, die bei der ersten Anmeldung eines noch nicht angelegten Admin-Benutzers (Einladung) geprüft werden.",
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. 30 Sekunden im Backend, in der Storefront 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. Aus: Das Backend lehnt diese Wege für Kunden ab; bestehende Konten melden sich dann nur noch mit den anderen eingeschalteten Methoden an.",
23
+ default_enabled: true,
24
+ },
25
+ {
26
+ key: customer_auth_1.CUSTOMER_AUTH_FEATURES.emailOtp,
27
+ label: "Anmeldecode per E-Mail",
28
+ description: "Kunden fordern einen sechsstelligen Code an, der 10 Minuten gültig ist, und melden sich damit an; für neue Adressen entsteht dabei ein Kundenkonto. Ein bestehendes Passwort-Konto derselben Adresse wird nur verknüpft, wenn die Passwort-Anmeldung aus ist oder die Adresse bestätigt wurde. Die Code-Mail geht unabhängig von den Schaltern des E-Mail-Plugins hinaus. Aus: Code-Anfrage und Code-Anmeldung sind nicht möglich.",
29
+ default_enabled: false,
30
+ },
31
+ ],
32
+ setting_groups: [
33
+ {
34
+ name: PASSWORD_RULES,
35
+ description: "Gelten nur für Kunden mit „E-Mail + Passwort“, bei Registrierung und Passwort-Zurücksetzen. Admin-Benutzer behalten 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: "Wie viele Zeichen ein Kundenpasswort mindestens haben muss (8 bis 128). NIST SP 800-63B Rev. 4, die US-Richtlinie für digitale Identität, empfiehlt mindestens 15 Zeichen, wenn das Passwort der einzige Faktor ist. Die Regeln gelten nur, solange „E-Mail + Passwort“ eingeschaltet ist, und nur für Kunden; Admin-Benutzer behalten mindestens 15 Zeichen. Bestehende Passwörter bleiben gültig, die Regeln greifen bei Registrierung und Passwort-Zurücksetzen. Grenze: Wer sich über den Admin-Weg registriert, kann 15 Zeichen ohne Zusammensetzungsregeln wählen, auch wenn der Shop mehr verlangt; das schwächt nur das eigene Passwort.",
44
+ hint: "8 bis 128 Zeichen. Empfohlen: mindestens 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 Zusammensetzungsregeln ab: Sie führen zu vorhersehbaren Passwörtern wie „Passwort1!“. 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 (kein Buchstabe, keine Ziffer, kein Leerzeichen).",
74
+ group: PASSWORD_RULES,
75
+ type: "boolean",
76
+ default: false,
77
+ },
78
+ ],
79
+ });
80
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWZlYXR1cmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvcGx1Z2luLWZlYXR1cmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDJHQUdnRjtBQUVoRixxREFBMkQ7QUFFM0QsTUFBTSxjQUFjLEdBQUcsaUJBQWlCLENBQUE7QUFFM0IsUUFBQSwwQkFBMEIsR0FBRyxJQUFBLGlDQUFvQixFQUFDO0lBQzdELFVBQVUsRUFBRSx3Q0FBd0I7SUFDcEMsS0FBSyxFQUFFLGNBQWM7SUFDckIsV0FBVyxFQUNULG1sQkFBbWxCO0lBQ3JsQixPQUFPLEVBQUUsaURBQWlEO0lBQzFELElBQUksRUFBRSxhQUFhO0lBQ25CLFVBQVUsRUFBRSxLQUFLO0lBQ2pCLGdCQUFnQixFQUFFLGlCQUFpQjtJQUNuQyxhQUFhLEVBQ1gsNkVBQTZFO0lBQy9FLGVBQWUsRUFDYixzRUFBc0U7SUFDeEUsdUJBQXVCLEVBQUUsSUFBSTtJQUM3QixRQUFRLEVBQUU7UUFDUjtZQUNFLEdBQUcsRUFBRSxzQ0FBc0IsQ0FBQyxhQUFhO1lBQ3pDLEtBQUssRUFBRSxtQkFBbUI7WUFDMUIsV0FBVyxFQUNULCtMQUErTDtZQUNqTSxlQUFlLEVBQUUsSUFBSTtTQUN0QjtRQUNEO1lBQ0UsR0FBRyxFQUFFLHNDQUFzQixDQUFDLFFBQVE7WUFDcEMsS0FBSyxFQUFFLHdCQUF3QjtZQUMvQixXQUFXLEVBQ1Qsb2FBQW9hO1lBQ3RhLGVBQWUsRUFBRSxLQUFLO1NBQ3ZCO0tBQ0Y7SUFDRCxjQUFjLEVBQUU7UUFDZDtZQUNFLElBQUksRUFBRSxjQUFjO1lBQ3BCLFdBQVcsRUFDVCw0SUFBNEk7WUFDOUksZ0JBQWdCLEVBQUUsc0NBQXNCLENBQUMsYUFBYTtTQUN2RDtLQUNGO0lBQ0QsUUFBUSxFQUFFO1FBQ1I7WUFDRSxHQUFHLEVBQUUscUJBQXFCO1lBQzFCLEtBQUssRUFBRSxjQUFjO1lBQ3JCLFdBQVcsRUFDVCxrbkJBQWtuQjtZQUNwbkIsSUFBSSxFQUFFLGdFQUFnRTtZQUN0RSxLQUFLLEVBQUUsY0FBYztZQUNyQixLQUFLLEVBQUUsTUFBTTtZQUNiLElBQUksRUFBRSxRQUFRO1lBQ2QsR0FBRyxFQUFFLENBQUM7WUFDTixHQUFHLEVBQUUsR0FBRztZQUNSLE9BQU8sRUFBRSxJQUFJO1lBQ2IsT0FBTyxFQUFFLEVBQUU7U0FDWjtRQUNEO1lBQ0UsR0FBRyxFQUFFLDZCQUE2QjtZQUNsQyxLQUFLLEVBQUUscUNBQXFDO1lBQzVDLFdBQVcsRUFBRSw4Q0FBOEM7WUFDM0QsSUFBSSxFQUFFLDBIQUEwSDtZQUNoSSxLQUFLLEVBQUUsY0FBYztZQUNyQixJQUFJLEVBQUUsU0FBUztZQUNmLE9BQU8sRUFBRSxLQUFLO1NBQ2Y7UUFDRDtZQUNFLEdBQUcsRUFBRSx3QkFBd0I7WUFDN0IsS0FBSyxFQUFFLGtCQUFrQjtZQUN6QixXQUFXLEVBQUUseUJBQXlCO1lBQ3RDLEtBQUssRUFBRSxjQUFjO1lBQ3JCLElBQUksRUFBRSxTQUFTO1lBQ2YsT0FBTyxFQUFFLEtBQUs7U0FDZjtRQUNEO1lBQ0UsR0FBRyxFQUFFLHlCQUF5QjtZQUM5QixLQUFLLEVBQUUseUJBQXlCO1lBQ2hDLFdBQVcsRUFBRSxnRkFBZ0Y7WUFDN0YsS0FBSyxFQUFFLGNBQWM7WUFDckIsSUFBSSxFQUFFLFNBQVM7WUFDZixPQUFPLEVBQUUsS0FBSztTQUNmO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeProviderIdentity = void 0;
4
+ /**
5
+ * Deletes a provider identity for good and, when no provider identity is left
6
+ * on its auth identity, the auth identity too. The unique index on
7
+ * `(entity_id, provider)` has no `deleted_at` filter, so a soft delete would
8
+ * block the address for a new registration.
9
+ */
10
+ const removeProviderIdentity = async (auth, identity) => {
11
+ await auth.deleteProviderIdentities([identity.providerIdentityId]);
12
+ const remaining = await auth.listProviderIdentities({ auth_identity_id: identity.authIdentityId }, { select: ["id"], take: 1 });
13
+ if (!remaining.length) {
14
+ await auth.deleteAuthIdentities([identity.authIdentityId]);
15
+ }
16
+ };
17
+ exports.removeProviderIdentity = removeProviderIdentity;
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLXByb3ZpZGVyLWlkZW50aXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvcmVtb3ZlLXByb3ZpZGVyLWlkZW50aXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBOzs7OztHQUtHO0FBQ0ksTUFBTSxzQkFBc0IsR0FBRyxLQUFLLEVBQ3pDLElBQXdCLEVBQ3hCLFFBQTBFLEVBQzNELEVBQUU7SUFDakIsTUFBTSxJQUFJLENBQUMsd0JBQXdCLENBQUMsQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUNqRCxFQUFFLGdCQUFnQixFQUFFLFFBQVEsQ0FBQyxjQUFjLEVBQUUsRUFDN0MsRUFBRSxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQzVCLENBQUE7SUFDRCxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQVpZLFFBQUEsc0JBQXNCLDBCQVlsQyJ9
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stopCustomerAuthMethods = exports.startCustomerAuthMethods = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const module_access_1 = require("../public/module-access");
6
+ const customer_auth_methods_snapshot_1 = require("./customer-auth-methods-snapshot");
7
+ const plugin_features_1 = require("./plugin-features");
8
+ /**
9
+ * Hands the snapshot behind `authMethodsPerActor` its reader (the effective
10
+ * `customer_auth` switches) and loads it once. Called when the application
11
+ * has started, with the application container: the config getter that needs
12
+ * the switches has no container of its own.
13
+ */
14
+ const startCustomerAuthMethods = (container) => (0, customer_auth_methods_snapshot_1.startCustomerAuthMethodsSnapshot)({
15
+ read: async () => (await (0, module_access_1.resolveBaseModule)(container).retrieveEffectiveFeatures(plugin_features_1.customerAuthPluginFeatures)).features,
16
+ warn: (message) => container.resolve(utils_1.ContainerRegistrationKeys.LOGGER).warn(message),
17
+ });
18
+ exports.startCustomerAuthMethods = startCustomerAuthMethods;
19
+ exports.stopCustomerAuthMethods = customer_auth_methods_snapshot_1.stopCustomerAuthMethodsSnapshot;
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnQtY3VzdG9tZXItYXV0aC1tZXRob2RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2N1c3RvbWVyLWF1dGgvc3RhcnQtY3VzdG9tZXItYXV0aC1tZXRob2RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUFxRTtBQUVyRSwyREFBMkQ7QUFFM0QscUZBR3lDO0FBQ3pDLHVEQUE4RDtBQVE5RDs7Ozs7R0FLRztBQUNJLE1BQU0sd0JBQXdCLEdBQUcsQ0FDdEMsU0FBMEIsRUFDWCxFQUFFLENBQ2pCLElBQUEsaUVBQWdDLEVBQUM7SUFDL0IsSUFBSSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQ2YsQ0FDRSxNQUFNLElBQUEsaUNBQWlCLEVBQ3JCLFNBQVMsQ0FDVixDQUFDLHlCQUF5QixDQUFDLDRDQUEwQixDQUFDLENBQ3hELENBQUMsUUFBUTtJQUNaLElBQUksRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQ2hCLFNBQVMsQ0FBQyxPQUFPLENBQVMsaUNBQXlCLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQztDQUM1RSxDQUFDLENBQUE7QUFaUyxRQUFBLHdCQUF3Qiw0QkFZakM7QUFFUyxRQUFBLHVCQUF1QixHQUFHLGdFQUErQixDQUFBIn0=
@@ -6,6 +6,10 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
6
6
  plugin_key: "documents",
7
7
  label: "Dokumente",
8
8
  description: "Rechnungen, Stornos und Packlisten zu Bestellungen. Ausgeschaltet sind alle Dokument-Routen in Admin und Storefront nicht erreichbar und es werden keine neuen Dokumente erstellt; bereits ausgestellte Dokumente bleiben gespeichert. Checkout, Fulfillment und Versand laufen unverändert weiter.",
9
+ summary: "Rechnungen, Stornos und Packlisten zu Bestellungen.",
10
+ icon: "DocumentText",
11
+ disable_hint: "Aus: Dokument-Routen sind nicht erreichbar, es entstehen keine neuen Dokumente. Bestehende bleiben gespeichert.",
12
+ disable_confirm: "Alle Dokument-Routen in Admin und Storefront sind dann nicht erreichbar, und es werden keine neuen Dokumente erstellt. Bereits ausgestellte Dokumente bleiben gespeichert. Checkout, Fulfillment und Versand laufen unverändert weiter.",
9
13
  features: [
10
14
  {
11
15
  key: "invoices",
@@ -16,7 +20,7 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
16
20
  {
17
21
  key: "packing_lists",
18
22
  label: "Packlisten",
19
- description: "Packlisten je Fulfillment inklusive Chargenrevisionen und Abruf im Admin.",
23
+ description: "Eine Packliste je Fulfillment, inklusive Chargenrevisionen und Abruf im Admin.",
20
24
  default_enabled: true,
21
25
  },
22
26
  ],
@@ -25,8 +29,10 @@ exports.documentsPluginFeatures = (0, public_1.definePluginFeatures)({
25
29
  key: "payment_terms",
26
30
  label: "Zahlungskonditionen",
27
31
  description: "Erscheint auf Rechnungen unter den Summen, z. B. „Der Betrag wurde bei der Bestellung per Kreditkarte bezahlt.“ Leer lassen, wenn nichts stehen soll. Gilt für neu erstellte und neu erzeugte Rechnungen.",
32
+ hint: "Erscheint auf Rechnungen unter den Summen.",
33
+ placeholder: "z. B. Der Betrag wurde bei der Bestellung per Kreditkarte bezahlt.",
28
34
  type: "string",
29
35
  },
30
36
  ],
31
37
  });
32
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWZlYXR1cmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2RvY3VtZW50cy9wbHVnaW4tZmVhdHVyZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsc0NBQWdEO0FBRW5DLFFBQUEsdUJBQXVCLEdBQUcsSUFBQSw2QkFBb0IsRUFBQztJQUMxRCxVQUFVLEVBQUUsV0FBVztJQUN2QixLQUFLLEVBQUUsV0FBVztJQUNsQixXQUFXLEVBQ1QscVNBQXFTO0lBQ3ZTLFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLFVBQVU7WUFDZixLQUFLLEVBQUUsc0JBQXNCO1lBQzdCLFdBQVcsRUFDVCxzYkFBc2I7WUFDeGIsZUFBZSxFQUFFLElBQUk7U0FDdEI7UUFDRDtZQUNFLEdBQUcsRUFBRSxlQUFlO1lBQ3BCLEtBQUssRUFBRSxZQUFZO1lBQ25CLFdBQVcsRUFDVCwyRUFBMkU7WUFDN0UsZUFBZSxFQUFFLElBQUk7U0FDdEI7S0FDRjtJQUNELFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLGVBQWU7WUFDcEIsS0FBSyxFQUFFLHFCQUFxQjtZQUM1QixXQUFXLEVBQ1QsMk1BQTJNO1lBQzdNLElBQUksRUFBRSxRQUFRO1NBQ2Y7S0FDRjtDQUNGLENBQUMsQ0FBQSJ9
38
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLWZlYXR1cmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2RvY3VtZW50cy9wbHVnaW4tZmVhdHVyZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsc0NBQWdEO0FBRW5DLFFBQUEsdUJBQXVCLEdBQUcsSUFBQSw2QkFBb0IsRUFBQztJQUMxRCxVQUFVLEVBQUUsV0FBVztJQUN2QixLQUFLLEVBQUUsV0FBVztJQUNsQixXQUFXLEVBQ1QscVNBQXFTO0lBQ3ZTLE9BQU8sRUFBRSxxREFBcUQ7SUFDOUQsSUFBSSxFQUFFLGNBQWM7SUFDcEIsWUFBWSxFQUNWLGlIQUFpSDtJQUNuSCxlQUFlLEVBQ2IseU9BQXlPO0lBQzNPLFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLFVBQVU7WUFDZixLQUFLLEVBQUUsc0JBQXNCO1lBQzdCLFdBQVcsRUFDVCxzYkFBc2I7WUFDeGIsZUFBZSxFQUFFLElBQUk7U0FDdEI7UUFDRDtZQUNFLEdBQUcsRUFBRSxlQUFlO1lBQ3BCLEtBQUssRUFBRSxZQUFZO1lBQ25CLFdBQVcsRUFDVCxnRkFBZ0Y7WUFDbEYsZUFBZSxFQUFFLElBQUk7U0FDdEI7S0FDRjtJQUNELFFBQVEsRUFBRTtRQUNSO1lBQ0UsR0FBRyxFQUFFLGVBQWU7WUFDcEIsS0FBSyxFQUFFLHFCQUFxQjtZQUM1QixXQUFXLEVBQ1QsMk1BQTJNO1lBQzdNLElBQUksRUFBRSw0Q0FBNEM7WUFDbEQsV0FBVyxFQUFFLG9FQUFvRTtZQUNqRixJQUFJLEVBQUUsUUFBUTtTQUNmO0tBQ0Y7Q0FDRixDQUFDLENBQUEifQ==
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deliverEphemeralAuthMail = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const failure_classification_1 = require("../domain/failure-classification");
6
+ const load_email_shop_details_1 = require("../rendering/load-email-shop-details");
7
+ const load_template_1 = require("../templates/load-template");
8
+ const dispatch_email_message_1 = require("../../workflows/email/dispatch-email-message");
9
+ const asNotificationRecord = (value) => {
10
+ if (!value || typeof value !== "object") {
11
+ return null;
12
+ }
13
+ const record = value;
14
+ if (typeof record.id !== "string" ||
15
+ !record.id.trim() ||
16
+ (record.status !== "failure" &&
17
+ record.status !== "pending" &&
18
+ record.status !== "success")) {
19
+ return null;
20
+ }
21
+ return { id: record.id, status: record.status };
22
+ };
23
+ const failClaim = async (email, claim, code, now, notificationId = null) => {
24
+ const completed = await email.markAttemptFailed({
25
+ attempt_id: claim.attempt.id,
26
+ attempt_token: claim.attempt_token,
27
+ failure_code: code,
28
+ finished_at: now,
29
+ notification_id: notificationId,
30
+ });
31
+ return {
32
+ kind: "done",
33
+ result: {
34
+ attempt_id: claim.attempt.id,
35
+ code,
36
+ message_id: completed.message.id,
37
+ status: "failed",
38
+ },
39
+ };
40
+ };
41
+ /**
42
+ * One delivery attempt of a claimed message whose content carries a secret
43
+ * that lives only in the caller's `render` closure (password reset link,
44
+ * account confirmation link, login code): renders it, renews the lease, hands
45
+ * the content to the notification module and records the outcome on the
46
+ * attempt. The message stores no template data of the secret, the notification
47
+ * record keeps only ids, and nothing here is logged.
48
+ *
49
+ * Customer mails only (`resource_type` "customer").
50
+ */
51
+ const deliverEphemeralAuthMail = async ({ claim, container, email, expiresAt, getNow, kind, maxAttempts, render, retryDelayMs, triggerType, }) => {
52
+ // A stored template never fails the mail: it falls back to the default.
53
+ const [shop, template] = await Promise.all([
54
+ (0, load_email_shop_details_1.loadEmailShopDetails)(container),
55
+ (0, load_template_1.loadEmailTemplate)(container, email, kind),
56
+ ]);
57
+ let rendered;
58
+ try {
59
+ rendered = render({
60
+ configuration: email.getDispatchConfiguration(),
61
+ shop,
62
+ template,
63
+ });
64
+ }
65
+ catch {
66
+ return await failClaim(email, claim, "render_failed", getNow());
67
+ }
68
+ const beforeNotification = getNow();
69
+ if (expiresAt.getTime() <= beforeNotification.getTime()) {
70
+ return await failClaim(email, claim, "payload_expired", beforeNotification);
71
+ }
72
+ try {
73
+ await email.renewAttemptLease({
74
+ attempt_id: claim.attempt.id,
75
+ attempt_token: claim.attempt_token,
76
+ now: beforeNotification,
77
+ });
78
+ }
79
+ catch {
80
+ return { kind: "retry" };
81
+ }
82
+ const notifications = container.resolve(utils_1.Modules.NOTIFICATION);
83
+ const safeData = {
84
+ attempt_id: claim.attempt.id,
85
+ kind: claim.message.kind,
86
+ message_id: claim.message.id,
87
+ };
88
+ let creationError;
89
+ let created = null;
90
+ try {
91
+ created = asNotificationRecord(await notifications.createNotifications({
92
+ channel: "email",
93
+ content: {
94
+ html: rendered.html,
95
+ subject: rendered.subject,
96
+ text: rendered.text,
97
+ },
98
+ data: safeData,
99
+ idempotency_key: (0, failure_classification_1.emailAttemptNotificationKey)(claim.attempt.id),
100
+ ...(claim.previous_notification_id
101
+ ? { original_notification_id: claim.previous_notification_id }
102
+ : {}),
103
+ provider_data: safeData,
104
+ ...(claim.message.resource_id
105
+ ? { resource_id: claim.message.resource_id }
106
+ : {}),
107
+ resource_type: "customer",
108
+ template: claim.message.kind,
109
+ to: claim.message.recipient,
110
+ trigger_type: triggerType,
111
+ }));
112
+ }
113
+ catch (error) {
114
+ creationError = error;
115
+ }
116
+ let persisted = null;
117
+ let lookupFailed = false;
118
+ if (!created || creationError) {
119
+ try {
120
+ persisted = await (0, dispatch_email_message_1.findAttemptNotification)(container, claim.attempt.id);
121
+ }
122
+ catch {
123
+ lookupFailed = true;
124
+ }
125
+ }
126
+ const notification = created ?? persisted;
127
+ const finishedAt = getNow();
128
+ if (notification?.status === "success") {
129
+ const completed = await email.markAttemptSent({
130
+ attempt_id: claim.attempt.id,
131
+ attempt_token: claim.attempt_token,
132
+ finished_at: finishedAt,
133
+ notification_id: notification.id,
134
+ });
135
+ return {
136
+ kind: "done",
137
+ result: {
138
+ attempt_id: claim.attempt.id,
139
+ message_id: completed.message.id,
140
+ status: "sent",
141
+ },
142
+ };
143
+ }
144
+ const failure = creationError && notification?.status === "failure" && !lookupFailed
145
+ ? (0, failure_classification_1.classifyNotificationFailure)(creationError)
146
+ : { code: "outcome_unknown", outcome: "unknown" };
147
+ if (failure.outcome === "retryable") {
148
+ const nextAttemptAt = new Date(finishedAt.getTime() + retryDelayMs);
149
+ if (claim.attempt.attempt_number < maxAttempts &&
150
+ nextAttemptAt.getTime() < expiresAt.getTime()) {
151
+ await email.markAttemptRetryable({
152
+ attempt_id: claim.attempt.id,
153
+ attempt_token: claim.attempt_token,
154
+ failure_code: failure.code,
155
+ finished_at: finishedAt,
156
+ next_attempt_at: nextAttemptAt,
157
+ notification_id: notification?.id ?? null,
158
+ });
159
+ return { kind: "retry" };
160
+ }
161
+ return await failClaim(email, claim, expiresAt.getTime() <= nextAttemptAt.getTime()
162
+ ? "payload_expired"
163
+ : "retry_exhausted", finishedAt, notification?.id ?? null);
164
+ }
165
+ if (failure.outcome === "failed") {
166
+ return await failClaim(email, claim, failure.code, finishedAt, notification?.id ?? null);
167
+ }
168
+ const completed = await email.markAttemptUnknown({
169
+ attempt_id: claim.attempt.id,
170
+ attempt_token: claim.attempt_token,
171
+ failure_code: failure.code,
172
+ finished_at: finishedAt,
173
+ notification_id: notification?.id ?? null,
174
+ });
175
+ return {
176
+ kind: "done",
177
+ result: {
178
+ attempt_id: claim.attempt.id,
179
+ code: failure.code,
180
+ message_id: completed.message.id,
181
+ status: "unknown",
182
+ },
183
+ };
184
+ };
185
+ exports.deliverEphemeralAuthMail = deliverEphemeralAuthMail;
186
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVsaXZlci1lcGhlbWVyYWwtYXV0aC1tYWlsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2VtYWlsL2FwcGxpY2F0aW9uL2RlbGl2ZXItZXBoZW1lcmFsLWF1dGgtbWFpbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFJQSxxREFBb0Q7QUFFcEQsNkVBRzBDO0FBRzFDLGtGQUE0RTtBQUU1RSw4REFBK0Q7QUFPL0QseUZBQXVGO0FBK0N2RixNQUFNLG9CQUFvQixHQUFHLENBQUMsS0FBYyxFQUE2QixFQUFFO0lBQ3pFLElBQUksQ0FBQyxLQUFLLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDeEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsS0FBZ0MsQ0FBQztJQUNoRCxJQUNFLE9BQU8sTUFBTSxDQUFDLEVBQUUsS0FBSyxRQUFRO1FBQzdCLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFDakIsQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLFNBQVM7WUFDMUIsTUFBTSxDQUFDLE1BQU0sS0FBSyxTQUFTO1lBQzNCLE1BQU0sQ0FBQyxNQUFNLEtBQUssU0FBUyxDQUFDLEVBQzlCLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sQ0FBQyxFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztBQUNsRCxDQUFDLENBQUM7QUFFRixNQUFNLFNBQVMsR0FBRyxLQUFLLEVBQ3JCLEtBQXlCLEVBQ3pCLEtBQXdCLEVBQ3hCLElBQVksRUFDWixHQUFTLEVBQ1QsaUJBQWdDLElBQUksRUFDRCxFQUFFO0lBQ3JDLE1BQU0sU0FBUyxHQUFHLE1BQU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDO1FBQzlDLFVBQVUsRUFBRSxLQUFLLENBQUMsT0FB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