@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.
- package/README.md +127 -23
- package/dist/adapters.d.ts +25 -0
- package/dist/adapters.js +56 -0
- package/dist/core/application/exceptions.d.ts +17 -0
- package/dist/core/application/migrate.d.ts +19 -0
- package/dist/core/application/schema.d.ts +30 -0
- package/dist/core/application/types/application.interface.d.ts +37 -0
- package/dist/core/auth/jwt.d.ts +30 -0
- package/dist/core/auth/types/jwt.interface.d.ts +66 -0
- package/dist/core/blueprint/exceptions.d.ts +5 -0
- package/dist/core/blueprint/optional.d.ts +3 -1
- package/dist/core/blueprint/types/blueprint.interface.d.ts +79 -5
- package/dist/core/blueprint/variant.d.ts +32 -0
- package/dist/core/entity/abstract-entity.d.ts +5 -0
- package/dist/core/entity/entity.d.ts +1 -1
- package/dist/core/entity/exceptions.d.ts +9 -0
- package/dist/core/entity/trust.d.ts +24 -0
- package/dist/core/entity/types/entity.interface.d.ts +30 -0
- package/dist/core/output/access.d.ts +16 -0
- package/dist/core/output/exceptions.d.ts +16 -0
- package/dist/core/output/http.d.ts +36 -0
- package/dist/core/output/openapi-sink.d.ts +23 -2
- package/dist/core/output/stage.d.ts +7 -2
- package/dist/core/output/status.d.ts +1 -1
- package/dist/core/output/types/http.interface.d.ts +130 -0
- package/dist/core/output/types/output.interface.d.ts +25 -5
- package/dist/core/parse/issue.d.ts +28 -3
- package/dist/core/parse/parse.d.ts +9 -0
- package/dist/core/property/abstract-property.d.ts +9 -0
- package/dist/core/property/define-property.d.ts +34 -6
- package/dist/core/property/types/property-options.interface.d.ts +10 -0
- package/dist/core/property/types/property-type.interface.d.ts +4 -0
- package/dist/core/record/abstract-record.d.ts +7 -0
- package/dist/core/record/types/record.interface.d.ts +12 -1
- package/dist/core/repository/exceptions.d.ts +13 -0
- package/dist/core/repository/in-memory.d.ts +2 -2
- package/dist/core/repository/migrate.d.ts +20 -0
- package/dist/core/repository/state.d.ts +2 -0
- package/dist/core/repository/types/repository.interface.d.ts +33 -0
- package/dist/core/repository/types/schema.interface.d.ts +126 -0
- package/dist/core/repository/upcast.d.ts +64 -0
- package/dist/core/schema/schema.d.ts +28 -0
- package/dist/core/symbols/kind.d.ts +1 -1
- package/dist/core/use-case/access.d.ts +12 -0
- package/dist/core/use-case/decorators.d.ts +40 -0
- package/dist/core/use-case/route.d.ts +18 -0
- package/dist/core/use-case/steps/types/check.interface.d.ts +214 -0
- package/dist/core/use-case/types/access.interface.d.ts +39 -0
- package/dist/core/use-case/types/route.interface.d.ts +66 -0
- package/dist/core/use-case/use-case.d.ts +52 -2
- package/dist/experimental.d.ts +13 -1
- package/dist/experimental.js +63 -25
- package/dist/index-3aae5jfq.js +2997 -0
- package/dist/index-hg1z2s2v.js +1055 -0
- package/dist/index-ny3as79r.js +4567 -0
- package/dist/index-xegv6a1p.js +4291 -0
- package/dist/index-y280ew0k.js +396 -0
- package/dist/index.d.ts +45 -49
- package/dist/index.js +110 -186
- package/dist/std/properties/password.d.ts +0 -2
- package/dist/std/record-plugins.d.ts +2 -2
- package/dist/std/resource.d.ts +31 -0
- package/dist/std/runtime.d.ts +6 -0
- package/dist/std/use-cases/auth.d.ts +5 -1
- package/dist/std/use-cases/index.d.ts +1 -1
- package/dist/std/use-cases/shared.d.ts +2 -2
- package/dist/std/use-cases/types.d.ts +97 -1
- package/dist/testing/http-provider-contract.d.ts +32 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +495 -4
- package/dist/testing/provider-contract.d.ts +17 -1
- package/package.json +16 -3
- package/dist/index-qxfkyd6k.js +0 -1721
- 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:
|
|
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 {};
|
package/dist/experimental.d.ts
CHANGED
|
@@ -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";
|
package/dist/experimental.js
CHANGED
|
@@ -1,47 +1,63 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
propertyLineageOf2,
|
|
3
|
+
isBlueprint,
|
|
4
|
+
isRecordClass,
|
|
5
|
+
recordFieldOf,
|
|
6
|
+
InvalidBlueprintVariant2,
|
|
7
|
+
UndeclaredBlueprintProperty2,
|
|
8
|
+
isBlueprintVariant,
|
|
5
9
|
recordNameOf2,
|
|
6
10
|
listOf2,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
UndeclaredBlueprintProperty2,
|
|
11
|
+
isBlueprintList,
|
|
12
|
+
AbstractRecord,
|
|
10
13
|
optionalOf2,
|
|
11
14
|
blueprintFromProperties,
|
|
12
|
-
declareRecord
|
|
13
|
-
|
|
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 (
|
|
35
|
+
if (isBlueprintList(field))
|
|
20
36
|
return propertiesOfField(field.item);
|
|
21
|
-
const record =
|
|
37
|
+
const record = recordFieldOf(field);
|
|
22
38
|
if (record)
|
|
23
39
|
return record.record.blueprint.properties;
|
|
24
|
-
return
|
|
40
|
+
return isBlueprint(field) ? field.properties : undefined;
|
|
25
41
|
}
|
|
26
42
|
function maskOf(selector, against) {
|
|
27
|
-
if (
|
|
43
|
+
if (isRecordClass(selector))
|
|
28
44
|
return maskOf(selector.blueprint, against);
|
|
29
|
-
if (
|
|
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 (
|
|
36
|
-
return fieldMaskOf(field.item,
|
|
51
|
+
if (isBlueprintList(field)) {
|
|
52
|
+
return fieldMaskOf(field.item, isBlueprintList(other) ? other.item : other);
|
|
37
53
|
}
|
|
38
|
-
const record =
|
|
54
|
+
const record = recordFieldOf(field);
|
|
39
55
|
if (record) {
|
|
40
|
-
if (record.record ===
|
|
56
|
+
if (record.record === recordFieldOf(other)?.record)
|
|
41
57
|
return true;
|
|
42
58
|
return maskOf(record.record, propertiesOfField(other));
|
|
43
59
|
}
|
|
44
|
-
if (
|
|
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(
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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 =
|
|
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
|
};
|