@eventferry/kafka-iam 0.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/README.md +77 -0
- package/dist/index.cjs +105 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +86 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @eventferry/kafka-iam
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@eventferry/kafka-iam)
|
|
4
|
+
|
|
5
|
+
**AWS MSK IAM** authentication for [`@eventferry/kafka`](../kafka). MSK exposes IAM auth via SASL/OAUTHBEARER — the bearer token is an AWS Signature V4 signature, not a JWT. This helper wraps the official `aws-msk-iam-sasl-signer-js` library and gives you a one-liner `sasl` block for the publisher.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @eventferry/kafka-iam aws-msk-iam-sasl-signer-js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`aws-msk-iam-sasl-signer-js` is an optional peer (the AWS-official SigV4 signer).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { KafkaPublisher } from "@eventferry/kafka";
|
|
19
|
+
import { createMskIamSasl } from "@eventferry/kafka-iam";
|
|
20
|
+
|
|
21
|
+
const publisher = new KafkaPublisher({
|
|
22
|
+
brokers: ["b-1.cluster.us-east-1.amazonaws.com:9098"],
|
|
23
|
+
ssl: true,
|
|
24
|
+
sasl: createMskIamSasl({ region: "us-east-1" }),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await publisher.connect();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
With a named AWS profile or an assumed IAM role:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
createMskIamSasl({ region: "us-east-1", awsProfile: "prod" });
|
|
34
|
+
|
|
35
|
+
createMskIamSasl({
|
|
36
|
+
region: "us-east-1",
|
|
37
|
+
awsRoleArn: "arn:aws:iam::123456789012:role/eventferry-publisher",
|
|
38
|
+
awsRoleSessionName: "eventferry-prod",
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Token caching & refresh
|
|
43
|
+
|
|
44
|
+
MSK IAM tokens have a 15-minute lifetime. This helper memoizes the most recent token and refreshes it shortly before expiry. Both kafkajs and librdkafka invoke the provider on demand (no fixed timer), so process-local caching is the natural place:
|
|
45
|
+
|
|
46
|
+
- Default `refreshAheadMs: 60_000` — token is regenerated when its remaining lifetime drops below 60 seconds.
|
|
47
|
+
- Concurrent invocations during a refresh dedupe onto a single in-flight `generateAuthToken` call (no thundering herd at the SigV4 signer).
|
|
48
|
+
- Transient signer failures clear the in-flight slot so the next call retries cleanly.
|
|
49
|
+
|
|
50
|
+
Tighten or loosen the refresh window:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
createMskIamSasl({ region: "us-east-1", refreshAheadMs: 120_000 });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Custom signer (DI)
|
|
57
|
+
|
|
58
|
+
For tests or non-standard signers, inject your own:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
createMskIamSasl({
|
|
62
|
+
region: "us-east-1",
|
|
63
|
+
signer: {
|
|
64
|
+
async generateAuthToken({ region }) {
|
|
65
|
+
return { token: await mySignerFor(region), expiryTime: Date.now() + 15 * 60 * 1000 };
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The helper does not dynamically import `aws-msk-iam-sasl-signer-js` when a `signer` is supplied.
|
|
72
|
+
|
|
73
|
+
📖 **Full documentation:** [github.com/SametGoktepe/eventferry](https://github.com/SametGoktepe/eventferry#readme)
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createMskIamSasl: () => createMskIamSasl
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/iam-provider.ts
|
|
38
|
+
function createMskIamSasl(opts) {
|
|
39
|
+
if (!opts.region) {
|
|
40
|
+
throw new Error("createMskIamSasl: `region` is required");
|
|
41
|
+
}
|
|
42
|
+
const refreshAheadMs = opts.refreshAheadMs ?? 6e4;
|
|
43
|
+
const principal = opts.principal ?? "eventferry";
|
|
44
|
+
let cached = null;
|
|
45
|
+
let inFlight = null;
|
|
46
|
+
let signer = opts.signer ?? null;
|
|
47
|
+
async function refresh() {
|
|
48
|
+
if (!signer) signer = await importDefaultSigner();
|
|
49
|
+
const { region, awsProfile, awsRoleArn, awsRoleSessionName, awsStsRegion } = opts;
|
|
50
|
+
const result = await signer.generateAuthToken({
|
|
51
|
+
region,
|
|
52
|
+
...awsProfile !== void 0 ? { awsProfile } : {},
|
|
53
|
+
...awsRoleArn !== void 0 ? { awsRoleArn } : {},
|
|
54
|
+
...awsRoleSessionName !== void 0 ? { awsRoleSessionName } : {},
|
|
55
|
+
...awsStsRegion !== void 0 ? { awsStsRegion } : {}
|
|
56
|
+
});
|
|
57
|
+
const expiryMs = result.expiryTime > 1e12 ? result.expiryTime : result.expiryTime * 1e3;
|
|
58
|
+
cached = { token: result.token, expiryMs };
|
|
59
|
+
return tokenFor(cached, principal);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
mechanism: "oauthbearer",
|
|
63
|
+
async oauthBearerProvider() {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
if (cached && cached.expiryMs - now > refreshAheadMs) {
|
|
66
|
+
return tokenFor(cached, principal);
|
|
67
|
+
}
|
|
68
|
+
if (!inFlight) {
|
|
69
|
+
inFlight = refresh().finally(() => {
|
|
70
|
+
inFlight = null;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return inFlight;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function tokenFor(cached, principal) {
|
|
78
|
+
const lifetime = Math.max(cached.expiryMs - Date.now(), 0);
|
|
79
|
+
return {
|
|
80
|
+
value: cached.token,
|
|
81
|
+
principal,
|
|
82
|
+
lifetime
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function importDefaultSigner() {
|
|
86
|
+
try {
|
|
87
|
+
const mod = await import("aws-msk-iam-sasl-signer-js");
|
|
88
|
+
if (typeof mod?.generateAuthToken === "function") {
|
|
89
|
+
return { generateAuthToken: mod.generateAuthToken };
|
|
90
|
+
}
|
|
91
|
+
if (typeof mod?.default?.generateAuthToken === "function") {
|
|
92
|
+
return { generateAuthToken: mod.default.generateAuthToken };
|
|
93
|
+
}
|
|
94
|
+
throw new Error("module shape mismatch \u2014 no generateAuthToken export found");
|
|
95
|
+
} catch (err) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`createMskIamSasl requires the "aws-msk-iam-sasl-signer-js" package. Run: npm i aws-msk-iam-sasl-signer-js. (underlying error: ${err.message})`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
102
|
+
0 && (module.exports = {
|
|
103
|
+
createMskIamSasl
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/iam-provider.ts"],"sourcesContent":["export * from \"./iam-provider.js\";\n","import type {\n OauthBearerToken,\n SaslOauthbearerConfig,\n} from \"@eventferry/kafka\";\n\n/**\n * AWS MSK IAM authentication for @eventferry/kafka.\n *\n * AWS MSK exposes IAM auth via SASL/OAUTHBEARER — the bearer token is an AWS\n * Signature V4 signature, not a JWT. This helper produces a token provider\n * compatible with eventferry's {@link SaslOauthbearerConfig.oauthBearerProvider}\n * shape, backed by the official `aws-msk-iam-sasl-signer-js` library.\n *\n * Wire it into the publisher's `sasl` option directly:\n *\n * import { createMskIamSasl } from \"@eventferry/kafka-iam\";\n *\n * new KafkaPublisher({\n * brokers: [\"b.cluster.us-east-1.amazonaws.com:9098\"],\n * ssl: true,\n * sasl: createMskIamSasl({ region: \"us-east-1\" }),\n * });\n */\n\n/**\n * Minimal structural shape of the official AWS signer. The real module's\n * `generateAuthToken` matches this — we keep the interface here so the\n * helper compiles without `aws-msk-iam-sasl-signer-js` installed (it's\n * an optional peer).\n */\nexport interface MskIamSigner {\n generateAuthToken(opts: {\n region: string;\n // The signer accepts other fields (awsProfile, awsRoleArn, awsRoleSessionName);\n // we forward whatever the caller supplied via `signerOptions`.\n [k: string]: unknown;\n }): Promise<{ token: string; expiryTime: number }>;\n}\n\n/** Options forwarded to `aws-msk-iam-sasl-signer-js`'s `generateAuthToken`. */\nexport interface MskIamSaslOptions {\n /** AWS region of the MSK cluster (e.g. \"us-east-1\"). REQUIRED. */\n region: string;\n /**\n * Optional named AWS profile to use (resolved by the signer the same way\n * the AWS SDK does). Mutually exclusive with `awsRoleArn`.\n */\n awsProfile?: string;\n /**\n * Optional IAM role to assume before signing. The signer calls\n * `AssumeRole` itself. Mutually exclusive with `awsProfile`.\n */\n awsRoleArn?: string;\n /** Session name to use with `awsRoleArn`. Default `\"MSKSASLDefaultSession\"`. */\n awsRoleSessionName?: string;\n /**\n * Optional signer endpoint override. Useful for VPC endpoints / FIPS\n * regions. Forwarded as-is to the signer.\n */\n awsStsRegion?: string;\n /**\n * Optional pre-built signer (DI for tests). When set, the helper does NOT\n * dynamically import `aws-msk-iam-sasl-signer-js`.\n */\n signer?: MskIamSigner;\n /**\n * Optional override for the SASL principal. AWS MSK accepts any string;\n * default `\"eventferry\"` is the conventional name.\n */\n principal?: string;\n /**\n * Refresh-ahead window. Token is regenerated when its remaining lifetime\n * drops below this many milliseconds. Default 60_000 (60 s) — the\n * signer's default token lifetime is 15 minutes, so this leaves ~14 min\n * of usable cache between refreshes.\n */\n refreshAheadMs?: number;\n}\n\n/**\n * Construct the `sasl` block for `KafkaPublisher`. The returned config\n * memoizes the most recent token and refreshes it shortly before expiry —\n * librdkafka and kafkajs both invoke the provider on demand (no fixed\n * timer), so a process-local cache is the natural place for this.\n *\n * Concurrent invocations during a refresh dedupe onto a single in-flight\n * `generateAuthToken` call.\n */\nexport function createMskIamSasl(\n opts: MskIamSaslOptions,\n): SaslOauthbearerConfig {\n if (!opts.region) {\n throw new Error(\"createMskIamSasl: `region` is required\");\n }\n const refreshAheadMs = opts.refreshAheadMs ?? 60_000;\n const principal = opts.principal ?? \"eventferry\";\n\n let cached: { token: string; expiryMs: number } | null = null;\n let inFlight: Promise<OauthBearerToken> | null = null;\n let signer: MskIamSigner | null = opts.signer ?? null;\n\n async function refresh(): Promise<OauthBearerToken> {\n if (!signer) signer = await importDefaultSigner();\n const { region, awsProfile, awsRoleArn, awsRoleSessionName, awsStsRegion } =\n opts;\n const result = await signer.generateAuthToken({\n region,\n ...(awsProfile !== undefined ? { awsProfile } : {}),\n ...(awsRoleArn !== undefined ? { awsRoleArn } : {}),\n ...(awsRoleSessionName !== undefined ? { awsRoleSessionName } : {}),\n ...(awsStsRegion !== undefined ? { awsStsRegion } : {}),\n });\n // Some signers return expiry in seconds, some in ms. The official\n // aws-msk-iam-sasl-signer-js returns ms. Be defensive: anything below\n // year-2001 (in ms) is treated as seconds and rescaled.\n const expiryMs =\n result.expiryTime > 1_000_000_000_000\n ? result.expiryTime\n : result.expiryTime * 1000;\n cached = { token: result.token, expiryMs };\n return tokenFor(cached, principal);\n }\n\n return {\n mechanism: \"oauthbearer\",\n async oauthBearerProvider(): Promise<OauthBearerToken> {\n const now = Date.now();\n // Fast path: cached token still has plenty of headroom.\n if (cached && cached.expiryMs - now > refreshAheadMs) {\n return tokenFor(cached, principal);\n }\n // Dedup concurrent refresh attempts so a thundering herd hits the\n // signer once, not N times.\n if (!inFlight) {\n inFlight = refresh().finally(() => {\n inFlight = null;\n });\n }\n return inFlight;\n },\n };\n}\n\nfunction tokenFor(\n cached: { token: string; expiryMs: number },\n principal: string,\n): OauthBearerToken {\n // Confluent driver requires {value, principal, lifetime}. kafkajs ignores\n // everything but `value` — supplying the extras is a no-op there.\n // Lifetime is in MILLISECONDS per the eventferry contract.\n const lifetime = Math.max(cached.expiryMs - Date.now(), 0);\n return {\n value: cached.token,\n principal,\n lifetime,\n };\n}\n\nasync function importDefaultSigner(): Promise<MskIamSigner> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod: any = await import(\"aws-msk-iam-sasl-signer-js\");\n // The package exports either `generateAuthToken` directly OR a default\n // object containing it. Normalize both shapes into an MskIamSigner.\n if (typeof mod?.generateAuthToken === \"function\") {\n return { generateAuthToken: mod.generateAuthToken };\n }\n if (typeof mod?.default?.generateAuthToken === \"function\") {\n return { generateAuthToken: mod.default.generateAuthToken };\n }\n throw new Error(\"module shape mismatch — no generateAuthToken export found\");\n } catch (err) {\n throw new Error(\n 'createMskIamSasl requires the \"aws-msk-iam-sasl-signer-js\" package. ' +\n 'Run: npm i aws-msk-iam-sasl-signer-js. ' +\n `(underlying error: ${(err as Error).message})`,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwFO,SAAS,iBACd,MACuB;AACvB,MAAI,CAAC,KAAK,QAAQ;AAChB,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AACA,QAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAM,YAAY,KAAK,aAAa;AAEpC,MAAI,SAAqD;AACzD,MAAI,WAA6C;AACjD,MAAI,SAA8B,KAAK,UAAU;AAEjD,iBAAe,UAAqC;AAClD,QAAI,CAAC,OAAQ,UAAS,MAAM,oBAAoB;AAChD,UAAM,EAAE,QAAQ,YAAY,YAAY,oBAAoB,aAAa,IACvE;AACF,UAAM,SAAS,MAAM,OAAO,kBAAkB;AAAA,MAC5C;AAAA,MACA,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,uBAAuB,SAAY,EAAE,mBAAmB,IAAI,CAAC;AAAA,MACjE,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,IACvD,CAAC;AAID,UAAM,WACJ,OAAO,aAAa,OAChB,OAAO,aACP,OAAO,aAAa;AAC1B,aAAS,EAAE,OAAO,OAAO,OAAO,SAAS;AACzC,WAAO,SAAS,QAAQ,SAAS;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,MAAM,sBAAiD;AACrD,YAAM,MAAM,KAAK,IAAI;AAErB,UAAI,UAAU,OAAO,WAAW,MAAM,gBAAgB;AACpD,eAAO,SAAS,QAAQ,SAAS;AAAA,MACnC;AAGA,UAAI,CAAC,UAAU;AACb,mBAAW,QAAQ,EAAE,QAAQ,MAAM;AACjC,qBAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,SACP,QACA,WACkB;AAIlB,QAAM,WAAW,KAAK,IAAI,OAAO,WAAW,KAAK,IAAI,GAAG,CAAC;AACzD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,sBAA6C;AAC1D,MAAI;AAEF,UAAM,MAAW,MAAM,OAAO,4BAA4B;AAG1D,QAAI,OAAO,KAAK,sBAAsB,YAAY;AAChD,aAAO,EAAE,mBAAmB,IAAI,kBAAkB;AAAA,IACpD;AACA,QAAI,OAAO,KAAK,SAAS,sBAAsB,YAAY;AACzD,aAAO,EAAE,mBAAmB,IAAI,QAAQ,kBAAkB;AAAA,IAC5D;AACA,UAAM,IAAI,MAAM,gEAA2D;AAAA,EAC7E,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,iIAEyB,IAAc,OAAO;AAAA,IAChD;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SaslOauthbearerConfig } from '@eventferry/kafka';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AWS MSK IAM authentication for @eventferry/kafka.
|
|
5
|
+
*
|
|
6
|
+
* AWS MSK exposes IAM auth via SASL/OAUTHBEARER — the bearer token is an AWS
|
|
7
|
+
* Signature V4 signature, not a JWT. This helper produces a token provider
|
|
8
|
+
* compatible with eventferry's {@link SaslOauthbearerConfig.oauthBearerProvider}
|
|
9
|
+
* shape, backed by the official `aws-msk-iam-sasl-signer-js` library.
|
|
10
|
+
*
|
|
11
|
+
* Wire it into the publisher's `sasl` option directly:
|
|
12
|
+
*
|
|
13
|
+
* import { createMskIamSasl } from "@eventferry/kafka-iam";
|
|
14
|
+
*
|
|
15
|
+
* new KafkaPublisher({
|
|
16
|
+
* brokers: ["b.cluster.us-east-1.amazonaws.com:9098"],
|
|
17
|
+
* ssl: true,
|
|
18
|
+
* sasl: createMskIamSasl({ region: "us-east-1" }),
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Minimal structural shape of the official AWS signer. The real module's
|
|
23
|
+
* `generateAuthToken` matches this — we keep the interface here so the
|
|
24
|
+
* helper compiles without `aws-msk-iam-sasl-signer-js` installed (it's
|
|
25
|
+
* an optional peer).
|
|
26
|
+
*/
|
|
27
|
+
interface MskIamSigner {
|
|
28
|
+
generateAuthToken(opts: {
|
|
29
|
+
region: string;
|
|
30
|
+
[k: string]: unknown;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
token: string;
|
|
33
|
+
expiryTime: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/** Options forwarded to `aws-msk-iam-sasl-signer-js`'s `generateAuthToken`. */
|
|
37
|
+
interface MskIamSaslOptions {
|
|
38
|
+
/** AWS region of the MSK cluster (e.g. "us-east-1"). REQUIRED. */
|
|
39
|
+
region: string;
|
|
40
|
+
/**
|
|
41
|
+
* Optional named AWS profile to use (resolved by the signer the same way
|
|
42
|
+
* the AWS SDK does). Mutually exclusive with `awsRoleArn`.
|
|
43
|
+
*/
|
|
44
|
+
awsProfile?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional IAM role to assume before signing. The signer calls
|
|
47
|
+
* `AssumeRole` itself. Mutually exclusive with `awsProfile`.
|
|
48
|
+
*/
|
|
49
|
+
awsRoleArn?: string;
|
|
50
|
+
/** Session name to use with `awsRoleArn`. Default `"MSKSASLDefaultSession"`. */
|
|
51
|
+
awsRoleSessionName?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Optional signer endpoint override. Useful for VPC endpoints / FIPS
|
|
54
|
+
* regions. Forwarded as-is to the signer.
|
|
55
|
+
*/
|
|
56
|
+
awsStsRegion?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional pre-built signer (DI for tests). When set, the helper does NOT
|
|
59
|
+
* dynamically import `aws-msk-iam-sasl-signer-js`.
|
|
60
|
+
*/
|
|
61
|
+
signer?: MskIamSigner;
|
|
62
|
+
/**
|
|
63
|
+
* Optional override for the SASL principal. AWS MSK accepts any string;
|
|
64
|
+
* default `"eventferry"` is the conventional name.
|
|
65
|
+
*/
|
|
66
|
+
principal?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Refresh-ahead window. Token is regenerated when its remaining lifetime
|
|
69
|
+
* drops below this many milliseconds. Default 60_000 (60 s) — the
|
|
70
|
+
* signer's default token lifetime is 15 minutes, so this leaves ~14 min
|
|
71
|
+
* of usable cache between refreshes.
|
|
72
|
+
*/
|
|
73
|
+
refreshAheadMs?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Construct the `sasl` block for `KafkaPublisher`. The returned config
|
|
77
|
+
* memoizes the most recent token and refreshes it shortly before expiry —
|
|
78
|
+
* librdkafka and kafkajs both invoke the provider on demand (no fixed
|
|
79
|
+
* timer), so a process-local cache is the natural place for this.
|
|
80
|
+
*
|
|
81
|
+
* Concurrent invocations during a refresh dedupe onto a single in-flight
|
|
82
|
+
* `generateAuthToken` call.
|
|
83
|
+
*/
|
|
84
|
+
declare function createMskIamSasl(opts: MskIamSaslOptions): SaslOauthbearerConfig;
|
|
85
|
+
|
|
86
|
+
export { type MskIamSaslOptions, type MskIamSigner, createMskIamSasl };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SaslOauthbearerConfig } from '@eventferry/kafka';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AWS MSK IAM authentication for @eventferry/kafka.
|
|
5
|
+
*
|
|
6
|
+
* AWS MSK exposes IAM auth via SASL/OAUTHBEARER — the bearer token is an AWS
|
|
7
|
+
* Signature V4 signature, not a JWT. This helper produces a token provider
|
|
8
|
+
* compatible with eventferry's {@link SaslOauthbearerConfig.oauthBearerProvider}
|
|
9
|
+
* shape, backed by the official `aws-msk-iam-sasl-signer-js` library.
|
|
10
|
+
*
|
|
11
|
+
* Wire it into the publisher's `sasl` option directly:
|
|
12
|
+
*
|
|
13
|
+
* import { createMskIamSasl } from "@eventferry/kafka-iam";
|
|
14
|
+
*
|
|
15
|
+
* new KafkaPublisher({
|
|
16
|
+
* brokers: ["b.cluster.us-east-1.amazonaws.com:9098"],
|
|
17
|
+
* ssl: true,
|
|
18
|
+
* sasl: createMskIamSasl({ region: "us-east-1" }),
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Minimal structural shape of the official AWS signer. The real module's
|
|
23
|
+
* `generateAuthToken` matches this — we keep the interface here so the
|
|
24
|
+
* helper compiles without `aws-msk-iam-sasl-signer-js` installed (it's
|
|
25
|
+
* an optional peer).
|
|
26
|
+
*/
|
|
27
|
+
interface MskIamSigner {
|
|
28
|
+
generateAuthToken(opts: {
|
|
29
|
+
region: string;
|
|
30
|
+
[k: string]: unknown;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
token: string;
|
|
33
|
+
expiryTime: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/** Options forwarded to `aws-msk-iam-sasl-signer-js`'s `generateAuthToken`. */
|
|
37
|
+
interface MskIamSaslOptions {
|
|
38
|
+
/** AWS region of the MSK cluster (e.g. "us-east-1"). REQUIRED. */
|
|
39
|
+
region: string;
|
|
40
|
+
/**
|
|
41
|
+
* Optional named AWS profile to use (resolved by the signer the same way
|
|
42
|
+
* the AWS SDK does). Mutually exclusive with `awsRoleArn`.
|
|
43
|
+
*/
|
|
44
|
+
awsProfile?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional IAM role to assume before signing. The signer calls
|
|
47
|
+
* `AssumeRole` itself. Mutually exclusive with `awsProfile`.
|
|
48
|
+
*/
|
|
49
|
+
awsRoleArn?: string;
|
|
50
|
+
/** Session name to use with `awsRoleArn`. Default `"MSKSASLDefaultSession"`. */
|
|
51
|
+
awsRoleSessionName?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Optional signer endpoint override. Useful for VPC endpoints / FIPS
|
|
54
|
+
* regions. Forwarded as-is to the signer.
|
|
55
|
+
*/
|
|
56
|
+
awsStsRegion?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional pre-built signer (DI for tests). When set, the helper does NOT
|
|
59
|
+
* dynamically import `aws-msk-iam-sasl-signer-js`.
|
|
60
|
+
*/
|
|
61
|
+
signer?: MskIamSigner;
|
|
62
|
+
/**
|
|
63
|
+
* Optional override for the SASL principal. AWS MSK accepts any string;
|
|
64
|
+
* default `"eventferry"` is the conventional name.
|
|
65
|
+
*/
|
|
66
|
+
principal?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Refresh-ahead window. Token is regenerated when its remaining lifetime
|
|
69
|
+
* drops below this many milliseconds. Default 60_000 (60 s) — the
|
|
70
|
+
* signer's default token lifetime is 15 minutes, so this leaves ~14 min
|
|
71
|
+
* of usable cache between refreshes.
|
|
72
|
+
*/
|
|
73
|
+
refreshAheadMs?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Construct the `sasl` block for `KafkaPublisher`. The returned config
|
|
77
|
+
* memoizes the most recent token and refreshes it shortly before expiry —
|
|
78
|
+
* librdkafka and kafkajs both invoke the provider on demand (no fixed
|
|
79
|
+
* timer), so a process-local cache is the natural place for this.
|
|
80
|
+
*
|
|
81
|
+
* Concurrent invocations during a refresh dedupe onto a single in-flight
|
|
82
|
+
* `generateAuthToken` call.
|
|
83
|
+
*/
|
|
84
|
+
declare function createMskIamSasl(opts: MskIamSaslOptions): SaslOauthbearerConfig;
|
|
85
|
+
|
|
86
|
+
export { type MskIamSaslOptions, type MskIamSigner, createMskIamSasl };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/iam-provider.ts
|
|
2
|
+
function createMskIamSasl(opts) {
|
|
3
|
+
if (!opts.region) {
|
|
4
|
+
throw new Error("createMskIamSasl: `region` is required");
|
|
5
|
+
}
|
|
6
|
+
const refreshAheadMs = opts.refreshAheadMs ?? 6e4;
|
|
7
|
+
const principal = opts.principal ?? "eventferry";
|
|
8
|
+
let cached = null;
|
|
9
|
+
let inFlight = null;
|
|
10
|
+
let signer = opts.signer ?? null;
|
|
11
|
+
async function refresh() {
|
|
12
|
+
if (!signer) signer = await importDefaultSigner();
|
|
13
|
+
const { region, awsProfile, awsRoleArn, awsRoleSessionName, awsStsRegion } = opts;
|
|
14
|
+
const result = await signer.generateAuthToken({
|
|
15
|
+
region,
|
|
16
|
+
...awsProfile !== void 0 ? { awsProfile } : {},
|
|
17
|
+
...awsRoleArn !== void 0 ? { awsRoleArn } : {},
|
|
18
|
+
...awsRoleSessionName !== void 0 ? { awsRoleSessionName } : {},
|
|
19
|
+
...awsStsRegion !== void 0 ? { awsStsRegion } : {}
|
|
20
|
+
});
|
|
21
|
+
const expiryMs = result.expiryTime > 1e12 ? result.expiryTime : result.expiryTime * 1e3;
|
|
22
|
+
cached = { token: result.token, expiryMs };
|
|
23
|
+
return tokenFor(cached, principal);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
mechanism: "oauthbearer",
|
|
27
|
+
async oauthBearerProvider() {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
if (cached && cached.expiryMs - now > refreshAheadMs) {
|
|
30
|
+
return tokenFor(cached, principal);
|
|
31
|
+
}
|
|
32
|
+
if (!inFlight) {
|
|
33
|
+
inFlight = refresh().finally(() => {
|
|
34
|
+
inFlight = null;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return inFlight;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function tokenFor(cached, principal) {
|
|
42
|
+
const lifetime = Math.max(cached.expiryMs - Date.now(), 0);
|
|
43
|
+
return {
|
|
44
|
+
value: cached.token,
|
|
45
|
+
principal,
|
|
46
|
+
lifetime
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function importDefaultSigner() {
|
|
50
|
+
try {
|
|
51
|
+
const mod = await import("aws-msk-iam-sasl-signer-js");
|
|
52
|
+
if (typeof mod?.generateAuthToken === "function") {
|
|
53
|
+
return { generateAuthToken: mod.generateAuthToken };
|
|
54
|
+
}
|
|
55
|
+
if (typeof mod?.default?.generateAuthToken === "function") {
|
|
56
|
+
return { generateAuthToken: mod.default.generateAuthToken };
|
|
57
|
+
}
|
|
58
|
+
throw new Error("module shape mismatch \u2014 no generateAuthToken export found");
|
|
59
|
+
} catch (err) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`createMskIamSasl requires the "aws-msk-iam-sasl-signer-js" package. Run: npm i aws-msk-iam-sasl-signer-js. (underlying error: ${err.message})`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
createMskIamSasl
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/iam-provider.ts"],"sourcesContent":["import type {\n OauthBearerToken,\n SaslOauthbearerConfig,\n} from \"@eventferry/kafka\";\n\n/**\n * AWS MSK IAM authentication for @eventferry/kafka.\n *\n * AWS MSK exposes IAM auth via SASL/OAUTHBEARER — the bearer token is an AWS\n * Signature V4 signature, not a JWT. This helper produces a token provider\n * compatible with eventferry's {@link SaslOauthbearerConfig.oauthBearerProvider}\n * shape, backed by the official `aws-msk-iam-sasl-signer-js` library.\n *\n * Wire it into the publisher's `sasl` option directly:\n *\n * import { createMskIamSasl } from \"@eventferry/kafka-iam\";\n *\n * new KafkaPublisher({\n * brokers: [\"b.cluster.us-east-1.amazonaws.com:9098\"],\n * ssl: true,\n * sasl: createMskIamSasl({ region: \"us-east-1\" }),\n * });\n */\n\n/**\n * Minimal structural shape of the official AWS signer. The real module's\n * `generateAuthToken` matches this — we keep the interface here so the\n * helper compiles without `aws-msk-iam-sasl-signer-js` installed (it's\n * an optional peer).\n */\nexport interface MskIamSigner {\n generateAuthToken(opts: {\n region: string;\n // The signer accepts other fields (awsProfile, awsRoleArn, awsRoleSessionName);\n // we forward whatever the caller supplied via `signerOptions`.\n [k: string]: unknown;\n }): Promise<{ token: string; expiryTime: number }>;\n}\n\n/** Options forwarded to `aws-msk-iam-sasl-signer-js`'s `generateAuthToken`. */\nexport interface MskIamSaslOptions {\n /** AWS region of the MSK cluster (e.g. \"us-east-1\"). REQUIRED. */\n region: string;\n /**\n * Optional named AWS profile to use (resolved by the signer the same way\n * the AWS SDK does). Mutually exclusive with `awsRoleArn`.\n */\n awsProfile?: string;\n /**\n * Optional IAM role to assume before signing. The signer calls\n * `AssumeRole` itself. Mutually exclusive with `awsProfile`.\n */\n awsRoleArn?: string;\n /** Session name to use with `awsRoleArn`. Default `\"MSKSASLDefaultSession\"`. */\n awsRoleSessionName?: string;\n /**\n * Optional signer endpoint override. Useful for VPC endpoints / FIPS\n * regions. Forwarded as-is to the signer.\n */\n awsStsRegion?: string;\n /**\n * Optional pre-built signer (DI for tests). When set, the helper does NOT\n * dynamically import `aws-msk-iam-sasl-signer-js`.\n */\n signer?: MskIamSigner;\n /**\n * Optional override for the SASL principal. AWS MSK accepts any string;\n * default `\"eventferry\"` is the conventional name.\n */\n principal?: string;\n /**\n * Refresh-ahead window. Token is regenerated when its remaining lifetime\n * drops below this many milliseconds. Default 60_000 (60 s) — the\n * signer's default token lifetime is 15 minutes, so this leaves ~14 min\n * of usable cache between refreshes.\n */\n refreshAheadMs?: number;\n}\n\n/**\n * Construct the `sasl` block for `KafkaPublisher`. The returned config\n * memoizes the most recent token and refreshes it shortly before expiry —\n * librdkafka and kafkajs both invoke the provider on demand (no fixed\n * timer), so a process-local cache is the natural place for this.\n *\n * Concurrent invocations during a refresh dedupe onto a single in-flight\n * `generateAuthToken` call.\n */\nexport function createMskIamSasl(\n opts: MskIamSaslOptions,\n): SaslOauthbearerConfig {\n if (!opts.region) {\n throw new Error(\"createMskIamSasl: `region` is required\");\n }\n const refreshAheadMs = opts.refreshAheadMs ?? 60_000;\n const principal = opts.principal ?? \"eventferry\";\n\n let cached: { token: string; expiryMs: number } | null = null;\n let inFlight: Promise<OauthBearerToken> | null = null;\n let signer: MskIamSigner | null = opts.signer ?? null;\n\n async function refresh(): Promise<OauthBearerToken> {\n if (!signer) signer = await importDefaultSigner();\n const { region, awsProfile, awsRoleArn, awsRoleSessionName, awsStsRegion } =\n opts;\n const result = await signer.generateAuthToken({\n region,\n ...(awsProfile !== undefined ? { awsProfile } : {}),\n ...(awsRoleArn !== undefined ? { awsRoleArn } : {}),\n ...(awsRoleSessionName !== undefined ? { awsRoleSessionName } : {}),\n ...(awsStsRegion !== undefined ? { awsStsRegion } : {}),\n });\n // Some signers return expiry in seconds, some in ms. The official\n // aws-msk-iam-sasl-signer-js returns ms. Be defensive: anything below\n // year-2001 (in ms) is treated as seconds and rescaled.\n const expiryMs =\n result.expiryTime > 1_000_000_000_000\n ? result.expiryTime\n : result.expiryTime * 1000;\n cached = { token: result.token, expiryMs };\n return tokenFor(cached, principal);\n }\n\n return {\n mechanism: \"oauthbearer\",\n async oauthBearerProvider(): Promise<OauthBearerToken> {\n const now = Date.now();\n // Fast path: cached token still has plenty of headroom.\n if (cached && cached.expiryMs - now > refreshAheadMs) {\n return tokenFor(cached, principal);\n }\n // Dedup concurrent refresh attempts so a thundering herd hits the\n // signer once, not N times.\n if (!inFlight) {\n inFlight = refresh().finally(() => {\n inFlight = null;\n });\n }\n return inFlight;\n },\n };\n}\n\nfunction tokenFor(\n cached: { token: string; expiryMs: number },\n principal: string,\n): OauthBearerToken {\n // Confluent driver requires {value, principal, lifetime}. kafkajs ignores\n // everything but `value` — supplying the extras is a no-op there.\n // Lifetime is in MILLISECONDS per the eventferry contract.\n const lifetime = Math.max(cached.expiryMs - Date.now(), 0);\n return {\n value: cached.token,\n principal,\n lifetime,\n };\n}\n\nasync function importDefaultSigner(): Promise<MskIamSigner> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const mod: any = await import(\"aws-msk-iam-sasl-signer-js\");\n // The package exports either `generateAuthToken` directly OR a default\n // object containing it. Normalize both shapes into an MskIamSigner.\n if (typeof mod?.generateAuthToken === \"function\") {\n return { generateAuthToken: mod.generateAuthToken };\n }\n if (typeof mod?.default?.generateAuthToken === \"function\") {\n return { generateAuthToken: mod.default.generateAuthToken };\n }\n throw new Error(\"module shape mismatch — no generateAuthToken export found\");\n } catch (err) {\n throw new Error(\n 'createMskIamSasl requires the \"aws-msk-iam-sasl-signer-js\" package. ' +\n 'Run: npm i aws-msk-iam-sasl-signer-js. ' +\n `(underlying error: ${(err as Error).message})`,\n );\n }\n}\n"],"mappings":";AAwFO,SAAS,iBACd,MACuB;AACvB,MAAI,CAAC,KAAK,QAAQ;AAChB,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AACA,QAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAM,YAAY,KAAK,aAAa;AAEpC,MAAI,SAAqD;AACzD,MAAI,WAA6C;AACjD,MAAI,SAA8B,KAAK,UAAU;AAEjD,iBAAe,UAAqC;AAClD,QAAI,CAAC,OAAQ,UAAS,MAAM,oBAAoB;AAChD,UAAM,EAAE,QAAQ,YAAY,YAAY,oBAAoB,aAAa,IACvE;AACF,UAAM,SAAS,MAAM,OAAO,kBAAkB;AAAA,MAC5C;AAAA,MACA,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,uBAAuB,SAAY,EAAE,mBAAmB,IAAI,CAAC;AAAA,MACjE,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,IACvD,CAAC;AAID,UAAM,WACJ,OAAO,aAAa,OAChB,OAAO,aACP,OAAO,aAAa;AAC1B,aAAS,EAAE,OAAO,OAAO,OAAO,SAAS;AACzC,WAAO,SAAS,QAAQ,SAAS;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,MAAM,sBAAiD;AACrD,YAAM,MAAM,KAAK,IAAI;AAErB,UAAI,UAAU,OAAO,WAAW,MAAM,gBAAgB;AACpD,eAAO,SAAS,QAAQ,SAAS;AAAA,MACnC;AAGA,UAAI,CAAC,UAAU;AACb,mBAAW,QAAQ,EAAE,QAAQ,MAAM;AACjC,qBAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,SAAS,SACP,QACA,WACkB;AAIlB,QAAM,WAAW,KAAK,IAAI,OAAO,WAAW,KAAK,IAAI,GAAG,CAAC;AACzD,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,sBAA6C;AAC1D,MAAI;AAEF,UAAM,MAAW,MAAM,OAAO,4BAA4B;AAG1D,QAAI,OAAO,KAAK,sBAAsB,YAAY;AAChD,aAAO,EAAE,mBAAmB,IAAI,kBAAkB;AAAA,IACpD;AACA,QAAI,OAAO,KAAK,SAAS,sBAAsB,YAAY;AACzD,aAAO,EAAE,mBAAmB,IAAI,QAAQ,kBAAkB;AAAA,IAC5D;AACA,UAAM,IAAI,MAAM,gEAA2D;AAAA,EAC7E,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,iIAEyB,IAAc,OAAO;AAAA,IAChD;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eventferry/kafka-iam",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AWS MSK IAM (SASL/OAUTHBEARER over SigV4) helper for @eventferry/kafka",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"test:run": "vitest run",
|
|
24
|
+
"typecheck": "tsc --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"outbox",
|
|
28
|
+
"transactional-outbox",
|
|
29
|
+
"kafka",
|
|
30
|
+
"msk",
|
|
31
|
+
"aws-msk",
|
|
32
|
+
"aws-iam",
|
|
33
|
+
"sasl-oauthbearer",
|
|
34
|
+
"sigv4",
|
|
35
|
+
"event-driven",
|
|
36
|
+
"nodejs",
|
|
37
|
+
"typescript"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/SametGoktepe/eventferry.git",
|
|
43
|
+
"directory": "packages/kafka-iam"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/SametGoktepe/eventferry/tree/main/packages/kafka-iam#readme",
|
|
46
|
+
"bugs": "https://github.com/SametGoktepe/eventferry/issues",
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@eventferry/kafka": "workspace:*"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"aws-msk-iam-sasl-signer-js": "^1.0.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"aws-msk-iam-sasl-signer-js": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@eventferry/kafka": "workspace:*",
|
|
63
|
+
"tsup": "^8.3.5",
|
|
64
|
+
"typescript": "^5.7.2",
|
|
65
|
+
"vitest": "^2.1.8"
|
|
66
|
+
}
|
|
67
|
+
}
|