natelindev / tsdav

WebDAV, CALDAV, and CARDDAV client for Nodejs and the Browser
https://tsdav.vercel.app
MIT License
225 stars 36 forks source link

tsdav not compatible with Vite #188

Closed pate313373 closed 2 months ago

pate313373 commented 5 months ago

Hi! I have successfully run my code as a VanillaJS script, but was unable to run the exact same code, which was bundled via Vite in a backround script of a webextension. Steps I did: 1) Created a DAVClient 2) Logged in 3) Ran fetchCalendars()

Bundling the exact same code into a Vue app with vite outputs the following error: TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Illegal invocation

  var _a;
  const { url, init, convertIncoming = true, parseOutgoing = true } = params;
  const { headers = {}, body, namespace, method, attributes } = init;
  const xmlBody = convertIncoming ? convert.js2xml(Object.assign(Object.assign({ _declaration: { _attributes: { version: "1.0", encoding: "utf-8" } } }, body), { _attributes: attributes }), {
    compact: true,
    spaces: 2,
    elementNameFn: (name) => {
      if (namespace && !/^.+:.+/.test(name)) {
        return `${namespace}:${name}`;
      }
      return name;
    }
  }) : body;
//////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////// FAILS HERE ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
  const davResponse = yield browserPonyfillExports.fetch(url, {
    headers: Object.assign({ "Content-Type": "text/xml;charset=UTF-8" }, cleanupFalsy(headers)),
    body: xmlBody,
    method
  });
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
  const resText = yield davResponse.text();
  if (!davResponse.ok || !((_a = davResponse.headers.get("content-type")) === null || _a === void 0 ? void 0 : _a.includes("xml")) || !parseOutgoing) {
    return [
      {
        href: davResponse.url,
        ok: davResponse.ok,
        status: davResponse.status,
        statusText: davResponse.statusText,
        raw: resText
      }
    ];
  }
  const result = convert.xml2js(resText, {
    compact: true,
    trim: true,
    textFn: (value, parentElement) => {
      try {
        const parentOfParent = parentElement._parent;
        const pOpKeys = Object.keys(parentOfParent);
        const keyNo = pOpKeys.length;
        const keyName = pOpKeys[keyNo - 1];
        const arrOfKey = parentOfParent[keyName];
        const arrOfKeyLen = arrOfKey.length;
        if (arrOfKeyLen > 0) {
          const arr = arrOfKey;
          const arrIndex = arrOfKey.length - 1;
          arr[arrIndex] = nativeType(value);
        } else {
          parentOfParent[keyName] = nativeType(value);
        }
      } catch (e) {
        debug$5(e.stack);
      }
    },
    // remove namespace & camelCase
    elementNameFn: (attributeName) => camelCase$1(attributeName.replace(/^.+:/, "")),
    attributesFn: (value) => {
      const newVal = Object.assign({}, value);
      delete newVal.xmlns;
      return newVal;
    },
    ignoreDeclaration: true
  });
  const responseBodies = Array.isArray(result.multistatus.response) ? result.multistatus.response : [result.multistatus.response];
  return responseBodies.map((responseBody) => {
    var _a2, _b;
    const statusRegex = /^\S+\s(?<status>\d+)\s(?<statusText>.+)$/;
    if (!responseBody) {
      return {
        status: davResponse.status,
        statusText: davResponse.statusText,
        ok: davResponse.ok
      };
    }
    const matchArr = statusRegex.exec(responseBody.status);
    return {
      raw: result,
      href: responseBody.href,
      status: (matchArr === null || matchArr === void 0 ? void 0 : matchArr.groups) ? Number.parseInt(matchArr === null || matchArr === void 0 ? void 0 : matchArr.groups.status, 10) : davResponse.status,
      statusText: (_b = (_a2 = matchArr === null || matchArr === void 0 ? void 0 : matchArr.groups) === null || _a2 === void 0 ? void 0 : _a2.statusText) !== null && _b !== void 0 ? _b : davResponse.statusText,
      ok: !responseBody.error,
      error: responseBody.error,
      responsedescription: responseBody.responsedescription,
      props: (Array.isArray(responseBody.propstat) ? responseBody.propstat : [responseBody.propstat]).reduce((prev, curr) => {
        return Object.assign(Object.assign({}, prev), curr === null || curr === void 0 ? void 0 : curr.prop);
      }, {})
    };
  });
});

I have highlighted the line where it fails with a comment. I'd highly appreciate your help!

Kind regards

natelindev commented 4 months ago

I'll investigate

pate313373 commented 4 months ago

Cool thank you! I'll provide you with a full sample project for your convenience later today.

natelindev commented 4 months ago

I created a new vite app and tried to use fetchCalendars and I can't seem to be able to reproduce this issue. I'm using vite v5.1.4 with tsdav v2.0.9

pate313373 commented 4 months ago

The issue is regarding bundling as a browser extension. I have created a demo for you here.

Just run the following to get started:

npm i
npm run watch
npm run serve:chrome

Open chrome://extensions/ in chrome and enable dev mode on the top right. Finally click on "Inspect views service worker (Inactive)" to see the issue.

Many thanks!

Sekki21956 commented 4 months ago

I don't know if this is related but I am also having issues with Vite: Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation

├── @logseq/libs@0.0.17
├── @playwright/test@1.43.1
├── @rushstack/eslint-patch@1.10.2
├── @vitejs/plugin-vue@5.0.4
├── @vue/eslint-config-prettier@9.0.0
├── @vue/test-utils@2.4.5
├── eslint-plugin-playwright@1.6.0
├── eslint-plugin-vue@9.25.0
├── eslint@8.57.0
├── jsdom@24.0.0
├── prettier@3.2.5
├── tsdav@2.0.9
├── typerscript@0.2.3
├── vite-plugin-vue-devtools@7.1.3
├── vite@5.2.11
├── vitest@1.6.0
└── vue@3.4.26

I have also tried building with vite@5.1.4 but the issue persists.

I'm using this inside a new Javascript based Vue project in which I'm experimenting for a Logseq plugin on a local Baikal server.

logseq.Editor.registerSlashCommand("Login CalDAV", async () => {
const client = await createDAVClient({
    serverUrl: "http://localhost:8080/dav.php",
    credentials: {
        username: "username",
        password: "password",
    },
    authMethod: "Digest",
    defaultAccountType: "caldav",
});
await client.login();
});

For me the Problem seems to be from request.ts/davRequest where the fetch is failing:

const davResponse = yield browserPonyfillExports.fetch(url, { // Fails here
  headers: Object.assign({ "Content-Type": "text/xml;charset=UTF-8" }, cleanupFalsy(headers)),
  body: xmlBody,
  method
});

It seems that the Polyfills causes some issues with Vite in the past https://github.com/vitejs/vite/issues/7384 I'll be honest, I have no clue what that is so I just wanted to leave this here in case someone does ;)

natelindev commented 3 months ago

I believe it's more of a chrome extension thing, you can try using esm version of the tsdav by copying the code from https://github.com/natelindev/tsdav/blob/master/dist/tsdav.esm.js directly into your own repo. and try it out.

The transform/tanspile process of rollup might have some issues with vite.

pate313373 commented 3 weeks ago

I have finally figured out, that the issue was with Vite, so I switched to webpack and it's working like a charm now! So if you wanna build a chrome web extension with Vue, I can only highly recommend to use webpack.

Many thanks @natelindev for your help and the awesome project!