@fougere/core 0.17.4-alpha.0 → 0.19.0-alpha.0
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 +1 -1
- package/dist/BootRefusal.d.ts +9 -0
- package/dist/BootRefusal.d.ts.map +1 -0
- package/dist/BootRefusal.js +12 -0
- package/dist/BootRefusal.js.map +1 -0
- package/dist/CallIdentity.d.ts.map +1 -1
- package/dist/CallIdentity.js +2 -1
- package/dist/CallIdentity.js.map +1 -1
- package/dist/Declared.d.ts +2 -5
- package/dist/Declared.d.ts.map +1 -1
- package/dist/DeclaredHandler.d.ts +3 -8
- package/dist/DeclaredHandler.d.ts.map +1 -1
- package/dist/DeclaredSubject.d.ts +2 -5
- package/dist/DeclaredSubject.d.ts.map +1 -1
- package/dist/EffectiveOperation.d.ts +7 -6
- package/dist/EffectiveOperation.d.ts.map +1 -1
- package/dist/EffectiveOperationModel.d.ts.map +1 -1
- package/dist/EffectiveOperationModel.js +9 -6
- package/dist/EffectiveOperationModel.js.map +1 -1
- package/dist/FrondConfig.d.ts +0 -2
- package/dist/FrondConfig.d.ts.map +1 -1
- package/dist/FrondConfig.js.map +1 -1
- package/dist/FrondDeclaration.d.ts +3 -8
- package/dist/FrondDeclaration.d.ts.map +1 -1
- package/dist/FrondDeclaration.js +2 -1
- package/dist/FrondDeclaration.js.map +1 -1
- package/dist/OperationOverride.d.ts +5 -3
- package/dist/OperationOverride.d.ts.map +1 -1
- package/dist/SourceView.d.ts +10 -8
- package/dist/SourceView.d.ts.map +1 -1
- package/dist/SourceView.js +10 -1
- package/dist/SourceView.js.map +1 -1
- package/dist/boot/AppLifecycle.d.ts.map +1 -1
- package/dist/boot/AppLifecycle.js +2 -1
- package/dist/boot/AppLifecycle.js.map +1 -1
- package/dist/boot/Emissions.d.ts +8 -1
- package/dist/boot/Emissions.d.ts.map +1 -1
- package/dist/boot/Emissions.js +16 -11
- package/dist/boot/Emissions.js.map +1 -1
- package/dist/boot/bootstrap.d.ts.map +1 -1
- package/dist/boot/bootstrap.js +27 -15
- package/dist/boot/bootstrap.js.map +1 -1
- package/dist/boot/card.d.ts.map +1 -1
- package/dist/boot/card.js.map +1 -1
- package/dist/boot/install.d.ts.map +1 -1
- package/dist/boot/install.js +1 -0
- package/dist/boot/install.js.map +1 -1
- package/dist/boot/ownership.d.ts.map +1 -1
- package/dist/boot/ownership.js +26 -14
- package/dist/boot/ownership.js.map +1 -1
- package/dist/boot/unregistered.d.ts +4 -2
- package/dist/boot/unregistered.d.ts.map +1 -1
- package/dist/boot/unregistered.js +30 -30
- package/dist/boot/unregistered.js.map +1 -1
- package/dist/contract.d.ts +3 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +3 -0
- package/dist/contract.js.map +1 -1
- package/dist/descriptor/CollectorEntry.d.ts +2 -7
- package/dist/descriptor/CollectorEntry.d.ts.map +1 -1
- package/dist/descriptor/FrondDescriptor.d.ts +5 -23
- package/dist/descriptor/FrondDescriptor.d.ts.map +1 -1
- package/dist/descriptor/FrondDescriptor.js.map +1 -1
- package/dist/descriptor/HandlerEntry.d.ts +2 -7
- package/dist/descriptor/HandlerEntry.d.ts.map +1 -1
- package/dist/descriptor/MiddlewareEntry.d.ts +2 -7
- package/dist/descriptor/MiddlewareEntry.d.ts.map +1 -1
- package/dist/descriptor/PresenterEntry.d.ts +2 -7
- package/dist/descriptor/PresenterEntry.d.ts.map +1 -1
- package/dist/descriptor/ProviderEntry.d.ts +2 -7
- package/dist/descriptor/ProviderEntry.d.ts.map +1 -1
- package/dist/descriptor/Subject.d.ts +10 -0
- package/dist/descriptor/Subject.d.ts.map +1 -0
- package/dist/descriptor/Subject.js +2 -0
- package/dist/descriptor/Subject.js.map +1 -0
- package/dist/descriptor/index.d.ts +1 -0
- package/dist/descriptor/index.d.ts.map +1 -1
- package/dist/descriptor/index.js +1 -0
- package/dist/descriptor/index.js.map +1 -1
- package/dist/diagnostic.d.ts +2 -1
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +2 -1
- package/dist/diagnostic.js.map +1 -1
- package/dist/dispatch/ArgumentResolver.d.ts +13 -1
- package/dist/dispatch/ArgumentResolver.d.ts.map +1 -1
- package/dist/dispatch/ArgumentResolver.js +48 -2
- package/dist/dispatch/ArgumentResolver.js.map +1 -1
- package/dist/dispatch/EntityLookup.d.ts +4 -0
- package/dist/dispatch/EntityLookup.d.ts.map +1 -0
- package/dist/dispatch/EntityLookup.js +2 -0
- package/dist/dispatch/EntityLookup.js.map +1 -0
- package/dist/dispatch/Facade.d.ts +3 -0
- package/dist/dispatch/Facade.d.ts.map +1 -1
- package/dist/dispatch/HandlerFacade.d.ts +2 -0
- package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
- package/dist/dispatch/HandlerFacade.js +14 -4
- package/dist/dispatch/HandlerFacade.js.map +1 -1
- package/dist/entry/facade.d.ts +10 -1
- package/dist/entry/facade.d.ts.map +1 -1
- package/dist/entry/facade.js +26 -2
- package/dist/entry/facade.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/prefab/AggregateConstructor.d.ts +2 -2
- package/dist/prefab/AggregateConstructor.d.ts.map +1 -1
- package/dist/prefab/AggregateOf.d.ts +2 -2
- package/dist/prefab/AggregateOf.d.ts.map +1 -1
- package/dist/prefab/AggregateShape.d.ts +2 -2
- package/dist/prefab/AggregateShape.d.ts.map +1 -1
- package/dist/prefab/CrudConstructor.d.ts +2 -2
- package/dist/prefab/CrudConstructor.d.ts.map +1 -1
- package/dist/prefab/CrudConstructor.js +13 -10
- package/dist/prefab/CrudConstructor.js.map +1 -1
- package/dist/prefab/CrudOps.d.ts +2 -2
- package/dist/prefab/CrudOps.d.ts.map +1 -1
- package/dist/prefab/CrudViews.d.ts +2 -2
- package/dist/prefab/CrudViews.d.ts.map +1 -1
- package/dist/prefab/Middleware.d.ts +13 -0
- package/dist/prefab/Middleware.d.ts.map +1 -0
- package/dist/prefab/Middleware.js +10 -0
- package/dist/prefab/Middleware.js.map +1 -0
- package/dist/prefab/MirrorConstructor.d.ts +2 -2
- package/dist/prefab/MirrorConstructor.d.ts.map +1 -1
- package/dist/prefab/MirrorConstructor.js.map +1 -1
- package/dist/prefab/RepositoryConstructor.d.ts +4 -4
- package/dist/prefab/RepositoryConstructor.d.ts.map +1 -1
- package/dist/prefab/RepositoryConstructor.js.map +1 -1
- package/dist/prefab/collector.d.ts +2 -1
- package/dist/prefab/collector.d.ts.map +1 -1
- package/dist/prefab/collector.js.map +1 -1
- package/dist/prefab/opsOf.d.ts +4 -0
- package/dist/prefab/opsOf.d.ts.map +1 -0
- package/dist/prefab/opsOf.js +5 -0
- package/dist/prefab/opsOf.js.map +1 -0
- package/dist/prefab/prefab.d.ts +3 -3
- package/dist/prefab/prefab.d.ts.map +1 -1
- package/dist/prefab/presenter.d.ts +3 -3
- package/dist/prefab/presenter.d.ts.map +1 -1
- package/dist/prefab/presenter.js.map +1 -1
- package/dist/wire/Addresses.d.ts +4 -4
- package/dist/wire/Addresses.d.ts.map +1 -1
- package/dist/wire/Cardinality.d.ts +4 -0
- package/dist/wire/Cardinality.d.ts.map +1 -0
- package/dist/wire/Cardinality.js +3 -0
- package/dist/wire/Cardinality.js.map +1 -0
- package/dist/wire/Facade.d.ts +5 -7
- package/dist/wire/Facade.d.ts.map +1 -1
- package/dist/wire/Facade.js.map +1 -1
- package/dist/wire/Invocation.d.ts +2 -1
- package/dist/wire/Invocation.d.ts.map +1 -1
- package/dist/wire/Invocation.js +4 -1
- package/dist/wire/Invocation.js.map +1 -1
- package/dist/wire/InvocationContext.d.ts +6 -0
- package/dist/wire/InvocationContext.d.ts.map +1 -1
- package/dist/wire/OperationContract.d.ts +2 -1
- package/dist/wire/OperationContract.d.ts.map +1 -1
- package/dist/wire/OperationContract.js +2 -2
- package/dist/wire/OperationContract.js.map +1 -1
- package/dist/wire/OperationKind.d.ts +3 -1
- package/dist/wire/OperationKind.d.ts.map +1 -1
- package/dist/wire/OperationKind.js +2 -1
- package/dist/wire/OperationKind.js.map +1 -1
- package/dist/wire/Placement.d.ts +4 -0
- package/dist/wire/Placement.d.ts.map +1 -0
- package/dist/wire/Placement.js +3 -0
- package/dist/wire/Placement.js.map +1 -0
- package/dist/wire/RouteKind.d.ts +2 -1
- package/dist/wire/RouteKind.d.ts.map +1 -1
- package/dist/wire/SignedCall.d.ts +2 -0
- package/dist/wire/SignedCall.d.ts.map +1 -1
- package/dist/wire/StateShape.d.ts +1 -2
- package/dist/wire/StateShape.d.ts.map +1 -1
- package/dist/wire/StateShape.js +11 -11
- package/dist/wire/StateShape.js.map +1 -1
- package/dist/wire/TypeRef.d.ts +7 -0
- package/dist/wire/TypeRef.d.ts.map +1 -1
- package/dist/wire/binding.d.ts +11 -1
- package/dist/wire/binding.d.ts.map +1 -1
- package/dist/wire/binding.js +4 -2
- package/dist/wire/binding.js.map +1 -1
- package/dist/wire/card/CardOp.d.ts +4 -10
- package/dist/wire/card/CardOp.d.ts.map +1 -1
- package/dist/wire/refusals.d.ts +2 -1
- package/dist/wire/refusals.d.ts.map +1 -1
- package/dist/wire/refusals.js.map +1 -1
- package/dist/wire/topology/DeclaredFrond.d.ts +2 -1
- package/dist/wire/topology/DeclaredFrond.d.ts.map +1 -1
- package/dist/wire/topology/FrondPlacement.d.ts +2 -1
- package/dist/wire/topology/FrondPlacement.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/BootRefusal.ts +11 -0
- package/src/CallIdentity.ts +2 -1
- package/src/Declared.ts +3 -7
- package/src/DeclaredHandler.ts +3 -8
- package/src/DeclaredSubject.ts +3 -5
- package/src/EffectiveOperation.ts +7 -6
- package/src/EffectiveOperationModel.ts +11 -7
- package/src/FrondConfig.ts +0 -2
- package/src/FrondDeclaration.ts +5 -8
- package/src/OperationOverride.ts +5 -3
- package/src/SourceView.ts +21 -3
- package/src/boot/AppLifecycle.ts +2 -1
- package/src/boot/Emissions.ts +18 -11
- package/src/boot/bootstrap.ts +32 -16
- package/src/boot/card.ts +1 -2
- package/src/boot/install.ts +1 -0
- package/src/boot/ownership.ts +35 -16
- package/src/boot/unregistered.ts +31 -33
- package/src/contract.ts +3 -0
- package/src/descriptor/CollectorEntry.ts +2 -7
- package/src/descriptor/FrondDescriptor.ts +5 -19
- package/src/descriptor/HandlerEntry.ts +2 -7
- package/src/descriptor/MiddlewareEntry.ts +2 -7
- package/src/descriptor/PresenterEntry.ts +2 -7
- package/src/descriptor/ProviderEntry.ts +2 -7
- package/src/descriptor/Subject.ts +10 -0
- package/src/descriptor/index.ts +1 -0
- package/src/diagnostic.ts +4 -2
- package/src/dispatch/ArgumentResolver.ts +54 -2
- package/src/dispatch/EntityLookup.ts +4 -0
- package/src/dispatch/Facade.ts +3 -0
- package/src/dispatch/HandlerFacade.ts +14 -6
- package/src/entry/facade.ts +38 -2
- package/src/index.ts +5 -1
- package/src/prefab/AggregateConstructor.ts +2 -2
- package/src/prefab/AggregateOf.ts +2 -2
- package/src/prefab/AggregateShape.ts +2 -2
- package/src/prefab/CrudConstructor.ts +18 -13
- package/src/prefab/CrudOps.ts +3 -3
- package/src/prefab/CrudViews.ts +2 -2
- package/src/prefab/Middleware.ts +13 -0
- package/src/prefab/MirrorConstructor.ts +2 -2
- package/src/prefab/RepositoryConstructor.ts +9 -9
- package/src/prefab/collector.ts +4 -3
- package/src/prefab/opsOf.ts +6 -0
- package/src/prefab/prefab.ts +5 -5
- package/src/prefab/presenter.ts +3 -3
- package/src/wire/Addresses.ts +4 -4
- package/src/wire/Cardinality.ts +4 -0
- package/src/wire/Facade.ts +6 -9
- package/src/wire/Invocation.ts +3 -1
- package/src/wire/InvocationContext.ts +6 -0
- package/src/wire/OperationContract.ts +4 -3
- package/src/wire/OperationKind.ts +4 -1
- package/src/wire/Placement.ts +4 -0
- package/src/wire/RouteKind.ts +3 -1
- package/src/wire/SignedCall.ts +2 -0
- package/src/wire/StateShape.ts +9 -11
- package/src/wire/TypeRef.ts +4 -0
- package/src/wire/binding.ts +10 -2
- package/src/wire/card/CardOp.ts +4 -10
- package/src/wire/refusals.ts +2 -1
- package/src/wire/topology/DeclaredFrond.ts +3 -1
- package/src/wire/topology/FrondPlacement.ts +3 -1
package/src/dispatch/Facade.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { HandlerEntry } from '../descriptor/HandlerEntry.js';
|
|
|
5
5
|
import type { PresenterEntry } from '../descriptor/PresenterEntry.js';
|
|
6
6
|
import type { StateShape } from '../wire/StateShape.js';
|
|
7
7
|
import type { OwnLogger } from '../builtin/OwnLoggers.js';
|
|
8
|
+
import type { EntityLookup } from './EntityLookup.js';
|
|
8
9
|
|
|
9
10
|
/** What boot resolved around one handler, beyond the handler and the scope it resolves in. */
|
|
10
11
|
export interface Facade {
|
|
@@ -16,6 +17,8 @@ export interface Facade {
|
|
|
16
17
|
handlers: readonly HandlerEntry[];
|
|
17
18
|
/** The canonical operation table resolved before boot performs any side effect. */
|
|
18
19
|
operations: EffectiveOperationsMap;
|
|
20
|
+
/** Where a parameter typed `Post['title']` finds the rules of `title`. */
|
|
21
|
+
entities: EntityLookup;
|
|
19
22
|
/** Entity names this frond has a collector for. */
|
|
20
23
|
collectors: Set<string>;
|
|
21
24
|
/** The presenter over this handler's entity, when the frond declares one. */
|
|
@@ -35,9 +35,7 @@ export class HandlerFacade {
|
|
|
35
35
|
private instance: any;
|
|
36
36
|
private readonly implementationKeys = new Map<string, string>();
|
|
37
37
|
private readonly implementationInstances = new Map<string, any>();
|
|
38
|
-
private readonly arguments
|
|
39
|
-
(typeName) => this.collectorResolver(typeName),
|
|
40
|
-
);
|
|
38
|
+
private readonly arguments: ArgumentResolver;
|
|
41
39
|
/** A computed field's parameters and the collectors in scope are both boot-time facts. */
|
|
42
40
|
private readonly presenterPlans: Map<string, BindingPlan>;
|
|
43
41
|
|
|
@@ -52,6 +50,7 @@ export class HandlerFacade {
|
|
|
52
50
|
private readonly facade: Facade,
|
|
53
51
|
) {
|
|
54
52
|
this.refuseCrudWithoutRepository(handler);
|
|
53
|
+
this.arguments = new ArgumentResolver((typeName) => this.collectorResolver(typeName), facade.entities);
|
|
55
54
|
|
|
56
55
|
this.presenterPlans = facade.presenter
|
|
57
56
|
? presenterPlans(facade.presenter, facade.collectors)
|
|
@@ -124,22 +123,31 @@ export class HandlerFacade {
|
|
|
124
123
|
entered: Record<string, unknown>,
|
|
125
124
|
): Promise<unknown> {
|
|
126
125
|
const state = this.facade.state.judge(context.state, this.handler.address, op, entered);
|
|
127
|
-
const
|
|
126
|
+
const written = invocation.args !== undefined;
|
|
127
|
+
const validated = written ? { ...invocation, state } : validateInput(contract.input, { ...invocation, state });
|
|
128
128
|
context.invocation = validated;
|
|
129
129
|
|
|
130
|
-
const args =
|
|
130
|
+
const args = await this.argumentsOf(contract, validated);
|
|
131
131
|
const { instance, method } = this.resolveImplementation(op);
|
|
132
132
|
const answered = await instance[method](...args);
|
|
133
133
|
|
|
134
134
|
const view = this.viewOf(op);
|
|
135
135
|
const { presenter } = this.facade;
|
|
136
|
-
const computed = view.closed || !presenter
|
|
136
|
+
const computed = written || view.closed || !presenter
|
|
137
137
|
? answered
|
|
138
138
|
: await this.present(op, presenter, answered, validated);
|
|
139
139
|
|
|
140
140
|
return view.project(computed);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** What code wrote, judged; or what a door received, read through the binding plan. */
|
|
144
|
+
private async argumentsOf(contract: OperationContract, invocation: InvocationContext): Promise<unknown[]> {
|
|
145
|
+
if (!contract.binding) return [];
|
|
146
|
+
if (invocation.args !== undefined) return this.arguments.given(contract.binding, contract.input, invocation);
|
|
147
|
+
|
|
148
|
+
return this.arguments.resolve(contract.binding, invocation);
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
/**
|
|
144
152
|
* The computed fields a presenter adds, over the rows the handler answered and BEFORE the
|
|
145
153
|
* view projects them. A presenter runs on the server, so what it reads is the row the
|
package/src/entry/facade.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Call } from '../wire/Call.js';
|
|
|
3
3
|
import { RouteAddress } from '../wire/RouteAddress.js';
|
|
4
4
|
import type { DispatchPort } from '../dispatch/DispatchPort.js';
|
|
5
5
|
import type { Received } from '../dispatch/Received.js';
|
|
6
|
+
import type { PartialInvocation } from '../wire/PartialInvocation.js';
|
|
6
7
|
|
|
7
8
|
type Operation = (...args: any[]) => unknown;
|
|
8
9
|
|
|
@@ -24,11 +25,14 @@ export function dynamicOperations(operation: (name: string) => Operation): Recor
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
+
* The facade code calls: each operation takes the handler's own arguments.
|
|
28
29
|
*
|
|
29
30
|
* `received` is what this side puts back before handing the answer over: a row leaves as data
|
|
30
31
|
* and `date-time` means a `Date` on both sides. A facade built without one hands over what the
|
|
31
32
|
* wire carried, which is what a caller holding no schema can do.
|
|
33
|
+
*
|
|
34
|
+
* The trailing `undefined`s are dropped because a JSON array writes them `null`, and the far
|
|
35
|
+
* side would read a value where the caller left one out. An optional parameter is always last.
|
|
32
36
|
*/
|
|
33
37
|
export function facadeOperations(
|
|
34
38
|
dispatcher: DispatchPort,
|
|
@@ -37,7 +41,30 @@ export function facadeOperations(
|
|
|
37
41
|
surface?: string,
|
|
38
42
|
received?: Received,
|
|
39
43
|
): Record<string, Operation> {
|
|
40
|
-
const
|
|
44
|
+
const send = sending(dispatcher, address, surface, received);
|
|
45
|
+
|
|
46
|
+
return operationsOf((name) => (...args: unknown[]) => send(name, { args: withoutTrailingAbsence(args) }), operationNames);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* What a door sends: the invocation it received, as a `Call`. A door holds a request, not the
|
|
51
|
+
* handler's arguments, so the binding plan reads it on the far side — collectors and presenter
|
|
52
|
+
* included.
|
|
53
|
+
*/
|
|
54
|
+
export function callOperations(
|
|
55
|
+
dispatcher: DispatchPort,
|
|
56
|
+
address: string,
|
|
57
|
+
operationNames?: Iterable<string>,
|
|
58
|
+
surface?: string,
|
|
59
|
+
received?: Received,
|
|
60
|
+
): Record<string, Operation> {
|
|
61
|
+
const send = sending(dispatcher, address, surface, received);
|
|
62
|
+
|
|
63
|
+
return operationsOf((name) => (invocation?: PartialInvocation) => send(name, invocation), operationNames);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sending(dispatcher: DispatchPort, address: string, surface?: string, received?: Received) {
|
|
67
|
+
return async (name: string, invocation?: PartialInvocation): Promise<unknown> => {
|
|
41
68
|
const answer = await dispatcher.dispatch(new Call(
|
|
42
69
|
new RouteAddress({
|
|
43
70
|
address,
|
|
@@ -49,8 +76,17 @@ export function facadeOperations(
|
|
|
49
76
|
|
|
50
77
|
return received ? received(name, answer) : answer;
|
|
51
78
|
};
|
|
79
|
+
}
|
|
52
80
|
|
|
81
|
+
function operationsOf(operation: (name: string) => Operation, operationNames?: Iterable<string>): Record<string, Operation> {
|
|
53
82
|
return operationNames
|
|
54
83
|
? Object.fromEntries([...operationNames].map((name) => [name, operation(name)]))
|
|
55
84
|
: dynamicOperations(operation);
|
|
56
85
|
}
|
|
86
|
+
|
|
87
|
+
function withoutTrailingAbsence(args: unknown[]): unknown[] {
|
|
88
|
+
let length = args.length;
|
|
89
|
+
while (length > 0 && args[length - 1] === undefined) length--;
|
|
90
|
+
|
|
91
|
+
return args.slice(0, length);
|
|
92
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type { CrudConstructor } from './prefab/CrudConstructor.js';
|
|
|
23
23
|
export type { CrudOpName } from './prefab/CrudOpName.js';
|
|
24
24
|
export type { CrudOps } from './prefab/CrudOps.js';
|
|
25
25
|
export type { CrudViews } from './prefab/CrudViews.js';
|
|
26
|
+
export { Middleware } from './prefab/Middleware.js';
|
|
26
27
|
export { Mirror } from './prefab/MirrorConstructor.js';
|
|
27
28
|
export type { MirrorConstructor } from './prefab/MirrorConstructor.js';
|
|
28
29
|
export type { MirrorOf } from './prefab/MirrorOf.js';
|
|
@@ -63,6 +64,7 @@ export type { AppMiddleware } from './wire/AppMiddleware.js';
|
|
|
63
64
|
export type { AppNext } from './wire/AppNext.js';
|
|
64
65
|
export type { OperationContext } from './wire/OperationContext.js';
|
|
65
66
|
export { assertIdentityCard, RPC_ADDRESS, ENVELOPE_BYTES, maxBodyBytes, maxFrameBytes, setMaxBodyBytes } from './contract.js';
|
|
67
|
+
export { OPERATION_KINDS, CARDINALITIES, PLACEMENTS, type OperationKind, type Cardinality, type Placement } from './contract.js';
|
|
66
68
|
export { createLocalRunner, createAppRunner } from './boot/runner.js';
|
|
67
69
|
|
|
68
70
|
// A frond an app STATES, for a boot that will not scan. No disk, no `typescript` — which
|
|
@@ -81,6 +83,7 @@ export { declaredTopologyOf } from './boot/declared.js';
|
|
|
81
83
|
// Both halves of what a call can refuse, put together where they are READ — the framework's
|
|
82
84
|
// follows from `kind` and `input`, so only the frond's travels.
|
|
83
85
|
export { refusalsOf, type Refusable } from './wire/refusals.js';
|
|
86
|
+
export type { Binding } from './Binding.js';
|
|
84
87
|
// The facades as TYPES — empty here, filled by the `.d.ts` the scan writes beside the app.
|
|
85
88
|
export type { Addresses } from './wire/Addresses.js';
|
|
86
89
|
export type { Answer } from './wire/Answer.js';
|
|
@@ -129,7 +132,7 @@ export { Storage, togetherKeyOf } from './storage/Storage.js';
|
|
|
129
132
|
export type { Constraint } from './Constraint.js';
|
|
130
133
|
export type { Source } from './Source.js';
|
|
131
134
|
export type { SourceConfig } from './SourceConfig.js';
|
|
132
|
-
export type
|
|
135
|
+
export { sourceViewOf, type SourceView } from './SourceView.js';
|
|
133
136
|
export { Sources } from './Source.js';
|
|
134
137
|
export { storageOver } from './storage/Store.js';
|
|
135
138
|
export type { Store } from './storage/Store.js';
|
|
@@ -138,6 +141,7 @@ export type { App } from './boot/App.js';
|
|
|
138
141
|
export type { CreateAppOptions } from './boot/CreateAppOptions.js';
|
|
139
142
|
export type { ScanResult } from './scan.js';
|
|
140
143
|
export type { Diagnostic } from './diagnostic.js';
|
|
144
|
+
export { BootRefusal } from './BootRefusal.js';
|
|
141
145
|
export { DEFAULT_CONVENTIONS, frondDirsOf, frondPackage, providerDirsOf, resolveConventions, type Conventions } from './Conventions.js';
|
|
142
146
|
export { type ConventionsInput } from './ConventionsInput.js';
|
|
143
147
|
export type { CollectorEntry } from './descriptor/CollectorEntry.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AggregateShape } from './AggregateShape.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Entity } from '@fougere/schema';
|
|
3
3
|
|
|
4
|
-
export type AggregateConstructor<E extends readonly
|
|
4
|
+
export type AggregateConstructor<E extends readonly Entity[]> =
|
|
5
5
|
(new (...storages: unknown[]) => AggregateShape<E>) & {
|
|
6
6
|
readonly __entity: unknown;
|
|
7
7
|
/** The entities this class owns. Present from two on — an owner of one owns nothing. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Entity } from '@fougere/schema';
|
|
2
2
|
import type { Storage } from '../storage/Storage.js';
|
|
3
3
|
|
|
4
4
|
/** The tuple an aggregate holds — the readable form of {@link AggregateShape}'s member. */
|
|
5
|
-
export type AggregateOf<E extends readonly
|
|
5
|
+
export type AggregateOf<E extends readonly Entity[]> =
|
|
6
6
|
{ [K in keyof E]: Storage<InstanceType<E[K]>> };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Storage } from '../storage/Storage.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Entity } from '@fougere/schema';
|
|
3
3
|
|
|
4
4
|
/** What an aggregate's subclass sees. */
|
|
5
|
-
export declare abstract class AggregateShape<E extends readonly
|
|
5
|
+
export declare abstract class AggregateShape<E extends readonly Entity[]> {
|
|
6
6
|
protected storages: { [K in keyof E]: Storage<InstanceType<E[K]>> };
|
|
7
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { lowerFirst, type
|
|
1
|
+
import { FieldSet, lowerFirst, type Entity, type SchemaView } from '@fougere/schema';
|
|
2
2
|
import type { ListOptions } from '../storage/ListOptions.js';
|
|
3
3
|
import type { Storage } from '../storage/Storage.js';
|
|
4
4
|
import type { OperationContract } from '../wire/OperationContract.js';
|
|
@@ -29,13 +29,15 @@ export function subjectOf(ctor: unknown, address: string): string {
|
|
|
29
29
|
return target?.name ? lowerFirst(target.name) : address;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/** The id of the row an op acts on — a route
|
|
33
|
-
const byId =
|
|
32
|
+
/** The id of the row an op acts on — `id: Post['id']`, which a route places before the op. */
|
|
33
|
+
const byId = (entity: string, field: string) =>
|
|
34
|
+
({ name: 'id', source: { kind: 'param' as const, name: 'id', field: { entity, name: field }, identifies: entity }, optional: false });
|
|
34
35
|
|
|
35
36
|
const fromBody = { name: 'input', source: { kind: 'input' as const }, optional: false };
|
|
36
37
|
|
|
37
38
|
/** The same five, written as the scan would have written them. */
|
|
38
|
-
const idParam =
|
|
39
|
+
const idParam = (entity: string, field: string) =>
|
|
40
|
+
({ name: 'id', type: { raw: `${entity}['${field}']`, name: 'string', field: { entity, name: field }, identifies: entity } });
|
|
39
41
|
|
|
40
42
|
const inputParam = (entity: string) => ({ name: 'input', type: { raw: `Partial<${entity}>`, name: entity } });
|
|
41
43
|
|
|
@@ -50,6 +52,9 @@ const returns = (raw: string, name: string, extra?: { array?: boolean; nullable?
|
|
|
50
52
|
function crudOps(entity: SchemaView & { partial?: () => SchemaView }): Record<string, OperationContract> {
|
|
51
53
|
const name = (entity as { name?: string }).name ?? 'Entity';
|
|
52
54
|
const input = inputParam(name);
|
|
55
|
+
const primary = FieldSet.of(entity.getFields()).primary ?? 'id';
|
|
56
|
+
const id = byId(name, primary);
|
|
57
|
+
const idSignature = idParam(name, primary);
|
|
53
58
|
|
|
54
59
|
return {
|
|
55
60
|
list: {
|
|
@@ -61,8 +66,8 @@ function crudOps(entity: SchemaView & { partial?: () => SchemaView }): Record<st
|
|
|
61
66
|
},
|
|
62
67
|
},
|
|
63
68
|
findById: {
|
|
64
|
-
output: entity, cardinality: 'maybe', binding: [
|
|
65
|
-
signature: { name: 'findById', returnType: returns(`${name} | undefined`, name, { nullable: true }), params: [
|
|
69
|
+
output: entity, cardinality: 'maybe', binding: [id],
|
|
70
|
+
signature: { name: 'findById', returnType: returns(`${name} | undefined`, name, { nullable: true }), params: [idSignature] },
|
|
66
71
|
},
|
|
67
72
|
create: {
|
|
68
73
|
input: entity, output: entity, cardinality: 'one', binding: [fromBody],
|
|
@@ -71,12 +76,12 @@ function crudOps(entity: SchemaView & { partial?: () => SchemaView }): Record<st
|
|
|
71
76
|
// The patch view carries its own mode: an absent field is untouched, an
|
|
72
77
|
// immutable one re-supplied is refused.
|
|
73
78
|
update: {
|
|
74
|
-
input: entity.partial?.(), output: entity, cardinality: 'one', binding: [
|
|
75
|
-
signature: { name: 'update', returnType: returns(name, name), params: [
|
|
79
|
+
input: entity.partial?.(), output: entity, cardinality: 'one', binding: [id, fromBody],
|
|
80
|
+
signature: { name: 'update', returnType: returns(name, name), params: [idSignature, input] },
|
|
76
81
|
},
|
|
77
82
|
delete: {
|
|
78
|
-
cardinality: 'none', binding: [
|
|
79
|
-
signature: { name: 'delete', returnType: returns('boolean', 'boolean'), params: [
|
|
83
|
+
cardinality: 'none', binding: [id],
|
|
84
|
+
signature: { name: 'delete', returnType: returns('boolean', 'boolean'), params: [idSignature] },
|
|
80
85
|
},
|
|
81
86
|
};
|
|
82
87
|
}
|
|
@@ -103,7 +108,7 @@ export interface CrudConstructor<T, V = {}> {
|
|
|
103
108
|
*
|
|
104
109
|
* Documented: [handlers](https://fougere.dev/docs/business/handlers).
|
|
105
110
|
*/
|
|
106
|
-
export function Crud<E extends
|
|
111
|
+
export function Crud<E extends Entity, V extends CrudViews | Entity = {}>(
|
|
107
112
|
entity: E,
|
|
108
113
|
output?: V,
|
|
109
114
|
): CrudConstructor<InstanceType<E>, V> {
|
|
@@ -112,7 +117,7 @@ export function Crud<E extends EntityConstructor, V extends CrudViews | EntityCo
|
|
|
112
117
|
|
|
113
118
|
// A view is a class (it carries fields) ; a map of views is a plain object.
|
|
114
119
|
const perOp = typeof output === 'object' && output !== null ? (output as CrudViews) : undefined;
|
|
115
|
-
const wholeHandler = typeof output === 'function' ? (output as
|
|
120
|
+
const wholeHandler = typeof output === 'function' ? (output as Entity) : undefined;
|
|
116
121
|
|
|
117
122
|
return asCrudConstructor<T, V>(class CrudHandler {
|
|
118
123
|
static __entity = entity;
|
|
@@ -123,7 +128,7 @@ export function Crud<E extends EntityConstructor, V extends CrudViews | EntityCo
|
|
|
123
128
|
* What this prefab handler declares — read by the façade, merged under the author's own
|
|
124
129
|
* methods.
|
|
125
130
|
*/
|
|
126
|
-
static __ops: Record<string, OperationContract> = crudOps(entity
|
|
131
|
+
static __ops: Record<string, OperationContract> = crudOps(entity);
|
|
127
132
|
|
|
128
133
|
// Private: the five ops are how a subclass reaches the rows — `super.findById(id)` — and a
|
|
129
134
|
// query worth a name belongs to the repository, which it asks for like any handler does.
|
package/src/prefab/CrudOps.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CrudOpName } from './CrudOpName.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Entity } from '@fougere/schema';
|
|
3
3
|
import type { ListOptions } from '../storage/ListOptions.js';
|
|
4
4
|
import type { Page } from '../wire/Page.js';
|
|
5
5
|
|
|
@@ -8,8 +8,8 @@ type OutOf<V, K extends CrudOpName, T> =
|
|
|
8
8
|
// Bracketed on purpose: a naked `V extends …` DISTRIBUTES, and the no-view default
|
|
9
9
|
// is the empty map, whose `keyof` is `never` — distribution would then collapse
|
|
10
10
|
// every op's output to `never` instead of falling through to the entity.
|
|
11
|
-
[V] extends [
|
|
12
|
-
: K extends keyof V ? (V[K] extends
|
|
11
|
+
[V] extends [Entity] ? InstanceType<V & Entity>
|
|
12
|
+
: K extends keyof V ? (V[K] extends Entity ? InstanceType<V[K]> : T)
|
|
13
13
|
: T;
|
|
14
14
|
|
|
15
15
|
/** The five ops, typed from the entity and its views. */
|
package/src/prefab/CrudViews.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Entity } from '@fougere/schema';
|
|
2
2
|
import type { CrudOpName } from './CrudOpName.js';
|
|
3
3
|
|
|
4
4
|
/** Which view each op speaks — omitted ops speak the entity, the trivial view. */
|
|
5
|
-
export type CrudViews = Partial<Record<CrudOpName,
|
|
5
|
+
export type CrudViews = Partial<Record<CrudOpName, Entity>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppNext } from '../wire/AppNext.js';
|
|
2
|
+
import type { OperationContext } from '../wire/OperationContext.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What runs around every operation of its frond, and of the fronds under it — a class in
|
|
6
|
+
* `middlewares/` that extends this one.
|
|
7
|
+
*
|
|
8
|
+
* It answers what `next()` answered, the same type: a caller reads the handler's signature,
|
|
9
|
+
* and nothing tells that signature a middleware stood in the way.
|
|
10
|
+
*/
|
|
11
|
+
export abstract class Middleware {
|
|
12
|
+
abstract around<T>(context: OperationContext, next: AppNext<T>): Promise<T>;
|
|
13
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Entity } from '@fougere/schema';
|
|
2
2
|
import type { Storage } from '../storage/Storage.js';
|
|
3
3
|
import type { Refreshed } from './Refreshed.js';
|
|
4
4
|
import type { MirrorOf } from './MirrorOf.js';
|
|
@@ -8,7 +8,7 @@ export interface MirrorConstructor<T> {
|
|
|
8
8
|
readonly __entity: unknown;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function Mirror<E extends
|
|
11
|
+
export function Mirror<E extends Entity>(shape: E): MirrorConstructor<InstanceType<E>> {
|
|
12
12
|
type T = InstanceType<E>;
|
|
13
13
|
|
|
14
14
|
abstract class MirrorBase implements MirrorOf<T> {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { upperFirst, type
|
|
1
|
+
import { upperFirst, type Entity } from '@fougere/schema';
|
|
2
2
|
import type { ListOptions } from '../storage/ListOptions.js';
|
|
3
3
|
import type { ListResult } from '../storage/ListResult.js';
|
|
4
4
|
import type { SelectOption } from '../storage/SelectOption.js';
|
|
@@ -14,22 +14,22 @@ export interface RepositoryConstructor<T> {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/** What an aggregate's subclass sees. */
|
|
17
|
-
declare abstract class AggregateShape<E extends readonly
|
|
17
|
+
declare abstract class AggregateShape<E extends readonly Entity[]> {
|
|
18
18
|
protected storages: { [K in keyof E]: Storage<InstanceType<E[K]>> };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function Repository<E extends
|
|
21
|
+
export function Repository<E extends Entity>(entity: E): RepositoryConstructor<InstanceType<E>>;
|
|
22
22
|
|
|
23
|
-
export function Repository<E extends readonly [
|
|
23
|
+
export function Repository<E extends readonly [Entity, Entity, ...Entity[]]>(
|
|
24
24
|
...entities: E
|
|
25
25
|
): AggregateConstructor<E>;
|
|
26
26
|
|
|
27
|
-
export function Repository(...entities:
|
|
27
|
+
export function Repository(...entities: Entity[]): unknown {
|
|
28
28
|
return entities.length === 1 ? one(entities[0]) : many(entities);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** The port forwarded, member by member. */
|
|
32
|
-
function one<E extends
|
|
32
|
+
function one<E extends Entity>(entity: E): RepositoryConstructor<InstanceType<E>> {
|
|
33
33
|
type T = InstanceType<E>;
|
|
34
34
|
|
|
35
35
|
class RepositoryBase implements Storage<T> {
|
|
@@ -56,7 +56,7 @@ function one<E extends EntityConstructor>(entity: E): RepositoryConstructor<Inst
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/** The aggregate. */
|
|
59
|
-
function many<E extends readonly
|
|
59
|
+
function many<E extends readonly Entity[]>(entities: E): AggregateConstructor<E> {
|
|
60
60
|
class AggregateBase {
|
|
61
61
|
static readonly __entity = entities[0];
|
|
62
62
|
static readonly __owns = entities;
|
|
@@ -77,6 +77,6 @@ export function repositoryKeyOf(entity: string): string {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/** The entities a class owns — empty for anything that is not an aggregate. */
|
|
80
|
-
export function ownedBy(ctor: unknown): readonly
|
|
81
|
-
return (ctor as { __owns?: readonly
|
|
80
|
+
export function ownedBy(ctor: unknown): readonly Entity[] {
|
|
81
|
+
return (ctor as { __owns?: readonly Entity[] } | undefined)?.__owns ?? [];
|
|
82
82
|
}
|
package/src/prefab/collector.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Documented: [collectors](https://fougere.dev/docs/business/collectors).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import type { AbstractCtor } from '../Ctor.js';
|
|
8
|
+
import { FieldValueValidator, dotted, json, upperFirst, type Entity, type Field, type SchemaView } from '@fougere/schema';
|
|
8
9
|
import { ErrorCode } from '../wire/ErrorCode.js';
|
|
9
10
|
import { FougereError } from '../wire/FougereError.js';
|
|
10
11
|
|
|
11
|
-
export function Collector<T extends
|
|
12
|
+
export function Collector<T extends AbstractCtor>(target: T) {
|
|
12
13
|
class CollectorBase {
|
|
13
14
|
static readonly __entity = target;
|
|
14
15
|
}
|
|
@@ -34,7 +35,7 @@ export function collectedAs(collector: object, value: unknown): unknown {
|
|
|
34
35
|
|
|
35
36
|
let reader = readers.get(target);
|
|
36
37
|
if (!reader) {
|
|
37
|
-
reader = json(target as
|
|
38
|
+
reader = json(target as Entity);
|
|
38
39
|
readers.set(target, reader);
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OperationContract } from '../wire/OperationContract.js';
|
|
2
|
+
|
|
3
|
+
/** The contracts a class carries on itself — `Crud(Post)` states its five as `__ops`. */
|
|
4
|
+
export function opsOf(ctor: unknown): Record<string, OperationContract> {
|
|
5
|
+
return (ctor as { __ops?: Record<string, OperationContract> }).__ops ?? {};
|
|
6
|
+
}
|
package/src/prefab/prefab.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Entity } from '@fougere/schema';
|
|
2
2
|
import type { PresenterViews } from './presenter.js';
|
|
3
3
|
|
|
4
4
|
/** What a prefab class declares about ITSELF, at runtime. */
|
|
5
5
|
interface Prefab {
|
|
6
6
|
/** The shape this class was built on. `Mirror` calls it a shape; it is the same slot. */
|
|
7
|
-
readonly __entity?:
|
|
7
|
+
readonly __entity?: Entity;
|
|
8
8
|
/** The view each computed field emits — `Presenter(Order, { items: [OrderItemView] })`. */
|
|
9
9
|
readonly __views?: PresenterViews;
|
|
10
10
|
/** The view the whole handler speaks — `Crud(Post, PostPublic)`. Same slot, one level up. */
|
|
11
|
-
readonly __output?:
|
|
11
|
+
readonly __output?: Entity;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/** The shape a prefab was built on, or `undefined` when the class is not one. */
|
|
15
|
-
export function targetOf(ctor: unknown):
|
|
15
|
+
export function targetOf(ctor: unknown): Entity | undefined {
|
|
16
16
|
return (ctor as Prefab | undefined)?.__entity;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -22,6 +22,6 @@ export function viewsOf(ctor: unknown): PresenterViews | undefined {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** The handler-wide view a prefab declares, same rule. */
|
|
25
|
-
export function outputOf(ctor: unknown):
|
|
25
|
+
export function outputOf(ctor: unknown): Entity | undefined {
|
|
26
26
|
return (ctor as Prefab | undefined)?.__output;
|
|
27
27
|
}
|
package/src/prefab/presenter.ts
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
* Documented: [presenters](https://fougere.dev/docs/business/presenters).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { upperFirst, type
|
|
7
|
+
import { upperFirst, type Entity } from '@fougere/schema';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The view a computed field emits — `OrderItemView` for one, `[OrderItemView]` for many.
|
|
11
11
|
* A view is a schema, so it derives: `Order.pick('id', 'status')`, never a hand-written type.
|
|
12
12
|
*/
|
|
13
|
-
export type PresenterViews = Record<string,
|
|
13
|
+
export type PresenterViews = Record<string, Entity | [Entity]>;
|
|
14
14
|
|
|
15
15
|
/** `Presenter(Order, { items: [OrderItemView] })` — the views its computed fields emit. */
|
|
16
|
-
export function Presenter<E extends
|
|
16
|
+
export function Presenter<E extends Entity>(entity: E, views?: PresenterViews) {
|
|
17
17
|
class PresenterBase {
|
|
18
18
|
static readonly __entity = entity;
|
|
19
19
|
static readonly __views = views;
|
package/src/wire/Addresses.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { FougereOperations } from './FougereOperations.js';
|
|
2
2
|
|
|
3
|
-
type
|
|
3
|
+
type Generated = keyof FougereOperations & string;
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Both halves below take the union through a type PARAMETER, which is what makes the
|
|
7
|
-
* conditional distribute: `
|
|
7
|
+
* conditional distribute: `Generated` is an alias, and an alias is matched whole — `'a.x' | 'b.y'`
|
|
8
8
|
* does not extend `` `${string}.${string}` `` as one thing, so the naive form answers `never`.
|
|
9
9
|
*/
|
|
10
10
|
type AddressIn<Key> = Key extends `${infer Address}.${string}` ? Address : never;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The addresses this app answers at — or any string, until a scan has said otherwise. The
|
|
14
|
-
* `[
|
|
14
|
+
* `[Generated] extends [never]` form is what keeps an ungenerated project compiling: a bare `never`
|
|
15
15
|
* in a parameter position refuses every call, the correct ones included.
|
|
16
16
|
*/
|
|
17
|
-
export type Addresses = [
|
|
17
|
+
export type Addresses = [Generated] extends [never] ? string : AddressIn<Generated>;
|
package/src/wire/Facade.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { lowerFirst } from '@fougere/schema';
|
|
2
2
|
|
|
3
|
-
import type { InvocationContext } from './InvocationContext.js';
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* The facade built in front of a handler — the framework's second port, after `Storage`.
|
|
7
5
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* promise comes back where the handler may answer a bare value
|
|
11
|
-
* crossing
|
|
12
|
-
* and awaits the collectors, so `readLocation(): string` was typed as answering now and never did.
|
|
6
|
+
* It takes what the handler takes: code calling it already holds the values, so nothing is
|
|
7
|
+
* collected for it and nothing is presented — that is a door's work, done on what a door
|
|
8
|
+
* received. A promise comes back where the handler may answer a bare value, because a facade
|
|
9
|
+
* is a crossing and a crossing is awaited, whether or not a transport carries it.
|
|
13
10
|
*
|
|
14
11
|
* `Awaited` because a promise does not stack: `Promise.resolve(p)` IS `p`, so writing
|
|
15
12
|
* `Promise<R>` over an async handler would describe a `Promise<Promise<Post>>` that no value can
|
|
16
13
|
* have — `.then` would hand its callback a promise the runtime never delivers.
|
|
17
14
|
*/
|
|
18
15
|
export type Facade<T> = {
|
|
19
|
-
[K in keyof T]: T[K] extends (...args:
|
|
20
|
-
? (
|
|
16
|
+
[K in keyof T]: T[K] extends (...args: infer A) => infer R
|
|
17
|
+
? (...args: A) => Promise<Awaited<R>>
|
|
21
18
|
: never;
|
|
22
19
|
};
|
|
23
20
|
|
package/src/wire/Invocation.ts
CHANGED
|
@@ -31,6 +31,7 @@ export class Invocation implements InvocationContext {
|
|
|
31
31
|
readonly params: Record<string, unknown>;
|
|
32
32
|
readonly query: Record<string, unknown>;
|
|
33
33
|
readonly input: unknown;
|
|
34
|
+
readonly args?: readonly unknown[];
|
|
34
35
|
readonly state: Record<string, unknown>;
|
|
35
36
|
readonly trace?: string;
|
|
36
37
|
readonly identity?: string;
|
|
@@ -38,11 +39,12 @@ export class Invocation implements InvocationContext {
|
|
|
38
39
|
readonly runAt?: number;
|
|
39
40
|
readonly crossed?: true;
|
|
40
41
|
|
|
41
|
-
/** Gives every entry and every transport the same
|
|
42
|
+
/** Gives every entry and every transport the same ten-member value, frozen. */
|
|
42
43
|
private constructor(context: PartialInvocation) {
|
|
43
44
|
this.params = canonicalRecord(context.params);
|
|
44
45
|
this.query = canonicalRecord(context.query);
|
|
45
46
|
this.input = canonicalValue(context.input);
|
|
47
|
+
if (context.args !== undefined) this.args = canonicalValue(context.args) as readonly unknown[];
|
|
46
48
|
// State is host-owned and may still be enriched by existing middlewares. Moving it
|
|
47
49
|
// to immutable lifecycle context is a separate migration.
|
|
48
50
|
this.state = context.state ?? {};
|
|
@@ -2,6 +2,12 @@ export interface InvocationContext {
|
|
|
2
2
|
params: Record<string, unknown>;
|
|
3
3
|
query: Record<string, unknown>;
|
|
4
4
|
input: unknown;
|
|
5
|
+
/**
|
|
6
|
+
* The handler's own arguments, written by code that called its facade. A door writes
|
|
7
|
+
* `params`, `query` and `input` — what it received — and the binding plan reads them; code
|
|
8
|
+
* already holds the values, so nothing is collected and nothing is presented.
|
|
9
|
+
*/
|
|
10
|
+
args?: readonly unknown[];
|
|
5
11
|
state: Record<string, unknown>;
|
|
6
12
|
trace?: string;
|
|
7
13
|
identity?: string;
|
|
@@ -5,6 +5,7 @@ import type { TypeRef } from './TypeRef.js';
|
|
|
5
5
|
import type { ErrorCode } from './ErrorCode.js';
|
|
6
6
|
import type { OperationKind } from './OperationKind.js';
|
|
7
7
|
import type { OperationKindInference } from './OperationKindInference.js';
|
|
8
|
+
import type { Cardinality } from './Cardinality.js';
|
|
8
9
|
|
|
9
10
|
/** The contract of one operation — everything the façade needs to serve a call. */
|
|
10
11
|
export interface OperationContract {
|
|
@@ -17,7 +18,7 @@ export interface OperationContract {
|
|
|
17
18
|
/** The operation in words, for a caller that meets it over the wire and has to choose. */
|
|
18
19
|
description?: string;
|
|
19
20
|
/** How MUCH comes back — the half of the return type that `output` cannot say. */
|
|
20
|
-
cardinality?:
|
|
21
|
+
cardinality?: Cardinality;
|
|
21
22
|
/** The argument names and TYPES — the one thing `binding` cannot say. */
|
|
22
23
|
signature?: Signature;
|
|
23
24
|
/**
|
|
@@ -48,8 +49,8 @@ const PRIMITIVE_RETURNS = new Set(['boolean', 'string', 'number', 'void', 'undef
|
|
|
48
49
|
|
|
49
50
|
/** Deliberately finite. */
|
|
50
51
|
const QUERY_VERBS = new Set([
|
|
51
|
-
'all', 'check', 'count', 'exists', 'fetch', 'find', 'get', 'has', 'health', '
|
|
52
|
-
'load', 'mine', 'ping', 'quote', 'read', 'search', 'stats', 'status', 'who',
|
|
52
|
+
'all', 'can', 'check', 'count', 'exists', 'fetch', 'find', 'get', 'has', 'health', 'is',
|
|
53
|
+
'list', 'load', 'mine', 'ping', 'quote', 'read', 'search', 'stats', 'status', 'who',
|
|
53
54
|
]);
|
|
54
55
|
|
|
55
56
|
/**
|