@farm.js/partytown 0.1.0-beta.0 → 0.1.0-beta.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAgB/B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GACb,MAAM,CAeR;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAMnF;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CA8BrE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUnE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnF"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GACb,MAAM,CAeR;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAMnF;AAcD,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CA8BrE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUnE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnF"}
package/dist/build.js CHANGED
@@ -1,4 +1,4 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { lstat, readFile, realpath, rm, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { partytownSnippet } from "@qwik.dev/partytown/integration";
4
4
  import { copyLibFiles, libDirPath } from "@qwik.dev/partytown/utils";
@@ -10,6 +10,10 @@ export async function writePartytownBuildArtifacts(input) {
10
10
  const relativeAssetsDir = path.posix.join(basePath.slice(1), PARTYTOWN_DIRECTORY);
11
11
  const assetsDir = path.join(publicDir, ...relativeAssetsDir.split("/"));
12
12
  const debug = input.options.debug ?? false;
13
+ await assertAssetsPathInsidePublicDir(publicDir, assetsDir);
14
+ // This directory is entirely plugin-owned. Recreate it so stale files and
15
+ // nested symlinks cannot redirect copyFile outside the verified destination.
16
+ await rm(assetsDir, { recursive: true, force: true });
13
17
  await copyLibFiles(assetsDir, { debugDir: debug });
14
18
  const bootstrapPath = path.join(assetsDir, PARTYTOWN_BOOTSTRAP);
15
19
  await writeFile(bootstrapPath, createPartytownBootstrap(input.options, basePath, false), "utf8");
@@ -36,7 +40,7 @@ export function createPartytownBootstrap(options, basePath, isDev) {
36
40
  return `${partytownSnippet(config)}\n`;
37
41
  }
38
42
  export function injectPartytownBootstrap(html, bootstrapUrl) {
39
- if (html.includes("data-farm-partytown"))
43
+ if (hasPartytownBootstrap(html))
40
44
  return html;
41
45
  const closingHead = html.search(/<\/head\s*>/i);
42
46
  if (closingHead < 0)
@@ -44,6 +48,18 @@ export function injectPartytownBootstrap(html, bootstrapUrl) {
44
48
  const tag = `<script src="${escapeHtmlAttribute(bootstrapUrl)}" data-farm-partytown></script>`;
45
49
  return `${html.slice(0, closingHead)}${tag}${html.slice(closingHead)}`;
46
50
  }
51
+ function hasPartytownBootstrap(html) {
52
+ const scriptTags = html.matchAll(/<script\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi);
53
+ for (const tag of scriptTags) {
54
+ const attributes = tag[0].slice("<script".length, -1);
55
+ const parsed = attributes.matchAll(/([^\s"'<>=]+)(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>]+))?/g);
56
+ for (const attribute of parsed) {
57
+ if (attribute[1]?.toLowerCase() === "data-farm-partytown")
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ }
47
63
  export async function readPartytownDevAsset(pathname, basePath, options) {
48
64
  const prefix = partytownAssetUrl("", basePath);
49
65
  if (!pathname.startsWith(prefix))
@@ -99,6 +115,46 @@ export function partytownAssetUrl(file, basePath) {
99
115
  export function resolvePartytownPublicDir(outputDir, preset) {
100
116
  return path.join(outputDir, preset.startsWith("vercel") ? "static" : "public");
101
117
  }
118
+ async function assertAssetsPathInsidePublicDir(publicDir, assetsDir) {
119
+ const prospectivePublicDir = await resolveProspectiveRealPath(publicDir);
120
+ const prospectiveAssetsDir = await resolveProspectiveRealPath(assetsDir);
121
+ const relativePath = path.relative(prospectivePublicDir, prospectiveAssetsDir);
122
+ if (relativePath === ".." ||
123
+ relativePath.startsWith(`..${path.sep}`) ||
124
+ path.isAbsolute(relativePath)) {
125
+ throw new Error("[farm:partytown] Asset output must stay inside the public output directory, including through symlinks.");
126
+ }
127
+ }
128
+ async function resolveProspectiveRealPath(candidate) {
129
+ const missingSegments = [];
130
+ let existingAncestor = path.resolve(candidate);
131
+ while (true) {
132
+ try {
133
+ return path.join(await realpath(existingAncestor), ...missingSegments);
134
+ }
135
+ catch (error) {
136
+ if (error.code !== "ENOENT")
137
+ throw error;
138
+ let existingEntry = false;
139
+ try {
140
+ await lstat(existingAncestor);
141
+ existingEntry = true;
142
+ }
143
+ catch (statError) {
144
+ if (statError.code !== "ENOENT")
145
+ throw statError;
146
+ }
147
+ if (existingEntry) {
148
+ throw new Error("[farm:partytown] Asset output must stay inside the public output directory, including through symlinks.");
149
+ }
150
+ const parent = path.dirname(existingAncestor);
151
+ if (parent === existingAncestor)
152
+ throw error;
153
+ missingSegments.unshift(path.basename(existingAncestor));
154
+ existingAncestor = parent;
155
+ }
156
+ }
157
+ }
102
158
  function contentTypeFor(file) {
103
159
  if (file.endsWith(".js"))
104
160
  return "text/javascript; charset=utf-8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/partytown",
3
- "version": "0.1.0-beta.0",
3
+ "version": "0.1.0-beta.1",
4
4
  "description": "Run opted-in third-party scripts in a web worker in Farm.js applications",
5
5
  "keywords": [
6
6
  "analytics",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@qwik.dev/partytown": "^0.14.4",
40
- "@farm.js/core": "0.1.0-beta.91"
40
+ "@farm.js/core": "0.1.0-beta.93"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^20.10.5",