@fougere/core 0.3.0-alpha.0 → 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.
Files changed (176) hide show
  1. package/README.md +1 -1
  2. package/dist/boot/HandlerFacade.js +2 -2
  3. package/dist/boot/HandlerFacade.js.map +1 -1
  4. package/dist/boot/apply.d.ts.map +1 -1
  5. package/dist/boot/apply.js +2 -12
  6. package/dist/boot/apply.js.map +1 -1
  7. package/dist/boot/binding.js +4 -4
  8. package/dist/boot/binding.js.map +1 -1
  9. package/dist/boot/boot.d.ts +1 -1
  10. package/dist/boot/boot.d.ts.map +1 -1
  11. package/dist/boot/bootstrap.d.ts.map +1 -1
  12. package/dist/boot/bootstrap.js +14 -13
  13. package/dist/boot/bootstrap.js.map +1 -1
  14. package/dist/boot/frame.d.ts +1 -1
  15. package/dist/boot/frame.d.ts.map +1 -1
  16. package/dist/boot/frame.js +5 -14
  17. package/dist/boot/frame.js.map +1 -1
  18. package/dist/boot/ownership.d.ts.map +1 -1
  19. package/dist/boot/ownership.js +5 -5
  20. package/dist/boot/ownership.js.map +1 -1
  21. package/dist/boot/remote.d.ts.map +1 -1
  22. package/dist/boot/remote.js +2 -2
  23. package/dist/boot/remote.js.map +1 -1
  24. package/dist/boot/together.d.ts.map +1 -1
  25. package/dist/boot/together.js +6 -6
  26. package/dist/boot/together.js.map +1 -1
  27. package/dist/boot/types.d.ts +2 -2
  28. package/dist/boot/types.d.ts.map +1 -1
  29. package/dist/contract.d.ts +1 -1
  30. package/dist/contract.d.ts.map +1 -1
  31. package/dist/contract.js +3 -3
  32. package/dist/contract.js.map +1 -1
  33. package/dist/dispatch/InFlight.d.ts.map +1 -1
  34. package/dist/dispatch/InFlight.js.map +1 -1
  35. package/dist/dispatch/InputValidator.d.ts.map +1 -1
  36. package/dist/dispatch/InputValidator.js +3 -2
  37. package/dist/dispatch/InputValidator.js.map +1 -1
  38. package/dist/dispatch/OutputProjector.js +2 -2
  39. package/dist/dispatch/OutputProjector.js.map +1 -1
  40. package/dist/dispatch/StorageGuard.d.ts.map +1 -1
  41. package/dist/dispatch/StorageGuard.js +2 -2
  42. package/dist/dispatch/StorageGuard.js.map +1 -1
  43. package/dist/effective-operation.js +4 -5
  44. package/dist/effective-operation.js.map +1 -1
  45. package/dist/emit.d.ts +2 -2
  46. package/dist/emit.d.ts.map +1 -1
  47. package/dist/emit.js +2 -2
  48. package/dist/emit.js.map +1 -1
  49. package/dist/identity.js +1 -1
  50. package/dist/identity.js.map +1 -1
  51. package/dist/imports.d.ts +1 -1
  52. package/dist/imports.d.ts.map +1 -1
  53. package/dist/imports.js.map +1 -1
  54. package/dist/orm.d.ts.map +1 -1
  55. package/dist/orm.js +4 -4
  56. package/dist/orm.js.map +1 -1
  57. package/dist/prefab/collector.js +2 -2
  58. package/dist/prefab/collector.js.map +1 -1
  59. package/dist/prefab/mirror.d.ts.map +1 -1
  60. package/dist/prefab/mirror.js +2 -2
  61. package/dist/prefab/mirror.js.map +1 -1
  62. package/dist/prefab/presenter.d.ts.map +1 -1
  63. package/dist/prefab/presenter.js +2 -2
  64. package/dist/prefab/presenter.js.map +1 -1
  65. package/dist/prefab/repository.d.ts.map +1 -1
  66. package/dist/prefab/repository.js +2 -2
  67. package/dist/prefab/repository.js.map +1 -1
  68. package/dist/scan/emit.d.ts.map +1 -1
  69. package/dist/scan/emit.js +3 -2
  70. package/dist/scan/emit.js.map +1 -1
  71. package/dist/scan/handler-parser.d.ts +2 -0
  72. package/dist/scan/handler-parser.d.ts.map +1 -1
  73. package/dist/scan/handler-parser.js +52 -12
  74. package/dist/scan/handler-parser.js.map +1 -1
  75. package/dist/scan/scanner.d.ts.map +1 -1
  76. package/dist/scan/scanner.js +37 -10
  77. package/dist/scan/scanner.js.map +1 -1
  78. package/dist/verify.js +2 -2
  79. package/dist/verify.js.map +1 -1
  80. package/dist/wire/call.d.ts +7 -7
  81. package/dist/wire/call.d.ts.map +1 -1
  82. package/dist/wire/call.js +6 -6
  83. package/dist/wire/call.js.map +1 -1
  84. package/package.json +6 -4
  85. package/src/boot/AppLifecycle.ts +50 -0
  86. package/src/boot/Emissions.ts +263 -0
  87. package/src/boot/HandlerFacade.ts +242 -0
  88. package/src/boot/Lifecycle.ts +3 -0
  89. package/src/boot/ambient-port.ts +41 -0
  90. package/src/boot/ambient.als.ts +43 -0
  91. package/src/boot/ambient.queue.ts +75 -0
  92. package/src/boot/apply.ts +47 -0
  93. package/src/boot/auth.ts +55 -0
  94. package/src/boot/binding.ts +182 -0
  95. package/src/boot/boot.ts +125 -0
  96. package/src/boot/bootstrap.ts +839 -0
  97. package/src/boot/frame.ts +210 -0
  98. package/src/boot/ownership.ts +140 -0
  99. package/src/boot/ports.ts +66 -0
  100. package/src/boot/remote.ts +179 -0
  101. package/src/boot/seed.ts +158 -0
  102. package/src/boot/together.ts +215 -0
  103. package/src/boot/types.ts +261 -0
  104. package/src/builtins/config.ts +19 -0
  105. package/src/builtins/logger.ts +175 -0
  106. package/src/config-loader.ts +146 -0
  107. package/src/contract/Call.ts +18 -0
  108. package/src/contract/Invocation.ts +72 -0
  109. package/src/contract/RouteAddress.ts +49 -0
  110. package/src/contract.ts +74 -0
  111. package/src/crypto/encoding.ts +59 -0
  112. package/src/crypto/node.ts +30 -0
  113. package/src/crypto/port.ts +45 -0
  114. package/src/crypto/webcrypto.ts +55 -0
  115. package/src/define.ts +10 -0
  116. package/src/dispatch/ArgumentResolver.ts +17 -0
  117. package/src/dispatch/ArrayResult.ts +10 -0
  118. package/src/dispatch/DispatchEvent.ts +38 -0
  119. package/src/dispatch/DispatchLifecycle.ts +23 -0
  120. package/src/dispatch/DispatchPort.ts +6 -0
  121. package/src/dispatch/Dispatcher.ts +48 -0
  122. package/src/dispatch/InFlight.ts +49 -0
  123. package/src/dispatch/InputValidator.ts +28 -0
  124. package/src/dispatch/LocalRoutePolicy.ts +34 -0
  125. package/src/dispatch/OperationExecutor.ts +60 -0
  126. package/src/dispatch/OperationRoute.ts +18 -0
  127. package/src/dispatch/OutputProjector.ts +32 -0
  128. package/src/dispatch/OutputView.ts +9 -0
  129. package/src/dispatch/PresenterArgumentResolver.ts +25 -0
  130. package/src/dispatch/PresenterExecutor.ts +55 -0
  131. package/src/dispatch/RemoteRouteResolver.ts +21 -0
  132. package/src/dispatch/Route.ts +11 -0
  133. package/src/dispatch/RouteNotFoundError.ts +29 -0
  134. package/src/dispatch/RoutePolicy.ts +8 -0
  135. package/src/dispatch/RouteRegistry.ts +73 -0
  136. package/src/dispatch/RouteResolver.ts +7 -0
  137. package/src/dispatch/StorageGuard.ts +71 -0
  138. package/src/effective-operation.ts +597 -0
  139. package/src/emit.ts +90 -0
  140. package/src/entry/DynamicFacade.ts +28 -0
  141. package/src/entry/FacadeEntry.ts +32 -0
  142. package/src/entry/TransportEntry.ts +30 -0
  143. package/src/frond-config.ts +138 -0
  144. package/src/graph.ts +180 -0
  145. package/src/identity-keys.ts +43 -0
  146. package/src/identity.ts +235 -0
  147. package/src/imports.ts +123 -0
  148. package/src/index.ts +106 -0
  149. package/src/loader.ts +41 -0
  150. package/src/node.ts +28 -0
  151. package/src/orm.ts +252 -0
  152. package/src/prefab/collector.ts +36 -0
  153. package/src/prefab/crud.ts +188 -0
  154. package/src/prefab/mirror.ts +122 -0
  155. package/src/prefab/prefab.ts +46 -0
  156. package/src/prefab/presenter.ts +63 -0
  157. package/src/prefab/repository.ts +170 -0
  158. package/src/scan/Fronds.ts +73 -0
  159. package/src/scan/conventions.ts +122 -0
  160. package/src/scan/emit.ts +231 -0
  161. package/src/scan/frond.ts +235 -0
  162. package/src/scan/handler-parser.ts +818 -0
  163. package/src/scan/scan-cache.ts +115 -0
  164. package/src/scan/scanner.ts +758 -0
  165. package/src/verify.ts +193 -0
  166. package/src/wire/call.ts +369 -0
  167. package/src/wire/errors.ts +124 -0
  168. package/src/wire/http-error.ts +77 -0
  169. package/src/wire/invocation.ts +10 -0
  170. package/src/wire/loggerMiddleware.ts +29 -0
  171. package/src/wire/middleware.ts +47 -0
  172. package/src/wire/operation.ts +258 -0
  173. package/dist/boot/egress.d.ts +0 -8
  174. package/dist/boot/egress.d.ts.map +0 -1
  175. package/dist/boot/egress.js +0 -11
  176. package/dist/boot/egress.js.map +0 -1
@@ -0,0 +1,73 @@
1
+ import { RouteAddress } from '../contract/RouteAddress.js';
2
+ import type { Route } from './Route.js';
3
+ import type { RouteResolver } from './RouteResolver.js';
4
+
5
+ /** Exact registry of every operation the application can dispatch. */
6
+ export class RouteRegistry {
7
+ private readonly byAddress = new Map<string, Route>();
8
+ private readonly resolvers: RouteResolver[] = [];
9
+ private readonly pending = new Map<string, Promise<Route | undefined>>();
10
+
11
+ get size(): number {
12
+ return this.byAddress.size;
13
+ }
14
+
15
+ register(route: Route): void {
16
+ const key = route.address.key();
17
+ const existing = this.byAddress.get(key);
18
+ if (existing) {
19
+ throw new Error(
20
+ `Route collision at '${route.address}': ${existing.kind} and ${route.kind}`,
21
+ );
22
+ }
23
+ this.byAddress.set(key, route);
24
+ }
25
+
26
+ addResolver(resolver: RouteResolver): void {
27
+ this.resolvers.push(resolver);
28
+ }
29
+
30
+ find(address: RouteAddress): Route | undefined {
31
+ const exact = this.byAddress.get(address.key());
32
+ if (exact || address.surface === undefined) return exact;
33
+
34
+ const shared = this.byAddress.get(new RouteAddress({
35
+ entity: address.entity,
36
+ operation: address.operation,
37
+ }).key());
38
+ return shared?.kind === 'system' ? shared : undefined;
39
+ }
40
+
41
+ async resolve(address: RouteAddress): Promise<Route | undefined> {
42
+ const known = this.find(address);
43
+ if (known) return known;
44
+
45
+ const key = address.key();
46
+ const running = this.pending.get(key);
47
+ if (running) return running;
48
+
49
+ const resolution = this.resolveUnknown(address).finally(() => this.pending.delete(key));
50
+ this.pending.set(key, resolution);
51
+ return resolution;
52
+ }
53
+
54
+ private async resolveUnknown(address: RouteAddress): Promise<Route | undefined> {
55
+ for (const resolver of this.resolvers) {
56
+ const route = await resolver.resolve(address);
57
+ if (!route) continue;
58
+ this.register(route);
59
+ return route;
60
+ }
61
+ return undefined;
62
+ }
63
+
64
+ routes(): Route[] {
65
+ return [...this.byAddress.values()];
66
+ }
67
+
68
+ operationNames(entity: string, surface?: string): string[] {
69
+ return this.routes()
70
+ .filter((route) => route.address.entity === entity && route.address.surface === surface)
71
+ .map((route) => route.address.operation);
72
+ }
73
+ }
@@ -0,0 +1,7 @@
1
+ import type { RouteAddress } from '../contract/RouteAddress.js';
2
+ import type { Route } from './Route.js';
3
+
4
+ /** Resolves routes that cannot be known when the registry is built. */
5
+ export interface RouteResolver {
6
+ resolve(address: RouteAddress): Route | undefined | Promise<Route | undefined>;
7
+ }
@@ -0,0 +1,71 @@
1
+ import { ValueJudge, type Fields } from '@fougere/schema';
2
+ import { assertListOptions } from '../orm.js';
3
+ import { ErrorCode, FougereError } from '../wire/errors.js';
4
+
5
+ interface Writer {
6
+ create(...args: [Record<string, unknown>, ...unknown[]]): Promise<unknown>;
7
+ update(...args: [unknown, Record<string, unknown>, ...unknown[]]): Promise<unknown>;
8
+ }
9
+
10
+ /**
11
+ * Judges storage writes and list options without narrowing the ORM interface.
12
+ *
13
+ * Storage is a way out like the client surface, so what goes to it is judged too — the
14
+ * same rule `OutputProjector` applies on the other exit.
15
+ */
16
+ export class StorageGuard {
17
+ constructor(
18
+ private readonly fields: Fields,
19
+ private readonly entity: string,
20
+ ) {}
21
+
22
+ guard<T extends object>(orm: T): T {
23
+ const writer = orm as unknown as Writer;
24
+ if (typeof writer.create !== 'function' || typeof writer.update !== 'function') return orm;
25
+
26
+ const validation = this;
27
+ const guarded = Object.create(orm) as T & Writer;
28
+
29
+ guarded.create = async function (...args) {
30
+ validation.judge(args[0], 'create');
31
+ return writer.create.apply(this, args);
32
+ };
33
+
34
+ guarded.update = async function (...args) {
35
+ validation.judge(args[1], 'update');
36
+ return writer.update.apply(this, args);
37
+ };
38
+
39
+ const reader = orm as unknown as { list?: (...args: unknown[]) => unknown };
40
+ if (typeof reader.list === 'function') {
41
+ (guarded as unknown as typeof reader).list = async function (...args: unknown[]) {
42
+ assertListOptions(args[0] as object | undefined, validation.entity);
43
+ return (reader.list as Function).apply(this, args);
44
+ };
45
+ }
46
+
47
+ return guarded;
48
+ }
49
+
50
+ private judge(value: unknown, operation: string): void {
51
+ if (typeof value !== 'object' || value === null) return;
52
+
53
+ const errors: string[] = [];
54
+ for (const [key, item] of Object.entries(value as Record<string, unknown>)) {
55
+ const field = this.fields[key];
56
+ if (!field || item === undefined) continue;
57
+ const checked = ValueJudge.of(field).check(item);
58
+ if ('error' in checked) errors.push(`${key}: ${checked.error}`);
59
+ }
60
+
61
+ if (errors.length > 0) {
62
+ throw new FougereError({
63
+ code: ErrorCode.INTERNAL_ERROR,
64
+ message: `Refused on the way out — ${errors.join(', ')}`,
65
+ entity: this.entity,
66
+ operation,
67
+ details: errors,
68
+ });
69
+ }
70
+ }
71
+ }