@juice-js/audit 0.0.0-stage → 1.0.0-alpha.2

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,1123 @@
1
+ import { AUDIT_RANGES, AuditContextService, AuditLinkPipe, AuditLinks } from "./juice-js-audit.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, Pipe, computed, inject, input, isSignal } from "@angular/core";
4
+ import { rxResource } from "@angular/core/rxjs-interop";
5
+ import { Router, RouterLink } from "@angular/router";
6
+ import { DatePipe } from "@angular/common";
7
+ import * as i1 from "@angular/forms";
8
+ import { FormsModule } from "@angular/forms";
9
+ import * as i3 from "@angular/material/icon";
10
+ import { MatIconModule } from "@angular/material/icon";
11
+ import * as i2 from "@angular/material/select";
12
+ import { MatSelectModule } from "@angular/material/select";
13
+ function apiResource(options) {
14
+ const ref = rxResource(options);
15
+ const value = computed(() => ref.hasValue() ? ref.value() : void 0, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
16
+ return new Proxy(ref, { get(target, prop) {
17
+ if (prop === "value") return value;
18
+ const member = Reflect.get(target, prop, target);
19
+ return typeof member === "function" && !isSignal(member) ? member.bind(target) : member;
20
+ } });
21
+ }
22
+ function formatDuration(ms) {
23
+ if (ms === null || ms === void 0 || Number.isNaN(ms)) return "—";
24
+ if (ms < 1e3) return `${Math.round(ms)} ms`;
25
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(2)} s`;
26
+ const s = Math.round(ms / 1e3);
27
+ return `${Math.floor(s / 60)} m ${String(s % 60).padStart(2, "0")} s`;
28
+ }
29
+ function formatValue(value) {
30
+ if (value === void 0) return "";
31
+ if (value === null) return "null";
32
+ return typeof value === "string" ? value : JSON.stringify(value);
33
+ }
34
+ function formatKeys(keys) {
35
+ if (keys && typeof keys === "object" && !Array.isArray(keys)) return Object.entries(keys).map(([k, v]) => `${k}=${formatValue(v)}`).join(", ");
36
+ return formatValue(keys);
37
+ }
38
+ function prettyJson(text) {
39
+ if (!text) return "";
40
+ try {
41
+ return JSON.stringify(JSON.parse(text), null, 2);
42
+ } catch {
43
+ return text;
44
+ }
45
+ }
46
+ function statusClass(status) {
47
+ if (!status) return "ja-tone-none";
48
+ return status >= 500 ? "ja-tone-bad" : status >= 400 ? "ja-tone-warn" : "ja-tone-ok";
49
+ }
50
+ function actionClass(action) {
51
+ const a = action.toLowerCase();
52
+ return a.startsWith("insert") || a === "added" ? "ja-tone-ok" : a.startsWith("delete") ? "ja-tone-bad" : a.startsWith("modif") || a.startsWith("update") ? "ja-tone-info" : "ja-tone-none";
53
+ }
54
+ var DurationPipe = class DurationPipe {
55
+ transform(ms) {
56
+ return formatDuration(ms);
57
+ }
58
+ static ɵfac = i0.ɵɵngDeclareFactory({
59
+ minVersion: "12.0.0",
60
+ version: "22.2.4",
61
+ ngImport: i0,
62
+ type: DurationPipe,
63
+ deps: [],
64
+ target: i0.ɵɵFactoryTarget.Pipe
65
+ });
66
+ static ɵpipe = i0.ɵɵngDeclarePipe({
67
+ minVersion: "14.0.0",
68
+ version: "22.2.4",
69
+ ngImport: i0,
70
+ type: DurationPipe,
71
+ isStandalone: true,
72
+ name: "jaDuration"
73
+ });
74
+ };
75
+ i0.ɵɵngDeclareClassMetadata({
76
+ minVersion: "12.0.0",
77
+ version: "22.2.4",
78
+ ngImport: i0,
79
+ type: DurationPipe,
80
+ decorators: [{
81
+ type: Pipe,
82
+ args: [{ name: "jaDuration" }]
83
+ }]
84
+ });
85
+ var KeysPipe = class KeysPipe {
86
+ transform(keys) {
87
+ return formatKeys(keys);
88
+ }
89
+ static ɵfac = i0.ɵɵngDeclareFactory({
90
+ minVersion: "12.0.0",
91
+ version: "22.2.4",
92
+ ngImport: i0,
93
+ type: KeysPipe,
94
+ deps: [],
95
+ target: i0.ɵɵFactoryTarget.Pipe
96
+ });
97
+ static ɵpipe = i0.ɵɵngDeclarePipe({
98
+ minVersion: "14.0.0",
99
+ version: "22.2.4",
100
+ ngImport: i0,
101
+ type: KeysPipe,
102
+ isStandalone: true,
103
+ name: "jaKeys"
104
+ });
105
+ };
106
+ i0.ɵɵngDeclareClassMetadata({
107
+ minVersion: "12.0.0",
108
+ version: "22.2.4",
109
+ ngImport: i0,
110
+ type: KeysPipe,
111
+ decorators: [{
112
+ type: Pipe,
113
+ args: [{ name: "jaKeys" }]
114
+ }]
115
+ });
116
+ var PrettyJsonPipe = class PrettyJsonPipe {
117
+ transform(text) {
118
+ return prettyJson(text);
119
+ }
120
+ static ɵfac = i0.ɵɵngDeclareFactory({
121
+ minVersion: "12.0.0",
122
+ version: "22.2.4",
123
+ ngImport: i0,
124
+ type: PrettyJsonPipe,
125
+ deps: [],
126
+ target: i0.ɵɵFactoryTarget.Pipe
127
+ });
128
+ static ɵpipe = i0.ɵɵngDeclarePipe({
129
+ minVersion: "14.0.0",
130
+ version: "22.2.4",
131
+ ngImport: i0,
132
+ type: PrettyJsonPipe,
133
+ isStandalone: true,
134
+ name: "jaPretty"
135
+ });
136
+ };
137
+ i0.ɵɵngDeclareClassMetadata({
138
+ minVersion: "12.0.0",
139
+ version: "22.2.4",
140
+ ngImport: i0,
141
+ type: PrettyJsonPipe,
142
+ decorators: [{
143
+ type: Pipe,
144
+ args: [{ name: "jaPretty" }]
145
+ }]
146
+ });
147
+ function problemText(error) {
148
+ const e = error;
149
+ if (!e) return "";
150
+ if (e.status === 0) return "The audit API cannot be reached.";
151
+ if (e.status === 401 || e.status === 403) return "You are not allowed to read the audit records.";
152
+ return [e.error?.title, e.error?.detail].filter(Boolean).join(": ") || `Request failed (${e.status}).`;
153
+ }
154
+ var AuditContextBar = class AuditContextBar {
155
+ ctx = inject(AuditContextService);
156
+ router = inject(Router);
157
+ links = inject(AuditLinks);
158
+ showRange = input(true, ...ngDevMode ? [{ debugName: "showRange" }] : /* istanbul ignore next */ []);
159
+ ranges = Object.keys(AUDIT_RANGES);
160
+ trace = "";
161
+ lookup() {
162
+ const id = this.trace.trim();
163
+ if (id) this.router.navigate(this.links.to("traces", id), { queryParams: this.ctx.keep() });
164
+ }
165
+ static ɵfac = i0.ɵɵngDeclareFactory({
166
+ minVersion: "12.0.0",
167
+ version: "22.2.4",
168
+ ngImport: i0,
169
+ type: AuditContextBar,
170
+ deps: [],
171
+ target: i0.ɵɵFactoryTarget.Component
172
+ });
173
+ static ɵcmp = i0.ɵɵngDeclareComponent({
174
+ minVersion: "17.0.0",
175
+ version: "22.2.4",
176
+ type: AuditContextBar,
177
+ isStandalone: true,
178
+ selector: "ja-context-bar",
179
+ inputs: { showRange: {
180
+ classPropertyName: "showRange",
181
+ publicName: "showRange",
182
+ isSignal: true,
183
+ isRequired: false,
184
+ transformFunction: null
185
+ } },
186
+ ngImport: i0,
187
+ template: `
188
+ <div class="ja-context">
189
+ <ng-content />
190
+ <span class="ja-spacer"></span>
191
+ @if (showRange()) {
192
+ <mat-form-field subscriptSizing="dynamic" class="ja-field-sm">
193
+ <mat-label>Time range</mat-label>
194
+ <mat-select [value]="ctx.range()" (valueChange)="ctx.set({ range: $event })">
195
+ @for (r of ranges; track r) { <mat-option [value]="r">Last {{ r }}</mat-option> }
196
+ @if (ctx.range() === 'custom') { <mat-option value="custom" disabled>Custom window</mat-option> }
197
+ </mat-select>
198
+ </mat-form-field>
199
+ }
200
+ <form class="ja-lookup" (ngSubmit)="lookup()">
201
+ <mat-icon>search</mat-icon>
202
+ <input name="trace" [(ngModel)]="trace" placeholder="Trace ID" aria-label="Open the audit records of a trace">
203
+ </form>
204
+ </div>`,
205
+ isInline: true,
206
+ dependencies: [
207
+ {
208
+ kind: "ngmodule",
209
+ type: FormsModule
210
+ },
211
+ {
212
+ kind: "directive",
213
+ type: i1.ɵNgNoValidate,
214
+ selector: "form:not([ngNoForm]):not([ngNativeValidate])"
215
+ },
216
+ {
217
+ kind: "directive",
218
+ type: i1.DefaultValueAccessor,
219
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
220
+ },
221
+ {
222
+ kind: "directive",
223
+ type: i1.NgControlStatus,
224
+ selector: "[formControlName],[ngModel],[formControl]"
225
+ },
226
+ {
227
+ kind: "directive",
228
+ type: i1.NgControlStatusGroup,
229
+ selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]"
230
+ },
231
+ {
232
+ kind: "directive",
233
+ type: i1.NgModel,
234
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
235
+ inputs: [
236
+ "name",
237
+ "disabled",
238
+ "ngModel",
239
+ "ngModelOptions"
240
+ ],
241
+ outputs: ["ngModelChange"],
242
+ exportAs: ["ngModel"]
243
+ },
244
+ {
245
+ kind: "directive",
246
+ type: i1.NgForm,
247
+ selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]",
248
+ inputs: ["ngFormOptions"],
249
+ outputs: ["ngSubmit"],
250
+ exportAs: ["ngForm"]
251
+ },
252
+ {
253
+ kind: "ngmodule",
254
+ type: MatSelectModule
255
+ },
256
+ {
257
+ kind: "component",
258
+ type: i2.MatFormField,
259
+ selector: "mat-form-field",
260
+ inputs: [
261
+ "hideRequiredMarker",
262
+ "color",
263
+ "floatLabel",
264
+ "appearance",
265
+ "subscriptSizing",
266
+ "hintLabel"
267
+ ],
268
+ exportAs: ["matFormField"]
269
+ },
270
+ {
271
+ kind: "directive",
272
+ type: i2.MatLabel,
273
+ selector: "mat-label"
274
+ },
275
+ {
276
+ kind: "component",
277
+ type: i2.MatSelect,
278
+ selector: "mat-select",
279
+ inputs: [
280
+ "aria-describedby",
281
+ "panelClass",
282
+ "disabled",
283
+ "disableRipple",
284
+ "tabIndex",
285
+ "hideSingleSelectionIndicator",
286
+ "placeholder",
287
+ "required",
288
+ "multiple",
289
+ "disableOptionCentering",
290
+ "compareWith",
291
+ "value",
292
+ "aria-label",
293
+ "aria-labelledby",
294
+ "errorStateMatcher",
295
+ "typeaheadDebounceInterval",
296
+ "sortComparator",
297
+ "id",
298
+ "panelWidth",
299
+ "canSelectNullableOptions"
300
+ ],
301
+ outputs: [
302
+ "openedChange",
303
+ "opened",
304
+ "closed",
305
+ "selectionChange",
306
+ "valueChange"
307
+ ],
308
+ exportAs: ["matSelect"]
309
+ },
310
+ {
311
+ kind: "component",
312
+ type: i2.MatOption,
313
+ selector: "mat-option",
314
+ inputs: [
315
+ "value",
316
+ "id",
317
+ "disabled"
318
+ ],
319
+ outputs: ["onSelectionChange"],
320
+ exportAs: ["matOption"]
321
+ },
322
+ {
323
+ kind: "ngmodule",
324
+ type: MatIconModule
325
+ },
326
+ {
327
+ kind: "component",
328
+ type: i3.MatIcon,
329
+ selector: "mat-icon",
330
+ inputs: [
331
+ "color",
332
+ "inline",
333
+ "svgIcon",
334
+ "fontSet",
335
+ "fontIcon"
336
+ ],
337
+ exportAs: ["matIcon"]
338
+ }
339
+ ],
340
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
341
+ });
342
+ };
343
+ i0.ɵɵngDeclareClassMetadata({
344
+ minVersion: "12.0.0",
345
+ version: "22.2.4",
346
+ ngImport: i0,
347
+ type: AuditContextBar,
348
+ decorators: [{
349
+ type: Component,
350
+ args: [{
351
+ selector: "ja-context-bar",
352
+ changeDetection: ChangeDetectionStrategy.OnPush,
353
+ imports: [
354
+ FormsModule,
355
+ MatSelectModule,
356
+ MatIconModule
357
+ ],
358
+ template: `
359
+ <div class="ja-context">
360
+ <ng-content />
361
+ <span class="ja-spacer"></span>
362
+ @if (showRange()) {
363
+ <mat-form-field subscriptSizing="dynamic" class="ja-field-sm">
364
+ <mat-label>Time range</mat-label>
365
+ <mat-select [value]="ctx.range()" (valueChange)="ctx.set({ range: $event })">
366
+ @for (r of ranges; track r) { <mat-option [value]="r">Last {{ r }}</mat-option> }
367
+ @if (ctx.range() === 'custom') { <mat-option value="custom" disabled>Custom window</mat-option> }
368
+ </mat-select>
369
+ </mat-form-field>
370
+ }
371
+ <form class="ja-lookup" (ngSubmit)="lookup()">
372
+ <mat-icon>search</mat-icon>
373
+ <input name="trace" [(ngModel)]="trace" placeholder="Trace ID" aria-label="Open the audit records of a trace">
374
+ </form>
375
+ </div>`
376
+ }]
377
+ }],
378
+ propDecorators: { showRange: [{
379
+ type: i0.Input,
380
+ args: [{
381
+ isSignal: true,
382
+ alias: "showRange",
383
+ required: false
384
+ }]
385
+ }] }
386
+ });
387
+ var StatusBadge = class StatusBadge {
388
+ status = input(...ngDevMode ? [void 0, { debugName: "status" }] : /* istanbul ignore next */ []);
389
+ tone = computed(() => statusClass(this.status()), ...ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []);
390
+ static ɵfac = i0.ɵɵngDeclareFactory({
391
+ minVersion: "12.0.0",
392
+ version: "22.2.4",
393
+ ngImport: i0,
394
+ type: StatusBadge,
395
+ deps: [],
396
+ target: i0.ɵɵFactoryTarget.Component
397
+ });
398
+ static ɵcmp = i0.ɵɵngDeclareComponent({
399
+ minVersion: "17.1.0",
400
+ version: "22.2.4",
401
+ type: StatusBadge,
402
+ isStandalone: true,
403
+ selector: "ja-status",
404
+ inputs: { status: {
405
+ classPropertyName: "status",
406
+ publicName: "status",
407
+ isSignal: true,
408
+ isRequired: false,
409
+ transformFunction: null
410
+ } },
411
+ ngImport: i0,
412
+ template: `<span class="ja-badge" [class]="tone()">{{ status() || '—' }}</span>`,
413
+ isInline: true,
414
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
415
+ });
416
+ };
417
+ i0.ɵɵngDeclareClassMetadata({
418
+ minVersion: "12.0.0",
419
+ version: "22.2.4",
420
+ ngImport: i0,
421
+ type: StatusBadge,
422
+ decorators: [{
423
+ type: Component,
424
+ args: [{
425
+ selector: "ja-status",
426
+ changeDetection: ChangeDetectionStrategy.OnPush,
427
+ template: `<span class="ja-badge" [class]="tone()">{{ status() || '—' }}</span>`
428
+ }]
429
+ }],
430
+ propDecorators: { status: [{
431
+ type: i0.Input,
432
+ args: [{
433
+ isSignal: true,
434
+ alias: "status",
435
+ required: false
436
+ }]
437
+ }] }
438
+ });
439
+ var ActionBadge = class ActionBadge {
440
+ action = input.required(...ngDevMode ? [{ debugName: "action" }] : /* istanbul ignore next */ []);
441
+ tone = computed(() => actionClass(this.action()), ...ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []);
442
+ static ɵfac = i0.ɵɵngDeclareFactory({
443
+ minVersion: "12.0.0",
444
+ version: "22.2.4",
445
+ ngImport: i0,
446
+ type: ActionBadge,
447
+ deps: [],
448
+ target: i0.ɵɵFactoryTarget.Component
449
+ });
450
+ static ɵcmp = i0.ɵɵngDeclareComponent({
451
+ minVersion: "17.1.0",
452
+ version: "22.2.4",
453
+ type: ActionBadge,
454
+ isStandalone: true,
455
+ selector: "ja-action",
456
+ inputs: { action: {
457
+ classPropertyName: "action",
458
+ publicName: "action",
459
+ isSignal: true,
460
+ isRequired: true,
461
+ transformFunction: null
462
+ } },
463
+ ngImport: i0,
464
+ template: `<span class="ja-badge" [class]="tone()">{{ action() }}</span>`,
465
+ isInline: true,
466
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
467
+ });
468
+ };
469
+ i0.ɵɵngDeclareClassMetadata({
470
+ minVersion: "12.0.0",
471
+ version: "22.2.4",
472
+ ngImport: i0,
473
+ type: ActionBadge,
474
+ decorators: [{
475
+ type: Component,
476
+ args: [{
477
+ selector: "ja-action",
478
+ changeDetection: ChangeDetectionStrategy.OnPush,
479
+ template: `<span class="ja-badge" [class]="tone()">{{ action() }}</span>`
480
+ }]
481
+ }],
482
+ propDecorators: { action: [{
483
+ type: i0.Input,
484
+ args: [{
485
+ isSignal: true,
486
+ alias: "action",
487
+ required: true
488
+ }]
489
+ }] }
490
+ });
491
+ var TraceRef = class TraceRef {
492
+ ctx = inject(AuditContextService);
493
+ links = inject(AuditLinks);
494
+ traceId = input(...ngDevMode ? [void 0, { debugName: "traceId" }] : /* istanbul ignore next */ []);
495
+ short = input(true, ...ngDevMode ? [{ debugName: "short" }] : /* istanbul ignore next */ []);
496
+ external = computed(() => this.links.externalTrace(this.traceId()), ...ngDevMode ? [{ debugName: "external" }] : /* istanbul ignore next */ []);
497
+ static ɵfac = i0.ɵɵngDeclareFactory({
498
+ minVersion: "12.0.0",
499
+ version: "22.2.4",
500
+ ngImport: i0,
501
+ type: TraceRef,
502
+ deps: [],
503
+ target: i0.ɵɵFactoryTarget.Component
504
+ });
505
+ static ɵcmp = i0.ɵɵngDeclareComponent({
506
+ minVersion: "17.0.0",
507
+ version: "22.2.4",
508
+ type: TraceRef,
509
+ isStandalone: true,
510
+ selector: "ja-trace-ref",
511
+ inputs: {
512
+ traceId: {
513
+ classPropertyName: "traceId",
514
+ publicName: "traceId",
515
+ isSignal: true,
516
+ isRequired: false,
517
+ transformFunction: null
518
+ },
519
+ short: {
520
+ classPropertyName: "short",
521
+ publicName: "short",
522
+ isSignal: true,
523
+ isRequired: false,
524
+ transformFunction: null
525
+ }
526
+ },
527
+ ngImport: i0,
528
+ template: `
529
+ @if (traceId(); as id) {
530
+ <a class="ja-mono" [routerLink]="['traces', id] | jaLink" [queryParams]="ctx.keep()" [attr.title]="'Everything audited under trace ' + id">{{ short() ? id.slice(0, 12) + '…' : id }}</a>
531
+ @if (external(); as x) {
532
+ @if ('href' in x) { <a class="ja-external" [href]="x.href" target="_blank" rel="noopener" title="Open the trace">spans ↗</a> }
533
+ @else { <a class="ja-external" [routerLink]="x.route" title="Open the trace">spans ↗</a> }
534
+ }
535
+ } @else { <span class="ja-muted">—</span> }`,
536
+ isInline: true,
537
+ dependencies: [{
538
+ kind: "directive",
539
+ type: RouterLink,
540
+ selector: "[routerLink]",
541
+ inputs: [
542
+ "target",
543
+ "queryParams",
544
+ "fragment",
545
+ "queryParamsHandling",
546
+ "state",
547
+ "info",
548
+ "relativeTo",
549
+ "preserveFragment",
550
+ "skipLocationChange",
551
+ "replaceUrl",
552
+ "browserUrl",
553
+ "routerLink"
554
+ ]
555
+ }, {
556
+ kind: "pipe",
557
+ type: AuditLinkPipe,
558
+ name: "jaLink"
559
+ }],
560
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
561
+ });
562
+ };
563
+ i0.ɵɵngDeclareClassMetadata({
564
+ minVersion: "12.0.0",
565
+ version: "22.2.4",
566
+ ngImport: i0,
567
+ type: TraceRef,
568
+ decorators: [{
569
+ type: Component,
570
+ args: [{
571
+ selector: "ja-trace-ref",
572
+ changeDetection: ChangeDetectionStrategy.OnPush,
573
+ imports: [RouterLink, AuditLinkPipe],
574
+ template: `
575
+ @if (traceId(); as id) {
576
+ <a class="ja-mono" [routerLink]="['traces', id] | jaLink" [queryParams]="ctx.keep()" [attr.title]="'Everything audited under trace ' + id">{{ short() ? id.slice(0, 12) + '…' : id }}</a>
577
+ @if (external(); as x) {
578
+ @if ('href' in x) { <a class="ja-external" [href]="x.href" target="_blank" rel="noopener" title="Open the trace">spans ↗</a> }
579
+ @else { <a class="ja-external" [routerLink]="x.route" title="Open the trace">spans ↗</a> }
580
+ }
581
+ } @else { <span class="ja-muted">—</span> }`
582
+ }]
583
+ }],
584
+ propDecorators: {
585
+ traceId: [{
586
+ type: i0.Input,
587
+ args: [{
588
+ isSignal: true,
589
+ alias: "traceId",
590
+ required: false
591
+ }]
592
+ }],
593
+ short: [{
594
+ type: i0.Input,
595
+ args: [{
596
+ isSignal: true,
597
+ alias: "short",
598
+ required: false
599
+ }]
600
+ }]
601
+ }
602
+ });
603
+ var AccessLogTable = class AccessLogTable {
604
+ ctx = inject(AuditContextService);
605
+ rows = input.required(...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
606
+ compact = input(false, ...ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []);
607
+ longest = computed(() => Math.max(0, ...this.rows().map((r) => r.elapsedMs ?? 0)), ...ngDevMode ? [{ debugName: "longest" }] : /* istanbul ignore next */ []);
608
+ static ɵfac = i0.ɵɵngDeclareFactory({
609
+ minVersion: "12.0.0",
610
+ version: "22.2.4",
611
+ ngImport: i0,
612
+ type: AccessLogTable,
613
+ deps: [],
614
+ target: i0.ɵɵFactoryTarget.Component
615
+ });
616
+ static ɵcmp = i0.ɵɵngDeclareComponent({
617
+ minVersion: "17.0.0",
618
+ version: "22.2.4",
619
+ type: AccessLogTable,
620
+ isStandalone: true,
621
+ selector: "ja-access-log-table",
622
+ inputs: {
623
+ rows: {
624
+ classPropertyName: "rows",
625
+ publicName: "rows",
626
+ isSignal: true,
627
+ isRequired: true,
628
+ transformFunction: null
629
+ },
630
+ compact: {
631
+ classPropertyName: "compact",
632
+ publicName: "compact",
633
+ isSignal: true,
634
+ isRequired: false,
635
+ transformFunction: null
636
+ }
637
+ },
638
+ ngImport: i0,
639
+ template: `
640
+ <table class="ja-table">
641
+ <thead><tr><th>Time</th><th>User</th><th>Action</th><th>Status</th><th>Duration</th>@if (!compact()) { <th>App</th><th>Trace</th> }</tr></thead>
642
+ <tbody>
643
+ @for (r of rows(); track r.id) {
644
+ <tr>
645
+ <td class="ja-nowrap">{{ r.dateTime | date: 'MMM d, HH:mm:ss' }}</td>
646
+ <td>{{ r.user || '—' }}@if (r.tenantId) { <div class="ja-muted ja-small">{{ r.tenantId }}</div> }</td>
647
+ <td><a [routerLink]="['access-logs', r.id] | jaLink" [queryParams]="ctx.keep()">{{ r.action }}</a>
648
+ @if (r.method || r.path) { <div class="ja-muted ja-small ja-mono">{{ r.method }} {{ r.path }}</div> }
649
+ @if (r.isRestricted) { <span class="ja-badge ja-tone-warn">restricted</span> }</td>
650
+ <td><ja-status [status]="r.status" /></td>
651
+ <td class="ja-nowrap">@if (longest() > 0 && r.elapsedMs !== null) { <span class="ja-bar" [style.width.px]="60 * r.elapsedMs / longest()"></span> }{{ r.elapsedMs | jaDuration }}</td>
652
+ @if (!compact()) {
653
+ <td class="ja-muted">{{ r.app || '—' }}</td>
654
+ <td class="ja-nowrap"><ja-trace-ref [traceId]="r.traceId" /></td>
655
+ }
656
+ </tr>
657
+ }
658
+ </tbody>
659
+ </table>`,
660
+ isInline: true,
661
+ dependencies: [
662
+ {
663
+ kind: "directive",
664
+ type: RouterLink,
665
+ selector: "[routerLink]",
666
+ inputs: [
667
+ "target",
668
+ "queryParams",
669
+ "fragment",
670
+ "queryParamsHandling",
671
+ "state",
672
+ "info",
673
+ "relativeTo",
674
+ "preserveFragment",
675
+ "skipLocationChange",
676
+ "replaceUrl",
677
+ "browserUrl",
678
+ "routerLink"
679
+ ]
680
+ },
681
+ {
682
+ kind: "component",
683
+ type: StatusBadge,
684
+ selector: "ja-status",
685
+ inputs: ["status"]
686
+ },
687
+ {
688
+ kind: "component",
689
+ type: TraceRef,
690
+ selector: "ja-trace-ref",
691
+ inputs: ["traceId", "short"]
692
+ },
693
+ {
694
+ kind: "pipe",
695
+ type: DatePipe,
696
+ name: "date"
697
+ },
698
+ {
699
+ kind: "pipe",
700
+ type: AuditLinkPipe,
701
+ name: "jaLink"
702
+ },
703
+ {
704
+ kind: "pipe",
705
+ type: DurationPipe,
706
+ name: "jaDuration"
707
+ }
708
+ ],
709
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
710
+ });
711
+ };
712
+ i0.ɵɵngDeclareClassMetadata({
713
+ minVersion: "12.0.0",
714
+ version: "22.2.4",
715
+ ngImport: i0,
716
+ type: AccessLogTable,
717
+ decorators: [{
718
+ type: Component,
719
+ args: [{
720
+ selector: "ja-access-log-table",
721
+ changeDetection: ChangeDetectionStrategy.OnPush,
722
+ imports: [
723
+ RouterLink,
724
+ DatePipe,
725
+ AuditLinkPipe,
726
+ DurationPipe,
727
+ StatusBadge,
728
+ TraceRef
729
+ ],
730
+ template: `
731
+ <table class="ja-table">
732
+ <thead><tr><th>Time</th><th>User</th><th>Action</th><th>Status</th><th>Duration</th>@if (!compact()) { <th>App</th><th>Trace</th> }</tr></thead>
733
+ <tbody>
734
+ @for (r of rows(); track r.id) {
735
+ <tr>
736
+ <td class="ja-nowrap">{{ r.dateTime | date: 'MMM d, HH:mm:ss' }}</td>
737
+ <td>{{ r.user || '—' }}@if (r.tenantId) { <div class="ja-muted ja-small">{{ r.tenantId }}</div> }</td>
738
+ <td><a [routerLink]="['access-logs', r.id] | jaLink" [queryParams]="ctx.keep()">{{ r.action }}</a>
739
+ @if (r.method || r.path) { <div class="ja-muted ja-small ja-mono">{{ r.method }} {{ r.path }}</div> }
740
+ @if (r.isRestricted) { <span class="ja-badge ja-tone-warn">restricted</span> }</td>
741
+ <td><ja-status [status]="r.status" /></td>
742
+ <td class="ja-nowrap">@if (longest() > 0 && r.elapsedMs !== null) { <span class="ja-bar" [style.width.px]="60 * r.elapsedMs / longest()"></span> }{{ r.elapsedMs | jaDuration }}</td>
743
+ @if (!compact()) {
744
+ <td class="ja-muted">{{ r.app || '—' }}</td>
745
+ <td class="ja-nowrap"><ja-trace-ref [traceId]="r.traceId" /></td>
746
+ }
747
+ </tr>
748
+ }
749
+ </tbody>
750
+ </table>`
751
+ }]
752
+ }],
753
+ propDecorators: {
754
+ rows: [{
755
+ type: i0.Input,
756
+ args: [{
757
+ isSignal: true,
758
+ alias: "rows",
759
+ required: true
760
+ }]
761
+ }],
762
+ compact: [{
763
+ type: i0.Input,
764
+ args: [{
765
+ isSignal: true,
766
+ alias: "compact",
767
+ required: false
768
+ }]
769
+ }]
770
+ }
771
+ });
772
+ var DataChangeTable = class DataChangeTable {
773
+ ctx = inject(AuditContextService);
774
+ rows = input.required(...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
775
+ compact = input(false, ...ngDevMode ? [{ debugName: "compact" }] : /* istanbul ignore next */ []);
776
+ static ɵfac = i0.ɵɵngDeclareFactory({
777
+ minVersion: "12.0.0",
778
+ version: "22.2.4",
779
+ ngImport: i0,
780
+ type: DataChangeTable,
781
+ deps: [],
782
+ target: i0.ɵɵFactoryTarget.Component
783
+ });
784
+ static ɵcmp = i0.ɵɵngDeclareComponent({
785
+ minVersion: "17.0.0",
786
+ version: "22.2.4",
787
+ type: DataChangeTable,
788
+ isStandalone: true,
789
+ selector: "ja-data-change-table",
790
+ inputs: {
791
+ rows: {
792
+ classPropertyName: "rows",
793
+ publicName: "rows",
794
+ isSignal: true,
795
+ isRequired: true,
796
+ transformFunction: null
797
+ },
798
+ compact: {
799
+ classPropertyName: "compact",
800
+ publicName: "compact",
801
+ isSignal: true,
802
+ isRequired: false,
803
+ transformFunction: null
804
+ }
805
+ },
806
+ ngImport: i0,
807
+ template: `
808
+ <table class="ja-table">
809
+ <thead><tr><th>Time</th><th>User</th><th>Change</th><th>Table</th><th>Keys</th>@if (!compact()) { <th>Trace</th> }</tr></thead>
810
+ <tbody>
811
+ @for (c of rows(); track c.id) {
812
+ <tr>
813
+ <td class="ja-nowrap"><a [routerLink]="['data-changes', c.id] | jaLink" [queryParams]="ctx.keep()">{{ c.dateTime | date: 'MMM d, HH:mm:ss' }}</a></td>
814
+ <td>{{ c.user || '—' }}@if (c.tenantId) { <div class="ja-muted ja-small">{{ c.tenantId }}</div> }</td>
815
+ <td><ja-action [action]="c.action" /></td>
816
+ <td class="ja-mono">{{ c.schema ? c.schema + '.' : '' }}{{ c.table }}@if (c.database) { <div class="ja-muted ja-small">{{ c.database }}</div> }</td>
817
+ <td class="ja-mono ja-small">{{ c.keys | jaKeys }}</td>
818
+ @if (!compact()) { <td class="ja-nowrap"><ja-trace-ref [traceId]="c.traceId" /></td> }
819
+ </tr>
820
+ }
821
+ </tbody>
822
+ </table>`,
823
+ isInline: true,
824
+ dependencies: [
825
+ {
826
+ kind: "directive",
827
+ type: RouterLink,
828
+ selector: "[routerLink]",
829
+ inputs: [
830
+ "target",
831
+ "queryParams",
832
+ "fragment",
833
+ "queryParamsHandling",
834
+ "state",
835
+ "info",
836
+ "relativeTo",
837
+ "preserveFragment",
838
+ "skipLocationChange",
839
+ "replaceUrl",
840
+ "browserUrl",
841
+ "routerLink"
842
+ ]
843
+ },
844
+ {
845
+ kind: "component",
846
+ type: ActionBadge,
847
+ selector: "ja-action",
848
+ inputs: ["action"]
849
+ },
850
+ {
851
+ kind: "component",
852
+ type: TraceRef,
853
+ selector: "ja-trace-ref",
854
+ inputs: ["traceId", "short"]
855
+ },
856
+ {
857
+ kind: "pipe",
858
+ type: DatePipe,
859
+ name: "date"
860
+ },
861
+ {
862
+ kind: "pipe",
863
+ type: AuditLinkPipe,
864
+ name: "jaLink"
865
+ },
866
+ {
867
+ kind: "pipe",
868
+ type: KeysPipe,
869
+ name: "jaKeys"
870
+ }
871
+ ],
872
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
873
+ });
874
+ };
875
+ i0.ɵɵngDeclareClassMetadata({
876
+ minVersion: "12.0.0",
877
+ version: "22.2.4",
878
+ ngImport: i0,
879
+ type: DataChangeTable,
880
+ decorators: [{
881
+ type: Component,
882
+ args: [{
883
+ selector: "ja-data-change-table",
884
+ changeDetection: ChangeDetectionStrategy.OnPush,
885
+ imports: [
886
+ RouterLink,
887
+ DatePipe,
888
+ AuditLinkPipe,
889
+ KeysPipe,
890
+ ActionBadge,
891
+ TraceRef
892
+ ],
893
+ template: `
894
+ <table class="ja-table">
895
+ <thead><tr><th>Time</th><th>User</th><th>Change</th><th>Table</th><th>Keys</th>@if (!compact()) { <th>Trace</th> }</tr></thead>
896
+ <tbody>
897
+ @for (c of rows(); track c.id) {
898
+ <tr>
899
+ <td class="ja-nowrap"><a [routerLink]="['data-changes', c.id] | jaLink" [queryParams]="ctx.keep()">{{ c.dateTime | date: 'MMM d, HH:mm:ss' }}</a></td>
900
+ <td>{{ c.user || '—' }}@if (c.tenantId) { <div class="ja-muted ja-small">{{ c.tenantId }}</div> }</td>
901
+ <td><ja-action [action]="c.action" /></td>
902
+ <td class="ja-mono">{{ c.schema ? c.schema + '.' : '' }}{{ c.table }}@if (c.database) { <div class="ja-muted ja-small">{{ c.database }}</div> }</td>
903
+ <td class="ja-mono ja-small">{{ c.keys | jaKeys }}</td>
904
+ @if (!compact()) { <td class="ja-nowrap"><ja-trace-ref [traceId]="c.traceId" /></td> }
905
+ </tr>
906
+ }
907
+ </tbody>
908
+ </table>`
909
+ }]
910
+ }],
911
+ propDecorators: {
912
+ rows: [{
913
+ type: i0.Input,
914
+ args: [{
915
+ isSignal: true,
916
+ alias: "rows",
917
+ required: true
918
+ }]
919
+ }],
920
+ compact: [{
921
+ type: i0.Input,
922
+ args: [{
923
+ isSignal: true,
924
+ alias: "compact",
925
+ required: false
926
+ }]
927
+ }]
928
+ }
929
+ });
930
+ var ChangeDiff = class ChangeDiff {
931
+ changes = input.required(...ngDevMode ? [{ debugName: "changes" }] : /* istanbul ignore next */ []);
932
+ rows = computed(() => {
933
+ const changes = asObject(this.changes());
934
+ const before = asObject(changes["OriginalValues"] ?? changes["originalValues"] ?? null);
935
+ const after = asObject(changes["CurrentValues"] ?? changes["currentValues"] ?? null);
936
+ const rows = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].map((field) => {
937
+ const hasBefore = field in before;
938
+ const hasAfter = field in after;
939
+ const b = formatValue(before[field]);
940
+ const a = formatValue(after[field]);
941
+ return {
942
+ field,
943
+ before: b,
944
+ after: a,
945
+ hasBefore,
946
+ hasAfter,
947
+ changed: hasBefore !== hasAfter || b !== a
948
+ };
949
+ });
950
+ return [...rows.filter((r) => r.changed), ...rows.filter((r) => !r.changed)];
951
+ }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
952
+ unchanged = computed(() => this.rows().filter((r) => !r.changed).length, ...ngDevMode ? [{ debugName: "unchanged" }] : /* istanbul ignore next */ []);
953
+ static ɵfac = i0.ɵɵngDeclareFactory({
954
+ minVersion: "12.0.0",
955
+ version: "22.2.4",
956
+ ngImport: i0,
957
+ type: ChangeDiff,
958
+ deps: [],
959
+ target: i0.ɵɵFactoryTarget.Component
960
+ });
961
+ static ɵcmp = i0.ɵɵngDeclareComponent({
962
+ minVersion: "17.0.0",
963
+ version: "22.2.4",
964
+ type: ChangeDiff,
965
+ isStandalone: true,
966
+ selector: "ja-change-diff",
967
+ inputs: { changes: {
968
+ classPropertyName: "changes",
969
+ publicName: "changes",
970
+ isSignal: true,
971
+ isRequired: true,
972
+ transformFunction: null
973
+ } },
974
+ ngImport: i0,
975
+ template: `
976
+ @if (rows().length === 0) { <p class="ja-muted">No values were recorded.</p> }
977
+ @else {
978
+ <table class="ja-table ja-diff">
979
+ <thead><tr><th>Field</th><th>Before</th><th>After</th></tr></thead>
980
+ <tbody>
981
+ @for (r of rows(); track r.field) {
982
+ <tr [class.ja-changed]="r.changed">
983
+ <th class="ja-mono">{{ r.field }}</th>
984
+ <td class="ja-mono" [class.ja-old]="r.changed && r.hasBefore">@if (r.hasBefore) { {{ r.before }} } @else { <span class="ja-muted">—</span> }</td>
985
+ <td class="ja-mono" [class.ja-new]="r.changed && r.hasAfter">@if (r.hasAfter) { {{ r.after }} } @else { <span class="ja-muted">—</span> }</td>
986
+ </tr>
987
+ }
988
+ </tbody>
989
+ </table>
990
+ @if (unchanged() > 0) { <p class="ja-muted ja-small">{{ unchanged() }} field{{ unchanged() === 1 ? '' : 's' }} unchanged, shown after the changed ones.</p> }
991
+ }`,
992
+ isInline: true,
993
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
994
+ });
995
+ };
996
+ i0.ɵɵngDeclareClassMetadata({
997
+ minVersion: "12.0.0",
998
+ version: "22.2.4",
999
+ ngImport: i0,
1000
+ type: ChangeDiff,
1001
+ decorators: [{
1002
+ type: Component,
1003
+ args: [{
1004
+ selector: "ja-change-diff",
1005
+ changeDetection: ChangeDetectionStrategy.OnPush,
1006
+ template: `
1007
+ @if (rows().length === 0) { <p class="ja-muted">No values were recorded.</p> }
1008
+ @else {
1009
+ <table class="ja-table ja-diff">
1010
+ <thead><tr><th>Field</th><th>Before</th><th>After</th></tr></thead>
1011
+ <tbody>
1012
+ @for (r of rows(); track r.field) {
1013
+ <tr [class.ja-changed]="r.changed">
1014
+ <th class="ja-mono">{{ r.field }}</th>
1015
+ <td class="ja-mono" [class.ja-old]="r.changed && r.hasBefore">@if (r.hasBefore) { {{ r.before }} } @else { <span class="ja-muted">—</span> }</td>
1016
+ <td class="ja-mono" [class.ja-new]="r.changed && r.hasAfter">@if (r.hasAfter) { {{ r.after }} } @else { <span class="ja-muted">—</span> }</td>
1017
+ </tr>
1018
+ }
1019
+ </tbody>
1020
+ </table>
1021
+ @if (unchanged() > 0) { <p class="ja-muted ja-small">{{ unchanged() }} field{{ unchanged() === 1 ? '' : 's' }} unchanged, shown after the changed ones.</p> }
1022
+ }`
1023
+ }]
1024
+ }],
1025
+ propDecorators: { changes: [{
1026
+ type: i0.Input,
1027
+ args: [{
1028
+ isSignal: true,
1029
+ alias: "changes",
1030
+ required: true
1031
+ }]
1032
+ }] }
1033
+ });
1034
+ function asObject(value) {
1035
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1036
+ }
1037
+ var KeyValueTable = class KeyValueTable {
1038
+ values = input({}, ...ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []);
1039
+ empty = input("None", ...ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []);
1040
+ entries = computed(() => Object.entries(this.values()).filter((e) => e[1] !== null && e[1] !== void 0 && e[1] !== "").sort((a, b) => a[0].localeCompare(b[0])), ...ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []);
1041
+ static ɵfac = i0.ɵɵngDeclareFactory({
1042
+ minVersion: "12.0.0",
1043
+ version: "22.2.4",
1044
+ ngImport: i0,
1045
+ type: KeyValueTable,
1046
+ deps: [],
1047
+ target: i0.ɵɵFactoryTarget.Component
1048
+ });
1049
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1050
+ minVersion: "17.0.0",
1051
+ version: "22.2.4",
1052
+ type: KeyValueTable,
1053
+ isStandalone: true,
1054
+ selector: "ja-kv",
1055
+ inputs: {
1056
+ values: {
1057
+ classPropertyName: "values",
1058
+ publicName: "values",
1059
+ isSignal: true,
1060
+ isRequired: false,
1061
+ transformFunction: null
1062
+ },
1063
+ empty: {
1064
+ classPropertyName: "empty",
1065
+ publicName: "empty",
1066
+ isSignal: true,
1067
+ isRequired: false,
1068
+ transformFunction: null
1069
+ }
1070
+ },
1071
+ ngImport: i0,
1072
+ template: `
1073
+ @if (entries().length === 0) { <p class="ja-muted">{{ empty() }}</p> }
1074
+ @else {
1075
+ <table class="ja-kv"><tbody>
1076
+ @for (e of entries(); track e[0]) { <tr><th>{{ e[0] }}</th><td>{{ e[1] }}</td></tr> }
1077
+ </tbody></table>
1078
+ }`,
1079
+ isInline: true,
1080
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
1081
+ });
1082
+ };
1083
+ i0.ɵɵngDeclareClassMetadata({
1084
+ minVersion: "12.0.0",
1085
+ version: "22.2.4",
1086
+ ngImport: i0,
1087
+ type: KeyValueTable,
1088
+ decorators: [{
1089
+ type: Component,
1090
+ args: [{
1091
+ selector: "ja-kv",
1092
+ changeDetection: ChangeDetectionStrategy.OnPush,
1093
+ template: `
1094
+ @if (entries().length === 0) { <p class="ja-muted">{{ empty() }}</p> }
1095
+ @else {
1096
+ <table class="ja-kv"><tbody>
1097
+ @for (e of entries(); track e[0]) { <tr><th>{{ e[0] }}</th><td>{{ e[1] }}</td></tr> }
1098
+ </tbody></table>
1099
+ }`
1100
+ }]
1101
+ }],
1102
+ propDecorators: {
1103
+ values: [{
1104
+ type: i0.Input,
1105
+ args: [{
1106
+ isSignal: true,
1107
+ alias: "values",
1108
+ required: false
1109
+ }]
1110
+ }],
1111
+ empty: [{
1112
+ type: i0.Input,
1113
+ args: [{
1114
+ isSignal: true,
1115
+ alias: "empty",
1116
+ required: false
1117
+ }]
1118
+ }]
1119
+ }
1120
+ });
1121
+ export { DataChangeTable as a, TraceRef as c, PrettyJsonPipe as d, formatValue as f, ChangeDiff as i, DurationPipe as l, apiResource as m, ActionBadge as n, KeyValueTable as o, problemText as p, AuditContextBar as r, StatusBadge as s, AccessLogTable as t, KeysPipe as u };
1122
+
1123
+ //# sourceMappingURL=juice-js-audit-ui-DnfhiB6p.mjs.map