@classytic/arc 1.1.0 → 2.1.3
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.
- package/README.md +247 -794
- package/bin/arc.js +91 -52
- package/dist/EventTransport-BkUDYZEb.d.mts +99 -0
- package/dist/HookSystem-BsGV-j2l.mjs +404 -0
- package/dist/ResourceRegistry-7Ic20ZMw.mjs +249 -0
- package/dist/adapters/index.d.mts +5 -0
- package/dist/adapters/index.mjs +3 -0
- package/dist/audit/index.d.mts +81 -0
- package/dist/audit/index.mjs +275 -0
- package/dist/audit/mongodb.d.mts +5 -0
- package/dist/audit/mongodb.mjs +3 -0
- package/dist/audited-CGdLiSlE.mjs +140 -0
- package/dist/auth/index.d.mts +188 -0
- package/dist/auth/index.mjs +1096 -0
- package/dist/auth/redis-session.d.mts +43 -0
- package/dist/auth/redis-session.mjs +75 -0
- package/dist/betterAuthOpenApi-DjWDddNc.mjs +249 -0
- package/dist/cache/index.d.mts +145 -0
- package/dist/cache/index.mjs +91 -0
- package/dist/caching-GSDJcA6-.mjs +93 -0
- package/dist/chunk-C7Uep-_p.mjs +20 -0
- package/dist/circuitBreaker-DYhWBW_D.mjs +1096 -0
- package/dist/cli/commands/describe.d.mts +18 -0
- package/dist/cli/commands/describe.mjs +238 -0
- package/dist/cli/commands/docs.d.mts +13 -0
- package/dist/cli/commands/docs.mjs +52 -0
- package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -2
- package/dist/cli/commands/generate.mjs +357 -0
- package/dist/cli/commands/{init.d.ts → init.d.mts} +11 -8
- package/dist/cli/commands/{init.js → init.mjs} +807 -617
- package/dist/cli/commands/introspect.d.mts +10 -0
- package/dist/cli/commands/introspect.mjs +75 -0
- package/dist/cli/index.d.mts +16 -0
- package/dist/cli/index.mjs +156 -0
- package/dist/constants-DdXFXQtN.mjs +84 -0
- package/dist/core/index.d.mts +5 -0
- package/dist/core/index.mjs +4 -0
- package/dist/createApp-D2D5XXaV.mjs +559 -0
- package/dist/defineResource-PXzSJ15_.mjs +2197 -0
- package/dist/discovery/index.d.mts +46 -0
- package/dist/discovery/index.mjs +109 -0
- package/dist/docs/index.d.mts +162 -0
- package/dist/docs/index.mjs +74 -0
- package/dist/elevation-DGo5shaX.d.mts +87 -0
- package/dist/elevation-DSTbVvYj.mjs +113 -0
- package/dist/errorHandler-C3GY3_ow.mjs +108 -0
- package/dist/errorHandler-CW3OOeYq.d.mts +72 -0
- package/dist/errors-DAWRdiYP.d.mts +124 -0
- package/dist/errors-DBANPbGr.mjs +211 -0
- package/dist/eventPlugin-BEOvaDqo.mjs +229 -0
- package/dist/eventPlugin-H6wDDjGO.d.mts +124 -0
- package/dist/events/index.d.mts +53 -0
- package/dist/events/index.mjs +51 -0
- package/dist/events/transports/redis-stream-entry.d.mts +2 -0
- package/dist/events/transports/redis-stream-entry.mjs +177 -0
- package/dist/events/transports/redis.d.mts +76 -0
- package/dist/events/transports/redis.mjs +124 -0
- package/dist/externalPaths-SyPF2tgK.d.mts +50 -0
- package/dist/factory/index.d.mts +63 -0
- package/dist/factory/index.mjs +3 -0
- package/dist/fastifyAdapter-C8DlE0YH.d.mts +216 -0
- package/dist/fields-Bi_AVKSo.d.mts +109 -0
- package/dist/fields-CTd_CrKr.mjs +114 -0
- package/dist/hooks/index.d.mts +4 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/idempotency/index.d.mts +96 -0
- package/dist/idempotency/index.mjs +319 -0
- package/dist/idempotency/mongodb.d.mts +2 -0
- package/dist/idempotency/mongodb.mjs +114 -0
- package/dist/idempotency/redis.d.mts +2 -0
- package/dist/idempotency/redis.mjs +103 -0
- package/dist/index.d.mts +260 -0
- package/dist/index.mjs +104 -0
- package/dist/integrations/event-gateway.d.mts +46 -0
- package/dist/integrations/event-gateway.mjs +43 -0
- package/dist/integrations/index.d.mts +5 -0
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs.d.mts +103 -0
- package/dist/integrations/jobs.mjs +123 -0
- package/dist/integrations/streamline.d.mts +60 -0
- package/dist/integrations/streamline.mjs +125 -0
- package/dist/integrations/websocket.d.mts +82 -0
- package/dist/integrations/websocket.mjs +288 -0
- package/dist/interface-CSNjltAc.d.mts +77 -0
- package/dist/interface-DTbsvIWe.d.mts +54 -0
- package/dist/interface-e9XfSsUV.d.mts +1097 -0
- package/dist/introspectionPlugin-B3JkrjwU.mjs +53 -0
- package/dist/keys-DhqDRxv3.mjs +42 -0
- package/dist/logger-ByrvQWZO.mjs +78 -0
- package/dist/memory-B2v7KrCB.mjs +143 -0
- package/dist/migrations/index.d.mts +156 -0
- package/dist/migrations/index.mjs +260 -0
- package/dist/mongodb-ClykrfGo.d.mts +118 -0
- package/dist/mongodb-DNKEExbf.mjs +93 -0
- package/dist/mongodb-Dg8O_gvd.d.mts +71 -0
- package/dist/openapi-9nB_kiuR.mjs +525 -0
- package/dist/org/index.d.mts +68 -0
- package/dist/org/index.mjs +513 -0
- package/dist/org/types.d.mts +82 -0
- package/dist/org/types.mjs +1 -0
- package/dist/permissions/index.d.mts +278 -0
- package/dist/permissions/index.mjs +579 -0
- package/dist/plugins/index.d.mts +172 -0
- package/dist/plugins/index.mjs +522 -0
- package/dist/plugins/response-cache.d.mts +87 -0
- package/dist/plugins/response-cache.mjs +283 -0
- package/dist/plugins/tracing-entry.d.mts +2 -0
- package/dist/plugins/tracing-entry.mjs +185 -0
- package/dist/pluralize-CM-jZg7p.mjs +86 -0
- package/dist/policies/{index.d.ts → index.d.mts} +204 -170
- package/dist/policies/index.mjs +321 -0
- package/dist/presets/{index.d.ts → index.d.mts} +62 -131
- package/dist/presets/index.mjs +143 -0
- package/dist/presets/multiTenant.d.mts +24 -0
- package/dist/presets/multiTenant.mjs +113 -0
- package/dist/presets-BTeYbw7h.d.mts +57 -0
- package/dist/presets-CeFtfDR8.mjs +119 -0
- package/dist/prisma-C3iornoK.d.mts +274 -0
- package/dist/prisma-DJbMt3yf.mjs +627 -0
- package/dist/queryCachePlugin-B6R0d4av.mjs +138 -0
- package/dist/queryCachePlugin-Q6SYuHZ6.d.mts +71 -0
- package/dist/redis-UwjEp8Ea.d.mts +49 -0
- package/dist/redis-stream-CBg0upHI.d.mts +103 -0
- package/dist/registry/index.d.mts +11 -0
- package/dist/registry/index.mjs +4 -0
- package/dist/requestContext-xi6OKBL-.mjs +55 -0
- package/dist/schemaConverter-Dtg0Kt9T.mjs +98 -0
- package/dist/schemas/index.d.mts +63 -0
- package/dist/schemas/index.mjs +82 -0
- package/dist/scope/index.d.mts +21 -0
- package/dist/scope/index.mjs +65 -0
- package/dist/sessionManager-D_iEHjQl.d.mts +186 -0
- package/dist/sse-DkqQ1uxb.mjs +123 -0
- package/dist/testing/index.d.mts +907 -0
- package/dist/testing/index.mjs +1976 -0
- package/dist/tracing-8CEbhF0w.d.mts +70 -0
- package/dist/typeGuards-DwxA1t_L.mjs +9 -0
- package/dist/types/index.d.mts +946 -0
- package/dist/types/index.mjs +14 -0
- package/dist/types-B0dhNrnd.d.mts +445 -0
- package/dist/types-Beqn1Un7.mjs +38 -0
- package/dist/types-DelU6kln.mjs +25 -0
- package/dist/types-RLkFVgaw.d.mts +101 -0
- package/dist/utils/index.d.mts +747 -0
- package/dist/utils/index.mjs +6 -0
- package/package.json +194 -68
- package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
- package/dist/adapters/index.d.ts +0 -237
- package/dist/adapters/index.js +0 -668
- package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
- package/dist/audit/index.d.ts +0 -195
- package/dist/audit/index.js +0 -319
- package/dist/auth/index.d.ts +0 -47
- package/dist/auth/index.js +0 -174
- package/dist/cli/commands/docs.d.ts +0 -11
- package/dist/cli/commands/docs.js +0 -474
- package/dist/cli/commands/generate.js +0 -334
- package/dist/cli/commands/introspect.d.ts +0 -8
- package/dist/cli/commands/introspect.js +0 -338
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/index.js +0 -3269
- package/dist/core/index.d.ts +0 -220
- package/dist/core/index.js +0 -2786
- package/dist/createApp-Ce9wl8W9.d.ts +0 -77
- package/dist/docs/index.d.ts +0 -166
- package/dist/docs/index.js +0 -658
- package/dist/errors-8WIxGS_6.d.ts +0 -122
- package/dist/events/index.d.ts +0 -117
- package/dist/events/index.js +0 -89
- package/dist/factory/index.d.ts +0 -38
- package/dist/factory/index.js +0 -1652
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.js +0 -199
- package/dist/idempotency/index.d.ts +0 -323
- package/dist/idempotency/index.js +0 -500
- package/dist/index-B4t03KQ0.d.ts +0 -1366
- package/dist/index.d.ts +0 -135
- package/dist/index.js +0 -4756
- package/dist/migrations/index.d.ts +0 -185
- package/dist/migrations/index.js +0 -274
- package/dist/org/index.d.ts +0 -129
- package/dist/org/index.js +0 -220
- package/dist/permissions/index.d.ts +0 -144
- package/dist/permissions/index.js +0 -103
- package/dist/plugins/index.d.ts +0 -46
- package/dist/plugins/index.js +0 -1069
- package/dist/policies/index.js +0 -196
- package/dist/presets/index.js +0 -384
- package/dist/presets/multiTenant.d.ts +0 -39
- package/dist/presets/multiTenant.js +0 -112
- package/dist/registry/index.d.ts +0 -16
- package/dist/registry/index.js +0 -253
- package/dist/testing/index.d.ts +0 -618
- package/dist/testing/index.js +0 -48020
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -8
- package/dist/types-B99TBmFV.d.ts +0 -76
- package/dist/types-BvckRbs2.d.ts +0 -143
- package/dist/utils/index.d.ts +0 -679
- package/dist/utils/index.js +0 -931
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import "../elevation-DGo5shaX.mjs";
|
|
2
|
+
import "../interface-e9XfSsUV.mjs";
|
|
3
|
+
import "../types-RLkFVgaw.mjs";
|
|
4
|
+
import { a as AuditContext, c as AuditStore, i as AuditAction, l as AuditStoreOptions, n as MongoAuditStoreOptions, o as AuditEntry, r as MongoConnection, s as AuditQueryOptions, u as createAuditEntry } from "../mongodb-ClykrfGo.mjs";
|
|
5
|
+
import { FastifyPluginAsync } from "fastify";
|
|
6
|
+
|
|
7
|
+
//#region src/audit/auditPlugin.d.ts
|
|
8
|
+
interface AuditPluginOptions {
|
|
9
|
+
/** Enable audit logging (default: false) */
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
/** Storage backends to use */
|
|
12
|
+
stores?: ('memory' | 'mongodb')[];
|
|
13
|
+
/** MongoDB connection (required if using mongodb store) */
|
|
14
|
+
mongoConnection?: MongoConnection;
|
|
15
|
+
/** MongoDB collection name (default: 'audit_logs') */
|
|
16
|
+
mongoCollection?: string;
|
|
17
|
+
/** TTL in days for MongoDB (default: 90) */
|
|
18
|
+
ttlDays?: number;
|
|
19
|
+
/** Custom stores (advanced) */
|
|
20
|
+
customStores?: AuditStore[];
|
|
21
|
+
/**
|
|
22
|
+
* Automatically audit CRUD operations via the hook system (default: true when enabled).
|
|
23
|
+
* When enabled, create/update/delete operations are auto-logged without manual calls.
|
|
24
|
+
*
|
|
25
|
+
* - `true`: Auto-audit all CRUD operations on all resources
|
|
26
|
+
* - `{ operations: ['create', 'delete'] }`: Only auto-audit specific operations
|
|
27
|
+
* - `{ exclude: ['health', 'metrics'] }`: Skip specific resources
|
|
28
|
+
* - `false`: Disable auto-audit (manual calls only)
|
|
29
|
+
*/
|
|
30
|
+
autoAudit?: boolean | {
|
|
31
|
+
operations?: ('create' | 'update' | 'delete')[];
|
|
32
|
+
exclude?: string[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
declare module 'fastify' {
|
|
36
|
+
interface FastifyInstance {
|
|
37
|
+
/** Log an audit entry */
|
|
38
|
+
audit: AuditLogger;
|
|
39
|
+
}
|
|
40
|
+
interface FastifyRequest {
|
|
41
|
+
/** Audit context for current request */
|
|
42
|
+
auditContext?: AuditContext;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
interface AuditLogger {
|
|
46
|
+
/** Log a create action */
|
|
47
|
+
create: (resource: string, documentId: string, data: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
48
|
+
/** Log an update action */
|
|
49
|
+
update: (resource: string, documentId: string, before: Record<string, unknown>, after: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
50
|
+
/** Log a delete action */
|
|
51
|
+
delete: (resource: string, documentId: string, data: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
52
|
+
/** Log a restore action (soft delete undo) */
|
|
53
|
+
restore: (resource: string, documentId: string, data: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
54
|
+
/** Log a custom action */
|
|
55
|
+
custom: (resource: string, documentId: string, action: string, data?: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
56
|
+
/** Query audit logs (if stores support it) */
|
|
57
|
+
query: (options: AuditQueryOptions) => Promise<AuditEntry[]>;
|
|
58
|
+
}
|
|
59
|
+
declare const auditPlugin: FastifyPluginAsync<AuditPluginOptions>;
|
|
60
|
+
declare const _default: FastifyPluginAsync<AuditPluginOptions>;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/audit/stores/memory.d.ts
|
|
63
|
+
interface MemoryAuditStoreOptions {
|
|
64
|
+
/** Maximum entries to keep (default: 1000) */
|
|
65
|
+
maxEntries?: number;
|
|
66
|
+
}
|
|
67
|
+
declare class MemoryAuditStore implements AuditStore {
|
|
68
|
+
readonly name = "memory";
|
|
69
|
+
private entries;
|
|
70
|
+
private maxEntries;
|
|
71
|
+
constructor(options?: MemoryAuditStoreOptions);
|
|
72
|
+
log(entry: AuditEntry): Promise<void>;
|
|
73
|
+
query(options?: AuditQueryOptions): Promise<AuditEntry[]>;
|
|
74
|
+
close(): Promise<void>;
|
|
75
|
+
/** Get all entries (for testing) */
|
|
76
|
+
getAll(): AuditEntry[];
|
|
77
|
+
/** Clear all entries (for testing) */
|
|
78
|
+
clear(): void;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { type AuditAction, type AuditContext, type AuditEntry, type AuditLogger, type AuditPluginOptions, type AuditQueryOptions, type AuditStore, type AuditStoreOptions, MemoryAuditStore, type MemoryAuditStoreOptions, type MongoAuditStoreOptions, _default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { t as MongoAuditStore } from "../mongodb-DNKEExbf.mjs";
|
|
2
|
+
import fp from "fastify-plugin";
|
|
3
|
+
|
|
4
|
+
//#region src/audit/stores/interface.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create audit entry from context
|
|
7
|
+
*/
|
|
8
|
+
function createAuditEntry(resource, documentId, action, context, data) {
|
|
9
|
+
const changes = data?.before && data?.after ? detectChanges(data.before, data.after) : void 0;
|
|
10
|
+
return {
|
|
11
|
+
id: generateAuditId(),
|
|
12
|
+
resource,
|
|
13
|
+
documentId,
|
|
14
|
+
action,
|
|
15
|
+
userId: context.user?._id?.toString() ?? context.user?.id,
|
|
16
|
+
organizationId: context.organizationId,
|
|
17
|
+
before: data?.before,
|
|
18
|
+
after: data?.after,
|
|
19
|
+
changes,
|
|
20
|
+
requestId: context.requestId,
|
|
21
|
+
ipAddress: context.ipAddress,
|
|
22
|
+
userAgent: context.userAgent,
|
|
23
|
+
metadata: data?.metadata,
|
|
24
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Detect changed fields between two objects
|
|
29
|
+
*/
|
|
30
|
+
function detectChanges(before, after) {
|
|
31
|
+
const changes = [];
|
|
32
|
+
const allKeys = new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
33
|
+
for (const key of allKeys) {
|
|
34
|
+
if (key.startsWith("_") || key === "updatedAt") continue;
|
|
35
|
+
if (JSON.stringify(before[key]) !== JSON.stringify(after[key])) changes.push(key);
|
|
36
|
+
}
|
|
37
|
+
return changes;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate unique audit ID
|
|
41
|
+
*/
|
|
42
|
+
function generateAuditId() {
|
|
43
|
+
return `aud_${Date.now().toString(36)}${Math.random().toString(36).substring(2, 10)}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/audit/stores/memory.ts
|
|
48
|
+
var MemoryAuditStore = class {
|
|
49
|
+
name = "memory";
|
|
50
|
+
entries = [];
|
|
51
|
+
maxEntries;
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.maxEntries = options.maxEntries ?? 1e3;
|
|
54
|
+
}
|
|
55
|
+
async log(entry) {
|
|
56
|
+
this.entries.unshift(entry);
|
|
57
|
+
if (this.entries.length > this.maxEntries) this.entries = this.entries.slice(0, this.maxEntries);
|
|
58
|
+
}
|
|
59
|
+
async query(options = {}) {
|
|
60
|
+
let results = [...this.entries];
|
|
61
|
+
if (options.resource) results = results.filter((e) => e.resource === options.resource);
|
|
62
|
+
if (options.documentId) results = results.filter((e) => e.documentId === options.documentId);
|
|
63
|
+
if (options.userId) results = results.filter((e) => e.userId === options.userId);
|
|
64
|
+
if (options.organizationId) results = results.filter((e) => e.organizationId === options.organizationId);
|
|
65
|
+
if (options.action) {
|
|
66
|
+
const actions = Array.isArray(options.action) ? options.action : [options.action];
|
|
67
|
+
results = results.filter((e) => actions.includes(e.action));
|
|
68
|
+
}
|
|
69
|
+
if (options.from) results = results.filter((e) => e.timestamp >= options.from);
|
|
70
|
+
if (options.to) results = results.filter((e) => e.timestamp <= options.to);
|
|
71
|
+
const offset = options.offset ?? 0;
|
|
72
|
+
const limit = options.limit ?? 100;
|
|
73
|
+
results = results.slice(offset, offset + limit);
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
async close() {
|
|
77
|
+
this.entries = [];
|
|
78
|
+
}
|
|
79
|
+
/** Get all entries (for testing) */
|
|
80
|
+
getAll() {
|
|
81
|
+
return [...this.entries];
|
|
82
|
+
}
|
|
83
|
+
/** Clear all entries (for testing) */
|
|
84
|
+
clear() {
|
|
85
|
+
this.entries = [];
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/audit/auditPlugin.ts
|
|
91
|
+
/**
|
|
92
|
+
* Audit Plugin
|
|
93
|
+
*
|
|
94
|
+
* Optional audit trail with flexible storage options.
|
|
95
|
+
* Disabled by default - enable explicitly for enterprise use cases.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* import { auditPlugin } from '@classytic/arc/audit';
|
|
99
|
+
*
|
|
100
|
+
* // Development: in-memory
|
|
101
|
+
* await fastify.register(auditPlugin, {
|
|
102
|
+
* enabled: true,
|
|
103
|
+
* stores: ['memory'],
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* // Production: MongoDB with TTL
|
|
107
|
+
* await fastify.register(auditPlugin, {
|
|
108
|
+
* enabled: true,
|
|
109
|
+
* stores: ['mongodb'],
|
|
110
|
+
* mongoConnection: mongoose.connection,
|
|
111
|
+
* ttlDays: 90,
|
|
112
|
+
* });
|
|
113
|
+
*/
|
|
114
|
+
const auditPlugin = async (fastify, opts = {}) => {
|
|
115
|
+
const { enabled = false, stores: storeTypes = ["memory"], mongoConnection, mongoCollection = "audit_logs", ttlDays = 90, customStores = [] } = opts;
|
|
116
|
+
if (!enabled) {
|
|
117
|
+
fastify.decorate("audit", createNoopLogger());
|
|
118
|
+
fastify.decorateRequest("auditContext", void 0);
|
|
119
|
+
fastify.log?.debug?.("Audit plugin disabled");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const stores = [...customStores];
|
|
123
|
+
for (const type of storeTypes) switch (type) {
|
|
124
|
+
case "memory":
|
|
125
|
+
stores.push(new MemoryAuditStore());
|
|
126
|
+
break;
|
|
127
|
+
case "mongodb":
|
|
128
|
+
if (!mongoConnection) throw new Error("Audit: mongoConnection required for mongodb store");
|
|
129
|
+
stores.push(new MongoAuditStore({
|
|
130
|
+
connection: mongoConnection,
|
|
131
|
+
collection: mongoCollection,
|
|
132
|
+
ttlDays
|
|
133
|
+
}));
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (stores.length === 0) throw new Error("Audit: at least one store must be configured");
|
|
137
|
+
async function logToStores(entry) {
|
|
138
|
+
await Promise.all(stores.map((store) => store.log(entry)));
|
|
139
|
+
}
|
|
140
|
+
const audit = {
|
|
141
|
+
async create(resource, documentId, data, context) {
|
|
142
|
+
await logToStores(createAuditEntry(resource, documentId, "create", context ?? {}, { after: data }));
|
|
143
|
+
},
|
|
144
|
+
async update(resource, documentId, before, after, context) {
|
|
145
|
+
await logToStores(createAuditEntry(resource, documentId, "update", context ?? {}, {
|
|
146
|
+
before,
|
|
147
|
+
after
|
|
148
|
+
}));
|
|
149
|
+
},
|
|
150
|
+
async delete(resource, documentId, data, context) {
|
|
151
|
+
await logToStores(createAuditEntry(resource, documentId, "delete", context ?? {}, { before: data }));
|
|
152
|
+
},
|
|
153
|
+
async restore(resource, documentId, data, context) {
|
|
154
|
+
await logToStores(createAuditEntry(resource, documentId, "restore", context ?? {}, { after: data }));
|
|
155
|
+
},
|
|
156
|
+
async custom(resource, documentId, action, data, context) {
|
|
157
|
+
await logToStores(createAuditEntry(resource, documentId, "custom", context ?? {}, { metadata: {
|
|
158
|
+
customAction: action,
|
|
159
|
+
...data
|
|
160
|
+
} }));
|
|
161
|
+
},
|
|
162
|
+
async query(options) {
|
|
163
|
+
for (const store of stores) if (store.query) return store.query(options);
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
fastify.decorate("audit", audit);
|
|
168
|
+
fastify.decorateRequest("auditContext", void 0);
|
|
169
|
+
fastify.addHook("onRequest", async (request) => {
|
|
170
|
+
const user = request.user;
|
|
171
|
+
request.context;
|
|
172
|
+
const scope = request.scope;
|
|
173
|
+
request.auditContext = {
|
|
174
|
+
user,
|
|
175
|
+
organizationId: scope?.kind === "member" ? scope.organizationId : scope?.kind === "elevated" ? scope.organizationId : void 0,
|
|
176
|
+
requestId: request.id,
|
|
177
|
+
ipAddress: request.ip,
|
|
178
|
+
userAgent: request.headers["user-agent"],
|
|
179
|
+
endpoint: void 0,
|
|
180
|
+
duration: void 0
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
184
|
+
if (request.auditContext) {
|
|
185
|
+
request.auditContext.endpoint = `${request.method} ${request.routeOptions?.url ?? request.url}`;
|
|
186
|
+
request.auditContext.duration = Math.round(reply.elapsedTime);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
fastify.addHook("onClose", async () => {
|
|
190
|
+
await Promise.all(stores.map((store) => store.close?.()));
|
|
191
|
+
});
|
|
192
|
+
const autoAuditConfig = opts.autoAudit ?? true;
|
|
193
|
+
if (autoAuditConfig !== false) {
|
|
194
|
+
const defaultOps = [
|
|
195
|
+
"create",
|
|
196
|
+
"update",
|
|
197
|
+
"delete"
|
|
198
|
+
];
|
|
199
|
+
const ops = typeof autoAuditConfig === "object" ? autoAuditConfig.operations ?? defaultOps : defaultOps;
|
|
200
|
+
const excludeResources = new Set(typeof autoAuditConfig === "object" ? autoAuditConfig.exclude ?? [] : []);
|
|
201
|
+
fastify.addHook("onReady", async () => {
|
|
202
|
+
const arc = "arc" in fastify ? fastify.arc : void 0;
|
|
203
|
+
if (!arc?.hooks) {
|
|
204
|
+
fastify.log?.debug?.("Auto-audit skipped: arc-core plugin not registered");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
for (const op of ops) arc.hooks.after("*", op, async (ctx) => {
|
|
208
|
+
if (excludeResources.has(ctx.resource)) return;
|
|
209
|
+
const docId = autoAuditExtractId(ctx.result);
|
|
210
|
+
const scope = ctx.context?._scope;
|
|
211
|
+
const auditCtx = {
|
|
212
|
+
user: ctx.user,
|
|
213
|
+
organizationId: scope ? autoAuditGetOrgId(scope) : void 0
|
|
214
|
+
};
|
|
215
|
+
try {
|
|
216
|
+
if (op === "create") await audit.create(ctx.resource, docId, autoAuditToPlain(ctx.result), auditCtx);
|
|
217
|
+
else if (op === "update") await audit.update(ctx.resource, docId, autoAuditToPlain(ctx.meta?.existing), autoAuditToPlain(ctx.result), auditCtx);
|
|
218
|
+
else if (op === "delete") await audit.delete(ctx.resource, docId, autoAuditToPlain(ctx.result), auditCtx);
|
|
219
|
+
} catch (err) {
|
|
220
|
+
fastify.log?.warn?.({
|
|
221
|
+
resource: ctx.resource,
|
|
222
|
+
op,
|
|
223
|
+
err
|
|
224
|
+
}, "Auto-audit failed");
|
|
225
|
+
}
|
|
226
|
+
}, 90);
|
|
227
|
+
fastify.log?.debug?.({
|
|
228
|
+
ops,
|
|
229
|
+
exclude: [...excludeResources]
|
|
230
|
+
}, "Auto-audit hooks registered");
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
fastify.log?.debug?.({ stores: storeTypes }, "Audit plugin enabled");
|
|
234
|
+
};
|
|
235
|
+
/** Extract document ID from a result */
|
|
236
|
+
function autoAuditExtractId(doc) {
|
|
237
|
+
if (!doc || typeof doc !== "object") return "";
|
|
238
|
+
const d = doc;
|
|
239
|
+
const rawId = d._id ?? d.id;
|
|
240
|
+
return rawId != null ? String(rawId) : "";
|
|
241
|
+
}
|
|
242
|
+
/** Convert Mongoose doc or plain object to plain object */
|
|
243
|
+
function autoAuditToPlain(doc) {
|
|
244
|
+
if (!doc || typeof doc !== "object") return {};
|
|
245
|
+
if (typeof doc.toObject === "function") return doc.toObject();
|
|
246
|
+
return doc;
|
|
247
|
+
}
|
|
248
|
+
/** Extract org ID from scope (avoids importing scope module to prevent circular deps) */
|
|
249
|
+
function autoAuditGetOrgId(scope) {
|
|
250
|
+
if (!scope || typeof scope !== "object") return void 0;
|
|
251
|
+
const s = scope;
|
|
252
|
+
if (s.kind === "member" || s.kind === "elevated") return s.organizationId;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Create no-op logger for when audit is disabled
|
|
256
|
+
*/
|
|
257
|
+
function createNoopLogger() {
|
|
258
|
+
const noop = async () => {};
|
|
259
|
+
return {
|
|
260
|
+
create: noop,
|
|
261
|
+
update: noop,
|
|
262
|
+
delete: noop,
|
|
263
|
+
restore: noop,
|
|
264
|
+
custom: noop,
|
|
265
|
+
query: async () => []
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
var auditPlugin_default = fp(auditPlugin, {
|
|
269
|
+
name: "arc-audit",
|
|
270
|
+
fastify: "5.x",
|
|
271
|
+
dependencies: ["arc-core"]
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
//#endregion
|
|
275
|
+
export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { c as isElevated, n as PUBLIC_SCOPE } from "./types-Beqn1Un7.mjs";
|
|
2
|
+
import { allowPublic, requireRoles } from "./permissions/index.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/presets/softDelete.ts
|
|
5
|
+
function softDeletePreset() {
|
|
6
|
+
return {
|
|
7
|
+
name: "softDelete",
|
|
8
|
+
additionalRoutes: (permissions) => [{
|
|
9
|
+
method: "GET",
|
|
10
|
+
path: "/deleted",
|
|
11
|
+
handler: "getDeleted",
|
|
12
|
+
summary: "Get soft-deleted items",
|
|
13
|
+
permissions: permissions.list ?? requireRoles(["admin"]),
|
|
14
|
+
wrapHandler: true,
|
|
15
|
+
operation: "listDeleted"
|
|
16
|
+
}, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
path: "/:id/restore",
|
|
19
|
+
handler: "restore",
|
|
20
|
+
summary: "Restore soft-deleted item",
|
|
21
|
+
permissions: permissions.update ?? requireRoles(["admin"]),
|
|
22
|
+
wrapHandler: true,
|
|
23
|
+
operation: "restore"
|
|
24
|
+
}]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/presets/slugLookup.ts
|
|
30
|
+
function slugLookupPreset(options = {}) {
|
|
31
|
+
const { slugField = "slug" } = options;
|
|
32
|
+
return {
|
|
33
|
+
name: "slugLookup",
|
|
34
|
+
additionalRoutes: (permissions) => [{
|
|
35
|
+
method: "GET",
|
|
36
|
+
path: `/slug/:${slugField}`,
|
|
37
|
+
handler: "getBySlug",
|
|
38
|
+
summary: "Get by slug",
|
|
39
|
+
permissions: permissions.get ?? allowPublic(),
|
|
40
|
+
wrapHandler: true,
|
|
41
|
+
operation: "getBySlug"
|
|
42
|
+
}],
|
|
43
|
+
controllerOptions: { slugField }
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/presets/ownedByUser.ts
|
|
49
|
+
/**
|
|
50
|
+
* Create ownership check middleware.
|
|
51
|
+
* Elevated scope (platform admin) bypasses ownership checks.
|
|
52
|
+
*/
|
|
53
|
+
function createOwnershipCheck(ownerField) {
|
|
54
|
+
return async (request, _reply) => {
|
|
55
|
+
const user = request.user;
|
|
56
|
+
if (!user) return;
|
|
57
|
+
if (isElevated(request.scope ?? PUBLIC_SCOPE)) return;
|
|
58
|
+
const userWithId = user;
|
|
59
|
+
const userId = userWithId._id ?? userWithId.id;
|
|
60
|
+
if (userId) request._ownershipCheck = {
|
|
61
|
+
field: ownerField,
|
|
62
|
+
userId
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function ownedByUserPreset(options = {}) {
|
|
67
|
+
const { ownerField = "userId" } = options;
|
|
68
|
+
const ownershipMiddleware = createOwnershipCheck(ownerField);
|
|
69
|
+
return {
|
|
70
|
+
name: "ownedByUser",
|
|
71
|
+
middlewares: {
|
|
72
|
+
update: [ownershipMiddleware],
|
|
73
|
+
delete: [ownershipMiddleware]
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/presets/tree.ts
|
|
80
|
+
function treePreset(options = {}) {
|
|
81
|
+
const { parentField = "parent" } = options;
|
|
82
|
+
return {
|
|
83
|
+
name: "tree",
|
|
84
|
+
additionalRoutes: (permissions) => [{
|
|
85
|
+
method: "GET",
|
|
86
|
+
path: "/tree",
|
|
87
|
+
handler: "getTree",
|
|
88
|
+
summary: "Get hierarchical tree",
|
|
89
|
+
permissions: permissions.list ?? allowPublic(),
|
|
90
|
+
wrapHandler: true,
|
|
91
|
+
operation: "getTree"
|
|
92
|
+
}, {
|
|
93
|
+
method: "GET",
|
|
94
|
+
path: `/:${parentField}/children`,
|
|
95
|
+
handler: "getChildren",
|
|
96
|
+
summary: "Get children of parent",
|
|
97
|
+
permissions: permissions.list ?? allowPublic(),
|
|
98
|
+
wrapHandler: true,
|
|
99
|
+
operation: "getChildren"
|
|
100
|
+
}],
|
|
101
|
+
controllerOptions: { parentField }
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/presets/audited.ts
|
|
107
|
+
/**
|
|
108
|
+
* Audited preset - adds createdBy/updatedBy tracking
|
|
109
|
+
*/
|
|
110
|
+
function auditedPreset(options = {}) {
|
|
111
|
+
const { createdByField = "createdBy", updatedByField = "updatedBy" } = options;
|
|
112
|
+
const injectCreatedBy = async (request, _reply) => {
|
|
113
|
+
const userWithId = request.user;
|
|
114
|
+
if (userWithId?._id || userWithId?.id) {
|
|
115
|
+
const userId = userWithId._id ?? userWithId.id;
|
|
116
|
+
request.body[createdByField] = userId;
|
|
117
|
+
request.body[updatedByField] = userId;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const injectUpdatedBy = async (request, _reply) => {
|
|
121
|
+
const userWithId = request.user;
|
|
122
|
+
if (userWithId?._id || userWithId?.id) request.body[updatedByField] = userWithId._id ?? userWithId.id;
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
name: "audited",
|
|
126
|
+
schemaOptions: { fieldRules: {
|
|
127
|
+
[createdByField]: { systemManaged: true },
|
|
128
|
+
[updatedByField]: { systemManaged: true },
|
|
129
|
+
createdAt: { systemManaged: true },
|
|
130
|
+
updatedAt: { systemManaged: true }
|
|
131
|
+
} },
|
|
132
|
+
middlewares: {
|
|
133
|
+
create: [injectCreatedBy],
|
|
134
|
+
update: [injectUpdatedBy]
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
export { softDeletePreset as a, slugLookupPreset as i, treePreset as n, ownedByUserPreset as r, auditedPreset as t };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import "../elevation-DGo5shaX.mjs";
|
|
2
|
+
import "../interface-e9XfSsUV.mjs";
|
|
3
|
+
import { t as PermissionCheck } from "../types-RLkFVgaw.mjs";
|
|
4
|
+
import { AuthHelpers, AuthPluginOptions } from "../types/index.mjs";
|
|
5
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-SyPF2tgK.mjs";
|
|
6
|
+
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-D_iEHjQl.mjs";
|
|
7
|
+
import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest as FastifyRequest$1 } from "fastify";
|
|
8
|
+
|
|
9
|
+
//#region src/auth/authPlugin.d.ts
|
|
10
|
+
declare module 'fastify' {
|
|
11
|
+
interface FastifyInstance {
|
|
12
|
+
/** Authenticate middleware - use in preHandler for protected routes */
|
|
13
|
+
authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
14
|
+
/** Optional authenticate - parses JWT if present, doesn't fail if absent */
|
|
15
|
+
optionalAuthenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
16
|
+
/** Authorize middleware factory - checks if user has required roles */
|
|
17
|
+
authorize: (...roles: string[]) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
18
|
+
/** Auth helpers - issueTokens, jwt utilities */
|
|
19
|
+
auth: AuthHelpers;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare const authPlugin: FastifyPluginAsync<AuthPluginOptions>;
|
|
23
|
+
declare const _default: FastifyPluginAsync<AuthPluginOptions>;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/auth/betterAuth.d.ts
|
|
26
|
+
declare module 'fastify' {
|
|
27
|
+
interface FastifyRequest {
|
|
28
|
+
/** Raw request body (from @fastify/raw-body plugin, if registered) */
|
|
29
|
+
rawBody?: Buffer | string;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Minimal interface for a Better Auth instance.
|
|
34
|
+
* We only require the `handler` method -- the full Better Auth type
|
|
35
|
+
* comes from the user's `better-auth` installation.
|
|
36
|
+
*/
|
|
37
|
+
interface BetterAuthHandler {
|
|
38
|
+
handler: (request: Request) => Promise<Response>;
|
|
39
|
+
/** The API endpoint map — each value has .path and .options. Used for OpenAPI docs extraction. */
|
|
40
|
+
api?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
interface BetterAuthAdapterOptions {
|
|
43
|
+
/** Better Auth instance (from betterAuth() in user's app) */
|
|
44
|
+
auth: BetterAuthHandler;
|
|
45
|
+
/** Base path for auth routes (default: '/api/auth') */
|
|
46
|
+
basePath?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Enable org context extraction from Better Auth's organization plugin.
|
|
49
|
+
* When enabled, the adapter will look up the user's active organization
|
|
50
|
+
* membership and populate `request.scope` with org roles.
|
|
51
|
+
*
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
orgContext?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* OpenAPI documentation for auth endpoints.
|
|
57
|
+
* - `true` (default): auto-extract from auth.api if available
|
|
58
|
+
* - `false`: disable (auth routes won't appear in OpenAPI docs)
|
|
59
|
+
* - `ExternalOpenApiPaths`: manual spec override
|
|
60
|
+
*/
|
|
61
|
+
openapi?: boolean | ExternalOpenApiPaths;
|
|
62
|
+
/**
|
|
63
|
+
* Additional user fields from Better Auth config.
|
|
64
|
+
* These get merged into signUpEmail/updateUser request body schemas
|
|
65
|
+
* and the User component schema in OpenAPI docs.
|
|
66
|
+
*
|
|
67
|
+
* Fields with `input: false` are excluded from request bodies
|
|
68
|
+
* but still appear in the User component schema (output-only).
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* userFields: {
|
|
73
|
+
* department: { type: 'string', description: 'Department', required: true },
|
|
74
|
+
* roles: { type: 'array', description: 'User roles', input: false },
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
userFields?: Record<string, {
|
|
79
|
+
type: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
required?: boolean;
|
|
82
|
+
input?: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Expose detailed auth error messages in 401 responses.
|
|
86
|
+
* When false (default), returns generic "Authentication required".
|
|
87
|
+
* When true, includes the actual error message for debugging.
|
|
88
|
+
*/
|
|
89
|
+
exposeAuthErrors?: boolean;
|
|
90
|
+
}
|
|
91
|
+
interface BetterAuthAdapterResult {
|
|
92
|
+
/** Fastify plugin that registers catch-all auth routes */
|
|
93
|
+
plugin: FastifyPluginAsync;
|
|
94
|
+
/** Authenticate preHandler -- validates session via Better Auth */
|
|
95
|
+
authenticate: (request: FastifyRequest$1, reply: FastifyReply$1) => Promise<void>;
|
|
96
|
+
/** Optional authenticate -- resolves session silently, continues as unauthenticated on failure */
|
|
97
|
+
optionalAuthenticate: (request: FastifyRequest$1, reply: FastifyReply$1) => Promise<void>;
|
|
98
|
+
/** Permission helpers bound to this auth adapter (available when orgContext is enabled) */
|
|
99
|
+
permissions: {
|
|
100
|
+
requireOrgRole: (...roles: string[]) => PermissionCheck;
|
|
101
|
+
requireOrgMembership: () => PermissionCheck;
|
|
102
|
+
requireTeamMembership: () => PermissionCheck;
|
|
103
|
+
};
|
|
104
|
+
/** OpenAPI paths extracted from Better Auth endpoints (undefined if openapi: false) */
|
|
105
|
+
openapi?: ExternalOpenApiPaths;
|
|
106
|
+
}
|
|
107
|
+
declare module 'fastify' {
|
|
108
|
+
interface FastifyInstance {
|
|
109
|
+
/**
|
|
110
|
+
* Authenticate middleware (Better Auth variant).
|
|
111
|
+
* Validates session by calling Better Auth's session endpoint internally.
|
|
112
|
+
* Set by the Better Auth adapter plugin.
|
|
113
|
+
*/
|
|
114
|
+
authenticate: (request: FastifyRequest$1, reply: FastifyReply$1) => Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Optional authenticate middleware (Better Auth variant).
|
|
117
|
+
* Tries to resolve session silently — populates request.user if valid,
|
|
118
|
+
* continues as unauthenticated if no session or invalid session.
|
|
119
|
+
* Used on allowPublic() routes so downstream middleware can apply
|
|
120
|
+
* org-scoped queries when a user IS authenticated.
|
|
121
|
+
*/
|
|
122
|
+
optionalAuthenticate: (request: FastifyRequest$1, reply: FastifyReply$1) => Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Create a Better Auth adapter for Arc/Fastify.
|
|
127
|
+
*
|
|
128
|
+
* Returns a Fastify plugin (registers catch-all auth routes) and an
|
|
129
|
+
* `authenticate` preHandler that validates sessions via Better Auth.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* import { betterAuth } from 'better-auth';
|
|
134
|
+
* import { createBetterAuthAdapter } from '@classytic/arc/auth';
|
|
135
|
+
*
|
|
136
|
+
* const auth = betterAuth({
|
|
137
|
+
* database: ...,
|
|
138
|
+
* emailAndPassword: { enabled: true },
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* const { plugin, authenticate } = createBetterAuthAdapter({ auth });
|
|
142
|
+
*
|
|
143
|
+
* // Register the plugin (catch-all auth routes)
|
|
144
|
+
* await fastify.register(plugin);
|
|
145
|
+
*
|
|
146
|
+
* // Use authenticate as a preHandler on protected routes
|
|
147
|
+
* fastify.get('/me', { preHandler: [authenticate] }, handler);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare function createBetterAuthAdapter(options: BetterAuthAdapterOptions): BetterAuthAdapterResult;
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/auth/betterAuthOpenApi.d.ts
|
|
153
|
+
interface BetterAuthOpenApiOptions {
|
|
154
|
+
/** Base path prefix for auth routes (default: '/api/auth') */
|
|
155
|
+
basePath?: string;
|
|
156
|
+
/** Tag name for auth routes in OpenAPI (default: 'Authentication') */
|
|
157
|
+
tagName?: string;
|
|
158
|
+
/** Tag description */
|
|
159
|
+
tagDescription?: string;
|
|
160
|
+
/** Exclude specific paths from the spec (e.g. ['/ok', '/error']) */
|
|
161
|
+
excludePaths?: string[];
|
|
162
|
+
/** Exclude SERVER_ONLY endpoints (default: true) */
|
|
163
|
+
excludeServerOnly?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Additional user fields from Better Auth config.
|
|
166
|
+
* These get merged into signUpEmail/updateUser request body schemas
|
|
167
|
+
* and the User component schema for $ref resolution.
|
|
168
|
+
*
|
|
169
|
+
* Fields with `input: false` are excluded from request bodies
|
|
170
|
+
* but still appear in the User component schema (output-only).
|
|
171
|
+
*/
|
|
172
|
+
userFields?: Record<string, {
|
|
173
|
+
type: string;
|
|
174
|
+
description?: string; /** Whether this field is required in sign-up (default: false) */
|
|
175
|
+
required?: boolean; /** Whether this field is accepted in request body (default: true). Set false for output-only fields. */
|
|
176
|
+
input?: boolean;
|
|
177
|
+
}>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Extract OpenAPI paths from a Better Auth instance's API object.
|
|
181
|
+
*
|
|
182
|
+
* Walks `authApi` (the `auth.api` object from Better Auth), discovers
|
|
183
|
+
* endpoints, converts their Zod schemas to JSON Schema via `z.toJSONSchema()`,
|
|
184
|
+
* and returns a complete `ExternalOpenApiPaths` object ready for Arc's spec builder.
|
|
185
|
+
*/
|
|
186
|
+
declare function extractBetterAuthOpenApi(authApi: Record<string, unknown>, options?: BetterAuthOpenApiOptions): ExternalOpenApiPaths;
|
|
187
|
+
//#endregion
|
|
188
|
+
export { type AuthPluginOptions, type BetterAuthAdapterOptions, type BetterAuthAdapterResult, type BetterAuthHandler, type BetterAuthOpenApiOptions, MemorySessionStore, type MemorySessionStoreOptions, type SessionCookieOptions, type SessionData, type SessionManagerOptions, type SessionManagerResult, type SessionStore, _default as authPlugin, authPlugin as authPluginFn, createBetterAuthAdapter, createSessionManager, extractBetterAuthOpenApi };
|