@fougere/core 0.8.4-alpha.0 → 0.9.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 (112) hide show
  1. package/dist/boot/AppLifecycle.d.ts +8 -0
  2. package/dist/boot/AppLifecycle.d.ts.map +1 -1
  3. package/dist/boot/AppLifecycle.js.map +1 -1
  4. package/dist/boot/Emissions.d.ts +26 -0
  5. package/dist/boot/Emissions.d.ts.map +1 -1
  6. package/dist/boot/Emissions.js +143 -8
  7. package/dist/boot/Emissions.js.map +1 -1
  8. package/dist/boot/bootstrap.d.ts.map +1 -1
  9. package/dist/boot/bootstrap.js +77 -18
  10. package/dist/boot/bootstrap.js.map +1 -1
  11. package/dist/boot/hosted.d.ts.map +1 -1
  12. package/dist/boot/hosted.js +1 -0
  13. package/dist/boot/hosted.js.map +1 -1
  14. package/dist/boot/install.d.ts +2 -0
  15. package/dist/boot/install.d.ts.map +1 -1
  16. package/dist/boot/install.js +33 -4
  17. package/dist/boot/install.js.map +1 -1
  18. package/dist/boot/ports.d.ts +12 -3
  19. package/dist/boot/ports.d.ts.map +1 -1
  20. package/dist/boot/ports.js +51 -21
  21. package/dist/boot/ports.js.map +1 -1
  22. package/dist/boot/remote.d.ts.map +1 -1
  23. package/dist/boot/remote.js +3 -3
  24. package/dist/boot/remote.js.map +1 -1
  25. package/dist/boot/types.d.ts +16 -1
  26. package/dist/boot/types.d.ts.map +1 -1
  27. package/dist/builtin/LogLine.d.ts +38 -0
  28. package/dist/builtin/LogLine.d.ts.map +1 -0
  29. package/dist/builtin/LogLine.js +37 -0
  30. package/dist/builtin/LogLine.js.map +1 -0
  31. package/dist/builtin/logger.d.ts +57 -0
  32. package/dist/builtin/logger.d.ts.map +1 -1
  33. package/dist/builtin/logger.js +72 -17
  34. package/dist/builtin/logger.js.map +1 -1
  35. package/dist/config-loader.d.ts +2 -1
  36. package/dist/config-loader.d.ts.map +1 -1
  37. package/dist/config-loader.js.map +1 -1
  38. package/dist/contract.d.ts +3 -7
  39. package/dist/contract.d.ts.map +1 -1
  40. package/dist/contract.js +3 -4
  41. package/dist/contract.js.map +1 -1
  42. package/dist/conventions.d.ts +6 -0
  43. package/dist/conventions.d.ts.map +1 -1
  44. package/dist/conventions.js +3 -2
  45. package/dist/conventions.js.map +1 -1
  46. package/dist/declare.d.ts +11 -1
  47. package/dist/declare.d.ts.map +1 -1
  48. package/dist/declare.js +9 -0
  49. package/dist/declare.js.map +1 -1
  50. package/dist/descriptor/frond.d.ts +34 -0
  51. package/dist/descriptor/frond.d.ts.map +1 -1
  52. package/dist/descriptor/index.d.ts +1 -1
  53. package/dist/descriptor/index.d.ts.map +1 -1
  54. package/dist/descriptor/index.js +1 -1
  55. package/dist/descriptor/index.js.map +1 -1
  56. package/dist/dispatch/ArgumentResolver.d.ts.map +1 -1
  57. package/dist/dispatch/ArgumentResolver.js +6 -2
  58. package/dist/dispatch/ArgumentResolver.js.map +1 -1
  59. package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
  60. package/dist/dispatch/HandlerFacade.js +2 -2
  61. package/dist/dispatch/HandlerFacade.js.map +1 -1
  62. package/dist/effective-operation.d.ts.map +1 -1
  63. package/dist/effective-operation.js +11 -7
  64. package/dist/effective-operation.js.map +1 -1
  65. package/dist/frond-config.d.ts +24 -0
  66. package/dist/frond-config.d.ts.map +1 -1
  67. package/dist/frond-config.js.map +1 -1
  68. package/dist/index.d.ts +30 -33
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +14 -13
  71. package/dist/index.js.map +1 -1
  72. package/dist/wire/Invocation.d.ts +1 -3
  73. package/dist/wire/Invocation.d.ts.map +1 -1
  74. package/dist/wire/Invocation.js +1 -5
  75. package/dist/wire/Invocation.js.map +1 -1
  76. package/dist/wire/binding.d.ts +9 -0
  77. package/dist/wire/binding.d.ts.map +1 -1
  78. package/dist/wire/binding.js +11 -6
  79. package/dist/wire/binding.js.map +1 -1
  80. package/dist/wire/emit.d.ts +35 -3
  81. package/dist/wire/emit.d.ts.map +1 -1
  82. package/dist/wire/emit.js +26 -3
  83. package/dist/wire/emit.js.map +1 -1
  84. package/dist/wire/loggerMiddleware.d.ts.map +1 -1
  85. package/dist/wire/loggerMiddleware.js +6 -0
  86. package/dist/wire/loggerMiddleware.js.map +1 -1
  87. package/package.json +4 -4
  88. package/src/boot/AppLifecycle.ts +8 -0
  89. package/src/boot/Emissions.ts +176 -8
  90. package/src/boot/bootstrap.ts +89 -17
  91. package/src/boot/hosted.ts +3 -1
  92. package/src/boot/install.ts +35 -4
  93. package/src/boot/ports.ts +71 -27
  94. package/src/boot/remote.ts +3 -3
  95. package/src/boot/types.ts +16 -1
  96. package/src/builtin/LogLine.ts +38 -0
  97. package/src/builtin/logger.ts +109 -17
  98. package/src/config-loader.ts +2 -1
  99. package/src/contract.ts +5 -9
  100. package/src/conventions.ts +9 -2
  101. package/src/declare.ts +19 -1
  102. package/src/descriptor/frond.ts +31 -0
  103. package/src/descriptor/index.ts +1 -1
  104. package/src/dispatch/ArgumentResolver.ts +6 -2
  105. package/src/dispatch/HandlerFacade.ts +2 -2
  106. package/src/effective-operation.ts +13 -7
  107. package/src/frond-config.ts +20 -0
  108. package/src/index.ts +36 -50
  109. package/src/wire/Invocation.ts +2 -7
  110. package/src/wire/binding.ts +17 -6
  111. package/src/wire/emit.ts +58 -6
  112. package/src/wire/loggerMiddleware.ts +6 -0
@@ -11,6 +11,12 @@ type ParamSource =
11
11
  | { kind: 'collector'; typeName: string }
12
12
  /** `Fact<PostPublished>` — something that happened, not something a caller typed. */
13
13
  | { kind: 'fact'; factName: string }
14
+ /**
15
+ * `Pipe<PostPublished>` — the same thing, BEFORE it is final: what this op answers is
16
+ * the fact every subscriber then receives. The core has one such link already
17
+ * (`Emissions.stamped`, which realizes `created()`); this is the declared form of it.
18
+ */
19
+ | { kind: 'pipe'; factName: string }
14
20
  | { kind: 'param'; name: string; coerce?: 'number' | 'boolean' }
15
21
  | { kind: 'input' }
16
22
  | { kind: 'context' }
@@ -49,14 +55,19 @@ export function computeBindingPlan(
49
55
  // `authoruser` missed `authorUser` and fell through to branch 4, the request input.
50
56
  const typeKey = lowerFirst(typeName);
51
57
 
52
- // 0. Fact `Fact<X>` names itself, so nothing has to be known in advance. It comes
53
- // FIRST because branch 4 would otherwise hand it the caller's input under the name
54
- // of something that happened.
55
- const factOf = param.type.name === 'Fact' ? param.type.generics?.[0]?.name : undefined;
56
- if (factOf) {
58
+ // 0. Fact, or the same fact before it is final. Both name themselves, so nothing has
59
+ // to be known in advance, and they come FIRST because branch 4 would otherwise hand
60
+ // either the caller's input under the name of something that happened.
61
+ const announced = param.type.name === 'Fact' || param.type.name === 'Pipe'
62
+ ? param.type.generics?.[0]?.name
63
+ : undefined;
64
+ if (announced) {
57
65
  return {
58
66
  name: param.name,
59
- source: { kind: 'fact' as const, factName: lowerFirst(factOf) },
67
+ source: {
68
+ kind: (param.type.name === 'Pipe' ? 'pipe' : 'fact') as 'pipe' | 'fact',
69
+ factName: lowerFirst(announced),
70
+ },
60
71
  optional: param.optional ?? false,
61
72
  };
62
73
  }
package/src/wire/emit.ts CHANGED
@@ -1,32 +1,84 @@
1
1
  /** Announcing a fact — the half of the topology `remotes:` never covered. */
2
2
  import { lowerFirst } from '@fougere/schema';
3
3
 
4
- /** What an emitter injects. */
5
- export type Emit<T> = (fact: T) => Promise<void>;
4
+ /**
5
+ * What an emitter injects. PARTIAL, because announcing is what REALIZES the fact's
6
+ * `lifecycle.create` — an `at: created()` is stamped by `Emissions`, so requiring it from
7
+ * the announcer made every emitter cast its way past its own type. A missing field is
8
+ * still refused, by the judge that reads the fact at announce time.
9
+ */
10
+ export type Emit<T, A = never> = (fact: Partial<T>) => Promise<A[]>;
6
11
 
7
- /** What a subscriber accepts — and what it PROMISES about itself. */
12
+ /**
13
+ * What a subscriber accepts — and what it PROMISES about itself.
14
+ *
15
+ * What it ANSWERS is its own to choose: `Promise<void>` says nothing, any other type is an
16
+ * opinion. It reaches an announcer that asked for one — `Emit<CanBook, Verdict>` — and
17
+ * nobody otherwise, since a plain announcement does not wait.
18
+ */
8
19
  export type Fact<T> = T;
9
20
 
21
+ /**
22
+ * The same fact, BEFORE it is final: an op taking one ANSWERS the value every subscriber
23
+ * then receives, so it is the declared form of what `Emissions.stamped` already does for
24
+ * `created()`.
25
+ *
26
+ * Not a second announcement and not a chain a subscriber joins: the links run once, before
27
+ * anyone is handed anything, which is what keeps a fact the same for every reader.
28
+ */
29
+ export type Pipe<T> = T;
30
+
10
31
  /** The container key of an emission — THE one place that spells the format. */
11
32
  export function emitKeyOf(fact: string): string {
12
33
  return `${lowerFirst(fact)}Emit`;
13
34
  }
14
35
 
36
+ /**
37
+ * The key of an emission that WAITS — `Emit<CanBook, Verdict>`, where the second type is
38
+ * the whole difference. A separate key because they are two functions: one hands the fact
39
+ * over and returns, the other waits for every subscriber and gives back what they said.
40
+ */
41
+ export function awaitKeyOf(fact: string): string {
42
+ return `${lowerFirst(fact)}Await`;
43
+ }
44
+
15
45
  const SUFFIX = 'Emit';
46
+ const AWAIT_SUFFIX = 'Await';
16
47
 
17
48
  /** The fact behind an emission key, or `undefined` when the key is not one. */
18
49
  export function factOfEmitKey(key: string): string | undefined {
19
- return key.length > SUFFIX.length && key.endsWith(SUFFIX)
20
- ? key.slice(0, -SUFFIX.length)
50
+ return behind(key, SUFFIX);
51
+ }
52
+
53
+ /** The fact behind an awaited emission's key — the dual, read the same way. */
54
+ export function factOfAwaitKey(key: string): string | undefined {
55
+ return behind(key, AWAIT_SUFFIX);
56
+ }
57
+
58
+ function behind(key: string, suffix: string): string | undefined {
59
+ return key.length > suffix.length && key.endsWith(suffix)
60
+ ? key.slice(0, -suffix.length)
21
61
  : undefined;
22
62
  }
23
63
 
24
64
  /** What a set of handlers ANNOUNCES — `Emit<T>` read back out of their dependencies. */
25
65
  export function factsAnnouncedBy(handlers: readonly { deps: readonly string[] }[]): string[] {
66
+ return factsIn(handlers, factOfEmitKey);
67
+ }
68
+
69
+ /** What a set of handlers announces AND WAITS FOR — the same reading, one suffix apart. */
70
+ export function factsAwaitedBy(handlers: readonly { deps: readonly string[] }[]): string[] {
71
+ return factsIn(handlers, factOfAwaitKey);
72
+ }
73
+
74
+ function factsIn(
75
+ handlers: readonly { deps: readonly string[] }[],
76
+ behindKey: (key: string) => string | undefined,
77
+ ): string[] {
26
78
  return [...new Set(
27
79
  handlers
28
80
  .flatMap((handler) => handler.deps)
29
- .map(factOfEmitKey)
81
+ .map(behindKey)
30
82
  .filter((fact): fact is string => fact !== undefined),
31
83
  )];
32
84
  }
@@ -3,10 +3,16 @@
3
3
  */
4
4
  import type { AppMiddleware } from './middleware.js';
5
5
  import type { Logger } from '../builtin/logger.js';
6
+ import { CARRIES_LINE } from '../builtin/LogLine.js';
6
7
 
7
8
  /** Create a logger middleware that logs operation entry, exit, and errors. */
8
9
  export function loggerMiddleware(logger: Logger): AppMiddleware {
9
10
  return async (ctx, next) => {
11
+ // An op that CARRIES a line writes none: it is dispatched, so logging it announces a
12
+ // line inside the announcement of one — `Emission cycle: logLine → logLine`, which is
13
+ // the emission catching what `onLog`'s try/catch used to swallow.
14
+ if (CARRIES_LINE.has(ctx.entity)) return next();
15
+
10
16
  const start = performance.now();
11
17
  logger.info(`${ctx.entity}.${ctx.operation}`);
12
18
  try {