@fougere/compiler 0.9.2-alpha.0 → 0.10.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 (58) hide show
  1. package/dist/boot.d.ts.map +1 -1
  2. package/dist/boot.js.map +1 -1
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/scan/Aliases.d.ts +16 -0
  8. package/dist/scan/Aliases.d.ts.map +1 -0
  9. package/dist/scan/Aliases.js +2 -0
  10. package/dist/scan/Aliases.js.map +1 -0
  11. package/dist/scan/FacadeOptions.d.ts +5 -0
  12. package/dist/scan/FacadeOptions.d.ts.map +1 -0
  13. package/dist/scan/FacadeOptions.js +2 -0
  14. package/dist/scan/FacadeOptions.js.map +1 -0
  15. package/dist/scan/{contract.d.ts → Live.d.ts} +2 -16
  16. package/dist/scan/Live.d.ts.map +1 -0
  17. package/dist/scan/{contract.js → Live.js} +5 -1
  18. package/dist/scan/Live.js.map +1 -0
  19. package/dist/scan/Served.d.ts +47 -0
  20. package/dist/scan/Served.d.ts.map +1 -0
  21. package/dist/scan/Served.js +111 -0
  22. package/dist/scan/Served.js.map +1 -0
  23. package/dist/scan/emit.d.ts.map +1 -1
  24. package/dist/scan/emit.js +1 -1
  25. package/dist/scan/emit.js.map +1 -1
  26. package/dist/scan/handler-parser.d.ts +8 -0
  27. package/dist/scan/handler-parser.d.ts.map +1 -1
  28. package/dist/scan/handler-parser.js +24 -6
  29. package/dist/scan/handler-parser.js.map +1 -1
  30. package/dist/scan/names.d.ts +7 -0
  31. package/dist/scan/names.d.ts.map +1 -0
  32. package/dist/scan/names.js +93 -0
  33. package/dist/scan/names.js.map +1 -0
  34. package/dist/scan/refusals.d.ts +33 -0
  35. package/dist/scan/refusals.d.ts.map +1 -0
  36. package/dist/scan/refusals.js +118 -0
  37. package/dist/scan/refusals.js.map +1 -0
  38. package/dist/scan/scanner.d.ts.map +1 -1
  39. package/dist/scan/scanner.js +38 -3
  40. package/dist/scan/scanner.js.map +1 -1
  41. package/dist/scan/statement.d.ts.map +1 -1
  42. package/dist/scan/statement.js +1 -1
  43. package/dist/scan/statement.js.map +1 -1
  44. package/package.json +6 -6
  45. package/src/boot.ts +1 -1
  46. package/src/index.ts +3 -0
  47. package/src/scan/Aliases.ts +16 -0
  48. package/src/scan/FacadeOptions.ts +4 -0
  49. package/src/scan/{contract.ts → Live.ts} +4 -16
  50. package/src/scan/Served.ts +135 -0
  51. package/src/scan/emit.ts +2 -1
  52. package/src/scan/handler-parser.ts +34 -7
  53. package/src/scan/names.ts +101 -0
  54. package/src/scan/refusals.ts +167 -0
  55. package/src/scan/scanner.ts +49 -7
  56. package/src/scan/statement.ts +2 -1
  57. package/dist/scan/contract.d.ts.map +0 -1
  58. package/dist/scan/contract.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { DEFAULT_CONVENTIONS, frondDirsOf, frondPackage, providerDirsOf, resolveConventions, togetherKeyOf, type Conventions, type ConventionsInput, type ScanDiagnostic, type ScanResult } from '@fougere/core';
2
- import { Fronds, awaitKeyOf, cardinalityOf, computeBindingPlan, emitKeyOf, getPresenterFields, outputOf, ownedBy, repositoryKeyOf, storageKeyOf, targetOf, type CollectorEntry, type EntityEntry, type FrondDescriptor, type HandlerEntry, type MiddlewareEntry, type OperationContract, type OperationsMap, type PresenterEntry, type ProviderEntry, type SeedEntry, type TypeRef, viewsOf } from '@fougere/core/descriptor';
1
+ import { DEFAULT_CONVENTIONS, frondDirsOf, frondPackage, providerDirsOf, resolveConventions, togetherKeyOf, type Conventions, type ConventionsInput, type Diagnostic, type ScanResult } from '@fougere/core';
2
+ import { Fronds, awaitKeyOf, cardinalityOf, computeBindingPlan, emitKeyOf, getPresenterFields, outputOf, ownedBy, repositoryKeyOf, storageKeyOf, targetOf, type CollectorEntry, type EntityEntry, type FrondDescriptor, type HandlerEntry, type MiddlewareEntry, type OperationContract, type OperationsMap, type PresenterEntry, type ProviderEntry, type SeedEntry, type TypeRef, viewsOf, type ExtensionEntry } from '@fougere/core/descriptor';
3
3
  import { getModuleLoader, loadFrondConfig } from '@fougere/core/node';
4
- import type { FrondConfig } from '@fougere/core';
4
+ import type { FrondConfig, ErrorCode } from '@fougere/core';
5
5
  import { readdir, readFile } from 'node:fs/promises';
6
6
  import { existsSync, type Dirent } from 'node:fs';
7
7
  import { join, dirname, basename, resolve as resolvePath } from 'node:path';
@@ -10,6 +10,7 @@ import { ANONYMOUS_SCHEMA_NAME, type SchemaView } from '@fougere/schema';
10
10
 
11
11
  import {
12
12
  parseAllHandlerMethods,
13
+ parseRefusals,
13
14
  parsePresenterMethods,
14
15
  parseConstructorParams,
15
16
  resetTypePrograms,
@@ -21,9 +22,9 @@ import { lowerFirst } from '@fougere/schema';
21
22
  // FS
22
23
 
23
24
  /** What this scan run could not do. */
24
- let diagnostics: ScanDiagnostic[] = [];
25
+ let diagnostics: Diagnostic[] = [];
25
26
 
26
- function record(d: ScanDiagnostic): void {
27
+ function record(d: Diagnostic): void {
27
28
  diagnostics.push(d);
28
29
  }
29
30
 
@@ -125,7 +126,7 @@ function toAddress(className: string): string {
125
126
  function depKeyOf(type: TypeRef): string {
126
127
  // `Facade<PostHandler>` — the second port, read exactly like the first. The type names
127
128
  // what arrives: not the handler (its methods take positional arguments and it is never
128
- // injected), but the door built in front of it. Same key whether that door is the local
129
+ // injected), but the facade built in front of it. Same key whether that facade is the local
129
130
  // façade or a doublure, which is what makes the topology invisible from a signature.
130
131
  const facadeOf = type.name === 'Facade' ? type.generics?.[0]?.name : undefined;
131
132
  if (facadeOf) return lowerFirst(facadeOf);
@@ -211,6 +212,28 @@ async function toProvider(filePath: string): Promise<ProviderEntry> {
211
212
  return { name, ctor, deps, filePath };
212
213
  }
213
214
 
215
+ /**
216
+ * An extension is recognized by its FORM: a module stating `up` or `down`.
217
+ *
218
+ * Not a class, unlike every other convention directory — nothing resolves it from a container
219
+ * and nothing asks for it by type. It is handed to the ascent as a value, which is what
220
+ * `CreateAppOptions.extensions` already takes, so a file here and a line there declare the
221
+ * same thing.
222
+ */
223
+ async function toExtensionEntry(filePath: string): Promise<ExtensionEntry | null> {
224
+ const exported = await loadDefault(filePath);
225
+ if (typeof exported !== 'object' || exported === null) return null;
226
+
227
+ const stated = exported as { name?: unknown; up?: unknown; down?: unknown };
228
+ if (typeof stated.up !== 'function' && typeof stated.down !== 'function') return null;
229
+
230
+ return {
231
+ name: typeof stated.name === 'string' ? stated.name : basename(filePath).replace(/\.[^.]+$/, ''),
232
+ extension: exported as ExtensionEntry['extension'],
233
+ filePath,
234
+ };
235
+ }
236
+
214
237
  async function toEntityEntry(filePath: string): Promise<EntityEntry | null> {
215
238
  const exported = await loadDefault(filePath);
216
239
  if (!isEntityClass(exported)) return null;
@@ -285,6 +308,16 @@ async function inferOperations(
285
308
  return map;
286
309
  }
287
310
 
311
+ // What every op of this handler can refuse, walked from each `throw` up to whoever reaches
312
+ // it. Read from the SAME program the signatures came from, so a dependency resolves through
313
+ // the checker rather than by name — and a guard living in a helper beside the handler is
314
+ // found, where reading the method's own body would have missed it.
315
+ const refusals = await parseRefusals(
316
+ filePath,
317
+ (name) => name.startsWith(`${handlerName}.`),
318
+ projectRoot,
319
+ ).catch(() => new Map<string, ErrorCode[]>());
320
+
288
321
  /**
289
322
  * A base class the parse could not open — an installed package, typically, whose source is not
290
323
  * in the workspace.
@@ -304,9 +337,11 @@ async function inferOperations(
304
337
  // The contract is what carries the description; `signature` is the raw material it
305
338
  // was read from. Leaving it only on the signature meant every consumer had to know
306
339
  // to look one level down, and only the façade did.
340
+ const refused = refusals.get(`${handlerName}.${method.name}`);
307
341
  const meta: OperationContract = {
308
342
  signature: method,
309
343
  ...(method.description && { description: method.description }),
344
+ ...(refused?.length ? { errors: refused } : {}),
310
345
  };
311
346
 
312
347
  // A convention may omit a declaration only when it has one answer. Only values the
@@ -387,7 +422,7 @@ async function toHandlerEntry(
387
422
 
388
423
  // Read output override from Crud(Entity, Output) — static __output property
389
424
  // A handler-wide view, when it is not simply the entity — the two are compared by
390
- // identity, which is why both slots are read through the same door.
425
+ // identity, which is why both slots are read through the same facade.
391
426
  const subject = targetOf(ctor);
392
427
  const declared = outputOf(ctor);
393
428
  const outputOverride = declared && subject && declared !== subject
@@ -525,9 +560,15 @@ async function scanFrond(frondPath: string, name: string, source: FrondDescripto
525
560
  return entries.filter((entry): entry is T => entry !== null);
526
561
  };
527
562
 
563
+ // vocabulary/ FIRST, and for its effect: a file here calls `Generators.register` or its
564
+ // neighbours at module level, and an entity below may write the name it just added. Nothing
565
+ // is kept — the registry holds what the module put in it.
566
+ for (const filePath of await files(join(frondPath, conventions.dirs.vocabulary))) await loadModule(filePath);
567
+
528
568
  // services/ and repositories/ — two spellings, one provider list.
529
569
  const providers = (await Promise.all(providerDirsOf(conventions).map((dir) => collect(dir, toProvider)))).flat();
530
570
  const entities = await collect(entitiesDir, toEntityEntry);
571
+ const extensions = await collect(conventions.dirs.extensions, toExtensionEntry);
531
572
  const collectors = await collect(collectorsDir, toCollectorEntry);
532
573
  const collectorTypeNames = new Set(collectors.map((collector) => collector.typeName));
533
574
  const frondConfig = await loadFrondConfig(frondPath);
@@ -599,6 +640,7 @@ async function scanFrond(frondPath: string, name: string, source: FrondDescripto
599
640
  collectors,
600
641
  seeds,
601
642
  middlewares,
643
+ ...(extensions.length > 0 ? { extensions } : {}),
602
644
  ...(frondConfig?.pipes ? { pipes: frondConfig.pipes } : {}),
603
645
  surfaces: frondConfig?.surfaces,
604
646
  reads: frondConfig?.reads,
@@ -3,7 +3,8 @@ import { type ScanResult } from '@fougere/core';
3
3
  import { nameOf, type FrondDescriptor } from '@fougere/core/descriptor';
4
4
  import { relative } from 'node:path';
5
5
 
6
- import { type Aliases, type Live, operationsOf } from './contract.js';
6
+ import { type Aliases } from './Aliases.js';
7
+ import { operationsOf, type Live } from './Live.js';
7
8
 
8
9
  /**
9
10
  * A file becomes the specifier the PROJECT already uses for it: `@fronds/blog/…`, the import scope
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/scan/contract.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAA+B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE/E,qEAAqE;AACrE,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3D,uFAAuF;IACvF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC5C;AAED,eAAO,MAAM,GAAG,UAAW,OAAO,KAAG,MAAuC,CAAC;AAE7E,0DAA0D;AAC1D,wBAAgB,SAAS,CACvB,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,MAAM,GAAG,SAAS,CAiBpB;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,iBAAiB,EAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,GACf,MAAM,CAYR;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAC1B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAClD,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAIR"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../src/scan/contract.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,IAAI,EAAmB,MAAM,iBAAiB,CAAC;AAoB/E,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAE7E,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CACvB,MAA8B,EAC9B,UAAkB,EAClB,OAAgB;IAEhB,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAc,CAAC,CAAC;IAC9C,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,MAAM,IAAI,GAAI,MAA4B,CAAC,IAAI,CAAC;IAChD,IAAI,IAAI,IAAI,IAAI,KAAK,qBAAqB;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,MAAc,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAEnG,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,IAAI,IAAI;QAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;IAEtD,MAAM,IAAI,KAAK,CACb,yDAAyD,UAAU,oBAAoB;UACrF,wEAAwE,CAC3E,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,UAAU,CACxB,EAAU,EACV,QAA2B,EAC3B,UAAkB,EAClB,OAAgB;IAEhB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC/D,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC;IACzC,IAAI,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IAC5C,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpF,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChG,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChG,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAE1F,OAAO,IAAI,GAAG,CAAC,EAAE,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;AACjD,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAC1B,UAAkD,EAClD,UAAkB,EAClB,OAAgB,EAChB,MAAc;IAEd,MAAM,GAAG,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAEhG,OAAO,cAAc,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC;AAC5E,CAAC"}