@metamask-previews/seedless-onboarding-controller 8.0.0-preview-e117c8109 → 8.0.0-preview-71413498f

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 (48) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/SecretMetadata.cjs +100 -82
  3. package/dist/SecretMetadata.cjs.map +1 -1
  4. package/dist/SecretMetadata.d.cts +65 -43
  5. package/dist/SecretMetadata.d.cts.map +1 -1
  6. package/dist/SecretMetadata.d.mts +65 -43
  7. package/dist/SecretMetadata.d.mts.map +1 -1
  8. package/dist/SecretMetadata.mjs +101 -83
  9. package/dist/SecretMetadata.mjs.map +1 -1
  10. package/dist/SeedlessOnboardingController.cjs +270 -77
  11. package/dist/SeedlessOnboardingController.cjs.map +1 -1
  12. package/dist/SeedlessOnboardingController.d.cts +36 -11
  13. package/dist/SeedlessOnboardingController.d.cts.map +1 -1
  14. package/dist/SeedlessOnboardingController.d.mts +36 -11
  15. package/dist/SeedlessOnboardingController.d.mts.map +1 -1
  16. package/dist/SeedlessOnboardingController.mjs +272 -79
  17. package/dist/SeedlessOnboardingController.mjs.map +1 -1
  18. package/dist/constants.cjs +6 -5
  19. package/dist/constants.cjs.map +1 -1
  20. package/dist/constants.d.cts +4 -3
  21. package/dist/constants.d.cts.map +1 -1
  22. package/dist/constants.d.mts +4 -3
  23. package/dist/constants.d.mts.map +1 -1
  24. package/dist/constants.mjs +5 -4
  25. package/dist/constants.mjs.map +1 -1
  26. package/dist/index.cjs +4 -1
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +2 -1
  29. package/dist/index.d.cts.map +1 -1
  30. package/dist/index.d.mts +2 -1
  31. package/dist/index.d.mts.map +1 -1
  32. package/dist/index.mjs +2 -1
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/types.cjs.map +1 -1
  35. package/dist/types.d.cts +6 -18
  36. package/dist/types.d.cts.map +1 -1
  37. package/dist/types.d.mts +6 -18
  38. package/dist/types.d.mts.map +1 -1
  39. package/dist/types.mjs.map +1 -1
  40. package/dist/utils.cjs +69 -1
  41. package/dist/utils.cjs.map +1 -1
  42. package/dist/utils.d.cts +38 -0
  43. package/dist/utils.d.cts.map +1 -1
  44. package/dist/utils.d.mts +38 -0
  45. package/dist/utils.d.mts.map +1 -1
  46. package/dist/utils.mjs +65 -0
  47. package/dist/utils.mjs.map +1 -1
  48. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add `runMigrations` method to run pending data migrations for legacy secrets ([#7284](https://github.com/MetaMask/core/pull/7284))
13
+ - Add `setMigrationVersion` method to set migration version directly for new users ([#7284](https://github.com/MetaMask/core/pull/7284))
14
+ - Add `SeedlessOnboardingMigrationVersion` enum for tracking migration versions ([#7284](https://github.com/MetaMask/core/pull/7284))
15
+ - Add `migrationVersion` to controller state to track applied migrations ([#7284](https://github.com/MetaMask/core/pull/7284))
16
+ - Add `itemId`, `dataType`, `createdAt`, and `storageVersion` storage-level properties to `SecretMetadata` ([#7284](https://github.com/MetaMask/core/pull/7284))
17
+ - Add `SecretMetadata.compare` static method for comparing metadata with PrimarySrp prioritization and TIMEUUID-based sorting ([#7284](https://github.com/MetaMask/core/pull/7284))
18
+ - Add `SecretMetadata.compareByTimestamp` static method for comparing metadata by timestamp ([#7284](https://github.com/MetaMask/core/pull/7284))
19
+ - Add `SecretMetadata.matchesType` static method for checking if metadata matches a given type ([#7284](https://github.com/MetaMask/core/pull/7284))
20
+ - Re-export `EncAccountDataType` from `@metamask/toprf-secure-backup` ([#7284](https://github.com/MetaMask/core/pull/7284))
21
+
22
+ ### Changed
23
+
24
+ - **BREAKING:** Change `addNewSecretData` method signature to require `dataType: EncAccountDataType` instead of `type: SecretType` ([#7284](https://github.com/MetaMask/core/pull/7284))
25
+ - `SecretType` is now derived internally from `EncAccountDataType`
26
+ - Encrypted payload still includes `type` for backward compatibility with older clients
27
+ - **BREAKING:** Remove `parseSecretsFromMetadataStore`, `fromBatch`, and `sort` methods from `SecretMetadata` ([#7284](https://github.com/MetaMask/core/pull/7284))
28
+ - Use `SecretMetadata.compare` or `SecretMetadata.compareByTimestamp` for sorting
29
+ - Use `SecretMetadata.matchesType` for filtering
30
+ - **BREAKING:** Change `SecretMetadata.fromRawMetadata` signature to require `storageMetadata` parameter ([#7284](https://github.com/MetaMask/core/pull/7284))
31
+ - **BREAKING:** Remove `version` getter from `SecretMetadata`; use `storageVersion` instead ([#7284](https://github.com/MetaMask/core/pull/7284))
32
+ - `refreshAuthTokens` now coalesces concurrent calls — if a refresh is already in-flight, subsequent callers share the same promise rather than issuing duplicate HTTP requests with the same token ([#7989](https://github.com/MetaMask/core/pull/7989))
33
+ - `refreshAuthTokens` now uses the live `state.refreshToken` value when calling `authenticate` after a successful HTTP refresh, preventing a stale-capture bug that could overwrite a concurrently-renewed token with an older one ([#7989](https://github.com/MetaMask/core/pull/7989))
34
+ - `renewRefreshToken` now queues the old token for revocation only after `#createNewVaultWithAuthData` succeeds, ensuring tokens are not queued if vault creation fails ([#7989](https://github.com/MetaMask/core/pull/7989))
35
+
36
+ ### Fixed
37
+
38
+ - Fix TIMEUUID sorting by extracting actual timestamps instead of using lexicographic comparison ([#7284](https://github.com/MetaMask/core/pull/7284))
39
+ - Fixed `refreshAuthTokens` throwing the generic `FailedToRefreshJWTTokens` error for all HTTP failures — a 401 response (permanently revoked token) now throws `InvalidRefreshToken` instead, giving callers a distinct signal to distinguish permanent from transient failures ([#7989](https://github.com/MetaMask/core/pull/7989))
40
+
10
41
  ## [8.0.0]
11
42
 
12
43
  ### Added
@@ -10,65 +10,40 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _SecretMetadata_data, _SecretMetadata_timestamp, _SecretMetadata_type, _SecretMetadata_version;
13
+ var _SecretMetadata_data, _SecretMetadata_timestamp, _SecretMetadata_type, _SecretMetadata_itemId, _SecretMetadata_dataType, _SecretMetadata_createdAt, _SecretMetadata_storageVersion;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SecretMetadata = void 0;
16
+ const toprf_secure_backup_1 = require("@metamask/toprf-secure-backup");
16
17
  const utils_1 = require("@metamask/utils");
17
18
  const constants_1 = require("./constants.cjs");
18
- /**
19
- * SecretMetadata is a class that adds metadata to the secret.
20
- *
21
- * It contains the secret and the timestamp when it was created.
22
- * It is used to store the secret in the metadata store.
23
- *
24
- * @example
25
- * ```ts
26
- * const secretMetadata = new SecretMetadata(secret);
27
- * ```
28
- */
19
+ const utils_2 = require("./utils.cjs");
29
20
  class SecretMetadata {
30
21
  /**
31
- * Create a new SecretMetadata instance.
32
- *
33
- * @param data - The secret to add metadata to.
34
- * @param options - The options for the secret metadata.
35
- * @param options.timestamp - The timestamp when the secret was created.
36
- * @param options.type - The type of the secret.
22
+ * @param data - The secret data.
23
+ * @param options - Optional metadata. New clients should provide `dataType`.
37
24
  */
38
25
  constructor(data, options) {
39
26
  _SecretMetadata_data.set(this, void 0);
40
27
  _SecretMetadata_timestamp.set(this, void 0);
41
28
  _SecretMetadata_type.set(this, void 0);
42
- _SecretMetadata_version.set(this, void 0);
29
+ // Storage-level metadata (not encrypted)
30
+ _SecretMetadata_itemId.set(this, void 0);
31
+ _SecretMetadata_dataType.set(this, void 0);
32
+ _SecretMetadata_createdAt.set(this, void 0);
33
+ _SecretMetadata_storageVersion.set(this, void 0);
43
34
  __classPrivateFieldSet(this, _SecretMetadata_data, data, "f");
44
35
  __classPrivateFieldSet(this, _SecretMetadata_timestamp, options?.timestamp ?? Date.now(), "f");
45
- __classPrivateFieldSet(this, _SecretMetadata_type, options?.type ?? constants_1.SecretType.Mnemonic, "f");
46
- __classPrivateFieldSet(this, _SecretMetadata_version, options?.version ?? constants_1.SecretMetadataVersion.V1, "f");
47
- }
48
- /**
49
- * Create an Array of SecretMetadata instances from an array of secrets.
50
- *
51
- * To respect the order of the secrets, we add the index to the timestamp
52
- * so that the first secret backup will have the oldest timestamp
53
- * and the last secret backup will have the newest timestamp.
54
- *
55
- * @param batchData - The data to add metadata to.
56
- * @param batchData.value - The SeedPhrase/PrivateKey to add metadata to.
57
- * @param batchData.options - The options for the seed phrase metadata.
58
- * @returns The SecretMetadata instances.
59
- */
60
- static fromBatch(batchData) {
61
- const timestamp = Date.now();
62
- return batchData.map((data, index) => {
63
- // To respect the order of the seed phrases, we add the index to the timestamp
64
- // so that the first seed phrase backup will have the oldest timestamp
65
- // and the last seed phrase backup will have the newest timestamp
66
- const backupCreatedAt = data.options?.timestamp ?? timestamp + index * 5;
67
- return new SecretMetadata(data.value, {
68
- timestamp: backupCreatedAt,
69
- type: data.options?.type,
70
- });
71
- });
36
+ __classPrivateFieldSet(this, _SecretMetadata_itemId, options?.itemId, "f");
37
+ __classPrivateFieldSet(this, _SecretMetadata_dataType, options?.dataType, "f");
38
+ __classPrivateFieldSet(this, _SecretMetadata_createdAt, options?.createdAt, "f");
39
+ __classPrivateFieldSet(this, _SecretMetadata_storageVersion, options?.storageVersion, "f");
40
+ // Derive type from dataType (new clients), or use provided type (V1 compat)
41
+ if (options?.dataType === undefined || options?.dataType === null) {
42
+ __classPrivateFieldSet(this, _SecretMetadata_type, options?.type ?? constants_1.SecretType.Mnemonic, "f");
43
+ }
44
+ else {
45
+ __classPrivateFieldSet(this, _SecretMetadata_type, (0, utils_2.getSecretTypeFromDataType)(options.dataType), "f");
46
+ }
72
47
  }
73
48
  /**
74
49
  * Assert that the provided value is a valid seed phrase metadata.
@@ -86,36 +61,18 @@ class SecretMetadata {
86
61
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidSecretMetadata);
87
62
  }
88
63
  }
89
- /**
90
- * Parse the SecretMetadata from the metadata store and return the array of SecretMetadata instances.
91
- *
92
- * This method also sorts the secrets by timestamp in ascending order, i.e. the oldest secret will be the first element in the array.
93
- *
94
- * @param secretMetadataArr - The array of SecretMetadata from the metadata store.
95
- * @param filterType - The type of the secret to filter.
96
- * @returns The array of SecretMetadata instances.
97
- */
98
- static parseSecretsFromMetadataStore(secretMetadataArr, filterType) {
99
- const parsedSecertMetadata = secretMetadataArr.map((metadata) => SecretMetadata.fromRawMetadata(metadata));
100
- const secrets = SecretMetadata.sort(parsedSecertMetadata);
101
- if (filterType) {
102
- return secrets.filter((secret) => secret.type === filterType);
103
- }
104
- return secrets;
105
- }
106
64
  /**
107
65
  * Parse and create the SecretMetadata instance from the raw metadata bytes.
108
66
  *
109
67
  * @param rawMetadata - The raw metadata.
68
+ * @param storageMetadata - Storage-level metadata from the metadata store.
110
69
  * @returns The parsed secret metadata.
111
70
  */
112
- static fromRawMetadata(rawMetadata) {
71
+ static fromRawMetadata(rawMetadata, storageMetadata) {
113
72
  const serializedMetadata = (0, utils_1.bytesToString)(rawMetadata);
114
73
  const parsedMetadata = JSON.parse(serializedMetadata);
115
74
  SecretMetadata.assertIsValidSecretMetadataJson(parsedMetadata);
116
- // if the type is not provided, we default to Mnemonic for the backwards compatibility
117
75
  const type = parsedMetadata.type ?? constants_1.SecretType.Mnemonic;
118
- const version = parsedMetadata.version ?? constants_1.SecretMetadataVersion.V1;
119
76
  let data;
120
77
  try {
121
78
  data = (0, utils_1.base64ToBytes)(parsedMetadata.data);
@@ -126,24 +83,72 @@ class SecretMetadata {
126
83
  return new SecretMetadata(data, {
127
84
  timestamp: parsedMetadata.timestamp,
128
85
  type,
129
- version,
86
+ ...storageMetadata,
130
87
  });
131
88
  }
132
89
  /**
133
- * Sort the seed phrases by timestamp.
90
+ * Compare two SecretMetadata instances by timestamp.
91
+ *
92
+ * @param a - The first SecretMetadata instance.
93
+ * @param b - The second SecretMetadata instance.
94
+ * @param order - The sort order. Default is 'asc'.
95
+ * @returns A negative number if a < b, positive if a > b, zero if equal.
96
+ */
97
+ static compareByTimestamp(a, b, order = 'asc') {
98
+ return order === 'asc'
99
+ ? a.timestamp - b.timestamp
100
+ : b.timestamp - a.timestamp;
101
+ }
102
+ /**
103
+ * Compare two SecretMetadata instances for ordering.
134
104
  *
135
- * @param data - The secret metadata array to sort.
136
- * @param order - The order to sort the seed phrases. Default is `desc`.
105
+ * Ordering priority:
106
+ * 1. PrimarySrp always comes first (regardless of order direction)
107
+ * 2. Server-side createdAt (TIMEUUID) if both have it
108
+ * 3. Legacy items (null createdAt) are considered older
109
+ * 4. Fall back to client-side timestamp
137
110
  *
138
- * @returns The sorted secret metadata array.
111
+ * @param a - The first SecretMetadata instance.
112
+ * @param b - The second SecretMetadata instance.
113
+ * @param order - The sort order. Default is 'asc'.
114
+ * @returns A negative number if a < b, positive if a > b, zero if equal.
139
115
  */
140
- static sort(data, order = 'asc') {
141
- return data.sort((a, b) => {
142
- if (order === 'asc') {
143
- return a.timestamp - b.timestamp;
144
- }
145
- return b.timestamp - a.timestamp;
146
- });
116
+ static compare(a, b, order = 'asc') {
117
+ // PrimarySrp always comes first (regardless of order direction)
118
+ const aIsPrimary = a.dataType === toprf_secure_backup_1.EncAccountDataType.PrimarySrp;
119
+ const bIsPrimary = b.dataType === toprf_secure_backup_1.EncAccountDataType.PrimarySrp;
120
+ if (aIsPrimary && bIsPrimary) {
121
+ return 0; // Both PrimarySrp: treat as equal (handles data corruption gracefully)
122
+ }
123
+ if (aIsPrimary) {
124
+ return -1;
125
+ }
126
+ if (bIsPrimary) {
127
+ return 1;
128
+ }
129
+ // Use server-side createdAt if available (TIMEUUID requires timestamp extraction)
130
+ if (a.createdAt && b.createdAt) {
131
+ return (0, utils_2.compareTimeuuid)(a.createdAt, b.createdAt, order);
132
+ }
133
+ // Handle mixed createdAt: legacy items (null) are older
134
+ if (!a.createdAt && b.createdAt) {
135
+ return order === 'asc' ? -1 : 1; // a (legacy/older) comes before b in asc
136
+ }
137
+ if (a.createdAt && !b.createdAt) {
138
+ return order === 'asc' ? 1 : -1; // b (legacy/older) comes before a in asc
139
+ }
140
+ // Both null: fall back to client-side timestamp
141
+ return SecretMetadata.compareByTimestamp(a, b, order);
142
+ }
143
+ /**
144
+ * Check if a SecretMetadata instance matches the given type.
145
+ *
146
+ * @param secret - The SecretMetadata instance to check.
147
+ * @param type - The type to match against.
148
+ * @returns True if the secret matches the type.
149
+ */
150
+ static matchesType(secret, type) {
151
+ return secret.type === type;
147
152
  }
148
153
  get data() {
149
154
  return __classPrivateFieldGet(this, _SecretMetadata_data, "f");
@@ -154,8 +159,22 @@ class SecretMetadata {
154
159
  get type() {
155
160
  return __classPrivateFieldGet(this, _SecretMetadata_type, "f");
156
161
  }
157
- get version() {
158
- return __classPrivateFieldGet(this, _SecretMetadata_version, "f");
162
+ get itemId() {
163
+ return __classPrivateFieldGet(this, _SecretMetadata_itemId, "f");
164
+ }
165
+ get dataType() {
166
+ return __classPrivateFieldGet(this, _SecretMetadata_dataType, "f");
167
+ }
168
+ get createdAt() {
169
+ return __classPrivateFieldGet(this, _SecretMetadata_createdAt, "f");
170
+ }
171
+ /**
172
+ * The storage-level version from the SDK ('v1' or 'v2').
173
+ *
174
+ * @returns The storage-level version.
175
+ */
176
+ get storageVersion() {
177
+ return __classPrivateFieldGet(this, _SecretMetadata_storageVersion, "f");
159
178
  }
160
179
  /**
161
180
  * Serialize the secret metadata and convert it to a Uint8Array.
@@ -174,12 +193,11 @@ class SecretMetadata {
174
193
  data: _data,
175
194
  timestamp: __classPrivateFieldGet(this, _SecretMetadata_timestamp, "f"),
176
195
  type: __classPrivateFieldGet(this, _SecretMetadata_type, "f"),
177
- version: __classPrivateFieldGet(this, _SecretMetadata_version, "f"),
178
196
  });
179
197
  // convert the serialized metadata to bytes(Uint8Array)
180
198
  return (0, utils_1.stringToBytes)(serializedMetadata);
181
199
  }
182
200
  }
183
201
  exports.SecretMetadata = SecretMetadata;
184
- _SecretMetadata_data = new WeakMap(), _SecretMetadata_timestamp = new WeakMap(), _SecretMetadata_type = new WeakMap(), _SecretMetadata_version = new WeakMap();
202
+ _SecretMetadata_data = new WeakMap(), _SecretMetadata_timestamp = new WeakMap(), _SecretMetadata_type = new WeakMap(), _SecretMetadata_itemId = new WeakMap(), _SecretMetadata_dataType = new WeakMap(), _SecretMetadata_createdAt = new WeakMap(), _SecretMetadata_storageVersion = new WeakMap();
185
203
  //# sourceMappingURL=SecretMetadata.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SecretMetadata.cjs","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n base64ToBytes,\n bytesToBase64,\n stringToBytes,\n bytesToString,\n} from '@metamask/utils';\n\nimport {\n SeedlessOnboardingControllerErrorMessage,\n SecretType,\n SecretMetadataVersion,\n} from './constants';\nimport type { SecretDataType, SecretMetadataOptions } from './types';\n\ntype ISecretMetadata<DataType extends SecretDataType = Uint8Array> = {\n data: DataType;\n timestamp: number;\n type: SecretType;\n version: SecretMetadataVersion;\n toBytes: () => Uint8Array;\n};\n\n/**\n * SecretMetadata type without the data and toBytes methods\n * in which the data is base64 encoded for more compacted metadata\n */\ntype SecretMetadataJson<DataType extends SecretDataType> = Omit<\n ISecretMetadata<DataType>,\n 'data' | 'toBytes'\n> & {\n data: string; // base64 encoded string\n};\n\n/**\n * SecretMetadata is a class that adds metadata to the secret.\n *\n * It contains the secret and the timestamp when it was created.\n * It is used to store the secret in the metadata store.\n *\n * @example\n * ```ts\n * const secretMetadata = new SecretMetadata(secret);\n * ```\n */\nexport class SecretMetadata<DataType extends SecretDataType = Uint8Array>\n implements ISecretMetadata<DataType>\n{\n readonly #data: DataType;\n\n readonly #timestamp: number;\n\n readonly #type: SecretType;\n\n readonly #version: SecretMetadataVersion;\n\n /**\n * Create a new SecretMetadata instance.\n *\n * @param data - The secret to add metadata to.\n * @param options - The options for the secret metadata.\n * @param options.timestamp - The timestamp when the secret was created.\n * @param options.type - The type of the secret.\n */\n constructor(data: DataType, options?: Partial<SecretMetadataOptions>) {\n this.#data = data;\n this.#timestamp = options?.timestamp ?? Date.now();\n this.#type = options?.type ?? SecretType.Mnemonic;\n this.#version = options?.version ?? SecretMetadataVersion.V1;\n }\n\n /**\n * Create an Array of SecretMetadata instances from an array of secrets.\n *\n * To respect the order of the secrets, we add the index to the timestamp\n * so that the first secret backup will have the oldest timestamp\n * and the last secret backup will have the newest timestamp.\n *\n * @param batchData - The data to add metadata to.\n * @param batchData.value - The SeedPhrase/PrivateKey to add metadata to.\n * @param batchData.options - The options for the seed phrase metadata.\n * @returns The SecretMetadata instances.\n */\n static fromBatch<DataType extends SecretDataType = Uint8Array>(\n batchData: {\n value: DataType;\n options?: Partial<SecretMetadataOptions>;\n }[],\n ): SecretMetadata<DataType>[] {\n const timestamp = Date.now();\n return batchData.map((data, index) => {\n // To respect the order of the seed phrases, we add the index to the timestamp\n // so that the first seed phrase backup will have the oldest timestamp\n // and the last seed phrase backup will have the newest timestamp\n const backupCreatedAt = data.options?.timestamp ?? timestamp + index * 5;\n return new SecretMetadata(data.value, {\n timestamp: backupCreatedAt,\n type: data.options?.type,\n });\n });\n }\n\n /**\n * Assert that the provided value is a valid seed phrase metadata.\n *\n * @param value - The value to check.\n * @throws If the value is not a valid seed phrase metadata.\n */\n static assertIsValidSecretMetadataJson<\n DataType extends SecretDataType = Uint8Array,\n >(value: unknown): asserts value is SecretMetadataJson<DataType> {\n if (\n typeof value !== 'object' ||\n !value ||\n !('data' in value) ||\n typeof value.data !== 'string' ||\n !('timestamp' in value) ||\n typeof value.timestamp !== 'number'\n ) {\n throw new Error(\n SeedlessOnboardingControllerErrorMessage.InvalidSecretMetadata,\n );\n }\n }\n\n /**\n * Parse the SecretMetadata from the metadata store and return the array of SecretMetadata instances.\n *\n * This method also sorts the secrets by timestamp in ascending order, i.e. the oldest secret will be the first element in the array.\n *\n * @param secretMetadataArr - The array of SecretMetadata from the metadata store.\n * @param filterType - The type of the secret to filter.\n * @returns The array of SecretMetadata instances.\n */\n static parseSecretsFromMetadataStore<\n DataType extends SecretDataType = Uint8Array,\n >(\n secretMetadataArr: Uint8Array[],\n filterType?: SecretType,\n ): SecretMetadata<DataType>[] {\n const parsedSecertMetadata = secretMetadataArr.map((metadata) =>\n SecretMetadata.fromRawMetadata<DataType>(metadata),\n );\n\n const secrets = SecretMetadata.sort(parsedSecertMetadata);\n\n if (filterType) {\n return secrets.filter((secret) => secret.type === filterType);\n }\n\n return secrets;\n }\n\n /**\n * Parse and create the SecretMetadata instance from the raw metadata bytes.\n *\n * @param rawMetadata - The raw metadata.\n * @returns The parsed secret metadata.\n */\n static fromRawMetadata<DataType extends SecretDataType>(\n rawMetadata: Uint8Array,\n ): SecretMetadata<DataType> {\n const serializedMetadata = bytesToString(rawMetadata);\n const parsedMetadata = JSON.parse(serializedMetadata);\n\n SecretMetadata.assertIsValidSecretMetadataJson<DataType>(parsedMetadata);\n\n // if the type is not provided, we default to Mnemonic for the backwards compatibility\n const type = parsedMetadata.type ?? SecretType.Mnemonic;\n const version = parsedMetadata.version ?? SecretMetadataVersion.V1;\n\n let data: DataType;\n try {\n data = base64ToBytes(parsedMetadata.data) as DataType;\n } catch {\n data = parsedMetadata.data as DataType;\n }\n\n return new SecretMetadata<DataType>(data, {\n timestamp: parsedMetadata.timestamp,\n type,\n version,\n });\n }\n\n /**\n * Sort the seed phrases by timestamp.\n *\n * @param data - The secret metadata array to sort.\n * @param order - The order to sort the seed phrases. Default is `desc`.\n *\n * @returns The sorted secret metadata array.\n */\n static sort<DataType extends SecretDataType = Uint8Array>(\n data: SecretMetadata<DataType>[],\n order: 'asc' | 'desc' = 'asc',\n ): SecretMetadata<DataType>[] {\n return data.sort((a, b) => {\n if (order === 'asc') {\n return a.timestamp - b.timestamp;\n }\n return b.timestamp - a.timestamp;\n });\n }\n\n get data(): DataType {\n return this.#data;\n }\n\n get timestamp(): number {\n return this.#timestamp;\n }\n\n get type(): SecretType {\n return this.#type;\n }\n\n get version(): SecretMetadataVersion {\n return this.#version;\n }\n\n /**\n * Serialize the secret metadata and convert it to a Uint8Array.\n *\n * @returns The serialized SecretMetadata value in bytes.\n */\n toBytes(): Uint8Array {\n let _data: unknown = this.#data;\n if (this.#data instanceof Uint8Array) {\n // encode the raw secret to base64 encoded string\n // to create more compacted metadata\n _data = bytesToBase64(this.#data);\n }\n\n // serialize the metadata to a JSON string\n const serializedMetadata = JSON.stringify({\n data: _data,\n timestamp: this.#timestamp,\n type: this.#type,\n version: this.#version,\n });\n\n // convert the serialized metadata to bytes(Uint8Array)\n return stringToBytes(serializedMetadata);\n }\n}\n"]}
1
+ {"version":3,"file":"SecretMetadata.cjs","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SecretDataItemOutput } from '@metamask/toprf-secure-backup';\nimport { EncAccountDataType } from '@metamask/toprf-secure-backup';\nimport {\n base64ToBytes,\n bytesToBase64,\n stringToBytes,\n bytesToString,\n} from '@metamask/utils';\n\nimport {\n SeedlessOnboardingControllerErrorMessage,\n SecretType,\n} from './constants';\nimport type { SecretDataType } from './types';\nimport { compareTimeuuid, getSecretTypeFromDataType } from './utils';\n\ntype ISecretMetadata<DataType extends SecretDataType = Uint8Array> = {\n data: DataType;\n timestamp: number;\n type: SecretType;\n toBytes: () => Uint8Array;\n};\n\n/**\n * SecretMetadata type without the data and toBytes methods\n * in which the data is base64 encoded for more compacted metadata\n */\ntype SecretMetadataJson<DataType extends SecretDataType> = Omit<\n ISecretMetadata<DataType>,\n 'data' | 'toBytes'\n> & {\n data: string; // base64 encoded string\n};\n\n/**\n * SecretMetadata is a class that adds metadata to the secret.\n *\n * It contains the secret and the timestamp when it was created.\n * It is used to store the secret in the metadata store.\n *\n * @example\n * ```ts\n * const secretMetadata = new SecretMetadata(secret);\n * ```\n */\n\n/**\n * Options for SecretMetadata constructor.\n *\n * New clients: provide V2 fields (`dataType`, `createdAt`, etc).\n * Reading V1 data: `timestamp` and `type` come from encrypted JSON.\n */\ntype SecretMetadataOptions = {\n // V1 fields (from encrypted JSON payload, for backward compat)\n timestamp?: number;\n type?: SecretType;\n\n // Storage-level metadata from the metadata store (not encrypted).\n itemId?: string;\n dataType?: EncAccountDataType;\n createdAt?: string;\n /**\n * The storage-level version from the SDK ('v1' or 'v2').\n * - 'v1': Legacy items created before dataType was introduced\n * - 'v2': Items with dataType set (either new or migrated)\n */\n storageVersion?: SecretDataItemOutput['version'];\n};\n\nexport class SecretMetadata<DataType extends SecretDataType = Uint8Array>\n implements ISecretMetadata<DataType>\n{\n readonly #data: DataType;\n\n readonly #timestamp: number;\n\n readonly #type: SecretType;\n\n // Storage-level metadata (not encrypted)\n readonly #itemId?: string;\n\n readonly #dataType?: EncAccountDataType;\n\n readonly #createdAt?: string;\n\n readonly #storageVersion?: SecretDataItemOutput['version'];\n\n /**\n * @param data - The secret data.\n * @param options - Optional metadata. New clients should provide `dataType`.\n */\n constructor(data: DataType, options?: SecretMetadataOptions) {\n this.#data = data;\n this.#timestamp = options?.timestamp ?? Date.now();\n this.#itemId = options?.itemId;\n this.#dataType = options?.dataType;\n this.#createdAt = options?.createdAt;\n this.#storageVersion = options?.storageVersion;\n\n // Derive type from dataType (new clients), or use provided type (V1 compat)\n if (options?.dataType === undefined || options?.dataType === null) {\n this.#type = options?.type ?? SecretType.Mnemonic;\n } else {\n this.#type = getSecretTypeFromDataType(options.dataType);\n }\n }\n\n /**\n * Assert that the provided value is a valid seed phrase metadata.\n *\n * @param value - The value to check.\n * @throws If the value is not a valid seed phrase metadata.\n */\n static assertIsValidSecretMetadataJson<\n DataType extends SecretDataType = Uint8Array,\n >(value: unknown): asserts value is SecretMetadataJson<DataType> {\n if (\n typeof value !== 'object' ||\n !value ||\n !('data' in value) ||\n typeof value.data !== 'string' ||\n !('timestamp' in value) ||\n typeof value.timestamp !== 'number'\n ) {\n throw new Error(\n SeedlessOnboardingControllerErrorMessage.InvalidSecretMetadata,\n );\n }\n }\n\n /**\n * Parse and create the SecretMetadata instance from the raw metadata bytes.\n *\n * @param rawMetadata - The raw metadata.\n * @param storageMetadata - Storage-level metadata from the metadata store.\n * @returns The parsed secret metadata.\n */\n static fromRawMetadata<DataType extends SecretDataType>(\n rawMetadata: Uint8Array,\n storageMetadata: Omit<SecretMetadataOptions, 'timestamp' | 'type'>,\n ): SecretMetadata<DataType> {\n const serializedMetadata = bytesToString(rawMetadata);\n const parsedMetadata = JSON.parse(serializedMetadata);\n\n SecretMetadata.assertIsValidSecretMetadataJson<DataType>(parsedMetadata);\n\n const type = parsedMetadata.type ?? SecretType.Mnemonic;\n\n let data: DataType;\n try {\n data = base64ToBytes(parsedMetadata.data) as DataType;\n } catch {\n data = parsedMetadata.data as DataType;\n }\n\n return new SecretMetadata<DataType>(data, {\n timestamp: parsedMetadata.timestamp,\n type,\n ...storageMetadata,\n });\n }\n\n /**\n * Compare two SecretMetadata instances by timestamp.\n *\n * @param a - The first SecretMetadata instance.\n * @param b - The second SecretMetadata instance.\n * @param order - The sort order. Default is 'asc'.\n * @returns A negative number if a < b, positive if a > b, zero if equal.\n */\n static compareByTimestamp<DataType extends SecretDataType = SecretDataType>(\n a: SecretMetadata<DataType>,\n b: SecretMetadata<DataType>,\n order: 'asc' | 'desc' = 'asc',\n ): number {\n return order === 'asc'\n ? a.timestamp - b.timestamp\n : b.timestamp - a.timestamp;\n }\n\n /**\n * Compare two SecretMetadata instances for ordering.\n *\n * Ordering priority:\n * 1. PrimarySrp always comes first (regardless of order direction)\n * 2. Server-side createdAt (TIMEUUID) if both have it\n * 3. Legacy items (null createdAt) are considered older\n * 4. Fall back to client-side timestamp\n *\n * @param a - The first SecretMetadata instance.\n * @param b - The second SecretMetadata instance.\n * @param order - The sort order. Default is 'asc'.\n * @returns A negative number if a < b, positive if a > b, zero if equal.\n */\n static compare<DataType extends SecretDataType = SecretDataType>(\n a: SecretMetadata<DataType>,\n b: SecretMetadata<DataType>,\n order: 'asc' | 'desc' = 'asc',\n ): number {\n // PrimarySrp always comes first (regardless of order direction)\n const aIsPrimary = a.dataType === EncAccountDataType.PrimarySrp;\n const bIsPrimary = b.dataType === EncAccountDataType.PrimarySrp;\n if (aIsPrimary && bIsPrimary) {\n return 0; // Both PrimarySrp: treat as equal (handles data corruption gracefully)\n }\n if (aIsPrimary) {\n return -1;\n }\n if (bIsPrimary) {\n return 1;\n }\n // Use server-side createdAt if available (TIMEUUID requires timestamp extraction)\n if (a.createdAt && b.createdAt) {\n return compareTimeuuid(a.createdAt, b.createdAt, order);\n }\n // Handle mixed createdAt: legacy items (null) are older\n if (!a.createdAt && b.createdAt) {\n return order === 'asc' ? -1 : 1; // a (legacy/older) comes before b in asc\n }\n if (a.createdAt && !b.createdAt) {\n return order === 'asc' ? 1 : -1; // b (legacy/older) comes before a in asc\n }\n // Both null: fall back to client-side timestamp\n return SecretMetadata.compareByTimestamp(a, b, order);\n }\n\n /**\n * Check if a SecretMetadata instance matches the given type.\n *\n * @param secret - The SecretMetadata instance to check.\n * @param type - The type to match against.\n * @returns True if the secret matches the type.\n */\n static matchesType<DataType extends SecretDataType = SecretDataType>(\n secret: SecretMetadata<DataType>,\n type: SecretType,\n ): boolean {\n return secret.type === type;\n }\n\n get data(): DataType {\n return this.#data;\n }\n\n get timestamp(): number {\n return this.#timestamp;\n }\n\n get type(): SecretType {\n return this.#type;\n }\n\n get itemId(): string | undefined {\n return this.#itemId;\n }\n\n get dataType(): EncAccountDataType | undefined {\n return this.#dataType;\n }\n\n get createdAt(): string | undefined {\n return this.#createdAt;\n }\n\n /**\n * The storage-level version from the SDK ('v1' or 'v2').\n *\n * @returns The storage-level version.\n */\n get storageVersion(): SecretDataItemOutput['version'] | undefined {\n return this.#storageVersion;\n }\n\n /**\n * Serialize the secret metadata and convert it to a Uint8Array.\n *\n * @returns The serialized SecretMetadata value in bytes.\n */\n toBytes(): Uint8Array {\n let _data: unknown = this.#data;\n if (this.#data instanceof Uint8Array) {\n // encode the raw secret to base64 encoded string\n // to create more compacted metadata\n _data = bytesToBase64(this.#data);\n }\n\n // serialize the metadata to a JSON string\n const serializedMetadata = JSON.stringify({\n data: _data,\n timestamp: this.#timestamp,\n type: this.#type,\n });\n\n // convert the serialized metadata to bytes(Uint8Array)\n return stringToBytes(serializedMetadata);\n }\n}\n"]}
@@ -1,10 +1,11 @@
1
- import { SecretType, SecretMetadataVersion } from "./constants.cjs";
2
- import type { SecretDataType, SecretMetadataOptions } from "./types.cjs";
1
+ import type { SecretDataItemOutput } from "@metamask/toprf-secure-backup";
2
+ import { EncAccountDataType } from "@metamask/toprf-secure-backup";
3
+ import { SecretType } from "./constants.cjs";
4
+ import type { SecretDataType } from "./types.cjs";
3
5
  type ISecretMetadata<DataType extends SecretDataType = Uint8Array> = {
4
6
  data: DataType;
5
7
  timestamp: number;
6
8
  type: SecretType;
7
- version: SecretMetadataVersion;
8
9
  toBytes: () => Uint8Array;
9
10
  };
10
11
  /**
@@ -25,33 +26,32 @@ type SecretMetadataJson<DataType extends SecretDataType> = Omit<ISecretMetadata<
25
26
  * const secretMetadata = new SecretMetadata(secret);
26
27
  * ```
27
28
  */
28
- export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array> implements ISecretMetadata<DataType> {
29
- #private;
29
+ /**
30
+ * Options for SecretMetadata constructor.
31
+ *
32
+ * New clients: provide V2 fields (`dataType`, `createdAt`, etc).
33
+ * Reading V1 data: `timestamp` and `type` come from encrypted JSON.
34
+ */
35
+ type SecretMetadataOptions = {
36
+ timestamp?: number;
37
+ type?: SecretType;
38
+ itemId?: string;
39
+ dataType?: EncAccountDataType;
40
+ createdAt?: string;
30
41
  /**
31
- * Create a new SecretMetadata instance.
32
- *
33
- * @param data - The secret to add metadata to.
34
- * @param options - The options for the secret metadata.
35
- * @param options.timestamp - The timestamp when the secret was created.
36
- * @param options.type - The type of the secret.
42
+ * The storage-level version from the SDK ('v1' or 'v2').
43
+ * - 'v1': Legacy items created before dataType was introduced
44
+ * - 'v2': Items with dataType set (either new or migrated)
37
45
  */
38
- constructor(data: DataType, options?: Partial<SecretMetadataOptions>);
46
+ storageVersion?: SecretDataItemOutput['version'];
47
+ };
48
+ export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array> implements ISecretMetadata<DataType> {
49
+ #private;
39
50
  /**
40
- * Create an Array of SecretMetadata instances from an array of secrets.
41
- *
42
- * To respect the order of the secrets, we add the index to the timestamp
43
- * so that the first secret backup will have the oldest timestamp
44
- * and the last secret backup will have the newest timestamp.
45
- *
46
- * @param batchData - The data to add metadata to.
47
- * @param batchData.value - The SeedPhrase/PrivateKey to add metadata to.
48
- * @param batchData.options - The options for the seed phrase metadata.
49
- * @returns The SecretMetadata instances.
51
+ * @param data - The secret data.
52
+ * @param options - Optional metadata. New clients should provide `dataType`.
50
53
  */
51
- static fromBatch<DataType extends SecretDataType = Uint8Array>(batchData: {
52
- value: DataType;
53
- options?: Partial<SecretMetadataOptions>;
54
- }[]): SecretMetadata<DataType>[];
54
+ constructor(data: DataType, options?: SecretMetadataOptions);
55
55
  /**
56
56
  * Assert that the provided value is a valid seed phrase metadata.
57
57
  *
@@ -59,36 +59,58 @@ export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array
59
59
  * @throws If the value is not a valid seed phrase metadata.
60
60
  */
61
61
  static assertIsValidSecretMetadataJson<DataType extends SecretDataType = Uint8Array>(value: unknown): asserts value is SecretMetadataJson<DataType>;
62
- /**
63
- * Parse the SecretMetadata from the metadata store and return the array of SecretMetadata instances.
64
- *
65
- * This method also sorts the secrets by timestamp in ascending order, i.e. the oldest secret will be the first element in the array.
66
- *
67
- * @param secretMetadataArr - The array of SecretMetadata from the metadata store.
68
- * @param filterType - The type of the secret to filter.
69
- * @returns The array of SecretMetadata instances.
70
- */
71
- static parseSecretsFromMetadataStore<DataType extends SecretDataType = Uint8Array>(secretMetadataArr: Uint8Array[], filterType?: SecretType): SecretMetadata<DataType>[];
72
62
  /**
73
63
  * Parse and create the SecretMetadata instance from the raw metadata bytes.
74
64
  *
75
65
  * @param rawMetadata - The raw metadata.
66
+ * @param storageMetadata - Storage-level metadata from the metadata store.
76
67
  * @returns The parsed secret metadata.
77
68
  */
78
- static fromRawMetadata<DataType extends SecretDataType>(rawMetadata: Uint8Array): SecretMetadata<DataType>;
69
+ static fromRawMetadata<DataType extends SecretDataType>(rawMetadata: Uint8Array, storageMetadata: Omit<SecretMetadataOptions, 'timestamp' | 'type'>): SecretMetadata<DataType>;
70
+ /**
71
+ * Compare two SecretMetadata instances by timestamp.
72
+ *
73
+ * @param a - The first SecretMetadata instance.
74
+ * @param b - The second SecretMetadata instance.
75
+ * @param order - The sort order. Default is 'asc'.
76
+ * @returns A negative number if a < b, positive if a > b, zero if equal.
77
+ */
78
+ static compareByTimestamp<DataType extends SecretDataType = SecretDataType>(a: SecretMetadata<DataType>, b: SecretMetadata<DataType>, order?: 'asc' | 'desc'): number;
79
79
  /**
80
- * Sort the seed phrases by timestamp.
80
+ * Compare two SecretMetadata instances for ordering.
81
81
  *
82
- * @param data - The secret metadata array to sort.
83
- * @param order - The order to sort the seed phrases. Default is `desc`.
82
+ * Ordering priority:
83
+ * 1. PrimarySrp always comes first (regardless of order direction)
84
+ * 2. Server-side createdAt (TIMEUUID) if both have it
85
+ * 3. Legacy items (null createdAt) are considered older
86
+ * 4. Fall back to client-side timestamp
84
87
  *
85
- * @returns The sorted secret metadata array.
88
+ * @param a - The first SecretMetadata instance.
89
+ * @param b - The second SecretMetadata instance.
90
+ * @param order - The sort order. Default is 'asc'.
91
+ * @returns A negative number if a < b, positive if a > b, zero if equal.
86
92
  */
87
- static sort<DataType extends SecretDataType = Uint8Array>(data: SecretMetadata<DataType>[], order?: 'asc' | 'desc'): SecretMetadata<DataType>[];
93
+ static compare<DataType extends SecretDataType = SecretDataType>(a: SecretMetadata<DataType>, b: SecretMetadata<DataType>, order?: 'asc' | 'desc'): number;
94
+ /**
95
+ * Check if a SecretMetadata instance matches the given type.
96
+ *
97
+ * @param secret - The SecretMetadata instance to check.
98
+ * @param type - The type to match against.
99
+ * @returns True if the secret matches the type.
100
+ */
101
+ static matchesType<DataType extends SecretDataType = SecretDataType>(secret: SecretMetadata<DataType>, type: SecretType): boolean;
88
102
  get data(): DataType;
89
103
  get timestamp(): number;
90
104
  get type(): SecretType;
91
- get version(): SecretMetadataVersion;
105
+ get itemId(): string | undefined;
106
+ get dataType(): EncAccountDataType | undefined;
107
+ get createdAt(): string | undefined;
108
+ /**
109
+ * The storage-level version from the SDK ('v1' or 'v2').
110
+ *
111
+ * @returns The storage-level version.
112
+ */
113
+ get storageVersion(): SecretDataItemOutput['version'] | undefined;
92
114
  /**
93
115
  * Serialize the secret metadata and convert it to a Uint8Array.
94
116
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SecretMetadata.d.cts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,UAAU,EACV,qBAAqB,EACtB,wBAAoB;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,oBAAgB;AAErE,KAAK,eAAe,CAAC,QAAQ,SAAS,cAAc,GAAG,UAAU,IAAI;IACnE,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,qBAAqB,CAAC;IAC/B,OAAO,EAAE,MAAM,UAAU,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,KAAK,kBAAkB,CAAC,QAAQ,SAAS,cAAc,IAAI,IAAI,CAC7D,eAAe,CAAC,QAAQ,CAAC,EACzB,MAAM,GAAG,SAAS,CACnB,GAAG;IACF,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,cAAc,CAAC,QAAQ,SAAS,cAAc,GAAG,UAAU,CACtE,YAAW,eAAe,CAAC,QAAQ,CAAC;;IAUpC;;;;;;;OAOG;gBACS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAOpE;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,SAAS,CAAC,QAAQ,SAAS,cAAc,GAAG,UAAU,EAC3D,SAAS,EAAE;QACT,KAAK,EAAE,QAAQ,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC;KAC1C,EAAE,GACF,cAAc,CAAC,QAAQ,CAAC,EAAE;IAc7B;;;;;OAKG;IACH,MAAM,CAAC,+BAA+B,CACpC,QAAQ,SAAS,cAAc,GAAG,UAAU,EAC5C,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,kBAAkB,CAAC,QAAQ,CAAC;IAehE;;;;;;;;OAQG;IACH,MAAM,CAAC,6BAA6B,CAClC,QAAQ,SAAS,cAAc,GAAG,UAAU,EAE5C,iBAAiB,EAAE,UAAU,EAAE,EAC/B,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,CAAC,QAAQ,CAAC,EAAE;IAc7B;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,QAAQ,SAAS,cAAc,EACpD,WAAW,EAAE,UAAU,GACtB,cAAc,CAAC,QAAQ,CAAC;IAwB3B;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CAAC,QAAQ,SAAS,cAAc,GAAG,UAAU,EACtD,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,EAChC,KAAK,GAAE,KAAK,GAAG,MAAc,GAC5B,cAAc,CAAC,QAAQ,CAAC,EAAE;IAS7B,IAAI,IAAI,IAAI,QAAQ,CAEnB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,IAAI,IAAI,UAAU,CAErB;IAED,IAAI,OAAO,IAAI,qBAAqB,CAEnC;IAED;;;;OAIG;IACH,OAAO,IAAI,UAAU;CAmBtB"}
1
+ {"version":3,"file":"SecretMetadata.d.cts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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"}