@fougere/core 0.6.0-alpha.0 → 0.7.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/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 +3 -15
- 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 +3 -10
- package/dist/boot/boot.js.map +1 -1
- package/dist/boot/bootstrap.d.ts.map +1 -1
- package/dist/boot/bootstrap.js +61 -127
- 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 +20 -54
- 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 +28 -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 +13 -31
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +10 -26
- package/dist/contract.js.map +1 -1
- 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 +6 -36
- package/dist/declare.d.ts.map +1 -1
- package/dist/declare.js +4 -29
- 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 +14 -63
- package/dist/descriptor/frond.d.ts.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 +8 -7
- package/dist/dispatch/StorageGuard.d.ts.map +1 -1
- package/dist/dispatch/StorageGuard.js +61 -18
- 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/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 +4 -38
- 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 +28 -127
- package/dist/scan/scanner.js.map +1 -1
- package/dist/scan/statement.d.ts.map +1 -1
- package/dist/scan/statement.js +4 -30
- package/dist/scan/statement.js.map +1 -1
- package/dist/source.d.ts +41 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +10 -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 +146 -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/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 +3 -15
- package/src/boot/auth.ts +2 -12
- package/src/boot/boot.ts +5 -20
- package/src/boot/bootstrap.ts +69 -136
- package/src/boot/card.ts +4 -33
- package/src/boot/frame.ts +28 -62
- 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 +28 -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 +15 -39
- 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 +8 -39
- package/src/define.ts +2 -3
- package/src/descriptor/Fronds.ts +6 -31
- package/src/descriptor/frond.ts +14 -63
- 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 +70 -18
- 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/conventions.ts +6 -40
- package/src/scan/emit.ts +4 -38
- package/src/scan/handler-parser.ts +27 -96
- package/src/scan/result.ts +3 -16
- package/src/scan/scanner.ts +27 -126
- package/src/scan/statement.ts +4 -30
- package/src/source.ts +44 -0
- package/src/storage.ts +14 -234
- package/src/store.ts +152 -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
package/src/scan/statement.ts
CHANGED
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The scan, written as the STATEMENT an author would have written.
|
|
3
|
-
*
|
|
4
|
-
* A scan runs at build and the app runs elsewhere — two runtimes, no shared memory — so
|
|
5
|
-
* something has to cross. What used to cross was the scan RESULT: every operation's
|
|
6
|
-
* TypeScript signature re-serialized as JSON, beside the five CRUD ops `Crud.__ops`
|
|
7
|
-
* declares at runtime anyway. A second writing of what the classes carry, and one that
|
|
8
|
-
* drifts, since nothing compares the two.
|
|
9
|
-
*
|
|
10
|
-
* What crosses here is what `fronds.ts` holds: imports that bring the ORIGINAL classes
|
|
11
|
-
* back, and `frond()` calls. Everything else `frond()` derives — `Post` is stored as
|
|
12
|
-
* `post`, `PostHandler` answers at `post`, a computed field IS a method, and
|
|
13
|
-
* `Presenter(Post)` keeps its subject. Only what TypeScript erases is named: a
|
|
14
|
-
* constructor's parameter types (`deps`), and the surface a handler answers on, which the
|
|
15
|
-
* scan read from its directory.
|
|
16
|
-
*
|
|
17
|
-
* Measured on demos/nuxt-blog: 102 lines of scan against 31 of statement, and the same app
|
|
18
|
-
* — same rows, same computed fields, and no `typescript` loaded at boot.
|
|
19
|
-
*/
|
|
1
|
+
/** The scan, written as the STATEMENT an author would have written. */
|
|
20
2
|
import { relative } from 'node:path';
|
|
21
3
|
import type { FrondDescriptor } from '../descriptor/frond.js';
|
|
22
4
|
import type { ScanResult } from './result.js';
|
|
@@ -24,17 +6,9 @@ import type { ScanResult } from './result.js';
|
|
|
24
6
|
type Live = { name?: string };
|
|
25
7
|
|
|
26
8
|
/**
|
|
27
|
-
* A file becomes the specifier the PROJECT already uses for it: `@fronds/blog/…`, the
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* A relative path would name the same file by a second route, and two routes to one module
|
|
32
|
-
* are two modules — so `Post` would not equal `Post` and every identity check would fail
|
|
33
|
-
* quietly. It also puts the file outside what the alias resolves, which is what made Node,
|
|
34
|
-
* not the bundler, read the frond's `.ts` and answer 500 on `../entities/Post.js`.
|
|
35
|
-
*
|
|
36
|
-
* `.js`, because the source is `.ts` and the project spells a TypeScript source that way
|
|
37
|
-
* everywhere else — the alias resolves through the same rule as a hand-written import.
|
|
9
|
+
* A file becomes the specifier the PROJECT already uses for it: `@fronds/blog/…`, the import scope
|
|
10
|
+
* a handler names its neighbour by, and which the Nuxt module registers as an alias for every
|
|
11
|
+
* frond it found.
|
|
38
12
|
*/
|
|
39
13
|
function specifierOf(filePath: string, frond: FrondDescriptor): string {
|
|
40
14
|
const inside = relative(frond.source.path, filePath).replace(/\.tsx?$/, '.js');
|
package/src/source.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Registry, type SchemaView } from '@fougere/schema';
|
|
2
|
+
import type { StorageFactory } from './storage.js';
|
|
3
|
+
|
|
4
|
+
/** A place rows live, whatever realizes it. */
|
|
5
|
+
export interface Source {
|
|
6
|
+
storageFactory: StorageFactory;
|
|
7
|
+
/** Bring the shape of what lives here up to date. */
|
|
8
|
+
migrate?(view: SourceView): Promise<void>;
|
|
9
|
+
/** Run `fn` as ONE unit of work, with a factory bound to it. */
|
|
10
|
+
transacted?<R>(fn: (factory: StorageFactory) => Promise<R>): Promise<R>;
|
|
11
|
+
close?(): Promise<void>;
|
|
12
|
+
/** What distinguishes it when a query is reported. */
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The app as ONE source sees it. */
|
|
17
|
+
export interface SourceView {
|
|
18
|
+
fronds: readonly { readonly name: string; readonly entities: readonly { readonly name: string }[] }[];
|
|
19
|
+
/** The auth provider's own entities, when they ride with this source. */
|
|
20
|
+
auth?: unknown;
|
|
21
|
+
elsewhere: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** What a config file can carry about a source — values, never a live driver. */
|
|
25
|
+
export interface SourceConfig {
|
|
26
|
+
/** The adapter that realizes it. Absent means the conventional one. */
|
|
27
|
+
source?: string;
|
|
28
|
+
/** The entities whose rows live here. Absent on the default source: it holds the rest. */
|
|
29
|
+
entities?: string[];
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Which adapter answers a source name — one per process, no subject to hold. */
|
|
34
|
+
class SourceRegistry extends Registry<(conf: SourceConfig) => Source> {
|
|
35
|
+
/** The source this name stands for, built from the config entry that named it. */
|
|
36
|
+
open(name: string, conf: SourceConfig, path?: string): Source {
|
|
37
|
+
return this.resolve(name, path)(conf);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const Sources = new SourceRegistry(
|
|
42
|
+
'source',
|
|
43
|
+
'import the adapter that answers it, or call Sources.register(name, build)',
|
|
44
|
+
);
|
package/src/storage.ts
CHANGED
|
@@ -24,9 +24,8 @@ export interface ListOptions {
|
|
|
24
24
|
count?: boolean;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Equality criteria, field by field — `{ orderId: '…' }`. Named rather than spread
|
|
28
|
-
*
|
|
29
|
-
* filter. `listBy(criteria)` is the same thing said as an intention.
|
|
27
|
+
* Equality criteria, field by field — `{ orderId: '…' }`. Named rather than spread across the
|
|
28
|
+
* options so an unknown key stays ignored instead of silently becoming a filter.
|
|
30
29
|
*/
|
|
31
30
|
where?: Record<string, unknown>;
|
|
32
31
|
}
|
|
@@ -41,12 +40,7 @@ export interface ListResult<T> extends Array<T> {
|
|
|
41
40
|
hasMore?: boolean;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
/**
|
|
45
|
-
* The keys `list()` answers to. Anything else is a mistake, and saying so is the point:
|
|
46
|
-
* the façade refuses an unknown key in a CLIENT's input (`Unknown field`) — this applies the
|
|
47
|
-
* same rule to the framework's own arguments. `list({ orderId })` used to be accepted and
|
|
48
|
-
* the filter dropped, so a one-to-many relation quietly returned the whole table.
|
|
49
|
-
*/
|
|
43
|
+
/** The keys `list()` answers to. */
|
|
50
44
|
const LIST_OPTION_KEYS = [
|
|
51
45
|
'limit', 'offset', 'page', 'after', 'orderBy', 'order', 'count', 'where', 'select',
|
|
52
46
|
] as const;
|
|
@@ -73,83 +67,27 @@ export interface Storage<T = Record<string, unknown>> {
|
|
|
73
67
|
list(options?: ListOptions & SelectOption): Promise<ListResult<T>>;
|
|
74
68
|
findById(id: string, options?: SelectOption): Promise<T | undefined>;
|
|
75
69
|
/**
|
|
76
|
-
* Read by criteria — `findBy({ email })` for the one, `findAllBy({ orderId })` for the
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* Both existed on the SQL implementation from the start and neither was declared here.
|
|
80
|
-
* A port that hides what it offers is a port nobody can use: `auth-better` cast its way
|
|
81
|
-
* in (`storage as StorageWithFindBy`), a presenter that needed the lines of an order read the
|
|
82
|
-
* whole table instead, and the GraphQL relation resolver passed criteria to `list()`
|
|
83
|
-
* — which drops what it does not know.
|
|
70
|
+
* Read by criteria — `findBy({ email })` for the one, `findAllBy({ orderId })` for the many,
|
|
71
|
+
* which is what a one-to-many relation *is*.
|
|
84
72
|
*/
|
|
85
73
|
findBy(criteria: Partial<T> | Record<string, unknown>, options?: SelectOption): Promise<T | undefined>;
|
|
86
74
|
findAllBy(criteria: Partial<T> | Record<string, unknown>, options?: SelectOption): Promise<T[]>;
|
|
87
75
|
/**
|
|
88
|
-
* Read a SET of rows by their key, in one go — the gesture every other one was
|
|
89
|
-
* being bent into.
|
|
90
|
-
*
|
|
91
|
-
* `findAllBy` compares with `=`, so a list of ids could not be passed to it; the
|
|
92
|
-
* only way to read N rows was `list()` then filter in memory, which is what 14 of
|
|
93
|
-
* 14 handlers of a real app measured on 2026-08-14 were doing. It is invisible on
|
|
94
|
-
* SQLite and it is the whole table the day the rows are not local.
|
|
95
|
-
*
|
|
96
|
-
* It answers a MAP, because the caller holds keys and not positions: a page zips
|
|
97
|
-
* against it by `get(row.authorId)`, a miss is the absence of a key, and a repeated
|
|
98
|
-
* key is one entry. The list form this replaced promised that zip and could not
|
|
99
|
-
* keep it — dropping a miss shifts every later position — so each caller rebuilt
|
|
100
|
-
* the very index the implementation had just thrown away.
|
|
76
|
+
* Read a SET of rows by their key, in one go — the gesture every other one was being bent into.
|
|
101
77
|
*/
|
|
102
78
|
findByKeys(ids: readonly string[], options?: SelectOption): Promise<Map<string, T>>;
|
|
103
|
-
/**
|
|
104
|
-
* Its dual: the rows that point AT each of these keys, grouped by the one they point at.
|
|
105
|
-
*
|
|
106
|
-
* `findByKeys` answers the side a row designates — one each, at most. This answers the
|
|
107
|
-
* side that designates the row — several each, and a key with none is simply absent.
|
|
108
|
-
* Together they are both directions of a relation, each in one query.
|
|
109
|
-
*
|
|
110
|
-
* Written because the page-shaped gesture only covered one of them: a presenter holding
|
|
111
|
-
* its page and wanting "the items of these lists" had no vocabulary for it and read the
|
|
112
|
-
* whole table instead, measured on a real app. `field` names the foreign key on THIS
|
|
113
|
-
* entity, the one carrying the value.
|
|
114
|
-
*/
|
|
79
|
+
/** The dual of `findByKeys`: many rows per key, not one. */
|
|
115
80
|
findAllByKeys(field: string, keys: readonly string[], options?: SelectOption): Promise<Map<string, T[]>>;
|
|
116
81
|
create(input: Partial<T>, options?: SelectOption): Promise<T>;
|
|
117
|
-
/**
|
|
118
|
-
* Write the row, or make the existing one look like this — one statement.
|
|
119
|
-
*
|
|
120
|
-
* What an import needs and the port did not have: `create` throws on the second run,
|
|
121
|
-
* so re-reading a source meant deleting first. Measured pulling an API twice.
|
|
122
|
-
*
|
|
123
|
-
* The key and the creation stamps survive an overwrite — a row keeps the moment it
|
|
124
|
-
* appeared. An engine with no upsert clause refuses by name rather than emulating
|
|
125
|
-
* one with a read in front, which would promise an atomicity it has not got.
|
|
126
|
-
*/
|
|
82
|
+
/** Write the row, or make the existing one look like this — one statement. */
|
|
127
83
|
upsert(input: Partial<T>, options?: SelectOption): Promise<T>;
|
|
128
|
-
/**
|
|
129
|
-
* A whole page in one statement — what an import writes through.
|
|
130
|
-
*
|
|
131
|
-
* Row by row, 500 rows were 500 statements (measured); the shape of an import is a
|
|
132
|
-
* page, so the write is one too. Answers how many rows were written rather than the
|
|
133
|
-
* rows: `create` hands back the complete row because a caller acts on it, and an
|
|
134
|
-
* import acts on none of them — re-reading a page for a symmetry nobody uses would
|
|
135
|
-
* double the work.
|
|
136
|
-
*/
|
|
84
|
+
/** A whole page in one statement — what an import writes through. */
|
|
137
85
|
upsertAll(inputs: readonly Partial<T>[], options?: SelectOption): Promise<number>;
|
|
138
86
|
update(id: string, input: Partial<T>, options?: SelectOption): Promise<T>;
|
|
139
87
|
delete(id: string): Promise<boolean>;
|
|
140
88
|
/** Returns a scoped storage that restricts all read results to the fields of the given schema. */
|
|
141
89
|
output(schema: SchemaView): Storage<T>;
|
|
142
|
-
/**
|
|
143
|
-
* What this storage wraps — the Kysely instance for the SQL one, something else elsewhere.
|
|
144
|
-
*
|
|
145
|
-
* Every judge sits on the storage's own methods, so a statement issued here meets none of
|
|
146
|
-
* them: a value the entity refuses lands in the table without a word. It is the port's
|
|
147
|
-
* own escape hatch rather than a handle on the side, so it keeps the scope the container
|
|
148
|
-
* gave you — `productStorage.client` reaches the products, not the whole database.
|
|
149
|
-
*
|
|
150
|
-
* `unknown` on purpose: the client belongs to the implementation, and narrowing it is
|
|
151
|
-
* the caller saying out loud which one they are standing on.
|
|
152
|
-
*/
|
|
90
|
+
/** What this storage wraps — the Kysely instance for the SQL one, something else elsewhere. */
|
|
153
91
|
readonly client: unknown;
|
|
154
92
|
}
|
|
155
93
|
|
|
@@ -159,118 +97,12 @@ export interface Storage<T = Record<string, unknown>> {
|
|
|
159
97
|
*/
|
|
160
98
|
export type StorageFactory = (entity: SchemaView, name: string) => Storage;
|
|
161
99
|
|
|
162
|
-
/**
|
|
163
|
-
* A place rows live, whatever realizes it.
|
|
164
|
-
*
|
|
165
|
-
* `StorageFactory` plus its lifecycle, which is why it lives beside it. Three of the four
|
|
166
|
-
* gestures are optional and ABSENCE IS THE ANSWER: no `transacted` and a frame compensates
|
|
167
|
-
* instead of transacting (`boot/together.ts`), no `migrate` and there is no shape to bring
|
|
168
|
-
* up to date. What a source is MADE OF is not here — `adapter/sql` states `dialect`, `db`
|
|
169
|
-
* and `sink` on its own `SqlSource`, reached by narrowing, the rule {@link Storage.client}
|
|
170
|
-
* already obeys one level down.
|
|
171
|
-
*/
|
|
172
|
-
export interface Source {
|
|
173
|
-
storageFactory: StorageFactory;
|
|
174
|
-
/** Bring the shape of what lives here up to date. */
|
|
175
|
-
migrate?(view: SourceView): Promise<void>;
|
|
176
|
-
/** Run `fn` as ONE unit of work, with a factory bound to it. */
|
|
177
|
-
transacted?<R>(fn: (factory: StorageFactory) => Promise<R>): Promise<R>;
|
|
178
|
-
close?(): Promise<void>;
|
|
179
|
-
/** What distinguishes it when a query is reported. */
|
|
180
|
-
name?: string;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* The app as ONE source sees it: what lives there, and the NAMES of what does not.
|
|
185
|
-
*
|
|
186
|
-
* The second half is what lets a DDL stop lying. A batch holding every entity cannot tell
|
|
187
|
-
* a cross-source target from a typo, so a `ref()` falls back to a derived table name and
|
|
188
|
-
* the constraint is emitted against a table that may not exist. `elsewhere` says which
|
|
189
|
-
* misses are legitimate — and a target in neither list is a mistake, out loud.
|
|
190
|
-
*/
|
|
191
|
-
export interface SourceView {
|
|
192
|
-
fronds: readonly { readonly name: string; readonly entities: readonly { readonly name: string }[] }[];
|
|
193
|
-
/** The auth provider's own entities, when they ride with this source. */
|
|
194
|
-
auth?: unknown;
|
|
195
|
-
elsewhere: readonly string[];
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* What a config file can carry about a source — values, never a live driver.
|
|
200
|
-
*
|
|
201
|
-
* `source` names the ADAPTER, and everything else belongs to the adapter it named:
|
|
202
|
-
* `adapter/sql` reads `dialect`, a file one reads `path`. That is the shape
|
|
203
|
-
* `EntityAdapters` already has — addressed by adapter, and what sits below is the
|
|
204
|
-
* adapter's own. Which is why the level `schema` owns is not repeated here.
|
|
205
|
-
*/
|
|
206
|
-
export interface SourceConfig {
|
|
207
|
-
/** The adapter that realizes it. Absent means the conventional one. */
|
|
208
|
-
source?: string;
|
|
209
|
-
/** The entities whose rows live here. Absent on the default source: it holds the rest. */
|
|
210
|
-
entities?: string[];
|
|
211
|
-
[key: string]: unknown;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Which adapter answers a source name — one per process, no subject to hold.
|
|
216
|
-
*
|
|
217
|
-
* The shape `Generators` already has, and the refusal `resolveStorage` already made: only
|
|
218
|
-
* `sqlite` could be resolved from its name, because it was the one driver `defaults`
|
|
219
|
-
* depended on. That refusal had no owner — it lived in the package that happened to import
|
|
220
|
-
* the driver — so a second adapter had nowhere to say it exists. Here it does, and an
|
|
221
|
-
* unknown name is refused naming what this process answers.
|
|
222
|
-
*
|
|
223
|
-
* Registered at IMPORT by each adapter, so nothing central lists them.
|
|
224
|
-
*/
|
|
225
|
-
export class Sources {
|
|
226
|
-
private static readonly registry = new Map<string, (conf: SourceConfig) => Source>();
|
|
227
|
-
|
|
228
|
-
static register(name: string, build: (conf: SourceConfig) => Source): void {
|
|
229
|
-
this.registry.set(name, build);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** The source this name stands for, refused by name when nothing does. */
|
|
233
|
-
static resolve(name: string, conf: SourceConfig): Source {
|
|
234
|
-
const build = this.registry.get(name);
|
|
235
|
-
if (build) return build(conf);
|
|
236
|
-
|
|
237
|
-
throw new Error(
|
|
238
|
-
`Unknown source '${name}' — import the adapter that answers it, or register one with `
|
|
239
|
-
+ `Sources.register('${name}', build). This process answers `
|
|
240
|
-
+ `${[...this.registry.keys()].join(', ') || 'nothing yet'}.`,
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/** Whether a name is answered, for a caller that must not throw to find out. */
|
|
245
|
-
static answers(name: string): boolean {
|
|
246
|
-
return this.registry.has(name);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/** What this process answers — what a refusal elsewhere prints. */
|
|
250
|
-
static answered(): string[] {
|
|
251
|
-
return [...this.registry.keys()];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Container key of an entity's storage — 'reading' → 'ReadingStorage'.
|
|
257
|
-
*
|
|
258
|
-
* The twin of {@link repositoryKeyOf} and of `facadeKeyOf`: the key of a thing
|
|
259
|
-
* lives with the thing. This one was spelled by hand in four places in
|
|
260
|
-
* `bootstrap.ts` and a fifth in `scanner.ts` — where the SCAN derives what a
|
|
261
|
-
* constructor asks for. Two readers of one convention, neither of them naming it,
|
|
262
|
-
* so a rename would have moved one and left the other resolving to nothing.
|
|
263
|
-
*/
|
|
100
|
+
/** Container key of an entity's storage — 'reading' → 'ReadingStorage'. */
|
|
264
101
|
export function storageKeyOf(entity: string): string {
|
|
265
102
|
return `${upperFirst(entity)}${HELD}`;
|
|
266
103
|
}
|
|
267
104
|
|
|
268
|
-
/**
|
|
269
|
-
* The entity behind a storage key, or `undefined` when the key is not one.
|
|
270
|
-
*
|
|
271
|
-
* The dual of the line above, for the reason `membersOfTogetherKey` is. `known` is what the
|
|
272
|
-
* suffix alone cannot answer: `FileStorage` is an ordinary class name.
|
|
273
|
-
*/
|
|
105
|
+
/** The entity behind a storage key, or `undefined` when the key is not one. */
|
|
274
106
|
export function entityOfStorageKey(key: string, known: (entity: string) => boolean): string | undefined {
|
|
275
107
|
if (key.length <= HELD.length || !key.endsWith(HELD)) return undefined;
|
|
276
108
|
const entity = lowerFirst(key.slice(0, -HELD.length));
|
|
@@ -281,47 +113,7 @@ export function entityOfStorageKey(key: string, known: (entity: string) => boole
|
|
|
281
113
|
/** What a holder keeps, said in the key it is registered under. */
|
|
282
114
|
const HELD = 'Storage';
|
|
283
115
|
|
|
284
|
-
/**
|
|
285
|
-
* `Together<[Account, Ledger]>` — writes that stand or fall as one.
|
|
286
|
-
*
|
|
287
|
-
* `Storage` is the port whose every gesture is ONE statement, and one statement is
|
|
288
|
-
* atomic in every engine. This is the port whose unit is a BLOCK: what the callback did
|
|
289
|
-
* happens entirely, or not at all. Nothing else separates them — the arity of the unit
|
|
290
|
-
* of work is the whole distinction, which is why this lives here and not in a file of
|
|
291
|
-
* its own.
|
|
292
|
-
*
|
|
293
|
-
* ```ts
|
|
294
|
-
* constructor(private together: Together<[Account, Ledger]>) {}
|
|
295
|
-
*
|
|
296
|
-
* await this.together.run(async ([accounts, ledger]) => {
|
|
297
|
-
* await accounts.update(from, { balance: b - amount });
|
|
298
|
-
* await ledger.create({ from, to, amount });
|
|
299
|
-
* });
|
|
300
|
-
* ```
|
|
301
|
-
*
|
|
302
|
-
* **The second list is providers**, rebuilt inside the frame so that what THEY write is
|
|
303
|
-
* covered too — a `Mirror` writes its pages through `Storage<T>`, so naming it puts
|
|
304
|
-
* them under the same unwind, with no locator and no second injection path:
|
|
305
|
-
*
|
|
306
|
-
* ```ts
|
|
307
|
-
* constructor(private together: Together<[RateCard, Ledger], [RateMirror]>) {}
|
|
308
|
-
*
|
|
309
|
-
* await this.together.run(async ([rates, ledger], [mirror]) => { await mirror.refresh(); });
|
|
310
|
-
* ```
|
|
311
|
-
*
|
|
312
|
-
* Two lists rather than one, because in a signature an entity and a provider are both
|
|
313
|
-
* written as a class name and their instance types do not separate them — the type would
|
|
314
|
-
* have to guess, by looking for methods or for a brand, and both answers are worse than
|
|
315
|
-
* saying it. They are two different facts anyway: what the unwind covers, and what is
|
|
316
|
-
* rebuilt to make that true.
|
|
317
|
-
*
|
|
318
|
-
* **All or nothing is the promise; isolation is not.** On one engine the members are
|
|
319
|
-
* rebuilt over a transaction and the engine gives both. Across engines a transaction
|
|
320
|
-
* cannot exist, so the frame keeps the before-image of each write and replays the
|
|
321
|
-
* inverse in reverse order — the unwind holds, the isolation does not, and a reader
|
|
322
|
-
* between two writes sees the half. The boot says which of the two it built rather
|
|
323
|
-
* than letting the author assume the stronger one.
|
|
324
|
-
*/
|
|
116
|
+
/** `Together<[Account, Ledger]>` — writes that stand or fall as one. */
|
|
325
117
|
export interface Together<E extends readonly unknown[], P extends readonly unknown[] = []> {
|
|
326
118
|
run<R>(fn: (entities: { [K in keyof E]: Storage<E[K]> }, providers: P) => Promise<R>): Promise<R>;
|
|
327
119
|
}
|
|
@@ -329,12 +121,6 @@ export interface Together<E extends readonly unknown[], P extends readonly unkno
|
|
|
329
121
|
/**
|
|
330
122
|
* The container key of a frame — `[['Account', 'Ledger'], ['RateMirror']]` →
|
|
331
123
|
* 'Account+Ledger|RateMirrorTogether'.
|
|
332
|
-
*
|
|
333
|
-
* The DECLARED order, not a sorted one. The tuples are what the callback destructures, so
|
|
334
|
-
* reordering here would hand `[ledger, accounts]` to a signature that says the opposite —
|
|
335
|
-
* the type and the runtime disagreeing about the same line. Two orders of one frame are
|
|
336
|
-
* therefore two keys and two registrations; they cost nothing, a frame holding no state
|
|
337
|
-
* between runs.
|
|
338
124
|
*/
|
|
339
125
|
export function togetherKeyOf(entities: readonly string[], providers: readonly string[] = []): string {
|
|
340
126
|
const named = entities.map(upperFirst).join(SEPARATOR);
|
|
@@ -346,13 +132,7 @@ const SEPARATOR = '+';
|
|
|
346
132
|
/** Separates the two lists — what the unwind covers, and what is rebuilt to make it true. */
|
|
347
133
|
const KINDS = '|';
|
|
348
134
|
|
|
349
|
-
/**
|
|
350
|
-
* The members behind a frame key, or `undefined` when the key is not one.
|
|
351
|
-
*
|
|
352
|
-
* The dual of the line above, and here for the reason `factOfEmitKey` is: the boot reads
|
|
353
|
-
* the key back out of a handler's `deps` to know which frames to build. A key and the way
|
|
354
|
-
* to undo it belong together — the pair that is split is the pair that drifts.
|
|
355
|
-
*/
|
|
135
|
+
/** The members behind a frame key, or `undefined` when the key is not one. */
|
|
356
136
|
export function membersOfTogetherKey(key: string): { entities: string[]; providers: string[] } | undefined {
|
|
357
137
|
if (key.length <= FRAME.length || !key.endsWith(FRAME)) return undefined;
|
|
358
138
|
const [entities, providers = ''] = key.slice(0, -FRAME.length).split(KINDS);
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/** The thirteen gestures of {@link Storage}, derived from four. */
|
|
2
|
+
import { applyCreate, applyUpdate, Lifecycle, Role, type SchemaView } from '@fougere/schema';
|
|
3
|
+
import type { Storage, StorageFactory } from './storage.js';
|
|
4
|
+
|
|
5
|
+
/** One instance, as every realization hands it over. */
|
|
6
|
+
export type Values = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
/** Instances addressed by key — what an adapter supplies, and all of it. */
|
|
9
|
+
export interface Store {
|
|
10
|
+
get(key: string): Promise<Values | undefined>;
|
|
11
|
+
has(key: string): Promise<boolean>;
|
|
12
|
+
set(key: string, values: Values): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<boolean>;
|
|
14
|
+
/** Everything it holds. A realization that cannot answer this cheaply says so in its doc. */
|
|
15
|
+
all(): Promise<Values[]>;
|
|
16
|
+
/** Handed on as `Storage.client` — the Map, the directory, whatever it is. */
|
|
17
|
+
readonly client: unknown;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function storageOver(open: (entity: SchemaView, name: string) => Store): StorageFactory {
|
|
21
|
+
return (entity: SchemaView, name: string): Storage => {
|
|
22
|
+
const fields = entity.getFields();
|
|
23
|
+
const store = open(entity, name);
|
|
24
|
+
const pk = Object.entries(fields).find(([, field]) => Role.of(field).isPrimary)?.[0] ?? 'id';
|
|
25
|
+
// `Storage.findById(id: string)` — but a key can hold a number, and a Map keyed on
|
|
26
|
+
// `1` does not answer `'1'`. SQL never had the question; here the divergence was
|
|
27
|
+
// silent and only on this storage.
|
|
28
|
+
const keyOf = (value: unknown) => String(value);
|
|
29
|
+
// Same contract as SQL: a criterion may name a SET, and an empty set matches nothing.
|
|
30
|
+
const matches = (values: Record<string, unknown>, criteria: Record<string, unknown>) =>
|
|
31
|
+
Object.entries(criteria).every(([key, value]) => Array.isArray(value)
|
|
32
|
+
? value.some((v) => Object.is(values[key], v))
|
|
33
|
+
: Object.is(values[key], value));
|
|
34
|
+
|
|
35
|
+
// A store holds whole instances and reads them whole, so the scope SQL puts in its
|
|
36
|
+
// SELECT is applied here on the way out. Same set of gestures either way: everything
|
|
37
|
+
// that hands back an instance, which leaves `delete` and `upsertAll` alone.
|
|
38
|
+
const scoped = (selected?: Set<string>): Storage => {
|
|
39
|
+
const pick = (values: Values): Values => (selected
|
|
40
|
+
? Object.fromEntries(Object.entries(values).filter(([key]) => selected.has(key)))
|
|
41
|
+
: values);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
client: store.client,
|
|
45
|
+
async list(options?: any) {
|
|
46
|
+
let items = await store.all();
|
|
47
|
+
if (options?.where) items = items.filter((values) => matches(values, options.where));
|
|
48
|
+
// Held before the page is cut, and after the filter: `total` answers "how many
|
|
49
|
+
// match", which is what a paginator divides. Reading `store.size` at the end
|
|
50
|
+
// answered a different question — everything the store holds, including the ones
|
|
51
|
+
// the filter exists to keep out of this caller's sight.
|
|
52
|
+
const matching = items.length;
|
|
53
|
+
const limit = options?.limit;
|
|
54
|
+
const offset = options?.page && limit ? (options.page - 1) * limit : options?.offset ?? 0;
|
|
55
|
+
if (offset > 0) items = items.slice(offset);
|
|
56
|
+
const hasMore = limit ? items.length > limit : false;
|
|
57
|
+
if (limit) items = items.slice(0, limit);
|
|
58
|
+
// The cursor is read before the scope cuts: a view that drops the key still
|
|
59
|
+
// pages, the way it does over SQL.
|
|
60
|
+
const endCursor = items.length > 0 ? String((items[items.length - 1] as any)[pk] ?? '') : undefined;
|
|
61
|
+
const result = items.map(pick) as any;
|
|
62
|
+
result.hasMore = hasMore;
|
|
63
|
+
result.endCursor = endCursor;
|
|
64
|
+
if (options?.count) result.total = matching;
|
|
65
|
+
return result;
|
|
66
|
+
},
|
|
67
|
+
async findById(id: string) {
|
|
68
|
+
const values = await store.get(keyOf(id));
|
|
69
|
+
return values && pick(values);
|
|
70
|
+
},
|
|
71
|
+
async findBy(criteria: Record<string, unknown>) {
|
|
72
|
+
const values = (await store.all()).find((held) => matches(held, criteria));
|
|
73
|
+
return values && pick(values);
|
|
74
|
+
},
|
|
75
|
+
async findAllBy(criteria: Record<string, unknown>) {
|
|
76
|
+
return (await store.all()).filter((values) => matches(values, criteria)).map(pick);
|
|
77
|
+
},
|
|
78
|
+
// Same contract as SQL: a map keyed by the primary key, a miss being an absent key.
|
|
79
|
+
async findByKeys(ids: readonly string[]) {
|
|
80
|
+
const found = new Map<string, Record<string, unknown>>();
|
|
81
|
+
for (const id of ids) {
|
|
82
|
+
const values = await store.get(keyOf(id));
|
|
83
|
+
if (values) found.set(String(id), pick(values));
|
|
84
|
+
}
|
|
85
|
+
return found;
|
|
86
|
+
},
|
|
87
|
+
// The dual, same contract as SQL: grouped by the value read off the instance.
|
|
88
|
+
async findAllByKeys(field: string, keys: readonly string[]) {
|
|
89
|
+
const grouped = new Map<string, Record<string, unknown>[]>();
|
|
90
|
+
if (keys.length === 0) return grouped;
|
|
91
|
+
const wanted = new Set(keys.map(String));
|
|
92
|
+
for (const values of await store.all()) {
|
|
93
|
+
const key = String(values[field]);
|
|
94
|
+
if (!wanted.has(key)) continue;
|
|
95
|
+
const bucket = grouped.get(key);
|
|
96
|
+
if (bucket) bucket.push(pick(values)); else grouped.set(key, [pick(values)]);
|
|
97
|
+
}
|
|
98
|
+
return grouped;
|
|
99
|
+
},
|
|
100
|
+
// Same contract as SQL: the key and the creation stamps survive an overwrite.
|
|
101
|
+
async upsert(input: Partial<Record<string, unknown>>) {
|
|
102
|
+
const values = applyCreate(fields, applyUpdate(fields, input));
|
|
103
|
+
const id = values[pk] as string | undefined;
|
|
104
|
+
if (id === undefined) throw new Error(`${name}.upsert(): no \`${pk}\` — an upsert needs the key it writes at.`);
|
|
105
|
+
const previous = await store.get(keyOf(id));
|
|
106
|
+
if (previous) {
|
|
107
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
108
|
+
if (key === pk || Lifecycle.of(field).stampedOnce) values[key] = previous[key];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
await store.set(keyOf(id), values);
|
|
112
|
+
return pick(values);
|
|
113
|
+
},
|
|
114
|
+
async upsertAll(inputs: readonly Partial<Record<string, unknown>>[]) {
|
|
115
|
+
for (const input of inputs) await this.upsert(input);
|
|
116
|
+
return inputs.length;
|
|
117
|
+
},
|
|
118
|
+
async create(input: Partial<Record<string, unknown>>) {
|
|
119
|
+
const values = applyCreate(fields, input);
|
|
120
|
+
const id = values[pk] as string | undefined;
|
|
121
|
+
// `primary(text())` declares no generator, so nothing fills the hole and the
|
|
122
|
+
// caller has to. Keying on `undefined` would let the second create overwrite the
|
|
123
|
+
// first, in silence — the old version hid this by inventing an `id` field the
|
|
124
|
+
// entity never declared.
|
|
125
|
+
if (id === undefined) {
|
|
126
|
+
throw new Error(`${name}.create: '${pk}' is the primary key and nothing supplied it — this entity declares no generator for it.`);
|
|
127
|
+
}
|
|
128
|
+
// A create is not an upsert. `Map.set` overwrites, so a second create under the
|
|
129
|
+
// same key answered "created" while destroying the previous instance — SQL answers a
|
|
130
|
+
// constraint violation, and a store that loses data silently is worse than one
|
|
131
|
+
// that fails.
|
|
132
|
+
if (await store.has(keyOf(id))) {
|
|
133
|
+
throw new Error(`${name}.create: '${pk}' ${JSON.stringify(id)} already exists.`);
|
|
134
|
+
}
|
|
135
|
+
await store.set(keyOf(id), values);
|
|
136
|
+
return pick(values);
|
|
137
|
+
},
|
|
138
|
+
async update(id: string, input: Partial<Record<string, unknown>>) {
|
|
139
|
+
const existing = await store.get(keyOf(id));
|
|
140
|
+
if (!existing) throw new Error(`Not found: ${id}`);
|
|
141
|
+
const updated = { ...existing, ...applyUpdate(fields, input), [pk]: existing[pk] };
|
|
142
|
+
await store.set(keyOf(id), updated);
|
|
143
|
+
return pick(updated);
|
|
144
|
+
},
|
|
145
|
+
async delete(id: string) { return await store.delete(keyOf(id)); },
|
|
146
|
+
output(schema: SchemaView) { return scoped(new Set(Object.keys(schema.getFields()))); },
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return scoped();
|
|
151
|
+
};
|
|
152
|
+
}
|