pinqloq 1.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `pinqloq` npm package are documented here. This package follows
4
+ [Semantic Versioning](https://semver.org). Its version numbers are independent of the .NET
5
+ `pinqloq` NuGet package (`sdk/ClientLogs.Client/`) — the two ship on separate cadences for the
6
+ same platform, and mirror each other's feature set rather than their version numbers.
7
+
8
+ ## 1.1.0 — 2026-09-08
9
+
10
+ **Added:**
11
+
12
+ - `PinqloqLogEntry.path` — a fixed, indexed field for the request path, matching the .NET SDK's
13
+ 4.3.0 wire contract change ([pinqponq/pinqloq#108](https://github.com/pinqponq/pinqloq/issues/108)).
14
+ The request-logging middleware fills it automatically; on a manual `enqueue` it stays unset
15
+ unless set explicitly.
16
+
17
+ **Changed:**
18
+
19
+ - `event` and `path` are no longer duplicated into `metadata`. The request-logging middleware
20
+ used to also write `metadata.event`, `metadata.path`, and `metadata.RequestPath` — those keys
21
+ are gone from newly-sent logs now that `event` and `path` travel as their own fixed, indexed
22
+ fields. A `metadata: { event: ... }` enricher still overrides the event title; it just no
23
+ longer leaves a copy behind in `metadata`.
24
+
25
+ If you built a saved filter or dashboard on `metadata.event`, `metadata.path`, or
26
+ `metadata.RequestPath`, switch it to the `Event` / `Path` fields directly. No other consumer
27
+ action is needed — existing code compiles unchanged.
28
+
29
+ ## 1.0.0 — 2026-09-04
30
+
31
+ **Added:**
32
+
33
+ - Initial release: feature parity with the .NET SDK's core surface.
34
+ - `createPinqloq(options)` — buffered, batched manual structured logging (`logger.enqueue` /
35
+ `logger.enqueueMany`), delivered to the same `/bulk` ingest endpoint the .NET SDK uses.
36
+ - `client.requestLogging(options)` — Express middleware for automatic HTTP request/response
37
+ logging: captures method/path/status/duration, request/response bodies (32KB cap) and headers,
38
+ resolves `deviceIdentifier` (selector → `device-identifier` header → global fallback, HTTP 400
39
+ if none resolve) and `correlationId` (`correlation-id` header → generated id).
40
+ - Redaction: a built-in, unconditional credential-name floor (password, token, Authorization,
41
+ ...) plus `redactFields` (name-based, any nesting depth) and `redactPaths` (whole-body/header
42
+ masking, the `[PinqloqRedactEndpoint]` equivalent). Fails closed on a sensitive body that can't
43
+ be parsed as JSON, matching the .NET SDK's behavior.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pinqponq
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Pinqloq (Node.js / Express)
2
+
3
+ [![npm](https://img.shields.io/npm/v/pinqloq)](https://www.npmjs.com/package/pinqloq)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Pinqloq is a structured logging and log shipping SDK for centralized application logs. It
7
+ captures HTTP request/response logs through Express middleware and sends manual application
8
+ events to the Pinqloq log management platform using in-memory buffering, batching, and HTTPS
9
+ delivery. This is the Node.js counterpart of the [.NET `pinqloq` SDK](https://www.nuget.org/packages/pinqloq) —
10
+ same platform, same wire protocol, idiomatic API on each side.
11
+
12
+ ## Features
13
+
14
+ - Automatic Express request/response logging
15
+ - Correlation id read from the caller's header, falling back to a generated request id
16
+ - Name-based redaction of sensitive fields, headers, and whole endpoints
17
+ - Manual structured application events
18
+ - Buffered and batched HTTPS delivery
19
+ - Graceful shutdown flush
20
+
21
+ ## Requirements
22
+
23
+ - Node.js 18 or later
24
+ - Express 4 or 5
25
+ - A Pinqloq project and secret key
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install pinqloq
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ Store your secret key in an environment variable or a secret manager. Do not hardcode production
36
+ credentials.
37
+
38
+ ```ts
39
+ import express from "express";
40
+ import { createPinqloq } from "pinqloq";
41
+
42
+ const pinqloq = createPinqloq({
43
+ secretKey: process.env.PINQLOQ_SECRET_KEY!,
44
+ apiLogsCollectionName: "myapp_api_logs",
45
+ deviceIdentifier: "myapp-instance-1"
46
+ });
47
+
48
+ const app = express();
49
+ app.use(express.json());
50
+
51
+ // Mount AFTER body-parsing middleware so req.body is populated when the log is captured.
52
+ app.use(
53
+ pinqloq.requestLogging({
54
+ excludePaths: ["/health"]
55
+ })
56
+ );
57
+
58
+ app.listen(3000);
59
+
60
+ process.on("SIGTERM", async () => {
61
+ await pinqloq.shutdown();
62
+ process.exit(0);
63
+ });
64
+ ```
65
+
66
+ The middleware captures the HTTP method, path, and status code as searchable metadata. The
67
+ request body, response body, request headers, and response headers go to the log detail as
68
+ `InputJson`, `OutputJson`, `RequestHeaders`, and `ResponseHeaders`. Bodies are truncated at 32 KB.
69
+
70
+ ## Manual Logging
71
+
72
+ Use `pinqloq.logger` to send structured application events:
73
+
74
+ ```ts
75
+ pinqloq.logger.enqueue({
76
+ event: "order.created",
77
+ deviceIdentifier: order.customerId,
78
+ logLevel: PinqloqLogLevel.Information,
79
+ logSourceType: PinqloqLogSourceType.Backend,
80
+ metadata: { orderId: order.id }
81
+ });
82
+ ```
83
+
84
+ `event` and `deviceIdentifier` are required on every entry. Leave `deviceIdentifier` unset on an
85
+ entry to inherit the global `deviceIdentifier` option. `enqueue` throws if an entry has no
86
+ `deviceIdentifier` and no global fallback is set — a missing required field fails loudly rather
87
+ than being silently dropped.
88
+
89
+ ## Add Request Metadata
90
+
91
+ By default the middleware reads the required `deviceIdentifier` from the `device-identifier`
92
+ request header automatically. Override how it is resolved with `resolveDeviceIdentifier`; the
93
+ override wins, and if it returns undefined/blank the middleware falls back to the
94
+ `device-identifier` header, then to the global `deviceIdentifier` option. If none of these resolve
95
+ a value, the middleware rejects the request with **HTTP 400** before it runs.
96
+
97
+ ```ts
98
+ app.use(
99
+ pinqloq.requestLogging({
100
+ excludePaths: ["/health"],
101
+ resolveDeviceIdentifier: (req) => req.user?.id,
102
+ resolveAppVersionName: (req) => req.header("x-app-version"),
103
+ metadata: {
104
+ userId: (req) => req.user?.id
105
+ }
106
+ })
107
+ );
108
+ ```
109
+
110
+ Use `metadata` for searchable values such as user and tenant IDs. Use `detail` for additional
111
+ drill-down information. The `event` key (the panel title) defaults to `"{method} {path}"` and can
112
+ be overridden via `metadata.event`.
113
+
114
+ ## Correlation ID
115
+
116
+ Every log carries a `correlationId` that ties together the records of a single request or flow.
117
+ The request-logging middleware fills it with no configuration: the caller's `correlation-id`
118
+ request header when present, otherwise a generated id.
119
+
120
+ ```ts
121
+ pinqloq.logger.enqueue({
122
+ event: "order.created",
123
+ deviceIdentifier: order.customerId,
124
+ correlationId: currentCorrelationId
125
+ });
126
+ ```
127
+
128
+ ## Redacting Sensitive Values
129
+
130
+ Request and response bodies and headers may contain credentials, tokens, or personal information.
131
+ Unlike the .NET SDK's attribute-based redaction (which relies on C# reflection over typed DTOs —
132
+ not available at runtime in TypeScript/Express), this SDK redacts by **name**:
133
+
134
+ - `redactFields` — case-insensitive field/header names masked with `*****REDACTED*****` wherever
135
+ they appear in a captured body or header, at any nesting depth.
136
+ - `redactPaths` — path prefixes (matched the same way as `excludePaths`) where every value in
137
+ `InputJson`, `OutputJson`, `RequestHeaders`, and `ResponseHeaders` is masked, keeping the JSON
138
+ structure and header names intact — the equivalent of the .NET SDK's `[PinqloqRedactEndpoint]`.
139
+
140
+ ```ts
141
+ app.use(
142
+ pinqloq.requestLogging({
143
+ redactFields: ["ssnLastFour"],
144
+ redactPaths: ["/payment"]
145
+ })
146
+ );
147
+ ```
148
+
149
+ A built-in, unconditional floor of common credential names (password, token, `Authorization`,
150
+ card numbers, ...) is always masked, even with no configuration — see
151
+ [`src/redaction/plan.ts`](src/redaction/plan.ts) for the full list.
152
+
153
+ ## Security and Reliability
154
+
155
+ Logs are buffered in memory and sent in batches. Buffered logs may be lost if the process is
156
+ terminated without a graceful shutdown — call `pinqloq.shutdown()` on `SIGTERM`/`SIGINT`.
157
+
158
+ Delivery failures are reported through `onFailed` callbacks and, even without callbacks, as
159
+ throttled `console.error`/`console.warn` output — never silently discarded, but also never
160
+ blocking. If your secret key is authorized for more than one collection, set
161
+ `apiLogsCollectionName` (or a per-entry `collectionName`); otherwise the batch is rejected.
162
+
163
+ ## Documentation
164
+
165
+ - [.NET SDK](https://www.nuget.org/packages/pinqloq) — the reference implementation for this
166
+ platform's wire protocol and feature set.
167
+ - [Ruby SDK](https://rubygems.org/gems/pinqloq), [Go SDK](https://pkg.go.dev/github.com/pinqponq/pinqloq-go-sdk) — the other implementations.
168
+ - [Full documentation](https://pinqloq.pinqponq.io/documentation.html)
169
+
170
+ ## License
171
+
172
+ MIT