@fougere/core 0.2.0-alpha.2 → 0.4.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 +3 -2
- package/dist/boot/AppLifecycle.d.ts +18 -0
- package/dist/boot/AppLifecycle.d.ts.map +1 -0
- package/dist/boot/AppLifecycle.js +42 -0
- package/dist/boot/AppLifecycle.js.map +1 -0
- package/dist/boot/Emissions.d.ts +126 -0
- package/dist/boot/Emissions.d.ts.map +1 -0
- package/dist/boot/Emissions.js +233 -0
- package/dist/boot/Emissions.js.map +1 -0
- package/dist/boot/HandlerFacade.d.ts +72 -0
- package/dist/boot/HandlerFacade.d.ts.map +1 -0
- package/dist/boot/HandlerFacade.js +174 -0
- package/dist/boot/HandlerFacade.js.map +1 -0
- package/dist/boot/Lifecycle.d.ts +4 -0
- package/dist/boot/Lifecycle.d.ts.map +1 -0
- package/dist/boot/Lifecycle.js +3 -0
- package/dist/boot/Lifecycle.js.map +1 -0
- package/dist/boot/ambient-port.d.ts +38 -0
- package/dist/boot/ambient-port.d.ts.map +1 -0
- package/dist/boot/ambient-port.js +2 -0
- package/dist/boot/ambient-port.js.map +1 -0
- package/dist/boot/ambient.als.d.ts +3 -0
- package/dist/boot/ambient.als.d.ts.map +1 -0
- package/dist/boot/ambient.als.js +34 -0
- package/dist/boot/ambient.als.js.map +1 -0
- package/dist/boot/ambient.queue.d.ts +10 -0
- package/dist/boot/ambient.queue.d.ts.map +1 -0
- package/dist/boot/ambient.queue.js +60 -0
- package/dist/boot/ambient.queue.js.map +1 -0
- package/dist/boot/apply.d.ts +22 -0
- package/dist/boot/apply.d.ts.map +1 -0
- package/dist/boot/apply.js +38 -0
- package/dist/boot/apply.js.map +1 -0
- package/dist/boot/auth.d.ts +53 -0
- package/dist/boot/auth.d.ts.map +1 -0
- package/dist/boot/auth.js +2 -0
- package/dist/boot/auth.js.map +1 -0
- package/dist/{binding.d.ts → boot/binding.d.ts} +14 -17
- package/dist/boot/binding.d.ts.map +1 -0
- package/dist/{binding.js → boot/binding.js} +19 -10
- package/dist/boot/binding.js.map +1 -0
- package/dist/{boot.d.ts → boot/boot.d.ts} +25 -5
- package/dist/boot/boot.d.ts.map +1 -0
- package/dist/boot/boot.js +70 -0
- package/dist/boot/boot.js.map +1 -0
- package/dist/boot/bootstrap.d.ts.map +1 -0
- package/dist/boot/bootstrap.js +722 -0
- package/dist/boot/bootstrap.js.map +1 -0
- package/dist/boot/frame.d.ts +44 -0
- package/dist/boot/frame.d.ts.map +1 -0
- package/dist/boot/frame.js +172 -0
- package/dist/boot/frame.js.map +1 -0
- package/dist/boot/ownership.d.ts +42 -0
- package/dist/boot/ownership.d.ts.map +1 -0
- package/dist/boot/ownership.js +127 -0
- package/dist/boot/ownership.js.map +1 -0
- package/dist/boot/ports.d.ts +17 -0
- package/dist/boot/ports.d.ts.map +1 -0
- package/dist/boot/ports.js +57 -0
- package/dist/boot/ports.js.map +1 -0
- package/dist/{remote.d.ts → boot/remote.d.ts} +11 -3
- package/dist/boot/remote.d.ts.map +1 -0
- package/dist/{remote.js → boot/remote.js} +26 -28
- package/dist/boot/remote.js.map +1 -0
- package/dist/{seed.d.ts → boot/seed.d.ts} +13 -1
- package/dist/boot/seed.d.ts.map +1 -0
- package/dist/{seed.js → boot/seed.js} +24 -3
- package/dist/boot/seed.js.map +1 -0
- package/dist/boot/together.d.ts +26 -0
- package/dist/boot/together.d.ts.map +1 -0
- package/dist/boot/together.js +163 -0
- package/dist/boot/together.js.map +1 -0
- package/dist/boot/types.d.ts +260 -0
- package/dist/boot/types.d.ts.map +1 -0
- package/dist/boot/types.js.map +1 -0
- package/dist/builtins/config.d.ts +9 -0
- package/dist/builtins/config.d.ts.map +1 -1
- package/dist/builtins/config.js +16 -1
- package/dist/builtins/config.js.map +1 -1
- package/dist/builtins/logger.d.ts +37 -4
- package/dist/builtins/logger.d.ts.map +1 -1
- package/dist/builtins/logger.js +66 -7
- package/dist/builtins/logger.js.map +1 -1
- package/dist/config-loader.d.ts +39 -35
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +15 -54
- package/dist/config-loader.js.map +1 -1
- package/dist/contract/Call.d.ts +10 -0
- package/dist/contract/Call.d.ts.map +1 -0
- package/dist/contract/Call.js +15 -0
- package/dist/contract/Call.js.map +1 -0
- package/dist/contract/Invocation.d.ts +27 -0
- package/dist/contract/Invocation.d.ts.map +1 -0
- package/dist/contract/Invocation.js +59 -0
- package/dist/contract/Invocation.js.map +1 -0
- package/dist/contract/RouteAddress.d.ts +17 -0
- package/dist/contract/RouteAddress.d.ts.map +1 -0
- package/dist/contract/RouteAddress.js +38 -0
- package/dist/contract/RouteAddress.js.map +1 -0
- package/dist/contract.d.ts +25 -19
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +25 -10
- package/dist/contract.js.map +1 -1
- package/dist/crypto/encoding.d.ts +25 -0
- package/dist/crypto/encoding.d.ts.map +1 -0
- package/dist/crypto/encoding.js +58 -0
- package/dist/crypto/encoding.js.map +1 -0
- package/dist/crypto/node.d.ts +4 -0
- package/dist/crypto/node.d.ts.map +1 -0
- package/dist/crypto/node.js +25 -0
- package/dist/crypto/node.js.map +1 -0
- package/dist/crypto/port.d.ts +42 -0
- package/dist/crypto/port.d.ts.map +1 -0
- package/dist/crypto/port.js +19 -0
- package/dist/crypto/port.js.map +1 -0
- package/dist/crypto/webcrypto.d.ts +4 -0
- package/dist/crypto/webcrypto.d.ts.map +1 -0
- package/dist/crypto/webcrypto.js +34 -0
- package/dist/crypto/webcrypto.js.map +1 -0
- package/dist/dispatch/ArgumentResolver.d.ts +10 -0
- package/dist/dispatch/ArgumentResolver.d.ts.map +1 -0
- package/dist/dispatch/ArgumentResolver.js +12 -0
- package/dist/dispatch/ArgumentResolver.js.map +1 -0
- package/dist/dispatch/ArrayResult.d.ts +3 -0
- package/dist/dispatch/ArrayResult.d.ts.map +1 -0
- package/dist/dispatch/ArrayResult.js +11 -0
- package/dist/dispatch/ArrayResult.js.map +1 -0
- package/dist/dispatch/DispatchEvent.d.ts +18 -0
- package/dist/dispatch/DispatchEvent.d.ts.map +1 -0
- package/dist/dispatch/DispatchEvent.js +30 -0
- package/dist/dispatch/DispatchEvent.js.map +1 -0
- package/dist/dispatch/DispatchLifecycle.d.ts +9 -0
- package/dist/dispatch/DispatchLifecycle.d.ts.map +1 -0
- package/dist/dispatch/DispatchLifecycle.js +25 -0
- package/dist/dispatch/DispatchLifecycle.js.map +1 -0
- package/dist/dispatch/DispatchPort.d.ts +6 -0
- package/dist/dispatch/DispatchPort.d.ts.map +1 -0
- package/dist/dispatch/DispatchPort.js +2 -0
- package/dist/dispatch/DispatchPort.js.map +1 -0
- package/dist/dispatch/Dispatcher.d.ts +16 -0
- package/dist/dispatch/Dispatcher.d.ts.map +1 -0
- package/dist/dispatch/Dispatcher.js +44 -0
- package/dist/dispatch/Dispatcher.js.map +1 -0
- package/dist/dispatch/InFlight.d.ts +12 -0
- package/dist/dispatch/InFlight.d.ts.map +1 -0
- package/dist/dispatch/InFlight.js +46 -0
- package/dist/dispatch/InFlight.js.map +1 -0
- package/dist/dispatch/InputValidator.d.ts +7 -0
- package/dist/dispatch/InputValidator.d.ts.map +1 -0
- package/dist/dispatch/InputValidator.js +22 -0
- package/dist/dispatch/InputValidator.js.map +1 -0
- package/dist/dispatch/LocalRoutePolicy.d.ts +11 -0
- package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -0
- package/dist/dispatch/LocalRoutePolicy.js +32 -0
- package/dist/dispatch/LocalRoutePolicy.js.map +1 -0
- package/dist/dispatch/OperationExecutor.d.ts +25 -0
- package/dist/dispatch/OperationExecutor.d.ts.map +1 -0
- package/dist/dispatch/OperationExecutor.js +32 -0
- package/dist/dispatch/OperationExecutor.js.map +1 -0
- package/dist/dispatch/OperationRoute.d.ts +13 -0
- package/dist/dispatch/OperationRoute.d.ts.map +1 -0
- package/dist/dispatch/OperationRoute.js +15 -0
- package/dist/dispatch/OperationRoute.js.map +1 -0
- package/dist/dispatch/OutputProjector.d.ts +12 -0
- package/dist/dispatch/OutputProjector.d.ts.map +1 -0
- package/dist/dispatch/OutputProjector.js +30 -0
- package/dist/dispatch/OutputProjector.js.map +1 -0
- package/dist/dispatch/OutputView.d.ts +8 -0
- package/dist/dispatch/OutputView.d.ts.map +1 -0
- package/dist/dispatch/OutputView.js +10 -0
- package/dist/dispatch/OutputView.js.map +1 -0
- package/dist/dispatch/PresenterArgumentResolver.d.ts +12 -0
- package/dist/dispatch/PresenterArgumentResolver.d.ts.map +1 -0
- package/dist/dispatch/PresenterArgumentResolver.js +20 -0
- package/dist/dispatch/PresenterArgumentResolver.js.map +1 -0
- package/dist/dispatch/PresenterExecutor.d.ts +11 -0
- package/dist/dispatch/PresenterExecutor.d.ts.map +1 -0
- package/dist/dispatch/PresenterExecutor.js +56 -0
- package/dist/dispatch/PresenterExecutor.js.map +1 -0
- package/dist/dispatch/RemoteRouteResolver.d.ts +13 -0
- package/dist/dispatch/RemoteRouteResolver.d.ts.map +1 -0
- package/dist/dispatch/RemoteRouteResolver.js +18 -0
- package/dist/dispatch/RemoteRouteResolver.js.map +1 -0
- package/dist/dispatch/Route.d.ts +10 -0
- package/dist/dispatch/Route.d.ts.map +1 -0
- package/dist/dispatch/Route.js +2 -0
- package/dist/dispatch/Route.js.map +1 -0
- package/dist/dispatch/RouteNotFoundError.d.ts +10 -0
- package/dist/dispatch/RouteNotFoundError.d.ts.map +1 -0
- package/dist/dispatch/RouteNotFoundError.js +24 -0
- package/dist/dispatch/RouteNotFoundError.js.map +1 -0
- package/dist/dispatch/RoutePolicy.d.ts +8 -0
- package/dist/dispatch/RoutePolicy.d.ts.map +1 -0
- package/dist/dispatch/RoutePolicy.js +2 -0
- package/dist/dispatch/RoutePolicy.js.map +1 -0
- package/dist/dispatch/RouteRegistry.d.ts +18 -0
- package/dist/dispatch/RouteRegistry.d.ts.map +1 -0
- package/dist/dispatch/RouteRegistry.js +62 -0
- package/dist/dispatch/RouteRegistry.js.map +1 -0
- package/dist/dispatch/RouteResolver.d.ts +7 -0
- package/dist/dispatch/RouteResolver.d.ts.map +1 -0
- package/dist/dispatch/RouteResolver.js +2 -0
- package/dist/dispatch/RouteResolver.js.map +1 -0
- package/dist/dispatch/StorageGuard.d.ts +15 -0
- package/dist/dispatch/StorageGuard.d.ts.map +1 -0
- package/dist/dispatch/StorageGuard.js +63 -0
- package/dist/dispatch/StorageGuard.js.map +1 -0
- package/dist/effective-operation.d.ts +90 -0
- package/dist/effective-operation.d.ts.map +1 -0
- package/dist/effective-operation.js +449 -0
- package/dist/effective-operation.js.map +1 -0
- package/dist/emit.d.ts +2 -2
- package/dist/emit.d.ts.map +1 -1
- package/dist/emit.js +2 -2
- package/dist/emit.js.map +1 -1
- package/dist/entry/DynamicFacade.d.ts +10 -0
- package/dist/entry/DynamicFacade.d.ts.map +1 -0
- package/dist/entry/DynamicFacade.js +27 -0
- package/dist/entry/DynamicFacade.js.map +1 -0
- package/dist/entry/FacadeEntry.d.ts +11 -0
- package/dist/entry/FacadeEntry.d.ts.map +1 -0
- package/dist/entry/FacadeEntry.js +26 -0
- package/dist/entry/FacadeEntry.js.map +1 -0
- package/dist/entry/TransportEntry.d.ts +13 -0
- package/dist/entry/TransportEntry.d.ts.map +1 -0
- package/dist/entry/TransportEntry.js +24 -0
- package/dist/entry/TransportEntry.js.map +1 -0
- package/dist/frond-config.d.ts +24 -7
- package/dist/frond-config.d.ts.map +1 -1
- package/dist/frond-config.js.map +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +3 -2
- package/dist/graph.js.map +1 -1
- package/dist/identity-keys.d.ts +14 -0
- package/dist/identity-keys.d.ts.map +1 -0
- package/dist/identity-keys.js +37 -0
- package/dist/identity-keys.js.map +1 -0
- package/dist/identity.d.ts +58 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +182 -0
- package/dist/identity.js.map +1 -0
- package/dist/imports.d.ts +2 -2
- package/dist/imports.d.ts.map +1 -1
- package/dist/imports.js +7 -3
- package/dist/imports.js.map +1 -1
- package/dist/index.d.ts +57 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -32
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +16 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +25 -0
- package/dist/loader.js.map +1 -0
- package/dist/node.d.ts +21 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +24 -0
- package/dist/node.js.map +1 -0
- package/dist/orm.d.ts +69 -8
- package/dist/orm.d.ts.map +1 -1
- package/dist/orm.js +35 -2
- package/dist/orm.js.map +1 -1
- package/dist/prefab/collector.d.ts +29 -0
- package/dist/prefab/collector.d.ts.map +1 -0
- package/dist/prefab/collector.js +34 -0
- package/dist/prefab/collector.js.map +1 -0
- package/dist/{crud.d.ts → prefab/crud.d.ts} +7 -8
- package/dist/prefab/crud.d.ts.map +1 -0
- package/dist/{crud.js → prefab/crud.js} +39 -5
- package/dist/prefab/crud.js.map +1 -0
- package/dist/prefab/mirror.d.ts +52 -0
- package/dist/prefab/mirror.d.ts.map +1 -0
- package/dist/prefab/mirror.js +66 -0
- package/dist/prefab/mirror.js.map +1 -0
- package/dist/prefab/prefab.d.ts +15 -0
- package/dist/prefab/prefab.d.ts.map +1 -0
- package/dist/prefab/prefab.js +19 -0
- package/dist/prefab/prefab.js.map +1 -0
- package/dist/{presenter.d.ts → prefab/presenter.d.ts} +6 -17
- package/dist/prefab/presenter.d.ts.map +1 -0
- package/dist/{presenter.js → prefab/presenter.js} +5 -23
- package/dist/prefab/presenter.js.map +1 -0
- package/dist/prefab/repository.d.ts +104 -0
- package/dist/prefab/repository.d.ts.map +1 -0
- package/dist/prefab/repository.js +62 -0
- package/dist/prefab/repository.js.map +1 -0
- package/dist/scan/Fronds.d.ts +45 -0
- package/dist/scan/Fronds.d.ts.map +1 -0
- package/dist/scan/Fronds.js +66 -0
- package/dist/scan/Fronds.js.map +1 -0
- package/dist/scan/conventions.d.ts +74 -0
- package/dist/scan/conventions.d.ts.map +1 -0
- package/dist/scan/conventions.js +82 -0
- package/dist/scan/conventions.js.map +1 -0
- package/dist/scan/emit.d.ts +14 -0
- package/dist/scan/emit.d.ts.map +1 -0
- package/dist/scan/emit.js +212 -0
- package/dist/scan/emit.js.map +1 -0
- package/dist/scan/frond.d.ts +224 -0
- package/dist/scan/frond.d.ts.map +1 -0
- package/dist/scan/frond.js +2 -0
- package/dist/scan/frond.js.map +1 -0
- package/dist/{handler-parser.d.ts → scan/handler-parser.d.ts} +20 -9
- package/dist/scan/handler-parser.d.ts.map +1 -0
- package/dist/scan/handler-parser.js +665 -0
- package/dist/scan/handler-parser.js.map +1 -0
- package/dist/scan/scan-cache.d.ts +16 -0
- package/dist/scan/scan-cache.d.ts.map +1 -0
- package/dist/{scan-cache.js → scan/scan-cache.js} +27 -20
- package/dist/scan/scan-cache.js.map +1 -0
- package/dist/scan/scanner.d.ts +20 -0
- package/dist/scan/scanner.d.ts.map +1 -0
- package/dist/{scanner.js → scan/scanner.js} +212 -167
- package/dist/scan/scanner.js.map +1 -0
- package/dist/verify.d.ts +16 -3
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +21 -18
- package/dist/verify.js.map +1 -1
- package/dist/{call.d.ts → wire/call.d.ts} +76 -7
- package/dist/wire/call.d.ts.map +1 -0
- package/dist/{call.js → wire/call.js} +53 -80
- package/dist/wire/call.js.map +1 -0
- package/dist/{middleware.d.ts → wire/errors.d.ts} +18 -19
- package/dist/wire/errors.d.ts.map +1 -0
- package/dist/{middleware.js → wire/errors.js} +26 -14
- package/dist/wire/errors.js.map +1 -0
- package/dist/{errors.d.ts → wire/http-error.d.ts} +5 -6
- package/dist/wire/http-error.d.ts.map +1 -0
- package/dist/{errors.js → wire/http-error.js} +7 -6
- package/dist/wire/http-error.js.map +1 -0
- package/dist/wire/invocation.d.ts +4 -0
- package/dist/wire/invocation.d.ts.map +1 -0
- package/dist/wire/invocation.js +3 -0
- package/dist/wire/invocation.js.map +1 -0
- package/dist/{middleware/logger.d.ts → wire/loggerMiddleware.d.ts} +3 -3
- package/dist/wire/loggerMiddleware.d.ts.map +1 -0
- package/dist/{middleware/logger.js → wire/loggerMiddleware.js} +2 -2
- package/dist/wire/loggerMiddleware.js.map +1 -0
- package/dist/wire/middleware.d.ts +29 -0
- package/dist/wire/middleware.d.ts.map +1 -0
- package/dist/wire/middleware.js +20 -0
- package/dist/wire/middleware.js.map +1 -0
- package/dist/{operation.d.ts → wire/operation.d.ts} +39 -10
- package/dist/wire/operation.d.ts.map +1 -0
- package/dist/wire/operation.js +167 -0
- package/dist/wire/operation.js.map +1 -0
- package/package.json +27 -8
- package/src/boot/AppLifecycle.ts +50 -0
- package/src/boot/Emissions.ts +263 -0
- package/src/boot/HandlerFacade.ts +242 -0
- package/src/boot/Lifecycle.ts +3 -0
- package/src/boot/ambient-port.ts +41 -0
- package/src/boot/ambient.als.ts +43 -0
- package/src/boot/ambient.queue.ts +75 -0
- package/src/boot/apply.ts +47 -0
- package/src/boot/auth.ts +55 -0
- package/src/boot/binding.ts +182 -0
- package/src/boot/boot.ts +125 -0
- package/src/boot/bootstrap.ts +839 -0
- package/src/boot/frame.ts +210 -0
- package/src/boot/ownership.ts +140 -0
- package/src/boot/ports.ts +66 -0
- package/src/boot/remote.ts +179 -0
- package/src/boot/seed.ts +158 -0
- package/src/boot/together.ts +215 -0
- package/src/boot/types.ts +261 -0
- package/src/builtins/config.ts +19 -0
- package/src/builtins/logger.ts +175 -0
- package/src/config-loader.ts +146 -0
- package/src/contract/Call.ts +18 -0
- package/src/contract/Invocation.ts +72 -0
- package/src/contract/RouteAddress.ts +49 -0
- package/src/contract.ts +74 -0
- package/src/crypto/encoding.ts +59 -0
- package/src/crypto/node.ts +30 -0
- package/src/crypto/port.ts +45 -0
- package/src/crypto/webcrypto.ts +55 -0
- package/src/define.ts +10 -0
- package/src/dispatch/ArgumentResolver.ts +17 -0
- package/src/dispatch/ArrayResult.ts +10 -0
- package/src/dispatch/DispatchEvent.ts +38 -0
- package/src/dispatch/DispatchLifecycle.ts +23 -0
- package/src/dispatch/DispatchPort.ts +6 -0
- package/src/dispatch/Dispatcher.ts +48 -0
- package/src/dispatch/InFlight.ts +49 -0
- package/src/dispatch/InputValidator.ts +28 -0
- package/src/dispatch/LocalRoutePolicy.ts +34 -0
- package/src/dispatch/OperationExecutor.ts +60 -0
- package/src/dispatch/OperationRoute.ts +18 -0
- package/src/dispatch/OutputProjector.ts +32 -0
- package/src/dispatch/OutputView.ts +9 -0
- package/src/dispatch/PresenterArgumentResolver.ts +25 -0
- package/src/dispatch/PresenterExecutor.ts +55 -0
- package/src/dispatch/RemoteRouteResolver.ts +21 -0
- package/src/dispatch/Route.ts +11 -0
- package/src/dispatch/RouteNotFoundError.ts +29 -0
- package/src/dispatch/RoutePolicy.ts +8 -0
- package/src/dispatch/RouteRegistry.ts +73 -0
- package/src/dispatch/RouteResolver.ts +7 -0
- package/src/dispatch/StorageGuard.ts +71 -0
- package/src/effective-operation.ts +597 -0
- package/src/emit.ts +90 -0
- package/src/entry/DynamicFacade.ts +28 -0
- package/src/entry/FacadeEntry.ts +32 -0
- package/src/entry/TransportEntry.ts +30 -0
- package/src/frond-config.ts +138 -0
- package/src/graph.ts +180 -0
- package/src/identity-keys.ts +43 -0
- package/src/identity.ts +235 -0
- package/src/imports.ts +123 -0
- package/src/index.ts +106 -0
- package/src/loader.ts +41 -0
- package/src/node.ts +28 -0
- package/src/orm.ts +252 -0
- package/src/prefab/collector.ts +36 -0
- package/src/prefab/crud.ts +188 -0
- package/src/prefab/mirror.ts +122 -0
- package/src/prefab/prefab.ts +46 -0
- package/src/prefab/presenter.ts +63 -0
- package/src/prefab/repository.ts +170 -0
- package/src/scan/Fronds.ts +73 -0
- package/src/scan/conventions.ts +122 -0
- package/src/scan/emit.ts +231 -0
- package/src/scan/frond.ts +235 -0
- package/src/scan/handler-parser.ts +818 -0
- package/src/scan/scan-cache.ts +115 -0
- package/src/scan/scanner.ts +758 -0
- package/src/verify.ts +193 -0
- package/src/wire/call.ts +369 -0
- package/src/wire/errors.ts +124 -0
- package/src/wire/http-error.ts +77 -0
- package/src/wire/invocation.ts +10 -0
- package/src/wire/loggerMiddleware.ts +29 -0
- package/src/wire/middleware.ts +47 -0
- package/src/wire/operation.ts +258 -0
- package/dist/binding.d.ts.map +0 -1
- package/dist/binding.js.map +0 -1
- package/dist/boot.d.ts.map +0 -1
- package/dist/boot.js +0 -53
- package/dist/boot.js.map +0 -1
- package/dist/bootstrap.d.ts.map +0 -1
- package/dist/bootstrap.js +0 -879
- package/dist/bootstrap.js.map +0 -1
- package/dist/call.d.ts.map +0 -1
- package/dist/call.js.map +0 -1
- package/dist/collector.d.ts +0 -28
- package/dist/collector.d.ts.map +0 -1
- package/dist/collector.js +0 -32
- package/dist/collector.js.map +0 -1
- package/dist/crud.d.ts.map +0 -1
- package/dist/crud.js.map +0 -1
- package/dist/egress.d.ts +0 -52
- package/dist/egress.d.ts.map +0 -1
- package/dist/egress.js +0 -186
- package/dist/egress.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/handler-parser.d.ts.map +0 -1
- package/dist/handler-parser.js +0 -505
- package/dist/handler-parser.js.map +0 -1
- package/dist/invocation.d.ts +0 -20
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js +0 -15
- package/dist/invocation.js.map +0 -1
- package/dist/middleware/error-handler.d.ts +0 -15
- package/dist/middleware/error-handler.d.ts.map +0 -1
- package/dist/middleware/error-handler.js +0 -29
- package/dist/middleware/error-handler.js.map +0 -1
- package/dist/middleware/logger.d.ts.map +0 -1
- package/dist/middleware/logger.js.map +0 -1
- package/dist/middleware.d.ts.map +0 -1
- package/dist/middleware.js.map +0 -1
- package/dist/mirror.d.ts +0 -93
- package/dist/mirror.d.ts.map +0 -1
- package/dist/mirror.js +0 -96
- package/dist/mirror.js.map +0 -1
- package/dist/operation.d.ts.map +0 -1
- package/dist/operation.js +0 -95
- package/dist/operation.js.map +0 -1
- package/dist/presenter.d.ts.map +0 -1
- package/dist/presenter.js.map +0 -1
- package/dist/remote.d.ts.map +0 -1
- package/dist/remote.js.map +0 -1
- package/dist/repository.d.ts +0 -55
- package/dist/repository.d.ts.map +0 -1
- package/dist/repository.js +0 -26
- package/dist/repository.js.map +0 -1
- package/dist/scan-cache.d.ts +0 -10
- package/dist/scan-cache.d.ts.map +0 -1
- package/dist/scan-cache.js.map +0 -1
- package/dist/scanner.d.ts +0 -34
- package/dist/scanner.d.ts.map +0 -1
- package/dist/scanner.js.map +0 -1
- package/dist/seed.d.ts.map +0 -1
- package/dist/seed.js.map +0 -1
- package/dist/types.d.ts +0 -421
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- /package/dist/{bootstrap.d.ts → boot/bootstrap.d.ts} +0 -0
- /package/dist/{types.js → boot/types.js} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
type Operation = (...args: any[]) => unknown;
|
|
2
|
+
|
|
3
|
+
/** Facade for a remote contract whose operation names are not known before discovery. */
|
|
4
|
+
export class DynamicFacade {
|
|
5
|
+
readonly operations: Record<string, Operation>;
|
|
6
|
+
|
|
7
|
+
constructor(private readonly operation: (name: string) => Operation) {
|
|
8
|
+
this.operations = new Proxy({}, {
|
|
9
|
+
get: (_target, name) => this.isOperationName(name) ? this.operation(name) : undefined,
|
|
10
|
+
has: (_target, name) => this.isOperationName(name),
|
|
11
|
+
getOwnPropertyDescriptor: (_target, name) => this.isOperationName(name)
|
|
12
|
+
? {
|
|
13
|
+
value: this.operation(name),
|
|
14
|
+
writable: false,
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
}
|
|
18
|
+
: undefined,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private isOperationName(name: string | symbol): name is string {
|
|
23
|
+
return typeof name === 'string'
|
|
24
|
+
&& name !== 'then'
|
|
25
|
+
&& name !== 'toJSON'
|
|
26
|
+
&& !Object.hasOwn(Object.prototype, name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Call } from '../contract/Call.js';
|
|
2
|
+
import type { InvocationInput } from '../contract/Invocation.js';
|
|
3
|
+
import { RouteAddress } from '../contract/RouteAddress.js';
|
|
4
|
+
import type { DispatchPort } from '../dispatch/DispatchPort.js';
|
|
5
|
+
import { DynamicFacade } from './DynamicFacade.js';
|
|
6
|
+
|
|
7
|
+
/** Object-shaped entry that turns facade method calls into canonical dispatches. */
|
|
8
|
+
export class FacadeEntry {
|
|
9
|
+
readonly operations: Record<string, Function>;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly dispatcher: DispatchPort,
|
|
13
|
+
private readonly entity: string,
|
|
14
|
+
operationNames?: Iterable<string>,
|
|
15
|
+
private readonly surface?: string,
|
|
16
|
+
) {
|
|
17
|
+
this.operations = operationNames
|
|
18
|
+
? Object.fromEntries([...operationNames].map((name) => [name, this.operation(name)]))
|
|
19
|
+
: new DynamicFacade((name) => this.operation(name)).operations;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private operation(name: string): (invocation?: InvocationInput) => Promise<unknown> {
|
|
23
|
+
return (invocation) => this.dispatcher.dispatch(new Call(
|
|
24
|
+
new RouteAddress({
|
|
25
|
+
entity: this.entity,
|
|
26
|
+
operation: name,
|
|
27
|
+
...(this.surface !== undefined ? { surface: this.surface } : {}),
|
|
28
|
+
}),
|
|
29
|
+
invocation,
|
|
30
|
+
));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Call } from '../contract/Call.js';
|
|
2
|
+
import { RouteAddress } from '../contract/RouteAddress.js';
|
|
3
|
+
import type { DispatchPort } from '../dispatch/DispatchPort.js';
|
|
4
|
+
import type { FrondCall, Transport } from '../wire/call.js';
|
|
5
|
+
import type { InvocationContext } from '../contract/Invocation.js';
|
|
6
|
+
|
|
7
|
+
/** Object entry for the historical transport contract. */
|
|
8
|
+
export class TransportEntry {
|
|
9
|
+
constructor(
|
|
10
|
+
private readonly dispatcher: DispatchPort,
|
|
11
|
+
private readonly surface?: string,
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
receive(legacy: FrondCall, invocation: InvocationContext): Promise<unknown> {
|
|
15
|
+
return this.dispatcher.dispatch(new Call(
|
|
16
|
+
new RouteAddress({
|
|
17
|
+
entity: legacy.entity,
|
|
18
|
+
operation: legacy.op,
|
|
19
|
+
...(this.surface !== undefined ? { surface: this.surface } : {}),
|
|
20
|
+
}),
|
|
21
|
+
invocation,
|
|
22
|
+
));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Adapt the historical transport signature to the canonical dispatch port. */
|
|
27
|
+
export function createTransportEntry(dispatcher: DispatchPort, surface?: string): Transport {
|
|
28
|
+
const entry = new TransportEntry(dispatcher, surface);
|
|
29
|
+
return entry.receive.bind(entry);
|
|
30
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import type { EntityConstructor, SchemaView } from '@fougere/schema';
|
|
4
|
+
import type { BindingPlan } from './boot/binding.js';
|
|
5
|
+
|
|
6
|
+
// ── Types ────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Per-operation override. Config takes precedence over conventions (isReadOp, facade lookup).
|
|
10
|
+
*
|
|
11
|
+
* Two families of keys, and they act at different depths:
|
|
12
|
+
*
|
|
13
|
+
* - **Resolution** (`kind`, `handler`, `method`) — intent and implementation, resolved
|
|
14
|
+
* once into core's `EffectiveOperation`; every local or remote door consumes it.
|
|
15
|
+
* - **Projection** (`graphql`) — protocol vocabulary, read only by that adapter.
|
|
16
|
+
* - **Contract** (`input`, `output`, `binding`) — what the op IS. Read by the façade,
|
|
17
|
+
* and this is the third producer of an {@link OperationContract}: a prefab DECLARES its
|
|
18
|
+
* ops (`Crud.__ops`), the scan DERIVES them from source, config STATES them outright.
|
|
19
|
+
* The façade cannot tell the three apart.
|
|
20
|
+
*
|
|
21
|
+
* Stating a contract here makes the scan optional rather than load-bearing. Two cases it
|
|
22
|
+
* answers that nothing else does:
|
|
23
|
+
* - a method inherited from an **installed** base class — the scan resolves nothing there
|
|
24
|
+
* (workspace-only heritage) and says nothing, so the op silently misses the façade;
|
|
25
|
+
* - an operation inherited from a declaration unavailable to the project's type checker.
|
|
26
|
+
*
|
|
27
|
+
* Config wins over both other producers — it is the most explicit statement, made by
|
|
28
|
+
* whoever assembles the app. Precedence: CLI > frond config > fougere config > scan >
|
|
29
|
+
* conventions.
|
|
30
|
+
*/
|
|
31
|
+
export interface OperationOverride {
|
|
32
|
+
/**
|
|
33
|
+
* Force operation kind, over the naming convention (`isReadOp`).
|
|
34
|
+
*
|
|
35
|
+
* The convention reads the NAME, which is a weak signal — an app naming its reads in
|
|
36
|
+
* domain terms (`ofBook`, `roots`, `bySlug`) matches no prefix and every one of them is
|
|
37
|
+
* published as a mutation and a POST. This is the only place that fixes it.
|
|
38
|
+
*/
|
|
39
|
+
kind?: 'query' | 'command';
|
|
40
|
+
/**
|
|
41
|
+
* The GraphQL root field this op answers to. Defaults to the method name.
|
|
42
|
+
*
|
|
43
|
+
* A GraphQL root is flat, so two handlers naming a method `ofBook` claim one field and
|
|
44
|
+
* the build is refused, naming both. Nothing is renamed for you: the root vocabulary is
|
|
45
|
+
* the app's, and a derived name would move the day a distant frond gained an entity.
|
|
46
|
+
*/
|
|
47
|
+
graphql?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Handler class to delegate to (overrides the default `{Entity}Handler` lookup).
|
|
50
|
+
* Class name is used to resolve from DI. E.g. `ArchiveHandler` → `app.resolve('ArchiveHandler')`.
|
|
51
|
+
*/
|
|
52
|
+
handler?: EntityConstructor;
|
|
53
|
+
/** Method name on `handler` (defaults to the operation name). */
|
|
54
|
+
method?: string;
|
|
55
|
+
/**
|
|
56
|
+
* CASL ability check (e.g. 'archive Post'). **Declared, not implemented** — no
|
|
57
|
+
* reader interprets it today, in any adapter. Kept as the named slot for the
|
|
58
|
+
* question rather than silently dropped, but do not rely on it: a config that
|
|
59
|
+
* states a policy is not enforced anywhere.
|
|
60
|
+
*
|
|
61
|
+
* Wiring it means adopting a rules engine, which is precisely what the design
|
|
62
|
+
* turns down: an identity-dependent right is judged INSIDE the operation
|
|
63
|
+
* (`publish` reads `post.authorId !== user.id`), and a door-dependent one is a
|
|
64
|
+
* surface. Neither needs a second vocabulary evaluated by a second engine.
|
|
65
|
+
*/
|
|
66
|
+
policy?: string;
|
|
67
|
+
|
|
68
|
+
// ── The contract itself — see the note above ──
|
|
69
|
+
|
|
70
|
+
/** What judges the input. The view carries its own mode (`partial()` → patch). */
|
|
71
|
+
input?: SchemaView;
|
|
72
|
+
/**
|
|
73
|
+
* Where each argument is read from — states what the scan would otherwise derive
|
|
74
|
+
* from the method signature. An empty array is meaningful: "this op takes nothing".
|
|
75
|
+
*/
|
|
76
|
+
binding?: BindingPlan;
|
|
77
|
+
|
|
78
|
+
/** Per-operation output view, below prefab declarations and above handler/entity defaults. */
|
|
79
|
+
output?: SchemaView;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface FrondConfig {
|
|
83
|
+
/** Class names exposed as the frond's public contract (all surfaces). */
|
|
84
|
+
expose?: string[];
|
|
85
|
+
/**
|
|
86
|
+
* The entities this frond may read ACROSS sources, by name.
|
|
87
|
+
*
|
|
88
|
+
* It is not a permission checked after the fact: the list IS the environment a
|
|
89
|
+
* cross-source query runs in — a source holding none of these is never opened, so
|
|
90
|
+
* its tables do not exist in that connection. `facadeFor` excludes an entity with no
|
|
91
|
+
* door on purpose ("it would publish the auth tables to anyone who asks"), and a SQL
|
|
92
|
+
* door at app scope would hand them over; this is what keeps it shut.
|
|
93
|
+
*
|
|
94
|
+
* Declaring it is what makes `Sources` injectable here. A frond that declares none
|
|
95
|
+
* asks for none, and nothing is attached on its behalf.
|
|
96
|
+
*/
|
|
97
|
+
reads?: string[];
|
|
98
|
+
/** Per-surface entity lists. Overrides default deduction for each named surface. */
|
|
99
|
+
surfaces?: Record<string, string[]>;
|
|
100
|
+
/** Interface → implementation bindings for DI (e.g. { Database: 'SqliteDatabase' }). */
|
|
101
|
+
bindings?: Record<string, string>;
|
|
102
|
+
/** Per-operation overrides. Key = operation name (method name on a handler). */
|
|
103
|
+
operations?: Record<string, OperationOverride>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── Helper ───────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Define a frond configuration (typed identity helper).
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* // fronds/auth/frond.config.ts
|
|
113
|
+
* import { defineFrond } from '@fougere/core'
|
|
114
|
+
* export default defineFrond({ expose: ['User', 'AuthHandler'] })
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function defineFrond(config: FrondConfig): FrondConfig {
|
|
118
|
+
return config;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ── Loading ──────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
const FROND_CONFIG_FILES = ['frond.config.ts', 'frond.config.js', 'frond.config.mjs'];
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Load a frond's `frond.config.{ts,js,mjs}` from the given frond directory.
|
|
127
|
+
* Returns `undefined` if no config file is found.
|
|
128
|
+
*/
|
|
129
|
+
export async function loadFrondConfig(frondPath: string): Promise<FrondConfig | undefined> {
|
|
130
|
+
for (const file of FROND_CONFIG_FILES) {
|
|
131
|
+
const path = resolve(frondPath, file);
|
|
132
|
+
if (existsSync(path)) {
|
|
133
|
+
const mod = await import(path);
|
|
134
|
+
return mod.default ?? mod;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
package/src/graph.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Role } from '@fougere/schema';
|
|
2
|
+
/**
|
|
3
|
+
* Entity dependency graph — analyzes ref() links between entities
|
|
4
|
+
* to suggest domain boundaries for splitting fronds.
|
|
5
|
+
*/
|
|
6
|
+
import type { FrondDescriptor } from './scan/frond.js';
|
|
7
|
+
|
|
8
|
+
export interface EntityNode {
|
|
9
|
+
name: string;
|
|
10
|
+
frond: string;
|
|
11
|
+
refs: string[]; // entity names this entity references
|
|
12
|
+
referencedBy: string[]; // entity names that reference this entity
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface DomainCluster {
|
|
16
|
+
name: string;
|
|
17
|
+
entities: string[];
|
|
18
|
+
crossRefs: { from: string; to: string; targetCluster: string }[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Build the full entity dependency graph from scanned fronds. */
|
|
22
|
+
export function buildGraph(fronds: FrondDescriptor[]): Map<string, EntityNode> {
|
|
23
|
+
const nodes = new Map<string, EntityNode>();
|
|
24
|
+
|
|
25
|
+
// First pass: create nodes and collect refs
|
|
26
|
+
for (const frond of fronds) {
|
|
27
|
+
for (const entity of frond.entities) {
|
|
28
|
+
const refs: string[] = [];
|
|
29
|
+
for (const [, field] of Object.entries(entity.entityClass.getFields())) {
|
|
30
|
+
if (Role.of(field).isReference) {
|
|
31
|
+
const resolved = Role.of(field).target;
|
|
32
|
+
const targetName = typeof resolved === 'function' && 'name' in resolved
|
|
33
|
+
? (resolved.name as string).toLowerCase()
|
|
34
|
+
: undefined;
|
|
35
|
+
if (targetName) refs.push(targetName);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
nodes.set(entity.name, { name: entity.name, frond: frond.name, refs, referencedBy: [] });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Second pass: populate referencedBy
|
|
43
|
+
for (const [, node] of nodes) {
|
|
44
|
+
for (const ref of node.refs) {
|
|
45
|
+
const target = nodes.get(ref);
|
|
46
|
+
if (target) target.referencedBy.push(node.name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return nodes;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Cluster entities into domains using connected component analysis.
|
|
55
|
+
* Entities linked by refs (in either direction) belong to the same cluster.
|
|
56
|
+
*/
|
|
57
|
+
export function clusterEntities(nodes: Map<string, EntityNode>): DomainCluster[] {
|
|
58
|
+
const visited = new Set<string>();
|
|
59
|
+
const clusters: string[][] = [];
|
|
60
|
+
|
|
61
|
+
// BFS to find connected components
|
|
62
|
+
for (const [name] of nodes) {
|
|
63
|
+
if (visited.has(name)) continue;
|
|
64
|
+
|
|
65
|
+
const cluster: string[] = [];
|
|
66
|
+
const queue = [name];
|
|
67
|
+
|
|
68
|
+
while (queue.length > 0) {
|
|
69
|
+
const current = queue.shift()!;
|
|
70
|
+
if (visited.has(current)) continue;
|
|
71
|
+
visited.add(current);
|
|
72
|
+
cluster.push(current);
|
|
73
|
+
|
|
74
|
+
const node = nodes.get(current)!;
|
|
75
|
+
for (const ref of [...node.refs, ...node.referencedBy]) {
|
|
76
|
+
if (!visited.has(ref) && nodes.has(ref)) {
|
|
77
|
+
queue.push(ref);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (cluster.length > 0) clusters.push(cluster);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Name each cluster by its hub (most referenced entity)
|
|
86
|
+
return clusters.map((entities) => {
|
|
87
|
+
const hub = entities
|
|
88
|
+
.map((name) => ({ name, score: nodes.get(name)!.referencedBy.length }))
|
|
89
|
+
.sort((a, b) => b.score - a.score)[0];
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
name: hub.name,
|
|
93
|
+
entities,
|
|
94
|
+
crossRefs: [],
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* If there's only one big cluster, try to split it using Kernighan-Lin-style
|
|
101
|
+
* heuristic: find the entity with the fewest cross-links and use it as a cut point.
|
|
102
|
+
*/
|
|
103
|
+
export function suggestSplit(
|
|
104
|
+
nodes: Map<string, EntityNode>,
|
|
105
|
+
minClusterSize = 2,
|
|
106
|
+
): DomainCluster[] {
|
|
107
|
+
const clusters = clusterEntities(nodes);
|
|
108
|
+
|
|
109
|
+
// If already multiple clusters, just annotate cross-refs
|
|
110
|
+
if (clusters.length > 1) {
|
|
111
|
+
return annotateCrossRefs(clusters, nodes);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// No entities at all — nothing to cluster (empty or fresh workspace).
|
|
115
|
+
if (clusters.length === 0) return clusters;
|
|
116
|
+
|
|
117
|
+
// Single cluster — try to find natural cut points
|
|
118
|
+
const allEntities = clusters[0].entities;
|
|
119
|
+
if (allEntities.length <= minClusterSize) return clusters;
|
|
120
|
+
|
|
121
|
+
// Score each entity by how many unique "groups" it connects
|
|
122
|
+
// Entities with high betweenness are boundaries, not cluster members
|
|
123
|
+
const subClusters: string[][] = [];
|
|
124
|
+
const assigned = new Set<string>();
|
|
125
|
+
|
|
126
|
+
// Start with entities that have no outgoing refs (leaves/roots)
|
|
127
|
+
const roots = allEntities.filter((name) => nodes.get(name)!.refs.length === 0);
|
|
128
|
+
|
|
129
|
+
for (const root of roots) {
|
|
130
|
+
if (assigned.has(root)) continue;
|
|
131
|
+
const group: string[] = [root];
|
|
132
|
+
assigned.add(root);
|
|
133
|
+
|
|
134
|
+
// Add entities that reference this root
|
|
135
|
+
for (const refBy of nodes.get(root)!.referencedBy) {
|
|
136
|
+
if (!assigned.has(refBy)) {
|
|
137
|
+
group.push(refBy);
|
|
138
|
+
assigned.add(refBy);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
subClusters.push(group);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Any remaining unassigned entities form their own group
|
|
145
|
+
const remaining = allEntities.filter((name) => !assigned.has(name));
|
|
146
|
+
if (remaining.length > 0) subClusters.push(remaining);
|
|
147
|
+
|
|
148
|
+
// Convert to domain clusters
|
|
149
|
+
const result = subClusters.map((entities) => {
|
|
150
|
+
const hub = entities
|
|
151
|
+
.map((name) => ({ name, score: nodes.get(name)!.referencedBy.length }))
|
|
152
|
+
.sort((a, b) => b.score - a.score)[0];
|
|
153
|
+
return { name: hub.name, entities, crossRefs: [] as DomainCluster['crossRefs'] };
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return annotateCrossRefs(result, nodes);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function annotateCrossRefs(clusters: DomainCluster[], nodes: Map<string, EntityNode>): DomainCluster[] {
|
|
160
|
+
const entityToCluster = new Map<string, string>();
|
|
161
|
+
for (const cluster of clusters) {
|
|
162
|
+
for (const entity of cluster.entities) {
|
|
163
|
+
entityToCluster.set(entity, cluster.name);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const cluster of clusters) {
|
|
168
|
+
for (const entity of cluster.entities) {
|
|
169
|
+
const node = nodes.get(entity)!;
|
|
170
|
+
for (const ref of node.refs) {
|
|
171
|
+
const targetCluster = entityToCluster.get(ref);
|
|
172
|
+
if (targetCluster && targetCluster !== cluster.name) {
|
|
173
|
+
cluster.crossRefs.push({ from: entity, to: ref, targetCluster });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return clusters;
|
|
180
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Making keys and issuing grants — the half of identity that runs at a DEPLOYMENT.
|
|
3
|
+
*
|
|
4
|
+
* It sits on the Node entry because it is the CLI speaking: `fougere keys` generates a
|
|
5
|
+
* pair, `fougere grant` binds a name to one, and both happen once, on a machine with a
|
|
6
|
+
* filesystem. Neither is reachable from a request path, so nothing here is on any hot
|
|
7
|
+
* path and `generateKeyPairSync` — the one gesture WebCrypto could not replace without
|
|
8
|
+
* changing what a key IS — never has to leave `node:crypto`.
|
|
9
|
+
*
|
|
10
|
+
* The verifying half is elsewhere (`identity.ts`) because it runs per call, everywhere.
|
|
11
|
+
*/
|
|
12
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync, sign } from 'node:crypto';
|
|
13
|
+
import { b64url } from './crypto/encoding.js';
|
|
14
|
+
|
|
15
|
+
/** A fresh Ed25519 pair, PEM both ways. The root's and a frond's are the same kind. */
|
|
16
|
+
export function generateKeyPair(): { privateKey: string; publicKey: string } {
|
|
17
|
+
const { privateKey, publicKey } = generateKeyPairSync('ed25519');
|
|
18
|
+
return {
|
|
19
|
+
privateKey: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),
|
|
20
|
+
publicKey: publicKey.export({ type: 'spki', format: 'pem' }).toString(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bind a name to a public key, signed by the root — the whole of what a receiver
|
|
26
|
+
* needs to recognize a frond it has never seen.
|
|
27
|
+
*
|
|
28
|
+
* `ttlDays` bounds the damage of a leaked frond key without a revocation list:
|
|
29
|
+
* re-issuing is a deployment, which is the same gesture that leaked it.
|
|
30
|
+
*/
|
|
31
|
+
export function issueGrant(
|
|
32
|
+
rootPrivateKey: string,
|
|
33
|
+
name: string,
|
|
34
|
+
frondPublicKey: string,
|
|
35
|
+
ttlDays = 90,
|
|
36
|
+
): string {
|
|
37
|
+
const jwk = createPublicKey(frondPublicKey).export({ format: 'jwk' });
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const header = b64url(JSON.stringify({ alg: 'EdDSA', typ: 'fougere-grant' }));
|
|
40
|
+
const payload = b64url(JSON.stringify({ sub: name, jwk, iat: now, exp: now + ttlDays * 86_400_000 }));
|
|
41
|
+
const signingInput = `${header}.${payload}`;
|
|
42
|
+
return `${signingInput}.${b64url(new Uint8Array(sign(null, Buffer.from(signingInput), createPrivateKey(rootPrivateKey))))}`;
|
|
43
|
+
}
|