@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.
Files changed (200) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BkUDYZEb.d.mts +99 -0
  4. package/dist/HookSystem-BsGV-j2l.mjs +404 -0
  5. package/dist/ResourceRegistry-7Ic20ZMw.mjs +249 -0
  6. package/dist/adapters/index.d.mts +5 -0
  7. package/dist/adapters/index.mjs +3 -0
  8. package/dist/audit/index.d.mts +81 -0
  9. package/dist/audit/index.mjs +275 -0
  10. package/dist/audit/mongodb.d.mts +5 -0
  11. package/dist/audit/mongodb.mjs +3 -0
  12. package/dist/audited-CGdLiSlE.mjs +140 -0
  13. package/dist/auth/index.d.mts +188 -0
  14. package/dist/auth/index.mjs +1096 -0
  15. package/dist/auth/redis-session.d.mts +43 -0
  16. package/dist/auth/redis-session.mjs +75 -0
  17. package/dist/betterAuthOpenApi-DjWDddNc.mjs +249 -0
  18. package/dist/cache/index.d.mts +145 -0
  19. package/dist/cache/index.mjs +91 -0
  20. package/dist/caching-GSDJcA6-.mjs +93 -0
  21. package/dist/chunk-C7Uep-_p.mjs +20 -0
  22. package/dist/circuitBreaker-DYhWBW_D.mjs +1096 -0
  23. package/dist/cli/commands/describe.d.mts +18 -0
  24. package/dist/cli/commands/describe.mjs +238 -0
  25. package/dist/cli/commands/docs.d.mts +13 -0
  26. package/dist/cli/commands/docs.mjs +52 -0
  27. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -2
  28. package/dist/cli/commands/generate.mjs +357 -0
  29. package/dist/cli/commands/{init.d.ts → init.d.mts} +11 -8
  30. package/dist/cli/commands/{init.js → init.mjs} +807 -617
  31. package/dist/cli/commands/introspect.d.mts +10 -0
  32. package/dist/cli/commands/introspect.mjs +75 -0
  33. package/dist/cli/index.d.mts +16 -0
  34. package/dist/cli/index.mjs +156 -0
  35. package/dist/constants-DdXFXQtN.mjs +84 -0
  36. package/dist/core/index.d.mts +5 -0
  37. package/dist/core/index.mjs +4 -0
  38. package/dist/createApp-D2D5XXaV.mjs +559 -0
  39. package/dist/defineResource-PXzSJ15_.mjs +2197 -0
  40. package/dist/discovery/index.d.mts +46 -0
  41. package/dist/discovery/index.mjs +109 -0
  42. package/dist/docs/index.d.mts +162 -0
  43. package/dist/docs/index.mjs +74 -0
  44. package/dist/elevation-DGo5shaX.d.mts +87 -0
  45. package/dist/elevation-DSTbVvYj.mjs +113 -0
  46. package/dist/errorHandler-C3GY3_ow.mjs +108 -0
  47. package/dist/errorHandler-CW3OOeYq.d.mts +72 -0
  48. package/dist/errors-DAWRdiYP.d.mts +124 -0
  49. package/dist/errors-DBANPbGr.mjs +211 -0
  50. package/dist/eventPlugin-BEOvaDqo.mjs +229 -0
  51. package/dist/eventPlugin-H6wDDjGO.d.mts +124 -0
  52. package/dist/events/index.d.mts +53 -0
  53. package/dist/events/index.mjs +51 -0
  54. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  55. package/dist/events/transports/redis-stream-entry.mjs +177 -0
  56. package/dist/events/transports/redis.d.mts +76 -0
  57. package/dist/events/transports/redis.mjs +124 -0
  58. package/dist/externalPaths-SyPF2tgK.d.mts +50 -0
  59. package/dist/factory/index.d.mts +63 -0
  60. package/dist/factory/index.mjs +3 -0
  61. package/dist/fastifyAdapter-C8DlE0YH.d.mts +216 -0
  62. package/dist/fields-Bi_AVKSo.d.mts +109 -0
  63. package/dist/fields-CTd_CrKr.mjs +114 -0
  64. package/dist/hooks/index.d.mts +4 -0
  65. package/dist/hooks/index.mjs +3 -0
  66. package/dist/idempotency/index.d.mts +96 -0
  67. package/dist/idempotency/index.mjs +319 -0
  68. package/dist/idempotency/mongodb.d.mts +2 -0
  69. package/dist/idempotency/mongodb.mjs +114 -0
  70. package/dist/idempotency/redis.d.mts +2 -0
  71. package/dist/idempotency/redis.mjs +103 -0
  72. package/dist/index.d.mts +260 -0
  73. package/dist/index.mjs +104 -0
  74. package/dist/integrations/event-gateway.d.mts +46 -0
  75. package/dist/integrations/event-gateway.mjs +43 -0
  76. package/dist/integrations/index.d.mts +5 -0
  77. package/dist/integrations/index.mjs +1 -0
  78. package/dist/integrations/jobs.d.mts +103 -0
  79. package/dist/integrations/jobs.mjs +123 -0
  80. package/dist/integrations/streamline.d.mts +60 -0
  81. package/dist/integrations/streamline.mjs +125 -0
  82. package/dist/integrations/websocket.d.mts +82 -0
  83. package/dist/integrations/websocket.mjs +288 -0
  84. package/dist/interface-CSNjltAc.d.mts +77 -0
  85. package/dist/interface-DTbsvIWe.d.mts +54 -0
  86. package/dist/interface-e9XfSsUV.d.mts +1097 -0
  87. package/dist/introspectionPlugin-B3JkrjwU.mjs +53 -0
  88. package/dist/keys-DhqDRxv3.mjs +42 -0
  89. package/dist/logger-ByrvQWZO.mjs +78 -0
  90. package/dist/memory-B2v7KrCB.mjs +143 -0
  91. package/dist/migrations/index.d.mts +156 -0
  92. package/dist/migrations/index.mjs +260 -0
  93. package/dist/mongodb-ClykrfGo.d.mts +118 -0
  94. package/dist/mongodb-DNKEExbf.mjs +93 -0
  95. package/dist/mongodb-Dg8O_gvd.d.mts +71 -0
  96. package/dist/openapi-9nB_kiuR.mjs +525 -0
  97. package/dist/org/index.d.mts +68 -0
  98. package/dist/org/index.mjs +513 -0
  99. package/dist/org/types.d.mts +82 -0
  100. package/dist/org/types.mjs +1 -0
  101. package/dist/permissions/index.d.mts +278 -0
  102. package/dist/permissions/index.mjs +579 -0
  103. package/dist/plugins/index.d.mts +172 -0
  104. package/dist/plugins/index.mjs +522 -0
  105. package/dist/plugins/response-cache.d.mts +87 -0
  106. package/dist/plugins/response-cache.mjs +283 -0
  107. package/dist/plugins/tracing-entry.d.mts +2 -0
  108. package/dist/plugins/tracing-entry.mjs +185 -0
  109. package/dist/pluralize-CM-jZg7p.mjs +86 -0
  110. package/dist/policies/{index.d.ts → index.d.mts} +204 -170
  111. package/dist/policies/index.mjs +321 -0
  112. package/dist/presets/{index.d.ts → index.d.mts} +62 -131
  113. package/dist/presets/index.mjs +143 -0
  114. package/dist/presets/multiTenant.d.mts +24 -0
  115. package/dist/presets/multiTenant.mjs +113 -0
  116. package/dist/presets-BTeYbw7h.d.mts +57 -0
  117. package/dist/presets-CeFtfDR8.mjs +119 -0
  118. package/dist/prisma-C3iornoK.d.mts +274 -0
  119. package/dist/prisma-DJbMt3yf.mjs +627 -0
  120. package/dist/queryCachePlugin-B6R0d4av.mjs +138 -0
  121. package/dist/queryCachePlugin-Q6SYuHZ6.d.mts +71 -0
  122. package/dist/redis-UwjEp8Ea.d.mts +49 -0
  123. package/dist/redis-stream-CBg0upHI.d.mts +103 -0
  124. package/dist/registry/index.d.mts +11 -0
  125. package/dist/registry/index.mjs +4 -0
  126. package/dist/requestContext-xi6OKBL-.mjs +55 -0
  127. package/dist/schemaConverter-Dtg0Kt9T.mjs +98 -0
  128. package/dist/schemas/index.d.mts +63 -0
  129. package/dist/schemas/index.mjs +82 -0
  130. package/dist/scope/index.d.mts +21 -0
  131. package/dist/scope/index.mjs +65 -0
  132. package/dist/sessionManager-D_iEHjQl.d.mts +186 -0
  133. package/dist/sse-DkqQ1uxb.mjs +123 -0
  134. package/dist/testing/index.d.mts +907 -0
  135. package/dist/testing/index.mjs +1976 -0
  136. package/dist/tracing-8CEbhF0w.d.mts +70 -0
  137. package/dist/typeGuards-DwxA1t_L.mjs +9 -0
  138. package/dist/types/index.d.mts +946 -0
  139. package/dist/types/index.mjs +14 -0
  140. package/dist/types-B0dhNrnd.d.mts +445 -0
  141. package/dist/types-Beqn1Un7.mjs +38 -0
  142. package/dist/types-DelU6kln.mjs +25 -0
  143. package/dist/types-RLkFVgaw.d.mts +101 -0
  144. package/dist/utils/index.d.mts +747 -0
  145. package/dist/utils/index.mjs +6 -0
  146. package/package.json +194 -68
  147. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  148. package/dist/adapters/index.d.ts +0 -237
  149. package/dist/adapters/index.js +0 -668
  150. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  151. package/dist/audit/index.d.ts +0 -195
  152. package/dist/audit/index.js +0 -319
  153. package/dist/auth/index.d.ts +0 -47
  154. package/dist/auth/index.js +0 -174
  155. package/dist/cli/commands/docs.d.ts +0 -11
  156. package/dist/cli/commands/docs.js +0 -474
  157. package/dist/cli/commands/generate.js +0 -334
  158. package/dist/cli/commands/introspect.d.ts +0 -8
  159. package/dist/cli/commands/introspect.js +0 -338
  160. package/dist/cli/index.d.ts +0 -4
  161. package/dist/cli/index.js +0 -3269
  162. package/dist/core/index.d.ts +0 -220
  163. package/dist/core/index.js +0 -2786
  164. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  165. package/dist/docs/index.d.ts +0 -166
  166. package/dist/docs/index.js +0 -658
  167. package/dist/errors-8WIxGS_6.d.ts +0 -122
  168. package/dist/events/index.d.ts +0 -117
  169. package/dist/events/index.js +0 -89
  170. package/dist/factory/index.d.ts +0 -38
  171. package/dist/factory/index.js +0 -1652
  172. package/dist/hooks/index.d.ts +0 -4
  173. package/dist/hooks/index.js +0 -199
  174. package/dist/idempotency/index.d.ts +0 -323
  175. package/dist/idempotency/index.js +0 -500
  176. package/dist/index-B4t03KQ0.d.ts +0 -1366
  177. package/dist/index.d.ts +0 -135
  178. package/dist/index.js +0 -4756
  179. package/dist/migrations/index.d.ts +0 -185
  180. package/dist/migrations/index.js +0 -274
  181. package/dist/org/index.d.ts +0 -129
  182. package/dist/org/index.js +0 -220
  183. package/dist/permissions/index.d.ts +0 -144
  184. package/dist/permissions/index.js +0 -103
  185. package/dist/plugins/index.d.ts +0 -46
  186. package/dist/plugins/index.js +0 -1069
  187. package/dist/policies/index.js +0 -196
  188. package/dist/presets/index.js +0 -384
  189. package/dist/presets/multiTenant.d.ts +0 -39
  190. package/dist/presets/multiTenant.js +0 -112
  191. package/dist/registry/index.d.ts +0 -16
  192. package/dist/registry/index.js +0 -253
  193. package/dist/testing/index.d.ts +0 -618
  194. package/dist/testing/index.js +0 -48020
  195. package/dist/types/index.d.ts +0 -4
  196. package/dist/types/index.js +0 -8
  197. package/dist/types-B99TBmFV.d.ts +0 -76
  198. package/dist/types-BvckRbs2.d.ts +0 -143
  199. package/dist/utils/index.d.ts +0 -679
  200. package/dist/utils/index.js +0 -931
@@ -0,0 +1,21 @@
1
+ import { a as AUTHENTICATED_SCOPE, c as getOrgId, d as hasOrgAccess, f as isAuthenticated, i as elevationPlugin, l as getOrgRoles, m as isMember, n as ElevationOptions, o as PUBLIC_SCOPE, p as isElevated, r as _default, s as RequestScope, t as ElevationEvent, u as getTeamId } from "../elevation-DGo5shaX.mjs";
2
+ import { FastifyReply, FastifyRequest } from "fastify";
3
+
4
+ //#region src/scope/resolveOrgFromHeader.d.ts
5
+ interface ResolveOrgFromHeaderOptions {
6
+ /** Header name (default: 'x-organization-id') */
7
+ header?: string;
8
+ /** Resolve user's membership in the org. Return roles or null if not a member. */
9
+ resolveMembership: (userId: string, orgId: string) => Promise<{
10
+ roles: string[];
11
+ } | null>;
12
+ }
13
+ /**
14
+ * Create a preHandler hook that resolves org scope from a header.
15
+ * Must run AFTER authentication so `request.user` is populated.
16
+ * If the header is present and user is a member, sets `request.scope` to `member`.
17
+ * If the header is absent, scope stays as-is (typically `authenticated`).
18
+ */
19
+ declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
20
+ //#endregion
21
+ export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RequestScope, type ResolveOrgFromHeaderOptions, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
@@ -0,0 +1,65 @@
1
+ import { a as getTeamId, c as isElevated, i as getOrgRoles, l as isMember, n as PUBLIC_SCOPE, o as hasOrgAccess, r as getOrgId, s as isAuthenticated, t as AUTHENTICATED_SCOPE } from "../types-Beqn1Un7.mjs";
2
+ import { n as elevation_default, t as elevationPlugin } from "../elevation-DSTbVvYj.mjs";
3
+
4
+ //#region src/scope/resolveOrgFromHeader.ts
5
+ /**
6
+ * Create a preHandler hook that resolves org scope from a header.
7
+ * Must run AFTER authentication so `request.user` is populated.
8
+ * If the header is present and user is a member, sets `request.scope` to `member`.
9
+ * If the header is absent, scope stays as-is (typically `authenticated`).
10
+ */
11
+ function resolveOrgFromHeader(options) {
12
+ const { header = "x-organization-id", resolveMembership } = options;
13
+ return async (request, reply) => {
14
+ const orgId = request.headers[header];
15
+ if (!orgId) return;
16
+ const scope = request.scope;
17
+ if (!scope || scope.kind === "public") {
18
+ reply.code(401).send({
19
+ success: false,
20
+ error: "Unauthorized",
21
+ message: "Authentication required for organization access",
22
+ code: "ORG_AUTH_REQUIRED"
23
+ });
24
+ return;
25
+ }
26
+ if (scope.kind === "elevated") return;
27
+ const user = request.user;
28
+ if (!user) {
29
+ reply.code(401).send({
30
+ success: false,
31
+ error: "Unauthorized",
32
+ message: "Authentication required for organization access",
33
+ code: "ORG_AUTH_REQUIRED"
34
+ });
35
+ return;
36
+ }
37
+ const userId = String(user.id ?? user._id ?? "");
38
+ if (!userId) {
39
+ reply.code(401).send({
40
+ success: false,
41
+ error: "Unauthorized",
42
+ message: "User identity required for organization access"
43
+ });
44
+ return;
45
+ }
46
+ const membership = await resolveMembership(userId, orgId);
47
+ if (!membership) {
48
+ reply.code(403).send({
49
+ success: false,
50
+ error: "Forbidden",
51
+ message: "Not a member of this organization",
52
+ code: "ORG_ACCESS_DENIED"
53
+ });
54
+ return;
55
+ }
56
+ request.scope = {
57
+ kind: "member",
58
+ organizationId: orgId,
59
+ orgRoles: membership.roles
60
+ };
61
+ };
62
+ }
63
+
64
+ //#endregion
65
+ export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
@@ -0,0 +1,186 @@
1
+ import { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
2
+
3
+ //#region src/auth/sessionManager.d.ts
4
+ /**
5
+ * Session data stored in the session store.
6
+ */
7
+ interface SessionData {
8
+ /** User ID associated with this session */
9
+ userId: string;
10
+ /** Timestamp (ms since epoch) when session was created */
11
+ createdAt: number;
12
+ /** Timestamp (ms since epoch) when session was last refreshed */
13
+ updatedAt: number;
14
+ /** Timestamp (ms since epoch) when session expires */
15
+ expiresAt: number;
16
+ /** Optional metadata attached to the session */
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ /**
20
+ * Session store interface.
21
+ * Implement this for custom storage backends (Redis, database, etc.).
22
+ */
23
+ interface SessionStore {
24
+ /** Retrieve a session by ID. Returns null if not found or expired. */
25
+ get(sessionId: string): Promise<SessionData | null>;
26
+ /** Create or update a session. */
27
+ set(sessionId: string, data: SessionData): Promise<void>;
28
+ /** Delete a single session. */
29
+ delete(sessionId: string): Promise<void>;
30
+ /** Delete all sessions for a user. */
31
+ deleteAll(userId: string): Promise<void>;
32
+ /** Delete all sessions for a user except the specified one. */
33
+ deleteAllExcept(userId: string, currentSessionId: string): Promise<void>;
34
+ }
35
+ /**
36
+ * Cookie configuration options.
37
+ */
38
+ interface SessionCookieOptions {
39
+ /** Send cookie only over HTTPS (default: true in production) */
40
+ secure?: boolean;
41
+ /** Prevent client-side JavaScript access (default: true) */
42
+ httpOnly?: boolean;
43
+ /** SameSite attribute (default: 'lax') */
44
+ sameSite?: 'strict' | 'lax' | 'none';
45
+ /** Cookie path (default: '/') */
46
+ path?: string;
47
+ /** Cookie domain */
48
+ domain?: string;
49
+ }
50
+ /**
51
+ * Options for creating a session manager.
52
+ */
53
+ interface SessionManagerOptions {
54
+ /** Session store implementation */
55
+ store: SessionStore;
56
+ /** Secret for signing session cookies (min 32 characters) */
57
+ secret: string;
58
+ /** Session max age in seconds (default: 604800 = 7 days) */
59
+ maxAge?: number;
60
+ /** Minimum interval between session updates in seconds (default: 86400 = 24h) */
61
+ updateAge?: number;
62
+ /** Time in seconds after which a session is no longer "fresh" (default: 600 = 10 min) */
63
+ freshAge?: number;
64
+ /** Cookie name (default: 'arc.session') */
65
+ cookieName?: string;
66
+ /** Cookie options */
67
+ cookie?: SessionCookieOptions;
68
+ }
69
+ /**
70
+ * Return type from createSessionManager.
71
+ */
72
+ interface SessionManagerResult {
73
+ /** Fastify plugin that adds session middleware */
74
+ plugin: FastifyPluginAsync;
75
+ /** PreHandler that rejects requests without a fresh session */
76
+ requireFresh: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
77
+ }
78
+ declare module 'fastify' {
79
+ interface FastifyInstance {
80
+ /** Authenticate middleware — validates session and sets request.user */
81
+ authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
82
+ /** Session management helpers */
83
+ sessionManager: {
84
+ /** Create a new session for a user */createSession: (userId: string, metadata?: Record<string, unknown>) => Promise<{
85
+ sessionId: string;
86
+ cookie: string;
87
+ }>; /** Revoke a specific session */
88
+ revokeSession: (sessionId: string) => Promise<void>; /** Revoke all sessions for a user */
89
+ revokeAllSessions: (userId: string) => Promise<void>; /** Revoke all sessions except the current one */
90
+ revokeOtherSessions: (userId: string, currentSessionId: string) => Promise<void>; /** Refresh a session (reset updatedAt, extend expiry if needed) */
91
+ refreshSession: (sessionId: string) => Promise<SessionData | null>;
92
+ };
93
+ }
94
+ interface FastifyRequest {
95
+ /** Current session data (set by session plugin) */
96
+ session?: SessionData & {
97
+ id: string;
98
+ };
99
+ }
100
+ }
101
+ interface MemorySessionStoreOptions {
102
+ /** Cleanup interval in milliseconds (default: 60000 = 1 min) */
103
+ cleanupIntervalMs?: number;
104
+ }
105
+ /**
106
+ * In-memory session store for development and single-instance deployments.
107
+ * NOT suitable for multi-instance/clustered deployments — use Redis or similar.
108
+ */
109
+ declare class MemorySessionStore implements SessionStore {
110
+ private sessions;
111
+ /** Reverse index: userId -> Set<sessionId> for efficient bulk operations */
112
+ private userIndex;
113
+ private cleanupInterval;
114
+ constructor(options?: MemorySessionStoreOptions);
115
+ get(sessionId: string): Promise<SessionData | null>;
116
+ set(sessionId: string, data: SessionData): Promise<void>;
117
+ delete(sessionId: string): Promise<void>;
118
+ deleteAll(userId: string): Promise<void>;
119
+ deleteAllExcept(userId: string, currentSessionId: string): Promise<void>;
120
+ /**
121
+ * Close the store and clean up resources.
122
+ */
123
+ close(): void;
124
+ /**
125
+ * Get current stats (for debugging/monitoring).
126
+ */
127
+ getStats(): {
128
+ sessions: number;
129
+ users: number;
130
+ };
131
+ /**
132
+ * Remove expired sessions.
133
+ */
134
+ private cleanup;
135
+ }
136
+ /**
137
+ * Create a session manager for Arc.
138
+ *
139
+ * Returns a Fastify plugin and a `requireFresh` preHandler.
140
+ *
141
+ * The plugin:
142
+ * - Parses session cookie on each request
143
+ * - Validates session against the store
144
+ * - Sets `request.user` and `request.session` from session data
145
+ * - Refreshes session token if older than `updateAge`
146
+ * - Provides `fastify.authenticate` decorator
147
+ * - Provides `fastify.sessionManager` decorator for session CRUD
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * import { createSessionManager, MemorySessionStore } from '@classytic/arc/auth';
152
+ *
153
+ * const sessions = createSessionManager({
154
+ * store: new MemorySessionStore(),
155
+ * secret: process.env.SESSION_SECRET!,
156
+ * maxAge: 7 * 24 * 60 * 60,
157
+ * updateAge: 24 * 60 * 60,
158
+ * freshAge: 10 * 60,
159
+ * });
160
+ *
161
+ * await fastify.register(sessions.plugin);
162
+ *
163
+ * // Login route
164
+ * fastify.post('/login', async (request, reply) => {
165
+ * const user = await authenticateUser(request.body);
166
+ * const { cookie } = await fastify.sessionManager.createSession(user.id);
167
+ * reply.header('Set-Cookie', cookie);
168
+ * return { success: true, user };
169
+ * });
170
+ *
171
+ * // Protected route
172
+ * fastify.get('/me', {
173
+ * preHandler: [fastify.authenticate],
174
+ * }, async (request) => {
175
+ * return { user: request.user };
176
+ * });
177
+ *
178
+ * // Sensitive route (requires fresh session)
179
+ * fastify.post('/change-password', {
180
+ * preHandler: [fastify.authenticate, sessions.requireFresh],
181
+ * }, handler);
182
+ * ```
183
+ */
184
+ declare function createSessionManager(options: SessionManagerOptions): SessionManagerResult;
185
+ //#endregion
186
+ export { SessionManagerOptions as a, createSessionManager as c, SessionData as i, MemorySessionStoreOptions as n, SessionManagerResult as o, SessionCookieOptions as r, SessionStore as s, MemorySessionStore as t };
@@ -0,0 +1,123 @@
1
+ import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
+ import { n as PUBLIC_SCOPE, r as getOrgId } from "./types-Beqn1Un7.mjs";
3
+ import { t as arcLog } from "./logger-ByrvQWZO.mjs";
4
+ import fp from "fastify-plugin";
5
+
6
+ //#region src/plugins/sse.ts
7
+ /**
8
+ * SSE Plugin (Server-Sent Events)
9
+ *
10
+ * Streams domain events to clients over HTTP using Server-Sent Events.
11
+ * Requires the events plugin (`arc-events`) to be registered first.
12
+ *
13
+ * @example
14
+ * import { ssePlugin } from '@classytic/arc/plugins';
15
+ *
16
+ * // Basic — stream all events at /events/stream
17
+ * await fastify.register(ssePlugin);
18
+ *
19
+ * // Filtered + org-scoped
20
+ * await fastify.register(ssePlugin, {
21
+ * path: '/api/events',
22
+ * patterns: ['order.*', 'product.*'],
23
+ * orgScoped: true,
24
+ * });
25
+ */
26
+ var sse_exports = /* @__PURE__ */ __exportAll({
27
+ default: () => sse_default,
28
+ ssePlugin: () => ssePlugin
29
+ });
30
+ const log = arcLog("sse");
31
+ const ssePlugin = async (fastify, opts = {}) => {
32
+ const { path = "/events/stream", requireAuth = true, patterns = ["*"], heartbeat = 3e4, orgScoped = false, filter: customFilter } = opts;
33
+ if (!fastify.hasDecorator("events")) {
34
+ log.warn("Events plugin (arc-events) not registered. SSE plugin will not function. Register eventPlugin before ssePlugin.");
35
+ return;
36
+ }
37
+ const activeConnections = /* @__PURE__ */ new Set();
38
+ const routeOpts = {
39
+ method: "GET",
40
+ url: path,
41
+ schema: {
42
+ tags: ["Events"],
43
+ summary: "SSE event stream",
44
+ description: "Server-Sent Events stream for real-time domain events",
45
+ response: { 200: {
46
+ type: "string",
47
+ description: "text/event-stream"
48
+ } }
49
+ },
50
+ handler: async (request, reply) => {
51
+ reply.hijack();
52
+ reply.raw.writeHead(200, {
53
+ "content-type": "text/event-stream",
54
+ "cache-control": "no-cache",
55
+ connection: "keep-alive",
56
+ "x-accel-buffering": "no"
57
+ });
58
+ reply.raw.flushHeaders();
59
+ const unsubscribers = [];
60
+ const requestOrgId = getOrgId(request.scope ?? PUBLIC_SCOPE);
61
+ const dropOrgEvents = orgScoped && !requestOrgId;
62
+ for (const pattern of patterns) {
63
+ const unsub = await fastify.events.subscribe(pattern, async (event) => {
64
+ if (orgScoped) {
65
+ const eventOrgId = event.meta?.organizationId;
66
+ if (dropOrgEvents && eventOrgId) return;
67
+ if (requestOrgId && eventOrgId && eventOrgId !== requestOrgId) return;
68
+ }
69
+ if (customFilter && !customFilter(event, request)) return;
70
+ const data = JSON.stringify({
71
+ type: event.type,
72
+ payload: event.payload,
73
+ meta: {
74
+ id: event.meta.id,
75
+ timestamp: event.meta.timestamp
76
+ }
77
+ });
78
+ if (!reply.raw.write(`event: ${event.type}\ndata: ${data}\n\n`)) {
79
+ request.raw.destroy(/* @__PURE__ */ new Error("SSE connection terminated: slow client backpressure"));
80
+ cleanup();
81
+ }
82
+ });
83
+ unsubscribers.push(unsub);
84
+ }
85
+ const heartbeatTimer = setInterval(() => {
86
+ if (!reply.raw.write(": heartbeat\n\n")) {
87
+ request.raw.destroy(/* @__PURE__ */ new Error("SSE connection terminated: heartbeat backpressure"));
88
+ cleanup();
89
+ }
90
+ }, heartbeat);
91
+ const cleanup = () => {
92
+ clearInterval(heartbeatTimer);
93
+ for (const unsub of unsubscribers) unsub();
94
+ if (!reply.raw.writableEnded) reply.raw.end();
95
+ activeConnections.delete(cleanup);
96
+ };
97
+ activeConnections.add(cleanup);
98
+ request.raw.on("close", cleanup);
99
+ }
100
+ };
101
+ if (requireAuth) {
102
+ if (!fastify.hasDecorator("authenticate")) throw new Error("[arc-sse] requireAuth is true but fastify.authenticate is not registered. Register an auth plugin before SSE, or set requireAuth: false.");
103
+ routeOpts.preHandler = fastify.authenticate;
104
+ }
105
+ fastify.route(routeOpts);
106
+ fastify.addHook("onClose", async () => {
107
+ for (const cleanup of activeConnections) cleanup();
108
+ activeConnections.clear();
109
+ });
110
+ log.debug("Plugin registered", {
111
+ path,
112
+ patterns,
113
+ orgScoped
114
+ });
115
+ };
116
+ var sse_default = fp(ssePlugin, {
117
+ name: "arc-sse",
118
+ fastify: "5.x",
119
+ dependencies: ["arc-events"]
120
+ });
121
+
122
+ //#endregion
123
+ export { sse_default as n, sse_exports as r, ssePlugin as t };