@metamask-previews/seedless-onboarding-controller 10.0.2-preview-50e82f088 → 10.0.3-preview-773e4e83d

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 CHANGED
@@ -7,10 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [10.0.3]
11
+
10
12
  ### Changed
11
13
 
12
14
  - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074))
13
15
  - Bump `@metamask/keyring-controller` from `^27.0.0` to `^27.1.0` ([#9129](https://github.com/MetaMask/core/pull/9129))
16
+ - `SecretMetadata.compare` no longer uses `createdAt` (server-stamped TIMEUUID) for ordering; sort priority is now `PrimarySrp` tag first, then client-side timestamp ([#9247](https://github.com/MetaMask/core/pull/9247))
17
+ - The password change flow passes a `transformDataItems` callback to `changeEncKey` so sorting happens inside the metadata lock, preventing data loss from concurrent writes ([#9247](https://github.com/MetaMask/core/pull/9247))
18
+
19
+ ### Fixed
20
+
21
+ - Fix `InvalidPrimarySecretDataType` crash caused by `changeEncKey` re-inserting items without sorting, causing the server to stamp `createdAt` in arbitrary order on password change ([#9247](https://github.com/MetaMask/core/pull/9247))
14
22
 
15
23
  ## [10.0.2]
16
24
 
@@ -400,7 +408,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
400
408
  - `checkIsPasswordOutdated`: Check if the password is current device is outdated, i.e. user changed password in another device.
401
409
  - `clearState`: Reset the state of the controller to the defaults.
402
410
 
403
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@10.0.2...HEAD
411
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@10.0.3...HEAD
412
+ [10.0.3]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@10.0.2...@metamask/seedless-onboarding-controller@10.0.3
404
413
  [10.0.2]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@10.0.1...@metamask/seedless-onboarding-controller@10.0.2
405
414
  [10.0.1]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@10.0.0...@metamask/seedless-onboarding-controller@10.0.1
406
415
  [10.0.0]: https://github.com/MetaMask/core/compare/@metamask/seedless-onboarding-controller@9.1.0...@metamask/seedless-onboarding-controller@10.0.0
@@ -104,9 +104,7 @@ class SecretMetadata {
104
104
  *
105
105
  * Ordering priority:
106
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
107
+ * 2. Fall back to client-side timestamp
110
108
  *
111
109
  * @param a - The first SecretMetadata instance.
112
110
  * @param b - The second SecretMetadata instance.
@@ -126,18 +124,7 @@ class SecretMetadata {
126
124
  if (bIsPrimary) {
127
125
  return 1;
128
126
  }
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
127
+ // Fall back to client-side timestamp
141
128
  return SecretMetadata.compareByTimestamp(a, b, order);
142
129
  }
143
130
  /**
@@ -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<\n DataType extends SecretDataType = Uint8Array,\n> implements ISecretMetadata<DataType> {\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 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 { 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<\n DataType extends SecretDataType = Uint8Array,\n> implements ISecretMetadata<DataType> {\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. 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 // 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"]}
@@ -81,9 +81,7 @@ export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array
81
81
  *
82
82
  * Ordering priority:
83
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
+ * 2. Fall back to client-side timestamp
87
85
  *
88
86
  * @param a - The first SecretMetadata instance.
89
87
  * @param b - The second SecretMetadata instance.
@@ -1 +1 @@
1
- {"version":3,"file":"SecretMetadata.d.cts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SecretMetadata.d.cts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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"}
@@ -81,9 +81,7 @@ export declare class SecretMetadata<DataType extends SecretDataType = Uint8Array
81
81
  *
82
82
  * Ordering priority:
83
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
+ * 2. Fall back to client-side timestamp
87
85
  *
88
86
  * @param a - The first SecretMetadata instance.
89
87
  * @param b - The second SecretMetadata instance.
@@ -1 +1 @@
1
- {"version":3,"file":"SecretMetadata.d.mts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SecretMetadata.d.mts","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ var _SecretMetadata_data, _SecretMetadata_timestamp, _SecretMetadata_type, _Secr
13
13
  import { EncAccountDataType } from "@metamask/toprf-secure-backup";
14
14
  import { base64ToBytes, bytesToBase64, stringToBytes, bytesToString } from "@metamask/utils";
15
15
  import { SeedlessOnboardingControllerErrorMessage, SecretType } from "./constants.mjs";
16
- import { compareTimeuuid, getSecretTypeFromDataType } from "./utils.mjs";
16
+ import { getSecretTypeFromDataType } from "./utils.mjs";
17
17
  export class SecretMetadata {
18
18
  /**
19
19
  * @param data - The secret data.
@@ -101,9 +101,7 @@ export class SecretMetadata {
101
101
  *
102
102
  * Ordering priority:
103
103
  * 1. PrimarySrp always comes first (regardless of order direction)
104
- * 2. Server-side createdAt (TIMEUUID) if both have it
105
- * 3. Legacy items (null createdAt) are considered older
106
- * 4. Fall back to client-side timestamp
104
+ * 2. Fall back to client-side timestamp
107
105
  *
108
106
  * @param a - The first SecretMetadata instance.
109
107
  * @param b - The second SecretMetadata instance.
@@ -123,18 +121,7 @@ export class SecretMetadata {
123
121
  if (bIsPrimary) {
124
122
  return 1;
125
123
  }
126
- // Use server-side createdAt if available (TIMEUUID requires timestamp extraction)
127
- if (a.createdAt && b.createdAt) {
128
- return compareTimeuuid(a.createdAt, b.createdAt, order);
129
- }
130
- // Handle mixed createdAt: legacy items (null) are older
131
- if (!a.createdAt && b.createdAt) {
132
- return order === 'asc' ? -1 : 1; // a (legacy/older) comes before b in asc
133
- }
134
- if (a.createdAt && !b.createdAt) {
135
- return order === 'asc' ? 1 : -1; // b (legacy/older) comes before a in asc
136
- }
137
- // Both null: fall back to client-side timestamp
124
+ // Fall back to client-side timestamp
138
125
  return SecretMetadata.compareByTimestamp(a, b, order);
139
126
  }
140
127
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"SecretMetadata.mjs","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<\n DataType extends SecretDataType = Uint8Array,\n> implements ISecretMetadata<DataType> {\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.mjs","sourceRoot":"","sources":["../src/SecretMetadata.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,kBAAkB,EAAE,sCAAsC;AACnE,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACd,wBAAwB;AAEzB,OAAO,EACL,wCAAwC,EACxC,UAAU,EACX,wBAAoB;AAErB,OAAO,EAAE,yBAAyB,EAAE,oBAAgB;AAuDpD,MAAM,OAAO,cAAc;IAkBzB;;;OAGG;IACH,YAAY,IAAc,EAAE,OAA+B;QAnBlD,uCAAgB;QAEhB,4CAAmB;QAEnB,uCAAkB;QAE3B,yCAAyC;QAChC,yCAAiB;QAEjB,2CAA+B;QAE/B,4CAAoB;QAEpB,iDAAkD;QAOzD,uBAAA,IAAI,wBAAS,IAAI,MAAA,CAAC;QAClB,uBAAA,IAAI,6BAAc,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,MAAA,CAAC;QACnD,uBAAA,IAAI,0BAAW,OAAO,EAAE,MAAM,MAAA,CAAC;QAC/B,uBAAA,IAAI,4BAAa,OAAO,EAAE,QAAQ,MAAA,CAAC;QACnC,uBAAA,IAAI,6BAAc,OAAO,EAAE,SAAS,MAAA,CAAC;QACrC,uBAAA,IAAI,kCAAmB,OAAO,EAAE,cAAc,MAAA,CAAC;QAE/C,4EAA4E;QAC5E,IAAI,OAAO,EAAE,QAAQ,KAAK,SAAS,IAAI,OAAO,EAAE,QAAQ,KAAK,IAAI,EAAE,CAAC;YAClE,uBAAA,IAAI,wBAAS,OAAO,EAAE,IAAI,IAAI,UAAU,CAAC,QAAQ,MAAA,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,uBAAA,IAAI,wBAAS,yBAAyB,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAA,CAAC;QAC3D,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,+BAA+B,CAEpC,KAAc;QACd,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,KAAK;YACN,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;YAClB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;YAC9B,CAAC,CAAC,WAAW,IAAI,KAAK,CAAC;YACvB,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,EACnC,CAAC;YACD,MAAM,IAAI,KAAK,CACb,wCAAwC,CAAC,qBAAqB,CAC/D,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,eAAe,CACpB,WAAuB,EACvB,eAAkE;QAElE,MAAM,kBAAkB,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;QACtD,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAEtD,cAAc,CAAC,+BAA+B,CAAW,cAAc,CAAC,CAAC;QAEzE,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,CAAC;QAExD,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,aAAa,CAAC,cAAc,CAAC,IAAI,CAAa,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,cAAc,CAAC,IAAgB,CAAC;QACzC,CAAC;QAED,OAAO,IAAI,cAAc,CAAW,IAAI,EAAE;YACxC,SAAS,EAAE,cAAc,CAAC,SAAS;YACnC,IAAI;YACJ,GAAG,eAAe;SACnB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,kBAAkB,CACvB,CAA2B,EAC3B,CAA2B,EAC3B,QAAwB,KAAK;QAE7B,OAAO,KAAK,KAAK,KAAK;YACpB,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS;YAC3B,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;IAChC,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,OAAO,CACZ,CAA2B,EAC3B,CAA2B,EAC3B,QAAwB,KAAK;QAE7B,gEAAgE;QAChE,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,KAAK,kBAAkB,CAAC,UAAU,CAAC;QAChE,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,KAAK,kBAAkB,CAAC,UAAU,CAAC;QAChE,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;YAC7B,OAAO,CAAC,CAAC,CAAC,uEAAuE;QACnF,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,OAAO,CAAC,CAAC;QACX,CAAC;QACD,qCAAqC;QACrC,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAChB,MAAgC,EAChC,IAAgB;QAEhB,OAAO,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC;IAC9B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,4BAAM,CAAC;IACpB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,uBAAA,IAAI,iCAAW,CAAC;IACzB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,4BAAM,CAAC;IACpB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,uBAAA,IAAI,8BAAQ,CAAC;IACtB,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,uBAAA,IAAI,gCAAU,CAAC;IACxB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,uBAAA,IAAI,iCAAW,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,IAAI,cAAc;QAChB,OAAO,uBAAA,IAAI,sCAAgB,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,OAAO;QACL,IAAI,KAAK,GAAY,uBAAA,IAAI,4BAAM,CAAC;QAChC,IAAI,uBAAA,IAAI,4BAAM,YAAY,UAAU,EAAE,CAAC;YACrC,iDAAiD;YACjD,oCAAoC;YACpC,KAAK,GAAG,aAAa,CAAC,uBAAA,IAAI,4BAAM,CAAC,CAAC;QACpC,CAAC;QAED,0CAA0C;QAC1C,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC;YACxC,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,uBAAA,IAAI,iCAAW;YAC1B,IAAI,EAAE,uBAAA,IAAI,4BAAM;SACjB,CAAC,CAAC;QAEH,uDAAuD;QACvD,OAAO,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC3C,CAAC;CACF","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 { 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<\n DataType extends SecretDataType = Uint8Array,\n> implements ISecretMetadata<DataType> {\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. 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 // 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"]}
@@ -1323,17 +1323,19 @@ async function _SeedlessOnboardingController_recoverEncKey(password) {
1323
1323
  createdAt: item.createdAt,
1324
1324
  storageVersion: item.version,
1325
1325
  }));
1326
- // Sort: PrimarySrp first, then by createdAt/timestamp (oldest first)
1326
+ // Sort: PrimarySrp first, then by client timestamp (oldest first)
1327
1327
  results.sort((a, b) => SecretMetadata_1.SecretMetadata.compare(a, b, 'asc'));
1328
- // Validate the first item is the primary SRP
1329
- const firstItem = results[0];
1330
- const isDataTypePrimary = firstItem.dataType === undefined ||
1331
- firstItem.dataType === null ||
1332
- firstItem.dataType === toprf_secure_backup_1.EncAccountDataType.PrimarySrp;
1333
- const isMnemonic = SecretMetadata_1.SecretMetadata.matchesType(firstItem, constants_1.SecretType.Mnemonic);
1334
- if (!isDataTypePrimary || !isMnemonic) {
1328
+ const primaryIndex = results.findIndex((result) => SecretMetadata_1.SecretMetadata.matchesType(result, constants_1.SecretType.Mnemonic) &&
1329
+ (result.dataType === undefined ||
1330
+ result.dataType === null ||
1331
+ result.dataType === toprf_secure_backup_1.EncAccountDataType.PrimarySrp));
1332
+ if (primaryIndex === -1) {
1335
1333
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.InvalidPrimarySecretDataType);
1336
1334
  }
1335
+ if (primaryIndex !== 0) {
1336
+ const [primary] = results.splice(primaryIndex, 1);
1337
+ results.unshift(primary);
1338
+ }
1337
1339
  return results;
1338
1340
  }
1339
1341
  throw new Error(constants_1.SeedlessOnboardingControllerErrorMessage.NoSecretDataFound);
@@ -1379,6 +1381,13 @@ async function _SeedlessOnboardingController_changeEncryptionKey({ oldPassword,
1379
1381
  oldAuthKeyPair: authKeyPair,
1380
1382
  newKeyShareIndex: globalKeyIndex,
1381
1383
  newPassword,
1384
+ transformDataItems: (items) => items
1385
+ .sort((a, b) => SecretMetadata_1.SecretMetadata.compare(SecretMetadata_1.SecretMetadata.fromRawMetadata(a.data, { dataType: a.dataType }), SecretMetadata_1.SecretMetadata.fromRawMetadata(b.data, { dataType: b.dataType }), 'asc'))
1386
+ .map(({ data, dataType, version }) => ({
1387
+ data,
1388
+ dataType,
1389
+ version: dataType === undefined ? 'v1' : version,
1390
+ })),
1382
1391
  });
1383
1392
  return result;
1384
1393
  }, _SeedlessOnboardingController_encryptAndStoreSecretData =