@fgv/ts-extras 5.0.2 → 5.1.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/index.browser.js +6 -2
  2. package/dist/index.js +5 -1
  3. package/dist/packlets/ai-assist/apiClient.js +484 -0
  4. package/dist/packlets/ai-assist/converters.js +121 -0
  5. package/dist/packlets/ai-assist/index.js +10 -0
  6. package/dist/packlets/ai-assist/model.js +90 -0
  7. package/dist/packlets/ai-assist/registry.js +145 -0
  8. package/dist/packlets/ai-assist/toolFormats.js +160 -0
  9. package/dist/packlets/crypto-utils/constants.js +48 -0
  10. package/dist/packlets/crypto-utils/converters.js +155 -0
  11. package/dist/packlets/crypto-utils/directEncryptionProvider.js +86 -0
  12. package/dist/packlets/crypto-utils/encryptedFile.js +161 -0
  13. package/dist/packlets/crypto-utils/index.browser.js +41 -0
  14. package/dist/packlets/crypto-utils/index.js +41 -0
  15. package/dist/packlets/crypto-utils/keystore/converters.js +84 -0
  16. package/dist/packlets/crypto-utils/keystore/index.js +31 -0
  17. package/dist/packlets/crypto-utils/keystore/keyStore.js +758 -0
  18. package/dist/packlets/crypto-utils/keystore/model.js +64 -0
  19. package/dist/packlets/crypto-utils/model.js +39 -0
  20. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +159 -0
  21. package/dist/packlets/experimental/formatter.js +1 -1
  22. package/dist/packlets/mustache/index.js +23 -0
  23. package/dist/packlets/mustache/interfaces.js +25 -0
  24. package/dist/packlets/mustache/mustacheTemplate.js +242 -0
  25. package/dist/packlets/record-jar/recordJarHelpers.js +1 -1
  26. package/dist/packlets/yaml/converters.js +46 -0
  27. package/dist/packlets/yaml/index.js +23 -0
  28. package/dist/packlets/zip-file-tree/index.js +1 -0
  29. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +43 -2
  30. package/dist/packlets/zip-file-tree/zipFileTreeWriter.js +40 -0
  31. package/dist/ts-extras.d.ts +1990 -112
  32. package/dist/tsdoc-metadata.json +1 -1
  33. package/lib/index.browser.d.ts +3 -1
  34. package/lib/index.browser.js +6 -1
  35. package/lib/index.d.ts +5 -1
  36. package/lib/index.js +9 -1
  37. package/lib/packlets/ai-assist/apiClient.d.ts +60 -0
  38. package/lib/packlets/ai-assist/apiClient.js +488 -0
  39. package/lib/packlets/ai-assist/converters.d.ts +55 -0
  40. package/lib/packlets/ai-assist/converters.js +124 -0
  41. package/lib/packlets/ai-assist/index.d.ts +10 -0
  42. package/lib/packlets/ai-assist/index.js +33 -0
  43. package/lib/packlets/ai-assist/model.d.ts +222 -0
  44. package/lib/packlets/ai-assist/model.js +95 -0
  45. package/lib/packlets/ai-assist/registry.d.ts +25 -0
  46. package/lib/packlets/ai-assist/registry.js +150 -0
  47. package/lib/packlets/ai-assist/toolFormats.d.ts +44 -0
  48. package/lib/packlets/ai-assist/toolFormats.js +166 -0
  49. package/lib/packlets/crypto-utils/constants.d.ts +26 -0
  50. package/lib/packlets/crypto-utils/constants.js +51 -0
  51. package/lib/packlets/crypto-utils/converters.d.ts +58 -0
  52. package/lib/packlets/crypto-utils/converters.js +192 -0
  53. package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts +69 -0
  54. package/lib/packlets/crypto-utils/directEncryptionProvider.js +90 -0
  55. package/lib/packlets/crypto-utils/encryptedFile.d.ts +88 -0
  56. package/lib/packlets/crypto-utils/encryptedFile.js +201 -0
  57. package/lib/packlets/crypto-utils/index.browser.d.ts +14 -0
  58. package/lib/packlets/crypto-utils/index.browser.js +91 -0
  59. package/lib/packlets/crypto-utils/index.d.ts +15 -0
  60. package/lib/packlets/crypto-utils/index.js +88 -0
  61. package/lib/packlets/crypto-utils/keystore/converters.d.ts +29 -0
  62. package/lib/packlets/crypto-utils/keystore/converters.js +87 -0
  63. package/lib/packlets/crypto-utils/keystore/index.d.ts +9 -0
  64. package/lib/packlets/crypto-utils/keystore/index.js +71 -0
  65. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +239 -0
  66. package/lib/packlets/crypto-utils/keystore/keyStore.js +795 -0
  67. package/lib/packlets/crypto-utils/keystore/model.d.ts +245 -0
  68. package/lib/packlets/crypto-utils/keystore/model.js +68 -0
  69. package/lib/packlets/crypto-utils/model.d.ts +236 -0
  70. package/lib/packlets/crypto-utils/model.js +76 -0
  71. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +62 -0
  72. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +196 -0
  73. package/lib/packlets/experimental/formatter.d.ts +1 -1
  74. package/lib/packlets/experimental/formatter.js +1 -1
  75. package/lib/packlets/mustache/index.d.ts +3 -0
  76. package/lib/packlets/mustache/index.js +27 -0
  77. package/lib/packlets/mustache/interfaces.d.ts +97 -0
  78. package/lib/packlets/mustache/interfaces.js +26 -0
  79. package/lib/packlets/mustache/mustacheTemplate.d.ts +76 -0
  80. package/lib/packlets/mustache/mustacheTemplate.js +249 -0
  81. package/lib/packlets/record-jar/recordJarHelpers.js +1 -1
  82. package/lib/packlets/yaml/converters.d.ts +9 -0
  83. package/lib/packlets/yaml/converters.js +82 -0
  84. package/lib/packlets/yaml/index.d.ts +2 -0
  85. package/lib/packlets/yaml/index.js +39 -0
  86. package/lib/packlets/zip-file-tree/index.d.ts +1 -0
  87. package/lib/packlets/zip-file-tree/index.js +15 -0
  88. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +31 -2
  89. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +42 -1
  90. package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts +27 -0
  91. package/lib/packlets/zip-file-tree/zipFileTreeWriter.js +43 -0
  92. package/package.json +37 -18
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ // Copyright (c) 2024 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
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
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;
56
+ exports.createEncryptedFileConverter = createEncryptedFileConverter;
57
+ const ts_json_base_1 = require("@fgv/ts-json-base");
58
+ const ts_utils_1 = require("@fgv/ts-utils");
59
+ const Constants = __importStar(require("./constants"));
60
+ // ============================================================================
61
+ // Base Converters
62
+ // ============================================================================
63
+ /**
64
+ * Converter for {@link CryptoUtils.EncryptionAlgorithm | encryption algorithm} values.
65
+ * @public
66
+ */
67
+ exports.encryptionAlgorithm = ts_utils_1.Converters.enumeratedValue([Constants.DEFAULT_ALGORITHM]);
68
+ /**
69
+ * Converter for {@link CryptoUtils.EncryptedFileFormat | encrypted file format} version.
70
+ * @public
71
+ */
72
+ exports.encryptedFileFormat = ts_utils_1.Converters.enumeratedValue([
73
+ Constants.ENCRYPTED_FILE_FORMAT
74
+ ]);
75
+ /**
76
+ * Converter for {@link CryptoUtils.EncryptedFileErrorMode | encrypted file error mode}.
77
+ * @public
78
+ */
79
+ exports.encryptedFileErrorMode = ts_utils_1.Converters.enumeratedValue(['fail', 'skip', 'warn']);
80
+ /**
81
+ * Converter for {@link CryptoUtils.KeyDerivationFunction | key derivation function} type.
82
+ * @public
83
+ */
84
+ exports.keyDerivationFunction = ts_utils_1.Converters.enumeratedValue(['pbkdf2']);
85
+ /**
86
+ * Converter for {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
87
+ * @public
88
+ */
89
+ exports.keyDerivationParams = ts_utils_1.Converters.object({
90
+ kdf: exports.keyDerivationFunction,
91
+ salt: ts_utils_1.Converters.string,
92
+ iterations: ts_utils_1.Converters.number
93
+ });
94
+ /**
95
+ * Converter for base64 strings (validates format).
96
+ * @public
97
+ */
98
+ exports.base64String = ts_utils_1.Converters.string.withConstraint((value) => {
99
+ // Basic base64 validation - check for valid characters and padding
100
+ const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
101
+ if (!base64Regex.test(value)) {
102
+ return (0, ts_utils_1.fail)('Invalid base64 encoding');
103
+ }
104
+ return (0, ts_utils_1.succeed)(value);
105
+ });
106
+ // ============================================================================
107
+ // Uint8Array Converter
108
+ // ============================================================================
109
+ /**
110
+ * Converter which converts a base64 string to a Uint8Array.
111
+ * @public
112
+ */
113
+ exports.uint8ArrayFromBase64 = ts_utils_1.Converters.string.map((base64) => {
114
+ try {
115
+ // Use Buffer in Node.js environment, atob in browser
116
+ if (typeof Buffer !== 'undefined') {
117
+ return (0, ts_utils_1.succeed)(Uint8Array.from(Buffer.from(base64, 'base64')));
118
+ }
119
+ /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
120
+ const binaryString = atob(base64);
121
+ const bytes = new Uint8Array(binaryString.length);
122
+ for (let i = 0; i < binaryString.length; i++) {
123
+ bytes[i] = binaryString.charCodeAt(i);
124
+ }
125
+ return (0, ts_utils_1.succeed)(bytes);
126
+ }
127
+ catch (e) {
128
+ // This catch is for browser's atob() which throws on invalid base64
129
+ // Node's Buffer.from() doesn't throw, it ignores invalid characters
130
+ const message = e instanceof Error ? e.message : String(e);
131
+ return (0, ts_utils_1.fail)(`Invalid base64: ${message}`);
132
+ }
133
+ /* c8 ignore stop */
134
+ });
135
+ // ============================================================================
136
+ // Named Secret Converter
137
+ // ============================================================================
138
+ /**
139
+ * Converter for {@link CryptoUtils.INamedSecret | named secret} from JSON representation.
140
+ * Expects key as base64 string in JSON, converts to Uint8Array.
141
+ * @public
142
+ */
143
+ exports.namedSecret = ts_utils_1.Converters.object({
144
+ name: ts_utils_1.Converters.string,
145
+ key: exports.uint8ArrayFromBase64
146
+ });
147
+ /**
148
+ * Base converter for encrypted file structure (without typed metadata).
149
+ */
150
+ const baseEncryptedFileConverter = ts_utils_1.Converters.object({
151
+ format: exports.encryptedFileFormat,
152
+ secretName: ts_utils_1.Converters.string,
153
+ algorithm: exports.encryptionAlgorithm,
154
+ iv: exports.base64String,
155
+ authTag: exports.base64String,
156
+ encryptedData: exports.base64String,
157
+ keyDerivation: exports.keyDerivationParams,
158
+ metadata: ts_json_base_1.Converters.jsonValue
159
+ }, { optionalFields: ['keyDerivation', 'metadata'] });
160
+ /**
161
+ * Creates a converter for {@link CryptoUtils.IEncryptedFile | encrypted files} with optional typed metadata.
162
+ * @typeParam TMetadata - Type of optional unencrypted metadata
163
+ * @param metadataConverter - Optional converter for validating metadata field
164
+ * @returns A converter that validates and converts encrypted file structures
165
+ * @public
166
+ */
167
+ function createEncryptedFileConverter(metadataConverter) {
168
+ return ts_utils_1.Converters.generic((from) => {
169
+ // First validate base structure
170
+ const baseResult = baseEncryptedFileConverter.convert(from);
171
+ if (baseResult.isFailure()) {
172
+ return (0, ts_utils_1.fail)(baseResult.message);
173
+ }
174
+ const base = baseResult.value;
175
+ // Validate metadata with specific converter if provided and metadata exists
176
+ if (metadataConverter !== undefined && base.metadata !== undefined) {
177
+ const metaResult = metadataConverter.convert(base.metadata);
178
+ if (metaResult.isFailure()) {
179
+ return (0, ts_utils_1.fail)(`Invalid metadata: ${metaResult.message}`);
180
+ }
181
+ return (0, ts_utils_1.succeed)(Object.assign(Object.assign({}, base), { metadata: metaResult.value }));
182
+ }
183
+ // Return as-is (metadata is either undefined or untyped JsonValue)
184
+ return (0, ts_utils_1.succeed)(base);
185
+ });
186
+ }
187
+ /**
188
+ * Default converter for encrypted files without typed metadata.
189
+ * @public
190
+ */
191
+ exports.encryptedFile = createEncryptedFileConverter();
192
+ //# sourceMappingURL=converters.js.map
@@ -0,0 +1,69 @@
1
+ import { JsonValue } from '@fgv/ts-json-base';
2
+ import { Result } from '@fgv/ts-utils';
3
+ import { ICryptoProvider, IEncryptedFile, IEncryptionProvider } from './model';
4
+ /**
5
+ * Parameters for creating a {@link DirectEncryptionProvider}.
6
+ * @public
7
+ */
8
+ export interface IDirectEncryptionProviderParams {
9
+ /**
10
+ * The crypto provider to use for encryption operations.
11
+ */
12
+ readonly cryptoProvider: ICryptoProvider;
13
+ /**
14
+ * The encryption key (32 bytes for AES-256).
15
+ */
16
+ readonly key: Uint8Array;
17
+ /**
18
+ * Optional bound secret name.
19
+ * When set, `encryptByName` will fail if called with a different name.
20
+ * When unset, any secret name is accepted.
21
+ */
22
+ readonly boundSecretName?: string;
23
+ }
24
+ /**
25
+ * An {@link IEncryptionProvider} that uses a pre-supplied key and crypto provider.
26
+ *
27
+ * This is useful when you have the raw encryption key from an external source
28
+ * (e.g. a `SecretProvider` callback, password derivation, or a one-shot
29
+ * operation) and don't want to open a full KeyStore.
30
+ *
31
+ * Optionally bound to a specific secret name for safety: if a `boundSecretName`
32
+ * is provided, calls to `encryptByName` with a different name will fail.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const provider = DirectEncryptionProvider.create({
37
+ * cryptoProvider: nodeCryptoProvider,
38
+ * key: myKey,
39
+ * boundSecretName: 'my-collection'
40
+ * }).orThrow();
41
+ *
42
+ * const encrypted = await provider.encryptByName('my-collection', jsonContent);
43
+ * ```
44
+ *
45
+ * @public
46
+ */
47
+ export declare class DirectEncryptionProvider implements IEncryptionProvider {
48
+ private readonly _cryptoProvider;
49
+ private readonly _key;
50
+ private readonly _boundSecretName;
51
+ private constructor();
52
+ /**
53
+ * Creates a new DirectEncryptionProvider.
54
+ * @param params - Provider configuration
55
+ * @returns Success with provider, or Failure if parameters are invalid
56
+ * @public
57
+ */
58
+ static create(params: IDirectEncryptionProviderParams): Result<DirectEncryptionProvider>;
59
+ /**
60
+ * The secret name this provider is bound to, if any.
61
+ * @public
62
+ */
63
+ get boundSecretName(): string | undefined;
64
+ /**
65
+ * {@inheritDoc IEncryptionProvider.encryptByName}
66
+ */
67
+ encryptByName<TMetadata = JsonValue>(secretName: string, content: JsonValue, metadata?: TMetadata): Promise<Result<IEncryptedFile<TMetadata>>>;
68
+ }
69
+ //# sourceMappingURL=directEncryptionProvider.d.ts.map
@@ -0,0 +1,90 @@
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.DirectEncryptionProvider = void 0;
23
+ const ts_utils_1 = require("@fgv/ts-utils");
24
+ const encryptedFile_1 = require("./encryptedFile");
25
+ /**
26
+ * An {@link IEncryptionProvider} that uses a pre-supplied key and crypto provider.
27
+ *
28
+ * This is useful when you have the raw encryption key from an external source
29
+ * (e.g. a `SecretProvider` callback, password derivation, or a one-shot
30
+ * operation) and don't want to open a full KeyStore.
31
+ *
32
+ * Optionally bound to a specific secret name for safety: if a `boundSecretName`
33
+ * is provided, calls to `encryptByName` with a different name will fail.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const provider = DirectEncryptionProvider.create({
38
+ * cryptoProvider: nodeCryptoProvider,
39
+ * key: myKey,
40
+ * boundSecretName: 'my-collection'
41
+ * }).orThrow();
42
+ *
43
+ * const encrypted = await provider.encryptByName('my-collection', jsonContent);
44
+ * ```
45
+ *
46
+ * @public
47
+ */
48
+ class DirectEncryptionProvider {
49
+ constructor(params) {
50
+ this._cryptoProvider = params.cryptoProvider;
51
+ this._key = params.key;
52
+ this._boundSecretName = params.boundSecretName;
53
+ }
54
+ /**
55
+ * Creates a new DirectEncryptionProvider.
56
+ * @param params - Provider configuration
57
+ * @returns Success with provider, or Failure if parameters are invalid
58
+ * @public
59
+ */
60
+ static create(params) {
61
+ if (params.key.length === 0) {
62
+ return (0, ts_utils_1.fail)('Encryption key cannot be empty');
63
+ }
64
+ return (0, ts_utils_1.succeed)(new DirectEncryptionProvider(params));
65
+ }
66
+ /**
67
+ * The secret name this provider is bound to, if any.
68
+ * @public
69
+ */
70
+ get boundSecretName() {
71
+ return this._boundSecretName;
72
+ }
73
+ /**
74
+ * {@inheritDoc IEncryptionProvider.encryptByName}
75
+ */
76
+ async encryptByName(secretName, content, metadata) {
77
+ if (this._boundSecretName !== undefined && secretName !== this._boundSecretName) {
78
+ return (0, ts_utils_1.fail)(`Secret name mismatch: requested '${secretName}' but provider is bound to '${this._boundSecretName}'`);
79
+ }
80
+ return (0, encryptedFile_1.createEncryptedFile)({
81
+ content,
82
+ secretName,
83
+ key: this._key,
84
+ cryptoProvider: this._cryptoProvider,
85
+ metadata
86
+ });
87
+ }
88
+ }
89
+ exports.DirectEncryptionProvider = DirectEncryptionProvider;
90
+ //# sourceMappingURL=directEncryptionProvider.js.map
@@ -0,0 +1,88 @@
1
+ import { JsonValue } from '@fgv/ts-json-base';
2
+ import { Converter, Result } from '@fgv/ts-utils';
3
+ import { ICryptoProvider, IEncryptedFile, IKeyDerivationParams } from './model';
4
+ /**
5
+ * Encodes a `Uint8Array` to a base64 string.
6
+ * @param bytes - Bytes to encode
7
+ * @returns Base64 string
8
+ * @public
9
+ */
10
+ export declare function toBase64(bytes: Uint8Array): string;
11
+ /**
12
+ * Decodes a base64 string to a `Uint8Array`.
13
+ * @param base64 - Base64 string to decode
14
+ * @returns Decoded bytes
15
+ * @public
16
+ */
17
+ export declare function fromBase64(base64: string): Uint8Array;
18
+ /**
19
+ * Parameters for creating an {@link CryptoUtils.IEncryptedFile | encrypted file}.
20
+ * @typeParam TMetadata - Type of optional unencrypted metadata
21
+ * @public
22
+ */
23
+ export interface ICreateEncryptedFileParams<TMetadata = JsonValue> {
24
+ /**
25
+ * The JSON content to encrypt.
26
+ */
27
+ readonly content: JsonValue;
28
+ /**
29
+ * Name of the secret used for encryption.
30
+ */
31
+ readonly secretName: string;
32
+ /**
33
+ * The encryption key (32 bytes for AES-256).
34
+ */
35
+ readonly key: Uint8Array;
36
+ /**
37
+ * {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for encryption.
38
+ */
39
+ readonly cryptoProvider: ICryptoProvider;
40
+ /**
41
+ * Optional metadata to include unencrypted.
42
+ */
43
+ readonly metadata?: TMetadata;
44
+ /**
45
+ * Optional converter to validate metadata before including.
46
+ * If provided, metadata will be validated before encryption.
47
+ */
48
+ readonly metadataConverter?: Converter<TMetadata>;
49
+ /**
50
+ * Optional {@link CryptoUtils.IKeyDerivationParams | key derivation parameters}.
51
+ * If provided, stores the salt and iterations used to derive the key from a password.
52
+ * This allows decryption using only a password (the salt/iterations are read from the file).
53
+ */
54
+ readonly keyDerivation?: IKeyDerivationParams;
55
+ }
56
+ /**
57
+ * Creates an {@link CryptoUtils.IEncryptedFile | encrypted file} from JSON content.
58
+ * @typeParam TMetadata - Type of optional unencrypted metadata
59
+ * @param params - Encryption parameters
60
+ * @returns `Success` with encrypted file structure, or `Failure` with an error.
61
+ * @public
62
+ */
63
+ export declare function createEncryptedFile<TMetadata = JsonValue>(params: ICreateEncryptedFileParams<TMetadata>): Promise<Result<IEncryptedFile<TMetadata>>>;
64
+ /**
65
+ * Decrypts an {@link CryptoUtils.IEncryptedFile | encrypted file} and returns the JSON content.
66
+ * @typeParam TPayload - Expected type of decrypted content
67
+ * @param file - The encrypted file structure
68
+ * @param key - The decryption key (32 bytes for AES-256)
69
+ * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption
70
+ * @param payloadConverter - Optional converter to validate and convert decrypted content
71
+ * @returns `Success` with decrypted content, or `Failure` with an error.
72
+ * @public
73
+ */
74
+ export declare function decryptFile<TPayload extends JsonValue = JsonValue>(file: IEncryptedFile<unknown>, key: Uint8Array, cryptoProvider: ICryptoProvider, payloadConverter?: Converter<TPayload>): Promise<Result<TPayload>>;
75
+ /**
76
+ * Attempts to parse and decrypt a JSON object as an {@link CryptoUtils.IEncryptedFile | encrypted file}.
77
+ * @typeParam TPayload - Expected type of decrypted content
78
+ * @typeParam TMetadata - Type of optional unencrypted metadata
79
+ * @param json - JSON object that may be an encrypted file
80
+ * @param key - The decryption key (32 bytes for AES-256)
81
+ * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption
82
+ * @param payloadConverter - Optional converter to validate and convert decrypted content
83
+ * @param metadataConverter - Optional converter to validate metadata before decryption
84
+ * @returns `Success` with decrypted content, or `Failure` with an error (including if not encrypted)
85
+ * @public
86
+ */
87
+ export declare function tryDecryptFile<TPayload extends JsonValue = JsonValue, TMetadata = JsonValue>(json: JsonValue, key: Uint8Array, cryptoProvider: ICryptoProvider, payloadConverter?: Converter<TPayload>, metadataConverter?: Converter<TMetadata>): Promise<Result<TPayload>>;
88
+ //# sourceMappingURL=encryptedFile.d.ts.map
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ // Copyright (c) 2024 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
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.toBase64 = toBase64;
56
+ exports.fromBase64 = fromBase64;
57
+ exports.createEncryptedFile = createEncryptedFile;
58
+ exports.decryptFile = decryptFile;
59
+ exports.tryDecryptFile = tryDecryptFile;
60
+ const ts_utils_1 = require("@fgv/ts-utils");
61
+ const Constants = __importStar(require("./constants"));
62
+ const converters_1 = require("./converters");
63
+ const model_1 = require("./model");
64
+ // ============================================================================
65
+ // Base64 Utilities
66
+ // ============================================================================
67
+ /**
68
+ * Encodes a `Uint8Array` to a base64 string.
69
+ * @param bytes - Bytes to encode
70
+ * @returns Base64 string
71
+ * @public
72
+ */
73
+ /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
74
+ function toBase64(bytes) {
75
+ if (typeof Buffer !== 'undefined') {
76
+ return Buffer.from(bytes).toString('base64');
77
+ }
78
+ // Browser fallback
79
+ let binary = '';
80
+ for (let i = 0; i < bytes.length; i++) {
81
+ binary += String.fromCharCode(bytes[i]);
82
+ }
83
+ return btoa(binary);
84
+ }
85
+ /* c8 ignore stop */
86
+ /**
87
+ * Decodes a base64 string to a `Uint8Array`.
88
+ * @param base64 - Base64 string to decode
89
+ * @returns Decoded bytes
90
+ * @public
91
+ */
92
+ /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
93
+ function fromBase64(base64) {
94
+ if (typeof Buffer !== 'undefined') {
95
+ return new Uint8Array(Buffer.from(base64, 'base64'));
96
+ }
97
+ // Browser fallback
98
+ const binary = atob(base64);
99
+ const bytes = new Uint8Array(binary.length);
100
+ for (let i = 0; i < binary.length; i++) {
101
+ bytes[i] = binary.charCodeAt(i);
102
+ }
103
+ return bytes;
104
+ }
105
+ // ============================================================================
106
+ // Encryption Functions
107
+ // ============================================================================
108
+ /**
109
+ * Creates an {@link CryptoUtils.IEncryptedFile | encrypted file} from JSON content.
110
+ * @typeParam TMetadata - Type of optional unencrypted metadata
111
+ * @param params - Encryption parameters
112
+ * @returns `Success` with encrypted file structure, or `Failure` with an error.
113
+ * @public
114
+ */
115
+ async function createEncryptedFile(params) {
116
+ const { content, secretName, key, metadata, metadataConverter, keyDerivation, cryptoProvider } = params;
117
+ // Validate metadata if converter provided
118
+ /* c8 ignore next 6 - metadata validation path exercised via higher-level tests */
119
+ if (metadata !== undefined && metadataConverter !== undefined) {
120
+ const metadataResult = metadataConverter.convert(metadata);
121
+ if (metadataResult.isFailure()) {
122
+ return (0, ts_utils_1.fail)(`Invalid metadata: ${metadataResult.message}`);
123
+ }
124
+ }
125
+ // Serialize content to JSON string
126
+ const jsonResult = (0, ts_utils_1.captureResult)(() => JSON.stringify(content));
127
+ if (jsonResult.isFailure()) {
128
+ return (0, ts_utils_1.fail)(`Failed to serialize content: ${jsonResult.message}`);
129
+ }
130
+ // Encrypt the JSON string
131
+ const encryptResult = await cryptoProvider.encrypt(jsonResult.value, key);
132
+ if (encryptResult.isFailure()) {
133
+ return (0, ts_utils_1.fail)(`Encryption failed: ${encryptResult.message}`);
134
+ }
135
+ const { iv, authTag, encryptedData } = encryptResult.value;
136
+ // Build the encrypted file structure
137
+ const encryptedFile = Object.assign(Object.assign({ format: Constants.ENCRYPTED_FILE_FORMAT, secretName, algorithm: Constants.DEFAULT_ALGORITHM, iv: toBase64(iv), authTag: toBase64(authTag), encryptedData: toBase64(encryptedData) }, (metadata !== undefined ? { metadata } : {})), (keyDerivation !== undefined ? { keyDerivation } : {}));
138
+ return (0, ts_utils_1.succeed)(encryptedFile);
139
+ }
140
+ // ============================================================================
141
+ // Decryption Functions
142
+ // ============================================================================
143
+ /**
144
+ * Decrypts an {@link CryptoUtils.IEncryptedFile | encrypted file} and returns the JSON content.
145
+ * @typeParam TPayload - Expected type of decrypted content
146
+ * @param file - The encrypted file structure
147
+ * @param key - The decryption key (32 bytes for AES-256)
148
+ * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption
149
+ * @param payloadConverter - Optional converter to validate and convert decrypted content
150
+ * @returns `Success` with decrypted content, or `Failure` with an error.
151
+ * @public
152
+ */
153
+ async function decryptFile(file, key, cryptoProvider, payloadConverter) {
154
+ // Decode base64 values
155
+ const iv = fromBase64(file.iv);
156
+ const authTag = fromBase64(file.authTag);
157
+ const encryptedData = fromBase64(file.encryptedData);
158
+ // Decrypt
159
+ const decryptResult = await cryptoProvider.decrypt(encryptedData, key, iv, authTag);
160
+ if (decryptResult.isFailure()) {
161
+ return (0, ts_utils_1.fail)(decryptResult.message);
162
+ }
163
+ // Parse JSON
164
+ const parseResult = (0, ts_utils_1.captureResult)(() => JSON.parse(decryptResult.value)).withErrorFormat((e) => `Failed to parse decrypted content as JSON: ${e}`);
165
+ /* c8 ignore next 3 - JSON parse failure only occurs with corrupted encrypted data */
166
+ if (parseResult.isFailure()) {
167
+ return parseResult;
168
+ }
169
+ // Validate with converter if provided
170
+ /* c8 ignore next 3 - payload converter path exercised via higher-level tests */
171
+ if (payloadConverter !== undefined) {
172
+ return payloadConverter.convert(parseResult.value);
173
+ }
174
+ return parseResult;
175
+ }
176
+ /**
177
+ * Attempts to parse and decrypt a JSON object as an {@link CryptoUtils.IEncryptedFile | encrypted file}.
178
+ * @typeParam TPayload - Expected type of decrypted content
179
+ * @typeParam TMetadata - Type of optional unencrypted metadata
180
+ * @param json - JSON object that may be an encrypted file
181
+ * @param key - The decryption key (32 bytes for AES-256)
182
+ * @param cryptoProvider - {@link CryptoUtils.ICryptoProvider | Crypto provider} to use for decryption
183
+ * @param payloadConverter - Optional converter to validate and convert decrypted content
184
+ * @param metadataConverter - Optional converter to validate metadata before decryption
185
+ * @returns `Success` with decrypted content, or `Failure` with an error (including if not encrypted)
186
+ * @public
187
+ */
188
+ async function tryDecryptFile(json, key, cryptoProvider, payloadConverter, metadataConverter) {
189
+ // Check if it's an encrypted file
190
+ if (!(0, model_1.isEncryptedFile)(json)) {
191
+ return (0, ts_utils_1.fail)('Not an encrypted file');
192
+ }
193
+ // Validate and convert to typed encrypted file
194
+ const fileConverter = (0, converters_1.createEncryptedFileConverter)(metadataConverter);
195
+ const fileResult = fileConverter.convert(json);
196
+ if (fileResult.isFailure()) {
197
+ return (0, ts_utils_1.fail)(`Invalid encrypted file format: ${fileResult.message}`);
198
+ }
199
+ return decryptFile(fileResult.value, key, cryptoProvider, payloadConverter);
200
+ }
201
+ //# sourceMappingURL=encryptedFile.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Crypto utilities for encrypted file handling and key management (browser version).
3
+ * Note: For browser crypto provider, use \@fgv/ts-web-extras.
4
+ * @packageDocumentation
5
+ */
6
+ export * from './model';
7
+ export { AES_256_KEY_SIZE, DEFAULT_ALGORITHM, ENCRYPTED_FILE_FORMAT, GCM_AUTH_TAG_SIZE, GCM_IV_SIZE } from './constants';
8
+ import * as KeyStore from './keystore';
9
+ export { KeyStore };
10
+ import * as Converters from './converters';
11
+ export { Converters };
12
+ export { DirectEncryptionProvider, IDirectEncryptionProviderParams } from './directEncryptionProvider';
13
+ export { createEncryptedFile, decryptFile, fromBase64, ICreateEncryptedFileParams, toBase64, tryDecryptFile } from './encryptedFile';
14
+ //# sourceMappingURL=index.browser.d.ts.map