@capixjs/core 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +329 -0
  3. package/dist/capability.d.ts +165 -0
  4. package/dist/capability.d.ts.map +1 -0
  5. package/dist/capability.js +268 -0
  6. package/dist/capability.js.map +1 -0
  7. package/dist/context.d.ts +35 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +28 -0
  10. package/dist/context.js.map +1 -0
  11. package/dist/enhancers.d.ts +92 -0
  12. package/dist/enhancers.d.ts.map +1 -0
  13. package/dist/enhancers.js +251 -0
  14. package/dist/enhancers.js.map +1 -0
  15. package/dist/errors.d.ts +53 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +74 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/event-bus.d.ts +56 -0
  20. package/dist/event-bus.d.ts.map +1 -0
  21. package/dist/event-bus.js +51 -0
  22. package/dist/event-bus.js.map +1 -0
  23. package/dist/execution-engine.d.ts +39 -0
  24. package/dist/execution-engine.d.ts.map +1 -0
  25. package/dist/execution-engine.js +210 -0
  26. package/dist/execution-engine.js.map +1 -0
  27. package/dist/guards.d.ts +78 -0
  28. package/dist/guards.d.ts.map +1 -0
  29. package/dist/guards.js +56 -0
  30. package/dist/guards.js.map +1 -0
  31. package/dist/index.d.ts +22 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +22 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/plugin.d.ts +28 -0
  36. package/dist/plugin.d.ts.map +1 -0
  37. package/dist/plugin.js +43 -0
  38. package/dist/plugin.js.map +1 -0
  39. package/dist/server.d.ts +70 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +97 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/type-tests.d.ts +12 -0
  44. package/dist/type-tests.d.ts.map +1 -0
  45. package/dist/type-tests.js +175 -0
  46. package/dist/type-tests.js.map +1 -0
  47. package/package.json +47 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * enhancers.ts — built-in capability enhancers
3
+ * Depends on: capability.ts
4
+ */
5
+ import { defineError, isFrameworkError } from './errors.js';
6
+ import { defaultErrors } from './errors.js';
7
+ /** Pass-through for type inference. */
8
+ export function defineEnhancer(fn) {
9
+ return fn;
10
+ }
11
+ /** Logs capability name, duration, and outcome. Falls back to console if ctx has no logger. */
12
+ export const withLogging = defineEnhancer((cap) => ({
13
+ ...cap,
14
+ resolve: async (input, ctx) => {
15
+ const start = Date.now();
16
+ const logger = typeof ctx['logger'] === 'object' && ctx['logger'] !== null
17
+ ? ctx['logger']
18
+ : { info: console.info, error: console.error };
19
+ try {
20
+ const result = await cap._resolverOnly(input, ctx);
21
+ logger.info(`[capix] ${cap.name} ok (${Date.now() - start}ms)`);
22
+ return result;
23
+ }
24
+ catch (err) {
25
+ logger.error(`[capix] ${cap.name} error (${Date.now() - start}ms)`);
26
+ throw err;
27
+ }
28
+ },
29
+ }));
30
+ /** In-memory cache. Key = capabilityName:JSON(input). TTL in seconds. */
31
+ export function withCache(ttlSeconds) {
32
+ const store = new Map();
33
+ return defineEnhancer((cap) => ({
34
+ ...cap,
35
+ resolve: async (input, ctx) => {
36
+ const key = `${cap.name}:${JSON.stringify(input)}`;
37
+ const cached = store.get(key);
38
+ if (cached !== undefined && cached.expiresAt > Date.now()) {
39
+ return cached.value;
40
+ }
41
+ const result = await cap._resolverOnly(input, ctx);
42
+ store.set(key, { value: result, expiresAt: Date.now() + ttlSeconds * 1000 });
43
+ return result;
44
+ },
45
+ }));
46
+ }
47
+ /** Rejects if the resolver exceeds the given milliseconds. */
48
+ export function withTimeout(ms) {
49
+ return defineEnhancer((cap) => ({
50
+ ...cap,
51
+ resolve: (input, ctx) => {
52
+ let handle;
53
+ const timeoutPromise = new Promise((_, reject) => {
54
+ handle = setTimeout(() => reject(defaultErrors.Timeout({ capability: cap.name, ms })), ms);
55
+ });
56
+ return Promise.race([
57
+ cap._resolverOnly(input, ctx),
58
+ timeoutPromise,
59
+ ]).finally(() => {
60
+ if (handle !== undefined)
61
+ clearTimeout(handle);
62
+ });
63
+ },
64
+ }));
65
+ }
66
+ /** Retries on non-FrameworkError failures with exponential backoff. */
67
+ export function withRetry(maxAttempts, delayMs = 100) {
68
+ return defineEnhancer((cap) => ({
69
+ ...cap,
70
+ resolve: async (input, ctx) => {
71
+ let lastError;
72
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
73
+ try {
74
+ return await cap._resolverOnly(input, ctx);
75
+ }
76
+ catch (err) {
77
+ // Don't retry FrameworkErrors — they are intentional
78
+ if (isFrameworkError(err))
79
+ throw err;
80
+ lastError = err;
81
+ if (attempt < maxAttempts) {
82
+ await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt - 1)));
83
+ }
84
+ }
85
+ }
86
+ throw lastError;
87
+ },
88
+ }));
89
+ }
90
+ /** Sliding-window in-memory rate limiter. Throws 429 when limit exceeded. */
91
+ export function withRateLimit(options) {
92
+ const { limit, windowMs, keyFn } = options;
93
+ const store = new Map();
94
+ return defineEnhancer((cap) => ({
95
+ ...cap,
96
+ resolve: async (input, ctx) => {
97
+ const key = keyFn ? keyFn(input, ctx) : cap.name;
98
+ const now = Date.now();
99
+ const windowStart = now - windowMs;
100
+ let timestamps = store.get(key) ?? [];
101
+ timestamps = timestamps.filter((t) => t > windowStart);
102
+ if (timestamps.length >= limit) {
103
+ // Find the oldest timestamp in the window — client can retry after it expires
104
+ const oldestInWindow = timestamps[0] ?? now;
105
+ const resetAt = oldestInWindow + windowMs;
106
+ const retryAfter = Math.ceil((resetAt - now) / 1000);
107
+ throw defaultErrors.TooManyRequests({
108
+ retryAfter,
109
+ resetAt: new Date(resetAt).toISOString(),
110
+ limit,
111
+ });
112
+ }
113
+ timestamps.push(now);
114
+ store.set(key, timestamps);
115
+ return cap._resolverOnly(input, ctx);
116
+ },
117
+ }));
118
+ }
119
+ /**
120
+ * Adds explicit rollback support to a capability.
121
+ *
122
+ * Use ctx.onRollback(fn) to register compensation actions that run in reverse
123
+ * order if the resolver throws after one or more steps have already executed.
124
+ *
125
+ * This is NOT a database transaction. It does not provide atomicity,
126
+ * isolation, or durability. For real transactions, use your database's
127
+ * transaction API directly inside the resolver.
128
+ *
129
+ * Use this for: in-memory stores, multi-step operations where each step can
130
+ * be independently compensated (undone).
131
+ *
132
+ * @example
133
+ * export const checkout = cap(z.object({}), async (_, ctx) => {
134
+ * const order = ctx.db.orders.create({ ... });
135
+ * ctx.onRollback(() => ctx.db.orders.delete(order.id));
136
+ *
137
+ * ctx.db.inventory.decrement(item.id);
138
+ * ctx.onRollback(() => ctx.db.inventory.increment(item.id));
139
+ *
140
+ * return order;
141
+ * }).enhance(withRollback);
142
+ */
143
+ export const withRollback = defineEnhancer((cap) => ({
144
+ ...cap,
145
+ resolve: async (input, ctx) => {
146
+ const rollbacks = [];
147
+ const txCtx = {
148
+ ...ctx,
149
+ onRollback: (fn) => { rollbacks.push(fn); },
150
+ };
151
+ try {
152
+ return await cap._resolverOnly(input, txCtx);
153
+ }
154
+ catch (err) {
155
+ for (const rollback of rollbacks.reverse()) {
156
+ try {
157
+ await rollback();
158
+ }
159
+ catch (rollbackErr) {
160
+ console.error('[capix] Rollback failed:', rollbackErr);
161
+ }
162
+ }
163
+ throw err;
164
+ }
165
+ },
166
+ }));
167
+ /** Metrics collector that logs to console. */
168
+ export const consoleMetricsCollector = {
169
+ increment(name, tags) {
170
+ console.log(`[capix:metrics] ${name}`, tags ?? {});
171
+ },
172
+ histogram(name, value, tags) {
173
+ console.log(`[capix:metrics] ${name}=${value}ms`, tags ?? {});
174
+ },
175
+ };
176
+ /** Wraps a capability to emit duration and success/error metrics. */
177
+ export function withMetrics(collector) {
178
+ return defineEnhancer((cap) => ({
179
+ ...cap,
180
+ resolve: async (input, ctx) => {
181
+ const start = Date.now();
182
+ const tags = { capability: cap.name };
183
+ try {
184
+ const result = await cap._resolverOnly(input, ctx);
185
+ collector.histogram('capability.duration', Date.now() - start, tags);
186
+ collector.increment('capability.success', tags);
187
+ return result;
188
+ }
189
+ catch (err) {
190
+ collector.histogram('capability.duration', Date.now() - start, tags);
191
+ collector.increment('capability.error', tags);
192
+ throw err;
193
+ }
194
+ },
195
+ }));
196
+ }
197
+ const circuitUnavailable = defineError(503, 'Service unavailable');
198
+ /**
199
+ * Circuit breaker with closed/open/half-open states.
200
+ * State is closure-captured per capability application.
201
+ * FrameworkErrors do not count toward the failure threshold.
202
+ */
203
+ export function withCircuitBreaker(options) {
204
+ const { failureThreshold, successThreshold, timeoutMs } = options;
205
+ return defineEnhancer((cap) => {
206
+ let state = 'closed';
207
+ let failures = 0;
208
+ let successes = 0;
209
+ let openedAt = 0;
210
+ return {
211
+ ...cap,
212
+ resolve: async (input, ctx) => {
213
+ if (state === 'open') {
214
+ if (Date.now() - openedAt >= timeoutMs) {
215
+ state = 'half-open';
216
+ successes = 0;
217
+ }
218
+ else {
219
+ throw circuitUnavailable({ reason: `Circuit open for '${cap.name}'` });
220
+ }
221
+ }
222
+ try {
223
+ const result = await cap._resolverOnly(input, ctx);
224
+ if (state === 'half-open') {
225
+ successes++;
226
+ if (successes >= successThreshold) {
227
+ state = 'closed';
228
+ failures = 0;
229
+ }
230
+ }
231
+ else {
232
+ failures = 0;
233
+ }
234
+ return result;
235
+ }
236
+ catch (err) {
237
+ if (!isFrameworkError(err)) {
238
+ failures++;
239
+ if (state === 'half-open' || failures >= failureThreshold) {
240
+ state = 'open';
241
+ openedAt = Date.now();
242
+ failures = 0;
243
+ }
244
+ }
245
+ throw err;
246
+ }
247
+ },
248
+ };
249
+ });
250
+ }
251
+ //# sourceMappingURL=enhancers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enhancers.js","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * errors.ts — typed framework errors
3
+ * No dependencies. Foundation for everything else.
4
+ */
5
+ declare const FRAMEWORK_ERROR_BRAND: unique symbol;
6
+ /** Typed error produced by defineError factories. */
7
+ export type FrameworkError = {
8
+ readonly status: number;
9
+ readonly error: string;
10
+ readonly message: string;
11
+ readonly meta?: Record<string, unknown>;
12
+ readonly [FRAMEWORK_ERROR_BRAND]: true;
13
+ };
14
+ /** A callable factory that produces FrameworkError instances. */
15
+ export type ErrorFactory = (meta?: Record<string, unknown>) => FrameworkError;
16
+ /**
17
+ * Creates a typed error factory for a given HTTP status code and message.
18
+ *
19
+ * @param status HTTP status code
20
+ * @param message Human-readable error message
21
+ * @param code Machine-readable PascalCase error code (defaults to message-derived)
22
+ *
23
+ * The error code in responses is derived from the message:
24
+ * - Natural language: 'Not found' → 'NotFound'
25
+ * - Already PascalCase: 'QuotaExceeded' → 'QuotaExceeded' (preserved as-is)
26
+ *
27
+ * For full control over the error code, pass it explicitly:
28
+ * `defineError(429, 'Quota exceeded for this resource', 'QuotaExceeded')`
29
+ *
30
+ * @example
31
+ * // Explicit code — predictable, easy to test against:
32
+ * const NotPurchased = defineError(403, 'You can only review products you have purchased', 'NotPurchased');
33
+ * // → { error: 'NotPurchased', message: 'You can only review products you have purchased' }
34
+ */
35
+ export declare function defineError(status: number, message: string, code?: string): ErrorFactory;
36
+ /**
37
+ * Returns true only for FrameworkError values created by defineError factories.
38
+ * Plain objects that happen to have the right shape return false.
39
+ */
40
+ export declare function isFrameworkError(value: unknown): value is FrameworkError;
41
+ /** Standard error set shipped with Capix. */
42
+ export declare const defaultErrors: {
43
+ readonly BadRequest: ErrorFactory;
44
+ readonly Unauthorized: ErrorFactory;
45
+ readonly Forbidden: ErrorFactory;
46
+ readonly NotFound: ErrorFactory;
47
+ readonly Conflict: ErrorFactory;
48
+ readonly TooManyRequests: ErrorFactory;
49
+ readonly Internal: ErrorFactory;
50
+ readonly Timeout: ErrorFactory;
51
+ };
52
+ export {};
53
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAA,MAAM,qBAAqB,eAAiC,CAAC;AAE7D,qDAAqD;AACrD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,EAAE,IAAI,CAAC;CACxC,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,cAAc,CAAC;AAmB9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAYxF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAMxE;AAED,6CAA6C;AAC7C,eAAO,MAAM,aAAa;;;;;;;;;CAShB,CAAC"}
package/dist/errors.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * errors.ts — typed framework errors
3
+ * No dependencies. Foundation for everything else.
4
+ */
5
+ const FRAMEWORK_ERROR_BRAND = Symbol('capix.FrameworkError');
6
+ /**
7
+ * Derives a PascalCase error name from a human-readable message.
8
+ * - Natural language: 'Not found' → 'NotFound', 'Too many requests' → 'TooManyRequests'
9
+ * - Already PascalCase (no spaces, starts with uppercase): returned as-is
10
+ * e.g. 'QuotaExceeded' → 'QuotaExceeded'
11
+ */
12
+ function deriveErrorName(message) {
13
+ if (!message.includes(' ') && /^[A-Z]/.test(message)) {
14
+ return message;
15
+ }
16
+ return message
17
+ .split(/\s+/)
18
+ .filter(Boolean)
19
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
20
+ .join('');
21
+ }
22
+ /**
23
+ * Creates a typed error factory for a given HTTP status code and message.
24
+ *
25
+ * @param status HTTP status code
26
+ * @param message Human-readable error message
27
+ * @param code Machine-readable PascalCase error code (defaults to message-derived)
28
+ *
29
+ * The error code in responses is derived from the message:
30
+ * - Natural language: 'Not found' → 'NotFound'
31
+ * - Already PascalCase: 'QuotaExceeded' → 'QuotaExceeded' (preserved as-is)
32
+ *
33
+ * For full control over the error code, pass it explicitly:
34
+ * `defineError(429, 'Quota exceeded for this resource', 'QuotaExceeded')`
35
+ *
36
+ * @example
37
+ * // Explicit code — predictable, easy to test against:
38
+ * const NotPurchased = defineError(403, 'You can only review products you have purchased', 'NotPurchased');
39
+ * // → { error: 'NotPurchased', message: 'You can only review products you have purchased' }
40
+ */
41
+ export function defineError(status, message, code) {
42
+ const errorName = code ?? deriveErrorName(message);
43
+ return (meta) => {
44
+ const err = {
45
+ status,
46
+ error: errorName,
47
+ message,
48
+ [FRAMEWORK_ERROR_BRAND]: true,
49
+ ...(meta !== undefined ? { meta } : {}),
50
+ };
51
+ return err;
52
+ };
53
+ }
54
+ /**
55
+ * Returns true only for FrameworkError values created by defineError factories.
56
+ * Plain objects that happen to have the right shape return false.
57
+ */
58
+ export function isFrameworkError(value) {
59
+ return (typeof value === 'object' &&
60
+ value !== null &&
61
+ value[FRAMEWORK_ERROR_BRAND] === true);
62
+ }
63
+ /** Standard error set shipped with Capix. */
64
+ export const defaultErrors = {
65
+ BadRequest: defineError(400, 'Bad request', 'BadRequest'),
66
+ Unauthorized: defineError(401, 'Unauthorized', 'Unauthorized'),
67
+ Forbidden: defineError(403, 'Forbidden', 'Forbidden'),
68
+ NotFound: defineError(404, 'Not found', 'NotFound'),
69
+ Conflict: defineError(409, 'Conflict', 'Conflict'),
70
+ TooManyRequests: defineError(429, 'Too many requests', 'TooManyRequests'),
71
+ Internal: defineError(500, 'Internal server error', 'Internal'),
72
+ Timeout: defineError(504, 'Timeout', 'Timeout'),
73
+ };
74
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,qBAAqB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAc7D;;;;;GAKG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,OAAO;SACX,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SAClD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe,EAAE,IAAa;IACxE,MAAM,SAAS,GAAG,IAAI,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,CAAC,IAA8B,EAAkB,EAAE;QACxD,MAAM,GAAG,GAAmB;YAC1B,MAAM;YACN,KAAK,EAAE,SAAS;YAChB,OAAO;YACP,CAAC,qBAAqB,CAAC,EAAE,IAAI;YAC7B,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,qBAAqB,CAAC,KAAK,IAAI,CACnE,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,UAAU,EAAO,WAAW,CAAC,GAAG,EAAE,aAAa,EAAY,YAAY,CAAC;IACxE,YAAY,EAAK,WAAW,CAAC,GAAG,EAAE,cAAc,EAAW,cAAc,CAAC;IAC1E,SAAS,EAAQ,WAAW,CAAC,GAAG,EAAE,WAAW,EAAc,WAAW,CAAC;IACvE,QAAQ,EAAS,WAAW,CAAC,GAAG,EAAE,WAAW,EAAc,UAAU,CAAC;IACtE,QAAQ,EAAS,WAAW,CAAC,GAAG,EAAE,UAAU,EAAe,UAAU,CAAC;IACtE,eAAe,EAAE,WAAW,CAAC,GAAG,EAAE,mBAAmB,EAAM,iBAAiB,CAAC;IAC7E,QAAQ,EAAS,WAAW,CAAC,GAAG,EAAE,uBAAuB,EAAE,UAAU,CAAC;IACtE,OAAO,EAAU,WAAW,CAAC,GAAG,EAAE,SAAS,EAAgB,SAAS,CAAC;CAC7D,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * event-bus.ts — typed event bus for server-push notifications
3
+ *
4
+ * createEventBus<TEvents>() produces a pub/sub hub that any code can publish
5
+ * to, while wsTransport delivers those events to subscribed WS clients.
6
+ *
7
+ * Usage:
8
+ * const bus = createEventBus<{ 'order:paid': { orderId: string } }>();
9
+ * bus.publish('order:paid', { orderId: 'abc' }); // called from REST resolver
10
+ * // wsTransport wires subscribe/unsubscribe from WS client messages
11
+ */
12
+ export type EventMap = Record<string, unknown>;
13
+ export type SubscribeOptions<TData = unknown> = {
14
+ /** When provided, the handler is only called when this returns true. */
15
+ filter?: (data: TData) => boolean;
16
+ };
17
+ export type EventBus<TEvents extends EventMap> = {
18
+ /** Deliver an event to all clients currently subscribed to it. */
19
+ publish<K extends keyof TEvents & string>(event: K, data: TEvents[K]): void;
20
+ subscribe: {
21
+ /**
22
+ * Server-internal subscription (no client context).
23
+ * Not removed by unsubscribeAll — use the returned unsubscribe fn to clean up.
24
+ *
25
+ * @param handler - Called synchronously when the event is published.
26
+ * If the handler is async, the returned Promise is NOT awaited by `publish()`.
27
+ * This is intentional fire-and-forget behavior — suitable for webhooks and
28
+ * notifications where failures should be handled independently.
29
+ *
30
+ * Always wrap async subscriber logic in try/catch:
31
+ * ```ts
32
+ * eventBus.subscribe('order:completed', async (data) => {
33
+ * try {
34
+ * await sendWebhook(data);
35
+ * } catch (err) {
36
+ * logger.error('Webhook delivery failed', err);
37
+ * }
38
+ * });
39
+ * ```
40
+ */
41
+ <K extends keyof TEvents & string>(event: K, handler: (data: TEvents[K]) => void, options?: SubscribeOptions<TEvents[K]>): () => void;
42
+ /**
43
+ * Client subscription — removed when unsubscribeAll(clientId) is called on disconnect.
44
+ *
45
+ * @param handler - Called synchronously when the event is published.
46
+ * Async handlers are fire-and-forget — errors do not propagate to the publisher.
47
+ */
48
+ <K extends keyof TEvents & string>(clientId: string, event: K, handler: (data: TEvents[K]) => void, options?: SubscribeOptions<TEvents[K]>): () => void;
49
+ };
50
+ /** Remove every subscription held by a client (call on disconnect). */
51
+ unsubscribeAll(clientId: string): void;
52
+ /** Used internally by wsTransport to look up a handler for a specific client+event. */
53
+ _getHandler(clientId: string, event: string): ((data: unknown) => void) | undefined;
54
+ };
55
+ export declare function createEventBus<TEvents extends EventMap>(): EventBus<TEvents>;
56
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C,MAAM,MAAM,gBAAgB,CAAC,KAAK,GAAG,OAAO,IAAI;IAC9C,wEAAwE;IACxE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,OAAO,SAAS,QAAQ,IAAI;IAC/C,kEAAkE;IAClE,OAAO,CAAC,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAE5E,SAAS,EAAE;QACT;;;;;;;;;;;;;;;;;;;WAmBG;QACH,CAAC,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAC/B,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,EACnC,OAAO,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GACrC,MAAM,IAAI,CAAC;QACd;;;;;WAKG;QACH,CAAC,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,EAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,EACnC,OAAO,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GACrC,MAAM,IAAI,CAAC;KACf,CAAC;IAEF,uEAAuE;IACvE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvC,uFAAuF;IACvF,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACrF,CAAC;AAOF,wBAAgB,cAAc,CAAC,OAAO,SAAS,QAAQ,KAAK,QAAQ,CAAC,OAAO,CAAC,CAwD5E"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * event-bus.ts — typed event bus for server-push notifications
3
+ *
4
+ * createEventBus<TEvents>() produces a pub/sub hub that any code can publish
5
+ * to, while wsTransport delivers those events to subscribed WS clients.
6
+ *
7
+ * Usage:
8
+ * const bus = createEventBus<{ 'order:paid': { orderId: string } }>();
9
+ * bus.publish('order:paid', { orderId: 'abc' }); // called from REST resolver
10
+ * // wsTransport wires subscribe/unsubscribe from WS client messages
11
+ */
12
+ export function createEventBus() {
13
+ // Map<event, Map<clientId | Symbol, Sub>>
14
+ const listeners = new Map();
15
+ function subscribeImpl(clientId, event, handler, filter) {
16
+ if (!listeners.has(event))
17
+ listeners.set(event, new Map());
18
+ listeners.get(event).set(clientId, { handler, filter });
19
+ return () => {
20
+ listeners.get(event)?.delete(clientId);
21
+ };
22
+ }
23
+ return {
24
+ publish(event, data) {
25
+ const eventListeners = listeners.get(event);
26
+ if (!eventListeners)
27
+ return;
28
+ for (const sub of eventListeners.values()) {
29
+ if (!sub.filter || sub.filter(data)) {
30
+ sub.handler(data);
31
+ }
32
+ }
33
+ },
34
+ subscribe(clientIdOrEvent, eventOrHandler, handlerOrOptions, maybeOptions) {
35
+ if (typeof eventOrHandler === 'function') {
36
+ const opts = handlerOrOptions;
37
+ return subscribeImpl(Symbol(), clientIdOrEvent, eventOrHandler, opts?.filter);
38
+ }
39
+ return subscribeImpl(clientIdOrEvent, eventOrHandler, handlerOrOptions, maybeOptions?.filter);
40
+ },
41
+ unsubscribeAll(clientId) {
42
+ for (const eventListeners of listeners.values()) {
43
+ eventListeners.delete(clientId);
44
+ }
45
+ },
46
+ _getHandler(clientId, event) {
47
+ return listeners.get(event)?.get(clientId)?.handler;
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiEH,MAAM,UAAU,cAAc;IAC5B,0CAA0C;IAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqC,CAAC;IAE/D,SAAS,aAAa,CACpB,QAAyB,EACzB,KAAa,EACb,OAAgC,EAChC,MAAgD;QAEhD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACzD,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,CAAC,KAAa,EAAE,IAAa;YAClC,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC,cAAc;gBAAE,OAAO;YAC5B,KAAK,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC1C,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;QACH,CAAC;QAED,SAAS,CACP,eAAuB,EACvB,cAAkD,EAClD,gBAAwE,EACxE,YAAwC;YAExC,IAAI,OAAO,cAAc,KAAK,UAAU,EAAE,CAAC;gBACzC,MAAM,IAAI,GAAG,gBAAyD,CAAC;gBACvE,OAAO,aAAa,CAAC,MAAM,EAAE,EAAE,eAAe,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAChF,CAAC;YACD,OAAO,aAAa,CAClB,eAAe,EACf,cAAc,EACd,gBAA2C,EAC3C,YAAY,EAAE,MAAM,CACrB,CAAC;QACJ,CAAC;QAED,cAAc,CAAC,QAAgB;YAC7B,KAAK,MAAM,cAAc,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;gBAChD,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,WAAW,CAAC,QAAgB,EAAE,KAAa;YACzC,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QACtD,CAAC;KAC8B,CAAC;AACpC,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * execution-engine.ts — the heart of the framework
3
+ * Depends on: capability.ts, context.ts, guards.ts, errors.ts
4
+ */
5
+ import type { CapabilityRegistry } from './capability.js';
6
+ import type { ContextBuilder } from './context.js';
7
+ export type CapabilityRequest = {
8
+ readonly capability: string;
9
+ readonly input: unknown;
10
+ readonly headers: Record<string, string>;
11
+ readonly signal: AbortSignal;
12
+ /** Raw body bytes forwarded from the transport. See RawRequest.rawBody. */
13
+ readonly rawBody?: Buffer;
14
+ };
15
+ export type SerializedError = {
16
+ readonly status: number;
17
+ readonly error: string;
18
+ readonly message: string;
19
+ readonly meta?: Record<string, unknown>;
20
+ };
21
+ export type CapabilityResponse = {
22
+ readonly ok: true;
23
+ readonly data: unknown;
24
+ } | {
25
+ readonly ok: false;
26
+ readonly error: SerializedError;
27
+ };
28
+ export type InvokeFn = (req: CapabilityRequest) => Promise<CapabilityResponse>;
29
+ export type ExecutionEngineOptions = {
30
+ readonly registry: CapabilityRegistry;
31
+ readonly buildContext: ContextBuilder;
32
+ readonly isDevelopment?: boolean;
33
+ };
34
+ /**
35
+ * Creates the invoke function that runs the full capability pipeline:
36
+ * lookup → buildContext → guards → validate input → resolve → validate output
37
+ */
38
+ export declare function createExecutionEngine(options: ExecutionEngineOptions): InvokeFn;
39
+ //# sourceMappingURL=execution-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-engine.d.ts","sourceRoot":"","sources":["../src/execution-engine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAe,cAAc,EAAc,MAAM,cAAc,CAAC;AAI5E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC7C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAE/E,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC;AA8BF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,QAAQ,CA6K/E"}