@fgv/ts-extras 5.1.0-26 → 5.1.0-28

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 (149) hide show
  1. package/dist/index.browser.js +2 -2
  2. package/dist/index.browser.js.map +1 -1
  3. package/dist/packlets/ai-assist/apiClient.js +300 -213
  4. package/dist/packlets/ai-assist/apiClient.js.map +1 -1
  5. package/dist/packlets/ai-assist/chatRequestBuilders.js +6 -0
  6. package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  7. package/dist/packlets/ai-assist/imageOptionsResolver.js +212 -0
  8. package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  9. package/dist/packlets/ai-assist/index.js +1 -0
  10. package/dist/packlets/ai-assist/index.js.map +1 -1
  11. package/dist/packlets/ai-assist/model.js +1 -1
  12. package/dist/packlets/ai-assist/model.js.map +1 -1
  13. package/dist/packlets/ai-assist/registry.js +120 -22
  14. package/dist/packlets/ai-assist/registry.js.map +1 -1
  15. package/dist/packlets/ai-assist/sseParser.js +1 -0
  16. package/dist/packlets/ai-assist/sseParser.js.map +1 -1
  17. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +17 -12
  18. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  19. package/dist/packlets/ai-assist/streamingAdapters/common.js +2 -0
  20. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  21. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +17 -5
  22. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  23. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +19 -4
  24. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  25. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +20 -5
  26. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  27. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +9 -3
  28. package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  29. package/dist/packlets/ai-assist/streamingClient.js +28 -6
  30. package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
  31. package/dist/packlets/ai-assist/thinkingOptionsResolver.js +265 -0
  32. package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  33. package/dist/packlets/conversion/converters.js +1 -0
  34. package/dist/packlets/conversion/converters.js.map +1 -1
  35. package/dist/packlets/crypto-utils/converters.js +24 -4
  36. package/dist/packlets/crypto-utils/converters.js.map +1 -1
  37. package/dist/packlets/crypto-utils/hpkeProvider.js +333 -0
  38. package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  39. package/dist/packlets/crypto-utils/index.browser.js +3 -0
  40. package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
  41. package/dist/packlets/crypto-utils/index.js +2 -0
  42. package/dist/packlets/crypto-utils/index.js.map +1 -1
  43. package/dist/packlets/crypto-utils/keystore/converters.js +2 -2
  44. package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -1
  45. package/dist/packlets/crypto-utils/keystore/keyStore.js +108 -2
  46. package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  47. package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
  48. package/dist/packlets/crypto-utils/model.js +21 -0
  49. package/dist/packlets/crypto-utils/model.js.map +1 -1
  50. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +74 -0
  51. package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
  52. package/dist/ts-extras.d.ts +1029 -137
  53. package/dist/tsdoc-metadata.json +1 -1
  54. package/lib/index.browser.d.ts +2 -2
  55. package/lib/index.browser.d.ts.map +1 -1
  56. package/lib/index.browser.js +4 -3
  57. package/lib/index.browser.js.map +1 -1
  58. package/lib/packlets/ai-assist/apiClient.d.ts +29 -85
  59. package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
  60. package/lib/packlets/ai-assist/apiClient.js +300 -213
  61. package/lib/packlets/ai-assist/apiClient.js.map +1 -1
  62. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -1
  63. package/lib/packlets/ai-assist/chatRequestBuilders.js +6 -0
  64. package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -1
  65. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +74 -0
  66. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -0
  67. package/lib/packlets/ai-assist/imageOptionsResolver.js +216 -0
  68. package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  69. package/lib/packlets/ai-assist/index.d.ts +2 -1
  70. package/lib/packlets/ai-assist/index.d.ts.map +1 -1
  71. package/lib/packlets/ai-assist/index.js +4 -1
  72. package/lib/packlets/ai-assist/index.js.map +1 -1
  73. package/lib/packlets/ai-assist/model.d.ts +410 -35
  74. package/lib/packlets/ai-assist/model.d.ts.map +1 -1
  75. package/lib/packlets/ai-assist/model.js +1 -1
  76. package/lib/packlets/ai-assist/model.js.map +1 -1
  77. package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
  78. package/lib/packlets/ai-assist/registry.js +120 -22
  79. package/lib/packlets/ai-assist/registry.js.map +1 -1
  80. package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -1
  81. package/lib/packlets/ai-assist/sseParser.js +1 -0
  82. package/lib/packlets/ai-assist/sseParser.js.map +1 -1
  83. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +2 -1
  84. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
  85. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +17 -12
  86. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  87. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +5 -1
  88. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
  89. package/lib/packlets/ai-assist/streamingAdapters/common.js +2 -0
  90. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  91. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +2 -1
  92. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
  93. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +17 -5
  94. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  95. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +2 -1
  96. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
  97. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +19 -4
  98. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  99. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +2 -1
  100. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
  101. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +20 -5
  102. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  103. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
  104. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +9 -3
  105. package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  106. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
  107. package/lib/packlets/ai-assist/streamingClient.js +28 -6
  108. package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
  109. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +71 -0
  110. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -0
  111. package/lib/packlets/ai-assist/thinkingOptionsResolver.js +270 -0
  112. package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  113. package/lib/packlets/conversion/converters.d.ts.map +1 -1
  114. package/lib/packlets/conversion/converters.js +1 -0
  115. package/lib/packlets/conversion/converters.js.map +1 -1
  116. package/lib/packlets/crypto-utils/converters.d.ts +12 -1
  117. package/lib/packlets/crypto-utils/converters.d.ts.map +1 -1
  118. package/lib/packlets/crypto-utils/converters.js +25 -5
  119. package/lib/packlets/crypto-utils/converters.js.map +1 -1
  120. package/lib/packlets/crypto-utils/hpkeProvider.d.ts +142 -0
  121. package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -0
  122. package/lib/packlets/crypto-utils/hpkeProvider.js +337 -0
  123. package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  124. package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
  125. package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
  126. package/lib/packlets/crypto-utils/index.browser.js +5 -1
  127. package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
  128. package/lib/packlets/crypto-utils/index.d.ts +1 -0
  129. package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
  130. package/lib/packlets/crypto-utils/index.js +4 -1
  131. package/lib/packlets/crypto-utils/index.js.map +1 -1
  132. package/lib/packlets/crypto-utils/keystore/converters.js +1 -1
  133. package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -1
  134. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +32 -2
  135. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
  136. package/lib/packlets/crypto-utils/keystore/keyStore.js +116 -10
  137. package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  138. package/lib/packlets/crypto-utils/keystore/model.d.ts +21 -3
  139. package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
  140. package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
  141. package/lib/packlets/crypto-utils/model.d.ts +165 -9
  142. package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
  143. package/lib/packlets/crypto-utils/model.js +22 -1
  144. package/lib/packlets/crypto-utils/model.js.map +1 -1
  145. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +39 -0
  146. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -1
  147. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +74 -0
  148. package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -1
  149. package/package.json +13 -13
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.providerDiscriminatorForId = providerDiscriminatorForId;
23
+ exports.mergeThinkingConfig = mergeThinkingConfig;
24
+ exports.checkTemperatureConflict = checkTemperatureConflict;
25
+ const ts_utils_1 = require("@fgv/ts-utils");
26
+ /**
27
+ * Maps an AiProviderId (registry key) to the coarse family discriminator used
28
+ * in IThinkingProviderConfig. Returns undefined for providers without thinking support.
29
+ * @internal
30
+ */
31
+ function providerDiscriminatorForId(providerId) {
32
+ switch (providerId) {
33
+ case 'anthropic':
34
+ return 'anthropic';
35
+ case 'openai':
36
+ return 'openai';
37
+ case 'google-gemini':
38
+ return 'google';
39
+ case 'xai-grok':
40
+ return 'xai';
41
+ default:
42
+ return undefined;
43
+ }
44
+ }
45
+ // ============================================================================
46
+ // Common-subset mapping
47
+ // ============================================================================
48
+ /**
49
+ * Maps generic effort to Anthropic wire effort. @internal
50
+ */
51
+ function genericEffortToAnthropic(effort) {
52
+ return effort; // 1:1 mapping for the common subset
53
+ }
54
+ /**
55
+ * Maps generic effort to OpenAI wire effort. @internal
56
+ */
57
+ function genericEffortToOpenAi(effort) {
58
+ return effort; // 1:1 mapping for the common subset
59
+ }
60
+ /**
61
+ * Maps generic effort to Gemini thinkingBudget. @internal
62
+ */
63
+ function genericEffortToGemini(effort) {
64
+ switch (effort) {
65
+ case 'low':
66
+ return 1024;
67
+ case 'medium':
68
+ return 4096;
69
+ case 'high':
70
+ return 8192;
71
+ }
72
+ }
73
+ /**
74
+ * Maps generic effort to xAI reasoning_effort. @internal
75
+ */
76
+ function genericEffortToXai(effort) {
77
+ return effort; // 1:1 mapping for the common subset
78
+ }
79
+ // ============================================================================
80
+ // Block applicability
81
+ // ============================================================================
82
+ /**
83
+ * Returns true when a provider config block applies to the given resolved model
84
+ * and provider discriminator.
85
+ *
86
+ * Applicability rules:
87
+ * - provider must match the coarse discriminator
88
+ * - if models array is present, resolved model must match (exact or base-name prefix)
89
+ * - if models array is absent, the block is provider-generic (applies to all)
90
+ *
91
+ * Prefix matching supports versioned IDs: `'claude-sonnet-4-5'` matches resolved
92
+ * `'claude-sonnet-4-5-20250929'`. An entry matches when it equals the resolved model
93
+ * or when the resolved model starts with the entry followed by a `-`.
94
+ *
95
+ * 'other' blocks require models to be present (enforced by the type).
96
+ * @internal
97
+ */
98
+ function modelNameMatches(resolvedModel, name) {
99
+ return resolvedModel === name || resolvedModel.startsWith(`${name}-`);
100
+ }
101
+ function blockApplies(block, resolvedModel, discriminator) {
102
+ if (block.provider !== discriminator && block.provider !== 'other') {
103
+ return false;
104
+ }
105
+ if (block.provider === 'other') {
106
+ return block.models.some((name) => modelNameMatches(resolvedModel, name));
107
+ }
108
+ if (block.models !== undefined) {
109
+ return block.models.some((name) => modelNameMatches(resolvedModel, name));
110
+ }
111
+ return true; // provider-generic block
112
+ }
113
+ /**
114
+ * Returns true when a block is model-specific (has a models array).
115
+ * Used to partition blocks into merge tiers.
116
+ * @internal
117
+ */
118
+ function isModelSpecific(block) {
119
+ if (block.provider === 'other') {
120
+ return true; // other blocks always require models
121
+ }
122
+ return block.models !== undefined;
123
+ }
124
+ // ============================================================================
125
+ // Merge function
126
+ // ============================================================================
127
+ /**
128
+ * Resolves the effective thinking wire parameters for a specific resolved model
129
+ * by merging all applicable config blocks in precedence order.
130
+ *
131
+ * Precedence (later tier wins; within tier, later declaration wins):
132
+ * 1. Generic effort (top-level IThinkingConfig.effort) → common-subset mapping
133
+ * 2. Provider-generic blocks (matching provider, no models filter)
134
+ * 3. Model-specific blocks (matching provider + models array includes resolved model)
135
+ * 4. Other blocks (provider: 'other', models includes resolved model) — same tier as 3
136
+ *
137
+ * Blocks whose provider does not match are silently skipped.
138
+ *
139
+ * Note: when the resolved OpenAI effort is `'none'`, reasoning is disabled and
140
+ * temperature is accepted; see {@link IOpenAiThinkingConfig.effort} for the full
141
+ * hybrid-mode semantics.
142
+ *
143
+ * @param config - The caller's IThinkingConfig
144
+ * @param resolvedModel - The concrete model string after registry resolution
145
+ * @param discriminator - Coarse provider family
146
+ * @returns Merged effective config for wire encoding
147
+ * @internal
148
+ */
149
+ function mergeThinkingConfig(config, resolvedModel, discriminator) {
150
+ let resolved = {};
151
+ // Tier 1: generic effort → common-subset mapping
152
+ if (config.effort !== undefined) {
153
+ switch (discriminator) {
154
+ case 'anthropic':
155
+ resolved = Object.assign(Object.assign({}, resolved), { anthropicEffort: genericEffortToAnthropic(config.effort) });
156
+ break;
157
+ case 'openai':
158
+ resolved = Object.assign(Object.assign({}, resolved), { openAiEffort: genericEffortToOpenAi(config.effort) });
159
+ break;
160
+ case 'google':
161
+ resolved = Object.assign(Object.assign({}, resolved), { geminiThinkingBudget: genericEffortToGemini(config.effort) });
162
+ break;
163
+ case 'xai':
164
+ resolved = Object.assign(Object.assign({}, resolved), { xaiEffort: genericEffortToXai(config.effort) });
165
+ break;
166
+ }
167
+ }
168
+ if (!config.providers) {
169
+ return (0, ts_utils_1.succeed)(resolved);
170
+ }
171
+ // Partition into tiers 2 and 3+4
172
+ const applicableBlocks = config.providers.filter((b) => blockApplies(b, resolvedModel, discriminator));
173
+ const genericBlocks = applicableBlocks.filter((b) => !isModelSpecific(b));
174
+ const specificBlocks = applicableBlocks.filter((b) => isModelSpecific(b));
175
+ // Tier 2: provider-generic blocks (declaration order; later wins)
176
+ for (const block of genericBlocks) {
177
+ resolved = applyBlock(resolved, block, discriminator);
178
+ }
179
+ // Tier 3+4: model-specific + other blocks (declaration order; later wins)
180
+ for (const block of specificBlocks) {
181
+ resolved = applyBlock(resolved, block, discriminator);
182
+ }
183
+ return (0, ts_utils_1.succeed)(resolved);
184
+ }
185
+ /**
186
+ * Applies a single config block to the accumulated resolved config.
187
+ * @internal
188
+ */
189
+ function applyBlock(current, block, discriminator) {
190
+ if (block.provider === 'other') {
191
+ const merged = current.otherParams !== undefined ? Object.assign(Object.assign({}, current.otherParams), block.config) : block.config;
192
+ return Object.assign(Object.assign({}, current), { otherParams: merged });
193
+ }
194
+ switch (discriminator) {
195
+ case 'anthropic':
196
+ if (block.provider === 'anthropic') {
197
+ if (block.config.effort !== undefined) {
198
+ return Object.assign(Object.assign({}, current), { anthropicEffort: block.config.effort });
199
+ }
200
+ }
201
+ break;
202
+ case 'openai':
203
+ if (block.provider === 'openai') {
204
+ if (block.config.effort !== undefined) {
205
+ return Object.assign(Object.assign({}, current), { openAiEffort: block.config.effort });
206
+ }
207
+ }
208
+ break;
209
+ case 'google':
210
+ if (block.provider === 'google') {
211
+ const updated = Object.assign({}, current);
212
+ if (block.config.thinkingBudget !== undefined) {
213
+ return Object.assign(Object.assign({}, updated), { geminiThinkingBudget: block.config.thinkingBudget });
214
+ }
215
+ return updated;
216
+ }
217
+ /* c8 ignore next - blockApplies guarantees provider match; unreachable for google */
218
+ break;
219
+ case 'xai':
220
+ if (block.provider === 'xai') {
221
+ if (block.config.effort !== undefined) {
222
+ return Object.assign(Object.assign({}, current), { xaiEffort: block.config.effort });
223
+ }
224
+ }
225
+ break;
226
+ }
227
+ return current;
228
+ }
229
+ // ============================================================================
230
+ // Temperature conflict check
231
+ // ============================================================================
232
+ /**
233
+ * Returns a Result.fail if temperature conflicts with thinking mode for the
234
+ * given provider, otherwise succeed(undefined).
235
+ *
236
+ * Per D4: temperature + thinking = Result.fail for Anthropic, OpenAI (when
237
+ * effective effort is non-null and non-'none'), and xAI (conservative default
238
+ * pending live verification). Gemini accepts temperature alongside thinking.
239
+ *
240
+ * @internal
241
+ */
242
+ function checkTemperatureConflict(resolved, discriminator, temperature) {
243
+ if (temperature === undefined) {
244
+ return (0, ts_utils_1.succeed)(undefined);
245
+ }
246
+ switch (discriminator) {
247
+ case 'anthropic':
248
+ if (resolved.anthropicEffort !== undefined) {
249
+ return (0, ts_utils_1.fail)('thinking mode is not compatible with temperature on provider anthropic: remove temperature or disable thinking');
250
+ }
251
+ break;
252
+ case 'openai':
253
+ // 'none' disables reasoning; temperature is accepted in that case
254
+ if (resolved.openAiEffort !== undefined && resolved.openAiEffort !== 'none') {
255
+ return (0, ts_utils_1.fail)('thinking mode is not compatible with temperature on provider openai: remove temperature or disable thinking');
256
+ }
257
+ break;
258
+ case 'xai':
259
+ // Conservative default: fail if xAI effort is active (per D8 — live verification pending)
260
+ if (resolved.xaiEffort !== undefined && resolved.xaiEffort !== 'none') {
261
+ return (0, ts_utils_1.fail)('thinking mode is not compatible with temperature on provider xai: remove temperature or disable thinking');
262
+ }
263
+ break;
264
+ case 'google':
265
+ // Gemini accepts temperature alongside thinkingConfig — no conflict
266
+ break;
267
+ }
268
+ return (0, ts_utils_1.succeed)(undefined);
269
+ }
270
+ //# sourceMappingURL=thinkingOptionsResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinkingOptionsResolver.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/thinkingOptionsResolver.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Merge logic and runtime validation for thinking/reasoning options.\n * @packageDocumentation\n */\n\nimport { type JsonObject } from '@fgv/ts-json-base';\nimport { fail, Result, succeed } from '@fgv/ts-utils';\n\nimport type {\n IThinkingConfig,\n IThinkingProviderConfig,\n IAnthropicThinkingConfig,\n IOpenAiThinkingConfig,\n IXAiThinkingConfig\n} from './model';\n\n// ============================================================================\n// Provider discriminator\n// ============================================================================\n\n/**\n * Coarse provider family used to discriminate thinking config blocks.\n * Maps from AiProviderId to the IThinkingProviderConfig `provider` discriminator.\n * @internal\n */\nexport type ThinkingProviderDiscriminator = 'anthropic' | 'openai' | 'google' | 'xai';\n\n/**\n * Maps an AiProviderId (registry key) to the coarse family discriminator used\n * in IThinkingProviderConfig. Returns undefined for providers without thinking support.\n * @internal\n */\nexport function providerDiscriminatorForId(providerId: string): ThinkingProviderDiscriminator | undefined {\n switch (providerId) {\n case 'anthropic':\n return 'anthropic';\n case 'openai':\n return 'openai';\n case 'google-gemini':\n return 'google';\n case 'xai-grok':\n return 'xai';\n default:\n return undefined;\n }\n}\n\n// ============================================================================\n// Resolved wire shape\n// ============================================================================\n\n/**\n * Resolved thinking wire parameters for a specific provider, after merging\n * all applicable config blocks. Ready for provider-specific wire encoding.\n * @internal\n */\nexport interface IResolvedThinkingConfig {\n /** Anthropic: output_config.effort value */\n readonly anthropicEffort?: IAnthropicThinkingConfig['effort'];\n /** OpenAI Chat: reasoning_effort value; OpenAI Responses: reasoning.effort */\n readonly openAiEffort?: IOpenAiThinkingConfig['effort'];\n /** Gemini: generationConfig.thinkingConfig.thinkingBudget */\n readonly geminiThinkingBudget?: number;\n /** xAI: reasoning_effort value (omit for grok-4) */\n readonly xaiEffort?: IXAiThinkingConfig['effort'];\n /** Other/passthrough: merged verbatim into wire request */\n readonly otherParams?: JsonObject;\n}\n\n// ============================================================================\n// Common-subset mapping\n// ============================================================================\n\n/**\n * Maps generic effort to Anthropic wire effort. @internal\n */\nfunction genericEffortToAnthropic(effort: 'low' | 'medium' | 'high'): IAnthropicThinkingConfig['effort'] {\n return effort; // 1:1 mapping for the common subset\n}\n\n/**\n * Maps generic effort to OpenAI wire effort. @internal\n */\nfunction genericEffortToOpenAi(effort: 'low' | 'medium' | 'high'): IOpenAiThinkingConfig['effort'] {\n return effort; // 1:1 mapping for the common subset\n}\n\n/**\n * Maps generic effort to Gemini thinkingBudget. @internal\n */\nfunction genericEffortToGemini(effort: 'low' | 'medium' | 'high'): number {\n switch (effort) {\n case 'low':\n return 1024;\n case 'medium':\n return 4096;\n case 'high':\n return 8192;\n }\n}\n\n/**\n * Maps generic effort to xAI reasoning_effort. @internal\n */\nfunction genericEffortToXai(effort: 'low' | 'medium' | 'high'): IXAiThinkingConfig['effort'] {\n return effort; // 1:1 mapping for the common subset\n}\n\n// ============================================================================\n// Block applicability\n// ============================================================================\n\n/**\n * Returns true when a provider config block applies to the given resolved model\n * and provider discriminator.\n *\n * Applicability rules:\n * - provider must match the coarse discriminator\n * - if models array is present, resolved model must match (exact or base-name prefix)\n * - if models array is absent, the block is provider-generic (applies to all)\n *\n * Prefix matching supports versioned IDs: `'claude-sonnet-4-5'` matches resolved\n * `'claude-sonnet-4-5-20250929'`. An entry matches when it equals the resolved model\n * or when the resolved model starts with the entry followed by a `-`.\n *\n * 'other' blocks require models to be present (enforced by the type).\n * @internal\n */\nfunction modelNameMatches(resolvedModel: string, name: string): boolean {\n return resolvedModel === name || resolvedModel.startsWith(`${name}-`);\n}\n\nfunction blockApplies(\n block: IThinkingProviderConfig,\n resolvedModel: string,\n discriminator: ThinkingProviderDiscriminator\n): boolean {\n if (block.provider !== discriminator && block.provider !== 'other') {\n return false;\n }\n if (block.provider === 'other') {\n return block.models.some((name) => modelNameMatches(resolvedModel, name));\n }\n if (block.models !== undefined) {\n return (block.models as ReadonlyArray<string>).some((name) => modelNameMatches(resolvedModel, name));\n }\n return true; // provider-generic block\n}\n\n/**\n * Returns true when a block is model-specific (has a models array).\n * Used to partition blocks into merge tiers.\n * @internal\n */\nfunction isModelSpecific(block: IThinkingProviderConfig): boolean {\n if (block.provider === 'other') {\n return true; // other blocks always require models\n }\n return block.models !== undefined;\n}\n\n// ============================================================================\n// Merge function\n// ============================================================================\n\n/**\n * Resolves the effective thinking wire parameters for a specific resolved model\n * by merging all applicable config blocks in precedence order.\n *\n * Precedence (later tier wins; within tier, later declaration wins):\n * 1. Generic effort (top-level IThinkingConfig.effort) → common-subset mapping\n * 2. Provider-generic blocks (matching provider, no models filter)\n * 3. Model-specific blocks (matching provider + models array includes resolved model)\n * 4. Other blocks (provider: 'other', models includes resolved model) — same tier as 3\n *\n * Blocks whose provider does not match are silently skipped.\n *\n * Note: when the resolved OpenAI effort is `'none'`, reasoning is disabled and\n * temperature is accepted; see {@link IOpenAiThinkingConfig.effort} for the full\n * hybrid-mode semantics.\n *\n * @param config - The caller's IThinkingConfig\n * @param resolvedModel - The concrete model string after registry resolution\n * @param discriminator - Coarse provider family\n * @returns Merged effective config for wire encoding\n * @internal\n */\nexport function mergeThinkingConfig(\n config: IThinkingConfig,\n resolvedModel: string,\n discriminator: ThinkingProviderDiscriminator\n): Result<IResolvedThinkingConfig> {\n let resolved: IResolvedThinkingConfig = {};\n\n // Tier 1: generic effort → common-subset mapping\n if (config.effort !== undefined) {\n switch (discriminator) {\n case 'anthropic':\n resolved = { ...resolved, anthropicEffort: genericEffortToAnthropic(config.effort) };\n break;\n case 'openai':\n resolved = { ...resolved, openAiEffort: genericEffortToOpenAi(config.effort) };\n break;\n case 'google':\n resolved = { ...resolved, geminiThinkingBudget: genericEffortToGemini(config.effort) };\n break;\n case 'xai':\n resolved = { ...resolved, xaiEffort: genericEffortToXai(config.effort) };\n break;\n }\n }\n\n if (!config.providers) {\n return succeed(resolved);\n }\n\n // Partition into tiers 2 and 3+4\n const applicableBlocks = config.providers.filter((b) => blockApplies(b, resolvedModel, discriminator));\n const genericBlocks = applicableBlocks.filter((b) => !isModelSpecific(b));\n const specificBlocks = applicableBlocks.filter((b) => isModelSpecific(b));\n\n // Tier 2: provider-generic blocks (declaration order; later wins)\n for (const block of genericBlocks) {\n resolved = applyBlock(resolved, block, discriminator);\n }\n\n // Tier 3+4: model-specific + other blocks (declaration order; later wins)\n for (const block of specificBlocks) {\n resolved = applyBlock(resolved, block, discriminator);\n }\n\n return succeed(resolved);\n}\n\n/**\n * Applies a single config block to the accumulated resolved config.\n * @internal\n */\nfunction applyBlock(\n current: IResolvedThinkingConfig,\n block: IThinkingProviderConfig,\n discriminator: ThinkingProviderDiscriminator\n): IResolvedThinkingConfig {\n if (block.provider === 'other') {\n const merged =\n current.otherParams !== undefined ? { ...current.otherParams, ...block.config } : block.config;\n return { ...current, otherParams: merged };\n }\n\n switch (discriminator) {\n case 'anthropic':\n if (block.provider === 'anthropic') {\n if (block.config.effort !== undefined) {\n return { ...current, anthropicEffort: block.config.effort };\n }\n }\n break;\n case 'openai':\n if (block.provider === 'openai') {\n if (block.config.effort !== undefined) {\n return { ...current, openAiEffort: block.config.effort };\n }\n }\n break;\n case 'google':\n if (block.provider === 'google') {\n const updated: IResolvedThinkingConfig = { ...current };\n if (block.config.thinkingBudget !== undefined) {\n return { ...updated, geminiThinkingBudget: block.config.thinkingBudget };\n }\n return updated;\n }\n /* c8 ignore next - blockApplies guarantees provider match; unreachable for google */\n break;\n case 'xai':\n if (block.provider === 'xai') {\n if (block.config.effort !== undefined) {\n return { ...current, xaiEffort: block.config.effort };\n }\n }\n break;\n }\n return current;\n}\n\n// ============================================================================\n// Temperature conflict check\n// ============================================================================\n\n/**\n * Returns a Result.fail if temperature conflicts with thinking mode for the\n * given provider, otherwise succeed(undefined).\n *\n * Per D4: temperature + thinking = Result.fail for Anthropic, OpenAI (when\n * effective effort is non-null and non-'none'), and xAI (conservative default\n * pending live verification). Gemini accepts temperature alongside thinking.\n *\n * @internal\n */\nexport function checkTemperatureConflict(\n resolved: IResolvedThinkingConfig,\n discriminator: ThinkingProviderDiscriminator,\n temperature: number | undefined\n): Result<undefined> {\n if (temperature === undefined) {\n return succeed(undefined);\n }\n\n switch (discriminator) {\n case 'anthropic':\n if (resolved.anthropicEffort !== undefined) {\n return fail(\n 'thinking mode is not compatible with temperature on provider anthropic: remove temperature or disable thinking'\n );\n }\n break;\n case 'openai':\n // 'none' disables reasoning; temperature is accepted in that case\n if (resolved.openAiEffort !== undefined && resolved.openAiEffort !== 'none') {\n return fail(\n 'thinking mode is not compatible with temperature on provider openai: remove temperature or disable thinking'\n );\n }\n break;\n case 'xai':\n // Conservative default: fail if xAI effort is active (per D8 — live verification pending)\n if (resolved.xaiEffort !== undefined && resolved.xaiEffort !== 'none') {\n return fail(\n 'thinking mode is not compatible with temperature on provider xai: remove temperature or disable thinking'\n );\n }\n break;\n case 'google':\n // Gemini accepts temperature alongside thinkingConfig — no conflict\n break;\n }\n return succeed(undefined);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/packlets/conversion/converters.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAc,MAAM,EAAgC,MAAM,eAAe,CAAC;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE5E;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAWpG;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,CAkB3C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,QAAQ,EAAE,OAAO,CAoBpD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,EAAE,GAAG,SAAS,EAC/C,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAC3B,OAAO,GAAE,UAAU,CAAC,OAAuB,GAC1C,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAIjC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,EAChE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAC3B,WAAW,EAAE,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,GACtD,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAcnB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAE/F"}
1
+ {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/packlets/conversion/converters.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAc,MAAM,EAAgC,MAAM,eAAe,CAAC;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE5E;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAWpG;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,CAkB3C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,QAAQ,EAAE,OAAO,CAqBpD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,EAAE,GAAG,SAAS,EAC/C,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAC3B,OAAO,GAAE,UAAU,CAAC,OAAuB,GAC1C,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAIjC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,EAChE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAC3B,WAAW,EAAE,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC,GACtD,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAcnB;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAE/F"}
@@ -93,6 +93,7 @@ exports.isoDateTime = new ts_utils_1.Conversion.BaseConverter((from) => {
93
93
  }
94
94
  else if (typeof from === 'number') {
95
95
  return (0, ts_utils_1.succeed)(luxon_1.DateTime.fromMillis(from));
96
+ /* c8 ignore next 3 - Date instance path not exercised in current test suite */
96
97
  }
97
98
  else if (from instanceof Date) {
98
99
  return (0, ts_utils_1.succeed)(luxon_1.DateTime.fromJSDate(from));
@@ -1 +1 @@
1
- {"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/conversion/converters.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2020 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Conversion, Converter, Converters, Result, captureResult, fail, succeed } from '@fgv/ts-utils';\nimport { DateTime } from 'luxon';\nimport Mustache from 'mustache';\nimport { ExtendedArray, RangeOf, RangeOfProperties } from '../experimental';\n\n/**\n * Helper function to create a `StringConverter` which converts\n * `unknown` to `string`, applying template conversions supplied at construction time or at\n * runtime as context.\n * @remarks\n * Template conversions are applied using `mustache` syntax.\n * @param defaultContext - Optional default context to use for template values.\n * @returns A new `Converter` returning `string`.\n * @public\n */\nexport function templateString(defaultContext?: unknown): Conversion.StringConverter<string, unknown> {\n return new Conversion.StringConverter<string, unknown>(\n defaultContext,\n undefined,\n (from: unknown, __self: Converter<string, unknown>, context?: unknown) => {\n if (typeof from !== 'string') {\n return fail(`Not a string: ${JSON.stringify(from)}`);\n }\n return captureResult(() => Mustache.render(from, context));\n }\n );\n}\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `Date` object.\n * @public\n */\nexport const isoDate: Converter<Date, unknown> = new Conversion.BaseConverter<Date>((from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt.toJSDate());\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(new Date(from));\n } else if (from instanceof Date) {\n return succeed(from);\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from.toJSDate());\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to Date`);\n});\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `DateTime` object.\n * @public\n */\nexport const isoDateTime: Converter<DateTime, unknown> = new Conversion.BaseConverter<DateTime>(\n (from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt);\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(DateTime.fromMillis(from));\n } else if (from instanceof Date) {\n return succeed(DateTime.fromJSDate(from));\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from);\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to DateTime`);\n }\n);\n\n/**\n * A helper function to create a `Converter` which converts `unknown` to {@link Experimental.ExtendedArray | ExtendedArray<T>}.\n * @remarks\n * If `onError` is `'failOnError'` (default), then the entire conversion fails if any element cannot\n * be converted. If `onError` is `'ignoreErrors'`, then failing elements are silently ignored.\n * @param converter - `Converter` used to convert each item in the array\n * @param onError - Specifies treatment of unconvertible elements\n * @beta\n */\nexport function extendedArrayOf<T, TC = undefined>(\n label: string,\n converter: Converter<T, TC>,\n onError: Conversion.OnError = 'failOnError'\n): Converter<ExtendedArray<T>, TC> {\n return Converters.arrayOf(converter, onError).map((items: T[]) => {\n return captureResult(() => new ExtendedArray(label, ...items));\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to an arbitrary strongly-typed\n * range of some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @param constructor - Static constructor to instantiate the object.\n * @public\n */\nexport function rangeTypeOf<T, RT extends RangeOf<T>, TC = unknown>(\n converter: Converter<T, TC>,\n constructor: (init: RangeOfProperties<T>) => Result<RT>\n): Converter<RT, TC> {\n return new Conversion.BaseConverter((from: unknown, __self, context?: TC) => {\n const result = Converters.object(\n {\n min: converter,\n max: converter\n },\n { optionalFields: ['min', 'max'] }\n ).convert(from, context);\n if (result.isSuccess()) {\n return constructor({ min: result.value.min, max: result.value.max });\n }\n return fail(result.message);\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to {@link Experimental.RangeOf | RangeOf<T>}\n * where `<T>` is some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @public\n */\nexport function rangeOf<T, TC = unknown>(converter: Converter<T, TC>): Converter<RangeOf<T>, TC> {\n return rangeTypeOf<T, RangeOf<T>, TC>(converter, RangeOf.createRange);\n}\n"]}
1
+ {"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/conversion/converters.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2020 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { Conversion, Converter, Converters, Result, captureResult, fail, succeed } from '@fgv/ts-utils';\nimport { DateTime } from 'luxon';\nimport Mustache from 'mustache';\nimport { ExtendedArray, RangeOf, RangeOfProperties } from '../experimental';\n\n/**\n * Helper function to create a `StringConverter` which converts\n * `unknown` to `string`, applying template conversions supplied at construction time or at\n * runtime as context.\n * @remarks\n * Template conversions are applied using `mustache` syntax.\n * @param defaultContext - Optional default context to use for template values.\n * @returns A new `Converter` returning `string`.\n * @public\n */\nexport function templateString(defaultContext?: unknown): Conversion.StringConverter<string, unknown> {\n return new Conversion.StringConverter<string, unknown>(\n defaultContext,\n undefined,\n (from: unknown, __self: Converter<string, unknown>, context?: unknown) => {\n if (typeof from !== 'string') {\n return fail(`Not a string: ${JSON.stringify(from)}`);\n }\n return captureResult(() => Mustache.render(from, context));\n }\n );\n}\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `Date` object.\n * @public\n */\nexport const isoDate: Converter<Date, unknown> = new Conversion.BaseConverter<Date>((from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt.toJSDate());\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(new Date(from));\n } else if (from instanceof Date) {\n return succeed(from);\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from.toJSDate());\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to Date`);\n});\n\n/**\n * A `Converter` which converts an iso formatted string, a number or a `Date` object to\n * a `DateTime` object.\n * @public\n */\nexport const isoDateTime: Converter<DateTime, unknown> = new Conversion.BaseConverter<DateTime>(\n (from: unknown) => {\n if (typeof from === 'string') {\n const dt = DateTime.fromISO(from);\n if (dt.isValid) {\n return succeed(dt);\n }\n return fail(`Invalid date: ${dt.invalidExplanation}`);\n } else if (typeof from === 'number') {\n return succeed(DateTime.fromMillis(from));\n /* c8 ignore next 3 - Date instance path not exercised in current test suite */\n } else if (from instanceof Date) {\n return succeed(DateTime.fromJSDate(from));\n } else if (from instanceof DateTime) {\n if (from.isValid) {\n return succeed(from);\n }\n return fail(`Invalid date: ${from.invalidExplanation}`);\n }\n return fail(`Cannot convert ${JSON.stringify(from)} to DateTime`);\n }\n);\n\n/**\n * A helper function to create a `Converter` which converts `unknown` to {@link Experimental.ExtendedArray | ExtendedArray<T>}.\n * @remarks\n * If `onError` is `'failOnError'` (default), then the entire conversion fails if any element cannot\n * be converted. If `onError` is `'ignoreErrors'`, then failing elements are silently ignored.\n * @param converter - `Converter` used to convert each item in the array\n * @param onError - Specifies treatment of unconvertible elements\n * @beta\n */\nexport function extendedArrayOf<T, TC = undefined>(\n label: string,\n converter: Converter<T, TC>,\n onError: Conversion.OnError = 'failOnError'\n): Converter<ExtendedArray<T>, TC> {\n return Converters.arrayOf(converter, onError).map((items: T[]) => {\n return captureResult(() => new ExtendedArray(label, ...items));\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to an arbitrary strongly-typed\n * range of some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @param constructor - Static constructor to instantiate the object.\n * @public\n */\nexport function rangeTypeOf<T, RT extends RangeOf<T>, TC = unknown>(\n converter: Converter<T, TC>,\n constructor: (init: RangeOfProperties<T>) => Result<RT>\n): Converter<RT, TC> {\n return new Conversion.BaseConverter((from: unknown, __self, context?: TC) => {\n const result = Converters.object(\n {\n min: converter,\n max: converter\n },\n { optionalFields: ['min', 'max'] }\n ).convert(from, context);\n if (result.isSuccess()) {\n return constructor({ min: result.value.min, max: result.value.max });\n }\n return fail(result.message);\n });\n}\n\n/**\n * A helper wrapper to construct a `Converter` which converts to {@link Experimental.RangeOf | RangeOf<T>}\n * where `<T>` is some comparable type.\n * @param converter - `Converter` used to convert `min` and `max` extent of the range.\n * @public\n */\nexport function rangeOf<T, TC = unknown>(converter: Converter<T, TC>): Converter<RangeOf<T>, TC> {\n return rangeTypeOf<T, RangeOf<T>, TC>(converter, RangeOf.createRange);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { JsonValue } from '@fgv/ts-json-base';
2
2
  import { Converter } from '@fgv/ts-utils';
3
- import { EncryptedFileErrorMode, EncryptedFileFormat, EncryptionAlgorithm, IEncryptedFile, IKeyDerivationParams, INamedSecret, KeyDerivationFunction } from './model';
3
+ import { EncryptedFileErrorMode, EncryptedFileFormat, EncryptionAlgorithm, IArgon2idKeyDerivationParams, IEncryptedFile, IKeyDerivationParams, INamedSecret, IPbkdf2KeyDerivationParams, KeyDerivationFunction } from './model';
4
4
  /**
5
5
  * Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.
6
6
  * @public
@@ -21,8 +21,19 @@ export declare const encryptedFileErrorMode: Converter<EncryptedFileErrorMode>;
21
21
  * @public
22
22
  */
23
23
  export declare const keyDerivationFunction: Converter<KeyDerivationFunction>;
24
+ /**
25
+ * Converter for {@link CryptoUtils.IPbkdf2KeyDerivationParams | PBKDF2 key derivation parameters}.
26
+ * @public
27
+ */
28
+ export declare const pbkdf2KeyDerivationParams: Converter<IPbkdf2KeyDerivationParams>;
29
+ /**
30
+ * Converter for {@link CryptoUtils.IArgon2idKeyDerivationParams | Argon2id key derivation parameters}.
31
+ * @public
32
+ */
33
+ export declare const argon2idKeyDerivationParams: Converter<IArgon2idKeyDerivationParams>;
24
34
  /**
25
35
  * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
36
+ * Handles both PBKDF2 and Argon2id discriminated union arms.
26
37
  * @public
27
38
  */
28
39
  export declare const keyDerivationParams: Converter<IKeyDerivationParams>;
@@ -1 +1 @@
1
- {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/converters.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAgC,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAA6B,MAAM,eAAe,CAAC;AAErE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAMjB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CACiB,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CAE7D,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,CAAC,sBAAsB,CACS,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,CAAC,qBAAqB,CACJ,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,oBAAoB,CAI9D,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,CAAC,MAAM,CAOzC,CAAC;AAMH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,UAAU,CAoBrD,CAAC;AAMH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,YAAY,CAG9C,CAAC;AAsCH;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,GAAG,SAAS,EAChE,iBAAiB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GACvC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAyBtC;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,cAAc,CAAkC,CAAC"}
1
+ {"version":3,"file":"converters.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/converters.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAgC,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAA6B,MAAM,eAAe,CAAC;AAErE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,4BAA4B,EAC5B,cAAc,EACd,oBAAoB,EACpB,YAAY,EACZ,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAMjB;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CACiB,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,CAE7D,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,CAAC,sBAAsB,CACS,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,CAAC,qBAAqB,CACQ,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,CAAC,0BAA0B,CAKxE,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,CAAC,4BAA4B,CAO5E,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,CAAC,oBAAoB,CAG9D,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,CAAC,MAAM,CAOzC,CAAC;AAMH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,UAAU,CAoBrD,CAAC;AAMH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,YAAY,CAG9C,CAAC;AAsCH;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,GAAG,SAAS,EAChE,iBAAiB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,GACvC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAyBtC;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,cAAc,CAAkC,CAAC"}
@@ -52,7 +52,7 @@ var __importStar = (this && this.__importStar) || (function () {
52
52
  };
53
53
  })();
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
- exports.encryptedFile = exports.namedSecret = exports.uint8ArrayFromBase64 = exports.base64String = exports.keyDerivationParams = exports.keyDerivationFunction = exports.encryptedFileErrorMode = exports.encryptedFileFormat = exports.encryptionAlgorithm = void 0;
55
+ exports.encryptedFile = exports.namedSecret = exports.uint8ArrayFromBase64 = exports.base64String = exports.keyDerivationParams = exports.argon2idKeyDerivationParams = exports.pbkdf2KeyDerivationParams = exports.keyDerivationFunction = exports.encryptedFileErrorMode = exports.encryptedFileFormat = exports.encryptionAlgorithm = void 0;
56
56
  exports.createEncryptedFileConverter = createEncryptedFileConverter;
57
57
  const ts_json_base_1 = require("@fgv/ts-json-base");
58
58
  const ts_utils_1 = require("@fgv/ts-utils");
@@ -81,16 +81,36 @@ exports.encryptedFileErrorMode = ts_utils_1.Converters.enumeratedValue(['fail',
81
81
  * Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.
82
82
  * @public
83
83
  */
84
- exports.keyDerivationFunction = ts_utils_1.Converters.enumeratedValue(['pbkdf2']);
84
+ exports.keyDerivationFunction = ts_utils_1.Converters.enumeratedValue(['pbkdf2', 'argon2id']);
85
85
  /**
86
- * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
86
+ * Converter for {@link CryptoUtils.IPbkdf2KeyDerivationParams | PBKDF2 key derivation parameters}.
87
87
  * @public
88
88
  */
89
- exports.keyDerivationParams = ts_utils_1.Converters.object({
90
- kdf: exports.keyDerivationFunction,
89
+ exports.pbkdf2KeyDerivationParams = ts_utils_1.Converters.object({
90
+ kdf: ts_utils_1.Converters.enumeratedValue(['pbkdf2']),
91
91
  salt: ts_utils_1.Converters.string,
92
92
  iterations: ts_utils_1.Converters.number
93
93
  });
94
+ /**
95
+ * Converter for {@link CryptoUtils.IArgon2idKeyDerivationParams | Argon2id key derivation parameters}.
96
+ * @public
97
+ */
98
+ exports.argon2idKeyDerivationParams = ts_utils_1.Converters.object({
99
+ kdf: ts_utils_1.Converters.enumeratedValue(['argon2id']),
100
+ salt: ts_utils_1.Converters.string,
101
+ memoryKiB: ts_utils_1.Converters.number,
102
+ iterations: ts_utils_1.Converters.number,
103
+ parallelism: ts_utils_1.Converters.number
104
+ });
105
+ /**
106
+ * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
107
+ * Handles both PBKDF2 and Argon2id discriminated union arms.
108
+ * @public
109
+ */
110
+ exports.keyDerivationParams = ts_utils_1.Converters.oneOf([
111
+ exports.pbkdf2KeyDerivationParams,
112
+ exports.argon2idKeyDerivationParams
113
+ ]);
94
114
  /**
95
115
  * Converter for base64 strings (validates format).
96
116
  * @public
@@ -1 +1 @@
1
- {"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/converters.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Converters as JsonConverters, JsonValue } from '@fgv/ts-json-base';\nimport { Converter, Converters, fail, succeed } from '@fgv/ts-utils';\nimport * as Constants from './constants';\nimport {\n EncryptedFileErrorMode,\n EncryptedFileFormat,\n EncryptionAlgorithm,\n IEncryptedFile,\n IKeyDerivationParams,\n INamedSecret,\n KeyDerivationFunction\n} from './model';\n\n// ============================================================================\n// Base Converters\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.\n * @public\n */\nexport const encryptionAlgorithm: Converter<EncryptionAlgorithm> =\n Converters.enumeratedValue<EncryptionAlgorithm>([Constants.DEFAULT_ALGORITHM]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileFormat | encrypted file format} version.\n * @public\n */\nexport const encryptedFileFormat: Converter<EncryptedFileFormat> = Converters.enumeratedValue([\n Constants.ENCRYPTED_FILE_FORMAT\n]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileErrorMode | encrypted file error mode}.\n * @public\n */\nexport const encryptedFileErrorMode: Converter<EncryptedFileErrorMode> =\n Converters.enumeratedValue<EncryptedFileErrorMode>(['fail', 'skip', 'warn']);\n\n/**\n * Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.\n * @public\n */\nexport const keyDerivationFunction: Converter<KeyDerivationFunction> =\n Converters.enumeratedValue<KeyDerivationFunction>(['pbkdf2']);\n\n/**\n * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.\n * @public\n */\nexport const keyDerivationParams: Converter<IKeyDerivationParams> = Converters.object<IKeyDerivationParams>({\n kdf: keyDerivationFunction,\n salt: Converters.string,\n iterations: Converters.number\n});\n\n/**\n * Converter for base64 strings (validates format).\n * @public\n */\nexport const base64String: Converter<string> = Converters.string.withConstraint((value) => {\n // Basic base64 validation - check for valid characters and padding\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n if (!base64Regex.test(value)) {\n return fail('Invalid base64 encoding');\n }\n return succeed(value);\n});\n\n// ============================================================================\n// Uint8Array Converter\n// ============================================================================\n\n/**\n * Converter which converts a base64 string to a Uint8Array.\n * @public\n */\nexport const uint8ArrayFromBase64: Converter<Uint8Array> = Converters.string.map((base64) => {\n try {\n // Use Buffer in Node.js environment, atob in browser\n if (typeof Buffer !== 'undefined') {\n return succeed(Uint8Array.from(Buffer.from(base64, 'base64')));\n }\n /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\n const binaryString = atob(base64);\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 succeed(bytes);\n } catch (e) {\n // This catch is for browser's atob() which throws on invalid base64\n // Node's Buffer.from() doesn't throw, it ignores invalid characters\n const message = e instanceof Error ? e.message : String(e);\n return fail(`Invalid base64: ${message}`);\n }\n /* c8 ignore stop */\n});\n\n// ============================================================================\n// Named Secret Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.INamedSecret | named secret} from JSON representation.\n * Expects key as base64 string in JSON, converts to Uint8Array.\n * @public\n */\nexport const namedSecret: Converter<INamedSecret> = Converters.object<INamedSecret>({\n name: Converters.string,\n key: uint8ArrayFromBase64\n});\n\n// ============================================================================\n// Encrypted File Converter Factory\n// ============================================================================\n\n/**\n * Base encrypted file structure without metadata typing.\n * Used internally by the converter factory.\n */\ninterface IBaseEncryptedFile {\n readonly format: EncryptedFileFormat;\n readonly secretName: string;\n readonly algorithm: EncryptionAlgorithm;\n readonly iv: string;\n readonly authTag: string;\n readonly encryptedData: string;\n readonly keyDerivation?: IKeyDerivationParams;\n readonly metadata?: JsonValue;\n}\n\n/**\n * Base converter for encrypted file structure (without typed metadata).\n */\nconst baseEncryptedFileConverter: Converter<IBaseEncryptedFile> = Converters.object<IBaseEncryptedFile>(\n {\n format: encryptedFileFormat,\n secretName: Converters.string,\n algorithm: encryptionAlgorithm,\n iv: base64String,\n authTag: base64String,\n encryptedData: base64String,\n keyDerivation: keyDerivationParams,\n metadata: JsonConverters.jsonValue\n },\n { optionalFields: ['keyDerivation', 'metadata'] }\n);\n\n/**\n * Creates a converter for {@link CryptoUtils.IEncryptedFile | encrypted files} with optional typed metadata.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @param metadataConverter - Optional converter for validating metadata field\n * @returns A converter that validates and converts encrypted file structures\n * @public\n */\nexport function createEncryptedFileConverter<TMetadata = JsonValue>(\n metadataConverter?: Converter<TMetadata>\n): Converter<IEncryptedFile<TMetadata>> {\n return Converters.generic<IEncryptedFile<TMetadata>>((from: unknown) => {\n // First validate base structure\n const baseResult = baseEncryptedFileConverter.convert(from);\n if (baseResult.isFailure()) {\n return fail(baseResult.message);\n }\n\n const base = baseResult.value;\n\n // Validate metadata with specific converter if provided and metadata exists\n if (metadataConverter !== undefined && base.metadata !== undefined) {\n const metaResult = metadataConverter.convert(base.metadata);\n if (metaResult.isFailure()) {\n return fail(`Invalid metadata: ${metaResult.message}`);\n }\n return succeed({\n ...base,\n metadata: metaResult.value\n } as IEncryptedFile<TMetadata>);\n }\n\n // Return as-is (metadata is either undefined or untyped JsonValue)\n return succeed(base as IEncryptedFile<TMetadata>);\n });\n}\n\n/**\n * Default converter for encrypted files without typed metadata.\n * @public\n */\nexport const encryptedFile: Converter<IEncryptedFile> = createEncryptedFileConverter();\n"]}
1
+ {"version":3,"file":"converters.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/converters.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Converters as JsonConverters, JsonValue } from '@fgv/ts-json-base';\nimport { Converter, Converters, fail, succeed } from '@fgv/ts-utils';\nimport * as Constants from './constants';\nimport {\n EncryptedFileErrorMode,\n EncryptedFileFormat,\n EncryptionAlgorithm,\n IArgon2idKeyDerivationParams,\n IEncryptedFile,\n IKeyDerivationParams,\n INamedSecret,\n IPbkdf2KeyDerivationParams,\n KeyDerivationFunction\n} from './model';\n\n// ============================================================================\n// Base Converters\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.\n * @public\n */\nexport const encryptionAlgorithm: Converter<EncryptionAlgorithm> =\n Converters.enumeratedValue<EncryptionAlgorithm>([Constants.DEFAULT_ALGORITHM]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileFormat | encrypted file format} version.\n * @public\n */\nexport const encryptedFileFormat: Converter<EncryptedFileFormat> = Converters.enumeratedValue([\n Constants.ENCRYPTED_FILE_FORMAT\n]);\n\n/**\n * Converter for {@link CryptoUtils.EncryptedFileErrorMode | encrypted file error mode}.\n * @public\n */\nexport const encryptedFileErrorMode: Converter<EncryptedFileErrorMode> =\n Converters.enumeratedValue<EncryptedFileErrorMode>(['fail', 'skip', 'warn']);\n\n/**\n * Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.\n * @public\n */\nexport const keyDerivationFunction: Converter<KeyDerivationFunction> =\n Converters.enumeratedValue<KeyDerivationFunction>(['pbkdf2', 'argon2id']);\n\n/**\n * Converter for {@link CryptoUtils.IPbkdf2KeyDerivationParams | PBKDF2 key derivation parameters}.\n * @public\n */\nexport const pbkdf2KeyDerivationParams: Converter<IPbkdf2KeyDerivationParams> =\n Converters.object<IPbkdf2KeyDerivationParams>({\n kdf: Converters.enumeratedValue<'pbkdf2'>(['pbkdf2']),\n salt: Converters.string,\n iterations: Converters.number\n });\n\n/**\n * Converter for {@link CryptoUtils.IArgon2idKeyDerivationParams | Argon2id key derivation parameters}.\n * @public\n */\nexport const argon2idKeyDerivationParams: Converter<IArgon2idKeyDerivationParams> =\n Converters.object<IArgon2idKeyDerivationParams>({\n kdf: Converters.enumeratedValue<'argon2id'>(['argon2id']),\n salt: Converters.string,\n memoryKiB: Converters.number,\n iterations: Converters.number,\n parallelism: Converters.number\n });\n\n/**\n * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.\n * Handles both PBKDF2 and Argon2id discriminated union arms.\n * @public\n */\nexport const keyDerivationParams: Converter<IKeyDerivationParams> = Converters.oneOf<IKeyDerivationParams>([\n pbkdf2KeyDerivationParams,\n argon2idKeyDerivationParams\n]);\n\n/**\n * Converter for base64 strings (validates format).\n * @public\n */\nexport const base64String: Converter<string> = Converters.string.withConstraint((value) => {\n // Basic base64 validation - check for valid characters and padding\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n if (!base64Regex.test(value)) {\n return fail('Invalid base64 encoding');\n }\n return succeed(value);\n});\n\n// ============================================================================\n// Uint8Array Converter\n// ============================================================================\n\n/**\n * Converter which converts a base64 string to a Uint8Array.\n * @public\n */\nexport const uint8ArrayFromBase64: Converter<Uint8Array> = Converters.string.map((base64) => {\n try {\n // Use Buffer in Node.js environment, atob in browser\n if (typeof Buffer !== 'undefined') {\n return succeed(Uint8Array.from(Buffer.from(base64, 'base64')));\n }\n /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */\n const binaryString = atob(base64);\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 succeed(bytes);\n } catch (e) {\n // This catch is for browser's atob() which throws on invalid base64\n // Node's Buffer.from() doesn't throw, it ignores invalid characters\n const message = e instanceof Error ? e.message : String(e);\n return fail(`Invalid base64: ${message}`);\n }\n /* c8 ignore stop */\n});\n\n// ============================================================================\n// Named Secret Converter\n// ============================================================================\n\n/**\n * Converter for {@link CryptoUtils.INamedSecret | named secret} from JSON representation.\n * Expects key as base64 string in JSON, converts to Uint8Array.\n * @public\n */\nexport const namedSecret: Converter<INamedSecret> = Converters.object<INamedSecret>({\n name: Converters.string,\n key: uint8ArrayFromBase64\n});\n\n// ============================================================================\n// Encrypted File Converter Factory\n// ============================================================================\n\n/**\n * Base encrypted file structure without metadata typing.\n * Used internally by the converter factory.\n */\ninterface IBaseEncryptedFile {\n readonly format: EncryptedFileFormat;\n readonly secretName: string;\n readonly algorithm: EncryptionAlgorithm;\n readonly iv: string;\n readonly authTag: string;\n readonly encryptedData: string;\n readonly keyDerivation?: IKeyDerivationParams;\n readonly metadata?: JsonValue;\n}\n\n/**\n * Base converter for encrypted file structure (without typed metadata).\n */\nconst baseEncryptedFileConverter: Converter<IBaseEncryptedFile> = Converters.object<IBaseEncryptedFile>(\n {\n format: encryptedFileFormat,\n secretName: Converters.string,\n algorithm: encryptionAlgorithm,\n iv: base64String,\n authTag: base64String,\n encryptedData: base64String,\n keyDerivation: keyDerivationParams,\n metadata: JsonConverters.jsonValue\n },\n { optionalFields: ['keyDerivation', 'metadata'] }\n);\n\n/**\n * Creates a converter for {@link CryptoUtils.IEncryptedFile | encrypted files} with optional typed metadata.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @param metadataConverter - Optional converter for validating metadata field\n * @returns A converter that validates and converts encrypted file structures\n * @public\n */\nexport function createEncryptedFileConverter<TMetadata = JsonValue>(\n metadataConverter?: Converter<TMetadata>\n): Converter<IEncryptedFile<TMetadata>> {\n return Converters.generic<IEncryptedFile<TMetadata>>((from: unknown) => {\n // First validate base structure\n const baseResult = baseEncryptedFileConverter.convert(from);\n if (baseResult.isFailure()) {\n return fail(baseResult.message);\n }\n\n const base = baseResult.value;\n\n // Validate metadata with specific converter if provided and metadata exists\n if (metadataConverter !== undefined && base.metadata !== undefined) {\n const metaResult = metadataConverter.convert(base.metadata);\n if (metaResult.isFailure()) {\n return fail(`Invalid metadata: ${metaResult.message}`);\n }\n return succeed({\n ...base,\n metadata: metaResult.value\n } as IEncryptedFile<TMetadata>);\n }\n\n // Return as-is (metadata is either undefined or untyped JsonValue)\n return succeed(base as IEncryptedFile<TMetadata>);\n });\n}\n\n/**\n * Default converter for encrypted files without typed metadata.\n * @public\n */\nexport const encryptedFile: Converter<IEncryptedFile> = createEncryptedFileConverter();\n"]}