syntropylog 0.12.1 → 0.12.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.3
4
+
5
+ ### Patch Changes
6
+
7
+ - **SECURITY.md:** Document supply-chain alerts that may appear on the **yaml** package: (1) **URLs** — documentation links only (caniuse, MDN), no runtime network requests; (2) **Behavioral (medium)** — stringify/serialization analysis, vendor states no malicious activity. Clarifies that SyntropyLog uses only `parse` with schema `json`.
8
+
9
+ ## 0.12.2
10
+
11
+ ### Patch Changes
12
+
13
+ - **Socket / security:** Addressed Socket.dev alerts and clarified behavior in docs. Native addon no longer uses shell (`execSync`): resolves `ldd` path via `PATH` and `fs.existsSync` only. Documented filesystem access (native loader + `loadLoggerConfig`), environment variables (only `PATH` in optional native addon), dynamic require (static paths only), and URL/network (no runtime URLs). SECURITY.md now lists the single env var read (`PATH`) and README Security & Compliance section covers network, env, dynamic require, and filesystem.
14
+
15
+ **Docs - Universal Adapter:** README section 3 reworked: mapping is defined once with `UniversalLogFormatter` (outside the executor); executor receives the mapped object and can send it to multiple backends (e.g. Prisma, TypeORM, Mongoose) in one block. Single example shows one mapping → one object → three destinations with `Promise.all`.
16
+
17
+ - **YAML / supply chain:** Replaced **js-yaml** with **yaml** (eemeli/yaml). The new dependency has no external packages (no argparse), removing the transitive alerts for URLs, filesystem, and env vars that came from js-yaml’s CLI helper. `loadLoggerConfig` now uses `parse(..., { schema: 'json' })` for safe parsing.
18
+
3
19
  ## 0.12.0
4
20
 
5
21
  First release after 0.11.3. Includes all framework refinements validated end-to-end with the examples repo (0.11.4 was never published to npm).
@@ -151,7 +167,7 @@ _Nothing at the moment._
151
167
 
152
168
  ### Security
153
169
 
154
- - **loadLoggerConfig**: Use js-yaml's `JSON_SCHEMA` when parsing YAML files to avoid prototype pollution and dangerous types. Use only with configuration files under deployment team control.
170
+ - **loadLoggerConfig**: Use the **yaml** package (eemeli/yaml) with schema `json` when parsing YAML files to avoid prototype pollution and dangerous types; **yaml** has no external dependencies (no argparse). Use only with configuration files under deployment team control.
155
171
 
156
172
  ### Fixed
157
173
 
package/CONTRIBUTING.md CHANGED
@@ -88,27 +88,47 @@ We welcome code contributions! To contribute code, please follow these steps:
88
88
 
89
89
  ## Release process (maintainers)
90
90
 
91
- Guía detallada: [docs/PREPARAR_PUBLICACION.md](docs/PREPARAR_PUBLICACION.md).
91
+ Detailed guide: [docs/PREPARAR_PUBLICACION.md](docs/PREPARAR_PUBLICACION.md) (Spanish).
92
92
 
93
- **Ramas auxiliares (feature, develop, etc.):** en cada push y en los PRs se ejecuta solo el workflow **CI** (lint, build, tests, benchmark con addon nativo). No se publica nada en npm. Así puedes validar que todo pase antes de integrar a `main`.
93
+ **Feature and develop branches:** On every push and on PRs, only the **CI** workflow runs (lint, build, tests, benchmark with native addon). Nothing is published to npm. This lets you validate everything before merging into `main`.
94
94
 
95
- **Solo al hacer push a `main`** se ejecuta el workflow **Release** (build del addon en todas las plataformas y, si hay changesets, versionado/publicación en npm).
95
+ **Only when pushing to `main`** does the **Release** workflow run (build the addon on all platforms and, if there are changesets, versioning/publishing to npm).
96
96
 
97
- Releases use [Changesets](https://github.com/changesets/changesets) and GitHub Actions. **Para que la versión suba en npm, siempre tienes que hacer una de estas dos cosas:**
97
+ Releases use [Changesets](https://github.com/changesets/changesets) and GitHub Actions. **To have a new version published to npm, you must do one of the following:**
98
98
 
99
- ### Opción A: Usar el PR que crea el action
99
+ ### Option A: Use the PR created by the action
100
100
 
101
- 1. **Añade un changeset** al cambiar la librería: `pnpm changeset` (elige tipo de versión y describe el cambio).
102
- 2. **Push a `main`**. El workflow crea un PR **"Version Packages"** con el bump (ej. 0.9.12 → 0.9.13) y el CHANGELOG actualizado. **Todavía no publica en npm.**
103
- 3. **Mergea ese PR** en `main`. Ese merge vuelve a disparar el workflow y **ahí sí** se ejecuta `publish` y la nueva versión aparece en npm.
101
+ 1. **Add a changeset** when changing the library: `pnpm changeset` (choose version bump type and describe the change).
102
+ 2. **Push to `main`**. The workflow creates a **"Version Packages"** PR with the version bump (e.g. 0.9.12 → 0.9.13) and updated CHANGELOG. **Nothing is published to npm yet.**
103
+ 3. **Merge that PR** into `main`. That merge triggers the workflow again and **then** `publish` runs and the new version appears on npm.
104
104
 
105
- ### Opción B: Subir la versión a mano (sin PR)
105
+ ### Option B: Bump the version manually (no PR)
106
106
 
107
- 1. Con changesets ya en el repo, en local: `pnpm run version-packages`. Eso actualiza `package.json`, CHANGELOG y borra los changesets.
108
- 2. **Commit** (package.json, CHANGELOG.md, y los .changeset/*.md borrados) y **push a `main`**.
109
- 3. El workflow corre, no hay changesets que aplicar, y ejecuta **publish** → la versión sube a npm.
107
+ 1. With changesets already in the repo, run locally: `pnpm run version-packages`. This updates `package.json`, CHANGELOG, and removes the consumed changesets.
108
+ 2. **Commit** (package.json, CHANGELOG.md, and the removed .changeset/*.md files) and **push to `main`**.
109
+ 3. The workflow runs, there are no changesets to apply, and it runs **publish** → the new version is published to npm.
110
110
 
111
- En ambos casos, **main** y **npm** quedan con la misma versión.
111
+ In both cases, **main** and **npm** end up with the same version.
112
+
113
+ ## Repository size and what not to commit
114
+
115
+ The repo should stay small so clones are fast. The following are in `.gitignore` and **must not** be committed:
116
+
117
+ - `node_modules/`, `dist/`, `coverage/`
118
+ - `assets/` (images; the README uses an external logo URL)
119
+ - `docs/`, `.cursor/`, `.turbo`
120
+
121
+ **If `assets/` or `coverage/` are already tracked**, remove them from Git (files stay on disk, only tracking is removed). Check first with `git ls-files assets/ coverage/`; if that lists files, run:
122
+
123
+ ```bash
124
+ git rm -r --cached assets/
125
+ git rm -r --cached coverage/
126
+ git commit -m "chore: stop tracking assets and coverage"
127
+ ```
128
+
129
+ If the path is not tracked, `git rm --cached` will report "did not match any file(s)" — that’s expected; nothing to do.
130
+
131
+ The clone size (~36 MB) is mostly **Git history** (old copies of heavy files). To reduce it you would need to rewrite history (e.g. `git filter-repo` or BFG to remove `assets/` from the past). That changes commit hashes and requires a force-push; only do it if the team agrees.
112
132
 
113
133
  ## Getting Help
114
134
 
package/README.md CHANGED
@@ -160,33 +160,86 @@ await syntropyLog.init({
160
160
 
161
161
  ## 3. Universal Adapter — log to any backend
162
162
 
163
- **What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in; you receive one structured object per log (already masked).
163
+ **What:** Send each log to PostgreSQL, MongoDB, Elasticsearch, S3, etc. by implementing a single `executor`. No vendor lock-in. You define **once** how the log entry maps to your schema; the executor only receives that mapped object and persists it (ORM, raw client, HTTP). If `executor` throws, SyntropyLog logs the error and continues (Silent Observer).
164
164
 
165
- **How:** Use `AdapterTransport` + `UniversalAdapter`:
165
+ **How:** Use `AdapterTransport` + `UniversalAdapter` + `UniversalLogFormatter`. Three steps:
166
+
167
+ ---
168
+
169
+ ### 1. Define the mapping (outside the executor)
170
+
171
+ Define how each log entry field maps to your persistence shape. One place, no repetition in code. Use `UniversalLogFormatter` with a `mapping` object: keys = your schema (e.g. DB columns), values = path in the log entry.
172
+
173
+ | Log entry path | Meaning |
174
+ |----------------|--------|
175
+ | `level` | Log level |
176
+ | `message` | Message string |
177
+ | `serviceName` | From init config |
178
+ | `correlationId` | From context |
179
+ | `timestamp` | ISO string |
180
+ | `meta` | Merged metadata (use as payload/JSON column) |
181
+
182
+ Example: map to a table with columns `level`, `message`, `serviceName`, `correlationId`, `payload`, `timestamp`.
183
+
184
+ ```typescript
185
+ import { UniversalLogFormatter } from 'syntropylog';
186
+
187
+ const logToDbMapping = {
188
+ level: 'level',
189
+ message: 'message',
190
+ serviceName: 'serviceName',
191
+ correlationId: 'correlationId',
192
+ payload: 'meta',
193
+ timestamp: 'timestamp',
194
+ };
195
+
196
+ const formatter = new UniversalLogFormatter({ mapping: logToDbMapping });
197
+ ```
198
+
199
+ The formatter turns every log entry into an object with exactly those keys. Change the mapping here when your schema changes; the executor stays the same.
200
+
201
+ ---
202
+
203
+ ### 2. The object the executor receives
204
+
205
+ When you attach this formatter to the transport, the `executor` receives **that mapped object** (not the raw log entry). So the executor only sees something like `{ level, message, serviceName, correlationId, payload, timestamp }`. Your only job in the executor is to **persist** that object.
206
+
207
+ ---
208
+
209
+ ### 3. Sending that object to your backend
210
+
211
+ One mapping produces one object shape. You can send **that same object** to as many backends as you want in a single executor: same `data`, different destinations (e.g. Prisma, TypeORM, Mongoose, Elasticsearch, S3). No field list — the shape comes from the mapping.
212
+
213
+ **Example: one mapped object → three destinations**
166
214
 
167
215
  ```typescript
168
216
  import { AdapterTransport, UniversalAdapter } from 'syntropylog';
217
+ import { prisma } from './prisma';
218
+ import { getRepository } from 'typeorm';
219
+ import { SystemLog as TypeOrmSystemLog } from './entities/SystemLog';
220
+ import { SystemLogModel } from './models/SystemLog';
169
221
 
170
222
  const dbTransport = new AdapterTransport({
171
223
  name: 'db',
224
+ formatter,
172
225
  adapter: new UniversalAdapter({
173
- executor: async (logEntry) => {
174
- await prisma.systemLog.create({
175
- data: {
176
- level: logEntry.level,
177
- message: logEntry.message,
178
- serviceName: logEntry.serviceName,
179
- correlationId: logEntry.correlationId,
180
- payload: logEntry.meta,
181
- timestamp: new Date(logEntry.timestamp),
182
- },
183
- });
226
+ executor: async (data) => {
227
+ const row = {
228
+ ...data,
229
+ timestamp: new Date(data.timestamp as string),
230
+ };
231
+ // Same object, three destinations (e.g. Postgres + TypeORM DB + MongoDB)
232
+ await Promise.all([
233
+ prisma.systemLog.create({ data: row }),
234
+ getRepository(TypeOrmSystemLog).save(row),
235
+ SystemLogModel.create(row),
236
+ ]);
184
237
  },
185
238
  }),
186
239
  });
187
240
  ```
188
241
 
189
- `logEntry` has: `level`, `message`, `serviceName`, `correlationId`, `timestamp`, `meta`. If `executor` throws, SyntropyLog logs the error and continues (Silent Observer).
242
+ Use one transport and one executor; add or remove destinations in that same block. Use this transport in your `init()` (e.g. in `logger.transports` or `logger.transportList` + `logger.env`).
190
243
 
191
244
  ---
192
245
 
@@ -569,6 +622,17 @@ Secure this route (e.g. auth, internal only). When debugging in a POD is finishe
569
622
 
570
623
  ## Security & Compliance
571
624
 
625
+ **Network & URLs:** This package does not contact any external URLs or IPs at runtime. The only network I/O is what you configure (e.g. your `executor` sending logs to your PostgreSQL, Elasticsearch, or API). URLs in this README and in `package.json` are documentation and metadata only (badges, repository links); they are not used for outbound connections.
626
+
627
+ **Environment variables:** The main package does not read any. The optional native addon (`syntropylog-native`) reads only `PATH` on Linux to locate the `ldd` binary for musl/glibc detection. No credentials or other env vars are read. See [SECURITY.md](./SECURITY.md) for the full list.
628
+
629
+ **Dynamic require:** The package does not use dynamic `require(variable)` or user-controlled paths. Module paths are static string literals (e.g. `require('syntropylog-native')`, `require('./index.js')`). The native addon loader chooses one of several fixed `.node` paths based on platform/arch; no user input is used to build require paths.
630
+
631
+ **Filesystem access:** The package only reads the files described below; it does not scan or read arbitrary paths.
632
+
633
+ - **Native addon loader** (`syntropylog-native`): Reads only (1) the presence of native `.node` binaries inside the package’s own directory (`__dirname`) to choose the correct build for the current OS/arch, and (2) on Linux only, the system `ldd` binary (e.g. `/usr/bin/ldd`) to detect musl vs glibc. No user or application files are read.
634
+ - **Config loader** (`loadLoggerConfig`): Reads only paths you control. If you use it, it reads at most one file: either the path you pass in `opts.configPath`, or a file under `opts.configDir` with a name derived from `opts.defaultBase` and `opts.environment` (default: `./config/logger.yaml` or `./config/logger-{env}.yaml`). You can avoid filesystem access entirely by not calling `loadLoggerConfig` and passing config directly to `init()`.
635
+
572
636
  | Dynamically configurable | Fixed at init |
573
637
  |--------------------------|---------------|
574
638
  | Log level, additive masking rules, transports (debug: add console only for visual help in a POD; existing stay; `resetTransports()` to remove added) | Core masking config (`maskChar`, `maxDepth`), Redis/HTTP/broker |
package/dist/index.cjs CHANGED
@@ -2397,8 +2397,8 @@ class ConsoleTransport extends Transport {
2397
2397
  * @file src/logger/transports/optionalChalk.ts
2398
2398
  * @description Built-in chalk-like API using ANSI escape codes. No chalk dependency.
2399
2399
  * Used by ClassicConsoleTransport, PrettyConsoleTransport, CompactConsoleTransport, ColorfulConsoleTransport.
2400
- * Does not read process.env; pass disableColors from transport options (e.g. for NO_COLOR use
2401
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0').
2400
+ * This module does not read environment variables. Pass disableColors from transport options;
2401
+ * to respect the NO_COLOR convention, set disableColors from your app (e.g. from the NO_COLOR env var). See SECURITY.md.
2402
2402
  */
2403
2403
  const RESET = '\x1b[0m';
2404
2404
  function wrap(s, codes) {
@@ -2450,9 +2450,8 @@ let cachedWithColors = null;
2450
2450
  let cachedNoColors = null;
2451
2451
  /**
2452
2452
  * Returns a chalk-like instance using built-in ANSI colors. No external chalk dependency.
2453
- * Does not read process.env. Pass disableColors from transport options; when true, output has no colors.
2454
- * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, pass
2455
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0'.
2453
+ * Does not read environment variables. Pass disableColors from transport options; when true, output has no colors.
2454
+ * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, derive disableColors in your app and pass it here. See SECURITY.md.
2456
2455
  */
2457
2456
  function getOptionalChalk(disableColors) {
2458
2457
  if (disableColors) {
package/dist/index.d.ts CHANGED
@@ -796,8 +796,8 @@ declare class ConsoleTransport extends Transport {
796
796
  * @file src/logger/transports/optionalChalk.ts
797
797
  * @description Built-in chalk-like API using ANSI escape codes. No chalk dependency.
798
798
  * Used by ClassicConsoleTransport, PrettyConsoleTransport, CompactConsoleTransport, ColorfulConsoleTransport.
799
- * Does not read process.env; pass disableColors from transport options (e.g. for NO_COLOR use
800
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0').
799
+ * This module does not read environment variables. Pass disableColors from transport options;
800
+ * to respect the NO_COLOR convention, set disableColors from your app (e.g. from the NO_COLOR env var). See SECURITY.md.
801
801
  */
802
802
  /** Chalk-like API: chainable style that returns wrapped string when called. */
803
803
  type ChalkLike = {
package/dist/index.mjs CHANGED
@@ -2375,8 +2375,8 @@ class ConsoleTransport extends Transport {
2375
2375
  * @file src/logger/transports/optionalChalk.ts
2376
2376
  * @description Built-in chalk-like API using ANSI escape codes. No chalk dependency.
2377
2377
  * Used by ClassicConsoleTransport, PrettyConsoleTransport, CompactConsoleTransport, ColorfulConsoleTransport.
2378
- * Does not read process.env; pass disableColors from transport options (e.g. for NO_COLOR use
2379
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0').
2378
+ * This module does not read environment variables. Pass disableColors from transport options;
2379
+ * to respect the NO_COLOR convention, set disableColors from your app (e.g. from the NO_COLOR env var). See SECURITY.md.
2380
2380
  */
2381
2381
  const RESET = '\x1b[0m';
2382
2382
  function wrap(s, codes) {
@@ -2428,9 +2428,8 @@ let cachedWithColors = null;
2428
2428
  let cachedNoColors = null;
2429
2429
  /**
2430
2430
  * Returns a chalk-like instance using built-in ANSI colors. No external chalk dependency.
2431
- * Does not read process.env. Pass disableColors from transport options; when true, output has no colors.
2432
- * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, pass
2433
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0'.
2431
+ * Does not read environment variables. Pass disableColors from transport options; when true, output has no colors.
2432
+ * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, derive disableColors in your app and pass it here. See SECURITY.md.
2434
2433
  */
2435
2434
  function getOptionalChalk(disableColors) {
2436
2435
  if (disableColors) {
@@ -35,9 +35,11 @@ export interface LoggerConfigLoaderOptions {
35
35
  * It does NOT read environment variables directly; all state must be passed via `opts`.
36
36
  * If no file is found, it returns an empty object, making the config file optional.
37
37
  *
38
- * **Security:** A restricted schema (JSON_SCHEMA) is used to avoid prototype pollution
38
+ * **Security:** A restricted schema (`json`) is used to avoid prototype pollution
39
39
  * and dangerous types. Only use with configuration files under deployment team
40
- * control (controlled paths and permissions).
40
+ * control (controlled paths and permissions). This function reads only the paths
41
+ * derived from opts (configPath, configDir, defaultBase, environment); no other
42
+ * filesystem access is performed.
41
43
  *
42
44
  * @param opts - Options to customize the loading behavior.
43
45
  * @returns A partial `LoggerOptions` object, or an empty object if no file is found.
@@ -2,8 +2,8 @@
2
2
  * @file src/logger/transports/optionalChalk.ts
3
3
  * @description Built-in chalk-like API using ANSI escape codes. No chalk dependency.
4
4
  * Used by ClassicConsoleTransport, PrettyConsoleTransport, CompactConsoleTransport, ColorfulConsoleTransport.
5
- * Does not read process.env; pass disableColors from transport options (e.g. for NO_COLOR use
6
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0').
5
+ * This module does not read environment variables. Pass disableColors from transport options;
6
+ * to respect the NO_COLOR convention, set disableColors from your app (e.g. from the NO_COLOR env var). See SECURITY.md.
7
7
  */
8
8
  /** Chalk-like API: chainable style that returns wrapped string when called. */
9
9
  export type ChalkLike = {
@@ -23,8 +23,7 @@ export type ChalkLike = {
23
23
  };
24
24
  /**
25
25
  * Returns a chalk-like instance using built-in ANSI colors. No external chalk dependency.
26
- * Does not read process.env. Pass disableColors from transport options; when true, output has no colors.
27
- * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, pass
28
- * disableColors: process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0'.
26
+ * Does not read environment variables. Pass disableColors from transport options; when true, output has no colors.
27
+ * When false, colors are used only if stdout is a TTY. To respect NO_COLOR, derive disableColors in your app and pass it here. See SECURITY.md.
29
28
  */
30
29
  export declare function getOptionalChalk(disableColors: boolean): ChalkLike;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syntropylog",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "engines": {
5
5
  "node": ">=20.0.0"
6
6
  },
@@ -140,7 +140,7 @@
140
140
  "provenance": true
141
141
  },
142
142
  "dependencies": {
143
- "js-yaml": "^4.1.1"
143
+ "yaml": "^2.8.2"
144
144
  },
145
145
  "optionalDependencies": {
146
146
  "syntropylog-native": "0.1.0"
@@ -153,7 +153,6 @@
153
153
  "@rollup/plugin-json": "^6.1.0",
154
154
  "@rollup/plugin-node-resolve": "^16.0.0",
155
155
  "@rollup/plugin-typescript": "^12.1.2",
156
- "@types/js-yaml": "^4.0.9",
157
156
  "@types/node": "^22.13.5",
158
157
  "@typescript-eslint/eslint-plugin": "8.56.1",
159
158
  "@typescript-eslint/parser": "8.56.1",