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,992 +0,0 @@
1
- let alepha = require("alepha");
2
- let alepha_security = require("alepha/security");
3
- let alepha_server = require("alepha/server");
4
- let node_crypto = require("node:crypto");
5
- let alepha_logger = require("alepha/logger");
6
- let alepha_retry = require("alepha/retry");
7
- let node_stream_web = require("node:stream/web");
8
-
9
- //#region src/server-security/providers/ServerBasicAuthProvider.ts
10
- var ServerBasicAuthProvider = class {
11
- alepha = (0, alepha.$inject)(alepha.Alepha);
12
- log = (0, alepha_logger.$logger)();
13
- routerProvider = (0, alepha.$inject)(alepha_server.ServerRouterProvider);
14
- realm = "Secure Area";
15
- /**
16
- * Registered basic auth descriptors with their configurations
17
- */
18
- registeredAuths = [];
19
- /**
20
- * Register a basic auth configuration (called by descriptors)
21
- */
22
- registerAuth(config) {
23
- this.registeredAuths.push(config);
24
- }
25
- onStart = (0, alepha.$hook)({
26
- on: "start",
27
- handler: async () => {
28
- for (const auth of this.registeredAuths) if (auth.paths) for (const pattern of auth.paths) {
29
- const matchedRoutes = this.routerProvider.getRoutes(pattern);
30
- for (const route of matchedRoutes) route.secure = { basic: {
31
- username: auth.username,
32
- password: auth.password
33
- } };
34
- }
35
- if (this.registeredAuths.length > 0) this.log.info(`Initialized with ${this.registeredAuths.length} registered basic-auth configurations.`);
36
- }
37
- });
38
- /**
39
- * Hook into server:onRequest to check basic auth
40
- */
41
- onRequest = (0, alepha.$hook)({
42
- on: "server:onRequest",
43
- handler: async ({ route, request }) => {
44
- const routeAuth = route.secure;
45
- if (typeof routeAuth === "object" && "basic" in routeAuth && routeAuth.basic) this.checkAuth(request, routeAuth.basic);
46
- }
47
- });
48
- /**
49
- * Hook into action:onRequest to check basic auth for actions
50
- */
51
- onActionRequest = (0, alepha.$hook)({
52
- on: "action:onRequest",
53
- handler: async ({ action, request }) => {
54
- const routeAuth = action.route.secure;
55
- if (isBasicAuth(routeAuth)) this.checkAuth(request, routeAuth.basic);
56
- }
57
- });
58
- /**
59
- * Check basic authentication
60
- */
61
- checkAuth(request, options) {
62
- const authHeader = request.headers?.authorization;
63
- if (!authHeader || !authHeader.startsWith("Basic ")) {
64
- this.sendAuthRequired(request);
65
- throw new alepha_server.HttpError({
66
- status: 401,
67
- message: "Authentication required"
68
- });
69
- }
70
- const base64Credentials = authHeader.slice(6);
71
- const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8");
72
- const colonIndex = credentials.indexOf(":");
73
- const username = colonIndex !== -1 ? credentials.slice(0, colonIndex) : credentials;
74
- const password = colonIndex !== -1 ? credentials.slice(colonIndex + 1) : "";
75
- if (!this.timingSafeCredentialCheck(username, password, options.username, options.password)) {
76
- this.sendAuthRequired(request);
77
- this.log.warn(`Failed basic auth attempt for user`, { username });
78
- throw new alepha_server.HttpError({
79
- status: 401,
80
- message: "Invalid credentials"
81
- });
82
- }
83
- }
84
- /**
85
- * Performs a timing-safe comparison of credentials to prevent timing attacks.
86
- * Always compares both username and password to avoid leaking which one is wrong.
87
- */
88
- timingSafeCredentialCheck(inputUsername, inputPassword, expectedUsername, expectedPassword) {
89
- const inputUserBuf = Buffer.from(inputUsername, "utf-8");
90
- const expectedUserBuf = Buffer.from(expectedUsername, "utf-8");
91
- const inputPassBuf = Buffer.from(inputPassword, "utf-8");
92
- const expectedPassBuf = Buffer.from(expectedPassword, "utf-8");
93
- return (this.safeCompare(inputUserBuf, expectedUserBuf) & this.safeCompare(inputPassBuf, expectedPassBuf)) === 1;
94
- }
95
- /**
96
- * Compares two buffers in constant time, handling different lengths safely.
97
- * Returns 1 if equal, 0 if not equal.
98
- */
99
- safeCompare(input, expected) {
100
- if (input.length !== expected.length) {
101
- (0, node_crypto.timingSafeEqual)(input, input);
102
- return 0;
103
- }
104
- return (0, node_crypto.timingSafeEqual)(input, expected) ? 1 : 0;
105
- }
106
- /**
107
- * Send WWW-Authenticate header
108
- */
109
- sendAuthRequired(request) {
110
- request.reply.setHeader("WWW-Authenticate", `Basic realm="${this.realm}"`);
111
- }
112
- };
113
- const isBasicAuth = (value) => {
114
- return typeof value === "object" && !!value && "basic" in value && !!value.basic;
115
- };
116
-
117
- //#endregion
118
- //#region src/server-security/descriptors/$basicAuth.ts
119
- /**
120
- * Declares HTTP Basic Authentication for server routes.
121
- * This descriptor provides methods to protect routes with username/password authentication.
122
- */
123
- const $basicAuth = (options) => {
124
- return (0, alepha.createDescriptor)(BasicAuthDescriptor, options);
125
- };
126
- var BasicAuthDescriptor = class extends alepha.Descriptor {
127
- serverBasicAuthProvider = (0, alepha.$inject)(ServerBasicAuthProvider);
128
- get name() {
129
- return this.options.name ?? `${this.config.propertyKey}`;
130
- }
131
- onInit() {
132
- this.serverBasicAuthProvider.registerAuth(this.options);
133
- }
134
- /**
135
- * Checks basic auth for the given request using this descriptor's configuration.
136
- */
137
- check(request, options) {
138
- const mergedOptions = {
139
- ...this.options,
140
- ...options
141
- };
142
- this.serverBasicAuthProvider.checkAuth(request, mergedOptions);
143
- }
144
- };
145
- $basicAuth[alepha.KIND] = BasicAuthDescriptor;
146
-
147
- //#endregion
148
- //#region src/server-security/providers/ServerSecurityProvider.ts
149
- var ServerSecurityProvider = class {
150
- log = (0, alepha_logger.$logger)();
151
- securityProvider = (0, alepha.$inject)(alepha_security.SecurityProvider);
152
- jwtProvider = (0, alepha.$inject)(alepha_security.JwtProvider);
153
- alepha = (0, alepha.$inject)(alepha.Alepha);
154
- onConfigure = (0, alepha.$hook)({
155
- on: "configure",
156
- handler: async () => {
157
- for (const action of this.alepha.descriptors(alepha_server.$action)) {
158
- if (action.options.disabled || action.options.secure === false || this.securityProvider.getRealms().length === 0) continue;
159
- if (typeof action.options.secure !== "object") this.securityProvider.createPermission({
160
- name: action.name,
161
- group: action.group,
162
- method: action.route.method,
163
- path: action.route.path
164
- });
165
- }
166
- }
167
- });
168
- onActionRequest = (0, alepha.$hook)({
169
- on: "action:onRequest",
170
- handler: async ({ action, request, options }) => {
171
- if (action.options.secure === false && !options.user) {
172
- this.log.trace("Skipping security check for route");
173
- return;
174
- }
175
- if (isBasicAuth(action.route.secure)) return;
176
- const permission = this.securityProvider.getPermissions().find((it) => it.path === action.route.path && it.method === action.route.method);
177
- try {
178
- request.user = this.createUserFromLocalFunctionContext(options, permission);
179
- const route = action.route;
180
- if (typeof route.secure === "object") this.check(request.user, route.secure);
181
- this.alepha.state.set("alepha.server.request.user", this.alepha.codec.decode(alepha_security.userAccountInfoSchema, request.user));
182
- } catch (error) {
183
- if (action.options.secure || permission) throw error;
184
- this.log.trace("Skipping security check for action");
185
- }
186
- }
187
- });
188
- onRequest = (0, alepha.$hook)({
189
- on: "server:onRequest",
190
- priority: "last",
191
- handler: async ({ request, route }) => {
192
- if (route.secure === false) {
193
- this.log.trace("Skipping security check for route - explicitly disabled");
194
- return;
195
- }
196
- if (isBasicAuth(route.secure)) return;
197
- const permission = this.securityProvider.getPermissions().find((it) => it.path === route.path && it.method === route.method);
198
- if (!request.headers.authorization && !route.secure && !permission) {
199
- this.log.trace("Skipping security check for route - no authorization header and not secure");
200
- return;
201
- }
202
- try {
203
- request.user = await this.securityProvider.createUserFromToken(request.headers.authorization, { permission });
204
- if (typeof route.secure === "object") this.check(request.user, route.secure);
205
- this.alepha.state.set("alepha.server.request.user", this.alepha.codec.decode(alepha_security.userAccountInfoSchema, request.user));
206
- this.log.trace("User set from request token", {
207
- user: request.user,
208
- permission
209
- });
210
- } catch (error) {
211
- if (route.secure || permission) throw error;
212
- this.log.trace("Skipping security check for route - error occurred", error);
213
- }
214
- }
215
- });
216
- check(user, secure) {
217
- if (secure.realm) {
218
- if (user.realm !== secure.realm) throw new alepha_server.ForbiddenError(`User must belong to realm '${secure.realm}' to access this route`);
219
- }
220
- }
221
- /**
222
- * Get the user account token for a local action call.
223
- * There are three possible sources for the user:
224
- * - `options.user`: the user passed in the options
225
- * - `"system"`: the system user from the state (you MUST set state `server.security.system.user`)
226
- * - `"context"`: the user from the request context (you MUST be in an HTTP request context)
227
- *
228
- * Priority order: `options.user` > `"system"` > `"context"`.
229
- *
230
- * In testing environment, if no user is provided, a test user is created based on the SecurityProvider's roles.
231
- */
232
- createUserFromLocalFunctionContext(options, permission) {
233
- const fromOptions = typeof options.user === "object" ? options.user : void 0;
234
- const type = typeof options.user === "string" ? options.user : void 0;
235
- let user;
236
- const fromContext = this.alepha.context.get("request")?.user;
237
- const fromSystem = this.alepha.state.get("alepha.server.security.system.user");
238
- if (type === "system") user = fromSystem;
239
- else if (type === "context") user = fromContext;
240
- else user = fromOptions ?? fromContext ?? fromSystem;
241
- if (!user) {
242
- if (this.alepha.isTest() && !("user" in options)) return this.createTestUser();
243
- throw new alepha_server.UnauthorizedError("User is required for calling this action");
244
- }
245
- const roles = user.roles ?? (this.alepha.isTest() ? this.securityProvider.getRoles().map((role) => role.name) : []);
246
- let ownership;
247
- if (permission) {
248
- const result = this.securityProvider.checkPermission(permission, ...roles);
249
- if (!result.isAuthorized) throw new alepha_server.ForbiddenError(`Permission '${this.securityProvider.permissionToString(permission)}' is required for this route`);
250
- ownership = result.ownership;
251
- }
252
- return {
253
- ...user,
254
- ownership
255
- };
256
- }
257
- createTestUser() {
258
- return {
259
- id: (0, node_crypto.randomUUID)(),
260
- name: "Test",
261
- roles: this.securityProvider.getRoles().map((role) => role.name)
262
- };
263
- }
264
- onClientRequest = (0, alepha.$hook)({
265
- on: "client:onRequest",
266
- handler: async ({ request, options }) => {
267
- if (!this.alepha.isTest()) return;
268
- if ("user" in options && options.user === void 0) return;
269
- request.headers = new Headers(request.headers);
270
- if (!request.headers.has("authorization")) {
271
- const test = this.createTestUser();
272
- const user = typeof options?.user === "object" ? options.user : void 0;
273
- const sub = user?.id ?? test.id;
274
- const roles = user?.roles ?? test.roles;
275
- const token = await this.jwtProvider.create({
276
- sub,
277
- roles
278
- }, user?.realm ?? this.securityProvider.getRealms()[0]?.name);
279
- request.headers.set("authorization", `Bearer ${token}`);
280
- }
281
- }
282
- });
283
- };
284
-
285
- //#endregion
286
- //#region src/server-security/index.ts
287
- /**
288
- * Plugin for Alepha Server that provides security features. Based on the Alepha Security module.
289
- *
290
- * By default, all $action will be guarded by a permission check.
291
- *
292
- * @see {@link ServerSecurityProvider}
293
- * @module alepha.server.security
294
- */
295
- const AlephaServerSecurity = (0, alepha.$module)({
296
- name: "alepha.server.security",
297
- descriptors: [
298
- alepha_security.$realm,
299
- alepha_security.$role,
300
- alepha_security.$permission,
301
- $basicAuth
302
- ],
303
- services: [
304
- alepha_server.AlephaServer,
305
- alepha_security.AlephaSecurity,
306
- ServerSecurityProvider,
307
- ServerBasicAuthProvider
308
- ]
309
- });
310
-
311
- //#endregion
312
- //#region src/server-links/schemas/apiLinksResponseSchema.ts
313
- const apiLinkSchema = alepha.t.object({
314
- name: alepha.t.text({ description: "Name of the API link, used for identification." }),
315
- group: alepha.t.optional(alepha.t.text({ description: "Group to which the API link belongs, used for categorization." })),
316
- path: alepha.t.text({ description: "Pathname used to access the API link." }),
317
- method: alepha.t.optional(alepha.t.text({ description: "HTTP method used for the API link, e.g., GET, POST, etc. If not specified, defaults to GET." })),
318
- requestBodyType: alepha.t.optional(alepha.t.text({ description: "Type of the request body for the API link. Default is application/json for POST/PUT/PATCH, null for others." })),
319
- service: alepha.t.optional(alepha.t.text({ description: "Service name associated with the API link, used for service discovery." }))
320
- });
321
- const apiLinksResponseSchema = alepha.t.object({
322
- prefix: alepha.t.optional(alepha.t.text()),
323
- links: alepha.t.array(apiLinkSchema)
324
- });
325
-
326
- //#endregion
327
- //#region src/server-links/providers/LinkProvider.ts
328
- /**
329
- * Browser, SSR friendly, service to handle links.
330
- */
331
- var LinkProvider = class LinkProvider {
332
- static path = {
333
- apiLinks: "/api/_links",
334
- apiSchema: "/api/_links/:name/schema"
335
- };
336
- log = (0, alepha_logger.$logger)();
337
- alepha = (0, alepha.$inject)(alepha.Alepha);
338
- httpClient = (0, alepha.$inject)(alepha_server.HttpClient);
339
- serverLinks = [];
340
- /**
341
- * Get applicative links registered on the server.
342
- * This does not include lazy-loaded remote links.
343
- */
344
- getServerLinks() {
345
- if (this.alepha.isBrowser()) {
346
- this.log.warn("Getting server links in the browser is not supported. Use `fetchLinks` to get links from the server.");
347
- return [];
348
- }
349
- return this.serverLinks;
350
- }
351
- /**
352
- * Register a new link for the application.
353
- */
354
- registerLink(link) {
355
- if (this.alepha.isBrowser()) {
356
- this.log.warn("Registering links in the browser is not supported. Use `fetchLinks` to get links from the server.");
357
- return;
358
- }
359
- if (!link.handler && !link.host) throw new alepha.AlephaError("Can't create link - 'handler' or 'host' is required");
360
- if (this.serverLinks.some((l) => l.name === link.name)) this.serverLinks = this.serverLinks.filter((l) => l.name !== link.name);
361
- this.serverLinks.push(link);
362
- }
363
- get links() {
364
- const apiLinks = this.alepha.state.get("alepha.server.request.apiLinks")?.links;
365
- if (apiLinks) {
366
- if (this.alepha.isBrowser()) return apiLinks;
367
- const links = [];
368
- for (const link of apiLinks) {
369
- const originalLink = this.serverLinks.find((l) => l.name === link.name);
370
- if (originalLink) links.push(originalLink);
371
- }
372
- return links;
373
- }
374
- return this.serverLinks ?? [];
375
- }
376
- /**
377
- * Force browser to refresh links from the server.
378
- */
379
- async fetchLinks() {
380
- const { data } = await this.httpClient.fetch(`${LinkProvider.path.apiLinks}`, {
381
- method: "GET",
382
- schema: { response: apiLinksResponseSchema }
383
- });
384
- this.alepha.state.set("alepha.server.request.apiLinks", data);
385
- return data.links;
386
- }
387
- /**
388
- * Create a virtual client that can be used to call actions.
389
- *
390
- * Use js Proxy under the hood.
391
- */
392
- client(scope = {}) {
393
- return new Proxy({}, { get: (_, prop) => {
394
- if (typeof prop !== "string") return;
395
- return this.createVirtualAction(prop, scope);
396
- } });
397
- }
398
- /**
399
- * Check if a link with the given name exists.
400
- * @param name
401
- */
402
- can(name) {
403
- return this.links.some((link) => link.name === name);
404
- }
405
- /**
406
- * Resolve a link by its name and call it.
407
- * - If link is local, it will call the local handler.
408
- * - If link is remote, it will make a fetch request to the remote server.
409
- */
410
- async follow(name, config = {}, options = {}) {
411
- this.log.trace("Following link", {
412
- name,
413
- config,
414
- options
415
- });
416
- const link = await this.getLinkByName(name, options);
417
- if (link.handler && !options.request) {
418
- this.log.trace("Local link found", { name });
419
- return link.handler({
420
- method: link.method,
421
- url: new URL(`http://localhost${link.path}`),
422
- query: config.query ?? {},
423
- body: config.body ?? {},
424
- params: config.params ?? {},
425
- headers: config.headers ?? {},
426
- metadata: {},
427
- reply: new alepha_server.ServerReply()
428
- }, options);
429
- }
430
- this.log.trace("Remote link found", {
431
- name,
432
- host: link.host,
433
- service: link.service
434
- });
435
- return this.followRemote(link, config, options).then((response) => response.data);
436
- }
437
- createVirtualAction(name, scope = {}) {
438
- const $ = async (config = {}, options = {}) => {
439
- return this.follow(name, config, {
440
- ...scope,
441
- ...options
442
- });
443
- };
444
- Object.defineProperty($, "name", {
445
- value: name,
446
- writable: false
447
- });
448
- $.run = async (config = {}, options = {}) => {
449
- return this.follow(name, config, {
450
- ...scope,
451
- ...options
452
- });
453
- };
454
- $.fetch = async (config = {}, options = {}) => {
455
- const link = await this.getLinkByName(name, scope);
456
- return this.followRemote(link, config, options);
457
- };
458
- $.can = () => {
459
- return this.can(name);
460
- };
461
- return $;
462
- }
463
- async followRemote(link, config = {}, options = {}) {
464
- options.request ??= {};
465
- options.request.headers = new Headers(options.request.headers);
466
- const als = this.alepha.context.get("request");
467
- if (als?.headers.authorization) options.request.headers.set("authorization", als.headers.authorization);
468
- const context = this.alepha.context.get("context");
469
- if (typeof context === "string") options.request.headers.set("x-request-id", context);
470
- const action = {
471
- ...link,
472
- schema: {
473
- body: alepha.t.any(),
474
- response: alepha.t.any()
475
- }
476
- };
477
- if (!link.host && link.service) action.path = `/${link.service}${action.path}`;
478
- action.path = `${action.prefix ?? "/api"}${action.path}`;
479
- action.prefix = void 0;
480
- return this.httpClient.fetchAction({
481
- host: link.host,
482
- config,
483
- options,
484
- action
485
- });
486
- }
487
- async getLinkByName(name, options = {}) {
488
- if (this.alepha.isBrowser() && !this.alepha.state.get("alepha.server.request.apiLinks")) await this.fetchLinks();
489
- const link = this.links.find((a) => a.name === name && (!options.group || a.group === options.group) && (!options.service || options.service === a.service));
490
- if (!link) {
491
- const error = new alepha_server.UnauthorizedError(`Action ${name} not found.`);
492
- await this.alepha.events.emit("client:onError", {
493
- route: link,
494
- error
495
- });
496
- throw error;
497
- }
498
- if (options.hostname) return {
499
- ...link,
500
- host: options.hostname
501
- };
502
- return link;
503
- }
504
- };
505
-
506
- //#endregion
507
- //#region src/server-links/descriptors/$client.ts
508
- /**
509
- * Create a new client.
510
- */
511
- const $client = (scope) => {
512
- return (0, alepha.$inject)(LinkProvider).client(scope);
513
- };
514
- $client[alepha.KIND] = "$client";
515
-
516
- //#endregion
517
- //#region src/server-links/descriptors/$remote.ts
518
- /**
519
- * $remote is a descriptor that allows you to define remote service access.
520
- *
521
- * Use it only when you have 2 or more services that need to communicate with each other.
522
- *
523
- * All remote services can be exposed as actions, ... or not.
524
- *
525
- * You can add a service account if you want to use a security layer.
526
- */
527
- const $remote = (options) => {
528
- return (0, alepha.createDescriptor)(RemoteDescriptor, options);
529
- };
530
- var RemoteDescriptor = class extends alepha.Descriptor {
531
- get name() {
532
- return this.options.name ?? this.config.propertyKey;
533
- }
534
- };
535
- $remote[alepha.KIND] = RemoteDescriptor;
536
-
537
- //#endregion
538
- //#region src/server-proxy/descriptors/$proxy.ts
539
- /**
540
- * Creates a proxy descriptor to forward requests to another server.
541
- *
542
- * This descriptor enables you to create reverse proxy functionality, allowing your Alepha server
543
- * to forward requests to other services while maintaining a unified API surface. It's particularly
544
- * useful for microservice architectures, API gateways, or when you need to aggregate multiple
545
- * services behind a single endpoint.
546
- *
547
- * **Key Features**
548
- *
549
- * - **Path-based routing**: Match specific paths or patterns to proxy
550
- * - **Dynamic targets**: Support both static and dynamic target resolution
551
- * - **Request/Response hooks**: Modify requests before forwarding and responses after receiving
552
- * - **URL rewriting**: Transform URLs before forwarding to the target
553
- * - **Conditional proxying**: Enable/disable proxies based on environment or conditions
554
- *
555
- * @example
556
- * **Basic proxy setup:**
557
- * ```ts
558
- * import { $proxy } from "alepha/server/proxy";
559
- *
560
- * class ApiGateway {
561
- * // Forward all /api/* requests to external service
562
- * api = $proxy({
563
- * path: "/api/*",
564
- * target: "https://api.example.com"
565
- * });
566
- * }
567
- * ```
568
- *
569
- * @example
570
- * **Dynamic target with environment-based routing:**
571
- * ```ts
572
- * class ApiGateway {
573
- * // Route to different environments based on configuration
574
- * api = $proxy({
575
- * path: "/api/*",
576
- * target: () => process.env.NODE_ENV === "production"
577
- * ? "https://api.prod.example.com"
578
- * : "https://api.dev.example.com"
579
- * });
580
- * }
581
- * ```
582
- *
583
- * @example
584
- * **Advanced proxy with request/response modification:**
585
- * ```ts
586
- * class SecureProxy {
587
- * secure = $proxy({
588
- * path: "/secure/*",
589
- * target: "https://secure-api.example.com",
590
- * beforeRequest: async (request, proxyRequest) => {
591
- * // Add authentication headers
592
- * proxyRequest.headers = {
593
- * ...proxyRequest.headers,
594
- * 'Authorization': `Bearer ${await getServiceToken()}`,
595
- * 'X-Forwarded-For': request.headers['x-forwarded-for'] || request.ip
596
- * };
597
- * },
598
- * afterResponse: async (request, proxyResponse) => {
599
- * // Log response for monitoring
600
- * console.log(`Proxied ${request.url} -> ${proxyResponse.status}`);
601
- * },
602
- * rewrite: (url) => {
603
- * // Remove /secure prefix when forwarding
604
- * url.pathname = url.pathname.replace('/secure', '');
605
- * }
606
- * });
607
- * }
608
- * ```
609
- *
610
- * @example
611
- * **Conditional proxy based on feature flags:**
612
- * ```ts
613
- * class FeatureProxy {
614
- * newApi = $proxy({
615
- * path: "/v2/*",
616
- * target: "https://new-api.example.com",
617
- * disabled: !process.env.ENABLE_V2_API // Disable if feature flag is off
618
- * });
619
- * }
620
- * ```
621
- */
622
- const $proxy = (options) => {
623
- return (0, alepha.createDescriptor)(ProxyDescriptor, options);
624
- };
625
- var ProxyDescriptor = class extends alepha.Descriptor {};
626
- $proxy[alepha.KIND] = ProxyDescriptor;
627
-
628
- //#endregion
629
- //#region src/server-proxy/providers/ServerProxyProvider.ts
630
- var ServerProxyProvider = class {
631
- log = (0, alepha_logger.$logger)();
632
- routerProvider = (0, alepha.$inject)(alepha_server.ServerRouterProvider);
633
- alepha = (0, alepha.$inject)(alepha.Alepha);
634
- configure = (0, alepha.$hook)({
635
- on: "configure",
636
- handler: () => {
637
- for (const proxy of this.alepha.descriptors($proxy)) this.createProxy(proxy.options);
638
- }
639
- });
640
- createProxy(options) {
641
- if (options.disabled) return;
642
- const path = options.path;
643
- const target = typeof options.target === "function" ? options.target() : options.target;
644
- if (!path.endsWith("/*")) throw new alepha.AlephaError("Proxy path should end with '/*'");
645
- const handler = this.createProxyHandler(target, options);
646
- for (const method of alepha_server.routeMethods) this.routerProvider.createRoute({
647
- method,
648
- path,
649
- handler
650
- });
651
- this.log.info("Proxying", {
652
- path,
653
- target
654
- });
655
- }
656
- createProxyHandler(target, options) {
657
- return async (request) => {
658
- const url = new URL(target + request.url.pathname);
659
- if (request.url.search) url.search = request.url.search;
660
- options.rewrite?.(url);
661
- const requestInit = {
662
- url: url.toString(),
663
- method: request.method,
664
- headers: {
665
- ...request.headers,
666
- "accept-encoding": "identity"
667
- },
668
- body: this.getRawRequestBody(request)
669
- };
670
- if (requestInit.body) requestInit.duplex = "half";
671
- if (options.beforeRequest) await options.beforeRequest(request, requestInit);
672
- this.log.debug("Proxying request", {
673
- url: url.toString(),
674
- method: request.method,
675
- headers: request.headers
676
- });
677
- const response = await fetch(requestInit.url, requestInit);
678
- request.reply.status = response.status;
679
- request.reply.headers = Object.fromEntries(response.headers.entries());
680
- request.reply.body = response.body;
681
- this.log.debug("Received response", {
682
- status: request.reply.status,
683
- headers: request.reply.headers
684
- });
685
- if (options.afterResponse) await options.afterResponse(request, response);
686
- };
687
- }
688
- getRawRequestBody(req) {
689
- const { method } = req;
690
- if (method === "GET" || method === "HEAD" || method === "OPTIONS") return;
691
- if (req.raw?.web?.req) return req.raw.web.req.body;
692
- if (req.raw?.node?.req) {
693
- const nodeReq = req.raw.node.req;
694
- return node_stream_web.ReadableStream.from(nodeReq);
695
- }
696
- }
697
- };
698
-
699
- //#endregion
700
- //#region src/server-proxy/index.ts
701
- /**
702
- * Plugin for Alepha that provides a proxy server functionality.
703
- *
704
- * @see {@link $proxy}
705
- * @module alepha.server.proxy
706
- */
707
- const AlephaServerProxy = (0, alepha.$module)({
708
- name: "alepha.server.proxy",
709
- descriptors: [$proxy],
710
- services: [alepha_server.AlephaServer, ServerProxyProvider]
711
- });
712
-
713
- //#endregion
714
- //#region src/server-links/providers/RemoteDescriptorProvider.ts
715
- const envSchema$1 = alepha.t.object({ SERVER_API_PREFIX: alepha.t.text({
716
- description: "Prefix for all API routes (e.g. $action).",
717
- default: "/api"
718
- }) });
719
- var RemoteDescriptorProvider = class {
720
- env = (0, alepha.$env)(envSchema$1);
721
- alepha = (0, alepha.$inject)(alepha.Alepha);
722
- proxyProvider = (0, alepha.$inject)(ServerProxyProvider);
723
- linkProvider = (0, alepha.$inject)(LinkProvider);
724
- remotes = [];
725
- log = (0, alepha_logger.$logger)();
726
- getRemotes() {
727
- return this.remotes;
728
- }
729
- configure = (0, alepha.$hook)({
730
- on: "configure",
731
- handler: async () => {
732
- const remotes = this.alepha.descriptors($remote);
733
- for (const remote of remotes) await this.registerRemote(remote);
734
- }
735
- });
736
- start = (0, alepha.$hook)({
737
- on: "start",
738
- handler: async () => {
739
- for (const remote of this.remotes) {
740
- const token = typeof remote.serviceAccount?.token === "function" ? await remote.serviceAccount.token() : void 0;
741
- if (!remote.internal) continue;
742
- const { links } = await remote.links({ authorization: token });
743
- for (const link of links) {
744
- let path = link.path.replace(remote.prefix, "");
745
- if (link.service) path = `/${link.service}${path}`;
746
- this.linkProvider.registerLink({
747
- ...link,
748
- prefix: remote.prefix,
749
- path,
750
- method: link.method ?? "GET",
751
- host: remote.url,
752
- service: remote.name
753
- });
754
- }
755
- this.log.info(`Remote '${remote.name}' OK`, {
756
- links: remote.links.length,
757
- prefix: remote.prefix
758
- });
759
- }
760
- }
761
- });
762
- async registerRemote(value) {
763
- const options = value.options;
764
- const url = typeof options.url === "string" ? options.url : options.url();
765
- const linkPath = LinkProvider.path.apiLinks;
766
- const name = value.name;
767
- const proxy = typeof options.proxy === "object" ? options.proxy : {};
768
- const remote = {
769
- url,
770
- name,
771
- prefix: "/api",
772
- serviceAccount: options.serviceAccount,
773
- proxy: !!options.proxy,
774
- internal: !proxy.noInternal,
775
- schema: async (opts) => {
776
- const { authorization, name: name$1 } = opts;
777
- return await fetch(`${url}${linkPath}/${name$1}/schema`, { headers: new Headers(authorization ? { authorization } : {}) }).then((it) => it.json());
778
- },
779
- links: async (opts) => {
780
- const { authorization } = opts;
781
- const remoteApi = await this.fetchLinks.run({
782
- service: name,
783
- url: `${url}${linkPath}`,
784
- authorization
785
- });
786
- if (remoteApi.prefix != null) remote.prefix = remoteApi.prefix;
787
- return remoteApi;
788
- }
789
- };
790
- this.remotes.push(remote);
791
- if (options.proxy) this.proxyProvider.createProxy({
792
- path: `${this.env.SERVER_API_PREFIX}/${name}/*`,
793
- target: url,
794
- rewrite: (url$1) => {
795
- url$1.pathname = url$1.pathname.replace(`${this.env.SERVER_API_PREFIX}/${name}`, remote.prefix);
796
- },
797
- ...proxy
798
- });
799
- }
800
- fetchLinks = (0, alepha_retry.$retry)({
801
- max: 10,
802
- backoff: { initial: 1e3 },
803
- onError: (_, attempt, { service, url }) => {
804
- this.log.warn(`Failed to fetch links, retry (${attempt})...`, {
805
- service,
806
- url
807
- });
808
- },
809
- handler: async (opts) => {
810
- const { url, authorization } = opts;
811
- const response = await fetch(url, { headers: new Headers(authorization ? { authorization } : {}) });
812
- if (!response.ok) throw new Error(`Failed to fetch links from ${url}`);
813
- return this.alepha.codec.decode(apiLinksResponseSchema, await response.json());
814
- }
815
- });
816
- };
817
-
818
- //#endregion
819
- //#region src/server-links/providers/ServerLinksProvider.ts
820
- const envSchema = alepha.t.object({ SERVER_API_PREFIX: alepha.t.text({
821
- description: "Prefix for all API routes (e.g. $action).",
822
- default: "/api"
823
- }) });
824
- var ServerLinksProvider = class {
825
- env = (0, alepha.$env)(envSchema);
826
- alepha = (0, alepha.$inject)(alepha.Alepha);
827
- linkProvider = (0, alepha.$inject)(LinkProvider);
828
- remoteProvider = (0, alepha.$inject)(RemoteDescriptorProvider);
829
- serverTimingProvider = (0, alepha.$inject)(alepha_server.ServerTimingProvider);
830
- get prefix() {
831
- return this.env.SERVER_API_PREFIX;
832
- }
833
- onRoute = (0, alepha.$hook)({
834
- on: "configure",
835
- handler: () => {
836
- for (const action of this.alepha.descriptors(alepha_server.$action)) this.linkProvider.registerLink({
837
- name: action.name,
838
- group: action.group,
839
- schema: action.options.schema,
840
- requestBodyType: action.getBodyContentType(),
841
- secured: action.options.secure ?? true,
842
- method: action.method === "GET" ? void 0 : action.method,
843
- prefix: action.prefix,
844
- path: action.path,
845
- handler: (config, options = {}) => action.run(config, options)
846
- });
847
- }
848
- });
849
- /**
850
- * First API - Get all API links for the user.
851
- *
852
- * This is based on the user's permissions.
853
- */
854
- links = (0, alepha_server.$route)({
855
- path: LinkProvider.path.apiLinks,
856
- schema: { response: apiLinksResponseSchema },
857
- handler: ({ user, headers }) => {
858
- return this.getUserApiLinks({
859
- user,
860
- authorization: headers.authorization
861
- });
862
- }
863
- });
864
- /**
865
- * Second API - Get schema for a specific API link.
866
- *
867
- * Note: Body/Response schema are not included in `links` API because it's TOO BIG.
868
- * I mean for 150+ links, you got 50ms of serialization time.
869
- */
870
- schema = (0, alepha_server.$route)({
871
- path: LinkProvider.path.apiSchema,
872
- schema: {
873
- params: alepha.t.object({ name: alepha.t.text() }),
874
- response: alepha.t.json()
875
- },
876
- handler: ({ params, user, headers }) => {
877
- return this.getSchemaByName(params.name, {
878
- user,
879
- authorization: headers.authorization
880
- });
881
- }
882
- });
883
- async getSchemaByName(name, options = {}) {
884
- const authorization = options.authorization;
885
- const api = await this.getUserApiLinks({
886
- user: options.user,
887
- authorization
888
- });
889
- for (const link of api.links) if (link.name === name) {
890
- if (link.service) return this.remoteProvider.getRemotes().find((it) => it.name === link.service)?.schema({
891
- name,
892
- authorization
893
- });
894
- return this.linkProvider.getServerLinks().find((it) => it.name === name)?.schema ?? {};
895
- }
896
- return {};
897
- }
898
- /**
899
- * Retrieves API links for the user based on their permissions.
900
- * Will check on local links and remote links.
901
- */
902
- async getUserApiLinks(options) {
903
- const { user } = options;
904
- let permissions;
905
- let permissionMap;
906
- const hasSecurity = this.alepha.has(alepha_security.SecurityProvider);
907
- if (hasSecurity && user) {
908
- permissions = this.alepha.inject(alepha_security.SecurityProvider).getPermissions(user);
909
- permissionMap = new Map(permissions.map((it) => [`${it.group}:${it.name}`, it]));
910
- }
911
- const userLinks = [];
912
- for (const permission of permissions ?? []) if (!permission.path && !permission.method && permission.name && permission.group) userLinks.push({
913
- path: "",
914
- name: permission.name,
915
- group: permission.group
916
- });
917
- for (const link of this.linkProvider.getServerLinks()) {
918
- if (link.host) continue;
919
- if (hasSecurity && link.secured) {
920
- if (!user) continue;
921
- if (typeof link.secured === "object" && link.secured.realm) {
922
- if (user.realm !== link.secured.realm) continue;
923
- } else if (permissionMap) {
924
- if (!permissionMap.has(`${link.group}:${link.name}`)) continue;
925
- }
926
- }
927
- userLinks.push({
928
- name: link.name,
929
- group: link.group,
930
- requestBodyType: link.requestBodyType,
931
- method: link.method,
932
- path: link.path
933
- });
934
- }
935
- this.serverTimingProvider.beginTiming("fetchRemoteLinks");
936
- const promises = this.remoteProvider.getRemotes().filter((it) => it.proxy).map(async (remote) => {
937
- const { links, prefix } = await remote.links(options);
938
- return links.map((link) => {
939
- let path = link.path.replace(prefix ?? "/api", "");
940
- if (link.service) path = `/${link.service}${path}`;
941
- return {
942
- ...link,
943
- path,
944
- proxy: true,
945
- service: remote.name
946
- };
947
- });
948
- });
949
- userLinks.push(...(await Promise.all(promises)).flat());
950
- this.serverTimingProvider.endTiming("fetchRemoteLinks");
951
- return {
952
- prefix: this.env.SERVER_API_PREFIX,
953
- links: userLinks
954
- };
955
- }
956
- };
957
-
958
- //#endregion
959
- //#region src/server-links/index.ts
960
- /**
961
- * Provides server-side link management and remote capabilities for client-server interactions.
962
- *
963
- * The server-links module enables declarative link definitions using `$remote` and `$client` descriptors,
964
- * facilitating seamless API endpoint management and client-server communication. It integrates with server
965
- * security features to ensure safe and controlled access to resources.
966
- *
967
- * @see {@link $remote}
968
- * @see {@link $client}
969
- * @module alepha.server.links
970
- */
971
- const AlephaServerLinks = (0, alepha.$module)({
972
- name: "alepha.server.links",
973
- descriptors: [$remote, $client],
974
- services: [
975
- alepha_server.AlephaServer,
976
- ServerLinksProvider,
977
- RemoteDescriptorProvider,
978
- LinkProvider
979
- ]
980
- });
981
-
982
- //#endregion
983
- exports.$client = $client;
984
- exports.$remote = $remote;
985
- exports.AlephaServerLinks = AlephaServerLinks;
986
- exports.LinkProvider = LinkProvider;
987
- exports.RemoteDescriptor = RemoteDescriptor;
988
- exports.RemoteDescriptorProvider = RemoteDescriptorProvider;
989
- exports.ServerLinksProvider = ServerLinksProvider;
990
- exports.apiLinkSchema = apiLinkSchema;
991
- exports.apiLinksResponseSchema = apiLinksResponseSchema;
992
- //# sourceMappingURL=index.cjs.map