@nwire/envelope 0.7.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Gefter / 200apps Ltd.
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,59 @@
1
+ # @nwire/envelope
2
+
3
+ > Universal message envelope — correlation, causation, tenant, user, version.
4
+
5
+ ## What it is
6
+
7
+ `MessageEnvelope` is the small bag of identifiers every message in Nwire carries — an HTTP request, a queue job, a scheduled tick, an action dispatched from a CLI. It makes chains of work debuggable, traceable, and tenant-scoped. Zero-dep leaf package: logger, dead-letter, queue, http, store all depend on this.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @nwire/envelope
13
+ ```
14
+
15
+ ## Standalone use
16
+
17
+ For developers who want a tiny correlation-id / causation-chain primitive in any TS app — no Nwire dependency required.
18
+
19
+ ```ts
20
+ import { seedEnvelope, deriveEnvelope } from "@nwire/envelope";
21
+
22
+ // At the edge — seed a new envelope for an incoming request
23
+ const root = seedEnvelope({
24
+ source: "http",
25
+ tenant: "acme",
26
+ userId: req.userId,
27
+ });
28
+
29
+ // Inside a handler — derive a child envelope for the next message
30
+ const child = deriveEnvelope(root, { source: "queue" });
31
+ // child.correlationId === root.correlationId (same chain)
32
+ // child.causationId === root.messageId (parent reference)
33
+ ```
34
+
35
+ ## Within nwire-app
36
+
37
+ For developers using this package as part of the Nwire stack. Already transitively present in every layer; you usually read `ctx.envelope` inside a handler. Touch this package directly when writing a custom transport or bus adapter that must construct envelopes.
38
+
39
+ ```ts
40
+ // inside a handler:
41
+ defineAction("recordWash", {
42
+ handler: async ({ input, ctx }) => {
43
+ ctx.logger.info({ stationId: input.stationId }, "wash recorded");
44
+ // ctx.envelope.correlationId, .tenant, .userId all attached automatically
45
+ },
46
+ });
47
+ ```
48
+
49
+ ## API
50
+
51
+ - `MessageEnvelope` — shape: `messageId`, `correlationId`, `causationId`, `tenant?`, `userId?`, `timestamp`, `version`, `source?`.
52
+ - `seedEnvelope(input)` — start a new chain at a system edge.
53
+ - `deriveEnvelope(parent, overrides?)` — descend one step; carries correlation, advances causation.
54
+ - `SeedEnvelopeInput` — caller-side type.
55
+
56
+ ## See also
57
+
58
+ - [Architecture sketch §05 — Foundation tier](../../architecture-sketch.html#packages)
59
+ - Sibling packages: [@nwire/messages](../nwire-messages), [@nwire/logger](../nwire-logger), [@nwire/queue](../nwire-queue)
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `@nwire/envelope` — the framework's universal message envelope.
3
+ *
4
+ * Every message in nwire — an HTTP request, a queue job, a scheduled tick,
5
+ * an action dispatched from a CLI — carries a `MessageEnvelope`. The
6
+ * envelope is what makes a chain of work debuggable, traceable, and tenant-
7
+ * scoped.
8
+ *
9
+ * messageId — unique to this individual message
10
+ * correlationId — ties a chain of related messages together
11
+ * causationId — the messageId of the message that caused this one
12
+ * tenant — tenant scope (multi-tenancy)
13
+ * userId — acting user (audit + authz)
14
+ * timestamp — ISO 8601 at envelope creation
15
+ * version — schema evolution
16
+ *
17
+ * Zero-dep core package. Logger, DLQ, queue, http, mongo all depend on this.
18
+ */
19
+ export { seedEnvelope, deriveEnvelope, type MessageEnvelope, type SeedEnvelopeInput, } from "./message-envelope.js";
20
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,YAAY,EACZ,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `@nwire/envelope` — the framework's universal message envelope.
3
+ *
4
+ * Every message in nwire — an HTTP request, a queue job, a scheduled tick,
5
+ * an action dispatched from a CLI — carries a `MessageEnvelope`. The
6
+ * envelope is what makes a chain of work debuggable, traceable, and tenant-
7
+ * scoped.
8
+ *
9
+ * messageId — unique to this individual message
10
+ * correlationId — ties a chain of related messages together
11
+ * causationId — the messageId of the message that caused this one
12
+ * tenant — tenant scope (multi-tenancy)
13
+ * userId — acting user (audit + authz)
14
+ * timestamp — ISO 8601 at envelope creation
15
+ * version — schema evolution
16
+ *
17
+ * Zero-dep core package. Logger, DLQ, queue, http, mongo all depend on this.
18
+ */
19
+ export { seedEnvelope, deriveEnvelope, } from "./message-envelope.js";
20
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,YAAY,EACZ,cAAc,GAGf,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Every message in the framework — an HTTP request entering a handler, an
3
+ * event flowing through the bus, a queued job, a scheduled tick — carries
4
+ * an envelope. The envelope is what makes a chain of work debuggable.
5
+ *
6
+ * messageId — unique to this individual message
7
+ * correlationId — ties a chain of related messages together; preserved
8
+ * across all handlers triggered by an originating event
9
+ * causationId — the messageId of the message that caused this one;
10
+ * enables reconstructing the *graph* of work, not just
11
+ * the chain
12
+ * tenant / userId — actor context propagated for authorization + audit
13
+ * timestamp — ISO 8601 string at envelope creation
14
+ * version — for schema evolution; default 1
15
+ *
16
+ * The envelope is created at entry points (HTTP middleware, queue consumer)
17
+ * and propagated automatically through every sub-handler invocation. The
18
+ * developer never constructs one; the framework does.
19
+ */
20
+ export interface MessageEnvelope {
21
+ readonly messageId: string;
22
+ readonly correlationId: string;
23
+ readonly causationId: string;
24
+ readonly tenant?: string;
25
+ readonly userId?: string;
26
+ /**
27
+ * Resolved User object — attached by the identity plugin's HTTP/transport
28
+ * middleware after token verification, alongside `userId`. RBAC + audit
29
+ * + per-request scoping read from here. Kept as `unknown` here so the
30
+ * envelope stays domain-agnostic; `@nwire/auth` narrows this to `User`.
31
+ */
32
+ readonly user?: unknown;
33
+ readonly timestamp: string;
34
+ readonly version: number;
35
+ }
36
+ export interface SeedEnvelopeInput {
37
+ correlationId?: string;
38
+ causationId?: string;
39
+ tenant?: string;
40
+ userId?: string;
41
+ user?: unknown;
42
+ version?: number;
43
+ }
44
+ /**
45
+ * Create a fresh envelope at an entry point (HTTP request, queue message,
46
+ * scheduled tick). Use this where the chain begins.
47
+ *
48
+ * If a correlationId is supplied (e.g., via an `x-correlation-id` header),
49
+ * use it; otherwise mint a new one. If a causationId is supplied (e.g., via
50
+ * `x-causation-id` header from an upstream service), use it; otherwise the
51
+ * envelope sits at the head of its chain (causationId === messageId).
52
+ */
53
+ export declare function seedEnvelope(input?: SeedEnvelopeInput): MessageEnvelope;
54
+ /**
55
+ * Derive a child envelope from a parent. Use this when a handler invokes
56
+ * another handler, or when an event handler is dispatched with the event's
57
+ * envelope as its parent.
58
+ *
59
+ * - new messageId (this message is its own thing)
60
+ * - same correlationId (chain stays linked)
61
+ * - causationId = parent's messageId (this was caused by that)
62
+ * - tenant / userId / version inherited unless overridden
63
+ */
64
+ export declare function deriveEnvelope(parent: MessageEnvelope, overrides?: Partial<Pick<MessageEnvelope, "tenant" | "userId" | "user" | "version">>): MessageEnvelope;
65
+ //# sourceMappingURL=message-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-envelope.d.ts","sourceRoot":"","sources":["../src/message-envelope.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,GAAE,iBAAsB,GAAG,eAAe,CAY3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,EACvB,SAAS,GAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC,CAAM,GACvF,eAAe,CAWjB"}
@@ -0,0 +1,46 @@
1
+ import { randomUUID } from "node:crypto";
2
+ /**
3
+ * Create a fresh envelope at an entry point (HTTP request, queue message,
4
+ * scheduled tick). Use this where the chain begins.
5
+ *
6
+ * If a correlationId is supplied (e.g., via an `x-correlation-id` header),
7
+ * use it; otherwise mint a new one. If a causationId is supplied (e.g., via
8
+ * `x-causation-id` header from an upstream service), use it; otherwise the
9
+ * envelope sits at the head of its chain (causationId === messageId).
10
+ */
11
+ export function seedEnvelope(input = {}) {
12
+ const messageId = randomUUID();
13
+ return {
14
+ messageId,
15
+ correlationId: input.correlationId ?? messageId,
16
+ causationId: input.causationId ?? messageId,
17
+ tenant: input.tenant,
18
+ userId: input.userId,
19
+ user: input.user,
20
+ timestamp: new Date().toISOString(),
21
+ version: input.version ?? 1,
22
+ };
23
+ }
24
+ /**
25
+ * Derive a child envelope from a parent. Use this when a handler invokes
26
+ * another handler, or when an event handler is dispatched with the event's
27
+ * envelope as its parent.
28
+ *
29
+ * - new messageId (this message is its own thing)
30
+ * - same correlationId (chain stays linked)
31
+ * - causationId = parent's messageId (this was caused by that)
32
+ * - tenant / userId / version inherited unless overridden
33
+ */
34
+ export function deriveEnvelope(parent, overrides = {}) {
35
+ return {
36
+ messageId: randomUUID(),
37
+ correlationId: parent.correlationId,
38
+ causationId: parent.messageId,
39
+ tenant: overrides.tenant ?? parent.tenant,
40
+ userId: overrides.userId ?? parent.userId,
41
+ user: overrides.user ?? parent.user,
42
+ timestamp: new Date().toISOString(),
43
+ version: overrides.version ?? parent.version,
44
+ };
45
+ }
46
+ //# sourceMappingURL=message-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-envelope.js","sourceRoot":"","sources":["../src/message-envelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+CzC;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,QAA2B,EAAE;IACxD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC/B,OAAO;QACL,SAAS;QACT,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,SAAS;QAC/C,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,SAAS;QAC3C,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,CAAC;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAuB,EACvB,YAAsF,EAAE;IAExF,OAAO;QACL,SAAS,EAAE,UAAU,EAAE;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,WAAW,EAAE,MAAM,CAAC,SAAS;QAC7B,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;QACzC,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;QACzC,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI;QACnC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO;KAC7C,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@nwire/envelope",
3
+ "version": "0.7.0",
4
+ "description": "Nwire — universal message envelope. messageId, correlationId, causationId, tenant, userId. Zero-dep; every nwire package depends on this.",
5
+ "keywords": [
6
+ "causation",
7
+ "correlation",
8
+ "envelope",
9
+ "nwire",
10
+ "tracing"
11
+ ],
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "type": "module",
17
+ "main": "./dist/envelope.js",
18
+ "types": "./dist/envelope.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "import": "./dist/envelope.js",
22
+ "types": "./dist/envelope.d.ts"
23
+ }
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.19.9",
30
+ "typescript": "^5.9.3"
31
+ },
32
+ "scripts": {
33
+ "build": "tsc && node ../../scripts/fix-dist-extensions.mjs dist",
34
+ "dev": "tsc --watch",
35
+ "typecheck": "tsc --noEmit"
36
+ }
37
+ }