@endora-commerce/mod-customer-accounts 0.100.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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/api/customer-groups-client.d.ts +27 -0
  4. package/dist/admin/api/customer-groups-client.d.ts.map +1 -0
  5. package/dist/admin/api/customer-groups-client.js +17 -0
  6. package/dist/admin/api/customer-groups-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +33 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +38 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/CustomerGroupsPage.d.ts +18 -0
  12. package/dist/admin/pages/CustomerGroupsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomerGroupsPage.js +108 -0
  14. package/dist/admin/pages/CustomerGroupsPage.js.map +1 -0
  15. package/dist/backend/entities/customer-account.entity.d.ts +111 -0
  16. package/dist/backend/entities/customer-account.entity.d.ts.map +1 -0
  17. package/dist/backend/entities/customer-account.entity.js +230 -0
  18. package/dist/backend/entities/customer-account.entity.js.map +1 -0
  19. package/dist/backend/entities/customer-group.entity.d.ts +29 -0
  20. package/dist/backend/entities/customer-group.entity.d.ts.map +1 -0
  21. package/dist/backend/entities/customer-group.entity.js +70 -0
  22. package/dist/backend/entities/customer-group.entity.js.map +1 -0
  23. package/dist/backend/entities/password-reset-token.entity.d.ts +16 -0
  24. package/dist/backend/entities/password-reset-token.entity.d.ts.map +1 -0
  25. package/dist/backend/entities/password-reset-token.entity.js +59 -0
  26. package/dist/backend/entities/password-reset-token.entity.js.map +1 -0
  27. package/dist/backend/index.d.ts +144 -0
  28. package/dist/backend/index.d.ts.map +1 -0
  29. package/dist/backend/index.js +407 -0
  30. package/dist/backend/index.js.map +1 -0
  31. package/dist/backend/routes.admin.d.ts +26 -0
  32. package/dist/backend/routes.admin.d.ts.map +1 -0
  33. package/dist/backend/routes.admin.js +35 -0
  34. package/dist/backend/routes.admin.js.map +1 -0
  35. package/dist/backend/services/audit-references.d.ts +22 -0
  36. package/dist/backend/services/audit-references.d.ts.map +1 -0
  37. package/dist/backend/services/audit-references.js +46 -0
  38. package/dist/backend/services/audit-references.js.map +1 -0
  39. package/dist/backend/services/customer-account-lifecycle-ports.d.ts +168 -0
  40. package/dist/backend/services/customer-account-lifecycle-ports.d.ts.map +1 -0
  41. package/dist/backend/services/customer-account-lifecycle-ports.js +420 -0
  42. package/dist/backend/services/customer-account-lifecycle-ports.js.map +1 -0
  43. package/dist/backend/services/customer-account-ports.d.ts +143 -0
  44. package/dist/backend/services/customer-account-ports.d.ts.map +1 -0
  45. package/dist/backend/services/customer-account-ports.js +422 -0
  46. package/dist/backend/services/customer-account-ports.js.map +1 -0
  47. package/dist/backend/services/customer-auth-service.d.ts +62 -0
  48. package/dist/backend/services/customer-auth-service.d.ts.map +1 -0
  49. package/dist/backend/services/customer-auth-service.js +105 -0
  50. package/dist/backend/services/customer-auth-service.js.map +1 -0
  51. package/dist/backend/services/customer-group-read-port.d.ts +24 -0
  52. package/dist/backend/services/customer-group-read-port.d.ts.map +1 -0
  53. package/dist/backend/services/customer-group-read-port.js +44 -0
  54. package/dist/backend/services/customer-group-read-port.js.map +1 -0
  55. package/dist/backend/services/customer-group-service.d.ts +21 -0
  56. package/dist/backend/services/customer-group-service.d.ts.map +1 -0
  57. package/dist/backend/services/customer-group-service.js +97 -0
  58. package/dist/backend/services/customer-group-service.js.map +1 -0
  59. package/dist/backend/services/customer-rollup-scope.d.ts +30 -0
  60. package/dist/backend/services/customer-rollup-scope.d.ts.map +1 -0
  61. package/dist/backend/services/customer-rollup-scope.js +42 -0
  62. package/dist/backend/services/customer-rollup-scope.js.map +1 -0
  63. package/dist/backend/services/password-reset-service.d.ts +15 -0
  64. package/dist/backend/services/password-reset-service.d.ts.map +1 -0
  65. package/dist/backend/services/password-reset-service.js +90 -0
  66. package/dist/backend/services/password-reset-service.js.map +1 -0
  67. package/dist/backend/services/role-service.d.ts +22 -0
  68. package/dist/backend/services/role-service.d.ts.map +1 -0
  69. package/dist/backend/services/role-service.js +83 -0
  70. package/dist/backend/services/role-service.js.map +1 -0
  71. package/dist/backend/services/two-factor-enrolments.d.ts +18 -0
  72. package/dist/backend/services/two-factor-enrolments.d.ts.map +1 -0
  73. package/dist/backend/services/two-factor-enrolments.js +2 -0
  74. package/dist/backend/services/two-factor-enrolments.js.map +1 -0
  75. package/dist/manifest.d.ts +169 -0
  76. package/dist/manifest.d.ts.map +1 -0
  77. package/dist/manifest.js +212 -0
  78. package/dist/manifest.js.map +1 -0
  79. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts +6 -0
  80. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.d.ts.map +1 -0
  81. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js +25 -0
  82. package/dist/migrations/20260425T055041_customer_accounts_password_reset_tokens.js.map +1 -0
  83. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts +34 -0
  84. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.d.ts.map +1 -0
  85. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js +43 -0
  86. package/dist/migrations/20260611T140351_customer_accounts_organization_optional.js.map +1 -0
  87. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts +54 -0
  88. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.d.ts.map +1 -0
  89. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js +97 -0
  90. package/dist/migrations/20260611T140403_customer_accounts_lifecycle.js.map +1 -0
  91. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts +12 -0
  92. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.d.ts.map +1 -0
  93. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js +16 -0
  94. package/dist/migrations/20260718T200341_customer_accounts_customer_account_custom_field_values.js.map +1 -0
  95. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts +14 -0
  96. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.d.ts.map +1 -0
  97. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js +18 -0
  98. package/dist/migrations/20260720T044255_customer_accounts_customer_subtree_rollup.js.map +1 -0
  99. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts +22 -0
  100. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.d.ts.map +1 -0
  101. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js +26 -0
  102. package/dist/migrations/20260819T074816_customer_accounts_password_set_at.js.map +1 -0
  103. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts +46 -0
  104. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.d.ts.map +1 -0
  105. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js +97 -0
  106. package/dist/migrations/20260819T142837_customer_accounts_fold_email_case.js.map +1 -0
  107. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts +31 -0
  108. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.d.ts.map +1 -0
  109. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js +35 -0
  110. package/dist/migrations/20260825T124759_customer_accounts_drop_legacy_two_factor_secret.js.map +1 -0
  111. package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts +58 -0
  112. package/dist/migrations/20260825T141659_customer_accounts_organization_required.d.ts.map +1 -0
  113. package/dist/migrations/20260825T141659_customer_accounts_organization_required.js +133 -0
  114. package/dist/migrations/20260825T141659_customer_accounts_organization_required.js.map +1 -0
  115. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts +27 -0
  116. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.d.ts.map +1 -0
  117. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js +44 -0
  118. package/dist/migrations/20260912T094701_customer_accounts_sales_channel_customer_accounts.js.map +1 -0
  119. package/dist/migrations/index.d.ts +36 -0
  120. package/dist/migrations/index.d.ts.map +1 -0
  121. package/dist/migrations/index.js +47 -0
  122. package/dist/migrations/index.js.map +1 -0
  123. package/docs/customer_accounts.md +38 -0
  124. package/i18n/en.json +30 -0
  125. package/i18n/pl.json +30 -0
  126. package/package.json +94 -0
  127. package/tailwind.css +14 -0
@@ -0,0 +1,83 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { CustomerAccount } from '../entities/customer-account.entity.js';
4
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
5
+ /**
6
+ * RoleService (T174).
7
+ *
8
+ * Members management on the buyer side. Two safety guards:
9
+ * - CANNOT_REMOVE_LAST_ADMIN — DELETE on the only `organization_admin`
10
+ * member of an org returns 409.
11
+ * - CANNOT_DEMOTE_LAST_ADMIN — PATCH /role downgrading the only
12
+ * `organization_admin` returns 409.
13
+ */
14
+ export class RoleService {
15
+ emFactory;
16
+ auditLog;
17
+ constructor(emFactory, auditLog) {
18
+ this.emFactory = emFactory;
19
+ this.auditLog = auditLog;
20
+ }
21
+ #audit(em, action, objectId, stateBefore, stateAfter) {
22
+ if (this.auditLog) {
23
+ recordAuditFromContext(this.auditLog, em, {
24
+ action,
25
+ objectType: 'customer_account',
26
+ objectId,
27
+ stateBefore,
28
+ stateAfter,
29
+ });
30
+ }
31
+ }
32
+ async listMembers(organizationId) {
33
+ const em = this.emFactory();
34
+ return em.find(CustomerAccount, { organizationId, deletedAt: null }, { orderBy: { role: 'asc', createdAt: 'asc' } });
35
+ }
36
+ async changeRole(organizationId, targetCustomerAccountId, newRole) {
37
+ const em = this.emFactory();
38
+ const target = await em.findOne(CustomerAccount, {
39
+ id: targetCustomerAccountId,
40
+ organizationId,
41
+ deletedAt: null,
42
+ });
43
+ if (!target) {
44
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Member not found.');
45
+ }
46
+ if (target.role === 'organization_admin' &&
47
+ newRole === 'regular_user' &&
48
+ (await this.#countAdmins(em, organizationId)) === 1) {
49
+ throw new HttpError(409, ERROR_CODES.CANNOT_DEMOTE_LAST_ADMIN, 'Cannot demote the only organization admin.');
50
+ }
51
+ const previousRole = target.role;
52
+ target.role = newRole;
53
+ this.#audit(em, 'customer_account.change_role', target.id, { role: previousRole }, { role: newRole });
54
+ await em.flush();
55
+ return target;
56
+ }
57
+ async removeMember(organizationId, targetCustomerAccountId) {
58
+ const em = this.emFactory();
59
+ const target = await em.findOne(CustomerAccount, {
60
+ id: targetCustomerAccountId,
61
+ organizationId,
62
+ deletedAt: null,
63
+ });
64
+ if (!target) {
65
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Member not found.');
66
+ }
67
+ if (target.role === 'organization_admin' &&
68
+ (await this.#countAdmins(em, organizationId)) === 1) {
69
+ throw new HttpError(409, ERROR_CODES.CANNOT_REMOVE_LAST_ADMIN, 'Cannot remove the only organization admin.');
70
+ }
71
+ target.deletedAt = new Date();
72
+ this.#audit(em, 'customer_account.remove_member', target.id, { role: target.role }, null);
73
+ await em.flush();
74
+ }
75
+ async #countAdmins(em, organizationId) {
76
+ return em.count(CustomerAccount, {
77
+ organizationId,
78
+ role: 'organization_admin',
79
+ deletedAt: null,
80
+ });
81
+ }
82
+ }
83
+ //# sourceMappingURL=role-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-service.js","sourceRoot":"","sources":["../../../src/backend/services/role-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Which customer accounts hold a second factor — this module's own view of a
3
+ * fact `mfa` owns.
4
+ *
5
+ * A function type rather than an interface, and not shared with `admin_users`,
6
+ * because there is nothing to share: the published shape is
7
+ * `MfaEnrolmentStatePort` in `@endora-commerce/contracts`, and this is only the
8
+ * shape in which *this* module's mapper wants the answer — `subjectType` is
9
+ * already decided, and a `Set` is what a list surface asks per row.
10
+ *
11
+ * `backend.ts` builds the one implementation: it decides `mfa`'s presence and
12
+ * then resolves the port, so an absent owner produces an empty set rather than
13
+ * a `ModuleDisabledError` on a customer's own account page. That is a declared
14
+ * degrade — see the `degrades-without` entry for `mfaEnrolmentStatePort` in
15
+ * this module's manifest — and not a caught gate.
16
+ */
17
+ export type TwoFactorEnrolmentReader = (customerAccountIds: readonly string[]) => Promise<ReadonlySet<string>>;
18
+ //# sourceMappingURL=two-factor-enrolments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-factor-enrolments.d.ts","sourceRoot":"","sources":["../../../src/backend/services/two-factor-enrolments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,wBAAwB,GAAG,CACrC,kBAAkB,EAAE,SAAS,MAAM,EAAE,KAClC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=two-factor-enrolments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-factor-enrolments.js","sourceRoot":"","sources":["../../../src/backend/services/two-factor-enrolments.ts"],"names":[],"mappings":""}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Customer Accounts module — manifest backfill (Module Lifecycle, feature 018).
3
+ *
4
+ * Predates the lifecycle system; this manifest is the static record
5
+ * required so the module participates in the registry. No install /
6
+ * uninstall hook today — the module's schema is owned by earlier
7
+ * platform-wide migrations.
8
+ */
9
+ export declare const manifest: {
10
+ id: string;
11
+ name: string;
12
+ version: string;
13
+ dependencies: string[];
14
+ description?: string | undefined;
15
+ acknowledgedDependencies?: {
16
+ moduleId: string;
17
+ port: string;
18
+ reason: string;
19
+ }[] | undefined;
20
+ nonBindingDependencies?: {
21
+ moduleId: string;
22
+ name: string;
23
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
24
+ reason: string;
25
+ whenAbsent?: string | undefined;
26
+ }[] | undefined;
27
+ activation?: {
28
+ settingCode: string;
29
+ default: boolean;
30
+ } | {
31
+ nonDeactivatable: true;
32
+ reason: string;
33
+ } | undefined;
34
+ settings?: {
35
+ moduleCode: string;
36
+ groups: {
37
+ code: string;
38
+ name: string;
39
+ salesChannelCodes?: string[] | undefined;
40
+ isSystemProtected?: boolean | undefined;
41
+ }[];
42
+ settings: {
43
+ code: string;
44
+ name: string;
45
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
46
+ defaultValue: unknown;
47
+ description?: string | undefined;
48
+ groupCode?: string | undefined;
49
+ previousDefaultValues?: unknown[] | undefined;
50
+ salesChannelCodes?: string[] | undefined;
51
+ enumOptions?: string[] | undefined;
52
+ configurationType?: string | undefined;
53
+ hidden?: boolean | undefined;
54
+ }[];
55
+ } | undefined;
56
+ i18n?: {
57
+ bundlesDir: string;
58
+ } | undefined;
59
+ docs?: false | {
60
+ dir: string;
61
+ } | undefined;
62
+ demo?: false | {
63
+ summary: string;
64
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
65
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
66
+ after?: readonly string[] | undefined;
67
+ package?: string | undefined;
68
+ } | undefined;
69
+ actions?: {
70
+ id: string;
71
+ labelKey: string;
72
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
73
+ targetRoute: string;
74
+ keywords: string[];
75
+ weight: number;
76
+ descriptionKey?: string | undefined;
77
+ requiredPermission?: string | undefined;
78
+ }[] | undefined;
79
+ permissions?: {
80
+ code: string;
81
+ label: string;
82
+ module?: string | undefined;
83
+ description?: string | undefined;
84
+ requires?: string[] | undefined;
85
+ }[] | undefined;
86
+ transactionalEmails?: {
87
+ code: string;
88
+ name: string;
89
+ variables: {
90
+ key: string;
91
+ label: string;
92
+ sampleValue?: string | undefined;
93
+ description?: string | undefined;
94
+ }[];
95
+ description?: string | undefined;
96
+ group?: string | undefined;
97
+ }[] | undefined;
98
+ capabilities?: string[] | undefined;
99
+ exclusiveCapabilities?: {
100
+ key: string;
101
+ errorCode: string;
102
+ }[] | undefined;
103
+ errorCodes?: {
104
+ code: string;
105
+ tokens?: string[] | undefined;
106
+ }[] | undefined;
107
+ blocks?: {
108
+ name: string;
109
+ labelKey: string;
110
+ category: string;
111
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
112
+ fields: Record<string, {
113
+ type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
114
+ label?: string | undefined;
115
+ required?: boolean | undefined;
116
+ options?: {
117
+ label: string;
118
+ value: string | number;
119
+ }[] | undefined;
120
+ refKind?: string | undefined;
121
+ }>;
122
+ descriptionKey?: string | undefined;
123
+ defaultProps?: Record<string, unknown> | undefined;
124
+ responsiveFields?: string[] | undefined;
125
+ previewIcon?: string | undefined;
126
+ weight?: number | undefined;
127
+ }[] | undefined;
128
+ blockCategories?: {
129
+ key: string;
130
+ titleKey: string;
131
+ contexts: ("email" | "invoice" | "cms" | "newsletter")[];
132
+ weight?: number | undefined;
133
+ visible?: boolean | undefined;
134
+ }[] | undefined;
135
+ env?: {
136
+ name: string;
137
+ describes: {
138
+ en: string;
139
+ pl: string;
140
+ };
141
+ requirement: {
142
+ kind: "required";
143
+ } | {
144
+ kind: "requiredWhen";
145
+ input: string;
146
+ equals: string;
147
+ } | {
148
+ kind: "optional";
149
+ without: {
150
+ en: string;
151
+ pl: string;
152
+ };
153
+ };
154
+ secret: boolean;
155
+ generable: boolean;
156
+ owner: {
157
+ kind: "platform";
158
+ } | {
159
+ kind: "application";
160
+ application: "admin" | "backend" | "storefront";
161
+ } | {
162
+ kind: "module";
163
+ moduleId: string;
164
+ };
165
+ consumers: ("admin" | "backend" | "storefront")[];
166
+ addressOf: "admin" | "backend" | "storefront" | null;
167
+ }[] | undefined;
168
+ };
169
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+MnB,CAAC"}
@@ -0,0 +1,212 @@
1
+ import { defineModuleManifest } from '@endora-commerce/contracts';
2
+ /**
3
+ * Customer Accounts module — manifest backfill (Module Lifecycle, feature 018).
4
+ *
5
+ * Predates the lifecycle system; this manifest is the static record
6
+ * required so the module participates in the registry. No install /
7
+ * uninstall hook today — the module's schema is owned by earlier
8
+ * platform-wide migrations.
9
+ */
10
+ export const manifest = defineModuleManifest({
11
+ id: 'customer_accounts',
12
+ name: 'Customer Accounts',
13
+ description: 'Customer (B2B) account records, including organization membership and role.',
14
+ version: '1.0.0',
15
+ // `auth` owns `authSessionPort`, which `CustomerAuthService` resolves to mint
16
+ // and destroy a customer session; the edge became real with the conversion
17
+ // (feature 072, T094) and stays binding — a platform that cannot mint a
18
+ // session must refuse the login rather than issue one nothing can validate.
19
+ // It is *only* that: feature 075's Phase C took password hashing and the TOTP
20
+ // primitives out of this edge, because a pure function has no owner to be
21
+ // switched off.
22
+ // `customer_accounts.organization_id` is a real foreign key, and feature 072
23
+ // is what made it visible: the module exports its entities now, so the ORM
24
+ // registry attributes the table to it and the FK-drift check can see across
25
+ // the boundary. The edge predates the conversion — it was simply
26
+ // unattributable while the table belonged to nobody.
27
+ // `customer_accounts.customer_group_id` is a real foreign key too, and since
28
+ // feature 076 (D-79) it points at `customer_groups`, which this module now
29
+ // owns: the constraint is intra-module and declares nothing.
30
+ // `audit_logs` owns `auditReferenceRegistry`, the registry this module pushes
31
+ // its own "what is this audit row called, and where does the admin app show
32
+ // it?" resolver into (feature 075, D-87). The registry is ungated and its
33
+ // owner is non-deactivatable, so the declaration buys install and migration
34
+ // order rather than a flip-time refusal.
35
+ dependencies: ['audit_logs', 'auth', 'organizations'],
36
+ /**
37
+ * D-96 — `mfaLoginPort`, the second factor on customer login.
38
+ *
39
+ * Real to the container, binding on no operator. `mfa` declares this module
40
+ * in its own `dependencies`, so the ordinary declaration closes a cycle; and
41
+ * an acknowledged edge would put customer login among the dependents that
42
+ * refuse the flip, making a client security policy permanently unswitchable.
43
+ */
44
+ nonBindingDependencies: [
45
+ {
46
+ moduleId: 'mfa',
47
+ name: 'mfaLoginPort',
48
+ kind: 'degrades-without',
49
+ whenAbsent: 'Customer sign-in stops asking for a second factor and offers no Google/Microsoft ' +
50
+ 'button. An account created by social sign-in has no known password: its route in is ' +
51
+ '"forgot password".',
52
+ reason: 'CustomerAuthService verifies the password first and then asks the second factor what ' +
53
+ 'to do. With `mfa` absent it asks nobody: `backend.ts` probes ' +
54
+ '`effectiveState.isPresent("mfa")` and passes `undefined`, which selects the ' +
55
+ 'password-only branch feature 042 FR-033 requires and this service has always had. ' +
56
+ 'Nothing catches `ModuleDisabledError` — the decision is taken before the port is ' +
57
+ 'resolved. Org-level TOTP enforcement is `mfa`\'s own policy and goes with it. One ' +
58
+ 'edge is not repaired by the degrade and the operator has to know it: an account this ' +
59
+ 'module auto-created from a Google/Microsoft sign-in holds a random password nobody ' +
60
+ 'was ever told, so with the buttons gone its only route back is a password reset, ' +
61
+ 'keyed on the e-mail the provider verified.',
62
+ },
63
+ {
64
+ moduleId: 'mfa',
65
+ name: 'mfaEnrolmentStatePort',
66
+ kind: 'degrades-without',
67
+ whenAbsent: 'Reads "no second factor" for every customer — admin customer detail, both ' +
68
+ 'organisation member panels, and the buyer\'s own account page. True while MFA is off; ' +
69
+ 'not a claim that nobody is enrolled.',
70
+ reason: '`twoFactorEnabled` on `CustomerAccountRecord` is the live `mfa` enrolment, read in ' +
71
+ 'one batch through `mfaEnrolmentStatePort`. It was ' +
72
+ '`Boolean(account.twoFactorConfirmedAt)` until 2026-08-28, and that column\'s only ' +
73
+ 'non-null writer was the superseded TOTP path deleted on 2026-08-25 — so GET ' +
74
+ '/api/v1/me/customer told a buyer who had enrolled an hour earlier that their own ' +
75
+ 'account was unprotected. Non-binding for the reason the login edge beside it is: ' +
76
+ '`mfa` declares this module in its own `dependencies`, so an ordinary declaration ' +
77
+ 'closes a cycle, and an acknowledged edge would make a client security policy ' +
78
+ 'unswitchable. The degrade is taken by **not resolving** — `backend.ts` probes ' +
79
+ 'presence first and answers an empty set — so nothing catches `ModuleDisabledError`.',
80
+ },
81
+ ],
82
+ // Feature 074 (Constitution XVII), test C1 — reachability. The flag used to
83
+ // rest on four port edges another module declares; ruling 2 withdraws that
84
+ // authority, so the ground is now this module's own and it is the stronger
85
+ // one anyway. This module owns the identity a buyer signs in as. Switch it
86
+ // off and no customer-side path exists at all — no registration, no login,
87
+ // no cart belonging to anyone, no order placed by anyone — which is the
88
+ // reachability test rather than a reduction in capability.
89
+ /**
90
+ * Nothing to demonstrate of its own (feature 113, T211/T222 — contract §1.2).
91
+ *
92
+ * The demo shop does have a buyer, and it is not this module's demo data:
93
+ * `customer_accounts.organization_id` is `NOT NULL` (Principle XI), so there
94
+ * is no account to create before an `organizations` row exists to hold it, and
95
+ * "create the account, then join it" cannot be written at all. The account and
96
+ * its organisation are created in one statement, which makes them two modules'
97
+ * rows and a composition step (§5.1) — step 5 of
98
+ * `backend/src/seeds/demo-composition.ts`, which says so in its own words.
99
+ *
100
+ * `false` rather than absent, because the two are different states: this is a
101
+ * decision that the module owes nothing, not a module nobody has looked at.
102
+ */
103
+ demo: false,
104
+ activation: {
105
+ nonDeactivatable: true,
106
+ reason: 'The identity a buyer signs in as; no customer-side path — registration, login, cart, ' +
107
+ 'order or account — exists without it.',
108
+ },
109
+ /**
110
+ * The seven error codes this module owns — D-129's remaining sweep, MR 4
111
+ * (`specs/090-module-owned-error-codes/d129-sweep.md` §5.2, Appendix A;
112
+ * D-186 in `specs/080-f4-real-scope/rulings.md`).
113
+ *
114
+ * This module declared none until now. All seven were `_i18n`'s, not because
115
+ * anybody judged them the platform's but because the deleted prefix chain had
116
+ * no rule for them and its last line was `return 'core'`.
117
+ *
118
+ * **Four of them are the `CUSTOMER_*` record family, and D-186 §1 is what put
119
+ * them here rather than in `customers`.** The two modules are a data/behaviour
120
+ * pair — this one holds the `CustomerAccount` row, `customers` holds the
121
+ * lifecycle logic and the admin and storefront surfaces on top of it — and the
122
+ * ruling splits the family by noun: the customer **record** is this module's,
123
+ * `CustomerAddress` is `customers`', which is why `CUSTOMER_ADDRESS_NOT_FOUND`
124
+ * went there in MR 3 and these four come here.
125
+ *
126
+ * **Two of the four are codes `customers` raises and this module owns**, and
127
+ * that is the shape a reviewer will stop on. `CUSTOMER_NOT_FOUND` is raised
128
+ * six times, four of them in `customers`
129
+ * (`routes.self.ts`, `customer-deletion-service.ts`,
130
+ * `customer-moderation-service.ts`, `customer-org-assignment-service.ts`) and
131
+ * two here; `CUSTOMER_ALREADY_DELETED` is raised once in each. In every one of
132
+ * those `customers` sites the module is resolving or re-checking a
133
+ * `CustomerAccount` before doing its own work — it is the caller, and the row
134
+ * whose absence or deleted state it is reporting is this module's. D-95.2
135
+ * already ruled that shape for `INVOICE_NOT_READY`: the owner of a noun is not
136
+ * required to be the module that throws about it, and D-186 §1 accepts this
137
+ * consequence in terms.
138
+ *
139
+ * `CUSTOMER_NOT_DELETED` and `CUSTOMER_RESTORE_WINDOW_ELAPSED` are the same
140
+ * noun read the other way round, and both are raised only here, from
141
+ * `customer-account-lifecycle-ports.ts` — the soft-delete window is a property
142
+ * of the account row and of nothing on top of it.
143
+ *
144
+ * **`ACCOUNT_BLOCKED`** names the account itself: `customer-auth-service.ts`
145
+ * refuses a sign-in for a `CustomerAccount` whose status is blocked, which is
146
+ * this module's own column and its own gate.
147
+ *
148
+ * **`CANNOT_DEMOTE_LAST_ADMIN` and `CANNOT_REMOVE_LAST_ADMIN`** read like
149
+ * `organizations` and are not. The invariant they protect is counted over
150
+ * `CustomerAccount.role` inside one organization, in this module's
151
+ * `role-service.ts`, which is the only thing in the tree that raises either;
152
+ * `organizations` owns the organization, this module owns its membership.
153
+ *
154
+ * **The two of them arrive with a sentence, and it is a new one.** Both
155
+ * carried a placeholder in `_i18n`'s bundle — `"Cannot Demote Last Admin."` /
156
+ * `"Błąd: cannot demote last admin."` — which D-186 §2 deletes rather than
157
+ * moves, because a placeholder in a module's own bundle reads as that module's
158
+ * answer and every instrument then counts the code as translated.
159
+ * `d129-sweep.md` §5.4 keeps writing the prose available and calls it the
160
+ * better outcome; here the reader is an organization administrator managing
161
+ * their own company's users and the refusal is one sentence to explain, so it
162
+ * is written. The other five had no sentence in either language before the
163
+ * move and have none after: they were entries in
164
+ * `check-error-translations.ts`'s `UNTRANSLATED_ERROR_CODES` under `_i18n` and
165
+ * are entries under `customer_accounts`, which changes who owes the sentence
166
+ * and not whether one is owed.
167
+ *
168
+ * **No `tokens`, derived from the raise sites rather than from the bundle**
169
+ * (runbook §5). All eleven raise sites were read by balancing the parentheses
170
+ * of the call itself rather than by a line-wise grep, and not one passes a
171
+ * `details` object at all — so `refusalToken` has nothing to read and a
172
+ * sentence written at `errors.<CODE>` is the key the envelope actually looks
173
+ * up. That mattered enough to measure: where a code always carries a
174
+ * `details.code`, `localizeErrorEnvelope` resolves `errors.<CODE>.<token>`
175
+ * with no fallback to the base key, and a base sentence would be dead text
176
+ * that P1 nevertheless counts as translated.
177
+ */
178
+ errorCodes: [
179
+ { code: 'ACCOUNT_BLOCKED' },
180
+ { code: 'CANNOT_DEMOTE_LAST_ADMIN' },
181
+ { code: 'CANNOT_REMOVE_LAST_ADMIN' },
182
+ { code: 'CUSTOMER_ALREADY_DELETED' },
183
+ { code: 'CUSTOMER_NOT_DELETED' },
184
+ { code: 'CUSTOMER_NOT_FOUND' },
185
+ { code: 'CUSTOMER_RESTORE_WINDOW_ELAPSED' },
186
+ ],
187
+ i18n: { bundlesDir: 'i18n' },
188
+ docs: { dir: 'docs' },
189
+ // Feature 076 (D-79) — the customer-group admin surface came here with the
190
+ // entity, and its gate came with a correction. `price_lists` served these
191
+ // three routes under `catalog:write`, which asks a pricing question about a
192
+ // customer's segmentation; these two codes ask the right one. Granting them
193
+ // is a deliberate act on each admin role — nothing inherits from
194
+ // `catalog:write`.
195
+ permissions: [
196
+ { code: 'customer_groups:read', label: 'View customer groups' },
197
+ { code: 'customer_groups:write', label: 'Manage customer groups' },
198
+ ],
199
+ actions: [
200
+ {
201
+ id: 'open-customer-groups',
202
+ labelKey: 'actions.openCustomerGroups.label',
203
+ descriptionKey: 'actions.openCustomerGroups.description',
204
+ icon: 'Users',
205
+ targetRoute: '/customer-groups',
206
+ requiredPermission: 'customer_groups:read',
207
+ keywords: ['customer', 'group', 'segment', 'klient', 'grupa', 'segment'],
208
+ weight: 140,
209
+ },
210
+ ],
211
+ });
212
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ export declare class Migration20260425T055041CustomerAccountsPasswordResetTokens extends Migration {
3
+ up(): Promise<void>;
4
+ down(): Promise<void>;
5
+ }
6
+ //# sourceMappingURL=20260425T055041_customer_accounts_password_reset_tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260425T055041_customer_accounts_password_reset_tokens.d.ts","sourceRoot":"","sources":["../../src/migrations/20260425T055041_customer_accounts_password_reset_tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,qBAAa,2DAA4D,SAAQ,SAAS;IACzE,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrC"}
@@ -0,0 +1,25 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ export class Migration20260425T055041CustomerAccountsPasswordResetTokens extends Migration {
3
+ async up() {
4
+ this.addSql(`
5
+ create table "password_reset_tokens" (
6
+ "id" uuid not null,
7
+ "customer_account_id" uuid not null,
8
+ "token_hash" varchar(128) not null,
9
+ "expires_at" timestamptz not null,
10
+ "consumed_at" timestamptz null,
11
+ "created_at" timestamptz not null,
12
+ constraint "password_reset_tokens_pkey" primary key ("id"),
13
+ constraint "password_reset_tokens_token_hash_unique" unique ("token_hash"),
14
+ constraint "password_reset_tokens_customer_fk" foreign key ("customer_account_id")
15
+ references "customer_accounts" ("id") on delete cascade
16
+ );
17
+ `);
18
+ this.addSql('create index "password_reset_tokens_customer_account_id_index" on "password_reset_tokens" ("customer_account_id");');
19
+ this.addSql('create index "password_reset_tokens_expires_at_index" on "password_reset_tokens" ("expires_at");');
20
+ }
21
+ async down() {
22
+ this.addSql('drop table if exists "password_reset_tokens" cascade;');
23
+ }
24
+ }
25
+ //# sourceMappingURL=20260425T055041_customer_accounts_password_reset_tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260425T055041_customer_accounts_password_reset_tokens.js","sourceRoot":"","sources":["../../src/migrations/20260425T055041_customer_accounts_password_reset_tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,OAAO,2DAA4D,SAAQ,SAAS;IAC/E,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;;KAaX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,oHAAoH,CAAC,CAAC;QAClI,IAAI,CAAC,MAAM,CAAC,kGAAkG,CAAC,CAAC;IAClH,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;CACF"}
@@ -0,0 +1,34 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Relaxes `customer_accounts.organization_id` from NOT NULL to nullable
4
+ * (feature 026 US2 — Customer accounts may exist without an Organization).
5
+ *
6
+ * **This migration's design is dead, and everything below it describes a
7
+ * platform that no longer exists.** Feature 051 replaced it — an individual is
8
+ * backed by a single-member personal Organization — and D-178 re-tightened the
9
+ * column in
10
+ * `20260825T141659_customer_accounts_organization_required`. The paragraphs
11
+ * below are kept because they say what this statement did on the day it ran;
12
+ * read them as history and not as current behaviour. In particular the `down()`
13
+ * here is not a working re-tightening recipe: it sets `organization_id` to the
14
+ * account's own id against a live `customer_accounts_organization_fk`, so it
15
+ * fails rather than corrupting. Do not copy it.
16
+ *
17
+ * Existing rows are unaffected (they keep their organization_id). The
18
+ * column's foreign-key constraint stays in place — when the column IS
19
+ * populated, it still references organizations(id) ON DELETE RESTRICT.
20
+ *
21
+ * Application-side implications (handled in code, not in this migration):
22
+ * - The Customer's effective Organization (for prices, credit limit,
23
+ * payment / delivery / warehouse allow-lists) becomes null → falls
24
+ * back to platform defaults.
25
+ * - Order placement and Quote Request submission still REQUIRE an
26
+ * Organization — the route handlers surface a friendly 422 when the
27
+ * caller has no Organization. Orders and RFQs remain NOT NULL at the
28
+ * schema level.
29
+ */
30
+ export declare class Migration20260611T140351CustomerAccountsOrganizationOptional extends Migration {
31
+ up(): Promise<void>;
32
+ down(): Promise<void>;
33
+ }
34
+ //# sourceMappingURL=20260611T140351_customer_accounts_organization_optional.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260611T140351_customer_accounts_organization_optional.d.ts","sourceRoot":"","sources":["../../src/migrations/20260611T140351_customer_accounts_organization_optional.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,4DAA6D,SAAQ,SAAS;IAC1E,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAInB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAUrC"}
@@ -0,0 +1,43 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * Relaxes `customer_accounts.organization_id` from NOT NULL to nullable
4
+ * (feature 026 US2 — Customer accounts may exist without an Organization).
5
+ *
6
+ * **This migration's design is dead, and everything below it describes a
7
+ * platform that no longer exists.** Feature 051 replaced it — an individual is
8
+ * backed by a single-member personal Organization — and D-178 re-tightened the
9
+ * column in
10
+ * `20260825T141659_customer_accounts_organization_required`. The paragraphs
11
+ * below are kept because they say what this statement did on the day it ran;
12
+ * read them as history and not as current behaviour. In particular the `down()`
13
+ * here is not a working re-tightening recipe: it sets `organization_id` to the
14
+ * account's own id against a live `customer_accounts_organization_fk`, so it
15
+ * fails rather than corrupting. Do not copy it.
16
+ *
17
+ * Existing rows are unaffected (they keep their organization_id). The
18
+ * column's foreign-key constraint stays in place — when the column IS
19
+ * populated, it still references organizations(id) ON DELETE RESTRICT.
20
+ *
21
+ * Application-side implications (handled in code, not in this migration):
22
+ * - The Customer's effective Organization (for prices, credit limit,
23
+ * payment / delivery / warehouse allow-lists) becomes null → falls
24
+ * back to platform defaults.
25
+ * - Order placement and Quote Request submission still REQUIRE an
26
+ * Organization — the route handlers surface a friendly 422 when the
27
+ * caller has no Organization. Orders and RFQs remain NOT NULL at the
28
+ * schema level.
29
+ */
30
+ export class Migration20260611T140351CustomerAccountsOrganizationOptional extends Migration {
31
+ async up() {
32
+ this.addSql(`alter table "customer_accounts" alter column "organization_id" drop not null;`);
33
+ }
34
+ async down() {
35
+ // Safe re-tightening: only re-add NOT NULL if every existing row has a
36
+ // value. (Production should NEVER hit this path while feature 026 is in
37
+ // use — but the down() must be valid even if it cannot succeed against
38
+ // partially-migrated data.)
39
+ this.addSql(`update "customer_accounts" set "organization_id" = "id" where "organization_id" is null;`);
40
+ this.addSql(`alter table "customer_accounts" alter column "organization_id" set not null;`);
41
+ }
42
+ }
43
+ //# sourceMappingURL=20260611T140351_customer_accounts_organization_optional.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260611T140351_customer_accounts_organization_optional.js","sourceRoot":"","sources":["../../src/migrations/20260611T140351_customer_accounts_organization_optional.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,4DAA6D,SAAQ,SAAS;IAChF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC,+EAA+E,CAAC,CAAC;IAC/F,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,uEAAuE;QACvE,wEAAwE;QACxE,uEAAuE;QACvE,4BAA4B;QAC5B,IAAI,CAAC,MAAM,CACT,0FAA0F,CAC3F,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,8EAA8E,CAAC,CAAC;IAC9F,CAAC;CACF"}