@strapi/data-transfer 4.25.10 → 4.25.12

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
@@ -2541,6 +2541,17 @@ const connectToWebsocket = (address, options) => {
2541
2541
  const trimTrailingSlash = (input) => {
2542
2542
  return input.replace(/\/$/, "");
2543
2543
  };
2544
+ const wait = (ms) => {
2545
+ return new Promise((resolve) => {
2546
+ setTimeout(resolve, ms);
2547
+ });
2548
+ };
2549
+ const waitUntil = async (test, interval) => {
2550
+ while (!test()) {
2551
+ await wait(interval);
2552
+ }
2553
+ return Promise.resolve();
2554
+ };
2544
2555
  const TRANSFER_PATH = "/transfer/runner";
2545
2556
  const TRANSFER_METHODS = ["push", "pull"];
2546
2557
  const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -2866,32 +2877,105 @@ class RemoteStrapiSourceProvider {
2866
2877
  });
2867
2878
  };
2868
2879
  async createAssetsReadStream() {
2869
- const assets = {};
2870
2880
  const stream2 = await this.#createStageReadStream("assets");
2871
2881
  const pass = new PassThrough({ objectMode: true });
2882
+ const assets = {};
2872
2883
  stream2.on("data", async (payload) => {
2873
2884
  for (const item of payload) {
2874
- const { action: action2 } = item;
2885
+ const { action: action2, assetID } = item;
2875
2886
  if (action2 === "start") {
2876
- assets[item.assetID] = { ...item.data, stream: new PassThrough() };
2877
- await this.writeAsync(pass, assets[item.assetID]);
2887
+ if (assets[assetID]) {
2888
+ continue;
2889
+ }
2890
+ assets[assetID] = {
2891
+ ...item.data,
2892
+ stream: new PassThrough(),
2893
+ status: "idle",
2894
+ queue: []
2895
+ };
2896
+ await this.writeAsync(pass, assets[assetID]);
2878
2897
  } else if (action2 === "stream") {
2879
- const rawBuffer = item.data;
2880
- const chunk = Buffer.from(rawBuffer.data);
2881
- await this.writeAsync(assets[item.assetID].stream, chunk);
2898
+ if (!assets[assetID]) {
2899
+ continue;
2900
+ }
2901
+ switch (assets[assetID].status) {
2902
+ case "idle":
2903
+ await writeAssetChunk(assetID, item.data);
2904
+ break;
2905
+ case "busy":
2906
+ assets[assetID].queue.push(item);
2907
+ break;
2908
+ }
2882
2909
  } else if (action2 === "end") {
2883
- await new Promise((resolve, reject2) => {
2884
- const { stream: assetStream } = assets[item.assetID];
2885
- assetStream.on("close", () => {
2886
- delete assets[item.assetID];
2887
- resolve();
2888
- }).on("error", reject2).end();
2889
- });
2910
+ if (!assets[assetID]) {
2911
+ continue;
2912
+ }
2913
+ switch (assets[assetID].status) {
2914
+ case "idle":
2915
+ case "errored":
2916
+ await closeAssetStream(assetID);
2917
+ break;
2918
+ case "busy":
2919
+ await Promise.race([
2920
+ // Either: wait for the asset to be ready to be closed
2921
+ waitUntil(() => assets[assetID].status !== "busy", 100),
2922
+ // Or: if the last chunks are still not processed after ten seconds
2923
+ wait(1e4)
2924
+ ]);
2925
+ await closeAssetStream(assetID);
2926
+ break;
2927
+ }
2890
2928
  }
2891
2929
  }
2892
2930
  }).on("close", () => {
2893
2931
  pass.end();
2894
2932
  });
2933
+ const writeAssetChunk = async (id, data) => {
2934
+ if (!assets[id]) {
2935
+ throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
2936
+ }
2937
+ const { status: currentStatus } = assets[id];
2938
+ if (currentStatus !== "idle") {
2939
+ throw new Error(
2940
+ `Failed to write asset chunk for "${id}". The asset is currently "${currentStatus}"`
2941
+ );
2942
+ }
2943
+ const nextItemInQueue = () => assets[id].queue.shift();
2944
+ try {
2945
+ assets[id].status = "busy";
2946
+ await unsafe_writeAssetChunk(id, data);
2947
+ let item = nextItemInQueue();
2948
+ while (item) {
2949
+ await unsafe_writeAssetChunk(id, item.data);
2950
+ item = nextItemInQueue();
2951
+ }
2952
+ assets[id].status = "idle";
2953
+ } catch {
2954
+ assets[id].status = "errored";
2955
+ }
2956
+ };
2957
+ const unsafe_writeAssetChunk = async (id, data) => {
2958
+ const asset = assets[id];
2959
+ if (!asset) {
2960
+ throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
2961
+ }
2962
+ const rawBuffer = data;
2963
+ const chunk = Buffer.from(rawBuffer.data);
2964
+ await this.writeAsync(asset.stream, chunk);
2965
+ };
2966
+ const closeAssetStream = async (id) => {
2967
+ if (!assets[id]) {
2968
+ throw new Error(`Failed to close asset "${id}". Asset not found.`);
2969
+ }
2970
+ assets[id].status = "closed";
2971
+ await new Promise((resolve, reject2) => {
2972
+ const { stream: stream22 } = assets[id];
2973
+ stream22.on("close", () => {
2974
+ delete assets[id];
2975
+ resolve();
2976
+ }).on("error", reject2).end();
2977
+ });
2978
+ };
2895
2979
  return pass;
2896
2980
  }
2897
2981
  createConfigurationReadStream() {
@@ -2965,8 +3049,8 @@ class RemoteStrapiSourceProvider {
2965
3049
  async getSchemas() {
2966
3050
  const schemas = await this.dispatcher?.dispatchTransferAction(
2967
3051
  "getSchemas"
2968
- ) ?? null;
2969
- return schemas;
3052
+ );
3053
+ return schemas ?? null;
2970
3054
  }
2971
3055
  async #startStep(step) {
2972
3056
  try {