@metamask-previews/seedless-onboarding-controller 9.0.0-preview-93f8bb3d0 → 9.0.0-preview-ea4ea0a
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.
- package/CHANGELOG.md +0 -18
- package/dist/SecretMetadata.cjs +82 -100
- package/dist/SecretMetadata.cjs.map +1 -1
- package/dist/SecretMetadata.d.cts +43 -65
- package/dist/SecretMetadata.d.cts.map +1 -1
- package/dist/SecretMetadata.d.mts +43 -65
- package/dist/SecretMetadata.d.mts.map +1 -1
- package/dist/SecretMetadata.mjs +83 -101
- package/dist/SecretMetadata.mjs.map +1 -1
- package/dist/SeedlessOnboardingController-method-action-types.cjs.map +1 -1
- package/dist/SeedlessOnboardingController-method-action-types.d.cts +4 -4
- package/dist/SeedlessOnboardingController-method-action-types.d.mts +4 -4
- package/dist/SeedlessOnboardingController-method-action-types.mjs.map +1 -1
- package/dist/SeedlessOnboardingController.cjs +24 -179
- package/dist/SeedlessOnboardingController.cjs.map +1 -1
- package/dist/SeedlessOnboardingController.d.cts +7 -30
- package/dist/SeedlessOnboardingController.d.cts.map +1 -1
- package/dist/SeedlessOnboardingController.d.mts +7 -30
- package/dist/SeedlessOnboardingController.d.mts.map +1 -1
- package/dist/SeedlessOnboardingController.mjs +26 -181
- package/dist/SeedlessOnboardingController.mjs.map +1 -1
- package/dist/constants.cjs +5 -6
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +3 -4
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +3 -4
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +4 -5
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +1 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -2
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +18 -6
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +18 -6
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils.cjs +1 -69
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +0 -38
- package/dist/utils.d.cts.map +1 -1
- package/dist/utils.d.mts +0 -38
- package/dist/utils.d.mts.map +1 -1
- package/dist/utils.mjs +0 -65
- package/dist/utils.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,15 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
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
12
|
- Expose all public `SeedlessOnboardingController` methods through its messenger ([#8219](https://github.com/MetaMask/core/pull/8219))
|
|
22
13
|
- The following actions are now available:
|
|
23
14
|
- `SeedlessOnboardingController:fetchMetadataAccessCreds`
|
|
@@ -68,21 +59,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
68
59
|
|
|
69
60
|
### Changed
|
|
70
61
|
|
|
71
|
-
- **BREAKING:** Change `addNewSecretData` method signature to require `dataType: EncAccountDataType` instead of `type: SecretType` ([#7284](https://github.com/MetaMask/core/pull/7284))
|
|
72
|
-
- `SecretType` is now derived internally from `EncAccountDataType`
|
|
73
|
-
- Encrypted payload still includes `type` for backward compatibility with older clients
|
|
74
|
-
- **BREAKING:** Remove `parseSecretsFromMetadataStore`, `fromBatch`, and `sort` methods from `SecretMetadata` ([#7284](https://github.com/MetaMask/core/pull/7284))
|
|
75
|
-
- Use `SecretMetadata.compare` or `SecretMetadata.compareByTimestamp` for sorting
|
|
76
|
-
- Use `SecretMetadata.matchesType` for filtering
|
|
77
|
-
- **BREAKING:** Change `SecretMetadata.fromRawMetadata` signature to require `storageMetadata` parameter ([#7284](https://github.com/MetaMask/core/pull/7284))
|
|
78
|
-
- **BREAKING:** Remove `version` getter from `SecretMetadata`; use `storageVersion` instead ([#7284](https://github.com/MetaMask/core/pull/7284))
|
|
79
62
|
- `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))
|
|
80
63
|
- `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))
|
|
81
64
|
- `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))
|
|
82
65
|
|
|
83
66
|
### Fixed
|
|
84
67
|
|
|
85
|
-
- Fix TIMEUUID sorting by extracting actual timestamps instead of using lexicographic comparison ([#7284](https://github.com/MetaMask/core/pull/7284))
|
|
86
68
|
- 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))
|
|
87
69
|
|
|
88
70
|
## [8.0.0]
|
package/dist/SecretMetadata.cjs
CHANGED
|
@@ -10,40 +10,65 @@ 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,
|
|
13
|
+
var _SecretMetadata_data, _SecretMetadata_timestamp, _SecretMetadata_type, _SecretMetadata_version;
|
|
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");
|
|
17
16
|
const utils_1 = require("@metamask/utils");
|
|
18
17
|
const constants_1 = require("./constants.cjs");
|
|
19
|
-
|
|
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
|
+
*/
|
|
20
29
|
class SecretMetadata {
|
|
21
30
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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.
|
|
24
37
|
*/
|
|
25
38
|
constructor(data, options) {
|
|
26
39
|
_SecretMetadata_data.set(this, void 0);
|
|
27
40
|
_SecretMetadata_timestamp.set(this, void 0);
|
|
28
41
|
_SecretMetadata_type.set(this, void 0);
|
|
29
|
-
|
|
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);
|
|
42
|
+
_SecretMetadata_version.set(this, void 0);
|
|
34
43
|
__classPrivateFieldSet(this, _SecretMetadata_data, data, "f");
|
|
35
44
|
__classPrivateFieldSet(this, _SecretMetadata_timestamp, options?.timestamp ?? Date.now(), "f");
|
|
36
|
-
__classPrivateFieldSet(this,
|
|
37
|
-
__classPrivateFieldSet(this,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
});
|
|
47
72
|
}
|
|
48
73
|
/**
|
|
49
74
|
* Assert that the provided value is a valid seed phrase metadata.
|
|
@@ -61,18 +86,36 @@ class SecretMetadata {
|
|
|
61
86
|
throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidSecretMetadata);
|
|
62
87
|
}
|
|
63
88
|
}
|
|
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
|
+
}
|
|
64
106
|
/**
|
|
65
107
|
* Parse and create the SecretMetadata instance from the raw metadata bytes.
|
|
66
108
|
*
|
|
67
109
|
* @param rawMetadata - The raw metadata.
|
|
68
|
-
* @param storageMetadata - Storage-level metadata from the metadata store.
|
|
69
110
|
* @returns The parsed secret metadata.
|
|
70
111
|
*/
|
|
71
|
-
static fromRawMetadata(rawMetadata
|
|
112
|
+
static fromRawMetadata(rawMetadata) {
|
|
72
113
|
const serializedMetadata = (0, utils_1.bytesToString)(rawMetadata);
|
|
73
114
|
const parsedMetadata = JSON.parse(serializedMetadata);
|
|
74
115
|
SecretMetadata.assertIsValidSecretMetadataJson(parsedMetadata);
|
|
116
|
+
// if the type is not provided, we default to Mnemonic for the backwards compatibility
|
|
75
117
|
const type = parsedMetadata.type ?? constants_1.SecretType.Mnemonic;
|
|
118
|
+
const version = parsedMetadata.version ?? constants_1.SecretMetadataVersion.V1;
|
|
76
119
|
let data;
|
|
77
120
|
try {
|
|
78
121
|
data = (0, utils_1.base64ToBytes)(parsedMetadata.data);
|
|
@@ -83,72 +126,24 @@ class SecretMetadata {
|
|
|
83
126
|
return new SecretMetadata(data, {
|
|
84
127
|
timestamp: parsedMetadata.timestamp,
|
|
85
128
|
type,
|
|
86
|
-
|
|
129
|
+
version,
|
|
87
130
|
});
|
|
88
131
|
}
|
|
89
132
|
/**
|
|
90
|
-
*
|
|
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.
|
|
104
|
-
*
|
|
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
|
|
133
|
+
* Sort the seed phrases by timestamp.
|
|
110
134
|
*
|
|
111
|
-
* @param
|
|
112
|
-
* @param
|
|
113
|
-
* @param order - The sort order. Default is 'asc'.
|
|
114
|
-
* @returns A negative number if a < b, positive if a > b, zero if equal.
|
|
115
|
-
*/
|
|
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.
|
|
135
|
+
* @param data - The secret metadata array to sort.
|
|
136
|
+
* @param order - The order to sort the seed phrases. Default is `desc`.
|
|
145
137
|
*
|
|
146
|
-
* @
|
|
147
|
-
* @param type - The type to match against.
|
|
148
|
-
* @returns True if the secret matches the type.
|
|
138
|
+
* @returns The sorted secret metadata array.
|
|
149
139
|
*/
|
|
150
|
-
static
|
|
151
|
-
return
|
|
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
|
+
});
|
|
152
147
|
}
|
|
153
148
|
get data() {
|
|
154
149
|
return __classPrivateFieldGet(this, _SecretMetadata_data, "f");
|
|
@@ -159,22 +154,8 @@ class SecretMetadata {
|
|
|
159
154
|
get type() {
|
|
160
155
|
return __classPrivateFieldGet(this, _SecretMetadata_type, "f");
|
|
161
156
|
}
|
|
162
|
-
get
|
|
163
|
-
return __classPrivateFieldGet(this,
|
|
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");
|
|
157
|
+
get version() {
|
|
158
|
+
return __classPrivateFieldGet(this, _SecretMetadata_version, "f");
|
|
178
159
|
}
|
|
179
160
|
/**
|
|
180
161
|
* Serialize the secret metadata and convert it to a Uint8Array.
|
|
@@ -193,11 +174,12 @@ class SecretMetadata {
|
|
|
193
174
|
data: _data,
|
|
194
175
|
timestamp: __classPrivateFieldGet(this, _SecretMetadata_timestamp, "f"),
|
|
195
176
|
type: __classPrivateFieldGet(this, _SecretMetadata_type, "f"),
|
|
177
|
+
version: __classPrivateFieldGet(this, _SecretMetadata_version, "f"),
|
|
196
178
|
});
|
|
197
179
|
// convert the serialized metadata to bytes(Uint8Array)
|
|
198
180
|
return (0, utils_1.stringToBytes)(serializedMetadata);
|
|
199
181
|
}
|
|
200
182
|
}
|
|
201
183
|
exports.SecretMetadata = SecretMetadata;
|
|
202
|
-
_SecretMetadata_data = new WeakMap(), _SecretMetadata_timestamp = new WeakMap(), _SecretMetadata_type = new WeakMap(),
|
|
184
|
+
_SecretMetadata_data = new WeakMap(), _SecretMetadata_timestamp = new WeakMap(), _SecretMetadata_type = new WeakMap(), _SecretMetadata_version = new WeakMap();
|
|
203
185
|
//# 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 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
|
+
{"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,11 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { SecretType } from "./constants.cjs";
|
|
4
|
-
import type { SecretDataType } from "./types.cjs";
|
|
1
|
+
import { SecretType, SecretMetadataVersion } from "./constants.cjs";
|
|
2
|
+
import type { SecretDataType, SecretMetadataOptions } from "./types.cjs";
|
|
5
3
|
type ISecretMetadata<DataType extends SecretDataType = Uint8Array> = {
|
|
6
4
|
data: DataType;
|
|
7
5
|
timestamp: number;
|
|
8
6
|
type: SecretType;
|
|
7
|
+
version: SecretMetadataVersion;
|
|
9
8
|
toBytes: () => Uint8Array;
|
|
10
9
|
};
|
|
11
10
|
/**
|
|
@@ -26,32 +25,33 @@ type SecretMetadataJson<DataType extends SecretDataType> = Omit<ISecretMetadata<
|
|
|
26
25
|
* const secretMetadata = new SecretMetadata(secret);
|
|
27
26
|
* ```
|
|
28
27
|
*/
|
|
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;
|
|
41
|
-
/**
|
|
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)
|
|
45
|
-
*/
|
|
46
|
-
storageVersion?: SecretDataItemOutput['version'];
|
|
47
|
-
};
|
|
48
28
|
export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array> implements ISecretMetadata<DataType> {
|
|
49
29
|
#private;
|
|
50
30
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
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.
|
|
37
|
+
*/
|
|
38
|
+
constructor(data: DataType, options?: Partial<SecretMetadataOptions>);
|
|
39
|
+
/**
|
|
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.
|
|
53
50
|
*/
|
|
54
|
-
|
|
51
|
+
static fromBatch<DataType extends SecretDataType = Uint8Array>(batchData: {
|
|
52
|
+
value: DataType;
|
|
53
|
+
options?: Partial<SecretMetadataOptions>;
|
|
54
|
+
}[]): SecretMetadata<DataType>[];
|
|
55
55
|
/**
|
|
56
56
|
* Assert that the provided value is a valid seed phrase metadata.
|
|
57
57
|
*
|
|
@@ -60,57 +60,35 @@ export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array
|
|
|
60
60
|
*/
|
|
61
61
|
static assertIsValidSecretMetadataJson<DataType extends SecretDataType = Uint8Array>(value: unknown): asserts value is SecretMetadataJson<DataType>;
|
|
62
62
|
/**
|
|
63
|
-
* Parse
|
|
63
|
+
* Parse the SecretMetadata from the metadata store and return the array of SecretMetadata instances.
|
|
64
64
|
*
|
|
65
|
-
*
|
|
66
|
-
* @param storageMetadata - Storage-level metadata from the metadata store.
|
|
67
|
-
* @returns The parsed secret metadata.
|
|
68
|
-
*/
|
|
69
|
-
static fromRawMetadata<DataType extends SecretDataType>(rawMetadata: Uint8Array, storageMetadata: Omit<SecretMetadataOptions, 'timestamp' | 'type'>): SecretMetadata<DataType>;
|
|
70
|
-
/**
|
|
71
|
-
* Compare two SecretMetadata instances by timestamp.
|
|
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.
|
|
72
66
|
*
|
|
73
|
-
* @param
|
|
74
|
-
* @param
|
|
75
|
-
* @
|
|
76
|
-
* @returns A negative number if a < b, positive if a > b, zero if equal.
|
|
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.
|
|
77
70
|
*/
|
|
78
|
-
static
|
|
71
|
+
static parseSecretsFromMetadataStore<DataType extends SecretDataType = Uint8Array>(secretMetadataArr: Uint8Array[], filterType?: SecretType): SecretMetadata<DataType>[];
|
|
79
72
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
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
|
|
73
|
+
* Parse and create the SecretMetadata instance from the raw metadata bytes.
|
|
87
74
|
*
|
|
88
|
-
* @param
|
|
89
|
-
* @
|
|
90
|
-
* @param order - The sort order. Default is 'asc'.
|
|
91
|
-
* @returns A negative number if a < b, positive if a > b, zero if equal.
|
|
75
|
+
* @param rawMetadata - The raw metadata.
|
|
76
|
+
* @returns The parsed secret metadata.
|
|
92
77
|
*/
|
|
93
|
-
static
|
|
78
|
+
static fromRawMetadata<DataType extends SecretDataType>(rawMetadata: Uint8Array): SecretMetadata<DataType>;
|
|
94
79
|
/**
|
|
95
|
-
*
|
|
80
|
+
* Sort the seed phrases by timestamp.
|
|
81
|
+
*
|
|
82
|
+
* @param data - The secret metadata array to sort.
|
|
83
|
+
* @param order - The order to sort the seed phrases. Default is `desc`.
|
|
96
84
|
*
|
|
97
|
-
* @
|
|
98
|
-
* @param type - The type to match against.
|
|
99
|
-
* @returns True if the secret matches the type.
|
|
85
|
+
* @returns The sorted secret metadata array.
|
|
100
86
|
*/
|
|
101
|
-
static
|
|
87
|
+
static sort<DataType extends SecretDataType = Uint8Array>(data: SecretMetadata<DataType>[], order?: 'asc' | 'desc'): SecretMetadata<DataType>[];
|
|
102
88
|
get data(): DataType;
|
|
103
89
|
get timestamp(): number;
|
|
104
90
|
get type(): SecretType;
|
|
105
|
-
get
|
|
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;
|
|
91
|
+
get version(): SecretMetadataVersion;
|
|
114
92
|
/**
|
|
115
93
|
* Serialize the secret metadata and convert it to a Uint8Array.
|
|
116
94
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretMetadata.d.cts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"
|
|
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"}
|