@fougere/core 0.6.0-alpha.0 → 0.8.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/boot/AppLifecycle.d.ts +1 -1
- package/dist/boot/AppLifecycle.d.ts.map +1 -1
- package/dist/boot/AppLifecycle.js +13 -2
- package/dist/boot/AppLifecycle.js.map +1 -1
- package/dist/boot/Emissions.d.ts +13 -90
- package/dist/boot/Emissions.d.ts.map +1 -1
- package/dist/boot/Emissions.js +21 -118
- package/dist/boot/Emissions.js.map +1 -1
- package/dist/boot/ambient-port.d.ts +3 -24
- package/dist/boot/ambient-port.d.ts.map +1 -1
- package/dist/boot/ambient.als.d.ts.map +1 -1
- package/dist/boot/ambient.als.js +7 -12
- package/dist/boot/ambient.als.js.map +1 -1
- package/dist/boot/ambient.queue.d.ts +2 -5
- package/dist/boot/ambient.queue.d.ts.map +1 -1
- package/dist/boot/ambient.queue.js.map +1 -1
- package/dist/boot/apply.d.ts +1 -13
- package/dist/boot/apply.d.ts.map +1 -1
- package/dist/boot/apply.js +9 -16
- package/dist/boot/apply.js.map +1 -1
- package/dist/boot/auth.d.ts +2 -12
- package/dist/boot/auth.d.ts.map +1 -1
- package/dist/boot/boot.d.ts +3 -16
- package/dist/boot/boot.d.ts.map +1 -1
- package/dist/boot/boot.js +4 -11
- package/dist/boot/boot.js.map +1 -1
- package/dist/boot/bootstrap.d.ts.map +1 -1
- package/dist/boot/bootstrap.js +84 -130
- package/dist/boot/bootstrap.js.map +1 -1
- package/dist/boot/card.d.ts +1 -17
- package/dist/boot/card.d.ts.map +1 -1
- package/dist/boot/card.js +3 -32
- package/dist/boot/card.js.map +1 -1
- package/dist/boot/frame.d.ts +8 -32
- package/dist/boot/frame.d.ts.map +1 -1
- package/dist/boot/frame.js +17 -56
- package/dist/boot/frame.js.map +1 -1
- package/dist/boot/hosted.d.ts +2 -24
- package/dist/boot/hosted.d.ts.map +1 -1
- package/dist/boot/hosted.js +10 -15
- package/dist/boot/hosted.js.map +1 -1
- package/dist/boot/ownership.d.ts +3 -36
- package/dist/boot/ownership.d.ts.map +1 -1
- package/dist/boot/ownership.js +5 -40
- package/dist/boot/ownership.js.map +1 -1
- package/dist/boot/ports.d.ts +1 -14
- package/dist/boot/ports.d.ts.map +1 -1
- package/dist/boot/ports.js +1 -14
- package/dist/boot/ports.js.map +1 -1
- package/dist/boot/remote.d.ts +5 -38
- package/dist/boot/remote.d.ts.map +1 -1
- package/dist/boot/remote.js +8 -41
- package/dist/boot/remote.js.map +1 -1
- package/dist/boot/runner.d.ts +4 -21
- package/dist/boot/runner.d.ts.map +1 -1
- package/dist/boot/runner.js +4 -17
- package/dist/boot/runner.js.map +1 -1
- package/dist/boot/seed.d.ts +4 -31
- package/dist/boot/seed.d.ts.map +1 -1
- package/dist/boot/seed.js +6 -37
- package/dist/boot/seed.js.map +1 -1
- package/dist/boot/statement-drift.d.ts +2 -21
- package/dist/boot/statement-drift.d.ts.map +1 -1
- package/dist/boot/statement-drift.js +4 -9
- package/dist/boot/statement-drift.js.map +1 -1
- package/dist/boot/together.d.ts +4 -8
- package/dist/boot/together.d.ts.map +1 -1
- package/dist/boot/together.js +15 -50
- package/dist/boot/together.js.map +1 -1
- package/dist/boot/types.d.ts +31 -173
- package/dist/boot/types.d.ts.map +1 -1
- package/dist/builtin/config.d.ts +7 -0
- package/dist/builtin/config.d.ts.map +1 -0
- package/dist/builtin/config.js +15 -0
- package/dist/builtin/config.js.map +1 -0
- package/dist/{builtins → builtin}/logger.d.ts +4 -25
- package/dist/builtin/logger.d.ts.map +1 -0
- package/dist/{builtins → builtin}/logger.js +5 -32
- package/dist/builtin/logger.js.map +1 -0
- package/dist/config-loader.d.ts +9 -67
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +5 -18
- package/dist/config-loader.js.map +1 -1
- package/dist/contract.d.ts +15 -31
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +13 -26
- package/dist/contract.js.map +1 -1
- package/dist/criterion.d.ts +37 -0
- package/dist/criterion.d.ts.map +1 -0
- package/dist/criterion.js +28 -0
- package/dist/criterion.js.map +1 -0
- package/dist/crypto/encoding.d.ts +2 -15
- package/dist/crypto/encoding.d.ts.map +1 -1
- package/dist/crypto/encoding.js +2 -15
- package/dist/crypto/encoding.js.map +1 -1
- package/dist/crypto/node.d.ts.map +1 -1
- package/dist/crypto/node.js +1 -7
- package/dist/crypto/node.js.map +1 -1
- package/dist/crypto/port.d.ts +2 -18
- package/dist/crypto/port.d.ts.map +1 -1
- package/dist/crypto/port.js +1 -17
- package/dist/crypto/port.js.map +1 -1
- package/dist/crypto/webcrypto.d.ts.map +1 -1
- package/dist/crypto/webcrypto.js +1 -12
- package/dist/crypto/webcrypto.js.map +1 -1
- package/dist/declare.d.ts +19 -37
- package/dist/declare.d.ts.map +1 -1
- package/dist/declare.js +23 -30
- package/dist/declare.js.map +1 -1
- package/dist/define.d.ts +2 -3
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +2 -3
- package/dist/define.js.map +1 -1
- package/dist/descriptor/Fronds.d.ts +3 -28
- package/dist/descriptor/Fronds.d.ts.map +1 -1
- package/dist/descriptor/Fronds.js +6 -31
- package/dist/descriptor/Fronds.js.map +1 -1
- package/dist/descriptor/frond.d.ts +25 -60
- package/dist/descriptor/frond.d.ts.map +1 -1
- package/dist/descriptor/frond.js +5 -1
- package/dist/descriptor/frond.js.map +1 -1
- package/dist/dispatch/ArgumentResolver.d.ts +1 -1
- package/dist/dispatch/ArgumentResolver.d.ts.map +1 -1
- package/dist/dispatch/ArgumentResolver.js +5 -5
- package/dist/dispatch/ArgumentResolver.js.map +1 -1
- package/dist/dispatch/DispatchEvent.d.ts +2 -2
- package/dist/dispatch/DispatchEvent.d.ts.map +1 -1
- package/dist/dispatch/DispatchLifecycle.d.ts +1 -7
- package/dist/dispatch/DispatchLifecycle.d.ts.map +1 -1
- package/dist/dispatch/DispatchLifecycle.js +1 -7
- package/dist/dispatch/DispatchLifecycle.js.map +1 -1
- package/dist/dispatch/DispatchPort.d.ts +1 -1
- package/dist/dispatch/DispatchPort.d.ts.map +1 -1
- package/dist/dispatch/Dispatcher.d.ts +1 -1
- package/dist/dispatch/Dispatcher.d.ts.map +1 -1
- package/dist/dispatch/Dispatcher.js +2 -2
- package/dist/dispatch/Dispatcher.js.map +1 -1
- package/dist/{boot → dispatch}/HandlerFacade.d.ts +25 -38
- package/dist/dispatch/HandlerFacade.d.ts.map +1 -0
- package/dist/{boot → dispatch}/HandlerFacade.js +65 -74
- package/dist/dispatch/HandlerFacade.js.map +1 -0
- package/dist/dispatch/LocalRoutePolicy.d.ts +1 -1
- package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -1
- package/dist/dispatch/LocalRoutePolicy.js +4 -4
- package/dist/dispatch/LocalRoutePolicy.js.map +1 -1
- package/dist/dispatch/OperationRoute.d.ts +2 -2
- package/dist/dispatch/OperationRoute.d.ts.map +1 -1
- package/dist/dispatch/OutputView.d.ts +3 -2
- package/dist/dispatch/OutputView.d.ts.map +1 -1
- package/dist/dispatch/OutputView.js +19 -1
- package/dist/dispatch/OutputView.js.map +1 -1
- package/dist/dispatch/Route.d.ts +2 -2
- package/dist/dispatch/Route.d.ts.map +1 -1
- package/dist/dispatch/RoutePolicy.d.ts +1 -1
- package/dist/dispatch/RoutePolicy.d.ts.map +1 -1
- package/dist/dispatch/RouteRegistry.d.ts +1 -1
- package/dist/dispatch/RouteRegistry.d.ts.map +1 -1
- package/dist/dispatch/RouteRegistry.js +2 -2
- package/dist/dispatch/RouteRegistry.js.map +1 -1
- package/dist/dispatch/RouteResolver.d.ts +2 -4
- package/dist/dispatch/RouteResolver.d.ts.map +1 -1
- package/dist/dispatch/StorageGuard.d.ts +40 -8
- package/dist/dispatch/StorageGuard.d.ts.map +1 -1
- package/dist/dispatch/StorageGuard.js +151 -20
- package/dist/dispatch/StorageGuard.js.map +1 -1
- package/dist/dispatch/presenterArguments.d.ts +7 -0
- package/dist/dispatch/presenterArguments.d.ts.map +1 -0
- package/dist/dispatch/presenterArguments.js +12 -0
- package/dist/dispatch/presenterArguments.js.map +1 -0
- package/dist/dispatch/remoteRoutes.d.ts +7 -0
- package/dist/dispatch/remoteRoutes.d.ts.map +1 -0
- package/dist/dispatch/{RemoteRouteResolver.js → remoteRoutes.js} +6 -10
- package/dist/dispatch/remoteRoutes.js.map +1 -0
- package/dist/dispatch/routeNotFound.d.ts +9 -0
- package/dist/dispatch/routeNotFound.d.ts.map +1 -0
- package/dist/dispatch/routeNotFound.js +21 -0
- package/dist/dispatch/routeNotFound.js.map +1 -0
- package/dist/dispatch/validateInput.d.ts +5 -0
- package/dist/dispatch/validateInput.d.ts.map +1 -0
- package/dist/dispatch/validateInput.js +20 -0
- package/dist/dispatch/validateInput.js.map +1 -0
- package/dist/effective-operation.d.ts +1 -24
- package/dist/effective-operation.d.ts.map +1 -1
- package/dist/effective-operation.js +21 -50
- package/dist/effective-operation.js.map +1 -1
- package/dist/emit.d.ts +4 -42
- package/dist/emit.d.ts.map +1 -1
- package/dist/emit.js +3 -39
- package/dist/emit.js.map +1 -1
- package/dist/entry/facade.d.ts +8 -0
- package/dist/entry/facade.d.ts.map +1 -0
- package/dist/entry/facade.js +28 -0
- package/dist/entry/facade.js.map +1 -0
- package/dist/entry/transport.d.ts +5 -0
- package/dist/entry/transport.d.ts.map +1 -0
- package/dist/entry/transport.js +11 -0
- package/dist/entry/transport.js.map +1 -0
- package/dist/frond-config.d.ts +7 -70
- package/dist/frond-config.d.ts.map +1 -1
- package/dist/frond-config.js +1 -9
- package/dist/frond-config.js.map +1 -1
- package/dist/identity-keys.d.ts +2 -5
- package/dist/identity-keys.d.ts.map +1 -1
- package/dist/identity-keys.js +3 -16
- package/dist/identity-keys.js.map +1 -1
- package/dist/identity.d.ts +4 -30
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +11 -73
- package/dist/identity.js.map +1 -1
- package/dist/imports.d.ts +1 -7
- package/dist/imports.d.ts.map +1 -1
- package/dist/imports.js +1 -7
- package/dist/imports.js.map +1 -1
- package/dist/index.d.ts +17 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -18
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +1 -8
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +1 -8
- package/dist/loader.js.map +1 -1
- package/dist/node.d.ts +2 -11
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +2 -11
- package/dist/node.js.map +1 -1
- package/dist/prefab/collector.d.ts +1 -22
- package/dist/prefab/collector.d.ts.map +1 -1
- package/dist/prefab/collector.js +1 -22
- package/dist/prefab/collector.js.map +1 -1
- package/dist/prefab/crud.d.ts +16 -32
- package/dist/prefab/crud.d.ts.map +1 -1
- package/dist/prefab/crud.js +31 -58
- package/dist/prefab/crud.js.map +1 -1
- package/dist/prefab/mirror.d.ts +2 -24
- package/dist/prefab/mirror.d.ts.map +1 -1
- package/dist/prefab/mirror.js +2 -20
- package/dist/prefab/mirror.js.map +1 -1
- package/dist/prefab/prefab.d.ts +1 -7
- package/dist/prefab/prefab.d.ts.map +1 -1
- package/dist/prefab/prefab.js +1 -7
- package/dist/prefab/prefab.js.map +1 -1
- package/dist/prefab/presenter.d.ts +2 -36
- package/dist/prefab/presenter.d.ts.map +1 -1
- package/dist/prefab/presenter.js +2 -36
- package/dist/prefab/presenter.js.map +1 -1
- package/dist/prefab/repository.d.ts +5 -73
- package/dist/prefab/repository.d.ts.map +1 -1
- package/dist/prefab/repository.js +3 -16
- package/dist/prefab/repository.js.map +1 -1
- package/dist/scan/adapters.d.ts +3 -0
- package/dist/scan/adapters.d.ts.map +1 -0
- package/dist/scan/adapters.js +25 -0
- package/dist/scan/adapters.js.map +1 -0
- package/dist/scan/bundling.d.ts +1 -12
- package/dist/scan/bundling.d.ts.map +1 -1
- package/dist/scan/bundling.js +1 -12
- package/dist/scan/bundling.js.map +1 -1
- package/dist/scan/contract.d.ts +27 -0
- package/dist/scan/contract.d.ts.map +1 -0
- package/dist/scan/contract.js +46 -0
- package/dist/scan/contract.js.map +1 -0
- package/dist/scan/conventions.d.ts +6 -40
- package/dist/scan/conventions.d.ts.map +1 -1
- package/dist/scan/conventions.js +5 -34
- package/dist/scan/conventions.js.map +1 -1
- package/dist/scan/emit.d.ts +1 -5
- package/dist/scan/emit.d.ts.map +1 -1
- package/dist/scan/emit.js +10 -78
- package/dist/scan/emit.js.map +1 -1
- package/dist/scan/handler-parser.d.ts +4 -26
- package/dist/scan/handler-parser.d.ts.map +1 -1
- package/dist/scan/handler-parser.js +26 -83
- package/dist/scan/handler-parser.js.map +1 -1
- package/dist/scan/result.d.ts +3 -16
- package/dist/scan/result.d.ts.map +1 -1
- package/dist/scan/scanner.d.ts +2 -27
- package/dist/scan/scanner.d.ts.map +1 -1
- package/dist/scan/scanner.js +45 -133
- package/dist/scan/scanner.js.map +1 -1
- package/dist/scan/statement.d.ts.map +1 -1
- package/dist/scan/statement.js +60 -37
- package/dist/scan/statement.js.map +1 -1
- package/dist/source.d.ts +53 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +18 -0
- package/dist/source.js.map +1 -0
- package/dist/storage.d.ts +13 -210
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -68
- package/dist/storage.js.map +1 -1
- package/dist/store.d.ts +18 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +179 -0
- package/dist/store.js.map +1 -0
- package/dist/verify.d.ts +5 -38
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +17 -37
- package/dist/verify.js.map +1 -1
- package/dist/wire/CallLog.d.ts +32 -0
- package/dist/wire/CallLog.d.ts.map +1 -0
- package/dist/{contract → wire}/CallLog.js.map +1 -1
- package/dist/wire/Invocation.d.ts +32 -0
- package/dist/wire/Invocation.d.ts.map +1 -0
- package/dist/wire/Invocation.js +66 -0
- package/dist/wire/Invocation.js.map +1 -0
- package/dist/{contract → wire}/RouteAddress.d.ts +5 -0
- package/dist/wire/RouteAddress.d.ts.map +1 -0
- package/dist/{contract → wire}/RouteAddress.js +7 -0
- package/dist/wire/RouteAddress.js.map +1 -0
- package/dist/wire/binding.d.ts +5 -21
- package/dist/wire/binding.d.ts.map +1 -1
- package/dist/wire/binding.js +5 -10
- package/dist/wire/binding.js.map +1 -1
- package/dist/wire/call.d.ts +24 -133
- package/dist/wire/call.d.ts.map +1 -1
- package/dist/wire/call.js +15 -41
- package/dist/wire/call.js.map +1 -1
- package/dist/wire/drift.d.ts +3 -28
- package/dist/wire/drift.d.ts.map +1 -1
- package/dist/wire/drift.js +4 -23
- package/dist/wire/drift.js.map +1 -1
- package/dist/wire/errors.d.ts +4 -24
- package/dist/wire/errors.d.ts.map +1 -1
- package/dist/wire/errors.js +4 -24
- package/dist/wire/errors.js.map +1 -1
- package/dist/wire/http-error.d.ts +2 -13
- package/dist/wire/http-error.d.ts.map +1 -1
- package/dist/wire/http-error.js +5 -19
- package/dist/wire/http-error.js.map +1 -1
- package/dist/wire/loggerMiddleware.d.ts +2 -8
- package/dist/wire/loggerMiddleware.d.ts.map +1 -1
- package/dist/wire/loggerMiddleware.js +1 -7
- package/dist/wire/loggerMiddleware.js.map +1 -1
- package/dist/wire/middleware.d.ts +3 -7
- package/dist/wire/middleware.d.ts.map +1 -1
- package/dist/wire/middleware.js +1 -5
- package/dist/wire/middleware.js.map +1 -1
- package/dist/wire/operation.d.ts +7 -43
- package/dist/wire/operation.d.ts.map +1 -1
- package/dist/wire/operation.js +4 -15
- package/dist/wire/operation.js.map +1 -1
- package/dist/wire/signature.d.ts +4 -25
- package/dist/wire/signature.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/boot/AppLifecycle.ts +15 -2
- package/src/boot/Emissions.ts +21 -118
- package/src/boot/ambient-port.ts +3 -24
- package/src/boot/ambient.als.ts +7 -12
- package/src/boot/ambient.queue.ts +2 -5
- package/src/boot/apply.ts +9 -16
- package/src/boot/auth.ts +2 -12
- package/src/boot/boot.ts +6 -21
- package/src/boot/bootstrap.ts +95 -140
- package/src/boot/card.ts +4 -33
- package/src/boot/frame.ts +24 -64
- package/src/boot/hosted.ts +12 -34
- package/src/boot/ownership.ts +5 -39
- package/src/boot/ports.ts +1 -14
- package/src/boot/remote.ts +11 -58
- package/src/boot/runner.ts +5 -22
- package/src/boot/seed.ts +6 -37
- package/src/boot/statement-drift.ts +5 -24
- package/src/boot/together.ts +18 -51
- package/src/boot/types.ts +31 -173
- package/src/{builtins → builtin}/config.ts +1 -7
- package/src/{builtins → builtin}/logger.ts +6 -39
- package/src/config-loader.ts +12 -71
- package/src/contract.ts +19 -39
- package/src/criterion.ts +54 -0
- package/src/crypto/encoding.ts +2 -15
- package/src/crypto/node.ts +1 -7
- package/src/crypto/port.ts +2 -18
- package/src/crypto/webcrypto.ts +1 -12
- package/src/declare.ts +42 -41
- package/src/define.ts +2 -3
- package/src/descriptor/Fronds.ts +6 -31
- package/src/descriptor/frond.ts +26 -60
- package/src/dispatch/ArgumentResolver.ts +6 -6
- package/src/dispatch/DispatchEvent.ts +2 -2
- package/src/dispatch/DispatchLifecycle.ts +1 -7
- package/src/dispatch/DispatchPort.ts +1 -1
- package/src/dispatch/Dispatcher.ts +3 -3
- package/src/dispatch/HandlerFacade.ts +231 -0
- package/src/dispatch/LocalRoutePolicy.ts +4 -4
- package/src/dispatch/OperationRoute.ts +2 -2
- package/src/dispatch/OutputView.ts +24 -2
- package/src/dispatch/Route.ts +2 -2
- package/src/dispatch/RoutePolicy.ts +1 -1
- package/src/dispatch/RouteRegistry.ts +2 -2
- package/src/dispatch/RouteResolver.ts +2 -4
- package/src/dispatch/StorageGuard.ts +175 -19
- package/src/dispatch/presenterArguments.ts +25 -0
- package/src/dispatch/{RemoteRouteResolver.ts → remoteRoutes.ts} +7 -9
- package/src/dispatch/routeNotFound.ts +33 -0
- package/src/dispatch/validateInput.ts +28 -0
- package/src/effective-operation.ts +21 -50
- package/src/emit.ts +5 -63
- package/src/entry/facade.ts +44 -0
- package/src/entry/transport.ts +16 -0
- package/src/frond-config.ts +7 -70
- package/src/identity-keys.ts +3 -16
- package/src/identity.ts +12 -80
- package/src/imports.ts +2 -31
- package/src/index.ts +17 -24
- package/src/loader.ts +2 -16
- package/src/node.ts +2 -11
- package/src/prefab/collector.ts +1 -22
- package/src/prefab/crud.ts +37 -76
- package/src/prefab/mirror.ts +4 -44
- package/src/prefab/prefab.ts +2 -21
- package/src/prefab/presenter.ts +2 -36
- package/src/prefab/repository.ts +7 -83
- package/src/scan/adapters.ts +32 -0
- package/src/scan/bundling.ts +1 -12
- package/src/scan/contract.ts +80 -0
- package/src/scan/conventions.ts +6 -40
- package/src/scan/emit.ts +11 -80
- package/src/scan/handler-parser.ts +27 -96
- package/src/scan/result.ts +3 -16
- package/src/scan/scanner.ts +46 -132
- package/src/scan/statement.ts +73 -41
- package/src/source.ts +63 -0
- package/src/storage.ts +14 -234
- package/src/store.ts +191 -0
- package/src/verify.ts +16 -57
- package/src/wire/CallLog.ts +30 -0
- package/src/{contract → wire}/Invocation.ts +24 -17
- package/src/{contract → wire}/RouteAddress.ts +7 -0
- package/src/wire/binding.ts +9 -25
- package/src/wire/call.ts +30 -133
- package/src/wire/drift.ts +5 -30
- package/src/wire/errors.ts +4 -24
- package/src/wire/http-error.ts +5 -19
- package/src/wire/loggerMiddleware.ts +2 -8
- package/src/wire/middleware.ts +3 -7
- package/src/wire/operation.ts +8 -48
- package/src/wire/signature.ts +4 -25
- package/dist/boot/HandlerFacade.d.ts.map +0 -1
- package/dist/boot/HandlerFacade.js.map +0 -1
- package/dist/builtins/config.d.ts +0 -13
- package/dist/builtins/config.d.ts.map +0 -1
- package/dist/builtins/config.js +0 -21
- package/dist/builtins/config.js.map +0 -1
- package/dist/builtins/logger.d.ts.map +0 -1
- package/dist/builtins/logger.js.map +0 -1
- package/dist/contract/Call.d.ts +0 -10
- package/dist/contract/Call.d.ts.map +0 -1
- package/dist/contract/Call.js +0 -15
- package/dist/contract/Call.js.map +0 -1
- package/dist/contract/CallLog.d.ts +0 -57
- package/dist/contract/CallLog.d.ts.map +0 -1
- package/dist/contract/Invocation.d.ts +0 -27
- package/dist/contract/Invocation.d.ts.map +0 -1
- package/dist/contract/Invocation.js +0 -59
- package/dist/contract/Invocation.js.map +0 -1
- package/dist/contract/RouteAddress.d.ts.map +0 -1
- package/dist/contract/RouteAddress.js.map +0 -1
- package/dist/dispatch/InputValidator.d.ts +0 -7
- package/dist/dispatch/InputValidator.d.ts.map +0 -1
- package/dist/dispatch/InputValidator.js +0 -22
- package/dist/dispatch/InputValidator.js.map +0 -1
- package/dist/dispatch/OperationExecutor.d.ts +0 -25
- package/dist/dispatch/OperationExecutor.d.ts.map +0 -1
- package/dist/dispatch/OperationExecutor.js +0 -32
- package/dist/dispatch/OperationExecutor.js.map +0 -1
- package/dist/dispatch/OutputProjector.d.ts +0 -12
- package/dist/dispatch/OutputProjector.d.ts.map +0 -1
- package/dist/dispatch/OutputProjector.js +0 -30
- package/dist/dispatch/OutputProjector.js.map +0 -1
- package/dist/dispatch/PresenterArgumentResolver.d.ts +0 -12
- package/dist/dispatch/PresenterArgumentResolver.d.ts.map +0 -1
- package/dist/dispatch/PresenterArgumentResolver.js +0 -20
- package/dist/dispatch/PresenterArgumentResolver.js.map +0 -1
- package/dist/dispatch/RemoteRouteResolver.d.ts +0 -13
- package/dist/dispatch/RemoteRouteResolver.d.ts.map +0 -1
- package/dist/dispatch/RemoteRouteResolver.js.map +0 -1
- package/dist/dispatch/RouteNotFoundError.d.ts +0 -10
- package/dist/dispatch/RouteNotFoundError.d.ts.map +0 -1
- package/dist/dispatch/RouteNotFoundError.js +0 -24
- package/dist/dispatch/RouteNotFoundError.js.map +0 -1
- package/dist/entry/DynamicFacade.d.ts +0 -10
- package/dist/entry/DynamicFacade.d.ts.map +0 -1
- package/dist/entry/DynamicFacade.js +0 -27
- package/dist/entry/DynamicFacade.js.map +0 -1
- package/dist/entry/FacadeEntry.d.ts +0 -11
- package/dist/entry/FacadeEntry.d.ts.map +0 -1
- package/dist/entry/FacadeEntry.js +0 -26
- package/dist/entry/FacadeEntry.js.map +0 -1
- package/dist/entry/TransportEntry.d.ts +0 -13
- package/dist/entry/TransportEntry.d.ts.map +0 -1
- package/dist/entry/TransportEntry.js +0 -24
- package/dist/entry/TransportEntry.js.map +0 -1
- package/dist/rows.d.ts +0 -40
- package/dist/rows.d.ts.map +0 -1
- package/dist/rows.js +0 -146
- package/dist/rows.js.map +0 -1
- package/src/boot/HandlerFacade.ts +0 -242
- package/src/contract/Call.ts +0 -18
- package/src/contract/CallLog.ts +0 -55
- package/src/dispatch/InputValidator.ts +0 -28
- package/src/dispatch/OperationExecutor.ts +0 -60
- package/src/dispatch/OutputProjector.ts +0 -32
- package/src/dispatch/PresenterArgumentResolver.ts +0 -25
- package/src/dispatch/RouteNotFoundError.ts +0 -29
- package/src/entry/DynamicFacade.ts +0 -28
- package/src/entry/FacadeEntry.ts +0 -32
- package/src/entry/TransportEntry.ts +0 -30
- package/src/rows.ts +0 -155
- /package/dist/{contract → wire}/CallLog.js +0 -0
|
@@ -11,13 +11,7 @@ export class DispatchLifecycle {
|
|
|
11
11
|
this.observers = [...observers];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Subscribe after the dispatcher was built, and get the unsubscription back.
|
|
16
|
-
*
|
|
17
|
-
* The list used to be settled at construction, so only the caller of `createApp` could
|
|
18
|
-
* observe — an extension, which runs in `up(app)`, could not. `app.use` already adds a
|
|
19
|
-
* middleware this late; this is its dual, and the pair is *participate* / *watch*.
|
|
20
|
-
*/
|
|
14
|
+
/** Subscribe after the dispatcher was built, and get the unsubscription back. */
|
|
21
15
|
add(observer: DispatchObserver): () => void {
|
|
22
16
|
this.observers.push(observer);
|
|
23
17
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Call } from '../
|
|
1
|
+
import type { Call } from '../wire/call.js';
|
|
2
2
|
import { DispatchEvent } from './DispatchEvent.js';
|
|
3
3
|
import { DispatchLifecycle } from './DispatchLifecycle.js';
|
|
4
4
|
import type { DispatchPort } from './DispatchPort.js';
|
|
5
5
|
import type { Route } from './Route.js';
|
|
6
6
|
import type { RoutePolicy } from './RoutePolicy.js';
|
|
7
7
|
import { RouteRegistry } from './RouteRegistry.js';
|
|
8
|
-
import {
|
|
8
|
+
import { routeNotFound, servedOperations } from './routeNotFound.js';
|
|
9
9
|
import type { InFlight } from './InFlight.js';
|
|
10
10
|
|
|
11
11
|
/** Resolves and executes every call through the same transverse lifecycle. */
|
|
@@ -29,7 +29,7 @@ export class Dispatcher implements DispatchPort {
|
|
|
29
29
|
route = resolved && (!this.policy || this.policy.accepts(resolved)) ? resolved : undefined;
|
|
30
30
|
if (!route) {
|
|
31
31
|
throw this.policy?.notFound?.(call, this.routes.routes())
|
|
32
|
-
??
|
|
32
|
+
?? routeNotFound(call, servedOperations(call, this.routes.routes(), this.policy));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
this.lifecycle.publish(DispatchEvent.resolved(call, route.kind));
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type { Fields } from '@fougere/schema';
|
|
2
|
+
import type { Container } from '@fougere/container';
|
|
3
|
+
import {
|
|
4
|
+
runMiddlewares,
|
|
5
|
+
type AppMiddleware,
|
|
6
|
+
type OperationContext,
|
|
7
|
+
} from '../wire/middleware.js';
|
|
8
|
+
import type { CollectorResolver } from './ArgumentResolver.js';
|
|
9
|
+
import { collectorKeyOf } from '../prefab/collector.js';
|
|
10
|
+
import { inheritsCrud, subjectOf } from '../prefab/crud.js';
|
|
11
|
+
import { presenterKeyOf } from '../prefab/presenter.js';
|
|
12
|
+
import { repositoryKeyOf } from '../prefab/repository.js';
|
|
13
|
+
import type { OperationContract, OperationsMap } from '../wire/operation.js';
|
|
14
|
+
import type { EffectiveOperation, EffectiveOperationsMap } from '../effective-operation.js';
|
|
15
|
+
import { canonicalInvocation, type InvocationContext } from '../wire/Invocation.js';
|
|
16
|
+
import type { HandlerEntry, PresenterEntry } from '../descriptor/frond.js';
|
|
17
|
+
import { ArgumentResolver } from './ArgumentResolver.js';
|
|
18
|
+
import { OutputView } from './OutputView.js';
|
|
19
|
+
import { PresenterExecutor } from './PresenterExecutor.js';
|
|
20
|
+
import { presenterArguments } from './presenterArguments.js';
|
|
21
|
+
import { validateInput } from './validateInput.js';
|
|
22
|
+
|
|
23
|
+
/** What boot resolved around one handler, beyond the handler and the scope it resolves in. */
|
|
24
|
+
export interface Door {
|
|
25
|
+
/** The container key this door answers under. */
|
|
26
|
+
key: string;
|
|
27
|
+
/** The frond this door belongs to — travels on every OperationContext. */
|
|
28
|
+
frond: string;
|
|
29
|
+
/** Handlers in the owning frond, used to realize a resolved implementation override. */
|
|
30
|
+
handlers: readonly HandlerEntry[];
|
|
31
|
+
/** The canonical operation table resolved before boot performs any side effect. */
|
|
32
|
+
operations: EffectiveOperationsMap;
|
|
33
|
+
/** Entity names this frond has a collector for. */
|
|
34
|
+
collectors: Set<string>;
|
|
35
|
+
/** The presenter over this handler's entity, when the frond declares one. */
|
|
36
|
+
presenter: PresenterEntry | undefined;
|
|
37
|
+
/** Presenters live in the frond's own scope, whatever sub-scope this door resolves in. */
|
|
38
|
+
presenterScope: Container;
|
|
39
|
+
/** The middlewares that apply to this address, read at call time and never at boot. */
|
|
40
|
+
middlewares: () => AppMiddleware[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Adapts one handler door to executable operations. */
|
|
44
|
+
export class HandlerFacade {
|
|
45
|
+
/** Rich operation facts shared with check, explain and adapters. */
|
|
46
|
+
readonly effectiveOperations: EffectiveOperationsMap;
|
|
47
|
+
/** Contracts served by this door. */
|
|
48
|
+
readonly contracts: OperationsMap;
|
|
49
|
+
|
|
50
|
+
private readonly cachedViews = new Map<string, OutputView>();
|
|
51
|
+
private instance: any;
|
|
52
|
+
private readonly implementationKeys = new Map<string, string>();
|
|
53
|
+
private readonly implementationInstances = new Map<string, any>();
|
|
54
|
+
private readonly arguments = new ArgumentResolver(
|
|
55
|
+
(typeName) => this.collectorResolver(typeName),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
private readonly handler: HandlerEntry,
|
|
60
|
+
private readonly scope: Container,
|
|
61
|
+
private readonly door: Door,
|
|
62
|
+
) {
|
|
63
|
+
this.refuseCrudWithoutRepository(handler);
|
|
64
|
+
|
|
65
|
+
scope.register(this.handlerKey, handler.ctor, { deps: this.depsOf(handler) });
|
|
66
|
+
|
|
67
|
+
this.effectiveOperations = door.operations;
|
|
68
|
+
this.contracts = new Map(
|
|
69
|
+
[...door.operations].map(([name, operation]) => [name, operation as OperationContract] as const),
|
|
70
|
+
);
|
|
71
|
+
handler.operations = this.contracts;
|
|
72
|
+
|
|
73
|
+
// Register model-selected implementations in the same execution scope.
|
|
74
|
+
for (const [name, operation] of door.operations) {
|
|
75
|
+
if (this.isBaseImplementation(operation)) continue;
|
|
76
|
+
const implementation = this.implementationHandler(name);
|
|
77
|
+
this.refuseCrudWithoutRepository(implementation);
|
|
78
|
+
const key = `${this.handlerKey}:implementation:${operation.implementation.className}`;
|
|
79
|
+
if (!this.implementationKeys.has(operation.implementation.className)) {
|
|
80
|
+
scope.register(key, implementation.ctor, { deps: this.depsOf(implementation) });
|
|
81
|
+
this.implementationKeys.set(operation.implementation.className, key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** One operation, through the same ordered boundary steps on every route. */
|
|
87
|
+
async execute(op: string, input?: Partial<InvocationContext>): Promise<unknown> {
|
|
88
|
+
const entity = this.handler.address;
|
|
89
|
+
const contract = this.contracts.get(op);
|
|
90
|
+
if (!contract) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${entity} serves no operation '${op}'. `
|
|
93
|
+
+ `It serves ${[...this.contracts.keys()].join(', ')}.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const invocation = canonicalInvocation(input);
|
|
98
|
+
const context: OperationContext = {
|
|
99
|
+
entity,
|
|
100
|
+
frond: this.door.frond,
|
|
101
|
+
operation: op,
|
|
102
|
+
args: [],
|
|
103
|
+
state: invocation.state,
|
|
104
|
+
invocation,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return runMiddlewares(this.door.middlewares(), context, async () => {
|
|
108
|
+
const validated = validateInput(contract.input, invocation, entity, op);
|
|
109
|
+
context.invocation = validated;
|
|
110
|
+
|
|
111
|
+
const args = contract.binding
|
|
112
|
+
? await this.arguments.resolve(contract.binding, validated)
|
|
113
|
+
: [];
|
|
114
|
+
const { instance, method } = this.resolveImplementation(op);
|
|
115
|
+
const view = this.viewOf(op);
|
|
116
|
+
const output = view.project(await instance[method](...args));
|
|
117
|
+
|
|
118
|
+
const { presenter } = this.door;
|
|
119
|
+
return view.closed || !presenter ? output : this.present(op, presenter, output, validated);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The computed fields a presenter adds, over the page the façade just projected. */
|
|
124
|
+
private present(
|
|
125
|
+
op: string,
|
|
126
|
+
presenter: PresenterEntry,
|
|
127
|
+
output: unknown,
|
|
128
|
+
invocation: InvocationContext,
|
|
129
|
+
): Promise<unknown> {
|
|
130
|
+
const entity = this.handler.address;
|
|
131
|
+
const executor = new PresenterExecutor(
|
|
132
|
+
this.door.presenterScope.resolve(presenterKeyOf(entity)),
|
|
133
|
+
presenter.fields,
|
|
134
|
+
entity,
|
|
135
|
+
op,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return presenterArguments(presenter, invocation, this.arguments, this.door.collectors)
|
|
139
|
+
.then((args) => executor.present(output, args));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private get handlerKey(): string {
|
|
143
|
+
return `_handler:${this.door.key}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private depsOf(handler: HandlerEntry): string[] {
|
|
147
|
+
const { deps } = handler;
|
|
148
|
+
if (deps.length > 0) return deps;
|
|
149
|
+
|
|
150
|
+
return inheritsCrud(handler.ctor)
|
|
151
|
+
? [repositoryKeyOf(subjectOf(handler.ctor, handler.address))]
|
|
152
|
+
: [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** A custom Crud constructor must explicitly receive its repository. */
|
|
156
|
+
private refuseCrudWithoutRepository(handler: HandlerEntry): void {
|
|
157
|
+
const repoTypeName = repositoryKeyOf(subjectOf(handler.ctor, handler.address));
|
|
158
|
+
if (!inheritsCrud(handler.ctor) || handler.deps.length === 0 || handler.deps.includes(repoTypeName)) return;
|
|
159
|
+
throw new Error(
|
|
160
|
+
`${handler.ctor.name} extends Crud() and declares a constructor, so its storage is no ` +
|
|
161
|
+
`longer injected for it — but it does not take any.\n` +
|
|
162
|
+
` Add it and hand it to super():\n` +
|
|
163
|
+
` constructor(repo: ${repoTypeName}, …) { super(repo); }`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Resolve and cache the output view declared for one operation. */
|
|
168
|
+
private viewOf(op: string): OutputView {
|
|
169
|
+
const known = this.cachedViews.get(op);
|
|
170
|
+
if (known) return known;
|
|
171
|
+
|
|
172
|
+
const operation = this.effectiveOperations.get(op);
|
|
173
|
+
const schema = operation?.output as { getFields?: () => Fields } | undefined;
|
|
174
|
+
const resolved = new OutputView(
|
|
175
|
+
typeof schema?.getFields === 'function' ? schema.getFields() : {},
|
|
176
|
+
operation?.outputClosed ?? false,
|
|
177
|
+
);
|
|
178
|
+
this.cachedViews.set(op, resolved);
|
|
179
|
+
return resolved;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private collectorResolver = (typeName: string): CollectorResolver | undefined => {
|
|
183
|
+
try { return this.scope.resolve(collectorKeyOf(typeName)) as CollectorResolver; }
|
|
184
|
+
catch { return undefined; }
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/** The handler itself, resolved on first call — never at boot. */
|
|
188
|
+
private resolveHandler(): any {
|
|
189
|
+
if (!this.instance) this.instance = this.scope.resolve(this.handlerKey);
|
|
190
|
+
return this.instance;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private isBaseImplementation(operation: EffectiveOperation): boolean {
|
|
194
|
+
return operation.implementation.className === this.handler.ctor.name
|
|
195
|
+
&& operation.implementation.address === this.handler.address
|
|
196
|
+
&& operation.implementation.filePath === this.handler.filePath;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** The exact handler entry the pure model selected; no name-only retry or fallback. */
|
|
200
|
+
private implementationHandler(operationName: string): HandlerEntry {
|
|
201
|
+
const operation = this.effectiveOperations.get(operationName)!;
|
|
202
|
+
const matches = this.door.handlers.filter((handler) =>
|
|
203
|
+
handler.ctor.name === operation.implementation.className
|
|
204
|
+
&& handler.address === operation.implementation.address
|
|
205
|
+
&& handler.filePath === operation.implementation.filePath);
|
|
206
|
+
if (matches.length !== 1) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`EffectiveOperation '${operation.id}' names ${operation.implementation.className}.`
|
|
209
|
+
+ `${operation.implementation.method}, but boot found ${matches.length} matching handlers.`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
return matches[0]!;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private resolveImplementation(operationName: string): { instance: any; method: string } {
|
|
216
|
+
const operation = this.effectiveOperations.get(operationName)!;
|
|
217
|
+
if (this.isBaseImplementation(operation)) {
|
|
218
|
+
return { instance: this.resolveHandler(), method: operation.implementation.method };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const className = operation.implementation.className;
|
|
222
|
+
const key = this.implementationKeys.get(className);
|
|
223
|
+
if (!key) throw new Error(`No registered implementation for EffectiveOperation '${operation.id}'.`);
|
|
224
|
+
let instance = this.implementationInstances.get(key);
|
|
225
|
+
if (!instance) {
|
|
226
|
+
instance = this.scope.resolve(key);
|
|
227
|
+
this.implementationInstances.set(key, instance);
|
|
228
|
+
}
|
|
229
|
+
return { instance, method: operation.implementation.method };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Call } from '../
|
|
1
|
+
import type { Call } from '../wire/call.js';
|
|
2
2
|
import { ErrorCode, FougereError } from '../wire/errors.js';
|
|
3
3
|
import type { Route } from './Route.js';
|
|
4
4
|
import type { RoutePolicy } from './RoutePolicy.js';
|
|
5
|
-
import {
|
|
5
|
+
import { routeNotFound, servedOperations } from './routeNotFound.js';
|
|
6
6
|
|
|
7
7
|
/** Incoming calls may execute here but never forward to another host. */
|
|
8
8
|
export class LocalRoutePolicy implements RoutePolicy {
|
|
@@ -15,8 +15,8 @@ export class LocalRoutePolicy implements RoutePolicy {
|
|
|
15
15
|
notFound(call: Call, routes: readonly Route[]): Error | undefined {
|
|
16
16
|
if (call.address.entity === 'rpc') return undefined;
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
18
|
+
const served = servedOperations(call, routes, this);
|
|
19
|
+
if (served.length > 0) return routeNotFound(call, served);
|
|
20
20
|
|
|
21
21
|
const hosted = this.hostedNames(call.address.surface);
|
|
22
22
|
const entity = call.address.entity;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Call } from '../
|
|
2
|
-
import type { RouteAddress } from '../
|
|
1
|
+
import type { Call } from '../wire/call.js';
|
|
2
|
+
import type { RouteAddress } from '../wire/RouteAddress.js';
|
|
3
3
|
import type { Route } from './Route.js';
|
|
4
4
|
|
|
5
5
|
export type RouteExecution = (call: Call) => unknown | Promise<unknown>;
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { Visibility, type Fields } from '@fougere/schema';
|
|
2
|
+
import { preserveArrayProperties } from './ArrayResult.js';
|
|
2
3
|
|
|
3
|
-
/**
|
|
4
|
+
/** What one operation result may show. One of the two exits a validator watches. */
|
|
4
5
|
export class OutputView {
|
|
5
6
|
constructor(
|
|
6
7
|
readonly fields: Fields,
|
|
7
8
|
readonly closed = false,
|
|
8
9
|
) {}
|
|
10
|
+
|
|
11
|
+
project(result: unknown): unknown {
|
|
12
|
+
if (result === null || result === undefined) return result;
|
|
13
|
+
|
|
14
|
+
if (Array.isArray(result)) {
|
|
15
|
+
return preserveArrayProperties(result, result.map((item) => this.project(item)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof result !== 'object') return result;
|
|
19
|
+
|
|
20
|
+
const record = result as Record<string, unknown>;
|
|
21
|
+
const scoped = this.closed
|
|
22
|
+
? Object.fromEntries(
|
|
23
|
+
Object.keys(this.fields)
|
|
24
|
+
.filter((key) => key in record)
|
|
25
|
+
.map((key) => [key, record[key]]),
|
|
26
|
+
)
|
|
27
|
+
: record;
|
|
28
|
+
|
|
29
|
+
return Visibility.of(this.fields).encode(scoped);
|
|
30
|
+
}
|
|
9
31
|
}
|
package/src/dispatch/Route.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Call } from '../
|
|
2
|
-
import type { RouteAddress, RouteKind } from '../
|
|
1
|
+
import type { Call } from '../wire/call.js';
|
|
2
|
+
import type { RouteAddress, RouteKind } from '../wire/RouteAddress.js';
|
|
3
3
|
|
|
4
4
|
/** Executable destination known by the dispatcher. */
|
|
5
5
|
export interface Route {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RouteAddress } from '../
|
|
1
|
+
import { RouteAddress } from '../wire/RouteAddress.js';
|
|
2
2
|
import type { Route } from './Route.js';
|
|
3
3
|
import type { RouteResolver } from './RouteResolver.js';
|
|
4
4
|
|
|
@@ -53,7 +53,7 @@ export class RouteRegistry {
|
|
|
53
53
|
|
|
54
54
|
private async resolveUnknown(address: RouteAddress): Promise<Route | undefined> {
|
|
55
55
|
for (const resolver of this.resolvers) {
|
|
56
|
-
const route = await resolver
|
|
56
|
+
const route = await resolver(address);
|
|
57
57
|
if (!route) continue;
|
|
58
58
|
this.register(route);
|
|
59
59
|
return route;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import type { RouteAddress } from '../
|
|
1
|
+
import type { RouteAddress } from '../wire/RouteAddress.js';
|
|
2
2
|
import type { Route } from './Route.js';
|
|
3
3
|
|
|
4
4
|
/** Resolves routes that cannot be known when the registry is built. */
|
|
5
|
-
export
|
|
6
|
-
resolve(address: RouteAddress): Route | undefined | Promise<Route | undefined>;
|
|
7
|
-
}
|
|
5
|
+
export type RouteResolver = (address: RouteAddress) => Route | undefined | Promise<Route | undefined>;
|
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Boundary, FieldSet, FieldValueValidator, InputRefusal, type Fields } from '@fougere/schema';
|
|
2
|
+
import { COMPARISONS, comparisonOf, unknownIn } from '../criterion.js';
|
|
2
3
|
import { assertListOptions } from '../storage.js';
|
|
3
4
|
import { ErrorCode, FougereError } from '../wire/errors.js';
|
|
4
5
|
|
|
6
|
+
/** The gestures this guard grafts onto. */
|
|
5
7
|
interface Writer {
|
|
6
8
|
create(...args: [Record<string, unknown>, ...unknown[]]): Promise<unknown>;
|
|
7
9
|
update(...args: [unknown, Record<string, unknown>, ...unknown[]]): Promise<unknown>;
|
|
10
|
+
upsert?(...args: [Record<string, unknown>, ...unknown[]]): Promise<unknown>;
|
|
11
|
+
upsertAll?(...args: [readonly Record<string, unknown>[], ...unknown[]]): Promise<unknown>;
|
|
12
|
+
list?(...args: unknown[]): unknown;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/** What a guard says about a filter it let through. The boot owns the voice. */
|
|
16
|
+
export interface GuardReport {
|
|
17
|
+
/** The fields this door hands back, when it hands back fewer than the entity has. */
|
|
18
|
+
view?: Fields;
|
|
19
|
+
/** Said once per field — a filter is not a write, and a warning per call is noise. */
|
|
20
|
+
outOfView?: (message: string) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Judges storage writes and list options without narrowing the storage interface. */
|
|
16
24
|
export class StorageGuard {
|
|
25
|
+
/** What has already been said, so a filter in a loop says it once. */
|
|
26
|
+
private readonly said = new Set<string>();
|
|
27
|
+
|
|
17
28
|
constructor(
|
|
18
29
|
private readonly fields: Fields,
|
|
19
30
|
private readonly entity: string,
|
|
31
|
+
private readonly report: GuardReport = {},
|
|
20
32
|
) {}
|
|
21
33
|
|
|
22
34
|
guard<T extends object>(storage: T): T {
|
|
@@ -27,35 +39,174 @@ export class StorageGuard {
|
|
|
27
39
|
const guarded = Object.create(storage) as T & Writer;
|
|
28
40
|
|
|
29
41
|
guarded.create = async function (...args) {
|
|
30
|
-
validation.
|
|
42
|
+
args[0] = validation.validated(args[0], 'create');
|
|
31
43
|
return writer.create.apply(this, args);
|
|
32
44
|
};
|
|
33
45
|
|
|
34
46
|
guarded.update = async function (...args) {
|
|
35
|
-
validation.
|
|
47
|
+
args[1] = validation.validated(args[1], 'update');
|
|
36
48
|
return writer.update.apply(this, args);
|
|
37
49
|
};
|
|
38
50
|
|
|
39
|
-
const
|
|
40
|
-
if (typeof
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
51
|
+
const upsert = writer.upsert;
|
|
52
|
+
if (typeof upsert === 'function') {
|
|
53
|
+
guarded.upsert = async function (...args) {
|
|
54
|
+
args[0] = validation.validated(args[0], 'upsert');
|
|
55
|
+
return upsert.apply(this, args);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const upsertAll = writer.upsertAll;
|
|
60
|
+
if (typeof upsertAll === 'function') {
|
|
61
|
+
// Every row before the first write: a page refused halfway leaves rows behind that
|
|
62
|
+
// the caller asked for as one, and the refusal is readable from the input alone.
|
|
63
|
+
guarded.upsertAll = async function (...args) {
|
|
64
|
+
args[0] = args[0].map((row, index) => validation.validated(row, 'upsertAll', index));
|
|
65
|
+
return upsertAll.apply(this, args);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const list = writer.list;
|
|
70
|
+
if (typeof list === 'function') {
|
|
71
|
+
guarded.list = async function (...args: unknown[]) {
|
|
72
|
+
const options = args[0] as { where?: Record<string, unknown> } | undefined;
|
|
73
|
+
assertListOptions(options, validation.entity);
|
|
74
|
+
if (options?.where) args[0] = { ...options, where: validation.criteria(options.where) };
|
|
75
|
+
|
|
76
|
+
return list.apply(this, args);
|
|
44
77
|
};
|
|
45
78
|
}
|
|
46
79
|
|
|
47
80
|
return guarded;
|
|
48
81
|
}
|
|
49
82
|
|
|
50
|
-
|
|
51
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The values this write carries, validated and handed on PARSED — the rule the client door
|
|
85
|
+
* already holds (`InputValidator`), applied to a write that never passed through it. What a
|
|
86
|
+
* handler is allowed to write is not asked here: only the value is.
|
|
87
|
+
*/
|
|
88
|
+
private rowAt(row: unknown, index: number): string {
|
|
89
|
+
const primary = FieldSet.of(this.fields).primary;
|
|
90
|
+
const key = primary === undefined ? undefined : (row as Record<string, unknown>)[primary];
|
|
91
|
+
return key === undefined ? `row ${index} of this page` : `row ${primary} ${JSON.stringify(key)}`;
|
|
92
|
+
}
|
|
52
93
|
|
|
94
|
+
/**
|
|
95
|
+
* What a read may ask for. The write door judges what LANDS in a row; this one judges
|
|
96
|
+
* what a caller says about one — and it was the single entrance to the port with no
|
|
97
|
+
* judge at all, while `params.filter` from a browser reaches it verbatim.
|
|
98
|
+
*
|
|
99
|
+
* A criterion may name a SET, which is the one thing the write door would refuse: an
|
|
100
|
+
* array is judged member by member, since that is what `IN` binds.
|
|
101
|
+
*/
|
|
102
|
+
private criteria(where: Record<string, unknown>): Record<string, unknown> {
|
|
53
103
|
const errors: string[] = [];
|
|
104
|
+
const parsed: Record<string, unknown> = {};
|
|
105
|
+
|
|
106
|
+
for (const [key, asked] of Object.entries(where)) {
|
|
107
|
+
const field = this.fields[key];
|
|
108
|
+
if (!field) {
|
|
109
|
+
errors.push(`${key}: ${InputRefusal.unknownField}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// A comparison names its own vocabulary, and a typo in it would otherwise be a
|
|
113
|
+
// criterion that filters nothing — the silent truncation this door exists to stop.
|
|
114
|
+
const comparison = comparisonOf(field, asked);
|
|
115
|
+
if (comparison) {
|
|
116
|
+
const unknown = unknownIn(comparison);
|
|
117
|
+
if (unknown.length) {
|
|
118
|
+
errors.push(`${key}: unknown comparison ${unknown.join(', ')} — one of ${COMPARISONS.join(', ')}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
parsed[key] = comparison;
|
|
122
|
+
this.beyondTheView(key);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const values = Array.isArray(asked) ? asked : [asked];
|
|
127
|
+
const each = values.map((value) => this.value(field, value));
|
|
128
|
+
const refused = each.find((one) => typeof one === 'object' && one !== null && 'error' in one);
|
|
129
|
+
if (refused) {
|
|
130
|
+
errors.push(`${key}: ${(refused as { error: string }).error}`);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
parsed[key] = Array.isArray(asked) ? each.map(unwrap) : unwrap(each[0]);
|
|
134
|
+
this.beyondTheView(key);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (errors.length > 0) {
|
|
138
|
+
throw new FougereError({
|
|
139
|
+
code: ErrorCode.BAD_REQUEST,
|
|
140
|
+
message: `Refused as a filter — ${errors.join(', ')}`,
|
|
141
|
+
entity: this.entity,
|
|
142
|
+
operation: 'list',
|
|
143
|
+
details: errors,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A filter on a field this door does not hand back.
|
|
152
|
+
*
|
|
153
|
+
* `output(schema)` narrows what is RETURNED and has never narrowed what is asked, so a
|
|
154
|
+
* caller can already sort a hidden column into existence one comparison at a time — and
|
|
155
|
+
* the admin door copies a browser's filter here verbatim. Said rather than refused: it
|
|
156
|
+
* is legal today, GraphQL batches a relation on a key a view may not carry, and a
|
|
157
|
+
* refusal would break that on the way to fixing this.
|
|
158
|
+
*/
|
|
159
|
+
private beyondTheView(field: string): void {
|
|
160
|
+
const view = this.report.view;
|
|
161
|
+
if (!view || view[field] || this.said.has(field)) return;
|
|
162
|
+
this.said.add(field);
|
|
163
|
+
this.report.outOfView?.(
|
|
164
|
+
`${this.entity}.list() filtered on '${field}', which this door does not hand back — `
|
|
165
|
+
+ 'a filter on a hidden field answers questions about it one call at a time.',
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** One value against one field — validated, then decoded the way the wire hands it. */
|
|
170
|
+
private value(field: Fields[string], asked: unknown): { value: unknown } | { error: string } {
|
|
171
|
+
if (asked === null || asked === undefined) return { value: asked };
|
|
172
|
+
const checked = FieldValueValidator.of(field).validate(asked);
|
|
173
|
+
if ('error' in checked) return checked;
|
|
174
|
+
if (checked.value === null) return { value: null };
|
|
175
|
+
|
|
176
|
+
return Boundary.of(field).decode(checked.value);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private validated<T>(value: T, operation: string, index?: number): T {
|
|
180
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
181
|
+
|
|
182
|
+
const errors: string[] = [];
|
|
183
|
+
const where = index === undefined ? '' : `${this.rowAt(value, index)} — `;
|
|
184
|
+
const parsed: Record<string, unknown> = {};
|
|
185
|
+
|
|
54
186
|
for (const [key, item] of Object.entries(value as Record<string, unknown>)) {
|
|
55
187
|
const field = this.fields[key];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
188
|
+
// A key the entity does not declare has no column to land in and no judge to pass:
|
|
189
|
+
// on the client door it is a typo, and on this one it is a mapping that went stale.
|
|
190
|
+
if (!field) {
|
|
191
|
+
errors.push(`${where}${key}: ${InputRefusal.unknownField}`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (item === undefined) {
|
|
195
|
+
parsed[key] = item;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const checked = FieldValueValidator.of(field).validate(item);
|
|
199
|
+
if ('error' in checked) {
|
|
200
|
+
errors.push(`${where}${key}: ${checked.error}`);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (checked.value === null) {
|
|
204
|
+
parsed[key] = null;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const decoded = Boundary.of(field).decode(checked.value);
|
|
208
|
+
if ('error' in decoded) errors.push(`${where}${key}: ${decoded.error}`);
|
|
209
|
+
else parsed[key] = decoded.value;
|
|
59
210
|
}
|
|
60
211
|
|
|
61
212
|
if (errors.length > 0) {
|
|
@@ -67,5 +218,10 @@ export class StorageGuard {
|
|
|
67
218
|
details: errors,
|
|
68
219
|
});
|
|
69
220
|
}
|
|
221
|
+
|
|
222
|
+
return parsed as T;
|
|
70
223
|
}
|
|
71
224
|
}
|
|
225
|
+
|
|
226
|
+
const unwrap = (one: { value: unknown } | { error: string }): unknown =>
|
|
227
|
+
'value' in one ? one.value : undefined;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { computeBindingPlan } from '../wire/binding.js';
|
|
2
|
+
import type { InvocationContext } from '../wire/Invocation.js';
|
|
3
|
+
import type { PresenterEntry } from '../descriptor/frond.js';
|
|
4
|
+
import type { ArgumentResolver } from './ArgumentResolver.js';
|
|
5
|
+
import type { PresenterArgs } from './PresenterExecutor.js';
|
|
6
|
+
|
|
7
|
+
/** The invocation-dependent arguments of every computed presenter field. */
|
|
8
|
+
export async function presenterArguments(
|
|
9
|
+
meta: PresenterEntry,
|
|
10
|
+
invocation: InvocationContext,
|
|
11
|
+
resolver: ArgumentResolver,
|
|
12
|
+
collectorTypes: Set<string>,
|
|
13
|
+
): Promise<PresenterArgs> {
|
|
14
|
+
const argumentsByField: PresenterArgs = {};
|
|
15
|
+
|
|
16
|
+
for (const field of meta.fieldMeta) {
|
|
17
|
+
if (!field.params?.length) continue;
|
|
18
|
+
argumentsByField[field.name] = await resolver.resolve(
|
|
19
|
+
computeBindingPlan(field.params, collectorTypes),
|
|
20
|
+
invocation,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return argumentsByField;
|
|
25
|
+
}
|