@fluidframework/container-runtime 2.51.0 → 2.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/api-report/container-runtime.legacy.alpha.api.md +1 -2
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts +15 -7
  5. package/dist/blobManager/blobManager.d.ts.map +1 -1
  6. package/dist/blobManager/blobManager.js +72 -186
  7. package/dist/blobManager/blobManager.js.map +1 -1
  8. package/dist/containerCompatibility.d.ts +34 -0
  9. package/dist/containerCompatibility.d.ts.map +1 -0
  10. package/dist/containerCompatibility.js +125 -0
  11. package/dist/containerCompatibility.js.map +1 -0
  12. package/dist/containerRuntime.d.ts +27 -15
  13. package/dist/containerRuntime.d.ts.map +1 -1
  14. package/dist/containerRuntime.js +175 -136
  15. package/dist/containerRuntime.js.map +1 -1
  16. package/dist/dataStoreContext.d.ts +6 -6
  17. package/dist/dataStoreContext.d.ts.map +1 -1
  18. package/dist/dataStoreContext.js.map +1 -1
  19. package/dist/index.d.ts +5 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/metadata.d.ts +3 -2
  23. package/dist/metadata.d.ts.map +1 -1
  24. package/dist/metadata.js +7 -1
  25. package/dist/metadata.js.map +1 -1
  26. package/dist/packageVersion.d.ts +1 -1
  27. package/dist/packageVersion.js +1 -1
  28. package/dist/packageVersion.js.map +1 -1
  29. package/dist/storageServiceWithAttachBlobs.d.ts +40 -5
  30. package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -1
  31. package/dist/storageServiceWithAttachBlobs.js +56 -5
  32. package/dist/storageServiceWithAttachBlobs.js.map +1 -1
  33. package/dist/summary/documentSchema.d.ts +1 -1
  34. package/dist/summary/documentSchema.d.ts.map +1 -1
  35. package/dist/summary/documentSchema.js.map +1 -1
  36. package/dist/summary/summaryFormat.d.ts +3 -3
  37. package/dist/summary/summaryFormat.d.ts.map +1 -1
  38. package/dist/summary/summaryFormat.js.map +1 -1
  39. package/lib/blobManager/blobManager.d.ts +15 -7
  40. package/lib/blobManager/blobManager.d.ts.map +1 -1
  41. package/lib/blobManager/blobManager.js +39 -153
  42. package/lib/blobManager/blobManager.js.map +1 -1
  43. package/lib/containerCompatibility.d.ts +34 -0
  44. package/lib/containerCompatibility.d.ts.map +1 -0
  45. package/lib/containerCompatibility.js +120 -0
  46. package/lib/containerCompatibility.js.map +1 -0
  47. package/lib/containerRuntime.d.ts +27 -15
  48. package/lib/containerRuntime.d.ts.map +1 -1
  49. package/lib/containerRuntime.js +103 -64
  50. package/lib/containerRuntime.js.map +1 -1
  51. package/lib/dataStoreContext.d.ts +6 -6
  52. package/lib/dataStoreContext.d.ts.map +1 -1
  53. package/lib/dataStoreContext.js +1 -1
  54. package/lib/dataStoreContext.js.map +1 -1
  55. package/lib/index.d.ts +5 -1
  56. package/lib/index.d.ts.map +1 -1
  57. package/lib/index.js.map +1 -1
  58. package/lib/metadata.d.ts +3 -2
  59. package/lib/metadata.d.ts.map +1 -1
  60. package/lib/metadata.js +5 -0
  61. package/lib/metadata.js.map +1 -1
  62. package/lib/packageVersion.d.ts +1 -1
  63. package/lib/packageVersion.js +1 -1
  64. package/lib/packageVersion.js.map +1 -1
  65. package/lib/storageServiceWithAttachBlobs.d.ts +40 -5
  66. package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -1
  67. package/lib/storageServiceWithAttachBlobs.js +56 -5
  68. package/lib/storageServiceWithAttachBlobs.js.map +1 -1
  69. package/lib/summary/documentSchema.d.ts +1 -1
  70. package/lib/summary/documentSchema.d.ts.map +1 -1
  71. package/lib/summary/documentSchema.js.map +1 -1
  72. package/lib/summary/summaryFormat.d.ts +3 -3
  73. package/lib/summary/summaryFormat.d.ts.map +1 -1
  74. package/lib/summary/summaryFormat.js.map +1 -1
  75. package/package.json +20 -20
  76. package/src/blobManager/blobManager.ts +53 -195
  77. package/src/containerCompatibility.ts +176 -0
  78. package/src/containerRuntime.ts +157 -122
  79. package/src/dataStoreContext.ts +13 -5
  80. package/src/index.ts +6 -1
  81. package/src/metadata.ts +10 -2
  82. package/src/packageVersion.ts +1 -1
  83. package/src/storageServiceWithAttachBlobs.ts +92 -10
  84. package/src/summary/documentSchema.ts +1 -1
  85. package/src/summary/summaryFormat.ts +2 -2
  86. package/dist/compatUtils.d.ts +0 -106
  87. package/dist/compatUtils.d.ts.map +0 -1
  88. package/dist/compatUtils.js +0 -251
  89. package/dist/compatUtils.js.map +0 -1
  90. package/lib/compatUtils.d.ts +0 -106
  91. package/lib/compatUtils.d.ts.map +0 -1
  92. package/lib/compatUtils.js +0 -242
  93. package/lib/compatUtils.js.map +0 -1
  94. package/src/compatUtils.ts +0 -365
@@ -1 +1 @@
1
- {"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../../src/summary/summaryFormat.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tIDocumentStorageService,\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { readAndParse } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tISummaryTreeWithStats,\n\tchannelsTreeName,\n\tgcTreeKey,\n} from \"@fluidframework/runtime-definitions/internal\";\n\nimport { blobsTreeName } from \"../blobManager/index.js\";\nimport { IGCMetadata } from \"../gc/index.js\";\n\nimport { IDocumentSchema } from \"./documentSchema.js\";\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport type OmitAttributesVersions<T> = Omit<\n\tT,\n\t\"snapshotFormatVersion\" | \"summaryFormatVersion\"\n>;\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes0 {\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion?: undefined;\n\tpkg: string;\n\t/**\n\t * This tells whether a data store is root. Root data stores are never collected.\n\t * Non-root data stores may be collected if they are not used. If this is not present, default it to\n\t * true. This will ensure that older data stores are incorrectly collected.\n\t */\n\treadonly isRootDataStore?: boolean;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes1\n\textends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n\treadonly snapshotFormatVersion: \"0.1\";\n\treadonly summaryFormatVersion?: undefined;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes2\n\textends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n\t/**\n\t * Switch from snapshotFormatVersion to summaryFormatVersion\n\t */\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion: 2;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t * This is required in both datastore attributes as well as the root container,\n\t * because reused summary handles may cause different format versions in each\n\t * datastore subtree within the summary.\n\t */\n\treadonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n *\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n *\n * @legacy\n * @alpha\n *\n */\nexport type ReadFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes0\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n\tif (attributes.summaryFormatVersion) {\n\t\t/**\n\t\t * Version 2+: Introduces .channels trees for isolation of\n\t\t * channel trees from data store objects.\n\t\t */\n\t\treturn attributes.summaryFormatVersion;\n\t} else if (attributes.snapshotFormatVersion === \"0.1\") {\n\t\t/**\n\t\t * Version 1: from this version the pkg within the data store\n\t\t * attributes blob is a JSON array rather than a string.\n\t\t */\n\t\treturn 1;\n\t}\n\t/**\n\t * Version 0: format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n\treturn !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\n/**\n * @internal\n */\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n\treadonly summaryFormatVersion: 1;\n\t/**\n\t * @deprecated - used by old (prior to 2.0 RC3) runtimes\n\t */\n\treadonly message?: ISummaryMetadataMessage;\n\t/**\n\t * The last message processed at the time of summary. Only primitive property types are added to the summary.\n\t */\n\treadonly lastMessage?: ISummaryMetadataMessage;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t */\n\treadonly disableIsolatedChannels?: true;\n\t/**\n\t * The summary number for a container's summary. Incremented on summaries throughout its lifetime.\n\t */\n\treadonly summaryNumber?: number;\n\t/**\n\t * GUID to identify a document in telemetry\n\t */\n\treadonly telemetryDocumentId?: string;\n\n\treadonly documentSchema?: IDocumentSchema;\n}\n\n/**\n * @internal\n */\nexport interface ICreateContainerMetadata {\n\t/**\n\t * Runtime version of the container when it was first created\n\t */\n\tcreateContainerRuntimeVersion?: string;\n\t/**\n\t * Timestamp of the container when it was first created\n\t */\n\tcreateContainerTimestamp?: number;\n}\n\n/**\n * The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary.\n * @internal\n */\nexport type ISummaryMetadataMessage = Pick<\n\tISequencedDocumentMessage,\n\t| \"clientId\"\n\t| \"clientSequenceNumber\"\n\t| \"minimumSequenceNumber\"\n\t| \"referenceSequenceNumber\"\n\t| \"sequenceNumber\"\n\t| \"timestamp\"\n\t| \"type\"\n>;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n\tmessage?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined =>\n\tmessage === undefined\n\t\t? undefined\n\t\t: {\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tclientSequenceNumber: message.clientSequenceNumber,\n\t\t\t\tminimumSequenceNumber: message.minimumSequenceNumber,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\tsequenceNumber: message.sequenceNumber,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t\ttype: message.type,\n\t\t\t};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n\t/**\n\t * Version 2+: Introduces runtime sequence number for data verification.\n\t *\n\t * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n\t * data store trees from container-level objects.\n\t * Also introduces enableGC option stored in the summary.\n\t *\n\t * Version 0: metadata blob missing; format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const recentBatchInfoBlobName = \".recentBatchInfo\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const idCompressorBlobName = \".idCompressor\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n\treturn !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [\n\tprotocolTreeName,\n\t\".logTail\",\n\t\".serviceProtocol\",\n\tblobsTreeName,\n\tgcTreeKey,\n\tidCompressorBlobName,\n];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n *\n * @param summarizeResult - Summary tree and stats to modify\n *\n * @example\n *\n * Converts from:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n *\n * And adds +1 to treeNodeCount in stats.\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n\tsummarizeResult.summary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { [channelsTreeName]: summarizeResult.summary },\n\t};\n\tsummarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n\tstorage: IDocumentStorageService,\n\tsnapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n\tconst attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n\t\tstorage,\n\t\tsnapshot.blobs[dataStoreAttributesBlobName],\n\t);\n\t// Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n\t// For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n\t// However the feature of loading a detached container from snapshot, is added when the\n\t// snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n\tconst formatVersion = getAttributesFormatVersion(attributes);\n\tassert(formatVersion > 0, 0x1d5 /* Invalid snapshot format version */);\n\treturn attributes;\n}\n\nexport { blobHeadersBlobName } from \"@fluidframework/driver-utils/internal\";\n"]}
1
+ {"version":3,"file":"summaryFormat.js","sourceRoot":"","sources":["../../src/summary/summaryFormat.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport {\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { readAndParse } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tISummaryTreeWithStats,\n\tchannelsTreeName,\n\tgcTreeKey,\n\ttype IRuntimeStorageService,\n} from \"@fluidframework/runtime-definitions/internal\";\n\nimport { blobsTreeName } from \"../blobManager/index.js\";\nimport { IGCMetadata } from \"../gc/index.js\";\n\nimport { IDocumentSchema } from \"./documentSchema.js\";\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport type OmitAttributesVersions<T> = Omit<\n\tT,\n\t\"snapshotFormatVersion\" | \"summaryFormatVersion\"\n>;\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes0 {\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion?: undefined;\n\tpkg: string;\n\t/**\n\t * This tells whether a data store is root. Root data stores are never collected.\n\t * Non-root data stores may be collected if they are not used. If this is not present, default it to\n\t * true. This will ensure that older data stores are incorrectly collected.\n\t */\n\treadonly isRootDataStore?: boolean;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes1\n\textends OmitAttributesVersions<IFluidDataStoreAttributes0> {\n\treadonly snapshotFormatVersion: \"0.1\";\n\treadonly summaryFormatVersion?: undefined;\n}\n\n/**\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n * @legacy\n * @alpha\n */\nexport interface IFluidDataStoreAttributes2\n\textends OmitAttributesVersions<IFluidDataStoreAttributes1> {\n\t/**\n\t * Switch from snapshotFormatVersion to summaryFormatVersion\n\t */\n\treadonly snapshotFormatVersion?: undefined;\n\treadonly summaryFormatVersion: 2;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t * This is required in both datastore attributes as well as the root container,\n\t * because reused summary handles may cause different format versions in each\n\t * datastore subtree within the summary.\n\t */\n\treadonly disableIsolatedChannels?: true;\n}\n/**\n * Added IFluidDataStoreAttributes similar to IChannelAttributes which will tell the attributes of a\n * store like the package, snapshotFormatVersion to take different decisions based on a particular\n * snapshotFormatVersion.\n *\n * @deprecated - This interface will no longer be exported in the future(AB#8004).\n *\n * @legacy\n * @alpha\n *\n */\nexport type ReadFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes0\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\nexport type WriteFluidDataStoreAttributes =\n\t| IFluidDataStoreAttributes1\n\t| IFluidDataStoreAttributes2;\n\nexport function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttributes): number {\n\tif (attributes.summaryFormatVersion) {\n\t\t/**\n\t\t * Version 2+: Introduces .channels trees for isolation of\n\t\t * channel trees from data store objects.\n\t\t */\n\t\treturn attributes.summaryFormatVersion;\n\t} else if (attributes.snapshotFormatVersion === \"0.1\") {\n\t\t/**\n\t\t * Version 1: from this version the pkg within the data store\n\t\t * attributes blob is a JSON array rather than a string.\n\t\t */\n\t\treturn 1;\n\t}\n\t/**\n\t * Version 0: format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn 0;\n}\n\nexport function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {\n\treturn !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;\n}\n\n/**\n * @internal\n */\n\nexport interface IContainerRuntimeMetadata extends ICreateContainerMetadata, IGCMetadata {\n\treadonly summaryFormatVersion: 1;\n\t/**\n\t * @deprecated - used by old (prior to 2.0 RC3) runtimes\n\t */\n\treadonly message?: ISummaryMetadataMessage;\n\t/**\n\t * The last message processed at the time of summary. Only primitive property types are added to the summary.\n\t */\n\treadonly lastMessage?: ISummaryMetadataMessage;\n\t/**\n\t * True if channels are not isolated in .channels subtrees, otherwise isolated.\n\t */\n\treadonly disableIsolatedChannels?: true;\n\t/**\n\t * The summary number for a container's summary. Incremented on summaries throughout its lifetime.\n\t */\n\treadonly summaryNumber?: number;\n\t/**\n\t * GUID to identify a document in telemetry\n\t */\n\treadonly telemetryDocumentId?: string;\n\n\treadonly documentSchema?: IDocumentSchema;\n}\n\n/**\n * @internal\n */\nexport interface ICreateContainerMetadata {\n\t/**\n\t * Runtime version of the container when it was first created\n\t */\n\tcreateContainerRuntimeVersion?: string;\n\t/**\n\t * Timestamp of the container when it was first created\n\t */\n\tcreateContainerTimestamp?: number;\n}\n\n/**\n * The properties of an ISequencedDocumentMessage to be stored in the metadata blob in summary.\n * @internal\n */\nexport type ISummaryMetadataMessage = Pick<\n\tISequencedDocumentMessage,\n\t| \"clientId\"\n\t| \"clientSequenceNumber\"\n\t| \"minimumSequenceNumber\"\n\t| \"referenceSequenceNumber\"\n\t| \"sequenceNumber\"\n\t| \"timestamp\"\n\t| \"type\"\n>;\n\n/**\n * Extracts the properties from an ISequencedDocumentMessage as defined by ISummaryMetadataMessage. This message is\n * added to the metadata blob in summary.\n */\nexport const extractSummaryMetadataMessage = (\n\tmessage?: ISequencedDocumentMessage,\n): ISummaryMetadataMessage | undefined =>\n\tmessage === undefined\n\t\t? undefined\n\t\t: {\n\t\t\t\tclientId: message.clientId,\n\t\t\t\tclientSequenceNumber: message.clientSequenceNumber,\n\t\t\t\tminimumSequenceNumber: message.minimumSequenceNumber,\n\t\t\t\treferenceSequenceNumber: message.referenceSequenceNumber,\n\t\t\t\tsequenceNumber: message.sequenceNumber,\n\t\t\t\ttimestamp: message.timestamp,\n\t\t\t\ttype: message.type,\n\t\t\t};\n\nexport function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {\n\t/**\n\t * Version 2+: Introduces runtime sequence number for data verification.\n\t *\n\t * Version 1+: Introduces .metadata blob and .channels trees for isolation of\n\t * data store trees from container-level objects.\n\t * Also introduces enableGC option stored in the summary.\n\t *\n\t * Version 0: metadata blob missing; format version is missing from summary.\n\t * This indicates it is an older version.\n\t */\n\treturn metadata?.summaryFormatVersion ?? 0;\n}\n\nexport const aliasBlobName = \".aliases\";\nexport const metadataBlobName = \".metadata\";\nexport const chunksBlobName = \".chunks\";\nexport const recentBatchInfoBlobName = \".recentBatchInfo\";\nexport const electedSummarizerBlobName = \".electedSummarizer\";\nexport const idCompressorBlobName = \".idCompressor\";\n\nexport function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {\n\treturn !!metadata && !metadata.disableIsolatedChannels;\n}\n\nexport const protocolTreeName = \".protocol\";\n\n/**\n * List of tree IDs at the container level which are reserved.\n * This is for older versions of summaries that do not yet have an\n * isolated data stores namespace. Without the namespace, this must\n * be used to prevent name collisions with data store IDs.\n */\nexport const nonDataStorePaths = [\n\tprotocolTreeName,\n\t\".logTail\",\n\t\".serviceProtocol\",\n\tblobsTreeName,\n\tgcTreeKey,\n\tidCompressorBlobName,\n];\n\nexport const dataStoreAttributesBlobName = \".component\";\n\n/**\n * Modifies summary tree and stats to put tree under .channels tree.\n *\n * @param summarizeResult - Summary tree and stats to modify\n *\n * @example\n *\n * Converts from:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} },\n * }\n * ```\n *\n * to:\n *\n * ```typescript\n * {\n * type: SummaryType.Tree,\n * tree: {\n * \".channels\": {\n * type: SummaryType.Tree,\n * tree: { a: {...}, b: {...}, c: {...} }\n * },\n * },\n * }\n * ```\n *\n * And adds +1 to treeNodeCount in stats.\n */\nexport function wrapSummaryInChannelsTree(summarizeResult: ISummaryTreeWithStats): void {\n\tsummarizeResult.summary = {\n\t\ttype: SummaryType.Tree,\n\t\ttree: { [channelsTreeName]: summarizeResult.summary },\n\t};\n\tsummarizeResult.stats.treeNodeCount++;\n}\n\nexport async function getFluidDataStoreAttributes(\n\tstorage: IRuntimeStorageService,\n\tsnapshot: ISnapshotTree,\n): Promise<ReadFluidDataStoreAttributes> {\n\tconst attributes = await readAndParse<ReadFluidDataStoreAttributes>(\n\t\tstorage,\n\t\tsnapshot.blobs[dataStoreAttributesBlobName],\n\t);\n\t// Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.\n\t// For snapshotFormatVersion = \"0.1\" (1) or above, pkg is jsonified, otherwise it is just a string.\n\t// However the feature of loading a detached container from snapshot, is added when the\n\t// snapshotFormatVersion is at least \"0.1\" (1), so we don't expect it to be anything else.\n\tconst formatVersion = getAttributesFormatVersion(attributes);\n\tassert(formatVersion > 0, 0x1d5 /* Invalid snapshot format version */);\n\treturn attributes;\n}\n\nexport { blobHeadersBlobName } from \"@fluidframework/driver-utils/internal\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.51.0",
3
+ "version": "2.52.0",
4
4
  "description": "Fluid container runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -119,18 +119,18 @@
119
119
  "temp-directory": "nyc/.nyc_output"
120
120
  },
121
121
  "dependencies": {
122
- "@fluid-internal/client-utils": "~2.51.0",
123
- "@fluidframework/container-definitions": "~2.51.0",
124
- "@fluidframework/container-runtime-definitions": "~2.51.0",
125
- "@fluidframework/core-interfaces": "~2.51.0",
126
- "@fluidframework/core-utils": "~2.51.0",
127
- "@fluidframework/datastore": "~2.51.0",
128
- "@fluidframework/driver-definitions": "~2.51.0",
129
- "@fluidframework/driver-utils": "~2.51.0",
130
- "@fluidframework/id-compressor": "~2.51.0",
131
- "@fluidframework/runtime-definitions": "~2.51.0",
132
- "@fluidframework/runtime-utils": "~2.51.0",
133
- "@fluidframework/telemetry-utils": "~2.51.0",
122
+ "@fluid-internal/client-utils": "~2.52.0",
123
+ "@fluidframework/container-definitions": "~2.52.0",
124
+ "@fluidframework/container-runtime-definitions": "~2.52.0",
125
+ "@fluidframework/core-interfaces": "~2.52.0",
126
+ "@fluidframework/core-utils": "~2.52.0",
127
+ "@fluidframework/datastore": "~2.52.0",
128
+ "@fluidframework/driver-definitions": "~2.52.0",
129
+ "@fluidframework/driver-utils": "~2.52.0",
130
+ "@fluidframework/id-compressor": "~2.52.0",
131
+ "@fluidframework/runtime-definitions": "~2.52.0",
132
+ "@fluidframework/runtime-utils": "~2.52.0",
133
+ "@fluidframework/telemetry-utils": "~2.52.0",
134
134
  "@tylerbu/sorted-btree-es6": "^1.8.0",
135
135
  "double-ended-queue": "^2.1.0-0",
136
136
  "lz4js": "^0.2.0",
@@ -140,16 +140,16 @@
140
140
  "devDependencies": {
141
141
  "@arethetypeswrong/cli": "^0.17.1",
142
142
  "@biomejs/biome": "~1.9.3",
143
- "@fluid-internal/mocha-test-setup": "~2.51.0",
144
- "@fluid-private/stochastic-test-utils": "~2.51.0",
145
- "@fluid-private/test-pairwise-generator": "~2.51.0",
143
+ "@fluid-internal/mocha-test-setup": "~2.52.0",
144
+ "@fluid-private/stochastic-test-utils": "~2.52.0",
145
+ "@fluid-private/test-pairwise-generator": "~2.52.0",
146
146
  "@fluid-tools/benchmark": "^0.51.0",
147
- "@fluid-tools/build-cli": "^0.56.0",
147
+ "@fluid-tools/build-cli": "^0.57.0",
148
148
  "@fluidframework/build-common": "^2.0.3",
149
- "@fluidframework/build-tools": "^0.56.0",
150
- "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.50.0",
149
+ "@fluidframework/build-tools": "^0.57.0",
150
+ "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.51.0",
151
151
  "@fluidframework/eslint-config-fluid": "^5.7.4",
152
- "@fluidframework/test-runtime-utils": "~2.51.0",
152
+ "@fluidframework/test-runtime-utils": "~2.52.0",
153
153
  "@microsoft/api-extractor": "7.52.8",
154
154
  "@types/double-ended-queue": "^2.1.0",
155
155
  "@types/lz4js": "^0.2.0",
@@ -3,8 +3,11 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { bufferToString, createEmitter, stringToBuffer } from "@fluid-internal/client-utils";
7
- import { AttachState } from "@fluidframework/container-definitions";
6
+ import { createEmitter } from "@fluid-internal/client-utils";
7
+ import {
8
+ AttachState,
9
+ type IContainerStorageService,
10
+ } from "@fluidframework/container-definitions/internal";
8
11
  import {
9
12
  IContainerRuntime,
10
13
  IContainerRuntimeEvents,
@@ -20,10 +23,7 @@ import type {
20
23
  PayloadState,
21
24
  } from "@fluidframework/core-interfaces/internal";
22
25
  import { assert, Deferred } from "@fluidframework/core-utils/internal";
23
- import {
24
- IDocumentStorageService,
25
- ICreateBlobResponse,
26
- } from "@fluidframework/driver-definitions/internal";
26
+ import { ICreateBlobResponse } from "@fluidframework/driver-definitions/internal";
27
27
  import { canRetryOnError, runWithRetry } from "@fluidframework/driver-utils/internal";
28
28
  import {
29
29
  IGarbageCollectionData,
@@ -41,12 +41,13 @@ import {
41
41
  LoggingError,
42
42
  MonitoringContext,
43
43
  PerformanceEvent,
44
+ UsageError,
44
45
  createChildMonitoringContext,
45
46
  wrapError,
46
47
  } from "@fluidframework/telemetry-utils/internal";
47
48
  import { v4 as uuid } from "uuid";
48
49
 
49
- import { IBlobMetadata } from "../metadata.js";
50
+ import { isBlobMetadata } from "../metadata.js";
50
51
 
51
52
  import {
52
53
  getStorageIds,
@@ -148,7 +149,6 @@ interface PendingBlob {
148
149
  attached?: boolean;
149
150
  acked?: boolean;
150
151
  abortSignal?: AbortSignal;
151
- stashedUpload?: boolean;
152
152
  }
153
153
 
154
154
  export interface IPendingBlobs {
@@ -171,16 +171,6 @@ interface IBlobManagerInternalEvents {
171
171
  processedBlobAttach: (localId: string, storageId: string) => void;
172
172
  }
173
173
 
174
- const stashedPendingBlobOverrides: Pick<
175
- PendingBlob,
176
- "stashedUpload" | "storageId" | "minTTLInSeconds" | "uploadTime"
177
- > = {
178
- stashedUpload: true,
179
- storageId: undefined,
180
- minTTLInSeconds: undefined,
181
- uploadTime: undefined,
182
- } as const;
183
-
184
174
  export const blobManagerBasePath = "_blobs" as const;
185
175
 
186
176
  export class BlobManager {
@@ -213,11 +203,10 @@ export class BlobManager {
213
203
  */
214
204
  private readonly opsInFlight: Map<string, Set<string>> = new Map();
215
205
 
216
- private readonly sendBlobAttachOp: (localId: string, storageId?: string) => void;
217
- private stopAttaching: boolean = false;
206
+ private readonly sendBlobAttachOp: (localId: string, storageId: string) => void;
218
207
 
219
208
  private readonly routeContext: IFluidHandleContext;
220
- private readonly storage: IDocumentStorageService;
209
+ private readonly storage: Pick<IContainerStorageService, "createBlob" | "readBlob">;
221
210
  // Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.
222
211
  // blobPath's format - `/<basePath>/<blobId>`.
223
212
  private readonly blobRequested: (blobPath: string) => void;
@@ -226,9 +215,6 @@ export class BlobManager {
226
215
  private readonly isBlobDeleted: (blobPath: string) => boolean;
227
216
  private readonly runtime: IBlobManagerRuntime;
228
217
  private readonly localBlobIdGenerator: () => string;
229
- private readonly pendingStashedBlobs: Map<string, Promise<ICreateBlobResponse | void>> =
230
- new Map();
231
- public readonly stashedBlobsUploadP: Promise<(void | ICreateBlobResponse)[]>;
232
218
 
233
219
  private readonly createBlobPayloadPending: boolean;
234
220
 
@@ -236,7 +222,7 @@ export class BlobManager {
236
222
  readonly routeContext: IFluidHandleContext;
237
223
 
238
224
  blobManagerLoadInfo: IBlobManagerLoadInfo;
239
- readonly storage: IDocumentStorageService;
225
+ readonly storage: Pick<IContainerStorageService, "createBlob" | "readBlob">;
240
226
  /**
241
227
  * Submit a BlobAttach op. When a blob is uploaded, there is a short grace period before which the blob is
242
228
  * deleted. The BlobAttach op notifies the server that blob is in use. The server will then not delete the
@@ -247,7 +233,7 @@ export class BlobManager {
247
233
  * knowledge of which they cannot request the blob from storage. It's important that this op is sequenced
248
234
  * before any ops that reference the local ID, otherwise, an invalid handle could be added to the document.
249
235
  */
250
- sendBlobAttachOp: (localId: string, storageId?: string) => void;
236
+ sendBlobAttachOp: (localId: string, storageId: string) => void;
251
237
  // Called when a blob node is requested. blobPath is the path of the blob's node in GC's graph.
252
238
  // blobPath's format - `/<basePath>/<blobId>`.
253
239
  readonly blobRequested: (blobPath: string) => void;
@@ -267,7 +253,6 @@ export class BlobManager {
267
253
  blobRequested,
268
254
  isBlobDeleted,
269
255
  runtime,
270
- stashedBlobs,
271
256
  localBlobIdGenerator,
272
257
  createBlobPayloadPending,
273
258
  } = props;
@@ -290,48 +275,7 @@ export class BlobManager {
290
275
  this.runtime.attachState,
291
276
  );
292
277
 
293
- // Begin uploading stashed blobs from previous container instance
294
- for (const [localId, entry] of Object.entries(stashedBlobs ?? {})) {
295
- const { acked, storageId, minTTLInSeconds, uploadTime } = entry;
296
- const blob = stringToBuffer(entry.blob, "base64");
297
- const pendingEntry: PendingBlob = {
298
- blob,
299
- opsent: true,
300
- handleP: new Deferred(),
301
- storageId,
302
- uploadP: undefined,
303
- uploadTime,
304
- minTTLInSeconds,
305
- attached: true,
306
- acked,
307
- };
308
- this.pendingBlobs.set(localId, pendingEntry);
309
-
310
- if (storageId !== undefined && minTTLInSeconds && uploadTime) {
311
- const timeLapseSinceLocalUpload = (Date.now() - uploadTime) / 1000;
312
- // stashed entries with more than half-life in storage will not be reuploaded
313
- if (minTTLInSeconds - timeLapseSinceLocalUpload > minTTLInSeconds / 2) {
314
- continue;
315
- }
316
- }
317
- this.pendingStashedBlobs.set(localId, this.uploadBlob(localId, blob));
318
- this.pendingBlobs.set(localId, {
319
- ...pendingEntry,
320
- ...stashedPendingBlobOverrides,
321
- uploadP: this.pendingStashedBlobs.get(localId),
322
- });
323
- }
324
-
325
- this.stashedBlobsUploadP = PerformanceEvent.timedExecAsync(
326
- this.mc.logger,
327
- { eventName: "BlobUploadProcessStashedChanges", count: this.pendingStashedBlobs.size },
328
- async () => Promise.all(this.pendingStashedBlobs.values()),
329
- { start: true, end: true },
330
- ).finally(() => {
331
- this.pendingStashedBlobs.clear();
332
- });
333
-
334
- this.sendBlobAttachOp = (localId: string, blobId?: string) => {
278
+ this.sendBlobAttachOp = (localId: string, blobId: string) => {
335
279
  const pendingEntry = this.pendingBlobs.get(localId);
336
280
  assert(
337
281
  pendingEntry !== undefined,
@@ -387,10 +331,6 @@ export class BlobManager {
387
331
  });
388
332
  }
389
333
 
390
- public hasPendingStashedUploads(): boolean {
391
- return [...this.pendingBlobs.values()].some((e) => e.stashedUpload === true);
392
- }
393
-
394
334
  public hasBlob(blobId: string): boolean {
395
335
  return this.redirectTable.get(blobId) !== undefined;
396
336
  }
@@ -420,7 +360,7 @@ export class BlobManager {
420
360
  assert(this.redirectTable.has(blobId), 0x383 /* requesting unknown blobs */);
421
361
 
422
362
  // Blobs created while the container is detached are stored in IDetachedBlobStorage.
423
- // The 'IDocumentStorageService.readBlob()' call below will retrieve these via localId.
363
+ // The 'IContainerStorageService.readBlob()' call below will retrieve these via localId.
424
364
  storageId = blobId;
425
365
  } else {
426
366
  const attachedStorageId = this.redirectTable.get(blobId);
@@ -491,7 +431,7 @@ export class BlobManager {
491
431
  blob: ArrayBufferLike,
492
432
  ): Promise<IFluidHandleInternalPayloadPending<ArrayBufferLike>> {
493
433
  // Blobs created while the container is detached are stored in IDetachedBlobStorage.
494
- // The 'IDocumentStorageService.createBlob()' call below will respond with a localId.
434
+ // The 'IContainerStorageService.createBlob()' call below will respond with a localId.
495
435
  const response = await this.storage.createBlob(blob);
496
436
  this.setRedirection(response.id, undefined);
497
437
  return this.getBlobHandle(response.id);
@@ -671,18 +611,9 @@ export class BlobManager {
671
611
  response: ICreateBlobResponseWithTTL,
672
612
  ): ICreateBlobResponseWithTTL | undefined {
673
613
  const entry = this.pendingBlobs.get(localId);
674
- if (entry === undefined && this.pendingStashedBlobs.has(localId)) {
675
- // The blob was already processed and deleted. This can happen if the blob was reuploaded by
676
- // the stashing process and the original upload was processed before the stashed upload.
677
- this.mc.logger.sendTelemetryEvent({
678
- eventName: "StashedBlobAlreadyProcessed",
679
- localId,
680
- });
681
- return;
682
- }
683
614
 
684
615
  assert(entry !== undefined, 0x6c8 /* pending blob entry not found for uploaded blob */);
685
- if ((entry.abortSignal?.aborted === true && !entry.opsent) || this.stopAttaching) {
616
+ if (entry.abortSignal?.aborted === true && !entry.opsent) {
686
617
  this.mc.logger.sendTelemetryEvent({
687
618
  eventName: "BlobAborted",
688
619
  localId,
@@ -694,7 +625,6 @@ export class BlobManager {
694
625
  entry.storageId === undefined,
695
626
  0x386 /* Must have pending blob entry for uploaded blob */,
696
627
  );
697
- entry.stashedUpload = undefined;
698
628
  entry.storageId = response.id;
699
629
  entry.uploadTime = Date.now();
700
630
  entry.minTTLInSeconds = response.minTTLInSeconds;
@@ -738,46 +668,35 @@ export class BlobManager {
738
668
  * @param metadata - op metadata containing storage and/or local IDs
739
669
  */
740
670
  public reSubmit(metadata: Record<string, unknown> | undefined): void {
741
- assert(!!metadata, 0x38b /* Resubmitted ops must have metadata */);
742
- const { localId, blobId }: { localId?: string; blobId?: string } = metadata;
743
- assert(localId !== undefined, 0x50d /* local ID not available on reSubmit */);
744
- const pendingEntry = this.pendingBlobs.get(localId);
745
-
746
- if (!blobId) {
747
- // We submitted this op while offline. The blob should have been uploaded by now.
748
- assert(
749
- pendingEntry?.opsent === true && !!pendingEntry?.storageId,
750
- 0x38d /* blob must be uploaded before resubmitting BlobAttach op */,
751
- );
752
- return this.sendBlobAttachOp(localId, pendingEntry?.storageId);
671
+ assert(isBlobMetadata(metadata), 0xc01 /* Expected blob metadata for a BlobAttach op */);
672
+ const { localId, blobId } = metadata;
673
+ // Any blob that we're actively trying to advance to attached state must have a
674
+ // pendingBlobs entry. Decline to resubmit for anything else.
675
+ // For example, we might be asked to resubmit stashed ops for blobs that never had
676
+ // their handle attached - these won't have a pendingBlobs entry and we shouldn't
677
+ // try to attach them since they won't be accessible to the customer and would just
678
+ // be considered garbage immediately.
679
+ if (this.pendingBlobs.has(localId)) {
680
+ this.sendBlobAttachOp(localId, blobId);
753
681
  }
754
- return this.sendBlobAttachOp(localId, blobId);
755
682
  }
756
683
 
757
684
  public processBlobAttachMessage(message: ISequencedMessageEnvelope, local: boolean): void {
758
- const localId = (message.metadata as IBlobMetadata | undefined)?.localId;
759
- const blobId = (message.metadata as IBlobMetadata | undefined)?.blobId;
760
-
761
- if (localId) {
762
- const pendingEntry = this.pendingBlobs.get(localId);
763
- if (pendingEntry?.abortSignal?.aborted) {
764
- this.deletePendingBlob(localId);
765
- return;
766
- }
767
- }
768
- assert(blobId !== undefined, 0x12a /* "Missing blob id on metadata" */);
769
-
770
- // Set up a mapping from local ID to storage ID. This is crucial since without this the blob cannot be
771
- // requested from the server.
772
- // Note: The check for undefined is needed for back-compat when localId was not part of the BlobAttach op that
773
- // was sent when online.
774
- if (localId !== undefined) {
775
- this.setRedirection(localId, blobId);
685
+ assert(
686
+ isBlobMetadata(message.metadata),
687
+ 0xc02 /* Expected blob metadata for a BlobAttach op */,
688
+ );
689
+ const { localId, blobId } = message.metadata;
690
+ const pendingEntry = this.pendingBlobs.get(localId);
691
+ if (pendingEntry?.abortSignal?.aborted) {
692
+ this.deletePendingBlob(localId);
693
+ return;
776
694
  }
695
+
696
+ this.setRedirection(localId, blobId);
777
697
  // set identity (id -> id) entry
778
698
  this.setRedirection(blobId, blobId);
779
699
 
780
- assert(localId !== undefined, 0x50e /* local ID not present in blob attach message */);
781
700
  if (local) {
782
701
  const waitingBlobs = this.opsInFlight.get(blobId);
783
702
  if (waitingBlobs !== undefined) {
@@ -950,6 +869,21 @@ export class BlobManager {
950
869
  }
951
870
  }
952
871
 
872
+ /**
873
+ * To be used in getPendingLocalState flow. Get a serializable record of the blobs that are
874
+ * pending upload and/or their BlobAttach op, which can be given to a new BlobManager to
875
+ * resume work.
876
+ *
877
+ * @privateRemarks
878
+ * For now, we don't track any pending blobs since the getPendingBlobs flow doesn't enable
879
+ * restoring to a state where an accessible handle has been stored by the customer (and we'll
880
+ * just drop any BlobAttach ops on the ground during reSubmit). However, once we add support
881
+ * for payload-pending handles, this will return the blobs associated with those handles.
882
+ */
883
+ public getPendingBlobs(): IPendingBlobs | undefined {
884
+ return undefined;
885
+ }
886
+
953
887
  /**
954
888
  * Part of container serialization when imminent closure is enabled (Currently when calling closeAndGetPendingLocalState).
955
889
  * This asynchronous function resolves all pending createBlob calls and waits for each blob
@@ -963,83 +897,7 @@ export class BlobManager {
963
897
  public async attachAndGetPendingBlobs(
964
898
  stopBlobAttachingSignal?: AbortSignal,
965
899
  ): Promise<IPendingBlobs | undefined> {
966
- return PerformanceEvent.timedExecAsync(
967
- this.mc.logger,
968
- { eventName: "GetPendingBlobs" },
969
- async () => {
970
- if (this.pendingBlobs.size === 0) {
971
- return;
972
- }
973
- const blobs = {};
974
- const localBlobs = new Set<PendingBlob>();
975
- // This while is used to stash blobs created while attaching and getting blobs
976
- while (localBlobs.size < this.pendingBlobs.size) {
977
- const attachHandlesP: Promise<void>[] = [];
978
- for (const [localId, entry] of this.pendingBlobs) {
979
- if (!localBlobs.has(entry)) {
980
- localBlobs.add(entry);
981
- // In order to follow natural blob creation flow we need to:
982
- // 1 send the blob attach op
983
- // 2 resolve the blob handle
984
- // 3 wait for op referencing the blob
985
- if (!entry.opsent) {
986
- this.sendBlobAttachOp(localId, entry.storageId);
987
- }
988
- // Resolving the blob handle to let hosts continue with their operations (it will resolve
989
- // original createBlob call) and let them attach the blob. This is a lie we told since the upload
990
- // hasn't finished yet, but it's fine since we will retry on rehydration.
991
- entry.handleP.resolve(this.getBlobHandle(localId));
992
- // Array of promises that will resolve when handles get attached.
993
- attachHandlesP.push(
994
- new Promise<void>((resolve, reject) => {
995
- stopBlobAttachingSignal?.addEventListener(
996
- "abort",
997
- () => {
998
- this.stopAttaching = true;
999
- reject(new Error("Operation aborted"));
1000
- },
1001
- { once: true },
1002
- );
1003
- const onHandleAttached = (attachedEntry: PendingBlob): void => {
1004
- if (attachedEntry === entry) {
1005
- this.internalEvents.off("handleAttached", onHandleAttached);
1006
- resolve();
1007
- }
1008
- };
1009
- if (entry.attached) {
1010
- resolve();
1011
- } else {
1012
- this.internalEvents.on("handleAttached", onHandleAttached);
1013
- }
1014
- }),
1015
- );
1016
- }
1017
- }
1018
- // Wait for all blobs to be attached. This is important, otherwise serialized container
1019
- // could send the blobAttach op without any op that references the blob, making it useless.
1020
- await Promise.allSettled(attachHandlesP);
1021
- }
1022
-
1023
- for (const [localId, entry] of this.pendingBlobs) {
1024
- if (stopBlobAttachingSignal?.aborted && !entry.attached) {
1025
- this.mc.logger.sendTelemetryEvent({
1026
- eventName: "UnableToStashBlob",
1027
- id: localId,
1028
- });
1029
- continue;
1030
- }
1031
- assert(entry.attached === true, 0x790 /* stashed blob should be attached */);
1032
- blobs[localId] = {
1033
- blob: bufferToString(entry.blob, "base64"),
1034
- storageId: entry.storageId,
1035
- acked: entry.acked,
1036
- minTTLInSeconds: entry.minTTLInSeconds,
1037
- uploadTime: entry.uploadTime,
1038
- };
1039
- }
1040
- return Object.keys(blobs).length > 0 ? blobs : undefined;
1041
- },
1042
- );
900
+ throw new UsageError("attachAndGetPendingBlobs is no longer supported");
1043
901
  }
1044
902
  }
1045
903