@fougere/core 0.12.1-alpha.0 → 0.12.4-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 (261) hide show
  1. package/dist/CallIdentity.d.ts.map +1 -1
  2. package/dist/CallIdentity.js.map +1 -1
  3. package/dist/DomainCluster.d.ts.map +1 -1
  4. package/dist/DomainCluster.js +1 -1
  5. package/dist/DomainCluster.js.map +1 -1
  6. package/dist/EffectiveOperationModel.d.ts +1 -4
  7. package/dist/EffectiveOperationModel.d.ts.map +1 -1
  8. package/dist/EffectiveOperationModel.js +182 -173
  9. package/dist/EffectiveOperationModel.js.map +1 -1
  10. package/dist/FougereConfig.d.ts +5 -3
  11. package/dist/FougereConfig.d.ts.map +1 -1
  12. package/dist/FougereConfig.js +11 -13
  13. package/dist/FougereConfig.js.map +1 -1
  14. package/dist/FrondConfig.d.ts.map +1 -1
  15. package/dist/FrondConfig.js.map +1 -1
  16. package/dist/FrondDeclaration.d.ts.map +1 -1
  17. package/dist/FrondDeclaration.js +1 -5
  18. package/dist/FrondDeclaration.js.map +1 -1
  19. package/dist/Source.js +3 -3
  20. package/dist/Source.js.map +1 -1
  21. package/dist/SourceView.d.ts +0 -2
  22. package/dist/SourceView.d.ts.map +1 -1
  23. package/dist/boot/App.d.ts +2 -4
  24. package/dist/boot/App.d.ts.map +1 -1
  25. package/dist/boot/AppLifecycle.d.ts +5 -0
  26. package/dist/boot/AppLifecycle.d.ts.map +1 -1
  27. package/dist/boot/AppLifecycle.js +19 -10
  28. package/dist/boot/AppLifecycle.js.map +1 -1
  29. package/dist/boot/AuthRuntime.d.ts +5 -12
  30. package/dist/boot/AuthRuntime.d.ts.map +1 -1
  31. package/dist/boot/AuthRuntime.js +2 -1
  32. package/dist/boot/AuthRuntime.js.map +1 -1
  33. package/dist/boot/CreateAppOptions.d.ts +0 -11
  34. package/dist/boot/CreateAppOptions.d.ts.map +1 -1
  35. package/dist/boot/Emissions.d.ts.map +1 -1
  36. package/dist/boot/Emissions.js +3 -6
  37. package/dist/boot/Emissions.js.map +1 -1
  38. package/dist/boot/Hosting.d.ts +2 -1
  39. package/dist/boot/Hosting.d.ts.map +1 -1
  40. package/dist/boot/ambient.als.d.ts.map +1 -1
  41. package/dist/boot/ambient.als.js.map +1 -1
  42. package/dist/boot/apply.d.ts.map +1 -1
  43. package/dist/boot/apply.js.map +1 -1
  44. package/dist/boot/arity.d.ts +13 -0
  45. package/dist/boot/arity.d.ts.map +1 -0
  46. package/dist/boot/arity.js +33 -0
  47. package/dist/boot/arity.js.map +1 -0
  48. package/dist/boot/bootstrap.d.ts.map +1 -1
  49. package/dist/boot/bootstrap.js +143 -265
  50. package/dist/boot/bootstrap.js.map +1 -1
  51. package/dist/boot/card.d.ts.map +1 -1
  52. package/dist/boot/card.js.map +1 -1
  53. package/dist/boot/frame.d.ts.map +1 -1
  54. package/dist/boot/frame.js.map +1 -1
  55. package/dist/boot/install.d.ts.map +1 -1
  56. package/dist/boot/install.js +105 -115
  57. package/dist/boot/install.js.map +1 -1
  58. package/dist/boot/ownership.d.ts.map +1 -1
  59. package/dist/boot/ownership.js.map +1 -1
  60. package/dist/boot/relations.d.ts +10 -0
  61. package/dist/boot/relations.d.ts.map +1 -1
  62. package/dist/boot/relations.js +35 -2
  63. package/dist/boot/relations.js.map +1 -1
  64. package/dist/boot/remote.d.ts.map +1 -1
  65. package/dist/boot/remote.js.map +1 -1
  66. package/dist/boot/seed.d.ts.map +1 -1
  67. package/dist/boot/seed.js +1 -1
  68. package/dist/boot/seed.js.map +1 -1
  69. package/dist/boot/statement-drift.d.ts +6 -0
  70. package/dist/boot/statement-drift.d.ts.map +1 -1
  71. package/dist/boot/statement-drift.js +19 -0
  72. package/dist/boot/statement-drift.js.map +1 -1
  73. package/dist/boot/together.d.ts.map +1 -1
  74. package/dist/boot/together.js.map +1 -1
  75. package/dist/builtin/Carry.d.ts.map +1 -1
  76. package/dist/builtin/Carry.js.map +1 -1
  77. package/dist/builtin/Logger.d.ts.map +1 -1
  78. package/dist/builtin/Logger.js +3 -1
  79. package/dist/builtin/Logger.js.map +1 -1
  80. package/dist/builtin/config.js.map +1 -1
  81. package/dist/contract.d.ts.map +1 -1
  82. package/dist/contract.js.map +1 -1
  83. package/dist/crypto/encoding.d.ts.map +1 -1
  84. package/dist/crypto/encoding.js.map +1 -1
  85. package/dist/crypto/node.d.ts.map +1 -1
  86. package/dist/crypto/node.js.map +1 -1
  87. package/dist/crypto/webcrypto.d.ts.map +1 -1
  88. package/dist/crypto/webcrypto.js.map +1 -1
  89. package/dist/descriptor/Fronds.d.ts +5 -0
  90. package/dist/descriptor/Fronds.d.ts.map +1 -1
  91. package/dist/descriptor/Fronds.js +8 -0
  92. package/dist/descriptor/Fronds.js.map +1 -1
  93. package/dist/descriptor/index.d.ts +1 -0
  94. package/dist/descriptor/index.d.ts.map +1 -1
  95. package/dist/descriptor/index.js +1 -0
  96. package/dist/descriptor/index.js.map +1 -1
  97. package/dist/dispatch/ArrayResult.d.ts.map +1 -1
  98. package/dist/dispatch/ArrayResult.js.map +1 -1
  99. package/dist/dispatch/Dispatcher.d.ts.map +1 -1
  100. package/dist/dispatch/Dispatcher.js.map +1 -1
  101. package/dist/dispatch/HandlerFacade.d.ts +6 -1
  102. package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
  103. package/dist/dispatch/HandlerFacade.js +11 -3
  104. package/dist/dispatch/HandlerFacade.js.map +1 -1
  105. package/dist/dispatch/InFlight.d.ts +3 -2
  106. package/dist/dispatch/InFlight.d.ts.map +1 -1
  107. package/dist/dispatch/InFlight.js +11 -1
  108. package/dist/dispatch/InFlight.js.map +1 -1
  109. package/dist/dispatch/LocalRoutePolicy.d.ts.map +1 -1
  110. package/dist/dispatch/LocalRoutePolicy.js.map +1 -1
  111. package/dist/dispatch/RouteRegistry.d.ts.map +1 -1
  112. package/dist/dispatch/RouteRegistry.js.map +1 -1
  113. package/dist/dispatch/StorageGuard.d.ts.map +1 -1
  114. package/dist/dispatch/StorageGuard.js +4 -1
  115. package/dist/dispatch/StorageGuard.js.map +1 -1
  116. package/dist/identity-keys.d.ts.map +1 -1
  117. package/dist/identity-keys.js.map +1 -1
  118. package/dist/index.d.ts +2 -4
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +2 -0
  121. package/dist/index.js.map +1 -1
  122. package/dist/loader.d.ts.map +1 -1
  123. package/dist/loader.js.map +1 -1
  124. package/dist/node.d.ts +1 -1
  125. package/dist/node.d.ts.map +1 -1
  126. package/dist/node.js +1 -1
  127. package/dist/node.js.map +1 -1
  128. package/dist/prefab/CrudConstructor.d.ts.map +1 -1
  129. package/dist/prefab/CrudConstructor.js.map +1 -1
  130. package/dist/prefab/collector.d.ts.map +1 -1
  131. package/dist/prefab/collector.js.map +1 -1
  132. package/dist/prefab/presenter.d.ts.map +1 -1
  133. package/dist/prefab/presenter.js.map +1 -1
  134. package/dist/storage/Comparison.d.ts +2 -2
  135. package/dist/storage/Comparison.d.ts.map +1 -1
  136. package/dist/storage/Comparison.js +3 -1
  137. package/dist/storage/Comparison.js.map +1 -1
  138. package/dist/storage/Storage.d.ts.map +1 -1
  139. package/dist/storage/Storage.js.map +1 -1
  140. package/dist/storage/Store.d.ts.map +1 -1
  141. package/dist/storage/Store.js +1 -1
  142. package/dist/storage/Store.js.map +1 -1
  143. package/dist/verify.d.ts.map +1 -1
  144. package/dist/verify.js.map +1 -1
  145. package/dist/wire/AppMiddleware.d.ts.map +1 -1
  146. package/dist/wire/AppMiddleware.js.map +1 -1
  147. package/dist/wire/Emit.d.ts +0 -2
  148. package/dist/wire/Emit.d.ts.map +1 -1
  149. package/dist/wire/Emit.js +1 -1
  150. package/dist/wire/Emit.js.map +1 -1
  151. package/dist/wire/Facade.d.ts +7 -0
  152. package/dist/wire/Facade.d.ts.map +1 -1
  153. package/dist/wire/Facade.js +13 -0
  154. package/dist/wire/Facade.js.map +1 -1
  155. package/dist/wire/FougereError.d.ts.map +1 -1
  156. package/dist/wire/FougereError.js.map +1 -1
  157. package/dist/wire/Invocation.d.ts.map +1 -1
  158. package/dist/wire/Invocation.js.map +1 -1
  159. package/dist/wire/OperationContract.d.ts +0 -4
  160. package/dist/wire/OperationContract.d.ts.map +1 -1
  161. package/dist/wire/OperationContract.js +1 -1
  162. package/dist/wire/OperationContract.js.map +1 -1
  163. package/dist/wire/OperationsMap.d.ts +0 -3
  164. package/dist/wire/OperationsMap.d.ts.map +1 -1
  165. package/dist/wire/OperationsMap.js +0 -5
  166. package/dist/wire/OperationsMap.js.map +1 -1
  167. package/dist/wire/RouteAddress.d.ts.map +1 -1
  168. package/dist/wire/RouteAddress.js.map +1 -1
  169. package/dist/wire/binding.d.ts.map +1 -1
  170. package/dist/wire/binding.js.map +1 -1
  171. package/dist/wire/card/IdentityCard.d.ts.map +1 -1
  172. package/dist/wire/card/IdentityCard.js.map +1 -1
  173. package/dist/wire/drift.d.ts.map +1 -1
  174. package/dist/wire/drift.js.map +1 -1
  175. package/dist/wire/http-error.d.ts.map +1 -1
  176. package/dist/wire/http-error.js.map +1 -1
  177. package/dist/wire/loggerMiddleware.d.ts.map +1 -1
  178. package/dist/wire/loggerMiddleware.js.map +1 -1
  179. package/package.json +5 -5
  180. package/src/CallIdentity.ts +6 -0
  181. package/src/DomainCluster.ts +2 -1
  182. package/src/EffectiveOperationModel.ts +236 -198
  183. package/src/FougereConfig.ts +17 -16
  184. package/src/FrondConfig.ts +2 -0
  185. package/src/FrondDeclaration.ts +1 -7
  186. package/src/Source.ts +3 -3
  187. package/src/SourceView.ts +0 -2
  188. package/src/boot/App.ts +2 -4
  189. package/src/boot/AppLifecycle.ts +22 -10
  190. package/src/boot/AuthRuntime.ts +6 -13
  191. package/src/boot/CreateAppOptions.ts +0 -11
  192. package/src/boot/Emissions.ts +7 -7
  193. package/src/boot/Hosting.ts +2 -1
  194. package/src/boot/ambient.als.ts +2 -0
  195. package/src/boot/apply.ts +1 -0
  196. package/src/boot/arity.ts +42 -0
  197. package/src/boot/bootstrap.ts +175 -300
  198. package/src/boot/card.ts +2 -0
  199. package/src/boot/frame.ts +6 -0
  200. package/src/boot/install.ts +123 -146
  201. package/src/boot/ownership.ts +1 -0
  202. package/src/boot/relations.ts +42 -10
  203. package/src/boot/remote.ts +1 -0
  204. package/src/boot/seed.ts +3 -5
  205. package/src/boot/statement-drift.ts +25 -0
  206. package/src/boot/together.ts +3 -0
  207. package/src/builtin/Carry.ts +1 -0
  208. package/src/builtin/Logger.ts +9 -1
  209. package/src/builtin/config.ts +1 -0
  210. package/src/contract.ts +1 -0
  211. package/src/crypto/encoding.ts +4 -0
  212. package/src/crypto/node.ts +2 -0
  213. package/src/crypto/webcrypto.ts +2 -0
  214. package/src/descriptor/Fronds.ts +12 -0
  215. package/src/descriptor/index.ts +1 -0
  216. package/src/dispatch/ArrayResult.ts +1 -0
  217. package/src/dispatch/Dispatcher.ts +1 -0
  218. package/src/dispatch/HandlerFacade.ts +16 -4
  219. package/src/dispatch/InFlight.ts +18 -2
  220. package/src/dispatch/LocalRoutePolicy.ts +1 -0
  221. package/src/dispatch/RouteRegistry.ts +4 -0
  222. package/src/dispatch/StorageGuard.ts +9 -1
  223. package/src/identity-keys.ts +2 -0
  224. package/src/index.ts +2 -4
  225. package/src/loader.ts +1 -0
  226. package/src/node.ts +1 -1
  227. package/src/prefab/CrudConstructor.ts +1 -11
  228. package/src/prefab/collector.ts +1 -0
  229. package/src/prefab/presenter.ts +1 -0
  230. package/src/storage/Comparison.ts +7 -4
  231. package/src/storage/Storage.ts +2 -0
  232. package/src/storage/Store.ts +9 -1
  233. package/src/verify.ts +2 -0
  234. package/src/wire/AppMiddleware.ts +2 -0
  235. package/src/wire/Emit.ts +1 -1
  236. package/src/wire/Facade.ts +17 -10
  237. package/src/wire/FougereError.ts +2 -0
  238. package/src/wire/Invocation.ts +2 -0
  239. package/src/wire/OperationContract.ts +2 -1
  240. package/src/wire/OperationsMap.ts +0 -10
  241. package/src/wire/RouteAddress.ts +2 -0
  242. package/src/wire/binding.ts +1 -0
  243. package/src/wire/card/IdentityCard.ts +1 -0
  244. package/src/wire/drift.ts +3 -0
  245. package/src/wire/http-error.ts +2 -0
  246. package/src/wire/loggerMiddleware.ts +1 -0
  247. package/dist/boot/AuthConfig.d.ts +0 -14
  248. package/dist/boot/AuthConfig.d.ts.map +0 -1
  249. package/dist/boot/AuthConfig.js +0 -2
  250. package/dist/boot/AuthConfig.js.map +0 -1
  251. package/dist/boot/AuthContext.d.ts +0 -12
  252. package/dist/boot/AuthContext.d.ts.map +0 -1
  253. package/dist/boot/AuthContext.js +0 -2
  254. package/dist/boot/AuthContext.js.map +0 -1
  255. package/dist/storage/ComparisonName.d.ts +0 -3
  256. package/dist/storage/ComparisonName.d.ts.map +0 -1
  257. package/dist/storage/ComparisonName.js +0 -2
  258. package/dist/storage/ComparisonName.js.map +0 -1
  259. package/src/boot/AuthConfig.ts +0 -14
  260. package/src/boot/AuthContext.ts +0 -12
  261. package/src/storage/ComparisonName.ts +0 -3
package/src/Source.ts CHANGED
@@ -7,10 +7,10 @@ import type { SourceConfig } from './SourceConfig.js';
7
7
  /** Whether an entity asks for one, anywhere but on its key. */
8
8
  export function declares(schema: SchemaView, constraint: Constraint): boolean {
9
9
  const fields = Object.values(schema.getFields());
10
- if (constraint === 'relation') return fields.some((field) => Role.of(field).isReference);
11
- if ((schema.getUnique() ?? []).length > 0) return true;
10
+ if (constraint === 'relation') return fields.some((field) => Role.of(field).isReference());
11
+ if (schema.getUnique().length > 0) return true;
12
12
 
13
- return fields.some((field) => Role.of(field).isUnique);
13
+ return fields.some((field) => Role.of(field).isUnique());
14
14
  }
15
15
 
16
16
  /**
package/src/SourceView.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  /** The app as ONE source sees it. */
2
2
  export interface SourceView {
3
3
  fronds: readonly { readonly name: string; readonly entities: readonly { readonly name: string }[] }[];
4
- /** The auth provider's own entities, when they ride with this source. */
5
- auth?: unknown;
6
4
  elsewhere: readonly string[];
7
5
  }
package/src/boot/App.ts CHANGED
@@ -3,10 +3,10 @@ import type { Fronds } from '../descriptor/Fronds.js';
3
3
  import type { SchemaView } from '@fougere/schema';
4
4
  import type { AppMiddleware } from '../wire/AppMiddleware.js';
5
5
  import type { RpcAnswer } from '../wire/RpcAnswer.js';
6
- import type { AuthRuntime } from './AuthRuntime.js';
7
6
  import type { EffectiveOperationsMap } from '../EffectiveOperationsMap.js';
8
7
  import type { DispatchObserver } from '../dispatch/DispatchObserver.js';
9
8
  import type { DispatchPort } from '../dispatch/DispatchPort.js';
9
+ import type { Storage } from '../storage/Storage.js';
10
10
 
11
11
  /** The App object returned by createApp(). */
12
12
  export interface App extends DispatchPort {
@@ -42,7 +42,7 @@ export interface App extends DispatchPort {
42
42
  * The storage an entity is backed by, resolved through its owning frond's scope — the dual of
43
43
  * {@link facadeFor}.
44
44
  */
45
- storageFor(entity: string): unknown | undefined;
45
+ storageFor<T = Record<string, unknown>>(entity: string): Storage<T> | undefined;
46
46
  /** The presenter of an entity, resolved through its owning frond's scope. */
47
47
  presenterFor(entity: string): unknown | undefined;
48
48
  /** Dispose the root container. */
@@ -66,6 +66,4 @@ export interface App extends DispatchPort {
66
66
  use(middleware: AppMiddleware): void;
67
67
  /** Register an app middleware scoped to a specific entity. */
68
68
  use(entity: string, middleware: AppMiddleware): void;
69
- /** Auth runtime, present when fougere.config.ts declares `auth`. */
70
- auth?: AuthRuntime;
71
69
  }
@@ -12,6 +12,7 @@ export class AppLifecycle {
12
12
  if (at === -1) this.members.push(extension);
13
13
  else this.members[at] = extension;
14
14
  }
15
+
15
16
  return this;
16
17
  }
17
18
 
@@ -24,18 +25,29 @@ export class AppLifecycle {
24
25
  }
25
26
 
26
27
  async down(app: App): Promise<void> {
27
- const refused: unknown[] = [];
28
- for (const extension of [...this.members].reverse()) {
29
- try {
30
- await extension.down?.(app);
31
- } catch (error) {
32
- refused.push(error);
33
- }
34
- }
35
- if (refused.length > 0) {
36
- throw new AggregateError(refused, `${refused.length} extension(s) refused to release`);
28
+ await closeAll(
29
+ [...this.members].reverse().map((extension) => () => extension.down?.(app)),
30
+ 'extension(s) refused to release',
31
+ );
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Every level told to close even when one refuses, the refusals leaving together in one
37
+ * `AggregateError` — one already carried by a level is flattened into the list.
38
+ */
39
+ export async function closeAll(levels: readonly (() => unknown)[], refusals: string): Promise<void> {
40
+ const refused: unknown[] = [];
41
+ for (const level of levels) {
42
+ try {
43
+ await level();
44
+ } catch (error) {
45
+ if (error instanceof AggregateError) refused.push(...error.errors);
46
+ else refused.push(error);
37
47
  }
38
48
  }
49
+
50
+ if (refused.length > 0) throw new AggregateError(refused, `${refused.length} ${refusals}`);
39
51
  }
40
52
 
41
53
  /** The replaceable migration slot of the application lifecycle. */
@@ -1,19 +1,12 @@
1
- import type { SchemaView } from '@fougere/schema';
2
- import type { Storage } from '../storage/Storage.js';
3
-
4
- /**
5
- * Runtime returned by an auth provider's create(config, ctx) function.
6
- * The core mounts this on the App and the HTTP layer (Nuxt module / router) uses it.
7
- */
1
+ /** What an auth extension registers under {@link AUTH} once it has risen. */
8
2
  export interface AuthRuntime {
9
- /** Entities used by the provider, including any defaults it filled in. */
10
- entities: Record<string, SchemaView>;
11
- /** Per-entity ORMs the provider built for itself. */
12
- storages: Record<string, Storage>;
13
- /** Web Standard handler that processes /auth/* requests. */
3
+ /** Web Standard handler that processes `/auth/*` requests. */
14
4
  handler: (request: Request) => Promise<Response>;
15
5
  /** Programmatic API exposed by the provider (getSession, signOut, ...). */
16
6
  api: Record<string, unknown>;
17
- /** Effective mount path (echoes config.basePath or the provider's default). */
7
+ /** Effective mount path. */
18
8
  basePath: string;
19
9
  }
10
+
11
+ /** The container key an auth provider registers its runtime under. */
12
+ export const AUTH = 'AuthRuntime';
@@ -6,8 +6,6 @@ import type { StorageFactory } from '../storage/StorageFactory.js';
6
6
  import type { Constraint } from '../Constraint.js';
7
7
  import type { Transport } from '../wire/Transport.js';
8
8
  import type { Extension } from './Extension.js';
9
- import type { AuthConfig } from './AuthConfig.js';
10
- import type { DispatchObserver } from '../dispatch/DispatchObserver.js';
11
9
  import type { App } from './App.js';
12
10
 
13
11
  /** Options for createApp(). */
@@ -86,13 +84,4 @@ export interface CreateAppOptions {
86
84
  adapters?: Record<string, boolean | undefined>;
87
85
  /** Carries an announced fact out of this process — a broker, a queue, a log. */
88
86
  onEmit?: (fact: string, payload: unknown) => void | Promise<void>;
89
- /** Passive observers of every dispatch transition. */
90
- dispatchObservers?: readonly DispatchObserver[];
91
- /**
92
- * Storage handle to expose to the auth provider via AuthContext.db.
93
- * Required when `auth` is set.
94
- */
95
- db?: unknown;
96
- /** Auth declaration to wire into the app at boot. */
97
- auth?: AuthConfig;
98
87
  }
@@ -1,4 +1,5 @@
1
1
  import { applyCreate, dotted, lowerFirst, type SchemaView } from '@fougere/schema';
2
+ import { addressOf, facadeKeyOf } from '../wire/Facade.js';
2
3
  import type { Container } from '@fougere/container';
3
4
  import { validationErrorsOf } from '../wire/FougereError.js';
4
5
  import { awaitKeyOf, emitKeyOf, factsAnnouncedBy, factsAwaitedBy } from '../wire/Emit.js';
@@ -318,6 +319,7 @@ export class Emissions {
318
319
  */
319
320
  private stamped(fact: string, raw: unknown): unknown {
320
321
  const shape = this.shapes.get(fact);
322
+
321
323
  return shape && raw !== null && typeof raw === 'object' && !Array.isArray(raw)
322
324
  ? applyCreate(shape.getFields(), raw as Record<string, unknown>)
323
325
  : raw;
@@ -337,6 +339,7 @@ export class Emissions {
337
339
  const listeners = this.subscribers.get(fact) ?? [];
338
340
  if (listeners.length === 0) {
339
341
  this.log.debug(`${fact} — nobody listens in this process`);
342
+
340
343
  return [];
341
344
  }
342
345
 
@@ -350,6 +353,7 @@ export class Emissions {
350
353
  } catch (cause) {
351
354
  throw new Error(`${fact} → ${facade} could not be reached`, { cause });
352
355
  }
356
+
353
357
  return handler[op]({ ...Invocation.empty, input: payload });
354
358
  }),
355
359
  }));
@@ -359,17 +363,13 @@ export class Emissions {
359
363
  private describeRefusal(fact: string, cause: unknown): string | undefined {
360
364
  const refusals = validationErrorsOf(cause);
361
365
  if (!refusals?.length) return undefined;
366
+
362
367
  return `refused the shape — ${refusals.map((d) => `${dotted(d.path)}: ${d.message}`).join(', ')}.`
363
368
  + ` If '${fact}' gained a field, this copy is older than the sender's: re-run \`fougere sync\`.`;
364
369
  }
365
370
  }
366
371
 
367
- /**
368
- * The container key a declared CLASS NAME answers under — `RedactHandler` → `redactHandler`,
369
- * the same key `facadeKeyOf` builds from an address.
370
- */
372
+ /** The container key a declared CLASS NAME answers under — `RedactHandler` → `redactHandler`. */
371
373
  function facadeOf(declared: string): string {
372
- const key = lowerFirst(declared);
373
-
374
- return key.endsWith('Handler') ? key : `${key}Handler`;
374
+ return facadeKeyOf(addressOf(declared));
375
375
  }
@@ -2,6 +2,7 @@ import type { SchemaView } from '@fougere/schema';
2
2
  import type { Constraint } from '../Constraint.js';
3
3
  import type { Peer } from './Peer.js';
4
4
  import type { Journal } from '../dispatch/Journal.js';
5
+ import type { Storage } from '../storage/Storage.js';
5
6
 
6
7
  /**
7
8
  * Where an entity's rows are hosted, and what the host keeps — what a `ref()` asks the boot.
@@ -17,7 +18,7 @@ export interface Hosting {
17
18
  * Resolved through the frond that owns it, never the asking frond's scope: a scope sees its
18
19
  * parent and not its siblings, so a target of another frond was answered by nothing.
19
20
  */
20
- storageOf(entity: string): unknown | undefined;
21
+ storageOf(entity: string): Storage | undefined;
21
22
  /** Every entity of every frond — a row is named from wherever its namer was declared. */
22
23
  entities(): ReadonlyMap<string, SchemaView>;
23
24
  /**
@@ -17,12 +17,14 @@ export const ambient: Ambient = {
17
17
  + `are ONE frame — declare a single Together naming every member.`,
18
18
  ));
19
19
  }
20
+
20
21
  return frame.run(key, fn);
21
22
  },
22
23
 
23
24
  beforeAnnounce(fact: string): Promise<void> {
24
25
  const open = frame.getStore();
25
26
  if (open === undefined) return Promise.resolve();
27
+
26
28
  return Promise.reject(new Error(
27
29
  `${fact} cannot be announced inside Together<[…]> (${open}): announcing is dispatch, `
28
30
  + `so subscribers and the carrier would have it while these writes can still be taken `
package/src/boot/apply.ts CHANGED
@@ -40,5 +40,6 @@ export function applyConfig(next: FougereConfig, inForce?: FougereConfig): Confi
40
40
  pending.push(key);
41
41
  }
42
42
  }
43
+
43
44
  return { applied, pending };
44
45
  }
@@ -0,0 +1,42 @@
1
+ import type { Diagnostic } from '../diagnostic.js';
2
+ import type { FrondDescriptor } from '../descriptor/FrondDescriptor.js';
3
+
4
+ interface Constructed {
5
+ ctor: { name: string; length: number };
6
+ deps: string[];
7
+ filePath: string;
8
+ }
9
+
10
+ /**
11
+ * What the container hands a constructor is `deps`, one argument per key and in order — so a
12
+ * constructor asking for more is handed `undefined`. The scan reads `deps` off the signature and
13
+ * cannot miss; a frond declared by hand can, and `Function.length` is the one thing a
14
+ * constructor says about itself once the types are gone.
15
+ *
16
+ * `length` is a floor and never a count: a class that writes no constructor inherits
17
+ * `(...args) => super(...args)`, which is 0, while the scan read its parent's dependencies.
18
+ */
19
+ export function refuseArityDrift(frond: FrondDescriptor, refused: Diagnostic[]): void {
20
+ const constructed: Constructed[] = [
21
+ ...frond.handlers,
22
+ ...frond.presenters,
23
+ ...frond.collectors,
24
+ ...frond.providers,
25
+ ...frond.middlewares,
26
+ ];
27
+
28
+ for (const { ctor, deps, filePath } of constructed) {
29
+ if (ctor.length <= deps.length) continue;
30
+
31
+ refused.push({
32
+ severity: 'blocking',
33
+ code: 'constructor-arity',
34
+ filePath,
35
+ frond: frond.name,
36
+ subject: ctor.name,
37
+ message: `${ctor.name}'s constructor takes ${ctor.length} parameter(s) and ${deps.length} `
38
+ + `dependenc${deps.length === 1 ? 'y is' : 'ies are'} declared (${deps.join(', ') || 'none'}) — `
39
+ + 'the rest would be handed `undefined`.',
40
+ });
41
+ }
42
+ }