@afora/core 0.0.7 → 0.0.8
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.
- package/README.md +34 -6
- package/dist/core/application/start.d.ts +7 -0
- package/dist/core/application/types/application.interface.d.ts +36 -2
- package/dist/core/command/types/command.interface.d.ts +7 -0
- package/dist/core/entity/abstract-entity.d.ts +28 -1
- package/dist/core/entity/entity.d.ts +1 -1
- package/dist/core/entity/event.d.ts +13 -2
- package/dist/core/entity/exceptions.d.ts +8 -0
- package/dist/core/entity/types/entity.interface.d.ts +66 -2
- package/dist/core/entity/version.d.ts +13 -0
- package/dist/core/event-policy/event-policy.d.ts +25 -0
- package/dist/core/event-policy/exceptions.d.ts +11 -0
- package/dist/core/event-policy/publish.d.ts +17 -0
- package/dist/core/event-policy/publishers.d.ts +18 -0
- package/dist/core/event-policy/types/event-policy.interface.d.ts +72 -0
- package/dist/core/output/coerce.d.ts +9 -0
- package/dist/core/output/openapi-sink.d.ts +138 -38
- package/dist/core/output/status.d.ts +1 -1
- package/dist/core/parse/parse.d.ts +10 -0
- package/dist/core/parse/strip.d.ts +13 -0
- package/dist/core/repository/exceptions.d.ts +30 -0
- package/dist/core/repository/in-memory.d.ts +14 -1
- package/dist/core/repository/plan.d.ts +20 -0
- package/dist/core/repository/provider.d.ts +1 -1
- package/dist/core/repository/query.d.ts +9 -2
- package/dist/core/repository/repository.d.ts +40 -8
- package/dist/core/repository/state.d.ts +30 -2
- package/dist/core/repository/types/query.interface.d.ts +106 -5
- package/dist/core/repository/types/repository.interface.d.ts +106 -7
- package/dist/experimental.js +4 -4
- package/dist/{index-r78dsvtc.js → index-qxfkyd6k.js} +612 -612
- package/dist/{index-q6hggpzw.js → index-t5qwtyj5.js} +1807 -280
- package/dist/index.d.ts +22 -11
- package/dist/index.js +62 -14
- package/dist/std/blueprint-plugin.d.ts +6 -2
- package/dist/std/blueprint.d.ts +3 -0
- package/dist/std/entity.d.ts +2 -1
- package/dist/std/event-policies.d.ts +2 -0
- package/dist/std/properties/exceptions.d.ts +11 -0
- package/dist/std/properties/json.d.ts +54 -0
- package/dist/std/properties/time.d.ts +4 -0
- package/dist/std/use-cases/crud.d.ts +6 -4
- package/dist/std/use-cases/shared.d.ts +32 -4
- package/dist/std/use-cases/types.d.ts +100 -21
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +347 -2
- package/dist/testing/memory-publisher.d.ts +16 -0
- package/dist/testing/provider-contract.d.ts +14 -0
- package/package.json +3 -2
|
@@ -3,8 +3,93 @@ import type { OperationDescriptor } from "./types/output.interface.js";
|
|
|
3
3
|
declare const METHODS: readonly ["get", "post", "put", "patch", "delete"];
|
|
4
4
|
/** An HTTP method an operation can be routed to. */
|
|
5
5
|
export type OpenAPIMethod = (typeof METHODS)[number];
|
|
6
|
+
/** The status of a successful call of a route; `204` has no body. */
|
|
7
|
+
export type RouteStatus = 200 | 201 | 202 | 204;
|
|
8
|
+
/**
|
|
9
|
+
* A security scheme of the document (`components.securitySchemes`), as
|
|
10
|
+
* OpenAPI 3.1 writes it.
|
|
11
|
+
*/
|
|
12
|
+
export type OpenAPISecurityScheme = {
|
|
13
|
+
readonly type: "http";
|
|
14
|
+
/** `"bearer"`, `"basic"`... */
|
|
15
|
+
readonly scheme: string;
|
|
16
|
+
readonly bearerFormat?: string;
|
|
17
|
+
readonly description?: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "apiKey";
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly in: "header" | "query" | "cookie";
|
|
22
|
+
readonly description?: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly type: "oauth2";
|
|
25
|
+
readonly flows: {
|
|
26
|
+
readonly [flow: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly type: "openIdConnect";
|
|
31
|
+
readonly openIdConnectUrl: string;
|
|
32
|
+
readonly description?: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly type: "mutualTLS";
|
|
35
|
+
readonly description?: string;
|
|
36
|
+
};
|
|
37
|
+
/** The scheme an operation needs, and its scopes (none by default). */
|
|
38
|
+
export interface SecurityRequirement {
|
|
39
|
+
readonly scheme: string;
|
|
40
|
+
readonly scopes?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The options that route operations: shared by the `openapi` sink and the
|
|
44
|
+
* sinks that serve HTTP, so the served routes and the document agree.
|
|
45
|
+
*/
|
|
46
|
+
export interface RouteOptions {
|
|
47
|
+
/** Prefix of every path, e.g. `"/api"`. Empty by default. */
|
|
48
|
+
readonly basePath?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Method, path and success status by operation id, e.g.
|
|
51
|
+
* `{ "Entries.get": { method: "get", path: "/entries/{slug}" } }`. A
|
|
52
|
+
* `{name}` segment of the path fills the scalar input key `name`.
|
|
53
|
+
*/
|
|
54
|
+
readonly route?: {
|
|
55
|
+
readonly [operationId: string]: {
|
|
56
|
+
readonly method?: OpenAPIMethod;
|
|
57
|
+
readonly path?: string;
|
|
58
|
+
/** `200` by default. */
|
|
59
|
+
readonly status?: RouteStatus;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
/** Static headers of the successful responses, by operation id. */
|
|
63
|
+
readonly responses?: {
|
|
64
|
+
readonly [operationId: string]: {
|
|
65
|
+
readonly headers?: {
|
|
66
|
+
readonly [name: string]: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Every response carries a request id: the one the request sent in this
|
|
72
|
+
* header, else a new UUID. `true` is `"x-request-id"`.
|
|
73
|
+
*/
|
|
74
|
+
readonly requestId?: true | string;
|
|
75
|
+
/**
|
|
76
|
+
* Security schemes and the one each operation needs, for the document
|
|
77
|
+
* (`components.securitySchemes` and `security`). `default` applies to
|
|
78
|
+
* every operation not in `operations`; `false` there marks one public.
|
|
79
|
+
* Sinks that serve HTTP do not enforce it.
|
|
80
|
+
*/
|
|
81
|
+
readonly security?: {
|
|
82
|
+
readonly schemes: {
|
|
83
|
+
readonly [name: string]: OpenAPISecurityScheme;
|
|
84
|
+
};
|
|
85
|
+
readonly operations?: {
|
|
86
|
+
readonly [operationId: string]: SecurityRequirement | false;
|
|
87
|
+
};
|
|
88
|
+
readonly default?: SecurityRequirement;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
6
91
|
/** The options of the `openapi` sink. */
|
|
7
|
-
export interface OpenAPISinkOptions {
|
|
92
|
+
export interface OpenAPISinkOptions extends RouteOptions {
|
|
8
93
|
readonly info: {
|
|
9
94
|
readonly title: string;
|
|
10
95
|
readonly version: string;
|
|
@@ -14,21 +99,15 @@ export interface OpenAPISinkOptions {
|
|
|
14
99
|
readonly url: string;
|
|
15
100
|
readonly description?: string;
|
|
16
101
|
}[];
|
|
17
|
-
/** Prefix of every path, e.g. `"/api"`. Empty by default. */
|
|
18
|
-
readonly basePath?: string;
|
|
19
|
-
/** Method and path by operation id, e.g. `{ "Users.login": { path: "/session" } }`. */
|
|
20
|
-
readonly route?: {
|
|
21
|
-
readonly [operationId: string]: {
|
|
22
|
-
readonly method?: OpenAPIMethod;
|
|
23
|
-
readonly path?: string;
|
|
24
|
-
};
|
|
25
|
-
};
|
|
26
102
|
}
|
|
27
103
|
/** An OpenAPI 3.1 document. */
|
|
28
104
|
export interface OpenAPIDocument {
|
|
29
105
|
readonly openapi: "3.1.0";
|
|
30
106
|
readonly info: OpenAPISinkOptions["info"];
|
|
31
107
|
readonly servers?: OpenAPISinkOptions["servers"];
|
|
108
|
+
readonly security?: readonly {
|
|
109
|
+
readonly [scheme: string]: string[];
|
|
110
|
+
}[];
|
|
32
111
|
readonly paths: {
|
|
33
112
|
readonly [path: string]: Record<string, unknown>;
|
|
34
113
|
};
|
|
@@ -36,30 +115,45 @@ export interface OpenAPIDocument {
|
|
|
36
115
|
readonly schemas: {
|
|
37
116
|
readonly [name: string]: OpenAPISchema;
|
|
38
117
|
};
|
|
118
|
+
readonly securitySchemes?: {
|
|
119
|
+
readonly [name: string]: OpenAPISecurityScheme;
|
|
120
|
+
};
|
|
39
121
|
};
|
|
40
122
|
}
|
|
41
|
-
/** The
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
123
|
+
/** The option names of {@link RouteOptions}, for sinks that list their options. */
|
|
124
|
+
export declare const ROUTE_OPTION_KEYS: readonly ["basePath", "route", "responses", "requestId", "security"];
|
|
125
|
+
/**
|
|
126
|
+
* Why the {@link RouteOptions} (`basePath`, `route`, `responses`,
|
|
127
|
+
* `requestId`, `security`) are not valid, naming them `where.<option>`;
|
|
128
|
+
* `undefined` when they are. Other keys of `options` are not looked at.
|
|
129
|
+
*/
|
|
130
|
+
export declare function routeOptionsIssue(options: RouteOptions, where: string): string | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* The header that carries the request id with these options (`"x-request-id"`
|
|
133
|
+
* for `requestId: true`), or `undefined` without `requestId`.
|
|
134
|
+
*/
|
|
135
|
+
export declare function requestIdHeaderOf(options: RouteOptions): string | undefined;
|
|
136
|
+
/** Where an operation is served, and how it answers. */
|
|
47
137
|
export interface OperationRoute {
|
|
48
138
|
readonly method: OpenAPIMethod;
|
|
49
|
-
/** `basePath` included
|
|
139
|
+
/** `basePath` included, parameters as `{name}`. */
|
|
50
140
|
readonly path: string;
|
|
141
|
+
/** The input keys the `{name}` segments of `path` fill, in order. */
|
|
142
|
+
readonly parameters: readonly string[];
|
|
143
|
+
/** The status of a success: `200` unless `route` says otherwise. */
|
|
144
|
+
readonly status: RouteStatus;
|
|
145
|
+
/** The static headers of a success, from `responses`. */
|
|
146
|
+
readonly headers: {
|
|
147
|
+
readonly [name: string]: string;
|
|
148
|
+
};
|
|
51
149
|
}
|
|
52
150
|
/**
|
|
53
|
-
*
|
|
54
|
-
* `
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
* changed per operation id with `route`. Throws `InvalidOutputStage` (with
|
|
60
|
-
* `source`) when `route` names an unknown operation or two operations share a
|
|
61
|
-
* method and path. The routing of the `openapi` sink and of the sinks that
|
|
62
|
-
* serve HTTP.
|
|
151
|
+
* The route of every operation, by id: `POST {basePath}/{kebab(command)}/{kebab(operation)}`
|
|
152
|
+
* with `200`, changed per operation id with `route` and `responses`. Throws
|
|
153
|
+
* `InvalidOutputStage` (with `source`) when `route`, `responses` or
|
|
154
|
+
* `security.operations` name an unknown operation, a path parameter is not a
|
|
155
|
+
* scalar key of the input, or two operations share a method and path. The
|
|
156
|
+
* routing of the `openapi` sink and of the sinks that serve HTTP.
|
|
63
157
|
*/
|
|
64
158
|
export declare function operationRoutes(operations: readonly OperationDescriptor[], options: RouteOptions, source?: string): ReadonlyMap<string, OperationRoute>;
|
|
65
159
|
/**
|
|
@@ -67,17 +161,23 @@ export declare function operationRoutes(operations: readonly OperationDescriptor
|
|
|
67
161
|
*
|
|
68
162
|
* Routes: `POST {basePath}/{kebab(command)}/{kebab(operation)}`, changed per
|
|
69
163
|
* operation id with `route`; two operations on one method and path throw
|
|
70
|
-
* `InvalidOutputStage`.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
164
|
+
* `InvalidOutputStage`. A `{name}` segment of a path is a required path
|
|
165
|
+
* parameter, the input key `name` (a scalar), left out of the body or query.
|
|
166
|
+
* The `operationId` is the id with its dot as an underscore (`Users.register`
|
|
167
|
+
* -> `Users_register`), unique or the build throws. `get` and `delete` send
|
|
168
|
+
* the input as query parameters, which needs every input field to be a
|
|
169
|
+
* scalar property. Responses: the `route` status (`200` by default; `204`
|
|
170
|
+
* without content) with the response schema and the `responses` headers,
|
|
171
|
+
* `400` and `500` always, and the status of each declared error (`statusOf`:
|
|
172
|
+
* `statuses` of the output, else 422); `requestId` adds its header to all of
|
|
173
|
+
* them. Operations carry no repository dependencies, so `404` and `409`
|
|
174
|
+
* appear only when an error that maps to them is declared. Records and
|
|
175
|
+
* entities reachable from inputs and responses are named by class name in
|
|
176
|
+
* `components.schemas`, beside `Problem` (RFC 9457). `security` gives
|
|
177
|
+
* `components.securitySchemes`, the document `security` (`default`) and the
|
|
178
|
+
* `security` of each operation. The `context` of an operation is
|
|
179
|
+
* transport-specific and not part of the document; sinks that serve HTTP map
|
|
180
|
+
* it (headers, middleware).
|
|
81
181
|
*/
|
|
82
182
|
export declare const openapi: import("./types/output.interface.js").SinkFactory<OpenAPIDocument, OpenAPISinkOptions>;
|
|
83
183
|
export {};
|
|
@@ -10,7 +10,7 @@ export declare function raisedOf(error: Exception): Exception;
|
|
|
10
10
|
* The HTTP-style status of an error, in this order: `statuses` by exception
|
|
11
11
|
* name; 400 for invalid input or context (and any exception whose
|
|
12
12
|
* `details.issues` is a list, except `InvalidCommandEnvironment`, a 500); 404 for a missing entity or operation; 409 for
|
|
13
|
-
* a unique violation or an entity that already exists; 401 for
|
|
13
|
+
* a unique violation, a concurrency conflict or an entity that already exists; 401 for
|
|
14
14
|
* `UseCaseInvalidCredentials`; 422 for a class in `operation.errors`; 500 for the
|
|
15
15
|
* rest, errors that are not exceptions included. Exceptions match by name,
|
|
16
16
|
* or by class for `operation.errors`.
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { BlueprintField, IBlueprintCollection } from "../blueprint/types/blueprint.interface.js";
|
|
2
2
|
import type { ParseResult } from "./types/parse.interface.js";
|
|
3
3
|
type Path = readonly (string | number)[];
|
|
4
|
+
/**
|
|
5
|
+
* `Object.entries(properties)`, kept for the next time.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare function fieldsOf(properties: IBlueprintCollection): readonly (readonly [string, BlueprintField])[];
|
|
9
|
+
/**
|
|
10
|
+
* Whether `value` is an object made by `{}` or `Object.create(null)`, as parsed JSON is.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
4
14
|
/**
|
|
5
15
|
* `value` handled by each field of `properties`, or every issue found: each
|
|
6
16
|
* key `properties` does not declare, then those of each field, in order. Keys
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AnyBlueprint, AnyRecordClass, BlueprintField } from "../blueprint/types/blueprint.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* A copy of `value` without the keys `shape` does not declare, at any depth:
|
|
4
|
+
* in nested blueprints, the items of `listOf`, `optionalOf` and the records
|
|
5
|
+
* and entities held as fields. For a payload with more than the operation
|
|
6
|
+
* reads, such as a webhook from another service, before it is parsed, since
|
|
7
|
+
* parsing rejects an undeclared key (`undeclared_key`).
|
|
8
|
+
*
|
|
9
|
+
* It does not validate, never throws and does not change `value`: a value of
|
|
10
|
+
* the wrong type, a property's value (even an object) and a record instance
|
|
11
|
+
* come back as they are, for parsing to accept or reject.
|
|
12
|
+
*/
|
|
13
|
+
export declare function stripUndeclared(shape: AnyBlueprint | AnyRecordClass | BlueprintField, value: unknown): unknown;
|
|
@@ -24,7 +24,37 @@ export declare const InvalidRepositoryData: import("../../index.js").DeclaredExc
|
|
|
24
24
|
export declare const RepositoryUniqueViolation: import("../../index.js").DeclaredExceptionClass<{
|
|
25
25
|
readonly key: string;
|
|
26
26
|
}>;
|
|
27
|
+
/**
|
|
28
|
+
* A repository declared at module level was called after an application
|
|
29
|
+
* bound it to its provider: the calls of the application go through
|
|
30
|
+
* `app.repositories.<key>`, its use cases (`app.run`, `app.useCase`) or a
|
|
31
|
+
* repository given to `execute`; `source` is the repository name.
|
|
32
|
+
*/
|
|
33
|
+
export declare const RepositoryUsedOutsideApplication: import("../../index.js").DeclaredExceptionClass<{
|
|
34
|
+
readonly application: string;
|
|
35
|
+
readonly repository: string;
|
|
36
|
+
}>;
|
|
27
37
|
/** No entity of the id was found; `source` is the repository name. */
|
|
28
38
|
export declare const RepositoryEntityNotFound: import("../../index.js").DeclaredExceptionClass<{
|
|
29
39
|
readonly id: string;
|
|
30
40
|
}>;
|
|
41
|
+
/**
|
|
42
|
+
* `update` of a versioned entity found another version stored than the one
|
|
43
|
+
* it read: someone else wrote it in between (optimistic concurrency).
|
|
44
|
+
* `actual` is the stored version, when the provider knows it; `source` is the
|
|
45
|
+
* repository name. An output answers it with 409.
|
|
46
|
+
*/
|
|
47
|
+
export declare const RepositoryConcurrencyConflict: import("../../index.js").DeclaredExceptionClass<{
|
|
48
|
+
readonly id: string;
|
|
49
|
+
readonly expected: number;
|
|
50
|
+
readonly actual?: number;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* `increment` got deltas it cannot apply: a key that is not a number in the
|
|
54
|
+
* `change` list of the entity, or a value that is not a finite number. Its
|
|
55
|
+
* `issues` name each, so an output answers it as invalid input.
|
|
56
|
+
*/
|
|
57
|
+
export declare const InvalidRepositoryIncrement: import("../../index.js").DeclaredExceptionClass<{
|
|
58
|
+
readonly reason: string;
|
|
59
|
+
readonly issues: readonly Issue[];
|
|
60
|
+
}>;
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { IRepositoryProvider } from "./types/repository.interface.js";
|
|
2
|
+
/**
|
|
3
|
+
* The provider of `inMemoryProvider`: it runs transactions, reads by cursor,
|
|
4
|
+
* honors expected versions, increments atomically and leaves expired records out.
|
|
5
|
+
*/
|
|
6
|
+
export type InMemoryProvider = IRepositoryProvider & {
|
|
7
|
+
readonly capabilities: {
|
|
8
|
+
readonly transactions: true;
|
|
9
|
+
readonly keyset: true;
|
|
10
|
+
readonly versioning: true;
|
|
11
|
+
readonly atomic: true;
|
|
12
|
+
readonly expiry: true;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
2
15
|
/**
|
|
3
16
|
* A provider that keeps the values in memory, one table per entity class,
|
|
4
17
|
* enforcing its unique keys. Each `repositoryOf` without a provider gets its
|
|
@@ -7,4 +20,4 @@ import type { IRepositoryProvider } from "./types/repository.interface.js";
|
|
|
7
20
|
* on rejection, runs one at a time and makes other operations wait; nested
|
|
8
21
|
* transactions are savepoints.
|
|
9
22
|
*/
|
|
10
|
-
export declare function inMemoryProvider():
|
|
23
|
+
export declare function inMemoryProvider(): InMemoryProvider;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RepositoryIndex, RepositoryOrder, RepositoryPlan } from "./types/query.interface.js";
|
|
2
|
+
/** What the planner reads of a query: its conditions and its orders. */
|
|
3
|
+
export interface PlannedQuery {
|
|
4
|
+
readonly where?: Readonly<Record<string, unknown>>;
|
|
5
|
+
readonly orderBy?: readonly RepositoryOrder[];
|
|
6
|
+
}
|
|
7
|
+
/** What the planner reads of a repository: its unique keys and its indexes. */
|
|
8
|
+
export interface PlannedRepository {
|
|
9
|
+
readonly unique: readonly string[];
|
|
10
|
+
readonly indexes?: Readonly<Record<string, RepositoryIndex>>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* How `query` reaches its rows. A condition on `id`, or on another unique
|
|
14
|
+
* key, by equality or `in` (a batch of key lookups) gives `id` or `unique`.
|
|
15
|
+
* An index whose `where` keys all hold a condition by equality, and whose
|
|
16
|
+
* `orderBy` is the first order of the query (when it has one), gives
|
|
17
|
+
* `index`: the one with the most keys, the first declared on a tie. The rest
|
|
18
|
+
* is a `scan`. It reads nothing, and expects a query `prepareQuery` checked.
|
|
19
|
+
*/
|
|
20
|
+
export declare function planQuery(repository: PlannedRepository, query?: PlannedQuery): RepositoryPlan;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProviderDescription, ProviderOptions, ProviderSettings } from "./types/query.interface.js";
|
|
2
2
|
import type { IRepositoryProvider } from "./types/repository.interface.js";
|
|
3
3
|
/** Creates a provider once and attaches an immutable, serializable declaration out of band. */
|
|
4
|
-
export declare function providerOf<Options extends ProviderOptions = ProviderOptions, Provider extends IRepositoryProvider = IRepositoryProvider>(settings: ProviderSettings<Options, Provider>): Provider;
|
|
4
|
+
export declare function providerOf<Options extends ProviderOptions = ProviderOptions, const Provider extends IRepositoryProvider = IRepositoryProvider>(settings: ProviderSettings<Options, Provider>): Provider;
|
|
5
5
|
/** Describes a named provider; an anonymous one needs an explicit import on extraction. */
|
|
6
6
|
export declare function providerDescriptionOf(provider: IRepositoryProvider): ProviderDescription;
|
|
@@ -4,6 +4,13 @@ import type { RepositoryContext, RepositoryData } from "./types/repository.inter
|
|
|
4
4
|
export declare function prepareQuery(context: RepositoryContext, input?: unknown, map?: (key: string, value: unknown) => unknown, options?: readonly string[]): RepositoryQuery;
|
|
5
5
|
/** Structural JSON/data equality without depending on object key insertion order. */
|
|
6
6
|
export declare function sameValue(a: unknown, b: unknown): boolean;
|
|
7
|
-
|
|
7
|
+
/** The time of `query.now`, or the current one. */
|
|
8
|
+
export declare function nowOf(query: ProviderQuery): number;
|
|
9
|
+
/**
|
|
10
|
+
* Whether `data` is a row of `query`: alive unless `withDeleted`, not expired
|
|
11
|
+
* by `expiry` (the key of `context.expiry`) unless `withExpired`, and matching
|
|
12
|
+
* `where`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function matchesQuery(data: RepositoryData, query: ProviderQuery, expiry?: string, now?: number): boolean;
|
|
8
15
|
/** The rows of `query` after ordering, `after`, offset and limit. */
|
|
9
|
-
export declare function selectRows(rows: Iterable<RepositoryData>, query: ProviderQuery): RepositoryData[];
|
|
16
|
+
export declare function selectRows(rows: Iterable<RepositoryData>, query: ProviderQuery, expiry?: string): RepositoryData[];
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { type EnvironmentDependencies, type MergedEnvironment } from "../environment/environment.js";
|
|
2
2
|
import { Kind } from "../symbols/kind.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProviderCapabilities, RepositoryIndex, RepositoryScans } from "./types/query.interface.js";
|
|
4
|
+
import type { AnyRepository, AnyRepositoryEntity, EntityInstanceOf, EntityKeyOf, IRepositoryProvider, ProviderKeyset, RepositoryAction, RepositoryContext, RepositoryData, RepositoryOf as RepositoryType } from "./types/repository.interface.js";
|
|
4
5
|
/** What `repositoryOf` takes. */
|
|
5
|
-
export interface RepositorySettings<Entity extends AnyRepositoryEntity, Actions extends readonly RepositoryAction<Entity>[], Environment extends EnvironmentDependencies | undefined> {
|
|
6
|
+
export interface RepositorySettings<Entity extends AnyRepositoryEntity, Actions extends readonly RepositoryAction<Entity>[], Environment extends EnvironmentDependencies | undefined, Capabilities extends ProviderCapabilities = ProviderCapabilities> {
|
|
6
7
|
/** The entity class whose entities it holds. */
|
|
7
8
|
readonly entity: Entity;
|
|
8
9
|
/** Its methods, e.g. `["findById", "findByEmail", "findManyByName", "create"]`. */
|
|
9
10
|
readonly actions: Actions;
|
|
10
11
|
/** Physically remove rows (default), or retain them with `deletedAt`. */
|
|
11
12
|
readonly deletion?: "hard" | "soft";
|
|
12
|
-
/**
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* How it reads and writes; a new `inMemoryProvider()` by default. When the
|
|
15
|
+
* type of its `capabilities.keyset` cannot be `true`, the repository has no
|
|
16
|
+
* `findCursor`. Only `capabilities` is inferred, so the methods of a
|
|
17
|
+
* provider written inline keep their parameter types.
|
|
18
|
+
*/
|
|
19
|
+
readonly provider?: IRepositoryProvider<MergedEnvironment<Environment>> & {
|
|
20
|
+
readonly capabilities?: Capabilities;
|
|
21
|
+
};
|
|
14
22
|
/** The environments the provider gets, merged, as `context.env`. */
|
|
15
23
|
readonly environmentDependencies?: Environment;
|
|
16
24
|
/**
|
|
@@ -19,7 +27,25 @@ export interface RepositorySettings<Entity extends AnyRepositoryEntity, Actions
|
|
|
19
27
|
* loaded when read.
|
|
20
28
|
*/
|
|
21
29
|
readonly relations?: readonly AnyRepository[];
|
|
30
|
+
/**
|
|
31
|
+
* Its access patterns, by name, e.g.
|
|
32
|
+
* `{ byTypeStatus: { where: ["type", "status"], orderBy: "publishedAt" } }`:
|
|
33
|
+
* a query that matches one reads through it, and the provider gets its
|
|
34
|
+
* name as `query.index` and every index as `context.indexes`.
|
|
35
|
+
*/
|
|
36
|
+
readonly indexes?: Readonly<Record<string, RepositoryIndex<EntityKeyOf<Entity>>>>;
|
|
37
|
+
/**
|
|
38
|
+
* What a query only a scan serves does: runs (`"allow"`, the default),
|
|
39
|
+
* logs `repository-scan` and runs (`"warn"`), or throws
|
|
40
|
+
* `InvalidRepositoryQuery` (`"reject"`).
|
|
41
|
+
*/
|
|
42
|
+
readonly scans?: RepositoryScans;
|
|
22
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The repository `repositoryOf` gives: without `findCursor` when its provider
|
|
46
|
+
* cannot read by cursor (`Keyset` is `false`).
|
|
47
|
+
*/
|
|
48
|
+
type RepositoryOfProvider<Entity extends AnyRepositoryEntity, Actions extends readonly RepositoryAction<Entity>[], Environment, Keyset extends boolean> = Keyset extends true ? RepositoryType<Entity, Actions, Environment> : RepositoryType<Entity, Actions, Environment, false>;
|
|
23
49
|
/**
|
|
24
50
|
* A repository: the base of every class `repositoryOf` makes. Its methods are
|
|
25
51
|
* the actions it was declared with; extend it, through `RepositoryOf`, to add
|
|
@@ -51,19 +77,24 @@ export declare abstract class AbstractRepository<Entity extends AnyRepositoryEnt
|
|
|
51
77
|
* A repository of `entity` with the methods of `actions`: `findBy<Key>` for a
|
|
52
78
|
* unique key (`id`, `options.unique`, a `unique` property), giving the entity
|
|
53
79
|
* or `undefined`; `findManyBy<Key>` for any other key, giving a list;
|
|
54
|
-
* `create`, `update`, `delete`, `restore` (soft deletion only), `count`, and the queries `findMany`,
|
|
55
|
-
* `exists`, `findPage` and `findCursor
|
|
80
|
+
* `create`, `update`, `delete`, `restore` (soft deletion only), `increment`, `count`, and the queries `findMany`,
|
|
81
|
+
* `exists`, `findPage` and `findCursor`, and `forEachPage`, which walks the
|
|
82
|
+
* pages of a cursor query, resumably. Every method is async and reads and writes through `provider`
|
|
56
83
|
* (a new `inMemoryProvider()` by default), which gets the variables of
|
|
57
84
|
* `environmentDependencies` as `context.env`. The repository can be a
|
|
58
85
|
* dependency of a use case, and `RepositoryOf` extends it with a class.
|
|
59
86
|
*/
|
|
60
|
-
export declare function repositoryOf<Entity extends AnyRepositoryEntity, const Actions extends readonly RepositoryAction<Entity>[], const Environment extends EnvironmentDependencies | undefined = undefined
|
|
87
|
+
export declare function repositoryOf<Entity extends AnyRepositoryEntity, const Actions extends readonly RepositoryAction<Entity>[], const Environment extends EnvironmentDependencies | undefined = undefined, const Capabilities extends ProviderCapabilities = {
|
|
88
|
+
readonly keyset: true;
|
|
89
|
+
}>(settings: RepositorySettings<Entity, Actions, Environment, Capabilities>): RepositoryOfProvider<Entity, Actions, MergedEnvironment<Environment>, ProviderKeyset<{
|
|
90
|
+
readonly capabilities: Capabilities;
|
|
91
|
+
}>>;
|
|
61
92
|
/**
|
|
62
93
|
* A repository of `Entity` with the methods of `Actions`, whose provider gets
|
|
63
94
|
* `Environment` as `context.env`, e.g.
|
|
64
95
|
* `RepositoryOf<typeof User, ["findById", "create"]>`.
|
|
65
96
|
*/
|
|
66
|
-
export type RepositoryOf<Entity extends AnyRepositoryEntity, Actions extends readonly RepositoryAction<Entity>[], Environment = Readonly<Record<never, never
|
|
97
|
+
export type RepositoryOf<Entity extends AnyRepositoryEntity, Actions extends readonly RepositoryAction<Entity>[], Environment = Readonly<Record<never, never>>, Keyset extends boolean = true> = RepositoryType<Entity, Actions, Environment, Keyset>;
|
|
67
98
|
/**
|
|
68
99
|
* The class of `repository`, to extend: its instances share the entity,
|
|
69
100
|
* actions, provider and environment of `repository`, e.g.
|
|
@@ -73,3 +104,4 @@ export type RepositoryOf<Entity extends AnyRepositoryEntity, Actions extends rea
|
|
|
73
104
|
export declare function RepositoryOf<Repository extends AnyRepository>(repository: Repository): new () => Repository;
|
|
74
105
|
/** Whether `value` is a repository, made by `repositoryOf` or extending one. */
|
|
75
106
|
export declare function isRepository(value: unknown): value is AnyRepository;
|
|
107
|
+
export {};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type EnvironmentDependencies } from "../environment/environment.js";
|
|
2
|
+
import type { ILogger } from "../logger/logger.js";
|
|
3
|
+
import type { RepositoryIndex, RepositoryScans } from "./types/query.interface.js";
|
|
2
4
|
import type { AnyRepositoryEntity, IRepositoryProvider, RepositoryContext } from "./types/repository.interface.js";
|
|
3
5
|
/** What a repository class holds, shared by the classes extending it. */
|
|
4
6
|
export interface RepositoryState {
|
|
@@ -12,6 +14,12 @@ export interface RepositoryState {
|
|
|
12
14
|
readonly environment: EnvironmentDependencies | undefined;
|
|
13
15
|
/** The repository of each entity class its entities hold, at any depth. */
|
|
14
16
|
readonly relations: ReadonlyMap<object, RepositoryState>;
|
|
17
|
+
/** Its declared indexes, normalized and frozen. */
|
|
18
|
+
readonly indexes: Readonly<Record<string, RepositoryIndex>>;
|
|
19
|
+
/** What it does with a query only a scan serves. */
|
|
20
|
+
readonly scans: RepositoryScans;
|
|
21
|
+
/** The logger of the application that bound it, for `scans: "warn"`. */
|
|
22
|
+
readonly logger?: ILogger;
|
|
15
23
|
context?: RepositoryContext;
|
|
16
24
|
}
|
|
17
25
|
export declare const states: WeakMap<object, RepositoryState>;
|
|
@@ -26,6 +34,8 @@ export interface RepositoryInspection {
|
|
|
26
34
|
readonly entity: AnyRepositoryEntity;
|
|
27
35
|
readonly actions: readonly string[];
|
|
28
36
|
readonly deletion: "hard" | "soft";
|
|
37
|
+
readonly indexes: Readonly<Record<string, RepositoryIndex>>;
|
|
38
|
+
readonly scans: RepositoryScans;
|
|
29
39
|
readonly explicitProvider: boolean;
|
|
30
40
|
/** The provider, only when the repository declared it. */
|
|
31
41
|
readonly provider: IRepositoryProvider | undefined;
|
|
@@ -46,9 +56,27 @@ export declare function declareRepository(state: RepositoryState, repository: ob
|
|
|
46
56
|
* @internal
|
|
47
57
|
*/
|
|
48
58
|
export declare function relatedRepositoryOf(repository: object, Entity: object): object | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Throws `RepositoryUsedOutsideApplication` when `state` is a declaration an
|
|
61
|
+
* application bound to its provider: its calls would read another store.
|
|
62
|
+
*/
|
|
63
|
+
export declare function assertUnbound(state: RepositoryState): void;
|
|
64
|
+
/** Where the application binding a repository comes from. */
|
|
65
|
+
export interface RepositoryBinding {
|
|
66
|
+
/** The name of the application. */
|
|
67
|
+
readonly application: string;
|
|
68
|
+
/** The key of the repository in the application. */
|
|
69
|
+
readonly key: string;
|
|
70
|
+
/** The logger of the application, for `scans: "warn"`. */
|
|
71
|
+
readonly logger?: ILogger;
|
|
72
|
+
}
|
|
49
73
|
/**
|
|
50
74
|
* A repository sharing the declaration of `repository` that reads and writes
|
|
51
75
|
* through `provider`, unless it declared a provider of its own; a declared
|
|
52
|
-
* provider found in `instances` is replaced by its value.
|
|
76
|
+
* provider found in `instances` is replaced by its value. With `binding`,
|
|
77
|
+
* the declaration (made by `repositoryOf`) throws
|
|
78
|
+
* `RepositoryUsedOutsideApplication` from then on.
|
|
53
79
|
*/
|
|
54
|
-
export declare function bindRepository<Repository extends object>(repository: Repository, provider: IRepositoryProvider | undefined, instances?: ReadonlyMap<object, IRepositoryProvider
|
|
80
|
+
export declare function bindRepository<Repository extends object>(repository: Repository, provider: IRepositoryProvider | undefined, instances?: ReadonlyMap<object, IRepositoryProvider>, binding?: RepositoryBinding): Repository;
|
|
81
|
+
/** The provider `repository` reads and writes through. @internal */
|
|
82
|
+
export declare function providerOfRepository(repository: unknown): IRepositoryProvider | undefined;
|