@afora/core 0.0.8 → 0.0.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.
Files changed (74) hide show
  1. package/README.md +127 -23
  2. package/dist/adapters.d.ts +25 -0
  3. package/dist/adapters.js +56 -0
  4. package/dist/core/application/exceptions.d.ts +17 -0
  5. package/dist/core/application/migrate.d.ts +19 -0
  6. package/dist/core/application/schema.d.ts +30 -0
  7. package/dist/core/application/types/application.interface.d.ts +37 -0
  8. package/dist/core/auth/jwt.d.ts +30 -0
  9. package/dist/core/auth/types/jwt.interface.d.ts +66 -0
  10. package/dist/core/blueprint/exceptions.d.ts +5 -0
  11. package/dist/core/blueprint/optional.d.ts +3 -1
  12. package/dist/core/blueprint/types/blueprint.interface.d.ts +79 -5
  13. package/dist/core/blueprint/variant.d.ts +32 -0
  14. package/dist/core/entity/abstract-entity.d.ts +5 -0
  15. package/dist/core/entity/entity.d.ts +1 -1
  16. package/dist/core/entity/exceptions.d.ts +9 -0
  17. package/dist/core/entity/trust.d.ts +24 -0
  18. package/dist/core/entity/types/entity.interface.d.ts +30 -0
  19. package/dist/core/output/access.d.ts +16 -0
  20. package/dist/core/output/exceptions.d.ts +16 -0
  21. package/dist/core/output/http.d.ts +36 -0
  22. package/dist/core/output/openapi-sink.d.ts +23 -2
  23. package/dist/core/output/stage.d.ts +7 -2
  24. package/dist/core/output/status.d.ts +1 -1
  25. package/dist/core/output/types/http.interface.d.ts +130 -0
  26. package/dist/core/output/types/output.interface.d.ts +25 -5
  27. package/dist/core/parse/issue.d.ts +28 -3
  28. package/dist/core/parse/parse.d.ts +9 -0
  29. package/dist/core/property/abstract-property.d.ts +9 -0
  30. package/dist/core/property/define-property.d.ts +34 -6
  31. package/dist/core/property/types/property-options.interface.d.ts +10 -0
  32. package/dist/core/property/types/property-type.interface.d.ts +4 -0
  33. package/dist/core/record/abstract-record.d.ts +7 -0
  34. package/dist/core/record/types/record.interface.d.ts +12 -1
  35. package/dist/core/repository/exceptions.d.ts +13 -0
  36. package/dist/core/repository/in-memory.d.ts +2 -2
  37. package/dist/core/repository/migrate.d.ts +20 -0
  38. package/dist/core/repository/state.d.ts +2 -0
  39. package/dist/core/repository/types/repository.interface.d.ts +33 -0
  40. package/dist/core/repository/types/schema.interface.d.ts +126 -0
  41. package/dist/core/repository/upcast.d.ts +64 -0
  42. package/dist/core/schema/schema.d.ts +28 -0
  43. package/dist/core/symbols/kind.d.ts +1 -1
  44. package/dist/core/use-case/access.d.ts +12 -0
  45. package/dist/core/use-case/decorators.d.ts +40 -0
  46. package/dist/core/use-case/route.d.ts +18 -0
  47. package/dist/core/use-case/steps/types/check.interface.d.ts +214 -0
  48. package/dist/core/use-case/types/access.interface.d.ts +39 -0
  49. package/dist/core/use-case/types/route.interface.d.ts +66 -0
  50. package/dist/core/use-case/use-case.d.ts +52 -2
  51. package/dist/experimental.d.ts +13 -1
  52. package/dist/experimental.js +63 -25
  53. package/dist/index-3aae5jfq.js +2997 -0
  54. package/dist/index-hg1z2s2v.js +1055 -0
  55. package/dist/index-ny3as79r.js +4567 -0
  56. package/dist/index-xegv6a1p.js +4291 -0
  57. package/dist/index-y280ew0k.js +396 -0
  58. package/dist/index.d.ts +45 -49
  59. package/dist/index.js +110 -186
  60. package/dist/std/properties/password.d.ts +0 -2
  61. package/dist/std/record-plugins.d.ts +2 -2
  62. package/dist/std/resource.d.ts +31 -0
  63. package/dist/std/runtime.d.ts +6 -0
  64. package/dist/std/use-cases/auth.d.ts +5 -1
  65. package/dist/std/use-cases/index.d.ts +1 -1
  66. package/dist/std/use-cases/shared.d.ts +2 -2
  67. package/dist/std/use-cases/types.d.ts +97 -1
  68. package/dist/testing/http-provider-contract.d.ts +32 -0
  69. package/dist/testing/index.d.ts +2 -0
  70. package/dist/testing/index.js +495 -4
  71. package/dist/testing/provider-contract.d.ts +17 -1
  72. package/package.json +16 -3
  73. package/dist/index-qxfkyd6k.js +0 -1721
  74. package/dist/index-t5qwtyj5.js +0 -8194
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The compile-time checking of `useCaseOf({ steps })`: the steps are inferred
3
+ * as a tuple and folded in order. Each step sees the values in scope (the
4
+ * input, the context, the environments and the names bound by the earlier
5
+ * steps with `as`); its references must name a path of one of them, and its
6
+ * `args`, `values`, `value` or `input` must fit what the called function,
7
+ * record, repository or use case takes. A reference that does not fit is
8
+ * replaced by the references that would, which is the error TypeScript shows
9
+ * and what the editor offers inside a `"$…"` string.
10
+ *
11
+ * The list of the paths of the scope is computed only for a reference that
12
+ * does not fit, so that right steps cost little to check. The conditionals on
13
+ * the steps are not distributive (`[Step] extends [...]`): TypeScript then
14
+ * never evaluates them with the constraint of the steps while it infers them.
15
+ */
16
+ import type { BlueprintValue, IBlueprint, IBlueprintCollection } from "../../../blueprint/types/blueprint.interface.js";
17
+ import type { AnyEnvironment, EnvironmentValue } from "../../../environment/environment.js";
18
+ import type { Meta } from "../../../symbols/meta.js";
19
+ import type { AnyUseCaseClass, UseCaseInput, UseCaseResponse } from "../../use-case.js";
20
+ /** The values the first step can reference: the input, the context (if declared) and the environments. */
21
+ export type StepsInitialScope<Input extends IBlueprintCollection, Context extends IBlueprintCollection | undefined, Dependencies> = {
22
+ readonly input: BlueprintValue<Input>;
23
+ readonly env: {
24
+ readonly [Key in keyof Dependencies as Dependencies[Key] extends AnyEnvironment ? Key : never]: Dependencies[Key] extends AnyEnvironment ? EnvironmentValue<Dependencies[Key]> : never;
25
+ };
26
+ } & (Context extends IBlueprintCollection ? {
27
+ readonly context: BlueprintValue<Context>;
28
+ } : unknown);
29
+ type Split<Path extends string> = Path extends `${infer Head}.${infer Tail}` ? [Head, ...Split<Tail>] : [Path];
30
+ /** `Value[Key]` for a value that is not `null` or `undefined`: by index (or `length`) in a list; `never` when absent. */
31
+ type GetDefined<Value, Key extends string> = Value extends readonly unknown[] ? Key extends `${number}` ? Key extends keyof Value ? Value[Key] : Value[number] : Key extends "length" ? number : never : Key extends keyof Value ? Value[Key] : never;
32
+ /**
33
+ * `Value[Key]` as `followPath` reads it: `undefined` through a value that
34
+ * may be `undefined` or `null`, and `never` when no value of the type has
35
+ * the key; `unknown` stays `unknown`.
36
+ */
37
+ type Get<Value, Key extends string> = unknown extends Value ? unknown : [GetDefined<NonNullable<Value>, Key>] extends [infer Found] ? [Found] extends [never] ? never : Found | (Value extends null | undefined ? undefined : never) : never;
38
+ type Walk<Value, Path extends readonly string[]> = Path extends readonly [
39
+ infer Key extends string,
40
+ ...infer Rest extends string[]
41
+ ] ? Walk<Get<Value, Key>, Rest> : Value;
42
+ /** The type of the value the reference `$root.a.b` stands for in `Scope`; `never` when it names nothing. */
43
+ export type StepReferenceType<Scope, Reference> = [Reference] extends [
44
+ `$${infer Path}`
45
+ ] ? Split<Path> extends [
46
+ infer Root extends string,
47
+ ...infer Rest extends string[]
48
+ ] ? Root extends "env" ? Rest extends [string, ...string[]] ? Walk<Get<Scope, Root>, Rest> : never : Walk<Get<Scope, Root>, Rest> : never : never;
49
+ /** What a path walks into: the value of a record, not its methods. */
50
+ type DataOf<Value> = Value extends {
51
+ readonly [Meta]: {
52
+ readonly blueprint: IBlueprint<infer Properties>;
53
+ };
54
+ } ? BlueprintValue<Properties> : Value;
55
+ type Leaf = string | number | boolean | bigint | symbol | null | undefined | Date | readonly unknown[] | ((...args: any[]) => unknown);
56
+ type Below<Value, Prefix extends string, Expected, Depth extends unknown[]> = Depth["length"] extends 3 ? never : DataOf<NonNullable<Value>> extends infer Data ? Data extends Leaf ? never : {
57
+ [Key in keyof Data & string]-?: PathsFrom<Data[Key] | (Value extends null | undefined ? undefined : never), `${Prefix}.${Key}`, Expected, [
58
+ ...Depth,
59
+ 0
60
+ ]>;
61
+ }[keyof Data & string] : never;
62
+ type PathsFrom<Value, Path extends string, Expected, Depth extends unknown[]> = Path extends unknown ? ([Value] extends [Expected] ? Path : never) | Below<Value, Path, Expected, Depth> : never;
63
+ /** Every reference of `Scope` whose value fits `Expected` (three keys deep at most). */
64
+ export type StepReferencesOf<Scope, Expected = unknown> = Scope extends unknown ? {
65
+ [Root in keyof Scope & string]-?: Root extends "env" ? Below<Scope[Root], `$${Root}`, Expected, [0]> : PathsFrom<Scope[Root], `$${Root}`, Expected, []>;
66
+ }[keyof Scope & string] : never;
67
+ /** `Reference` when it names a value of `Scope` that fits `Expected`; else the references that would. */
68
+ type CheckReference<Reference, Expected, Scope> = [
69
+ StepReferenceType<Scope, Reference>
70
+ ] extends [infer Type] ? [Type] extends [never] ? StepReferencesOf<Scope, Expected> : [Type] extends [Expected] ? Reference : StepReferencesOf<Scope, Expected> : never;
71
+ type Primitive = string | number | boolean | null;
72
+ /** The type of the key `Key` in what `Expected` takes, over each object of it. */
73
+ type ExpectedAt<Expected, Key> = Expected extends object ? Key extends keyof Expected ? Expected[Key] : never : never;
74
+ /** The keys `Expected` takes in a mapping. */
75
+ type KnownKeys<Expected> = Expected extends object ? keyof Expected : never;
76
+ /** The keys the plain objects of `Expected` require (a record instance is not built from a mapping). */
77
+ type RequiredKeys<Expected> = Expected extends {
78
+ readonly [Meta]: unknown;
79
+ } ? never : Expected extends object ? {
80
+ [Key in keyof Expected]-?: undefined extends Expected[Key] ? never : Key;
81
+ }[keyof Expected] : never;
82
+ /** The keys of `Value` that `Expected` takes, each checked; the others are excess properties. */
83
+ type CheckKeys<Value, Expected, Scope> = Value extends unknown ? {
84
+ readonly [Key in keyof Value as Key extends KnownKeys<Expected> ? Key : never]: StepValueCheck<Value[Key], ExpectedAt<Expected, Key>, Scope>;
85
+ } : never;
86
+ type CheckMapping<Value, Expected, Scope> = unknown extends Expected ? {
87
+ readonly [Key in keyof Value]: StepValueCheck<Value[Key], unknown, Scope>;
88
+ } : [Exclude<RequiredKeys<Expected>, keyof Value>] extends [never] ? CheckKeys<Value, Expected, Scope> : CheckKeys<Value, Expected, Scope> & {
89
+ readonly [Key in Exclude<RequiredKeys<Expected>, keyof Value>]: ExpectedAt<Expected, Key>;
90
+ };
91
+ type ElementAt<Expected, Index> = unknown extends Expected ? unknown : Expected extends readonly unknown[] ? Index extends keyof Expected ? Expected[Index] : Expected[number] : never;
92
+ /**
93
+ * What the step value `Value` must be where `Expected` is taken: itself when
94
+ * it fits; else, for a reference, the references that would fit, and for a
95
+ * literal, `Expected`. A mapping is checked key by key, a list item by item.
96
+ */
97
+ export type StepValueCheck<Value, Expected, Scope> = [Value] extends [
98
+ `$${string}`
99
+ ] ? CheckReference<Value, Expected, Scope> : [Value] extends [Primitive] ? [Value] extends [Expected] ? Value : Expected : [Value] extends [{
100
+ readonly literal: unknown;
101
+ }] ? Value : [Value] extends [readonly unknown[]] ? {
102
+ readonly [Index in keyof Value]: StepValueCheck<Value[Index], ElementAt<Expected, Index>, Scope>;
103
+ } : [Value] extends [object] ? CheckMapping<Value, Expected, Scope> : Value;
104
+ /** The parameter `Index` of a function whose parameters are `Parameters`. */
105
+ type ParameterAt<Parameters extends readonly unknown[], Index> = Index extends keyof Parameters ? Parameters[Index] : number extends Parameters["length"] ? Parameters[number] : undefined;
106
+ /** What the `args` of a call must be: each against its parameter, or one reference to a list. */
107
+ type CheckArgs<Args, Parameters extends readonly unknown[], Scope> = [
108
+ Args
109
+ ] extends [`$${string}`] ? CheckReference<Args, readonly unknown[], Scope> : {
110
+ readonly [Index in keyof Args]: StepValueCheck<Args[Index], ParameterAt<Parameters, Index>, Scope>;
111
+ };
112
+ type Fn = (...args: any[]) => unknown;
113
+ type ParametersOf<Value> = [Value] extends [Fn] ? Parameters<Value> : unknown[];
114
+ type FirstParameter<Value> = [Value] extends [never] ? unknown : [Value] extends [Fn] ? Parameters<Value>[0] : unknown;
115
+ type ResultOf<Value> = [Value] extends [Fn] ? Awaited<ReturnType<Value>> : unknown;
116
+ /** `Dependencies[Key][Member]`, or `unknown`. */
117
+ type MemberOf<Dependencies, Key, Member> = Key extends keyof Dependencies ? Member extends keyof Dependencies[Key] ? Dependencies[Key][Member] : unknown : unknown;
118
+ /** The method `"users.findByEmail"` names among `Dependencies`. */
119
+ type FindMethod<Dependencies, Target> = Target extends `${infer Repository}.${infer Action}` ? MemberOf<Dependencies, Repository, Action> : unknown;
120
+ /** The members of a record no `invoke` may call. */
121
+ type ForbiddenMethod = "set" | "emit" | "constructor" | "toLocaleString" | "valueOf" | "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable";
122
+ /** The public methods of the record `Record`: what `invoke.method` names. */
123
+ type StepInvokeMethod<Record> = Record extends unknown ? {
124
+ [Key in keyof Record & string]-?: Record[Key] extends Fn ? Key : never;
125
+ }[Exclude<keyof Record & string, ForbiddenMethod>] : never;
126
+ type ChangeOf<Record> = Record extends {
127
+ readonly change: infer Change;
128
+ } ? Change : never;
129
+ type UseCaseOf<Dependencies, Key> = Key extends keyof Dependencies ? Dependencies[Key] extends AnyUseCaseClass ? Dependencies[Key] : never : never;
130
+ /** A step that calls `Method` with its `args`: `[checked step, result]`. */
131
+ type CallStep<Step, Method, Scope> = [
132
+ {
133
+ readonly [Key in keyof Step]: Key extends "args" ? CheckArgs<Step[Key], ParametersOf<Method>, Scope> : Step[Key];
134
+ },
135
+ ResultOf<Method>
136
+ ];
137
+ /** A step whose `Field` is checked against `Expected`: `[checked step, result]`. */
138
+ type ValueStep<Step, Field, Expected, Result, Scope> = [
139
+ {
140
+ readonly [Key in keyof Step]: Key extends Field ? StepValueCheck<Step[Key], Expected, Scope> : Step[Key];
141
+ },
142
+ Result
143
+ ];
144
+ type InvokeStep<Step, Record, Scope> = [Record] extends [never] ? ValueStep<Step, "invoke", object, unknown, Scope> : [Step] extends [{
145
+ readonly method: infer Method;
146
+ }] ? Method extends keyof Record ? CallStep<Step, Record[Method], Scope> : [
147
+ {
148
+ readonly [Key in keyof Step]: Key extends "method" ? StepInvokeMethod<Record> : Step[Key];
149
+ },
150
+ unknown
151
+ ] : [Step, unknown];
152
+ type ChangeStep<Step, Record, Scope> = [Record] extends [never] ? ValueStep<Step, "change", object, unknown, Scope> : [Step] extends [{
153
+ readonly key: infer Name;
154
+ }] ? Name extends keyof ChangeOf<Record> ? ValueStep<Step, "value", FirstParameter<ChangeOf<Record>[Name]>, Record, Scope> : [
155
+ {
156
+ readonly [Key in keyof Step]: Key extends "key" ? keyof ChangeOf<Record> & string : Step[Key];
157
+ },
158
+ Record
159
+ ] : [Step, Record];
160
+ /** `[what Step must be, what it gives to its as]`, read against `Scope`. */
161
+ type CheckStep<Step, Dependencies, Scope> = [Step] extends [
162
+ {
163
+ readonly find: infer Target;
164
+ }
165
+ ] ? CallStep<Step, FindMethod<Dependencies, Target>, Scope> : [Step] extends [{
166
+ readonly run: unknown;
167
+ readonly fn: infer Run;
168
+ }] ? CallStep<Step, Run, Scope> : [Step] extends [{
169
+ readonly invoke: infer Reference;
170
+ }] ? InvokeStep<Step, NonNullable<StepReferenceType<Scope, Reference>>, Scope> : [Step] extends [{
171
+ readonly change: infer Reference;
172
+ }] ? ChangeStep<Step, NonNullable<StepReferenceType<Scope, Reference>>, Scope> : [Step] extends [
173
+ {
174
+ readonly build: abstract new (value: infer Input) => infer Built;
175
+ }
176
+ ] ? ValueStep<Step, "values", Input, Built, Scope> : [Step] extends [{
177
+ readonly call: infer Name;
178
+ }] ? ValueStep<Step, "input", UseCaseInput<UseCaseOf<Dependencies, Name>>, UseCaseResponse<UseCaseOf<Dependencies, Name>>, Scope> : [Step] extends [{
179
+ readonly create: infer Repository;
180
+ }] ? WriteStep<Step, MemberOf<Dependencies, Repository, "create">, Scope> : [Step] extends [{
181
+ readonly update: infer Repository;
182
+ }] ? WriteStep<Step, MemberOf<Dependencies, Repository, "update">, Scope> : [Step] extends [{
183
+ readonly delete: infer Repository;
184
+ }] ? WriteStep<Step, MemberOf<Dependencies, Repository, "delete">, Scope> : [Step] extends [{
185
+ readonly restore: infer Repository;
186
+ }] ? WriteStep<Step, MemberOf<Dependencies, Repository, "restore">, Scope> : ValueStep<Step, "value" | "other" | "details" | "return", unknown, unknown, Scope>;
187
+ type WriteStep<Step, Method, Scope> = ValueStep<Step, "value", FirstParameter<Method>, ResultOf<Method>, Scope>;
188
+ /** The scope after `Step`: its result bound to its `as`, or a value it ensures exists. */
189
+ type ScopeAfter<Step, Result, Scope> = [Step] extends [
190
+ {
191
+ readonly as: infer Name extends string;
192
+ }
193
+ ] ? Scope & {
194
+ readonly [Key in Name]: Result;
195
+ } : [Step] extends [
196
+ {
197
+ readonly ensure: "exists";
198
+ readonly value: `$${infer Name}`;
199
+ }
200
+ ] ? Name extends keyof Scope ? Scope & {
201
+ readonly [Key in Name]: NonNullable<Scope[Name]>;
202
+ } : Scope : Scope;
203
+ /** The steps folded in order, each checked against the scope the earlier ones built. */
204
+ type CheckSteps<Steps extends readonly unknown[], Dependencies, Scope, Checked extends readonly unknown[] = []> = [Steps] extends [readonly [infer Step, ...infer Rest]] ? CheckStep<Step, Dependencies, Scope> extends [infer Current, infer Result] ? CheckSteps<Rest, Dependencies, ScopeAfter<Step, Result, Scope>, [
205
+ ...Checked,
206
+ Current
207
+ ]> : never : readonly [...Checked];
208
+ /**
209
+ * What `useCaseOf` takes as `steps`: the steps, inferred as a tuple, each
210
+ * checked against the values in scope. Steps that are not a tuple (built at
211
+ * run time) are checked only when the use case is declared.
212
+ */
213
+ export type CheckedSteps<Steps, Input extends IBlueprintCollection, Context extends IBlueprintCollection | undefined, Dependencies> = [Steps] extends [readonly [unknown, ...unknown[]]] ? CheckSteps<Steps, Dependencies, StepsInitialScope<Input, Context, Dependencies>> : Steps;
214
+ export {};
@@ -0,0 +1,39 @@
1
+ import type { JsonValue } from "../../json.js";
2
+ /**
3
+ * Who is calling: what an authenticator made of the credentials of a request.
4
+ * `subject` identifies the caller (a user id), `scopes` is what it may do.
5
+ */
6
+ export interface Principal {
7
+ readonly subject: string;
8
+ readonly scopes: readonly string[];
9
+ /** Anything else the credentials say (a tenant, a role). */
10
+ readonly claims?: Readonly<Record<string, JsonValue>>;
11
+ }
12
+ /** A value of the principal: its subject, its scopes or one of its claims. */
13
+ type PrincipalValue = "subject" | "scopes" | `claims.${string}`;
14
+ /**
15
+ * Who may call a use case, as data: declared with the `access` setting of
16
+ * `useCaseOf` or with `@Access`, and enforced by every output before the use
17
+ * case runs. `{ public: true }` lets anyone in. Any other declaration needs
18
+ * an authenticated caller (`{}` asks for nothing more), and may add:
19
+ *
20
+ * - `scopes`: the caller has all of them;
21
+ * - `owner`: the input key `input` equals the subject of the caller, or its
22
+ * claim `claim`;
23
+ * - `context`: keys of the context of the call filled from the caller, over
24
+ * whatever the request sent.
25
+ */
26
+ export type AccessDeclaration = {
27
+ readonly public: true;
28
+ } | {
29
+ readonly public?: false;
30
+ readonly scopes?: readonly string[];
31
+ readonly owner?: {
32
+ readonly input: string;
33
+ readonly claim?: string;
34
+ };
35
+ readonly context?: {
36
+ readonly [contextKey: string]: PrincipalValue;
37
+ };
38
+ };
39
+ export {};
@@ -0,0 +1,66 @@
1
+ /** An HTTP method an operation can be routed to. */
2
+ export type RouteMethod = "get" | "post" | "put" | "patch" | "delete";
3
+ /** The status of a successful call of a route; `204` has no body. */
4
+ export type RouteSuccessStatus = 200 | 201 | 202 | 204;
5
+ /** How a cookie a route sets is sent. The defaults are the safe ones for a session. */
6
+ export interface RouteCookie {
7
+ /** The name of the cookie. */
8
+ readonly name: string;
9
+ /** Not readable by scripts of the page. `true` by default. */
10
+ readonly httpOnly?: boolean;
11
+ /** Sent over HTTPS only. `true` by default. */
12
+ readonly secure?: boolean;
13
+ /** `"lax"` by default. */
14
+ readonly sameSite?: "strict" | "lax" | "none";
15
+ /** `"/"` by default. */
16
+ readonly path?: string;
17
+ readonly domain?: string;
18
+ /** The seconds the cookie lives; without it, until the browser closes. */
19
+ readonly maxAge?: number;
20
+ }
21
+ /**
22
+ * Where a use case is served over HTTP, as data: declared with the `route`
23
+ * setting of `useCaseOf` or with `@Route`, and read by the sinks that serve
24
+ * or document HTTP. The blueprint of the use case stays the only description
25
+ * of its input: a route only says where each key comes from.
26
+ */
27
+ export interface RouteDeclaration {
28
+ readonly method: RouteMethod;
29
+ /**
30
+ * The path, after the `basePath` of the sink, e.g. `"/posts/{slug}"`. A
31
+ * `{name}` segment fills the scalar input key `name`. Without it, the path
32
+ * is the default of the sink: `/{kebab(command)}/{kebab(operation)}`.
33
+ */
34
+ readonly path?: string;
35
+ /** `200` by default. */
36
+ readonly status?: RouteSuccessStatus;
37
+ /**
38
+ * Scalar input keys read from the query string. A `get` or `delete` route
39
+ * reads from the query every key that has no other place, so this is for
40
+ * the routes with a body.
41
+ */
42
+ readonly query?: readonly string[];
43
+ /** Scalar input keys read from request headers: `{ locale: "accept-language" }`. */
44
+ readonly headers?: {
45
+ readonly [inputKey: string]: string;
46
+ };
47
+ /** Scalar input keys read from cookies: `{ session: "sid" }`. */
48
+ readonly cookie?: {
49
+ readonly [inputKey: string]: string;
50
+ };
51
+ /**
52
+ * Scalar keys of the response sent as cookies instead of in the body:
53
+ * `{ token: { name: "sid" } }`. An empty text removes the cookie, and an
54
+ * absent value sets none.
55
+ */
56
+ readonly setCookie?: {
57
+ readonly [responseKey: string]: RouteCookie;
58
+ };
59
+ }
60
+ /**
61
+ * What the `route` setting and `@Route` take: a {@link RouteDeclaration}
62
+ * whose method may be written in upper case.
63
+ */
64
+ export interface RouteSettings extends Omit<RouteDeclaration, "method"> {
65
+ readonly method: RouteMethod | Uppercase<RouteMethod>;
66
+ }
@@ -5,7 +5,10 @@ import { type DeclaredExceptionClass } from "../exception/exception.js";
5
5
  import { type ILogger, type LoggerDeclaration } from "../logger/logger.js";
6
6
  import type { AnyRepository } from "../repository/types/repository.interface.js";
7
7
  import { Kind } from "../symbols/kind.js";
8
+ import type { CheckedSteps } from "./steps/types/check.interface.js";
8
9
  import type { UseCaseStep } from "./steps/types/steps.interface.js";
10
+ import type { AccessDeclaration } from "./types/access.interface.js";
11
+ import type { RouteDeclaration, RouteSettings } from "./types/route.interface.js";
9
12
  /** A concrete UseCase declaration, usable as a dependency. */
10
13
  export interface AnyUseCaseClass {
11
14
  readonly [Kind]: "use-case";
@@ -18,6 +21,10 @@ export interface AnyUseCaseClass {
18
21
  readonly tags: readonly string[];
19
22
  readonly deprecated: boolean;
20
23
  readonly errors: readonly AnyExceptionClass[];
24
+ /** Where it is served over HTTP, if it says so (see `RouteDeclaration`). */
25
+ readonly route: RouteDeclaration | undefined;
26
+ /** Who may call it, if it says so (see `AccessDeclaration`). */
27
+ readonly access: AccessDeclaration | undefined;
21
28
  new (input: any, ...options: any[]): {
22
29
  execute(): Promise<unknown>;
23
30
  pullEvents(): EntityEvent[];
@@ -81,6 +88,22 @@ export interface UseCaseSettings<Input extends IBlueprintCollection, Output exte
81
88
  readonly deprecated?: boolean;
82
89
  /** The exceptions (made by `exceptionOf`) a caller should expect; documentation only. */
83
90
  readonly errors?: readonly AnyExceptionClass[];
91
+ /**
92
+ * Where the use case is served over HTTP: method, path and where each
93
+ * input key is read from (see `RouteDeclaration`). A decorated class says
94
+ * the same with `@Route`. Read by the sinks that serve or document HTTP;
95
+ * the `route` option of a sink overrides the method, path and status.
96
+ */
97
+ readonly route?: RouteSettings;
98
+ /**
99
+ * Who may call the use case through an output: anyone (`{ public: true }`),
100
+ * or an authenticated caller with these `scopes`, that is the `owner`
101
+ * named by an input key, and whose values fill the `context` (see
102
+ * `AccessDeclaration`). A decorated class says the same with `@Access`.
103
+ * Enforced before the use case runs; a call made in process, with
104
+ * `execute` or `command.run`, is trusted and not checked.
105
+ */
106
+ readonly access?: AccessDeclaration;
84
107
  }
85
108
  /** `UseCaseSettings` with the `steps` that are its `execute`. */
86
109
  export interface UseCaseStepsSettings<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined> extends UseCaseSettings<Input, Output, Dependencies, Context> {
@@ -89,6 +112,30 @@ export interface UseCaseStepsSettings<Input extends IBlueprintCollection, Output
89
112
  * interpreted by its `execute`, and described by `describe()`.
90
113
  */
91
114
  readonly steps: readonly UseCaseStep<NoInfer<Dependencies>>[];
115
+ /** Checks the references and values of the steps at compile time (see `CheckedSteps`). */
116
+ readonly strictSteps?: boolean;
117
+ }
118
+ /**
119
+ * What `useCaseOf` takes: `UseCaseSettings`, and the `steps` that make the
120
+ * class concrete, if any.
121
+ */
122
+ export interface UseCaseDeclaration<Input extends IBlueprintCollection, Output extends IBlueprintCollection, Dependencies extends UseCaseCollection, Context extends IBlueprintCollection | undefined = undefined, Steps extends readonly UseCaseStep<Dependencies>[] | undefined = undefined, Strict extends boolean = false> extends UseCaseSettings<Input, Output, Dependencies, Context> {
123
+ /**
124
+ * What the use case does, as data (see `UseCaseStep`), which makes the
125
+ * class concrete: validated here, interpreted by its `execute`, and
126
+ * described by `describe()`. TypeScript checks the kinds of the steps and
127
+ * the names of dependencies and actions; with `strictSteps`, also their
128
+ * references and values (see `CheckedSteps`).
129
+ */
130
+ readonly steps?: [Strict] extends [true] ? CheckedSteps<Steps, NoInfer<Input>, NoInfer<Context>, NoInfer<Dependencies>> : Steps;
131
+ /**
132
+ * `true` checks the steps, written inline, at compile time: each reference
133
+ * must name a value in scope (the input, the context, an environment or the
134
+ * result of an earlier step, at any depth), and the `args`, `values`,
135
+ * `value` and `input` of a step must fit what it calls or builds. It costs
136
+ * the editor more than the default checks. Without effect at run time.
137
+ */
138
+ readonly strictSteps?: Strict;
92
139
  }
93
140
  export declare const InvalidUseCaseDeclaration: DeclaredExceptionClass<{
94
141
  reason: string;
@@ -119,6 +166,10 @@ type UseCaseStatics<Input extends IBlueprintCollection, Output extends IBlueprin
119
166
  readonly deprecated: boolean;
120
167
  /** The exceptions a caller should expect. */
121
168
  readonly errors: readonly AnyExceptionClass[];
169
+ /** Where it is served over HTTP, from the `route` setting or `@Route`. */
170
+ readonly route: RouteDeclaration | undefined;
171
+ /** Who may call it, from the `access` setting or `@Access`. */
172
+ readonly access: AccessDeclaration | undefined;
122
173
  };
123
174
  /** What a use case instance carries beyond `AbstractUseCase`. */
124
175
  type UseCaseInstanceContext<Context extends IBlueprintCollection | undefined> = {
@@ -138,8 +189,7 @@ export type StepsUseCaseConstructor<Input extends IBlueprintCollection, Output e
138
189
  * interprets them. Without, it is abstract: extend it and implement an async
139
190
  * execute method.
140
191
  */
141
- export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>, Context extends IBlueprintCollection | undefined = undefined>(settings: UseCaseStepsSettings<Input, Output, Dependencies, Context>): StepsUseCaseConstructor<Input, Output, Dependencies, Context>;
142
- export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>, Context extends IBlueprintCollection | undefined = undefined>(settings: UseCaseSettings<Input, Output, Dependencies, Context>): UseCaseConstructor<Input, Output, Dependencies, Context>;
192
+ export declare function useCaseOf<Input extends IBlueprintCollection, Output extends IBlueprintCollection, const Dependencies extends UseCaseCollection = Record<never, never>, Context extends IBlueprintCollection | undefined = undefined, const Steps extends readonly UseCaseStep<NoInfer<Dependencies>>[] | undefined = undefined, Strict extends boolean = false>(settings: UseCaseDeclaration<Input, Output, Dependencies, Context, Steps, Strict>): [Steps] extends [undefined] ? UseCaseConstructor<Input, Output, Dependencies, Context> : StepsUseCaseConstructor<Input, Output, Dependencies, Context>;
143
193
  export declare function isUseCaseClass(value: unknown): value is AnyUseCaseClass;
144
194
  export declare function isUseCase(value: unknown): value is AbstractUseCase<IBlueprintCollection, IBlueprintCollection, UseCaseCollection>;
145
195
  export {};
@@ -1,6 +1,18 @@
1
1
  /**
2
2
  * `@afora/core/experimental`: features that may change in any release. Not
3
- * covered by the compatibility policy in `VERSIONING.md`.
3
+ * covered by the compatibility policy in `VERSIONING.md`. See
4
+ * `docs/api-tiers.md`.
4
5
  */
6
+ export { InvalidJwtOptions, jwtOf } from "./core/auth/jwt.js";
7
+ export type { DescribedJwtOptions, Jwt, JwtAlgorithm, JwtOptions, JwtSubject, JwtTokenOptions, } from "./core/auth/types/jwt.interface.js";
5
8
  export { sliceOf, sliceValue } from "./core/blueprint/slice.js";
6
9
  export type { SlicedBlueprint, SlicedValue, SliceMask, ValueMask, } from "./core/blueprint/types/slice.interface.js";
10
+ export { eventPolicyCatalogOf, eventPolicyDescriptionOf, } from "./core/event-policy/event-policy.js";
11
+ export { stageDescriptionOf } from "./core/output/stage.js";
12
+ export { type PropertyLineage, propertyLineageOf, } from "./core/property/lineage.js";
13
+ export { providerDescriptionOf } from "./core/repository/provider.js";
14
+ export { matchesQuery, prepareQuery, sameValue, selectRows, } from "./core/repository/query.js";
15
+ export { useCaseOriginOf } from "./core/use-case/plugin.js";
16
+ export { useCaseTargetOf } from "./core/use-case/target.js";
17
+ export type { UseCaseOriginInfo } from "./core/use-case/types/plugin.interface.js";
18
+ export type { StdTokenOptions } from "./std/use-cases/types.js";
@@ -1,47 +1,63 @@
1
1
  import {
2
- isBlueprint2,
3
- isRecordClass2,
4
- recordFieldOf2,
2
+ propertyLineageOf2,
3
+ isBlueprint,
4
+ isRecordClass,
5
+ recordFieldOf,
6
+ InvalidBlueprintVariant2,
7
+ UndeclaredBlueprintProperty2,
8
+ isBlueprintVariant,
5
9
  recordNameOf2,
6
10
  listOf2,
7
- isBlueprintList2,
8
- AbstractRecord2,
9
- UndeclaredBlueprintProperty2,
11
+ isBlueprintList,
12
+ AbstractRecord,
10
13
  optionalOf2,
11
14
  blueprintFromProperties,
12
- declareRecord
13
- } from "./index-qxfkyd6k.js";
14
-
15
+ declareRecord,
16
+ stageDescriptionOf2,
17
+ InvalidJwtOptions2,
18
+ jwtOf2
19
+ } from "./index-xegv6a1p.js";
20
+ import {
21
+ providerDescriptionOf2,
22
+ prepareQuery2,
23
+ sameValue2,
24
+ matchesQuery2,
25
+ selectRows2,
26
+ useCaseTargetOf2,
27
+ useCaseOriginOf2,
28
+ eventPolicyDescriptionOf2,
29
+ eventPolicyCatalogOf2
30
+ } from "./index-3aae5jfq.js";
15
31
  // src/core/blueprint/slice.ts
16
32
  function propertiesOfField(field) {
17
33
  if (field === undefined)
18
34
  return;
19
- if (isBlueprintList2(field))
35
+ if (isBlueprintList(field))
20
36
  return propertiesOfField(field.item);
21
- const record = recordFieldOf2(field);
37
+ const record = recordFieldOf(field);
22
38
  if (record)
23
39
  return record.record.blueprint.properties;
24
- return isBlueprint2(field) ? field.properties : undefined;
40
+ return isBlueprint(field) ? field.properties : undefined;
25
41
  }
26
42
  function maskOf(selector, against) {
27
- if (isRecordClass2(selector))
43
+ if (isRecordClass(selector))
28
44
  return maskOf(selector.blueprint, against);
29
- if (isBlueprint2(selector)) {
45
+ if (isBlueprint(selector)) {
30
46
  return Object.fromEntries(Object.entries(selector.properties).map(([key, field]) => [key, fieldMaskOf(field, against?.[key])]));
31
47
  }
32
48
  return selector;
33
49
  }
34
50
  function fieldMaskOf(field, other) {
35
- if (isBlueprintList2(field)) {
36
- return fieldMaskOf(field.item, isBlueprintList2(other) ? other.item : other);
51
+ if (isBlueprintList(field)) {
52
+ return fieldMaskOf(field.item, isBlueprintList(other) ? other.item : other);
37
53
  }
38
- const record = recordFieldOf2(field);
54
+ const record = recordFieldOf(field);
39
55
  if (record) {
40
- if (record.record === recordFieldOf2(other)?.record)
56
+ if (record.record === recordFieldOf(other)?.record)
41
57
  return true;
42
58
  return maskOf(record.record, propertiesOfField(other));
43
59
  }
44
- if (isBlueprint2(field)) {
60
+ if (isBlueprint(field)) {
45
61
  return field === other ? true : maskOf(field, propertiesOfField(other));
46
62
  }
47
63
  return true;
@@ -66,7 +82,7 @@ function slicedRecordOf(Original, mask, path) {
66
82
  const blueprint = blueprintFromProperties(source.plugins, sliceCollection(source.properties, mask, `${path}.`));
67
83
  const kept = (keys) => keys.filter((key) => Object.hasOwn(blueprint.properties, key));
68
84
  const { sensitive, unique, change } = Original.options;
69
- const Sliced = declareRecord(AbstractRecord2, blueprint, {
85
+ const Sliced = declareRecord(AbstractRecord, blueprint, {
70
86
  sensitive: kept(sensitive),
71
87
  unique: kept(unique),
72
88
  change: kept(change)
@@ -81,22 +97,31 @@ function slicedRecordOf(Original, mask, path) {
81
97
  function sliceField(field, selected, path) {
82
98
  if (selected === true)
83
99
  return field;
84
- if (isBlueprintList2(field)) {
100
+ if (isBlueprintList(field)) {
85
101
  const list = listOf2(sliceField(field.item, selected, path), {
86
102
  minItems: field.minItems,
87
103
  maxItems: field.maxItems
88
104
  });
89
105
  return field.optional ? optionalOf2(list) : list;
90
106
  }
91
- const record = recordFieldOf2(field);
107
+ const record = recordFieldOf(field);
92
108
  if (record) {
93
109
  const sliced = slicedRecordOf(record.record, selected, path);
94
110
  return record.optional ? optionalOf2(sliced) : sliced;
95
111
  }
96
- if (isBlueprint2(field)) {
112
+ if (isBlueprint(field)) {
97
113
  const sliced = blueprintFromProperties(field.plugins, sliceCollection(field.properties, selected, `${path}.`));
98
114
  return field.optional ? optionalOf2(sliced) : sliced;
99
115
  }
116
+ if (isBlueprintVariant(field)) {
117
+ throw new InvalidBlueprintVariant2({
118
+ source: "blueprint",
119
+ details: {
120
+ key: field.key,
121
+ reason: `"${path}" cannot be sliced by its keys yet, only picked whole`
122
+ }
123
+ });
124
+ }
100
125
  throw new UndeclaredBlueprintProperty2({
101
126
  source: "blueprint",
102
127
  details: { key: `${path}.${Object.keys(selected)[0] ?? ""}` }
@@ -109,7 +134,7 @@ function pick(value, mask) {
109
134
  return value.map((item) => pick(item, mask));
110
135
  if (typeof value !== "object" || value === null)
111
136
  return value;
112
- const source = isRecordClass2(value.constructor) ? value.toJSON() : value;
137
+ const source = isRecordClass(value.constructor) ? value.toJSON() : value;
113
138
  const picked = {};
114
139
  for (const [key, selected] of Object.entries(mask)) {
115
140
  if (!selected || !Object.hasOwn(source, key))
@@ -119,6 +144,19 @@ function pick(value, mask) {
119
144
  return picked;
120
145
  }
121
146
  export {
147
+ InvalidJwtOptions2 as InvalidJwtOptions,
148
+ eventPolicyCatalogOf2 as eventPolicyCatalogOf,
149
+ eventPolicyDescriptionOf2 as eventPolicyDescriptionOf,
150
+ jwtOf2 as jwtOf,
151
+ matchesQuery2 as matchesQuery,
152
+ prepareQuery2 as prepareQuery,
153
+ propertyLineageOf2 as propertyLineageOf,
154
+ providerDescriptionOf2 as providerDescriptionOf,
155
+ sameValue2 as sameValue,
156
+ selectRows2 as selectRows,
122
157
  sliceOf,
123
- sliceValue
158
+ sliceValue,
159
+ stageDescriptionOf2 as stageDescriptionOf,
160
+ useCaseOriginOf2 as useCaseOriginOf,
161
+ useCaseTargetOf2 as useCaseTargetOf
124
162
  };