@strapi/data-transfer 5.0.0-rc.8 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2550,6 +2550,17 @@ const connectToWebsocket = (address, options) => {
2550
2550
  const trimTrailingSlash = (input) => {
2551
2551
  return input.replace(/\/$/, "");
2552
2552
  };
2553
+ const wait = (ms) => {
2554
+ return new Promise((resolve) => {
2555
+ setTimeout(resolve, ms);
2556
+ });
2557
+ };
2558
+ const waitUntil = async (test, interval) => {
2559
+ while (!test()) {
2560
+ await wait(interval);
2561
+ }
2562
+ return Promise.resolve();
2563
+ };
2553
2564
  const TRANSFER_PATH = "/transfer/runner";
2554
2565
  const TRANSFER_METHODS = ["push", "pull"];
2555
2566
  const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -2875,32 +2886,105 @@ class RemoteStrapiSourceProvider {
2875
2886
  });
2876
2887
  };
2877
2888
  async createAssetsReadStream() {
2878
- const assets = {};
2879
2889
  const stream2 = await this.#createStageReadStream("assets");
2880
2890
  const pass = new PassThrough({ objectMode: true });
2891
+ const assets = {};
2881
2892
  stream2.on("data", async (payload) => {
2882
2893
  for (const item of payload) {
2883
- const { action } = item;
2894
+ const { action, assetID } = item;
2884
2895
  if (action === "start") {
2885
- assets[item.assetID] = { ...item.data, stream: new PassThrough() };
2886
- await this.writeAsync(pass, assets[item.assetID]);
2896
+ if (assets[assetID]) {
2897
+ continue;
2898
+ }
2899
+ assets[assetID] = {
2900
+ ...item.data,
2901
+ stream: new PassThrough(),
2902
+ status: "idle",
2903
+ queue: []
2904
+ };
2905
+ await this.writeAsync(pass, assets[assetID]);
2887
2906
  } else if (action === "stream") {
2888
- const rawBuffer = item.data;
2889
- const chunk = Buffer.from(rawBuffer.data);
2890
- await this.writeAsync(assets[item.assetID].stream, chunk);
2907
+ if (!assets[assetID]) {
2908
+ continue;
2909
+ }
2910
+ switch (assets[assetID].status) {
2911
+ case "idle":
2912
+ await writeAssetChunk(assetID, item.data);
2913
+ break;
2914
+ case "busy":
2915
+ assets[assetID].queue.push(item);
2916
+ break;
2917
+ }
2891
2918
  } else if (action === "end") {
2892
- await new Promise((resolve, reject2) => {
2893
- const { stream: assetStream } = assets[item.assetID];
2894
- assetStream.on("close", () => {
2895
- delete assets[item.assetID];
2896
- resolve();
2897
- }).on("error", reject2).end();
2898
- });
2919
+ if (!assets[assetID]) {
2920
+ continue;
2921
+ }
2922
+ switch (assets[assetID].status) {
2923
+ case "idle":
2924
+ case "errored":
2925
+ await closeAssetStream(assetID);
2926
+ break;
2927
+ case "busy":
2928
+ await Promise.race([
2929
+ // Either: wait for the asset to be ready to be closed
2930
+ waitUntil(() => assets[assetID].status !== "busy", 100),
2931
+ // Or: if the last chunks are still not processed after ten seconds
2932
+ wait(1e4)
2933
+ ]);
2934
+ await closeAssetStream(assetID);
2935
+ break;
2936
+ }
2899
2937
  }
2900
2938
  }
2901
2939
  }).on("close", () => {
2902
2940
  pass.end();
2903
2941
  });
2942
+ const writeAssetChunk = async (id, data) => {
2943
+ if (!assets[id]) {
2944
+ throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
2945
+ }
2946
+ const { status: currentStatus } = assets[id];
2947
+ if (currentStatus !== "idle") {
2948
+ throw new Error(
2949
+ `Failed to write asset chunk for "${id}". The asset is currently "${currentStatus}"`
2950
+ );
2951
+ }
2952
+ const nextItemInQueue = () => assets[id].queue.shift();
2953
+ try {
2954
+ assets[id].status = "busy";
2955
+ await unsafe_writeAssetChunk(id, data);
2956
+ let item = nextItemInQueue();
2957
+ while (item) {
2958
+ await unsafe_writeAssetChunk(id, item.data);
2959
+ item = nextItemInQueue();
2960
+ }
2961
+ assets[id].status = "idle";
2962
+ } catch {
2963
+ assets[id].status = "errored";
2964
+ }
2965
+ };
2966
+ const unsafe_writeAssetChunk = async (id, data) => {
2967
+ const asset = assets[id];
2968
+ if (!asset) {
2969
+ throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
2970
+ }
2971
+ const rawBuffer = data;
2972
+ const chunk = Buffer.from(rawBuffer.data);
2973
+ await this.writeAsync(asset.stream, chunk);
2974
+ };
2975
+ const closeAssetStream = async (id) => {
2976
+ if (!assets[id]) {
2977
+ throw new Error(`Failed to close asset "${id}". Asset not found.`);
2978
+ }
2979
+ assets[id].status = "closed";
2980
+ await new Promise((resolve, reject2) => {
2981
+ const { stream: stream22 } = assets[id];
2982
+ stream22.on("close", () => {
2983
+ delete assets[id];
2984
+ resolve();
2985
+ }).on("error", reject2).end();
2986
+ });
2987
+ };
2904
2988
  return pass;
2905
2989
  }
2906
2990
  createConfigurationReadStream() {