Skip to content

Commit be1b916

Browse files
os-zhuangclaude
andauthored
feat(audit): move audit/realtime objects to owning plugins (ADR-0029 K2) (#1460)
Decompose the platform-objects monolith further: the audit/activity data model moves to @objectstack/plugin-audit and presence to @objectstack/service-realtime, so each plugin owns the sys_* objects its own behavior produces/observes (ADR-0029 K2/D7/D8). Objects moved (git mv, preserving history): - sys_audit_log / sys_activity / sys_comment → @objectstack/plugin-audit (exactly what the audit writers emit: audit_log + activity rows, and the sys_comment @mention hook) - sys_presence → @objectstack/service-realtime Deliberately left in platform-objects (still registered, not owned here): - sys_notification — reworked by ADR-0030 messaging. - sys_attachment — a polymorphic file↔record link that depends on sys_file; it belongs with @objectstack/service-storage, so it moves in the storage-domain decomposition rather than being mis-homed under the audit plugin. The audit plugin keeps registering it (imported from platform-objects) so it stays available meanwhile. Wiring: - Each plugin registers its objects via manifest.register and owns the Audit Logs setup-nav contribution (D7), so the entry follows the object that backs it. - i18n bundles (en/zh-CN/ja-JP/es-ES) extracted into each plugin and loaded on kernel:ready (D8). Seeded from the platform-objects bundles so existing translations are preserved; regenerate with `os i18n extract` against each scripts/i18n-extract.config.ts. - platform-objects audit barrel + i18n-extract config + tests updated to drop the moved objects (orphan generated translations remain as the accepted interim state, matching the security/sharing/webhooks moves). Adds canonical-identity tests for the moved audit objects (parity with the existing sys_presence test). Co-authored-by: Claude <noreply@anthropic.com>
1 parent 24c9013 commit be1b916

26 files changed

Lines changed: 1383 additions & 23 deletions

packages/platform-objects/scripts/i18n-extract.config.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ import {
5757
// bundles keep working until then.
5858

5959
// ── Audit ─────────────────────────────────────────────────────────────────
60+
// sys_audit_log / sys_activity / sys_comment moved to @objectstack/plugin-audit
61+
// and sys_presence to @objectstack/service-realtime (ADR-0029 K2 / D8). Their
62+
// i18n extraction now lives in those packages; the already-generated bundles
63+
// here keep working until the next regeneration. sys_attachment stays here
64+
// pending the storage-domain decomposition (it belongs with service-storage).
6065
import {
61-
SysAuditLog,
62-
SysPresence,
63-
SysActivity,
64-
SysComment,
65-
SysAttachment,
6666
SysNotification,
67+
SysAttachment,
6768
SysEmail,
6869
SysEmailTemplate,
6970
SysSavedReport,
@@ -144,13 +145,11 @@ export default defineStack({
144145
// Security: RBAC moved to @objectstack/plugin-security, sharing to
145146
// @objectstack/plugin-sharing (ADR-0029 K2 / D8).
146147

147-
// Audit
148-
SysAuditLog,
149-
SysPresence,
150-
SysActivity,
151-
SysComment,
152-
SysAttachment,
148+
// Audit (sys_audit_log / sys_activity / sys_comment moved to
149+
// @objectstack/plugin-audit; sys_presence to @objectstack/service-realtime;
150+
// sys_attachment stays pending storage-domain decomposition)
153151
SysNotification,
152+
SysAttachment,
154153
SysEmail,
155154
SysEmailTemplate,
156155
SysSavedReport,

packages/platform-objects/src/apps/setup-nav.contributions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,9 @@ export const SETUP_NAV_CONTRIBUTIONS: NavigationContribution[] = [
9292
group: 'group_diagnostics',
9393
priority: BASE_PRIORITY,
9494
items: [
95+
// Audit Logs (sys_audit_log) is contributed by @objectstack/plugin-audit
96+
// which now owns it (ADR-0029 K2).
9597
{ id: 'nav_sessions', type: 'object', label: 'Sessions', objectName: 'sys_session', icon: 'monitor' },
96-
{ id: 'nav_audit_logs', type: 'object', label: 'Audit Logs', objectName: 'sys_audit_log', icon: 'scroll-text' },
9798
{ id: 'nav_notifications', type: 'object', label: 'Notification Events', objectName: 'sys_notification', viewName: 'recent', icon: 'bell', requiresObject: 'sys_notification' },
9899
],
99100
},

packages/platform-objects/src/audit/index.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
* platform-objects/audit — Audit & Realtime Platform Objects
55
*/
66

7-
export { SysAuditLog } from './sys-audit-log.object.js';
8-
export { SysPresence } from './sys-presence.object.js';
9-
export { SysActivity } from './sys-activity.object.js';
10-
export { SysComment } from './sys-comment.object.js';
11-
export { SysAttachment } from './sys-attachment.object.js';
7+
// sys_audit_log / sys_activity / sys_comment moved to @objectstack/plugin-audit
8+
// and sys_presence to @objectstack/service-realtime (ADR-0029 K2).
9+
// sys_notification stays here pending ADR-0030 messaging rework; sys_attachment
10+
// stays here pending the storage-domain decomposition (it belongs with
11+
// @objectstack/service-storage's sys_file, not the audit plugin).
1212
export { SysNotification } from './sys-notification.object.js';
13+
export { SysAttachment } from './sys-attachment.object.js';
1314
export { SysEmail } from './sys-email.object.js';
1415
export { SysEmailTemplate } from './sys-email-template.object.js';
1516
export { SysSavedReport } from './sys-saved-report.object.js';

packages/platform-objects/src/platform-objects.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import {
1919
// RBAC objects (SysRole/SysPermissionSet/… + defaultPermissionSets) moved to
2020
// @objectstack/plugin-security and the sharing objects to
2121
// @objectstack/plugin-sharing per ADR-0029 K2 — see their packages' tests.
22-
import { SysAuditLog, SysPresence } from './audit/index.js';
22+
// sys_audit_log / sys_activity / sys_comment / sys_attachment moved to
23+
// @objectstack/plugin-audit and sys_presence to @objectstack/service-realtime
24+
// per ADR-0029 K2 — see their packages' tests.
2325
// sys_webhook moved to @objectstack/plugin-webhooks per ADR-0029 (K2.a).
2426
import {
2527
SysMetadata,
@@ -42,8 +44,6 @@ const systemObjects = [
4244
['SysApiKey', SysApiKey, 'sys_api_key'],
4345
['SysTwoFactor', SysTwoFactor, 'sys_two_factor'],
4446
['SysUserPreference', SysUserPreference, 'sys_user_preference'],
45-
['SysAuditLog', SysAuditLog, 'sys_audit_log'],
46-
['SysPresence', SysPresence, 'sys_presence'],
4747
['SysMetadata', SysMetadata, 'sys_metadata'],
4848
['SysMetadataHistoryObject', SysMetadataHistoryObject, 'sys_metadata_history'],
4949
['SysSetting', SysSetting, 'sys_setting'],
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* Build-time only config for `os i18n extract` (ADR-0029 D8). Not deployed.
5+
* The plugin owns the i18n extraction for the objects it owns; the
6+
* `translations` baseline is this plugin's OWN generated bundles so re-running
7+
* `--merge` preserves every hand-translated string. (Initial zh-CN/ja-JP/es-ES
8+
* strings were seeded from @objectstack/platform-objects.)
9+
*
10+
* os i18n extract packages/plugins/plugin-audit/scripts/i18n-extract.config.ts \
11+
* --locales=zh-CN,ja-JP,es-ES --fill=default --objects-only \
12+
* --out=packages/plugins/plugin-audit/src/translations
13+
*/
14+
15+
import { defineStack } from '@objectstack/spec';
16+
import { SysAuditLog } from '../src/objects/sys-audit-log.object.js';
17+
import { SysActivity } from '../src/objects/sys-activity.object.js';
18+
import { SysComment } from '../src/objects/sys-comment.object.js';
19+
import { enObjects } from '../src/translations/en.objects.generated.js';
20+
import { zhCNObjects } from '../src/translations/zh-CN.objects.generated.js';
21+
import { jaJPObjects } from '../src/translations/ja-JP.objects.generated.js';
22+
import { esESObjects } from '../src/translations/es-ES.objects.generated.js';
23+
24+
export default defineStack({
25+
name: 'plugin-audit-i18n-extract',
26+
objects: [SysAuditLog, SysActivity, SysComment] as any,
27+
translations: [
28+
{ en: { objects: enObjects } },
29+
{ 'zh-CN': { objects: zhCNObjects } },
30+
{ 'ja-JP': { objects: jaJPObjects } },
31+
{ 'es-ES': { objects: esESObjects } },
32+
],
33+
});

packages/plugins/plugin-audit/src/audit-plugin.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
import type { Plugin, PluginContext } from '@objectstack/core';
44
import type { IDataEngine } from '@objectstack/spec/contracts';
5-
import { SysAuditLog, SysActivity, SysComment, SysAttachment, SysNotification } from '@objectstack/platform-objects/audit';
5+
import { SysAuditLog, SysActivity, SysComment } from './objects/index.js';
6+
// Registered here but still owned by platform-objects (the plugin contributes
7+
// them to the kernel without owning the definition yet):
8+
// - sys_notification — reworked by ADR-0030 messaging (notification→event).
9+
// - sys_attachment — a file↔record link belonging with service-storage's
10+
// sys_file; moves in the storage-domain decomposition, not this audit move.
11+
import { SysNotification, SysAttachment } from '@objectstack/platform-objects/audit';
612
import { installAuditWriters, type MessagingEmitSurface } from './audit-writers.js';
713

814
/**
@@ -32,8 +38,36 @@ export class AuditPlugin implements Plugin {
3238
defaultDatasource: 'cloud',
3339
namespace: 'sys',
3440
objects: [SysAuditLog, SysActivity, SysComment, SysAttachment, SysNotification],
41+
// ADR-0029 D7 — contribute the Audit Logs entry into the Setup app's
42+
// `group_diagnostics` slot. The plugin owns sys_audit_log (K2).
43+
navigationContributions: [
44+
{
45+
app: 'setup',
46+
group: 'group_diagnostics',
47+
priority: 100,
48+
items: [
49+
{ id: 'nav_audit_logs', type: 'object', label: 'Audit Logs', objectName: 'sys_audit_log', icon: 'scroll-text' },
50+
],
51+
},
52+
],
3553
});
3654

55+
// ADR-0029 D8 — contribute this plugin's object translations to the i18n
56+
// service on kernel:ready (the i18n plugin may register after this one).
57+
if (typeof (ctx as any).hook === 'function') {
58+
(ctx as any).hook('kernel:ready', async () => {
59+
try {
60+
const i18n = ctx.getService<any>('i18n');
61+
if (i18n && typeof i18n.loadTranslations === 'function') {
62+
const { AuditTranslations } = await import('./translations/index.js');
63+
for (const [locale, data] of Object.entries(AuditTranslations)) {
64+
i18n.loadTranslations(locale, data as Record<string, unknown>);
65+
}
66+
}
67+
} catch { /* i18n optional */ }
68+
});
69+
}
70+
3771
ctx.logger.info('Audit Plugin initialized');
3872
}
3973

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* Audit & collaboration objects owned by `@objectstack/plugin-audit`
5+
* (ADR-0029 K2). Moved here from the `@objectstack/platform-objects` monolith
6+
* so the plugin owns its data model + behavior — exactly the objects the audit
7+
* writers produce/observe (sys_audit_log + sys_activity rows; sys_comment
8+
* @mention hook).
9+
*
10+
* Intentionally NOT moved here:
11+
* - `sys_notification` — reworked by ADR-0030 messaging.
12+
* - `sys_attachment` — a file↔record link belonging with service-storage's
13+
* sys_file; stays in platform-objects pending the storage-domain move.
14+
*/
15+
16+
export { SysAuditLog } from './sys-audit-log.object.js';
17+
export { SysActivity } from './sys-activity.object.js';
18+
export { SysComment } from './sys-comment.object.js';
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { describe, it, expect } from 'vitest';
4+
import { StorageNameMapping } from '@objectstack/spec/system';
5+
import { SysAuditLog, SysActivity, SysComment } from './index.js';
6+
7+
/**
8+
* Canonical-identity coverage for the audit/collaboration objects this plugin
9+
* owns after the ADR-0029 K2 move out of @objectstack/platform-objects. Locks
10+
* in the short names, system flag, and the storage-name resolution so a future
11+
* rename can't silently change the physical table.
12+
*/
13+
const ownedObjects = [
14+
['SysAuditLog', SysAuditLog, 'sys_audit_log'],
15+
['SysActivity', SysActivity, 'sys_activity'],
16+
['SysComment', SysComment, 'sys_comment'],
17+
] as const;
18+
19+
describe('@objectstack/plugin-audit objects', () => {
20+
it.each(ownedObjects)('%s uses a canonical sys_ short name', (_name, object, name) => {
21+
expect(object.name).toBe(name);
22+
});
23+
24+
it.each(ownedObjects)('%s resolves to the same physical table name', (_name, object, name) => {
25+
expect(StorageNameMapping.resolveTableName(object)).toBe(name);
26+
});
27+
28+
it.each(ownedObjects)('%s is marked as a system object', (_name, object) => {
29+
expect(object.isSystem).toBe(true);
30+
});
31+
32+
it.each(ownedObjects)('%s does not carry deprecated storage identity fields', (_name, object) => {
33+
expect((object as any).namespace).toBeUndefined();
34+
expect((object as any).tableName).toBeUndefined();
35+
});
36+
});

packages/platform-objects/src/audit/sys-activity.object.ts renamed to packages/plugins/plugin-audit/src/objects/sys-activity.object.ts

File renamed without changes.

packages/platform-objects/src/audit/sys-audit-log.object.ts renamed to packages/plugins/plugin-audit/src/objects/sys-audit-log.object.ts

File renamed without changes.

0 commit comments

Comments
 (0)