@stone-js/resources 0.8.9 → 0.8.11

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.
@@ -1,8 +1,8 @@
1
1
  import { IResource } from '../declarations.js';
2
2
  import { IBlueprint, IContainer, IncomingEvent, NextMiddleware, OutgoingResponse, type MetaMiddleware } from '@stone-js/core';
3
3
  /**
4
- * The shape a route's `resource` option may take: the resource itself, or the name of one
5
- * registered under `stone.resources.registry`.
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
6
  */
7
7
  export type RouteResource = IResource<any, any> | string;
8
8
  /**
@@ -11,17 +11,12 @@ export type RouteResource = IResource<any, any> | string;
11
11
  * A route says what it exposes, once, where the route is defined:
12
12
  *
13
13
  * ```ts
14
- * @Get('/users/:id', { resource: userResource })
14
+ * @Get('/users/:id', { resource: UserResource })
15
15
  * ```
16
16
  *
17
- * The handler then returns its domain model, whole, and this middleware applies the resource on the
18
- * way out. That is the point: a service should not have to know which fields are public, and a
19
- * handler should not have to remember to strip them. Whatever the model gains later, a password
20
- * hash, an internal flag, is not exposed by accident, because the resource decides what leaves.
21
- *
22
- * It runs on the raw value the handler returned, before any response wrapping, so it knows nothing
23
- * of HTTP and works in every context. Sparse fieldsets are read from the event, so `?fields=id,name`
24
- * narrows the output without the route changing.
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.
25
20
  */
26
21
  export declare class ResourceRouteMiddleware {
27
22
  private readonly blueprint;
@@ -36,11 +31,36 @@ export declare class ResourceRouteMiddleware {
36
31
  /**
37
32
  * Run the handler, then shape what it returned.
38
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
+ *
39
41
  * @param event - The incoming event.
40
42
  * @param next - The next middleware.
41
43
  * @returns The shaped output, or the untouched result when the route declares no resource.
42
44
  */
43
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;
44
64
  /**
45
65
  * The resource the matched route declared, with a registered name resolved to its resource.
46
66
  *
@@ -69,7 +89,8 @@ export declare class ResourceRouteMiddleware {
69
89
  private declaredOnHandler;
70
90
  /**
71
91
  * Resolve a registered entry: a resource class goes through the container, so its constructor gets
72
- * the services it asked for and `toArray` can use them, i18n included.
92
+ * the services it asked for the validator it holds its own contract against, and whatever its
93
+ * `data()` needs to complete a model.
73
94
  *
74
95
  * @param entry - A resource, or a class to resolve into one.
75
96
  * @returns The resource.
@@ -1,9 +1,28 @@
1
1
  import { IResource } from '../declarations.js';
2
- import { AppConfig, StoneBlueprint } from '@stone-js/core';
2
+ import { AppConfig, MetaService, StoneBlueprint } from '@stone-js/core';
3
3
  /**
4
4
  * Resources configuration bucket (`stone.resources`).
5
5
  */
6
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';
7
26
  /**
8
27
  * Named resources a route can refer to by name, instead of importing them at the route.
9
28
  *
@@ -43,4 +62,13 @@ export interface ResourcesBlueprint extends StoneBlueprint {
43
62
  * export const Application = defineStoneApp({ name: 'my-app' }, [resourcesBlueprint])
44
63
  * ```
45
64
  */
65
+ /**
66
+ * The reader every resource holds its contract against, as a service.
67
+ *
68
+ * Bound so a resource's constructor can simply ask for it. A dependency read off the container that
69
+ * nothing ever bound is not optional, it is a crash, which is what made every container-resolved
70
+ * resource fail on a service nobody was told to register. The fix is the registration, not a
71
+ * conditional read.
72
+ */
73
+ export declare const MetaContractChecker: MetaService;
46
74
  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.9",
3
+ "version": "0.8.11",
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,9 @@
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",
61
+ "@stone-js/service-container": "0.8.11"
60
62
  },
61
63
  "ts-standard": {
62
64
  "globals": [
@@ -69,10 +71,10 @@
69
71
  ]
70
72
  },
71
73
  "dependencies": {
72
- "@stone-js/config": "0.8.9"
74
+ "@stone-js/config": "0.8.11"
73
75
  },
74
76
  "peerDependencies": {
75
- "@stone-js/core": "0.8.9"
77
+ "@stone-js/core": "0.8.11"
76
78
  },
77
79
  "scripts": {
78
80
  "lint": "ts-standard src",