@endora-commerce/mod-admin-users 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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/index.d.ts +47 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +44 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/AdminRolesPage.d.ts +3 -0
  8. package/dist/admin/pages/AdminRolesPage.d.ts.map +1 -0
  9. package/dist/admin/pages/AdminRolesPage.js +180 -0
  10. package/dist/admin/pages/AdminRolesPage.js.map +1 -0
  11. package/dist/admin/pages/AdminUsersPage.d.ts +3 -0
  12. package/dist/admin/pages/AdminUsersPage.d.ts.map +1 -0
  13. package/dist/admin/pages/AdminUsersPage.js +151 -0
  14. package/dist/admin/pages/AdminUsersPage.js.map +1 -0
  15. package/dist/admin/permission-label.d.ts +26 -0
  16. package/dist/admin/permission-label.d.ts.map +1 -0
  17. package/dist/admin/permission-label.js +67 -0
  18. package/dist/admin/permission-label.js.map +1 -0
  19. package/dist/backend/cli/create-admin.d.ts +16 -0
  20. package/dist/backend/cli/create-admin.d.ts.map +1 -0
  21. package/dist/backend/cli/create-admin.js +101 -0
  22. package/dist/backend/cli/create-admin.js.map +1 -0
  23. package/dist/backend/demo/reset.d.ts +15 -0
  24. package/dist/backend/demo/reset.d.ts.map +1 -0
  25. package/dist/backend/demo/reset.js +13 -0
  26. package/dist/backend/demo/reset.js.map +1 -0
  27. package/dist/backend/demo/rows.d.ts +50 -0
  28. package/dist/backend/demo/rows.d.ts.map +1 -0
  29. package/dist/backend/demo/rows.js +73 -0
  30. package/dist/backend/demo/rows.js.map +1 -0
  31. package/dist/backend/demo/seed.d.ts +24 -0
  32. package/dist/backend/demo/seed.d.ts.map +1 -0
  33. package/dist/backend/demo/seed.js +43 -0
  34. package/dist/backend/demo/seed.js.map +1 -0
  35. package/dist/backend/entities/admin-user.entity.d.ts +44 -0
  36. package/dist/backend/entities/admin-user.entity.d.ts.map +1 -0
  37. package/dist/backend/entities/admin-user.entity.js +115 -0
  38. package/dist/backend/entities/admin-user.entity.js.map +1 -0
  39. package/dist/backend/index.d.ts +77 -0
  40. package/dist/backend/index.d.ts.map +1 -0
  41. package/dist/backend/index.js +111 -0
  42. package/dist/backend/index.js.map +1 -0
  43. package/dist/backend/plugin.d.ts +70 -0
  44. package/dist/backend/plugin.d.ts.map +1 -0
  45. package/dist/backend/plugin.js +50 -0
  46. package/dist/backend/plugin.js.map +1 -0
  47. package/dist/backend/routes.admin.d.ts +32 -0
  48. package/dist/backend/routes.admin.d.ts.map +1 -0
  49. package/dist/backend/routes.admin.js +192 -0
  50. package/dist/backend/routes.admin.js.map +1 -0
  51. package/dist/backend/routes.impersonation.d.ts +25 -0
  52. package/dist/backend/routes.impersonation.d.ts.map +1 -0
  53. package/dist/backend/routes.impersonation.js +108 -0
  54. package/dist/backend/routes.impersonation.js.map +1 -0
  55. package/dist/backend/routes.public.d.ts +20 -0
  56. package/dist/backend/routes.public.d.ts.map +1 -0
  57. package/dist/backend/routes.public.js +75 -0
  58. package/dist/backend/routes.public.js.map +1 -0
  59. package/dist/backend/services/admin-auth-service.d.ts +47 -0
  60. package/dist/backend/services/admin-auth-service.d.ts.map +1 -0
  61. package/dist/backend/services/admin-auth-service.js +93 -0
  62. package/dist/backend/services/admin-auth-service.js.map +1 -0
  63. package/dist/backend/services/admin-user-ports.d.ts +75 -0
  64. package/dist/backend/services/admin-user-ports.d.ts.map +1 -0
  65. package/dist/backend/services/admin-user-ports.js +160 -0
  66. package/dist/backend/services/admin-user-ports.js.map +1 -0
  67. package/dist/backend/services/admin-user-service.d.ts +134 -0
  68. package/dist/backend/services/admin-user-service.d.ts.map +1 -0
  69. package/dist/backend/services/admin-user-service.js +241 -0
  70. package/dist/backend/services/admin-user-service.js.map +1 -0
  71. package/dist/backend/services/impersonation-service.d.ts +65 -0
  72. package/dist/backend/services/impersonation-service.d.ts.map +1 -0
  73. package/dist/backend/services/impersonation-service.js +103 -0
  74. package/dist/backend/services/impersonation-service.js.map +1 -0
  75. package/dist/backend/services/two-factor-enrolments.d.ts +19 -0
  76. package/dist/backend/services/two-factor-enrolments.d.ts.map +1 -0
  77. package/dist/backend/services/two-factor-enrolments.js +2 -0
  78. package/dist/backend/services/two-factor-enrolments.js.map +1 -0
  79. package/dist/manifest.d.ts +182 -0
  80. package/dist/manifest.d.ts.map +1 -0
  81. package/dist/manifest.js +208 -0
  82. package/dist/manifest.js.map +1 -0
  83. package/dist/migrations/20260425T053028_admin_users_init.d.ts +11 -0
  84. package/dist/migrations/20260425T053028_admin_users_init.d.ts.map +1 -0
  85. package/dist/migrations/20260425T053028_admin_users_init.js +51 -0
  86. package/dist/migrations/20260425T053028_admin_users_init.js.map +1 -0
  87. package/dist/migrations/20260819T155150_admin_users_fold_email_case.d.ts +55 -0
  88. package/dist/migrations/20260819T155150_admin_users_fold_email_case.d.ts.map +1 -0
  89. package/dist/migrations/20260819T155150_admin_users_fold_email_case.js +106 -0
  90. package/dist/migrations/20260819T155150_admin_users_fold_email_case.js.map +1 -0
  91. package/dist/migrations/20260825T124801_admin_users_drop_legacy_two_factor_secret.d.ts +25 -0
  92. package/dist/migrations/20260825T124801_admin_users_drop_legacy_two_factor_secret.d.ts.map +1 -0
  93. package/dist/migrations/20260825T124801_admin_users_drop_legacy_two_factor_secret.js +29 -0
  94. package/dist/migrations/20260825T124801_admin_users_drop_legacy_two_factor_secret.js.map +1 -0
  95. package/dist/migrations/index.d.ts +29 -0
  96. package/dist/migrations/index.d.ts.map +1 -0
  97. package/dist/migrations/index.js +33 -0
  98. package/dist/migrations/index.js.map +1 -0
  99. package/docs/admin_users.md +53 -0
  100. package/i18n/en.json +5 -0
  101. package/i18n/pl.json +5 -0
  102. package/package.json +100 -0
  103. package/tailwind.css +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-label.d.ts","sourceRoot":"","sources":["../../src/admin/permission-label.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEtF,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;AAEhE,gFAAgF;AAChF,eAAO,MAAM,6BAA6B,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASpF;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAYxE"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolving a permission label — feature 091, Phase 3.
3
+ *
4
+ * `/admin-roles` renders one row per grantable code and translates it through
5
+ * `adminRoles.permission.<code>`. That key used to be looked up in exactly one
6
+ * namespace, the synthetic `core` that `_i18n`'s bundle is served under, and
7
+ * that is what made a module owning its own labels impossible: a module's
8
+ * bundle is served under the module's own id, so a label written there was
9
+ * **read by nobody**.
10
+ *
11
+ * It was not hypothetical. `mfa`, `pwa`, `stripe` and `prompt_actions` each
12
+ * shipped their permission labels in their own bundle *and* in `_i18n`'s, and
13
+ * the copy in their own bundle had never rendered — the screen took `core`'s.
14
+ * Four authors did the right thing and the platform silently ignored it.
15
+ *
16
+ * So the lookup is over the merged bundle rather than over one namespace:
17
+ * `core` first, because that is where the legacy block still lives and a
18
+ * migrating label must not flicker between two answers, then the module
19
+ * namespaces. The key carries the permission code, which is unique across the
20
+ * catalogue, so a namespace scan cannot collide two different labels — and
21
+ * which module may write one is a question for the ratchet in
22
+ * `backend/test/helpers/permission-labels.ts`, not for the renderer. Being
23
+ * permissive here and strict there is deliberate: the alternative is resolving
24
+ * by the catalogue row's `module` field, which is a **display grouping** and is
25
+ * `module_lifecycle` for a module whose id is `_lifecycle`.
26
+ */
27
+ import { resolve } from '@endora-commerce/admin-kit/i18n';
28
+ /** The key `/admin-roles` builds for every catalogue row. */
29
+ export const PERMISSION_LABEL_PREFIX = 'adminRoles.permission.';
30
+ /** The namespace `_i18n`'s bundle is served under — the legacy block's home. */
31
+ export const LEGACY_PERMISSION_LABEL_SCOPE = 'core';
32
+ /**
33
+ * The namespace that carries `key`, preferring the legacy block, or `null`.
34
+ *
35
+ * Sorted, so two bundles that both carried the key would resolve to the same
36
+ * one on every render rather than to whichever the server serialised first.
37
+ */
38
+ export function scopeCarrying(bundle, key) {
39
+ if (bundle === undefined)
40
+ return null;
41
+ if (bundle[LEGACY_PERMISSION_LABEL_SCOPE]?.[key] != null) {
42
+ return LEGACY_PERMISSION_LABEL_SCOPE;
43
+ }
44
+ const carrying = Object.keys(bundle)
45
+ .filter((scope) => bundle[scope]?.[key] != null)
46
+ .sort();
47
+ return carrying[0] ?? null;
48
+ }
49
+ /**
50
+ * The label for one permission code: the operator's language, then English,
51
+ * then the manifest label the API already sent.
52
+ */
53
+ export function resolvePermissionLabel(args) {
54
+ const key = `${PERMISSION_LABEL_PREFIX}${args.code}`;
55
+ const scope = scopeCarrying(args.bundle, key) ?? scopeCarrying(args.fallbackBundle, key);
56
+ if (scope === null)
57
+ return args.fallbackLabel;
58
+ const result = resolve({
59
+ scope,
60
+ key,
61
+ language: args.language,
62
+ bundle: args.bundle,
63
+ ...(args.fallbackBundle !== undefined ? { fallbackBundle: args.fallbackBundle } : {}),
64
+ });
65
+ return result.outcome === 'placeholder' ? args.fallbackLabel : result.value;
66
+ }
67
+ //# sourceMappingURL=permission-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-label.js","sourceRoot":"","sources":["../../src/admin/permission-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAG1D,6DAA6D;AAC7D,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AAEhE,gFAAgF;AAChF,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,MAA0B,EAAE,GAAW;IACnE,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,MAAM,CAAC,6BAA6B,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QACzD,OAAO,6BAA6B,CAAC;IACvC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;SACjC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;SAC/C,IAAI,EAAE,CAAC;IACV,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC7B,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAyB;IAC9D,MAAM,GAAG,GAAG,GAAG,uBAAuB,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACzF,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,aAAa,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC;QACrB,KAAK;QACL,GAAG;QACH,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ModuleCliCommandContext } from '@endora-commerce/contracts';
2
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
3
+ interface ParsedArgs {
4
+ email: string;
5
+ password: string;
6
+ firstName: string;
7
+ lastName: string;
8
+ role: string;
9
+ skipRoleBootstrap: boolean;
10
+ }
11
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs | {
12
+ error: string;
13
+ };
14
+ export declare function createAdmin({ ctx, argv, out, err, }: ModuleCliCommandContext<ModuleContext>): Promise<number>;
15
+ export {};
16
+ //# sourceMappingURL=create-admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-admin.d.ts","sourceRoot":"","sources":["../../../src/backend/cli/create-admin.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAIhF,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAOD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA0BjF;AAED,wBAAsB,WAAW,CAAC,EAChC,GAAG,EACH,IAAI,EACJ,GAAG,EACH,GAAG,GACJ,EAAE,uBAAuB,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAoE1D"}
@@ -0,0 +1,101 @@
1
+ import { normalizeEmailAddress } from '@endora-commerce/contracts';
2
+ import { lazyPort } from '@endora-commerce/platform/kernel';
3
+ import { hashPassword } from '@endora-commerce/platform/kernel';
4
+ import { AdminUser } from '../entities/admin-user.entity.js';
5
+ export function parseArgs(argv) {
6
+ const map = new Map();
7
+ let skipRoleBootstrap = false;
8
+ for (const arg of argv) {
9
+ if (arg === '--skip-role-bootstrap') {
10
+ skipRoleBootstrap = true;
11
+ continue;
12
+ }
13
+ if (!arg.startsWith('--'))
14
+ continue;
15
+ const eq = arg.indexOf('=');
16
+ if (eq === -1)
17
+ continue;
18
+ map.set(arg.slice(2, eq), arg.slice(eq + 1));
19
+ }
20
+ for (const key of ['email', 'password', 'first-name', 'last-name']) {
21
+ if (!map.get(key)?.trim())
22
+ return { error: `Missing required flag: --${key}=...` };
23
+ }
24
+ const password = map.get('password').trim();
25
+ if (password.length < 12)
26
+ return { error: 'Password must be at least 12 characters.' };
27
+ return {
28
+ email: normalizeEmailAddress(map.get('email').trim()),
29
+ password,
30
+ firstName: map.get('first-name').trim(),
31
+ lastName: map.get('last-name').trim(),
32
+ role: map.get('role')?.trim() || 'platform_admin',
33
+ skipRoleBootstrap,
34
+ };
35
+ }
36
+ export async function createAdmin({ ctx, argv, out, err, }) {
37
+ // command-coverage-ignore: the bootstrap CLI that mints the first
38
+ // administrator. It runs with shell access to the deployment and, by
39
+ // construction, before any Admin User exists — so there is no acting
40
+ // principal for the Command Bus to attribute the write to. Every subsequent
41
+ // admin-user write goes through the audited admin_users surface; this one
42
+ // exists so that surface has somebody to sign in to it.
43
+ const args = parseArgs(argv);
44
+ if ('error' in args) {
45
+ err(args.error);
46
+ return 1;
47
+ }
48
+ const adminRoles = lazyPort(ctx, 'adminRolePort');
49
+ const em = ctx.cradle().emFactory();
50
+ // 1. Resolve or bootstrap the role — two methods of the port `admin_roles`
51
+ // publishes, in place of a `findOne` + `em.create` over its entity.
52
+ let role = await adminRoles.findByCode(args.role);
53
+ if (!role) {
54
+ if (args.skipRoleBootstrap || args.role !== 'platform_admin') {
55
+ err(`Role "${args.role}" not found. Either create it via the admin panel or omit ` +
56
+ `--role to bootstrap the platform_admin role.`);
57
+ return 1;
58
+ }
59
+ role = await adminRoles.upsertByCode({
60
+ code: 'platform_admin',
61
+ name: 'Platform Admin',
62
+ permissions: ['*'],
63
+ requiresTwoFactor: false,
64
+ });
65
+ out(`Created bootstrap role: platform_admin (${role.id})`);
66
+ }
67
+ // 2. Upsert the admin user — this module's own table, written directly for
68
+ // the reason the ignore marker above gives.
69
+ const passwordHash = await hashPassword(args.password);
70
+ const existing = await em.findOne(AdminUser, { email: args.email });
71
+ if (existing) {
72
+ existing.passwordHash = passwordHash;
73
+ existing.firstName = args.firstName;
74
+ existing.lastName = args.lastName;
75
+ existing.adminRoleId = role.id;
76
+ existing.status = 'active';
77
+ existing.deletedAt = null;
78
+ await em.flush();
79
+ out(`Updated existing admin: ${args.email}`);
80
+ out(` id : ${existing.id}`);
81
+ out(` role : ${role.code} (${role.id})`);
82
+ }
83
+ else {
84
+ const created = em.create(AdminUser, {
85
+ email: args.email,
86
+ passwordHash,
87
+ firstName: args.firstName,
88
+ lastName: args.lastName,
89
+ adminRoleId: role.id,
90
+ status: 'active',
91
+ });
92
+ await em.persistAndFlush(created);
93
+ out(`Created admin: ${args.email}`);
94
+ out(` id : ${created.id}`);
95
+ out(` role : ${role.code} (${role.id})`);
96
+ }
97
+ out('');
98
+ out('Sign in at the admin panel with the email + password above.');
99
+ return 0;
100
+ }
101
+ //# sourceMappingURL=create-admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-admin.js","sourceRoot":"","sources":["../../../src/backend/cli/create-admin.ts"],"names":[],"mappings":"AAyCA,OAAO,EAAE,qBAAqB,EAAsB,MAAM,4BAA4B,CAAC;AAEvF,OAAO,EAAE,QAAQ,EAAsB,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAgB7D,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,KAAK,uBAAuB,EAAE,CAAC;YACpC,iBAAiB,GAAG,IAAI,CAAC;YACzB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS;QACxB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE;YAAE,OAAO,EAAE,KAAK,EAAE,4BAA4B,GAAG,MAAM,EAAE,CAAC;IACrF,CAAC;IACD,MAAM,QAAQ,GAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAY,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,QAAQ,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,0CAA0C,EAAE,CAAC;IACvF,OAAO;QACL,KAAK,EAAE,qBAAqB,CAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAY,CAAC,IAAI,EAAE,CAAC;QACjE,QAAQ;QACR,SAAS,EAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAAY,CAAC,IAAI,EAAE;QACnD,QAAQ,EAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAY,CAAC,IAAI,EAAE;QACjD,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,gBAAgB;QACjD,iBAAiB;KAClB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,EAChC,GAAG,EACH,IAAI,EACJ,GAAG,EACH,GAAG,GACoC;IACvC,kEAAkE;IAClE,qEAAqE;IACrE,qEAAqE;IACrE,4EAA4E;IAC5E,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAgB,GAAG,EAAE,eAAe,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAqB,CAAC,SAAS,EAAE,CAAC;IAEvD,2EAA2E;IAC3E,uEAAuE;IACvE,IAAI,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC7D,GAAG,CACD,SAAS,IAAI,CAAC,IAAI,4DAA4D;gBAC5E,8CAA8C,CACjD,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC;YACnC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,gBAAgB;YACtB,WAAW,EAAE,CAAC,GAAG,CAAC;YAClB,iBAAiB,EAAE,KAAK;SACzB,CAAC,CAAC;QACH,GAAG,CAAC,2CAA2C,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7D,CAAC;IAED,2EAA2E;IAC3E,+CAA+C;IAC/C,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACpE,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,YAAY,GAAG,YAAY,CAAC;QACrC,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACpC,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAClC,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC;QAC/B,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC3B,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC;QAC1B,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,GAAG,CAAC,2BAA2B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7C,GAAG,CAAC,YAAY,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/B,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;YACnC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,WAAW,EAAE,IAAI,CAAC,EAAE;YACpB,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,GAAG,CAAC,kBAAkB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACpC,GAAG,CAAC,YAAY,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9B,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,CAAC;IACD,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,GAAG,CAAC,6DAA6D,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Withdraw `admin_users`' demo data (feature 113, T222 — contract §2.5).
3
+ *
4
+ * By the fixed addresses `seed` assigns, never by a predicate over the table:
5
+ * an operator's own administrator is indistinguishable from a demo one by every
6
+ * other column, and this is the table the host's `truncate … cascade` emptied
7
+ * outright — including the account a developer had created for themselves.
8
+ *
9
+ * The role each account holds is unassigned first, by the composition: `reset`
10
+ * runs the composition's withdrawal before any module's (§5.5).
11
+ */
12
+ import type { DemoResetResult, ModuleDemoContext } from '@endora-commerce/contracts';
13
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
14
+ export declare function resetDemo(context: ModuleDemoContext<ModuleContext>): Promise<DemoResetResult>;
15
+ //# sourceMappingURL=reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/reset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAStE,wBAAsB,SAAS,CAC7B,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,GACxC,OAAO,CAAC,eAAe,CAAC,CAS1B"}
@@ -0,0 +1,13 @@
1
+ import { AdminUser } from '../entities/admin-user.entity.js';
2
+ import { DEMO_ADMIN_USER_EMAILS } from './rows.js';
3
+ export async function resetDemo(context) {
4
+ // command-coverage-ignore: the withdrawal half of the demo data above. Same
5
+ // entry point, same `mustBeNonProduction()` guard, same absence of an
6
+ // operator to attribute the write to (contract §2.7).
7
+ const em = context.ctx.cradle().emFactory();
8
+ const removed = await em.nativeDelete(AdminUser, {
9
+ email: { $in: [...DEMO_ADMIN_USER_EMAILS] },
10
+ });
11
+ return { removed: [{ entity: 'AdminUser', count: removed }] };
12
+ }
13
+ //# sourceMappingURL=reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.js","sourceRoot":"","sources":["../../../src/backend/demo/reset.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAMnD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAyC;IAEzC,4EAA4E;IAC5E,sEAAsE;IACtE,sDAAsD;IACtD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAwB,CAAC,SAAS,EAAE,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE;QAC/C,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,sBAAsB,CAAC,EAAE;KAC5C,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The administrator accounts this module's demo data creates (feature 113,
3
+ * T222).
4
+ *
5
+ * One place holding the data, imported by both bodies: `seed.ts` creates these
6
+ * accounts and `reset.ts` withdraws exactly them, by the `email` they are keyed
7
+ * on (contract §2.5). The values are the host seed's verbatim, moved rather
8
+ * than rewritten; the frozen copy the parity comparison read them against is
9
+ * deleted with that comparison (T226), and
10
+ * `test/integration/demo/demo-shop.test.ts`' recorded delta is what holds them
11
+ * now.
12
+ *
13
+ * ## The role each account holds is deliberately not here
14
+ *
15
+ * An `admin_users` row carrying an `admin_roles` id is two modules' rows in one
16
+ * statement, so the assignment is a composition step (§5.1). `roleCode` below is
17
+ * **not** written by this body: it records which role the demo shop means each
18
+ * account to hold, so that `reset` and this module's own test can say what the
19
+ * composition is expected to have done. `AdminUser.adminRoleId` is nullable —
20
+ * `[OptionalProps]` names it — which is what makes the split available at all,
21
+ * and it is the difference from the demo *buyer*, whose
22
+ * `customer_accounts.organization_id` is `NOT NULL` and who therefore has to be
23
+ * created by the composition outright.
24
+ *
25
+ * ## The password is a real one and is printed
26
+ *
27
+ * It is reported as a {@link DemoCredential} rather than logged, so the runner
28
+ * formats it once (§3.7). `mustBeNonProduction()` at the entry point is what
29
+ * makes shipping a known password defensible: the command refuses to run
30
+ * against a production database at all.
31
+ */
32
+ /** One demo administrator, in `AdminUser`'s own field names plus its role. */
33
+ export interface DemoAdminUserRow {
34
+ readonly email: string;
35
+ readonly firstName: string;
36
+ readonly lastName: string;
37
+ readonly status: 'active' | 'inactive';
38
+ /** The `admin_roles` code the composition assigns — never written here. */
39
+ readonly roleCode: string;
40
+ /** How the report announces this account. */
41
+ readonly credentialLabel: string;
42
+ }
43
+ /** The one password every demo administrator signs in with. */
44
+ export declare const DEMO_ADMIN_PASSWORD = "ChangeMe!123";
45
+ /** The platform administrator's address, which the demo's report leads with. */
46
+ export declare const DEMO_ADMIN_EMAIL = "admin@demo.local";
47
+ export declare const DEMO_ADMIN_USERS: readonly DemoAdminUserRow[];
48
+ /** The addresses `reset` withdraws — derived from the rows, never a second list. */
49
+ export declare const DEMO_ADMIN_USER_EMAILS: readonly string[];
50
+ //# sourceMappingURL=rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IACvC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD,eAAO,MAAM,gBAAgB,EAAE,SAAS,gBAAgB,EAkCvD,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAA6C,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The administrator accounts this module's demo data creates (feature 113,
3
+ * T222).
4
+ *
5
+ * One place holding the data, imported by both bodies: `seed.ts` creates these
6
+ * accounts and `reset.ts` withdraws exactly them, by the `email` they are keyed
7
+ * on (contract §2.5). The values are the host seed's verbatim, moved rather
8
+ * than rewritten; the frozen copy the parity comparison read them against is
9
+ * deleted with that comparison (T226), and
10
+ * `test/integration/demo/demo-shop.test.ts`' recorded delta is what holds them
11
+ * now.
12
+ *
13
+ * ## The role each account holds is deliberately not here
14
+ *
15
+ * An `admin_users` row carrying an `admin_roles` id is two modules' rows in one
16
+ * statement, so the assignment is a composition step (§5.1). `roleCode` below is
17
+ * **not** written by this body: it records which role the demo shop means each
18
+ * account to hold, so that `reset` and this module's own test can say what the
19
+ * composition is expected to have done. `AdminUser.adminRoleId` is nullable —
20
+ * `[OptionalProps]` names it — which is what makes the split available at all,
21
+ * and it is the difference from the demo *buyer*, whose
22
+ * `customer_accounts.organization_id` is `NOT NULL` and who therefore has to be
23
+ * created by the composition outright.
24
+ *
25
+ * ## The password is a real one and is printed
26
+ *
27
+ * It is reported as a {@link DemoCredential} rather than logged, so the runner
28
+ * formats it once (§3.7). `mustBeNonProduction()` at the entry point is what
29
+ * makes shipping a known password defensible: the command refuses to run
30
+ * against a production database at all.
31
+ */
32
+ /** The one password every demo administrator signs in with. */
33
+ export const DEMO_ADMIN_PASSWORD = 'ChangeMe!123';
34
+ /** The platform administrator's address, which the demo's report leads with. */
35
+ export const DEMO_ADMIN_EMAIL = 'admin@demo.local';
36
+ export const DEMO_ADMIN_USERS = [
37
+ {
38
+ email: DEMO_ADMIN_EMAIL,
39
+ firstName: 'Demo',
40
+ lastName: 'Admin',
41
+ status: 'active',
42
+ roleCode: 'platform_admin',
43
+ credentialLabel: 'Platform Administrator',
44
+ },
45
+ // Feature 008 — two accounts on the sales_representative role, so the
46
+ // quickstart can exercise assignment-scoped visibility: one representative
47
+ // sees their own organisations and the other's are invisible to them.
48
+ {
49
+ email: 'sales-rep@demo.local',
50
+ firstName: 'Anna',
51
+ // A Polish surname, and it stays one. `check:default-language-prose` reads
52
+ // a literal as prose at two word tokens, or at one in the prop-default
53
+ // position, and this is one token in neither — measured, not assumed. It is
54
+ // also not prose in the sense the rule is about: a person's name is not a
55
+ // sentence a reader is meant to understand, and translating it would be
56
+ // renaming somebody.
57
+ lastName: 'Wiśniewska',
58
+ status: 'active',
59
+ roleCode: 'sales_representative',
60
+ credentialLabel: 'Sales Representative',
61
+ },
62
+ {
63
+ email: 'sales-rep-other@demo.local',
64
+ firstName: 'Tomasz',
65
+ lastName: 'Nowak',
66
+ status: 'active',
67
+ roleCode: 'sales_representative',
68
+ credentialLabel: 'Sales Rep (other)',
69
+ },
70
+ ];
71
+ /** The addresses `reset` withdraws — derived from the rows, never a second list. */
72
+ export const DEMO_ADMIN_USER_EMAILS = DEMO_ADMIN_USERS.map((row) => row.email);
73
+ //# sourceMappingURL=rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../../src/backend/demo/rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAcH,+DAA+D;AAC/D,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,gFAAgF;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD,MAAM,CAAC,MAAM,gBAAgB,GAAgC;IAC3D;QACE,KAAK,EAAE,gBAAgB;QACvB,SAAS,EAAE,MAAM;QACjB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,gBAAgB;QAC1B,eAAe,EAAE,wBAAwB;KAC1C;IACD,sEAAsE;IACtE,2EAA2E;IAC3E,sEAAsE;IACtE;QACE,KAAK,EAAE,sBAAsB;QAC7B,SAAS,EAAE,MAAM;QACjB,2EAA2E;QAC3E,uEAAuE;QACvE,4EAA4E;QAC5E,0EAA0E;QAC1E,wEAAwE;QACxE,qBAAqB;QACrB,QAAQ,EAAE,YAAY;QACtB,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,sBAAsB;QAChC,eAAe,EAAE,sBAAsB;KACxC;IACD;QACE,KAAK,EAAE,4BAA4B;QACnC,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,sBAAsB;QAChC,eAAe,EAAE,mBAAmB;KACrC;CACF,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,sBAAsB,GAAsB,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `admin_users`' demo data (feature 113, T222 — contract §2).
3
+ *
4
+ * The three administrator accounts the quickstart signs in with, in the module
5
+ * that owns the table. It writes `admin_users` and nothing else (§2.1), reads no
6
+ * other module's table and resolves no port (§2.2), so declaring it added no
7
+ * entry to this module's manifest `dependencies` (§2.3).
8
+ *
9
+ * **It writes no `adminRoleId`, and that is the boundary the batch was cut on.**
10
+ * The host block these rows come from created each account with an
11
+ * `admin_roles` id in hand, which is two modules' rows in one statement — a
12
+ * composition step (§5.1). The column is nullable, so the account is this
13
+ * module's and the assignment is the instance's; the composition runs after
14
+ * every module's `seed` and finds both sides already there. Contrast the demo
15
+ * *buyer*, whose `customer_accounts.organization_id` is `NOT NULL`: there no
16
+ * split exists and the composition creates the row outright.
17
+ *
18
+ * Idempotent by an existence probe on the natural key (§2.4): a second run
19
+ * creates nothing, re-hashes nothing and reports the same count.
20
+ */
21
+ import type { DemoSeedResult, ModuleDemoContext } from '@endora-commerce/contracts';
22
+ import type { ModuleContext } from '@endora-commerce/platform/kernel';
23
+ export declare function seedDemo(context: ModuleDemoContext<ModuleContext>): Promise<DemoSeedResult>;
24
+ //# sourceMappingURL=seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../../src/backend/demo/seed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAEV,cAAc,EACd,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAWtE,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,GACxC,OAAO,CAAC,cAAc,CAAC,CAwCzB"}
@@ -0,0 +1,43 @@
1
+ import { hashPassword } from '@endora-commerce/platform/kernel';
2
+ import { AdminUser } from '../entities/admin-user.entity.js';
3
+ import { DEMO_ADMIN_PASSWORD, DEMO_ADMIN_USERS } from './rows.js';
4
+ export async function seedDemo(context) {
5
+ // command-coverage-ignore: demo data, reached only by `endora demo seed`,
6
+ // whose entry point calls `mustBeNonProduction()` as its first statement and
7
+ // outside every `try` (contract §2.7, §3.3). The guard is the enforcement:
8
+ // this write has no operator, no tenant and no audit reader, and the command
9
+ // refuses to run against a production database at all — which is also what
10
+ // makes a known password in `rows.ts` defensible.
11
+ const em = context.ctx.cradle().emFactory();
12
+ // One hash for the three accounts, exactly as the host block did: Argon2 is
13
+ // deliberately slow and the demo's password is one value.
14
+ let passwordHash;
15
+ for (const row of DEMO_ADMIN_USERS) {
16
+ const existing = await em.findOne(AdminUser, { email: row.email });
17
+ // Present already: left exactly as it is, password included. An operator
18
+ // may have changed it, and overwriting it here would be this body deciding
19
+ // its own literal outranks their change — `reset`'s question, not `seed`'s.
20
+ if (existing)
21
+ continue;
22
+ passwordHash ??= await hashPassword(DEMO_ADMIN_PASSWORD);
23
+ em.create(AdminUser, {
24
+ email: row.email,
25
+ passwordHash,
26
+ firstName: row.firstName,
27
+ lastName: row.lastName,
28
+ status: row.status,
29
+ });
30
+ }
31
+ await em.flush();
32
+ const credentials = DEMO_ADMIN_USERS.map((row) => ({
33
+ label: row.credentialLabel,
34
+ value: `${row.email} / ${DEMO_ADMIN_PASSWORD}`,
35
+ }));
36
+ // The count is what the demo *holds* after the run, not what this call
37
+ // inserted: seeding twice must report the same counts (`DemoEntityCount`).
38
+ return {
39
+ created: [{ entity: 'AdminUser', count: DEMO_ADMIN_USERS.length }],
40
+ credentials,
41
+ };
42
+ }
43
+ //# sourceMappingURL=seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../../../src/backend/demo/seed.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAOlE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAyC;IAEzC,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,6EAA6E;IAC7E,2EAA2E;IAC3E,kDAAkD;IAClD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAwB,CAAC,SAAS,EAAE,CAAC;IAElE,4EAA4E;IAC5E,0DAA0D;IAC1D,IAAI,YAAgC,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACnE,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,IAAI,QAAQ;YAAE,SAAS;QACvB,YAAY,KAAK,MAAM,YAAY,CAAC,mBAAmB,CAAC,CAAC;QACzD,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;YACnB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,YAAY;YACZ,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IAEjB,MAAM,WAAW,GAAqB,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnE,KAAK,EAAE,GAAG,CAAC,eAAe;QAC1B,KAAK,EAAE,GAAG,GAAG,CAAC,KAAK,MAAM,mBAAmB,EAAE;KAC/C,CAAC,CAAC,CAAC;IAEJ,uEAAuE;IACvE,2EAA2E;IAC3E,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAC;QAClE,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ /**
3
+ * AdminUser — a Supplier employee with platform-side access. Distinct entity
4
+ * from CustomerAccount (FR-040 — different login surface, different sessions,
5
+ * own permission matrix via AdminRole).
6
+ */
7
+ export declare class AdminUser {
8
+ [OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'status' | 'adminRoleId' | 'twoFactorConfirmedAt' | 'lastLoginAt' | 'deletedAt' | 'preferredLanguage';
9
+ id: string;
10
+ email: string;
11
+ passwordHash: string;
12
+ firstName: string;
13
+ lastName: string;
14
+ adminRoleId?: string | null;
15
+ status: 'active' | 'inactive';
16
+ /**
17
+ * Never written, and since 2026-08-28 never **read** either.
18
+ *
19
+ * This table has no writer for it at all, not even a broken one, so the
20
+ * published `twoFactorEnabled` derived from it was a provably constant
21
+ * `false`: `/admin-users` reported no second factor for an administrator who
22
+ * had enrolled an hour earlier. That field is now the live `mfa` enrolment,
23
+ * read through `mfaEnrolmentStatePort`, and nothing in the tree reads this
24
+ * column.
25
+ *
26
+ * It is dead schema kept for one more step: dropping it is a migration on
27
+ * this module's table and is deliberately not folded into the repair of the
28
+ * five derivation sites. Do not read it — the honest answer is `mfa`'s.
29
+ */
30
+ twoFactorConfirmedAt?: Date | null;
31
+ lastLoginAt?: Date | null;
32
+ /**
33
+ * Admin UI preferred language — feature 019. NULL means "no preference
34
+ * saved", which the resolver treats as the platform default (English).
35
+ * The column is bounded by the `SupportedAdminLanguageSchema` allowlist
36
+ * in `@endora-commerce/contracts/src/admin-i18n.ts`; the validation lives at the
37
+ * service / route boundary, not at the column level.
38
+ */
39
+ preferredLanguage?: string | null;
40
+ createdAt: Date;
41
+ updatedAt: Date;
42
+ deletedAt?: Date | null;
43
+ }
44
+ //# sourceMappingURL=admin-user.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-user.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/admin-user.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAI7F;;;;GAIG;AACH,qBAEa,SAAS;IACpB,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,WAAW,GACX,QAAQ,GACR,aAAa,GACb,sBAAsB,GACtB,aAAa,GACb,WAAW,GACX,mBAAmB,CAAC;IAGxB,EAAE,EAAE,MAAM,CAAgB;IAI1B,KAAK,EAAG,MAAM,CAAC;IAGf,YAAY,EAAG,MAAM,CAAC;IAGtB,SAAS,EAAG,MAAM,CAAC;IAGnB,QAAQ,EAAG,MAAM,CAAC;IAIlB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAI5B,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEzC;;;;;;;;;;;;;OAaG;IAEH,oBAAoB,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAGnC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAE1B;;;;;;OAMG;IAEH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAGlC,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB"}
@@ -0,0 +1,115 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property, Unique } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ import { randomUUID } from 'crypto';
13
+ /**
14
+ * AdminUser — a Supplier employee with platform-side access. Distinct entity
15
+ * from CustomerAccount (FR-040 — different login surface, different sessions,
16
+ * own permission matrix via AdminRole).
17
+ */
18
+ let AdminUser = class AdminUser {
19
+ [OptionalProps];
20
+ id = randomUUID();
21
+ email;
22
+ passwordHash;
23
+ firstName;
24
+ lastName;
25
+ adminRoleId;
26
+ status = 'active';
27
+ /**
28
+ * Never written, and since 2026-08-28 never **read** either.
29
+ *
30
+ * This table has no writer for it at all, not even a broken one, so the
31
+ * published `twoFactorEnabled` derived from it was a provably constant
32
+ * `false`: `/admin-users` reported no second factor for an administrator who
33
+ * had enrolled an hour earlier. That field is now the live `mfa` enrolment,
34
+ * read through `mfaEnrolmentStatePort`, and nothing in the tree reads this
35
+ * column.
36
+ *
37
+ * It is dead schema kept for one more step: dropping it is a migration on
38
+ * this module's table and is deliberately not folded into the repair of the
39
+ * five derivation sites. Do not read it — the honest answer is `mfa`'s.
40
+ */
41
+ twoFactorConfirmedAt;
42
+ lastLoginAt;
43
+ /**
44
+ * Admin UI preferred language — feature 019. NULL means "no preference
45
+ * saved", which the resolver treats as the platform default (English).
46
+ * The column is bounded by the `SupportedAdminLanguageSchema` allowlist
47
+ * in `@endora-commerce/contracts/src/admin-i18n.ts`; the validation lives at the
48
+ * service / route boundary, not at the column level.
49
+ */
50
+ preferredLanguage;
51
+ createdAt = new Date();
52
+ updatedAt = new Date();
53
+ deletedAt;
54
+ };
55
+ __decorate([
56
+ PrimaryKey({ type: 'uuid' }),
57
+ __metadata("design:type", String)
58
+ ], AdminUser.prototype, "id", void 0);
59
+ __decorate([
60
+ Property({ type: 'string', length: 320 }),
61
+ Unique(),
62
+ __metadata("design:type", String)
63
+ ], AdminUser.prototype, "email", void 0);
64
+ __decorate([
65
+ Property({ type: 'string', length: 512 }),
66
+ __metadata("design:type", String)
67
+ ], AdminUser.prototype, "passwordHash", void 0);
68
+ __decorate([
69
+ Property({ type: 'string', length: 120 }),
70
+ __metadata("design:type", String)
71
+ ], AdminUser.prototype, "firstName", void 0);
72
+ __decorate([
73
+ Property({ type: 'string', length: 120 }),
74
+ __metadata("design:type", String)
75
+ ], AdminUser.prototype, "lastName", void 0);
76
+ __decorate([
77
+ Property({ type: 'uuid', nullable: true }),
78
+ Index(),
79
+ __metadata("design:type", Object)
80
+ ], AdminUser.prototype, "adminRoleId", void 0);
81
+ __decorate([
82
+ Property({ type: 'string', length: 16 }),
83
+ Index(),
84
+ __metadata("design:type", String)
85
+ ], AdminUser.prototype, "status", void 0);
86
+ __decorate([
87
+ Property({ type: 'datetime', nullable: true }),
88
+ __metadata("design:type", Object)
89
+ ], AdminUser.prototype, "twoFactorConfirmedAt", void 0);
90
+ __decorate([
91
+ Property({ type: 'datetime', nullable: true }),
92
+ __metadata("design:type", Object)
93
+ ], AdminUser.prototype, "lastLoginAt", void 0);
94
+ __decorate([
95
+ Property({ type: 'string', length: 12, nullable: true }),
96
+ __metadata("design:type", Object)
97
+ ], AdminUser.prototype, "preferredLanguage", void 0);
98
+ __decorate([
99
+ Property({ type: 'datetime', onCreate: () => new Date() }),
100
+ __metadata("design:type", Date)
101
+ ], AdminUser.prototype, "createdAt", void 0);
102
+ __decorate([
103
+ Property({ type: 'datetime', onUpdate: () => new Date() }),
104
+ __metadata("design:type", Date)
105
+ ], AdminUser.prototype, "updatedAt", void 0);
106
+ __decorate([
107
+ Property({ type: 'datetime', nullable: true }),
108
+ __metadata("design:type", Object)
109
+ ], AdminUser.prototype, "deletedAt", void 0);
110
+ AdminUser = __decorate([
111
+ GlobalEntity(),
112
+ Entity({ tableName: 'admin_users' })
113
+ ], AdminUser);
114
+ export { AdminUser };
115
+ //# sourceMappingURL=admin-user.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-user.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/admin-user.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC;;;;GAIG;AAGI,IAAM,SAAS,GAAf,MAAM,SAAS;IACpB,CAAC,aAAa,CAAC,CASS;IAGxB,EAAE,GAAW,UAAU,EAAE,CAAC;IAI1B,KAAK,CAAU;IAGf,YAAY,CAAU;IAGtB,SAAS,CAAU;IAGnB,QAAQ,CAAU;IAIlB,WAAW,CAAiB;IAI5B,MAAM,GAA0B,QAAQ,CAAC;IAEzC;;;;;;;;;;;;;OAaG;IAEH,oBAAoB,CAAe;IAGnC,WAAW,CAAe;IAE1B;;;;;;OAMG;IAEH,iBAAiB,CAAiB;IAGlC,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;IAG7B,SAAS,CAAe;CACzB,CAAA;AA7DC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qCACH;AAI1B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACzC,MAAM,EAAE;;wCACM;AAGf;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;+CACpB;AAGtB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;4CACvB;AAGnB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;2CACxB;AAIlB;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,KAAK,EAAE;;8CACoB;AAI5B;IAFC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACxC,KAAK,EAAE;;yCACiC;AAiBzC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uDACZ;AAGnC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACrB;AAU1B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACvB;AAGlC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;4CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAChD,IAAI;4CAAc;AAG7B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACvB;AAzEb,SAAS;IAFrB,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;GACxB,SAAS,CA0ErB"}