lumen-framework 3.0.3 → 3.1.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 (40) hide show
  1. package/dist/cli.cjs +70 -38
  2. package/dist/cli.cjs.map +4 -4
  3. package/dist/index.js +928 -259
  4. package/dist/index.js.map +4 -4
  5. package/dist/index.mjs +919 -250
  6. package/dist/index.mjs.map +4 -4
  7. package/dist/types/errors/namespaced-serializer-missing-error.d.ts +15 -0
  8. package/dist/types/packages/application/utils/validate-namespaced-serializers.d.ts +15 -0
  9. package/dist/types/packages/controller/errors/index.d.ts +1 -0
  10. package/dist/types/packages/controller/errors/related-record-not-found-error.d.ts +14 -0
  11. package/dist/types/packages/controller/index.d.ts +65 -0
  12. package/dist/types/packages/controller/utils/find-many.d.ts +2 -1
  13. package/dist/types/packages/controller/utils/find-one.d.ts +2 -1
  14. package/dist/types/packages/controller/utils/params-to-query.d.ts +7 -2
  15. package/dist/types/packages/controller/utils/validate-relationships.d.ts +8 -0
  16. package/dist/types/packages/database/constants.d.ts +1 -0
  17. package/dist/types/packages/database/model/index.d.ts +11 -0
  18. package/dist/types/packages/database/model/utils/process-write-error.d.ts +9 -1
  19. package/dist/types/packages/database/validation/errors/validation-error.d.ts +11 -2
  20. package/dist/types/packages/jsonapi/interfaces.d.ts +1 -1
  21. package/dist/types/packages/router/route/params/errors/client-generated-id-error.d.ts +13 -0
  22. package/dist/types/packages/router/route/params/errors/forbidden-parameter-error.d.ts +14 -0
  23. package/dist/types/packages/router/route/params/errors/index.d.ts +2 -0
  24. package/dist/types/packages/router/route/params/errors/invalid-parameter-error.d.ts +2 -0
  25. package/dist/types/packages/router/route/params/errors/parameter-not-nullable-error.d.ts +2 -0
  26. package/dist/types/packages/router/route/params/errors/parameter-required-error.d.ts +2 -0
  27. package/dist/types/packages/router/route/params/errors/parameter-type-error.d.ts +2 -0
  28. package/dist/types/packages/router/route/params/errors/parameter-value-error.d.ts +2 -0
  29. package/dist/types/packages/router/route/params/errors/resource-mismatch-error.d.ts +2 -0
  30. package/dist/types/packages/router/route/params/index.d.ts +1 -0
  31. package/dist/types/packages/router/route/params/parameter/forbidden-parameter.d.ts +12 -0
  32. package/dist/types/packages/router/route/params/parameter/index.d.ts +8 -0
  33. package/dist/types/packages/router/route/params/utils/validate-client-id.d.ts +8 -0
  34. package/dist/types/packages/serializer/index.d.ts +111 -36
  35. package/dist/types/packages/serializer/utils/include-tree.d.ts +29 -0
  36. package/dist/types/packages/serializer/utils/load-linkage.d.ts +22 -0
  37. package/dist/types/packages/server/index.d.ts +2 -1
  38. package/dist/types/packages/server/interfaces.d.ts +5 -0
  39. package/dist/types/packages/server/utils/source-for.d.ts +10 -0
  40. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Thrown at boot when a namespace that opted out of serializer fallback
3
+ * (`serializerFallback = false` on its ApplicationController) can reach a type
4
+ * that has no Serializer in that namespace.
5
+ *
6
+ * @private
7
+ */
8
+ declare class NamespacedSerializerMissingError extends ReferenceError {
9
+ /**
10
+ * @param missing - Each missing serializer key (`admin/comments`) with an
11
+ * example of how it is reached (`admin/posts?include=comments`).
12
+ */
13
+ constructor(missing: Map<string, string>);
14
+ }
15
+ export default NamespacedSerializerMissingError;
@@ -0,0 +1,15 @@
1
+ import type Controller from '../../controller';
2
+ import type Serializer from '../../serializer';
3
+ import type { Model } from '../../database';
4
+ import type { Bundle$Namespace } from '../../loader';
5
+ /**
6
+ * Enforce `serializerFallback = false`: for every namespace whose
7
+ * ApplicationController sets it, each type its controllers can serialize — the
8
+ * resource itself, and everything reachable through `include` (and so
9
+ * `fields`) down to the controller's `maxIncludeDepth` — must have a
10
+ * Serializer in exactly that namespace. Throws listing every gap, so a missing
11
+ * serializer fails the boot instead of silently falling back to the root one.
12
+ *
13
+ * @private
14
+ */
15
+ export default function validateNamespacedSerializers(controllers: Bundle$Namespace<Controller> | Map<string, Controller>, serializers: Bundle$Namespace<Serializer<Model>> | Map<string, Serializer<Model>>): void;
@@ -0,0 +1 @@
1
+ export { default as RelatedRecordNotFoundError } from './related-record-not-found-error';
@@ -0,0 +1,14 @@
1
+ import type { ModelClass } from '../../database';
2
+ import type { Server$ErrorSource } from '../../server';
3
+ /**
4
+ * JSON:API 1.0: "A server MUST return 404 Not Found when processing a request
5
+ * that references a related resource that does not exist."
6
+ *
7
+ * @private
8
+ */
9
+ declare class RelatedRecordNotFoundError extends Error {
10
+ source: Server$ErrorSource;
11
+ constructor({ name, primaryKey }: ModelClass, primaryKeyValue: unknown, path: string);
12
+ }
13
+ declare const _default: new (...args: Array<any>) => RelatedRecordNotFoundError & import("../../server/interfaces").Server$Error;
14
+ export default _default;
@@ -442,6 +442,71 @@ declare class Controller {
442
442
  * @public
443
443
  */
444
444
  defaultPerPage: number;
445
+ /**
446
+ * How many relationships deep an `?include` path may go on this
447
+ * controller's routes. `comments.reactions.user` is 3 levels deep; with `1`
448
+ * only direct relationships (`comments`) can be included. Paths deeper than
449
+ * this are rejected with `400 Bad Request`.
450
+ *
451
+ * Set it on `ApplicationController` to change it for the whole app, or on a
452
+ * single controller to override it there.
453
+ *
454
+ * ```javascript
455
+ * class ApplicationController extends Controller {
456
+ * maxIncludeDepth = 2;
457
+ * }
458
+ * ```
459
+ *
460
+ * Every allowed path is enumerated up front from the serializers'
461
+ * relationships, and each nested level costs its own queries per request, so
462
+ * keep this small.
463
+ *
464
+ * @property maxIncludeDepth
465
+ * @type {Number}
466
+ * @default 3
467
+ * @public
468
+ */
469
+ maxIncludeDepth: number;
470
+ /**
471
+ * Whether a namespace may fall back to the root Serializer of a type it has
472
+ * no Serializer for. Read from a namespace's `ApplicationController` and
473
+ * applies to the whole namespace.
474
+ *
475
+ * By default `app/controllers/admin/comments.js` without an
476
+ * `app/serializers/admin/comments.js` — or an included type without one —
477
+ * is serialized by the root Serializer, with every attribute and
478
+ * relationship it declares. For a namespace that must only expose what it
479
+ * declares itself, turn the fallback off:
480
+ *
481
+ * ```javascript
482
+ * // app/controllers/admin/application.js
483
+ * class AdminApplicationController extends ApplicationController {
484
+ * serializerFallback = false;
485
+ * }
486
+ * ```
487
+ *
488
+ * The application then refuses to boot while any type the namespace can
489
+ * serialize or `include` (down to each controller's `maxIncludeDepth`) has
490
+ * no Serializer in that namespace, listing each missing one.
491
+ *
492
+ * @property serializerFallback
493
+ * @type {Boolean}
494
+ * @default true
495
+ * @public
496
+ */
497
+ serializerFallback: boolean;
498
+ /**
499
+ * The Serializer to serialize (and validate, and load) related resources of
500
+ * this Controller's responses with: the related model's Serializer in this
501
+ * Controller's namespace, falling back to the root one.
502
+ *
503
+ * Always this Controller's namespace — not its Serializer's, which is the
504
+ * root one when the namespace has no Serializer for this resource.
505
+ *
506
+ * @method serializerFor
507
+ * @private
508
+ */
509
+ serializerFor(model: ModelClass): Serializer<Model>;
445
510
  /**
446
511
  * The resolved Model for a Controller instance.
447
512
  *
@@ -1,6 +1,7 @@
1
1
  import type { Model, ModelClass, Query } from '../../database';
2
+ import type Serializer from '../../serializer';
2
3
  import type { Request } from '../../server';
3
4
  /**
4
5
  * @private
5
6
  */
6
- export default function findMany<T extends Model>(model: ModelClass<T>, req: Request): Query<Array<Model>>;
7
+ export default function findMany<T extends Model>(model: ModelClass<T>, req: Request, serializerFor?: (model: ModelClass) => Serializer<Model>): Query<Array<Model>>;
@@ -1,6 +1,7 @@
1
1
  import type { Model, ModelClass, Query } from '../../database';
2
+ import type Serializer from '../../serializer';
2
3
  import type { Request } from '../../server';
3
4
  /**
4
5
  * @private
5
6
  */
6
- export default function findOne<T extends Model>(model: ModelClass<T>, req: Request): Query<T>;
7
+ export default function findOne<T extends Model>(model: ModelClass<T>, req: Request, serializerFor?: (model: ModelClass) => Serializer<Model>): Query<T>;
@@ -1,4 +1,5 @@
1
- import type { ModelClass } from '../../database';
1
+ import type { Model, ModelClass } from '../../database';
2
+ import type Serializer from '../../serializer';
2
3
  import type { Request$params } from '../../server';
3
4
  type Controller$query = {
4
5
  id?: number | string | Buffer;
@@ -10,7 +11,11 @@ type Controller$query = {
10
11
  include: Record<string, Array<string>>;
11
12
  };
12
13
  /**
14
+ * `serializerFor` resolves the Serializer an included resource is serialized
15
+ * with — in the request's namespace (`Controller#serializerFor()`) — so its
16
+ * attributes are the ones loaded. Defaults to the related model's root one.
17
+ *
13
18
  * @private
14
19
  */
15
- export default function paramsToQuery(model: ModelClass, { id, page, sort, filter, fields, include }: Request$params): Controller$query;
20
+ export default function paramsToQuery(model: ModelClass, { id, page, sort, filter, fields, include }: Request$params, serializerFor?: (related: ModelClass) => Serializer<Model>): Controller$query;
16
21
  export {};
@@ -0,0 +1,8 @@
1
+ import type { Model, ModelClass } from '../../database';
2
+ /**
3
+ * Check that every resource referenced from `data.relationships` exists, so a
4
+ * write cannot leave dangling linkage. One query per relationship.
5
+ *
6
+ * @private
7
+ */
8
+ export default function validateRelationships<T extends Model>(model: ModelClass<T>, relationships?: Record<string, unknown>): Promise<void>;
@@ -1,3 +1,4 @@
1
+ export declare const UNIQUE_CONSTRAINT_CODES: Set<string>;
1
2
  export declare const UNIQUE_CONSTRAINT: RegExp;
2
3
  export declare const VALID_DRIVERS: string[];
3
4
  export declare const TYPE_ALIASES: Map<string, string>;
@@ -78,6 +78,17 @@ declare class Model {
78
78
  * @private
79
79
  */
80
80
  prevAssociations: Set<Model>;
81
+ /**
82
+ * Names of `hasOne` relationships that were eager-loaded (joined) and found
83
+ * to have no related record. Lets the relationship getter answer `null`
84
+ * without a per-record query. Kept outside of the change sets so it never
85
+ * counts as a change to the record.
86
+ *
87
+ * @property absentRelationships
88
+ * @type {Set}
89
+ * @private
90
+ */
91
+ absentRelationships: Set<string>;
81
92
  /**
82
93
  * @property changeSets
83
94
  * @type {Array}
@@ -1,4 +1,12 @@
1
1
  /**
2
+ * Map a database write error to the server error it should surface as.
3
+ *
2
4
  * @private
3
5
  */
4
- export default function resolveWriteError(err: Error): Error;
6
+ export default function processWriteError(err: unknown): unknown;
7
+ /**
8
+ * `.catch()` handler for a write: rethrows the mapped error.
9
+ *
10
+ * @private
11
+ */
12
+ export declare function rethrowWriteError(err: unknown): never;
@@ -1,7 +1,16 @@
1
+ import type { Server$ErrorSource } from '../../../server';
1
2
  /**
3
+ * A model validator (`static validates`) rejected an attribute. Answered with
4
+ * 422 and a pointer to the attribute, which clients such as ember-data map to
5
+ * field errors. The rejected value is deliberately not in the message: it
6
+ * ends up in logs and may be a secret (e.g. a password).
7
+ *
2
8
  * @private
3
9
  */
4
10
  declare class ValidationError extends Error {
5
- constructor(key: string, value: string);
11
+ key: string;
12
+ source: Server$ErrorSource;
13
+ constructor(key: string);
6
14
  }
7
- export default ValidationError;
15
+ declare const _default: new (...args: Array<any>) => ValidationError & import("../../../server/interfaces").Server$Error;
16
+ export default _default;
@@ -28,7 +28,7 @@ export interface JSONAPI$ResourceObject {
28
28
  };
29
29
  }
30
30
  export interface JSONAPI$RelationshipObject {
31
- data: JSONAPI$IdentifierObject;
31
+ data: JSONAPI$IdentifierObject | Array<JSONAPI$IdentifierObject> | null;
32
32
  meta?: JSONAPI$BaseObject;
33
33
  links?: JSONAPI$ResourceLinksObject;
34
34
  }
@@ -0,0 +1,13 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
2
+ /**
3
+ * JSON:API 1.0: "A server MUST return 403 Forbidden in response to an
4
+ * unsupported request to create a resource with a client-generated ID."
5
+ *
6
+ * @private
7
+ */
8
+ declare class ClientGeneratedIdError extends TypeError {
9
+ source: Server$ErrorSource;
10
+ constructor();
11
+ }
12
+ declare const _default: new (...args: Array<any>) => ClientGeneratedIdError & import("../../../../server/interfaces").Server$Error;
13
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
2
+ /**
3
+ * JSON:API 1.0: "A server MUST return 403 Forbidden in response to an
4
+ * unsupported request to update a resource or relationship." Used for members
5
+ * the model knows about but the controller does not accept (`params`).
6
+ *
7
+ * @private
8
+ */
9
+ declare class ForbiddenParameterError extends TypeError {
10
+ source: Server$ErrorSource;
11
+ constructor(path: string);
12
+ }
13
+ declare const _default: new (...args: Array<any>) => ForbiddenParameterError & import("../../../../server/interfaces").Server$Error;
14
+ export default _default;
@@ -3,3 +3,5 @@ export { default as ParameterValueError } from './parameter-value-error';
3
3
  export { default as InvalidParameterError } from './invalid-parameter-error';
4
4
  export { default as ResourceMismatchError } from './resource-mismatch-error';
5
5
  export { default as ParameterRequiredError } from './parameter-required-error';
6
+ export { default as ClientGeneratedIdError } from './client-generated-id-error';
7
+ export { default as ForbiddenParameterError } from './forbidden-parameter-error';
@@ -1,7 +1,9 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  /**
2
3
  * @private
3
4
  */
4
5
  declare class InvalidParameterError extends TypeError {
6
+ source: Server$ErrorSource;
5
7
  constructor(path: string);
6
8
  }
7
9
  declare const _default: new (...args: Array<any>) => InvalidParameterError & import("../../../../server/interfaces").Server$Error;
@@ -1,8 +1,10 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  import type { ParameterLike } from '../index';
2
3
  /**
3
4
  * @private
4
5
  */
5
6
  declare class ParameterNotNullableError extends TypeError {
7
+ source: Server$ErrorSource;
6
8
  constructor({ path }: ParameterLike);
7
9
  }
8
10
  declare const _default: new (...args: Array<any>) => ParameterNotNullableError & import("../../../../server/interfaces").Server$Error;
@@ -1,7 +1,9 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  /**
2
3
  * @private
3
4
  */
4
5
  declare class ParameterRequiredError extends TypeError {
6
+ source: Server$ErrorSource;
5
7
  constructor(path: string);
6
8
  }
7
9
  declare const _default: new (...args: Array<any>) => ParameterRequiredError & import("../../../../server/interfaces").Server$Error;
@@ -1,8 +1,10 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  import type { ParameterLike } from '../index';
2
3
  /**
3
4
  * @private
4
5
  */
5
6
  declare class ParameterTypeError extends TypeError {
7
+ source: Server$ErrorSource;
6
8
  constructor(param: ParameterLike, actual: string);
7
9
  }
8
10
  declare const _default: new (...args: Array<any>) => ParameterTypeError & import("../../../../server/interfaces").Server$Error;
@@ -1,8 +1,10 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  import type { ParameterLike } from '../index';
2
3
  /**
3
4
  * @private
4
5
  */
5
6
  declare class ParameterValueError extends TypeError {
7
+ source: Server$ErrorSource;
6
8
  constructor(param: ParameterLike, actual: unknown);
7
9
  }
8
10
  declare const _default: new (...args: Array<any>) => ParameterValueError & import("../../../../server/interfaces").Server$Error;
@@ -1,7 +1,9 @@
1
+ import type { Server$ErrorSource } from '../../../../server';
1
2
  /**
2
3
  * @private
3
4
  */
4
5
  declare class ResourceMismatchError extends TypeError {
6
+ source: Server$ErrorSource;
5
7
  constructor(path: string, expected: unknown, actual: unknown);
6
8
  }
7
9
  declare const _default: new (...args: Array<any>) => ResourceMismatchError & import("../../../../server/interfaces").Server$Error;
@@ -12,6 +12,7 @@ export declare function defaultParamsFor({ type, controller }: {
12
12
  type: string;
13
13
  controller: Controller;
14
14
  }): Record<string, unknown>;
15
+ export { default as validateClientId } from './utils/validate-client-id';
15
16
  export { default as validateResourceId } from './utils/validate-resource-id';
16
17
  export type { ParameterLike, ParameterLike$opts } from './interfaces';
17
18
  export type { default as Parameter } from './parameter';
@@ -0,0 +1,12 @@
1
+ import Parameter from './index';
2
+ /**
3
+ * A member the resource has but the controller does not accept. Any value for
4
+ * it is rejected with 403 rather than the 400 an unknown member gets.
5
+ *
6
+ * @private
7
+ */
8
+ declare class ForbiddenParameter extends Parameter {
9
+ constructor(path: string);
10
+ validate<V>(): V;
11
+ }
12
+ export default ForbiddenParameter;
@@ -8,6 +8,14 @@ declare class Parameter extends FreezeableSet<unknown> {
8
8
  type: string;
9
9
  required: boolean;
10
10
  sanitize: boolean;
11
+ /**
12
+ * Whether the parameter only accepts the given `values`. A parameter built
13
+ * without `values` accepts any value of its type; one built with `values`
14
+ * accepts only those — so an empty list accepts nothing. (Both used to look
15
+ * alike as an empty set, which let e.g. `?include=anything` through on a
16
+ * serializer without relationships, where JSON:API requires a 400.)
17
+ */
18
+ restricted: boolean;
11
19
  constructor({ path, type, values, required, sanitize }: Parameter$opts);
12
20
  validate<V>(value: V): V;
13
21
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reject `data.id` on a create request before the parameter tree runs, so it
3
+ * is answered with 403 (unsupported client-generated ID) rather than the 400
4
+ * an unknown member gets.
5
+ *
6
+ * @private
7
+ */
8
+ export default function validateClientId(params: Record<string, unknown>): boolean;
@@ -1,6 +1,9 @@
1
+ import type { Bundle$Namespace } from '../loader';
1
2
  import type { Model, ModelClass } from '../database';
2
3
  import type { JSONAPI$Document, JSONAPI$DocumentLinks, JSONAPI$ResourceObject, JSONAPI$RelationshipObject } from '../jsonapi';
3
4
  import type { Serializer$opts } from './interfaces';
5
+ import type { Linkage } from './utils/load-linkage';
6
+ import type { IncludeTree } from './utils/include-tree';
4
7
  /**
5
8
  * ## Overview
6
9
  *
@@ -155,6 +158,17 @@ import type { Serializer$opts } from './interfaces';
155
158
  * If we request that the `posts` association is included from the `/users`
156
159
  * endpoint, we will only get the `attributes` that the `PostsSerializer` has
157
160
  * defined even though the response is processed by the `UsersSerializer`.
161
+ * The same goes for relationships: each included resource carries the
162
+ * `relationships` its own Serializer declares in `hasOne` and `hasMany`.
163
+ *
164
+ * Included resources follow the request's namespace: from `/admin/posts`,
165
+ * included comments are serialized by `AdminCommentsSerializer` when it
166
+ * exists and by `CommentsSerializer` otherwise (the same fallback a
167
+ * namespaced Controller uses), and their links point into `/admin`.
168
+ *
169
+ * Relationship paths may be nested, e.g. `/posts?include=comments.user`, up
170
+ * to the controller's `maxIncludeDepth` (3 by default). The intermediate
171
+ * resources (the comments) are included along with the leaves (their users).
158
172
  *
159
173
  * #### Sparse Fieldsets
160
174
  *
@@ -401,6 +415,17 @@ declare class Serializer<T extends Model> {
401
415
  * @private
402
416
  */
403
417
  namespace: string;
418
+ /**
419
+ * Every Serializer of the application, keyed by namespaced path (`posts`,
420
+ * `admin/posts`). Attached once all of them are built, and used by
421
+ * `serializerFor()` to serialize related resources in this Serializer's
422
+ * namespace.
423
+ *
424
+ * @property serializers
425
+ * @type {Map}
426
+ * @private
427
+ */
428
+ serializers?: Bundle$Namespace<Serializer<Model>>;
404
429
  constructor({ model, parent, namespace }: Serializer$opts<T>);
405
430
  /**
406
431
  * Transform an array of Model instances or a single Model instance into a
@@ -423,25 +448,40 @@ declare class Serializer<T extends Model> {
423
448
  * the resource and relationship objects in the returned [JSON API](
424
449
  * http://jsonapi.org) document object.
425
450
  *
426
- * @param {Array} options.include - An array of strings containing the
427
- * relationship keys that should be added to the top level included object of
428
- * the returned [JSON API](http://jsonapi.org) document object.
451
+ * @param {Array} options.include - An array of relationship paths (e.g.
452
+ * `'comments'` or `'comments.user'`) whose resources should be added to the
453
+ * top level included object of the returned [JSON API](http://jsonapi.org)
454
+ * document object. Intermediate resources of a nested path are included too.
455
+ *
456
+ * @param {String} options.namespace - The namespace of the request, i.e. of
457
+ * the Controller handling it. Every link in the document is built in it, and
458
+ * included resources are serialized by their Serializer in it (falling back
459
+ * to the root). Defaults to this Serializer's namespace — which is the root
460
+ * one when a namespaced Controller has no Serializer of its own, so the
461
+ * Controller passes its namespace explicitly.
429
462
  *
430
463
  * @return {Promise} Resolves with a [JSON API](http://jsonapi.org) document
431
464
  * object.
432
465
  *
433
466
  * @private
434
467
  */
435
- format({ data, links, domain, include }: {
468
+ format({ data, links, domain, include, namespace }: {
436
469
  data: T | Array<T>;
437
470
  links: JSONAPI$DocumentLinks;
438
471
  domain: string;
439
472
  include: Array<string>;
473
+ namespace?: string;
440
474
  }): Promise<JSONAPI$Document>;
441
475
  /**
442
476
  * Transform a single Model instance into a [JSON API](http://jsonapi.org)
443
477
  * resource object.
444
478
  *
479
+ * Relationships are serialized in one of two ways. By default each one is
480
+ * read from the Model instance (for primary data the query has already
481
+ * loaded them). When `linkage` is given — as it is for included resources,
482
+ * whose relationships are batch-loaded by `addIncluded()` — the resource
483
+ * linkage is built from it instead, without touching the database.
484
+ *
445
485
  * @method formatOne
446
486
  *
447
487
  * @param {Object} options - An options object used for building the returned
@@ -458,30 +498,33 @@ declare class Serializer<T extends Model> {
458
498
  * the top level links object or relationship links objects in the returned
459
499
  * [JSON API](http://jsonapi.org) resource object.
460
500
  *
461
- * @param {Array} options.include - An array of strings containing the
462
- * relationship keys that should be added to the top level included object of
463
- * a [JSON API](http://jsonapi.org) document object.
501
+ * @param {Array} options.include - An array of the relationship keys whose
502
+ * related records should be collected into `options.related`.
464
503
  *
465
- * @param {Array} options.included - An array of [JSON API](
466
- * http://jsonapi.org) resource objects that will be added to the top level
467
- * included array of a [JSON API](http://jsonapi.org) document object.
504
+ * @param {Map} options.related - Collects, per relationship key in
505
+ * `options.include`, the related Model instances that belong in the top
506
+ * level included object of a [JSON API](http://jsonapi.org) document object.
468
507
  *
469
- * @param {Boolean} options.formatRelationships - Wether or not
470
- * relationships should be formatted and included in the returned
471
- * [JSON API](http://jsonapi.org) resource object.
508
+ * @param {Object} options.linkage - Pre-loaded resource linkage (related
509
+ * primary keys per relationship key) to serialize relationships from.
510
+ *
511
+ * @param {String} options.namespace - The namespace to build links in.
512
+ * Defaults to this Serializer's; included resources pass the namespace of
513
+ * the request, so every link in a document points into the same namespace.
472
514
  *
473
515
  * @return {Promise} Resolves with a [JSON API](http://jsonapi.org) resource
474
516
  * object.
475
517
  *
476
518
  * @private
477
519
  */
478
- formatOne({ item, links, domain, include, included, formatRelationships }: {
520
+ formatOne({ item, links, domain, include, related, linkage, namespace }: {
479
521
  item: T;
480
522
  links?: boolean;
481
523
  domain: string;
482
- include: Array<string>;
483
- included: Array<JSONAPI$ResourceObject>;
484
- formatRelationships?: boolean;
524
+ include?: Array<string>;
525
+ related?: Map<string, Array<Model>>;
526
+ linkage?: Linkage;
527
+ namespace?: string;
485
528
  }): Promise<JSONAPI$ResourceObject>;
486
529
  /**
487
530
  * Transform a single Model instance into a [JSON API](http://jsonapi.org)
@@ -489,33 +532,65 @@ declare class Serializer<T extends Model> {
489
532
  *
490
533
  * @method formatRelationship
491
534
  *
492
- * @param {Object} options - An options object used for building the returned
535
+ * @param {Model} item - The Model instance to transform into the returned
493
536
  * [JSON API](http://jsonapi.org) relationship object.
494
537
  *
495
- * @param {Model} options.item - The Model instance to transform into the
538
+ * @param {String} domain - A string used to build links included in the
496
539
  * returned [JSON API](http://jsonapi.org) relationship object.
497
540
  *
498
- * @param {String} options.domain - A string used to build links included in
499
- * the returned [JSON API](http://jsonapi.org) relationship object.
500
- *
501
- * @param {Array} options.include - An array of strings containing the
502
- * relationship keys that should be added to the top level included object of
503
- * a [JSON API](http://jsonapi.org) document object.
541
+ * @return {Object} A [JSON API](http://jsonapi.org) relationship object.
504
542
  *
505
- * @param {Array} options.included - An array of [JSON API](
506
- * http://jsonapi.org) resource objects that will be added to the top level
507
- * included array of a [JSON API](http://jsonapi.org) document object.
508
- *
509
- * @return {Promise} Resolves with a [JSON API](http://jsonapi.org)
510
- * relationship object.
543
+ * @private
544
+ */
545
+ formatRelationship(item: Model, domain: string, namespace?: string): JSONAPI$RelationshipObject;
546
+ /**
547
+ * Build a [JSON API](http://jsonapi.org) relationship object from resource
548
+ * linkage, in the same shape `formatRelationship()` produces from Model
549
+ * instances: to-one relationships carry a `links` object, to-many ones only
550
+ * `data`, and a missing to-one relationship is `{ data: null }`.
511
551
  *
552
+ * @method formatLinkage
553
+ * @private
554
+ */
555
+ formatLinkage(domain: string, type: string | undefined, linkage: Array<string> | string | null | undefined, namespace?: string): JSONAPI$RelationshipObject;
556
+ /**
557
+ * Add `records` (instances of `model`) to `included` as resource objects,
558
+ * then recurse into the relationships named in `tree`. Each is serialized by
559
+ * `model`'s Serializer in this Serializer's namespace (`serializerFor()`), so
560
+ * `/admin/posts?include=comments` uses `AdminCommentsSerializer` when there
561
+ * is one and `CommentsSerializer` otherwise. The relationships of every level
562
+ * are batch-loaded with one query per relationship, not one per record.
563
+ *
564
+ * @method addIncluded
512
565
  * @private
513
566
  */
514
- formatRelationship({ item, domain, include, included }: {
515
- item: Model;
567
+ addIncluded({ model, records, tree, domain, included, namespace }: {
568
+ model: ModelClass;
569
+ records: Array<Model>;
570
+ tree: IncludeTree;
516
571
  domain: string;
517
- include: boolean;
518
- included: Array<JSONAPI$ResourceObject>;
519
- }): Promise<JSONAPI$RelationshipObject>;
572
+ included: Map<string, JSONAPI$ResourceObject>;
573
+ namespace: string;
574
+ }): Promise<void>;
575
+ /**
576
+ * Resolve the Serializer for `model` in `namespace` (this Serializer's by
577
+ * default), the way a namespaced Controller resolves its own:
578
+ * `admin/comments` if it exists, otherwise the closest ancestor namespace's,
579
+ * down to the root `comments` Serializer. Falls back to `model.serializer`
580
+ * when this Serializer was not created by an application (e.g. in
581
+ * isolation).
582
+ *
583
+ * Pass the request's namespace when there is one: a Serializer's own
584
+ * namespace is the root one whenever it is a namespaced Controller's
585
+ * fallback.
586
+ *
587
+ * @method serializerFor
588
+ * @private
589
+ */
590
+ serializerFor(model: ModelClass, namespace?: string): Serializer<Model>;
591
+ /**
592
+ * @private
593
+ */
594
+ linkFor(domain: string, type: string, id: string, namespace?: string): string;
520
595
  }
521
596
  export default Serializer;