@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 +21 -0
- package/README.md +59 -0
- package/dist/envelope.d.ts +20 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +20 -0
- package/dist/envelope.js.map +1 -0
- package/dist/message-envelope.d.ts +65 -0
- package/dist/message-envelope.d.ts.map +1 -0
- package/dist/message-envelope.js +46 -0
- package/dist/message-envelope.js.map +1 -0
- package/package.json +37 -0
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"}
|
package/dist/envelope.js
ADDED
|
@@ -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
|
+
}
|