alepha 0.12.1 → 0.13.1

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 (198) hide show
  1. package/dist/api-notifications/index.d.ts +111 -111
  2. package/dist/api-users/index.d.ts +1240 -1240
  3. package/dist/api-verifications/index.d.ts +94 -94
  4. package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
  5. package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
  6. package/dist/cli/index.d.ts +3 -11
  7. package/dist/cli/index.js +106 -74
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/email/index.js +71 -73
  10. package/dist/email/index.js.map +1 -1
  11. package/dist/orm/index.d.ts +1 -1
  12. package/dist/orm/index.js.map +1 -1
  13. package/dist/queue/index.d.ts +4 -4
  14. package/dist/redis/index.d.ts +10 -10
  15. package/dist/retry/index.d.ts +1 -1
  16. package/dist/retry/index.js +2 -2
  17. package/dist/retry/index.js.map +1 -1
  18. package/dist/scheduler/index.d.ts +6 -6
  19. package/dist/server/index.js +1 -1
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server-auth/index.d.ts +193 -193
  22. package/dist/server-health/index.d.ts +17 -17
  23. package/dist/server-links/index.d.ts +34 -34
  24. package/dist/server-metrics/index.js +170 -174
  25. package/dist/server-metrics/index.js.map +1 -1
  26. package/dist/server-security/index.d.ts +9 -9
  27. package/dist/vite/index.js +4 -5
  28. package/dist/vite/index.js.map +1 -1
  29. package/dist/websocket/index.d.ts +7 -7
  30. package/package.json +52 -103
  31. package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
  32. package/src/cli/assets/appRouterTs.ts +9 -0
  33. package/src/cli/assets/indexHtml.ts +2 -1
  34. package/src/cli/assets/mainBrowserTs.ts +10 -0
  35. package/src/cli/commands/CoreCommands.ts +6 -5
  36. package/src/cli/commands/DrizzleCommands.ts +65 -57
  37. package/src/cli/commands/VerifyCommands.ts +1 -1
  38. package/src/cli/services/ProjectUtils.ts +44 -38
  39. package/src/orm/providers/DrizzleKitProvider.ts +1 -1
  40. package/src/retry/descriptors/$retry.ts +5 -3
  41. package/src/server/providers/NodeHttpServerProvider.ts +1 -1
  42. package/src/vite/helpers/boot.ts +3 -3
  43. package/dist/api-files/index.cjs +0 -1293
  44. package/dist/api-files/index.cjs.map +0 -1
  45. package/dist/api-files/index.d.cts +0 -829
  46. package/dist/api-jobs/index.cjs +0 -274
  47. package/dist/api-jobs/index.cjs.map +0 -1
  48. package/dist/api-jobs/index.d.cts +0 -654
  49. package/dist/api-notifications/index.cjs +0 -380
  50. package/dist/api-notifications/index.cjs.map +0 -1
  51. package/dist/api-notifications/index.d.cts +0 -289
  52. package/dist/api-parameters/index.cjs +0 -66
  53. package/dist/api-parameters/index.cjs.map +0 -1
  54. package/dist/api-parameters/index.d.cts +0 -84
  55. package/dist/api-users/index.cjs +0 -6009
  56. package/dist/api-users/index.cjs.map +0 -1
  57. package/dist/api-users/index.d.cts +0 -4740
  58. package/dist/api-verifications/index.cjs +0 -407
  59. package/dist/api-verifications/index.cjs.map +0 -1
  60. package/dist/api-verifications/index.d.cts +0 -207
  61. package/dist/batch/index.cjs +0 -408
  62. package/dist/batch/index.cjs.map +0 -1
  63. package/dist/batch/index.d.cts +0 -330
  64. package/dist/bin/index.cjs +0 -17
  65. package/dist/bin/index.cjs.map +0 -1
  66. package/dist/bin/index.d.cts +0 -1
  67. package/dist/bucket/index.cjs +0 -303
  68. package/dist/bucket/index.cjs.map +0 -1
  69. package/dist/bucket/index.d.cts +0 -355
  70. package/dist/cache/index.cjs +0 -241
  71. package/dist/cache/index.cjs.map +0 -1
  72. package/dist/cache/index.d.cts +0 -202
  73. package/dist/cache-redis/index.cjs +0 -84
  74. package/dist/cache-redis/index.cjs.map +0 -1
  75. package/dist/cache-redis/index.d.cts +0 -40
  76. package/dist/cli/chunk-DSlc6foC.cjs +0 -43
  77. package/dist/cli/dist-BBPjuQ56.js +0 -2778
  78. package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
  79. package/dist/cli/index.cjs +0 -1241
  80. package/dist/cli/index.cjs.map +0 -1
  81. package/dist/cli/index.d.cts +0 -422
  82. package/dist/command/index.cjs +0 -693
  83. package/dist/command/index.cjs.map +0 -1
  84. package/dist/command/index.d.cts +0 -340
  85. package/dist/core/index.cjs +0 -2264
  86. package/dist/core/index.cjs.map +0 -1
  87. package/dist/core/index.d.cts +0 -1927
  88. package/dist/datetime/index.cjs +0 -318
  89. package/dist/datetime/index.cjs.map +0 -1
  90. package/dist/datetime/index.d.cts +0 -145
  91. package/dist/email/index.cjs +0 -10874
  92. package/dist/email/index.cjs.map +0 -1
  93. package/dist/email/index.d.cts +0 -186
  94. package/dist/fake/index.cjs +0 -34641
  95. package/dist/fake/index.cjs.map +0 -1
  96. package/dist/fake/index.d.cts +0 -74
  97. package/dist/file/index.cjs +0 -1212
  98. package/dist/file/index.cjs.map +0 -1
  99. package/dist/file/index.d.cts +0 -698
  100. package/dist/lock/index.cjs +0 -226
  101. package/dist/lock/index.cjs.map +0 -1
  102. package/dist/lock/index.d.cts +0 -361
  103. package/dist/lock-redis/index.cjs +0 -113
  104. package/dist/lock-redis/index.cjs.map +0 -1
  105. package/dist/lock-redis/index.d.cts +0 -24
  106. package/dist/logger/index.cjs +0 -521
  107. package/dist/logger/index.cjs.map +0 -1
  108. package/dist/logger/index.d.cts +0 -281
  109. package/dist/orm/index.cjs +0 -2986
  110. package/dist/orm/index.cjs.map +0 -1
  111. package/dist/orm/index.d.cts +0 -2213
  112. package/dist/queue/index.cjs +0 -1044
  113. package/dist/queue/index.cjs.map +0 -1
  114. package/dist/queue/index.d.cts +0 -1265
  115. package/dist/queue-redis/index.cjs +0 -873
  116. package/dist/queue-redis/index.cjs.map +0 -1
  117. package/dist/queue-redis/index.d.cts +0 -82
  118. package/dist/redis/index.cjs +0 -153
  119. package/dist/redis/index.cjs.map +0 -1
  120. package/dist/redis/index.d.cts +0 -82
  121. package/dist/retry/index.cjs +0 -146
  122. package/dist/retry/index.cjs.map +0 -1
  123. package/dist/retry/index.d.cts +0 -172
  124. package/dist/router/index.cjs +0 -111
  125. package/dist/router/index.cjs.map +0 -1
  126. package/dist/router/index.d.cts +0 -46
  127. package/dist/scheduler/index.cjs +0 -576
  128. package/dist/scheduler/index.cjs.map +0 -1
  129. package/dist/scheduler/index.d.cts +0 -145
  130. package/dist/security/index.cjs +0 -2402
  131. package/dist/security/index.cjs.map +0 -1
  132. package/dist/security/index.d.cts +0 -598
  133. package/dist/server/index.cjs +0 -1680
  134. package/dist/server/index.cjs.map +0 -1
  135. package/dist/server/index.d.cts +0 -810
  136. package/dist/server-auth/index.cjs +0 -3146
  137. package/dist/server-auth/index.cjs.map +0 -1
  138. package/dist/server-auth/index.d.cts +0 -1164
  139. package/dist/server-cache/index.cjs +0 -252
  140. package/dist/server-cache/index.cjs.map +0 -1
  141. package/dist/server-cache/index.d.cts +0 -164
  142. package/dist/server-compress/index.cjs +0 -141
  143. package/dist/server-compress/index.cjs.map +0 -1
  144. package/dist/server-compress/index.d.cts +0 -38
  145. package/dist/server-cookies/index.cjs +0 -234
  146. package/dist/server-cookies/index.cjs.map +0 -1
  147. package/dist/server-cookies/index.d.cts +0 -144
  148. package/dist/server-cors/index.cjs +0 -201
  149. package/dist/server-cors/index.cjs.map +0 -1
  150. package/dist/server-cors/index.d.cts +0 -140
  151. package/dist/server-health/index.cjs +0 -62
  152. package/dist/server-health/index.cjs.map +0 -1
  153. package/dist/server-health/index.d.cts +0 -58
  154. package/dist/server-helmet/index.cjs +0 -131
  155. package/dist/server-helmet/index.cjs.map +0 -1
  156. package/dist/server-helmet/index.d.cts +0 -97
  157. package/dist/server-links/index.cjs +0 -992
  158. package/dist/server-links/index.cjs.map +0 -1
  159. package/dist/server-links/index.d.cts +0 -513
  160. package/dist/server-metrics/index.cjs +0 -4535
  161. package/dist/server-metrics/index.cjs.map +0 -1
  162. package/dist/server-metrics/index.d.cts +0 -35
  163. package/dist/server-multipart/index.cjs +0 -237
  164. package/dist/server-multipart/index.cjs.map +0 -1
  165. package/dist/server-multipart/index.d.cts +0 -50
  166. package/dist/server-proxy/index.cjs +0 -186
  167. package/dist/server-proxy/index.cjs.map +0 -1
  168. package/dist/server-proxy/index.d.cts +0 -234
  169. package/dist/server-rate-limit/index.cjs +0 -241
  170. package/dist/server-rate-limit/index.cjs.map +0 -1
  171. package/dist/server-rate-limit/index.d.cts +0 -183
  172. package/dist/server-security/index.cjs +0 -316
  173. package/dist/server-security/index.cjs.map +0 -1
  174. package/dist/server-security/index.d.cts +0 -173
  175. package/dist/server-static/index.cjs +0 -170
  176. package/dist/server-static/index.cjs.map +0 -1
  177. package/dist/server-static/index.d.cts +0 -121
  178. package/dist/server-swagger/index.cjs +0 -1021
  179. package/dist/server-swagger/index.cjs.map +0 -1
  180. package/dist/server-swagger/index.d.cts +0 -382
  181. package/dist/sms/index.cjs +0 -221
  182. package/dist/sms/index.cjs.map +0 -1
  183. package/dist/sms/index.d.cts +0 -130
  184. package/dist/thread/index.cjs +0 -350
  185. package/dist/thread/index.cjs.map +0 -1
  186. package/dist/thread/index.d.cts +0 -260
  187. package/dist/topic/index.cjs +0 -282
  188. package/dist/topic/index.cjs.map +0 -1
  189. package/dist/topic/index.d.cts +0 -523
  190. package/dist/topic-redis/index.cjs +0 -71
  191. package/dist/topic-redis/index.cjs.map +0 -1
  192. package/dist/topic-redis/index.d.cts +0 -42
  193. package/dist/vite/index.cjs +0 -1077
  194. package/dist/vite/index.cjs.map +0 -1
  195. package/dist/vite/index.d.cts +0 -542
  196. package/dist/websocket/index.cjs +0 -1117
  197. package/dist/websocket/index.cjs.map +0 -1
  198. package/dist/websocket/index.d.cts +0 -861
@@ -1,234 +0,0 @@
1
- let alepha = require("alepha");
2
- let alepha_server = require("alepha/server");
3
- let node_crypto = require("node:crypto");
4
- let node_zlib = require("node:zlib");
5
- let alepha_datetime = require("alepha/datetime");
6
- let alepha_logger = require("alepha/logger");
7
- let alepha_security = require("alepha/security");
8
-
9
- //#region src/server-cookies/services/CookieParser.ts
10
- var CookieParser = class {
11
- parseRequestCookies(header) {
12
- const cookies = {};
13
- const parts = header.split(";");
14
- for (const part of parts) {
15
- const [key, value] = part.split("=");
16
- if (!key || !value) continue;
17
- cookies[key.trim()] = value.trim();
18
- }
19
- return cookies;
20
- }
21
- serializeResponseCookies(cookies, isHttps) {
22
- const headers = [];
23
- for (const [name, cookie] of Object.entries(cookies)) {
24
- if (cookie == null) {
25
- headers.push(`${name}=; Path=/; Max-Age=0`);
26
- continue;
27
- }
28
- if (!cookie.value) continue;
29
- headers.push(this.cookieToString(name, cookie, isHttps));
30
- }
31
- return headers;
32
- }
33
- cookieToString(name, cookie, isHttps) {
34
- const parts = [];
35
- parts.push(`${name}=${cookie.value}`);
36
- if (cookie.path) parts.push(`Path=${cookie.path}`);
37
- if (cookie.maxAge) parts.push(`Max-Age=${cookie.maxAge}`);
38
- if (cookie.secure !== false && isHttps) parts.push("Secure");
39
- if (cookie.httpOnly) parts.push("HttpOnly");
40
- if (cookie.sameSite) parts.push(`SameSite=${cookie.sameSite}`);
41
- if (cookie.domain) parts.push(`Domain=${cookie.domain}`);
42
- return parts.join("; ");
43
- }
44
- };
45
-
46
- //#endregion
47
- //#region src/server-cookies/providers/ServerCookiesProvider.ts
48
- const envSchema = alepha.t.object({ APP_SECRET: alepha.t.text({ default: alepha_security.DEFAULT_APP_SECRET }) });
49
- var ServerCookiesProvider = class {
50
- alepha = (0, alepha.$inject)(alepha.Alepha);
51
- log = (0, alepha_logger.$logger)();
52
- cookieParser = (0, alepha.$inject)(CookieParser);
53
- dateTimeProvider = (0, alepha.$inject)(alepha_datetime.DateTimeProvider);
54
- env = (0, alepha.$env)(envSchema);
55
- ALGORITHM = "aes-256-gcm";
56
- IV_LENGTH = 16;
57
- AUTH_TAG_LENGTH = 16;
58
- SIGNATURE_LENGTH = 32;
59
- onRequest = (0, alepha.$hook)({
60
- on: "server:onRequest",
61
- handler: async ({ request }) => {
62
- request.cookies = {
63
- req: this.cookieParser.parseRequestCookies(request.headers.cookie ?? ""),
64
- res: {}
65
- };
66
- }
67
- });
68
- onAction = (0, alepha.$hook)({
69
- on: "action:onRequest",
70
- handler: async ({ request }) => {
71
- request.cookies = {
72
- req: this.cookieParser.parseRequestCookies(request.headers.cookie ?? ""),
73
- res: {}
74
- };
75
- }
76
- });
77
- onSend = (0, alepha.$hook)({
78
- on: "server:onSend",
79
- handler: async ({ request }) => {
80
- if (request.cookies && Object.keys(request.cookies.res).length > 0) {
81
- const setCookieHeaders = this.cookieParser.serializeResponseCookies(request.cookies.res, request.url.protocol === "https:");
82
- if (setCookieHeaders.length > 0) request.reply.headers["set-cookie"] = setCookieHeaders;
83
- }
84
- }
85
- });
86
- getCookiesFromContext(cookies) {
87
- const contextCookies = this.alepha.context.get("request")?.cookies;
88
- if (cookies) return cookies;
89
- if (contextCookies) return contextCookies;
90
- throw new Error("Cookie context is not available. This method must be called within a server request cycle.");
91
- }
92
- getCookie(name, options, contextCookies) {
93
- const cookies = this.getCookiesFromContext(contextCookies);
94
- let rawValue = cookies.req[name];
95
- if (!rawValue) return void 0;
96
- try {
97
- rawValue = decodeURIComponent(rawValue);
98
- if (options.sign) {
99
- const signature = rawValue.substring(0, this.SIGNATURE_LENGTH * 2);
100
- const value = rawValue.substring(this.SIGNATURE_LENGTH * 2);
101
- const expectedSignature = this.sign(value);
102
- if (!(0, node_crypto.timingSafeEqual)(Buffer.from(signature, "hex"), Buffer.from(expectedSignature, "hex"))) {
103
- this.log.warn(`Invalid signature for cookie "${name}".`);
104
- return;
105
- }
106
- rawValue = value;
107
- }
108
- if (options.encrypt) rawValue = this.decrypt(rawValue);
109
- if (options.compress) rawValue = (0, node_zlib.inflateRawSync)(Buffer.from(rawValue, "base64")).toString("utf8");
110
- return this.alepha.codec.decode(options.schema, JSON.parse(rawValue));
111
- } catch (error) {
112
- this.log.warn(`Failed to parse cookie "${name}"`, error);
113
- this.deleteCookie(name, cookies);
114
- return;
115
- }
116
- }
117
- setCookie(name, options, data, contextCookies) {
118
- const cookies = this.getCookiesFromContext(contextCookies);
119
- let value = JSON.stringify(this.alepha.codec.decode(options.schema, data));
120
- if (options.compress) value = (0, node_zlib.deflateRawSync)(value).toString("base64");
121
- if (options.encrypt) value = this.encrypt(value);
122
- if (options.sign) value = this.sign(value) + value;
123
- const cookie = {
124
- value: encodeURIComponent(value),
125
- path: options.path ?? "/",
126
- sameSite: options.sameSite ?? "lax",
127
- secure: options.secure ?? this.alepha.isProduction(),
128
- httpOnly: options.httpOnly,
129
- domain: options.domain
130
- };
131
- if (options.ttl) cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as("seconds");
132
- cookies.res[name] = cookie;
133
- }
134
- deleteCookie(name, contextCookies) {
135
- const cookies = this.getCookiesFromContext(contextCookies);
136
- cookies.res[name] = null;
137
- }
138
- encrypt(text) {
139
- const iv = (0, node_crypto.randomBytes)(this.IV_LENGTH);
140
- const cipher = (0, node_crypto.createCipheriv)(this.ALGORITHM, Buffer.from(this.secretKey()), iv);
141
- const encrypted = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
142
- const authTag = cipher.getAuthTag();
143
- return Buffer.concat([
144
- iv,
145
- authTag,
146
- encrypted
147
- ]).toString("base64");
148
- }
149
- decrypt(encryptedText) {
150
- const data = Buffer.from(encryptedText, "base64");
151
- const iv = data.subarray(0, this.IV_LENGTH);
152
- const authTag = data.subarray(this.IV_LENGTH, this.IV_LENGTH + this.AUTH_TAG_LENGTH);
153
- const encrypted = data.subarray(this.IV_LENGTH + this.AUTH_TAG_LENGTH);
154
- const decipher = (0, node_crypto.createDecipheriv)(this.ALGORITHM, Buffer.from(this.secretKey()), iv);
155
- decipher.setAuthTag(authTag);
156
- return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
157
- }
158
- secretKey() {
159
- let secret = this.env.APP_SECRET;
160
- if (secret.length < 32) secret = secret.padEnd(32, "0");
161
- else if (secret.length > 32) secret = secret.substring(0, 32);
162
- return secret;
163
- }
164
- sign(data) {
165
- return (0, node_crypto.createHmac)("sha256", this.secretKey()).update(data).digest("hex");
166
- }
167
- };
168
-
169
- //#endregion
170
- //#region src/server-cookies/descriptors/$cookie.ts
171
- /**
172
- * Declares a type-safe, configurable HTTP cookie.
173
- * This descriptor provides methods to get, set, and delete the cookie
174
- * within the server request/response cycle.
175
- */
176
- const $cookie = (options) => {
177
- return (0, alepha.createDescriptor)(CookieDescriptor, options);
178
- };
179
- var CookieDescriptor = class extends alepha.Descriptor {
180
- serverCookiesProvider = (0, alepha.$inject)(ServerCookiesProvider);
181
- get schema() {
182
- return this.options.schema;
183
- }
184
- get name() {
185
- return this.options.name ?? `${this.config.propertyKey}`;
186
- }
187
- /**
188
- * Sets the cookie with the given value in the current request's response.
189
- */
190
- set(value, options) {
191
- this.serverCookiesProvider.setCookie(this.name, {
192
- ...this.options,
193
- ttl: options?.ttl ?? this.options.ttl
194
- }, value, options?.cookies);
195
- }
196
- /**
197
- * Gets the cookie value from the current request. Returns undefined if not found or invalid.
198
- */
199
- get(options) {
200
- return this.serverCookiesProvider.getCookie(this.name, this.options, options?.cookies);
201
- }
202
- /**
203
- * Deletes the cookie in the current request's response.
204
- */
205
- del(options) {
206
- this.serverCookiesProvider.deleteCookie(this.name, options?.cookies);
207
- }
208
- };
209
- $cookie[alepha.KIND] = CookieDescriptor;
210
-
211
- //#endregion
212
- //#region src/server-cookies/index.ts
213
- /**
214
- * Provides HTTP cookie management capabilities for server requests and responses with type-safe cookie descriptors.
215
- *
216
- * The server-cookies module enables declarative cookie handling using the `$cookie` descriptor on class properties.
217
- * It offers automatic cookie parsing, secure cookie configuration, and seamless integration with server routes
218
- * for managing user sessions, preferences, and authentication tokens.
219
- *
220
- * @see {@link $cookie}
221
- * @module alepha.server.cookies
222
- */
223
- const AlephaServerCookies = (0, alepha.$module)({
224
- name: "alepha.server.cookies",
225
- descriptors: [$cookie],
226
- services: [alepha_server.AlephaServer, ServerCookiesProvider]
227
- });
228
-
229
- //#endregion
230
- exports.$cookie = $cookie;
231
- exports.AlephaServerCookies = AlephaServerCookies;
232
- exports.CookieDescriptor = CookieDescriptor;
233
- exports.ServerCookiesProvider = ServerCookiesProvider;
234
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["cookies: Record<string, string>","parts: string[]","t","DEFAULT_APP_SECRET","Alepha","DateTimeProvider","cookie: Cookie","Descriptor","KIND","AlephaServer"],"sources":["../../src/server-cookies/services/CookieParser.ts","../../src/server-cookies/providers/ServerCookiesProvider.ts","../../src/server-cookies/descriptors/$cookie.ts","../../src/server-cookies/index.ts"],"sourcesContent":["import type { Cookie } from \"../descriptors/$cookie.ts\";\n\nexport class CookieParser {\n public parseRequestCookies(header: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n const parts = header.split(\";\");\n for (const part of parts) {\n const [key, value] = part.split(\"=\");\n if (!key || !value) {\n continue;\n }\n\n cookies[key.trim()] = value.trim();\n }\n\n return cookies;\n }\n\n public serializeResponseCookies(\n cookies: Record<string, Cookie | null>,\n isHttps: boolean,\n ): string[] {\n const headers = [];\n\n for (const [name, cookie] of Object.entries(cookies)) {\n // If the cookie is null, we need to delete it\n if (cookie == null) {\n headers.push(`${name}=; Path=/; Max-Age=0`);\n continue;\n }\n\n if (!cookie.value) {\n continue;\n }\n\n headers.push(this.cookieToString(name, cookie, isHttps));\n }\n\n return headers;\n }\n\n public cookieToString(\n name: string,\n cookie: Cookie,\n isHttps?: boolean,\n ): string {\n const parts: string[] = [];\n\n parts.push(`${name}=${cookie.value}`);\n\n if (cookie.path) {\n parts.push(`Path=${cookie.path}`);\n }\n if (cookie.maxAge) {\n parts.push(`Max-Age=${cookie.maxAge}`);\n }\n if (cookie.secure !== false && isHttps) {\n parts.push(\"Secure\");\n }\n if (cookie.httpOnly) {\n parts.push(\"HttpOnly\");\n }\n if (cookie.sameSite) {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n if (cookie.domain) {\n parts.push(`Domain=${cookie.domain}`);\n }\n\n return parts.join(\"; \");\n }\n}\n","import {\n createCipheriv,\n createDecipheriv,\n createHmac,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { deflateRawSync, inflateRawSync } from \"node:zlib\";\nimport {\n $env,\n $hook,\n $inject,\n Alepha,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { DEFAULT_APP_SECRET } from \"alepha/security\";\nimport type { ServerRequest } from \"alepha/server\";\nimport type {\n Cookie,\n CookieDescriptorOptions,\n Cookies,\n} from \"../descriptors/$cookie.ts\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\n\nconst envSchema = t.object({\n APP_SECRET: t.text({\n default: DEFAULT_APP_SECRET,\n }),\n});\n\nexport class ServerCookiesProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cookieParser = $inject(CookieParser);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly env = $env(envSchema);\n\n // crypto constants\n protected readonly ALGORITHM = \"aes-256-gcm\";\n protected readonly IV_LENGTH = 16; // For GCM\n protected readonly AUTH_TAG_LENGTH = 16;\n protected readonly SIGNATURE_LENGTH = 32; // For SHA256\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onAction = $hook({\n on: \"action:onRequest\",\n handler: async ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onSend = $hook({\n on: \"server:onSend\",\n handler: async ({ request }) => {\n if (request.cookies && Object.keys(request.cookies.res).length > 0) {\n const setCookieHeaders = this.cookieParser.serializeResponseCookies(\n request.cookies.res,\n request.url.protocol === \"https:\",\n );\n if (setCookieHeaders.length > 0) {\n request.reply.headers[\"set-cookie\"] = setCookieHeaders;\n }\n }\n },\n });\n\n protected getCookiesFromContext(cookies?: Cookies): Cookies {\n const contextCookies =\n this.alepha.context.get<ServerRequest>(\"request\")?.cookies;\n if (cookies) return cookies;\n if (contextCookies) return contextCookies;\n throw new Error(\n \"Cookie context is not available. This method must be called within a server request cycle.\",\n );\n }\n\n public getCookie<T extends TSchema>(\n name: string,\n options: CookieDescriptorOptions<T>,\n contextCookies?: Cookies,\n ): Static<T> | undefined {\n const cookies = this.getCookiesFromContext(contextCookies);\n let rawValue = cookies.req[name];\n\n if (!rawValue) return undefined;\n\n try {\n rawValue = decodeURIComponent(rawValue);\n\n if (options.sign) {\n const signature = rawValue.substring(0, this.SIGNATURE_LENGTH * 2);\n const value = rawValue.substring(this.SIGNATURE_LENGTH * 2);\n const expectedSignature = this.sign(value);\n\n if (\n !timingSafeEqual(\n Buffer.from(signature, \"hex\"),\n Buffer.from(expectedSignature, \"hex\"),\n )\n ) {\n this.log.warn(`Invalid signature for cookie \"${name}\".`);\n return undefined;\n }\n rawValue = value;\n }\n\n if (options.encrypt) {\n rawValue = this.decrypt(rawValue);\n }\n\n if (options.compress) {\n rawValue = inflateRawSync(Buffer.from(rawValue, \"base64\")).toString(\n \"utf8\",\n );\n }\n\n return this.alepha.codec.decode(options.schema, JSON.parse(rawValue));\n } catch (error) {\n this.log.warn(`Failed to parse cookie \"${name}\"`, error);\n // corrupted or invalid cookie, instruct browser to delete it on next response\n this.deleteCookie(name, cookies);\n return undefined;\n }\n }\n\n public setCookie<T extends TSchema>(\n name: string,\n options: CookieDescriptorOptions<T>,\n data: Static<T>,\n contextCookies?: Cookies,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n let value = JSON.stringify(this.alepha.codec.decode(options.schema, data));\n\n if (options.compress) {\n value = deflateRawSync(value).toString(\"base64\");\n }\n\n if (options.encrypt) {\n value = this.encrypt(value);\n }\n\n if (options.sign) {\n value = this.sign(value) + value;\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(value),\n path: options.path ?? \"/\",\n sameSite: options.sameSite ?? \"lax\",\n secure: options.secure ?? this.alepha.isProduction(),\n httpOnly: options.httpOnly,\n domain: options.domain,\n };\n\n if (options.ttl) {\n cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as(\"seconds\");\n }\n\n cookies.res[name] = cookie;\n }\n\n public deleteCookie<T extends TSchema>(\n name: string,\n contextCookies?: Cookies,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n cookies.res[name] = null;\n }\n\n // --- Crypto & Parsing ---\n\n protected encrypt(text: string): string {\n const iv = randomBytes(this.IV_LENGTH);\n const cipher = createCipheriv(\n this.ALGORITHM,\n Buffer.from(this.secretKey()),\n iv,\n );\n const encrypted = Buffer.concat([\n cipher.update(text, \"utf8\"),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n return Buffer.concat([iv, authTag, encrypted]).toString(\"base64\");\n }\n\n protected decrypt(encryptedText: string): string {\n const data = Buffer.from(encryptedText, \"base64\");\n const iv = data.subarray(0, this.IV_LENGTH);\n const authTag = data.subarray(\n this.IV_LENGTH,\n this.IV_LENGTH + this.AUTH_TAG_LENGTH,\n );\n\n const encrypted = data.subarray(this.IV_LENGTH + this.AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(\n this.ALGORITHM,\n Buffer.from(this.secretKey()),\n iv,\n );\n\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(encrypted),\n decipher.final(),\n ]);\n\n return decrypted.toString(\"utf8\");\n }\n\n public secretKey(): string {\n let secret = this.env.APP_SECRET;\n if (secret.length < 32) {\n // pad secret to 32 bytes\n secret = secret.padEnd(32, \"0\");\n } else if (secret.length > 32) {\n // truncate secret to 32 bytes\n secret = secret.substring(0, 32);\n }\n return secret;\n }\n\n protected sign(data: string): string {\n return createHmac(\"sha256\", this.secretKey()).update(data).digest(\"hex\");\n }\n}\n","import {\n $inject,\n createDescriptor,\n Descriptor,\n KIND,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { ServerCookiesProvider } from \"../providers/ServerCookiesProvider.ts\";\n\n/**\n * Declares a type-safe, configurable HTTP cookie.\n * This descriptor provides methods to get, set, and delete the cookie\n * within the server request/response cycle.\n */\nexport const $cookie = <T extends TSchema>(\n options: CookieDescriptorOptions<T>,\n): AbstractCookieDescriptor<T> => {\n return createDescriptor(CookieDescriptor<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CookieDescriptorOptions<T extends TSchema> {\n /** The schema for the cookie's value, used for validation and type safety. */\n schema: T;\n\n /** The name of the cookie. */\n name?: string;\n\n /** The cookie's path. Defaults to \"/\". */\n path?: string;\n\n /** Time-to-live for the cookie. Maps to `Max-Age`. */\n ttl?: DurationLike;\n\n /** If true, the cookie is only sent over HTTPS. Defaults to true in production. */\n secure?: boolean;\n\n /** If true, the cookie cannot be accessed by client-side scripts. */\n httpOnly?: boolean;\n\n /** SameSite policy for the cookie. Defaults to \"lax\". */\n sameSite?: \"strict\" | \"lax\" | \"none\";\n\n /** The domain for the cookie. */\n domain?: string;\n\n /** If true, the cookie value will be compressed using zlib. */\n compress?: boolean;\n\n /** If true, the cookie value will be encrypted. Requires `COOKIE_SECRET` env var. */\n encrypt?: boolean;\n\n /** If true, the cookie will be signed to prevent tampering. Requires `COOKIE_SECRET` env var. */\n sign?: boolean;\n}\n\nexport interface AbstractCookieDescriptor<T extends TSchema> {\n readonly name: string;\n readonly options: CookieDescriptorOptions<T>;\n set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void;\n get(options?: { cookies?: Cookies }): Static<T> | undefined;\n del(options?: { cookies?: Cookies }): void;\n}\n\nexport class CookieDescriptor<T extends TSchema>\n extends Descriptor<CookieDescriptorOptions<T>>\n implements AbstractCookieDescriptor<T>\n{\n protected readonly serverCookiesProvider = $inject(ServerCookiesProvider);\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Sets the cookie with the given value in the current request's response.\n */\n public set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void {\n this.serverCookiesProvider.setCookie(\n this.name,\n {\n ...this.options,\n ttl: options?.ttl ?? this.options.ttl,\n },\n value,\n options?.cookies,\n );\n }\n\n /**\n * Gets the cookie value from the current request. Returns undefined if not found or invalid.\n */\n public get(options?: { cookies?: Cookies }): Static<T> | undefined {\n return this.serverCookiesProvider.getCookie(\n this.name,\n this.options,\n options?.cookies,\n );\n }\n\n /**\n * Deletes the cookie in the current request's response.\n */\n public del(options?: { cookies?: Cookies }): void {\n this.serverCookiesProvider.deleteCookie(this.name, options?.cookies);\n }\n}\n\n$cookie[KIND] = CookieDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface Cookies {\n req: Record<string, string>;\n res: Record<string, Cookie | null>;\n}\n\nexport interface Cookie {\n value: string;\n path?: string;\n maxAge?: number;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n domain?: string;\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $cookie, type Cookies } from \"./descriptors/$cookie.ts\";\nimport { ServerCookiesProvider } from \"./providers/ServerCookiesProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$cookie.ts\";\nexport * from \"./providers/ServerCookiesProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRequest {\n cookies: Cookies;\n }\n}\n\n/**\n * Provides HTTP cookie management capabilities for server requests and responses with type-safe cookie descriptors.\n *\n * The server-cookies module enables declarative cookie handling using the `$cookie` descriptor on class properties.\n * It offers automatic cookie parsing, secure cookie configuration, and seamless integration with server routes\n * for managing user sessions, preferences, and authentication tokens.\n *\n * @see {@link $cookie}\n * @module alepha.server.cookies\n */\nexport const AlephaServerCookies = $module({\n name: \"alepha.server.cookies\",\n descriptors: [$cookie],\n services: [AlephaServer, ServerCookiesProvider],\n});\n"],"mappings":"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"}
@@ -1,144 +0,0 @@
1
- import * as alepha1 from "alepha";
2
- import { Alepha, Descriptor, KIND, Static, TSchema } from "alepha";
3
- import { DateTimeProvider, DurationLike } from "alepha/datetime";
4
- import * as alepha_logger0 from "alepha/logger";
5
-
6
- //#region src/server-cookies/services/CookieParser.d.ts
7
- declare class CookieParser {
8
- parseRequestCookies(header: string): Record<string, string>;
9
- serializeResponseCookies(cookies: Record<string, Cookie | null>, isHttps: boolean): string[];
10
- cookieToString(name: string, cookie: Cookie, isHttps?: boolean): string;
11
- }
12
- //#endregion
13
- //#region src/server-cookies/providers/ServerCookiesProvider.d.ts
14
- declare class ServerCookiesProvider {
15
- protected readonly alepha: Alepha;
16
- protected readonly log: alepha_logger0.Logger;
17
- protected readonly cookieParser: CookieParser;
18
- protected readonly dateTimeProvider: DateTimeProvider;
19
- protected readonly env: {
20
- APP_SECRET: string;
21
- };
22
- protected readonly ALGORITHM = "aes-256-gcm";
23
- protected readonly IV_LENGTH = 16;
24
- protected readonly AUTH_TAG_LENGTH = 16;
25
- protected readonly SIGNATURE_LENGTH = 32;
26
- readonly onRequest: alepha1.HookDescriptor<"server:onRequest">;
27
- readonly onAction: alepha1.HookDescriptor<"action:onRequest">;
28
- readonly onSend: alepha1.HookDescriptor<"server:onSend">;
29
- protected getCookiesFromContext(cookies?: Cookies): Cookies;
30
- getCookie<T extends TSchema>(name: string, options: CookieDescriptorOptions<T>, contextCookies?: Cookies): Static<T> | undefined;
31
- setCookie<T extends TSchema>(name: string, options: CookieDescriptorOptions<T>, data: Static<T>, contextCookies?: Cookies): void;
32
- deleteCookie<T extends TSchema>(name: string, contextCookies?: Cookies): void;
33
- protected encrypt(text: string): string;
34
- protected decrypt(encryptedText: string): string;
35
- secretKey(): string;
36
- protected sign(data: string): string;
37
- }
38
- //#endregion
39
- //#region src/server-cookies/descriptors/$cookie.d.ts
40
- /**
41
- * Declares a type-safe, configurable HTTP cookie.
42
- * This descriptor provides methods to get, set, and delete the cookie
43
- * within the server request/response cycle.
44
- */
45
- declare const $cookie: {
46
- <T extends TSchema>(options: CookieDescriptorOptions<T>): AbstractCookieDescriptor<T>;
47
- [KIND]: typeof CookieDescriptor;
48
- };
49
- interface CookieDescriptorOptions<T extends TSchema> {
50
- /** The schema for the cookie's value, used for validation and type safety. */
51
- schema: T;
52
- /** The name of the cookie. */
53
- name?: string;
54
- /** The cookie's path. Defaults to "/". */
55
- path?: string;
56
- /** Time-to-live for the cookie. Maps to `Max-Age`. */
57
- ttl?: DurationLike;
58
- /** If true, the cookie is only sent over HTTPS. Defaults to true in production. */
59
- secure?: boolean;
60
- /** If true, the cookie cannot be accessed by client-side scripts. */
61
- httpOnly?: boolean;
62
- /** SameSite policy for the cookie. Defaults to "lax". */
63
- sameSite?: "strict" | "lax" | "none";
64
- /** The domain for the cookie. */
65
- domain?: string;
66
- /** If true, the cookie value will be compressed using zlib. */
67
- compress?: boolean;
68
- /** If true, the cookie value will be encrypted. Requires `COOKIE_SECRET` env var. */
69
- encrypt?: boolean;
70
- /** If true, the cookie will be signed to prevent tampering. Requires `COOKIE_SECRET` env var. */
71
- sign?: boolean;
72
- }
73
- interface AbstractCookieDescriptor<T extends TSchema> {
74
- readonly name: string;
75
- readonly options: CookieDescriptorOptions<T>;
76
- set(value: Static<T>, options?: {
77
- cookies?: Cookies;
78
- ttl?: DurationLike;
79
- }): void;
80
- get(options?: {
81
- cookies?: Cookies;
82
- }): Static<T> | undefined;
83
- del(options?: {
84
- cookies?: Cookies;
85
- }): void;
86
- }
87
- declare class CookieDescriptor<T extends TSchema> extends Descriptor<CookieDescriptorOptions<T>> implements AbstractCookieDescriptor<T> {
88
- protected readonly serverCookiesProvider: ServerCookiesProvider;
89
- get schema(): T;
90
- get name(): string;
91
- /**
92
- * Sets the cookie with the given value in the current request's response.
93
- */
94
- set(value: Static<T>, options?: {
95
- cookies?: Cookies;
96
- ttl?: DurationLike;
97
- }): void;
98
- /**
99
- * Gets the cookie value from the current request. Returns undefined if not found or invalid.
100
- */
101
- get(options?: {
102
- cookies?: Cookies;
103
- }): Static<T> | undefined;
104
- /**
105
- * Deletes the cookie in the current request's response.
106
- */
107
- del(options?: {
108
- cookies?: Cookies;
109
- }): void;
110
- }
111
- interface Cookies {
112
- req: Record<string, string>;
113
- res: Record<string, Cookie | null>;
114
- }
115
- interface Cookie {
116
- value: string;
117
- path?: string;
118
- maxAge?: number;
119
- secure?: boolean;
120
- httpOnly?: boolean;
121
- sameSite?: "strict" | "lax" | "none";
122
- domain?: string;
123
- }
124
- //#endregion
125
- //#region src/server-cookies/index.d.ts
126
- declare module "alepha/server" {
127
- interface ServerRequest {
128
- cookies: Cookies;
129
- }
130
- }
131
- /**
132
- * Provides HTTP cookie management capabilities for server requests and responses with type-safe cookie descriptors.
133
- *
134
- * The server-cookies module enables declarative cookie handling using the `$cookie` descriptor on class properties.
135
- * It offers automatic cookie parsing, secure cookie configuration, and seamless integration with server routes
136
- * for managing user sessions, preferences, and authentication tokens.
137
- *
138
- * @see {@link $cookie}
139
- * @module alepha.server.cookies
140
- */
141
- declare const AlephaServerCookies: alepha1.Service<alepha1.Module>;
142
- //#endregion
143
- export { $cookie, AbstractCookieDescriptor, AlephaServerCookies, Cookie, CookieDescriptor, CookieDescriptorOptions, Cookies, ServerCookiesProvider };
144
- //# sourceMappingURL=index.d.cts.map
@@ -1,201 +0,0 @@
1
- let alepha = require("alepha");
2
- let alepha_logger = require("alepha/logger");
3
- let alepha_server = require("alepha/server");
4
-
5
- //#region src/server-cors/providers/ServerCorsProvider.ts
6
- /**
7
- * CORS configuration atom (global defaults)
8
- */
9
- const corsOptions = (0, alepha.$atom)({
10
- name: "alepha.server.cors.options",
11
- schema: alepha.t.object({
12
- origin: alepha.t.optional(alepha.t.string({
13
- description: "Allowed origins (* for all, string for single, comma-separated for multiple)",
14
- default: "*"
15
- })),
16
- methods: alepha.t.array(alepha.t.string(), {
17
- description: "Allowed HTTP methods",
18
- default: [
19
- "GET",
20
- "POST",
21
- "PUT",
22
- "PATCH",
23
- "DELETE",
24
- "OPTIONS"
25
- ]
26
- }),
27
- headers: alepha.t.array(alepha.t.string(), {
28
- description: "Allowed headers",
29
- default: ["Content-Type", "Authorization"]
30
- }),
31
- credentials: alepha.t.optional(alepha.t.boolean({
32
- description: "Allow credentials",
33
- default: true
34
- })),
35
- maxAge: alepha.t.optional(alepha.t.number({ description: "Preflight cache duration in seconds" }))
36
- }),
37
- default: {
38
- origin: "*",
39
- methods: [
40
- "GET",
41
- "POST",
42
- "PUT",
43
- "PATCH",
44
- "DELETE",
45
- "OPTIONS"
46
- ],
47
- headers: ["Content-Type", "Authorization"],
48
- credentials: true
49
- }
50
- });
51
- var ServerCorsProvider = class {
52
- log = (0, alepha_logger.$logger)();
53
- serverRouterProvider = (0, alepha.$inject)(alepha_server.ServerRouterProvider);
54
- globalOptions = (0, alepha.$use)(corsOptions);
55
- /**
56
- * Registered CORS configurations with their path patterns
57
- */
58
- registeredConfigs = [];
59
- /**
60
- * Register a CORS configuration (called by descriptors)
61
- */
62
- registerCors(config) {
63
- this.registeredConfigs.push(config);
64
- }
65
- onStart = (0, alepha.$hook)({
66
- on: "start",
67
- handler: async () => {
68
- for (const config of this.registeredConfigs) if (config.paths) for (const pattern of config.paths) {
69
- const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);
70
- for (const route of matchedRoutes) route.cors = this.buildCorsOptions(config);
71
- }
72
- if (this.registeredConfigs.length > 0) this.log.info(`Initialized with ${this.registeredConfigs.length} registered CORS configurations.`);
73
- }
74
- });
75
- configure = (0, alepha.$hook)({
76
- on: "configure",
77
- handler: () => {
78
- const routes = this.serverRouterProvider.getRoutes();
79
- for (const route of routes) {
80
- if (!route.method || route.method === "GET" || route.method === "OPTIONS") continue;
81
- this.serverRouterProvider.createRoute({
82
- path: route.path,
83
- method: "OPTIONS",
84
- handler: ({ reply }) => {
85
- reply.setStatus(204);
86
- }
87
- });
88
- }
89
- }
90
- });
91
- onRequest = (0, alepha.$hook)({
92
- on: "server:onRequest",
93
- handler: ({ route, request }) => {
94
- const corsConfig = route.cors ?? this.globalOptions;
95
- this.applyCorsHeaders(request, corsConfig);
96
- }
97
- });
98
- /**
99
- * Build complete CORS options by merging with global defaults
100
- */
101
- buildCorsOptions(config) {
102
- return {
103
- origin: config.origin ?? this.globalOptions.origin,
104
- methods: config.methods ?? this.globalOptions.methods,
105
- headers: config.headers ?? this.globalOptions.headers,
106
- credentials: config.credentials ?? this.globalOptions.credentials,
107
- maxAge: config.maxAge ?? this.globalOptions.maxAge
108
- };
109
- }
110
- /**
111
- * Apply CORS headers to the response
112
- */
113
- applyCorsHeaders(request, options) {
114
- const reqOrigin = request.headers.origin;
115
- const { origin, methods, headers, credentials, maxAge } = options;
116
- if (reqOrigin && this.isOriginAllowed(reqOrigin, origin)) request.reply.setHeader("Access-Control-Allow-Origin", reqOrigin);
117
- if (credentials) request.reply.setHeader("Access-Control-Allow-Credentials", "true");
118
- request.reply.setHeader("Access-Control-Allow-Methods", methods.join(", "));
119
- request.reply.setHeader("Access-Control-Allow-Headers", headers.join(", "));
120
- if (maxAge != null) request.reply.setHeader("Access-Control-Max-Age", String(maxAge));
121
- }
122
- isOriginAllowed(origin, allowed) {
123
- if (!allowed) return false;
124
- if (allowed === "*") return true;
125
- return allowed.split(",").map((o) => o.trim()).includes(origin ?? "");
126
- }
127
- };
128
-
129
- //#endregion
130
- //#region src/server-cors/descriptors/$cors.ts
131
- /**
132
- * Declares CORS configuration for specific server routes.
133
- * This descriptor provides path-based CORS configuration.
134
- *
135
- * @example
136
- * ```ts
137
- * class ApiService {
138
- * // Apply specific CORS to API routes
139
- * cors = $cors({
140
- * paths: ["/api/*"],
141
- * origin: "https://app.example.com",
142
- * credentials: true,
143
- * });
144
- * }
145
- * ```
146
- */
147
- const $cors = (options) => {
148
- return (0, alepha.createDescriptor)(CorsDescriptor, options);
149
- };
150
- var CorsDescriptor = class extends alepha.Descriptor {
151
- serverCorsProvider = (0, alepha.$inject)(ServerCorsProvider);
152
- get name() {
153
- return this.options.name ?? `${this.config.propertyKey}`;
154
- }
155
- onInit() {
156
- this.serverCorsProvider.registerCors(this.options);
157
- }
158
- };
159
- $cors[alepha.KIND] = CorsDescriptor;
160
-
161
- //#endregion
162
- //#region src/server-cors/index.ts
163
- /**
164
- * Plugin for configuring CORS on the Alepha server.
165
- *
166
- * @example
167
- * ```ts
168
- * import { Alepha, $route } from "alepha";
169
- * import { AlephaServerCors, $cors } from "alepha/server-cors";
170
- *
171
- * class ApiService {
172
- * // Global CORS is applied via corsOptions atom
173
- *
174
- * // Path-specific CORS for API routes
175
- * apiCors = $cors({
176
- * paths: ["/api/*"],
177
- * origin: "https://app.example.com",
178
- * credentials: true,
179
- * });
180
- *
181
- * route = $route({
182
- * path: "/api/data",
183
- * method: "POST",
184
- * handler: () => ({ data: "hello" }),
185
- * });
186
- * }
187
- * ```
188
- */
189
- const AlephaServerCors = (0, alepha.$module)({
190
- name: "alepha.server.cors",
191
- descriptors: [$cors],
192
- services: [ServerCorsProvider]
193
- });
194
-
195
- //#endregion
196
- exports.$cors = $cors;
197
- exports.AlephaServerCors = AlephaServerCors;
198
- exports.CorsDescriptor = CorsDescriptor;
199
- exports.ServerCorsProvider = ServerCorsProvider;
200
- exports.corsOptions = corsOptions;
201
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":["t","ServerRouterProvider","Descriptor","KIND"],"sources":["../../src/server-cors/providers/ServerCorsProvider.ts","../../src/server-cors/descriptors/$cors.ts","../../src/server-cors/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $use, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { ServerRouterProvider } from \"alepha/server\";\nimport type { CorsDescriptorConfig } from \"../descriptors/$cors.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * CORS configuration atom (global defaults)\n */\nexport const corsOptions = $atom({\n name: \"alepha.server.cors.options\",\n schema: t.object({\n origin: t.optional(\n t.string({\n description:\n \"Allowed origins (* for all, string for single, comma-separated for multiple)\",\n default: \"*\",\n }),\n ),\n methods: t.array(t.string(), {\n description: \"Allowed HTTP methods\",\n default: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n }),\n headers: t.array(t.string(), {\n description: \"Allowed headers\",\n default: [\"Content-Type\", \"Authorization\"],\n }),\n credentials: t.optional(\n t.boolean({\n description: \"Allow credentials\",\n default: true,\n }),\n ),\n maxAge: t.optional(\n t.number({\n description: \"Preflight cache duration in seconds\",\n }),\n ),\n }),\n default: {\n origin: \"*\",\n methods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"],\n headers: [\"Content-Type\", \"Authorization\"],\n credentials: true,\n },\n});\n\nexport type CorsOptions = Static<typeof corsOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [corsOptions.key]: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ServerCorsProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly globalOptions = $use(corsOptions);\n\n /**\n * Registered CORS configurations with their path patterns\n */\n public readonly registeredConfigs: CorsDescriptorConfig[] = [];\n\n /**\n * Register a CORS configuration (called by descriptors)\n */\n public registerCors(config: CorsDescriptorConfig): void {\n this.registeredConfigs.push(config);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Apply path-specific CORS configs to routes\n for (const config of this.registeredConfigs) {\n if (config.paths) {\n for (const pattern of config.paths) {\n const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.cors = this.buildCorsOptions(config);\n }\n }\n }\n }\n\n if (this.registeredConfigs.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredConfigs.length} registered CORS configurations.`,\n );\n }\n },\n });\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: () => {\n const routes = this.serverRouterProvider.getRoutes();\n for (const route of routes) {\n if (\n !route.method ||\n route.method === \"GET\" ||\n route.method === \"OPTIONS\"\n ) {\n continue;\n }\n\n this.serverRouterProvider.createRoute({\n path: route.path,\n method: \"OPTIONS\",\n handler: ({ reply }) => {\n reply.setStatus(204);\n },\n });\n }\n },\n });\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ route, request }) => {\n // Use route-specific CORS if defined, otherwise use global options\n const corsConfig = route.cors ?? this.globalOptions;\n this.applyCorsHeaders(request, corsConfig);\n },\n });\n\n /**\n * Build complete CORS options by merging with global defaults\n */\n protected buildCorsOptions(config: CorsDescriptorConfig): CorsOptions {\n return {\n origin: config.origin ?? this.globalOptions.origin,\n methods: config.methods ?? this.globalOptions.methods,\n headers: config.headers ?? this.globalOptions.headers,\n credentials: config.credentials ?? this.globalOptions.credentials,\n maxAge: config.maxAge ?? this.globalOptions.maxAge,\n };\n }\n\n /**\n * Apply CORS headers to the response\n */\n protected applyCorsHeaders(\n request: {\n headers: { origin?: string };\n reply: { setHeader: (name: string, value: string) => void };\n },\n options: CorsOptions,\n ): void {\n const reqOrigin = request.headers.origin;\n const { origin, methods, headers, credentials, maxAge } = options;\n\n if (reqOrigin && this.isOriginAllowed(reqOrigin, origin)) {\n request.reply.setHeader(\"Access-Control-Allow-Origin\", reqOrigin);\n }\n\n if (credentials) {\n request.reply.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n }\n\n request.reply.setHeader(\"Access-Control-Allow-Methods\", methods.join(\", \"));\n request.reply.setHeader(\"Access-Control-Allow-Headers\", headers.join(\", \"));\n\n if (maxAge != null) {\n request.reply.setHeader(\"Access-Control-Max-Age\", String(maxAge));\n }\n }\n\n public isOriginAllowed(\n origin: string | undefined,\n allowed: CorsOptions[\"origin\"],\n ): boolean {\n if (!allowed) return false;\n if (allowed === \"*\") return true;\n return allowed\n .split(\",\")\n .map((o) => o.trim())\n .includes(origin ?? \"\");\n }\n}\n\nexport type ServerCorsProviderOptions = CorsOptions;\n","import { $inject, createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type { CorsOptions } from \"../providers/ServerCorsProvider.ts\";\nimport { ServerCorsProvider } from \"../providers/ServerCorsProvider.ts\";\n\n/**\n * Declares CORS configuration for specific server routes.\n * This descriptor provides path-based CORS configuration.\n *\n * @example\n * ```ts\n * class ApiService {\n * // Apply specific CORS to API routes\n * cors = $cors({\n * paths: [\"/api/*\"],\n * origin: \"https://app.example.com\",\n * credentials: true,\n * });\n * }\n * ```\n */\nexport const $cors = (\n options: CorsDescriptorConfig,\n): AbstractCorsDescriptor => {\n return createDescriptor(CorsDescriptor, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CorsDescriptorConfig extends Partial<CorsOptions> {\n /** Name identifier for this CORS config (default: property key) */\n name?: string;\n /** Path patterns to match (supports wildcards like /api/*) */\n paths?: string[];\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface AbstractCorsDescriptor {\n readonly name: string;\n readonly options: CorsDescriptorConfig;\n}\n\nexport class CorsDescriptor\n extends Descriptor<CorsDescriptorConfig>\n implements AbstractCorsDescriptor\n{\n protected readonly serverCorsProvider = $inject(ServerCorsProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this CORS configuration with the provider\n this.serverCorsProvider.registerCors(this.options);\n }\n}\n\n$cors[KIND] = CorsDescriptor;\n","import { $module } from \"alepha\";\nimport { $cors } from \"./descriptors/$cors.ts\";\nimport {\n type CorsOptions,\n ServerCorsProvider,\n} from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$cors.ts\";\nexport * from \"./providers/ServerCorsProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRoute {\n /**\n * Route-specific CORS configuration.\n * If set, overrides the global CORS options for this route.\n */\n cors?: CorsOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for configuring CORS on the Alepha server.\n *\n * @example\n * ```ts\n * import { Alepha, $route } from \"alepha\";\n * import { AlephaServerCors, $cors } from \"alepha/server-cors\";\n *\n * class ApiService {\n * // Global CORS is applied via corsOptions atom\n *\n * // Path-specific CORS for API routes\n * apiCors = $cors({\n * paths: [\"/api/*\"],\n * origin: \"https://app.example.com\",\n * credentials: true,\n * });\n *\n * route = $route({\n * path: \"/api/data\",\n * method: \"POST\",\n * handler: () => ({ data: \"hello\" }),\n * });\n * }\n * ```\n */\nexport const AlephaServerCors = $module({\n name: \"alepha.server.cors\",\n descriptors: [$cors],\n services: [ServerCorsProvider],\n});\n"],"mappings":"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"}