@tearleads/client-sdk 0.1.41 → 0.1.43

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 (235) hide show
  1. package/client/Tearleads.js +3 -0
  2. package/client/Tearleads.js.map +1 -1
  3. package/data/blobs/transferPool.js +35 -8
  4. package/data/blobs/transferPool.js.map +1 -1
  5. package/data/containers/shared/projection.js +12 -0
  6. package/data/containers/shared/projection.js.map +1 -1
  7. package/data/documents/blob/shared/types.d.ts +2 -3
  8. package/data/documents/blob/shared/types.js.map +1 -1
  9. package/data/documents/shared/events.js +0 -1
  10. package/data/documents/shared/events.js.map +1 -1
  11. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js +2 -0
  12. package/data/keyingProjectionVerification/accessManifestCheckpointEnforcement.js.map +1 -1
  13. package/data/keyingProjectionVerification/checkpointContext.d.ts +12 -0
  14. package/data/keyingProjectionVerification/checkpointContext.js +16 -0
  15. package/data/keyingProjectionVerification/checkpointContext.js.map +1 -1
  16. package/data/keyingProjectionVerification/containerDestinationVerification.js +3 -1
  17. package/data/keyingProjectionVerification/containerDestinationVerification.js.map +1 -1
  18. package/data/keyingProjectionVerification/containerProjectionVerification.js +12 -6
  19. package/data/keyingProjectionVerification/containerProjectionVerification.js.map +1 -1
  20. package/data/keyingProjectionVerification/documentProjectionVerification.js +10 -4
  21. package/data/keyingProjectionVerification/documentProjectionVerification.js.map +1 -1
  22. package/data/persistence/container-contents/containerContentsPersistence.js +6 -22
  23. package/data/persistence/container-contents/containerContentsPersistence.js.map +1 -1
  24. package/data/persistence/container-contents/containerMetadataMutationPersistence.js +5 -15
  25. package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
  26. package/data/persistence/container-contents/containerPendingUpdatePersistence.js +6 -15
  27. package/data/persistence/container-contents/containerPendingUpdatePersistence.js.map +1 -1
  28. package/data/persistence/container-contents/containerRecoveryPersistence.js +3 -4
  29. package/data/persistence/container-contents/containerRecoveryPersistence.js.map +1 -1
  30. package/data/persistence/container-contents/containerRemovalWork.js +3 -5
  31. package/data/persistence/container-contents/containerRemovalWork.js.map +1 -1
  32. package/data/persistence/container-contents/dormantContainerMetadata.js +1 -5
  33. package/data/persistence/container-contents/dormantContainerMetadata.js.map +1 -1
  34. package/data/persistence/documents/documentsPersistence.js +4 -9
  35. package/data/persistence/documents/documentsPersistence.js.map +1 -1
  36. package/data/persistence/documents/internal/discardDocument.js +1 -2
  37. package/data/persistence/documents/internal/discardDocument.js.map +1 -1
  38. package/data/persistence/documents/internal/documentDeletionPersistence.js +1 -2
  39. package/data/persistence/documents/internal/documentDeletionPersistence.js.map +1 -1
  40. package/data/persistence/documents/internal/documentMutationPersistence.js +17 -25
  41. package/data/persistence/documents/internal/documentMutationPersistence.js.map +1 -1
  42. package/data/persistence/documents/internal/orphanSideRows.js +2 -14
  43. package/data/persistence/documents/internal/orphanSideRows.js.map +1 -1
  44. package/data/persistence/documents/internal/syncQueuePersistence.js +3 -3
  45. package/data/persistence/documents/internal/syncQueuePersistence.js.map +1 -1
  46. package/data/persistence/keyingCheckpointAdvancePersistence.js +27 -8
  47. package/data/persistence/keyingCheckpointAdvancePersistence.js.map +1 -1
  48. package/data/persistence/verifiedManifestBundlePersistence.d.ts +45 -0
  49. package/data/persistence/verifiedManifestBundlePersistence.js +282 -0
  50. package/data/persistence/verifiedManifestBundlePersistence.js.map +1 -0
  51. package/data/sqlite/documentHistoryPersistence.d.ts +9 -3
  52. package/data/sqlite/documentHistoryPersistence.js +31 -8
  53. package/data/sqlite/documentHistoryPersistence.js.map +1 -1
  54. package/data/sqlite/documentPendingUpdatePersistence.d.ts +58 -11
  55. package/data/sqlite/documentPendingUpdatePersistence.js +137 -63
  56. package/data/sqlite/documentPendingUpdatePersistence.js.map +1 -1
  57. package/data/sqlite/documentPersistence.d.ts +1 -1
  58. package/data/sqlite/documentPersistence.js +1 -1
  59. package/data/sqlite/documentPersistence.js.map +1 -1
  60. package/data/sqlite/documentPersistenceTypes.d.ts +2 -2
  61. package/data/sqlite/documentPersistenceTypes.js.map +1 -1
  62. package/data/sqlite/documentRecordPersistence.js +5 -4
  63. package/data/sqlite/documentRecordPersistence.js.map +1 -1
  64. package/data/sqlite/schema.d.ts +350 -443
  65. package/data/sqlite/schema.js +49 -47
  66. package/data/sqlite/schema.js.map +1 -1
  67. package/data/sqlite/verifiedManifestBundleSchema.d.ts +174 -0
  68. package/data/sqlite/verifiedManifestBundleSchema.js +36 -0
  69. package/data/sqlite/verifiedManifestBundleSchema.js.map +1 -0
  70. package/internal/api-client/ApiClient.d.ts +17 -19
  71. package/internal/api-client/ApiClient.js +10 -11
  72. package/internal/api-client/ApiClient.js.map +1 -1
  73. package/internal/api-client/binaryRequestOperationTransport.d.ts +18 -0
  74. package/internal/api-client/index.d.ts +2 -1
  75. package/internal/api-client/index.js +1 -1
  76. package/internal/api-client/index.js.map +1 -1
  77. package/internal/api-client/operationTransportFactory.d.ts +6 -0
  78. package/internal/api-client/principalHistoryRequest.js +8 -2
  79. package/internal/api-client/principalHistoryRequest.js.map +1 -1
  80. package/internal/api-client/principalHistoryRequestContext.js +15 -5
  81. package/internal/api-client/principalHistoryRequestContext.js.map +1 -1
  82. package/internal/api-client/routes/blobs/get.d.ts +7 -0
  83. package/internal/api-client/routes/blobs/get.js.map +1 -1
  84. package/internal/api-client/routes/blobs/multipart.js +1 -11
  85. package/internal/api-client/routes/blobs/multipart.js.map +1 -1
  86. package/internal/api-client/routes/blobs/multipartUpload.d.ts +14 -0
  87. package/internal/api-client/routes/blobs/multipartUpload.js +94 -43
  88. package/internal/api-client/routes/blobs/multipartUpload.js.map +1 -1
  89. package/internal/api-client/routes/writerProjections.js +6 -3
  90. package/internal/api-client/routes/writerProjections.js.map +1 -1
  91. package/internal/api-client/verifiedManifestCitations.d.ts +23 -0
  92. package/internal/api-client/verifiedManifestCitations.js +60 -0
  93. package/internal/api-client/verifiedManifestCitations.js.map +1 -0
  94. package/internal/api-client/writerProjectionRequests.js +75 -14
  95. package/internal/api-client/writerProjectionRequests.js.map +1 -1
  96. package/internal/crypto/index.d.ts +0 -1
  97. package/internal/crypto/index.js +0 -1
  98. package/internal/crypto/index.js.map +1 -1
  99. package/internal/validators/knownManifestsRefinements.js +5 -0
  100. package/internal/validators/knownManifestsRefinements.js.map +1 -0
  101. package/internal/validators/operation/attachments.d.ts +80 -160
  102. package/internal/validators/operation/auth.d.ts +760 -1664
  103. package/internal/validators/operation/blobDownloads.js +1 -1
  104. package/internal/validators/operation/blobDownloads.js.map +1 -1
  105. package/internal/validators/operation/containerMutations.d.ts +416 -856
  106. package/internal/validators/operation/documentMutations.d.ts +120 -264
  107. package/internal/validators/operation/documentSync.d.ts +40 -80
  108. package/internal/validators/operation/index.d.ts +1 -1
  109. package/internal/validators/operation/index.js +1 -1
  110. package/internal/validators/operation/index.js.map +1 -1
  111. package/internal/validators/operation/multipartBlobs.d.ts +70 -190
  112. package/internal/validators/operation/multipartBlobs.js +2 -24
  113. package/internal/validators/operation/multipartBlobs.js.map +1 -1
  114. package/internal/validators/operation/multipartPartUploads.d.ts +5 -5
  115. package/internal/validators/operation/multipartPartUploads.js +6 -20
  116. package/internal/validators/operation/multipartPartUploads.js.map +1 -1
  117. package/internal/validators/operation/organizations.d.ts +882 -1906
  118. package/internal/validators/operation/principals.d.ts +120 -240
  119. package/internal/validators/operation/registry.d.ts +1 -2
  120. package/internal/validators/operation/registry.js +1 -2
  121. package/internal/validators/operation/registry.js.map +1 -1
  122. package/internal/validators/operation/writerProjections.d.ts +85 -319
  123. package/internal/validators/operation/writerProjections.js +11 -11
  124. package/internal/validators/operation/writerProjections.js.map +1 -1
  125. package/internal/validators/request/blob.d.ts +100 -201
  126. package/internal/validators/request/blob.js +6 -13
  127. package/internal/validators/request/blob.js.map +1 -1
  128. package/internal/validators/request/container.d.ts +136 -272
  129. package/internal/validators/request/container.js +16 -10
  130. package/internal/validators/request/container.js.map +1 -1
  131. package/internal/validators/request/containerManifestRef.d.ts +54 -0
  132. package/internal/validators/request/containerManifestRef.js +28 -0
  133. package/internal/validators/request/containerManifestRef.js.map +1 -0
  134. package/internal/validators/request/containerMetadata.d.ts +160 -368
  135. package/internal/validators/request/document.d.ts +100 -245
  136. package/internal/validators/request/document.js +1 -2
  137. package/internal/validators/request/document.js.map +1 -1
  138. package/internal/validators/request/documentSyncSchema.d.ts +42 -109
  139. package/internal/validators/request/documentSyncSchema.js +8 -25
  140. package/internal/validators/request/documentSyncSchema.js.map +1 -1
  141. package/internal/validators/request/index.d.ts +1 -1
  142. package/internal/validators/request/index.js +1 -1
  143. package/internal/validators/request/index.js.map +1 -1
  144. package/internal/validators/request/organization.d.ts +160 -320
  145. package/internal/validators/request/organizationProvisioning.d.ts +2424 -5312
  146. package/internal/validators/request/principal.d.ts +160 -320
  147. package/internal/validators/request/registration.d.ts +760 -1664
  148. package/internal/validators/response/blob.d.ts +48 -27
  149. package/internal/validators/response/blob.js +3 -10
  150. package/internal/validators/response/blob.js.map +1 -1
  151. package/internal/validators/response/container.d.ts +429 -20
  152. package/internal/validators/response/container.js +40 -5
  153. package/internal/validators/response/container.js.map +1 -1
  154. package/internal/validators/response/documentMutation.d.ts +440 -20
  155. package/internal/validators/response/documentMutation.js +33 -3
  156. package/internal/validators/response/documentMutation.js.map +1 -1
  157. package/internal/validators/response/index.d.ts +4 -3
  158. package/internal/validators/response/index.js +4 -3
  159. package/internal/validators/response/index.js.map +1 -1
  160. package/internal/validators/response/manifestBundleReferences.js +79 -0
  161. package/internal/validators/response/manifestBundleReferences.js.map +1 -0
  162. package/internal/validators/response/writerProjectionWire.d.ts +19 -0
  163. package/internal/validators/response/writerProjectionWire.js +132 -0
  164. package/internal/validators/response/writerProjectionWire.js.map +1 -0
  165. package/internal/validators/schema.js +20 -0
  166. package/internal/validators/schema.js.map +1 -1
  167. package/internal/validators/util/blob.d.ts +7 -0
  168. package/internal/validators/util/blob.js +7 -0
  169. package/internal/validators/util/blob.js.map +1 -1
  170. package/internal/validators/util/blobTransferSchemas.d.ts +41 -0
  171. package/internal/validators/util/blobTransferSchemas.js +39 -0
  172. package/internal/validators/util/blobTransferSchemas.js.map +1 -0
  173. package/internal/validators/util/containerLimits.d.ts +7 -0
  174. package/internal/validators/util/containerLimits.js +7 -0
  175. package/internal/validators/util/containerLimits.js.map +1 -1
  176. package/internal/validators/util/index.d.ts +3 -2
  177. package/internal/validators/util/index.js +3 -2
  178. package/internal/validators/util/index.js.map +1 -1
  179. package/internal/validators/util/knownManifestWire.d.ts +11 -0
  180. package/internal/validators/util/knownManifestWire.js +28 -0
  181. package/internal/validators/util/knownManifestWire.js.map +1 -0
  182. package/internal/validators/writerProjectionRefinements.js +5 -0
  183. package/internal/validators/writerProjectionRefinements.js.map +1 -1
  184. package/package.json +1 -1
  185. package/workflows/blobs/multipartFirstParts.js +82 -0
  186. package/workflows/blobs/multipartFirstParts.js.map +1 -0
  187. package/workflows/blobs/multipartStageResolution.js +83 -0
  188. package/workflows/blobs/multipartStageResolution.js.map +1 -0
  189. package/workflows/blobs/multipartUpload.js +45 -89
  190. package/workflows/blobs/multipartUpload.js.map +1 -1
  191. package/workflows/container-contents/documentPriming.js +2 -1
  192. package/workflows/container-contents/documentPriming.js.map +1 -1
  193. package/workflows/container-contents/documentQueries/sql.js +4 -4
  194. package/workflows/container-contents/documentQueries/sql.js.map +1 -1
  195. package/workflows/container-contents/pendingWrites/deferredTails.js +6 -3
  196. package/workflows/container-contents/pendingWrites/deferredTails.js.map +1 -1
  197. package/workflows/container-contents/pendingWrites/sources.js +2 -1
  198. package/workflows/container-contents/pendingWrites/sources.js.map +1 -1
  199. package/workflows/container-contents/pendingWrites.js +2 -1
  200. package/workflows/container-contents/pendingWrites.js.map +1 -1
  201. package/workflows/containers/child/create.js +3 -3
  202. package/workflows/containers/child/create.js.map +1 -1
  203. package/workflows/containers/child/move.js +2 -2
  204. package/workflows/containers/child/move.js.map +1 -1
  205. package/workflows/containers/child/mutationRequestCore.js +5 -4
  206. package/workflows/containers/child/mutationRequestCore.js.map +1 -1
  207. package/workflows/containers/child/recitePlan.js +3 -2
  208. package/workflows/containers/child/recitePlan.js.map +1 -1
  209. package/workflows/containers/child/sharePlanCore.js +2 -2
  210. package/workflows/containers/child/sharePlanCore.js.map +1 -1
  211. package/workflows/containers/root/create.js +2 -2
  212. package/workflows/containers/root/create.js.map +1 -1
  213. package/workflows/registration/provisioningBootstrapBuilders.d.ts +20 -51
  214. package/workflows/sync/remoteReset.js +11 -9
  215. package/workflows/sync/remoteReset.js.map +1 -1
  216. package/workflows/sync/remoteResetPlans.js +6 -3
  217. package/workflows/sync/remoteResetPlans.js.map +1 -1
  218. package/internal/api-client/projectionHistoryTransport.js +0 -107
  219. package/internal/api-client/projectionHistoryTransport.js.map +0 -1
  220. package/internal/api-client/verifiedProjectionHistory.d.ts +0 -4
  221. package/internal/api-client/verifiedProjectionHistory.js +0 -12
  222. package/internal/api-client/verifiedProjectionHistory.js.map +0 -1
  223. package/internal/crypto/projectionHistory.d.ts +0 -14
  224. package/internal/crypto/projectionHistory.js +0 -90
  225. package/internal/crypto/projectionHistory.js.map +0 -1
  226. package/internal/crypto/projectionHistorySlots.d.ts +0 -13
  227. package/internal/crypto/projectionHistorySlots.js +0 -81
  228. package/internal/crypto/projectionHistorySlots.js.map +0 -1
  229. package/internal/validators/operation/blobTransferSchemas.js +0 -11
  230. package/internal/validators/operation/blobTransferSchemas.js.map +0 -1
  231. package/internal/validators/projectionHistoryRefinements.js +0 -5
  232. package/internal/validators/projectionHistoryRefinements.js.map +0 -1
  233. package/internal/validators/util/projectionHistoryWire.d.ts +0 -23
  234. package/internal/validators/util/projectionHistoryWire.js +0 -33
  235. package/internal/validators/util/projectionHistoryWire.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"principalHistoryRequest.js","sourceRoot":"","sources":["principalHistoryRequest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonOperation } from \"@tearleads/validators/operation\";\nimport type { ApiRequestRuntime } from \"./apiRequestRuntime\";\nimport { decodeJsonOperationResponse } from \"./operationResponse\";\nimport { principalHistoryDeadline } from \"./principalHistoryDeadline\";\nimport { PrincipalHistoryRequestContext } from \"./principalHistoryRequestContext\";\nimport type {\n HttpMethod,\n RequestFailure,\n RequestResult,\n RequestResultOptions,\n} from \"./types\";\n\n/** Retry only an explicit, validated response proving the operation rolled back. */\nexport async function principalHistoryRequest<T>(\n runtime: ApiRequestRuntime,\n input: {\n readonly path: string;\n readonly validator: (value: unknown) => value is T;\n readonly method: HttpMethod;\n readonly body?: string;\n readonly options?: RequestResultOptions;\n readonly operation: JsonOperation;\n readonly context?: PrincipalHistoryRequestContext;\n readonly requestTimeoutMs?: number;\n },\n): Promise<RequestResult<T>> {\n const { path, method } = input;\n const context =\n input.context ??\n new PrincipalHistoryRequestContext(runtime, method, path, input.options);\n const options = context.options;\n const progress: PreparationProgress = { previous: undefined, unchanged: 0 };\n while (true) {\n if (context.cancelled()) return context.failure();\n context.beforeRequest();\n const decoded = await readResponse(runtime, context, input);\n if (!decoded.ok && context.restartReadAfterRenewal()) continue;\n if (!decoded.ok) return preserveCommitUncertainty(method, context, decoded);\n if (decoded.data.status === 202) {\n context.afterPreparation();\n const failure = await continuePreparation(decoded.data.data, progress);\n if (failure)\n return runtime.responseRequest.reportFailure({\n ...failure,\n method,\n options,\n path,\n status: 202,\n statusText: decoded.data.statusText,\n });\n continue;\n }\n if (decoded.data.status !== 200 || !input.validator(decoded.data.data)) {\n const failure = runtime.responseRequest.reportFailure({\n kind: \"shape\",\n message: `Invalid principal policy response for ${path}`,\n method,\n options:\n method === \"GET\" ? options : { ...options, reportErrors: false },\n path,\n status: decoded.data.status,\n statusText: decoded.data.statusText,\n });\n return preserveCommitUncertainty(method, context, failure);\n }\n return { ok: true, data: decoded.data.data };\n }\n}\n\nfunction preserveCommitUncertainty(\n method: HttpMethod,\n context: PrincipalHistoryRequestContext,\n failure: RequestFailure,\n): RequestFailure {\n if (method === \"GET\") return failure;\n // A network/decode failure or intermediary 5xx says nothing about commit.\n // Inner write errors are silent until this final classification is known.\n const ambiguous =\n failure.kind !== \"http\" ||\n ((failure.status ?? 0) >= 500 &&\n // The declared 503 schema requires the exact code and committed:false.\n // Unvalidated codes never reach RequestFailure.code.\n !(\n failure.status === 503 &&\n failure.code === \"principal_history_preparation_unavailable\"\n )) ||\n failure.status === 408 ||\n failure.status === 499;\n const result =\n ambiguous && failure.kind !== \"outcome-unknown\"\n ? context.failure()\n : failure;\n if (\n context.options.reportErrors !== false &&\n (result.kind === \"http\" || !context.cancelled())\n )\n result.report();\n return result;\n}\n\nasync function readResponse(\n runtime: ApiRequestRuntime,\n context: PrincipalHistoryRequestContext,\n input: {\n readonly path: string;\n readonly method: HttpMethod;\n readonly body?: string;\n readonly operation: JsonOperation;\n readonly requestTimeoutMs?: number;\n },\n) {\n const deadline = principalHistoryDeadline(\n context.options,\n // Writes await acknowledgement unless the caller supplies a deadline.\n // Aborting a committed write cannot be repaired by the continuation loop.\n input.requestTimeoutMs ?? (input.method === \"GET\" ? undefined : null),\n );\n const options =\n input.method === \"GET\"\n ? deadline.options\n : { ...deadline.options, reportErrors: false };\n try {\n const response = await runtime.responseRequest(\n input.path,\n input.method,\n input.body,\n options,\n [],\n input.operation,\n );\n // Authentication rejection precedes the workflow, so renewal does not make\n // this write's outcome uncertain. Reads may restart after a known renewal.\n if (!response.ok && response.kind === \"http\" && response.status === 401)\n return response;\n // An expired write remains uncertain even if a failure status arrived.\n if (context.cancelled() || deadline.expired())\n return context.failure(\n response.ok ? response.data : undefined,\n deadline.expired() && !context.cancelled(),\n );\n if (!response.ok) return response;\n const decoded = await decodeJsonOperationResponse(\n runtime.responseRequest,\n input.operation,\n response.data,\n input.path,\n options,\n );\n if (context.cancelled() || deadline.expired())\n return context.failure(\n response.data,\n deadline.expired() && !context.cancelled(),\n );\n if (!decoded.ok) return decoded;\n return {\n ok: true as const,\n data: { ...decoded.data, statusText: response.data.statusText },\n };\n } finally {\n deadline.dispose();\n }\n}\n\ninterface PreparationProgress {\n previous: string | undefined;\n unchanged: number;\n}\n\nasync function continuePreparation(\n data: unknown,\n progress: PreparationProgress,\n): Promise<{\n readonly kind: \"shape\" | \"http\";\n readonly message: string;\n readonly code?: string;\n} | null> {\n const token =\n typeof data === \"object\" &&\n data !== null &&\n \"progressToken\" in data &&\n typeof data.progressToken === \"string\"\n ? data.progressToken\n : undefined;\n if (token === undefined)\n return {\n kind: \"shape\",\n message: \"Principal preparation progress is missing\",\n };\n progress.unchanged = token === progress.previous ? progress.unchanged + 1 : 0;\n progress.previous = token;\n if (progress.unchanged >= 2)\n return {\n code: \"principal_history_preparation_stalled\",\n kind: \"http\",\n message: \"Principal history preparation did not advance; retry later\",\n };\n // The next round checks cancellation and identity again before any request.\n await new Promise<void>((resolve) =>\n setTimeout(resolve, progress.unchanged ? 250 : 25),\n );\n return null;\n}\n"]}
1
+ {"version":3,"file":"principalHistoryRequest.js","sourceRoot":"","sources":["principalHistoryRequest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonOperation } from \"@tearleads/validators/operation\";\nimport type { ApiRequestRuntime } from \"./apiRequestRuntime\";\nimport { decodeJsonOperationResponse } from \"./operationResponse\";\nimport { principalHistoryDeadline } from \"./principalHistoryDeadline\";\nimport { PrincipalHistoryRequestContext } from \"./principalHistoryRequestContext\";\nimport type {\n HttpMethod,\n RequestFailure,\n RequestResult,\n RequestResultOptions,\n} from \"./types\";\n\n/** Retry only an explicit, validated response proving the operation rolled back. */\nexport async function principalHistoryRequest<T>(\n runtime: ApiRequestRuntime,\n input: {\n readonly path: string;\n readonly validator: (value: unknown) => value is T;\n /** Maps a schema-valid 200 body to the value the validator checks. */\n readonly decode?: (value: unknown) => unknown;\n readonly method: HttpMethod;\n readonly body?: string;\n readonly options?: RequestResultOptions;\n readonly operation: JsonOperation;\n readonly context?: PrincipalHistoryRequestContext;\n readonly requestTimeoutMs?: number;\n },\n): Promise<RequestResult<T>> {\n const { path, method } = input;\n const context =\n input.context ??\n new PrincipalHistoryRequestContext(runtime, method, path, input.options);\n const options = context.options;\n const progress: PreparationProgress = { previous: undefined, unchanged: 0 };\n while (true) {\n if (context.cancelled()) return context.failure();\n context.beforeRequest();\n const decoded = await readResponse(runtime, context, input);\n if (!decoded.ok && context.restartReadAfterRenewal()) continue;\n if (!decoded.ok) return preserveCommitUncertainty(method, context, decoded);\n if (decoded.data.status === 202) {\n context.afterPreparation();\n const failure = await continuePreparation(decoded.data.data, progress);\n if (failure)\n return runtime.responseRequest.reportFailure({\n ...failure,\n method,\n options,\n path,\n status: 202,\n statusText: decoded.data.statusText,\n });\n continue;\n }\n const data = decodeSuccess(decoded.data, input.decode);\n if (decoded.data.status !== 200 || !input.validator(data)) {\n const failure = runtime.responseRequest.reportFailure({\n kind: \"shape\",\n message: `Invalid principal policy response for ${path}`,\n method,\n options:\n method === \"GET\" ? options : { ...options, reportErrors: false },\n path,\n status: decoded.data.status,\n statusText: decoded.data.statusText,\n });\n return preserveCommitUncertainty(method, context, failure);\n }\n return { ok: true, data };\n }\n}\n\nfunction decodeSuccess(\n response: { readonly data: unknown; readonly status: number },\n decode: ((value: unknown) => unknown) | undefined,\n): unknown {\n return response.status === 200 && decode\n ? decode(response.data)\n : response.data;\n}\n\nfunction preserveCommitUncertainty(\n method: HttpMethod,\n context: PrincipalHistoryRequestContext,\n failure: RequestFailure,\n): RequestFailure {\n if (method === \"GET\") return failure;\n // A network/decode failure or intermediary 5xx says nothing about commit.\n // Inner write errors are silent until this final classification is known.\n const ambiguous =\n failure.kind !== \"http\" ||\n ((failure.status ?? 0) >= 500 &&\n // The declared 503 schema requires the exact code and committed:false.\n // Unvalidated codes never reach RequestFailure.code.\n !(\n failure.status === 503 &&\n failure.code === \"principal_history_preparation_unavailable\"\n )) ||\n failure.status === 408 ||\n failure.status === 499;\n const result =\n ambiguous && failure.kind !== \"outcome-unknown\"\n ? context.failure()\n : failure;\n if (\n context.options.reportErrors !== false &&\n (result.kind === \"http\" || !context.cancelled())\n )\n result.report();\n return result;\n}\n\nasync function readResponse(\n runtime: ApiRequestRuntime,\n context: PrincipalHistoryRequestContext,\n input: {\n readonly path: string;\n readonly method: HttpMethod;\n readonly body?: string;\n readonly operation: JsonOperation;\n readonly requestTimeoutMs?: number;\n },\n) {\n const deadline = principalHistoryDeadline(\n context.options,\n // Writes await acknowledgement unless the caller supplies a deadline.\n // Aborting a committed write cannot be repaired by the continuation loop.\n input.requestTimeoutMs ?? (input.method === \"GET\" ? undefined : null),\n );\n const options =\n input.method === \"GET\"\n ? deadline.options\n : { ...deadline.options, reportErrors: false };\n try {\n const response = await runtime.responseRequest(\n input.path,\n input.method,\n input.body,\n options,\n [],\n input.operation,\n );\n // Authentication rejection precedes the workflow, so renewal does not make\n // this write's outcome uncertain. Reads may restart after a known renewal.\n if (!response.ok && response.kind === \"http\" && response.status === 401)\n return response;\n // An expired write remains uncertain even if a failure status arrived.\n if (context.cancelled() || deadline.expired())\n return context.failure(\n response.ok ? response.data : undefined,\n deadline.expired() && !context.cancelled(),\n );\n if (!response.ok) return response;\n const decoded = await decodeJsonOperationResponse(\n runtime.responseRequest,\n input.operation,\n response.data,\n input.path,\n options,\n );\n if (context.cancelled() || deadline.expired())\n return context.failure(\n response.data,\n deadline.expired() && !context.cancelled(),\n );\n if (!decoded.ok) return decoded;\n return {\n ok: true as const,\n data: { ...decoded.data, statusText: response.data.statusText },\n };\n } finally {\n deadline.dispose();\n }\n}\n\ninterface PreparationProgress {\n previous: string | undefined;\n unchanged: number;\n}\n\nasync function continuePreparation(\n data: unknown,\n progress: PreparationProgress,\n): Promise<{\n readonly kind: \"shape\" | \"http\";\n readonly message: string;\n readonly code?: string;\n} | null> {\n const token =\n typeof data === \"object\" &&\n data !== null &&\n \"progressToken\" in data &&\n typeof data.progressToken === \"string\"\n ? data.progressToken\n : undefined;\n if (token === undefined)\n return {\n kind: \"shape\",\n message: \"Principal preparation progress is missing\",\n };\n progress.unchanged = token === progress.previous ? progress.unchanged + 1 : 0;\n progress.previous = token;\n if (progress.unchanged >= 2)\n return {\n code: \"principal_history_preparation_stalled\",\n kind: \"http\",\n message: \"Principal history preparation did not advance; retry later\",\n };\n // The next round checks cancellation and identity again before any request.\n await new Promise<void>((resolve) =>\n setTimeout(resolve, progress.unchanged ? 250 : 25),\n );\n return null;\n}\n"]}
@@ -3,30 +3,40 @@ export class PrincipalHistoryRequestContext {
3
3
  runtime;
4
4
  method;
5
5
  path;
6
+ identity;
6
7
  options;
7
- token;
8
8
  submitted = false;
9
9
  renewedRead = false;
10
10
  restartedRead = false;
11
- constructor(runtime, method, path, options) {
11
+ constructor(runtime, method, path, options,
12
+ // Requests of one operation share the identity they started under.
13
+ identity = { token: runtime.getAuthToken() }) {
12
14
  this.runtime = runtime;
13
15
  this.method = method;
14
16
  this.path = path;
15
- this.token = runtime.getAuthToken();
17
+ this.identity = identity;
16
18
  this.options = {
17
19
  ...options,
18
20
  retryOnSessionExpired: options?.retryOnSessionExpired === false ? false : "renew-only",
19
21
  onSessionRenewed: () => {
20
22
  if (method === "GET" && !options?.signal?.aborted) {
21
- this.token = runtime.getAuthToken();
23
+ this.identity.token = runtime.getAuthToken();
22
24
  this.renewedRead = true;
23
25
  }
24
26
  },
25
27
  };
26
28
  }
29
+ /**
30
+ * Another request of the same operation, with its own options: an account
31
+ * switch at any point cancels both, while a session renewal in either
32
+ * carries over.
33
+ */
34
+ derive(options) {
35
+ return new PrincipalHistoryRequestContext(this.runtime, this.method, this.path, options, this.identity);
36
+ }
27
37
  cancelled() {
28
38
  return (this.options.signal?.aborted === true ||
29
- this.runtime.getAuthToken() !== this.token);
39
+ this.runtime.getAuthToken() !== this.identity.token);
30
40
  }
31
41
  beforeRequest() {
32
42
  this.submitted = this.method !== "GET";
@@ -1 +1 @@
1
- {"version":3,"file":"principalHistoryRequestContext.js","sourceRoot":"","sources":["principalHistoryRequestContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiRequestRuntime } from \"./apiRequestRuntime\";\nimport type { HttpMethod, RequestFailure, RequestResultOptions } from \"./types\";\n\n/** Keep continuations within their authentication context without hiding commit uncertainty. */\nexport class PrincipalHistoryRequestContext {\n readonly options: RequestResultOptions;\n private token: string | null;\n private submitted = false;\n private renewedRead = false;\n private restartedRead = false;\n\n constructor(\n private readonly runtime: ApiRequestRuntime,\n private readonly method: HttpMethod,\n private readonly path: string,\n options: RequestResultOptions | undefined,\n ) {\n this.token = runtime.getAuthToken();\n this.options = {\n ...options,\n retryOnSessionExpired:\n options?.retryOnSessionExpired === false ? false : \"renew-only\",\n onSessionRenewed: () => {\n if (method === \"GET\" && !options?.signal?.aborted) {\n this.token = runtime.getAuthToken();\n this.renewedRead = true;\n }\n },\n };\n }\n\n cancelled(): boolean {\n return (\n this.options.signal?.aborted === true ||\n this.runtime.getAuthToken() !== this.token\n );\n }\n\n beforeRequest(): void {\n this.submitted = this.method !== \"GET\";\n }\n\n afterPreparation(): void {\n this.submitted = false;\n }\n\n afterReadPage(): void {\n // A completed page is forward progress. A later page may renew again,\n // while repeated 401s within one page still cannot loop indefinitely.\n this.renewedRead = false;\n this.restartedRead = false;\n }\n\n restartReadAfterRenewal(): boolean {\n if (!this.renewedRead || this.restartedRead || this.cancelled())\n return false;\n this.restartedRead = true;\n return true;\n }\n\n failure(response?: Response, timedOut = false): RequestFailure {\n void response?.body?.cancel().catch(() => {});\n return this.runtime.responseRequest.reportFailure({\n code: this.submitted\n ? \"principal_history_outcome_unknown\"\n : timedOut\n ? \"principal_history_request_timed_out\"\n : \"principal_history_context_changed\",\n kind: this.submitted\n ? \"outcome-unknown\"\n : timedOut\n ? \"network\"\n : \"cancelled\",\n message: this.submitted\n ? \"Policy request may have committed; refresh before retrying\"\n : timedOut\n ? \"Principal history request timed out\"\n : \"Principal history request was cancelled\",\n method: this.method,\n options: {\n ...this.options,\n reportErrors:\n timedOut &&\n this.method === \"GET\" &&\n this.options.reportErrors !== false,\n },\n path: this.path,\n status: null,\n statusText: \"\",\n });\n }\n}\n"]}
1
+ {"version":3,"file":"principalHistoryRequestContext.js","sourceRoot":"","sources":["principalHistoryRequestContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiRequestRuntime } from \"./apiRequestRuntime\";\nimport type { HttpMethod, RequestFailure, RequestResultOptions } from \"./types\";\n\n/** Keep continuations within their authentication context without hiding commit uncertainty. */\nexport class PrincipalHistoryRequestContext {\n readonly options: RequestResultOptions;\n private submitted = false;\n private renewedRead = false;\n private restartedRead = false;\n\n constructor(\n private readonly runtime: ApiRequestRuntime,\n private readonly method: HttpMethod,\n private readonly path: string,\n options: RequestResultOptions | undefined,\n // Requests of one operation share the identity they started under.\n private readonly identity = { token: runtime.getAuthToken() },\n ) {\n this.options = {\n ...options,\n retryOnSessionExpired:\n options?.retryOnSessionExpired === false ? false : \"renew-only\",\n onSessionRenewed: () => {\n if (method === \"GET\" && !options?.signal?.aborted) {\n this.identity.token = runtime.getAuthToken();\n this.renewedRead = true;\n }\n },\n };\n }\n\n /**\n * Another request of the same operation, with its own options: an account\n * switch at any point cancels both, while a session renewal in either\n * carries over.\n */\n derive(options: RequestResultOptions): PrincipalHistoryRequestContext {\n return new PrincipalHistoryRequestContext(\n this.runtime,\n this.method,\n this.path,\n options,\n this.identity,\n );\n }\n\n cancelled(): boolean {\n return (\n this.options.signal?.aborted === true ||\n this.runtime.getAuthToken() !== this.identity.token\n );\n }\n\n beforeRequest(): void {\n this.submitted = this.method !== \"GET\";\n }\n\n afterPreparation(): void {\n this.submitted = false;\n }\n\n afterReadPage(): void {\n // A completed page is forward progress. A later page may renew again,\n // while repeated 401s within one page still cannot loop indefinitely.\n this.renewedRead = false;\n this.restartedRead = false;\n }\n\n restartReadAfterRenewal(): boolean {\n if (!this.renewedRead || this.restartedRead || this.cancelled())\n return false;\n this.restartedRead = true;\n return true;\n }\n\n failure(response?: Response, timedOut = false): RequestFailure {\n void response?.body?.cancel().catch(() => {});\n return this.runtime.responseRequest.reportFailure({\n code: this.submitted\n ? \"principal_history_outcome_unknown\"\n : timedOut\n ? \"principal_history_request_timed_out\"\n : \"principal_history_context_changed\",\n kind: this.submitted\n ? \"outcome-unknown\"\n : timedOut\n ? \"network\"\n : \"cancelled\",\n message: this.submitted\n ? \"Policy request may have committed; refresh before retrying\"\n : timedOut\n ? \"Principal history request timed out\"\n : \"Principal history request was cancelled\",\n method: this.method,\n options: {\n ...this.options,\n reportErrors:\n timedOut &&\n this.method === \"GET\" &&\n this.options.reportErrors !== false,\n },\n path: this.path,\n status: null,\n statusText: \"\",\n });\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { MultipartBlobPartUploadSource } from "../../../validators/util/index.js";
1
2
  import type { BinaryResponseOperationTransport } from "../../binaryResponseOperationTransport.js";
2
3
  import type { JsonOperationTransport } from "../../operationTransport.js";
3
4
  import type { OperationResponseRequestFn, RequestResultOptions } from "../../types.js";
@@ -11,6 +12,12 @@ export interface UploadMultipartBlobPartBytesRequest {
11
12
  readonly byteLength: number;
12
13
  readonly encryptedBytes: Blob | BufferSource;
13
14
  readonly sha256: string;
15
+ /**
16
+ * An upload source the stage initiation already issued for this part. The
17
+ * first attempt uses it instead of authorizing; a failed attempt still
18
+ * reauthorizes once, exactly as when no source was issued.
19
+ */
20
+ readonly source?: MultipartBlobPartUploadSource | undefined;
14
21
  readonly uploadId: string;
15
22
  }
16
23
  type BlobDownloadTransport = Pick<JsonOperationTransport, "requestResult"> & Pick<BinaryResponseOperationTransport, "requestBinaryResponse"> & Pick<OperationResponseRequestFn, "reportFailure">;
@@ -1 +1 @@
1
- {"version":3,"file":"get.js","sourceRoot":"","sources":["get.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAOzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAsB5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAgC,EAChC,MAAc,EACd,gBAA+B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,EAC9D,UAAgC,EAAE;IAElC,wEAAwE;IACxE,4EAA4E;IAC5E,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,aAAa,CAC1C,wBAAwB,EACxB,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EACtB,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CACpC,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACpD,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;QAC/B,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACjC,SAAS,CAAC,aAAa,CAAC;gBACtB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,UAAU,MAAM,WAAW;gBACjC,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,QAAQ,MAAM,6CAA6C;aACrE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACrC,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC;YACH,uEAAuE;YACvE,qEAAqE;YACrE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAClC,UAAU,CAAC,MAAM,CAAC,GAAG,EACrB,mBAAmB,CAAC,OAAO,CAAC,CAC7B,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC9C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC5B,SAAS;gBACX,CAAC;gBACD,MAAM;YACR,CAAC;YACD,OAAO,MAAM,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,yEAAyE;QAC3E,CAAC;IACH,CAAC;IACD,yEAAyE;IACzE,wEAAwE;IACxE,mEAAmE;IACnE,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,UAAuE,EACvE,QAAkB;IAElB,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,cAAc,EAAE,QAAQ,CAAC,IAAI;YAC3B,CAAC,CAAC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC;YACzC,CAAC,CAAC,0BAA0B,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,MAAkC;IAElC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,0EAA0E;IAC1E,wEAAwE;IACxE,OAAO,IAAI,cAAc,CACvB;QACE,KAAK,CAAC,IAAI,CAAC,UAAU;YACnB,IAAI,CAAC;gBACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,MAAM,CAAC,WAAW,EAAE,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACzD,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,MAAM;YACjB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;KACF,EACD,EAAE,aAAa,EAAE,CAAC,EAAE,CACrB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,SAA0E,EAC1E,MAAc,EACd,OAA6B;IAE7B,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,qBAAqB,CAClD,qBAAqB,EACrB;QACE,MAAM,EAAE,EAAE,MAAM,EAAE;KACnB,EACD,OAAO,CACR,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACrC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAC;IAEtE,iEAAiE;IACjE,uEAAuE;IACvE,iFAAiF;IACjF,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,cAAc,GAClB,QAAQ,CAAC,IAAI,IAAI,0BAA0B,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAE5E,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC1C,UAAU;QACV,cAAc;QACd,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,0DAA0D;AAC1D,yEAAyE;AACzE,yCAAyC;AACzC,SAAS,0BAA0B,CACjC,MAAmB;IAEnB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,IAAI,cAAc,CAAa;QACpC,KAAK,CAAC,UAAU;YACd,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;gBACzB,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;YACD,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n blobWireHeaderKeys,\n getBlobBytesOperation,\n getBlobDownloadOperation,\n} from \"@tearleads/validators/operation\";\nimport type { BinaryResponseOperationTransport } from \"../../binaryResponseOperationTransport\";\nimport type { JsonOperationTransport } from \"../../operationTransport\";\nimport type {\n OperationResponseRequestFn,\n RequestResultOptions,\n} from \"../../types\";\nimport { storageFetchOptions } from \"./storageFetchOptions\";\n\nexport interface BlobBytesResponse {\n readonly blobId: string;\n readonly byteLength: number;\n readonly encryptedBytes: ReadableStream<Uint8Array>;\n readonly sha256: string;\n}\n\nexport interface UploadMultipartBlobPartBytesRequest {\n readonly byteLength: number;\n readonly encryptedBytes: Blob | BufferSource;\n readonly sha256: string;\n readonly uploadId: string;\n}\n\ntype BlobDownloadTransport = Pick<JsonOperationTransport, \"requestResult\"> &\n Pick<BinaryResponseOperationTransport, \"requestBinaryResponse\"> &\n Pick<OperationResponseRequestFn, \"reportFailure\">;\n\ntype DownloadFetch = (url: string, init: RequestInit) => Promise<Response>;\n\nexport async function getBlobBytes(\n transport: BlobDownloadTransport,\n blobId: string,\n downloadFetch: DownloadFetch = (url, init) => fetch(url, init),\n options: RequestResultOptions = {},\n): Promise<BlobBytesResponse | null> {\n // Refresh at most once. R2 expiration errors can appear as CORS/network\n // failures in browsers, so both rejected fetches and 403s need a fresh URL.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (options.signal?.aborted) return null;\n const result = await transport.requestResult(\n getBlobDownloadOperation,\n { params: { blobId } },\n { ...options, reportErrors: false },\n );\n if (!result.ok) {\n if (result.kind === \"http\" && result.status === 404) {\n return getBlobStream(transport, blobId, options);\n }\n result.report();\n return null;\n }\n const descriptor = result.data;\n if (descriptor.blobId !== blobId) {\n transport.reportFailure({\n kind: \"shape\",\n method: \"GET\",\n path: `/blobs/${blobId}/download`,\n status: 200,\n statusText: \"\",\n message: `Blob ${blobId} download descriptor has the wrong identity`,\n });\n return null;\n }\n if (descriptor.source.kind === \"api\") {\n return getBlobStream(transport, blobId, options);\n }\n try {\n // Never send the application's session or cookies to the object store.\n // Reject redirects and keep bearer URLs out of referrers and errors.\n const response = await downloadFetch(\n descriptor.source.url,\n storageFetchOptions(options),\n );\n if (response.status !== 200) {\n await response.body?.cancel().catch(() => {});\n if (response.status === 403) {\n continue;\n }\n break;\n }\n return await signedBlobResponse(descriptor, response);\n } catch {\n // The downstream SDK still verifies length, hash and authenticated\n // encryption. An error after streaming begins propagates to that reader.\n }\n }\n // Preserve downloads when storage is unreachable or a native HTTP bridge\n // cannot use the signed URL. This endpoint checks current access again.\n // A storage failure alone must not signal that the API is offline.\n return getBlobStream(transport, blobId, options);\n}\n\nasync function signedBlobResponse(\n descriptor: Pick<BlobBytesResponse, \"blobId\" | \"byteLength\" | \"sha256\">,\n response: Response,\n): Promise<BlobBytesResponse> {\n return {\n blobId: descriptor.blobId,\n byteLength: descriptor.byteLength,\n sha256: descriptor.sha256,\n encryptedBytes: response.body\n ? signedDownloadBodyStream(response.body)\n : bufferedResponseBodyStream(await response.arrayBuffer()),\n };\n}\n\nfunction signedDownloadBodyStream(\n stream: ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n const reader = stream.getReader();\n // Native network errors may contain the bearer URL. Keep failures generic\n // before downstream readers log them, without buffering or eager reads.\n return new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n reader.releaseLock();\n } else {\n controller.enqueue(value);\n }\n } catch {\n controller.error(new Error(\"Blob download interrupted\"));\n reader.releaseLock();\n }\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason);\n } catch {\n throw new Error(\"Blob download cancellation failed\");\n } finally {\n reader.releaseLock();\n }\n },\n },\n { highWaterMark: 0 },\n );\n}\n\nasync function getBlobStream(\n transport: Pick<BinaryResponseOperationTransport, \"requestBinaryResponse\">,\n blobId: string,\n options: RequestResultOptions,\n): Promise<BlobBytesResponse | null> {\n if (options.signal?.aborted) return null;\n const result = await transport.requestBinaryResponse(\n getBlobBytesOperation,\n {\n params: { blobId },\n },\n options,\n );\n if (!result) {\n return null;\n }\n\n const { headers, response } = result;\n const byteLength = Number(headers[blobWireHeaderKeys.blobByteLength]);\n\n // On the browser / WKWebView-fetch path the body is streamed, so\n // `response.body` is a live ReadableStream. Under a native HTTP bridge\n // (CapacitorHttp — which the Capacitor iOS/Android builds enable so cross-origin\n // API requests reach the backend at all; the WKWebView's own fetch fails from\n // the app's https://localhost origin) the response is buffered natively and\n // `response.body` is null even for a valid 200. So take the bytes either way:\n // the stream when present, otherwise buffer the response and re-wrap it as a\n // one-shot ReadableStream so callers keep a single streaming interface. This\n // does not weaken validation — a truncated or corrupted body is still caught\n // downstream by the X-Tearleads-Blob-Sha256 integrity check; it only stops a\n // buffered-but-valid response from being rejected here as malformed.\n const encryptedBytes =\n response.body ?? bufferedResponseBodyStream(await response.arrayBuffer());\n\n return {\n blobId: headers[blobWireHeaderKeys.blobId],\n byteLength,\n encryptedBytes,\n sha256: headers[blobWireHeaderKeys.blobSha256],\n };\n}\n\n// Wraps a fully-buffered response body as a single-chunk ReadableStream, so a\n// native HTTP bridge that only exposes the buffered bytes (no WHATWG\n// `response.body` stream) can still satisfy the streaming\n// `BlobBytesResponse.encryptedBytes` contract. An empty buffer yields an\n// immediately-closed, zero-chunk stream.\nfunction bufferedResponseBodyStream(\n buffer: ArrayBuffer,\n): ReadableStream<Uint8Array> {\n const bytes = new Uint8Array(buffer);\n return new ReadableStream<Uint8Array>({\n start(controller) {\n if (bytes.byteLength > 0) {\n controller.enqueue(bytes);\n }\n controller.close();\n },\n });\n}\n"]}
1
+ {"version":3,"file":"get.js","sourceRoot":"","sources":["get.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAQzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AA4B5D,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAgC,EAChC,MAAc,EACd,gBAA+B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,EAC9D,UAAgC,EAAE;IAElC,wEAAwE;IACxE,4EAA4E;IAC5E,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,aAAa,CAC1C,wBAAwB,EACxB,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EACtB,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CACpC,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACpD,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;QAC/B,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACjC,SAAS,CAAC,aAAa,CAAC;gBACtB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,UAAU,MAAM,WAAW;gBACjC,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,EAAE;gBACd,OAAO,EAAE,QAAQ,MAAM,6CAA6C;aACrE,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACrC,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,CAAC;YACH,uEAAuE;YACvE,qEAAqE;YACrE,MAAM,QAAQ,GAAG,MAAM,aAAa,CAClC,UAAU,CAAC,MAAM,CAAC,GAAG,EACrB,mBAAmB,CAAC,OAAO,CAAC,CAC7B,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC9C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC5B,SAAS;gBACX,CAAC;gBACD,MAAM;YACR,CAAC;YACD,OAAO,MAAM,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,yEAAyE;QAC3E,CAAC;IACH,CAAC;IACD,yEAAyE;IACzE,wEAAwE;IACxE,mEAAmE;IACnE,OAAO,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,UAAuE,EACvE,QAAkB;IAElB,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,cAAc,EAAE,QAAQ,CAAC,IAAI;YAC3B,CAAC,CAAC,wBAAwB,CAAC,QAAQ,CAAC,IAAI,CAAC;YACzC,CAAC,CAAC,0BAA0B,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,MAAkC;IAElC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,0EAA0E;IAC1E,wEAAwE;IACxE,OAAO,IAAI,cAAc,CACvB;QACE,KAAK,CAAC,IAAI,CAAC,UAAU;YACnB,IAAI,CAAC;gBACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI,EAAE,CAAC;oBACT,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,MAAM,CAAC,WAAW,EAAE,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,CAAC;gBACzD,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,MAAM;YACjB,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;KACF,EACD,EAAE,aAAa,EAAE,CAAC,EAAE,CACrB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,SAA0E,EAC1E,MAAc,EACd,OAA6B;IAE7B,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,qBAAqB,CAClD,qBAAqB,EACrB;QACE,MAAM,EAAE,EAAE,MAAM,EAAE;KACnB,EACD,OAAO,CACR,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACrC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAC;IAEtE,iEAAiE;IACjE,uEAAuE;IACvE,iFAAiF;IACjF,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,cAAc,GAClB,QAAQ,CAAC,IAAI,IAAI,0BAA0B,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAE5E,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC1C,UAAU;QACV,cAAc;QACd,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,UAAU,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,0DAA0D;AAC1D,yEAAyE;AACzE,yCAAyC;AACzC,SAAS,0BAA0B,CACjC,MAAmB;IAEnB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO,IAAI,cAAc,CAAa;QACpC,KAAK,CAAC,UAAU;YACd,IAAI,KAAK,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;gBACzB,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,CAAC;YACD,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n blobWireHeaderKeys,\n getBlobBytesOperation,\n getBlobDownloadOperation,\n} from \"@tearleads/validators/operation\";\nimport type { MultipartBlobPartUploadSource } from \"@tearleads/validators/util\";\nimport type { BinaryResponseOperationTransport } from \"../../binaryResponseOperationTransport\";\nimport type { JsonOperationTransport } from \"../../operationTransport\";\nimport type {\n OperationResponseRequestFn,\n RequestResultOptions,\n} from \"../../types\";\nimport { storageFetchOptions } from \"./storageFetchOptions\";\n\nexport interface BlobBytesResponse {\n readonly blobId: string;\n readonly byteLength: number;\n readonly encryptedBytes: ReadableStream<Uint8Array>;\n readonly sha256: string;\n}\n\nexport interface UploadMultipartBlobPartBytesRequest {\n readonly byteLength: number;\n readonly encryptedBytes: Blob | BufferSource;\n readonly sha256: string;\n /**\n * An upload source the stage initiation already issued for this part. The\n * first attempt uses it instead of authorizing; a failed attempt still\n * reauthorizes once, exactly as when no source was issued.\n */\n readonly source?: MultipartBlobPartUploadSource | undefined;\n readonly uploadId: string;\n}\n\ntype BlobDownloadTransport = Pick<JsonOperationTransport, \"requestResult\"> &\n Pick<BinaryResponseOperationTransport, \"requestBinaryResponse\"> &\n Pick<OperationResponseRequestFn, \"reportFailure\">;\n\ntype DownloadFetch = (url: string, init: RequestInit) => Promise<Response>;\n\nexport async function getBlobBytes(\n transport: BlobDownloadTransport,\n blobId: string,\n downloadFetch: DownloadFetch = (url, init) => fetch(url, init),\n options: RequestResultOptions = {},\n): Promise<BlobBytesResponse | null> {\n // Refresh at most once. R2 expiration errors can appear as CORS/network\n // failures in browsers, so both rejected fetches and 403s need a fresh URL.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (options.signal?.aborted) return null;\n const result = await transport.requestResult(\n getBlobDownloadOperation,\n { params: { blobId } },\n { ...options, reportErrors: false },\n );\n if (!result.ok) {\n if (result.kind === \"http\" && result.status === 404) {\n return getBlobStream(transport, blobId, options);\n }\n result.report();\n return null;\n }\n const descriptor = result.data;\n if (descriptor.blobId !== blobId) {\n transport.reportFailure({\n kind: \"shape\",\n method: \"GET\",\n path: `/blobs/${blobId}/download`,\n status: 200,\n statusText: \"\",\n message: `Blob ${blobId} download descriptor has the wrong identity`,\n });\n return null;\n }\n if (descriptor.source.kind === \"api\") {\n return getBlobStream(transport, blobId, options);\n }\n try {\n // Never send the application's session or cookies to the object store.\n // Reject redirects and keep bearer URLs out of referrers and errors.\n const response = await downloadFetch(\n descriptor.source.url,\n storageFetchOptions(options),\n );\n if (response.status !== 200) {\n await response.body?.cancel().catch(() => {});\n if (response.status === 403) {\n continue;\n }\n break;\n }\n return await signedBlobResponse(descriptor, response);\n } catch {\n // The downstream SDK still verifies length, hash and authenticated\n // encryption. An error after streaming begins propagates to that reader.\n }\n }\n // Preserve downloads when storage is unreachable or a native HTTP bridge\n // cannot use the signed URL. This endpoint checks current access again.\n // A storage failure alone must not signal that the API is offline.\n return getBlobStream(transport, blobId, options);\n}\n\nasync function signedBlobResponse(\n descriptor: Pick<BlobBytesResponse, \"blobId\" | \"byteLength\" | \"sha256\">,\n response: Response,\n): Promise<BlobBytesResponse> {\n return {\n blobId: descriptor.blobId,\n byteLength: descriptor.byteLength,\n sha256: descriptor.sha256,\n encryptedBytes: response.body\n ? signedDownloadBodyStream(response.body)\n : bufferedResponseBodyStream(await response.arrayBuffer()),\n };\n}\n\nfunction signedDownloadBodyStream(\n stream: ReadableStream<Uint8Array>,\n): ReadableStream<Uint8Array> {\n const reader = stream.getReader();\n // Native network errors may contain the bearer URL. Keep failures generic\n // before downstream readers log them, without buffering or eager reads.\n return new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n reader.releaseLock();\n } else {\n controller.enqueue(value);\n }\n } catch {\n controller.error(new Error(\"Blob download interrupted\"));\n reader.releaseLock();\n }\n },\n async cancel(reason) {\n try {\n await reader.cancel(reason);\n } catch {\n throw new Error(\"Blob download cancellation failed\");\n } finally {\n reader.releaseLock();\n }\n },\n },\n { highWaterMark: 0 },\n );\n}\n\nasync function getBlobStream(\n transport: Pick<BinaryResponseOperationTransport, \"requestBinaryResponse\">,\n blobId: string,\n options: RequestResultOptions,\n): Promise<BlobBytesResponse | null> {\n if (options.signal?.aborted) return null;\n const result = await transport.requestBinaryResponse(\n getBlobBytesOperation,\n {\n params: { blobId },\n },\n options,\n );\n if (!result) {\n return null;\n }\n\n const { headers, response } = result;\n const byteLength = Number(headers[blobWireHeaderKeys.blobByteLength]);\n\n // On the browser / WKWebView-fetch path the body is streamed, so\n // `response.body` is a live ReadableStream. Under a native HTTP bridge\n // (CapacitorHttp — which the Capacitor iOS/Android builds enable so cross-origin\n // API requests reach the backend at all; the WKWebView's own fetch fails from\n // the app's https://localhost origin) the response is buffered natively and\n // `response.body` is null even for a valid 200. So take the bytes either way:\n // the stream when present, otherwise buffer the response and re-wrap it as a\n // one-shot ReadableStream so callers keep a single streaming interface. This\n // does not weaken validation — a truncated or corrupted body is still caught\n // downstream by the X-Tearleads-Blob-Sha256 integrity check; it only stops a\n // buffered-but-valid response from being rejected here as malformed.\n const encryptedBytes =\n response.body ?? bufferedResponseBodyStream(await response.arrayBuffer());\n\n return {\n blobId: headers[blobWireHeaderKeys.blobId],\n byteLength,\n encryptedBytes,\n sha256: headers[blobWireHeaderKeys.blobSha256],\n };\n}\n\n// Wraps a fully-buffered response body as a single-chunk ReadableStream, so a\n// native HTTP bridge that only exposes the buffered bytes (no WHATWG\n// `response.body` stream) can still satisfy the streaming\n// `BlobBytesResponse.encryptedBytes` contract. An empty buffer yields an\n// immediately-closed, zero-chunk stream.\nfunction bufferedResponseBodyStream(\n buffer: ArrayBuffer,\n): ReadableStream<Uint8Array> {\n const bytes = new Uint8Array(buffer);\n return new ReadableStream<Uint8Array>({\n start(controller) {\n if (bytes.byteLength > 0) {\n controller.enqueue(bytes);\n }\n controller.close();\n },\n });\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { completeMultipartBlobStageOperation, getMultipartBlobStageOperation, initiateMultipartBlobStageOperation, isCompleteMultipartBlobStageOperationRequest, isCompleteMultipartBlobStageOperationResponse, isGetMultipartBlobStageOperationResponse, isInitiateMultipartBlobStageOperationRequest, isInitiateMultipartBlobStageOperationResponse, operationRequestPath, } from "../../../validators/operation/index.js";
1
+ import { getMultipartBlobStageOperation, initiateMultipartBlobStageOperation, isGetMultipartBlobStageOperationResponse, isInitiateMultipartBlobStageOperationRequest, isInitiateMultipartBlobStageOperationResponse, operationRequestPath, } from "../../../validators/operation/index.js";
2
2
  export const initiateMultipartBlobStage = {
3
3
  isRequest: isInitiateMultipartBlobStageOperationRequest,
4
4
  isResponse: isInitiateMultipartBlobStageOperationResponse,
@@ -12,14 +12,4 @@ export const getMultipartBlobStage = {
12
12
  return operationRequestPath(getMultipartBlobStageOperation, { stageId });
13
13
  },
14
14
  };
15
- export const completeMultipartBlobStage = {
16
- isRequest: isCompleteMultipartBlobStageOperationRequest,
17
- isResponse: isCompleteMultipartBlobStageOperationResponse,
18
- method: completeMultipartBlobStageOperation.method,
19
- path(stageId) {
20
- return operationRequestPath(completeMultipartBlobStageOperation, {
21
- stageId,
22
- });
23
- },
24
- };
25
15
  //# sourceMappingURL=multipart.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"multipart.js","sourceRoot":"","sources":["multipart.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mCAAmC,EACnC,8BAA8B,EAC9B,mCAAmC,EACnC,4CAA4C,EAC5C,6CAA6C,EAC7C,wCAAwC,EACxC,4CAA4C,EAC5C,6CAA6C,EAC7C,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,SAAS,EAAE,4CAA4C;IACvD,UAAU,EAAE,6CAA6C;IACzD,MAAM,EAAE,mCAAmC,CAAC,MAAM;IAClD,IAAI,EAAE,oBAAoB,CAAC,mCAAmC,EAAE,EAAE,CAAC;CACpE,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,UAAU,EAAE,wCAAwC;IACpD,MAAM,EAAE,8BAA8B,CAAC,MAAM;IAC7C,IAAI,CAAC,OAAe;QAClB,OAAO,oBAAoB,CAAC,8BAA8B,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,SAAS,EAAE,4CAA4C;IACvD,UAAU,EAAE,6CAA6C;IACzD,MAAM,EAAE,mCAAmC,CAAC,MAAM;IAClD,IAAI,CAAC,OAAe;QAClB,OAAO,oBAAoB,CAAC,mCAAmC,EAAE;YAC/D,OAAO;SACR,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import {\n completeMultipartBlobStageOperation,\n getMultipartBlobStageOperation,\n initiateMultipartBlobStageOperation,\n isCompleteMultipartBlobStageOperationRequest,\n isCompleteMultipartBlobStageOperationResponse,\n isGetMultipartBlobStageOperationResponse,\n isInitiateMultipartBlobStageOperationRequest,\n isInitiateMultipartBlobStageOperationResponse,\n operationRequestPath,\n} from \"@tearleads/validators/operation\";\n\nexport const initiateMultipartBlobStage = {\n isRequest: isInitiateMultipartBlobStageOperationRequest,\n isResponse: isInitiateMultipartBlobStageOperationResponse,\n method: initiateMultipartBlobStageOperation.method,\n path: operationRequestPath(initiateMultipartBlobStageOperation, {}),\n};\n\nexport const getMultipartBlobStage = {\n isResponse: isGetMultipartBlobStageOperationResponse,\n method: getMultipartBlobStageOperation.method,\n path(stageId: string) {\n return operationRequestPath(getMultipartBlobStageOperation, { stageId });\n },\n};\n\nexport const completeMultipartBlobStage = {\n isRequest: isCompleteMultipartBlobStageOperationRequest,\n isResponse: isCompleteMultipartBlobStageOperationResponse,\n method: completeMultipartBlobStageOperation.method,\n path(stageId: string) {\n return operationRequestPath(completeMultipartBlobStageOperation, {\n stageId,\n });\n },\n};\n"]}
1
+ {"version":3,"file":"multipart.js","sourceRoot":"","sources":["multipart.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,mCAAmC,EACnC,wCAAwC,EACxC,4CAA4C,EAC5C,6CAA6C,EAC7C,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,SAAS,EAAE,4CAA4C;IACvD,UAAU,EAAE,6CAA6C;IACzD,MAAM,EAAE,mCAAmC,CAAC,MAAM;IAClD,IAAI,EAAE,oBAAoB,CAAC,mCAAmC,EAAE,EAAE,CAAC;CACpE,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,UAAU,EAAE,wCAAwC;IACpD,MAAM,EAAE,8BAA8B,CAAC,MAAM;IAC7C,IAAI,CAAC,OAAe;QAClB,OAAO,oBAAoB,CAAC,8BAA8B,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3E,CAAC;CACF,CAAC","sourcesContent":["import {\n getMultipartBlobStageOperation,\n initiateMultipartBlobStageOperation,\n isGetMultipartBlobStageOperationResponse,\n isInitiateMultipartBlobStageOperationRequest,\n isInitiateMultipartBlobStageOperationResponse,\n operationRequestPath,\n} from \"@tearleads/validators/operation\";\n\nexport const initiateMultipartBlobStage = {\n isRequest: isInitiateMultipartBlobStageOperationRequest,\n isResponse: isInitiateMultipartBlobStageOperationResponse,\n method: initiateMultipartBlobStageOperation.method,\n path: operationRequestPath(initiateMultipartBlobStageOperation, {}),\n};\n\nexport const getMultipartBlobStage = {\n isResponse: isGetMultipartBlobStageOperationResponse,\n method: getMultipartBlobStageOperation.method,\n path(stageId: string) {\n return operationRequestPath(getMultipartBlobStageOperation, { stageId });\n },\n};\n"]}
@@ -0,0 +1,14 @@
1
+ import type { UploadMultipartBlobPartResponse } from "../../../validators/response/index.js";
2
+ import { type MultipartBlobPartDescriptor } from "../../../validators/util/index.js";
3
+ import type { OperationTransport } from "../../operationTransportFactory.js";
4
+ import type { RequestResultOptions } from "../../types.js";
5
+ import type { UploadMultipartBlobPartBytesRequest } from "./get.js";
6
+ type UploadFetch = (url: string, init: RequestInit) => Promise<Response>;
7
+ /**
8
+ * Describe an encrypted part the way a stage initiation lists the parts it
9
+ * will send first: number, exact length, and the Content-MD5 a signed upload
10
+ * URL binds.
11
+ */
12
+ export declare function describeMultipartBlobPart(partNumber: number, encryptedBytes: Uint8Array): MultipartBlobPartDescriptor;
13
+ export declare function uploadMultipartBlobPartBytes(transport: OperationTransport, stageId: string, partNumber: number, input: UploadMultipartBlobPartBytesRequest, uploadFetch?: UploadFetch, options?: RequestResultOptions): Promise<UploadMultipartBlobPartResponse | null>;
14
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { md5 } from "@noble/hashes/legacy.js";
2
2
  import { bytesToHex } from "../../../crypto/index.js";
3
3
  import { authorizeMultipartBlobPartUploadOperation, blobWireHeaderKeys, operationRequestPath, uploadMultipartBlobPartBytesOperation, } from "../../../validators/operation/index.js";
4
- import { MAX_MULTIPART_BLOB_PART_BYTES } from "../../../validators/util/index.js";
4
+ import { MAX_MULTIPART_BLOB_PART_BYTES, } from "../../../validators/util/index.js";
5
5
  import { storageFetchOptions } from "./storageFetchOptions.js";
6
6
  function invalidPart(transport, path) {
7
7
  transport.reportFailure({
@@ -23,6 +23,21 @@ async function retryStorageStatus(status, attempt) {
23
23
  }
24
24
  return transient;
25
25
  }
26
+ function contentMd5Base64(bytes) {
27
+ return btoa(String.fromCharCode(...md5(bytes)));
28
+ }
29
+ /**
30
+ * Describe an encrypted part the way a stage initiation lists the parts it
31
+ * will send first: number, exact length, and the Content-MD5 a signed upload
32
+ * URL binds.
33
+ */
34
+ export function describeMultipartBlobPart(partNumber, encryptedBytes) {
35
+ return {
36
+ byteLength: encryptedBytes.byteLength,
37
+ contentMd5: contentMd5Base64(encryptedBytes),
38
+ partNumber,
39
+ };
40
+ }
26
41
  async function preparePart(input) {
27
42
  // File snapshots mutable buffers and preserves ciphertext across Capacitor's
28
43
  // native HTTP bridge. Uint8Array is UTF-8 decoded there; a plain Blob becomes
@@ -42,64 +57,100 @@ async function preparePart(input) {
42
57
  input.sha256) {
43
58
  throw new TypeError("Multipart part SHA-256 does not match its bytes");
44
59
  }
45
- const contentMd5 = btoa(String.fromCharCode(...md5(new Uint8Array(bytes))));
46
- return { file, contentMd5 };
60
+ return { file, contentMd5: contentMd5Base64(new Uint8Array(bytes)) };
61
+ }
62
+ // Sign just in time for this part. The returned descriptor must name exactly
63
+ // the part, upload, length, and checksum that were requested.
64
+ async function authorizePart(context) {
65
+ const operation = authorizeMultipartBlobPartUploadOperation;
66
+ const { input, partNumber, stageId } = context;
67
+ const result = await context.transport.requestResult(operation, {
68
+ params: { stageId, partNumber },
69
+ body: {
70
+ byteLength: input.byteLength,
71
+ contentMd5: context.contentMd5,
72
+ uploadId: input.uploadId,
73
+ },
74
+ }, context.options);
75
+ if (!result.ok)
76
+ return null;
77
+ const descriptor = result.data;
78
+ if (descriptor.stageId !== stageId ||
79
+ descriptor.uploadId !== input.uploadId ||
80
+ descriptor.partNumber !== partNumber ||
81
+ descriptor.byteLength !== input.byteLength ||
82
+ descriptor.contentMd5 !== context.contentMd5) {
83
+ context.transport.reportFailure({
84
+ kind: "shape",
85
+ method: operation.method,
86
+ path: context.path,
87
+ status: 200,
88
+ statusText: "",
89
+ message: "Multipart upload descriptor does not match its part",
90
+ });
91
+ return null;
92
+ }
93
+ return descriptor.source;
94
+ }
95
+ // Parts can wait behind other transfers in the shared pool. An issued URL that
96
+ // is about to lapse would only spend this part's retry on a certain 403, so
97
+ // authorize afresh instead; a skewed clock errs toward authorizing.
98
+ const ISSUED_URL_MIN_REMAINING_MS = 30_000;
99
+ function isUsableIssuedSource(source) {
100
+ return (source !== undefined &&
101
+ (source.kind === "api" ||
102
+ Date.parse(source.expiresAt) - Date.now() > ISSUED_URL_MIN_REMAINING_MS));
103
+ }
104
+ // A capability path for memory/custom stores; S3/R2 never proxy parts.
105
+ function uploadThroughApi(context, file) {
106
+ const { input } = context;
107
+ return context.transport.requestBinaryRequest(uploadMultipartBlobPartBytesOperation, {
108
+ params: { stageId: context.stageId, partNumber: context.partNumber },
109
+ body: input.encryptedBytes,
110
+ headers: {
111
+ [blobWireHeaderKeys.partByteLength]: input.byteLength.toString(),
112
+ [blobWireHeaderKeys.partSha256]: input.sha256,
113
+ [blobWireHeaderKeys.partUploadId]: input.uploadId,
114
+ },
115
+ }, { ...context.options, encodeBody: () => file });
47
116
  }
48
117
  export async function uploadMultipartBlobPartBytes(transport, stageId, partNumber, input, uploadFetch = (url, init) => fetch(url, init), options = {}) {
49
118
  if (options.signal?.aborted)
50
119
  return null;
51
- const operation = authorizeMultipartBlobPartUploadOperation;
52
- const params = { stageId, partNumber };
53
- const path = operationRequestPath(operation, params);
120
+ const path = operationRequestPath(authorizeMultipartBlobPartUploadOperation, {
121
+ stageId,
122
+ partNumber,
123
+ });
54
124
  const prepared = await preparePart(input).catch(() => invalidPart(transport, path));
55
125
  if (!prepared)
56
126
  return null;
57
127
  const { file, contentMd5 } = prepared;
58
- const body = {
59
- byteLength: input.byteLength,
128
+ const context = {
60
129
  contentMd5,
61
- uploadId: input.uploadId,
130
+ input,
131
+ options,
132
+ partNumber,
133
+ path,
134
+ stageId,
135
+ transport,
62
136
  };
63
137
  let status = null;
64
- // Sign just in time for each part, not once for the entire file. A network
65
- // failure may be an expired URL hidden by R2 CORS, or a lost success response;
66
- // reauthorize once and send the same immutable bytes to the same part number.
138
+ // The first attempt uses the source the stage initiation issued, if any. A
139
+ // network failure may be an expired URL hidden by R2 CORS, or a lost success
140
+ // response; reauthorize once and send the same immutable bytes to the same
141
+ // part number.
67
142
  for (let attempt = 0; attempt < 2; attempt += 1) {
68
143
  if (options.signal?.aborted)
69
144
  return null;
70
- const result = await transport.requestResult(operation, { params, body }, options);
71
- if (!result.ok)
72
- return null;
73
- const descriptor = result.data;
74
- if (descriptor.stageId !== stageId ||
75
- descriptor.uploadId !== input.uploadId ||
76
- descriptor.partNumber !== partNumber ||
77
- descriptor.byteLength !== input.byteLength ||
78
- descriptor.contentMd5 !== contentMd5) {
79
- transport.reportFailure({
80
- kind: "shape",
81
- method: operation.method,
82
- path,
83
- status: 200,
84
- statusText: "",
85
- message: "Multipart upload descriptor does not match its part",
86
- });
145
+ const source = attempt === 0 && isUsableIssuedSource(input.source)
146
+ ? input.source
147
+ : await authorizePart(context);
148
+ if (!source)
87
149
  return null;
88
- }
89
- if (descriptor.source.kind === "api") {
90
- // A capability path for memory/custom stores; S3/R2 never proxy parts.
91
- return transport.requestBinaryRequest(uploadMultipartBlobPartBytesOperation, {
92
- params,
93
- body: input.encryptedBytes,
94
- headers: {
95
- [blobWireHeaderKeys.partByteLength]: input.byteLength.toString(),
96
- [blobWireHeaderKeys.partSha256]: input.sha256,
97
- [blobWireHeaderKeys.partUploadId]: input.uploadId,
98
- },
99
- }, { ...options, encodeBody: () => file });
100
- }
150
+ if (source.kind === "api")
151
+ return uploadThroughApi(context, file);
101
152
  try {
102
- const response = await uploadFetch(descriptor.source.url, {
153
+ const response = await uploadFetch(source.url, {
103
154
  method: "PUT",
104
155
  body: file,
105
156
  headers: {
@@ -1 +1 @@
1
- {"version":3,"file":"multipartUpload.js","sourceRoot":"","sources":["multipartUpload.ts"],"names":[],"mappings":"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","sourcesContent":["import { md5 } from \"@noble/hashes/legacy.js\";\nimport { bytesToHex } from \"@tearleads/crypto\";\nimport {\n authorizeMultipartBlobPartUploadOperation,\n blobWireHeaderKeys,\n operationRequestPath,\n uploadMultipartBlobPartBytesOperation,\n} from \"@tearleads/validators/operation\";\nimport type { UploadMultipartBlobPartResponse } from \"@tearleads/validators/response\";\nimport { MAX_MULTIPART_BLOB_PART_BYTES } from \"@tearleads/validators/util\";\nimport type { OperationTransport } from \"../../operationTransportFactory\";\nimport type { RequestResultOptions } from \"../../types\";\nimport type { UploadMultipartBlobPartBytesRequest } from \"./get\";\nimport { storageFetchOptions } from \"./storageFetchOptions\";\n\ntype UploadFetch = (url: string, init: RequestInit) => Promise<Response>;\n\nfunction invalidPart(transport: OperationTransport, path: string) {\n transport.reportFailure({\n kind: \"shape\",\n method: authorizeMultipartBlobPartUploadOperation.method,\n path,\n status: null,\n statusText: \"\",\n message: \"Multipart part bytes do not match the declared length or SHA-256\",\n });\n return null;\n}\n\nasync function retryStorageStatus(status: number, attempt: number) {\n if (status === 403) return true;\n const transient = status === 429 || (status >= 500 && status <= 599);\n if (transient && attempt === 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 250));\n }\n return transient;\n}\n\nasync function preparePart(input: UploadMultipartBlobPartBytesRequest) {\n // File snapshots mutable buffers and preserves ciphertext across Capacitor's\n // native HTTP bridge. Uint8Array is UTF-8 decoded there; a plain Blob becomes\n // JSON. File's base64 bridge path is byte-for-byte, including non-UTF-8 data.\n const file =\n input.encryptedBytes instanceof File\n ? input.encryptedBytes\n : new File([input.encryptedBytes], \"encrypted-blob-part\", {\n type: \"application/octet-stream\",\n });\n if (\n file.size !== input.byteLength ||\n file.size < 1 ||\n file.size > MAX_MULTIPART_BLOB_PART_BYTES\n ) {\n throw new TypeError(\"Invalid multipart part byte length\");\n }\n const bytes = await file.arrayBuffer();\n if (\n bytesToHex(new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes))) !==\n input.sha256\n ) {\n throw new TypeError(\"Multipart part SHA-256 does not match its bytes\");\n }\n const contentMd5 = btoa(String.fromCharCode(...md5(new Uint8Array(bytes))));\n return { file, contentMd5 };\n}\n\nexport async function uploadMultipartBlobPartBytes(\n transport: OperationTransport,\n stageId: string,\n partNumber: number,\n input: UploadMultipartBlobPartBytesRequest,\n uploadFetch: UploadFetch = (url, init) => fetch(url, init),\n options: RequestResultOptions = {},\n): Promise<UploadMultipartBlobPartResponse | null> {\n if (options.signal?.aborted) return null;\n const operation = authorizeMultipartBlobPartUploadOperation;\n const params = { stageId, partNumber };\n const path = operationRequestPath(operation, params);\n const prepared = await preparePart(input).catch(() =>\n invalidPart(transport, path),\n );\n if (!prepared) return null;\n const { file, contentMd5 } = prepared;\n const body = {\n byteLength: input.byteLength,\n contentMd5,\n uploadId: input.uploadId,\n };\n let status: number | null = null;\n // Sign just in time for each part, not once for the entire file. A network\n // failure may be an expired URL hidden by R2 CORS, or a lost success response;\n // reauthorize once and send the same immutable bytes to the same part number.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (options.signal?.aborted) return null;\n const result = await transport.requestResult(\n operation,\n { params, body },\n options,\n );\n if (!result.ok) return null;\n const descriptor = result.data;\n if (\n descriptor.stageId !== stageId ||\n descriptor.uploadId !== input.uploadId ||\n descriptor.partNumber !== partNumber ||\n descriptor.byteLength !== input.byteLength ||\n descriptor.contentMd5 !== contentMd5\n ) {\n transport.reportFailure({\n kind: \"shape\",\n method: operation.method,\n path,\n status: 200,\n statusText: \"\",\n message: \"Multipart upload descriptor does not match its part\",\n });\n return null;\n }\n if (descriptor.source.kind === \"api\") {\n // A capability path for memory/custom stores; S3/R2 never proxy parts.\n return transport.requestBinaryRequest(\n uploadMultipartBlobPartBytesOperation,\n {\n params,\n body: input.encryptedBytes,\n headers: {\n [blobWireHeaderKeys.partByteLength]: input.byteLength.toString(),\n [blobWireHeaderKeys.partSha256]: input.sha256,\n [blobWireHeaderKeys.partUploadId]: input.uploadId,\n },\n },\n { ...options, encodeBody: () => file },\n );\n }\n try {\n const response = await uploadFetch(descriptor.source.url, {\n method: \"PUT\",\n body: file,\n headers: {\n \"Content-Type\": \"application/octet-stream\",\n \"Content-MD5\": contentMd5,\n },\n ...storageFetchOptions(options),\n });\n status = response.status;\n const etag = response.headers.get(\"ETag\")?.trim();\n await response.body?.cancel().catch(() => {});\n if (status === 200 && etag) {\n return {\n stageId,\n uploadId: input.uploadId,\n part: { byteLength: input.byteLength, partNumber, etag },\n };\n }\n if (!(await retryStorageStatus(status, attempt))) break;\n } catch {\n // Never expose a provider exception or body: it may contain the bearer URL.\n status = null;\n }\n }\n if (options.signal?.aborted) return null;\n reportStorageFailure(transport, path, partNumber, status);\n return null;\n}\n\nfunction reportStorageFailure(\n transport: OperationTransport,\n path: string,\n partNumber: number,\n status: number | null,\n) {\n transport.reportFailure({\n kind: \"storage\",\n method: authorizeMultipartBlobPartUploadOperation.method,\n path,\n status,\n statusText: \"\",\n message:\n status === 200\n ? `Multipart part ${partNumber} storage response is missing ETag; check storage CORS exposure`\n : `Multipart part ${partNumber} storage upload failed${status === null ? \"\" : ` (HTTP ${status})`}`,\n });\n}\n"]}
1
+ {"version":3,"file":"multipartUpload.js","sourceRoot":"","sources":["multipartUpload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,yCAAyC,EACzC,kBAAkB,EAClB,oBAAoB,EACpB,qCAAqC,GACtC,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,6BAA6B,GAG9B,MAAM,4BAA4B,CAAC;AAIpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAI5D,SAAS,WAAW,CAAC,SAA6B,EAAE,IAAY;IAC9D,SAAS,CAAC,aAAa,CAAC;QACtB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,yCAAyC,CAAC,MAAM;QACxD,IAAI;QACJ,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,kEAAkE;KAC5E,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,MAAc,EAAE,OAAe;IAC/D,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,SAAS,GAAG,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC;IACrE,IAAI,SAAS,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAiB;IACzC,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAAkB,EAClB,cAA0B;IAE1B,OAAO;QACL,UAAU,EAAE,cAAc,CAAC,UAAU;QACrC,UAAU,EAAE,gBAAgB,CAAC,cAAc,CAAC;QAC5C,UAAU;KACX,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,KAA0C;IACnE,6EAA6E;IAC7E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,IAAI,GACR,KAAK,CAAC,cAAc,YAAY,IAAI;QAClC,CAAC,CAAC,KAAK,CAAC,cAAc;QACtB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,qBAAqB,EAAE;YACtD,IAAI,EAAE,0BAA0B;SACjC,CAAC,CAAC;IACT,IACE,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,UAAU;QAC9B,IAAI,CAAC,IAAI,GAAG,CAAC;QACb,IAAI,CAAC,IAAI,GAAG,6BAA6B,EACzC,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IACvC,IACE,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QACxE,KAAK,CAAC,MAAM,EACZ,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC;AAYD,6EAA6E;AAC7E,8DAA8D;AAC9D,KAAK,UAAU,aAAa,CAC1B,OAA0B;IAE1B,MAAM,SAAS,GAAG,yCAAyC,CAAC;IAC5D,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,aAAa,CAClD,SAAS,EACT;QACE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE;QAC/B,IAAI,EAAE;YACJ,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;KACF,EACD,OAAO,CAAC,OAAO,CAChB,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;IAC/B,IACE,UAAU,CAAC,OAAO,KAAK,OAAO;QAC9B,UAAU,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;QACtC,UAAU,CAAC,UAAU,KAAK,UAAU;QACpC,UAAU,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU;QAC1C,UAAU,CAAC,UAAU,KAAK,OAAO,CAAC,UAAU,EAC5C,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC;YAC9B,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,EAAE;YACd,OAAO,EAAE,qDAAqD;SAC/D,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,4EAA4E;AAC5E,oEAAoE;AACpE,MAAM,2BAA2B,GAAG,MAAM,CAAC;AAE3C,SAAS,oBAAoB,CAC3B,MAAiD;IAEjD,OAAO,CACL,MAAM,KAAK,SAAS;QACpB,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK;YACpB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,2BAA2B,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,OAA0B,EAAE,IAAU;IAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,OAAO,OAAO,CAAC,SAAS,CAAC,oBAAoB,CAC3C,qCAAqC,EACrC;QACE,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE;QACpE,IAAI,EAAE,KAAK,CAAC,cAAc;QAC1B,OAAO,EAAE;YACP,CAAC,kBAAkB,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE;YAChE,CAAC,kBAAkB,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM;YAC7C,CAAC,kBAAkB,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,QAAQ;SAClD;KACF,EACD,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAC/C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,SAA6B,EAC7B,OAAe,EACf,UAAkB,EAClB,KAA0C,EAC1C,cAA2B,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,EAC1D,UAAgC,EAAE;IAElC,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,IAAI,GAAG,oBAAoB,CAAC,yCAAyC,EAAE;QAC3E,OAAO;QACP,UAAU;KACX,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CACnD,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,CAC7B,CAAC;IACF,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC;IACtC,MAAM,OAAO,GAAsB;QACjC,UAAU;QACV,KAAK;QACL,OAAO;QACP,UAAU;QACV,IAAI;QACJ,OAAO;QACP,SAAS;KACV,CAAC;IACF,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,2EAA2E;IAC3E,6EAA6E;IAC7E,2EAA2E;IAC3E,eAAe;IACf,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,MAAM,GACV,OAAO,KAAK,CAAC,IAAI,oBAAoB,CAAC,KAAK,CAAC,MAAM,CAAC;YACjD,CAAC,CAAC,KAAK,CAAC,MAAM;YACd,CAAC,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE;gBAC7C,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE;oBACP,cAAc,EAAE,0BAA0B;oBAC1C,aAAa,EAAE,UAAU;iBAC1B;gBACD,GAAG,mBAAmB,CAAC,OAAO,CAAC;aAChC,CAAC,CAAC;YACH,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;YAClD,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC9C,IAAI,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;gBAC3B,OAAO;oBACL,OAAO;oBACP,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE;iBACzD,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,CAAC,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAAE,MAAM;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,4EAA4E;YAC5E,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,oBAAoB,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,oBAAoB,CAC3B,SAA6B,EAC7B,IAAY,EACZ,UAAkB,EAClB,MAAqB;IAErB,SAAS,CAAC,aAAa,CAAC;QACtB,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,yCAAyC,CAAC,MAAM;QACxD,IAAI;QACJ,MAAM;QACN,UAAU,EAAE,EAAE;QACd,OAAO,EACL,MAAM,KAAK,GAAG;YACZ,CAAC,CAAC,kBAAkB,UAAU,gEAAgE;YAC9F,CAAC,CAAC,kBAAkB,UAAU,yBAAyB,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,MAAM,GAAG,EAAE;KACxG,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { md5 } from \"@noble/hashes/legacy.js\";\nimport { bytesToHex } from \"@tearleads/crypto\";\nimport {\n authorizeMultipartBlobPartUploadOperation,\n blobWireHeaderKeys,\n operationRequestPath,\n uploadMultipartBlobPartBytesOperation,\n} from \"@tearleads/validators/operation\";\nimport type { UploadMultipartBlobPartResponse } from \"@tearleads/validators/response\";\nimport {\n MAX_MULTIPART_BLOB_PART_BYTES,\n type MultipartBlobPartDescriptor,\n type MultipartBlobPartUploadSource,\n} from \"@tearleads/validators/util\";\nimport type { OperationTransport } from \"../../operationTransportFactory\";\nimport type { RequestResultOptions } from \"../../types\";\nimport type { UploadMultipartBlobPartBytesRequest } from \"./get\";\nimport { storageFetchOptions } from \"./storageFetchOptions\";\n\ntype UploadFetch = (url: string, init: RequestInit) => Promise<Response>;\n\nfunction invalidPart(transport: OperationTransport, path: string) {\n transport.reportFailure({\n kind: \"shape\",\n method: authorizeMultipartBlobPartUploadOperation.method,\n path,\n status: null,\n statusText: \"\",\n message: \"Multipart part bytes do not match the declared length or SHA-256\",\n });\n return null;\n}\n\nasync function retryStorageStatus(status: number, attempt: number) {\n if (status === 403) return true;\n const transient = status === 429 || (status >= 500 && status <= 599);\n if (transient && attempt === 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, 250));\n }\n return transient;\n}\n\nfunction contentMd5Base64(bytes: Uint8Array): string {\n return btoa(String.fromCharCode(...md5(bytes)));\n}\n\n/**\n * Describe an encrypted part the way a stage initiation lists the parts it\n * will send first: number, exact length, and the Content-MD5 a signed upload\n * URL binds.\n */\nexport function describeMultipartBlobPart(\n partNumber: number,\n encryptedBytes: Uint8Array,\n): MultipartBlobPartDescriptor {\n return {\n byteLength: encryptedBytes.byteLength,\n contentMd5: contentMd5Base64(encryptedBytes),\n partNumber,\n };\n}\n\nasync function preparePart(input: UploadMultipartBlobPartBytesRequest) {\n // File snapshots mutable buffers and preserves ciphertext across Capacitor's\n // native HTTP bridge. Uint8Array is UTF-8 decoded there; a plain Blob becomes\n // JSON. File's base64 bridge path is byte-for-byte, including non-UTF-8 data.\n const file =\n input.encryptedBytes instanceof File\n ? input.encryptedBytes\n : new File([input.encryptedBytes], \"encrypted-blob-part\", {\n type: \"application/octet-stream\",\n });\n if (\n file.size !== input.byteLength ||\n file.size < 1 ||\n file.size > MAX_MULTIPART_BLOB_PART_BYTES\n ) {\n throw new TypeError(\"Invalid multipart part byte length\");\n }\n const bytes = await file.arrayBuffer();\n if (\n bytesToHex(new Uint8Array(await crypto.subtle.digest(\"SHA-256\", bytes))) !==\n input.sha256\n ) {\n throw new TypeError(\"Multipart part SHA-256 does not match its bytes\");\n }\n return { file, contentMd5: contentMd5Base64(new Uint8Array(bytes)) };\n}\n\ninterface PartUploadContext {\n readonly contentMd5: string;\n readonly input: UploadMultipartBlobPartBytesRequest;\n readonly options: RequestResultOptions;\n readonly partNumber: number;\n readonly path: string;\n readonly stageId: string;\n readonly transport: OperationTransport;\n}\n\n// Sign just in time for this part. The returned descriptor must name exactly\n// the part, upload, length, and checksum that were requested.\nasync function authorizePart(\n context: PartUploadContext,\n): Promise<MultipartBlobPartUploadSource | null> {\n const operation = authorizeMultipartBlobPartUploadOperation;\n const { input, partNumber, stageId } = context;\n const result = await context.transport.requestResult(\n operation,\n {\n params: { stageId, partNumber },\n body: {\n byteLength: input.byteLength,\n contentMd5: context.contentMd5,\n uploadId: input.uploadId,\n },\n },\n context.options,\n );\n if (!result.ok) return null;\n const descriptor = result.data;\n if (\n descriptor.stageId !== stageId ||\n descriptor.uploadId !== input.uploadId ||\n descriptor.partNumber !== partNumber ||\n descriptor.byteLength !== input.byteLength ||\n descriptor.contentMd5 !== context.contentMd5\n ) {\n context.transport.reportFailure({\n kind: \"shape\",\n method: operation.method,\n path: context.path,\n status: 200,\n statusText: \"\",\n message: \"Multipart upload descriptor does not match its part\",\n });\n return null;\n }\n return descriptor.source;\n}\n\n// Parts can wait behind other transfers in the shared pool. An issued URL that\n// is about to lapse would only spend this part's retry on a certain 403, so\n// authorize afresh instead; a skewed clock errs toward authorizing.\nconst ISSUED_URL_MIN_REMAINING_MS = 30_000;\n\nfunction isUsableIssuedSource(\n source: MultipartBlobPartUploadSource | undefined,\n): source is MultipartBlobPartUploadSource {\n return (\n source !== undefined &&\n (source.kind === \"api\" ||\n Date.parse(source.expiresAt) - Date.now() > ISSUED_URL_MIN_REMAINING_MS)\n );\n}\n\n// A capability path for memory/custom stores; S3/R2 never proxy parts.\nfunction uploadThroughApi(context: PartUploadContext, file: File) {\n const { input } = context;\n return context.transport.requestBinaryRequest(\n uploadMultipartBlobPartBytesOperation,\n {\n params: { stageId: context.stageId, partNumber: context.partNumber },\n body: input.encryptedBytes,\n headers: {\n [blobWireHeaderKeys.partByteLength]: input.byteLength.toString(),\n [blobWireHeaderKeys.partSha256]: input.sha256,\n [blobWireHeaderKeys.partUploadId]: input.uploadId,\n },\n },\n { ...context.options, encodeBody: () => file },\n );\n}\n\nexport async function uploadMultipartBlobPartBytes(\n transport: OperationTransport,\n stageId: string,\n partNumber: number,\n input: UploadMultipartBlobPartBytesRequest,\n uploadFetch: UploadFetch = (url, init) => fetch(url, init),\n options: RequestResultOptions = {},\n): Promise<UploadMultipartBlobPartResponse | null> {\n if (options.signal?.aborted) return null;\n const path = operationRequestPath(authorizeMultipartBlobPartUploadOperation, {\n stageId,\n partNumber,\n });\n const prepared = await preparePart(input).catch(() =>\n invalidPart(transport, path),\n );\n if (!prepared) return null;\n const { file, contentMd5 } = prepared;\n const context: PartUploadContext = {\n contentMd5,\n input,\n options,\n partNumber,\n path,\n stageId,\n transport,\n };\n let status: number | null = null;\n // The first attempt uses the source the stage initiation issued, if any. A\n // network failure may be an expired URL hidden by R2 CORS, or a lost success\n // response; reauthorize once and send the same immutable bytes to the same\n // part number.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (options.signal?.aborted) return null;\n const source =\n attempt === 0 && isUsableIssuedSource(input.source)\n ? input.source\n : await authorizePart(context);\n if (!source) return null;\n if (source.kind === \"api\") return uploadThroughApi(context, file);\n try {\n const response = await uploadFetch(source.url, {\n method: \"PUT\",\n body: file,\n headers: {\n \"Content-Type\": \"application/octet-stream\",\n \"Content-MD5\": contentMd5,\n },\n ...storageFetchOptions(options),\n });\n status = response.status;\n const etag = response.headers.get(\"ETag\")?.trim();\n await response.body?.cancel().catch(() => {});\n if (status === 200 && etag) {\n return {\n stageId,\n uploadId: input.uploadId,\n part: { byteLength: input.byteLength, partNumber, etag },\n };\n }\n if (!(await retryStorageStatus(status, attempt))) break;\n } catch {\n // Never expose a provider exception or body: it may contain the bearer URL.\n status = null;\n }\n }\n if (options.signal?.aborted) return null;\n reportStorageFailure(transport, path, partNumber, status);\n return null;\n}\n\nfunction reportStorageFailure(\n transport: OperationTransport,\n path: string,\n partNumber: number,\n status: number | null,\n) {\n transport.reportFailure({\n kind: \"storage\",\n method: authorizeMultipartBlobPartUploadOperation.method,\n path,\n status,\n statusText: \"\",\n message:\n status === 200\n ? `Multipart part ${partNumber} storage response is missing ETag; check storage CORS exposure`\n : `Multipart part ${partNumber} storage upload failed${status === null ? \"\" : ` (HTTP ${status})`}`,\n });\n}\n"]}
@@ -1,4 +1,7 @@
1
- import { getContainerWriterProjectionOperation, getDocumentWriterProjectionOperation, isGetContainerWriterProjectionOperationResponse, isGetDocumentWriterProjectionOperationResponse, operationRequestPath, } from "../../validators/operation/index.js";
1
+ import { getContainerWriterProjectionOperation, getDocumentWriterProjectionOperation, operationRequestPath, } from "../../validators/operation/index.js";
2
+ import { isContainerWriterProjectionResponse, isDocumentWriterProjectionResponse, } from "../../validators/response/index.js";
3
+ // Validators run on the decoded projection: the transport resolves the wire's
4
+ // manifest-hash citations into bundles first (writerProjectionRequests).
2
5
  // A writer projection is consumed by its leaf: callers wrap keys under
3
6
  // whatever container or document the response describes. Binding the
4
7
  // validator to the requested id makes a projection for any other object an
@@ -24,7 +27,7 @@ function containerProjectionDescribes(projection, containerId) {
24
27
  }
25
28
  export const containerWriterProjection = {
26
29
  isResponseFor(containerId) {
27
- return (value) => isGetContainerWriterProjectionOperationResponse(value) &&
30
+ return (value) => isContainerWriterProjectionResponse(value) &&
28
31
  containerProjectionDescribes(value, containerId);
29
32
  },
30
33
  method: getContainerWriterProjectionOperation.method,
@@ -39,7 +42,7 @@ export const documentWriterProjection = {
39
42
  // least describe the container it labels itself with, so a substituted
40
43
  // path cannot smuggle another container's leaf under a relabeled id.
41
44
  isResponseFor(documentId) {
42
- return (value) => isGetDocumentWriterProjectionOperationResponse(value) &&
45
+ return (value) => isDocumentWriterProjectionResponse(value) &&
43
46
  value.documentId === documentId &&
44
47
  manifestDescribes(value.documentManifest, "documentId", documentId) &&
45
48
  value.documentKekTargets.documentId === documentId &&
@@ -1 +1 @@
1
- {"version":3,"file":"writerProjections.js","sourceRoot":"","sources":["writerProjections.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,oCAAoC,EACpC,+CAA+C,EAC/C,8CAA8C,EAC9C,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAOzC,uEAAuE;AACvE,qEAAqE;AACrE,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,wEAAwE;AACxE,mEAAmE;AACnE,SAAS,iBAAiB,CACxB,MAA6E,EAC7E,QAAsC,EACtC,EAAU;IAEV,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,EAAE;QAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAC3C,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CACnC,UAAuC,EACvC,WAAmB;IAEnB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CACL,UAAU,CAAC,WAAW,KAAK,WAAW;QACtC,IAAI,KAAK,SAAS;QAClB,OAAO,KAAK,SAAS;QACrB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC;QACnD,OAAO,CAAC,WAAW,KAAK,WAAW;QACnC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,WAAW;QAC5D,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,KAAK,WAAW,CAAC,CAC1E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,aAAa,CAAC,WAAmB;QAC/B,OAAO,CAAC,KAAc,EAA8C,EAAE,CACpE,+CAA+C,CAAC,KAAK,CAAC;YACtD,4BAA4B,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,EAAE,qCAAqC,CAAC,MAAM;IACpD,IAAI,CAAC,WAAmB;QACtB,OAAO,oBAAoB,CAAC,qCAAqC,EAAE;YACjE,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,0EAA0E;IAC1E,uEAAuE;IACvE,qEAAqE;IACrE,aAAa,CAAC,UAAkB;QAC9B,OAAO,CAAC,KAAc,EAA6C,EAAE,CACnE,8CAA8C,CAAC,KAAK,CAAC;YACrD,KAAK,CAAC,UAAU,KAAK,UAAU;YAC/B,iBAAiB,CAAC,KAAK,CAAC,gBAAgB,EAAE,YAAY,EAAE,UAAU,CAAC;YACnE,KAAK,CAAC,kBAAkB,CAAC,UAAU,KAAK,UAAU;YAClD,KAAK,CAAC,gBAAgB,CAAC,UAAU,KAAK,UAAU;YAChD,KAAK,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CACnD,4BAA4B,CAAC,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,CACjE,CAAC;IACN,CAAC;IACD,MAAM,EAAE,oCAAoC,CAAC,MAAM;IACnD,IAAI,CAAC,UAAkB;QACrB,OAAO,oBAAoB,CAAC,oCAAoC,EAAE;YAChE,UAAU;SACX,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import {\n getContainerWriterProjectionOperation,\n getDocumentWriterProjectionOperation,\n isGetContainerWriterProjectionOperationResponse,\n isGetDocumentWriterProjectionOperationResponse,\n operationRequestPath,\n} from \"@tearleads/validators/operation\";\nimport type {\n ContainerKeyingPathResponse,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\n\n// A writer projection is consumed by its leaf: callers wrap keys under\n// whatever container or document the response describes. Binding the\n// validator to the requested id makes a projection for any other object an\n// invalid response, so a server cannot redirect a create, move, share, rekey,\n// or content-key re-wrap onto an object it substituted. The top-level id is a\n// mutable label, so the binding also covers the hash-committed manifest object\n// id, the manifest state the SDK later verifies, and the nested KEK and\n// content-key bundle ids a relabeled projection would still carry.\nfunction manifestDescribes(\n bundle: { manifest: Record<string, unknown>; state: Record<string, unknown> },\n stateKey: \"containerId\" | \"documentId\",\n id: string,\n): boolean {\n return (\n Reflect.get(bundle.manifest, \"objectId\") === id &&\n Reflect.get(bundle.state, stateKey) === id\n );\n}\n\nfunction containerProjectionDescribes(\n projection: ContainerKeyingPathResponse,\n containerId: string,\n): boolean {\n const leaf = projection.path.at(-1);\n const leafKek = projection.containerKeks.at(-1);\n return (\n projection.containerId === containerId &&\n leaf !== undefined &&\n leafKek !== undefined &&\n manifestDescribes(leaf, \"containerId\", containerId) &&\n leafKek.containerId === containerId &&\n Reflect.get(leafKek.keyEpoch, \"containerId\") === containerId &&\n (leafKek.keyring === null || leafKek.keyring.containerId === containerId)\n );\n}\n\nexport const containerWriterProjection = {\n isResponseFor(containerId: string) {\n return (value: unknown): value is ContainerWriterProjectionResponse =>\n isGetContainerWriterProjectionOperationResponse(value) &&\n containerProjectionDescribes(value, containerId);\n },\n method: getContainerWriterProjectionOperation.method,\n path(containerId: string) {\n return operationRequestPath(getContainerWriterProjectionOperation, {\n containerId,\n });\n },\n};\n\nexport const documentWriterProjection = {\n // The authorizing container paths carry no requested id, but each must at\n // least describe the container it labels itself with, so a substituted\n // path cannot smuggle another container's leaf under a relabeled id.\n isResponseFor(documentId: string) {\n return (value: unknown): value is DocumentWriterProjectionResponse =>\n isGetDocumentWriterProjectionOperationResponse(value) &&\n value.documentId === documentId &&\n manifestDescribes(value.documentManifest, \"documentId\", documentId) &&\n value.documentKekTargets.documentId === documentId &&\n value.contentKeyBundle.documentId === documentId &&\n value.authorizingContainerPaths.every((projection) =>\n containerProjectionDescribes(projection, projection.containerId),\n );\n },\n method: getDocumentWriterProjectionOperation.method,\n path(documentId: string) {\n return operationRequestPath(getDocumentWriterProjectionOperation, {\n documentId,\n });\n },\n};\n"]}
1
+ {"version":3,"file":"writerProjections.js","sourceRoot":"","sources":["writerProjections.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,oCAAoC,EACpC,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAIL,mCAAmC,EACnC,kCAAkC,GACnC,MAAM,gCAAgC,CAAC;AAExC,8EAA8E;AAC9E,yEAAyE;AACzE,uEAAuE;AACvE,qEAAqE;AACrE,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,wEAAwE;AACxE,mEAAmE;AACnE,SAAS,iBAAiB,CACxB,MAA6E,EAC7E,QAAsC,EACtC,EAAU;IAEV,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,EAAE;QAC/C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAC3C,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CACnC,UAAuC,EACvC,WAAmB;IAEnB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO,CACL,UAAU,CAAC,WAAW,KAAK,WAAW;QACtC,IAAI,KAAK,SAAS;QAClB,OAAO,KAAK,SAAS;QACrB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC;QACnD,OAAO,CAAC,WAAW,KAAK,WAAW;QACnC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,WAAW;QAC5D,CAAC,OAAO,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,KAAK,WAAW,CAAC,CAC1E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,aAAa,CAAC,WAAmB;QAC/B,OAAO,CAAC,KAAc,EAA8C,EAAE,CACpE,mCAAmC,CAAC,KAAK,CAAC;YAC1C,4BAA4B,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,EAAE,qCAAqC,CAAC,MAAM;IACpD,IAAI,CAAC,WAAmB;QACtB,OAAO,oBAAoB,CAAC,qCAAqC,EAAE;YACjE,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,0EAA0E;IAC1E,uEAAuE;IACvE,qEAAqE;IACrE,aAAa,CAAC,UAAkB;QAC9B,OAAO,CAAC,KAAc,EAA6C,EAAE,CACnE,kCAAkC,CAAC,KAAK,CAAC;YACzC,KAAK,CAAC,UAAU,KAAK,UAAU;YAC/B,iBAAiB,CAAC,KAAK,CAAC,gBAAgB,EAAE,YAAY,EAAE,UAAU,CAAC;YACnE,KAAK,CAAC,kBAAkB,CAAC,UAAU,KAAK,UAAU;YAClD,KAAK,CAAC,gBAAgB,CAAC,UAAU,KAAK,UAAU;YAChD,KAAK,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CACnD,4BAA4B,CAAC,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,CACjE,CAAC;IACN,CAAC;IACD,MAAM,EAAE,oCAAoC,CAAC,MAAM;IACnD,IAAI,CAAC,UAAkB;QACrB,OAAO,oBAAoB,CAAC,oCAAoC,EAAE;YAChE,UAAU;SACX,CAAC,CAAC;IACL,CAAC;CACF,CAAC","sourcesContent":["import {\n getContainerWriterProjectionOperation,\n getDocumentWriterProjectionOperation,\n operationRequestPath,\n} from \"@tearleads/validators/operation\";\nimport {\n type ContainerKeyingPathResponse,\n type ContainerWriterProjectionResponse,\n type DocumentWriterProjectionResponse,\n isContainerWriterProjectionResponse,\n isDocumentWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\n\n// Validators run on the decoded projection: the transport resolves the wire's\n// manifest-hash citations into bundles first (writerProjectionRequests).\n// A writer projection is consumed by its leaf: callers wrap keys under\n// whatever container or document the response describes. Binding the\n// validator to the requested id makes a projection for any other object an\n// invalid response, so a server cannot redirect a create, move, share, rekey,\n// or content-key re-wrap onto an object it substituted. The top-level id is a\n// mutable label, so the binding also covers the hash-committed manifest object\n// id, the manifest state the SDK later verifies, and the nested KEK and\n// content-key bundle ids a relabeled projection would still carry.\nfunction manifestDescribes(\n bundle: { manifest: Record<string, unknown>; state: Record<string, unknown> },\n stateKey: \"containerId\" | \"documentId\",\n id: string,\n): boolean {\n return (\n Reflect.get(bundle.manifest, \"objectId\") === id &&\n Reflect.get(bundle.state, stateKey) === id\n );\n}\n\nfunction containerProjectionDescribes(\n projection: ContainerKeyingPathResponse,\n containerId: string,\n): boolean {\n const leaf = projection.path.at(-1);\n const leafKek = projection.containerKeks.at(-1);\n return (\n projection.containerId === containerId &&\n leaf !== undefined &&\n leafKek !== undefined &&\n manifestDescribes(leaf, \"containerId\", containerId) &&\n leafKek.containerId === containerId &&\n Reflect.get(leafKek.keyEpoch, \"containerId\") === containerId &&\n (leafKek.keyring === null || leafKek.keyring.containerId === containerId)\n );\n}\n\nexport const containerWriterProjection = {\n isResponseFor(containerId: string) {\n return (value: unknown): value is ContainerWriterProjectionResponse =>\n isContainerWriterProjectionResponse(value) &&\n containerProjectionDescribes(value, containerId);\n },\n method: getContainerWriterProjectionOperation.method,\n path(containerId: string) {\n return operationRequestPath(getContainerWriterProjectionOperation, {\n containerId,\n });\n },\n};\n\nexport const documentWriterProjection = {\n // The authorizing container paths carry no requested id, but each must at\n // least describe the container it labels itself with, so a substituted\n // path cannot smuggle another container's leaf under a relabeled id.\n isResponseFor(documentId: string) {\n return (value: unknown): value is DocumentWriterProjectionResponse =>\n isDocumentWriterProjectionResponse(value) &&\n value.documentId === documentId &&\n manifestDescribes(value.documentManifest, \"documentId\", documentId) &&\n value.documentKekTargets.documentId === documentId &&\n value.contentKeyBundle.documentId === documentId &&\n value.authorizingContainerPaths.every((projection) =>\n containerProjectionDescribes(projection, projection.containerId),\n );\n },\n method: getDocumentWriterProjectionOperation.method,\n path(documentId: string) {\n return operationRequestPath(getDocumentWriterProjectionOperation, {\n documentId,\n });\n },\n};\n"]}
@@ -0,0 +1,23 @@
1
+ import { type AccessManifestBundleWireResponse } from "../validators/response/index.js";
2
+ /** Citation only saves bytes: a source slower than this sends the read uncited. */
3
+ export declare const MANIFEST_CITATION_DEADLINE_MS = 250;
4
+ export interface WriterProjectionTarget {
5
+ readonly kind: "container" | "document";
6
+ readonly id: string;
7
+ }
8
+ /**
9
+ * Verified access-manifest bundles a device durably holds that a writer
10
+ * projection of `target` may cite, most useful first. This is never trust
11
+ * input: the exact cited bytes only fill the response's omitted positions,
12
+ * and the caller verifies the rebuilt projection exactly as a full one.
13
+ */
14
+ export interface VerifiedManifestCitations {
15
+ cite(target: WriterProjectionTarget): Promise<readonly AccessManifestBundleWireResponse[]>;
16
+ }
17
+ /**
18
+ * The bytes one request cites, copied so a later eviction or mutation of the
19
+ * source cannot change what fills its omissions. Citing only saves bytes, so
20
+ * it never blocks or fails a read: a slow, failing, or abandoned source
21
+ * yields no hints.
22
+ */
23
+ export declare function heldManifestCitations(source: VerifiedManifestCitations | null, target: WriterProjectionTarget, signal?: AbortSignal | undefined): Promise<Map<string, AccessManifestBundleWireResponse>>;