@spfn/core 0.3.0-beta.20 → 0.3.0-beta.21

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.
@@ -2,7 +2,7 @@ import { Context, MiddlewareHandler } from 'hono';
2
2
  import { a as NamedMiddleware } from './define-middleware-CVKgqo8S.js';
3
3
  import { TSchema, Static } from '@sinclair/typebox';
4
4
  import { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';
5
- import { HttpMethod } from './route/types.js';
5
+ import { HttpMethod, RouteEffect } from './route/types.js';
6
6
 
7
7
  /**
8
8
  * Route Input Types
@@ -296,6 +296,13 @@ type RouteDef<TInput extends RouteInput = RouteInput, TInterceptor extends Route
296
296
  * the compatibility gate read it.
297
297
  */
298
298
  contract?: RouteContract;
299
+ /**
300
+ * What invoking this route does. Read by a command surface, which
301
+ * announces it in its manifest; codegen and the contract ignore it.
302
+ */
303
+ effect?: RouteEffect;
304
+ /** One line a command surface announces beside the route; codegen and the contract ignore it. */
305
+ summary?: string;
299
306
  handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;
300
307
  _input: TInput;
301
308
  _interceptor: TInterceptor;
@@ -312,6 +319,8 @@ declare class RouteBuilder<TInput extends RouteInput = {}, TInterceptor extends
312
319
  _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];
313
320
  _skipMiddlewares?: string[] | '*';
314
321
  _contract?: RouteContract;
322
+ _effect?: RouteEffect;
323
+ _summary?: string;
315
324
  /**
316
325
  * Create a new RouteBuilder with copied properties and optional overrides
317
326
  */
@@ -472,6 +481,45 @@ declare class RouteBuilder<TInput extends RouteInput = {}, TInterceptor extends
472
481
  * ```
473
482
  */
474
483
  contract(contract: RouteContract): RouteBuilder<TInput, TInterceptor, TResponse>;
484
+ /**
485
+ * Declare what invoking this route does
486
+ *
487
+ * Read by a command surface (`@spfn/core/surface`, `@spfn/core/ops`),
488
+ * which announces it in its manifest so a machine caller can tell a read
489
+ * from a write before it calls — `spfn ops call` asks for `--yes` before a
490
+ * `destructive` command. A surface built with `defineSurface` refuses an
491
+ * app command that does not declare one; on `/_ops` it is optional.
492
+ *
493
+ * Nothing else reads it: codegen, the route map, and the contract are
494
+ * unaffected, and a route outside any surface simply carries it.
495
+ *
496
+ * @example
497
+ * ```ts
498
+ * export const pageAdd = app.route.post('/pages')
499
+ * .input({ body: PageAddBody })
500
+ * .effect('write')
501
+ * .summary('Add a page to the blueprint')
502
+ * .handler(async (c) => { ... });
503
+ * ```
504
+ */
505
+ effect(effect: RouteEffect): RouteBuilder<TInput, TInterceptor, TResponse>;
506
+ /**
507
+ * Describe this route in one line for a command surface's manifest
508
+ *
509
+ * A surface announces it beside the command in `spfn ops list` and
510
+ * `--describe`, and refuses one that is empty or longer than 500
511
+ * characters. Codegen, the route map, and the contract ignore it. On a
512
+ * module command the module's own summary wins.
513
+ *
514
+ * @example
515
+ * ```ts
516
+ * export const pageAdd = app.route.post('/pages')
517
+ * .effect('write')
518
+ * .summary('Add a page to the blueprint')
519
+ * .handler(async (c) => { ... });
520
+ * ```
521
+ */
522
+ summary(summary: string): RouteBuilder<TInput, TInterceptor, TResponse>;
475
523
  /**
476
524
  * Define handler function
477
525
  *
@@ -1,5 +1,5 @@
1
1
  import { a as NamedMiddleware } from './define-middleware-CVKgqo8S.js';
2
- import { R as RouteDef } from './route-builder-BHKgGkwI.js';
2
+ import { R as RouteDef } from './route-builder-DRW-MSCJ.js';
3
3
 
4
4
  /**
5
5
  * Router Definition
@@ -12,7 +12,7 @@ import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WS
12
12
  import { DatabaseProvider, MigrationStatus, MigrationStatusDb } from '@spfn/core/db';
13
13
  import * as _sinclair_typebox from '@sinclair/typebox';
14
14
  import { Static } from '@sinclair/typebox';
15
- import { R as RouteDef } from '../route-builder-BHKgGkwI.js';
15
+ import { R as RouteDef } from '../route-builder-DRW-MSCJ.js';
16
16
  import { a as EnvRegistry } from '../registry-C2KQCh0m.js';
17
17
  import 'pg-boss';
18
18
  import '../token-manager-Ckjt2-qq.js';
@@ -1520,6 +1520,8 @@ var RouteBuilder = class _RouteBuilder {
1520
1520
  _middlewares;
1521
1521
  _skipMiddlewares;
1522
1522
  _contract;
1523
+ _effect;
1524
+ _summary;
1523
1525
  /**
1524
1526
  * Create a new RouteBuilder with copied properties and optional overrides
1525
1527
  */
@@ -1532,6 +1534,8 @@ var RouteBuilder = class _RouteBuilder {
1532
1534
  builder._middlewares = overrides?.middlewares ?? this._middlewares;
1533
1535
  builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;
1534
1536
  builder._contract = overrides?.contract ?? this._contract;
1537
+ builder._effect = overrides?.effect ?? this._effect;
1538
+ builder._summary = overrides?.summary ?? this._summary;
1535
1539
  return builder;
1536
1540
  }
1537
1541
  /**
@@ -1702,6 +1706,49 @@ var RouteBuilder = class _RouteBuilder {
1702
1706
  contract(contract) {
1703
1707
  return this.clone({ contract });
1704
1708
  }
1709
+ /**
1710
+ * Declare what invoking this route does
1711
+ *
1712
+ * Read by a command surface (`@spfn/core/surface`, `@spfn/core/ops`),
1713
+ * which announces it in its manifest so a machine caller can tell a read
1714
+ * from a write before it calls — `spfn ops call` asks for `--yes` before a
1715
+ * `destructive` command. A surface built with `defineSurface` refuses an
1716
+ * app command that does not declare one; on `/_ops` it is optional.
1717
+ *
1718
+ * Nothing else reads it: codegen, the route map, and the contract are
1719
+ * unaffected, and a route outside any surface simply carries it.
1720
+ *
1721
+ * @example
1722
+ * ```ts
1723
+ * export const pageAdd = app.route.post('/pages')
1724
+ * .input({ body: PageAddBody })
1725
+ * .effect('write')
1726
+ * .summary('Add a page to the blueprint')
1727
+ * .handler(async (c) => { ... });
1728
+ * ```
1729
+ */
1730
+ effect(effect) {
1731
+ return this.clone({ effect });
1732
+ }
1733
+ /**
1734
+ * Describe this route in one line for a command surface's manifest
1735
+ *
1736
+ * A surface announces it beside the command in `spfn ops list` and
1737
+ * `--describe`, and refuses one that is empty or longer than 500
1738
+ * characters. Codegen, the route map, and the contract ignore it. On a
1739
+ * module command the module's own summary wins.
1740
+ *
1741
+ * @example
1742
+ * ```ts
1743
+ * export const pageAdd = app.route.post('/pages')
1744
+ * .effect('write')
1745
+ * .summary('Add a page to the blueprint')
1746
+ * .handler(async (c) => { ... });
1747
+ * ```
1748
+ */
1749
+ summary(summary) {
1750
+ return this.clone({ summary });
1751
+ }
1705
1752
  /**
1706
1753
  * Define handler function
1707
1754
  *
@@ -1757,6 +1804,8 @@ var RouteBuilder = class _RouteBuilder {
1757
1804
  middlewares: this._middlewares,
1758
1805
  skipMiddlewares: this._skipMiddlewares,
1759
1806
  contract: this._contract,
1807
+ ...this._effect !== void 0 ? { effect: this._effect } : {},
1808
+ ...this._summary !== void 0 ? { summary: this._summary } : {},
1760
1809
  handler: fn,
1761
1810
  _input: {},
1762
1811
  _interceptor: {},