@stone-js/resources 0.8.8 → 0.8.10

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.
@@ -0,0 +1,18 @@
1
+ import { BlueprintContext, ClassType, IBlueprint, NextMiddleware, type MetaMiddleware } from '@stone-js/core';
2
+ /**
3
+ * Build-phase middleware: collect every class registered with `@ApiResource` into the registry.
4
+ *
5
+ * The same scan the router does for its route definitions, applied to this module's own key. After it
6
+ * runs, `stone.resources.registry` maps each alias to its class, so a route or a handler can name a
7
+ * resource instead of importing it, and `@stone-js/openapi` can walk the registry to publish response
8
+ * shapes without loading anything itself.
9
+ *
10
+ * @param context - The blueprint context.
11
+ * @param next - The next blueprint middleware.
12
+ * @returns The blueprint.
13
+ */
14
+ export declare function ApiResourceMiddleware(context: BlueprintContext<IBlueprint, ClassType>, next: NextMiddleware<BlueprintContext<IBlueprint, ClassType>, IBlueprint>): Promise<IBlueprint>;
15
+ /**
16
+ * Meta blueprint middleware for resource discovery.
17
+ */
18
+ export declare const MetaApiResourceMiddleware: MetaMiddleware<any, any>;
@@ -0,0 +1,106 @@
1
+ import { IResource } from '../declarations.js';
2
+ import { IBlueprint, IContainer, IncomingEvent, NextMiddleware, OutgoingResponse, type MetaMiddleware } from '@stone-js/core';
3
+ /**
4
+ * The shape a route's `resource` option may take: the resource itself, a class to resolve, or the
5
+ * name of one registered under `stone.resources.registry`.
6
+ */
7
+ export type RouteResource = IResource<any, any> | string;
8
+ /**
9
+ * Route middleware: shapes what a route returns, after its handler ran.
10
+ *
11
+ * A route says what it exposes, once, where the route is defined:
12
+ *
13
+ * ```ts
14
+ * @Get('/users/:id', { resource: UserResource })
15
+ * ```
16
+ *
17
+ * The handler returns its domain model, whole, and this applies the resource on the way out. That is
18
+ * the point: a service should not have to know which fields are public, and a handler should not have
19
+ * to remember to strip them.
20
+ */
21
+ export declare class ResourceRouteMiddleware {
22
+ private readonly blueprint;
23
+ private readonly container?;
24
+ /**
25
+ * @param dependencies - Auto-wired container services.
26
+ */
27
+ constructor({ blueprint, container }: {
28
+ blueprint: IBlueprint;
29
+ container?: IContainer;
30
+ });
31
+ /**
32
+ * Run the handler, then shape what it returned.
33
+ *
34
+ * It handles both of the things a handler may hand back, which is the part that used to be wrong. A
35
+ * handler carrying a response decorator (`@JsonHttpResponse(201)`) has already been turned into a
36
+ * response by the time any route middleware runs, because that decorator wraps the method itself.
37
+ * Projecting the response object produced an empty payload and dropped the status with it. So a
38
+ * response is now projected **through its content**, in place: the payload is shaped and the status,
39
+ * the headers and everything else the handler chose are left exactly as they were.
40
+ *
41
+ * @param event - The incoming event.
42
+ * @param next - The next middleware.
43
+ * @returns The shaped output, or the untouched result when the route declares no resource.
44
+ */
45
+ handle(event: IncomingEvent, next: NextMiddleware<IncomingEvent, OutgoingResponse>): Promise<OutgoingResponse>;
46
+ /**
47
+ * Project a value, whether it is one model or many.
48
+ *
49
+ * @param resource - The resource to apply.
50
+ * @param value - The value the handler produced.
51
+ * @param context - The resource context.
52
+ * @returns The projected value.
53
+ */
54
+ private shape;
55
+ /**
56
+ * Whether a value is a response carrying a payload this can replace.
57
+ *
58
+ * Duck-typed: the kernel is agnostic, and each platform has its own response type.
59
+ *
60
+ * @param value - The value to test.
61
+ * @returns Whether it carries content.
62
+ */
63
+ private isContentBearing;
64
+ /**
65
+ * The resource the matched route declared, with a registered name resolved to its resource.
66
+ *
67
+ * @param event - The incoming event.
68
+ * @returns The resource, or `undefined` when the route declares none.
69
+ */
70
+ private resourceFor;
71
+ /**
72
+ * What the handler about to run declared, from either of the two places it may live.
73
+ *
74
+ * The route's own option comes first, because when a router is in play a route is the single
75
+ * description of itself. Failing that, the handler's own `@Returns` metadata is read: that form owns
76
+ * its key and needs no router, so the same module shapes the output of a routed request, a
77
+ * single-handler service, a CLI command or a browser event.
78
+ *
79
+ * @param event - The incoming event.
80
+ * @returns What was declared, or `undefined`.
81
+ */
82
+ private declarationFor;
83
+ /**
84
+ * What a handler declared with `@Returns`, if anything.
85
+ *
86
+ * @param handler - The handler about to run.
87
+ * @returns What the matching method declared, or `undefined`.
88
+ */
89
+ private declaredOnHandler;
90
+ /**
91
+ * Resolve a registered entry: a resource class goes through the container, so its constructor gets
92
+ * the services it asked for — the validator it holds its own contract against, and whatever its
93
+ * `data()` needs to complete a model.
94
+ *
95
+ * @param entry - A resource, or a class to resolve into one.
96
+ * @returns The resource.
97
+ */
98
+ private resolve;
99
+ }
100
+ /**
101
+ * Meta middleware for route-declared resources.
102
+ *
103
+ * Registered on `stone.router.middleware` by `resourcesBlueprint`. Its priority puts it outside
104
+ * validation, so a request is shaped on the way out after having been validated on the way in.
105
+ */
106
+ export declare const MetaResourceRouteMiddleware: MetaMiddleware<any, any>;
@@ -0,0 +1,65 @@
1
+ import { IResource } from '../declarations.js';
2
+ import { AppConfig, StoneBlueprint } from '@stone-js/core';
3
+ /**
4
+ * Resources configuration bucket (`stone.resources`).
5
+ */
6
+ export interface ResourcesConfig {
7
+ /**
8
+ * The query parameters a caller uses to ask for a shape.
9
+ *
10
+ * Configuration rather than convention: an API that already answers `?only=` keeps its vocabulary
11
+ * instead of gaining a second one. Defaults: `fields`, `include`, `view`.
12
+ */
13
+ params?: {
14
+ fields?: string;
15
+ include?: string;
16
+ fragment?: string;
17
+ };
18
+ /**
19
+ * What to do when a projection breaks the contract its resource publishes.
20
+ *
21
+ * `throw` (the default) refuses to answer, because a caller cannot detect a broken contract and a
22
+ * consumer generated from it breaks on the field that is missing. `warn` chooses availability over
23
+ * integrity, explicitly, and puts the breach in the log.
24
+ */
25
+ onViolation?: 'throw' | 'warn';
26
+ /**
27
+ * Named resources a route can refer to by name, instead of importing them at the route.
28
+ *
29
+ * ```ts
30
+ * blueprint.set('stone.resources.registry', { user: userResource })
31
+ * // then, on the route: { resource: 'user' }
32
+ * ```
33
+ *
34
+ * Naming a resource that is not registered fails loudly at request time rather than returning the
35
+ * model unshaped, because an unshaped model is exactly what a resource exists to prevent.
36
+ */
37
+ registry?: Record<string, IResource<any, any>>;
38
+ }
39
+ /**
40
+ * Application config augmented with the resources bucket.
41
+ */
42
+ export interface ResourcesAppConfig extends Partial<AppConfig> {
43
+ resources: ResourcesConfig;
44
+ }
45
+ /**
46
+ * Blueprint for the resources module.
47
+ */
48
+ export interface ResourcesBlueprint extends StoneBlueprint {
49
+ stone: ResourcesAppConfig;
50
+ }
51
+ /**
52
+ * Opt-in blueprint: register it to shape what routes return.
53
+ *
54
+ * It contributes the route middleware that applies whatever a route declared under `resource`.
55
+ * `stone.router.middleware` is an array, so this merges with the rest of the app. The middleware is
56
+ * a no-op on routes that declare nothing.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * import { resourcesBlueprint } from '@stone-js/resources'
61
+ *
62
+ * export const Application = defineStoneApp({ name: 'my-app' }, [resourcesBlueprint])
63
+ * ```
64
+ */
65
+ export declare const resourcesBlueprint: ResourcesBlueprint;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stone-js/resources",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "description": "Framework-agnostic API resources for Stone.js. Shape what your domain exposes — sparse fieldsets, conditional fields, includes and envelopes — decoupled from controllers, the same on backend and frontend.",
5
5
  "author": "Mr. Stone <evensstone@gmail.com>",
6
6
  "license": "MIT",
@@ -56,7 +56,8 @@
56
56
  "typedoc": "^0.28.6",
57
57
  "typedoc-plugin-markdown": "^4.7.0",
58
58
  "typescript": "^5.6.3",
59
- "vitest": "^3.2.4"
59
+ "vitest": "^3.2.4",
60
+ "zod": "^3.25.76"
60
61
  },
61
62
  "ts-standard": {
62
63
  "globals": [
@@ -68,6 +69,12 @@
68
69
  "beforeEach"
69
70
  ]
70
71
  },
72
+ "dependencies": {
73
+ "@stone-js/config": "0.8.10"
74
+ },
75
+ "peerDependencies": {
76
+ "@stone-js/core": "0.8.10"
77
+ },
71
78
  "scripts": {
72
79
  "lint": "ts-standard src",
73
80
  "lint:fix": "ts-standard --fix src tests",