@fougere/core 0.11.0-alpha.0 → 0.12.1-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 (212) hide show
  1. package/dist/EffectiveOperationModel.d.ts.map +1 -1
  2. package/dist/EffectiveOperationModel.js +18 -0
  3. package/dist/EffectiveOperationModel.js.map +1 -1
  4. package/dist/FougereConfig.d.ts +10 -6
  5. package/dist/FougereConfig.d.ts.map +1 -1
  6. package/dist/FougereConfig.js +19 -4
  7. package/dist/FougereConfig.js.map +1 -1
  8. package/dist/FrondConfig.d.ts +0 -6
  9. package/dist/FrondConfig.d.ts.map +1 -1
  10. package/dist/FrondConfig.js.map +1 -1
  11. package/dist/FrondDeclaration.d.ts +2 -9
  12. package/dist/FrondDeclaration.d.ts.map +1 -1
  13. package/dist/FrondDeclaration.js +0 -1
  14. package/dist/FrondDeclaration.js.map +1 -1
  15. package/dist/FrondsStated.d.ts +54 -0
  16. package/dist/FrondsStated.d.ts.map +1 -0
  17. package/dist/FrondsStated.js +26 -0
  18. package/dist/FrondsStated.js.map +1 -0
  19. package/dist/StatedFrond.d.ts +21 -0
  20. package/dist/StatedFrond.d.ts.map +1 -0
  21. package/dist/StatedFrond.js +34 -0
  22. package/dist/StatedFrond.js.map +1 -0
  23. package/dist/StatedModules.d.ts +19 -0
  24. package/dist/StatedModules.d.ts.map +1 -0
  25. package/dist/StatedModules.js +37 -0
  26. package/dist/StatedModules.js.map +1 -0
  27. package/dist/boot/CreateAppOptions.d.ts +19 -2
  28. package/dist/boot/CreateAppOptions.d.ts.map +1 -1
  29. package/dist/boot/Emissions.d.ts +9 -0
  30. package/dist/boot/Emissions.d.ts.map +1 -1
  31. package/dist/boot/Emissions.js +11 -5
  32. package/dist/boot/Emissions.js.map +1 -1
  33. package/dist/boot/bootstrap.d.ts.map +1 -1
  34. package/dist/boot/bootstrap.js +342 -307
  35. package/dist/boot/bootstrap.js.map +1 -1
  36. package/dist/boot/declared.d.ts.map +1 -1
  37. package/dist/boot/declared.js +3 -0
  38. package/dist/boot/declared.js.map +1 -1
  39. package/dist/boot/frame.d.ts +0 -4
  40. package/dist/boot/frame.d.ts.map +1 -1
  41. package/dist/boot/frame.js +31 -14
  42. package/dist/boot/frame.js.map +1 -1
  43. package/dist/boot/hosted.d.ts.map +1 -1
  44. package/dist/boot/hosted.js +4 -3
  45. package/dist/boot/hosted.js.map +1 -1
  46. package/dist/boot/install.d.ts +9 -0
  47. package/dist/boot/install.d.ts.map +1 -1
  48. package/dist/boot/install.js +275 -185
  49. package/dist/boot/install.js.map +1 -1
  50. package/dist/boot/nesting.d.ts +33 -0
  51. package/dist/boot/nesting.d.ts.map +1 -0
  52. package/dist/boot/nesting.js +164 -0
  53. package/dist/boot/nesting.js.map +1 -0
  54. package/dist/boot/ownership.d.ts +0 -1
  55. package/dist/boot/ownership.d.ts.map +1 -1
  56. package/dist/boot/ownership.js +66 -51
  57. package/dist/boot/ownership.js.map +1 -1
  58. package/dist/boot/ports.js +30 -25
  59. package/dist/boot/ports.js.map +1 -1
  60. package/dist/boot/remote.d.ts.map +1 -1
  61. package/dist/boot/remote.js +39 -30
  62. package/dist/boot/remote.js.map +1 -1
  63. package/dist/boot/seed.d.ts.map +1 -1
  64. package/dist/boot/seed.js +27 -20
  65. package/dist/boot/seed.js.map +1 -1
  66. package/dist/boot/together.d.ts.map +1 -1
  67. package/dist/boot/together.js +71 -56
  68. package/dist/boot/together.js.map +1 -1
  69. package/dist/contract.d.ts +2 -0
  70. package/dist/contract.d.ts.map +1 -1
  71. package/dist/contract.js +2 -0
  72. package/dist/contract.js.map +1 -1
  73. package/dist/descriptor/FrondDescriptor.d.ts +7 -0
  74. package/dist/descriptor/FrondDescriptor.d.ts.map +1 -1
  75. package/dist/descriptor/MiddlewareEntry.d.ts +6 -5
  76. package/dist/descriptor/MiddlewareEntry.d.ts.map +1 -1
  77. package/dist/descriptor/ProviderEntry.d.ts +7 -0
  78. package/dist/descriptor/ProviderEntry.d.ts.map +1 -1
  79. package/dist/dispatch/HandlerFacade.d.ts +6 -1
  80. package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
  81. package/dist/dispatch/HandlerFacade.js +23 -20
  82. package/dist/dispatch/HandlerFacade.js.map +1 -1
  83. package/dist/dispatch/OutputView.d.ts.map +1 -1
  84. package/dist/dispatch/OutputView.js +7 -0
  85. package/dist/dispatch/OutputView.js.map +1 -1
  86. package/dist/dispatch/PresenterExecutor.d.ts +6 -0
  87. package/dist/dispatch/PresenterExecutor.d.ts.map +1 -1
  88. package/dist/dispatch/PresenterExecutor.js +37 -22
  89. package/dist/dispatch/PresenterExecutor.js.map +1 -1
  90. package/dist/dispatch/Received.d.ts +3 -0
  91. package/dist/dispatch/Received.d.ts.map +1 -0
  92. package/dist/dispatch/Received.js +2 -0
  93. package/dist/dispatch/Received.js.map +1 -0
  94. package/dist/dispatch/Release.js +53 -47
  95. package/dist/dispatch/Release.js.map +1 -1
  96. package/dist/dispatch/StorageGuard.d.ts +21 -0
  97. package/dist/dispatch/StorageGuard.d.ts.map +1 -1
  98. package/dist/dispatch/StorageGuard.js +84 -66
  99. package/dist/dispatch/StorageGuard.js.map +1 -1
  100. package/dist/dispatch/decoded.d.ts +19 -0
  101. package/dist/dispatch/decoded.d.ts.map +1 -0
  102. package/dist/dispatch/decoded.js +36 -0
  103. package/dist/dispatch/decoded.js.map +1 -0
  104. package/dist/entry/facade.d.ts +9 -2
  105. package/dist/entry/facade.d.ts.map +1 -1
  106. package/dist/entry/facade.js +16 -7
  107. package/dist/entry/facade.js.map +1 -1
  108. package/dist/index.d.ts +4 -0
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +4 -0
  111. package/dist/index.js.map +1 -1
  112. package/dist/node.d.ts +2 -1
  113. package/dist/node.d.ts.map +1 -1
  114. package/dist/node.js +2 -1
  115. package/dist/node.js.map +1 -1
  116. package/dist/prefab/CrudConstructor.d.ts.map +1 -1
  117. package/dist/prefab/CrudConstructor.js +3 -2
  118. package/dist/prefab/CrudConstructor.js.map +1 -1
  119. package/dist/prefab/CrudOps.d.ts +2 -2
  120. package/dist/prefab/CrudOps.d.ts.map +1 -1
  121. package/dist/prefab/MirrorConstructor.js +1 -1
  122. package/dist/prefab/MirrorConstructor.js.map +1 -1
  123. package/dist/prefab/Refreshed.d.ts +7 -2
  124. package/dist/prefab/Refreshed.d.ts.map +1 -1
  125. package/dist/storage/Storage.js +1 -1
  126. package/dist/storage/Storage.js.map +1 -1
  127. package/dist/storage/Store.d.ts.map +1 -1
  128. package/dist/storage/Store.js +35 -31
  129. package/dist/storage/Store.js.map +1 -1
  130. package/dist/verify.d.ts.map +1 -1
  131. package/dist/verify.js +14 -0
  132. package/dist/verify.js.map +1 -1
  133. package/dist/wire/AppMiddleware.d.ts +10 -1
  134. package/dist/wire/AppMiddleware.d.ts.map +1 -1
  135. package/dist/wire/AppMiddleware.js.map +1 -1
  136. package/dist/wire/AppNext.d.ts +2 -1
  137. package/dist/wire/AppNext.d.ts.map +1 -1
  138. package/dist/wire/Facade.d.ts +14 -2
  139. package/dist/wire/Facade.d.ts.map +1 -1
  140. package/dist/wire/Facade.js.map +1 -1
  141. package/dist/wire/FougereError.js +1 -1
  142. package/dist/wire/FougereError.js.map +1 -1
  143. package/dist/wire/Invocation.d.ts +1 -1
  144. package/dist/wire/Invocation.d.ts.map +1 -1
  145. package/dist/wire/Invocation.js +1 -1
  146. package/dist/wire/Invocation.js.map +1 -1
  147. package/dist/wire/OperationContract.js +1 -1
  148. package/dist/wire/OperationContract.js.map +1 -1
  149. package/dist/wire/Page.d.ts +32 -0
  150. package/dist/wire/Page.d.ts.map +1 -0
  151. package/dist/wire/Page.js +32 -0
  152. package/dist/wire/Page.js.map +1 -0
  153. package/dist/wire/Signature.d.ts +7 -0
  154. package/dist/wire/Signature.d.ts.map +1 -1
  155. package/dist/wire/binding.d.ts.map +1 -1
  156. package/dist/wire/binding.js +29 -57
  157. package/dist/wire/binding.js.map +1 -1
  158. package/dist/wire/drift.d.ts.map +1 -1
  159. package/dist/wire/drift.js +22 -19
  160. package/dist/wire/drift.js.map +1 -1
  161. package/package.json +4 -4
  162. package/src/EffectiveOperationModel.ts +19 -0
  163. package/src/FougereConfig.ts +24 -10
  164. package/src/FrondConfig.ts +0 -6
  165. package/src/FrondDeclaration.ts +2 -7
  166. package/src/FrondsStated.ts +72 -0
  167. package/src/StatedFrond.ts +51 -0
  168. package/src/StatedModules.ts +58 -0
  169. package/src/boot/CreateAppOptions.ts +19 -2
  170. package/src/boot/Emissions.ts +16 -5
  171. package/src/boot/bootstrap.ts +478 -359
  172. package/src/boot/declared.ts +3 -0
  173. package/src/boot/frame.ts +42 -11
  174. package/src/boot/hosted.ts +4 -3
  175. package/src/boot/install.ts +392 -212
  176. package/src/boot/nesting.ts +196 -0
  177. package/src/boot/ownership.ts +79 -51
  178. package/src/boot/ports.ts +45 -31
  179. package/src/boot/remote.ts +57 -32
  180. package/src/boot/seed.ts +36 -27
  181. package/src/boot/together.ts +98 -63
  182. package/src/contract.ts +2 -0
  183. package/src/descriptor/FrondDescriptor.ts +7 -0
  184. package/src/descriptor/MiddlewareEntry.ts +6 -5
  185. package/src/descriptor/ProviderEntry.ts +7 -0
  186. package/src/dispatch/HandlerFacade.ts +27 -17
  187. package/src/dispatch/OutputView.ts +7 -0
  188. package/src/dispatch/PresenterExecutor.ts +40 -22
  189. package/src/dispatch/Received.ts +2 -0
  190. package/src/dispatch/Release.ts +68 -44
  191. package/src/dispatch/StorageGuard.ts +90 -69
  192. package/src/dispatch/decoded.ts +37 -0
  193. package/src/entry/facade.ts +21 -9
  194. package/src/index.ts +4 -0
  195. package/src/node.ts +2 -1
  196. package/src/prefab/CrudConstructor.ts +3 -2
  197. package/src/prefab/CrudOps.ts +2 -2
  198. package/src/prefab/MirrorConstructor.ts +1 -1
  199. package/src/prefab/Refreshed.ts +7 -2
  200. package/src/storage/Storage.ts +2 -2
  201. package/src/storage/Store.ts +45 -27
  202. package/src/verify.ts +15 -0
  203. package/src/wire/AppMiddleware.ts +10 -1
  204. package/src/wire/AppNext.ts +2 -1
  205. package/src/wire/Facade.ts +14 -2
  206. package/src/wire/FougereError.ts +1 -1
  207. package/src/wire/Invocation.ts +2 -1
  208. package/src/wire/OperationContract.ts +1 -1
  209. package/src/wire/Page.ts +50 -0
  210. package/src/wire/Signature.ts +7 -0
  211. package/src/wire/binding.ts +26 -58
  212. package/src/wire/drift.ts +48 -21
@@ -0,0 +1,50 @@
1
+ /**
2
+ * What a paged operation answers — the rows, and what the storage said about the rest of them.
3
+ *
4
+ * An ENVELOPE rather than the array `Storage.list` hands back: that one carries `total` and
5
+ * `hasMore` as properties OF the array, and `JSON.stringify` keeps only the indices. Measured
6
+ * 2026-09-18 — `page.total` is 42 in this process and `undefined` behind `fronds:`, off the same
7
+ * code. What crosses has to be named, so it is a field.
8
+ *
9
+ * `Storage.list` keeps its array: it answers inside a frond and never crosses.
10
+ */
11
+ export interface Page<Row> {
12
+ items: Row[];
13
+ /** Only when the caller asked to count. */
14
+ total?: number;
15
+ hasMore?: boolean;
16
+ endCursor?: string;
17
+ }
18
+
19
+ /**
20
+ * The array `Storage.list` answers, read as the page an operation hands over.
21
+ *
22
+ * A storage handed in by a host may answer the envelope already — `storageFactory` is its door,
23
+ * and nothing there is obliged to subclass an array. Taken as it comes in that case.
24
+ */
25
+ export function pageOf<Row>(rows: Page<Row> | readonly Row[]): Page<Row> {
26
+ if (!Array.isArray(rows)) return rows as Page<Row>;
27
+
28
+ const { total, hasMore, endCursor } = rows as unknown as Omit<Page<Row>, 'items'>;
29
+
30
+ return {
31
+ items: [...rows],
32
+ ...(total !== undefined && { total }),
33
+ ...(hasMore !== undefined && { hasMore }),
34
+ ...(endCursor !== undefined && { endCursor }),
35
+ };
36
+ }
37
+
38
+ /**
39
+ * The value read as a page, or nothing.
40
+ *
41
+ * Read from the FORM, and arbitrated by the schema: an entity legally declaring a field named
42
+ * `items` answers a row, not a page, and it is the only thing that could tell the two apart.
43
+ */
44
+ export function asPage(value: unknown, fields: Record<string, unknown>): Page<unknown> | undefined {
45
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;
46
+ if ('items' in fields) return undefined;
47
+ const page = value as Page<unknown>;
48
+
49
+ return Array.isArray(page.items) ? page : undefined;
50
+ }
@@ -10,4 +10,11 @@ export interface Signature {
10
10
  inherited?: boolean;
11
11
  /** The operation in words — the first sentence of the method's doc comment. */
12
12
  description?: string;
13
+ /**
14
+ * The first thing in the return type that is not data — its path, and the type sitting there.
15
+ *
16
+ * Written whenever a checker read the signature, and judged only where nothing converts the
17
+ * output: what a declared entity answers is encoded by its fields.
18
+ */
19
+ notData?: string;
13
20
  }
@@ -48,66 +48,34 @@ export function computeBindingPlan(
48
48
  params: Param[],
49
49
  collectorTypeNames: Set<string>,
50
50
  ): BindingPlan {
51
- return params.map((param) => {
52
- const typeName = param.type.name;
53
- // `lowerFirst`, never `toLowerCase()`: the collector set is keyed the way the
54
- // scan spells it, and the two agree on one word only — `AuthorUser` looked up as
55
- // `authoruser` missed `authorUser` and fell through to branch 4, the request input.
56
- const typeKey = lowerFirst(typeName);
57
-
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) {
65
- return {
66
- name: param.name,
67
- source: {
68
- kind: (param.type.name === 'Pipe' ? 'pipe' : 'fact') as 'pipe' | 'fact',
69
- factName: lowerFirst(announced),
70
- },
71
- optional: param.optional ?? false,
72
- };
73
- }
51
+ return params.map((param) => ({
52
+ name: param.name,
53
+ source: sourceOf(param, collectorTypeNames),
54
+ optional: param.optional ?? false,
55
+ }));
56
+ }
74
57
 
75
- // 1. Collector — param type matches a type some collector answers for
76
- if (collectorTypeNames.has(typeKey)) {
77
- return {
78
- name: param.name,
79
- source: { kind: 'collector' as const, typeName: typeKey },
80
- optional: param.optional ?? false,
81
- };
82
- }
58
+ /**
59
+ * Where ONE parameter gets its value, in the order the branches must be asked.
60
+ *
61
+ * A fact and a link name themselves, so they come FIRST: asked later, the last branch would hand
62
+ * the caller's input under the name of something that happened.
63
+ */
64
+ function sourceOf(param: Param, collectorTypeNames: Set<string>): ParamSource {
65
+ const typeName = param.type.name;
66
+ // `lowerFirst`, never `toLowerCase()`: the collector set is keyed the way the scan spells it,
67
+ // and the two agree on one word only — `AuthorUser` looked up as `authoruser` missed
68
+ // `authorUser` and fell through to the request input.
69
+ const typeKey = lowerFirst(typeName);
83
70
 
84
- // 2. InvocationContext — inject the full context
85
- if (typeName === 'InvocationContext') {
86
- return {
87
- name: param.name,
88
- source: { kind: 'context' as const },
89
- optional: param.optional ?? false,
90
- };
91
- }
71
+ const announced = typeName === 'Fact' || typeName === 'Pipe' ? param.type.generics?.[0]?.name : undefined;
72
+ if (announced) {
73
+ return { kind: typeName === 'Pipe' ? 'pipe' : 'fact', factName: lowerFirst(announced) };
74
+ }
92
75
 
93
- // 3. Primitives — matched by name from params > query
94
- if (PRIMITIVES.has(typeName)) {
95
- return {
96
- name: param.name,
97
- source: {
98
- kind: 'param' as const,
99
- name: param.name,
100
- coerce: coercionFor(typeName),
101
- },
102
- optional: param.optional ?? false,
103
- };
104
- }
76
+ if (collectorTypeNames.has(typeKey)) return { kind: 'collector', typeName: typeKey };
77
+ if (typeName === 'InvocationContext') return { kind: 'context' };
78
+ if (PRIMITIVES.has(typeName)) return { kind: 'param', name: param.name, coerce: coercionFor(typeName) };
105
79
 
106
- // 4. Everything else — input
107
- return {
108
- name: param.name,
109
- source: { kind: 'input' as const },
110
- optional: param.optional ?? false,
111
- };
112
- });
80
+ return { kind: 'input' };
113
81
  }
package/src/wire/drift.ts CHANGED
@@ -15,8 +15,14 @@ export interface CardDrift {
15
15
  }
16
16
 
17
17
  /** Every facade of a card, by name. */
18
- function facadesOf(card: IdentityCard, frond: string): Map<string, { ops: Set<string>; schema?: SchemaDescriptor }> {
19
- const found = new Map<string, { ops: Set<string>; schema?: SchemaDescriptor }>();
18
+ /** What a door of that card answers, and the shape of the row it hands back. */
19
+ interface Answering {
20
+ ops: Set<string>;
21
+ schema?: SchemaDescriptor;
22
+ }
23
+
24
+ function facadesOf(card: IdentityCard, frond: string): Map<string, Answering> {
25
+ const found = new Map<string, Answering>();
20
26
  for (const one of card.fronds) {
21
27
  if (one.name !== frond) continue;
22
28
  for (const facade of one.facades) {
@@ -37,37 +43,58 @@ function factsOf(card: IdentityCard, frond: string): Map<string, SchemaDescripto
37
43
 
38
44
  /** What a consumer's synced copy no longer matches in what the producer serves. */
39
45
  export function driftOf(mine: IdentityCard, theirs: IdentityCard, frond: string): CardDrift {
40
- const mineFacades = facadesOf(mine, frond);
41
- const served = facadesOf(theirs, frond);
42
46
  const drift: CardDrift = { frond, missingDoors: [], missingOps: [], shapes: [], facts: [] };
43
47
 
44
- for (const [name, facade] of mineFacades) {
45
- const there = served.get(name);
46
- if (!there) { drift.missingDoors.push(name); continue; }
48
+ driftInDoors(facadesOf(mine, frond), facadesOf(theirs, frond), drift);
49
+ driftInFacts(factsOf(mine, frond), factsOf(theirs, frond), drift);
50
+
51
+ return drift;
52
+ }
53
+
54
+ /** A door that is gone, an operation it no longer answers, or a row that changed shape. */
55
+ function driftInDoors(
56
+ mine: Map<string, Answering>,
57
+ theirs: Map<string, Answering>,
58
+ drift: CardDrift,
59
+ ): void {
60
+ for (const [name, facade] of mine) {
61
+ const there = theirs.get(name);
62
+ if (!there) {
63
+ drift.missingDoors.push(name);
64
+ continue;
65
+ }
47
66
 
48
67
  const missing = [...facade.ops].filter((op) => !there.ops.has(op));
49
68
  if (missing.length > 0) drift.missingOps.push({ facade: name, ops: missing.sort() });
50
69
 
51
- if (facade.schema && there.schema) {
52
- // `Card.diff` never guesses a rename — a field gone plus a field appeared lands in
53
- // `ambiguous`, and only a declaration settles it. Here nobody can declare one, so
54
- // the pair is reported as it is and a human reads it.
55
- const moved = Card.fromDescriptor(facade.schema).diff(Card.fromDescriptor(there.schema));
56
- if (moved.changes.length > 0) drift.shapes.push({ facade: name, changes: moved.changes });
57
- }
70
+ if (!facade.schema || !there.schema) continue;
71
+
72
+ // `Card.diff` never guesses a rename — a field gone plus a field appeared lands in
73
+ // `ambiguous`, and only a declaration settles it. Here nobody can declare one, so the
74
+ // pair is reported as it is and a human reads it.
75
+ const moved = Card.fromDescriptor(facade.schema).diff(Card.fromDescriptor(there.schema));
76
+ if (moved.changes.length > 0) drift.shapes.push({ facade: name, changes: moved.changes });
58
77
  }
78
+ }
79
+
80
+ /** A fact nobody announces any more, or one whose payload moved under its readers. */
81
+ function driftInFacts(
82
+ mine: Map<string, SchemaDescriptor | undefined>,
83
+ theirs: Map<string, SchemaDescriptor | undefined>,
84
+ drift: CardDrift,
85
+ ): void {
86
+ for (const [name, shape] of mine) {
87
+ if (!theirs.has(name)) {
88
+ drift.facts.push({ fact: name, changes: 'gone' });
89
+ continue;
90
+ }
59
91
 
60
- const heldFacts = factsOf(mine, frond);
61
- const servedFacts = factsOf(theirs, frond);
62
- for (const [name, shape] of heldFacts) {
63
- if (!servedFacts.has(name)) { drift.facts.push({ fact: name, changes: 'gone' }); continue; }
64
- const there = servedFacts.get(name);
92
+ const there = theirs.get(name);
65
93
  if (!shape || !there) continue;
94
+
66
95
  const moved = Card.fromDescriptor(shape).diff(Card.fromDescriptor(there));
67
96
  if (moved.changes.length > 0) drift.facts.push({ fact: name, changes: moved.changes });
68
97
  }
69
-
70
- return drift;
71
98
  }
72
99
 
73
100
  /** Whether anything at all separates the two cards. */