@node-llm/core 1.6.2 → 1.8.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.
Files changed (105) hide show
  1. package/README.md +9 -8
  2. package/dist/aliases.d.ts +215 -0
  3. package/dist/aliases.d.ts.map +1 -1
  4. package/dist/aliases.js +241 -26
  5. package/dist/chat/Chat.d.ts +12 -3
  6. package/dist/chat/Chat.d.ts.map +1 -1
  7. package/dist/chat/Chat.js +35 -5
  8. package/dist/chat/ChatOptions.d.ts +2 -1
  9. package/dist/chat/ChatOptions.d.ts.map +1 -1
  10. package/dist/chat/ChatResponse.d.ts +9 -2
  11. package/dist/chat/ChatResponse.d.ts.map +1 -1
  12. package/dist/chat/ChatResponse.js +12 -3
  13. package/dist/chat/ChatStream.d.ts.map +1 -1
  14. package/dist/chat/ChatStream.js +15 -1
  15. package/dist/chat/Content.d.ts +7 -0
  16. package/dist/chat/Content.d.ts.map +1 -1
  17. package/dist/config.d.ts +28 -0
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +50 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/llm.d.ts.map +1 -1
  23. package/dist/llm.js +3 -2
  24. package/dist/models/models.d.ts +321 -76
  25. package/dist/models/models.d.ts.map +1 -1
  26. package/dist/models/models.js +7804 -2885
  27. package/dist/providers/Provider.d.ts +37 -0
  28. package/dist/providers/Provider.d.ts.map +1 -1
  29. package/dist/providers/anthropic/Capabilities.d.ts +1 -0
  30. package/dist/providers/anthropic/Capabilities.d.ts.map +1 -1
  31. package/dist/providers/anthropic/Capabilities.js +8 -5
  32. package/dist/providers/anthropic/Chat.d.ts.map +1 -1
  33. package/dist/providers/anthropic/Chat.js +23 -1
  34. package/dist/providers/anthropic/Streaming.d.ts.map +1 -1
  35. package/dist/providers/anthropic/Streaming.js +15 -0
  36. package/dist/providers/anthropic/types.d.ts +7 -1
  37. package/dist/providers/anthropic/types.d.ts.map +1 -1
  38. package/dist/providers/bedrock/BedrockProvider.d.ts +53 -0
  39. package/dist/providers/bedrock/BedrockProvider.d.ts.map +1 -0
  40. package/dist/providers/bedrock/BedrockProvider.js +107 -0
  41. package/dist/providers/bedrock/Capabilities.d.ts +50 -0
  42. package/dist/providers/bedrock/Capabilities.d.ts.map +1 -0
  43. package/dist/providers/bedrock/Capabilities.js +233 -0
  44. package/dist/providers/bedrock/Chat.d.ts +26 -0
  45. package/dist/providers/bedrock/Chat.d.ts.map +1 -0
  46. package/dist/providers/bedrock/Chat.js +170 -0
  47. package/dist/providers/bedrock/Embeddings.d.ts +22 -0
  48. package/dist/providers/bedrock/Embeddings.d.ts.map +1 -0
  49. package/dist/providers/bedrock/Embeddings.js +100 -0
  50. package/dist/providers/bedrock/Image.d.ts +33 -0
  51. package/dist/providers/bedrock/Image.d.ts.map +1 -0
  52. package/dist/providers/bedrock/Image.js +154 -0
  53. package/dist/providers/bedrock/Models.d.ts +34 -0
  54. package/dist/providers/bedrock/Models.d.ts.map +1 -0
  55. package/dist/providers/bedrock/Models.js +131 -0
  56. package/dist/providers/bedrock/Moderation.d.ts +23 -0
  57. package/dist/providers/bedrock/Moderation.d.ts.map +1 -0
  58. package/dist/providers/bedrock/Moderation.js +138 -0
  59. package/dist/providers/bedrock/Streaming.d.ts +21 -0
  60. package/dist/providers/bedrock/Streaming.d.ts.map +1 -0
  61. package/dist/providers/bedrock/Streaming.js +240 -0
  62. package/dist/providers/bedrock/config.d.ts +57 -0
  63. package/dist/providers/bedrock/config.d.ts.map +1 -0
  64. package/dist/providers/bedrock/config.js +33 -0
  65. package/dist/providers/bedrock/index.d.ts +8 -0
  66. package/dist/providers/bedrock/index.d.ts.map +1 -0
  67. package/dist/providers/bedrock/index.js +30 -0
  68. package/dist/providers/bedrock/mapper.d.ts +37 -0
  69. package/dist/providers/bedrock/mapper.d.ts.map +1 -0
  70. package/dist/providers/bedrock/mapper.js +204 -0
  71. package/dist/providers/bedrock/types.d.ts +179 -0
  72. package/dist/providers/bedrock/types.d.ts.map +1 -0
  73. package/dist/providers/bedrock/types.js +7 -0
  74. package/dist/providers/deepseek/Capabilities.d.ts +3 -2
  75. package/dist/providers/deepseek/Capabilities.d.ts.map +1 -1
  76. package/dist/providers/deepseek/Capabilities.js +19 -5
  77. package/dist/providers/deepseek/Chat.d.ts.map +1 -1
  78. package/dist/providers/deepseek/Chat.js +9 -2
  79. package/dist/providers/deepseek/Streaming.d.ts.map +1 -1
  80. package/dist/providers/deepseek/Streaming.js +3 -2
  81. package/dist/providers/gemini/Capabilities.d.ts +1 -0
  82. package/dist/providers/gemini/Capabilities.d.ts.map +1 -1
  83. package/dist/providers/gemini/Capabilities.js +9 -6
  84. package/dist/providers/gemini/Chat.d.ts.map +1 -1
  85. package/dist/providers/gemini/Chat.js +4 -5
  86. package/dist/providers/gemini/Streaming.d.ts.map +1 -1
  87. package/dist/providers/gemini/Streaming.js +17 -2
  88. package/dist/providers/gemini/types.d.ts +4 -0
  89. package/dist/providers/gemini/types.d.ts.map +1 -1
  90. package/dist/providers/ollama/Capabilities.d.ts.map +1 -1
  91. package/dist/providers/ollama/Capabilities.js +4 -1
  92. package/dist/providers/openai/Capabilities.d.ts +1 -0
  93. package/dist/providers/openai/Capabilities.d.ts.map +1 -1
  94. package/dist/providers/openai/Capabilities.js +14 -11
  95. package/dist/providers/openai/Chat.d.ts.map +1 -1
  96. package/dist/providers/openai/Chat.js +18 -3
  97. package/dist/providers/openai/Streaming.d.ts.map +1 -1
  98. package/dist/providers/openai/Streaming.js +11 -3
  99. package/dist/providers/registry.d.ts +2 -1
  100. package/dist/providers/registry.d.ts.map +1 -1
  101. package/dist/providers/registry.js +2 -1
  102. package/dist/utils/AwsSigV4.d.ts +51 -0
  103. package/dist/utils/AwsSigV4.d.ts.map +1 -0
  104. package/dist/utils/AwsSigV4.js +209 -0
  105. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAS3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK3B,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CA6F3D"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAS3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK3B,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CA+G3D"}
@@ -21,7 +21,7 @@ export class OpenAIChat {
21
21
  (typeof this.providerOrUrl === "string"
22
22
  ? Capabilities.supportsDeveloperRole(request.model)
23
23
  : this.providerOrUrl.capabilities?.supportsDeveloperRole(request.model));
24
- const { model, messages, tools, temperature: _, max_tokens, response_format, headers: _headers, requestTimeout: _requestTimeout, signal, ...rest } = request;
24
+ const { model, messages, tools, temperature: _, max_tokens, response_format, thinking, headers: _headers, requestTimeout: _requestTimeout, signal, ...rest } = request;
25
25
  const mappedMessages = mapSystemMessages(messages, !!supportsDeveloperRole);
26
26
  const body = {
27
27
  model,
@@ -42,6 +42,9 @@ export class OpenAIChat {
42
42
  body.tools = tools;
43
43
  if (response_format)
44
44
  body.response_format = response_format;
45
+ if (thinking?.effort && thinking.effort !== "none") {
46
+ body.reasoning_effort = thinking.effort;
47
+ }
45
48
  const url = buildUrl(this.baseUrl, "/chat/completions");
46
49
  logger.logRequest("OpenAI", "POST", url, body);
47
50
  const response = await fetchWithTimeout(url, {
@@ -62,7 +65,7 @@ export class OpenAIChat {
62
65
  const message = json.choices[0]?.message;
63
66
  const content = message?.content ?? null;
64
67
  const tool_calls = message?.tool_calls;
65
- const reasoning = message?.reasoning_content || null;
68
+ const reasoningText = message?.reasoning_content || null;
66
69
  const usage = json.usage
67
70
  ? {
68
71
  input_tokens: json.usage.prompt_tokens,
@@ -72,10 +75,22 @@ export class OpenAIChat {
72
75
  reasoning_tokens: json.usage.completion_tokens_details?.reasoning_tokens
73
76
  }
74
77
  : undefined;
78
+ const thinkingResult = reasoningText || usage?.reasoning_tokens
79
+ ? {
80
+ text: reasoningText || undefined,
81
+ tokens: usage?.reasoning_tokens
82
+ }
83
+ : undefined;
75
84
  if (!content && !tool_calls) {
76
85
  throw new Error("OpenAI returned empty response");
77
86
  }
78
87
  const calculatedUsage = usage ? ModelRegistry.calculateCost(usage, model, "openai") : undefined;
79
- return { content, tool_calls, usage: calculatedUsage, reasoning };
88
+ return {
89
+ content,
90
+ tool_calls,
91
+ usage: calculatedUsage,
92
+ thinking: thinkingResult,
93
+ reasoning: reasoningText
94
+ };
80
95
  }
81
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,SAAS,CAAC;CA0L9F"}
1
+ {"version":3,"file":"Streaming.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Streaming.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQxD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,eAAe;IAIxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,aAAa,EAAE,cAAc,GAAG,MAAM,EACtC,MAAM,EAAE,MAAM;IAK1B,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC,SAAS,CAAC;CAoM9F"}
@@ -15,8 +15,9 @@ export class OpenAIStreaming {
15
15
  this.baseUrl = typeof providerOrUrl === "string" ? providerOrUrl : providerOrUrl.apiBase();
16
16
  }
17
17
  async *execute(request, controller) {
18
- const abortController = controller ||
19
- (request.signal ? { signal: request.signal } : new AbortController());
18
+ const internalController = new AbortController();
19
+ const abortController = controller || internalController;
20
+ const signal = request.signal ? request.signal : abortController.signal;
20
21
  const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
21
22
  const isMainOpenAI = this.baseUrl.includes("api.openai.com");
22
23
  const supportsDeveloperRole = isMainOpenAI &&
@@ -46,6 +47,9 @@ export class OpenAIStreaming {
46
47
  if (request.tools && request.tools.length > 0) {
47
48
  body.tools = request.tools;
48
49
  }
50
+ if (request.thinking?.effort && request.thinking.effort !== "none") {
51
+ body.reasoning_effort = request.thinking.effort;
52
+ }
49
53
  let done = false;
50
54
  // Track tool calls being built across chunks
51
55
  const toolCallsMap = new Map();
@@ -60,7 +64,7 @@ export class OpenAIStreaming {
60
64
  ...request.headers
61
65
  },
62
66
  body: JSON.stringify(body),
63
- signal: abortController.signal
67
+ signal
64
68
  }, request.requestTimeout);
65
69
  if (!response.ok) {
66
70
  await handleOpenAIError(response, request.model);
@@ -119,6 +123,10 @@ export class OpenAIStreaming {
119
123
  if (delta?.content) {
120
124
  yield { content: delta.content };
121
125
  }
126
+ // Handle reasoning content delta
127
+ if (delta?.reasoning_content) {
128
+ yield { content: "", thinking: { text: delta.reasoning_content } };
129
+ }
122
130
  // Handle tool calls delta
123
131
  if (delta?.tool_calls) {
124
132
  for (const toolCallDelta of delta.tool_calls) {
@@ -5,6 +5,7 @@ import { registerGeminiProvider } from "./gemini/index.js";
5
5
  import { registerDeepSeekProvider } from "./deepseek/index.js";
6
6
  import { registerOllamaProvider } from "./ollama/index.js";
7
7
  import { registerOpenRouterProvider } from "./openrouter/index.js";
8
+ import { registerBedrockProvider } from "./bedrock/index.js";
8
9
  import { NodeLLMConfig } from "../config.js";
9
10
  type ProviderFactory = (config?: NodeLLMConfig) => Provider;
10
11
  declare class ProviderRegistry {
@@ -42,5 +43,5 @@ declare class ProviderRegistry {
42
43
  * ```
43
44
  */
44
45
  export declare const providerRegistry: ProviderRegistry;
45
- export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
46
+ export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider, registerBedrockProvider };
46
47
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,KAAK,eAAe,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,KAAK,QAAQ,CAAC;AAE5D,cAAM,gBAAgB;IACpB,OAAO,CAAC,SAAS,CAAsC;IAEvD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAQtD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,QAAQ;IAUvD;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,kBAAyB,CAAC;AAGvD,OAAO,EACL,sBAAsB,IAAI,sBAAsB,EAChD,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC3B,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,KAAK,eAAe,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,KAAK,QAAQ,CAAC;AAE5D,cAAM,gBAAgB;IACpB,OAAO,CAAC,SAAS,CAAsC;IAEvD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAQtD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,QAAQ;IAUvD;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;CAGjB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,kBAAyB,CAAC;AAGvD,OAAO,EACL,sBAAsB,IAAI,sBAAsB,EAChD,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,uBAAuB,EACxB,CAAC"}
@@ -4,6 +4,7 @@ import { registerGeminiProvider } from "./gemini/index.js";
4
4
  import { registerDeepSeekProvider } from "./deepseek/index.js";
5
5
  import { registerOllamaProvider } from "./ollama/index.js";
6
6
  import { registerOpenRouterProvider } from "./openrouter/index.js";
7
+ import { registerBedrockProvider } from "./bedrock/index.js";
7
8
  class ProviderRegistry {
8
9
  providers = new Map();
9
10
  /**
@@ -55,4 +56,4 @@ class ProviderRegistry {
55
56
  */
56
57
  export const providerRegistry = new ProviderRegistry();
57
58
  // Exported registration functions (delegates to provider-specific index files)
58
- export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider };
59
+ export { registerOpenAIProvider as ensureOpenAIRegistered, registerOpenAIProvider, registerAnthropicProvider, registerGeminiProvider, registerDeepSeekProvider, registerOllamaProvider, registerOpenRouterProvider, registerBedrockProvider };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * AWS Signature Version 4 (SigV4) Request Signing Utility
3
+ *
4
+ * This is a minimal, focused implementation for signing AWS API requests.
5
+ * It uses only Node.js native `crypto` module — no AWS SDK dependencies.
6
+ *
7
+ * Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
8
+ */
9
+ export interface AwsCredentials {
10
+ accessKeyId: string;
11
+ secretAccessKey: string;
12
+ sessionToken?: string;
13
+ }
14
+ export interface SignRequestOptions {
15
+ method: "POST" | "GET";
16
+ url: string;
17
+ body: string;
18
+ credentials: AwsCredentials;
19
+ region: string;
20
+ service: string;
21
+ timestamp?: Date;
22
+ }
23
+ export interface SignedHeaders {
24
+ host: string;
25
+ "x-amz-date": string;
26
+ "x-amz-content-sha256": string;
27
+ authorization: string;
28
+ "x-amz-security-token"?: string;
29
+ }
30
+ /**
31
+ * Sign an AWS API request using SigV4.
32
+ *
33
+ * @param options - The request details and credentials
34
+ * @returns Headers to include in the HTTP request
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const headers = signRequest({
39
+ * method: "POST",
40
+ * url: "https://bedrock-runtime.us-east-1.amazonaws.com/model/.../converse",
41
+ * body: JSON.stringify({ messages: [...] }),
42
+ * credentials: { accessKeyId: "...", secretAccessKey: "..." },
43
+ * region: "us-east-1",
44
+ * service: "bedrock"
45
+ * });
46
+ *
47
+ * fetch(url, { headers: { ...headers, "Content-Type": "application/json" }, body });
48
+ * ```
49
+ */
50
+ export declare function signRequest(options: SignRequestOptions): SignedHeaders;
51
+ //# sourceMappingURL=AwsSigV4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AwsSigV4.d.ts","sourceRoot":"","sources":["../../src/utils/AwsSigV4.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,cAAc,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AA8KD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAwEtE"}
@@ -0,0 +1,209 @@
1
+ /**
2
+ * AWS Signature Version 4 (SigV4) Request Signing Utility
3
+ *
4
+ * This is a minimal, focused implementation for signing AWS API requests.
5
+ * It uses only Node.js native `crypto` module — no AWS SDK dependencies.
6
+ *
7
+ * Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html
8
+ */
9
+ import { createHmac, createHash } from "node:crypto";
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ // Constants
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+ const ALGORITHM = "AWS4-HMAC-SHA256";
14
+ const AWS4_REQUEST = "aws4_request";
15
+ // ─────────────────────────────────────────────────────────────────────────────
16
+ // Helper Functions
17
+ // ─────────────────────────────────────────────────────────────────────────────
18
+ /**
19
+ * SHA256 hash of a string, returned as hex.
20
+ */
21
+ function sha256Hex(data) {
22
+ return createHash("sha256").update(data, "utf8").digest("hex");
23
+ }
24
+ /**
25
+ * HMAC-SHA256, returns raw bytes (Buffer).
26
+ */
27
+ function hmacSha256(key, data) {
28
+ return createHmac("sha256", key).update(data, "utf8").digest();
29
+ }
30
+ /**
31
+ * HMAC-SHA256, returns hex string.
32
+ */
33
+ function hmacSha256Hex(key, data) {
34
+ return createHmac("sha256", key).update(data, "utf8").digest("hex");
35
+ }
36
+ /**
37
+ * Format a Date as AWS timestamp: YYYYMMDD'T'HHMMSS'Z'
38
+ */
39
+ function formatAmzDate(date) {
40
+ return date.toISOString().replace(/[:-]|\.\d{3}/g, "");
41
+ }
42
+ /**
43
+ * Extract just the date portion: YYYYMMDD
44
+ */
45
+ function formatDateOnly(amzDate) {
46
+ return amzDate.substring(0, 8);
47
+ }
48
+ /**
49
+ * Parse host from URL.
50
+ */
51
+ function extractHost(url) {
52
+ const parsed = new URL(url);
53
+ return parsed.host;
54
+ }
55
+ /**
56
+ * Parse path from URL (without query string).
57
+ */
58
+ function extractPath(url) {
59
+ const parsed = new URL(url);
60
+ const path = parsed.pathname || "/";
61
+ return path
62
+ .split("/")
63
+ .map((segment) => encodeURIComponent(segment))
64
+ .join("/")
65
+ .replace(/%20/g, "%20") // encodeURIComponent already does %20
66
+ .replace(/'/g, "%27") // AWS expects single quotes encoded
67
+ .replace(/\(/g, "%28")
68
+ .replace(/\)/g, "%29")
69
+ .replace(/\*/g, "%2A");
70
+ }
71
+ // ─────────────────────────────────────────────────────────────────────────────
72
+ // Core Signing Logic
73
+ // ─────────────────────────────────────────────────────────────────────────────
74
+ /**
75
+ * Step 4: Derive the signing key.
76
+ *
77
+ * kSecret = "AWS4" + secretAccessKey
78
+ * kDate = HMAC-SHA256(kSecret, dateStamp)
79
+ * kRegion = HMAC-SHA256(kDate, region)
80
+ * kService = HMAC-SHA256(kRegion, service)
81
+ * kSigning = HMAC-SHA256(kService, "aws4_request")
82
+ */
83
+ function deriveSigningKey(secretAccessKey, dateStamp, region, service) {
84
+ const kSecret = "AWS4" + secretAccessKey;
85
+ const kDate = hmacSha256(kSecret, dateStamp);
86
+ const kRegion = hmacSha256(kDate, region);
87
+ const kService = hmacSha256(kRegion, service);
88
+ const kSigning = hmacSha256(kService, AWS4_REQUEST);
89
+ return kSigning;
90
+ }
91
+ /**
92
+ * Step 2: Build the canonical request string.
93
+ *
94
+ * Format:
95
+ * HTTPMethod
96
+ * CanonicalURI
97
+ * CanonicalQueryString (empty for us)
98
+ * CanonicalHeaders
99
+ * (blank line)
100
+ * SignedHeaders
101
+ * HashedPayload
102
+ */
103
+ function buildCanonicalRequest(method, path, headers, signedHeadersList, payloadHash) {
104
+ // Sort headers alphabetically
105
+ const sortedHeaders = signedHeadersList.sort();
106
+ // Build canonical headers: each line is "lowercase-key:trimmed-value\n"
107
+ const canonicalHeaders = sortedHeaders
108
+ .map((key) => `${key}:${(headers[key] ?? "").trim()}`)
109
+ .join("\n");
110
+ // Signed headers: semicolon-separated list
111
+ const signedHeaders = sortedHeaders.join(";");
112
+ return [
113
+ method,
114
+ path,
115
+ "", // Query string (empty)
116
+ canonicalHeaders + "\n", // Must end with newline
117
+ signedHeaders,
118
+ payloadHash
119
+ ].join("\n");
120
+ }
121
+ /**
122
+ * Step 3: Build the string to sign.
123
+ *
124
+ * Format:
125
+ * Algorithm
126
+ * RequestDateTime
127
+ * CredentialScope
128
+ * HashedCanonicalRequest
129
+ */
130
+ function buildStringToSign(amzDate, credentialScope, canonicalRequest) {
131
+ const hashedCanonicalRequest = sha256Hex(canonicalRequest);
132
+ return [ALGORITHM, amzDate, credentialScope, hashedCanonicalRequest].join("\n");
133
+ }
134
+ /**
135
+ * Step 6: Build the Authorization header value.
136
+ */
137
+ function buildAuthorizationHeader(accessKeyId, credentialScope, signedHeaders, signature) {
138
+ return `${ALGORITHM} Credential=${accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
139
+ }
140
+ // ─────────────────────────────────────────────────────────────────────────────
141
+ // Public API
142
+ // ─────────────────────────────────────────────────────────────────────────────
143
+ /**
144
+ * Sign an AWS API request using SigV4.
145
+ *
146
+ * @param options - The request details and credentials
147
+ * @returns Headers to include in the HTTP request
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const headers = signRequest({
152
+ * method: "POST",
153
+ * url: "https://bedrock-runtime.us-east-1.amazonaws.com/model/.../converse",
154
+ * body: JSON.stringify({ messages: [...] }),
155
+ * credentials: { accessKeyId: "...", secretAccessKey: "..." },
156
+ * region: "us-east-1",
157
+ * service: "bedrock"
158
+ * });
159
+ *
160
+ * fetch(url, { headers: { ...headers, "Content-Type": "application/json" }, body });
161
+ * ```
162
+ */
163
+ export function signRequest(options) {
164
+ const { method, url, body, credentials, region, service, timestamp } = options;
165
+ // Timestamps (use provided timestamp for testing, or current time)
166
+ const now = timestamp ?? new Date();
167
+ const amzDate = formatAmzDate(now);
168
+ const dateStamp = formatDateOnly(amzDate);
169
+ // Step 1: Hash the payload
170
+ const payloadHash = sha256Hex(body);
171
+ // Extract URL components
172
+ const host = extractHost(url);
173
+ const path = extractPath(url);
174
+ // Build headers to sign (sorted alphabetically)
175
+ const headersToSign = {
176
+ host: host,
177
+ "x-amz-content-sha256": payloadHash,
178
+ "x-amz-date": amzDate
179
+ };
180
+ // Add session token if present
181
+ if (credentials.sessionToken) {
182
+ headersToSign["x-amz-security-token"] = credentials.sessionToken;
183
+ }
184
+ const signedHeadersList = Object.keys(headersToSign);
185
+ // Step 2: Canonical Request
186
+ const canonicalRequest = buildCanonicalRequest(method, path, headersToSign, signedHeadersList, payloadHash);
187
+ // Credential scope: date/region/service/aws4_request
188
+ const credentialScope = `${dateStamp}/${region}/${service}/${AWS4_REQUEST}`;
189
+ // Step 3: String to Sign
190
+ const stringToSign = buildStringToSign(amzDate, credentialScope, canonicalRequest);
191
+ // Step 4: Signing Key
192
+ const signingKey = deriveSigningKey(credentials.secretAccessKey, dateStamp, region, service);
193
+ // Step 5: Signature
194
+ const signature = hmacSha256Hex(signingKey, stringToSign);
195
+ // Step 6: Authorization Header
196
+ const signedHeaders = signedHeadersList.sort().join(";");
197
+ const authorization = buildAuthorizationHeader(credentials.accessKeyId, credentialScope, signedHeaders, signature);
198
+ // Build final headers
199
+ const result = {
200
+ host: host,
201
+ "x-amz-date": amzDate,
202
+ "x-amz-content-sha256": payloadHash,
203
+ authorization: authorization
204
+ };
205
+ if (credentials.sessionToken) {
206
+ result["x-amz-security-token"] = credentials.sessionToken;
207
+ }
208
+ return result;
209
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-llm/core",
3
- "version": "1.6.2",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",