@providerprotocol/ai 0.0.18 → 0.0.20

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 (39) hide show
  1. package/README.md +364 -111
  2. package/dist/anthropic/index.d.ts +1 -1
  3. package/dist/anthropic/index.js +6 -6
  4. package/dist/chunk-P5IRTEM5.js +120 -0
  5. package/dist/chunk-P5IRTEM5.js.map +1 -0
  6. package/dist/{chunk-5FEAOEXV.js → chunk-U3FZWV4U.js} +53 -102
  7. package/dist/chunk-U3FZWV4U.js.map +1 -0
  8. package/dist/chunk-WAKD3OO5.js +224 -0
  9. package/dist/chunk-WAKD3OO5.js.map +1 -0
  10. package/dist/content-DEl3z_W2.d.ts +276 -0
  11. package/dist/google/index.d.ts +3 -1
  12. package/dist/google/index.js +123 -7
  13. package/dist/google/index.js.map +1 -1
  14. package/dist/http/index.d.ts +2 -2
  15. package/dist/http/index.js +4 -3
  16. package/dist/image-Dhq-Yuq4.d.ts +456 -0
  17. package/dist/index.d.ts +55 -163
  18. package/dist/index.js +81 -213
  19. package/dist/index.js.map +1 -1
  20. package/dist/ollama/index.d.ts +1 -1
  21. package/dist/ollama/index.js +6 -6
  22. package/dist/openai/index.d.ts +47 -20
  23. package/dist/openai/index.js +310 -7
  24. package/dist/openai/index.js.map +1 -1
  25. package/dist/openrouter/index.d.ts +1 -1
  26. package/dist/openrouter/index.js +6 -6
  27. package/dist/{provider-D5MO3-pS.d.ts → provider-BBMBZuGn.d.ts} +11 -11
  28. package/dist/proxy/index.d.ts +310 -86
  29. package/dist/proxy/index.js +33 -59
  30. package/dist/proxy/index.js.map +1 -1
  31. package/dist/{retry-DZ4Sqmxp.d.ts → retry-DR7YRJDz.d.ts} +1 -1
  32. package/dist/{stream-BjyVzBxV.d.ts → stream-DRHy6q1a.d.ts} +2 -275
  33. package/dist/xai/index.d.ts +29 -1
  34. package/dist/xai/index.js +119 -7
  35. package/dist/xai/index.js.map +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunk-5FEAOEXV.js.map +0 -1
  38. package/dist/chunk-DZQHVGNV.js +0 -71
  39. package/dist/chunk-DZQHVGNV.js.map +0 -1
@@ -0,0 +1,120 @@
1
+ import {
2
+ UPPError
3
+ } from "./chunk-U3FZWV4U.js";
4
+
5
+ // src/http/keys.ts
6
+ var RoundRobinKeys = class {
7
+ keys;
8
+ index = 0;
9
+ /**
10
+ * Creates a new RoundRobinKeys instance.
11
+ *
12
+ * @param keys - Array of API keys to rotate through
13
+ * @throws {Error} When the keys array is empty
14
+ */
15
+ constructor(keys) {
16
+ if (keys.length === 0) {
17
+ throw new Error("RoundRobinKeys requires at least one key");
18
+ }
19
+ this.keys = keys;
20
+ }
21
+ /**
22
+ * Returns the next key in the rotation sequence.
23
+ *
24
+ * @returns The next API key in round-robin order
25
+ */
26
+ getKey() {
27
+ const key = this.keys[this.index];
28
+ this.index = (this.index + 1) % this.keys.length;
29
+ return key;
30
+ }
31
+ };
32
+ var WeightedKeys = class {
33
+ entries;
34
+ totalWeight;
35
+ /**
36
+ * Creates a new WeightedKeys instance.
37
+ *
38
+ * @param keys - Array of key-weight pairs defining selection probabilities
39
+ * @throws {Error} When the keys array is empty
40
+ */
41
+ constructor(keys) {
42
+ if (keys.length === 0) {
43
+ throw new Error("WeightedKeys requires at least one key");
44
+ }
45
+ this.entries = keys;
46
+ this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);
47
+ }
48
+ /**
49
+ * Returns a randomly selected key based on configured weights.
50
+ *
51
+ * @returns An API key selected with probability proportional to its weight
52
+ */
53
+ getKey() {
54
+ const random = Math.random() * this.totalWeight;
55
+ let cumulative = 0;
56
+ for (const entry of this.entries) {
57
+ cumulative += entry.weight;
58
+ if (random <= cumulative) {
59
+ return entry.key;
60
+ }
61
+ }
62
+ return this.entries[this.entries.length - 1].key;
63
+ }
64
+ };
65
+ var DynamicKey = class {
66
+ selector;
67
+ /**
68
+ * Creates a new DynamicKey instance.
69
+ *
70
+ * @param selector - Function that returns an API key (sync or async)
71
+ */
72
+ constructor(selector) {
73
+ this.selector = selector;
74
+ }
75
+ /**
76
+ * Invokes the selector function to retrieve the current key.
77
+ *
78
+ * @returns Promise resolving to the selected API key
79
+ */
80
+ async getKey() {
81
+ return this.selector();
82
+ }
83
+ };
84
+ function isKeyStrategy(value) {
85
+ return typeof value === "object" && value !== null && "getKey" in value && typeof value.getKey === "function";
86
+ }
87
+ async function resolveApiKey(config, envVar, provider = "unknown", modality = "llm") {
88
+ const { apiKey } = config;
89
+ if (apiKey !== void 0) {
90
+ if (typeof apiKey === "string") {
91
+ return apiKey;
92
+ }
93
+ if (typeof apiKey === "function") {
94
+ return apiKey();
95
+ }
96
+ if (isKeyStrategy(apiKey)) {
97
+ return apiKey.getKey();
98
+ }
99
+ }
100
+ if (envVar) {
101
+ const envValue = process.env[envVar];
102
+ if (envValue) {
103
+ return envValue;
104
+ }
105
+ }
106
+ throw new UPPError(
107
+ envVar ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.` : "API key not found. Provide apiKey in config.",
108
+ "AUTHENTICATION_FAILED",
109
+ provider,
110
+ modality
111
+ );
112
+ }
113
+
114
+ export {
115
+ RoundRobinKeys,
116
+ WeightedKeys,
117
+ DynamicKey,
118
+ resolveApiKey
119
+ };
120
+ //# sourceMappingURL=chunk-P5IRTEM5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/http/keys.ts"],"sourcesContent":["/**\n * API key management strategies for load balancing and dynamic key selection.\n * @module http/keys\n */\n\nimport type { ProviderConfig, KeyStrategy } from '../types/provider.ts';\nimport { UPPError, type Modality } from '../types/errors.ts';\n\n/**\n * Distributes API requests across multiple keys using round-robin selection.\n *\n * Each call to {@link getKey} returns the next key in sequence, cycling back to\n * the first key after reaching the end. This provides even distribution of requests\n * across all available keys, which is useful for:\n * - Spreading rate limits across multiple API keys\n * - Load balancing between different accounts\n * - Maximizing throughput when multiple keys are available\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new RoundRobinKeys([\n * 'sk-key-1',\n * 'sk-key-2',\n * 'sk-key-3'\n * ]);\n *\n * keys.getKey(); // Returns 'sk-key-1'\n * keys.getKey(); // Returns 'sk-key-2'\n * keys.getKey(); // Returns 'sk-key-3'\n * keys.getKey(); // Returns 'sk-key-1' (cycles back)\n * ```\n */\nexport class RoundRobinKeys implements KeyStrategy {\n private keys: string[];\n private index = 0;\n\n /**\n * Creates a new RoundRobinKeys instance.\n *\n * @param keys - Array of API keys to rotate through\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: string[]) {\n if (keys.length === 0) {\n throw new Error('RoundRobinKeys requires at least one key');\n }\n this.keys = keys;\n }\n\n /**\n * Returns the next key in the rotation sequence.\n *\n * @returns The next API key in round-robin order\n */\n getKey(): string {\n const key = this.keys[this.index]!;\n this.index = (this.index + 1) % this.keys.length;\n return key;\n }\n}\n\n/**\n * Selects API keys using weighted random probability.\n *\n * Each key is assigned a weight that determines its probability of being selected.\n * Higher weights mean higher selection probability. This is useful for:\n * - Preferring higher-tier API keys with better rate limits\n * - Gradually migrating traffic between old and new keys\n * - A/B testing different API accounts\n * - Directing more traffic to keys with higher quotas\n *\n * The selection probability for each key is: weight / totalWeight\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * const keys = new WeightedKeys([\n * { key: 'sk-premium', weight: 70 }, // 70% of requests\n * { key: 'sk-standard', weight: 20 }, // 20% of requests\n * { key: 'sk-backup', weight: 10 } // 10% of requests\n * ]);\n *\n * // Configure provider with weighted key selection\n * const provider = createOpenAI({\n * apiKey: keys\n * });\n * ```\n */\nexport class WeightedKeys implements KeyStrategy {\n private entries: Array<{ key: string; weight: number }>;\n private totalWeight: number;\n\n /**\n * Creates a new WeightedKeys instance.\n *\n * @param keys - Array of key-weight pairs defining selection probabilities\n * @throws {Error} When the keys array is empty\n */\n constructor(keys: Array<{ key: string; weight: number }>) {\n if (keys.length === 0) {\n throw new Error('WeightedKeys requires at least one key');\n }\n this.entries = keys;\n this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);\n }\n\n /**\n * Returns a randomly selected key based on configured weights.\n *\n * @returns An API key selected with probability proportional to its weight\n */\n getKey(): string {\n const random = Math.random() * this.totalWeight;\n let cumulative = 0;\n\n for (const entry of this.entries) {\n cumulative += entry.weight;\n if (random <= cumulative) {\n return entry.key;\n }\n }\n\n return this.entries[this.entries.length - 1]!.key;\n }\n}\n\n/**\n * Provides dynamic key selection using custom logic.\n *\n * This strategy delegates key selection to a user-provided function, enabling\n * advanced scenarios such as:\n * - Fetching keys from a secrets manager (AWS Secrets Manager, HashiCorp Vault)\n * - Rotating keys based on external state or configuration\n * - Selecting keys based on request context or time of day\n * - Implementing custom load balancing algorithms\n *\n * The selector function can be synchronous or asynchronous.\n *\n * @implements {KeyStrategy}\n *\n * @example\n * ```typescript\n * // Fetch key from environment based on current mode\n * const dynamicKey = new DynamicKey(() => {\n * return process.env.NODE_ENV === 'production'\n * ? process.env.PROD_API_KEY!\n * : process.env.DEV_API_KEY!;\n * });\n *\n * // Async key fetching from a secrets manager\n * const vaultKey = new DynamicKey(async () => {\n * const secret = await vault.read('secret/openai');\n * return secret.data.apiKey;\n * });\n *\n * // Time-based key rotation\n * const timedKey = new DynamicKey(() => {\n * const hour = new Date().getHours();\n * return hour < 12 ? morningKey : afternoonKey;\n * });\n * ```\n */\nexport class DynamicKey implements KeyStrategy {\n private selector: () => string | Promise<string>;\n\n /**\n * Creates a new DynamicKey instance.\n *\n * @param selector - Function that returns an API key (sync or async)\n */\n constructor(selector: () => string | Promise<string>) {\n this.selector = selector;\n }\n\n /**\n * Invokes the selector function to retrieve the current key.\n *\n * @returns Promise resolving to the selected API key\n */\n async getKey(): Promise<string> {\n return this.selector();\n }\n}\n\n/**\n * Type guard to check if a value implements the KeyStrategy interface.\n *\n * @param value - The value to check\n * @returns True if the value has a getKey method\n */\nfunction isKeyStrategy(value: unknown): value is KeyStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'getKey' in value &&\n typeof (value as KeyStrategy).getKey === 'function'\n );\n}\n\n/**\n * Resolves an API key from provider configuration with multiple fallback options.\n *\n * This function handles various key specification methods in priority order:\n * 1. Direct string key in config.apiKey\n * 2. Function returning a key (sync or async) in config.apiKey\n * 3. KeyStrategy instance in config.apiKey (RoundRobinKeys, WeightedKeys, DynamicKey)\n * 4. Environment variable fallback (if envVar parameter is provided)\n *\n * @param config - Provider configuration containing the apiKey option\n * @param envVar - Optional environment variable name to check as fallback\n * @param provider - Provider identifier for error context (default: 'unknown')\n * @param modality - Request modality for error context (default: 'llm')\n * @returns The resolved API key string\n *\n * @throws {UPPError} AUTHENTICATION_FAILED - When no valid key is found\n *\n * @example\n * ```typescript\n * // Direct key in config\n * const key1 = await resolveApiKey({ apiKey: 'sk-...' }, 'OPENAI_API_KEY', 'openai');\n *\n * // Function-based key\n * const key2 = await resolveApiKey({ apiKey: () => getKeyFromVault() }, undefined, 'anthropic');\n *\n * // KeyStrategy instance\n * const key3 = await resolveApiKey({\n * apiKey: new RoundRobinKeys(['sk-1', 'sk-2', 'sk-3'])\n * }, 'OPENAI_API_KEY', 'openai');\n *\n * // Environment variable fallback\n * const key4 = await resolveApiKey({}, 'ANTHROPIC_API_KEY', 'anthropic');\n * ```\n */\nexport async function resolveApiKey(\n config: ProviderConfig,\n envVar?: string,\n provider = 'unknown',\n modality: Modality = 'llm'\n): Promise<string> {\n const { apiKey } = config;\n\n if (apiKey !== undefined) {\n if (typeof apiKey === 'string') {\n return apiKey;\n }\n\n if (typeof apiKey === 'function') {\n return apiKey();\n }\n\n if (isKeyStrategy(apiKey)) {\n return apiKey.getKey();\n }\n }\n\n if (envVar) {\n const envValue = process.env[envVar];\n if (envValue) {\n return envValue;\n }\n }\n\n throw new UPPError(\n envVar\n ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.`\n : 'API key not found. Provide apiKey in config.',\n 'AUTHENTICATION_FAILED',\n provider,\n modality\n );\n}\n"],"mappings":";;;;;AAkCO,IAAM,iBAAN,MAA4C;AAAA,EACzC;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQhB,YAAY,MAAgB;AAC1B,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AACA,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,SAAK,SAAS,KAAK,QAAQ,KAAK,KAAK,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AA8BO,IAAM,eAAN,MAA0C;AAAA,EACvC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQR,YAAY,MAA8C;AACxD,QAAI,KAAK,WAAW,GAAG;AACrB,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,SAAK,UAAU;AACf,SAAK,cAAc,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAiB;AACf,UAAM,SAAS,KAAK,OAAO,IAAI,KAAK;AACpC,QAAI,aAAa;AAEjB,eAAW,SAAS,KAAK,SAAS;AAChC,oBAAc,MAAM;AACpB,UAAI,UAAU,YAAY;AACxB,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,KAAK,QAAQ,SAAS,CAAC,EAAG;AAAA,EAChD;AACF;AAsCO,IAAM,aAAN,MAAwC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOR,YAAY,UAA0C;AACpD,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAA0B;AAC9B,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAQA,SAAS,cAAc,OAAsC;AAC3D,SACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAY,SACZ,OAAQ,MAAsB,WAAW;AAE7C;AAoCA,eAAsB,cACpB,QACA,QACA,WAAW,WACX,WAAqB,OACJ;AACjB,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,WAAW,QAAW;AACxB,QAAI,OAAO,WAAW,UAAU;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO,OAAO;AAAA,IAChB;AAEA,QAAI,cAAc,MAAM,GAAG;AACzB,aAAO,OAAO,OAAO;AAAA,IACvB;AAAA,EACF;AAEA,MAAI,QAAQ;AACV,UAAM,WAAW,QAAQ,IAAI,MAAM;AACnC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,SACI,0BAA0B,MAAM,uDAChC;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
@@ -1,115 +1,69 @@
1
- import {
2
- UPPError
3
- } from "./chunk-DZQHVGNV.js";
4
-
5
- // src/http/keys.ts
6
- var RoundRobinKeys = class {
7
- keys;
8
- index = 0;
9
- /**
10
- * Creates a new RoundRobinKeys instance.
11
- *
12
- * @param keys - Array of API keys to rotate through
13
- * @throws {Error} When the keys array is empty
14
- */
15
- constructor(keys) {
16
- if (keys.length === 0) {
17
- throw new Error("RoundRobinKeys requires at least one key");
18
- }
19
- this.keys = keys;
20
- }
21
- /**
22
- * Returns the next key in the rotation sequence.
23
- *
24
- * @returns The next API key in round-robin order
25
- */
26
- getKey() {
27
- const key = this.keys[this.index];
28
- this.index = (this.index + 1) % this.keys.length;
29
- return key;
30
- }
31
- };
32
- var WeightedKeys = class {
33
- entries;
34
- totalWeight;
1
+ // src/types/errors.ts
2
+ var UPPError = class _UPPError extends Error {
3
+ /** Normalized error code for programmatic handling */
4
+ code;
5
+ /** Name of the provider that generated the error */
6
+ provider;
7
+ /** The modality that was being used when the error occurred */
8
+ modality;
9
+ /** HTTP status code from the provider's response, if available */
10
+ statusCode;
11
+ /** The original error that caused this UPPError, if wrapping another error */
12
+ cause;
13
+ /** Error class name, always 'UPPError' */
14
+ name = "UPPError";
35
15
  /**
36
- * Creates a new WeightedKeys instance.
16
+ * Creates a new UPPError instance.
37
17
  *
38
- * @param keys - Array of key-weight pairs defining selection probabilities
39
- * @throws {Error} When the keys array is empty
18
+ * @param message - Human-readable error description
19
+ * @param code - Normalized error code for programmatic handling
20
+ * @param provider - Name of the provider that generated the error
21
+ * @param modality - The modality that was being used
22
+ * @param statusCode - HTTP status code from the provider's response
23
+ * @param cause - The original error being wrapped
40
24
  */
41
- constructor(keys) {
42
- if (keys.length === 0) {
43
- throw new Error("WeightedKeys requires at least one key");
25
+ constructor(message, code, provider, modality, statusCode, cause) {
26
+ super(message);
27
+ this.code = code;
28
+ this.provider = provider;
29
+ this.modality = modality;
30
+ this.statusCode = statusCode;
31
+ this.cause = cause;
32
+ if (Error.captureStackTrace) {
33
+ Error.captureStackTrace(this, _UPPError);
44
34
  }
45
- this.entries = keys;
46
- this.totalWeight = keys.reduce((sum, k) => sum + k.weight, 0);
47
35
  }
48
36
  /**
49
- * Returns a randomly selected key based on configured weights.
37
+ * Creates a string representation of the error.
50
38
  *
51
- * @returns An API key selected with probability proportional to its weight
39
+ * @returns Formatted error string including code, message, provider, and modality
52
40
  */
53
- getKey() {
54
- const random = Math.random() * this.totalWeight;
55
- let cumulative = 0;
56
- for (const entry of this.entries) {
57
- cumulative += entry.weight;
58
- if (random <= cumulative) {
59
- return entry.key;
60
- }
41
+ toString() {
42
+ let str = `UPPError [${this.code}]: ${this.message}`;
43
+ str += ` (provider: ${this.provider}, modality: ${this.modality}`;
44
+ if (this.statusCode) {
45
+ str += `, status: ${this.statusCode}`;
61
46
  }
62
- return this.entries[this.entries.length - 1].key;
63
- }
64
- };
65
- var DynamicKey = class {
66
- selector;
67
- /**
68
- * Creates a new DynamicKey instance.
69
- *
70
- * @param selector - Function that returns an API key (sync or async)
71
- */
72
- constructor(selector) {
73
- this.selector = selector;
47
+ str += ")";
48
+ return str;
74
49
  }
75
50
  /**
76
- * Invokes the selector function to retrieve the current key.
51
+ * Converts the error to a JSON-serializable object.
77
52
  *
78
- * @returns Promise resolving to the selected API key
53
+ * @returns Plain object representation suitable for logging or transmission
79
54
  */
80
- async getKey() {
81
- return this.selector();
55
+ toJSON() {
56
+ return {
57
+ name: this.name,
58
+ message: this.message,
59
+ code: this.code,
60
+ provider: this.provider,
61
+ modality: this.modality,
62
+ statusCode: this.statusCode,
63
+ cause: this.cause?.message
64
+ };
82
65
  }
83
66
  };
84
- function isKeyStrategy(value) {
85
- return typeof value === "object" && value !== null && "getKey" in value && typeof value.getKey === "function";
86
- }
87
- async function resolveApiKey(config, envVar, provider = "unknown", modality = "llm") {
88
- const { apiKey } = config;
89
- if (apiKey !== void 0) {
90
- if (typeof apiKey === "string") {
91
- return apiKey;
92
- }
93
- if (typeof apiKey === "function") {
94
- return apiKey();
95
- }
96
- if (isKeyStrategy(apiKey)) {
97
- return apiKey.getKey();
98
- }
99
- }
100
- if (envVar) {
101
- const envValue = process.env[envVar];
102
- if (envValue) {
103
- return envValue;
104
- }
105
- }
106
- throw new UPPError(
107
- envVar ? `API key not found. Set ${envVar} environment variable or provide apiKey in config.` : "API key not found. Provide apiKey in config.",
108
- "AUTHENTICATION_FAILED",
109
- provider,
110
- modality
111
- );
112
- }
113
67
 
114
68
  // src/http/errors.ts
115
69
  function statusToErrorCode(status) {
@@ -310,10 +264,7 @@ async function doStreamFetch(url, init, config, provider, modality) {
310
264
  }
311
265
 
312
266
  export {
313
- RoundRobinKeys,
314
- WeightedKeys,
315
- DynamicKey,
316
- resolveApiKey,
267
+ UPPError,
317
268
  statusToErrorCode,
318
269
  normalizeHttpError,
319
270
  networkError,
@@ -322,4 +273,4 @@ export {
322
273
  doFetch,
323
274
  doStreamFetch
324
275
  };
325
- //# sourceMappingURL=chunk-5FEAOEXV.js.map
276
+ //# sourceMappingURL=chunk-U3FZWV4U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/errors.ts","../src/http/errors.ts","../src/http/fetch.ts"],"sourcesContent":["/**\n * @fileoverview Error types for the Unified Provider Protocol.\n *\n * Provides normalized error codes and a unified error class for handling\n * errors across different AI providers in a consistent manner.\n *\n * @module types/errors\n */\n\n/**\n * Normalized error codes for cross-provider error handling.\n *\n * These codes provide a consistent way to identify and handle errors\n * regardless of which AI provider generated them.\n *\n * @example\n * ```typescript\n * try {\n * await llm.generate('Hello');\n * } catch (error) {\n * if (error instanceof UPPError) {\n * switch (error.code) {\n * case 'RATE_LIMITED':\n * await delay(error.retryAfter);\n * break;\n * case 'AUTHENTICATION_FAILED':\n * throw new Error('Invalid API key');\n * }\n * }\n * }\n * ```\n */\nexport type ErrorCode =\n /** API key is invalid or expired */\n | 'AUTHENTICATION_FAILED'\n /** Rate limit exceeded, retry after delay */\n | 'RATE_LIMITED'\n /** Input exceeds model's context window */\n | 'CONTEXT_LENGTH_EXCEEDED'\n /** Requested model does not exist */\n | 'MODEL_NOT_FOUND'\n /** Request parameters are malformed */\n | 'INVALID_REQUEST'\n /** Provider returned an unexpected response format */\n | 'INVALID_RESPONSE'\n /** Content was blocked by safety filters */\n | 'CONTENT_FILTERED'\n /** Account quota or credits exhausted */\n | 'QUOTA_EXCEEDED'\n /** Provider-specific error not covered by other codes */\n | 'PROVIDER_ERROR'\n /** Network connectivity issue */\n | 'NETWORK_ERROR'\n /** Request exceeded timeout limit */\n | 'TIMEOUT'\n /** Request was cancelled via AbortSignal */\n | 'CANCELLED';\n\n/**\n * Modality types supported by UPP.\n *\n * Each modality represents a different type of AI capability that\n * can be provided by a UPP-compatible provider.\n */\nexport type Modality =\n /** Large language model for text generation */\n | 'llm'\n /** Text/image embedding model */\n | 'embedding'\n /** Image generation model */\n | 'image'\n /** Audio processing/generation model */\n | 'audio'\n /** Video processing/generation model */\n | 'video';\n\n/**\n * Unified Provider Protocol Error.\n *\n * All provider-specific errors are normalized to this type, providing\n * a consistent interface for error handling across different AI providers.\n *\n * @example\n * ```typescript\n * throw new UPPError(\n * 'API key is invalid',\n * 'AUTHENTICATION_FAILED',\n * 'openai',\n * 'llm',\n * 401\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Wrapping a provider error\n * try {\n * await openai.chat.completions.create({ ... });\n * } catch (err) {\n * throw new UPPError(\n * 'OpenAI request failed',\n * 'PROVIDER_ERROR',\n * 'openai',\n * 'llm',\n * err.status,\n * err\n * );\n * }\n * ```\n */\nexport class UPPError extends Error {\n /** Normalized error code for programmatic handling */\n readonly code: ErrorCode;\n\n /** Name of the provider that generated the error */\n readonly provider: string;\n\n /** The modality that was being used when the error occurred */\n readonly modality: Modality;\n\n /** HTTP status code from the provider's response, if available */\n readonly statusCode?: number;\n\n /** The original error that caused this UPPError, if wrapping another error */\n override readonly cause?: Error;\n\n /** Error class name, always 'UPPError' */\n override readonly name = 'UPPError';\n\n /**\n * Creates a new UPPError instance.\n *\n * @param message - Human-readable error description\n * @param code - Normalized error code for programmatic handling\n * @param provider - Name of the provider that generated the error\n * @param modality - The modality that was being used\n * @param statusCode - HTTP status code from the provider's response\n * @param cause - The original error being wrapped\n */\n constructor(\n message: string,\n code: ErrorCode,\n provider: string,\n modality: Modality,\n statusCode?: number,\n cause?: Error\n ) {\n super(message);\n this.code = code;\n this.provider = provider;\n this.modality = modality;\n this.statusCode = statusCode;\n this.cause = cause;\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, UPPError);\n }\n }\n\n /**\n * Creates a string representation of the error.\n *\n * @returns Formatted error string including code, message, provider, and modality\n */\n override toString(): string {\n let str = `UPPError [${this.code}]: ${this.message}`;\n str += ` (provider: ${this.provider}, modality: ${this.modality}`;\n if (this.statusCode) {\n str += `, status: ${this.statusCode}`;\n }\n str += ')';\n return str;\n }\n\n /**\n * Converts the error to a JSON-serializable object.\n *\n * @returns Plain object representation suitable for logging or transmission\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n provider: this.provider,\n modality: this.modality,\n statusCode: this.statusCode,\n cause: this.cause?.message,\n };\n }\n}\n","/**\n * HTTP error handling and normalization utilities.\n * @module http/errors\n */\n\nimport { UPPError, type ErrorCode, type Modality } from '../types/errors.ts';\n\n/**\n * Maps HTTP status codes to standardized UPP error codes.\n *\n * This function provides consistent error categorization across all providers:\n * - 400 -> INVALID_REQUEST (bad request format or parameters)\n * - 401, 403 -> AUTHENTICATION_FAILED (invalid or missing credentials)\n * - 404 -> MODEL_NOT_FOUND (requested model does not exist)\n * - 408 -> TIMEOUT (request timed out)\n * - 413 -> CONTEXT_LENGTH_EXCEEDED (input too long)\n * - 429 -> RATE_LIMITED (too many requests)\n * - 5xx -> PROVIDER_ERROR (server-side issues)\n *\n * @param status - HTTP status code from the response\n * @returns The corresponding UPP ErrorCode\n *\n * @example\n * ```typescript\n * const errorCode = statusToErrorCode(429);\n * // Returns 'RATE_LIMITED'\n *\n * const serverError = statusToErrorCode(503);\n * // Returns 'PROVIDER_ERROR'\n * ```\n */\nexport function statusToErrorCode(status: number): ErrorCode {\n switch (status) {\n case 400:\n return 'INVALID_REQUEST';\n case 401:\n case 403:\n return 'AUTHENTICATION_FAILED';\n case 404:\n return 'MODEL_NOT_FOUND';\n case 408:\n return 'TIMEOUT';\n case 413:\n return 'CONTEXT_LENGTH_EXCEEDED';\n case 429:\n return 'RATE_LIMITED';\n case 500:\n case 502:\n case 503:\n case 504:\n return 'PROVIDER_ERROR';\n default:\n return 'PROVIDER_ERROR';\n }\n}\n\n/**\n * Normalizes HTTP error responses into standardized UPPError objects.\n *\n * This function performs several operations:\n * 1. Maps the HTTP status code to an appropriate ErrorCode\n * 2. Attempts to extract a meaningful error message from the response body\n * 3. Handles various provider-specific error response formats\n *\n * Supported error message formats:\n * - `{ error: { message: \"...\" } }` (OpenAI, Anthropic)\n * - `{ message: \"...\" }` (simple format)\n * - `{ error: { error: { message: \"...\" } } }` (nested format)\n * - `{ detail: \"...\" }` (FastAPI style)\n * - Plain text body (if under 200 characters)\n *\n * @param response - The HTTP Response object with non-2xx status\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with normalized code and message\n *\n * @example\n * ```typescript\n * if (!response.ok) {\n * const error = await normalizeHttpError(response, 'openai', 'llm');\n * // error.code might be 'RATE_LIMITED' for 429\n * // error.message contains provider's error message\n * throw error;\n * }\n * ```\n */\nexport async function normalizeHttpError(\n response: Response,\n provider: string,\n modality: Modality\n): Promise<UPPError> {\n const code = statusToErrorCode(response.status);\n let message = `HTTP ${response.status}: ${response.statusText}`;\n\n try {\n const body = await response.text();\n if (body) {\n try {\n const json = JSON.parse(body);\n const extractedMessage =\n json.error?.message ||\n json.message ||\n json.error?.error?.message ||\n json.detail;\n\n if (extractedMessage) {\n message = extractedMessage;\n }\n } catch {\n if (body.length < 200) {\n message = body;\n }\n }\n }\n } catch {\n }\n\n return new UPPError(message, code, provider, modality, response.status);\n}\n\n/**\n * Creates a UPPError for network failures (DNS, connection, etc.).\n *\n * Use this when the request fails before receiving any HTTP response,\n * such as DNS resolution failures, connection refused, or network unreachable.\n *\n * @param error - The underlying Error that caused the failure\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with NETWORK_ERROR code and the original error attached\n */\nexport function networkError(\n error: Error,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Network error: ${error.message}`,\n 'NETWORK_ERROR',\n provider,\n modality,\n undefined,\n error\n );\n}\n\n/**\n * Creates a UPPError for request timeout.\n *\n * Use this when the request exceeds the configured timeout duration\n * and is aborted by the AbortController.\n *\n * @param timeout - The timeout duration in milliseconds that was exceeded\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with TIMEOUT code\n */\nexport function timeoutError(\n timeout: number,\n provider: string,\n modality: Modality\n): UPPError {\n return new UPPError(\n `Request timed out after ${timeout}ms`,\n 'TIMEOUT',\n provider,\n modality\n );\n}\n\n/**\n * Creates a UPPError for user-initiated request cancellation.\n *\n * Use this when the request is aborted via a user-provided AbortSignal,\n * distinct from timeout-based cancellation.\n *\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns A UPPError with CANCELLED code\n */\nexport function cancelledError(provider: string, modality: Modality): UPPError {\n return new UPPError('Request was cancelled', 'CANCELLED', provider, modality);\n}\n","/**\n * HTTP fetch utilities with retry, timeout, and error normalization.\n * @module http/fetch\n */\n\nimport type { ProviderConfig } from '../types/provider.ts';\nimport type { Modality } from '../types/errors.ts';\nimport { UPPError } from '../types/errors.ts';\nimport {\n normalizeHttpError,\n networkError,\n timeoutError,\n cancelledError,\n} from './errors.ts';\n\n/** Default request timeout in milliseconds (2 minutes). */\nconst DEFAULT_TIMEOUT = 120000;\n\n/**\n * Executes an HTTP fetch request with automatic retry, timeout handling, and error normalization.\n *\n * This function wraps the standard fetch API with additional capabilities:\n * - Configurable timeout with automatic request cancellation\n * - Retry strategy support (exponential backoff, linear, token bucket, etc.)\n * - Pre-request delay support for rate limiting strategies\n * - Automatic Retry-After header parsing and handling\n * - Error normalization to UPPError format\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options (method, headers, body, etc.)\n * @param config - Provider configuration containing fetch customization, timeout, and retry strategy\n * @param provider - Provider identifier for error context (e.g., 'openai', 'anthropic')\n * @param modality - Request modality for error context (e.g., 'llm', 'embedding', 'image')\n * @returns The successful Response object\n *\n * @throws {UPPError} RATE_LIMITED - When rate limited and retries exhausted\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n * @throws {UPPError} Various codes based on HTTP status (see statusToErrorCode)\n *\n * @example\n * ```typescript\n * const response = await doFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [] })\n * },\n * { timeout: 30000, retryStrategy: new ExponentialBackoff() },\n * 'openai',\n * 'llm'\n * );\n * ```\n */\nexport async function doFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const strategy = config.retryStrategy;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n let lastError: UPPError | undefined;\n let attempt = 0;\n\n while (true) {\n attempt++;\n\n try {\n const response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n\n if (!response.ok) {\n const error = await normalizeHttpError(response, provider, modality);\n\n const retryAfter = response.headers.get('Retry-After');\n if (retryAfter && strategy) {\n const seconds = parseInt(retryAfter, 10);\n if (!isNaN(seconds) && 'setRetryAfter' in strategy) {\n (strategy as { setRetryAfter: (s: number) => void }).setRetryAfter(\n seconds\n );\n }\n }\n\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n lastError = error;\n continue;\n }\n }\n\n throw error;\n }\n\n strategy?.reset?.();\n\n return response;\n } catch (error) {\n if (error instanceof UPPError) {\n if (strategy) {\n const delay = await strategy.onRetry(error, attempt);\n if (delay !== null) {\n await sleep(delay);\n lastError = error;\n continue;\n }\n }\n throw error;\n }\n\n const uppError = networkError(error as Error, provider, modality);\n\n if (strategy) {\n const delay = await strategy.onRetry(uppError, attempt);\n if (delay !== null) {\n await sleep(delay);\n lastError = uppError;\n continue;\n }\n }\n\n throw uppError;\n }\n }\n}\n\n/**\n * Executes a fetch request with configurable timeout.\n *\n * Creates an AbortController to cancel the request if it exceeds the timeout.\n * Properly handles both user-provided abort signals and timeout-based cancellation,\n * throwing appropriate error types for each case.\n *\n * @param fetchFn - The fetch function to use (allows custom implementations)\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param timeout - Maximum time in milliseconds before aborting\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response from the fetch call\n *\n * @throws {UPPError} TIMEOUT - When the timeout is exceeded\n * @throws {UPPError} CANCELLED - When cancelled via user-provided signal\n * @throws {Error} Network errors are passed through unchanged\n */\nasync function fetchWithTimeout(\n fetchFn: typeof fetch,\n url: string,\n init: RequestInit,\n timeout: number,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const existingSignal = init.signal;\n if (existingSignal) {\n existingSignal.addEventListener('abort', () => controller.abort());\n }\n\n try {\n const response = await fetchFn(url, {\n ...init,\n signal: controller.signal,\n });\n return response;\n } catch (error) {\n if ((error as Error).name === 'AbortError') {\n if (existingSignal?.aborted) {\n throw cancelledError(provider, modality);\n }\n throw timeoutError(timeout, provider, modality);\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Delays execution for a specified duration.\n *\n * @param ms - Duration to sleep in milliseconds\n * @returns Promise that resolves after the specified delay\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Executes an HTTP fetch request for streaming responses.\n *\n * Unlike {@link doFetch}, this function returns the response immediately without\n * checking the HTTP status. This is necessary for Server-Sent Events (SSE) and\n * other streaming protocols where error information may be embedded in the stream.\n *\n * The caller is responsible for:\n * - Checking response.ok and handling HTTP errors\n * - Parsing the response stream (e.g., using parseSSEStream)\n * - Handling stream-specific error conditions\n *\n * Retries are not performed for streaming requests since partial data may have\n * already been consumed by the caller.\n *\n * @param url - The URL to fetch\n * @param init - Standard fetch RequestInit options\n * @param config - Provider configuration containing fetch customization and timeout\n * @param provider - Provider identifier for error context\n * @param modality - Request modality for error context\n * @returns The Response object (may have non-2xx status)\n *\n * @throws {UPPError} NETWORK_ERROR - When a network failure occurs\n * @throws {UPPError} TIMEOUT - When the request times out\n * @throws {UPPError} CANCELLED - When the request is aborted via signal\n *\n * @example\n * ```typescript\n * const response = await doStreamFetch(\n * 'https://api.openai.com/v1/chat/completions',\n * {\n * method: 'POST',\n * headers: { 'Authorization': 'Bearer sk-...' },\n * body: JSON.stringify({ model: 'gpt-4', messages: [], stream: true })\n * },\n * { timeout: 120000 },\n * 'openai',\n * 'llm'\n * );\n *\n * if (!response.ok) {\n * throw await normalizeHttpError(response, 'openai', 'llm');\n * }\n *\n * for await (const event of parseSSEStream(response.body!)) {\n * console.log(event);\n * }\n * ```\n */\nexport async function doStreamFetch(\n url: string,\n init: RequestInit,\n config: ProviderConfig,\n provider: string,\n modality: Modality\n): Promise<Response> {\n const fetchFn = config.fetch ?? fetch;\n const timeout = config.timeout ?? DEFAULT_TIMEOUT;\n const strategy = config.retryStrategy;\n\n if (strategy?.beforeRequest) {\n const delay = await strategy.beforeRequest();\n if (delay > 0) {\n await sleep(delay);\n }\n }\n\n try {\n const response = await fetchWithTimeout(\n fetchFn,\n url,\n init,\n timeout,\n provider,\n modality\n );\n return response;\n } catch (error) {\n if (error instanceof UPPError) {\n throw error;\n }\n throw networkError(error as Error, provider, modality);\n }\n}\n"],"mappings":";AA8GO,IAAM,WAAN,MAAM,kBAAiB,MAAM;AAAA;AAAA,EAEzB;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA;AAAA;AAAA,EAGS;AAAA;AAAA,EAGA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYzB,YACE,SACA,MACA,UACA,UACA,YACA,OACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,SAAK,WAAW;AAChB,SAAK,aAAa;AAClB,SAAK,QAAQ;AAEb,QAAI,MAAM,mBAAmB;AAC3B,YAAM,kBAAkB,MAAM,SAAQ;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,WAAmB;AAC1B,QAAI,MAAM,aAAa,KAAK,IAAI,MAAM,KAAK,OAAO;AAClD,WAAO,eAAe,KAAK,QAAQ,eAAe,KAAK,QAAQ;AAC/D,QAAI,KAAK,YAAY;AACnB,aAAO,aAAa,KAAK,UAAU;AAAA,IACrC;AACA,WAAO;AACP,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAkC;AAChC,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,YAAY,KAAK;AAAA,MACjB,OAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF;AACF;;;AC/JO,SAAS,kBAAkB,QAA2B;AAC3D,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAgCA,eAAsB,mBACpB,UACA,UACA,UACmB;AACnB,QAAM,OAAO,kBAAkB,SAAS,MAAM;AAC9C,MAAI,UAAU,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AAE7D,MAAI;AACF,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,QAAI,MAAM;AACR,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,cAAM,mBACJ,KAAK,OAAO,WACZ,KAAK,WACL,KAAK,OAAO,OAAO,WACnB,KAAK;AAEP,YAAI,kBAAkB;AACpB,oBAAU;AAAA,QACZ;AAAA,MACF,QAAQ;AACN,YAAI,KAAK,SAAS,KAAK;AACrB,oBAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EACR;AAEA,SAAO,IAAI,SAAS,SAAS,MAAM,UAAU,UAAU,SAAS,MAAM;AACxE;AAaO,SAAS,aACd,OACA,UACA,UACU;AACV,SAAO,IAAI;AAAA,IACT,kBAAkB,MAAM,OAAO;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAaO,SAAS,aACd,SACA,UACA,UACU;AACV,SAAO,IAAI;AAAA,IACT,2BAA2B,OAAO;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAYO,SAAS,eAAe,UAAkB,UAA8B;AAC7E,SAAO,IAAI,SAAS,yBAAyB,aAAa,UAAU,QAAQ;AAC9E;;;ACtKA,IAAM,kBAAkB;AAwCxB,eAAsB,QACpB,KACA,MACA,QACA,UACA,UACmB;AACnB,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,WAAW,OAAO;AAExB,MAAI,UAAU,eAAe;AAC3B,UAAM,QAAQ,MAAM,SAAS,cAAc;AAC3C,QAAI,QAAQ,GAAG;AACb,YAAM,MAAM,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,UAAU;AAEd,SAAO,MAAM;AACX;AAEA,QAAI;AACF,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,mBAAmB,UAAU,UAAU,QAAQ;AAEnE,cAAM,aAAa,SAAS,QAAQ,IAAI,aAAa;AACrD,YAAI,cAAc,UAAU;AAC1B,gBAAM,UAAU,SAAS,YAAY,EAAE;AACvC,cAAI,CAAC,MAAM,OAAO,KAAK,mBAAmB,UAAU;AAClD,YAAC,SAAoD;AAAA,cACnD;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,YAAI,UAAU;AACZ,gBAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO,OAAO;AACnD,cAAI,UAAU,MAAM;AAClB,kBAAM,MAAM,KAAK;AACjB,wBAAY;AACZ;AAAA,UACF;AAAA,QACF;AAEA,cAAM;AAAA,MACR;AAEA,gBAAU,QAAQ;AAElB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,UAAU;AAC7B,YAAI,UAAU;AACZ,gBAAM,QAAQ,MAAM,SAAS,QAAQ,OAAO,OAAO;AACnD,cAAI,UAAU,MAAM;AAClB,kBAAM,MAAM,KAAK;AACjB,wBAAY;AACZ;AAAA,UACF;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAEA,YAAM,WAAW,aAAa,OAAgB,UAAU,QAAQ;AAEhE,UAAI,UAAU;AACZ,cAAM,QAAQ,MAAM,SAAS,QAAQ,UAAU,OAAO;AACtD,YAAI,UAAU,MAAM;AAClB,gBAAM,MAAM,KAAK;AACjB,sBAAY;AACZ;AAAA,QACF;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAqBA,eAAe,iBACb,SACA,KACA,MACA,SACA,UACA,UACmB;AACnB,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,QAAM,iBAAiB,KAAK;AAC5B,MAAI,gBAAgB;AAClB,mBAAe,iBAAiB,SAAS,MAAM,WAAW,MAAM,CAAC;AAAA,EACnE;AAEA,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,MAClC,GAAG;AAAA,MACH,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAK,MAAgB,SAAS,cAAc;AAC1C,UAAI,gBAAgB,SAAS;AAC3B,cAAM,eAAe,UAAU,QAAQ;AAAA,MACzC;AACA,YAAM,aAAa,SAAS,UAAU,QAAQ;AAAA,IAChD;AACA,UAAM;AAAA,EACR,UAAE;AACA,iBAAa,SAAS;AAAA,EACxB;AACF;AAQA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAmDA,eAAsB,cACpB,KACA,MACA,QACA,UACA,UACmB;AACnB,QAAM,UAAU,OAAO,SAAS;AAChC,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,WAAW,OAAO;AAExB,MAAI,UAAU,eAAe;AAC3B,UAAM,QAAQ,MAAM,SAAS,cAAc;AAC3C,QAAI,QAAQ,GAAG;AACb,YAAM,MAAM,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,iBAAiB,UAAU;AAC7B,YAAM;AAAA,IACR;AACA,UAAM,aAAa,OAAgB,UAAU,QAAQ;AAAA,EACvD;AACF;","names":[]}
@@ -0,0 +1,224 @@
1
+ // src/core/media/Image.ts
2
+ var Image = class _Image {
3
+ /** The underlying image source (bytes, base64, or URL) */
4
+ source;
5
+ /** MIME type of the image (e.g., 'image/jpeg', 'image/png') */
6
+ mimeType;
7
+ /** Image width in pixels, if known */
8
+ width;
9
+ /** Image height in pixels, if known */
10
+ height;
11
+ constructor(source, mimeType, width, height) {
12
+ this.source = source;
13
+ this.mimeType = mimeType;
14
+ this.width = width;
15
+ this.height = height;
16
+ }
17
+ /**
18
+ * Whether this image has data loaded in memory.
19
+ *
20
+ * Returns `false` for URL-sourced images that reference external resources.
21
+ * These must be fetched before their data can be accessed.
22
+ */
23
+ get hasData() {
24
+ return this.source.type !== "url";
25
+ }
26
+ /**
27
+ * Converts the image to a base64-encoded string.
28
+ *
29
+ * @returns The image data as a base64 string
30
+ * @throws {Error} When the source is a URL (data must be fetched first)
31
+ */
32
+ toBase64() {
33
+ if (this.source.type === "base64") {
34
+ return this.source.data;
35
+ }
36
+ if (this.source.type === "bytes") {
37
+ return btoa(
38
+ Array.from(this.source.data).map((b) => String.fromCharCode(b)).join("")
39
+ );
40
+ }
41
+ throw new Error("Cannot convert URL image to base64. Fetch the image first.");
42
+ }
43
+ /**
44
+ * Converts the image to a data URL suitable for embedding in HTML or CSS.
45
+ *
46
+ * @returns A data URL in the format `data:{mimeType};base64,{data}`
47
+ * @throws {Error} When the source is a URL (data must be fetched first)
48
+ */
49
+ toDataUrl() {
50
+ const base64 = this.toBase64();
51
+ return `data:${this.mimeType};base64,${base64}`;
52
+ }
53
+ /**
54
+ * Gets the image data as raw bytes.
55
+ *
56
+ * @returns The image data as a Uint8Array
57
+ * @throws {Error} When the source is a URL (data must be fetched first)
58
+ */
59
+ toBytes() {
60
+ if (this.source.type === "bytes") {
61
+ return this.source.data;
62
+ }
63
+ if (this.source.type === "base64") {
64
+ const binaryString = atob(this.source.data);
65
+ const bytes = new Uint8Array(binaryString.length);
66
+ for (let i = 0; i < binaryString.length; i++) {
67
+ bytes[i] = binaryString.charCodeAt(i);
68
+ }
69
+ return bytes;
70
+ }
71
+ throw new Error("Cannot get bytes from URL image. Fetch the image first.");
72
+ }
73
+ /**
74
+ * Gets the URL for URL-sourced images.
75
+ *
76
+ * @returns The image URL
77
+ * @throws {Error} When the source is not a URL
78
+ */
79
+ toUrl() {
80
+ if (this.source.type === "url") {
81
+ return this.source.url;
82
+ }
83
+ throw new Error("This image does not have a URL source.");
84
+ }
85
+ /**
86
+ * Converts this Image to an ImageBlock for use in UPP messages.
87
+ *
88
+ * @returns An ImageBlock that can be included in message content arrays
89
+ */
90
+ toBlock() {
91
+ return {
92
+ type: "image",
93
+ source: this.source,
94
+ mimeType: this.mimeType,
95
+ width: this.width,
96
+ height: this.height
97
+ };
98
+ }
99
+ /**
100
+ * Creates an Image by reading a file from disk.
101
+ *
102
+ * The file is read into memory as bytes. MIME type is automatically
103
+ * detected from the file extension.
104
+ *
105
+ * @param path - Path to the image file
106
+ * @returns Promise resolving to an Image with the file contents
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const image = await Image.fromPath('./photos/vacation.jpg');
111
+ * ```
112
+ */
113
+ static async fromPath(path) {
114
+ const { readFile } = await import("fs/promises");
115
+ const data = await readFile(path);
116
+ const mimeType = detectMimeType(path);
117
+ return new _Image(
118
+ { type: "bytes", data: new Uint8Array(data) },
119
+ mimeType
120
+ );
121
+ }
122
+ /**
123
+ * Creates an Image from a URL reference.
124
+ *
125
+ * The URL is stored as a reference and not fetched. Providers will handle
126
+ * URL-to-data conversion if needed. MIME type is detected from the URL
127
+ * path if not provided.
128
+ *
129
+ * @param url - URL pointing to the image
130
+ * @param mimeType - Optional MIME type override
131
+ * @returns An Image referencing the URL
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const image = Image.fromUrl('https://example.com/logo.png');
136
+ * ```
137
+ */
138
+ static fromUrl(url, mimeType) {
139
+ const detected = mimeType || detectMimeTypeFromUrl(url);
140
+ return new _Image({ type: "url", url }, detected);
141
+ }
142
+ /**
143
+ * Creates an Image from raw byte data.
144
+ *
145
+ * @param data - The image data as a Uint8Array
146
+ * @param mimeType - The MIME type of the image
147
+ * @returns An Image containing the byte data
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const image = Image.fromBytes(pngData, 'image/png');
152
+ * ```
153
+ */
154
+ static fromBytes(data, mimeType) {
155
+ return new _Image({ type: "bytes", data }, mimeType);
156
+ }
157
+ /**
158
+ * Creates an Image from a base64-encoded string.
159
+ *
160
+ * @param base64 - The base64-encoded image data (without data URL prefix)
161
+ * @param mimeType - The MIME type of the image
162
+ * @returns An Image containing the base64 data
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const image = Image.fromBase64(base64String, 'image/jpeg');
167
+ * ```
168
+ */
169
+ static fromBase64(base64, mimeType) {
170
+ return new _Image({ type: "base64", data: base64 }, mimeType);
171
+ }
172
+ /**
173
+ * Creates an Image from an existing ImageBlock.
174
+ *
175
+ * Useful for converting content blocks received from providers back
176
+ * into Image instances for further processing.
177
+ *
178
+ * @param block - An ImageBlock from message content
179
+ * @returns An Image with the block's source and metadata
180
+ */
181
+ static fromBlock(block) {
182
+ return new _Image(
183
+ block.source,
184
+ block.mimeType,
185
+ block.width,
186
+ block.height
187
+ );
188
+ }
189
+ };
190
+ function detectMimeType(path) {
191
+ const ext = path.split(".").pop()?.toLowerCase();
192
+ switch (ext) {
193
+ case "jpg":
194
+ case "jpeg":
195
+ return "image/jpeg";
196
+ case "png":
197
+ return "image/png";
198
+ case "gif":
199
+ return "image/gif";
200
+ case "webp":
201
+ return "image/webp";
202
+ case "svg":
203
+ return "image/svg+xml";
204
+ case "bmp":
205
+ return "image/bmp";
206
+ case "ico":
207
+ return "image/x-icon";
208
+ default:
209
+ return "application/octet-stream";
210
+ }
211
+ }
212
+ function detectMimeTypeFromUrl(url) {
213
+ try {
214
+ const pathname = new URL(url).pathname;
215
+ return detectMimeType(pathname);
216
+ } catch {
217
+ return "application/octet-stream";
218
+ }
219
+ }
220
+
221
+ export {
222
+ Image
223
+ };
224
+ //# sourceMappingURL=chunk-WAKD3OO5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/media/Image.ts"],"sourcesContent":["/**\n * @fileoverview Image content handling for the Universal Provider Protocol.\n *\n * Provides a unified Image class for working with images across different sources\n * (file paths, URLs, raw bytes, base64). Supports conversion between formats and\n * integration with UPP message content blocks.\n *\n * @module core/media/Image\n */\n\nimport type { ImageSource, ImageBlock } from '../../types/content.ts';\n\n/**\n * Represents an image that can be used in UPP messages.\n *\n * Images can be created from various sources (files, URLs, bytes, base64) and\n * converted to different formats as needed by providers. The class provides\n * a unified interface regardless of the underlying source type.\n *\n * @example\n * ```typescript\n * // Load from file\n * const fileImage = await Image.fromPath('./photo.jpg');\n *\n * // Reference by URL\n * const urlImage = Image.fromUrl('https://example.com/image.png');\n *\n * // From raw bytes\n * const bytesImage = Image.fromBytes(uint8Array, 'image/png');\n *\n * // Use in a message\n * const message = new UserMessage([image.toBlock()]);\n * ```\n */\nexport class Image {\n /** The underlying image source (bytes, base64, or URL) */\n readonly source: ImageSource;\n /** MIME type of the image (e.g., 'image/jpeg', 'image/png') */\n readonly mimeType: string;\n /** Image width in pixels, if known */\n readonly width?: number;\n /** Image height in pixels, if known */\n readonly height?: number;\n\n private constructor(\n source: ImageSource,\n mimeType: string,\n width?: number,\n height?: number\n ) {\n this.source = source;\n this.mimeType = mimeType;\n this.width = width;\n this.height = height;\n }\n\n /**\n * Whether this image has data loaded in memory.\n *\n * Returns `false` for URL-sourced images that reference external resources.\n * These must be fetched before their data can be accessed.\n */\n get hasData(): boolean {\n return this.source.type !== 'url';\n }\n\n /**\n * Converts the image to a base64-encoded string.\n *\n * @returns The image data as a base64 string\n * @throws {Error} When the source is a URL (data must be fetched first)\n */\n toBase64(): string {\n if (this.source.type === 'base64') {\n return this.source.data;\n }\n\n if (this.source.type === 'bytes') {\n return btoa(\n Array.from(this.source.data)\n .map((b) => String.fromCharCode(b))\n .join('')\n );\n }\n\n throw new Error('Cannot convert URL image to base64. Fetch the image first.');\n }\n\n /**\n * Converts the image to a data URL suitable for embedding in HTML or CSS.\n *\n * @returns A data URL in the format `data:{mimeType};base64,{data}`\n * @throws {Error} When the source is a URL (data must be fetched first)\n */\n toDataUrl(): string {\n const base64 = this.toBase64();\n return `data:${this.mimeType};base64,${base64}`;\n }\n\n /**\n * Gets the image data as raw bytes.\n *\n * @returns The image data as a Uint8Array\n * @throws {Error} When the source is a URL (data must be fetched first)\n */\n toBytes(): Uint8Array {\n if (this.source.type === 'bytes') {\n return this.source.data;\n }\n\n if (this.source.type === 'base64') {\n const binaryString = atob(this.source.data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n }\n\n throw new Error('Cannot get bytes from URL image. Fetch the image first.');\n }\n\n /**\n * Gets the URL for URL-sourced images.\n *\n * @returns The image URL\n * @throws {Error} When the source is not a URL\n */\n toUrl(): string {\n if (this.source.type === 'url') {\n return this.source.url;\n }\n\n throw new Error('This image does not have a URL source.');\n }\n\n /**\n * Converts this Image to an ImageBlock for use in UPP messages.\n *\n * @returns An ImageBlock that can be included in message content arrays\n */\n toBlock(): ImageBlock {\n return {\n type: 'image',\n source: this.source,\n mimeType: this.mimeType,\n width: this.width,\n height: this.height,\n };\n }\n\n /**\n * Creates an Image by reading a file from disk.\n *\n * The file is read into memory as bytes. MIME type is automatically\n * detected from the file extension.\n *\n * @param path - Path to the image file\n * @returns Promise resolving to an Image with the file contents\n *\n * @example\n * ```typescript\n * const image = await Image.fromPath('./photos/vacation.jpg');\n * ```\n */\n static async fromPath(path: string): Promise<Image> {\n // Dynamic import to avoid bundling fs in browser builds\n const { readFile } = await import('node:fs/promises');\n const data = await readFile(path);\n const mimeType = detectMimeType(path);\n\n return new Image(\n { type: 'bytes', data: new Uint8Array(data) },\n mimeType\n );\n }\n\n /**\n * Creates an Image from a URL reference.\n *\n * The URL is stored as a reference and not fetched. Providers will handle\n * URL-to-data conversion if needed. MIME type is detected from the URL\n * path if not provided.\n *\n * @param url - URL pointing to the image\n * @param mimeType - Optional MIME type override\n * @returns An Image referencing the URL\n *\n * @example\n * ```typescript\n * const image = Image.fromUrl('https://example.com/logo.png');\n * ```\n */\n static fromUrl(url: string, mimeType?: string): Image {\n const detected = mimeType || detectMimeTypeFromUrl(url);\n return new Image({ type: 'url', url }, detected);\n }\n\n /**\n * Creates an Image from raw byte data.\n *\n * @param data - The image data as a Uint8Array\n * @param mimeType - The MIME type of the image\n * @returns An Image containing the byte data\n *\n * @example\n * ```typescript\n * const image = Image.fromBytes(pngData, 'image/png');\n * ```\n */\n static fromBytes(data: Uint8Array, mimeType: string): Image {\n return new Image({ type: 'bytes', data }, mimeType);\n }\n\n /**\n * Creates an Image from a base64-encoded string.\n *\n * @param base64 - The base64-encoded image data (without data URL prefix)\n * @param mimeType - The MIME type of the image\n * @returns An Image containing the base64 data\n *\n * @example\n * ```typescript\n * const image = Image.fromBase64(base64String, 'image/jpeg');\n * ```\n */\n static fromBase64(base64: string, mimeType: string): Image {\n return new Image({ type: 'base64', data: base64 }, mimeType);\n }\n\n /**\n * Creates an Image from an existing ImageBlock.\n *\n * Useful for converting content blocks received from providers back\n * into Image instances for further processing.\n *\n * @param block - An ImageBlock from message content\n * @returns An Image with the block's source and metadata\n */\n static fromBlock(block: ImageBlock): Image {\n return new Image(\n block.source,\n block.mimeType,\n block.width,\n block.height\n );\n }\n}\n\n/**\n * Detects the MIME type of an image based on its file extension.\n *\n * Supports common web image formats: JPEG, PNG, GIF, WebP, SVG, BMP, ICO.\n * Returns 'application/octet-stream' for unknown extensions.\n *\n * @param path - File path or filename with extension\n * @returns The detected MIME type string\n */\nfunction detectMimeType(path: string): string {\n const ext = path.split('.').pop()?.toLowerCase();\n\n switch (ext) {\n case 'jpg':\n case 'jpeg':\n return 'image/jpeg';\n case 'png':\n return 'image/png';\n case 'gif':\n return 'image/gif';\n case 'webp':\n return 'image/webp';\n case 'svg':\n return 'image/svg+xml';\n case 'bmp':\n return 'image/bmp';\n case 'ico':\n return 'image/x-icon';\n default:\n return 'application/octet-stream';\n }\n}\n\n/**\n * Detects the MIME type of an image from its URL.\n *\n * Extracts the pathname from the URL and delegates to `detectMimeType`.\n * Returns 'application/octet-stream' if the URL cannot be parsed.\n *\n * @param url - Full URL pointing to an image\n * @returns The detected MIME type string\n */\nfunction detectMimeTypeFromUrl(url: string): string {\n try {\n const pathname = new URL(url).pathname;\n return detectMimeType(pathname);\n } catch {\n return 'application/octet-stream';\n }\n}\n"],"mappings":"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","names":[]}