@fougere/core 0.16.3-alpha.0 → 0.16.5-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 (73) hide show
  1. package/dist/StatedModules.d.ts +2 -1
  2. package/dist/StatedModules.d.ts.map +1 -1
  3. package/dist/StatedModules.js +1 -2
  4. package/dist/StatedModules.js.map +1 -1
  5. package/dist/boot/ambient-port.d.ts +4 -0
  6. package/dist/boot/ambient-port.d.ts.map +1 -1
  7. package/dist/boot/ambient.als.d.ts.map +1 -1
  8. package/dist/boot/ambient.als.js +5 -0
  9. package/dist/boot/ambient.als.js.map +1 -1
  10. package/dist/boot/ambient.queue.d.ts.map +1 -1
  11. package/dist/boot/ambient.queue.js +4 -0
  12. package/dist/boot/ambient.queue.js.map +1 -1
  13. package/dist/boot/bootstrap.d.ts.map +1 -1
  14. package/dist/boot/bootstrap.js +2 -1
  15. package/dist/boot/bootstrap.js.map +1 -1
  16. package/dist/boot/install.d.ts.map +1 -1
  17. package/dist/boot/install.js +22 -16
  18. package/dist/boot/install.js.map +1 -1
  19. package/dist/builtin/LogLine.d.ts +3 -1
  20. package/dist/builtin/LogLine.d.ts.map +1 -1
  21. package/dist/builtin/LogLine.js +3 -1
  22. package/dist/builtin/LogLine.js.map +1 -1
  23. package/dist/builtin/LogRecord.d.ts +2 -0
  24. package/dist/builtin/LogRecord.d.ts.map +1 -1
  25. package/dist/builtin/Logger.d.ts +1 -0
  26. package/dist/builtin/Logger.d.ts.map +1 -1
  27. package/dist/builtin/Logger.js +12 -3
  28. package/dist/builtin/Logger.js.map +1 -1
  29. package/dist/builtin/LoggerOptions.d.ts +2 -0
  30. package/dist/builtin/LoggerOptions.d.ts.map +1 -1
  31. package/dist/builtin/OwnLoggers.d.ts +10 -0
  32. package/dist/builtin/OwnLoggers.d.ts.map +1 -0
  33. package/dist/builtin/OwnLoggers.js +15 -0
  34. package/dist/builtin/OwnLoggers.js.map +1 -0
  35. package/dist/builtin/Rendered.d.ts +1 -0
  36. package/dist/builtin/Rendered.d.ts.map +1 -1
  37. package/dist/dispatch/Facade.d.ts +5 -0
  38. package/dist/dispatch/Facade.d.ts.map +1 -1
  39. package/dist/dispatch/HandlerFacade.d.ts.map +1 -1
  40. package/dist/dispatch/HandlerFacade.js +5 -2
  41. package/dist/dispatch/HandlerFacade.js.map +1 -1
  42. package/dist/index.d.ts +0 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +0 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/node.d.ts +1 -1
  47. package/dist/node.d.ts.map +1 -1
  48. package/dist/node.js.map +1 -1
  49. package/dist/wire/OperationContext.d.ts +2 -0
  50. package/dist/wire/OperationContext.d.ts.map +1 -1
  51. package/package.json +3 -3
  52. package/src/StatedModules.ts +2 -2
  53. package/src/boot/ambient-port.ts +6 -0
  54. package/src/boot/ambient.als.ts +7 -0
  55. package/src/boot/ambient.queue.ts +6 -0
  56. package/src/boot/bootstrap.ts +2 -1
  57. package/src/boot/install.ts +23 -14
  58. package/src/builtin/LogLine.ts +3 -1
  59. package/src/builtin/LogRecord.ts +2 -0
  60. package/src/builtin/Logger.ts +12 -3
  61. package/src/builtin/LoggerOptions.ts +2 -0
  62. package/src/builtin/OwnLoggers.ts +21 -0
  63. package/src/builtin/Rendered.ts +1 -0
  64. package/src/dispatch/Facade.ts +5 -0
  65. package/src/dispatch/HandlerFacade.ts +7 -2
  66. package/src/index.ts +0 -1
  67. package/src/node.ts +1 -1
  68. package/src/wire/OperationContext.ts +2 -0
  69. package/dist/wire/loggerMiddleware.d.ts +0 -8
  70. package/dist/wire/loggerMiddleware.d.ts.map +0 -1
  71. package/dist/wire/loggerMiddleware.js +0 -25
  72. package/dist/wire/loggerMiddleware.js.map +0 -1
  73. package/src/wire/loggerMiddleware.ts +0 -30
@@ -7,6 +7,8 @@ export interface LogRecord {
7
7
  name: string;
8
8
  message: string;
9
9
  args: unknown[];
10
+ /** The operation it was written inside — 'blog:PostHandler.publish'. */
11
+ during?: string | null;
10
12
  /** Epoch milliseconds. */
11
13
  at: number;
12
14
  }
@@ -73,16 +73,22 @@ export class Logger {
73
73
  private name: string;
74
74
  private color: boolean;
75
75
  private carry?: Carry;
76
+ private during?: () => string | undefined;
76
77
 
77
78
  constructor(prefix?: string, options?: Omit<LoggerOptions, 'name'>) {
78
79
  this.name = prefix ?? 'app';
79
80
  this.color = options?.color ?? supportsColor();
80
81
  this.carry = options?.carry;
82
+ this.during = options?.during;
81
83
  }
82
84
 
83
85
  /** Create a child logger with a sub-name. It carries no level of its own either. */
84
86
  child(name: string): Logger {
85
- return new Logger(`${this.name}:${name}`, { color: this.color, ...(this.carry ? { carry: this.carry } : {}) });
87
+ return new Logger(`${this.name}:${name}`, {
88
+ color: this.color,
89
+ ...(this.carry ? { carry: this.carry } : {}),
90
+ ...(this.during ? { during: this.during } : {}),
91
+ });
86
92
  }
87
93
 
88
94
  debug(msg: string, ...args: unknown[]) { this.log('debug', msg, args); }
@@ -92,8 +98,10 @@ export class Logger {
92
98
 
93
99
  private log(level: string, msg: string, args: unknown[]) {
94
100
  if (LEVELS[level as LogLevel] < threshold) return;
101
+ const during = this.during?.();
95
102
  const record: LogRecord = {
96
103
  level: level as LogRecord['level'], name: this.name, message: msg, args, at: Date.now(),
104
+ ...(during ? { during } : {}),
97
105
  };
98
106
 
99
107
  this.carry?.push({ ...record, args: args.map(objectOf) });
@@ -118,13 +126,14 @@ export function formatted(
118
126
  const style = LEVEL_STYLE[record.level];
119
127
  const time = stamp(record.at);
120
128
  const method = record.level;
129
+ const during = record.during ? ` (${record.during})` : '';
121
130
 
122
131
  if (color) {
123
132
  const c = COLORS[style.color];
124
- const prefix = `${COLORS.dim}${time}${COLORS.reset} ${c}${COLORS.bold}${style.badge}${COLORS.reset} ${COLORS.magenta}${record.name}${COLORS.reset}`;
133
+ const prefix = `${COLORS.dim}${time}${COLORS.reset} ${c}${COLORS.bold}${style.badge}${COLORS.reset} ${COLORS.magenta}${record.name}${COLORS.reset}${COLORS.gray}${during}${COLORS.reset}`;
125
134
 
126
135
  return { method, text: [prefix, record.message, ...(record.args ?? [])] };
127
136
  }
128
137
 
129
- return { method, text: [`${time} ${style.badge} [${record.name}]`, record.message, ...(record.args ?? [])] };
138
+ return { method, text: [`${time} ${style.badge} [${record.name}]${during}`, record.message, ...(record.args ?? [])] };
130
139
  }
@@ -9,6 +9,8 @@ export interface LoggerOptions {
9
9
  * first lines do and what an app declaring no destination does forever.
10
10
  */
11
11
  carry?: Carry;
12
+ /** The operation a line is written inside, asked at each line — the boot hands it over. */
13
+ during?: () => string | undefined;
12
14
  /** Force color on/off. Auto-detected by default. */
13
15
  color?: boolean;
14
16
  }
@@ -0,0 +1,21 @@
1
+ import type { Container } from '@fougere/container';
2
+ import type { Logger } from './Logger.js';
3
+
4
+ /** A class's deps, its `Logger` replaced by one named after it. */
5
+ export type OwnLogger = (className: string, deps: readonly string[]) => string[];
6
+
7
+ /**
8
+ * A class asking for `Logger` gets one named after it — `app:blog:PricingService` — under a key of
9
+ * its own, so the container builds nothing new. A frond that `replaced` it with a class of its own
10
+ * keeps that class whole: `child()` hands back a bare `Logger`.
11
+ */
12
+ export function ownLoggers(scope: Container, replaced: boolean): OwnLogger {
13
+ return (className, deps) => {
14
+ if (replaced || !deps.includes('Logger')) return [...deps];
15
+
16
+ const key = `Logger:${className}`;
17
+ scope.registerValue(key, scope.resolve<Logger>('Logger').child(className));
18
+
19
+ return deps.map((dep) => (dep === 'Logger' ? key : dep));
20
+ };
21
+ }
@@ -14,6 +14,7 @@ export interface Rendered {
14
14
  level: Exclude<LogLevel, 'silent'>;
15
15
  name: string;
16
16
  message: string;
17
+ during?: string | null;
17
18
  /** Absent on most lines: a message usually carries its own detail. */
18
19
  args?: unknown[] | null;
19
20
  /** Epoch milliseconds from a logger, a `Date` from an entity that stamped it. */
@@ -4,6 +4,7 @@ import type { EffectiveOperationsMap } from '../EffectiveOperationsMap.js';
4
4
  import type { HandlerEntry } from '../descriptor/HandlerEntry.js';
5
5
  import type { PresenterEntry } from '../descriptor/PresenterEntry.js';
6
6
  import type { StateShape } from '../wire/StateShape.js';
7
+ import type { OwnLogger } from '../builtin/OwnLoggers.js';
7
8
 
8
9
  /** What boot resolved around one handler, beyond the handler and the scope it resolves in. */
9
10
  export interface Facade {
@@ -25,4 +26,8 @@ export interface Facade {
25
26
  middlewares: () => AppMiddleware[];
26
27
  /** What a call's `state` may hold in this process. */
27
28
  state: StateShape;
29
+ /** Runs the handler as the operation a line written inside it names. */
30
+ enterOperation: <R>(label: string, fn: () => Promise<R>) => Promise<R>;
31
+ /** The handler's deps, its `Logger` named after it. */
32
+ ownLogger: OwnLogger;
28
33
  }
@@ -92,16 +92,21 @@ export class HandlerFacade {
92
92
  const state = received.crossed ? { ...received.state } : this.facade.state.judge(received.state, entity, op);
93
93
  const invocation = received.withState(state);
94
94
  const entered = { ...state };
95
+ const { className, method } = this.effectiveOperations.get(op)!.implementation;
95
96
  const context: OperationContext = {
96
97
  entity,
97
98
  frond: this.facade.frond,
99
+ handler: className,
98
100
  operation: op,
99
101
  args: [],
100
102
  state: invocation.state,
101
103
  invocation,
102
104
  };
103
105
 
104
- return runMiddlewares(this.facade.middlewares(), context, () => this.answer(op, contract, context, invocation, entered));
106
+ const label = `${this.facade.frond}:${className}.${method}`;
107
+
108
+ return runMiddlewares(this.facade.middlewares(), context, () =>
109
+ this.facade.enterOperation(label, () => this.answer(op, contract, context, invocation, entered)));
105
110
  }
106
111
 
107
112
  /**
@@ -162,7 +167,7 @@ export class HandlerFacade {
162
167
 
163
168
  private depsOf(handler: HandlerEntry): string[] {
164
169
  const { deps } = handler;
165
- if (deps.length > 0) return deps;
170
+ if (deps.length > 0) return this.facade.ownLogger(handler.ctor.name, deps);
166
171
 
167
172
  return inheritsCrud(handler.ctor)
168
173
  ? [repositoryKeyOf(subjectOf(handler.ctor, handler.address))]
package/src/index.ts CHANGED
@@ -110,7 +110,6 @@ export { type Pipe } from './wire/Pipe.js';
110
110
  export { callValueOf } from './contract.js';
111
111
  export { toHttpError } from './wire/http-error.js';
112
112
  export { toPublicError } from './contract.js';
113
- export { loggerMiddleware } from './wire/loggerMiddleware.js';
114
113
  export { Carry } from './builtin/Carry.js';
115
114
  export { Logger, envLevel, formatted, logLevel, setLogLevel } from './builtin/Logger.js';
116
115
  export type { LogRecord } from './builtin/LogRecord.js';
package/src/node.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** What Fougere does with a filesystem, minus the scan — that is `@fougere/compiler`. */
2
- export { setModuleLoader, getModuleLoader } from './loader.js';
2
+ export { setModuleLoader, getModuleLoader, type ModuleLoader } from './loader.js';
3
3
  export { configFileIn, loadConfig, loadCascadedConfig, projectRootOf, remotesOf } from './FougereConfig.js';
4
4
  export { statedModules } from './StatedModules.js';
5
5
  export { defineFrond, loadFrondConfig } from './FrondConfig.js';
@@ -7,6 +7,8 @@ export interface OperationContext {
7
7
  * one a reader groups by first. Absent only where no frond claims the call.
8
8
  */
9
9
  frond?: string;
10
+ /** The class that answers, as the scan named it — `PostHandler`. */
11
+ handler?: string;
10
12
  /** Operation name (e.g. 'create', 'findById', 'searchByTitle'). */
11
13
  operation: string;
12
14
  /** Arguments passed to the operation. */
@@ -1,8 +0,0 @@
1
- /**
2
- * Logger middleware — logs every operation with timing.
3
- */
4
- import type { AppMiddleware } from './AppMiddleware.js';
5
- import type { Logger } from '../builtin/Logger.js';
6
- /** Create a logger middleware that logs operation entry, exit, and errors. */
7
- export declare function loggerMiddleware(logger: Logger): AppMiddleware;
8
- //# sourceMappingURL=loggerMiddleware.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loggerMiddleware.d.ts","sourceRoot":"","sources":["../../src/wire/loggerMiddleware.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAqB9D"}
@@ -1,25 +0,0 @@
1
- import { CARRIES_LINE } from '../builtin/LogLine.js';
2
- /** Create a logger middleware that logs operation entry, exit, and errors. */
3
- export function loggerMiddleware(logger) {
4
- return async (ctx, next) => {
5
- // An op that CARRIES a line writes none: it is dispatched, so logging it announces a
6
- // line inside the announcement of one — `Emission cycle: logLine → logLine`, which is
7
- // the emission catching what `onLog`'s try/catch used to swallow.
8
- if (CARRIES_LINE.has(ctx.entity))
9
- return next();
10
- const start = performance.now();
11
- logger.info(`${ctx.entity}.${ctx.operation}`);
12
- try {
13
- const result = await next();
14
- const ms = (performance.now() - start).toFixed(1);
15
- logger.info(`${ctx.entity}.${ctx.operation} (${ms}ms)`);
16
- return result;
17
- }
18
- catch (err) {
19
- const ms = (performance.now() - start).toFixed(1);
20
- logger.error(`${ctx.entity}.${ctx.operation} (${ms}ms)`, err);
21
- throw err;
22
- }
23
- };
24
- }
25
- //# sourceMappingURL=loggerMiddleware.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loggerMiddleware.js","sourceRoot":"","sources":["../../src/wire/loggerMiddleware.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,qFAAqF;QACrF,sFAAsF;QACtF,kEAAkE;QAClE,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAEhD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,SAAS,KAAK,EAAE,KAAK,CAAC,CAAC;YAExD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9D,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -1,30 +0,0 @@
1
- /**
2
- * Logger middleware — logs every operation with timing.
3
- */
4
- import type { AppMiddleware } from './AppMiddleware.js';
5
- import type { Logger } from '../builtin/Logger.js';
6
- import { CARRIES_LINE } from '../builtin/LogLine.js';
7
-
8
- /** Create a logger middleware that logs operation entry, exit, and errors. */
9
- export function loggerMiddleware(logger: Logger): AppMiddleware {
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
-
16
- const start = performance.now();
17
- logger.info(`${ctx.entity}.${ctx.operation}`);
18
- try {
19
- const result = await next();
20
- const ms = (performance.now() - start).toFixed(1);
21
- logger.info(`${ctx.entity}.${ctx.operation} (${ms}ms)`);
22
-
23
- return result;
24
- } catch (err) {
25
- const ms = (performance.now() - start).toFixed(1);
26
- logger.error(`${ctx.entity}.${ctx.operation} (${ms}ms)`, err);
27
- throw err;
28
- }
29
- };
30
- }