@archiva/archiva-nextjs 0.2.6 → 0.2.8

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,275 @@
1
+ // src/types.ts
2
+ var ArchivaError = class extends Error {
3
+ constructor(params) {
4
+ super(params.message);
5
+ this.statusCode = params.statusCode;
6
+ this.code = params.code;
7
+ this.retryAfterSeconds = params.retryAfterSeconds;
8
+ this.details = params.details;
9
+ }
10
+ };
11
+
12
+ // src/client.ts
13
+ var DEFAULT_BASE_URL = "https://api.archiva.app";
14
+ function buildHeaders(apiKey, overrides) {
15
+ const headers = new Headers(overrides);
16
+ headers.set("X-Project-Key", apiKey);
17
+ return headers;
18
+ }
19
+ async function parseError(response) {
20
+ const retryAfterHeader = response.headers.get("Retry-After");
21
+ const retryAfterSeconds = retryAfterHeader ? Number(retryAfterHeader) : void 0;
22
+ let payload = void 0;
23
+ try {
24
+ payload = await response.json();
25
+ } catch {
26
+ payload = void 0;
27
+ }
28
+ const errorMessage = typeof payload === "object" && payload !== null && "error" in payload ? String(payload.error) : response.statusText;
29
+ let code = "HTTP_ERROR";
30
+ if (response.status === 401) {
31
+ code = "UNAUTHORIZED";
32
+ } else if (response.status === 403) {
33
+ code = "FORBIDDEN";
34
+ } else if (response.status === 413) {
35
+ code = "PAYLOAD_TOO_LARGE";
36
+ } else if (response.status === 429) {
37
+ code = "RATE_LIMITED";
38
+ } else if (response.status === 409) {
39
+ code = "IDEMPOTENCY_CONFLICT";
40
+ }
41
+ throw new ArchivaError({
42
+ statusCode: response.status,
43
+ code,
44
+ message: errorMessage,
45
+ retryAfterSeconds,
46
+ details: payload
47
+ });
48
+ }
49
+ function createRequestId() {
50
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
51
+ return crypto.randomUUID();
52
+ }
53
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
54
+ }
55
+ function createIdempotencyKey() {
56
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
57
+ return `idem_${crypto.randomUUID()}`;
58
+ }
59
+ return `idem_${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
60
+ }
61
+ async function loadEvents(apiKey, params, baseUrl = DEFAULT_BASE_URL) {
62
+ const url = new URL(`${baseUrl}/api/events`);
63
+ if (params.entityId) {
64
+ url.searchParams.set("entityId", params.entityId);
65
+ }
66
+ if (params.actorId) {
67
+ url.searchParams.set("actorId", params.actorId);
68
+ }
69
+ if (params.entityType) {
70
+ url.searchParams.set("entityType", params.entityType);
71
+ }
72
+ if (params.actorType) {
73
+ url.searchParams.set("actorType", params.actorType);
74
+ }
75
+ if (params.limit) {
76
+ url.searchParams.set("limit", String(params.limit));
77
+ }
78
+ if (params.cursor) {
79
+ url.searchParams.set("cursor", params.cursor);
80
+ }
81
+ const response = await fetch(url.toString(), {
82
+ headers: buildHeaders(apiKey)
83
+ });
84
+ if (!response.ok) {
85
+ await parseError(response);
86
+ }
87
+ const payload = await response.json();
88
+ if (!payload || typeof payload !== "object" || !Array.isArray(payload.items)) {
89
+ throw new ArchivaError({
90
+ statusCode: response.status,
91
+ code: "HTTP_ERROR",
92
+ message: "Invalid response format",
93
+ details: payload
94
+ });
95
+ }
96
+ const items = payload.items.map((item) => {
97
+ if (typeof item !== "object" || item === null) {
98
+ throw new ArchivaError({
99
+ statusCode: response.status,
100
+ code: "HTTP_ERROR",
101
+ message: "Invalid item format in response",
102
+ details: item
103
+ });
104
+ }
105
+ const event = item;
106
+ return {
107
+ id: String(event.id ?? ""),
108
+ receivedAt: String(event.receivedAt ?? ""),
109
+ action: String(event.action ?? ""),
110
+ entityType: String(event.entityType ?? ""),
111
+ entityId: String(event.entityId ?? ""),
112
+ actorId: event.actorId !== null && event.actorId !== void 0 ? String(event.actorId) : null,
113
+ actorType: event.actorType && (event.actorType === "user" || event.actorType === "service" || event.actorType === "system") ? event.actorType : void 0,
114
+ actorDisplay: event.actorDisplay !== null && event.actorDisplay !== void 0 ? String(event.actorDisplay) : void 0,
115
+ source: event.source !== null && event.source !== void 0 ? String(event.source) : null
116
+ };
117
+ });
118
+ return {
119
+ items,
120
+ nextCursor: typeof payload.nextCursor === "string" ? payload.nextCursor : void 0
121
+ };
122
+ }
123
+ async function createEvent(apiKey, event, options, baseUrl = DEFAULT_BASE_URL) {
124
+ const idempotencyKey = options?.idempotencyKey ?? createIdempotencyKey();
125
+ const requestId = options?.requestId ?? createRequestId();
126
+ const headers = buildHeaders(apiKey, {
127
+ "Content-Type": "application/json",
128
+ "Idempotency-Key": idempotencyKey,
129
+ "X-Request-Id": requestId
130
+ });
131
+ const response = await fetch(`${baseUrl}/api/ingest/event`, {
132
+ method: "POST",
133
+ headers,
134
+ body: JSON.stringify(event)
135
+ });
136
+ if (!response.ok) {
137
+ await parseError(response);
138
+ }
139
+ const payload = await response.json();
140
+ if (!payload || typeof payload !== "object" || typeof payload.eventId !== "string") {
141
+ throw new ArchivaError({
142
+ statusCode: response.status,
143
+ code: "HTTP_ERROR",
144
+ message: "Invalid response format",
145
+ details: payload
146
+ });
147
+ }
148
+ return {
149
+ eventId: payload.eventId,
150
+ replayed: response.status === 200
151
+ };
152
+ }
153
+ async function createEvents(apiKey, events, options, baseUrl = DEFAULT_BASE_URL) {
154
+ const results = await Promise.all(
155
+ events.map(
156
+ (event, index) => createEvent(
157
+ apiKey,
158
+ event,
159
+ {
160
+ ...options,
161
+ idempotencyKey: options?.idempotencyKey ? `${options.idempotencyKey}_${index}` : void 0
162
+ },
163
+ baseUrl
164
+ )
165
+ )
166
+ );
167
+ return {
168
+ eventIds: results.map((r) => r.eventId)
169
+ };
170
+ }
171
+
172
+ // src/actions.ts
173
+ var DEFAULT_BASE_URL2 = "https://api.archiva.app";
174
+ function getApiKey(apiKey) {
175
+ const resolvedKey = apiKey || process.env.ARCHIVA_SECRET_KEY;
176
+ if (!resolvedKey) {
177
+ throw new Error("ARCHIVA_SECRET_KEY environment variable is required, or provide apiKey prop to ArchivaProvider");
178
+ }
179
+ return resolvedKey;
180
+ }
181
+ async function loadEvents2(params, apiKey) {
182
+ const resolvedApiKey = getApiKey(apiKey);
183
+ return loadEvents(resolvedApiKey, params, DEFAULT_BASE_URL2);
184
+ }
185
+ async function createEvent2(event, options, apiKey) {
186
+ const resolvedApiKey = getApiKey(apiKey);
187
+ return createEvent(resolvedApiKey, event, options, DEFAULT_BASE_URL2);
188
+ }
189
+ async function createEvents2(events, options, apiKey) {
190
+ const resolvedApiKey = getApiKey(apiKey);
191
+ return createEvents(resolvedApiKey, events, options, DEFAULT_BASE_URL2);
192
+ }
193
+
194
+ // src/server/frontendTokens.ts
195
+ import "server-only";
196
+ var DEFAULT_API_BASE_URL = "https://api.archiva.app";
197
+ async function createFrontendTokenGET(projectId, apiBaseUrl = DEFAULT_API_BASE_URL) {
198
+ const secretKey = process.env.ARCHIVA_SECRET_KEY;
199
+ if (!secretKey || secretKey.trim().length === 0) {
200
+ const exists = process.env.ARCHIVA_SECRET_KEY !== void 0;
201
+ throw new Error(
202
+ `ARCHIVA_SECRET_KEY environment variable is ${exists ? "empty" : "not configured"}. Please set it in your .env.local file (or .env) with a valid value and restart your Next.js dev server. The variable must be in the project root directory and must not be empty.`
203
+ );
204
+ }
205
+ const keyPrefix = secretKey.substring(0, 10);
206
+ console.log("[Archiva] Using API key prefix:", keyPrefix + "...", "Length:", secretKey.length);
207
+ const url = new URL(`${apiBaseUrl}/api/v1/frontend-tokens`);
208
+ const response = await fetch(url.toString(), {
209
+ method: "POST",
210
+ headers: {
211
+ "Content-Type": "application/json",
212
+ "Authorization": `Bearer ${secretKey}`
213
+ },
214
+ body: JSON.stringify({
215
+ scopes: ["timeline:read"],
216
+ ...projectId && { projectId }
217
+ })
218
+ });
219
+ if (!response.ok) {
220
+ const error = await response.json().catch(() => ({ error: response.statusText }));
221
+ const errorMessage = error.error || `Failed to fetch frontend token: ${response.status}`;
222
+ if (errorMessage.includes("ARCHIVA_SECRET_KEY") || response.status === 401 || response.status === 403) {
223
+ throw new Error(
224
+ `Archiva API authentication failed. Check that your ARCHIVA_SECRET_KEY is valid and has the correct permissions. API error: ${errorMessage}`
225
+ );
226
+ }
227
+ throw new Error(errorMessage);
228
+ }
229
+ const data = await response.json();
230
+ if (!data.token || typeof data.expiresAt !== "number") {
231
+ throw new Error("Invalid token response format");
232
+ }
233
+ return {
234
+ token: data.token,
235
+ expiresAt: data.expiresAt
236
+ };
237
+ }
238
+
239
+ // src/server/handlers/createFrontendTokenRoute.ts
240
+ import "server-only";
241
+ import { NextResponse } from "next/server";
242
+ var DEFAULT_API_BASE_URL2 = "https://api.archiva.app";
243
+ function createFrontendTokenRoute(options) {
244
+ return async function GET2(request) {
245
+ try {
246
+ const searchParams = request.nextUrl.searchParams;
247
+ const projectId = searchParams.get("projectId") || void 0;
248
+ const apiBaseUrl = options?.apiBaseUrl || DEFAULT_API_BASE_URL2;
249
+ const tokenResponse = await createFrontendTokenGET(projectId, apiBaseUrl);
250
+ return NextResponse.json(tokenResponse);
251
+ } catch (error) {
252
+ console.error("Error fetching frontend token:", error);
253
+ const message = error instanceof Error ? error.message : "Internal server error";
254
+ const statusCode = message.includes("ARCHIVA_SECRET_KEY") ? 500 : 500;
255
+ return NextResponse.json(
256
+ { error: message },
257
+ { status: statusCode }
258
+ );
259
+ }
260
+ };
261
+ }
262
+ async function GET(request) {
263
+ const handler = createFrontendTokenRoute();
264
+ return handler(request);
265
+ }
266
+
267
+ export {
268
+ ArchivaError,
269
+ loadEvents2 as loadEvents,
270
+ createEvent2 as createEvent,
271
+ createEvents2 as createEvents,
272
+ createFrontendTokenGET,
273
+ createFrontendTokenRoute,
274
+ GET
275
+ };
@@ -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';
package/dist/index.js CHANGED
@@ -299,6 +299,9 @@ async function loadEvents(apiKey, params, baseUrl = DEFAULT_BASE_URL) {
299
299
  if (params.entityType) {
300
300
  url.searchParams.set("entityType", params.entityType);
301
301
  }
302
+ if (params.actorType) {
303
+ url.searchParams.set("actorType", params.actorType);
304
+ }
302
305
  if (params.limit) {
303
306
  url.searchParams.set("limit", String(params.limit));
304
307
  }
@@ -320,8 +323,30 @@ async function loadEvents(apiKey, params, baseUrl = DEFAULT_BASE_URL) {
320
323
  details: payload
321
324
  });
322
325
  }
326
+ const items = payload.items.map((item) => {
327
+ if (typeof item !== "object" || item === null) {
328
+ throw new ArchivaError({
329
+ statusCode: response.status,
330
+ code: "HTTP_ERROR",
331
+ message: "Invalid item format in response",
332
+ details: item
333
+ });
334
+ }
335
+ const event = item;
336
+ return {
337
+ id: String(event.id ?? ""),
338
+ receivedAt: String(event.receivedAt ?? ""),
339
+ action: String(event.action ?? ""),
340
+ entityType: String(event.entityType ?? ""),
341
+ entityId: String(event.entityId ?? ""),
342
+ actorId: event.actorId !== null && event.actorId !== void 0 ? String(event.actorId) : null,
343
+ actorType: event.actorType && (event.actorType === "user" || event.actorType === "service" || event.actorType === "system") ? event.actorType : void 0,
344
+ actorDisplay: event.actorDisplay !== null && event.actorDisplay !== void 0 ? String(event.actorDisplay) : void 0,
345
+ source: event.source !== null && event.source !== void 0 ? String(event.source) : null
346
+ };
347
+ });
323
348
  return {
324
- items: payload.items,
349
+ items,
325
350
  nextCursor: typeof payload.nextCursor === "string" ? payload.nextCursor : void 0
326
351
  };
327
352
  }
package/dist/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  createFrontendTokenGET,
10
10
  createFrontendTokenRoute,
11
11
  loadEvents
12
- } from "./chunk-WW7LXHTM.mjs";
12
+ } from "./chunk-SQG3CD5M.mjs";
13
13
  export {
14
14
  ArchivaError,
15
15
  ArchivaProvider,
@@ -119,30 +119,32 @@ var CloudCogIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime2.js
119
119
  );
120
120
  function eventToTimelineItem(event, getActorAvatar) {
121
121
  const actorId = event.actorId;
122
- let actorType = "user";
122
+ const actorType = event.actorType || "user";
123
123
  let actorIdPart = actorId || "";
124
- let actorDisplay = "";
125
- if (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) {
126
130
  if (actorId.includes(":")) {
127
131
  const [type, ...rest] = actorId.split(":");
128
- actorType = type === "service" || type === "system" ? type : "user";
129
- actorIdPart = rest.join(":");
132
+ const idPart = rest.join(":");
130
133
  if (type === "service" || type === "system") {
131
- actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${actorIdPart.charAt(0).toUpperCase() + actorIdPart.slice(1)}`;
134
+ actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${idPart.charAt(0).toUpperCase() + idPart.slice(1)}`;
132
135
  } else {
133
- actorDisplay = actorIdPart.charAt(0).toUpperCase() + actorIdPart.slice(1);
136
+ actorDisplay = idPart.charAt(0).toUpperCase() + idPart.slice(1);
134
137
  }
135
138
  } else {
136
- actorIdPart = actorId;
137
139
  actorDisplay = actorId.charAt(0).toUpperCase() + actorId.slice(1);
138
140
  }
139
- } else {
141
+ }
142
+ if (!actorDisplay) {
140
143
  actorDisplay = "Unknown Actor";
141
- actorIdPart = "unknown";
142
144
  }
143
145
  const userName = actorDisplay;
144
- const userHandle = actorIdPart;
145
- const initials = actorIdPart.charAt(0).toUpperCase();
146
+ const userHandle = actorIdPart || "unknown";
147
+ const initials = userHandle.charAt(0).toUpperCase();
146
148
  const actorAvatarOrIcon = actorId && getActorAvatar ? getActorAvatar(actorId) : void 0;
147
149
  let actorAvatar = void 0;
148
150
  let actorIcon = void 0;
@@ -189,6 +191,9 @@ async function fetchEventsWithRetry(apiBaseUrl, getToken, forceRefreshToken, par
189
191
  if (params.entityType) {
190
192
  url.searchParams.set("entityType", params.entityType);
191
193
  }
194
+ if (params.actorType) {
195
+ url.searchParams.set("actorType", params.actorType);
196
+ }
192
197
  if (params.limit) {
193
198
  url.searchParams.set("limit", String(params.limit));
194
199
  }
@@ -318,22 +323,18 @@ function SimpleAvatar({
318
323
  }
319
324
  );
320
325
  }
321
- function getActorType(actorId) {
322
- if (!actorId) return "user";
323
- if (actorId.includes(":")) {
324
- const [type] = actorId.split(":");
325
- if (type === "service" || type === "system") {
326
- return type;
327
- }
328
- }
329
- return "user";
330
- }
331
326
  function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
332
327
  let filtered = events;
333
328
  if (!showSystemAndServices) {
334
329
  filtered = filtered.filter((event) => {
335
- const actorType = getActorType(event.actorId);
336
- return actorType === "user";
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;
337
338
  });
338
339
  }
339
340
  if (!searchQuery.trim()) {
@@ -341,7 +342,7 @@ function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
341
342
  }
342
343
  const query = searchQuery.toLowerCase();
343
344
  return filtered.filter((event) => {
344
- 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);
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);
345
346
  });
346
347
  }
347
348
  function Timeline({
@@ -372,6 +373,8 @@ function Timeline({
372
373
  entityId,
373
374
  actorId,
374
375
  entityType,
376
+ // Filter by actorType on API side
377
+ actorType: showSystemAndServices ? void 0 : "user",
375
378
  limit: initialLimit,
376
379
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
377
380
  };
@@ -390,13 +393,13 @@ function Timeline({
390
393
  setLoading(false);
391
394
  }
392
395
  },
393
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor]
396
+ [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
394
397
  );
395
398
  React2.useEffect(() => {
396
399
  setCursor(void 0);
397
400
  setAllEvents([]);
398
401
  void load({ reset: true });
399
- }, [entityId, actorId, entityType]);
402
+ }, [entityId, actorId, entityType, showSystemAndServices]);
400
403
  const filteredEvents = React2.useMemo(() => {
401
404
  return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
402
405
  }, [allEvents, searchQuery, showSystemAndServices]);
@@ -74,30 +74,32 @@ var CloudCogIcon = ({ className }) => /* @__PURE__ */ jsxs(
74
74
  );
75
75
  function eventToTimelineItem(event, getActorAvatar) {
76
76
  const actorId = event.actorId;
77
- let actorType = "user";
77
+ const actorType = event.actorType || "user";
78
78
  let actorIdPart = actorId || "";
79
- let actorDisplay = "";
80
- if (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) {
81
85
  if (actorId.includes(":")) {
82
86
  const [type, ...rest] = actorId.split(":");
83
- actorType = type === "service" || type === "system" ? type : "user";
84
- actorIdPart = rest.join(":");
87
+ const idPart = rest.join(":");
85
88
  if (type === "service" || type === "system") {
86
- actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${actorIdPart.charAt(0).toUpperCase() + actorIdPart.slice(1)}`;
89
+ actorDisplay = `${type.charAt(0).toUpperCase() + type.slice(1)} ${idPart.charAt(0).toUpperCase() + idPart.slice(1)}`;
87
90
  } else {
88
- actorDisplay = actorIdPart.charAt(0).toUpperCase() + actorIdPart.slice(1);
91
+ actorDisplay = idPart.charAt(0).toUpperCase() + idPart.slice(1);
89
92
  }
90
93
  } else {
91
- actorIdPart = actorId;
92
94
  actorDisplay = actorId.charAt(0).toUpperCase() + actorId.slice(1);
93
95
  }
94
- } else {
96
+ }
97
+ if (!actorDisplay) {
95
98
  actorDisplay = "Unknown Actor";
96
- actorIdPart = "unknown";
97
99
  }
98
100
  const userName = actorDisplay;
99
- const userHandle = actorIdPart;
100
- const initials = actorIdPart.charAt(0).toUpperCase();
101
+ const userHandle = actorIdPart || "unknown";
102
+ const initials = userHandle.charAt(0).toUpperCase();
101
103
  const actorAvatarOrIcon = actorId && getActorAvatar ? getActorAvatar(actorId) : void 0;
102
104
  let actorAvatar = void 0;
103
105
  let actorIcon = void 0;
@@ -144,6 +146,9 @@ async function fetchEventsWithRetry(apiBaseUrl, getToken, forceRefreshToken, par
144
146
  if (params.entityType) {
145
147
  url.searchParams.set("entityType", params.entityType);
146
148
  }
149
+ if (params.actorType) {
150
+ url.searchParams.set("actorType", params.actorType);
151
+ }
147
152
  if (params.limit) {
148
153
  url.searchParams.set("limit", String(params.limit));
149
154
  }
@@ -273,22 +278,18 @@ function SimpleAvatar({
273
278
  }
274
279
  );
275
280
  }
276
- function getActorType(actorId) {
277
- if (!actorId) return "user";
278
- if (actorId.includes(":")) {
279
- const [type] = actorId.split(":");
280
- if (type === "service" || type === "system") {
281
- return type;
282
- }
283
- }
284
- return "user";
285
- }
286
281
  function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
287
282
  let filtered = events;
288
283
  if (!showSystemAndServices) {
289
284
  filtered = filtered.filter((event) => {
290
- const actorType = getActorType(event.actorId);
291
- return actorType === "user";
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;
292
293
  });
293
294
  }
294
295
  if (!searchQuery.trim()) {
@@ -296,7 +297,7 @@ function applyClientSideFilters(events, searchQuery, showSystemAndServices) {
296
297
  }
297
298
  const query = searchQuery.toLowerCase();
298
299
  return filtered.filter((event) => {
299
- 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);
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);
300
301
  });
301
302
  }
302
303
  function Timeline({
@@ -327,6 +328,8 @@ function Timeline({
327
328
  entityId,
328
329
  actorId,
329
330
  entityType,
331
+ // Filter by actorType on API side
332
+ actorType: showSystemAndServices ? void 0 : "user",
330
333
  limit: initialLimit,
331
334
  cursor: options?.reset ? void 0 : options?.currentCursor ?? cursor
332
335
  };
@@ -345,13 +348,13 @@ function Timeline({
345
348
  setLoading(false);
346
349
  }
347
350
  },
348
- [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor]
351
+ [entityId, actorId, entityType, initialLimit, apiBaseUrl, getToken, forceRefreshToken, cursor, showSystemAndServices]
349
352
  );
350
353
  React.useEffect(() => {
351
354
  setCursor(void 0);
352
355
  setAllEvents([]);
353
356
  void load({ reset: true });
354
- }, [entityId, actorId, entityType]);
357
+ }, [entityId, actorId, entityType, showSystemAndServices]);
355
358
  const filteredEvents = React.useMemo(() => {
356
359
  return applyClientSideFilters(allEvents, searchQuery, showSystemAndServices);
357
360
  }, [allEvents, searchQuery, showSystemAndServices]);
@@ -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';
@@ -173,6 +173,9 @@ async function loadEvents(apiKey, params, baseUrl = DEFAULT_BASE_URL) {
173
173
  if (params.entityType) {
174
174
  url.searchParams.set("entityType", params.entityType);
175
175
  }
176
+ if (params.actorType) {
177
+ url.searchParams.set("actorType", params.actorType);
178
+ }
176
179
  if (params.limit) {
177
180
  url.searchParams.set("limit", String(params.limit));
178
181
  }
@@ -194,8 +197,30 @@ async function loadEvents(apiKey, params, baseUrl = DEFAULT_BASE_URL) {
194
197
  details: payload
195
198
  });
196
199
  }
200
+ const items = payload.items.map((item) => {
201
+ if (typeof item !== "object" || item === null) {
202
+ throw new ArchivaError({
203
+ statusCode: response.status,
204
+ code: "HTTP_ERROR",
205
+ message: "Invalid item format in response",
206
+ details: item
207
+ });
208
+ }
209
+ const event = item;
210
+ return {
211
+ id: String(event.id ?? ""),
212
+ receivedAt: String(event.receivedAt ?? ""),
213
+ action: String(event.action ?? ""),
214
+ entityType: String(event.entityType ?? ""),
215
+ entityId: String(event.entityId ?? ""),
216
+ actorId: event.actorId !== null && event.actorId !== void 0 ? String(event.actorId) : null,
217
+ actorType: event.actorType && (event.actorType === "user" || event.actorType === "service" || event.actorType === "system") ? event.actorType : void 0,
218
+ actorDisplay: event.actorDisplay !== null && event.actorDisplay !== void 0 ? String(event.actorDisplay) : void 0,
219
+ source: event.source !== null && event.source !== void 0 ? String(event.source) : null
220
+ };
221
+ });
197
222
  return {
198
- items: payload.items,
223
+ items,
199
224
  nextCursor: typeof payload.nextCursor === "string" ? payload.nextCursor : void 0
200
225
  };
201
226
  }
@@ -5,7 +5,7 @@ import {
5
5
  createFrontendTokenGET,
6
6
  createFrontendTokenRoute,
7
7
  loadEvents
8
- } from "../chunk-WW7LXHTM.mjs";
8
+ } from "../chunk-SQG3CD5M.mjs";
9
9
  export {
10
10
  GET,
11
11
  createEvent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archiva/archiva-nextjs",
3
- "version": "0.2.06",
3
+ "version": "0.2.08",
4
4
  "description": "Archiva Next.js SDK - Server Actions and Timeline Component",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",