@lenso/audit 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,169 @@
1
+ import {
2
+ invalid,
3
+ object,
4
+ uuid,
5
+ time,
6
+ scope,
7
+ actor,
8
+ summaryPolicy,
9
+ eventInput,
10
+ query
11
+ } from "./index-xk4zknet.js";
12
+ import {
13
+ AuditError2,
14
+ AuditOutcomeUnknownError2,
15
+ sameScope2
16
+ } from "./index-dxwdr6tz.js";
17
+
18
+ // src/service.ts
19
+ function createAuditService2(options) {
20
+ const { repository, authority, report } = options;
21
+ const policy = summaryPolicy(options.summaryPolicy ?? {});
22
+ const maxPageSize = options.maxPageSize ?? 100;
23
+ if (!Number.isSafeInteger(maxPageSize) || maxPageSize < 1 || maxPageSize > 500)
24
+ invalid();
25
+ const clock = options.clock ?? Date.now;
26
+ const receipts = new WeakMap;
27
+ async function identity(principal, scope, operation) {
28
+ try {
29
+ return actor(await authority.resolve(principal, scope, operation));
30
+ } catch {
31
+ throw new AuditError2("unauthorized");
32
+ }
33
+ }
34
+ async function diagnostic(mode, stage) {
35
+ try {
36
+ await report?.(Object.freeze({ mode, stage, code: "storage-failed" }));
37
+ } catch {
38
+ throw new AuditError2("diagnostics-failed");
39
+ }
40
+ }
41
+ async function storage(work) {
42
+ try {
43
+ return await work();
44
+ } catch {
45
+ throw new AuditError2("storage-failed");
46
+ }
47
+ }
48
+ async function persist(event) {
49
+ if (event.relation) {
50
+ if (event.relation.eventId === event.id)
51
+ invalid();
52
+ const previous = await storage(() => repository.get(event.scope, event.relation.eventId));
53
+ if (!previous)
54
+ throw new AuditError2("relation-missing");
55
+ if (!sameScope2(previous.scope, event.scope))
56
+ throw new AuditError2("storage-failed");
57
+ if (event.relation.kind === "outcome" && (previous.result !== "intent" || event.result === "intent" || previous.action !== event.action || previous.target.type !== event.target.type || previous.target.id !== event.target.id))
58
+ invalid();
59
+ }
60
+ const status = await storage(() => repository.insert(event));
61
+ if (status === "conflict")
62
+ throw new AuditError2("duplicate-conflict");
63
+ if (status !== "inserted" && status !== "duplicate")
64
+ throw new AuditError2("storage-failed");
65
+ const stored = status === "duplicate" ? await storage(() => repository.get(event.scope, event.id)) : event;
66
+ if (!stored)
67
+ throw new AuditError2("storage-failed");
68
+ return { status, event: stored };
69
+ }
70
+ async function make(input, principal) {
71
+ const data = eventInput(input, policy);
72
+ const actor = await identity(principal, data.scope, "append");
73
+ return Object.freeze({ ...data, actor, recordedAt: time(clock()) });
74
+ }
75
+ return {
76
+ async append(input, principal) {
77
+ return persist(await make(input, principal));
78
+ },
79
+ async appendBestEffort(input, principal) {
80
+ if (!report)
81
+ throw new AuditError2("diagnostics-required");
82
+ const event = await make(input, principal);
83
+ try {
84
+ return await persist(event);
85
+ } catch (error) {
86
+ if (!(error instanceof AuditError2) || error.code !== "storage-failed")
87
+ throw error;
88
+ await diagnostic("best-effort", "append");
89
+ return { status: "unconfirmed", code: "storage-failed" };
90
+ }
91
+ },
92
+ async get(input, principal) {
93
+ const raw = object(input, ["scope", "id"]);
94
+ const scope2 = scope(raw.scope);
95
+ const id = uuid(raw.id);
96
+ await identity(principal, scope2, "query");
97
+ const event = await storage(() => repository.get(scope2, id));
98
+ if (event && !sameScope2(event.scope, scope2))
99
+ throw new AuditError2("storage-failed");
100
+ return event;
101
+ },
102
+ async query(input, principal) {
103
+ const query2 = query(input, maxPageSize);
104
+ await identity(principal, query2.scope, "query");
105
+ const rows = await storage(() => repository.list({ ...query2, limit: query2.limit + 1 }));
106
+ if (rows.some((event) => !sameScope2(event.scope, query2.scope)))
107
+ throw new AuditError2("storage-failed");
108
+ const events = rows.slice(0, query2.limit);
109
+ const last = events.at(-1);
110
+ return {
111
+ events,
112
+ nextCursor: rows.length > query2.limit && last ? { recordedAt: last.recordedAt, id: last.id } : null
113
+ };
114
+ },
115
+ async prepare(input, principal) {
116
+ if (repository.durableIntents !== true)
117
+ throw new AuditError2("strict-unavailable");
118
+ const event = await make(input, principal);
119
+ if (event.result !== "intent" || event.relation)
120
+ invalid();
121
+ let result;
122
+ try {
123
+ result = await persist(event);
124
+ } catch (error) {
125
+ if (error instanceof AuditError2 && error.code === "storage-failed")
126
+ await diagnostic("strict", "intent");
127
+ throw error;
128
+ }
129
+ if (result.status === "duplicate")
130
+ return { status: "already-recorded", intentId: event.id };
131
+ const receipt = Object.freeze({ intentId: event.id });
132
+ receipts.set(receipt, event);
133
+ return { status: "ready", receipt };
134
+ },
135
+ async complete(receipt, outcome) {
136
+ const intent = receipts.get(receipt);
137
+ if (!intent)
138
+ throw new AuditError2("invalid-receipt");
139
+ try {
140
+ object(outcome, ["id", "occurredAt", "result", "reasonCode", "summary"]);
141
+ if (!["success", "failure", "denied", "unknown"].includes(outcome.result))
142
+ invalid();
143
+ const data = eventInput({
144
+ ...outcome,
145
+ scope: intent.scope,
146
+ action: intent.action,
147
+ target: intent.target,
148
+ ...intent.correlationId ? { correlationId: intent.correlationId } : {},
149
+ relation: { kind: "outcome", eventId: intent.id }
150
+ }, policy);
151
+ const event = Object.freeze({
152
+ ...data,
153
+ actor: intent.actor,
154
+ recordedAt: time(clock())
155
+ });
156
+ return (await persist(event)).event;
157
+ } catch {
158
+ try {
159
+ await diagnostic("strict", "outcome");
160
+ } catch {
161
+ throw new AuditOutcomeUnknownError2(intent.id, true);
162
+ }
163
+ throw new AuditOutcomeUnknownError2(intent.id);
164
+ }
165
+ }
166
+ };
167
+ }
168
+
169
+ export { createAuditService2 };
@@ -0,0 +1,191 @@
1
+ import {
2
+ AuditError2
3
+ } from "./index-dxwdr6tz.js";
4
+
5
+ // src/validation.ts
6
+ var forbidden = /secret|password|token|credential|cookie|authorization|digest|hash|body|payload|email|phone|address|__proto__|constructor|prototype/i;
7
+ var results = ["intent", "success", "failure", "denied", "unknown"];
8
+ var eventIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
9
+ function invalid() {
10
+ throw new AuditError2("invalid-input");
11
+ }
12
+ function object(value, keys) {
13
+ if (!value || typeof value !== "object" || Array.isArray(value))
14
+ invalid();
15
+ const prototype = Object.getPrototypeOf(value);
16
+ if (prototype !== Object.prototype && prototype !== null)
17
+ invalid();
18
+ for (const key of Reflect.ownKeys(value)) {
19
+ if (typeof key !== "string" || keys && !keys.includes(key))
20
+ invalid();
21
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
22
+ if (!("value" in descriptor) || !descriptor.enumerable)
23
+ invalid();
24
+ }
25
+ return value;
26
+ }
27
+ function identifier(value, max = 128) {
28
+ if (typeof value !== "string" || value.length > max || !/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/.test(value) || value.includes("://"))
29
+ invalid();
30
+ return value;
31
+ }
32
+ function uuid(value) {
33
+ if (typeof value !== "string" || !eventIdPattern.test(value))
34
+ invalid();
35
+ return value;
36
+ }
37
+ function time(value) {
38
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 8640000000000000)
39
+ invalid();
40
+ return value;
41
+ }
42
+ function scope(value) {
43
+ const input = object(value, ["tenantId", "scopeId"]);
44
+ return Object.freeze({
45
+ tenantId: input.tenantId === null ? null : identifier(input.tenantId),
46
+ scopeId: identifier(input.scopeId)
47
+ });
48
+ }
49
+ function actor(value) {
50
+ const input = object(value);
51
+ if (input.kind === "system") {
52
+ object(input, ["kind", "systemId"]);
53
+ return Object.freeze({ kind: "system", systemId: identifier(input.systemId) });
54
+ }
55
+ object(input, ["kind", "realmId", "subjectId"]);
56
+ if (!["user", "guest", "service"].includes(input.kind))
57
+ invalid();
58
+ if (typeof input.realmId !== "string" || !input.realmId.trim() || input.realmId.length > 256 || /\s/.test(input.realmId) || typeof input.subjectId !== "string" || !input.subjectId.trim() || input.subjectId.length > 512)
59
+ invalid();
60
+ return Object.freeze({
61
+ kind: input.kind,
62
+ realmId: input.realmId,
63
+ subjectId: input.subjectId
64
+ });
65
+ }
66
+ function summaryPolicy(data) {
67
+ const policy = object(data);
68
+ const copied = {};
69
+ if (Object.keys(policy).length > 100)
70
+ invalid();
71
+ for (const [action, fields] of Object.entries(policy)) {
72
+ identifier(action);
73
+ const rules = object(fields);
74
+ if (Object.keys(rules).length > 16)
75
+ invalid();
76
+ const target = {};
77
+ for (const [key, value] of Object.entries(rules)) {
78
+ identifier(key, 64);
79
+ if (forbidden.test(key))
80
+ invalid();
81
+ const rule = object(value);
82
+ if (rule.type === "boolean") {
83
+ object(rule, ["type"]);
84
+ target[key] = Object.freeze({ type: "boolean" });
85
+ } else if (rule.type === "integer") {
86
+ object(rule, ["type", "min", "max"]);
87
+ if (!Number.isSafeInteger(rule.min) || !Number.isSafeInteger(rule.max) || rule.min > rule.max)
88
+ invalid();
89
+ target[key] = Object.freeze({
90
+ type: "integer",
91
+ min: rule.min,
92
+ max: rule.max
93
+ });
94
+ } else if (rule.type === "enum") {
95
+ object(rule, ["type", "values"]);
96
+ if (!Array.isArray(rule.values) || !rule.values.length || rule.values.length > 32)
97
+ invalid();
98
+ target[key] = Object.freeze({
99
+ type: "enum",
100
+ values: Object.freeze(rule.values.map((literal) => identifier(literal, 64)))
101
+ });
102
+ } else
103
+ invalid();
104
+ }
105
+ copied[action] = Object.freeze(target);
106
+ }
107
+ return Object.freeze(copied);
108
+ }
109
+ function eventInput(data, policy) {
110
+ const raw = object(data, [
111
+ "id",
112
+ "occurredAt",
113
+ "scope",
114
+ "action",
115
+ "target",
116
+ "result",
117
+ "reasonCode",
118
+ "correlationId",
119
+ "summary",
120
+ "relation"
121
+ ]);
122
+ const action = identifier(raw.action);
123
+ const target = object(raw.target, ["type", "id"]);
124
+ if (!results.includes(raw.result))
125
+ invalid();
126
+ const summary = {};
127
+ for (const [key, value] of Object.entries(raw.summary === undefined ? {} : object(raw.summary))) {
128
+ const rule = Object.hasOwn(policy, action) && Object.hasOwn(policy[action], key) ? policy[action][key] : undefined;
129
+ if (!rule || forbidden.test(key))
130
+ invalid();
131
+ if (rule.type === "boolean" && typeof value !== "boolean" || rule.type === "integer" && (typeof value !== "number" || !Number.isSafeInteger(value) || value < rule.min || value > rule.max) || rule.type === "enum" && (typeof value !== "string" || !rule.values.includes(value)))
132
+ invalid();
133
+ summary[key] = value;
134
+ }
135
+ const relation = raw.relation === undefined ? undefined : object(raw.relation, ["kind", "eventId"]);
136
+ if (relation && !["correction", "outcome"].includes(relation.kind))
137
+ invalid();
138
+ return Object.freeze({
139
+ id: uuid(raw.id),
140
+ occurredAt: time(raw.occurredAt),
141
+ scope: scope(raw.scope),
142
+ action,
143
+ target: Object.freeze({ type: identifier(target.type, 64), id: identifier(target.id, 256) }),
144
+ result: raw.result,
145
+ reasonCode: identifier(raw.reasonCode, 64),
146
+ ...raw.correlationId === undefined ? {} : { correlationId: identifier(raw.correlationId) },
147
+ summary: Object.freeze(summary),
148
+ ...relation ? {
149
+ relation: Object.freeze({
150
+ kind: relation.kind,
151
+ eventId: uuid(relation.eventId)
152
+ })
153
+ } : {}
154
+ });
155
+ }
156
+ function query(value, maxPageSize) {
157
+ const raw = object(value, [
158
+ "scope",
159
+ "limit",
160
+ "cursor",
161
+ "action",
162
+ "target",
163
+ "result",
164
+ "correlationId",
165
+ "recordedFrom",
166
+ "recordedTo"
167
+ ]);
168
+ const limit = raw.limit ?? Math.min(50, maxPageSize);
169
+ if (typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1 || limit > maxPageSize)
170
+ invalid();
171
+ const target = raw.target === undefined ? undefined : object(raw.target, ["type", "id"]);
172
+ const cursor = raw.cursor === undefined ? undefined : object(raw.cursor, ["recordedAt", "id"]);
173
+ if (raw.result !== undefined && !results.includes(raw.result))
174
+ invalid();
175
+ const result = {
176
+ scope: scope(raw.scope),
177
+ limit,
178
+ ...cursor ? { cursor: { recordedAt: time(cursor.recordedAt), id: uuid(cursor.id) } } : {},
179
+ ...raw.action === undefined ? {} : { action: identifier(raw.action) },
180
+ ...target ? { target: { type: identifier(target.type, 64), id: identifier(target.id, 256) } } : {},
181
+ ...raw.result === undefined ? {} : { result: raw.result },
182
+ ...raw.correlationId === undefined ? {} : { correlationId: identifier(raw.correlationId) },
183
+ ...raw.recordedFrom === undefined ? {} : { recordedFrom: time(raw.recordedFrom) },
184
+ ...raw.recordedTo === undefined ? {} : { recordedTo: time(raw.recordedTo) }
185
+ };
186
+ if (result.recordedFrom !== undefined && result.recordedTo !== undefined && result.recordedFrom > result.recordedTo)
187
+ invalid();
188
+ return result;
189
+ }
190
+
191
+ export { eventIdPattern, invalid, object, uuid, time, scope, actor, summaryPolicy, eventInput, query };
@@ -0,0 +1,2 @@
1
+ export * from "./contracts";
2
+ export * from "./service";
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import {
2
+ createAuditService2
3
+ } from "./index-ph1dkars.js";
4
+ import {
5
+ AuditError2,
6
+ AuditOutcomeUnknownError2,
7
+ sameScope2,
8
+ sameEvent2
9
+ } from "./index-dxwdr6tz.js";
10
+ export {
11
+ AuditError2 as AuditError,
12
+ AuditOutcomeUnknownError2 as AuditOutcomeUnknownError,
13
+ createAuditService2 as createAuditService,
14
+ sameEvent2 as sameEvent,
15
+ sameScope2 as sameScope
16
+ };
@@ -0,0 +1,119 @@
1
+ import { type Plugin } from "@lenso/core";
2
+ import { z } from "zod";
3
+ import type { AuditService } from "./service";
4
+ declare const queryInput: z.ZodObject<{
5
+ scope: z.ZodObject<{
6
+ tenantId: z.ZodNullable<z.ZodString>;
7
+ scopeId: z.ZodString;
8
+ }, z.core.$strict>;
9
+ limit: z.ZodOptional<z.ZodNumber>;
10
+ cursor: z.ZodOptional<z.ZodObject<{
11
+ recordedAt: z.ZodNumber;
12
+ id: z.ZodString;
13
+ }, z.core.$strict>>;
14
+ action: z.ZodOptional<z.ZodString>;
15
+ target: z.ZodOptional<z.ZodObject<{
16
+ type: z.ZodString;
17
+ id: z.ZodString;
18
+ }, z.core.$strict>>;
19
+ result: z.ZodOptional<z.ZodEnum<{
20
+ denied: "denied";
21
+ failure: "failure";
22
+ intent: "intent";
23
+ success: "success";
24
+ unknown: "unknown";
25
+ }>>;
26
+ correlationId: z.ZodOptional<z.ZodString>;
27
+ recordedFrom: z.ZodOptional<z.ZodNumber>;
28
+ recordedTo: z.ZodOptional<z.ZodNumber>;
29
+ }, z.core.$strict>;
30
+ /** Creating this companion does not install it or open a transport. No append tool is declared. */
31
+ export declare function createAuditManage<P>(options: {
32
+ id: string;
33
+ audit: Plugin<AuditService<P>>;
34
+ }): {
35
+ plugin: Plugin<{
36
+ query(input: z.input<typeof queryInput>, context: {
37
+ principal: P;
38
+ }): Promise<import("./contracts").AuditPage>;
39
+ }>;
40
+ operation: import("@lenso/engine/operations").Operation<{
41
+ principal: P;
42
+ }> & {
43
+ readonly plugin: Plugin<{
44
+ query(input: z.input<typeof queryInput>, context: {
45
+ principal: P;
46
+ }): Promise<import("./contracts").AuditPage>;
47
+ }>;
48
+ readonly method: "query";
49
+ readonly input: z.ZodObject<{
50
+ scope: z.ZodObject<{
51
+ tenantId: z.ZodNullable<z.ZodString>;
52
+ scopeId: z.ZodString;
53
+ }, z.core.$strict>;
54
+ limit: z.ZodOptional<z.ZodNumber>;
55
+ cursor: z.ZodOptional<z.ZodObject<{
56
+ recordedAt: z.ZodNumber;
57
+ id: z.ZodString;
58
+ }, z.core.$strict>>;
59
+ action: z.ZodOptional<z.ZodString>;
60
+ target: z.ZodOptional<z.ZodObject<{
61
+ type: z.ZodString;
62
+ id: z.ZodString;
63
+ }, z.core.$strict>>;
64
+ result: z.ZodOptional<z.ZodEnum<{
65
+ denied: "denied";
66
+ failure: "failure";
67
+ intent: "intent";
68
+ success: "success";
69
+ unknown: "unknown";
70
+ }>>;
71
+ correlationId: z.ZodOptional<z.ZodString>;
72
+ recordedFrom: z.ZodOptional<z.ZodNumber>;
73
+ recordedTo: z.ZodOptional<z.ZodNumber>;
74
+ }, z.core.$strict>;
75
+ readonly context: true;
76
+ };
77
+ manage: import("@lenso/manage").Manage<Plugin<{
78
+ query(input: z.input<typeof queryInput>, context: {
79
+ principal: P;
80
+ }): Promise<import("./contracts").AuditPage>;
81
+ }>, readonly [import("@lenso/engine/operations").Operation<{
82
+ principal: P;
83
+ }> & {
84
+ readonly plugin: Plugin<{
85
+ query(input: z.input<typeof queryInput>, context: {
86
+ principal: P;
87
+ }): Promise<import("./contracts").AuditPage>;
88
+ }>;
89
+ readonly method: "query";
90
+ readonly input: z.ZodObject<{
91
+ scope: z.ZodObject<{
92
+ tenantId: z.ZodNullable<z.ZodString>;
93
+ scopeId: z.ZodString;
94
+ }, z.core.$strict>;
95
+ limit: z.ZodOptional<z.ZodNumber>;
96
+ cursor: z.ZodOptional<z.ZodObject<{
97
+ recordedAt: z.ZodNumber;
98
+ id: z.ZodString;
99
+ }, z.core.$strict>>;
100
+ action: z.ZodOptional<z.ZodString>;
101
+ target: z.ZodOptional<z.ZodObject<{
102
+ type: z.ZodString;
103
+ id: z.ZodString;
104
+ }, z.core.$strict>>;
105
+ result: z.ZodOptional<z.ZodEnum<{
106
+ denied: "denied";
107
+ failure: "failure";
108
+ intent: "intent";
109
+ success: "success";
110
+ unknown: "unknown";
111
+ }>>;
112
+ correlationId: z.ZodOptional<z.ZodString>;
113
+ recordedFrom: z.ZodOptional<z.ZodNumber>;
114
+ recordedTo: z.ZodOptional<z.ZodNumber>;
115
+ }, z.core.$strict>;
116
+ readonly context: true;
117
+ }]>;
118
+ };
119
+ export {};
package/dist/manage.js ADDED
@@ -0,0 +1,59 @@
1
+ import {
2
+ eventIdPattern
3
+ } from "./index-xk4zknet.js";
4
+
5
+ // src/manage.ts
6
+ import { definePlugin } from "@lenso/core";
7
+ import { defineOperation } from "@lenso/engine/operations";
8
+ import { defineManage } from "@lenso/manage";
9
+ import { z } from "zod";
10
+ var identifier = (max = 128) => z.string().min(1).max(max).regex(/^[A-Za-z0-9][A-Za-z0-9._:/-]*$/);
11
+ var scope = z.strictObject({ tenantId: identifier().nullable(), scopeId: identifier() });
12
+ var queryInput = z.strictObject({
13
+ scope,
14
+ limit: z.number().int().min(1).max(500).optional(),
15
+ cursor: z.strictObject({
16
+ recordedAt: z.number().int().nonnegative(),
17
+ id: z.string().regex(eventIdPattern)
18
+ }).optional(),
19
+ action: identifier().optional(),
20
+ target: z.strictObject({
21
+ type: identifier(64),
22
+ id: identifier(256)
23
+ }).optional(),
24
+ result: z.enum(["intent", "success", "failure", "denied", "unknown"]).optional(),
25
+ correlationId: identifier().optional(),
26
+ recordedFrom: z.number().int().nonnegative().optional(),
27
+ recordedTo: z.number().int().nonnegative().optional()
28
+ });
29
+ function createAuditManage(options) {
30
+ const plugin = definePlugin({
31
+ id: options.id,
32
+ requires: [options.audit],
33
+ setup(lifecycle) {
34
+ const audit = lifecycle.get(options.audit);
35
+ return {
36
+ query(input, context) {
37
+ return audit.query(input, context.principal);
38
+ }
39
+ };
40
+ }
41
+ });
42
+ const operation = defineOperation({
43
+ plugin,
44
+ method: "query",
45
+ context: true,
46
+ input: queryInput,
47
+ effect: "read",
48
+ destructive: false,
49
+ retry: "safe",
50
+ cancellation: "none",
51
+ description: "Query audit events within an explicitly authorized tenant and scope.",
52
+ outputDescription: "A bounded page of whitelist-only audit events, with no global count."
53
+ });
54
+ const manage = defineManage({ plugin, operations: [operation] });
55
+ return { plugin, operation, manage };
56
+ }
57
+ export {
58
+ createAuditManage
59
+ };
@@ -0,0 +1,20 @@
1
+ import type { Plugin } from "@lenso/core";
2
+ import { type ConfigSource } from "@lenso/core/config";
3
+ import { z } from "zod";
4
+ import type { AuditAuthority, AuditReporter, AuditRepository, SummaryPolicy } from "./contracts";
5
+ import { type AuditService } from "./service";
6
+ declare const configSchema: z.ZodObject<{
7
+ maxPageSize: z.ZodDefault<z.ZodNumber>;
8
+ }, z.core.$strict>;
9
+ export declare const auditConfig: import("@lenso/core").ConfigContract<z.ZodObject<{
10
+ maxPageSize: z.ZodDefault<z.ZodNumber>;
11
+ }, z.core.$strict>>;
12
+ export declare function createAuditPlugin<P>(options: {
13
+ id: string;
14
+ repository: Plugin<AuditRepository>;
15
+ authority: Plugin<AuditAuthority<P>>;
16
+ diagnostics?: Plugin<AuditReporter>;
17
+ summaryPolicy?: SummaryPolicy;
18
+ config?: z.input<typeof configSchema> | readonly ConfigSource[];
19
+ }): Plugin<AuditService<P>>;
20
+ export {};
package/dist/plugin.js ADDED
@@ -0,0 +1,37 @@
1
+ import {
2
+ createAuditService2
3
+ } from "./index-ph1dkars.js";
4
+
5
+ // src/plugin.ts
6
+ import { bindConfig, definePluginConfig } from "@lenso/core/config";
7
+ import { z } from "zod";
8
+ var configSchema = z.strictObject({
9
+ maxPageSize: z.number().int().min(1).max(500).default(100)
10
+ });
11
+ var auditConfig = definePluginConfig({
12
+ description: "Audit query page budget; identity and scope policy are trusted service bindings",
13
+ schema: configSchema
14
+ });
15
+ function createAuditPlugin(options) {
16
+ return bindConfig(auditConfig, options.config ?? {}, {
17
+ id: options.id,
18
+ requires: [
19
+ options.repository,
20
+ options.authority,
21
+ ...options.diagnostics ? [options.diagnostics] : []
22
+ ],
23
+ setup(context, config) {
24
+ return createAuditService2({
25
+ repository: context.get(options.repository),
26
+ authority: context.get(options.authority),
27
+ ...options.diagnostics ? { report: context.get(options.diagnostics) } : {},
28
+ summaryPolicy: options.summaryPolicy,
29
+ maxPageSize: config.maxPageSize
30
+ });
31
+ }
32
+ });
33
+ }
34
+ export {
35
+ auditConfig,
36
+ createAuditPlugin
37
+ };