@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Capix Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,329 @@
1
+ # capix
2
+
3
+ Core primitives for the Capix framework. Defines capabilities, context, guards, errors, enhancers, plugins, and the server.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @capixjs/core zod
9
+ ```
10
+
11
+ ## Quick example
12
+
13
+ ```ts
14
+ import { z } from 'zod';
15
+ import { capability, defineContext, defineGuard, defineError, createServer } from '@capixjs/core';
16
+ import { restTransport } from '@capixjs/transport-rest';
17
+
18
+ // 1. Define what your server knows about each request
19
+ const buildContext = defineContext(async (req) => ({
20
+ requestId: crypto.randomUUID(),
21
+ user: req.headers.authorization === 'Bearer admin'
22
+ ? { id: '1', role: 'admin' as const }
23
+ : null,
24
+ }));
25
+
26
+ // 2. Define guards — preconditions that run before a capability
27
+ const Errors = { Unauthorized: defineError(401, 'Unauthorized') };
28
+ const mustBeUser = defineGuard((ctx) => {
29
+ if (!ctx.user) throw Errors.Unauthorized();
30
+ });
31
+
32
+ // 3. Define capabilities — typed pure functions
33
+ const cap = capability.withContext<Awaited<ReturnType<typeof buildContext>>>();
34
+ const getUser = cap(
35
+ z.object({ id: z.string() }),
36
+ async ({ id }) => ({ id, name: 'Alice' }),
37
+ 'query',
38
+ ).guard(mustBeUser);
39
+
40
+ // 4. Create the server
41
+ createServer({
42
+ context: buildContext,
43
+ capabilities: { users: { getUser } },
44
+ transports: [restTransport({ port: 3000 })],
45
+ }).start();
46
+ // → GET /users/:id
47
+ ```
48
+
49
+ ## Capability
50
+
51
+ A capability wraps a resolver function with optional input/output schemas:
52
+
53
+ ```ts
54
+ // No schema — no input validation
55
+ const ping = capability(() => 'pong');
56
+
57
+ // With input schema — validates and infers types
58
+ const greet = capability(
59
+ z.object({ name: z.string() }),
60
+ ({ name }) => `Hello, ${name}`,
61
+ );
62
+
63
+ // With output schema — validates resolver return value in development
64
+ const getMetrics = capability(
65
+ z.object({ window: z.enum(['1h', '24h']) }),
66
+ fetchMetrics,
67
+ ).output(MetricsSchema);
68
+
69
+ // With explicit intent (overrides name-based inference)
70
+ const searchUsers = capability(
71
+ z.object({ q: z.string() }),
72
+ ({ q }) => db.users.search(q),
73
+ 'query',
74
+ );
75
+ ```
76
+
77
+ Capabilities are **immutable**. `.guard()`, `.enhance()`, and `.output()` each return a new instance:
78
+
79
+ ```ts
80
+ const adminOnly = greet.guard(mustBeAdmin);
81
+ const cached = getMetrics.enhance(withCache(60));
82
+ ```
83
+
84
+ ### Context-typed factory
85
+
86
+ Use `capability.withContext<TContext>()` to bind the factory to your application context type. Define it once, import everywhere:
87
+
88
+ ```ts
89
+ // src/capabilities.ts
90
+ import { capability } from '@capixjs/core';
91
+ import type { AppContext } from './context.js';
92
+
93
+ export const cap = capability.withContext<AppContext>();
94
+ ```
95
+
96
+ For capabilities that require a logged-in user, define a second factory with a narrowed context:
97
+
98
+ ```ts
99
+ type AuthContext = AppContext & { user: NonNullable<AppContext['user']> };
100
+
101
+ export const cap = capability.withContext<AppContext>(); // public endpoints
102
+ export const authCap = capability.withContext<AuthContext>(); // authenticated endpoints
103
+ ```
104
+
105
+ Without `withContext`, `ctx` is typed as `BaseContext` (only `requestId`).
106
+
107
+ ### Internal composition with `.resolve()`
108
+
109
+ Call one capability from inside another. Guards re-run — this is intentional and safe:
110
+
111
+ ```ts
112
+ export const getPost = cap(z.object({ id: z.string() }), async ({ id }) => {
113
+ const post = await db.posts.find(id);
114
+ if (!post) throw Errors.NotFound();
115
+ return post;
116
+ }, 'query').guard(mustBeUser);
117
+
118
+ export const updatePost = cap(z.object({ id: z.string(), title: z.string() }), async ({ id, title }, ctx) => {
119
+ const post = await getPost.resolve({ id }, ctx); // guards re-run
120
+ return db.posts.update(id, { title });
121
+ }, 'update').guard(mustBeUser);
122
+ ```
123
+
124
+ ## Guards
125
+
126
+ Guards run before the resolver and throw to reject the request:
127
+
128
+ ```ts
129
+ import { defineGuard, defineError } from '@capixjs/core';
130
+
131
+ const Errors = { Forbidden: defineError(403, 'Forbidden') };
132
+
133
+ const mustBeAdmin = defineGuard((ctx) => {
134
+ if (ctx.user?.role !== 'admin') throw Errors.Forbidden();
135
+ });
136
+
137
+ // Multiple guards run in order; first failure stops execution
138
+ const adminCap = capability(schema, handler)
139
+ .guard(mustBeLoggedIn)
140
+ .guard(mustBeAdmin);
141
+ ```
142
+
143
+ **Input guards** run after input validation and receive both `(input, ctx)`:
144
+
145
+ ```ts
146
+ import { defineInputGuard } from '@capixjs/core';
147
+
148
+ const mustOwnResource = defineInputGuard((input: { id: string }, ctx) => {
149
+ if (input.id !== ctx.user?.id) throw Errors.Forbidden();
150
+ });
151
+ ```
152
+
153
+ ## Errors
154
+
155
+ ```ts
156
+ import { defineError, defaultErrors } from '@capixjs/core';
157
+
158
+ // Define application-specific errors
159
+ const Errors = {
160
+ NotFound: defineError(404, 'Not found'),
161
+ OutOfStock: defineError(409, 'Out of stock'),
162
+ // Explicit code — predictable, easy to test:
163
+ NotPurchased: defineError(403, 'You can only review products you have purchased', 'NotPurchased'),
164
+ };
165
+
166
+ // Use built-in errors
167
+ throw defaultErrors.Unauthorized();
168
+ throw defaultErrors.NotFound({ detail: 'User not found' });
169
+
170
+ // Use custom errors
171
+ throw Errors.OutOfStock({ productId: '123' });
172
+ ```
173
+
174
+ The error code in responses is derived from the message (`'Not found'` → `'NotFound'`) unless you pass a third argument. Errors are serialized as:
175
+
176
+ ```json
177
+ { "error": "NotFound", "message": "Not found" }
178
+ ```
179
+
180
+ with the given HTTP status code.
181
+
182
+ ## Enhancers
183
+
184
+ Enhancers wrap the resolver for cross-cutting concerns:
185
+
186
+ ```ts
187
+ import { withCache, withRateLimit, withCircuitBreaker, withTimeout, withRetry, withMetrics } from '@capixjs/core';
188
+
189
+ const robustCap = capability(schema, handler)
190
+ .enhance(withCache(30))
191
+ .enhance(withRateLimit({ max: 100, windowMs: 60_000 }))
192
+ .enhance(withCircuitBreaker({ failureThreshold: 5, successThreshold: 2, timeoutMs: 30_000 }))
193
+ .enhance(withTimeout(5000))
194
+ .enhance(withRetry(3, 200));
195
+ ```
196
+
197
+ | Enhancer | Options | Description |
198
+ |---|---|---|
199
+ | `withCache(ttlSeconds)` | `number` | In-memory cache keyed by capability name + serialized input |
200
+ | `withRateLimit(opts)` | `{ max, windowMs }` | Sliding window rate limiter |
201
+ | `withCircuitBreaker(opts)` | `{ failureThreshold, successThreshold, timeoutMs }` | Opens after N failures; resets after timeout |
202
+ | `withTimeout(ms)` | `number` | Rejects after N milliseconds with `504 Timeout` |
203
+ | `withRetry(n, delayMs?)` | `number, number` | Retries on non-FrameworkError failures with backoff |
204
+ | `withRollback` | — | Enables `ctx.onRollback(fn)` for compensating failed multi-step mutations |
205
+ | `withMetrics(collector)` | `MetricsCollector` | Emits duration and success/error metrics |
206
+ | `withLogging` | — | Logs capability name, duration, and outcome |
207
+
208
+ ## Context
209
+
210
+ Context is built once per request and passed to every guard and resolver:
211
+
212
+ ```ts
213
+ import { defineContext } from '@capixjs/core';
214
+
215
+ const buildContext = defineContext(async (req) => ({
216
+ requestId: crypto.randomUUID(),
217
+ user: await verifyToken(req.headers.authorization),
218
+ db,
219
+ }));
220
+ ```
221
+
222
+ The `req` argument is `{ headers: Record<string, string | string[] | undefined> }`. Use `getHeader(req, 'authorization')` for safe string access.
223
+
224
+ ## Plugins
225
+
226
+ Plugins encapsulate capabilities and context extensions:
227
+
228
+ ```ts
229
+ import { definePlugin } from '@capixjs/core';
230
+
231
+ const tenantPlugin = definePlugin({
232
+ capabilities: { users: { getUser, createUser } },
233
+ context: (base) => ({ ...base, tenantId: 'default' }),
234
+ });
235
+
236
+ createServer({
237
+ plugins: [tenantPlugin, loggingPlugin],
238
+ transports: [restTransport({ port: 3000 })],
239
+ }).start();
240
+ ```
241
+
242
+ ## Event bus
243
+
244
+ Typed pub/sub for broadcasting events from REST capabilities to WebSocket clients:
245
+
246
+ ```ts
247
+ import { createEventBus } from '@capixjs/core';
248
+
249
+ type AppEvents = {
250
+ 'order:paid': { orderId: string; amount: number };
251
+ 'task:updated': { id: string; status: string };
252
+ };
253
+
254
+ export const eventBus = createEventBus<AppEvents>();
255
+
256
+ // Publish from any capability or anywhere in your app
257
+ eventBus.publish('order:paid', { orderId: '123', amount: 99 });
258
+
259
+ // Subscribe server-side
260
+ const unsub = eventBus.subscribe('order:paid', (data) => {
261
+ console.log('Order paid:', data.orderId);
262
+ });
263
+ unsub(); // unsubscribe
264
+ ```
265
+
266
+ Pass `eventBus` to `wsTransport({ eventBus })` to forward published events to subscribed WS clients.
267
+
268
+ ## Exports
269
+
270
+ ### Factories
271
+
272
+ | Export | Description |
273
+ |---|---|
274
+ | `capability()` | Create a capability |
275
+ | `capability.withContext<T>()` | Create a scoped factory with a pre-bound context type |
276
+ | `defineContext(fn)` | Define a context builder |
277
+ | `defineGuard(fn)` | Define a guard |
278
+ | `defineGuardFor<T>()` | Define a narrowing guard (asserts ctx is T) |
279
+ | `defineInputGuard(fn)` | Define an input guard (runs after validation, receives input + ctx) |
280
+ | `defineError(status, message, code?)` | Define a typed error factory |
281
+ | `defineEnhancer(fn)` | Define a capability enhancer |
282
+ | `definePlugin(plugin)` | Define a plugin |
283
+ | `defineConfig(config)` | Define server config with type inference |
284
+ | `createServer(config)` | Create a server |
285
+ | `createEventBus<TEvents>()` | Create a typed event bus |
286
+
287
+ ### Built-in enhancers
288
+
289
+ | Export | Description |
290
+ |---|---|
291
+ | `withCache(ttlSeconds)` | In-memory output cache |
292
+ | `withRateLimit(options)` | Sliding window rate limiter |
293
+ | `withCircuitBreaker(options)` | Circuit breaker (closed / open / half-open) |
294
+ | `withTimeout(ms)` | Cancel after N milliseconds |
295
+ | `withRetry(maxAttempts, delayMs?)` | Retry on non-framework failures with backoff |
296
+ | `withRollback` | Compensation registry for multi-step mutations |
297
+ | `withMetrics(collector)` | Record timing and outcome metrics |
298
+ | `withLogging` | Log capability name, duration, and outcome |
299
+ | `consoleMetricsCollector` | Default `MetricsCollector` that logs to console |
300
+
301
+ ### Utilities
302
+
303
+ | Export | Description |
304
+ |---|---|
305
+ | `isCapability(v)` | Type guard for capabilities |
306
+ | `isFrameworkError(v)` | Type guard for FrameworkErrors |
307
+ | `inferIntent(key)` | Infer capability intent from a key name |
308
+ | `compileRegistry(capabilities)` | Compile a group tree into a flat registry |
309
+ | `getHeader(req, name)` | Safe header access from `RawRequest` |
310
+ | `defaultErrors` | Built-in error factories: `BadRequest`, `Unauthorized`, `Forbidden`, `NotFound`, `Conflict`, `TooManyRequests`, `Internal`, `Timeout` |
311
+ | `runGuards` / `runInputGuards` | Run guard arrays (used by custom transports) |
312
+ | `createExecutionEngine(options)` | Create a capability execution engine |
313
+ | `mergePlugins(plugins)` | Merge a plugin array into a single plugin |
314
+
315
+ ### Types
316
+
317
+ `Capability<TInput, TOutput, TContext>`, `AnyCapability`, `BaseContext`, `ContextBuilder`,
318
+ `RawRequest`, `Guard`, `NarrowingGuard`, `InputGuard`, `AnyGuard`, `NarrowContext`,
319
+ `Enhancer`, `Resolver`, `Plugin`, `MergedPlugins`, `Intent`, `InferInput<Cap>`,
320
+ `InferOutput<Cap>`, `InferContext<Cap>`, `GroupTree`, `CapabilityRegistry`,
321
+ `ScopedCapabilityFactory`, `FrameworkError`, `ErrorFactory`, `WithRollback<T>`,
322
+ `MetricsCollector`, `CircuitBreakerOptions`, `RateLimitOptions`, `Transport`,
323
+ `TransportWithCapabilities`, `MountOptions`, `ServerConfig`, `Server`,
324
+ `EventBus<TEvents>`, `EventMap`, `SubscribeOptions`, `InvokeFn`, `CapabilityRequest`,
325
+ `CapabilityResponse`
326
+
327
+ ## License
328
+
329
+ MIT
@@ -0,0 +1,165 @@
1
+ /**
2
+ * capability.ts — the core capability primitive
3
+ * Depends on: errors.ts, context.ts, guards.ts, zod
4
+ */
5
+ import type { ZodSchema, ZodTypeAny } from 'zod';
6
+ import type { BaseContext } from './context.js';
7
+ import type { AnyGuard, AnyInputGuard, InputGuard, NarrowContext } from './guards.js';
8
+ declare const CAPABILITY_BRAND: unique symbol;
9
+ export type Intent = 'query' | 'mutation' | 'update' | 'replace' | 'delete';
10
+ /** Infers capability intent from its key name in the parent group. */
11
+ export declare function inferIntent(key: string): Intent;
12
+ export type Enhancer = <TInput, TOutput, TContext extends BaseContext>(cap: Capability<TInput, TOutput, TContext>) => Capability<TInput, TOutput, TContext>;
13
+ export type Resolver<TInput, TOutput, TContext extends BaseContext> = (input: TInput, ctx: TContext) => TOutput | Promise<TOutput>;
14
+ export type Capability<TInput, TOutput, TContext extends BaseContext> = {
15
+ readonly _capix: true;
16
+ readonly [CAPABILITY_BRAND]: true;
17
+ readonly name: string;
18
+ readonly _input: TInput;
19
+ readonly _output: TOutput;
20
+ readonly _context: TContext;
21
+ readonly inputSchema: ZodTypeAny | null;
22
+ readonly outputSchema: ZodTypeAny | null;
23
+ readonly guards: ReadonlyArray<AnyGuard>;
24
+ readonly inputGuards: ReadonlyArray<AnyInputGuard>;
25
+ readonly intent: Intent;
26
+ /** True when intent was explicitly passed to capability(); false when defaulted. */
27
+ readonly _intentExplicit: boolean;
28
+ /**
29
+ * Set by compileRegistry when inputSchema is a z.object({}) with no keys.
30
+ * The execution engine skips input validation entirely — there is nothing to validate.
31
+ */
32
+ readonly _skipValidation: boolean;
33
+ /**
34
+ * Invoke this capability's guards and resolver directly.
35
+ *
36
+ * Guards always re-run — this is safe to call from any context.
37
+ * If the context does not satisfy the guards, they throw as normal.
38
+ *
39
+ * TypeScript does not verify at the call site that the provided context
40
+ * satisfies this capability's guard requirements — guards enforce this
41
+ * at runtime. This enables capability composition without escape hatches:
42
+ *
43
+ * ```ts
44
+ * const getDashboard = cap(z.object({}), async (_, ctx) => {
45
+ * const [log, projects] = await Promise.all([
46
+ * listAuditLog.resolve({ limit: 10 }, ctx), // mustBeAdmin runs — throws if not admin
47
+ * listProjects.resolve({ limit: 5 }, ctx), // mustBeAuthenticated runs
48
+ * ]);
49
+ * return { log, projects };
50
+ * }, 'query').guard(mustBeAuthenticated);
51
+ * ```
52
+ *
53
+ * For HTTP/GraphQL/queue invocation, the execution engine uses _resolverOnly
54
+ * after running guards itself — guards run exactly once per external request.
55
+ */
56
+ resolve: (input: TInput, ctx: BaseContext) => Promise<TOutput>;
57
+ /**
58
+ * Raw resolver — no guards. Used by the execution engine after it has
59
+ * already run guards. Also used internally by enhancers wrapping the resolver.
60
+ * Do not call this from application code — use resolve() instead.
61
+ */
62
+ _resolverOnly: (input: TInput, ctx: TContext) => Promise<TOutput>;
63
+ /**
64
+ * Adds a guard to this capability.
65
+ *
66
+ * The guard must accept the capability's current TContext (or a supertype of it).
67
+ * Function-parameter contravariance means a guard typed for a broader context
68
+ * (e.g. BaseContext) is always assignable, while a guard typed for a more specific
69
+ * context (e.g. AppContext) requires TContext to already be at least that specific.
70
+ * Use `capability.withContext<AppContext>()` to start with TContext = AppContext so
71
+ * all AppContext guards are accepted without explicit resolver annotation.
72
+ */
73
+ guard<G extends (ctx: TContext) => any>(g: G): Capability<TInput, TOutput, NarrowContext<TContext, G>>;
74
+ /**
75
+ * Adds an input guard that receives both validated input and context.
76
+ * Runs after input validation, before the resolver.
77
+ * Use for resource ownership checks and other input-dependent access control.
78
+ */
79
+ inputGuard(g: InputGuard<TInput, TContext>): Capability<TInput, TOutput, TContext>;
80
+ enhance(e: Enhancer): Capability<TInput, TOutput, TContext>;
81
+ output<O>(schema: ZodSchema<O>): Capability<TInput, O, TContext>;
82
+ };
83
+ export type AnyCapability = Capability<any, any, any>;
84
+ /**
85
+ * Defines a capability — the unit of server-side logic in Capix.
86
+ *
87
+ * Wraps a resolver function with optional input validation (Zod schema), guards,
88
+ * and enhancers. Capabilities are the building blocks registered in createServer().
89
+ *
90
+ * @example No-input capability
91
+ * const ping = capability(() => ({ pong: true }));
92
+ *
93
+ * @example With Zod input schema
94
+ * const getUser = capability(
95
+ * z.object({ id: z.string() }),
96
+ * async (input, ctx) => db.users.find(input.id),
97
+ * );
98
+ *
99
+ * @example With explicit intent (overrides key-based inference in inferRoutes)
100
+ * const deletePost = capability(
101
+ * z.object({ id: z.string() }),
102
+ * async (input, ctx) => db.posts.delete(input.id),
103
+ * 'delete',
104
+ * );
105
+ *
106
+ * @example Adding guards and enhancers
107
+ * const createPost = capability(schema, resolver)
108
+ * .guard(mustBeAuthenticated)
109
+ * .enhance(withTimeout(5000));
110
+ *
111
+ * Use {@link capability.withContext} to pre-bind the context type when all
112
+ * capabilities in a module share the same application context.
113
+ */
114
+ /** No-input capability. */
115
+ export declare function capability<TOutput, TContext extends BaseContext = BaseContext>(resolver: (input: undefined, ctx: TContext) => TOutput | Promise<TOutput>): Capability<undefined, TOutput, TContext>;
116
+ /** No-input capability with explicit intent. */
117
+ export declare function capability<TOutput, TContext extends BaseContext = BaseContext>(resolver: (input: undefined, ctx: TContext) => TOutput | Promise<TOutput>, intent: Intent): Capability<undefined, TOutput, TContext>;
118
+ /** With typed input schema. */
119
+ export declare function capability<TSchema extends ZodTypeAny, TOutput, TContext extends BaseContext = BaseContext>(schema: TSchema, resolver: Resolver<TSchema['_output'], TOutput, TContext>): Capability<TSchema['_output'], TOutput, TContext>;
120
+ /** With typed input schema and explicit intent. */
121
+ export declare function capability<TSchema extends ZodTypeAny, TOutput, TContext extends BaseContext = BaseContext>(schema: TSchema, resolver: Resolver<TSchema['_output'], TOutput, TContext>, intent: Intent): Capability<TSchema['_output'], TOutput, TContext>;
122
+ /** Extract the validated input type from a Capability. */
123
+ export type InferInput<TCap extends AnyCapability> = TCap['_input'];
124
+ /** Extract the resolved output type from a Capability. */
125
+ export type InferOutput<TCap extends AnyCapability> = TCap['_output'];
126
+ /** Extract the required context type from a Capability (reflects guard narrowing). */
127
+ export type InferContext<TCap extends AnyCapability> = TCap['_context'];
128
+ /** Returns true for values created by capability(). Plain objects with _capix: true return false. */
129
+ export declare function isCapability(value: unknown): value is AnyCapability;
130
+ export type CapabilityRegistry = ReadonlyMap<string, AnyCapability>;
131
+ /** Interface to allow recursive references (type aliases can't be circular). */
132
+ export interface GroupTree {
133
+ [key: string]: AnyCapability | GroupTree;
134
+ }
135
+ export declare function compileRegistry(tree: GroupTree, prefix?: string): CapabilityRegistry;
136
+ /**
137
+ * The type returned by `capability.withContext<TContext>()`.
138
+ * Identical to the `capability()` overloads but with TContext fixed.
139
+ */
140
+ export type ScopedCapabilityFactory<TContext extends BaseContext> = {
141
+ <TOutput>(resolver: (input: undefined, ctx: TContext) => TOutput | Promise<TOutput>): Capability<undefined, TOutput, TContext>;
142
+ <TOutput>(resolver: (input: undefined, ctx: TContext) => TOutput | Promise<TOutput>, intent: Intent): Capability<undefined, TOutput, TContext>;
143
+ <TSchema extends ZodTypeAny, TOutput>(schema: TSchema, resolver: Resolver<TSchema['_output'], TOutput, TContext>): Capability<TSchema['_output'], TOutput, TContext>;
144
+ <TSchema extends ZodTypeAny, TOutput>(schema: TSchema, resolver: Resolver<TSchema['_output'], TOutput, TContext>, intent: Intent): Capability<TSchema['_output'], TOutput, TContext>;
145
+ };
146
+ export declare namespace capability {
147
+ /**
148
+ * Returns a `capability()` factory with TContext pre-bound.
149
+ * The resolver's `ctx` parameter is inferred as TContext without annotation,
150
+ * and guards typed for TContext are accepted without the `any` escape hatch.
151
+ *
152
+ * @example
153
+ * const appCap = capability.withContext<AppContext>();
154
+ * const getUser = appCap(
155
+ * z.object({ id: z.string() }),
156
+ * async (input, ctx) => { // ctx: AppContext — no annotation needed
157
+ * if (!ctx.user) throw Errors.Unauthorized();
158
+ * return db.users.find(input.id);
159
+ * },
160
+ * ).guard(mustBeAuthenticated);
161
+ */
162
+ function withContext<TContext extends BaseContext>(): ScopedCapabilityFactory<TContext>;
163
+ }
164
+ export {};
165
+ //# sourceMappingURL=capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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"}