alepha 0.15.0 → 0.15.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 (222) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +240 -240
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +2 -2
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +185 -185
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +2 -2
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +245 -245
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/notifications/index.browser.js +4 -4
  13. package/dist/api/notifications/index.browser.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +74 -74
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/notifications/index.js +4 -4
  17. package/dist/api/notifications/index.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +221 -221
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/users/index.d.ts +1632 -1631
  21. package/dist/api/users/index.d.ts.map +1 -1
  22. package/dist/api/users/index.js +26 -34
  23. package/dist/api/users/index.js.map +1 -1
  24. package/dist/api/verifications/index.d.ts +132 -132
  25. package/dist/api/verifications/index.d.ts.map +1 -1
  26. package/dist/batch/index.d.ts +122 -122
  27. package/dist/batch/index.d.ts.map +1 -1
  28. package/dist/bucket/index.d.ts +163 -163
  29. package/dist/bucket/index.d.ts.map +1 -1
  30. package/dist/cache/core/index.d.ts +46 -46
  31. package/dist/cache/core/index.d.ts.map +1 -1
  32. package/dist/cache/redis/index.d.ts.map +1 -1
  33. package/dist/cache/redis/index.js +2 -2
  34. package/dist/cache/redis/index.js.map +1 -1
  35. package/dist/cli/index.d.ts +5933 -201
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +609 -169
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/command/index.d.ts +296 -296
  40. package/dist/command/index.d.ts.map +1 -1
  41. package/dist/command/index.js +19 -19
  42. package/dist/command/index.js.map +1 -1
  43. package/dist/core/index.browser.js +268 -79
  44. package/dist/core/index.browser.js.map +1 -1
  45. package/dist/core/index.d.ts +768 -694
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +268 -79
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/index.native.js +268 -79
  50. package/dist/core/index.native.js.map +1 -1
  51. package/dist/datetime/index.d.ts +44 -44
  52. package/dist/datetime/index.d.ts.map +1 -1
  53. package/dist/email/index.d.ts +25 -25
  54. package/dist/email/index.d.ts.map +1 -1
  55. package/dist/fake/index.d.ts +5409 -5409
  56. package/dist/fake/index.d.ts.map +1 -1
  57. package/dist/fake/index.js +22 -22
  58. package/dist/fake/index.js.map +1 -1
  59. package/dist/file/index.d.ts +435 -435
  60. package/dist/file/index.d.ts.map +1 -1
  61. package/dist/lock/core/index.d.ts +208 -208
  62. package/dist/lock/core/index.d.ts.map +1 -1
  63. package/dist/lock/redis/index.d.ts.map +1 -1
  64. package/dist/logger/index.d.ts +24 -24
  65. package/dist/logger/index.d.ts.map +1 -1
  66. package/dist/logger/index.js +1 -5
  67. package/dist/logger/index.js.map +1 -1
  68. package/dist/mcp/index.d.ts +216 -198
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +28 -4
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/orm/index.browser.js +9 -9
  73. package/dist/orm/index.browser.js.map +1 -1
  74. package/dist/orm/index.bun.js +83 -76
  75. package/dist/orm/index.bun.js.map +1 -1
  76. package/dist/orm/index.d.ts +961 -960
  77. package/dist/orm/index.d.ts.map +1 -1
  78. package/dist/orm/index.js +88 -81
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +244 -244
  81. package/dist/queue/core/index.d.ts.map +1 -1
  82. package/dist/queue/redis/index.d.ts.map +1 -1
  83. package/dist/redis/index.d.ts +105 -105
  84. package/dist/redis/index.d.ts.map +1 -1
  85. package/dist/retry/index.d.ts +69 -69
  86. package/dist/retry/index.d.ts.map +1 -1
  87. package/dist/router/index.d.ts +6 -6
  88. package/dist/router/index.d.ts.map +1 -1
  89. package/dist/scheduler/index.d.ts +108 -26
  90. package/dist/scheduler/index.d.ts.map +1 -1
  91. package/dist/scheduler/index.js +393 -1
  92. package/dist/scheduler/index.js.map +1 -1
  93. package/dist/security/index.d.ts +532 -209
  94. package/dist/security/index.d.ts.map +1 -1
  95. package/dist/security/index.js +1422 -11
  96. package/dist/security/index.js.map +1 -1
  97. package/dist/server/auth/index.d.ts +1296 -271
  98. package/dist/server/auth/index.d.ts.map +1 -1
  99. package/dist/server/auth/index.js +1249 -18
  100. package/dist/server/auth/index.js.map +1 -1
  101. package/dist/server/cache/index.d.ts +56 -56
  102. package/dist/server/cache/index.d.ts.map +1 -1
  103. package/dist/server/compress/index.d.ts +3 -3
  104. package/dist/server/compress/index.d.ts.map +1 -1
  105. package/dist/server/cookies/index.d.ts +6 -6
  106. package/dist/server/cookies/index.d.ts.map +1 -1
  107. package/dist/server/core/index.d.ts +196 -186
  108. package/dist/server/core/index.d.ts.map +1 -1
  109. package/dist/server/core/index.js +43 -27
  110. package/dist/server/core/index.js.map +1 -1
  111. package/dist/server/cors/index.d.ts +11 -11
  112. package/dist/server/cors/index.d.ts.map +1 -1
  113. package/dist/server/health/index.d.ts.map +1 -1
  114. package/dist/server/helmet/index.d.ts +2 -2
  115. package/dist/server/helmet/index.d.ts.map +1 -1
  116. package/dist/server/links/index.browser.js +9 -1
  117. package/dist/server/links/index.browser.js.map +1 -1
  118. package/dist/server/links/index.d.ts +83 -83
  119. package/dist/server/links/index.d.ts.map +1 -1
  120. package/dist/server/links/index.js +13 -5
  121. package/dist/server/links/index.js.map +1 -1
  122. package/dist/server/metrics/index.d.ts +514 -1
  123. package/dist/server/metrics/index.d.ts.map +1 -1
  124. package/dist/server/metrics/index.js +4462 -4
  125. package/dist/server/metrics/index.js.map +1 -1
  126. package/dist/server/multipart/index.d.ts +6 -6
  127. package/dist/server/multipart/index.d.ts.map +1 -1
  128. package/dist/server/proxy/index.d.ts +102 -102
  129. package/dist/server/proxy/index.d.ts.map +1 -1
  130. package/dist/server/rate-limit/index.d.ts +16 -16
  131. package/dist/server/rate-limit/index.d.ts.map +1 -1
  132. package/dist/server/static/index.d.ts +44 -44
  133. package/dist/server/static/index.d.ts.map +1 -1
  134. package/dist/server/swagger/index.d.ts +47 -47
  135. package/dist/server/swagger/index.d.ts.map +1 -1
  136. package/dist/sms/index.d.ts +11 -11
  137. package/dist/sms/index.d.ts.map +1 -1
  138. package/dist/sms/index.js +3 -3
  139. package/dist/sms/index.js.map +1 -1
  140. package/dist/thread/index.d.ts +71 -71
  141. package/dist/thread/index.d.ts.map +1 -1
  142. package/dist/thread/index.js +2 -2
  143. package/dist/thread/index.js.map +1 -1
  144. package/dist/topic/core/index.d.ts +318 -318
  145. package/dist/topic/core/index.d.ts.map +1 -1
  146. package/dist/topic/redis/index.d.ts +6 -6
  147. package/dist/topic/redis/index.d.ts.map +1 -1
  148. package/dist/vite/index.d.ts +2324 -1719
  149. package/dist/vite/index.d.ts.map +1 -1
  150. package/dist/vite/index.js +123 -475
  151. package/dist/vite/index.js.map +1 -1
  152. package/dist/websocket/index.browser.js +3 -3
  153. package/dist/websocket/index.browser.js.map +1 -1
  154. package/dist/websocket/index.d.ts +275 -275
  155. package/dist/websocket/index.d.ts.map +1 -1
  156. package/dist/websocket/index.js +3 -3
  157. package/dist/websocket/index.js.map +1 -1
  158. package/package.json +9 -9
  159. package/src/api/users/services/SessionService.ts +0 -10
  160. package/src/cli/apps/AlephaCli.ts +2 -2
  161. package/src/cli/apps/AlephaPackageBuilderCli.ts +9 -1
  162. package/src/cli/assets/apiHelloControllerTs.ts +2 -1
  163. package/src/cli/assets/biomeJson.ts +2 -1
  164. package/src/cli/assets/claudeMd.ts +9 -4
  165. package/src/cli/assets/dummySpecTs.ts +2 -1
  166. package/src/cli/assets/editorconfig.ts +2 -1
  167. package/src/cli/assets/mainBrowserTs.ts +2 -1
  168. package/src/cli/assets/mainCss.ts +24 -0
  169. package/src/cli/assets/tsconfigJson.ts +2 -1
  170. package/src/cli/assets/webAppRouterTs.ts +2 -1
  171. package/src/cli/assets/webHelloComponentTsx.ts +6 -2
  172. package/src/cli/atoms/appEntryOptions.ts +13 -0
  173. package/src/cli/atoms/buildOptions.ts +1 -1
  174. package/src/cli/atoms/changelogOptions.ts +1 -1
  175. package/src/cli/commands/build.ts +63 -47
  176. package/src/cli/commands/dev.ts +16 -33
  177. package/src/cli/commands/gen/env.ts +1 -1
  178. package/src/cli/commands/init.ts +17 -8
  179. package/src/cli/commands/lint.ts +1 -1
  180. package/src/cli/defineConfig.ts +9 -0
  181. package/src/cli/index.ts +2 -1
  182. package/src/cli/providers/AppEntryProvider.ts +131 -0
  183. package/src/cli/providers/ViteBuildProvider.ts +82 -0
  184. package/src/cli/providers/ViteDevServerProvider.ts +350 -0
  185. package/src/cli/providers/ViteTemplateProvider.ts +27 -0
  186. package/src/cli/services/AlephaCliUtils.ts +33 -2
  187. package/src/cli/services/PackageManagerUtils.ts +13 -6
  188. package/src/cli/services/ProjectScaffolder.ts +72 -49
  189. package/src/core/Alepha.ts +2 -8
  190. package/src/core/primitives/$module.ts +12 -0
  191. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  192. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  193. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  194. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  195. package/src/mcp/errors/McpError.ts +30 -0
  196. package/src/mcp/index.ts +3 -0
  197. package/src/mcp/transports/SseMcpTransport.ts +16 -6
  198. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  199. package/src/orm/services/Repository.ts +11 -0
  200. package/src/server/core/index.ts +1 -1
  201. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  202. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  203. package/src/server/core/providers/NodeHttpServerProvider.ts +71 -22
  204. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  205. package/src/server/core/providers/ServerProvider.ts +9 -12
  206. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  207. package/src/server/links/index.browser.ts +2 -0
  208. package/src/server/links/index.ts +2 -0
  209. package/src/vite/index.ts +3 -2
  210. package/src/vite/tasks/buildClient.ts +0 -1
  211. package/src/vite/tasks/buildServer.ts +68 -21
  212. package/src/vite/tasks/copyAssets.ts +5 -4
  213. package/src/vite/tasks/generateSitemap.ts +64 -23
  214. package/src/vite/tasks/index.ts +0 -2
  215. package/src/vite/tasks/prerenderPages.ts +49 -24
  216. package/src/cli/assets/indexHtml.ts +0 -15
  217. package/src/cli/commands/format.ts +0 -23
  218. package/src/vite/helpers/boot.ts +0 -117
  219. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  220. package/src/vite/tasks/devServer.ts +0 -71
  221. package/src/vite/tasks/runAlepha.ts +0 -270
  222. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
@@ -8,8 +8,8 @@ import { biomeJson } from "../assets/biomeJson.ts";
8
8
  import { type ClaudeMdOptions, claudeMd } from "../assets/claudeMd.ts";
9
9
  import { dummySpecTs } from "../assets/dummySpecTs.ts";
10
10
  import { editorconfig } from "../assets/editorconfig.ts";
11
- import { indexHtml } from "../assets/indexHtml.ts";
12
11
  import { mainBrowserTs } from "../assets/mainBrowserTs.ts";
12
+ import { mainCss } from "../assets/mainCss.ts";
13
13
  import { mainServerTs } from "../assets/mainServerTs.ts";
14
14
  import { tsconfigJson } from "../assets/tsconfigJson.ts";
15
15
  import { webAppRouterTs } from "../assets/webAppRouterTs.ts";
@@ -54,6 +54,7 @@ export class ProjectScaffolder {
54
54
  public async ensureConfig(
55
55
  root: string,
56
56
  opts: {
57
+ force?: boolean;
57
58
  packageJson?: boolean | DependencyModes;
58
59
  tsconfigJson?: boolean;
59
60
  indexHtml?: boolean;
@@ -63,6 +64,7 @@ export class ProjectScaffolder {
63
64
  },
64
65
  ): Promise<void> {
65
66
  const tasks: Promise<void>[] = [];
67
+ const force = opts.force ?? false;
66
68
 
67
69
  if (opts.packageJson) {
68
70
  tasks.push(
@@ -75,22 +77,24 @@ export class ProjectScaffolder {
75
77
  );
76
78
  }
77
79
  if (opts.tsconfigJson) {
78
- tasks.push(this.ensureTsConfig(root));
80
+ tasks.push(this.ensureTsConfig(root, { force }));
79
81
  }
80
82
  if (opts.indexHtml) {
81
- tasks.push(this.ensureReactProject(root));
83
+ tasks.push(this.ensureReactProject(root, { force }));
82
84
  }
83
85
  if (opts.biomeJson) {
84
- tasks.push(this.ensureBiomeConfig(root));
86
+ tasks.push(this.ensureBiomeConfig(root, { force }));
85
87
  }
86
88
  if (opts.editorconfig) {
87
- tasks.push(this.ensureEditorConfig(root));
89
+ tasks.push(this.ensureEditorConfig(root, { force }));
88
90
  }
89
91
  if (opts.claudeMd) {
90
92
  tasks.push(
91
93
  this.ensureClaudeMd(
92
94
  root,
93
- typeof opts.claudeMd === "boolean" ? {} : opts.claudeMd,
95
+ typeof opts.claudeMd === "boolean"
96
+ ? { force }
97
+ : { ...opts.claudeMd, force },
94
98
  ),
95
99
  );
96
100
  }
@@ -102,30 +106,39 @@ export class ProjectScaffolder {
102
106
  // Config Files
103
107
  // ===========================================
104
108
 
105
- public async ensureTsConfig(root: string): Promise<void> {
109
+ public async ensureTsConfig(
110
+ root: string,
111
+ opts: { force?: boolean } = {},
112
+ ): Promise<void> {
106
113
  // Check if tsconfig.json exists in current or parent directories
107
- if (await this.existsInParents(root, "tsconfig.json")) {
114
+ if (!opts.force && (await this.existsInParents(root, "tsconfig.json"))) {
108
115
  return;
109
116
  }
110
- await this.fs.writeFile(this.fs.join(root, "tsconfig.json"), tsconfigJson);
117
+ await this.fs.writeFile(
118
+ this.fs.join(root, "tsconfig.json"),
119
+ tsconfigJson(),
120
+ );
111
121
  }
112
122
 
113
- public async ensureBiomeConfig(root: string): Promise<void> {
114
- await this.ensureFileIfNotExists(root, "biome.json", biomeJson);
123
+ public async ensureBiomeConfig(
124
+ root: string,
125
+ opts: { force?: boolean } = {},
126
+ ): Promise<void> {
127
+ await this.ensureFile(root, "biome.json", biomeJson(), opts.force);
115
128
  }
116
129
 
117
- public async ensureEditorConfig(root: string): Promise<void> {
118
- await this.ensureFileIfNotExists(root, ".editorconfig", editorconfig);
130
+ public async ensureEditorConfig(
131
+ root: string,
132
+ opts: { force?: boolean } = {},
133
+ ): Promise<void> {
134
+ await this.ensureFile(root, ".editorconfig", editorconfig(), opts.force);
119
135
  }
120
136
 
121
137
  public async ensureClaudeMd(
122
138
  root: string,
123
- options: ClaudeMdOptions = {},
139
+ options: ClaudeMdOptions & { force?: boolean } = {},
124
140
  ): Promise<void> {
125
- const path = this.fs.join(root, "CLAUDE.md");
126
- if (!(await this.fs.exists(path))) {
127
- await this.fs.writeFile(path, claudeMd(options));
128
- }
141
+ await this.ensureFile(root, "CLAUDE.md", claudeMd(options), options.force);
129
142
  }
130
143
 
131
144
  // ===========================================
@@ -140,11 +153,14 @@ export class ProjectScaffolder {
140
153
  * - src/api/index.ts (API module)
141
154
  * - src/api/controllers/HelloController.ts (example controller)
142
155
  */
143
- public async ensureApiProject(root: string): Promise<void> {
156
+ public async ensureApiProject(
157
+ root: string,
158
+ opts: { force?: boolean } = {},
159
+ ): Promise<void> {
144
160
  const srcDir = this.fs.join(root, "src");
145
161
 
146
- // Don't overwrite existing content
147
- if (await this.fs.exists(srcDir)) {
162
+ // Don't overwrite existing content unless force is set
163
+ if (!opts.force && (await this.fs.exists(srcDir))) {
148
164
  const files = await this.fs.ls(srcDir);
149
165
  if (files.length > 0) return;
150
166
  }
@@ -157,17 +173,18 @@ export class ProjectScaffolder {
157
173
  });
158
174
 
159
175
  // Create files
160
- await this.fs.writeFile(
161
- this.fs.join(srcDir, "main.server.ts"),
162
- mainServerTs(),
163
- );
164
- await this.fs.writeFile(
165
- this.fs.join(srcDir, "api/index.ts"),
176
+ await this.ensureFile(srcDir, "main.server.ts", mainServerTs(), opts.force);
177
+ await this.ensureFile(
178
+ srcDir,
179
+ "api/index.ts",
166
180
  apiIndexTs({ appName }),
181
+ opts.force,
167
182
  );
168
- await this.fs.writeFile(
169
- this.fs.join(srcDir, "api/controllers/HelloController.ts"),
183
+ await this.ensureFile(
184
+ srcDir,
185
+ "api/controllers/HelloController.ts",
170
186
  apiHelloControllerTs(),
187
+ opts.force,
171
188
  );
172
189
  }
173
190
 
@@ -179,16 +196,14 @@ export class ProjectScaffolder {
179
196
  * Ensure full React project structure exists.
180
197
  *
181
198
  * Creates:
182
- * - index.html
183
199
  * - src/main.server.ts, src/main.browser.ts
184
200
  * - src/api/index.ts, src/api/controllers/HelloController.ts
185
201
  * - src/web/index.ts, src/web/AppRouter.ts, src/web/components/Hello.tsx
186
202
  */
187
- public async ensureReactProject(root: string): Promise<void> {
188
- if (await this.fs.exists(this.fs.join(root, "index.html"))) {
189
- return;
190
- }
191
-
203
+ public async ensureReactProject(
204
+ root: string,
205
+ opts: { force?: boolean } = {},
206
+ ): Promise<void> {
192
207
  const appName = this.getAppName(root);
193
208
 
194
209
  // Create directories
@@ -199,49 +214,53 @@ export class ProjectScaffolder {
199
214
  recursive: true,
200
215
  });
201
216
 
202
- // index.html
203
- await this.fs.writeFile(
204
- this.fs.join(root, "index.html"),
205
- indexHtml("src/main.browser.ts"),
206
- );
217
+ // src/main.css
218
+ await this.ensureFile(root, "src/main.css", mainCss(), opts.force);
207
219
 
208
220
  // API structure
209
- await this.ensureFileIfNotExists(
221
+ await this.ensureFile(
210
222
  root,
211
223
  "src/api/index.ts",
212
224
  apiIndexTs({ appName }),
225
+ opts.force,
213
226
  );
214
- await this.ensureFileIfNotExists(
227
+ await this.ensureFile(
215
228
  root,
216
229
  "src/api/controllers/HelloController.ts",
217
230
  apiHelloControllerTs(),
231
+ opts.force,
218
232
  );
219
- await this.ensureFileIfNotExists(
233
+ await this.ensureFile(
220
234
  root,
221
235
  "src/main.server.ts",
222
236
  mainServerTs({ react: true }),
237
+ opts.force,
223
238
  );
224
239
 
225
240
  // Web structure
226
- await this.ensureFileIfNotExists(
241
+ await this.ensureFile(
227
242
  root,
228
243
  "src/web/index.ts",
229
244
  webIndexTs({ appName }),
245
+ opts.force,
230
246
  );
231
- await this.ensureFileIfNotExists(
247
+ await this.ensureFile(
232
248
  root,
233
249
  "src/web/AppRouter.ts",
234
250
  webAppRouterTs(),
251
+ opts.force,
235
252
  );
236
- await this.ensureFileIfNotExists(
253
+ await this.ensureFile(
237
254
  root,
238
255
  "src/web/components/Hello.tsx",
239
256
  webHelloComponentTsx(),
257
+ opts.force,
240
258
  );
241
- await this.ensureFileIfNotExists(
259
+ await this.ensureFile(
242
260
  root,
243
261
  "src/main.browser.ts",
244
262
  mainBrowserTs(),
263
+ opts.force,
245
264
  );
246
265
  }
247
266
 
@@ -272,13 +291,17 @@ export class ProjectScaffolder {
272
291
  // Helpers
273
292
  // ===========================================
274
293
 
275
- protected async ensureFileIfNotExists(
294
+ /**
295
+ * Write a file, optionally overriding if it exists.
296
+ */
297
+ protected async ensureFile(
276
298
  root: string,
277
299
  relativePath: string,
278
300
  content: string,
301
+ force?: boolean,
279
302
  ): Promise<void> {
280
303
  const fullPath = this.fs.join(root, relativePath);
281
- if (!(await this.fs.exists(fullPath))) {
304
+ if (force || !(await this.fs.exists(fullPath))) {
282
305
  await this.fs.writeFile(fullPath, content);
283
306
  }
284
307
  }
@@ -165,13 +165,6 @@ export class Alepha {
165
165
  ...state.env,
166
166
  ...process.env,
167
167
  };
168
-
169
- // remove empty env variables
170
- for (const key in state.env) {
171
- if (state.env[key] === "") {
172
- delete (state.env as any)[key];
173
- }
174
- }
175
168
  }
176
169
 
177
170
  // force production mode when building with vite
@@ -765,8 +758,9 @@ export class Alepha {
765
758
  }
766
759
 
767
760
  if (this.started) {
761
+ const mod = (service as WithModule)[MODULE]?.name;
768
762
  throw new ContainerLockedError(
769
- `Container is locked. No more services can be added. ${parent?.name} -> ${service.name}`,
763
+ `Container is locked. No more services can be added. Attempted to inject '${service.name}' from '${parent?.name}'. ${mod ? `Maybe register module '${mod}' in Alepha.` : ""}`,
770
764
  );
771
765
  }
772
766
  }
@@ -4,6 +4,7 @@ import { MODULE } from "../constants/MODULE.ts";
4
4
  import { AlephaError } from "../errors/AlephaError.ts";
5
5
  import type { PrimitiveFactoryLike } from "../helpers/primitive.ts";
6
6
  import type { Service } from "../interfaces/Service.ts";
7
+ import type { Atom } from "./$atom.ts";
7
8
 
8
9
  /**
9
10
  * Wrap Services and Primitives into a Module.
@@ -69,6 +70,12 @@ export const $module = <T extends object = {}>(
69
70
  options = options;
70
71
 
71
72
  register(alepha: Alepha): void {
73
+ if (options.atoms) {
74
+ for (const atom of options.atoms) {
75
+ alepha.store.register(atom);
76
+ }
77
+ }
78
+
72
79
  if (typeof options.register === "function") {
73
80
  options.register(alepha);
74
81
  return;
@@ -134,6 +141,11 @@ export interface ModulePrimitiveOptions {
134
141
  * Again, if you declare 'register', you must handle the registration of ALL services manually.
135
142
  */
136
143
  register?: (alepha: Alepha) => void;
144
+
145
+ /**
146
+ * List of atoms to register in the module.
147
+ */
148
+ atoms?: Array<Atom<any>>;
137
149
  }
138
150
 
139
151
  /**
@@ -1,5 +1,6 @@
1
1
  import { Alepha, t } from "alepha";
2
2
  import { describe, test } from "vitest";
3
+ import { KeylessJsonSchemaCodec } from "./KeylessJsonSchemaCodec.ts";
3
4
 
4
5
  describe("KeylessJsonSchemaCodec", () => {
5
6
  describe("Basic types", () => {
@@ -618,4 +619,260 @@ describe("KeylessJsonSchemaCodec", () => {
618
619
  expect(keylessSize).toBeLessThan(jsonSize);
619
620
  });
620
621
  });
622
+
623
+ describe("Safe Mode (Interpreted)", () => {
624
+ test("should work correctly in safe mode", async ({ expect }) => {
625
+ const alepha = Alepha.create();
626
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
627
+
628
+ // Force safe mode (no Function compilation)
629
+ codec.configure({ useFunctionCompilation: false });
630
+
631
+ const schema = t.object({
632
+ name: t.text(),
633
+ age: t.integer(),
634
+ active: t.boolean(),
635
+ });
636
+
637
+ const data = {
638
+ name: "Alice",
639
+ age: 30,
640
+ active: true,
641
+ };
642
+
643
+ const encoded = codec.encodeToString(schema, data);
644
+ expect(encoded).toBe('["Alice",30,true]');
645
+
646
+ const decoded = codec.decode(schema, encoded);
647
+ expect(decoded).toEqual(data);
648
+ });
649
+
650
+ test("should handle nested objects in safe mode", async ({ expect }) => {
651
+ const alepha = Alepha.create();
652
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
653
+
654
+ codec.configure({ useFunctionCompilation: false });
655
+
656
+ const schema = t.object({
657
+ user: t.object({
658
+ name: t.text(),
659
+ profile: t.object({
660
+ bio: t.text(),
661
+ age: t.integer(),
662
+ }),
663
+ }),
664
+ });
665
+
666
+ const data = {
667
+ user: {
668
+ name: "Alice",
669
+ profile: {
670
+ bio: "Developer",
671
+ age: 30,
672
+ },
673
+ },
674
+ };
675
+
676
+ const encoded = codec.encodeToString(schema, data);
677
+ expect(encoded).toBe('[["Alice",["Developer",30]]]');
678
+
679
+ const decoded = codec.decode(schema, encoded);
680
+ expect(decoded).toEqual(data);
681
+ });
682
+
683
+ test("should handle arrays of objects in safe mode", async ({ expect }) => {
684
+ const alepha = Alepha.create();
685
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
686
+
687
+ codec.configure({ useFunctionCompilation: false });
688
+
689
+ const schema = t.object({
690
+ users: t.array(
691
+ t.object({
692
+ name: t.text(),
693
+ age: t.integer(),
694
+ }),
695
+ ),
696
+ });
697
+
698
+ const data = {
699
+ users: [
700
+ { name: "Alice", age: 30 },
701
+ { name: "Bob", age: 25 },
702
+ ],
703
+ };
704
+
705
+ const encoded = codec.encodeToString(schema, data);
706
+ const decoded = codec.decode(schema, encoded);
707
+ expect(decoded).toEqual(data);
708
+ });
709
+
710
+ test("should handle optional fields in safe mode", async ({ expect }) => {
711
+ const alepha = Alepha.create();
712
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
713
+
714
+ codec.configure({ useFunctionCompilation: false });
715
+
716
+ const schema = t.object({
717
+ name: t.text(),
718
+ bio: t.optional(t.text()),
719
+ });
720
+
721
+ // With optional field
722
+ const dataWithBio = { name: "Alice", bio: "Developer" };
723
+ const encodedWith = codec.encodeToString(schema, dataWithBio);
724
+ const decodedWith = codec.decode(schema, encodedWith);
725
+ expect(decodedWith).toEqual(dataWithBio);
726
+
727
+ // Without optional field
728
+ const dataWithoutBio = { name: "Bob" };
729
+ const encodedWithout = codec.encodeToString(schema, dataWithoutBio);
730
+ const decodedWithout = codec.decode<{ name: string; bio?: string }>(
731
+ schema,
732
+ encodedWithout,
733
+ );
734
+ expect(decodedWithout.name).toBe("Bob");
735
+ expect(decodedWithout.bio).toBeUndefined();
736
+ });
737
+
738
+ test("should handle nullable fields in safe mode", async ({ expect }) => {
739
+ const alepha = Alepha.create();
740
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
741
+
742
+ codec.configure({ useFunctionCompilation: false });
743
+
744
+ const schema = t.object({
745
+ name: t.text(),
746
+ deletedAt: t.nullable(t.datetime()),
747
+ });
748
+
749
+ // With null value
750
+ const dataNull = { name: "Alice", deletedAt: null };
751
+ const encodedNull = codec.encodeToString(schema, dataNull);
752
+ const decodedNull = codec.decode<{
753
+ name: string;
754
+ deletedAt: string | null;
755
+ }>(schema, encodedNull);
756
+ expect(decodedNull.deletedAt).toBeNull();
757
+
758
+ // With non-null value
759
+ const dataValue = { name: "Bob", deletedAt: "2024-01-15T10:00:00Z" };
760
+ const encodedValue = codec.encodeToString(schema, dataValue);
761
+ const decodedValue = codec.decode(schema, encodedValue);
762
+ expect(decodedValue).toEqual(dataValue);
763
+ });
764
+ });
765
+
766
+ describe("Compiled Mode (Function)", () => {
767
+ test("should work correctly in compiled mode", async ({ expect }) => {
768
+ const alepha = Alepha.create();
769
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
770
+
771
+ // Force compiled mode
772
+ codec.configure({ useFunctionCompilation: true });
773
+
774
+ const schema = t.object({
775
+ name: t.text(),
776
+ age: t.integer(),
777
+ active: t.boolean(),
778
+ });
779
+
780
+ const data = {
781
+ name: "Alice",
782
+ age: 30,
783
+ active: true,
784
+ };
785
+
786
+ const encoded = codec.encodeToString(schema, data);
787
+ expect(encoded).toBe('["Alice",30,true]');
788
+
789
+ const decoded = codec.decode(schema, encoded);
790
+ expect(decoded).toEqual(data);
791
+ });
792
+
793
+ test("should produce same results as safe mode", async ({ expect }) => {
794
+ const alepha = Alepha.create();
795
+
796
+ const codecSafe = alepha.inject(KeylessJsonSchemaCodec);
797
+ codecSafe.configure({ useFunctionCompilation: false });
798
+
799
+ // Create a second Alepha instance for the compiled codec
800
+ const alepha2 = Alepha.create();
801
+ const codecCompiled = alepha2.inject(KeylessJsonSchemaCodec);
802
+ codecCompiled.configure({ useFunctionCompilation: true });
803
+
804
+ const schema = t.object({
805
+ user: t.object({
806
+ name: t.text(),
807
+ age: t.integer(),
808
+ }),
809
+ tags: t.array(t.text()),
810
+ active: t.boolean(),
811
+ });
812
+
813
+ const data = {
814
+ user: { name: "Alice", age: 30 },
815
+ tags: ["dev", "typescript"],
816
+ active: true,
817
+ };
818
+
819
+ const encodedSafe = codecSafe.encodeToString(schema, data);
820
+ const encodedCompiled = codecCompiled.encodeToString(schema, data);
821
+
822
+ // Both modes should produce the same output
823
+ expect(encodedSafe).toBe(encodedCompiled);
824
+
825
+ const decodedSafe = codecSafe.decode(schema, encodedSafe);
826
+ const decodedCompiled = codecCompiled.decode(schema, encodedCompiled);
827
+
828
+ // Both modes should decode to the same result
829
+ expect(decodedSafe).toEqual(data);
830
+ expect(decodedCompiled).toEqual(data);
831
+ });
832
+ });
833
+
834
+ describe("Configuration", () => {
835
+ test("should allow configuring options", async ({ expect }) => {
836
+ const alepha = Alepha.create();
837
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
838
+
839
+ // Configure all options
840
+ codec.configure({
841
+ useFunctionCompilation: false,
842
+ maxArrayLength: 100,
843
+ maxStringLength: 1000,
844
+ maxDepth: 10,
845
+ });
846
+
847
+ // Test that configuration works by encoding/decoding
848
+ const schema = t.object({ name: t.text() });
849
+ const data = { name: "test" };
850
+
851
+ const encoded = codec.encodeToString(schema, data);
852
+ const decoded = codec.decode(schema, encoded);
853
+
854
+ expect(decoded).toEqual(data);
855
+ });
856
+
857
+ test("should clear cache when compilation mode changes", async ({
858
+ expect,
859
+ }) => {
860
+ const alepha = Alepha.create();
861
+ const codec = alepha.inject(KeylessJsonSchemaCodec);
862
+
863
+ const schema = t.object({ name: t.text() });
864
+ const data = { name: "test" };
865
+
866
+ // Use compiled mode first
867
+ codec.configure({ useFunctionCompilation: true });
868
+ const encoded1 = codec.encodeToString(schema, data);
869
+
870
+ // Switch to safe mode (cache should be cleared)
871
+ codec.configure({ useFunctionCompilation: false });
872
+ const encoded2 = codec.encodeToString(schema, data);
873
+
874
+ // Both should produce the same result
875
+ expect(encoded1).toBe(encoded2);
876
+ });
877
+ });
621
878
  });