alepha 0.20.3 → 0.20.4

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 (217) hide show
  1. package/dist/api/audits/index.d.ts.map +1 -1
  2. package/dist/api/files/index.d.ts.map +1 -1
  3. package/dist/api/jobs/index.d.ts +14 -14
  4. package/dist/api/jobs/index.d.ts.map +1 -1
  5. package/dist/api/keys/index.d.ts +4 -4
  6. package/dist/api/organizations/index.d.ts.map +1 -1
  7. package/dist/api/parameters/index.d.ts +8 -3
  8. package/dist/api/parameters/index.d.ts.map +1 -1
  9. package/dist/api/parameters/index.js +20 -4
  10. package/dist/api/parameters/index.js.map +1 -1
  11. package/dist/api/payments/index.d.ts.map +1 -1
  12. package/dist/api/users/index.browser.js +6 -0
  13. package/dist/api/users/index.browser.js.map +1 -1
  14. package/dist/api/users/index.d.ts +5037 -139
  15. package/dist/api/users/index.d.ts.map +1 -1
  16. package/dist/api/users/index.js +58 -10
  17. package/dist/api/users/index.js.map +1 -1
  18. package/dist/bucket/index.d.ts +77 -107
  19. package/dist/bucket/index.d.ts.map +1 -1
  20. package/dist/bucket/index.js +148 -4
  21. package/dist/bucket/index.js.map +1 -1
  22. package/dist/bucket/index.workerd.js +7 -1
  23. package/dist/bucket/index.workerd.js.map +1 -1
  24. package/dist/cache/core/index.d.ts +26 -0
  25. package/dist/cache/core/index.d.ts.map +1 -1
  26. package/dist/cache/core/index.js +11 -1
  27. package/dist/cache/core/index.js.map +1 -1
  28. package/dist/cache/core/index.workerd.js +11 -1
  29. package/dist/cache/core/index.workerd.js.map +1 -1
  30. package/dist/cli/config/index.d.ts +7 -5
  31. package/dist/cli/config/index.d.ts.map +1 -1
  32. package/dist/cli/config/index.js +2 -3
  33. package/dist/cli/config/index.js.map +1 -1
  34. package/dist/cli/core/index.d.ts +420 -13
  35. package/dist/cli/core/index.d.ts.map +1 -1
  36. package/dist/cli/core/index.js +22 -511
  37. package/dist/cli/core/index.js.map +1 -1
  38. package/dist/cli/devtools/index.d.ts +4 -8
  39. package/dist/cli/devtools/index.d.ts.map +1 -1
  40. package/dist/cli/devtools/index.js +13 -15
  41. package/dist/cli/devtools/index.js.map +1 -1
  42. package/dist/cli/platform/index.d.ts +10 -13
  43. package/dist/cli/platform/index.d.ts.map +1 -1
  44. package/dist/cli/platform/index.js +18 -15
  45. package/dist/cli/platform/index.js.map +1 -1
  46. package/dist/cli/vendor/index.d.ts +10 -13
  47. package/dist/cli/vendor/index.d.ts.map +1 -1
  48. package/dist/cli/vendor/index.js +16 -13
  49. package/dist/cli/vendor/index.js.map +1 -1
  50. package/dist/core/index.browser.js +27 -3
  51. package/dist/core/index.browser.js.map +1 -1
  52. package/dist/core/index.d.ts +6 -3
  53. package/dist/core/index.d.ts.map +1 -1
  54. package/dist/core/index.js +27 -3
  55. package/dist/core/index.js.map +1 -1
  56. package/dist/core/index.native.js +27 -3
  57. package/dist/core/index.native.js.map +1 -1
  58. package/dist/core/index.workerd.js +27 -3
  59. package/dist/core/index.workerd.js.map +1 -1
  60. package/dist/datetime/index.d.ts +69 -10
  61. package/dist/datetime/index.d.ts.map +1 -1
  62. package/dist/datetime/index.js +135 -13
  63. package/dist/datetime/index.js.map +1 -1
  64. package/dist/email/smtp/index.js +10636 -2
  65. package/dist/email/smtp/index.js.map +1 -1
  66. package/dist/fake/index.d.ts +8085 -4
  67. package/dist/fake/index.d.ts.map +1 -1
  68. package/dist/fake/index.js +33554 -3
  69. package/dist/fake/index.js.map +1 -1
  70. package/dist/lock/core/index.d.ts +30 -2
  71. package/dist/lock/core/index.d.ts.map +1 -1
  72. package/dist/lock/core/index.js +35 -12
  73. package/dist/lock/core/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts +238 -31
  75. package/dist/mcp/index.d.ts.map +1 -1
  76. package/dist/mcp/index.js +198 -71
  77. package/dist/mcp/index.js.map +1 -1
  78. package/dist/orm/core/index.browser.js +1 -1
  79. package/dist/orm/core/index.browser.js.map +1 -1
  80. package/dist/orm/core/index.bun.js +4 -3
  81. package/dist/orm/core/index.bun.js.map +1 -1
  82. package/dist/orm/core/index.d.ts +4877 -9
  83. package/dist/orm/core/index.d.ts.map +1 -1
  84. package/dist/orm/core/index.js +4 -3
  85. package/dist/orm/core/index.js.map +1 -1
  86. package/dist/orm/postgres/index.d.ts +608 -1
  87. package/dist/orm/postgres/index.d.ts.map +1 -1
  88. package/dist/react/core/index.d.ts +102 -1
  89. package/dist/react/core/index.d.ts.map +1 -1
  90. package/dist/react/core/index.js +65 -1
  91. package/dist/react/core/index.js.map +1 -1
  92. package/dist/react/form/index.d.ts +6 -0
  93. package/dist/react/form/index.d.ts.map +1 -1
  94. package/dist/react/form/index.js +7 -7
  95. package/dist/react/form/index.js.map +1 -1
  96. package/dist/react/i18n/index.d.ts +7 -1
  97. package/dist/react/i18n/index.d.ts.map +1 -1
  98. package/dist/react/i18n/index.js +6 -0
  99. package/dist/react/i18n/index.js.map +1 -1
  100. package/dist/react/router/index.browser.js +20 -2
  101. package/dist/react/router/index.browser.js.map +1 -1
  102. package/dist/react/router/index.d.ts +36 -4
  103. package/dist/react/router/index.d.ts.map +1 -1
  104. package/dist/react/router/index.js +20 -2
  105. package/dist/react/router/index.js.map +1 -1
  106. package/dist/react/testing/chunk-6Ep1yQYe.js +16 -0
  107. package/dist/react/testing/index.d.ts +411 -1
  108. package/dist/react/testing/index.d.ts.map +1 -1
  109. package/dist/react/testing/index.js +12293 -13
  110. package/dist/react/testing/index.js.map +1 -1
  111. package/dist/react/ui/index.d.ts +195 -1
  112. package/dist/react/ui/index.d.ts.map +1 -1
  113. package/dist/react/ui/index.js +61 -1
  114. package/dist/react/ui/index.js.map +1 -1
  115. package/dist/scheduler/index.d.ts +84 -3
  116. package/dist/scheduler/index.d.ts.map +1 -1
  117. package/dist/scheduler/index.js +390 -1
  118. package/dist/scheduler/index.js.map +1 -1
  119. package/dist/scheduler/index.workerd.js +390 -1
  120. package/dist/scheduler/index.workerd.js.map +1 -1
  121. package/dist/security/index.d.ts +325 -2
  122. package/dist/security/index.d.ts.map +1 -1
  123. package/dist/security/index.js +1361 -2
  124. package/dist/security/index.js.map +1 -1
  125. package/dist/server/auth/index.d.ts +1054 -1
  126. package/dist/server/auth/index.d.ts.map +1 -1
  127. package/dist/server/auth/index.js +1223 -1
  128. package/dist/server/auth/index.js.map +1 -1
  129. package/dist/server/core/index.browser.js +10 -3
  130. package/dist/server/core/index.browser.js.map +1 -1
  131. package/dist/server/core/index.d.ts.map +1 -1
  132. package/dist/server/core/index.js +28 -5
  133. package/dist/server/core/index.js.map +1 -1
  134. package/dist/server/metrics/index.d.ts +514 -1
  135. package/dist/server/metrics/index.d.ts.map +1 -1
  136. package/dist/server/metrics/index.js +4374 -4
  137. package/dist/server/metrics/index.js.map +1 -1
  138. package/dist/server/swagger/index.d.ts.map +1 -1
  139. package/dist/server/swagger/index.js +3 -4
  140. package/dist/server/swagger/index.js.map +1 -1
  141. package/dist/websocket/index.browser.js +11 -5
  142. package/dist/websocket/index.browser.js.map +1 -1
  143. package/dist/websocket/index.d.ts +3 -1
  144. package/dist/websocket/index.d.ts.map +1 -1
  145. package/dist/websocket/index.js +21 -6
  146. package/dist/websocket/index.js.map +1 -1
  147. package/package.json +671 -263
  148. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  149. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  150. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  151. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  152. package/src/api/users/entities/sessions.ts +6 -0
  153. package/src/api/users/jobs/UserJobs.ts +44 -17
  154. package/src/api/users/providers/RealmProvider.ts +4 -0
  155. package/src/api/users/services/SessionService.ts +27 -0
  156. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  157. package/src/bucket/index.ts +19 -2
  158. package/src/bucket/primitives/$bucket.ts +9 -1
  159. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  160. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  161. package/src/cache/core/index.ts +29 -0
  162. package/src/cache/core/primitives/$cache.ts +14 -1
  163. package/src/cli/config/defineConfig.ts +13 -15
  164. package/src/cli/core/__tests__/init.spec.ts +6 -7
  165. package/src/cli/core/services/ProjectScaffolder.ts +18 -14
  166. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  167. package/src/cli/core/templates/agentMd.ts +2 -10
  168. package/src/cli/core/templates/saasAdminLayoutTsx.ts +3 -3
  169. package/src/cli/devtools/index.ts +12 -26
  170. package/src/cli/platform/index.ts +15 -24
  171. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  172. package/src/cli/vendor/index.ts +14 -23
  173. package/src/core/Alepha.ts +11 -1
  174. package/src/core/helpers/ref.ts +18 -0
  175. package/src/core/index.shared.ts +1 -0
  176. package/src/core/providers/SchemaValidator.ts +9 -1
  177. package/src/core/providers/TypeProvider.ts +1 -2
  178. package/src/datetime/REFACTORING.md +118 -0
  179. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  180. package/src/lock/core/index.ts +31 -0
  181. package/src/lock/core/primitives/$lock.ts +14 -1
  182. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  183. package/src/mcp/helpers/jsonrpc.ts +26 -1
  184. package/src/mcp/index.ts +10 -5
  185. package/src/mcp/interfaces/McpTypes.ts +83 -6
  186. package/src/mcp/primitives/$prompt.ts +18 -1
  187. package/src/mcp/primitives/$resource.ts +18 -1
  188. package/src/mcp/primitives/$tool.ts +83 -7
  189. package/src/mcp/providers/McpServerProvider.ts +74 -16
  190. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  191. package/src/orm/REFACTORING.md +330 -0
  192. package/src/orm/core/primitives/$transactional.ts +11 -0
  193. package/src/orm/core/schemas/updateSchema.ts +1 -1
  194. package/src/orm/core/services/PgRelationManager.ts +4 -2
  195. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  196. package/src/react/core/hooks/useQuery.ts +153 -0
  197. package/src/react/core/index.ts +1 -0
  198. package/src/react/form/services/FormModel.ts +15 -6
  199. package/src/react/form/services/parseField.ts +8 -0
  200. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  201. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  202. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  203. package/src/react/router/primitives/$page.ts +28 -4
  204. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  205. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  206. package/src/react/ui/index.ts +6 -0
  207. package/src/react/ui/services/SchemaControl.ts +209 -0
  208. package/src/security/primitives/$issuer.ts +6 -3
  209. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  210. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  211. package/src/server/core/errors/ValidationError.ts +13 -1
  212. package/src/server/core/primitives/$action.ts +16 -5
  213. package/src/server/core/providers/ServerRouterProvider.ts +26 -4
  214. package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -7
  215. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  216. package/src/websocket/services/WebSocketClient.ts +11 -5
  217. package/src/mcp/transports/SseMcpTransport.ts +0 -182
@@ -9,24 +9,189 @@ import { $hook, $inject, Alepha } from "alepha";
9
9
  import DayjsApi, {
10
10
  type Dayjs,
11
11
  type ManipulateType,
12
+ type OpUnitType,
12
13
  type PluginFunc,
14
+ type QUnitType,
13
15
  } from "dayjs";
14
- import dayjsDuration from "dayjs/plugin/duration.js";
16
+ import dayjsDuration, { type DurationUnitType } from "dayjs/plugin/duration.js";
15
17
  import dayjsLocalizedFormat from "dayjs/plugin/localizedFormat.js";
16
18
  import dayjsRelativeTime from "dayjs/plugin/relativeTime.js";
17
19
  import dayjsTimezone from "dayjs/plugin/timezone.js";
18
20
  import dayjsUtc from "dayjs/plugin/utc.js";
19
21
 
20
- export type DateTime = DayjsApi.Dayjs;
21
- export type Duration = dayjsDuration.Duration;
22
- export type DurationLike =
23
- | number
24
- | dayjsDuration.Duration
25
- | [number, ManipulateType];
22
+ export type { DurationUnitType, ManipulateType, OpUnitType, QUnitType };
23
+
24
+ export type DateTimeInput = string | number | Date | DateTime | Dayjs;
25
+
26
+ export type DurationLike = number | Duration | [number, ManipulateType];
27
+
28
+ /**
29
+ * Immutable wrapper around the underlying date-time engine.
30
+ *
31
+ * Designed to isolate consumers from the engine in use (currently dayjs).
32
+ * Methods that produce a new value return a new `DateTime` instance.
33
+ */
34
+ export class DateTime {
35
+ protected readonly inner: Dayjs;
36
+
37
+ constructor(inner: Dayjs) {
38
+ this.inner = inner;
39
+ }
40
+
41
+ /**
42
+ * Add a duration to this date-time.
43
+ */
44
+ add(amount: number, unit?: ManipulateType): DateTime;
45
+ add(duration: Duration): DateTime;
46
+ add(amount: number | Duration, unit?: ManipulateType): DateTime {
47
+ if (amount instanceof Duration) {
48
+ return new DateTime(this.inner.add(amount.toDayjs()));
49
+ }
50
+ return new DateTime(this.inner.add(amount, unit));
51
+ }
52
+
53
+ /**
54
+ * Subtract a duration from this date-time.
55
+ */
56
+ subtract(amount: number, unit?: ManipulateType): DateTime;
57
+ subtract(duration: Duration): DateTime;
58
+ subtract(amount: number | Duration, unit?: ManipulateType): DateTime {
59
+ if (amount instanceof Duration) {
60
+ return new DateTime(this.inner.subtract(amount.toDayjs()));
61
+ }
62
+ return new DateTime(this.inner.subtract(amount, unit));
63
+ }
64
+
65
+ startOf(unit: OpUnitType): DateTime {
66
+ return new DateTime(this.inner.startOf(unit));
67
+ }
68
+
69
+ endOf(unit: OpUnitType): DateTime {
70
+ return new DateTime(this.inner.endOf(unit));
71
+ }
72
+
73
+ isAfter(other: DateTimeInput): boolean {
74
+ return this.inner.isAfter(toDayjs(other));
75
+ }
76
+
77
+ isBefore(other: DateTimeInput): boolean {
78
+ return this.inner.isBefore(toDayjs(other));
79
+ }
80
+
81
+ isSame(other: DateTimeInput, unit?: OpUnitType): boolean {
82
+ return this.inner.isSame(toDayjs(other), unit);
83
+ }
84
+
85
+ diff(other: DateTimeInput, unit?: QUnitType | OpUnitType): number {
86
+ return this.inner.diff(toDayjs(other), unit);
87
+ }
88
+
89
+ tz(timezone: string): DateTime {
90
+ return new DateTime(this.inner.tz(timezone));
91
+ }
92
+
93
+ locale(lang: string): DateTime {
94
+ return new DateTime(this.inner.locale(lang));
95
+ }
96
+
97
+ format(template?: string): string {
98
+ return this.inner.format(template);
99
+ }
100
+
101
+ fromNow(withoutSuffix?: boolean): string {
102
+ return this.inner.fromNow(withoutSuffix);
103
+ }
104
+
105
+ toISOString(): string {
106
+ return this.inner.toISOString();
107
+ }
108
+
109
+ toDate(): Date {
110
+ return this.inner.toDate();
111
+ }
112
+
113
+ valueOf(): number {
114
+ return this.inner.valueOf();
115
+ }
116
+
117
+ unix(): number {
118
+ return this.inner.unix();
119
+ }
120
+
121
+ toJSON(): string {
122
+ return this.inner.toISOString();
123
+ }
124
+
125
+ toString(): string {
126
+ return this.inner.toISOString();
127
+ }
128
+
129
+ /**
130
+ * Escape hatch for the underlying dayjs instance.
131
+ *
132
+ * Use sparingly — anything calling this becomes coupled to dayjs and
133
+ * will need to migrate when the engine is replaced.
134
+ */
135
+ toDayjs(): Dayjs {
136
+ return this.inner;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Immutable wrapper around the underlying duration engine.
142
+ */
143
+ export class Duration {
144
+ protected readonly inner: dayjsDuration.Duration;
145
+
146
+ constructor(inner: dayjsDuration.Duration) {
147
+ this.inner = inner;
148
+ }
149
+
150
+ asMilliseconds(): number {
151
+ return this.inner.asMilliseconds();
152
+ }
153
+
154
+ asSeconds(): number {
155
+ return this.inner.asSeconds();
156
+ }
157
+
158
+ asMinutes(): number {
159
+ return this.inner.asMinutes();
160
+ }
161
+
162
+ asHours(): number {
163
+ return this.inner.asHours();
164
+ }
165
+
166
+ asDays(): number {
167
+ return this.inner.asDays();
168
+ }
169
+
170
+ as(unit: DurationUnitType): number {
171
+ return this.inner.as(unit);
172
+ }
173
+
174
+ toISOString(): string {
175
+ return this.inner.toISOString();
176
+ }
177
+
178
+ /**
179
+ * Escape hatch for the underlying dayjs duration.
180
+ */
181
+ toDayjs(): dayjsDuration.Duration {
182
+ return this.inner;
183
+ }
184
+ }
26
185
 
27
- export const dayjs = DayjsApi;
28
186
  export const isDateTime = (value: unknown): value is DateTime => {
29
- return dayjs.isDayjs(value);
187
+ return value instanceof DateTime;
188
+ };
189
+
190
+ const toDayjs = (value: DateTimeInput): Dayjs => {
191
+ if (value instanceof DateTime) {
192
+ return value.toDayjs();
193
+ }
194
+ return DayjsApi(value as any);
30
195
  };
31
196
 
32
197
  export class DateTimeProvider {
@@ -45,7 +210,7 @@ export class DateTimeProvider {
45
210
 
46
211
  constructor() {
47
212
  for (const plugin of DateTimeProvider.PLUGINS) {
48
- dayjs.extend(plugin);
213
+ DayjsApi.extend(plugin);
49
214
  }
50
215
  }
51
216
 
@@ -81,33 +246,34 @@ export class DateTimeProvider {
81
246
  });
82
247
 
83
248
  public setLocale(locale: string): void {
84
- dayjs.locale(locale);
249
+ DayjsApi.locale(locale);
85
250
  }
86
251
 
87
252
  public isDateTime(value: unknown): value is DateTime {
88
- return dayjs.isDayjs(value);
253
+ return value instanceof DateTime;
89
254
  }
90
255
 
91
256
  /**
92
257
  * Create a new UTC DateTime instance.
93
258
  */
94
- public utc(
95
- date: string | number | Date | Dayjs | null | undefined,
96
- ): DateTime {
97
- return dayjs.utc(date);
259
+ public utc(date: DateTimeInput | null | undefined): DateTime {
260
+ return new DateTime(DayjsApi.utc(unwrap(date)));
98
261
  }
99
262
 
100
263
  /**
101
264
  * Create a new DateTime instance.
102
265
  */
103
- public of(date: string | number | Date | Dayjs | null | undefined): DateTime {
104
- return dayjs(date);
266
+ public of(date: DateTimeInput | null | undefined): DateTime {
267
+ if (date instanceof DateTime) {
268
+ return date;
269
+ }
270
+ return new DateTime(DayjsApi(date as any));
105
271
  }
106
272
 
107
273
  /**
108
274
  * Get the current date as a string.
109
275
  */
110
- public toISOString(date: Date | string | DateTime = this.now()): string {
276
+ public toISOString(date: DateTimeInput = this.now()): string {
111
277
  return this.of(date).toISOString();
112
278
  }
113
279
 
@@ -152,7 +318,7 @@ export class DateTimeProvider {
152
318
  return this.ref;
153
319
  }
154
320
 
155
- return dayjs();
321
+ return new DateTime(DayjsApi());
156
322
  }
157
323
 
158
324
  /**
@@ -162,12 +328,16 @@ export class DateTimeProvider {
162
328
  duration: DurationLike,
163
329
  unit?: ManipulateType,
164
330
  ): Duration => {
331
+ if (duration instanceof Duration) {
332
+ return duration;
333
+ }
334
+
165
335
  if (Array.isArray(duration)) {
166
- return dayjs.duration(duration[0], duration[1]);
336
+ return new Duration(DayjsApi.duration(duration[0], duration[1]));
167
337
  }
168
338
 
169
339
  if (typeof duration === "number") {
170
- return dayjs.duration(duration, unit || "milliseconds");
340
+ return new Duration(DayjsApi.duration(duration, unit || "milliseconds"));
171
341
  }
172
342
 
173
343
  return duration;
@@ -175,7 +345,9 @@ export class DateTimeProvider {
175
345
 
176
346
  public isDurationLike(value: unknown): value is DurationLike {
177
347
  try {
178
- return dayjs.isDuration(this.duration(value as DurationLike));
348
+ return DayjsApi.isDuration(
349
+ this.duration(value as DurationLike).toDayjs(),
350
+ );
179
351
  } catch {
180
352
  return false;
181
353
  }
@@ -261,7 +433,7 @@ export class DateTimeProvider {
261
433
  ): Timeout {
262
434
  if (this.ref && now) {
263
435
  const next = this.of(now).add(this.duration(duration));
264
- if (next < this.now()) {
436
+ if (next.valueOf() < this.now().valueOf()) {
265
437
  callback();
266
438
  }
267
439
  return {
@@ -403,6 +575,13 @@ export class DateTimeProvider {
403
575
  }
404
576
  }
405
577
 
578
+ const unwrap = (value: DateTimeInput | null | undefined): any => {
579
+ if (value instanceof DateTime) {
580
+ return value.toDayjs();
581
+ }
582
+ return value;
583
+ };
584
+
406
585
  export interface Interval {
407
586
  timer?: any;
408
587
  duration: number;
@@ -13,6 +13,37 @@ export * from "./providers/MemoryLockProvider.ts";
13
13
 
14
14
  // ---------------------------------------------------------------------------------------------------------------------
15
15
 
16
+ declare module "alepha" {
17
+ interface Hooks {
18
+ /**
19
+ * Fires when a lock is successfully acquired.
20
+ */
21
+ "lock:acquired": {
22
+ name: string;
23
+ id: string;
24
+ maxDurationMs: number;
25
+ };
26
+ /**
27
+ * Fires when a lock is released (handler completed or threw).
28
+ */
29
+ "lock:released": {
30
+ name: string;
31
+ id: string;
32
+ heldMs: number;
33
+ };
34
+ /**
35
+ * Fires when a lock acquisition contends with another holder.
36
+ * Emitted whether the caller eventually acquires (`wait: true`) or fails.
37
+ */
38
+ "lock:contended": {
39
+ name: string;
40
+ id: string;
41
+ };
42
+ }
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------------------------------------------------
46
+
16
47
  /**
17
48
  * Resource locking for distributed systems.
18
49
  *
@@ -43,7 +43,7 @@ export const $lock = (options: LockMiddlewareOptions): Middleware => {
43
43
  return createMiddleware({
44
44
  name: "$lock",
45
45
  options: options as unknown as Record<string, unknown>,
46
- handler: ({ next }) => {
46
+ handler: ({ alepha, next }) => {
47
47
  const id = crypto.randomUUID();
48
48
  const maxDurationMs = dateTimeProvider
49
49
  .duration(options.maxDuration ?? [5, "minutes"])
@@ -71,11 +71,13 @@ export const $lock = (options: LockMiddlewareOptions): Middleware => {
71
71
 
72
72
  // Lock already ended (grace period active)
73
73
  if (endedAtStr) {
74
+ await alepha.events.emit("lock:contended", { name, id });
74
75
  throw new LockAcquireError(name);
75
76
  }
76
77
 
77
78
  // Lock held by someone else
78
79
  if (lockId !== id) {
80
+ await alepha.events.emit("lock:contended", { name, id });
79
81
  if (options.wait) {
80
82
  // Poll until lock is released
81
83
  const start = dateTimeProvider.nowMillis();
@@ -109,10 +111,21 @@ export const $lock = (options: LockMiddlewareOptions): Middleware => {
109
111
  }
110
112
 
111
113
  // We hold the lock — execute handler
114
+ const acquiredAt = dateTimeProvider.nowMillis();
115
+ await alepha.events.emit("lock:acquired", {
116
+ name,
117
+ id,
118
+ maxDurationMs,
119
+ });
112
120
  try {
113
121
  return await next(...args);
114
122
  } finally {
115
123
  await lockProvider.del(name);
124
+ await alepha.events.emit("lock:released", {
125
+ name,
126
+ id,
127
+ heldMs: dateTimeProvider.nowMillis() - acquiredAt,
128
+ });
116
129
  }
117
130
  };
118
131
  },
@@ -25,7 +25,7 @@ describe("jsonrpc constants", () => {
25
25
  });
26
26
 
27
27
  test("MCP_PROTOCOL_VERSION should be defined", () => {
28
- expect(MCP_PROTOCOL_VERSION).toBe("2024-11-05");
28
+ expect(MCP_PROTOCOL_VERSION).toBe("2025-11-25");
29
29
  });
30
30
 
31
31
  test("JsonRpcErrorCodes should have correct values", () => {
@@ -12,7 +12,32 @@ import type {
12
12
 
13
13
  export const JSONRPC_VERSION = "2.0" as const;
14
14
 
15
- export const MCP_PROTOCOL_VERSION = "2024-11-05" as const;
15
+ /**
16
+ * The latest MCP protocol revision Alepha targets.
17
+ * See {@link SUPPORTED_PROTOCOL_VERSIONS} for the full negotiation list.
18
+ */
19
+ export const MCP_PROTOCOL_VERSION = "2025-11-25" as const;
20
+
21
+ /**
22
+ * Protocol versions Alepha will accept during `initialize` negotiation,
23
+ * highest preference first. The server echoes back whichever version the
24
+ * client requested if it appears here, otherwise picks the first entry.
25
+ */
26
+ export const SUPPORTED_PROTOCOL_VERSIONS = [
27
+ "2025-11-25",
28
+ "2025-06-18",
29
+ "2025-03-26",
30
+ "2024-11-05",
31
+ ] as const;
32
+
33
+ export type SupportedProtocolVersion =
34
+ (typeof SUPPORTED_PROTOCOL_VERSIONS)[number];
35
+
36
+ export const isSupportedProtocolVersion = (
37
+ v: unknown,
38
+ ): v is SupportedProtocolVersion =>
39
+ typeof v === "string" &&
40
+ (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(v);
16
41
 
17
42
  export const JsonRpcErrorCodes = {
18
43
  PARSE_ERROR: -32700,
package/src/mcp/index.ts CHANGED
@@ -3,7 +3,7 @@ import { $prompt } from "./primitives/$prompt.ts";
3
3
  import { $resource } from "./primitives/$resource.ts";
4
4
  import { $tool } from "./primitives/$tool.ts";
5
5
  import { McpServerProvider } from "./providers/McpServerProvider.ts";
6
- import { SseMcpTransport } from "./transports/SseMcpTransport.ts";
6
+ import { StreamableHttpMcpTransport } from "./transports/StreamableHttpMcpTransport.ts";
7
7
 
8
8
  // ---------------------------------------------------------------------------------------------------------------------
9
9
 
@@ -28,12 +28,15 @@ export {
28
28
  createParseError,
29
29
  createResponse,
30
30
  isNotification,
31
+ isSupportedProtocolVersion,
31
32
  isValidJsonRpcRequest,
32
33
  JSONRPC_VERSION,
33
34
  JsonRpcErrorCodes,
34
35
  JsonRpcParseError,
35
36
  MCP_PROTOCOL_VERSION,
36
37
  parseMessage,
38
+ SUPPORTED_PROTOCOL_VERSIONS,
39
+ type SupportedProtocolVersion,
37
40
  } from "./helpers/jsonrpc.ts";
38
41
  export type {
39
42
  JsonRpcError,
@@ -88,8 +91,10 @@ export { $tool, ToolPrimitive } from "./primitives/$tool.ts";
88
91
  export { McpServerProvider } from "./providers/McpServerProvider.ts";
89
92
  export {
90
93
  mcpSseOptions,
94
+ mcpStreamableHttpOptions,
91
95
  SseMcpTransport,
92
- } from "./transports/SseMcpTransport.ts";
96
+ StreamableHttpMcpTransport,
97
+ } from "./transports/StreamableHttpMcpTransport.ts";
93
98
 
94
99
  // ---------------------------------------------------------------------------------------------------------------------
95
100
 
@@ -101,7 +106,7 @@ export {
101
106
  * - MCP tool definitions
102
107
  * - MCP prompt definitions
103
108
  * - JSON-RPC protocol
104
- * - SSE and Stdio transports
109
+ * - Streamable HTTP transport (spec 2025-03-26+)
105
110
  *
106
111
  * @module alepha.mcp
107
112
  */
@@ -109,6 +114,6 @@ export const AlephaMcp = $module({
109
114
  name: "alepha.mcp",
110
115
  primitives: [$tool, $resource, $prompt],
111
116
  services: [McpServerProvider],
112
- // Transports are opt-in — user wires the one(s) they need via alepha.with(SseMcpTransport).
113
- variants: [SseMcpTransport],
117
+ // Transports are opt-in — user wires the one(s) they need via alepha.with(StreamableHttpMcpTransport).
118
+ variants: [StreamableHttpMcpTransport],
114
119
  });
@@ -40,14 +40,48 @@ export interface McpCapabilities {
40
40
  prompts?: Record<string, never>;
41
41
  }
42
42
 
43
+ /**
44
+ * Spec 2025-11-25: optional `description` aligns with the MCP registry's
45
+ * `server.json` format and provides human-readable context during init.
46
+ */
43
47
  export interface McpServerInfo {
44
48
  name: string;
45
49
  version: string;
50
+ description?: string;
46
51
  }
47
52
 
48
53
  export interface McpClientInfo {
49
54
  name: string;
50
55
  version: string;
56
+ description?: string;
57
+ }
58
+
59
+ /**
60
+ * Icon descriptor (spec 2025-11-25 / SEP-973). Mirrors web app manifest
61
+ * icon shape — clients render these in tool palettes / picker UIs.
62
+ */
63
+ export interface McpIcon {
64
+ src: string;
65
+ mimeType?: string;
66
+ sizes?: string;
67
+ }
68
+
69
+ /**
70
+ * Tool annotations (spec 2025-03-26+). Hints to the client about how to
71
+ * present and gate the tool. None are guarantees — the client uses these
72
+ * heuristically (e.g. to show a confirmation prompt for destructive tools).
73
+ */
74
+ export interface McpToolAnnotations {
75
+ /** Human-friendly display title (distinct from `name`, the programmatic id). */
76
+ title?: string;
77
+ /** Tool reads only; safe to auto-approve. */
78
+ readOnlyHint?: boolean;
79
+ /** Tool may delete or overwrite data; client should require confirmation. */
80
+ destructiveHint?: boolean;
81
+ /** Calling the tool with the same args twice yields the same end state. */
82
+ idempotentHint?: boolean;
83
+ /** Tool interacts with the open world (network, etc.) vs. a closed system. */
84
+ openWorldHint?: boolean;
51
85
  }
52
86
 
53
87
  export interface McpInitializeParams {
@@ -68,14 +102,27 @@ export interface McpInitializeResult {
68
102
 
69
103
  export interface McpToolDescriptor {
70
104
  name: string;
105
+ /** Human-friendly display label (spec 2025-11-25). Distinct from `name`. */
106
+ title?: string;
71
107
  description: string;
72
108
  inputSchema: McpJsonSchema;
109
+ /** Output schema enabling `structuredContent` on call results (spec 2025-06-18). */
110
+ outputSchema?: McpJsonSchema;
111
+ /** Behavior hints (spec 2025-03-26+). */
112
+ annotations?: McpToolAnnotations;
113
+ /** Optional icons (spec 2025-11-25 / SEP-973). */
114
+ icons?: McpIcon[];
115
+ /** Arbitrary metadata passthrough (spec 2025-06-18+). */
116
+ _meta?: Record<string, unknown>;
73
117
  }
74
118
 
75
119
  export interface McpJsonSchema {
76
120
  type: string;
77
121
  properties?: Record<string, unknown>;
78
122
  required?: string[];
123
+ /** JSON Schema dialect (spec 2025-11-25 / SEP-1613 — defaults to 2020-12). */
124
+ $schema?: string;
125
+ [key: string]: unknown;
79
126
  }
80
127
 
81
128
  export interface McpToolCallParams {
@@ -85,15 +132,33 @@ export interface McpToolCallParams {
85
132
 
86
133
  export interface McpToolCallResult {
87
134
  content: McpContent[];
135
+ /**
136
+ * Structured tool output (spec 2025-06-18). When the tool declares an
137
+ * `outputSchema`, the server MUST populate `structuredContent`; the
138
+ * `content` array carrying a JSON-stringified text block is kept as
139
+ * a back-compat fallback for older clients.
140
+ */
141
+ structuredContent?: unknown;
88
142
  isError?: boolean;
143
+ _meta?: Record<string, unknown>;
89
144
  }
90
145
 
91
- export interface McpContent {
92
- type: "text" | "image" | "resource";
93
- text?: string;
94
- data?: string;
95
- mimeType?: string;
96
- }
146
+ /**
147
+ * Discriminated content union covering text, image, audio (added 2025-03-26),
148
+ * inlined resources, and resource links (added 2025-06-18).
149
+ */
150
+ export type McpContent =
151
+ | { type: "text"; text: string }
152
+ | { type: "image"; data: string; mimeType: string }
153
+ | { type: "audio"; data: string; mimeType: string }
154
+ | { type: "resource"; resource: McpResourceContent }
155
+ | {
156
+ type: "resource_link";
157
+ uri: string;
158
+ name: string;
159
+ description?: string;
160
+ mimeType?: string;
161
+ };
97
162
 
98
163
  // ---------------------------------------------------------------------------------------------------------------------
99
164
  // Resource Types
@@ -102,8 +167,14 @@ export interface McpContent {
102
167
  export interface McpResourceDescriptor {
103
168
  uri: string;
104
169
  name: string;
170
+ /** Human-friendly display label (spec 2025-11-25). Distinct from `name`. */
171
+ title?: string;
105
172
  description?: string;
106
173
  mimeType?: string;
174
+ /** Optional icons (spec 2025-11-25 / SEP-973). */
175
+ icons?: McpIcon[];
176
+ /** Arbitrary metadata passthrough (spec 2025-06-18+). */
177
+ _meta?: Record<string, unknown>;
107
178
  }
108
179
 
109
180
  export interface McpResourceReadParams {
@@ -127,8 +198,14 @@ export interface McpResourceContent {
127
198
 
128
199
  export interface McpPromptDescriptor {
129
200
  name: string;
201
+ /** Human-friendly display label (spec 2025-11-25). Distinct from `name`. */
202
+ title?: string;
130
203
  description?: string;
131
204
  arguments?: McpPromptArgument[];
205
+ /** Optional icons (spec 2025-11-25 / SEP-973). */
206
+ icons?: McpIcon[];
207
+ /** Arbitrary metadata passthrough (spec 2025-06-18+). */
208
+ _meta?: Record<string, unknown>;
132
209
  }
133
210
 
134
211
  export interface McpPromptArgument {
@@ -10,6 +10,7 @@ import {
10
10
  } from "alepha";
11
11
  import type {
12
12
  McpContext,
13
+ McpIcon,
13
14
  McpPromptArgument,
14
15
  McpPromptDescriptor,
15
16
  PromptHandlerArgs,
@@ -80,6 +81,12 @@ export interface PromptPrimitiveOptions<T extends TObject> {
80
81
  */
81
82
  name?: string;
82
83
 
84
+ /**
85
+ * Human-friendly display title (spec 2025-11-25). Distinct from `name`,
86
+ * which remains the programmatic identifier.
87
+ */
88
+ title?: string;
89
+
83
90
  /**
84
91
  * Description of what this prompt does.
85
92
  *
@@ -89,6 +96,11 @@ export interface PromptPrimitiveOptions<T extends TObject> {
89
96
  */
90
97
  description?: string;
91
98
 
99
+ /**
100
+ * Optional icons surfaced in client UIs (spec 2025-11-25 / SEP-973).
101
+ */
102
+ icons?: McpIcon[];
103
+
92
104
  /**
93
105
  * TypeBox schema defining the prompt arguments.
94
106
  *
@@ -157,13 +169,18 @@ export class PromptPrimitive<T extends TObject> extends Primitive<
157
169
  * Convert the prompt to an MCP prompt descriptor for protocol messages.
158
170
  */
159
171
  public toDescriptor(): McpPromptDescriptor {
160
- return {
172
+ const descriptor: McpPromptDescriptor = {
161
173
  name: this.name,
162
174
  description: this.description,
163
175
  arguments: this.options.args
164
176
  ? this.schemaToArguments(this.options.args)
165
177
  : [],
166
178
  };
179
+ if (this.options.title) descriptor.title = this.options.title;
180
+ if (this.options.icons && this.options.icons.length > 0) {
181
+ descriptor.icons = this.options.icons;
182
+ }
183
+ return descriptor;
167
184
  }
168
185
 
169
186
  /**