alepha 0.14.4 → 0.15.0

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