@archiva/archiva-nextjs 0.2.5 → 0.2.7

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.
@@ -0,0 +1,139 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ type EventChange = {
4
+ op: "set" | "unset" | "add" | "remove" | "replace" | string;
5
+ path: string;
6
+ before?: unknown;
7
+ after?: unknown;
8
+ };
9
+ type CreateEventInput = {
10
+ action: string;
11
+ entityType: string;
12
+ entityId: string;
13
+ actorType?: string;
14
+ actorId?: string;
15
+ actorDisplay?: string;
16
+ occurredAt?: string;
17
+ source?: string;
18
+ context?: Record<string, unknown>;
19
+ changes?: EventChange[];
20
+ };
21
+ type CreateEventOptions = {
22
+ idempotencyKey?: string;
23
+ requestId?: string;
24
+ };
25
+ type AuditEventListItem = {
26
+ id: string;
27
+ receivedAt: string;
28
+ action: string;
29
+ entityType: string;
30
+ entityId: string;
31
+ actorId: string | null;
32
+ actorType?: 'user' | 'service' | 'system';
33
+ actorDisplay?: string | null;
34
+ source: string | null;
35
+ };
36
+ type PageResult<T> = {
37
+ items: T[];
38
+ nextCursor?: string;
39
+ };
40
+ type LoadEventsParams = {
41
+ entityId?: string;
42
+ actorId?: string;
43
+ entityType?: string;
44
+ actorType?: 'user' | 'service' | 'system';
45
+ limit?: number;
46
+ cursor?: string;
47
+ };
48
+ declare class ArchivaError extends Error {
49
+ statusCode: number;
50
+ code: "UNAUTHORIZED" | "FORBIDDEN" | "PAYLOAD_TOO_LARGE" | "RATE_LIMITED" | "IDEMPOTENCY_CONFLICT" | "HTTP_ERROR";
51
+ retryAfterSeconds?: number;
52
+ details?: unknown;
53
+ constructor(params: {
54
+ statusCode: number;
55
+ code: "UNAUTHORIZED" | "FORBIDDEN" | "PAYLOAD_TOO_LARGE" | "RATE_LIMITED" | "IDEMPOTENCY_CONFLICT" | "HTTP_ERROR";
56
+ message: string;
57
+ retryAfterSeconds?: number;
58
+ details?: unknown;
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Server action to load audit events
64
+ *
65
+ * @param params - Query parameters for filtering events
66
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
67
+ * @returns Paginated list of audit events
68
+ */
69
+ declare function loadEvents(params: LoadEventsParams, apiKey?: string): Promise<PageResult<AuditEventListItem>>;
70
+ /**
71
+ * Server action to create a single audit event
72
+ *
73
+ * @param event - Event data to create
74
+ * @param options - Optional idempotency and request ID options
75
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
76
+ * @returns Created event ID and replay status
77
+ */
78
+ declare function createEvent(event: CreateEventInput, options?: CreateEventOptions, apiKey?: string): Promise<{
79
+ eventId: string;
80
+ replayed: boolean;
81
+ }>;
82
+ /**
83
+ * Server action to create multiple audit events (bulk)
84
+ *
85
+ * @param events - Array of events to create
86
+ * @param options - Optional idempotency and request ID options
87
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
88
+ * @returns Array of created event IDs
89
+ */
90
+ declare function createEvents(events: CreateEventInput[], options?: CreateEventOptions, apiKey?: string): Promise<{
91
+ eventIds: string[];
92
+ }>;
93
+
94
+ interface FrontendTokenResponse {
95
+ token: string;
96
+ expiresAt: number;
97
+ }
98
+ /**
99
+ * Fetches a frontend token from the Archiva API
100
+ *
101
+ * @param projectId - Optional project ID for scoping
102
+ * @param apiBaseUrl - Archiva API base URL (defaults to https://api.archiva.app)
103
+ * @returns Frontend token response
104
+ */
105
+ declare function createFrontendTokenGET(projectId?: string, apiBaseUrl?: string): Promise<FrontendTokenResponse>;
106
+
107
+ /**
108
+ * Next.js route handler for GET /api/archiva/frontend-token
109
+ *
110
+ * This handler can be used in the host app's route file:
111
+ *
112
+ * ```ts
113
+ * // app/api/archiva/frontend-token/route.ts
114
+ * export { GET } from '@archiva/archiva-nextjs/server';
115
+ * ```
116
+ *
117
+ * Or with custom configuration:
118
+ *
119
+ * ```ts
120
+ * import { createFrontendTokenRoute } from '@archiva/archiva-nextjs/server';
121
+ *
122
+ * export const GET = createFrontendTokenRoute({
123
+ * apiBaseUrl: process.env.ARCHIVA_API_URL,
124
+ * });
125
+ * ```
126
+ */
127
+ declare function createFrontendTokenRoute(options?: {
128
+ apiBaseUrl?: string;
129
+ }): (request: NextRequest) => Promise<NextResponse<FrontendTokenResponse> | NextResponse<{
130
+ error: string;
131
+ }>>;
132
+ /**
133
+ * Default GET handler (for direct export)
134
+ */
135
+ declare function GET(request: NextRequest): Promise<NextResponse<FrontendTokenResponse> | NextResponse<{
136
+ error: string;
137
+ }>>;
138
+
139
+ export { ArchivaError as A, type CreateEventInput as C, type EventChange as E, type FrontendTokenResponse as F, GET as G, type LoadEventsParams as L, type PageResult as P, createEvents as a, createFrontendTokenGET as b, createEvent as c, createFrontendTokenRoute as d, type CreateEventOptions as e, type AuditEventListItem as f, loadEvents as l };
@@ -0,0 +1,139 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ type EventChange = {
4
+ op: "set" | "unset" | "add" | "remove" | "replace" | string;
5
+ path: string;
6
+ before?: unknown;
7
+ after?: unknown;
8
+ };
9
+ type CreateEventInput = {
10
+ action: string;
11
+ entityType: string;
12
+ entityId: string;
13
+ actorType?: string;
14
+ actorId?: string;
15
+ actorDisplay?: string;
16
+ occurredAt?: string;
17
+ source?: string;
18
+ context?: Record<string, unknown>;
19
+ changes?: EventChange[];
20
+ };
21
+ type CreateEventOptions = {
22
+ idempotencyKey?: string;
23
+ requestId?: string;
24
+ };
25
+ type AuditEventListItem = {
26
+ id: string;
27
+ receivedAt: string;
28
+ action: string;
29
+ entityType: string;
30
+ entityId: string;
31
+ actorId: string | null;
32
+ actorType?: 'user' | 'service' | 'system';
33
+ actorDisplay?: string | null;
34
+ source: string | null;
35
+ };
36
+ type PageResult<T> = {
37
+ items: T[];
38
+ nextCursor?: string;
39
+ };
40
+ type LoadEventsParams = {
41
+ entityId?: string;
42
+ actorId?: string;
43
+ entityType?: string;
44
+ actorType?: 'user' | 'service' | 'system';
45
+ limit?: number;
46
+ cursor?: string;
47
+ };
48
+ declare class ArchivaError extends Error {
49
+ statusCode: number;
50
+ code: "UNAUTHORIZED" | "FORBIDDEN" | "PAYLOAD_TOO_LARGE" | "RATE_LIMITED" | "IDEMPOTENCY_CONFLICT" | "HTTP_ERROR";
51
+ retryAfterSeconds?: number;
52
+ details?: unknown;
53
+ constructor(params: {
54
+ statusCode: number;
55
+ code: "UNAUTHORIZED" | "FORBIDDEN" | "PAYLOAD_TOO_LARGE" | "RATE_LIMITED" | "IDEMPOTENCY_CONFLICT" | "HTTP_ERROR";
56
+ message: string;
57
+ retryAfterSeconds?: number;
58
+ details?: unknown;
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Server action to load audit events
64
+ *
65
+ * @param params - Query parameters for filtering events
66
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
67
+ * @returns Paginated list of audit events
68
+ */
69
+ declare function loadEvents(params: LoadEventsParams, apiKey?: string): Promise<PageResult<AuditEventListItem>>;
70
+ /**
71
+ * Server action to create a single audit event
72
+ *
73
+ * @param event - Event data to create
74
+ * @param options - Optional idempotency and request ID options
75
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
76
+ * @returns Created event ID and replay status
77
+ */
78
+ declare function createEvent(event: CreateEventInput, options?: CreateEventOptions, apiKey?: string): Promise<{
79
+ eventId: string;
80
+ replayed: boolean;
81
+ }>;
82
+ /**
83
+ * Server action to create multiple audit events (bulk)
84
+ *
85
+ * @param events - Array of events to create
86
+ * @param options - Optional idempotency and request ID options
87
+ * @param apiKey - Optional API key (otherwise uses ARCHIVA_SECRET_KEY env var)
88
+ * @returns Array of created event IDs
89
+ */
90
+ declare function createEvents(events: CreateEventInput[], options?: CreateEventOptions, apiKey?: string): Promise<{
91
+ eventIds: string[];
92
+ }>;
93
+
94
+ interface FrontendTokenResponse {
95
+ token: string;
96
+ expiresAt: number;
97
+ }
98
+ /**
99
+ * Fetches a frontend token from the Archiva API
100
+ *
101
+ * @param projectId - Optional project ID for scoping
102
+ * @param apiBaseUrl - Archiva API base URL (defaults to https://api.archiva.app)
103
+ * @returns Frontend token response
104
+ */
105
+ declare function createFrontendTokenGET(projectId?: string, apiBaseUrl?: string): Promise<FrontendTokenResponse>;
106
+
107
+ /**
108
+ * Next.js route handler for GET /api/archiva/frontend-token
109
+ *
110
+ * This handler can be used in the host app's route file:
111
+ *
112
+ * ```ts
113
+ * // app/api/archiva/frontend-token/route.ts
114
+ * export { GET } from '@archiva/archiva-nextjs/server';
115
+ * ```
116
+ *
117
+ * Or with custom configuration:
118
+ *
119
+ * ```ts
120
+ * import { createFrontendTokenRoute } from '@archiva/archiva-nextjs/server';
121
+ *
122
+ * export const GET = createFrontendTokenRoute({
123
+ * apiBaseUrl: process.env.ARCHIVA_API_URL,
124
+ * });
125
+ * ```
126
+ */
127
+ declare function createFrontendTokenRoute(options?: {
128
+ apiBaseUrl?: string;
129
+ }): (request: NextRequest) => Promise<NextResponse<FrontendTokenResponse> | NextResponse<{
130
+ error: string;
131
+ }>>;
132
+ /**
133
+ * Default GET handler (for direct export)
134
+ */
135
+ declare function GET(request: NextRequest): Promise<NextResponse<FrontendTokenResponse> | NextResponse<{
136
+ error: string;
137
+ }>>;
138
+
139
+ export { ArchivaError as A, type CreateEventInput as C, type EventChange as E, type FrontendTokenResponse as F, GET as G, type LoadEventsParams as L, type PageResult as P, createEvents as a, createFrontendTokenGET as b, createEvent as c, createFrontendTokenRoute as d, type CreateEventOptions as e, type AuditEventListItem as f, loadEvents as l };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ArchivaProvider, ArchivaProviderProps } from './react/index.mjs';
2
- export { A as ArchivaError, f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, E as EventChange, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from './index-D6XeJsvH.mjs';
2
+ export { A as ArchivaError, f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, E as EventChange, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from './index-BJ8aJsbs.mjs';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
5
5
  import 'next/server';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ArchivaProvider, ArchivaProviderProps } from './react/index.js';
2
- export { A as ArchivaError, f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, E as EventChange, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from './index-D6XeJsvH.js';
2
+ export { A as ArchivaError, f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, E as EventChange, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from './index-BJ8aJsbs.js';
3
3
  import 'react/jsx-runtime';
4
4
  import 'react';
5
5
  import 'next/server';
@@ -29,11 +29,12 @@ type TimelineProps = {
29
29
  emptyMessage?: string;
30
30
  showSearch?: boolean;
31
31
  showFilters?: boolean;
32
+ showSystemAndServices?: boolean;
32
33
  getActorAvatar?: (actorId: string) => string | React.ComponentType<{
33
34
  className?: string;
34
35
  }> | undefined;
35
36
  };
36
- declare function Timeline({ entityId, actorId, entityType, initialLimit, className, emptyMessage, showSearch, showFilters, getActorAvatar, }: TimelineProps): react_jsx_runtime.JSX.Element;
37
+ declare function Timeline({ entityId, actorId, entityType, initialLimit, className, emptyMessage, showSearch, showFilters, showSystemAndServices, getActorAvatar, }: TimelineProps): react_jsx_runtime.JSX.Element;
37
38
 
38
39
  type ArchivaContextValue = {
39
40
  apiBaseUrl: string;
@@ -29,11 +29,12 @@ type TimelineProps = {
29
29
  emptyMessage?: string;
30
30
  showSearch?: boolean;
31
31
  showFilters?: boolean;
32
+ showSystemAndServices?: boolean;
32
33
  getActorAvatar?: (actorId: string) => string | React.ComponentType<{
33
34
  className?: string;
34
35
  }> | undefined;
35
36
  };
36
- declare function Timeline({ entityId, actorId, entityType, initialLimit, className, emptyMessage, showSearch, showFilters, getActorAvatar, }: TimelineProps): react_jsx_runtime.JSX.Element;
37
+ declare function Timeline({ entityId, actorId, entityType, initialLimit, className, emptyMessage, showSearch, showFilters, showSystemAndServices, getActorAvatar, }: TimelineProps): react_jsx_runtime.JSX.Element;
37
38
 
38
39
  type ArchivaContextValue = {
39
40
  apiBaseUrl: string;
@@ -78,11 +78,73 @@ function formatTimestamp(timestamp, format = "default") {
78
78
  minute: "2-digit"
79
79
  });
80
80
  }
81
+ var MonitorCogIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
82
+ "svg",
83
+ {
84
+ className,
85
+ width: "16",
86
+ height: "16",
87
+ viewBox: "0 0 24 24",
88
+ fill: "none",
89
+ stroke: "currentColor",
90
+ strokeWidth: "2",
91
+ strokeLinecap: "round",
92
+ strokeLinejoin: "round",
93
+ children: [
94
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
95
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
96
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12", y1: "17", x2: "12", y2: "21" }),
97
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "18", cy: "8", r: "2" })
98
+ ]
99
+ }
100
+ );
101
+ var CloudCogIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
102
+ "svg",
103
+ {
104
+ className,
105
+ width: "16",
106
+ height: "16",
107
+ viewBox: "0 0 24 24",
108
+ fill: "none",
109
+ stroke: "currentColor",
110
+ strokeWidth: "2",
111
+ strokeLinecap: "round",
112
+ strokeLinejoin: "round",
113
+ children: [
114
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 20a2 2 0 0 0 2-2V8.5a2.5 2.5 0 0 0-2.5-2.5c-1.003 0-1.9.446-2.5 1.153" }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M13 18a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2" }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "18", cy: "8", r: "2" })
117
+ ]
118
+ }
119
+ );
81
120
  function eventToTimelineItem(event, getActorAvatar) {
82
121
  const actorId = event.actorId;
83
- const userName = actorId ? actorId.includes(":") ? actorId.split(":")[1] || actorId : actorId : "Unknown Actor";
84
- const userHandle = actorId || "unknown";
85
- const initials = userName.charAt(0).toUpperCase();
122
+ const actorType = event.actorType || "user";
123
+ let actorIdPart = actorId || "";
124
+ if (actorId && actorId.includes(":")) {
125
+ const [, ...rest] = actorId.split(":");
126
+ actorIdPart = rest.join(":") || actorId;
127
+ }
128
+ let actorDisplay = event.actorDisplay || null;
129
+ if (!actorDisplay && actorId) {
130
+ if (actorId.includes(":")) {
131
+ const [type, ...rest] = actorId.split(":");
132
+ const idPart = rest.join(":");
133
+ if (type === "service" || type === "system") {
134
+ actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${idPart.charAt(0).toUpperCase() + idPart.slice(1)}`;
135
+ } else {
136
+ actorDisplay = idPart.charAt(0).toUpperCase() + idPart.slice(1);
137
+ }
138
+ } else {
139
+ actorDisplay = actorId.charAt(0).toUpperCase() + actorId.slice(1);
140
+ }
141
+ }
142
+ if (!actorDisplay) {
143
+ actorDisplay = "Unknown Actor";
144
+ }
145
+ const userName = actorDisplay;
146
+ const userHandle = actorIdPart || "unknown";
147
+ const initials = userHandle.charAt(0).toUpperCase();
86
148
  const actorAvatarOrIcon = actorId && getActorAvatar ? getActorAvatar(actorId) : void 0;
87
149
  let actorAvatar = void 0;
88
150
  let actorIcon = void 0;
@@ -92,7 +154,13 @@ function eventToTimelineItem(event, getActorAvatar) {
92
154
  actorIcon = actorAvatarOrIcon;
93
155
  }
94
156
  if (!actorAvatar && !actorIcon && actorId) {
95
- actorAvatar = "https://www.gravatar.com/avatar?d=mp";
157
+ if (actorType === "user") {
158
+ actorAvatar = "https://www.gravatar.com/avatar?d=mp";
159
+ } else if (actorType === "system") {
160
+ actorIcon = MonitorCogIcon;
161
+ } else if (actorType === "service") {
162
+ actorIcon = CloudCogIcon;
163
+ }
96
164
  }
97
165
  const action = event.action.charAt(0).toUpperCase() + event.action.slice(1);
98
166
  return {
@@ -101,8 +169,9 @@ function eventToTimelineItem(event, getActorAvatar) {
101
169
  // Not used in activity layout
102
170
  description: action,
103
171
  timestamp: event.receivedAt,
104
- userName: userName.charAt(0).toUpperCase() + userName.slice(1),
172
+ userName,
105
173
  userHandle,
174
+ actorDisplay,
106
175
  entityType: event.entityType.toLowerCase(),
107
176
  avatar: actorAvatar,
108
177
  icon: actorIcon,
@@ -122,6 +191,9 @@ async function fetchEventsWithRetry(apiBaseUrl, getToken, forceRefreshToken, par
122
191
  if (params.entityType) {
123
192
  url.searchParams.set("entityType", params.entityType);
124
193
  }
194
+ if (params.actorType) {
195
+ url.searchParams.set("actorType", params.actorType);
196
+ }
125
197
  if (params.limit) {
126
198
  url.searchParams.set("limit", String(params.limit));
127
199
  }
@@ -218,7 +290,8 @@ function SimpleAvatar({
218
290
  display: "flex",
219
291
  alignItems: "center",
220
292
  justifyContent: "center",
221
- flexShrink: 0
293
+ flexShrink: 0,
294
+ color: "#6b7280"
222
295
  },
223
296
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { className: "" })
224
297
  }
@@ -250,13 +323,26 @@ function SimpleAvatar({
250
323
  }
251
324
  );
252
325
  }
253
- function applyClientSideFilters(events, searchQuery) {
326
+ function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
327
+ let filtered = events;
328
+ if (!showSystemAndServices) {
329
+ filtered = filtered.filter((event) => {
330
+ if (event.actorType) {
331
+ return event.actorType === "user";
332
+ }
333
+ if (event.actorId && event.actorId.includes(":")) {
334
+ const [type] = event.actorId.split(":");
335
+ return type !== "service" && type !== "system";
336
+ }
337
+ return true;
338
+ });
339
+ }
254
340
  if (!searchQuery.trim()) {
255
- return events;
341
+ return filtered;
256
342
  }
257
343
  const query = searchQuery.toLowerCase();
258
- return events.filter((event) => {
259
- return event.action.toLowerCase().includes(query) || event.entityType.toLowerCase().includes(query) || event.entityId.toLowerCase().includes(query) || event.actorId && event.actorId.toLowerCase().includes(query) || event.source && event.source.toLowerCase().includes(query);
344
+ return filtered.filter((event) => {
345
+ return event.action.toLowerCase().includes(query) || event.entityType.toLowerCase().includes(query) || event.entityId.toLowerCase().includes(query) || event.actorId && event.actorId.toLowerCase().includes(query) || event.source && event.source.toLowerCase().includes(query) || event.actorDisplay && event.actorDisplay.toLowerCase().includes(query);
260
346
  });
261
347
  }
262
348
  function Timeline({
@@ -268,6 +354,7 @@ function Timeline({
268
354
  emptyMessage = "No events yet.",
269
355
  showSearch = false,
270
356
  showFilters = false,
357
+ showSystemAndServices = false,
271
358
  getActorAvatar
272
359
  }) {
273
360
  const { apiBaseUrl, getToken, forceRefreshToken } = useArchiva();
@@ -286,6 +373,8 @@ function Timeline({
286
373
  entityId,
287
374
  actorId,
288
375
  entityType,
376
+ // Filter by actorType on API side
377
+ actorType: showSystemAndServices ? void 0 : "user",
289
378
  limit: initialLimit,
290
379
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
291
380
  };
@@ -304,16 +393,16 @@ function Timeline({
304
393
  setLoading(false);
305
394
  }
306
395
  },
307
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor]
396
+ [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
308
397
  );
309
398
  React2.useEffect(() => {
310
399
  setCursor(void 0);
311
400
  setAllEvents([]);
312
401
  void load({ reset: true });
313
- }, [entityId, actorId, entityType]);
402
+ }, [entityId, actorId, entityType, showSystemAndServices]);
314
403
  const filteredEvents = React2.useMemo(() => {
315
- return applyClientSideFilters(allEvents, searchQuery);
316
- }, [allEvents, searchQuery]);
404
+ return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
405
+ }, [allEvents, searchQuery, showSystemAndServices]);
317
406
  const timelineItems = React2.useMemo(() => {
318
407
  return filteredEvents.slice(0, 10).map(
319
408
  (event) => eventToTimelineItem(event, getActorAvatar)
@@ -382,13 +471,13 @@ function Timeline({
382
471
  ) }),
383
472
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: useActivityLayout ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "start", justifyContent: "space-between", gap: "1rem" }, children: [
384
473
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
385
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem" }, children: [
474
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", flexWrap: "wrap" }, children: [
386
475
  item.actorDisplay ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: item.actorDisplay }) : item.userName ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: item.userName }) : null,
387
476
  item.userHandle && item.userHandle !== "unknown" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: [
388
477
  "@",
389
478
  item.userHandle.toLowerCase()
390
479
  ] })
391
- ] }),
480
+ ] }) }),
392
481
  item.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { fontSize: "0.875rem", color: "#111827", margin: "0.5rem 0 0 0" }, children: item.description })
393
482
  ] }),
394
483
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flexShrink: 0, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.25rem" }, children: [
@@ -33,11 +33,73 @@ function formatTimestamp(timestamp, format = "default") {
33
33
  minute: "2-digit"
34
34
  });
35
35
  }
36
+ var MonitorCogIcon = ({ className }) => /* @__PURE__ */ jsxs(
37
+ "svg",
38
+ {
39
+ className,
40
+ width: "16",
41
+ height: "16",
42
+ viewBox: "0 0 24 24",
43
+ fill: "none",
44
+ stroke: "currentColor",
45
+ strokeWidth: "2",
46
+ strokeLinecap: "round",
47
+ strokeLinejoin: "round",
48
+ children: [
49
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
50
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "21", x2: "16", y2: "21" }),
51
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "17", x2: "12", y2: "21" }),
52
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "8", r: "2" })
53
+ ]
54
+ }
55
+ );
56
+ var CloudCogIcon = ({ className }) => /* @__PURE__ */ jsxs(
57
+ "svg",
58
+ {
59
+ className,
60
+ width: "16",
61
+ height: "16",
62
+ viewBox: "0 0 24 24",
63
+ fill: "none",
64
+ stroke: "currentColor",
65
+ strokeWidth: "2",
66
+ strokeLinecap: "round",
67
+ strokeLinejoin: "round",
68
+ children: [
69
+ /* @__PURE__ */ jsx("path", { d: "M18 20a2 2 0 0 0 2-2V8.5a2.5 2.5 0 0 0-2.5-2.5c-1.003 0-1.9.446-2.5 1.153" }),
70
+ /* @__PURE__ */ jsx("path", { d: "M13 18a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2" }),
71
+ /* @__PURE__ */ jsx("circle", { cx: "18", cy: "8", r: "2" })
72
+ ]
73
+ }
74
+ );
36
75
  function eventToTimelineItem(event, getActorAvatar) {
37
76
  const actorId = event.actorId;
38
- const userName = actorId ? actorId.includes(":") ? actorId.split(":")[1] || actorId : actorId : "Unknown Actor";
39
- const userHandle = actorId || "unknown";
40
- const initials = userName.charAt(0).toUpperCase();
77
+ const actorType = event.actorType || "user";
78
+ let actorIdPart = actorId || "";
79
+ if (actorId && actorId.includes(":")) {
80
+ const [, ...rest] = actorId.split(":");
81
+ actorIdPart = rest.join(":") || actorId;
82
+ }
83
+ let actorDisplay = event.actorDisplay || null;
84
+ if (!actorDisplay && actorId) {
85
+ if (actorId.includes(":")) {
86
+ const [type, ...rest] = actorId.split(":");
87
+ const idPart = rest.join(":");
88
+ if (type === "service" || type === "system") {
89
+ actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${idPart.charAt(0).toUpperCase() + idPart.slice(1)}`;
90
+ } else {
91
+ actorDisplay = idPart.charAt(0).toUpperCase() + idPart.slice(1);
92
+ }
93
+ } else {
94
+ actorDisplay = actorId.charAt(0).toUpperCase() + actorId.slice(1);
95
+ }
96
+ }
97
+ if (!actorDisplay) {
98
+ actorDisplay = "Unknown Actor";
99
+ }
100
+ const userName = actorDisplay;
101
+ const userHandle = actorIdPart || "unknown";
102
+ const initials = userHandle.charAt(0).toUpperCase();
41
103
  const actorAvatarOrIcon = actorId && getActorAvatar ? getActorAvatar(actorId) : void 0;
42
104
  let actorAvatar = void 0;
43
105
  let actorIcon = void 0;
@@ -47,7 +109,13 @@ function eventToTimelineItem(event, getActorAvatar) {
47
109
  actorIcon = actorAvatarOrIcon;
48
110
  }
49
111
  if (!actorAvatar && !actorIcon && actorId) {
50
- actorAvatar = "https://www.gravatar.com/avatar?d=mp";
112
+ if (actorType === "user") {
113
+ actorAvatar = "https://www.gravatar.com/avatar?d=mp";
114
+ } else if (actorType === "system") {
115
+ actorIcon = MonitorCogIcon;
116
+ } else if (actorType === "service") {
117
+ actorIcon = CloudCogIcon;
118
+ }
51
119
  }
52
120
  const action = event.action.charAt(0).toUpperCase() + event.action.slice(1);
53
121
  return {
@@ -56,8 +124,9 @@ function eventToTimelineItem(event, getActorAvatar) {
56
124
  // Not used in activity layout
57
125
  description: action,
58
126
  timestamp: event.receivedAt,
59
- userName: userName.charAt(0).toUpperCase() + userName.slice(1),
127
+ userName,
60
128
  userHandle,
129
+ actorDisplay,
61
130
  entityType: event.entityType.toLowerCase(),
62
131
  avatar: actorAvatar,
63
132
  icon: actorIcon,
@@ -77,6 +146,9 @@ async function fetchEventsWithRetry(apiBaseUrl, getToken, forceRefreshToken, par
77
146
  if (params.entityType) {
78
147
  url.searchParams.set("entityType", params.entityType);
79
148
  }
149
+ if (params.actorType) {
150
+ url.searchParams.set("actorType", params.actorType);
151
+ }
80
152
  if (params.limit) {
81
153
  url.searchParams.set("limit", String(params.limit));
82
154
  }
@@ -173,7 +245,8 @@ function SimpleAvatar({
173
245
  display: "flex",
174
246
  alignItems: "center",
175
247
  justifyContent: "center",
176
- flexShrink: 0
248
+ flexShrink: 0,
249
+ color: "#6b7280"
177
250
  },
178
251
  children: /* @__PURE__ */ jsx(Icon, { className: "" })
179
252
  }
@@ -205,13 +278,26 @@ function SimpleAvatar({
205
278
  }
206
279
  );
207
280
  }
208
- function applyClientSideFilters(events, searchQuery) {
281
+ function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
282
+ let filtered = events;
283
+ if (!showSystemAndServices) {
284
+ filtered = filtered.filter((event) => {
285
+ if (event.actorType) {
286
+ return event.actorType === "user";
287
+ }
288
+ if (event.actorId && event.actorId.includes(":")) {
289
+ const [type] = event.actorId.split(":");
290
+ return type !== "service" && type !== "system";
291
+ }
292
+ return true;
293
+ });
294
+ }
209
295
  if (!searchQuery.trim()) {
210
- return events;
296
+ return filtered;
211
297
  }
212
298
  const query = searchQuery.toLowerCase();
213
- return events.filter((event) => {
214
- return event.action.toLowerCase().includes(query) || event.entityType.toLowerCase().includes(query) || event.entityId.toLowerCase().includes(query) || event.actorId && event.actorId.toLowerCase().includes(query) || event.source && event.source.toLowerCase().includes(query);
299
+ return filtered.filter((event) => {
300
+ return event.action.toLowerCase().includes(query) || event.entityType.toLowerCase().includes(query) || event.entityId.toLowerCase().includes(query) || event.actorId && event.actorId.toLowerCase().includes(query) || event.source && event.source.toLowerCase().includes(query) || event.actorDisplay && event.actorDisplay.toLowerCase().includes(query);
215
301
  });
216
302
  }
217
303
  function Timeline({
@@ -223,6 +309,7 @@ function Timeline({
223
309
  emptyMessage = "No events yet.",
224
310
  showSearch = false,
225
311
  showFilters = false,
312
+ showSystemAndServices = false,
226
313
  getActorAvatar
227
314
  }) {
228
315
  const { apiBaseUrl, getToken, forceRefreshToken } = useArchiva();
@@ -241,6 +328,8 @@ function Timeline({
241
328
  entityId,
242
329
  actorId,
243
330
  entityType,
331
+ // Filter by actorType on API side
332
+ actorType: showSystemAndServices ? void 0 : "user",
244
333
  limit: initialLimit,
245
334
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
246
335
  };
@@ -259,16 +348,16 @@ function Timeline({
259
348
  setLoading(false);
260
349
  }
261
350
  },
262
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor]
351
+ [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
263
352
  );
264
353
  React.useEffect(() => {
265
354
  setCursor(void 0);
266
355
  setAllEvents([]);
267
356
  void load({ reset: true });
268
- }, [entityId, actorId, entityType]);
357
+ }, [entityId, actorId, entityType, showSystemAndServices]);
269
358
  const filteredEvents = React.useMemo(() => {
270
- return applyClientSideFilters(allEvents, searchQuery);
271
- }, [allEvents, searchQuery]);
359
+ return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
360
+ }, [allEvents, searchQuery, showSystemAndServices]);
272
361
  const timelineItems = React.useMemo(() => {
273
362
  return filteredEvents.slice(0, 10).map(
274
363
  (event) => eventToTimelineItem(event, getActorAvatar)
@@ -337,13 +426,13 @@ function Timeline({
337
426
  ) }),
338
427
  /* @__PURE__ */ jsx("div", { style: { flex: 1, minWidth: 0 }, children: useActivityLayout ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "start", justifyContent: "space-between", gap: "1rem" }, children: [
339
428
  /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
340
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem" }, children: [
429
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", flexWrap: "wrap" }, children: [
341
430
  item.actorDisplay ? /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: item.actorDisplay }) : item.userName ? /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: "0.875rem" }, children: item.userName }) : null,
342
431
  item.userHandle && item.userHandle !== "unknown" && /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: [
343
432
  "@",
344
433
  item.userHandle.toLowerCase()
345
434
  ] })
346
- ] }),
435
+ ] }) }),
347
436
  item.description && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: "#111827", margin: "0.5rem 0 0 0" }, children: item.description })
348
437
  ] }),
349
438
  /* @__PURE__ */ jsxs("div", { style: { flexShrink: 0, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.25rem" }, children: [
@@ -1,2 +1,2 @@
1
- export { f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from '../index-D6XeJsvH.mjs';
1
+ export { f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from '../index-BJ8aJsbs.mjs';
2
2
  import 'next/server';
@@ -1,2 +1,2 @@
1
- export { f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from '../index-D6XeJsvH.js';
1
+ export { f as AuditEventListItem, C as CreateEventInput, e as CreateEventOptions, F as FrontendTokenResponse, G as GET, L as LoadEventsParams, P as PageResult, c as createEvent, a as createEvents, b as createFrontendTokenGET, d as createFrontendTokenRoute, l as loadEvents } from '../index-BJ8aJsbs.js';
2
2
  import 'next/server';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archiva/archiva-nextjs",
3
- "version": "0.2.05",
3
+ "version": "0.2.07",
4
4
  "description": "Archiva Next.js SDK - Server Actions and Timeline Component",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",