shelving 1.181.0 → 1.181.2

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.
@@ -12,14 +12,19 @@ export type MockAPICall = {
12
12
  readonly response: Response;
13
13
  readonly result: unknown;
14
14
  };
15
- /** Construction options for a `MockAPIProvider`. */
16
- export interface MockAPIProviderOptions extends ClientAPIProviderOptions {
15
+ /**
16
+ * Construction options for a `MockAPIProvider`
17
+ * - Same as options for a normal `ClientAPIProvider`, but with an optional URL.
18
+ */
19
+ export interface MockAPIProviderOptions extends Omit<ClientAPIProviderOptions, "url"> {
20
+ /** Optional URL, defaults to `"https://api.mock.com"` */
21
+ url?: ClientAPIProviderOptions["url"];
17
22
  }
18
23
  /** Provider that logs API calls without sending network requests. */
19
24
  export declare class MockAPIProvider extends ClientAPIProvider {
20
25
  readonly calls: MockAPICall[];
21
26
  readonly handler: RequestHandler;
22
- constructor(handler: RequestHandler, options?: MockAPIProviderOptions);
27
+ constructor(handler: RequestHandler, { url, ...options }?: MockAPIProviderOptions);
23
28
  /**
24
29
  * Log a `fetch()` call without using the network.
25
30
  * - If `getResult` is configured, its return value is returned as-is (no schema validation).
@@ -4,8 +4,8 @@ import { ClientAPIProvider } from "./ClientAPIProvider.js";
4
4
  export class MockAPIProvider extends ClientAPIProvider {
5
5
  calls = [];
6
6
  handler;
7
- constructor(handler, options = { url: "https://api.mock.com" }) {
8
- super(options);
7
+ constructor(handler, { url = "https://api.mock.com", ...options } = {}) {
8
+ super({ url, ...options });
9
9
  this.handler = handler;
10
10
  }
11
11
  /**
@@ -1,6 +1,10 @@
1
1
  import { type EndpointHandlers } from "../endpoint/util.js";
2
2
  import { MockAPIProvider, type MockAPIProviderOptions } from "./MockAPIProvider.js";
3
- export interface MockEndpointAPIProviderOptions<C> extends MockAPIProviderOptions {
3
+ /**
4
+ * Construction options for a `MockAPIProvider`
5
+ * - Same as options for a normal `MockAPIProviderOptions`, but with a `context` property for the endpoints.
6
+ */
7
+ export interface MockEndpointAPIProviderOptions<C = void> extends MockAPIProviderOptions {
4
8
  context: C;
5
9
  }
6
10
  /**
@@ -1,27 +1,38 @@
1
1
  import { DataSchema } from "../../schema/DataSchema.js";
2
2
  import { NumberSchema } from "../../schema/NumberSchema.js";
3
3
  import type { Schema, Schemas } from "../../schema/Schema.js";
4
+ import type { ImmutableArray } from "../../util/array.js";
4
5
  import type { Data } from "../../util/data.js";
5
6
  import type { Identifier, Item } from "../../util/item.js";
6
7
  /** Default identifier schema (integer). */
7
8
  export declare const ID: NumberSchema;
8
9
  /** Declarative definition of a database collection/table. */
9
- export declare class Collection<K extends string, I extends Identifier, T extends Data> extends DataSchema<T> {
10
+ export declare class Collection<N extends string, I extends Identifier, T extends Data> extends DataSchema<T> {
10
11
  /** Collection name (used as the table/collection key). */
11
- readonly name: K;
12
+ readonly name: N;
12
13
  /** Schema for the identifier type. */
13
14
  readonly id: Schema<I>;
14
15
  /** Schema for a complete item (id + data). */
15
16
  readonly item: DataSchema<Item<I, T>>;
16
- constructor(name: K, id: Schema<I>, data: Schemas<T> | DataSchema<T>);
17
+ constructor(name: N, id: Schema<I>, data: Schemas<T> | DataSchema<T>);
17
18
  }
18
19
  /** Shortcut factory for creating a Collection. */
19
20
  export declare function COLLECTION<K extends string, I extends Identifier, T extends Data>(name: K, id: Schema<I>, data: Schemas<T> | DataSchema<T>): Collection<K, I, T>;
20
- /** A readonly array of Collection instances, possibly with a standardised `Identifier`. */
21
- export type Collections<I extends Identifier = Identifier> = ReadonlyArray<Collection<string, I, Data>>;
22
- /** Extract the union of collection key strings from a Collections type. */
23
- export type CollectionKeys<C extends Collections> = C[number]["name"];
24
- /** Convert a Collections array type to a Database-style object mapping. */
21
+ /** Any collection object, possibly with a standardised `Identifier` type. */
22
+ export type AnyCollection<I extends Identifier = Identifier> = Collection<string, I, Data>;
23
+ /** Extract the string name from a `Collection` instance. */
24
+ export type CollectionName<C extends AnyCollection> = C extends Collection<infer N, infer _I, infer _T> ? N : never;
25
+ /** Extract the `Identifier` type from a `Collection` instance. */
26
+ export type CollectionIdentifier<C extends AnyCollection> = C extends Collection<infer _N, infer I, infer _T> ? I : never;
27
+ /** Extract the `Data` type from a `Collection` instance. */
28
+ export type CollectionData<C extends AnyCollection> = C extends Collection<infer _N, infer _I, infer T> ? T : never;
29
+ /** Extract the `Item` type from a `Collection` instance. */
30
+ export type CollectionItem<C extends AnyCollection> = C extends Collection<infer _N, infer I, infer T> ? Item<I, T> : never;
31
+ /** A readonly array of Collection instances, possibly with a standardised `Identifier` type. */
32
+ export type Collections<I extends Identifier = Identifier> = ImmutableArray<AnyCollection<I>>;
33
+ /** Extract the union of string collection names from a `Collections` type. */
34
+ export type CollectionNames<C extends Collections> = C[number]["name"];
35
+ /** Convert a `Collections` array type to a Database-style object mapping in `{ name: data }` format. */
25
36
  export type CollectionsDatabase<C extends Collections> = {
26
- [E in C[number] as E extends Collection<infer K, any, any> ? K : never]: E extends Collection<string, any, infer T> ? T : never;
37
+ [E in C[number] as E extends Collection<infer N, Identifier, Data> ? N : never]: E extends Collection<string, Identifier, infer T> ? T : never;
27
38
  };
@@ -1,70 +1,85 @@
1
1
  import { ValueError } from "../../error/ValueError.js";
2
2
  import { PARTIAL } from "../../schema/DataSchema.js";
3
3
  import { getNamedMessage } from "../../util/error.js";
4
- import { validateData } from "../../util/validate.js";
5
4
  import { ThroughDBProvider } from "./ThroughDBProvider.js";
6
5
  /** Validate an asynchronous source provider (source can have any type because validation guarantees the type). */
7
6
  export class ValidationDBProvider extends ThroughDBProvider {
8
7
  async getItem(collection, id) {
9
- return _validateItem(collection.name, await super.getItem(collection, id), collection.id, collection, this.getItem);
8
+ return _validateItem(collection, await super.getItem(collection, id), this.getItem);
10
9
  }
11
10
  async *getItemSequence(collection, id) {
12
11
  for await (const item of super.getItemSequence(collection, id))
13
- yield _validateItem(collection.name, item, collection.id, collection, this.getItemSequence);
12
+ yield _validateItem(collection, item, this.getItemSequence);
14
13
  }
15
- addItem(collection, data) {
16
- return super.addItem(collection, validateData(data, collection.props));
14
+ async addItem(collection, data) {
15
+ return _validateIdentifier(collection, await super.addItem(collection, collection.validate(data)), this.addItem);
17
16
  }
18
17
  setItem(collection, id, data) {
19
- return super.setItem(collection, id, validateData(data, collection.props));
18
+ return super.setItem(collection, id, collection.validate(data));
20
19
  }
21
20
  updateItem(collection, id, updates) {
22
- return super.updateItem(collection, id, _validateUpdates(collection.name, updates, collection, this.updateItem));
21
+ return super.updateItem(collection, id, _validateUpdates(collection, updates, this.updateItem));
23
22
  }
24
23
  countQuery(collection, query) {
25
24
  return super.countQuery(collection, query);
26
25
  }
27
26
  async getQuery(collection, query) {
28
- return _validateItems(collection.name, await super.getQuery(collection, query), collection.id, collection, this.getQuery);
27
+ return _validateItems(collection, await super.getQuery(collection, query), this.getQuery);
29
28
  }
30
29
  async *getQuerySequence(collection, query) {
31
30
  for await (const items of super.getQuerySequence(collection, query))
32
- yield _validateItems(collection.name, items, collection.id, collection, this.getQuerySequence);
31
+ yield _validateItems(collection, items, this.getQuerySequence);
33
32
  }
34
33
  setQuery(collection, query, data) {
35
34
  return super.setQuery(collection, query, collection.validate(data));
36
35
  }
37
36
  updateQuery(collection, query, updates) {
38
- return super.updateQuery(collection, query, _validateUpdates(collection.name, updates, collection, this.updateQuery));
37
+ return super.updateQuery(collection, query, _validateUpdates(collection, updates, this.updateQuery));
39
38
  }
40
39
  deleteQuery(collection, query) {
41
40
  return super.deleteQuery(collection, query);
42
41
  }
43
42
  }
44
- function _validateItem(collection, item, identifier, schema, caller) {
43
+ /**
44
+ * Validate a returned `id` for a collection item.
45
+ */
46
+ function _validateIdentifier(collection, id, caller) {
47
+ try {
48
+ return collection.id.validate(id);
49
+ }
50
+ catch (thrown) {
51
+ if (typeof thrown !== "string")
52
+ throw thrown;
53
+ throw new ValueError(`Invalid identifier for "${collection}"\n${thrown}`, { received: id, caller });
54
+ }
55
+ }
56
+ function _validateItem(collection, //
57
+ item, caller) {
45
58
  if (!item)
46
59
  return undefined;
47
60
  try {
48
- return validateData(item, { id: identifier, ...schema.props });
61
+ return collection.item.validate(item);
49
62
  }
50
63
  catch (thrown) {
51
64
  if (typeof thrown !== "string")
52
65
  throw thrown;
53
- throw new ValueError(`Invalid data for "${collection}"\n${thrown}`, { item, caller });
66
+ throw new ValueError(`Invalid data for "${collection}"\n${thrown}`, { received: item, caller });
54
67
  }
55
68
  }
56
69
  /**
57
70
  * Validate a set of entities for this query reference.
58
71
  * @throws `ValueError` if one or more items did not validate (conflict because the program is not in an expected state).
59
72
  */
60
- function _validateItems(collection, items, identifier, schema, caller) {
61
- return Array.from(_yieldValidItems(collection, items, { id: identifier, ...schema.props }, caller));
73
+ function _validateItems(collection, //
74
+ items, caller) {
75
+ return Array.from(_yieldValidItems(collection, items, caller));
62
76
  }
63
- function* _yieldValidItems(collection, items, validators, caller) {
77
+ function* _yieldValidItems(collection, //
78
+ items, caller) {
64
79
  const messages = [];
65
80
  for (const item of items) {
66
81
  try {
67
- yield validateData(item, validators);
82
+ yield collection.item.validate(item);
68
83
  }
69
84
  catch (thrown) {
70
85
  if (typeof thrown !== "string")
@@ -73,15 +88,16 @@ function* _yieldValidItems(collection, items, validators, caller) {
73
88
  }
74
89
  }
75
90
  if (messages.length)
76
- throw new ValueError(`Invalid data for "${collection}"\n${messages.join("\n")}`, { items, caller });
91
+ throw new ValueError(`Invalid data for "${collection}"\n${messages.join("\n")}`, { received: items, caller });
77
92
  }
78
- function _validateUpdates(collection, updates, schema, caller) {
93
+ function _validateUpdates(collection, //
94
+ updates, caller) {
79
95
  try {
80
- return validateData(updates, PARTIAL(schema).props);
96
+ return PARTIAL(collection).validate(updates);
81
97
  }
82
98
  catch (thrown) {
83
99
  if (typeof thrown !== "string")
84
100
  throw thrown;
85
- throw new ValueError(`Invalid updates for "${collection}"\n${thrown}`, { updates, caller });
101
+ throw new ValueError(`Invalid updates for "${collection}"\n${thrown}`, { received: updates, caller });
86
102
  }
87
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.181.0",
3
+ "version": "1.181.2",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",