@webpieces/core-util 0.4.394 → 0.4.396

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.4.394",
3
+ "version": "0.4.396",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,5 +1,6 @@
1
1
  /**
2
- * ServiceInfo - the ONE answer to "what service am I?", for every part of webpieces that needs it.
2
+ * ServiceInfo - the ONE answer to "what service am I, and which build of it?", for every part of
3
+ * webpieces that needs it.
3
4
  *
4
5
  * Configured like {@link HeaderRegistry} / {@link ClientRegistry} / LogManager — populated once at
5
6
  * startup, then globally accessible with NO DI wiring. Browser-safe (no `process.env`, no node-only
@@ -7,49 +8,68 @@
7
8
  *
8
9
  * ```ts
9
10
  * // startup, FIRST — before the logger factory is constructed (it reads this):
10
- * ServiceInfo.setName('my-service');
11
+ * ServiceInfo.setInfo('my-service', '2.1.0');
11
12
  * ```
12
13
  *
13
- * WHY THIS EXISTS. The name used to live on `BunyanFactoryOptions`, which made it an artifact of
14
- * WHICH LOGGING LIBRARY YOU PICKED rather than a fact about the service:
15
- * - bunyan REQUIRES a root-logger name (`options.name (string) is required` — bunyan's own
16
- * TypeError), so a bunyan app was forced to supply one.
17
- * - winston has no such concept, so a winston app supplied nothing and shipped its logs UNNAMED.
14
+ * WHY THIS EXISTS. Both facts used to be artifacts of WHICH LOGGING LIBRARY YOU PICKED rather than
15
+ * facts about the service:
16
+ * - the name lived on `BunyanFactoryOptions`, because bunyan REQUIRES a root-logger name
17
+ * (`options.name (string) is required` bunyan's own TypeError). winston has no such concept, so
18
+ * a winston app shipped its logs UNNAMED.
19
+ * - the version lived on `WinstonFactoryOptions` as `svcGitHash`, so a bunyan app could not stamp a
20
+ * version AT ALL — and the name presumed a git SHA, which not every project deploys from.
18
21
  *
19
- * Three unrelated readers need the same fact, so it belongs to the framework, not to a backend:
20
- * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`;
21
- * - the winston backend, to stamp `svcName` (which it never had);
22
+ * Switching backends silently changed which fields your logs carried. Several unrelated readers
23
+ * need these same two facts, so they belong to the framework, not to a backend:
24
+ * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`, and to stamp `version`;
25
+ * - the winston backend, to stamp `svcName` + `version`;
22
26
  * - {@link WebpiecesCoreHeaders.REQUEST_ID_SOURCE}, to record WHO minted a request id.
23
27
  *
24
- * FAIL FAST, AT STARTUP. {@link getName} throws, and the logger factories + `setupRuntime` call it
25
- * while the process is booting so a forgotten `setName` kills the deploy (the revision never goes
26
- * healthy) instead of quietly shipping unnamed logs. Nothing on the REQUEST path may throw over
27
- * this: a missing log field must never 500 live traffic, so per-request readers use
28
- * {@link tryGetName} and simply omit the field. See {@link tryGetName}.
28
+ * VERSION IS OPAQUE. It is whatever string identifies THIS build a git SHA, a semver tag, a CI
29
+ * build number. webpieces neither parses nor derives it; the app decides where it comes from (a
30
+ * generated file, an env var, a Docker build arg) and passes it here.
31
+ *
32
+ * FAIL FAST, AT STARTUP. {@link getName} / {@link getVersion} throw, and the logger factories +
33
+ * `setupRuntime` call them while the process is booting — so a forgotten `setInfo` kills the deploy
34
+ * (the revision never goes healthy) instead of quietly shipping logs that cannot say which build
35
+ * emitted them. Nothing on the REQUEST path may throw over this: a missing log field must never 500
36
+ * live traffic, so per-request readers use {@link tryGetName} / {@link tryGetVersion} and simply
37
+ * omit the field.
29
38
  */
30
39
  export declare class ServiceInfo {
31
40
  /** This service's name. Process-global; set once at startup. */
32
41
  private static svcName;
42
+ /** This build's version — an opaque app-chosen string. Process-global; set once at startup. */
43
+ private static svcVersion;
33
44
  /**
34
- * Name this service. Call it FIRST at startup — before constructing the logger factory, which
35
- * reads it during its own constructor.
45
+ * Identify this service. Call it FIRST at startup — before constructing the logger factory,
46
+ * which reads both values during its own constructor.
36
47
  *
37
- * LAST CALL WINS, deliberately. A real deployment names itself once, but an in-process test can
38
- * legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a
39
- * "one process = one name" rule would reject a case that genuinely exists. Since a logger
40
- * factory captures the name in its own constructor, each server built that way still keeps the
41
- * name that was set when IT was built.
48
+ * LAST CALL WINS, deliberately. A real deployment identifies itself once, but an in-process test
49
+ * can legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a
50
+ * "one process = one service" rule would reject a case that genuinely exists. Since a logger
51
+ * factory captures both values in its own constructor, each server built that way still keeps
52
+ * what was set when IT was built.
42
53
  *
43
- * @throws Error on a blank name — that is always a bug, never a use case.
54
+ * @param name - this service's name, e.g. 'my-service'.
55
+ * @param version - the opaque identifier of THIS build (git SHA, semver, CI build number).
56
+ * @throws Error on a blank name or version — that is always a bug, never a use case.
44
57
  */
45
- static setName(name: string): void;
58
+ static setInfo(name: string, version: string): void;
46
59
  /**
47
60
  * This service's name, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime) —
48
61
  * they run while booting, so throwing here fails the deploy rather than live traffic.
49
62
  *
50
- * @throws Error if {@link setName} was never called.
63
+ * @throws Error if {@link setInfo} was never called.
51
64
  */
52
65
  static getName(): string;
66
+ /**
67
+ * This build's version, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime),
68
+ * for the same reason as {@link getName}.
69
+ *
70
+ * @throws Error if {@link setInfo} was never called.
71
+ */
72
+ static getVersion(): string;
53
73
  /**
54
74
  * This service's name, or undefined when unset. For the REQUEST path, which must NOT throw: a
55
75
  * server that booted has already passed the {@link getName} check in `setupRuntime`, so a real
@@ -57,6 +77,18 @@ export declare class ServiceInfo {
57
77
  * undefined — and there, omitting a log field beats exploding.
58
78
  */
59
79
  static tryGetName(): string | undefined;
80
+ /**
81
+ * This build's version, or undefined when unset. For the REQUEST path, which must NOT throw —
82
+ * same reasoning as {@link tryGetName}.
83
+ */
84
+ static tryGetVersion(): string | undefined;
60
85
  /** Reset — for tests, mirroring {@link ClientRegistry.clear}. */
61
86
  static clear(): void;
87
+ /**
88
+ * The one actionable "you forgot to call setInfo" message. Shared by {@link getName} and
89
+ * {@link getVersion}: setInfo sets BOTH, so either being missing has the identical cause and
90
+ * the identical fix — telling the caller only about the half they happened to read first would
91
+ * send them back for a second round.
92
+ */
93
+ private static notSetMessage;
62
94
  }
@@ -2,7 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ServiceInfo = void 0;
4
4
  /**
5
- * ServiceInfo - the ONE answer to "what service am I?", for every part of webpieces that needs it.
5
+ * ServiceInfo - the ONE answer to "what service am I, and which build of it?", for every part of
6
+ * webpieces that needs it.
6
7
  *
7
8
  * Configured like {@link HeaderRegistry} / {@link ClientRegistry} / LogManager — populated once at
8
9
  * startup, then globally accessible with NO DI wiring. Browser-safe (no `process.env`, no node-only
@@ -10,65 +11,92 @@ exports.ServiceInfo = void 0;
10
11
  *
11
12
  * ```ts
12
13
  * // startup, FIRST — before the logger factory is constructed (it reads this):
13
- * ServiceInfo.setName('my-service');
14
+ * ServiceInfo.setInfo('my-service', '2.1.0');
14
15
  * ```
15
16
  *
16
- * WHY THIS EXISTS. The name used to live on `BunyanFactoryOptions`, which made it an artifact of
17
- * WHICH LOGGING LIBRARY YOU PICKED rather than a fact about the service:
18
- * - bunyan REQUIRES a root-logger name (`options.name (string) is required` — bunyan's own
19
- * TypeError), so a bunyan app was forced to supply one.
20
- * - winston has no such concept, so a winston app supplied nothing and shipped its logs UNNAMED.
17
+ * WHY THIS EXISTS. Both facts used to be artifacts of WHICH LOGGING LIBRARY YOU PICKED rather than
18
+ * facts about the service:
19
+ * - the name lived on `BunyanFactoryOptions`, because bunyan REQUIRES a root-logger name
20
+ * (`options.name (string) is required` bunyan's own TypeError). winston has no such concept, so
21
+ * a winston app shipped its logs UNNAMED.
22
+ * - the version lived on `WinstonFactoryOptions` as `svcGitHash`, so a bunyan app could not stamp a
23
+ * version AT ALL — and the name presumed a git SHA, which not every project deploys from.
21
24
  *
22
- * Three unrelated readers need the same fact, so it belongs to the framework, not to a backend:
23
- * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`;
24
- * - the winston backend, to stamp `svcName` (which it never had);
25
+ * Switching backends silently changed which fields your logs carried. Several unrelated readers
26
+ * need these same two facts, so they belong to the framework, not to a backend:
27
+ * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`, and to stamp `version`;
28
+ * - the winston backend, to stamp `svcName` + `version`;
25
29
  * - {@link WebpiecesCoreHeaders.REQUEST_ID_SOURCE}, to record WHO minted a request id.
26
30
  *
27
- * FAIL FAST, AT STARTUP. {@link getName} throws, and the logger factories + `setupRuntime` call it
28
- * while the process is booting so a forgotten `setName` kills the deploy (the revision never goes
29
- * healthy) instead of quietly shipping unnamed logs. Nothing on the REQUEST path may throw over
30
- * this: a missing log field must never 500 live traffic, so per-request readers use
31
- * {@link tryGetName} and simply omit the field. See {@link tryGetName}.
31
+ * VERSION IS OPAQUE. It is whatever string identifies THIS build a git SHA, a semver tag, a CI
32
+ * build number. webpieces neither parses nor derives it; the app decides where it comes from (a
33
+ * generated file, an env var, a Docker build arg) and passes it here.
34
+ *
35
+ * FAIL FAST, AT STARTUP. {@link getName} / {@link getVersion} throw, and the logger factories +
36
+ * `setupRuntime` call them while the process is booting — so a forgotten `setInfo` kills the deploy
37
+ * (the revision never goes healthy) instead of quietly shipping logs that cannot say which build
38
+ * emitted them. Nothing on the REQUEST path may throw over this: a missing log field must never 500
39
+ * live traffic, so per-request readers use {@link tryGetName} / {@link tryGetVersion} and simply
40
+ * omit the field.
32
41
  */
33
42
  class ServiceInfo {
34
43
  /** This service's name. Process-global; set once at startup. */
35
44
  static svcName;
45
+ /** This build's version — an opaque app-chosen string. Process-global; set once at startup. */
46
+ static svcVersion;
36
47
  /**
37
- * Name this service. Call it FIRST at startup — before constructing the logger factory, which
38
- * reads it during its own constructor.
48
+ * Identify this service. Call it FIRST at startup — before constructing the logger factory,
49
+ * which reads both values during its own constructor.
39
50
  *
40
- * LAST CALL WINS, deliberately. A real deployment names itself once, but an in-process test can
41
- * legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a
42
- * "one process = one name" rule would reject a case that genuinely exists. Since a logger
43
- * factory captures the name in its own constructor, each server built that way still keeps the
44
- * name that was set when IT was built.
51
+ * LAST CALL WINS, deliberately. A real deployment identifies itself once, but an in-process test
52
+ * can legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a
53
+ * "one process = one service" rule would reject a case that genuinely exists. Since a logger
54
+ * factory captures both values in its own constructor, each server built that way still keeps
55
+ * what was set when IT was built.
45
56
  *
46
- * @throws Error on a blank name — that is always a bug, never a use case.
57
+ * @param name - this service's name, e.g. 'my-service'.
58
+ * @param version - the opaque identifier of THIS build (git SHA, semver, CI build number).
59
+ * @throws Error on a blank name or version — that is always a bug, never a use case.
47
60
  */
48
61
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
49
- static setName(name) {
62
+ static setInfo(name, version) {
50
63
  if (!name || !name.trim()) {
51
- throw new Error('ServiceInfo.setName(...) requires a non-blank service name.');
64
+ throw new Error('ServiceInfo.setInfo(...) requires a non-blank service name.');
65
+ }
66
+ if (!version || !version.trim()) {
67
+ throw new Error('ServiceInfo.setInfo(...) requires a non-blank version. It is opaque to webpieces — ' +
68
+ 'a git SHA, a semver tag, a CI build number — but every deployed build must be able ' +
69
+ 'to say which build it is.');
52
70
  }
53
71
  ServiceInfo.svcName = name;
72
+ ServiceInfo.svcVersion = version;
54
73
  }
55
74
  /**
56
75
  * This service's name, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime) —
57
76
  * they run while booting, so throwing here fails the deploy rather than live traffic.
58
77
  *
59
- * @throws Error if {@link setName} was never called.
78
+ * @throws Error if {@link setInfo} was never called.
60
79
  */
61
80
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
62
81
  static getName() {
63
82
  if (!ServiceInfo.svcName) {
64
- throw new Error('ServiceInfo.setName(...) has not been called. Name this service at startup, ' +
65
- 'BEFORE constructing the logger factory (it reads the name in its constructor):\n' +
66
- " ServiceInfo.setName('my-service');\n" +
67
- 'It names every log line and records which service minted a request id ' +
68
- '(requestIdSource).');
83
+ throw new Error(ServiceInfo.notSetMessage());
69
84
  }
70
85
  return ServiceInfo.svcName;
71
86
  }
87
+ /**
88
+ * This build's version, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime),
89
+ * for the same reason as {@link getName}.
90
+ *
91
+ * @throws Error if {@link setInfo} was never called.
92
+ */
93
+ // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
94
+ static getVersion() {
95
+ if (!ServiceInfo.svcVersion) {
96
+ throw new Error(ServiceInfo.notSetMessage());
97
+ }
98
+ return ServiceInfo.svcVersion;
99
+ }
72
100
  /**
73
101
  * This service's name, or undefined when unset. For the REQUEST path, which must NOT throw: a
74
102
  * server that booted has already passed the {@link getName} check in `setupRuntime`, so a real
@@ -79,10 +107,34 @@ class ServiceInfo {
79
107
  static tryGetName() {
80
108
  return ServiceInfo.svcName;
81
109
  }
110
+ /**
111
+ * This build's version, or undefined when unset. For the REQUEST path, which must NOT throw —
112
+ * same reasoning as {@link tryGetName}.
113
+ */
114
+ // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
115
+ static tryGetVersion() {
116
+ return ServiceInfo.svcVersion;
117
+ }
82
118
  /** Reset — for tests, mirroring {@link ClientRegistry.clear}. */
83
119
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
84
120
  static clear() {
85
121
  ServiceInfo.svcName = undefined;
122
+ ServiceInfo.svcVersion = undefined;
123
+ }
124
+ /**
125
+ * The one actionable "you forgot to call setInfo" message. Shared by {@link getName} and
126
+ * {@link getVersion}: setInfo sets BOTH, so either being missing has the identical cause and
127
+ * the identical fix — telling the caller only about the half they happened to read first would
128
+ * send them back for a second round.
129
+ */
130
+ // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected
131
+ static notSetMessage() {
132
+ return ('ServiceInfo.setInfo(...) has not been called. Identify this service at startup, ' +
133
+ 'BEFORE constructing the logger factory (it reads name+version in its constructor):\n' +
134
+ " ServiceInfo.setInfo('my-service', '2.1.0');\n" +
135
+ 'The name+version stamp every log line (so you can tell WHICH BUILD emitted a line), ' +
136
+ 'and the name records which service minted a request id (requestIdSource). The version ' +
137
+ 'is opaque — a git SHA, a semver tag, a CI build number, whatever identifies your build.');
86
138
  }
87
139
  }
88
140
  exports.ServiceInfo = ServiceInfo;
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceInfo.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ServiceInfo.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAa,WAAW;IACpB,gEAAgE;IACxD,MAAM,CAAC,OAAO,CAAqB;IAE3C;;;;;;;;;;;OAWG;IACH,4JAA4J;IAC5J,MAAM,CAAC,OAAO,CAAC,IAAY;QACvB,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACnF,CAAC;QACD,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IAC5J,MAAM,CAAC,OAAO;QACV,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACX,8EAA8E;gBAC9E,kFAAkF;gBAClF,0CAA0C;gBAC1C,wEAAwE;gBACxE,oBAAoB,CACvB,CAAC;QACN,CAAC;QACD,OAAO,WAAW,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IAC5J,MAAM,CAAC,UAAU;QACb,OAAO,WAAW,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED,iEAAiE;IACjE,4JAA4J;IAC5J,MAAM,CAAC,KAAK;QACR,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC;IACpC,CAAC;CACJ;AA5DD,kCA4DC","sourcesContent":["/**\n * ServiceInfo - the ONE answer to \"what service am I?\", for every part of webpieces that needs it.\n *\n * Configured like {@link HeaderRegistry} / {@link ClientRegistry} / LogManager — populated once at\n * startup, then globally accessible with NO DI wiring. Browser-safe (no `process.env`, no node-only\n * deps), which is why it lives in core-util beside {@link ClientRegistry}.\n *\n * ```ts\n * // startup, FIRST — before the logger factory is constructed (it reads this):\n * ServiceInfo.setName('my-service');\n * ```\n *\n * WHY THIS EXISTS. The name used to live on `BunyanFactoryOptions`, which made it an artifact of\n * WHICH LOGGING LIBRARY YOU PICKED rather than a fact about the service:\n * - bunyan REQUIRES a root-logger name (`options.name (string) is required` — bunyan's own\n * TypeError), so a bunyan app was forced to supply one.\n * - winston has no such concept, so a winston app supplied nothing and shipped its logs UNNAMED.\n *\n * Three unrelated readers need the same fact, so it belongs to the framework, not to a backend:\n * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`;\n * - the winston backend, to stamp `svcName` (which it never had);\n * - {@link WebpiecesCoreHeaders.REQUEST_ID_SOURCE}, to record WHO minted a request id.\n *\n * FAIL FAST, AT STARTUP. {@link getName} throws, and the logger factories + `setupRuntime` call it\n * while the process is booting — so a forgotten `setName` kills the deploy (the revision never goes\n * healthy) instead of quietly shipping unnamed logs. Nothing on the REQUEST path may throw over\n * this: a missing log field must never 500 live traffic, so per-request readers use\n * {@link tryGetName} and simply omit the field. See {@link tryGetName}.\n */\nexport class ServiceInfo {\n /** This service's name. Process-global; set once at startup. */\n private static svcName: string | undefined;\n\n /**\n * Name this service. Call it FIRST at startup — before constructing the logger factory, which\n * reads it during its own constructor.\n *\n * LAST CALL WINS, deliberately. A real deployment names itself once, but an in-process test can\n * legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a\n * \"one process = one name\" rule would reject a case that genuinely exists. Since a logger\n * factory captures the name in its own constructor, each server built that way still keeps the\n * name that was set when IT was built.\n *\n * @throws Error on a blank name — that is always a bug, never a use case.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static setName(name: string): void {\n if (!name || !name.trim()) {\n throw new Error('ServiceInfo.setName(...) requires a non-blank service name.');\n }\n ServiceInfo.svcName = name;\n }\n\n /**\n * This service's name, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime) —\n * they run while booting, so throwing here fails the deploy rather than live traffic.\n *\n * @throws Error if {@link setName} was never called.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static getName(): string {\n if (!ServiceInfo.svcName) {\n throw new Error(\n 'ServiceInfo.setName(...) has not been called. Name this service at startup, ' +\n 'BEFORE constructing the logger factory (it reads the name in its constructor):\\n' +\n \" ServiceInfo.setName('my-service');\\n\" +\n 'It names every log line and records which service minted a request id ' +\n '(requestIdSource).',\n );\n }\n return ServiceInfo.svcName;\n }\n\n /**\n * This service's name, or undefined when unset. For the REQUEST path, which must NOT throw: a\n * server that booted has already passed the {@link getName} check in `setupRuntime`, so a real\n * request always finds a name here. Only a unit test driving the context directly can see\n * undefined — and there, omitting a log field beats exploding.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static tryGetName(): string | undefined {\n return ServiceInfo.svcName;\n }\n\n /** Reset — for tests, mirroring {@link ClientRegistry.clear}. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static clear(): void {\n ServiceInfo.svcName = undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"ServiceInfo.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ServiceInfo.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAa,WAAW;IACpB,gEAAgE;IACxD,MAAM,CAAC,OAAO,CAAqB;IAE3C,+FAA+F;IACvF,MAAM,CAAC,UAAU,CAAqB;IAE9C;;;;;;;;;;;;;OAaG;IACH,4JAA4J;IAC5J,MAAM,CAAC,OAAO,CAAC,IAAY,EAAE,OAAe;QACxC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACX,qFAAqF;gBACrF,qFAAqF;gBACrF,2BAA2B,CAC9B,CAAC;QACN,CAAC;QACD,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAC3B,WAAW,CAAC,UAAU,GAAG,OAAO,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IAC5J,MAAM,CAAC,OAAO;QACV,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,WAAW,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IAC5J,MAAM,CAAC,UAAU;QACb,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,WAAW,CAAC,UAAU,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IAC5J,MAAM,CAAC,UAAU;QACb,OAAO,WAAW,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,4JAA4J;IAC5J,MAAM,CAAC,aAAa;QAChB,OAAO,WAAW,CAAC,UAAU,CAAC;IAClC,CAAC;IAED,iEAAiE;IACjE,4JAA4J;IAC5J,MAAM,CAAC,KAAK;QACR,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC;QAChC,WAAW,CAAC,UAAU,GAAG,SAAS,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,4JAA4J;IACpJ,MAAM,CAAC,aAAa;QACxB,OAAO,CACH,kFAAkF;YAClF,sFAAsF;YACtF,mDAAmD;YACnD,sFAAsF;YACtF,wFAAwF;YACxF,yFAAyF,CAC5F,CAAC;IACN,CAAC;CACJ;AA7GD,kCA6GC","sourcesContent":["/**\n * ServiceInfo - the ONE answer to \"what service am I, and which build of it?\", for every part of\n * webpieces that needs it.\n *\n * Configured like {@link HeaderRegistry} / {@link ClientRegistry} / LogManager — populated once at\n * startup, then globally accessible with NO DI wiring. Browser-safe (no `process.env`, no node-only\n * deps), which is why it lives in core-util beside {@link ClientRegistry}.\n *\n * ```ts\n * // startup, FIRST — before the logger factory is constructed (it reads this):\n * ServiceInfo.setInfo('my-service', '2.1.0');\n * ```\n *\n * WHY THIS EXISTS. Both facts used to be artifacts of WHICH LOGGING LIBRARY YOU PICKED rather than\n * facts about the service:\n * - the name lived on `BunyanFactoryOptions`, because bunyan REQUIRES a root-logger name\n * (`options.name (string) is required` — bunyan's own TypeError). winston has no such concept, so\n * a winston app shipped its logs UNNAMED.\n * - the version lived on `WinstonFactoryOptions` as `svcGitHash`, so a bunyan app could not stamp a\n * version AT ALL — and the name presumed a git SHA, which not every project deploys from.\n *\n * Switching backends silently changed which fields your logs carried. Several unrelated readers\n * need these same two facts, so they belong to the framework, not to a backend:\n * - the bunyan backend, to satisfy bunyan's mandatory root-logger `name`, and to stamp `version`;\n * - the winston backend, to stamp `svcName` + `version`;\n * - {@link WebpiecesCoreHeaders.REQUEST_ID_SOURCE}, to record WHO minted a request id.\n *\n * VERSION IS OPAQUE. It is whatever string identifies THIS build — a git SHA, a semver tag, a CI\n * build number. webpieces neither parses nor derives it; the app decides where it comes from (a\n * generated file, an env var, a Docker build arg) and passes it here.\n *\n * FAIL FAST, AT STARTUP. {@link getName} / {@link getVersion} throw, and the logger factories +\n * `setupRuntime` call them while the process is booting — so a forgotten `setInfo` kills the deploy\n * (the revision never goes healthy) instead of quietly shipping logs that cannot say which build\n * emitted them. Nothing on the REQUEST path may throw over this: a missing log field must never 500\n * live traffic, so per-request readers use {@link tryGetName} / {@link tryGetVersion} and simply\n * omit the field.\n */\nexport class ServiceInfo {\n /** This service's name. Process-global; set once at startup. */\n private static svcName: string | undefined;\n\n /** This build's version — an opaque app-chosen string. Process-global; set once at startup. */\n private static svcVersion: string | undefined;\n\n /**\n * Identify this service. Call it FIRST at startup — before constructing the logger factory,\n * which reads both values during its own constructor.\n *\n * LAST CALL WINS, deliberately. A real deployment identifies itself once, but an in-process test\n * can legitimately boot TWO services back-to-back (see the app-example e2e two-server flow), so a\n * \"one process = one service\" rule would reject a case that genuinely exists. Since a logger\n * factory captures both values in its own constructor, each server built that way still keeps\n * what was set when IT was built.\n *\n * @param name - this service's name, e.g. 'my-service'.\n * @param version - the opaque identifier of THIS build (git SHA, semver, CI build number).\n * @throws Error on a blank name or version — that is always a bug, never a use case.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static setInfo(name: string, version: string): void {\n if (!name || !name.trim()) {\n throw new Error('ServiceInfo.setInfo(...) requires a non-blank service name.');\n }\n if (!version || !version.trim()) {\n throw new Error(\n 'ServiceInfo.setInfo(...) requires a non-blank version. It is opaque to webpieces — ' +\n 'a git SHA, a semver tag, a CI build number — but every deployed build must be able ' +\n 'to say which build it is.',\n );\n }\n ServiceInfo.svcName = name;\n ServiceInfo.svcVersion = version;\n }\n\n /**\n * This service's name, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime) —\n * they run while booting, so throwing here fails the deploy rather than live traffic.\n *\n * @throws Error if {@link setInfo} was never called.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static getName(): string {\n if (!ServiceInfo.svcName) {\n throw new Error(ServiceInfo.notSetMessage());\n }\n return ServiceInfo.svcName;\n }\n\n /**\n * This build's version, REQUIRED. For STARTUP callers only (the logger factories, setupRuntime),\n * for the same reason as {@link getName}.\n *\n * @throws Error if {@link setInfo} was never called.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static getVersion(): string {\n if (!ServiceInfo.svcVersion) {\n throw new Error(ServiceInfo.notSetMessage());\n }\n return ServiceInfo.svcVersion;\n }\n\n /**\n * This service's name, or undefined when unset. For the REQUEST path, which must NOT throw: a\n * server that booted has already passed the {@link getName} check in `setupRuntime`, so a real\n * request always finds a name here. Only a unit test driving the context directly can see\n * undefined — and there, omitting a log field beats exploding.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static tryGetName(): string | undefined {\n return ServiceInfo.svcName;\n }\n\n /**\n * This build's version, or undefined when unset. For the REQUEST path, which must NOT throw —\n * same reasoning as {@link tryGetName}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static tryGetVersion(): string | undefined {\n return ServiceInfo.svcVersion;\n }\n\n /** Reset — for tests, mirroring {@link ClientRegistry.clear}. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static clear(): void {\n ServiceInfo.svcName = undefined;\n ServiceInfo.svcVersion = undefined;\n }\n\n /**\n * The one actionable \"you forgot to call setInfo\" message. Shared by {@link getName} and\n * {@link getVersion}: setInfo sets BOTH, so either being missing has the identical cause and\n * the identical fix — telling the caller only about the half they happened to read first would\n * send them back for a second round.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n private static notSetMessage(): string {\n return (\n 'ServiceInfo.setInfo(...) has not been called. Identify this service at startup, ' +\n 'BEFORE constructing the logger factory (it reads name+version in its constructor):\\n' +\n \" ServiceInfo.setInfo('my-service', '2.1.0');\\n\" +\n 'The name+version stamp every log line (so you can tell WHICH BUILD emitted a line), ' +\n 'and the name records which service minted a request id (requestIdSource). The version ' +\n 'is opaque — a git SHA, a semver tag, a CI build number, whatever identifies your build.'\n );\n }\n}\n"]}