alepha 0.13.0 → 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 (195) hide show
  1. package/dist/api-jobs/index.d.ts +26 -26
  2. package/dist/api-users/index.d.ts +1 -1
  3. package/dist/cli/{dist-Sz2EXvQX.cjs → dist-Dl9Vl7Ur.js} +17 -13
  4. package/dist/cli/{dist-BBPjuQ56.js.map → dist-Dl9Vl7Ur.js.map} +1 -1
  5. package/dist/cli/index.d.ts +3 -11
  6. package/dist/cli/index.js +106 -74
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/email/index.js +71 -73
  9. package/dist/email/index.js.map +1 -1
  10. package/dist/orm/index.d.ts +1 -1
  11. package/dist/orm/index.js.map +1 -1
  12. package/dist/queue/index.d.ts +4 -4
  13. package/dist/retry/index.d.ts +1 -1
  14. package/dist/retry/index.js +2 -2
  15. package/dist/retry/index.js.map +1 -1
  16. package/dist/scheduler/index.d.ts +6 -6
  17. package/dist/security/index.d.ts +28 -28
  18. package/dist/server/index.js +1 -1
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server-health/index.d.ts +17 -17
  21. package/dist/server-metrics/index.js +170 -174
  22. package/dist/server-metrics/index.js.map +1 -1
  23. package/dist/server-security/index.d.ts +9 -9
  24. package/dist/vite/index.js +4 -5
  25. package/dist/vite/index.js.map +1 -1
  26. package/dist/websocket/index.d.ts +7 -7
  27. package/package.json +52 -103
  28. package/src/cli/apps/AlephaPackageBuilderCli.ts +7 -2
  29. package/src/cli/assets/appRouterTs.ts +9 -0
  30. package/src/cli/assets/indexHtml.ts +2 -1
  31. package/src/cli/assets/mainBrowserTs.ts +10 -0
  32. package/src/cli/commands/CoreCommands.ts +6 -5
  33. package/src/cli/commands/DrizzleCommands.ts +65 -57
  34. package/src/cli/commands/VerifyCommands.ts +1 -1
  35. package/src/cli/services/ProjectUtils.ts +44 -38
  36. package/src/orm/providers/DrizzleKitProvider.ts +1 -1
  37. package/src/retry/descriptors/$retry.ts +5 -3
  38. package/src/server/providers/NodeHttpServerProvider.ts +1 -1
  39. package/src/vite/helpers/boot.ts +3 -3
  40. package/dist/api-files/index.cjs +0 -1293
  41. package/dist/api-files/index.cjs.map +0 -1
  42. package/dist/api-files/index.d.cts +0 -829
  43. package/dist/api-jobs/index.cjs +0 -274
  44. package/dist/api-jobs/index.cjs.map +0 -1
  45. package/dist/api-jobs/index.d.cts +0 -654
  46. package/dist/api-notifications/index.cjs +0 -380
  47. package/dist/api-notifications/index.cjs.map +0 -1
  48. package/dist/api-notifications/index.d.cts +0 -289
  49. package/dist/api-parameters/index.cjs +0 -66
  50. package/dist/api-parameters/index.cjs.map +0 -1
  51. package/dist/api-parameters/index.d.cts +0 -84
  52. package/dist/api-users/index.cjs +0 -6009
  53. package/dist/api-users/index.cjs.map +0 -1
  54. package/dist/api-users/index.d.cts +0 -4740
  55. package/dist/api-verifications/index.cjs +0 -407
  56. package/dist/api-verifications/index.cjs.map +0 -1
  57. package/dist/api-verifications/index.d.cts +0 -207
  58. package/dist/batch/index.cjs +0 -408
  59. package/dist/batch/index.cjs.map +0 -1
  60. package/dist/batch/index.d.cts +0 -330
  61. package/dist/bin/index.cjs +0 -17
  62. package/dist/bin/index.cjs.map +0 -1
  63. package/dist/bin/index.d.cts +0 -1
  64. package/dist/bucket/index.cjs +0 -303
  65. package/dist/bucket/index.cjs.map +0 -1
  66. package/dist/bucket/index.d.cts +0 -355
  67. package/dist/cache/index.cjs +0 -241
  68. package/dist/cache/index.cjs.map +0 -1
  69. package/dist/cache/index.d.cts +0 -202
  70. package/dist/cache-redis/index.cjs +0 -84
  71. package/dist/cache-redis/index.cjs.map +0 -1
  72. package/dist/cache-redis/index.d.cts +0 -40
  73. package/dist/cli/chunk-DSlc6foC.cjs +0 -43
  74. package/dist/cli/dist-BBPjuQ56.js +0 -2778
  75. package/dist/cli/dist-Sz2EXvQX.cjs.map +0 -1
  76. package/dist/cli/index.cjs +0 -1241
  77. package/dist/cli/index.cjs.map +0 -1
  78. package/dist/cli/index.d.cts +0 -422
  79. package/dist/command/index.cjs +0 -693
  80. package/dist/command/index.cjs.map +0 -1
  81. package/dist/command/index.d.cts +0 -340
  82. package/dist/core/index.cjs +0 -2264
  83. package/dist/core/index.cjs.map +0 -1
  84. package/dist/core/index.d.cts +0 -1927
  85. package/dist/datetime/index.cjs +0 -318
  86. package/dist/datetime/index.cjs.map +0 -1
  87. package/dist/datetime/index.d.cts +0 -145
  88. package/dist/email/index.cjs +0 -10874
  89. package/dist/email/index.cjs.map +0 -1
  90. package/dist/email/index.d.cts +0 -186
  91. package/dist/fake/index.cjs +0 -34641
  92. package/dist/fake/index.cjs.map +0 -1
  93. package/dist/fake/index.d.cts +0 -74
  94. package/dist/file/index.cjs +0 -1212
  95. package/dist/file/index.cjs.map +0 -1
  96. package/dist/file/index.d.cts +0 -698
  97. package/dist/lock/index.cjs +0 -226
  98. package/dist/lock/index.cjs.map +0 -1
  99. package/dist/lock/index.d.cts +0 -361
  100. package/dist/lock-redis/index.cjs +0 -113
  101. package/dist/lock-redis/index.cjs.map +0 -1
  102. package/dist/lock-redis/index.d.cts +0 -24
  103. package/dist/logger/index.cjs +0 -521
  104. package/dist/logger/index.cjs.map +0 -1
  105. package/dist/logger/index.d.cts +0 -281
  106. package/dist/orm/index.cjs +0 -2986
  107. package/dist/orm/index.cjs.map +0 -1
  108. package/dist/orm/index.d.cts +0 -2213
  109. package/dist/queue/index.cjs +0 -1044
  110. package/dist/queue/index.cjs.map +0 -1
  111. package/dist/queue/index.d.cts +0 -1265
  112. package/dist/queue-redis/index.cjs +0 -873
  113. package/dist/queue-redis/index.cjs.map +0 -1
  114. package/dist/queue-redis/index.d.cts +0 -82
  115. package/dist/redis/index.cjs +0 -153
  116. package/dist/redis/index.cjs.map +0 -1
  117. package/dist/redis/index.d.cts +0 -82
  118. package/dist/retry/index.cjs +0 -146
  119. package/dist/retry/index.cjs.map +0 -1
  120. package/dist/retry/index.d.cts +0 -172
  121. package/dist/router/index.cjs +0 -111
  122. package/dist/router/index.cjs.map +0 -1
  123. package/dist/router/index.d.cts +0 -46
  124. package/dist/scheduler/index.cjs +0 -576
  125. package/dist/scheduler/index.cjs.map +0 -1
  126. package/dist/scheduler/index.d.cts +0 -145
  127. package/dist/security/index.cjs +0 -2402
  128. package/dist/security/index.cjs.map +0 -1
  129. package/dist/security/index.d.cts +0 -598
  130. package/dist/server/index.cjs +0 -1680
  131. package/dist/server/index.cjs.map +0 -1
  132. package/dist/server/index.d.cts +0 -810
  133. package/dist/server-auth/index.cjs +0 -3146
  134. package/dist/server-auth/index.cjs.map +0 -1
  135. package/dist/server-auth/index.d.cts +0 -1164
  136. package/dist/server-cache/index.cjs +0 -252
  137. package/dist/server-cache/index.cjs.map +0 -1
  138. package/dist/server-cache/index.d.cts +0 -164
  139. package/dist/server-compress/index.cjs +0 -141
  140. package/dist/server-compress/index.cjs.map +0 -1
  141. package/dist/server-compress/index.d.cts +0 -38
  142. package/dist/server-cookies/index.cjs +0 -234
  143. package/dist/server-cookies/index.cjs.map +0 -1
  144. package/dist/server-cookies/index.d.cts +0 -144
  145. package/dist/server-cors/index.cjs +0 -201
  146. package/dist/server-cors/index.cjs.map +0 -1
  147. package/dist/server-cors/index.d.cts +0 -140
  148. package/dist/server-health/index.cjs +0 -62
  149. package/dist/server-health/index.cjs.map +0 -1
  150. package/dist/server-health/index.d.cts +0 -58
  151. package/dist/server-helmet/index.cjs +0 -131
  152. package/dist/server-helmet/index.cjs.map +0 -1
  153. package/dist/server-helmet/index.d.cts +0 -97
  154. package/dist/server-links/index.cjs +0 -992
  155. package/dist/server-links/index.cjs.map +0 -1
  156. package/dist/server-links/index.d.cts +0 -513
  157. package/dist/server-metrics/index.cjs +0 -4535
  158. package/dist/server-metrics/index.cjs.map +0 -1
  159. package/dist/server-metrics/index.d.cts +0 -35
  160. package/dist/server-multipart/index.cjs +0 -237
  161. package/dist/server-multipart/index.cjs.map +0 -1
  162. package/dist/server-multipart/index.d.cts +0 -50
  163. package/dist/server-proxy/index.cjs +0 -186
  164. package/dist/server-proxy/index.cjs.map +0 -1
  165. package/dist/server-proxy/index.d.cts +0 -234
  166. package/dist/server-rate-limit/index.cjs +0 -241
  167. package/dist/server-rate-limit/index.cjs.map +0 -1
  168. package/dist/server-rate-limit/index.d.cts +0 -183
  169. package/dist/server-security/index.cjs +0 -316
  170. package/dist/server-security/index.cjs.map +0 -1
  171. package/dist/server-security/index.d.cts +0 -173
  172. package/dist/server-static/index.cjs +0 -170
  173. package/dist/server-static/index.cjs.map +0 -1
  174. package/dist/server-static/index.d.cts +0 -121
  175. package/dist/server-swagger/index.cjs +0 -1021
  176. package/dist/server-swagger/index.cjs.map +0 -1
  177. package/dist/server-swagger/index.d.cts +0 -382
  178. package/dist/sms/index.cjs +0 -221
  179. package/dist/sms/index.cjs.map +0 -1
  180. package/dist/sms/index.d.cts +0 -130
  181. package/dist/thread/index.cjs +0 -350
  182. package/dist/thread/index.cjs.map +0 -1
  183. package/dist/thread/index.d.cts +0 -260
  184. package/dist/topic/index.cjs +0 -282
  185. package/dist/topic/index.cjs.map +0 -1
  186. package/dist/topic/index.d.cts +0 -523
  187. package/dist/topic-redis/index.cjs +0 -71
  188. package/dist/topic-redis/index.cjs.map +0 -1
  189. package/dist/topic-redis/index.d.cts +0 -42
  190. package/dist/vite/index.cjs +0 -1077
  191. package/dist/vite/index.cjs.map +0 -1
  192. package/dist/vite/index.d.cts +0 -542
  193. package/dist/websocket/index.cjs +0 -1117
  194. package/dist/websocket/index.cjs.map +0 -1
  195. 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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"}