alepha 0.14.4 → 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 (322) hide show
  1. package/README.md +44 -102
  2. package/dist/api/audits/index.d.ts +331 -443
  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 +0 -113
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +2 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +151 -262
  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 +164 -276
  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 +265 -377
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/users/index.browser.js +1 -2
  21. package/dist/api/users/index.browser.js.map +1 -1
  22. package/dist/api/users/index.d.ts +195 -301
  23. package/dist/api/users/index.d.ts.map +1 -1
  24. package/dist/api/users/index.js +203 -184
  25. package/dist/api/users/index.js.map +1 -1
  26. package/dist/api/verifications/index.d.ts.map +1 -1
  27. package/dist/batch/index.d.ts.map +1 -1
  28. package/dist/batch/index.js +1 -2
  29. package/dist/batch/index.js.map +1 -1
  30. package/dist/bucket/index.d.ts.map +1 -1
  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 +5900 -165
  36. package/dist/cli/index.d.ts.map +1 -1
  37. package/dist/cli/index.js +1481 -639
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/command/index.d.ts +8 -4
  40. package/dist/command/index.d.ts.map +1 -1
  41. package/dist/command/index.js +29 -25
  42. package/dist/command/index.js.map +1 -1
  43. package/dist/core/index.browser.js +563 -54
  44. package/dist/core/index.browser.js.map +1 -1
  45. package/dist/core/index.d.ts +175 -8
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +564 -54
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/index.native.js +563 -54
  50. package/dist/core/index.native.js.map +1 -1
  51. package/dist/datetime/index.d.ts.map +1 -1
  52. package/dist/datetime/index.js +4 -4
  53. package/dist/datetime/index.js.map +1 -1
  54. package/dist/email/index.d.ts +89 -42
  55. package/dist/email/index.d.ts.map +1 -1
  56. package/dist/email/index.js +129 -33
  57. package/dist/email/index.js.map +1 -1
  58. package/dist/fake/index.d.ts +7969 -2
  59. package/dist/fake/index.d.ts.map +1 -1
  60. package/dist/fake/index.js +22 -22
  61. package/dist/fake/index.js.map +1 -1
  62. package/dist/file/index.d.ts +134 -1
  63. package/dist/file/index.d.ts.map +1 -1
  64. package/dist/file/index.js +253 -1
  65. package/dist/file/index.js.map +1 -1
  66. package/dist/lock/core/index.d.ts.map +1 -1
  67. package/dist/lock/redis/index.d.ts.map +1 -1
  68. package/dist/logger/index.d.ts +1 -2
  69. package/dist/logger/index.d.ts.map +1 -1
  70. package/dist/logger/index.js +1 -5
  71. package/dist/logger/index.js.map +1 -1
  72. package/dist/mcp/index.d.ts +19 -1
  73. package/dist/mcp/index.d.ts.map +1 -1
  74. package/dist/mcp/index.js +28 -4
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/orm/chunk-DH6iiROE.js +38 -0
  77. package/dist/orm/index.browser.js +9 -9
  78. package/dist/orm/index.browser.js.map +1 -1
  79. package/dist/orm/index.bun.js +2821 -0
  80. package/dist/orm/index.bun.js.map +1 -0
  81. package/dist/orm/index.d.ts +318 -169
  82. package/dist/orm/index.d.ts.map +1 -1
  83. package/dist/orm/index.js +2086 -1776
  84. package/dist/orm/index.js.map +1 -1
  85. package/dist/queue/core/index.d.ts +4 -4
  86. package/dist/queue/core/index.d.ts.map +1 -1
  87. package/dist/queue/redis/index.d.ts.map +1 -1
  88. package/dist/redis/index.bun.js +285 -0
  89. package/dist/redis/index.bun.js.map +1 -0
  90. package/dist/redis/index.d.ts +13 -31
  91. package/dist/redis/index.d.ts.map +1 -1
  92. package/dist/redis/index.js +18 -38
  93. package/dist/redis/index.js.map +1 -1
  94. package/dist/retry/index.d.ts.map +1 -1
  95. package/dist/router/index.d.ts.map +1 -1
  96. package/dist/scheduler/index.d.ts +83 -1
  97. package/dist/scheduler/index.d.ts.map +1 -1
  98. package/dist/scheduler/index.js +393 -1
  99. package/dist/scheduler/index.js.map +1 -1
  100. package/dist/security/index.browser.js +5 -1
  101. package/dist/security/index.browser.js.map +1 -1
  102. package/dist/security/index.d.ts +598 -112
  103. package/dist/security/index.d.ts.map +1 -1
  104. package/dist/security/index.js +1808 -97
  105. package/dist/security/index.js.map +1 -1
  106. package/dist/server/auth/index.d.ts +1200 -175
  107. package/dist/server/auth/index.d.ts.map +1 -1
  108. package/dist/server/auth/index.js +1268 -37
  109. package/dist/server/auth/index.js.map +1 -1
  110. package/dist/server/cache/index.d.ts +6 -3
  111. package/dist/server/cache/index.d.ts.map +1 -1
  112. package/dist/server/cache/index.js +1 -1
  113. package/dist/server/cache/index.js.map +1 -1
  114. package/dist/server/compress/index.d.ts.map +1 -1
  115. package/dist/server/cookies/index.d.ts.map +1 -1
  116. package/dist/server/cookies/index.js +3 -3
  117. package/dist/server/cookies/index.js.map +1 -1
  118. package/dist/server/core/index.d.ts +115 -13
  119. package/dist/server/core/index.d.ts.map +1 -1
  120. package/dist/server/core/index.js +321 -139
  121. package/dist/server/core/index.js.map +1 -1
  122. package/dist/server/cors/index.d.ts +0 -1
  123. package/dist/server/cors/index.d.ts.map +1 -1
  124. package/dist/server/health/index.d.ts +0 -1
  125. package/dist/server/health/index.d.ts.map +1 -1
  126. package/dist/server/helmet/index.d.ts.map +1 -1
  127. package/dist/server/links/index.browser.js +9 -1
  128. package/dist/server/links/index.browser.js.map +1 -1
  129. package/dist/server/links/index.d.ts +1 -2
  130. package/dist/server/links/index.d.ts.map +1 -1
  131. package/dist/server/links/index.js +14 -7
  132. package/dist/server/links/index.js.map +1 -1
  133. package/dist/server/metrics/index.d.ts +514 -1
  134. package/dist/server/metrics/index.d.ts.map +1 -1
  135. package/dist/server/metrics/index.js +4462 -4
  136. package/dist/server/metrics/index.js.map +1 -1
  137. package/dist/server/multipart/index.d.ts.map +1 -1
  138. package/dist/server/proxy/index.d.ts +0 -1
  139. package/dist/server/proxy/index.d.ts.map +1 -1
  140. package/dist/server/rate-limit/index.d.ts.map +1 -1
  141. package/dist/server/static/index.d.ts.map +1 -1
  142. package/dist/server/swagger/index.d.ts +1 -2
  143. package/dist/server/swagger/index.d.ts.map +1 -1
  144. package/dist/server/swagger/index.js +1 -2
  145. package/dist/server/swagger/index.js.map +1 -1
  146. package/dist/sms/index.d.ts +3 -1
  147. package/dist/sms/index.d.ts.map +1 -1
  148. package/dist/sms/index.js +10 -10
  149. package/dist/sms/index.js.map +1 -1
  150. package/dist/thread/index.d.ts +0 -1
  151. package/dist/thread/index.d.ts.map +1 -1
  152. package/dist/thread/index.js +2 -2
  153. package/dist/thread/index.js.map +1 -1
  154. package/dist/topic/core/index.d.ts.map +1 -1
  155. package/dist/topic/redis/index.d.ts.map +1 -1
  156. package/dist/vite/index.d.ts +6315 -149
  157. package/dist/vite/index.d.ts.map +1 -1
  158. package/dist/vite/index.js +140 -469
  159. package/dist/vite/index.js.map +1 -1
  160. package/dist/websocket/index.browser.js +9 -9
  161. package/dist/websocket/index.browser.js.map +1 -1
  162. package/dist/websocket/index.d.ts +28 -28
  163. package/dist/websocket/index.d.ts.map +1 -1
  164. package/dist/websocket/index.js +9 -9
  165. package/dist/websocket/index.js.map +1 -1
  166. package/package.json +13 -18
  167. package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
  168. package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
  169. package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
  170. package/src/api/users/entities/users.ts +1 -1
  171. package/src/api/users/index.ts +8 -8
  172. package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
  173. package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
  174. package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
  175. package/src/api/users/services/CredentialService.ts +7 -7
  176. package/src/api/users/services/IdentityService.ts +4 -4
  177. package/src/api/users/services/RegistrationService.spec.ts +25 -27
  178. package/src/api/users/services/RegistrationService.ts +38 -27
  179. package/src/api/users/services/SessionCrudService.ts +3 -3
  180. package/src/api/users/services/SessionService.spec.ts +3 -3
  181. package/src/api/users/services/SessionService.ts +27 -18
  182. package/src/api/users/services/UserService.ts +7 -7
  183. package/src/batch/providers/BatchProvider.ts +1 -2
  184. package/src/cli/apps/AlephaCli.ts +2 -2
  185. package/src/cli/apps/AlephaPackageBuilderCli.ts +47 -20
  186. package/src/cli/assets/apiHelloControllerTs.ts +19 -0
  187. package/src/cli/assets/apiIndexTs.ts +16 -0
  188. package/src/cli/assets/biomeJson.ts +2 -1
  189. package/src/cli/assets/claudeMd.ts +308 -0
  190. package/src/cli/assets/dummySpecTs.ts +2 -1
  191. package/src/cli/assets/editorconfig.ts +2 -1
  192. package/src/cli/assets/mainBrowserTs.ts +4 -3
  193. package/src/cli/assets/mainCss.ts +24 -0
  194. package/src/cli/assets/mainServerTs.ts +24 -0
  195. package/src/cli/assets/tsconfigJson.ts +2 -1
  196. package/src/cli/assets/webAppRouterTs.ts +16 -0
  197. package/src/cli/assets/webHelloComponentTsx.ts +20 -0
  198. package/src/cli/assets/webIndexTs.ts +16 -0
  199. package/src/cli/atoms/appEntryOptions.ts +13 -0
  200. package/src/cli/atoms/buildOptions.ts +1 -1
  201. package/src/cli/atoms/changelogOptions.ts +1 -1
  202. package/src/cli/commands/build.ts +97 -61
  203. package/src/cli/commands/db.ts +21 -18
  204. package/src/cli/commands/deploy.ts +17 -5
  205. package/src/cli/commands/dev.ts +26 -47
  206. package/src/cli/commands/gen/env.ts +1 -1
  207. package/src/cli/commands/init.ts +79 -25
  208. package/src/cli/commands/lint.ts +9 -3
  209. package/src/cli/commands/test.ts +8 -2
  210. package/src/cli/commands/typecheck.ts +5 -1
  211. package/src/cli/commands/verify.ts +4 -2
  212. package/src/cli/defineConfig.ts +9 -0
  213. package/src/cli/index.ts +2 -1
  214. package/src/cli/providers/AppEntryProvider.ts +131 -0
  215. package/src/cli/providers/ViteBuildProvider.ts +82 -0
  216. package/src/cli/providers/ViteDevServerProvider.ts +350 -0
  217. package/src/cli/providers/ViteTemplateProvider.ts +27 -0
  218. package/src/cli/services/AlephaCliUtils.ts +72 -602
  219. package/src/cli/services/PackageManagerUtils.ts +308 -0
  220. package/src/cli/services/ProjectScaffolder.ts +329 -0
  221. package/src/command/helpers/Runner.ts +15 -3
  222. package/src/core/Alepha.ts +2 -8
  223. package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
  224. package/src/core/index.shared.ts +1 -0
  225. package/src/core/index.ts +2 -0
  226. package/src/core/primitives/$hook.ts +6 -2
  227. package/src/core/primitives/$module.spec.ts +4 -0
  228. package/src/core/primitives/$module.ts +12 -0
  229. package/src/core/providers/AlsProvider.ts +1 -1
  230. package/src/core/providers/CodecManager.spec.ts +12 -6
  231. package/src/core/providers/CodecManager.ts +26 -6
  232. package/src/core/providers/EventManager.ts +169 -13
  233. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +878 -0
  234. package/src/core/providers/KeylessJsonSchemaCodec.ts +789 -0
  235. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  236. package/src/core/providers/StateManager.spec.ts +27 -16
  237. package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
  238. package/src/email/providers/LocalEmailProvider.ts +52 -15
  239. package/src/email/providers/NodemailerEmailProvider.ts +167 -56
  240. package/src/file/errors/FileError.ts +7 -0
  241. package/src/file/index.ts +9 -1
  242. package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
  243. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  244. package/src/mcp/errors/McpError.ts +30 -0
  245. package/src/mcp/index.ts +3 -0
  246. package/src/mcp/transports/SseMcpTransport.ts +16 -6
  247. package/src/orm/index.browser.ts +1 -19
  248. package/src/orm/index.bun.ts +77 -0
  249. package/src/orm/index.shared-server.ts +22 -0
  250. package/src/orm/index.shared.ts +15 -0
  251. package/src/orm/index.ts +19 -39
  252. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  253. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
  254. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  255. package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
  256. package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
  257. package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
  258. package/src/orm/services/Repository.ts +19 -0
  259. package/src/redis/index.bun.ts +35 -0
  260. package/src/redis/providers/BunRedisProvider.ts +12 -43
  261. package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
  262. package/src/redis/providers/NodeRedisProvider.ts +16 -34
  263. package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
  264. package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
  265. package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
  266. package/src/security/index.browser.ts +5 -0
  267. package/src/security/index.ts +90 -7
  268. package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
  269. package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
  270. package/src/security/primitives/$role.ts +5 -5
  271. package/src/security/primitives/$serviceAccount.spec.ts +5 -5
  272. package/src/security/primitives/$serviceAccount.ts +3 -3
  273. package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
  274. package/src/server/auth/primitives/$auth.ts +10 -10
  275. package/src/server/auth/primitives/$authCredentials.ts +3 -3
  276. package/src/server/auth/primitives/$authGithub.ts +3 -3
  277. package/src/server/auth/primitives/$authGoogle.ts +3 -3
  278. package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
  279. package/src/server/cache/providers/ServerCacheProvider.ts +1 -1
  280. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
  281. package/src/server/core/index.ts +1 -1
  282. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  283. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  284. package/src/server/core/providers/NodeHttpServerProvider.ts +92 -24
  285. package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
  286. package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
  287. package/src/server/core/providers/ServerProvider.ts +144 -24
  288. package/src/server/core/providers/ServerRouterProvider.ts +259 -115
  289. package/src/server/core/providers/ServerTimingProvider.ts +2 -2
  290. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  291. package/src/server/links/index.browser.ts +2 -0
  292. package/src/server/links/index.ts +3 -1
  293. package/src/server/links/providers/LinkProvider.ts +1 -1
  294. package/src/server/swagger/index.ts +1 -1
  295. package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
  296. package/src/sms/providers/LocalSmsProvider.ts +8 -7
  297. package/src/vite/index.ts +3 -2
  298. package/src/vite/tasks/buildClient.ts +0 -1
  299. package/src/vite/tasks/buildServer.ts +80 -22
  300. package/src/vite/tasks/copyAssets.ts +5 -4
  301. package/src/vite/tasks/generateCloudflare.ts +7 -0
  302. package/src/vite/tasks/generateSitemap.ts +64 -23
  303. package/src/vite/tasks/index.ts +0 -2
  304. package/src/vite/tasks/prerenderPages.ts +49 -24
  305. package/dist/server/security/index.browser.js +0 -13
  306. package/dist/server/security/index.browser.js.map +0 -1
  307. package/dist/server/security/index.d.ts +0 -173
  308. package/dist/server/security/index.d.ts.map +0 -1
  309. package/dist/server/security/index.js +0 -311
  310. package/dist/server/security/index.js.map +0 -1
  311. package/src/cli/assets/appRouterTs.ts +0 -9
  312. package/src/cli/assets/indexHtml.ts +0 -15
  313. package/src/cli/assets/mainTs.ts +0 -13
  314. package/src/cli/commands/format.ts +0 -17
  315. package/src/server/security/index.browser.ts +0 -10
  316. package/src/server/security/index.ts +0 -94
  317. package/src/vite/helpers/boot.ts +0 -106
  318. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  319. package/src/vite/tasks/devServer.ts +0 -69
  320. package/src/vite/tasks/runAlepha.ts +0 -270
  321. /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
  322. /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
@@ -0,0 +1,878 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { describe, test } from "vitest";
3
+ import { KeylessJsonSchemaCodec } from "./KeylessJsonSchemaCodec.ts";
4
+
5
+ describe("KeylessJsonSchemaCodec", () => {
6
+ describe("Basic types", () => {
7
+ test("should encode and decode primitive types", async ({ expect }) => {
8
+ const alepha = Alepha.create();
9
+
10
+ const userSchema = t.object({
11
+ name: t.text(),
12
+ age: t.integer(),
13
+ active: t.boolean(),
14
+ score: t.number(),
15
+ });
16
+
17
+ const data = {
18
+ name: "Alice",
19
+ age: 30,
20
+ active: true,
21
+ score: 98.5,
22
+ };
23
+
24
+ const encoded = alepha.codec.encode(userSchema, data, {
25
+ as: "string",
26
+ encoder: "keyless",
27
+ });
28
+
29
+ // Keyless format is an array
30
+ expect(encoded).toBe('["Alice",30,true,98.5]');
31
+
32
+ const decoded = alepha.codec.decode(userSchema, encoded, {
33
+ encoder: "keyless",
34
+ });
35
+
36
+ expect(decoded).toEqual(data);
37
+ });
38
+
39
+ test("should produce smaller output than JSON", async ({ expect }) => {
40
+ const alepha = Alepha.create();
41
+
42
+ const userSchema = t.object({
43
+ username: t.text(),
44
+ email: t.text(),
45
+ age: t.integer(),
46
+ isVerified: t.boolean(),
47
+ });
48
+
49
+ const data = {
50
+ username: "john_doe",
51
+ email: "john@example.com",
52
+ age: 25,
53
+ isVerified: true,
54
+ };
55
+
56
+ const jsonEncoded = alepha.codec.encode(userSchema, data, {
57
+ as: "string",
58
+ encoder: "json",
59
+ });
60
+
61
+ const keylessEncoded = alepha.codec.encode(userSchema, data, {
62
+ as: "string",
63
+ encoder: "keyless",
64
+ });
65
+
66
+ // Keyless should be smaller (no keys)
67
+ expect(keylessEncoded.length).toBeLessThan(jsonEncoded.length);
68
+
69
+ // Both should decode to same data
70
+ const jsonDecoded = alepha.codec.decode(userSchema, jsonEncoded, {
71
+ encoder: "json",
72
+ });
73
+ const keylessDecoded = alepha.codec.decode(userSchema, keylessEncoded, {
74
+ encoder: "keyless",
75
+ });
76
+
77
+ expect(jsonDecoded).toEqual(data);
78
+ expect(keylessDecoded).toEqual(data);
79
+ });
80
+
81
+ test("should handle bigint values as strings", async ({ expect }) => {
82
+ const alepha = Alepha.create();
83
+
84
+ // In Alepha, t.bigint() is a string type with format "bigint"
85
+ // It represents large integers as strings to avoid precision loss
86
+ const schema = t.object({
87
+ id: t.bigint(),
88
+ name: t.text(),
89
+ });
90
+
91
+ const data = {
92
+ id: "9007199254740993",
93
+ name: "Test",
94
+ };
95
+
96
+ const encoded = alepha.codec.encode(schema, data, {
97
+ as: "string",
98
+ encoder: "keyless",
99
+ });
100
+
101
+ // BigInt in Alepha is stored as a string
102
+ expect(encoded).toBe('["9007199254740993","Test"]');
103
+
104
+ const decoded = alepha.codec.decode(schema, encoded, {
105
+ encoder: "keyless",
106
+ });
107
+
108
+ expect(decoded.id).toBe("9007199254740993");
109
+ expect(decoded.name).toBe("Test");
110
+ });
111
+ });
112
+
113
+ describe("Nested objects", () => {
114
+ test("should handle nested objects", async ({ expect }) => {
115
+ const alepha = Alepha.create();
116
+
117
+ const schema = t.object({
118
+ user: t.object({
119
+ name: t.text(),
120
+ profile: t.object({
121
+ bio: t.text(),
122
+ age: t.integer(),
123
+ }),
124
+ }),
125
+ });
126
+
127
+ const data = {
128
+ user: {
129
+ name: "Alice",
130
+ profile: {
131
+ bio: "Developer",
132
+ age: 30,
133
+ },
134
+ },
135
+ };
136
+
137
+ const encoded = alepha.codec.encode(schema, data, {
138
+ as: "string",
139
+ encoder: "keyless",
140
+ });
141
+
142
+ // Nested objects become nested arrays
143
+ expect(encoded).toBe('[["Alice",["Developer",30]]]');
144
+
145
+ const decoded = alepha.codec.decode(schema, encoded, {
146
+ encoder: "keyless",
147
+ });
148
+
149
+ expect(decoded).toEqual(data);
150
+ });
151
+ });
152
+
153
+ describe("Arrays", () => {
154
+ test("should handle arrays of primitives", async ({ expect }) => {
155
+ const alepha = Alepha.create();
156
+
157
+ const schema = t.object({
158
+ tags: t.array(t.text()),
159
+ scores: t.array(t.number()),
160
+ });
161
+
162
+ const data = {
163
+ tags: ["typescript", "nodejs"],
164
+ scores: [95.5, 88.0, 92.3],
165
+ };
166
+
167
+ const encoded = alepha.codec.encode(schema, data, {
168
+ as: "string",
169
+ encoder: "keyless",
170
+ });
171
+
172
+ const decoded = alepha.codec.decode(schema, encoded, {
173
+ encoder: "keyless",
174
+ });
175
+
176
+ expect(decoded).toEqual(data);
177
+ });
178
+
179
+ test("should handle arrays of objects", async ({ expect }) => {
180
+ const alepha = Alepha.create();
181
+
182
+ const schema = t.object({
183
+ users: t.array(
184
+ t.object({
185
+ name: t.text(),
186
+ age: t.integer(),
187
+ }),
188
+ ),
189
+ socialProfiles: t.array(
190
+ t.object({
191
+ platform: t.text(),
192
+ username: t.text(),
193
+ }),
194
+ ),
195
+ });
196
+
197
+ const data = {
198
+ users: [
199
+ { name: "Alice", age: 30 },
200
+ { name: "Bob", age: 25 },
201
+ ],
202
+ socialProfiles: [
203
+ { platform: "twitter", username: "alice" },
204
+ { platform: "github", username: "alice123" },
205
+ ],
206
+ };
207
+
208
+ const encoded = alepha.codec.encode(schema, data, {
209
+ as: "string",
210
+ encoder: "keyless",
211
+ });
212
+
213
+ // Arrays of objects should be encoded as nested arrays
214
+ expect(encoded).toBe(
215
+ '[[["Alice",30],["Bob",25]],[["twitter","alice"],["github","alice123"]]]',
216
+ );
217
+
218
+ const decoded = alepha.codec.decode(schema, encoded, {
219
+ encoder: "keyless",
220
+ });
221
+
222
+ expect(decoded).toEqual(data);
223
+ });
224
+ });
225
+
226
+ describe("Optional and nullable types", () => {
227
+ test("should handle optional fields", async ({ expect }) => {
228
+ const alepha = Alepha.create();
229
+
230
+ const schema = t.object({
231
+ name: t.text(),
232
+ bio: t.optional(t.text()),
233
+ });
234
+
235
+ const dataWithBio = {
236
+ name: "Alice",
237
+ bio: "Developer",
238
+ };
239
+
240
+ const dataWithoutBio = {
241
+ name: "Bob",
242
+ };
243
+
244
+ // With optional field present
245
+ const encodedWith = alepha.codec.encode(schema, dataWithBio, {
246
+ as: "string",
247
+ encoder: "keyless",
248
+ });
249
+ const decodedWith = alepha.codec.decode(schema, encodedWith, {
250
+ encoder: "keyless",
251
+ });
252
+ expect(decodedWith).toEqual(dataWithBio);
253
+
254
+ // With optional field missing
255
+ const encodedWithout = alepha.codec.encode(schema, dataWithoutBio, {
256
+ as: "string",
257
+ encoder: "keyless",
258
+ });
259
+ const decodedWithout = alepha.codec.decode(schema, encodedWithout, {
260
+ encoder: "keyless",
261
+ });
262
+ expect(decodedWithout.name).toBe("Bob");
263
+ expect(decodedWithout.bio).toBeUndefined();
264
+ });
265
+
266
+ test("should handle nullable fields", async ({ expect }) => {
267
+ const alepha = Alepha.create();
268
+
269
+ const schema = t.object({
270
+ name: t.text(),
271
+ deletedAt: t.nullable(t.datetime()),
272
+ });
273
+
274
+ const activeUser = {
275
+ name: "Alice",
276
+ deletedAt: null,
277
+ };
278
+
279
+ const deletedUser = {
280
+ name: "Bob",
281
+ deletedAt: "2024-01-15T10:00:00Z",
282
+ };
283
+
284
+ // Active user (null deletedAt)
285
+ const encodedActive = alepha.codec.encode(schema, activeUser, {
286
+ as: "string",
287
+ encoder: "keyless",
288
+ });
289
+ const decodedActive = alepha.codec.decode(schema, encodedActive, {
290
+ encoder: "keyless",
291
+ });
292
+ expect(decodedActive.name).toBe("Alice");
293
+ expect(decodedActive.deletedAt).toBeNull();
294
+
295
+ // Deleted user (non-null deletedAt)
296
+ const encodedDeleted = alepha.codec.encode(schema, deletedUser, {
297
+ as: "string",
298
+ encoder: "keyless",
299
+ });
300
+ const decodedDeleted = alepha.codec.decode(schema, encodedDeleted, {
301
+ encoder: "keyless",
302
+ });
303
+ expect(decodedDeleted).toEqual(deletedUser);
304
+ });
305
+ });
306
+
307
+ describe("Enums", () => {
308
+ test("should handle enum values", async ({ expect }) => {
309
+ const alepha = Alepha.create();
310
+
311
+ const schema = t.object({
312
+ status: t.enum(["ACTIVE", "INACTIVE", "PENDING"]),
313
+ name: t.text(),
314
+ });
315
+
316
+ const data = {
317
+ status: "ACTIVE",
318
+ name: "Test",
319
+ };
320
+
321
+ const encoded = alepha.codec.encode(schema, data, {
322
+ as: "string",
323
+ encoder: "keyless",
324
+ });
325
+
326
+ const decoded = alepha.codec.decode(schema, encoded, {
327
+ encoder: "keyless",
328
+ });
329
+
330
+ expect(decoded).toEqual(data);
331
+ });
332
+ });
333
+
334
+ describe("Binary encoding", () => {
335
+ test("should encode and decode binary format", async ({ expect }) => {
336
+ const alepha = Alepha.create();
337
+
338
+ const schema = t.object({
339
+ name: t.text(),
340
+ age: t.integer(),
341
+ });
342
+
343
+ const data = {
344
+ name: "Alice",
345
+ age: 30,
346
+ };
347
+
348
+ const binary = alepha.codec.encode(schema, data, {
349
+ as: "binary",
350
+ encoder: "keyless",
351
+ });
352
+
353
+ expect(binary).toBeInstanceOf(Uint8Array);
354
+
355
+ const decoded = alepha.codec.decode(schema, binary, {
356
+ encoder: "keyless",
357
+ });
358
+
359
+ expect(decoded).toEqual(data);
360
+ });
361
+ });
362
+
363
+ describe("Complex schemas", () => {
364
+ test("should handle complex nested structures", async ({ expect }) => {
365
+ const alepha = Alepha.create();
366
+
367
+ const schema = t.object({
368
+ user: t.object({
369
+ id: t.text(),
370
+ profile: t.object({
371
+ name: t.text(),
372
+ age: t.nullable(t.integer()),
373
+ tags: t.array(t.text()),
374
+ }),
375
+ }),
376
+ status: t.enum(["ACTIVE", "INACTIVE"]),
377
+ });
378
+
379
+ const data = {
380
+ user: {
381
+ id: "123",
382
+ profile: {
383
+ name: "Alice",
384
+ age: 30,
385
+ tags: ["developer", "typescript"],
386
+ },
387
+ },
388
+ status: "ACTIVE",
389
+ };
390
+
391
+ const encoded = alepha.codec.encode(schema, data, {
392
+ as: "string",
393
+ encoder: "keyless",
394
+ });
395
+
396
+ const decoded = alepha.codec.decode(schema, encoded, {
397
+ encoder: "keyless",
398
+ });
399
+
400
+ expect(decoded).toEqual(data);
401
+ });
402
+
403
+ test("should handle comprehensive schema with all types", async ({
404
+ expect,
405
+ }) => {
406
+ const alepha = Alepha.create();
407
+
408
+ // Comprehensive schema with all supported types
409
+ const comprehensiveSchema = t.object({
410
+ // Primitive types
411
+ id: t.integer(),
412
+ uuid: t.uuid(),
413
+ name: t.text(),
414
+ email: t.text(),
415
+ score: t.number(),
416
+ isActive: t.boolean(),
417
+ bigNumber: t.bigint(),
418
+
419
+ // Date/time types (stored as strings)
420
+ createdAt: t.datetime(),
421
+ updatedAt: t.datetime(),
422
+
423
+ // Enum type
424
+ status: t.enum(["ACTIVE", "INACTIVE", "PENDING"]),
425
+ role: t.enum(["USER", "ADMIN", "MODERATOR"]),
426
+
427
+ // Optional fields
428
+ nickname: t.optional(t.text()),
429
+ bio: t.optional(t.text()),
430
+
431
+ // Nullable fields
432
+ deletedAt: t.nullable(t.datetime()),
433
+ lastLoginAt: t.nullable(t.datetime()),
434
+
435
+ // Arrays of primitives
436
+ tags: t.array(t.text()),
437
+ scores: t.array(t.number()),
438
+ flags: t.array(t.boolean()),
439
+
440
+ // Nested object
441
+ profile: t.object({
442
+ firstName: t.text(),
443
+ lastName: t.text(),
444
+ age: t.integer(),
445
+ }),
446
+
447
+ // Deeply nested objects
448
+ settings: t.object({
449
+ theme: t.text(),
450
+ preferences: t.object({
451
+ notifications: t.boolean(),
452
+ emailAlerts: t.boolean(),
453
+ language: t.text(),
454
+ }),
455
+ }),
456
+
457
+ // Arrays of objects
458
+ contacts: t.array(
459
+ t.object({
460
+ type: t.text(),
461
+ value: t.text(),
462
+ }),
463
+ ),
464
+
465
+ // Nested arrays of objects
466
+ socialProfiles: t.array(
467
+ t.object({
468
+ platform: t.text(),
469
+ username: t.text(),
470
+ verified: t.boolean(),
471
+ }),
472
+ ),
473
+
474
+ // Optional nested object
475
+ address: t.optional(
476
+ t.object({
477
+ street: t.text(),
478
+ city: t.text(),
479
+ country: t.text(),
480
+ postalCode: t.text(),
481
+ }),
482
+ ),
483
+
484
+ // Nullable nested object
485
+ company: t.nullable(
486
+ t.object({
487
+ name: t.text(),
488
+ position: t.text(),
489
+ }),
490
+ ),
491
+ });
492
+
493
+ const fullData = {
494
+ id: 12345,
495
+ uuid: "550e8400-e29b-41d4-a716-446655440000",
496
+ name: "John Doe",
497
+ email: "john.doe@example.com",
498
+ score: 98.75,
499
+ isActive: true,
500
+ bigNumber: "9007199254740993",
501
+ createdAt: "2023-01-15T10:30:00Z",
502
+ updatedAt: "2024-06-20T14:45:30Z",
503
+ status: "ACTIVE",
504
+ role: "ADMIN",
505
+ nickname: "johnny",
506
+ bio: "Software developer and tech enthusiast",
507
+ deletedAt: null,
508
+ lastLoginAt: "2024-06-20T14:00:00Z",
509
+ tags: ["developer", "typescript", "nodejs", "premium"],
510
+ scores: [95.5, 88.0, 92.3, 100.0],
511
+ flags: [true, false, true, true],
512
+ profile: {
513
+ firstName: "John",
514
+ lastName: "Doe",
515
+ age: 35,
516
+ },
517
+ settings: {
518
+ theme: "dark",
519
+ preferences: {
520
+ notifications: true,
521
+ emailAlerts: false,
522
+ language: "en-US",
523
+ },
524
+ },
525
+ contacts: [
526
+ { type: "phone", value: "+1234567890" },
527
+ { type: "fax", value: "+0987654321" },
528
+ ],
529
+ socialProfiles: [
530
+ { platform: "twitter", username: "johndoe", verified: true },
531
+ { platform: "github", username: "johndoe123", verified: false },
532
+ { platform: "linkedin", username: "john-doe", verified: true },
533
+ ],
534
+ address: {
535
+ street: "123 Main Street",
536
+ city: "San Francisco",
537
+ country: "USA",
538
+ postalCode: "94102",
539
+ },
540
+ company: {
541
+ name: "Tech Corp",
542
+ position: "Senior Developer",
543
+ },
544
+ };
545
+
546
+ const encoded = alepha.codec.encode(comprehensiveSchema, fullData, {
547
+ as: "string",
548
+ encoder: "keyless",
549
+ });
550
+
551
+ const decoded = alepha.codec.decode(comprehensiveSchema, encoded, {
552
+ encoder: "keyless",
553
+ });
554
+
555
+ expect(decoded).toEqual(fullData);
556
+
557
+ // Test with missing optional fields
558
+ const partialData = {
559
+ id: 12345,
560
+ uuid: "550e8400-e29b-41d4-a716-446655440000",
561
+ name: "Jane Doe",
562
+ email: "jane.doe@example.com",
563
+ score: 85.5,
564
+ isActive: false,
565
+ bigNumber: "123456789",
566
+ createdAt: "2023-02-20T08:00:00Z",
567
+ updatedAt: "2024-05-15T12:30:00Z",
568
+ status: "PENDING",
569
+ role: "USER",
570
+ // nickname and bio are omitted (optional)
571
+ deletedAt: "2024-01-01T00:00:00Z", // not null this time
572
+ lastLoginAt: null,
573
+ tags: [],
574
+ scores: [75.0],
575
+ flags: [false],
576
+ profile: {
577
+ firstName: "Jane",
578
+ lastName: "Doe",
579
+ age: 28,
580
+ },
581
+ settings: {
582
+ theme: "light",
583
+ preferences: {
584
+ notifications: false,
585
+ emailAlerts: true,
586
+ language: "fr-FR",
587
+ },
588
+ },
589
+ contacts: [],
590
+ socialProfiles: [
591
+ { platform: "instagram", username: "janedoe", verified: false },
592
+ ],
593
+ // address is omitted (optional)
594
+ company: null, // nullable field set to null
595
+ };
596
+
597
+ const encodedPartial = alepha.codec.encode(
598
+ comprehensiveSchema,
599
+ partialData,
600
+ {
601
+ as: "string",
602
+ encoder: "keyless",
603
+ },
604
+ );
605
+
606
+ const decodedPartial = alepha.codec.decode(
607
+ comprehensiveSchema,
608
+ encodedPartial,
609
+ {
610
+ encoder: "keyless",
611
+ },
612
+ );
613
+
614
+ expect(decodedPartial).toEqual(partialData);
615
+
616
+ // Verify size reduction
617
+ const jsonSize = JSON.stringify(fullData).length;
618
+ const keylessSize = encoded.length;
619
+ expect(keylessSize).toBeLessThan(jsonSize);
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
+ });
878
+ });