@endora-commerce/mod-audit-logs 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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +39 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AuditLogViewer.d.ts +3 -0
- package/dist/admin/pages/AuditLogViewer.d.ts.map +1 -0
- package/dist/admin/pages/AuditLogViewer.js +112 -0
- package/dist/admin/pages/AuditLogViewer.js.map +1 -0
- package/dist/backend/cli/read.d.ts +20 -0
- package/dist/backend/cli/read.d.ts.map +1 -0
- package/dist/backend/cli/read.js +94 -0
- package/dist/backend/cli/read.js.map +1 -0
- package/dist/backend/commands/recent-activity-visibility.commands.d.ts +31 -0
- package/dist/backend/commands/recent-activity-visibility.commands.d.ts.map +1 -0
- package/dist/backend/commands/recent-activity-visibility.commands.js +55 -0
- package/dist/backend/commands/recent-activity-visibility.commands.js.map +1 -0
- package/dist/backend/index.d.ts +91 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +117 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +29 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +85 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.admin.recent-activity.d.ts +126 -0
- package/dist/backend/routes.admin.recent-activity.d.ts.map +1 -0
- package/dist/backend/routes.admin.recent-activity.js +106 -0
- package/dist/backend/routes.admin.recent-activity.js.map +1 -0
- package/dist/backend/services/audit-reference-registry.d.ts +28 -0
- package/dist/backend/services/audit-reference-registry.d.ts.map +1 -0
- package/dist/backend/services/audit-reference-registry.js +57 -0
- package/dist/backend/services/audit-reference-registry.js.map +1 -0
- package/dist/backend/services/recent-activity-catalog.d.ts +92 -0
- package/dist/backend/services/recent-activity-catalog.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-catalog.js +82 -0
- package/dist/backend/services/recent-activity-catalog.js.map +1 -0
- package/dist/backend/services/recent-activity-service.d.ts +105 -0
- package/dist/backend/services/recent-activity-service.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-service.js +308 -0
- package/dist/backend/services/recent-activity-service.js.map +1 -0
- package/dist/backend/services/recent-activity-visibility.d.ts +37 -0
- package/dist/backend/services/recent-activity-visibility.d.ts.map +1 -0
- package/dist/backend/services/recent-activity-visibility.js +81 -0
- package/dist/backend/services/recent-activity-visibility.js.map +1 -0
- package/dist/manifest.d.ts +191 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +123 -0
- package/dist/manifest.js.map +1 -0
- package/docs/audit_logs.md +47 -0
- package/i18n/en.json +5 -0
- package/i18n/pl.json +5 -0
- package/package.json +86 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type KnownIconName, type ModuleManifest, type ModuleRecentActivity } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The recent-activity catalog — feature 080, T042j / D-163.1.
|
|
4
|
+
*
|
|
5
|
+
* **This file is what replaced four hand-maintained tables**, and it is worth
|
|
6
|
+
* saying which four, because their shapes were different and only one of them
|
|
7
|
+
* looked like a list:
|
|
8
|
+
*
|
|
9
|
+
* 1. `action-catalog.ts`'s `RECENT_ACTIVITY_ACTIONS` — 23 tokens, the
|
|
10
|
+
* dashboard query's `$in`. A token missing from it was **silently absent**
|
|
11
|
+
* from the card rather than refused, which is the whole of D-163's finding:
|
|
12
|
+
* a package could never appear there.
|
|
13
|
+
* 2. The same file's `PREFIX_TO_MODULE` plus `RecentActivityModule`, a **closed
|
|
14
|
+
* union of four core module ids**. A closed union is a list written down,
|
|
15
|
+
* and a package's id could not be in it.
|
|
16
|
+
* 3. `routes.admin.recent-activity.ts`'s `module: z.enum([...])`, three members.
|
|
17
|
+
* 4. The admin's `ACTIVITY_RENDERING`, 22 entries mapping a token to a
|
|
18
|
+
* lucide icon and an i18n key.
|
|
19
|
+
*
|
|
20
|
+
* All four are computed from one input now: the `recentActivity` export of each
|
|
21
|
+
* composed module's `manifest.ts`. The **drift already standing inside core**
|
|
22
|
+
* is repaired by that rather than by a fifth entry — `prompt_action.execute`
|
|
23
|
+
* was in (1) and (2) and absent from (3) and (4), so a prompt-assistant row was
|
|
24
|
+
* fetched, classified, and then rendered as an unknown verb against a response
|
|
25
|
+
* schema that did not list the `module` value the server emitted.
|
|
26
|
+
*
|
|
27
|
+
* What this class does **not** decide is whether a declared module's rows
|
|
28
|
+
* appear. That is the operator's axis (see {@link RecentActivityVisibility}),
|
|
29
|
+
* read per request from the module's own Setting and defaulting to visible.
|
|
30
|
+
* Neither axis is derivable from the other, which is Constitution XVII's shape
|
|
31
|
+
* applied to a narrower object.
|
|
32
|
+
*/
|
|
33
|
+
/** The two properties of a resolved registry entry this derivation reads. */
|
|
34
|
+
export interface RecentActivityDeclarationSource {
|
|
35
|
+
readonly manifest: Pick<ModuleManifest, 'id' | 'name'>;
|
|
36
|
+
readonly recentActivity?: ModuleRecentActivity | undefined;
|
|
37
|
+
}
|
|
38
|
+
/** One declared action, with everything the card and the route need about it. */
|
|
39
|
+
export interface RecentActivityDescriptor {
|
|
40
|
+
/** The `audit_log_entries.action` token. */
|
|
41
|
+
readonly action: string;
|
|
42
|
+
/** The module that declared it — what used to be `RecentActivityModule`. */
|
|
43
|
+
readonly moduleId: string;
|
|
44
|
+
/** Operator-facing module name, for the visibility screen. */
|
|
45
|
+
readonly moduleName: string;
|
|
46
|
+
readonly icon: KnownIconName;
|
|
47
|
+
/** Relative to `moduleId`'s i18n namespace — the admin resolves it there. */
|
|
48
|
+
readonly labelKey: string;
|
|
49
|
+
}
|
|
50
|
+
/** One eligible module, and the Setting that decides whether it is shown. */
|
|
51
|
+
export interface RecentActivityModuleEntry {
|
|
52
|
+
readonly moduleId: string;
|
|
53
|
+
readonly moduleName: string;
|
|
54
|
+
readonly settingCode: string;
|
|
55
|
+
readonly actions: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Two modules declaring one action token.
|
|
59
|
+
*
|
|
60
|
+
* Refused rather than resolved by declaration order, for the reason a package
|
|
61
|
+
* claiming a taken module id is refused (D-155.7): the token decides which
|
|
62
|
+
* module's visibility Setting governs a row and which namespace its verb is
|
|
63
|
+
* looked up in, so picking a winner silently attributes one module's activity
|
|
64
|
+
* to another, and an operator switching the loser off would change nothing.
|
|
65
|
+
*/
|
|
66
|
+
export declare class RecentActivityActionConflict extends Error {
|
|
67
|
+
readonly name = "RecentActivityActionConflict";
|
|
68
|
+
}
|
|
69
|
+
export declare class RecentActivityCatalog {
|
|
70
|
+
private readonly byAction;
|
|
71
|
+
private readonly modules;
|
|
72
|
+
constructor(sources: ReadonlyArray<RecentActivityDeclarationSource>);
|
|
73
|
+
/** Every declared action token, sorted so the derived route enum is stable. */
|
|
74
|
+
actions(): string[];
|
|
75
|
+
/** Every declaring module's id — what `RecentActivityModule` used to enumerate. */
|
|
76
|
+
moduleIds(): string[];
|
|
77
|
+
/** Every eligible module, in composition order. */
|
|
78
|
+
eligibleModules(): readonly RecentActivityModuleEntry[];
|
|
79
|
+
/**
|
|
80
|
+
* The descriptor for an action token, or `undefined` for one nobody declared.
|
|
81
|
+
*
|
|
82
|
+
* `undefined` rather than a throw, unlike the `moduleForAction` this replaced:
|
|
83
|
+
* that function's own comment called an unmapped token "defensive:
|
|
84
|
+
* unreachable as long as the allowlist and the prefix table stay aligned",
|
|
85
|
+
* and they were not aligned. A row can only get here by being in the query's
|
|
86
|
+
* `$in`, which is built from this same map — but an operator flipping a
|
|
87
|
+
* module off between the two is a real interleaving, and taking the dashboard
|
|
88
|
+
* down for it would be worse than omitting a row.
|
|
89
|
+
*/
|
|
90
|
+
descriptorFor(action: string): RecentActivityDescriptor | undefined;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=recent-activity-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-catalog.d.ts","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EAC1B,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;CAC5D;AAED,iFAAiF;AACjF,MAAM,WAAW,wBAAwB;IACvC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,6EAA6E;AAC7E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,SAAkB,IAAI,kCAAkC;CACzD;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgD;IACzE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;gBAEnD,OAAO,EAAE,aAAa,CAAC,+BAA+B,CAAC;IAwCnE,+EAA+E;IAC/E,OAAO,IAAI,MAAM,EAAE;IAInB,mFAAmF;IACnF,SAAS,IAAI,MAAM,EAAE;IAIrB,mDAAmD;IACnD,eAAe,IAAI,SAAS,yBAAyB,EAAE;IAIvD;;;;;;;;;;OAUG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS;CAGpE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { recentActivityVisibilitySettingCode, } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Two modules declaring one action token.
|
|
4
|
+
*
|
|
5
|
+
* Refused rather than resolved by declaration order, for the reason a package
|
|
6
|
+
* claiming a taken module id is refused (D-155.7): the token decides which
|
|
7
|
+
* module's visibility Setting governs a row and which namespace its verb is
|
|
8
|
+
* looked up in, so picking a winner silently attributes one module's activity
|
|
9
|
+
* to another, and an operator switching the loser off would change nothing.
|
|
10
|
+
*/
|
|
11
|
+
export class RecentActivityActionConflict extends Error {
|
|
12
|
+
name = 'RecentActivityActionConflict';
|
|
13
|
+
}
|
|
14
|
+
export class RecentActivityCatalog {
|
|
15
|
+
byAction;
|
|
16
|
+
modules;
|
|
17
|
+
constructor(sources) {
|
|
18
|
+
const byAction = new Map();
|
|
19
|
+
const modules = [];
|
|
20
|
+
for (const source of sources) {
|
|
21
|
+
const declaration = source.recentActivity;
|
|
22
|
+
if (!declaration)
|
|
23
|
+
continue;
|
|
24
|
+
const moduleId = source.manifest.id;
|
|
25
|
+
const moduleName = source.manifest.name;
|
|
26
|
+
const actions = [];
|
|
27
|
+
for (const entry of declaration.entries) {
|
|
28
|
+
const claimed = byAction.get(entry.action);
|
|
29
|
+
if (claimed) {
|
|
30
|
+
throw new RecentActivityActionConflict(`[audit_logs] modules "${claimed.moduleId}" and "${moduleId}" both declare the ` +
|
|
31
|
+
`recent-activity action "${entry.action}". The token decides whose visibility ` +
|
|
32
|
+
`Setting governs the row and in whose i18n namespace its verb is resolved, so ` +
|
|
33
|
+
`there is no winner to pick: one of the two declarations is wrong.`);
|
|
34
|
+
}
|
|
35
|
+
byAction.set(entry.action, {
|
|
36
|
+
action: entry.action,
|
|
37
|
+
moduleId,
|
|
38
|
+
moduleName,
|
|
39
|
+
icon: entry.icon,
|
|
40
|
+
labelKey: entry.labelKey,
|
|
41
|
+
});
|
|
42
|
+
actions.push(entry.action);
|
|
43
|
+
}
|
|
44
|
+
modules.push({
|
|
45
|
+
moduleId,
|
|
46
|
+
moduleName,
|
|
47
|
+
// Derived, never declared — see the contracts helper's note.
|
|
48
|
+
settingCode: recentActivityVisibilitySettingCode(moduleId),
|
|
49
|
+
actions,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
this.byAction = byAction;
|
|
53
|
+
this.modules = modules;
|
|
54
|
+
}
|
|
55
|
+
/** Every declared action token, sorted so the derived route enum is stable. */
|
|
56
|
+
actions() {
|
|
57
|
+
return [...this.byAction.keys()].sort();
|
|
58
|
+
}
|
|
59
|
+
/** Every declaring module's id — what `RecentActivityModule` used to enumerate. */
|
|
60
|
+
moduleIds() {
|
|
61
|
+
return this.modules.map((entry) => entry.moduleId).sort();
|
|
62
|
+
}
|
|
63
|
+
/** Every eligible module, in composition order. */
|
|
64
|
+
eligibleModules() {
|
|
65
|
+
return this.modules;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The descriptor for an action token, or `undefined` for one nobody declared.
|
|
69
|
+
*
|
|
70
|
+
* `undefined` rather than a throw, unlike the `moduleForAction` this replaced:
|
|
71
|
+
* that function's own comment called an unmapped token "defensive:
|
|
72
|
+
* unreachable as long as the allowlist and the prefix table stay aligned",
|
|
73
|
+
* and they were not aligned. A row can only get here by being in the query's
|
|
74
|
+
* `$in`, which is built from this same map — but an operator flipping a
|
|
75
|
+
* module off between the two is a real interleaving, and taking the dashboard
|
|
76
|
+
* down for it would be worse than omitting a row.
|
|
77
|
+
*/
|
|
78
|
+
descriptorFor(action) {
|
|
79
|
+
return this.byAction.get(action);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=recent-activity-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-catalog.js","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mCAAmC,GAIpC,MAAM,4BAA4B,CAAC;AA6DpC;;;;;;;;GAQG;AACH,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACnC,IAAI,GAAG,8BAA8B,CAAC;CACzD;AAED,MAAM,OAAO,qBAAqB;IACf,QAAQ,CAAgD;IACxD,OAAO,CAAuC;IAE/D,YAAY,OAAuD;QACjE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoC,CAAC;QAC7D,MAAM,OAAO,GAAgC,EAAE,CAAC;QAChD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC;YAC1C,IAAI,CAAC,WAAW;gBAAE,SAAS;YAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YACxC,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC3C,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,IAAI,4BAA4B,CACpC,yBAAyB,OAAO,CAAC,QAAQ,UAAU,QAAQ,qBAAqB;wBAC9E,2BAA2B,KAAK,CAAC,MAAM,wCAAwC;wBAC/E,+EAA+E;wBAC/E,mEAAmE,CACtE,CAAC;gBACJ,CAAC;gBACD,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE;oBACzB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,QAAQ;oBACR,UAAU;oBACV,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACzB,CAAC,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC7B,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACX,QAAQ;gBACR,UAAU;gBACV,6DAA6D;gBAC7D,WAAW,EAAE,mCAAmC,CAAC,QAAQ,CAAC;gBAC1D,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,+EAA+E;IAC/E,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;IAED,mFAAmF;IACnF,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5D,CAAC;IAED,mDAAmD;IACnD,eAAe;QACb,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;;;;;OAUG;IACH,aAAa,CAAC,MAAc;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { AuditReferenceRegistryPort, KnownIconName } from '@endora-commerce/contracts';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import type { AuditActorIdentity } from '../routes.admin.js';
|
|
4
|
+
import type { RecentActivityCatalog } from './recent-activity-catalog.js';
|
|
5
|
+
import type { RecentActivityVisibility } from './recent-activity-visibility.js';
|
|
6
|
+
/**
|
|
7
|
+
* Feature 024 — Dashboard "Recent Activity" read service.
|
|
8
|
+
*
|
|
9
|
+
* Curated read view over `audit_log_entries` that powers the admin home
|
|
10
|
+
* dashboard's Recent Activity card. Server-enforced allowlist of action
|
|
11
|
+
* tokens; bulk-resolves actor + target display labels in at most a small
|
|
12
|
+
* fixed number of round-trips (one per object-type bucket).
|
|
13
|
+
*
|
|
14
|
+
* **Where the action list comes from — feature 080, T042j / D-163.1.** It used
|
|
15
|
+
* to come from `action-catalog.ts`: a 23-token array a host maintained by hand,
|
|
16
|
+
* a prefix table beside it, and a `RecentActivityModule` union closed over four
|
|
17
|
+
* core module ids. A module the host had not written could not appear in any of
|
|
18
|
+
* the three, so a package's audit row was **silently absent** from this card
|
|
19
|
+
* rather than refused. Both are derived from the composed manifests now, and
|
|
20
|
+
* what a row's `module` is comes from the module that declared its token.
|
|
21
|
+
*
|
|
22
|
+
* The declaration is only half of it. Whether a declared module's rows appear
|
|
23
|
+
* is the operator's, read per request through {@link RecentActivityVisibility}
|
|
24
|
+
* — per request, because a flip must take effect without a restart, and because
|
|
25
|
+
* a captured answer is a preference frozen at boot.
|
|
26
|
+
*
|
|
27
|
+
* **Where the labels come from — feature 075, D-87.** They used to come from
|
|
28
|
+
* five hand-written SQL statements naming `admin_users`, `products`,
|
|
29
|
+
* `warehouses`, `price_lists`, `customer_accounts` and `organizations`, and the
|
|
30
|
+
* deep-link URLs from a template map spelling three other modules' admin routes.
|
|
31
|
+
* Both are inverted now: the actor name is `auditActorResolver`, the
|
|
32
|
+
* contribution point this module has owned since feature 072, and everything
|
|
33
|
+
* else is `auditReferenceRegistry`, where each owner pushes a resolver for its
|
|
34
|
+
* own object type and supplies its own route. This service knows the audit row
|
|
35
|
+
* and nothing else about anybody's schema.
|
|
36
|
+
*/
|
|
37
|
+
export interface RecentActivityItem {
|
|
38
|
+
id: string;
|
|
39
|
+
actedAt: string;
|
|
40
|
+
action: string;
|
|
41
|
+
/** The module that declared this action token. Any composed module's id. */
|
|
42
|
+
module: string;
|
|
43
|
+
/**
|
|
44
|
+
* What the card renders for this row, from the declaring module's manifest.
|
|
45
|
+
*
|
|
46
|
+
* They travel on the item because the admin's `ACTIVITY_RENDERING` — the
|
|
47
|
+
* fourth hand-maintained table — was the only thing that knew them, and it
|
|
48
|
+
* knew nothing about a module the SPA was not built with. `labelKey` is
|
|
49
|
+
* relative to `module`'s i18n namespace, so the card resolves it as
|
|
50
|
+
* `t(module, labelKey)` and a package's verb arrives in the operator's
|
|
51
|
+
* language from the package's own bundle.
|
|
52
|
+
*/
|
|
53
|
+
icon: KnownIconName;
|
|
54
|
+
labelKey: string;
|
|
55
|
+
actorDisplayName: string;
|
|
56
|
+
actorKind: 'admin' | 'system';
|
|
57
|
+
targetType: string;
|
|
58
|
+
targetId: string;
|
|
59
|
+
targetDisplayName: string;
|
|
60
|
+
targetUrl: string | null;
|
|
61
|
+
summary: Record<string, unknown> | null;
|
|
62
|
+
}
|
|
63
|
+
export interface RecentActivityResponse {
|
|
64
|
+
data: RecentActivityItem[];
|
|
65
|
+
pagination: {
|
|
66
|
+
limit: number;
|
|
67
|
+
fetchedAt: string;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface ListRecentActivityInput {
|
|
71
|
+
/** Caller-supplied limit. Clamped to [1, 12] server-side. Defaults to 8. */
|
|
72
|
+
limit?: number;
|
|
73
|
+
}
|
|
74
|
+
export declare class RecentActivityService {
|
|
75
|
+
private readonly emFactory;
|
|
76
|
+
/** Every declared action token, derived from the composed manifests. */
|
|
77
|
+
private readonly catalog;
|
|
78
|
+
/** The operator's half of D-163.1, asked per request. */
|
|
79
|
+
private readonly visibility;
|
|
80
|
+
/**
|
|
81
|
+
* The contribution point this module owns and defaults absent. Called per
|
|
82
|
+
* request, never captured: a resolver held once would go on naming actors
|
|
83
|
+
* after the directory behind it went away.
|
|
84
|
+
*/
|
|
85
|
+
private readonly resolveActors;
|
|
86
|
+
private readonly references;
|
|
87
|
+
constructor(emFactory: () => EntityManager,
|
|
88
|
+
/** Every declared action token, derived from the composed manifests. */
|
|
89
|
+
catalog: RecentActivityCatalog,
|
|
90
|
+
/** The operator's half of D-163.1, asked per request. */
|
|
91
|
+
visibility: RecentActivityVisibility,
|
|
92
|
+
/**
|
|
93
|
+
* The contribution point this module owns and defaults absent. Called per
|
|
94
|
+
* request, never captured: a resolver held once would go on naming actors
|
|
95
|
+
* after the directory behind it went away.
|
|
96
|
+
*/
|
|
97
|
+
resolveActors: (ids: string[]) => Promise<AuditActorIdentity[]>, references: AuditReferenceRegistryPort);
|
|
98
|
+
list(input?: ListRecentActivityInput): Promise<RecentActivityResponse>;
|
|
99
|
+
/**
|
|
100
|
+
* Bulk-resolve actor + target labels for the given rows in at most one
|
|
101
|
+
* SQL round-trip per object-type bucket. Never N+1.
|
|
102
|
+
*/
|
|
103
|
+
private enrich;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=recent-activity-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,0BAA0B,EAC1B,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,qBAAa,qBAAqB;IAE9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAXV,SAAS,EAAE,MAAM,aAAa;IAC/C,wEAAwE;IACvD,OAAO,EAAE,qBAAqB;IAC/C,yDAAyD;IACxC,UAAU,EAAE,wBAAwB;IACrD;;;;OAIG;IACc,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,kBAAkB,EAAE,CAAC,EAC/D,UAAU,EAAE,0BAA0B;IAGnD,IAAI,CAAC,KAAK,GAAE,uBAA4B,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAkChF;;;OAGG;YACW,MAAM;CA8BrB"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { AuditLogEntry } from '@endora-commerce/platform/kernel';
|
|
2
|
+
const DEFAULT_LIMIT = 8;
|
|
3
|
+
const MAX_LIMIT = 12;
|
|
4
|
+
export class RecentActivityService {
|
|
5
|
+
emFactory;
|
|
6
|
+
catalog;
|
|
7
|
+
visibility;
|
|
8
|
+
resolveActors;
|
|
9
|
+
references;
|
|
10
|
+
constructor(emFactory,
|
|
11
|
+
/** Every declared action token, derived from the composed manifests. */
|
|
12
|
+
catalog,
|
|
13
|
+
/** The operator's half of D-163.1, asked per request. */
|
|
14
|
+
visibility,
|
|
15
|
+
/**
|
|
16
|
+
* The contribution point this module owns and defaults absent. Called per
|
|
17
|
+
* request, never captured: a resolver held once would go on naming actors
|
|
18
|
+
* after the directory behind it went away.
|
|
19
|
+
*/
|
|
20
|
+
resolveActors, references) {
|
|
21
|
+
this.emFactory = emFactory;
|
|
22
|
+
this.catalog = catalog;
|
|
23
|
+
this.visibility = visibility;
|
|
24
|
+
this.resolveActors = resolveActors;
|
|
25
|
+
this.references = references;
|
|
26
|
+
}
|
|
27
|
+
async list(input = {}) {
|
|
28
|
+
const limit = clampLimit(input.limit);
|
|
29
|
+
// The conjunction of the two axes. Empty is answered here rather than by
|
|
30
|
+
// an unfiltered query: with every eligible module hidden, `$in: []` and
|
|
31
|
+
// "no filter" differ by the whole audit trail.
|
|
32
|
+
const actions = await this.visibility.visibleActions();
|
|
33
|
+
if (actions.length === 0) {
|
|
34
|
+
return { data: [], pagination: { limit, fetchedAt: new Date().toISOString() } };
|
|
35
|
+
}
|
|
36
|
+
const em = this.emFactory();
|
|
37
|
+
// The existing `actedAt DESC` index handles the sort; the secondary `id DESC`
|
|
38
|
+
// tiebreak (research §R2) gives the dashboard deterministic ordering when
|
|
39
|
+
// two admins act inside the same second. Row count is bounded by `limit`,
|
|
40
|
+
// so the extra sort key has negligible cost at our volumes.
|
|
41
|
+
const rows = await em.find(AuditLogEntry, { action: { $in: actions } }, {
|
|
42
|
+
orderBy: { actedAt: 'desc', id: 'desc' },
|
|
43
|
+
limit,
|
|
44
|
+
});
|
|
45
|
+
const enriched = await this.enrich(rows);
|
|
46
|
+
return {
|
|
47
|
+
data: enriched,
|
|
48
|
+
pagination: { limit, fetchedAt: new Date().toISOString() },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Bulk-resolve actor + target labels for the given rows in at most one
|
|
53
|
+
* SQL round-trip per object-type bucket. Never N+1.
|
|
54
|
+
*/
|
|
55
|
+
async enrich(rows) {
|
|
56
|
+
if (rows.length === 0)
|
|
57
|
+
return [];
|
|
58
|
+
// --- Actor labels: this module's own contribution point. ---
|
|
59
|
+
const adminIds = uniqueNonNull(rows.map((r) => r.actorAdminUserId ?? null));
|
|
60
|
+
const adminNames = adminIds.length > 0 ? abbreviate(await this.resolveActors(adminIds)) : new Map();
|
|
61
|
+
// --- Every other label: one bucket per reference type, one round trip each. ---
|
|
62
|
+
const [customerNames, ...targetBuckets] = await Promise.all([
|
|
63
|
+
this.references.resolve('customer_account', uniqueNonNull(rows.map((r) => r.impersonatedCustomerAccountId ?? null))),
|
|
64
|
+
...TARGET_REFERENCE_TYPES.map((type) => this.references.resolve(type, uniqueByType(rows, type))),
|
|
65
|
+
]);
|
|
66
|
+
// Ids are UUIDs, so one map across the target types collides with nothing
|
|
67
|
+
// and keeps `resolveTarget` reading the row it was handed.
|
|
68
|
+
const targets = new Map();
|
|
69
|
+
for (const bucket of targetBuckets) {
|
|
70
|
+
for (const [id, label] of bucket)
|
|
71
|
+
targets.set(id, label);
|
|
72
|
+
}
|
|
73
|
+
return rows
|
|
74
|
+
.map((r) => buildItem(r, this.catalog, { adminNames, customerNames, targets }))
|
|
75
|
+
.filter((item) => item !== null);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The audit `objectType` values an owner module can put a name and a link on.
|
|
80
|
+
*
|
|
81
|
+
* A type nobody claims resolves to an empty bucket, which is the same answer as
|
|
82
|
+
* a deleted row: the snapshot label, and no link.
|
|
83
|
+
*/
|
|
84
|
+
const TARGET_REFERENCE_TYPES = ['product', 'warehouse', 'price_list'];
|
|
85
|
+
/**
|
|
86
|
+
* "Jan Kowalski" → "Jan K.". The abbreviation is the dashboard card's, which is
|
|
87
|
+
* why it lives here and not in the module that owns the directory: `admin_users`
|
|
88
|
+
* answers who somebody is, and how much of that fits on one line of a widget is
|
|
89
|
+
* this surface's question.
|
|
90
|
+
*/
|
|
91
|
+
function abbreviate(identities) {
|
|
92
|
+
const map = new Map();
|
|
93
|
+
for (const identity of identities) {
|
|
94
|
+
const first = (identity.firstName ?? '').trim();
|
|
95
|
+
const last = (identity.lastName ?? '').trim();
|
|
96
|
+
const initial = last.length > 0 ? `${last.slice(0, 1)}.` : '';
|
|
97
|
+
const display = [first, initial].filter((s) => s.length > 0).join(' ').trim() || identity.id;
|
|
98
|
+
map.set(identity.id, display);
|
|
99
|
+
}
|
|
100
|
+
return map;
|
|
101
|
+
}
|
|
102
|
+
function clampLimit(value) {
|
|
103
|
+
if (value === undefined || !Number.isFinite(value))
|
|
104
|
+
return DEFAULT_LIMIT;
|
|
105
|
+
const n = Math.floor(value);
|
|
106
|
+
if (n < 1)
|
|
107
|
+
return 1;
|
|
108
|
+
if (n > MAX_LIMIT)
|
|
109
|
+
return MAX_LIMIT;
|
|
110
|
+
return n;
|
|
111
|
+
}
|
|
112
|
+
function uniqueNonNull(arr) {
|
|
113
|
+
const set = new Set();
|
|
114
|
+
for (const v of arr)
|
|
115
|
+
if (v !== null && v !== undefined)
|
|
116
|
+
set.add(v);
|
|
117
|
+
return [...set];
|
|
118
|
+
}
|
|
119
|
+
function uniqueByType(rows, objectType) {
|
|
120
|
+
const set = new Set();
|
|
121
|
+
for (const r of rows)
|
|
122
|
+
if (r.objectType === objectType && r.objectId)
|
|
123
|
+
set.add(r.objectId);
|
|
124
|
+
return [...set];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* `null` for a token no composed module declares.
|
|
128
|
+
*
|
|
129
|
+
* The row can only be here by having matched the `$in` built from the same
|
|
130
|
+
* catalog, so this is unreachable in a stable composition — but the alternative
|
|
131
|
+
* is `moduleForAction`'s, which threw with a comment calling itself
|
|
132
|
+
* "defensive: unreachable as long as the allowlist and the prefix table stay
|
|
133
|
+
* aligned". They were not aligned, and a throw here would take the whole card
|
|
134
|
+
* down for one row.
|
|
135
|
+
*/
|
|
136
|
+
function buildItem(r, catalog, lookups) {
|
|
137
|
+
const action = r.action;
|
|
138
|
+
const descriptor = catalog.descriptorFor(action);
|
|
139
|
+
if (!descriptor)
|
|
140
|
+
return null;
|
|
141
|
+
// --- Actor ---
|
|
142
|
+
const actorKind = r.actorAdminUserId ? 'admin' : 'system';
|
|
143
|
+
let actorDisplayName;
|
|
144
|
+
if (r.actorAdminUserId) {
|
|
145
|
+
actorDisplayName = lookups.adminNames.get(r.actorAdminUserId) ?? 'Admin';
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
actorDisplayName = 'System';
|
|
149
|
+
}
|
|
150
|
+
if (r.impersonatedCustomerAccountId) {
|
|
151
|
+
const customerLabel = lookups.customerNames.get(r.impersonatedCustomerAccountId)?.label ??
|
|
152
|
+
r.impersonatedCustomerAccountId;
|
|
153
|
+
actorDisplayName = `${actorDisplayName} (as ${customerLabel})`;
|
|
154
|
+
}
|
|
155
|
+
// --- Target display + URL ---
|
|
156
|
+
const { targetDisplayName, targetUrl } = resolveTarget(r, lookups);
|
|
157
|
+
return {
|
|
158
|
+
id: r.id,
|
|
159
|
+
actedAt: r.actedAt.toISOString(),
|
|
160
|
+
action,
|
|
161
|
+
module: descriptor.moduleId,
|
|
162
|
+
icon: descriptor.icon,
|
|
163
|
+
labelKey: descriptor.labelKey,
|
|
164
|
+
actorDisplayName,
|
|
165
|
+
actorKind,
|
|
166
|
+
targetType: r.objectType,
|
|
167
|
+
targetId: r.objectId,
|
|
168
|
+
targetDisplayName,
|
|
169
|
+
targetUrl,
|
|
170
|
+
summary: extractSummary(r),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function resolveTarget(r, lookups) {
|
|
174
|
+
const before = (r.stateBefore ?? null);
|
|
175
|
+
const after = (r.stateAfter ?? null);
|
|
176
|
+
switch (r.objectType) {
|
|
177
|
+
case 'product': {
|
|
178
|
+
// The snapshot wins over the live row, and always has: the card reports
|
|
179
|
+
// what the action did, so a rename shows the name the actor typed.
|
|
180
|
+
const fromSnapshot = pickProductLabel(after) ?? pickProductLabel(before);
|
|
181
|
+
const live = lookups.targets.get(r.objectId);
|
|
182
|
+
return {
|
|
183
|
+
targetDisplayName: fromSnapshot ?? live?.label ?? r.objectId,
|
|
184
|
+
targetUrl: live?.url ?? null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
case 'warehouse':
|
|
188
|
+
case 'price_list': {
|
|
189
|
+
const live = lookups.targets.get(r.objectId);
|
|
190
|
+
const fromSnapshot = pickStr(after, 'name') ?? pickStr(before, 'name');
|
|
191
|
+
const fromSnapshotCode = pickStr(after, 'code') ?? pickStr(before, 'code');
|
|
192
|
+
return {
|
|
193
|
+
targetDisplayName: fromSnapshot ?? live?.label ?? fromSnapshotCode ?? r.objectId,
|
|
194
|
+
targetUrl: live?.url ?? null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
case 'stock_level': {
|
|
198
|
+
// Composite id "<productId>:<warehouseId>"; snapshot carries the
|
|
199
|
+
// human-readable parts.
|
|
200
|
+
const productSku = pickStr(after, 'productSku') ?? pickStr(before, 'productSku');
|
|
201
|
+
const warehouseCode = pickStr(after, 'warehouseCode') ?? pickStr(before, 'warehouseCode');
|
|
202
|
+
const display = productSku && warehouseCode
|
|
203
|
+
? `${productSku} @ ${warehouseCode}`
|
|
204
|
+
: productSku ?? warehouseCode ?? r.objectId;
|
|
205
|
+
const productId = pickStr(after, 'productId') ?? pickStr(before, 'productId');
|
|
206
|
+
const url = productId ? `/catalog/products/${productId}` : null;
|
|
207
|
+
return { targetDisplayName: display, targetUrl: url };
|
|
208
|
+
}
|
|
209
|
+
case 'low_stock_threshold': {
|
|
210
|
+
const display = pickStr(after, 'productSku') ?? pickStr(before, 'productSku') ?? 'threshold';
|
|
211
|
+
const productId = pickStr(after, 'productId') ?? pickStr(before, 'productId');
|
|
212
|
+
const url = productId ? `/catalog/products/${productId}` : '/inventory/low-stock';
|
|
213
|
+
return { targetDisplayName: display, targetUrl: url };
|
|
214
|
+
}
|
|
215
|
+
case 'prompt_action_request': {
|
|
216
|
+
// Feature 043 — prompt-assistant execution summary. The snapshot
|
|
217
|
+
// carries the original prompt; surface a trimmed form of it.
|
|
218
|
+
const prompt = pickStr(after, 'prompt');
|
|
219
|
+
const display = prompt
|
|
220
|
+
? prompt.length > 80
|
|
221
|
+
? `${prompt.slice(0, 77)}…`
|
|
222
|
+
: prompt
|
|
223
|
+
: 'prompt action';
|
|
224
|
+
// No standalone detail page in v1 — outcomes live in the palette.
|
|
225
|
+
return { targetDisplayName: display, targetUrl: null };
|
|
226
|
+
}
|
|
227
|
+
case 'bulk_operation': {
|
|
228
|
+
const fileName = pickStr(after, 'fileName');
|
|
229
|
+
const warehouseCode = pickStr(after, 'warehouseCode') ?? pickStr(before, 'warehouseCode');
|
|
230
|
+
// The last fallback used to test `r.action === 'product.bulk_update'` and
|
|
231
|
+
// render the untranslated English string "bulk product update" for it.
|
|
232
|
+
// Two things wrong with one line, and both are D-163's shape one layer
|
|
233
|
+
// down: a host file naming another module's action token, and a
|
|
234
|
+
// user-visible sentence with no `pl`. The id is what every other
|
|
235
|
+
// unlabelled row falls back to, including this switch's own `default`.
|
|
236
|
+
const display = fileName && warehouseCode ? `${fileName} → ${warehouseCode}` : fileName ?? r.objectId;
|
|
237
|
+
// Bulk operations don't have a single canonical detail page in v1.
|
|
238
|
+
return { targetDisplayName: display, targetUrl: null };
|
|
239
|
+
}
|
|
240
|
+
default:
|
|
241
|
+
return { targetDisplayName: r.objectId, targetUrl: null };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function pickProductLabel(snapshot) {
|
|
245
|
+
if (!snapshot)
|
|
246
|
+
return null;
|
|
247
|
+
const name = snapshot['name'];
|
|
248
|
+
if (typeof name === 'string' && name.length > 0)
|
|
249
|
+
return name;
|
|
250
|
+
if (name && typeof name === 'object') {
|
|
251
|
+
const byLang = name;
|
|
252
|
+
// Try common language fallbacks before iterating.
|
|
253
|
+
for (const key of ['en', 'en-US', 'pl', 'pl-PL']) {
|
|
254
|
+
const v = byLang[key];
|
|
255
|
+
if (typeof v === 'string' && v.length > 0)
|
|
256
|
+
return v;
|
|
257
|
+
}
|
|
258
|
+
for (const v of Object.values(byLang)) {
|
|
259
|
+
if (typeof v === 'string' && v.length > 0)
|
|
260
|
+
return v;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const sku = snapshot['sku'];
|
|
264
|
+
if (typeof sku === 'string' && sku.length > 0)
|
|
265
|
+
return sku;
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
function pickStr(snapshot, key) {
|
|
269
|
+
if (!snapshot)
|
|
270
|
+
return null;
|
|
271
|
+
const v = snapshot[key];
|
|
272
|
+
return typeof v === 'string' && v.length > 0 ? v : null;
|
|
273
|
+
}
|
|
274
|
+
function extractSummary(r) {
|
|
275
|
+
// Surface only a compact summary for bulk-style rows. Never the full
|
|
276
|
+
// stateBefore/stateAfter blobs (FR-030).
|
|
277
|
+
//
|
|
278
|
+
// **The key list is the filter**, and it always was. There used to be a
|
|
279
|
+
// pre-test above it — `objectType !== 'bulk_operation' && action !==
|
|
280
|
+
// 'price_list.products_replace'` — which is a second, hand-written answer to
|
|
281
|
+
// "which rows are bulk-shaped", drifting against the very list below it and
|
|
282
|
+
// naming another module's action token in this host file (D-163's shape, one
|
|
283
|
+
// layer down from the four tables T042j retired). FR-030 is satisfied by the
|
|
284
|
+
// allow-list: a row that carries none of these keys still returns `null`,
|
|
285
|
+
// and a row that carries one is bulk-shaped by the only evidence there is.
|
|
286
|
+
const after = r.stateAfter;
|
|
287
|
+
if (!after)
|
|
288
|
+
return null;
|
|
289
|
+
const keys = [
|
|
290
|
+
'fileName',
|
|
291
|
+
'warehouseId',
|
|
292
|
+
'warehouseCode',
|
|
293
|
+
'rowsProcessed',
|
|
294
|
+
'rowsSkipped',
|
|
295
|
+
'rowsErrored',
|
|
296
|
+
'added',
|
|
297
|
+
'removed',
|
|
298
|
+
'kept',
|
|
299
|
+
'touchedFields',
|
|
300
|
+
];
|
|
301
|
+
const out = {};
|
|
302
|
+
for (const k of keys) {
|
|
303
|
+
if (k in after)
|
|
304
|
+
out[k] = after[k];
|
|
305
|
+
}
|
|
306
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=recent-activity-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-activity-service.js","sourceRoot":"","sources":["../../../src/backend/services/recent-activity-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import type { RecentActivityCatalog } from './recent-activity-catalog.js';
|
|
3
|
+
/** One module's answer, for the admin screen. */
|
|
4
|
+
export interface RecentActivityModuleVisibility {
|
|
5
|
+
moduleId: string;
|
|
6
|
+
moduleName: string;
|
|
7
|
+
visible: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class RecentActivityVisibility {
|
|
10
|
+
private readonly catalog;
|
|
11
|
+
private readonly settings;
|
|
12
|
+
constructor(catalog: RecentActivityCatalog, settings: SettingsReadPort);
|
|
13
|
+
/**
|
|
14
|
+
* Every eligible module with the operator's current answer.
|
|
15
|
+
*
|
|
16
|
+
* One batch read, `salesChannelId: null` — the choice is platform-wide, for
|
|
17
|
+
* the reason activation is (FR-009): a home dashboard is not per-storefront,
|
|
18
|
+
* and joining `setting_values` would reintroduce channel dependence through
|
|
19
|
+
* the back door (Constitution XII).
|
|
20
|
+
*
|
|
21
|
+
* A code the settings store does not know resolves to **visible**, not to
|
|
22
|
+
* hidden. The row is created by the boot reconcile for a shipped module and
|
|
23
|
+
* by `install` for a package, so its absence means one has not run yet — a
|
|
24
|
+
* platform mid-install, not an operator's decision — and the ruling's default
|
|
25
|
+
* is the honest answer to "nobody has chosen".
|
|
26
|
+
*/
|
|
27
|
+
list(): Promise<RecentActivityModuleVisibility[]>;
|
|
28
|
+
/**
|
|
29
|
+
* The action tokens the card may query — the conjunction of the two axes.
|
|
30
|
+
*
|
|
31
|
+
* Empty when every eligible module is hidden, and the caller must not turn
|
|
32
|
+
* that into "no filter": an unfiltered query would put every audited write on
|
|
33
|
+
* the dashboard, which is the opposite of what the operator asked for.
|
|
34
|
+
*/
|
|
35
|
+
visibleActions(): Promise<string[]>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=recent-activity-visibility.d.ts.map
|