@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.
Files changed (52) hide show
  1. package/CHANGELOG.md +0 -18
  2. package/dist/SecretMetadata.cjs +82 -100
  3. package/dist/SecretMetadata.cjs.map +1 -1
  4. package/dist/SecretMetadata.d.cts +43 -65
  5. package/dist/SecretMetadata.d.cts.map +1 -1
  6. package/dist/SecretMetadata.d.mts +43 -65
  7. package/dist/SecretMetadata.d.mts.map +1 -1
  8. package/dist/SecretMetadata.mjs +83 -101
  9. package/dist/SecretMetadata.mjs.map +1 -1
  10. package/dist/SeedlessOnboardingController-method-action-types.cjs.map +1 -1
  11. package/dist/SeedlessOnboardingController-method-action-types.d.cts +4 -4
  12. package/dist/SeedlessOnboardingController-method-action-types.d.mts +4 -4
  13. package/dist/SeedlessOnboardingController-method-action-types.mjs.map +1 -1
  14. package/dist/SeedlessOnboardingController.cjs +24 -179
  15. package/dist/SeedlessOnboardingController.cjs.map +1 -1
  16. package/dist/SeedlessOnboardingController.d.cts +7 -30
  17. package/dist/SeedlessOnboardingController.d.cts.map +1 -1
  18. package/dist/SeedlessOnboardingController.d.mts +7 -30
  19. package/dist/SeedlessOnboardingController.d.mts.map +1 -1
  20. package/dist/SeedlessOnboardingController.mjs +26 -181
  21. package/dist/SeedlessOnboardingController.mjs.map +1 -1
  22. package/dist/constants.cjs +5 -6
  23. package/dist/constants.cjs.map +1 -1
  24. package/dist/constants.d.cts +3 -4
  25. package/dist/constants.d.cts.map +1 -1
  26. package/dist/constants.d.mts +3 -4
  27. package/dist/constants.d.mts.map +1 -1
  28. package/dist/constants.mjs +4 -5
  29. package/dist/constants.mjs.map +1 -1
  30. package/dist/index.cjs +1 -4
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +1 -2
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.mts +1 -2
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs +1 -2
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/types.cjs.map +1 -1
  39. package/dist/types.d.cts +18 -6
  40. package/dist/types.d.cts.map +1 -1
  41. package/dist/types.d.mts +18 -6
  42. package/dist/types.d.mts.map +1 -1
  43. package/dist/types.mjs.map +1 -1
  44. package/dist/utils.cjs +1 -69
  45. package/dist/utils.cjs.map +1 -1
  46. package/dist/utils.d.cts +0 -38
  47. package/dist/utils.d.cts.map +1 -1
  48. package/dist/utils.d.mts +0 -38
  49. package/dist/utils.d.mts.map +1 -1
  50. package/dist/utils.mjs +0 -65
  51. package/dist/utils.mjs.map +1 -1
  52. 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]
@@ -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, _SecretMetadata_itemId, _SecretMetadata_dataType, _SecretMetadata_createdAt, _SecretMetadata_storageVersion;
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
- const utils_2 = require("./utils.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
+ */
20
29
  class SecretMetadata {
21
30
  /**
22
- * @param data - The secret data.
23
- * @param options - Optional metadata. New clients should provide `dataType`.
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
- // 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);
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, _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
- }
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, storageMetadata) {
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
- ...storageMetadata,
129
+ version,
87
130
  });
88
131
  }
89
132
  /**
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.
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 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.
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
- * @param secret - The SecretMetadata instance to check.
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 matchesType(secret, type) {
151
- return secret.type === type;
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 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");
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(), _SecretMetadata_itemId = new WeakMap(), _SecretMetadata_dataType = new WeakMap(), _SecretMetadata_createdAt = new WeakMap(), _SecretMetadata_storageVersion = 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 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";
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
- * @param data - The secret data.
52
- * @param options - Optional metadata. New clients should provide `dataType`.
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
- constructor(data: DataType, options?: SecretMetadataOptions);
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 and create the SecretMetadata instance from the raw metadata bytes.
63
+ * Parse the SecretMetadata from the metadata store and return the array of SecretMetadata instances.
64
64
  *
65
- * @param rawMetadata - The raw metadata.
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 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.
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 compareByTimestamp<DataType extends SecretDataType = SecretDataType>(a: SecretMetadata<DataType>, b: SecretMetadata<DataType>, order?: 'asc' | 'desc'): number;
71
+ static parseSecretsFromMetadataStore<DataType extends SecretDataType = Uint8Array>(secretMetadataArr: Uint8Array[], filterType?: SecretType): SecretMetadata<DataType>[];
79
72
  /**
80
- * Compare two SecretMetadata instances for ordering.
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 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.
75
+ * @param rawMetadata - The raw metadata.
76
+ * @returns The parsed secret metadata.
92
77
  */
93
- static compare<DataType extends SecretDataType = SecretDataType>(a: SecretMetadata<DataType>, b: SecretMetadata<DataType>, order?: 'asc' | 'desc'): number;
78
+ static fromRawMetadata<DataType extends SecretDataType>(rawMetadata: Uint8Array): SecretMetadata<DataType>;
94
79
  /**
95
- * Check if a SecretMetadata instance matches the given type.
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
- * @param secret - The SecretMetadata instance to check.
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 matchesType<DataType extends SecretDataType = SecretDataType>(secret: SecretMetadata<DataType>, type: SecretType): boolean;
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 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;
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":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,sCAAsC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,sCAAsC;AAQnE,OAAO,EAEL,UAAU,EACX,wBAAoB;AACrB,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAgB;AAG9C,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,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;AAEH;;;;;GAKG;AACH,KAAK,qBAAqB,GAAG;IAE3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAGlB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;CAClD,CAAC;AAEF,qBAAa,cAAc,CAAC,QAAQ,SAAS,cAAc,GAAG,UAAU,CACtE,YAAW,eAAe,CAAC,QAAQ,CAAC;;IAiBpC;;;OAGG;gBACS,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,qBAAqB;IAgB3D;;;;;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;;;;;;OAMG;IACH,MAAM,CAAC,eAAe,CAAC,QAAQ,SAAS,cAAc,EACpD,WAAW,EAAE,UAAU,EACvB,eAAe,EAAE,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,MAAM,CAAC,GACjE,cAAc,CAAC,QAAQ,CAAC;IAsB3B;;;;;;;OAOG;IACH,MAAM,CAAC,kBAAkB,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,EACxE,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC3B,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC3B,KAAK,GAAE,KAAK,GAAG,MAAc,GAC5B,MAAM;IAMT;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,EAC7D,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC3B,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,EAC3B,KAAK,GAAE,KAAK,GAAG,MAAc,GAC5B,MAAM;IA4BT;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,QAAQ,SAAS,cAAc,GAAG,cAAc,EACjE,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,EAChC,IAAI,EAAE,UAAU,GACf,OAAO;IAIV,IAAI,IAAI,IAAI,QAAQ,CAEnB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,IAAI,IAAI,UAAU,CAErB;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,IAAI,QAAQ,IAAI,kBAAkB,GAAG,SAAS,CAE7C;IAED,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED;;;;OAIG;IACH,IAAI,cAAc,IAAI,oBAAoB,CAAC,SAAS,CAAC,GAAG,SAAS,CAEhE;IAED;;;;OAIG;IACH,OAAO,IAAI,UAAU;CAkBtB"}
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"}