@pipelab/plugin-construct 1.0.0-beta.22 → 1.0.0-beta.23

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.
@@ -0,0 +1,144 @@
1
+ import type { Page } from "playwright";
2
+
3
+ export const registerInstallButtonListener = (page: Page, log: typeof console.log) => {
4
+ const installDialog = page.locator("#addonConfirmInstallDialog");
5
+ const installBtn = installDialog.locator(".okButton");
6
+ installBtn
7
+ .waitFor({
8
+ timeout: 0,
9
+ })
10
+ .then(async () => {
11
+ await installBtn.click();
12
+ log("installBtn clicked");
13
+ registerInstallButtonListener(page, log);
14
+ })
15
+ .catch(async (e) => {
16
+ if (e.message.includes("Target page, context or browser has been closed")) return;
17
+ log("installBtn.click() failed", e.message);
18
+ });
19
+ };
20
+
21
+ export const registerSaveLoginExpiredistener = (page: Page, log: typeof console.log) => {
22
+ const installDialog = page.locator("#confirmDialog");
23
+ const cancelBtn = installDialog.locator(".cancelConfirmButton");
24
+ cancelBtn
25
+ .waitFor({
26
+ timeout: 0,
27
+ })
28
+ .then(async () => {
29
+ await cancelBtn.click();
30
+ log("cancelBtn clicked");
31
+ registerSaveLoginExpiredistener(page, log);
32
+ })
33
+ .catch(async (e) => {
34
+ if (e.message.includes("Target page, context or browser has been closed")) return;
35
+ log("cancelBtn.click() failed", e.message);
36
+ });
37
+ };
38
+
39
+ export const registerWebglErrorListener = (page: Page, log: typeof console.log) => {
40
+ const okDialog = page.locator("#okDialog");
41
+ const webglErrorButton = okDialog.locator(".okButton");
42
+ webglErrorButton
43
+ .waitFor({
44
+ timeout: 0,
45
+ })
46
+ .then(async () => {
47
+ const text = await okDialog.allInnerTexts();
48
+
49
+ if (text.join().toLowerCase().includes("webgl")) {
50
+ await webglErrorButton.click();
51
+ log("webglErrorButton clicked");
52
+ registerWebglErrorListener(page, log);
53
+ }
54
+ })
55
+ .catch(async (e) => {
56
+ if (e.message.includes("Target page, context or browser has been closed")) return;
57
+ log("webglErrorButton.click() failed", e.message);
58
+ });
59
+ };
60
+
61
+ export const registerDeprecatedFeatures = (page: Page, log: typeof console.log) => {
62
+ const deprecatedFeaturesDialog = page.locator("#deprecatedFeaturesDialog");
63
+ const okButton = deprecatedFeaturesDialog.locator(".okButton");
64
+ okButton
65
+ .waitFor({
66
+ timeout: 0,
67
+ })
68
+ .then(async () => {
69
+ await okButton.click();
70
+ log("okButton clicked");
71
+ registerDeprecatedFeatures(page, log);
72
+ })
73
+ .catch(async (e) => {
74
+ if (e.message.includes("Target page, context or browser has been closed")) return;
75
+ log("deprecatedFeatures.okButton.click() failed", e.message);
76
+ });
77
+ };
78
+
79
+ export const registerWelcomeToConstructListener = (page: Page, log: typeof console.log) => {
80
+ const welcomeTourDialog = page.locator("#welcomeTourDialog");
81
+ const okButton = welcomeTourDialog.locator(".noThanksLink");
82
+ okButton
83
+ .waitFor({
84
+ timeout: 0,
85
+ })
86
+ .then(async () => {
87
+ await okButton.click();
88
+ log("okButton clicked");
89
+ })
90
+ .catch(async (e) => {
91
+ if (e.message.includes("Target page, context or browser has been closed")) return;
92
+ log("welcomeTour.okButton.click() failed", e.message);
93
+ });
94
+ };
95
+
96
+ export const registerMissingAddonErrorListener = (page: Page, log: typeof console.log) => {
97
+ const okDialog = page.locator("#missingAddonsDialog");
98
+ const webglErrorButton = okDialog.locator(".okButton");
99
+ webglErrorButton
100
+ .waitFor({
101
+ timeout: 0,
102
+ })
103
+ .then(async () => {
104
+ throw new Error("Missing addon. You should bundle addons with your project");
105
+ })
106
+ .catch(async (e) => {
107
+ if (e.message.includes("Target page, context or browser has been closed")) return;
108
+ log("missingAddon.okButton.waitFor() failed", e.message);
109
+ });
110
+ };
111
+
112
+ export const registerNewVersionAvailableListener = (page: Page, log: typeof console.log) => {
113
+ const newVersionAvailableDialog = page.locator("#confirmDialog");
114
+ const cancelButton = newVersionAvailableDialog.locator(".cancelConfirmButton");
115
+ cancelButton
116
+ .waitFor({
117
+ timeout: 0,
118
+ })
119
+ .then(async () => {
120
+ await cancelButton.click();
121
+ log("cancelButton clicked");
122
+ registerNewVersionAvailableListener(page, log);
123
+ })
124
+ .catch(async (e) => {
125
+ if (e.message.includes("Target page, context or browser has been closed")) return;
126
+ log("cancelButton.click() failed", e.message);
127
+ });
128
+ };
129
+
130
+ export const registerNotNowListener = (page: Page, log: typeof console.log) => {
131
+ const notNowBtn = page.getByText("Not now");
132
+ notNowBtn
133
+ .waitFor({
134
+ timeout: 0,
135
+ })
136
+ .then(async () => {
137
+ await notNowBtn.click();
138
+ log("notNowBtn clicked");
139
+ })
140
+ .catch(async (e) => {
141
+ if (e.message.includes("Target page, context or browser has been closed")) return;
142
+ log("notNowBtn.click() failed", e.message);
143
+ });
144
+ };
@@ -1,145 +1,15 @@
1
1
  import { Page } from "playwright";
2
2
  import { join } from "node:path";
3
-
4
- const registerInstallButtonListener = (page: Page, log: typeof console.log) => {
5
- // as soon as it appear, without blocking flow
6
- // accept installing plugins
7
- const installDialog = page.locator("#addonConfirmInstallDialog");
8
- const installBtn = installDialog.locator(".okButton");
9
- installBtn
10
- .waitFor({
11
- timeout: 0,
12
- })
13
- .then(async () => {
14
- await installBtn.click();
15
- log("installBtn clicked");
16
- registerInstallButtonListener(page, log);
17
- })
18
- .catch(async (e) => {
19
- if (e.message.includes("Target page, context or browser has been closed")) return;
20
- log("installBtn.click() failed", e.message);
21
- });
22
- };
23
-
24
- const registerSaveLoginExpiredistener = (page: Page, log: typeof console.log) => {
25
- // as soon as it appear, without blocking flow
26
- // accept installing plugins
27
- const installDialog = page.locator("#confirmDialog");
28
- const cancelBtn = installDialog.locator(".cancelConfirmButton");
29
- cancelBtn
30
- .waitFor({
31
- timeout: 0,
32
- })
33
- .then(async () => {
34
- await cancelBtn.click();
35
- log("cancelBtn clicked");
36
- registerSaveLoginExpiredistener(page, log);
37
- })
38
- .catch(async (e) => {
39
- if (e.message.includes("Target page, context or browser has been closed")) return;
40
- log("cancelBtn.click() failed", e.message);
41
- });
42
- };
43
-
44
- const registerWebglErrorListener = (page: Page, log: typeof console.log) => {
45
- // as soon as it appear, without blocking flow
46
- // ignore webgl error
47
- const okDialog = page.locator("#okDialog");
48
- const webglErrorButton = okDialog.locator(".okButton");
49
- webglErrorButton
50
- .waitFor({
51
- timeout: 0,
52
- })
53
- .then(async () => {
54
- const text = await okDialog.allInnerTexts();
55
-
56
- if (text.join().toLowerCase().includes("webgl")) {
57
- await webglErrorButton.click();
58
- log("webglErrorButton clicked");
59
- registerWebglErrorListener(page, log);
60
- }
61
- })
62
- .catch(async (e) => {
63
- if (e.message.includes("Target page, context or browser has been closed")) return;
64
- log("webglErrorButton.click() failed", e.message);
65
- });
66
- };
67
- const registerDeprecatedFeatures = (page: Page, log: typeof console.log) => {
68
- // as soon as it appear, without blocking flow
69
- // ignore deprecated feature
70
- const deprecatedFeaturesDialog = page.locator("#deprecatedFeaturesDialog");
71
- const okButton = deprecatedFeaturesDialog.locator(".okButton");
72
- okButton
73
- .waitFor({
74
- timeout: 0,
75
- })
76
- .then(async () => {
77
- await okButton.click();
78
- log("okButton clicked");
79
- registerDeprecatedFeatures(page, log);
80
- })
81
- .catch(async (e) => {
82
- if (e.message.includes("Target page, context or browser has been closed")) return;
83
- log("deprecatedFeatures.okButton.click() failed", e.message);
84
- });
85
- };
86
- const registerWelcomeToConstructListener = (page: Page, log: typeof console.log) => {
87
- // as soon as it appear, without blocking flow
88
- // ignore deprecated feature
89
- const welcomeTourDialog = page.locator("#welcomeTourDialog");
90
- const okButton = welcomeTourDialog.locator(".noThanksLink");
91
- okButton
92
- .waitFor({
93
- timeout: 0,
94
- })
95
- .then(async () => {
96
- await okButton.click();
97
- log("okButton clicked");
98
- // registerWelcomeToConstructListener(page, log); // usually only once
99
- })
100
- .catch(async (e) => {
101
- if (e.message.includes("Target page, context or browser has been closed")) return;
102
- log("welcomeTour.okButton.click() failed", e.message);
103
- });
104
- };
105
-
106
- const registerMissingAddonErrorListener = (page: Page, log: typeof console.log) => {
107
- // as soon as it appear, without blocking flow
108
- // ignore missing addon and throw
109
- const okDialog = page.locator("#missingAddonsDialog");
110
- const webglErrorButton = okDialog.locator(".okButton");
111
- webglErrorButton
112
- .waitFor({
113
- timeout: 0,
114
- })
115
- .then(async () => {
116
- throw new Error("Missing addon. You should bundle addons with your project");
117
- })
118
- .catch(async (e) => {
119
- if (e.message.includes("Target page, context or browser has been closed")) return;
120
- log("missingAddon.okButton.waitFor() failed", e.message);
121
- });
122
- };
123
-
124
- const registerNewVersionAvailableListener = (page: Page, log: typeof console.log) => {
125
- // as soon as it appear, without blocking flow
126
- // ignore new version available and throw
127
- const newVersionAvailableDialog = page.locator("#confirmDialog");
128
- const cancelButton = newVersionAvailableDialog.locator(".cancelConfirmButton");
129
- cancelButton
130
- .waitFor({
131
- timeout: 0,
132
- })
133
- .then(async () => {
134
- await cancelButton.click();
135
- log("cancelButton clicked");
136
- registerNewVersionAvailableListener(page, log);
137
- })
138
- .catch(async (e) => {
139
- if (e.message.includes("Target page, context or browser has been closed")) return;
140
- log("cancelButton.click() failed", e.message);
141
- });
142
- };
3
+ import {
4
+ registerInstallButtonListener,
5
+ registerSaveLoginExpiredistener,
6
+ registerWebglErrorListener,
7
+ registerDeprecatedFeatures,
8
+ registerWelcomeToConstructListener,
9
+ registerMissingAddonErrorListener,
10
+ registerNewVersionAvailableListener,
11
+ registerNotNowListener,
12
+ } from "./listeners.js";
143
13
 
144
14
  export const script = async (
145
15
  page: Page,
@@ -150,64 +20,68 @@ export const script = async (
150
20
  version: string | undefined,
151
21
  downloadDir: string,
152
22
  ) => {
23
+ if (username && password) {
24
+ log("Directly authenticating via Construct 3 account API...");
25
+ const formData = new FormData();
26
+ formData.append("username", username);
27
+ formData.append("password", password);
28
+ formData.append("productType", "games");
29
+
30
+ const res = await fetch("https://account.construct.net/login.json", {
31
+ method: "POST",
32
+ body: formData,
33
+ });
34
+ const json = await res.json() as any;
35
+ if (json.request.status !== "ok") {
36
+ throw new Error(json.request.errorMessage || "Invalid credentials");
37
+ }
38
+
39
+ const { userID, token } = json.response;
40
+ log("API login successful, injecting credentials into browser context...");
41
+
42
+ // Navigate to account.construct.net to set the origin context for IndexedDB
43
+ await page.goto("https://account.construct.net/");
44
+
45
+ // Inject token into IndexedDB
46
+ await page.evaluate(async ({ userID, token }) => {
47
+ return new Promise<void>((resolve, reject) => {
48
+ const request = indexedDB.open("localforage", 1);
49
+ request.onerror = () => reject(new Error("Failed to open DB"));
50
+ request.onsuccess = (e: any) => {
51
+ const db = e.target.result;
52
+ try {
53
+ const transaction = db.transaction(["keyvaluepairs"], "readwrite");
54
+ const store = transaction.objectStore("keyvaluepairs");
55
+ const putRequest = store.put({ userID, token }, "login-data");
56
+ putRequest.onsuccess = () => resolve();
57
+ putRequest.onerror = () => reject(new Error("Failed to put item"));
58
+ } catch (err) {
59
+ reject(err);
60
+ }
61
+ };
62
+ request.onupgradeneeded = (e: any) => {
63
+ const db = e.target.result;
64
+ db.createObjectStore("keyvaluepairs");
65
+ };
66
+ });
67
+ }, { userID, token });
68
+ log("Credentials injected successfully.");
69
+ }
70
+
153
71
  let url = "https://editor.construct.net/";
154
72
  if (version) {
155
73
  url += version;
156
74
  }
157
75
  log("Navigating to URL", url);
158
- // const serviceWorkerPromise = page.waitForEvent("serviceworker");
159
76
  await page.goto(url);
160
77
  log("after navigating");
161
78
 
162
- // const serviceworker = await serviceWorkerPromise;
163
79
  registerWelcomeToConstructListener(page, log);
164
80
  registerNewVersionAvailableListener(page, log);
165
-
166
- // as soon as it appear, without blocking flow
167
- // ignore asking for update
168
- const notNowBtn = page.getByText("Not now");
169
- notNowBtn
170
- .waitFor({
171
- timeout: 0,
172
- })
173
- .then(async () => {
174
- return notNowBtn.click();
175
- })
176
- .then(() => {
177
- log("notNowBtn clicked");
178
- })
179
- .catch(async (e) => {
180
- if (e.message.includes("Target page, context or browser has been closed")) return;
181
- log("notNowBtn.click() failed", e.message);
182
- });
81
+ registerNotNowListener(page, log);
183
82
 
184
83
  log("after event");
185
84
 
186
- await page.waitForTimeout(2000);
187
- log("after wait");
188
-
189
- if (username && password) {
190
- log("Authenticating");
191
- await page.getByTitle("User account").locator("ui-icon").click();
192
- await page.getByRole("menuitem", { name: "Log in" }).locator("span").click();
193
- await page.frameLocator("#loginDialog iframe").getByLabel("Username").fill(username);
194
- await page.frameLocator("#loginDialog iframe").getByLabel("Password").fill(password);
195
-
196
- const tokenPromise = page.waitForResponse(/https:\/\/account.*\.construct\.net\/login.json/i);
197
-
198
- await page.frameLocator("#loginDialog iframe").getByRole("button", { name: "Log in" }).click();
199
-
200
- const response = await tokenPromise;
201
- const jsonResponse = await response.json();
202
-
203
- if (jsonResponse.request.status === "error") {
204
- await page.close();
205
-
206
- throw new Error("Invalid credentials");
207
- }
208
- log("Authenticated");
209
- }
210
-
211
85
  await page.waitForTimeout(2000);
212
86
 
213
87
  const [fileChooser] = await Promise.all([
package/dist/index.cjs CHANGED
@@ -151604,7 +151604,7 @@ const zipFolder = async (from, to, log) => {
151604
151604
  });
151605
151605
  };
151606
151606
  //#endregion
151607
- //#region src/assets/script.ts
151607
+ //#region src/assets/listeners.ts
151608
151608
  const registerInstallButtonListener = (page, log) => {
151609
151609
  const installBtn = page.locator("#addonConfirmInstallDialog").locator(".okButton");
151610
151610
  installBtn.waitFor({ timeout: 0 }).then(async () => {
@@ -151681,40 +151681,69 @@ const registerNewVersionAvailableListener = (page, log) => {
151681
151681
  log("cancelButton.click() failed", e.message);
151682
151682
  });
151683
151683
  };
151684
- const script = async (page, log, filePath, username, password, version, downloadDir) => {
151685
- let url = "https://editor.construct.net/";
151686
- if (version) url += version;
151687
- log("Navigating to URL", url);
151688
- await page.goto(url);
151689
- log("after navigating");
151690
- registerWelcomeToConstructListener(page, log);
151691
- registerNewVersionAvailableListener(page, log);
151684
+ const registerNotNowListener = (page, log) => {
151692
151685
  const notNowBtn = page.getByText("Not now");
151693
151686
  notNowBtn.waitFor({ timeout: 0 }).then(async () => {
151694
- return notNowBtn.click();
151695
- }).then(() => {
151687
+ await notNowBtn.click();
151696
151688
  log("notNowBtn clicked");
151697
151689
  }).catch(async (e) => {
151698
151690
  if (e.message.includes("Target page, context or browser has been closed")) return;
151699
151691
  log("notNowBtn.click() failed", e.message);
151700
151692
  });
151701
- log("after event");
151702
- await page.waitForTimeout(2e3);
151703
- log("after wait");
151693
+ };
151694
+ //#endregion
151695
+ //#region src/assets/script.ts
151696
+ const script = async (page, log, filePath, username, password, version, downloadDir) => {
151704
151697
  if (username && password) {
151705
- log("Authenticating");
151706
- await page.getByTitle("User account").locator("ui-icon").click();
151707
- await page.getByRole("menuitem", { name: "Log in" }).locator("span").click();
151708
- await page.frameLocator("#loginDialog iframe").getByLabel("Username").fill(username);
151709
- await page.frameLocator("#loginDialog iframe").getByLabel("Password").fill(password);
151710
- const tokenPromise = page.waitForResponse(/https:\/\/account.*\.construct\.net\/login.json/i);
151711
- await page.frameLocator("#loginDialog iframe").getByRole("button", { name: "Log in" }).click();
151712
- if ((await (await tokenPromise).json()).request.status === "error") {
151713
- await page.close();
151714
- throw new Error("Invalid credentials");
151715
- }
151716
- log("Authenticated");
151698
+ log("Directly authenticating via Construct 3 account API...");
151699
+ const formData = new FormData();
151700
+ formData.append("username", username);
151701
+ formData.append("password", password);
151702
+ formData.append("productType", "games");
151703
+ const json = await (await fetch("https://account.construct.net/login.json", {
151704
+ method: "POST",
151705
+ body: formData
151706
+ })).json();
151707
+ if (json.request.status !== "ok") throw new Error(json.request.errorMessage || "Invalid credentials");
151708
+ const { userID, token } = json.response;
151709
+ log("API login successful, injecting credentials into browser context...");
151710
+ await page.goto("https://account.construct.net/");
151711
+ await page.evaluate(async ({ userID, token }) => {
151712
+ return new Promise((resolve, reject) => {
151713
+ const request = indexedDB.open("localforage", 1);
151714
+ request.onerror = () => reject(/* @__PURE__ */ new Error("Failed to open DB"));
151715
+ request.onsuccess = (e) => {
151716
+ const db = e.target.result;
151717
+ try {
151718
+ const putRequest = db.transaction(["keyvaluepairs"], "readwrite").objectStore("keyvaluepairs").put({
151719
+ userID,
151720
+ token
151721
+ }, "login-data");
151722
+ putRequest.onsuccess = () => resolve();
151723
+ putRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to put item"));
151724
+ } catch (err) {
151725
+ reject(err);
151726
+ }
151727
+ };
151728
+ request.onupgradeneeded = (e) => {
151729
+ e.target.result.createObjectStore("keyvaluepairs");
151730
+ };
151731
+ });
151732
+ }, {
151733
+ userID,
151734
+ token
151735
+ });
151736
+ log("Credentials injected successfully.");
151717
151737
  }
151738
+ let url = "https://editor.construct.net/";
151739
+ if (version) url += version;
151740
+ log("Navigating to URL", url);
151741
+ await page.goto(url);
151742
+ log("after navigating");
151743
+ registerWelcomeToConstructListener(page, log);
151744
+ registerNewVersionAvailableListener(page, log);
151745
+ registerNotNowListener(page, log);
151746
+ log("after event");
151718
151747
  await page.waitForTimeout(2e3);
151719
151748
  const [fileChooser] = await Promise.all([page.waitForEvent("filechooser"), page.keyboard.press("ControlOrMeta+O")]);
151720
151749
  log("filechooser");
@@ -151772,7 +151801,7 @@ const { LOCALAPPDATA, XDG_CONFIG_HOME } = process.env;
151772
151801
  const isCI = process.env.CI === "true";
151773
151802
  let baseProfile;
151774
151803
  if (platform === "win32") baseProfile = (0, node_path.join)(LOCALAPPDATA ?? "", "Google", "Chrome", "User Data");
151775
- else if (platform === "linux") baseProfile = (0, node_path.join)(XDG_CONFIG_HOME ?? "", "google-chrome");
151804
+ else if (platform === "linux") baseProfile = (0, node_path.join)(XDG_CONFIG_HOME && XDG_CONFIG_HOME.trim() !== "" ? XDG_CONFIG_HOME : (0, node_path.join)((0, node_os.homedir)(), ".config"), "google-chrome");
151776
151805
  else if (platform === "darwin") baseProfile = (0, node_path.join)((0, node_os.homedir)(), "Library", "Application Support", "Google", "Chrome");
151777
151806
  const sharedParams = {
151778
151807
  username: createStringParam("", {
@@ -151867,7 +151896,14 @@ const exportc3p = async (file, { cwd, log, inputs, setOutput, paths, abortSignal
151867
151896
  await (0, node_fs_promises.mkdir)(customProfile, { recursive: true });
151868
151897
  const indexedDbPathSource = (0, node_path.join)(newInputs.customProfile, "Default", "IndexedDB");
151869
151898
  const indexedDbPathDestination = (0, node_path.join)(customProfile, "Default", "IndexedDB");
151870
- for (const p of ["https_editor.construct.net_0.indexeddb.blob", "https_editor.construct.net_0.indexeddb.leveldb"]) await (0, node_fs_promises.cp)((0, node_path.join)(indexedDbPathSource, p), (0, node_path.join)(indexedDbPathDestination, p), { recursive: true });
151899
+ await (0, node_fs_promises.mkdir)(indexedDbPathDestination, { recursive: true });
151900
+ for (const p of ["https_editor.construct.net_0.indexeddb.blob", "https_editor.construct.net_0.indexeddb.leveldb"]) {
151901
+ const from = (0, node_path.join)(indexedDbPathSource, p);
151902
+ const to = (0, node_path.join)(indexedDbPathDestination, p);
151903
+ try {
151904
+ await (0, node_fs_promises.cp)(from, to, { recursive: true });
151905
+ } catch (e) {}
151906
+ }
151871
151907
  browserContext = await browserInstance.launchPersistentContext(customProfile, {
151872
151908
  headless,
151873
151909
  locale: "en-US",
package/dist/index.mjs CHANGED
@@ -151601,7 +151601,7 @@ const zipFolder = async (from, to, log) => {
151601
151601
  });
151602
151602
  };
151603
151603
  //#endregion
151604
- //#region src/assets/script.ts
151604
+ //#region src/assets/listeners.ts
151605
151605
  const registerInstallButtonListener = (page, log) => {
151606
151606
  const installBtn = page.locator("#addonConfirmInstallDialog").locator(".okButton");
151607
151607
  installBtn.waitFor({ timeout: 0 }).then(async () => {
@@ -151678,40 +151678,69 @@ const registerNewVersionAvailableListener = (page, log) => {
151678
151678
  log("cancelButton.click() failed", e.message);
151679
151679
  });
151680
151680
  };
151681
- const script = async (page, log, filePath, username, password, version, downloadDir) => {
151682
- let url = "https://editor.construct.net/";
151683
- if (version) url += version;
151684
- log("Navigating to URL", url);
151685
- await page.goto(url);
151686
- log("after navigating");
151687
- registerWelcomeToConstructListener(page, log);
151688
- registerNewVersionAvailableListener(page, log);
151681
+ const registerNotNowListener = (page, log) => {
151689
151682
  const notNowBtn = page.getByText("Not now");
151690
151683
  notNowBtn.waitFor({ timeout: 0 }).then(async () => {
151691
- return notNowBtn.click();
151692
- }).then(() => {
151684
+ await notNowBtn.click();
151693
151685
  log("notNowBtn clicked");
151694
151686
  }).catch(async (e) => {
151695
151687
  if (e.message.includes("Target page, context or browser has been closed")) return;
151696
151688
  log("notNowBtn.click() failed", e.message);
151697
151689
  });
151698
- log("after event");
151699
- await page.waitForTimeout(2e3);
151700
- log("after wait");
151690
+ };
151691
+ //#endregion
151692
+ //#region src/assets/script.ts
151693
+ const script = async (page, log, filePath, username, password, version, downloadDir) => {
151701
151694
  if (username && password) {
151702
- log("Authenticating");
151703
- await page.getByTitle("User account").locator("ui-icon").click();
151704
- await page.getByRole("menuitem", { name: "Log in" }).locator("span").click();
151705
- await page.frameLocator("#loginDialog iframe").getByLabel("Username").fill(username);
151706
- await page.frameLocator("#loginDialog iframe").getByLabel("Password").fill(password);
151707
- const tokenPromise = page.waitForResponse(/https:\/\/account.*\.construct\.net\/login.json/i);
151708
- await page.frameLocator("#loginDialog iframe").getByRole("button", { name: "Log in" }).click();
151709
- if ((await (await tokenPromise).json()).request.status === "error") {
151710
- await page.close();
151711
- throw new Error("Invalid credentials");
151712
- }
151713
- log("Authenticated");
151695
+ log("Directly authenticating via Construct 3 account API...");
151696
+ const formData = new FormData();
151697
+ formData.append("username", username);
151698
+ formData.append("password", password);
151699
+ formData.append("productType", "games");
151700
+ const json = await (await fetch("https://account.construct.net/login.json", {
151701
+ method: "POST",
151702
+ body: formData
151703
+ })).json();
151704
+ if (json.request.status !== "ok") throw new Error(json.request.errorMessage || "Invalid credentials");
151705
+ const { userID, token } = json.response;
151706
+ log("API login successful, injecting credentials into browser context...");
151707
+ await page.goto("https://account.construct.net/");
151708
+ await page.evaluate(async ({ userID, token }) => {
151709
+ return new Promise((resolve, reject) => {
151710
+ const request = indexedDB.open("localforage", 1);
151711
+ request.onerror = () => reject(/* @__PURE__ */ new Error("Failed to open DB"));
151712
+ request.onsuccess = (e) => {
151713
+ const db = e.target.result;
151714
+ try {
151715
+ const putRequest = db.transaction(["keyvaluepairs"], "readwrite").objectStore("keyvaluepairs").put({
151716
+ userID,
151717
+ token
151718
+ }, "login-data");
151719
+ putRequest.onsuccess = () => resolve();
151720
+ putRequest.onerror = () => reject(/* @__PURE__ */ new Error("Failed to put item"));
151721
+ } catch (err) {
151722
+ reject(err);
151723
+ }
151724
+ };
151725
+ request.onupgradeneeded = (e) => {
151726
+ e.target.result.createObjectStore("keyvaluepairs");
151727
+ };
151728
+ });
151729
+ }, {
151730
+ userID,
151731
+ token
151732
+ });
151733
+ log("Credentials injected successfully.");
151714
151734
  }
151735
+ let url = "https://editor.construct.net/";
151736
+ if (version) url += version;
151737
+ log("Navigating to URL", url);
151738
+ await page.goto(url);
151739
+ log("after navigating");
151740
+ registerWelcomeToConstructListener(page, log);
151741
+ registerNewVersionAvailableListener(page, log);
151742
+ registerNotNowListener(page, log);
151743
+ log("after event");
151715
151744
  await page.waitForTimeout(2e3);
151716
151745
  const [fileChooser] = await Promise.all([page.waitForEvent("filechooser"), page.keyboard.press("ControlOrMeta+O")]);
151717
151746
  log("filechooser");
@@ -151769,7 +151798,7 @@ const { LOCALAPPDATA, XDG_CONFIG_HOME } = process.env;
151769
151798
  const isCI = process.env.CI === "true";
151770
151799
  let baseProfile;
151771
151800
  if (platform$1 === "win32") baseProfile = join(LOCALAPPDATA ?? "", "Google", "Chrome", "User Data");
151772
- else if (platform$1 === "linux") baseProfile = join(XDG_CONFIG_HOME ?? "", "google-chrome");
151801
+ else if (platform$1 === "linux") baseProfile = join(XDG_CONFIG_HOME && XDG_CONFIG_HOME.trim() !== "" ? XDG_CONFIG_HOME : join(homedir(), ".config"), "google-chrome");
151773
151802
  else if (platform$1 === "darwin") baseProfile = join(homedir(), "Library", "Application Support", "Google", "Chrome");
151774
151803
  const sharedParams = {
151775
151804
  username: createStringParam("", {
@@ -151864,7 +151893,14 @@ const exportc3p = async (file, { cwd, log, inputs, setOutput, paths, abortSignal
151864
151893
  await mkdir(customProfile, { recursive: true });
151865
151894
  const indexedDbPathSource = join(newInputs.customProfile, "Default", "IndexedDB");
151866
151895
  const indexedDbPathDestination = join(customProfile, "Default", "IndexedDB");
151867
- for (const p of ["https_editor.construct.net_0.indexeddb.blob", "https_editor.construct.net_0.indexeddb.leveldb"]) await cp(join(indexedDbPathSource, p), join(indexedDbPathDestination, p), { recursive: true });
151896
+ await mkdir(indexedDbPathDestination, { recursive: true });
151897
+ for (const p of ["https_editor.construct.net_0.indexeddb.blob", "https_editor.construct.net_0.indexeddb.leveldb"]) {
151898
+ const from = join(indexedDbPathSource, p);
151899
+ const to = join(indexedDbPathDestination, p);
151900
+ try {
151901
+ await cp(from, to, { recursive: true });
151902
+ } catch (e) {}
151903
+ }
151868
151904
  browserContext = await browserInstance.launchPersistentContext(customProfile, {
151869
151905
  headless,
151870
151906
  locale: "en-US",