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,407 @@
1
+ import type { TArray, TObject, TSchema, TUnion } from "typebox";
2
+ import { SchemaCodec } from "./SchemaCodec.ts";
3
+ import { type Static, t } from "./TypeProvider.ts";
4
+
5
+ // =============================================================================
6
+ // Keyless JSON Codec
7
+ // =============================================================================
8
+ // Schema-driven JSON encoding without keys:
9
+ // - Schema defines field order → no keys needed in output
10
+ // - Uses native JSON.stringify on arrays (fast!)
11
+ // - Uses native JSON.parse for decoding (blazing fast)
12
+ // - 50-56% smaller than JSON, decode 1.7-2x faster
13
+ //
14
+ // Example:
15
+ // JSON: {"name":"Alice","age":30,"active":true} (39 bytes)
16
+ // Keyless: ["Alice",30,true] (17 bytes)
17
+ // =============================================================================
18
+
19
+ export interface KeylessCodec<T = any> {
20
+ encode: (value: T) => string;
21
+ decode: (str: string) => T;
22
+ }
23
+
24
+ /**
25
+ * KeylessJsonSchemaCodec provides schema-driven JSON encoding without keys.
26
+ *
27
+ * It uses the schema to determine field order, allowing the encoded output
28
+ * to be a simple JSON array instead of an object with keys.
29
+ *
30
+ * Performance characteristics:
31
+ * - Encode: 0.94-1.53x vs JSON.stringify (faster for complex objects)
32
+ * - Decode: 1.76-2.00x vs JSON.parse
33
+ * - Size: 50-56% smaller than JSON
34
+ */
35
+ export class KeylessJsonSchemaCodec extends SchemaCodec {
36
+ protected readonly cache = new Map<TSchema, KeylessCodec>();
37
+ protected readonly encoder = new TextEncoder();
38
+ protected readonly decoder = new TextDecoder();
39
+ protected varCounter = 0;
40
+
41
+ /**
42
+ * Encode value to a keyless JSON string.
43
+ */
44
+ public encodeToString<T extends TSchema>(
45
+ schema: T,
46
+ value: Static<T>,
47
+ ): string {
48
+ return this.getCodec(schema).encode(value);
49
+ }
50
+
51
+ /**
52
+ * Encode value to binary (UTF-8 encoded keyless JSON).
53
+ */
54
+ public encodeToBinary<T extends TSchema>(
55
+ schema: T,
56
+ value: Static<T>,
57
+ ): Uint8Array {
58
+ return this.encoder.encode(this.encodeToString(schema, value));
59
+ }
60
+
61
+ /**
62
+ * Decode keyless JSON string or binary to value.
63
+ */
64
+ public decode<T>(schema: TSchema, value: unknown): T {
65
+ if (value instanceof Uint8Array) {
66
+ const text = this.decoder.decode(value);
67
+ return this.getCodec(schema).decode(text) as T;
68
+ }
69
+
70
+ if (typeof value === "string") {
71
+ return this.getCodec(schema).decode(value) as T;
72
+ }
73
+
74
+ // If already an array (parsed JSON), reconstruct object
75
+ if (Array.isArray(value)) {
76
+ return this.reconstructObject(schema, value) as T;
77
+ }
78
+
79
+ return value as T;
80
+ }
81
+
82
+ // ===========================================================================
83
+ // Codec Compilation
84
+ // ===========================================================================
85
+
86
+ /**
87
+ * Get a compiled codec for the given schema.
88
+ * Codecs are cached for reuse.
89
+ */
90
+ protected getCodec<T>(schema: TSchema): KeylessCodec<T> {
91
+ let c = this.cache.get(schema);
92
+ if (!c) {
93
+ c = this.compile(schema);
94
+ this.cache.set(schema, c);
95
+ }
96
+ return c as KeylessCodec<T>;
97
+ }
98
+
99
+ protected nextVar(): string {
100
+ return `_${this.varCounter++}`;
101
+ }
102
+
103
+ protected compile(schema: TSchema): KeylessCodec {
104
+ this.varCounter = 0;
105
+ const encBody = this.genEnc(schema, "v");
106
+
107
+ this.varCounter = 0;
108
+ const decBody = this.genDec(schema);
109
+
110
+ const encoder = new Function("v", `return JSON.stringify(${encBody});`) as (
111
+ value: any,
112
+ ) => string;
113
+
114
+ const decoder = new Function(
115
+ "s",
116
+ `const a=JSON.parse(s);let i=0;${decBody.code}return ${decBody.result};`,
117
+ ) as (str: string) => any;
118
+
119
+ return { encode: encoder, decode: decoder };
120
+ }
121
+
122
+ // ===========================================================================
123
+ // Encoder - generates code that returns an array representation
124
+ // ===========================================================================
125
+
126
+ protected genEnc(schema: TSchema, ve: string): string {
127
+ if (
128
+ t.schema.isString(schema) ||
129
+ t.schema.isNumber(schema) ||
130
+ t.schema.isInteger(schema) ||
131
+ t.schema.isBoolean(schema) ||
132
+ this.isEnum(schema)
133
+ ) {
134
+ return ve;
135
+ }
136
+
137
+ if (t.schema.isBigInt(schema)) {
138
+ return `${ve}+'n'`;
139
+ }
140
+
141
+ if (t.schema.isArray(schema)) {
142
+ const arrSchema = schema as TArray;
143
+ const itemEnc = this.genEnc(arrSchema.items, "e");
144
+ if (
145
+ t.schema.isString(arrSchema.items) ||
146
+ t.schema.isNumber(arrSchema.items) ||
147
+ t.schema.isInteger(arrSchema.items) ||
148
+ t.schema.isBoolean(arrSchema.items)
149
+ ) {
150
+ return ve;
151
+ }
152
+ return `${ve}.map(e=>${itemEnc})`;
153
+ }
154
+
155
+ if (t.schema.isObject(schema)) {
156
+ const objSchema = schema as TObject;
157
+ const props = objSchema.properties as Record<string, TSchema>;
158
+ const keys = Object.keys(props);
159
+ const req = new Set((objSchema.required as string[]) || []);
160
+
161
+ const parts: string[] = [];
162
+ for (const k of keys) {
163
+ const ps = props[k];
164
+ const isOpt = !req.has(k) || t.schema.isOptional(ps);
165
+ const isNullable = this.isNullable(ps);
166
+ const inner = this.unwrap(ps);
167
+ const innerEnc = this.genEnc(inner, `${ve}.${k}`);
168
+
169
+ if (isOpt) {
170
+ parts.push(`${ve}.${k}!==undefined?${innerEnc}:null`);
171
+ } else if (isNullable) {
172
+ parts.push(`${ve}.${k}!==null?${innerEnc}:null`);
173
+ } else {
174
+ parts.push(innerEnc);
175
+ }
176
+ }
177
+
178
+ return `[${parts.join(",")}]`;
179
+ }
180
+
181
+ if (t.schema.isOptional(schema) || t.schema.isUnion(schema)) {
182
+ const inner = this.unwrap(schema);
183
+ const innerEnc = this.genEnc(inner, ve);
184
+ if (this.isNullable(schema)) {
185
+ return `${ve}!==null?${innerEnc}:null`;
186
+ }
187
+ return `${ve}!==undefined?${innerEnc}:null`;
188
+ }
189
+
190
+ return ve;
191
+ }
192
+
193
+ // ===========================================================================
194
+ // Decoder - generates code to reconstruct object from parsed array
195
+ // ===========================================================================
196
+
197
+ protected genDec(schema: TSchema): { code: string; result: string } {
198
+ const v = this.nextVar();
199
+
200
+ if (
201
+ t.schema.isString(schema) ||
202
+ t.schema.isNumber(schema) ||
203
+ t.schema.isInteger(schema) ||
204
+ t.schema.isBoolean(schema) ||
205
+ this.isEnum(schema)
206
+ ) {
207
+ return { code: "", result: "a[i++]" };
208
+ }
209
+
210
+ if (t.schema.isBigInt(schema)) {
211
+ return { code: "", result: "BigInt(a[i++].slice(0,-1))" };
212
+ }
213
+
214
+ if (t.schema.isArray(schema)) {
215
+ const arrSchema = schema as TArray;
216
+ // Check if array items need transformation (objects)
217
+ if (t.schema.isObject(arrSchema.items)) {
218
+ const itemTransform = this.genDecFromValue(arrSchema.items, "e");
219
+ return { code: "", result: `a[i++].map(e=>${itemTransform})` };
220
+ }
221
+ return { code: "", result: "a[i++]" };
222
+ }
223
+
224
+ if (t.schema.isObject(schema)) {
225
+ const objSchema = schema as TObject;
226
+ const props = objSchema.properties as Record<string, TSchema>;
227
+ const keys = Object.keys(props);
228
+ const req = new Set((objSchema.required as string[]) || []);
229
+
230
+ // Check if simple (all required primitives)
231
+ let simple = true;
232
+ for (const k of keys) {
233
+ const ps = props[k];
234
+ const isOpt = !req.has(k) || t.schema.isOptional(ps);
235
+ const isNullable = this.isNullable(ps);
236
+ const inner = this.unwrap(ps);
237
+ if (
238
+ isOpt ||
239
+ isNullable ||
240
+ t.schema.isObject(inner) ||
241
+ t.schema.isArray(inner)
242
+ ) {
243
+ simple = false;
244
+ break;
245
+ }
246
+ }
247
+
248
+ if (simple) {
249
+ const fields = keys.map((k) => `${k}:a[i++]`);
250
+ return { code: "", result: `{${fields.join(",")}}` };
251
+ }
252
+
253
+ let code = `const ${v}={};`;
254
+ for (const k of keys) {
255
+ const ps = props[k];
256
+ const isOpt = !req.has(k) || t.schema.isOptional(ps);
257
+ const isNullable = this.isNullable(ps);
258
+ const inner = this.unwrap(ps);
259
+
260
+ if (isOpt) {
261
+ const nested = this.genDecFromValue(inner, "t");
262
+ code += `{const t=a[i++];if(t!==null){${v}.${k}=${nested};}}`;
263
+ } else if (isNullable) {
264
+ const nested = this.genDecFromValue(inner, "t");
265
+ code += `{const t=a[i++];if(t===null){${v}.${k}=null;}else{${v}.${k}=${nested};}}`;
266
+ } else if (t.schema.isObject(inner)) {
267
+ const nested = this.genDecFromValue(inner, "a[i++]");
268
+ code += `${v}.${k}=${nested};`;
269
+ } else if (t.schema.isArray(inner)) {
270
+ // Handle arrays - check if items need transformation
271
+ const arrSchema = inner as TArray;
272
+ if (t.schema.isObject(arrSchema.items)) {
273
+ const itemTransform = this.genDecFromValue(arrSchema.items, "e");
274
+ code += `${v}.${k}=a[i++].map(e=>${itemTransform});`;
275
+ } else {
276
+ code += `${v}.${k}=a[i++];`;
277
+ }
278
+ } else {
279
+ code += `${v}.${k}=a[i++];`;
280
+ }
281
+ }
282
+
283
+ return { code, result: v };
284
+ }
285
+
286
+ if (t.schema.isOptional(schema) || t.schema.isUnion(schema)) {
287
+ const inner = this.unwrap(schema);
288
+ const innerDec = this.genDec(inner);
289
+ const nullVal = this.isNullable(schema) ? "null" : "undefined";
290
+ return {
291
+ code: `const ${v}t=a[i++];let ${v};if(${v}t===null){${v}=${nullVal};}else{${innerDec.code.replace(/a\[i\+\+\]/g, `${v}t`)}${v}=${innerDec.result.replace(/a\[i\+\+\]/g, `${v}t`)};}`,
292
+ result: v,
293
+ };
294
+ }
295
+
296
+ return { code: "", result: "a[i++]" };
297
+ }
298
+
299
+ protected genDecFromValue(schema: TSchema, expr: string): string {
300
+ if (
301
+ t.schema.isString(schema) ||
302
+ t.schema.isNumber(schema) ||
303
+ t.schema.isInteger(schema) ||
304
+ t.schema.isBoolean(schema) ||
305
+ this.isEnum(schema)
306
+ ) {
307
+ return expr;
308
+ }
309
+ if (t.schema.isBigInt(schema)) {
310
+ return `BigInt(${expr}.slice(0,-1))`;
311
+ }
312
+ if (t.schema.isArray(schema)) {
313
+ return expr;
314
+ }
315
+ if (t.schema.isObject(schema)) {
316
+ const objSchema = schema as TObject;
317
+ const props = objSchema.properties as Record<string, TSchema>;
318
+ const keys = Object.keys(props);
319
+ const v = this.nextVar();
320
+ const fields = keys.map((k, idx) => {
321
+ const inner = this.unwrap(props[k]);
322
+ const innerExpr = `${v}[${idx}]`;
323
+ if (t.schema.isObject(inner)) {
324
+ return `${k}:${this.genDecFromValue(inner, innerExpr)}`;
325
+ }
326
+ if (t.schema.isBigInt(inner)) {
327
+ return `${k}:BigInt(${innerExpr}.slice(0,-1))`;
328
+ }
329
+ return `${k}:${innerExpr}`;
330
+ });
331
+ return `((${v}=${expr})=>({${fields.join(",")}}))()`;
332
+ }
333
+ return expr;
334
+ }
335
+
336
+ // ===========================================================================
337
+ // Helpers
338
+ // ===========================================================================
339
+
340
+ protected isEnum(schema: TSchema): boolean {
341
+ return (
342
+ "enum" in schema &&
343
+ Array.isArray((schema as { enum?: unknown[] }).enum) &&
344
+ ((schema as { enum?: unknown[] }).enum?.length ?? 0) > 0
345
+ );
346
+ }
347
+
348
+ protected isNullable(schema: TSchema): boolean {
349
+ if (!t.schema.isUnion(schema)) return false;
350
+ const unionSchema = schema as TUnion;
351
+ return unionSchema.anyOf?.some((s: TSchema) => t.schema.isNull(s)) ?? false;
352
+ }
353
+
354
+ protected unwrap(schema: TSchema): TSchema {
355
+ if ("anyOf" in schema && Array.isArray((schema as TUnion).anyOf)) {
356
+ const unionSchema = schema as TUnion;
357
+ return (
358
+ unionSchema.anyOf.find((s: TSchema) => !t.schema.isNull(s)) || schema
359
+ );
360
+ }
361
+ return schema;
362
+ }
363
+
364
+ /**
365
+ * Reconstruct an object from a parsed array (for when input is already parsed).
366
+ */
367
+ protected reconstructObject(schema: TSchema, arr: any[]): any {
368
+ if (!t.schema.isObject(schema)) {
369
+ return arr;
370
+ }
371
+
372
+ const objSchema = schema as TObject;
373
+ const props = objSchema.properties as Record<string, TSchema>;
374
+ const keys = Object.keys(props);
375
+ const result: Record<string, any> = {};
376
+ let i = 0;
377
+
378
+ for (const k of keys) {
379
+ const ps = props[k];
380
+ const isOpt = t.schema.isOptional(ps);
381
+ const isNullable = this.isNullable(ps);
382
+ const inner = this.unwrap(ps);
383
+ const val = arr[i++];
384
+
385
+ if (isOpt) {
386
+ if (val !== null) {
387
+ result[k] = t.schema.isObject(inner)
388
+ ? this.reconstructObject(inner, val)
389
+ : val;
390
+ }
391
+ } else if (isNullable) {
392
+ result[k] =
393
+ val === null
394
+ ? null
395
+ : t.schema.isObject(inner)
396
+ ? this.reconstructObject(inner, val)
397
+ : val;
398
+ } else {
399
+ result[k] = t.schema.isObject(inner)
400
+ ? this.reconstructObject(inner, val)
401
+ : val;
402
+ }
403
+ }
404
+
405
+ return result;
406
+ }
407
+ }
@@ -209,12 +209,16 @@ describe("StateManager", () => {
209
209
  });
210
210
 
211
211
  it("should set values in ALS when context exists", async () => {
212
- const store: any = { context: "test-context" };
213
- alepha.context.run(() => {
214
- stateManager.set("name", "ALS Value");
215
- }, store);
212
+ // Note: AlsProvider spreads the store object, so we check values inside the callback
213
+ const result = alepha.context.run(
214
+ () => {
215
+ stateManager.set("name", "ALS Value");
216
+ return stateManager.get("name");
217
+ },
218
+ { context: "test-context" },
219
+ );
216
220
 
217
- expect(store.name).toBe("ALS Value");
221
+ expect(result).toBe("ALS Value");
218
222
  });
219
223
 
220
224
  it("should set values in local store when no ALS context", () => {
@@ -267,14 +271,16 @@ describe("StateManager", () => {
267
271
  stateManager.set("name", "Local Value");
268
272
 
269
273
  // In ALS context, ALS values should take priority
270
- const store: any = { context: "test-context" };
271
- const result = alepha.context.run(() => {
272
- stateManager.set("name", "ALS Priority");
273
- return stateManager.get("name");
274
- }, store);
274
+ // Note: AlsProvider spreads the store object, so we check values inside the callback
275
+ const result = alepha.context.run(
276
+ () => {
277
+ stateManager.set("name", "ALS Priority");
278
+ return stateManager.get("name");
279
+ },
280
+ { context: "test-context" },
281
+ );
275
282
 
276
283
  expect(result).toBe("ALS Priority");
277
- expect(store.name).toBe("ALS Priority");
278
284
  });
279
285
 
280
286
  it("should not skip event emission when values are the same", async () => {
@@ -291,12 +297,17 @@ describe("StateManager", () => {
291
297
  });
292
298
 
293
299
  it("should delete values from ALS when context exists", async () => {
294
- const store = { name: "To Delete", context: "test-context" };
295
- alepha.context.run(() => {
296
- stateManager.del("name");
297
- }, store);
300
+ // Note: AlsProvider spreads the store object, so we check values inside the callback
301
+ const result = alepha.context.run(
302
+ () => {
303
+ stateManager.set("name", "To Delete");
304
+ stateManager.del("name");
305
+ return stateManager.get("name");
306
+ },
307
+ { context: "test-context" },
308
+ );
298
309
 
299
- expect(store.name).toBeUndefined();
310
+ expect(result).toBeUndefined();
300
311
  });
301
312
 
302
313
  it("should clear only local store, not ALS", async () => {