@stone-js/resources 0.8.7 → 0.8.9

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,4 +1,4 @@
1
- import { IResource, ResourceContext, ResourceEnvelope, ResourceOutput } from './declarations';
1
+ import { IResource, ResourceContext, ResourceEnvelope, ResourceOutput } from './declarations.js';
2
2
  /**
3
3
  * Base API resource — the declarative way to shape what your domain exposes.
4
4
  *
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Class decorator: register a resource class under a name.
3
+ *
4
+ * ```ts
5
+ * @ApiResource('user')
6
+ * export class UserResource extends Resource<User> {
7
+ * toArray (user: User) { return { id: user.id, name: user.name } }
8
+ * }
9
+ * ```
10
+ *
11
+ * Routes and handlers then refer to it by name (`@Returns('user')`, or `{ resource: 'user' }`), so
12
+ * resources live in their own files, organised however the application likes, and nothing has to be
13
+ * imported at the route. The class is resolved by the container, so its constructor receives services
14
+ * and `toArray` can use them: a resource that formats dates for the caller's locale needs i18n, and
15
+ * this is how it gets it.
16
+ *
17
+ * @param alias - The name the resource is registered under. Defaults to the class name, which the
18
+ * discovery middleware fills in, since it is the one holding the class.
19
+ * @returns A class decorator.
20
+ */
21
+ export declare const ApiResource: (alias?: string) => ClassDecorator;
@@ -0,0 +1,27 @@
1
+ import { ResourcesConfig } from '../options/ResourcesBlueprint.js';
2
+ import { ClassType } from '@stone-js/core';
3
+ /**
4
+ * Options for the `@Resources` decorator: the `stone.resources` bucket, every key optional.
5
+ */
6
+ export interface ResourcesDecoratorOptions extends ResourcesConfig {
7
+ }
8
+ /**
9
+ * Class decorator: shape what routes return, declaratively.
10
+ *
11
+ * `@Resources()` installs the route middleware that applies whatever a route declared under
12
+ * `resource`, so a handler returns its domain model and only what the resource allows leaves the
13
+ * application.
14
+ *
15
+ * @param options - The resources configuration. Everything is optional.
16
+ * @returns A class decorator.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { Resources } from '@stone-js/resources'
21
+ *
22
+ * @Resources({ registry: { user: userResource } })
23
+ * @StoneApp({ name: 'my-app' })
24
+ * export class Application {}
25
+ * ```
26
+ */
27
+ export declare const Resources: <T extends ClassType = ClassType>(options?: ResourcesDecoratorOptions) => ClassDecorator;
@@ -0,0 +1,37 @@
1
+ import { IResource } from '../declarations.js';
2
+ /**
3
+ * What a handler declares it exposes: a resource, or the alias of a resource class registered with
4
+ * `@ApiResource`.
5
+ */
6
+ export type ReturnsInput = IResource<any, any> | string;
7
+ /**
8
+ * What `@Returns` records for one handler method.
9
+ */
10
+ export interface ReturnsMetadata {
11
+ /** The decorated method's name, so the declaration can be found again at request time. */
12
+ action: string | symbol;
13
+ /** What that method exposes. */
14
+ resource: ReturnsInput;
15
+ }
16
+ /**
17
+ * Method decorator: declare what a handler exposes.
18
+ *
19
+ * ```ts
20
+ * @Returns(userResource) // the resource itself
21
+ * @Returns('user') // a registered resource class
22
+ * ```
23
+ *
24
+ * The counterpart of `@Validate`: one says what comes in, the other what goes out, and between them
25
+ * the handler is free to return its domain model whole. Whatever the model gains later, a password
26
+ * hash, an internal flag, does not leak, because the resource decides what leaves.
27
+ *
28
+ * Like `@Validate`, this knows nothing about the router. The declaration is recorded on the handler
29
+ * under this module's own key, so it works in a routed application, a single-handler service, a CLI
30
+ * command or the browser. When a router is in play you may put it on the route instead
31
+ * (`@Get('/users/:id', { resource: userResource })`), which keeps everything a route does in one
32
+ * place; both forms end up in the same middleware.
33
+ *
34
+ * @param resource - What the handler exposes.
35
+ * @returns A method decorator.
36
+ */
37
+ export declare const Returns: <T extends Function = Function>(resource: ReturnsInput) => MethodDecorator;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Metadata key carrying what a handler method declared with `@Returns`.
3
+ *
4
+ * The module owns its key, which is what makes it independent: a resource shapes the output whether
5
+ * or not a router is in play, because the declaration lives on the handler, not on a route.
6
+ */
7
+ export declare const RETURNS_KEY = "@stone-js/resources/returns";
8
+ /**
9
+ * Metadata key carrying the alias a resource class registered itself under.
10
+ */
11
+ export declare const API_RESOURCE_KEY = "@stone-js/resources/resource";
@@ -1,5 +1,5 @@
1
- import { Resource } from './Resource';
2
- import { ResourceContext, ResourceOutput } from './declarations';
1
+ import { Resource } from './Resource.js';
2
+ import { ResourceContext, ResourceOutput } from './declarations.js';
3
3
  /**
4
4
  * The imperative/functional way to define a resource — a plain transform function instead of a
5
5
  * class. Returns a full {@link Resource} (so you still get `item`/`collection`/`response` and
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ResourceContext, ResourceOutput } from './declarations';
1
+ import { ResourceContext, ResourceOutput } from './declarations.js';
2
2
  /**
3
3
  * Returns a copy of `object` without any `undefined` values (so conditional fields simply vanish).
4
4
  *
package/dist/index.d.ts CHANGED
@@ -1,4 +1,11 @@
1
- export * from './Resource';
2
- export * from './declarations';
3
- export * from './defineResource';
4
- export * from './helpers';
1
+ export * from './Resource.js';
2
+ export * from './declarations.js';
3
+ export * from './decorators/ApiResource.js';
4
+ export * from './decorators/Resources.js';
5
+ export * from './decorators/Returns.js';
6
+ export * from './decorators/constants.js';
7
+ export * from './defineResource.js';
8
+ export * from './helpers.js';
9
+ export * from './middleware/BlueprintMiddleware.js';
10
+ export * from './middleware/ResourceRouteMiddleware.js';
11
+ export * from './options/ResourcesBlueprint.js';
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { hasMetadata, getMetadata, setClassMetadata, classDecoratorLegacyWrapper, addBlueprint, methodDecoratorLegacyWrapper, addMetadata } from '@stone-js/core';
2
+ import { cloneValue } from '@stone-js/config';
3
+
1
4
  /**
2
5
  * Returns a copy of `object` without any `undefined` values (so conditional fields simply vanish).
3
6
  *
@@ -188,4 +191,296 @@ function defineResource(transform) {
188
191
  }();
189
192
  }
190
193
 
191
- export { Resource, applyFields, contextFromEvent, defineResource, except, only, stripUndefined };
194
+ /**
195
+ * Metadata key carrying what a handler method declared with `@Returns`.
196
+ *
197
+ * The module owns its key, which is what makes it independent: a resource shapes the output whether
198
+ * or not a router is in play, because the declaration lives on the handler, not on a route.
199
+ */
200
+ const RETURNS_KEY = '@stone-js/resources/returns';
201
+ /**
202
+ * Metadata key carrying the alias a resource class registered itself under.
203
+ */
204
+ const API_RESOURCE_KEY = '@stone-js/resources/resource';
205
+
206
+ /**
207
+ * Build-phase middleware: collect every class registered with `@ApiResource` into the registry.
208
+ *
209
+ * The same scan the router does for its route definitions, applied to this module's own key. After it
210
+ * runs, `stone.resources.registry` maps each alias to its class, so a route or a handler can name a
211
+ * resource instead of importing it, and `@stone-js/openapi` can walk the registry to publish response
212
+ * shapes without loading anything itself.
213
+ *
214
+ * @param context - The blueprint context.
215
+ * @param next - The next blueprint middleware.
216
+ * @returns The blueprint.
217
+ */
218
+ async function ApiResourceMiddleware(context, next) {
219
+ const registered = context
220
+ .modules
221
+ .filter((module) => hasMetadata(module, API_RESOURCE_KEY))
222
+ .reduce((registry, module) => {
223
+ const { alias } = getMetadata(module, API_RESOURCE_KEY, {});
224
+ return { ...registry, [alias ?? module.name]: module };
225
+ }, {});
226
+ if (Object.keys(registered).length > 0) {
227
+ context.blueprint.set('stone.resources.registry', {
228
+ ...context.blueprint.get('stone.resources.registry', {}),
229
+ ...registered
230
+ });
231
+ }
232
+ return await next(context);
233
+ }
234
+ /**
235
+ * Meta blueprint middleware for resource discovery.
236
+ */
237
+ const MetaApiResourceMiddleware = {
238
+ module: ApiResourceMiddleware,
239
+ priority: 5
240
+ };
241
+
242
+ /**
243
+ * Route middleware: shapes what a route returns, after its handler ran.
244
+ *
245
+ * A route says what it exposes, once, where the route is defined:
246
+ *
247
+ * ```ts
248
+ * @Get('/users/:id', { resource: userResource })
249
+ * ```
250
+ *
251
+ * The handler then returns its domain model, whole, and this middleware applies the resource on the
252
+ * way out. That is the point: a service should not have to know which fields are public, and a
253
+ * handler should not have to remember to strip them. Whatever the model gains later, a password
254
+ * hash, an internal flag, is not exposed by accident, because the resource decides what leaves.
255
+ *
256
+ * It runs on the raw value the handler returned, before any response wrapping, so it knows nothing
257
+ * of HTTP and works in every context. Sparse fieldsets are read from the event, so `?fields=id,name`
258
+ * narrows the output without the route changing.
259
+ */
260
+ class ResourceRouteMiddleware {
261
+ blueprint;
262
+ container;
263
+ /**
264
+ * @param dependencies - Auto-wired container services.
265
+ */
266
+ constructor({ blueprint, container }) {
267
+ this.blueprint = blueprint;
268
+ this.container = container;
269
+ }
270
+ /**
271
+ * Run the handler, then shape what it returned.
272
+ *
273
+ * @param event - The incoming event.
274
+ * @param next - The next middleware.
275
+ * @returns The shaped output, or the untouched result when the route declares no resource.
276
+ */
277
+ async handle(event, next) {
278
+ const resource = this.resourceFor(event);
279
+ const result = await next(event);
280
+ if (resource === undefined || result === undefined || result === null) {
281
+ return result;
282
+ }
283
+ const context = contextFromEvent(event);
284
+ return (Array.isArray(result) ? resource.collection(result, context) : resource.item(result, context));
285
+ }
286
+ /**
287
+ * The resource the matched route declared, with a registered name resolved to its resource.
288
+ *
289
+ * @param event - The incoming event.
290
+ * @returns The resource, or `undefined` when the route declares none.
291
+ */
292
+ resourceFor(event) {
293
+ const declared = this.declarationFor(event);
294
+ if (declared === undefined) {
295
+ return undefined;
296
+ }
297
+ if (typeof declared !== 'string') {
298
+ return this.resolve(declared);
299
+ }
300
+ const registry = this.blueprint.get('stone.resources', {}).registry ?? {};
301
+ const resource = registry[declared];
302
+ if (resource === undefined) {
303
+ throw new TypeError(`The route declares \`resource: '${declared}'\`, but no resource is registered under that ` +
304
+ 'name. Register it with `blueprint.set(\'stone.resources.registry\', { ' + declared + ': … })`, ' +
305
+ 'or declare the resource inline on the route.');
306
+ }
307
+ return this.resolve(resource);
308
+ }
309
+ /**
310
+ * What the handler about to run declared, from either of the two places it may live.
311
+ *
312
+ * The route's own option comes first, because when a router is in play a route is the single
313
+ * description of itself. Failing that, the handler's own `@Returns` metadata is read: that form owns
314
+ * its key and needs no router, so the same module shapes the output of a routed request, a
315
+ * single-handler service, a CLI command or a browser event.
316
+ *
317
+ * @param event - The incoming event.
318
+ * @returns What was declared, or `undefined`.
319
+ */
320
+ declarationFor(event) {
321
+ // Duck-typed throughout: the kernel is agnostic, and an event without a router carries no route.
322
+ const route = event.getRoute?.();
323
+ const onRoute = route?.getOption?.('resource');
324
+ if (onRoute !== undefined) {
325
+ return onRoute;
326
+ }
327
+ const handler = route?.getOption?.('handler') ??
328
+ this.blueprint.get('stone.kernel.eventHandler', {});
329
+ return this.declaredOnHandler(handler);
330
+ }
331
+ /**
332
+ * What a handler declared with `@Returns`, if anything.
333
+ *
334
+ * @param handler - The handler about to run.
335
+ * @returns What the matching method declared, or `undefined`.
336
+ */
337
+ declaredOnHandler(handler) {
338
+ const module = handler?.module;
339
+ if (module === undefined || !hasMetadata(module, RETURNS_KEY)) {
340
+ return undefined;
341
+ }
342
+ const declarations = getMetadata(module, RETURNS_KEY, []);
343
+ const action = handler?.action;
344
+ // A single-handler module declares one; a controller declares one per method.
345
+ return (action === undefined
346
+ ? declarations[0]
347
+ : declarations.find((declaration) => declaration.action === action))?.resource;
348
+ }
349
+ /**
350
+ * Resolve a registered entry: a resource class goes through the container, so its constructor gets
351
+ * the services it asked for and `toArray` can use them, i18n included.
352
+ *
353
+ * @param entry - A resource, or a class to resolve into one.
354
+ * @returns The resource.
355
+ */
356
+ resolve(entry) {
357
+ if (typeof entry !== 'function') {
358
+ return entry;
359
+ }
360
+ const ResourceClass = entry;
361
+ return this.container?.resolve?.(ResourceClass, true) ?? new ResourceClass({});
362
+ }
363
+ }
364
+ /**
365
+ * Meta middleware for route-declared resources.
366
+ *
367
+ * Registered on `stone.router.middleware` by `resourcesBlueprint`. Its priority puts it outside
368
+ * validation, so a request is shaped on the way out after having been validated on the way in.
369
+ */
370
+ const MetaResourceRouteMiddleware = {
371
+ module: ResourceRouteMiddleware,
372
+ isClass: true,
373
+ priority: 4
374
+ };
375
+
376
+ /**
377
+ * Class decorator: register a resource class under a name.
378
+ *
379
+ * ```ts
380
+ * @ApiResource('user')
381
+ * export class UserResource extends Resource<User> {
382
+ * toArray (user: User) { return { id: user.id, name: user.name } }
383
+ * }
384
+ * ```
385
+ *
386
+ * Routes and handlers then refer to it by name (`@Returns('user')`, or `{ resource: 'user' }`), so
387
+ * resources live in their own files, organised however the application likes, and nothing has to be
388
+ * imported at the route. The class is resolved by the container, so its constructor receives services
389
+ * and `toArray` can use them: a resource that formats dates for the caller's locale needs i18n, and
390
+ * this is how it gets it.
391
+ *
392
+ * @param alias - The name the resource is registered under. Defaults to the class name, which the
393
+ * discovery middleware fills in, since it is the one holding the class.
394
+ * @returns A class decorator.
395
+ */
396
+ const ApiResource = (alias) => {
397
+ return setClassMetadata(API_RESOURCE_KEY, { alias });
398
+ };
399
+
400
+ /**
401
+ * Opt-in blueprint: register it to shape what routes return.
402
+ *
403
+ * It contributes the route middleware that applies whatever a route declared under `resource`.
404
+ * `stone.router.middleware` is an array, so this merges with the rest of the app. The middleware is
405
+ * a no-op on routes that declare nothing.
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * import { resourcesBlueprint } from '@stone-js/resources'
410
+ *
411
+ * export const Application = defineStoneApp({ name: 'my-app' }, [resourcesBlueprint])
412
+ * ```
413
+ */
414
+ const resourcesBlueprint = {
415
+ stone: {
416
+ resources: {},
417
+ blueprint: {
418
+ middleware: [
419
+ MetaApiResourceMiddleware
420
+ ]
421
+ },
422
+ router: {
423
+ middleware: [
424
+ MetaResourceRouteMiddleware
425
+ ]
426
+ }
427
+ }
428
+ };
429
+
430
+ /**
431
+ * Class decorator: shape what routes return, declaratively.
432
+ *
433
+ * `@Resources()` installs the route middleware that applies whatever a route declared under
434
+ * `resource`, so a handler returns its domain model and only what the resource allows leaves the
435
+ * application.
436
+ *
437
+ * @param options - The resources configuration. Everything is optional.
438
+ * @returns A class decorator.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * import { Resources } from '@stone-js/resources'
443
+ *
444
+ * @Resources({ registry: { user: userResource } })
445
+ * @StoneApp({ name: 'my-app' })
446
+ * export class Application {}
447
+ * ```
448
+ */
449
+ const Resources = (options = {}) => {
450
+ return classDecoratorLegacyWrapper((target, context) => {
451
+ // The blueprint is the single source of truth for what the module declares; the decorator only
452
+ // overrides what it can, its options bucket.
453
+ const blueprint = cloneValue(resourcesBlueprint);
454
+ blueprint.stone.resources = { ...blueprint.stone.resources, ...options };
455
+ addBlueprint(target, context, blueprint);
456
+ });
457
+ };
458
+
459
+ /**
460
+ * Method decorator: declare what a handler exposes.
461
+ *
462
+ * ```ts
463
+ * @Returns(userResource) // the resource itself
464
+ * @Returns('user') // a registered resource class
465
+ * ```
466
+ *
467
+ * The counterpart of `@Validate`: one says what comes in, the other what goes out, and between them
468
+ * the handler is free to return its domain model whole. Whatever the model gains later, a password
469
+ * hash, an internal flag, does not leak, because the resource decides what leaves.
470
+ *
471
+ * Like `@Validate`, this knows nothing about the router. The declaration is recorded on the handler
472
+ * under this module's own key, so it works in a routed application, a single-handler service, a CLI
473
+ * command or the browser. When a router is in play you may put it on the route instead
474
+ * (`@Get('/users/:id', { resource: userResource })`), which keeps everything a route does in one
475
+ * place; both forms end up in the same middleware.
476
+ *
477
+ * @param resource - What the handler exposes.
478
+ * @returns A method decorator.
479
+ */
480
+ const Returns = (resource) => {
481
+ return methodDecoratorLegacyWrapper((_target, context) => {
482
+ addMetadata(context, RETURNS_KEY, { action: context.name, resource });
483
+ });
484
+ };
485
+
486
+ export { API_RESOURCE_KEY, ApiResource, ApiResourceMiddleware, MetaApiResourceMiddleware, MetaResourceRouteMiddleware, RETURNS_KEY, Resource, ResourceRouteMiddleware, Resources, Returns, applyFields, contextFromEvent, defineResource, except, only, resourcesBlueprint, stripUndefined };
@@ -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,85 @@
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, or the name of one
5
+ * 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 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.
25
+ */
26
+ export declare class ResourceRouteMiddleware {
27
+ private readonly blueprint;
28
+ private readonly container?;
29
+ /**
30
+ * @param dependencies - Auto-wired container services.
31
+ */
32
+ constructor({ blueprint, container }: {
33
+ blueprint: IBlueprint;
34
+ container?: IContainer;
35
+ });
36
+ /**
37
+ * Run the handler, then shape what it returned.
38
+ *
39
+ * @param event - The incoming event.
40
+ * @param next - The next middleware.
41
+ * @returns The shaped output, or the untouched result when the route declares no resource.
42
+ */
43
+ handle(event: IncomingEvent, next: NextMiddleware<IncomingEvent, OutgoingResponse>): Promise<OutgoingResponse>;
44
+ /**
45
+ * The resource the matched route declared, with a registered name resolved to its resource.
46
+ *
47
+ * @param event - The incoming event.
48
+ * @returns The resource, or `undefined` when the route declares none.
49
+ */
50
+ private resourceFor;
51
+ /**
52
+ * What the handler about to run declared, from either of the two places it may live.
53
+ *
54
+ * The route's own option comes first, because when a router is in play a route is the single
55
+ * description of itself. Failing that, the handler's own `@Returns` metadata is read: that form owns
56
+ * its key and needs no router, so the same module shapes the output of a routed request, a
57
+ * single-handler service, a CLI command or a browser event.
58
+ *
59
+ * @param event - The incoming event.
60
+ * @returns What was declared, or `undefined`.
61
+ */
62
+ private declarationFor;
63
+ /**
64
+ * What a handler declared with `@Returns`, if anything.
65
+ *
66
+ * @param handler - The handler about to run.
67
+ * @returns What the matching method declared, or `undefined`.
68
+ */
69
+ private declaredOnHandler;
70
+ /**
71
+ * 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.
73
+ *
74
+ * @param entry - A resource, or a class to resolve into one.
75
+ * @returns The resource.
76
+ */
77
+ private resolve;
78
+ }
79
+ /**
80
+ * Meta middleware for route-declared resources.
81
+ *
82
+ * Registered on `stone.router.middleware` by `resourcesBlueprint`. Its priority puts it outside
83
+ * validation, so a request is shaped on the way out after having been validated on the way in.
84
+ */
85
+ export declare const MetaResourceRouteMiddleware: MetaMiddleware<any, any>;
@@ -0,0 +1,46 @@
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
+ * Named resources a route can refer to by name, instead of importing them at the route.
9
+ *
10
+ * ```ts
11
+ * blueprint.set('stone.resources.registry', { user: userResource })
12
+ * // then, on the route: { resource: 'user' }
13
+ * ```
14
+ *
15
+ * Naming a resource that is not registered fails loudly at request time rather than returning the
16
+ * model unshaped, because an unshaped model is exactly what a resource exists to prevent.
17
+ */
18
+ registry?: Record<string, IResource<any, any>>;
19
+ }
20
+ /**
21
+ * Application config augmented with the resources bucket.
22
+ */
23
+ export interface ResourcesAppConfig extends Partial<AppConfig> {
24
+ resources: ResourcesConfig;
25
+ }
26
+ /**
27
+ * Blueprint for the resources module.
28
+ */
29
+ export interface ResourcesBlueprint extends StoneBlueprint {
30
+ stone: ResourcesAppConfig;
31
+ }
32
+ /**
33
+ * Opt-in blueprint: register it to shape what routes return.
34
+ *
35
+ * It contributes the route middleware that applies whatever a route declared under `resource`.
36
+ * `stone.router.middleware` is an array, so this merges with the rest of the app. The middleware is
37
+ * a no-op on routes that declare nothing.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import { resourcesBlueprint } from '@stone-js/resources'
42
+ *
43
+ * export const Application = defineStoneApp({ name: 'my-app' }, [resourcesBlueprint])
44
+ * ```
45
+ */
46
+ 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.7",
3
+ "version": "0.8.9",
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",
@@ -68,6 +68,12 @@
68
68
  "beforeEach"
69
69
  ]
70
70
  },
71
+ "dependencies": {
72
+ "@stone-js/config": "0.8.9"
73
+ },
74
+ "peerDependencies": {
75
+ "@stone-js/core": "0.8.9"
76
+ },
71
77
  "scripts": {
72
78
  "lint": "ts-standard src",
73
79
  "lint:fix": "ts-standard --fix src tests",