@mcpher/gas-fakes 2.3.11 → 2.3.13

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.
Files changed (46) hide show
  1. package/README.md +14 -14
  2. package/gas-fakes.js +1 -0
  3. package/gf_agent/scripts/builder.js +26 -1
  4. package/package.json +1 -1
  5. package/src/cli/lib-manager.js +14 -4
  6. package/src/cli/setup.js +17 -4
  7. package/src/services/chartsapp/fakechartsapp.js +6 -1
  8. package/src/services/driveapp/driveiterators.js +53 -8
  9. package/src/services/driveapp/fakedriveapp.js +49 -15
  10. package/src/services/driveapp/fakedrivefile.js +105 -0
  11. package/src/services/driveapp/fakedrivefolder.js +8 -0
  12. package/src/services/driveapp/fakedrivemeta.js +68 -16
  13. package/src/services/driveapp/fakefolderapp.js +19 -6
  14. package/src/services/enums/chartsenums.js +53 -20
  15. package/src/services/enums/driveenums.js +1 -0
  16. package/src/services/slidesapp/fakeautofit.js +23 -15
  17. package/src/services/slidesapp/fakecolorscheme.js +160 -0
  18. package/src/services/slidesapp/fakelayout.js +11 -1
  19. package/src/services/slidesapp/fakemaster.js +10 -0
  20. package/src/services/slidesapp/fakepresentation.js +27 -0
  21. package/src/services/slidesapp/fakeslide.js +9 -0
  22. package/src/services/slidesapp/faketextrange.js +6 -11
  23. package/src/services/spreadsheetapp/chartenummapping.js +15 -0
  24. package/src/services/spreadsheetapp/fakebooleancondition.js +119 -0
  25. package/src/services/spreadsheetapp/fakecellimage.js +42 -0
  26. package/src/services/spreadsheetapp/fakecellimagebuilder.js +59 -0
  27. package/src/services/spreadsheetapp/fakeconditionalformatrule.js +55 -0
  28. package/src/services/spreadsheetapp/fakeconditionalformatrulebuilder.js +330 -0
  29. package/src/services/spreadsheetapp/fakedevelopermetadata.js +32 -4
  30. package/src/services/spreadsheetapp/fakedevelopermetadatalocation.js +27 -5
  31. package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +155 -21
  32. package/src/services/spreadsheetapp/fakegradientcondition.js +71 -0
  33. package/src/services/spreadsheetapp/fakesheet.js +63 -0
  34. package/src/services/spreadsheetapp/fakesheetrange.js +12 -1
  35. package/src/services/spreadsheetapp/fakespreadsheet.js +30 -11
  36. package/src/services/spreadsheetapp/fakespreadsheetapp.js +21 -3
  37. package/src/services/urlfetchapp/app.js +33 -1
  38. package/src/support/fileiterators.js +3 -1
  39. package/src/support/filesharers.js +7 -3
  40. package/src/support/peeker.js +8 -2
  41. package/src/support/sheetutils.js +1 -0
  42. package/src/support/sxdrive.js +26 -15
  43. package/src/support/syncit.js +4 -6
  44. package/src/support/workersync/synchronizer.js +24 -4
  45. package/src/support/workersync/worker.js +13 -2
  46. package/summarize_advanced.js +0 -69
@@ -8,13 +8,19 @@ class Peeker {
8
8
  /**
9
9
  * @constructor
10
10
  * @param {function} generator the generator function to add a hasNext() to
11
+ * @param {function} [continuationHandler] a function to return a continuation token
11
12
  * @returns {Peeker}
12
13
  */
13
- constructor(generator) {
14
+ constructor(generator, continuationHandler) {
14
15
  this.generator = generator
15
16
  // in order to be able to do a hasnext we have to actually get the value
16
17
  // this is the next value stored
17
18
  this.peeked = generator.next()
19
+ this.continuationHandler = continuationHandler
20
+ }
21
+
22
+ getContinuationToken() {
23
+ return this.continuationHandler ? this.continuationHandler(this.peeked) : null
18
24
  }
19
25
 
20
26
  /**
@@ -37,7 +43,7 @@ class Peeker {
37
43
  // instead of returning the next, we return the prepeeked next
38
44
  const value = this.peeked.value
39
45
  this.peeked = this.generator.next()
40
- return value
46
+ return value?.__fakeResolved ?? value
41
47
  }
42
48
  }
43
49
 
@@ -256,4 +256,5 @@ export const SheetUtils = {
256
256
  fromRange,
257
257
  a1ToR1C1,
258
258
  r1c1ToA1,
259
+ columnToLetter,
259
260
  }
@@ -550,25 +550,36 @@ const sxStreamer = async ({
550
550
  params,
551
551
  options = {},
552
552
  method = 'get' }) => {
553
- // this is the node drive service
554
- const drive = getDriveApiClient();
555
- const streamed = await drive.files[method](params, {
556
- responseType: 'stream',
557
- ...options
558
- })
559
- const response = responseSyncify(streamed)
560
- if (response.status === 200) {
561
- const buf = await getStreamAsBuffer(streamed.data)
562
- const data = Array.from(buf)
553
+ try {
554
+ // this is the node drive service
555
+ const drive = getDriveApiClient();
556
+ const streamed = await drive.files[method](params, {
557
+ responseType: 'stream',
558
+ ...options
559
+ })
560
+ const response = responseSyncify(streamed)
561
+ if (response.status === 200) {
562
+ const buf = await getStreamAsBuffer(streamed.data)
563
+ const data = Array.from(buf)
563
564
 
564
- return {
565
- data,
566
- response
565
+ return {
566
+ data,
567
+ response
568
+ }
569
+ } else {
570
+ return {
571
+ data: null,
572
+ response
573
+ }
567
574
  }
568
- } else {
575
+ } catch (err) {
576
+ // We don't want to crash the worker if the API call fails
577
+ // (e.g. exporting a non-exportable file)
578
+ const response = responseSyncify(err?.response || { status: err?.code || 500, statusText: err?.message })
569
579
  return {
570
580
  data: null,
571
- response
581
+ response,
582
+ error: err?.response?.data || err?.message || err
572
583
  }
573
584
  }
574
585
  }
@@ -70,10 +70,8 @@ const register = (id, cacher, result, allow404 = false, params) => {
70
70
  const { data, response } = result;
71
71
 
72
72
  if (checkResponseCacher(id, response, allow404, cacher)) {
73
- return {
74
- ...result,
75
- data: cacher.setEntry(id, params, data),
76
- };
73
+ cacher.setEntry(id, params, normalizeSerialization(data));
74
+ return result;
77
75
  } else {
78
76
  return result;
79
77
  }
@@ -147,7 +145,7 @@ const fxGeneric = ({
147
145
  const data = cacher.getEntry(resourceId, otherParams);
148
146
  if (data) {
149
147
  return {
150
- data,
148
+ data: normalizeSerialization(data),
151
149
  response: {
152
150
  status: 200,
153
151
  fromCache: true,
@@ -203,7 +201,7 @@ const fxDriveGet = ({
203
201
  const { cachedFile, good } = getFromFileCache(id, params.fields);
204
202
  if (good)
205
203
  return {
206
- data: cachedFile,
204
+ data: normalizeSerialization(cachedFile),
207
205
  // fake a good sxresponse
208
206
  response: {
209
207
  status: 200,
@@ -101,7 +101,12 @@ function cleanup() {
101
101
  }
102
102
 
103
103
  // The 'exit' event is for when the process is already shutting down normally.
104
- process.on('exit', cleanup);
104
+ process.on('exit', () => {
105
+ // Only terminate the worker if we aren't in the middle of an interactive CLI session
106
+ if (!process.env.GF_CLI_INTERACTIVE) {
107
+ cleanup();
108
+ }
109
+ });
105
110
  // By not listening for 'SIGINT', we allow Node.js to perform its default action,
106
111
  // which is to exit the process. The 'exit' event will then be fired to clean up the worker.
107
112
  process.on('SIGTERM', cleanup); // Catches `kill`
@@ -181,10 +186,25 @@ export function callSync(method, ...args) {
181
186
 
182
187
  if (hasError) {
183
188
  // Re-hydrate the error object on the main thread.
184
- const err = new Error(resultData.message);
189
+ // The error message from the worker might be a simple string or a JSON object from an API response
190
+ let message = resultData.message || resultString || "Unknown worker error";
191
+
192
+ // If it's a Drive API error object, the message is often nested
193
+ if (resultData.error?.message) {
194
+ message = resultData.error.message;
195
+ }
196
+
197
+ const err = new Error(message);
185
198
  err.stack = resultData.stack;
186
- // Copy other properties if they exist.
187
- Object.assign(err, resultData);
199
+
200
+ // Copy other important properties if they exist, except the ones we've handled
201
+ const skip = ['message', 'stack', 'name'];
202
+ Object.keys(resultData).forEach(key => {
203
+ if (!skip.includes(key)) {
204
+ err[key] = resultData[key];
205
+ }
206
+ });
207
+
188
208
  throw err;
189
209
  }
190
210
 
@@ -54,11 +54,23 @@ async function writeResult(result) {
54
54
  * @param {Error} error The error to write.
55
55
  */
56
56
  function writeError(error) {
57
- const errorObject = {
57
+ let errorObject = {
58
58
  message: error.message,
59
59
  stack: error.stack,
60
60
  name: error.name,
61
61
  };
62
+
63
+ // If the message is a JSON string (common for API errors that have been caught and re-thrown),
64
+ // try to parse it and merge the properties for better re-hydration on the main thread.
65
+ if (error.message && error.message.startsWith('{') && error.message.endsWith('}')) {
66
+ try {
67
+ const parsedMessage = JSON.parse(error.message);
68
+ errorObject = { ...errorObject, ...parsedMessage };
69
+ } catch (e) {
70
+ // Not valid JSON, keep as is
71
+ }
72
+ }
73
+
62
74
  const errorString = JSON.stringify(errorObject);
63
75
  const encodedError = textEncoder.encode(errorString);
64
76
 
@@ -142,7 +154,6 @@ parentPort.on('message', async (task) => {
142
154
  await writeResult(result);
143
155
 
144
156
  } catch (error) {
145
- syncError('An unhandled error occurred in the worker', error);
146
157
  writeError(error);
147
158
  } finally {
148
159
  // 3. Signal completion and wake up the main thread.
@@ -1,69 +0,0 @@
1
- import './main.js';
2
-
3
- try {
4
- console.log('Searching for emails from Martin...');
5
-
6
- // Search Gmail for messages from Martin (getting up to 10 recent threads)
7
- const threads = GmailApp.search('from:Martin', 0, 10);
8
-
9
- if (threads.length === 0) {
10
- console.log('No emails found from Martin.');
11
- process.exit(0);
12
- }
13
-
14
- console.log(`Found ${threads.length} threads. Creating summary doc...`);
15
-
16
- // Create the Document
17
- const doc = DocumentApp.create('Email Summary: Martin');
18
- const body = doc.getBody();
19
-
20
- // Add a title
21
- body.appendParagraph('Summary of Recent Emails from Martin')
22
- .setHeading(DocumentApp.ParagraphHeading.TITLE);
23
-
24
- body.appendParagraph(`Generated on: ${new Date().toLocaleString()}`);
25
- body.appendParagraph('');
26
-
27
- // We have to use the advanced Gmail service directly because gas-fakes'
28
- // FakeGmailMessage does not yet support getSubject(), getDate(), or getSnippet()
29
- for (const thread of threads) {
30
- // Get the raw thread resource from the Gmail API
31
- const threadResource = Gmail.Users.Threads.get('me', thread.getId());
32
- if (!threadResource.messages || threadResource.messages.length === 0) continue;
33
-
34
- const firstMsg = threadResource.messages[0];
35
-
36
- // Extract Subject from headers
37
- const headers = firstMsg.payload && firstMsg.payload.headers ? firstMsg.payload.headers : [];
38
- const subjectHeader = headers.find(h => h.name.toLowerCase() === 'subject');
39
- const dateHeader = headers.find(h => h.name.toLowerCase() === 'date');
40
-
41
- const subject = subjectHeader ? subjectHeader.value : 'No Subject';
42
- const date = dateHeader ? dateHeader.value : 'Unknown Date';
43
-
44
- body.appendParagraph(`Subject: ${subject}`)
45
- .setHeading(DocumentApp.ParagraphHeading.HEADING1);
46
-
47
- for (const msg of threadResource.messages) {
48
- const msgHeaders = msg.payload && msg.payload.headers ? msg.payload.headers : [];
49
- const msgDateHeader = msgHeaders.find(h => h.name.toLowerCase() === 'date');
50
- const msgDate = msgDateHeader ? msgDateHeader.value : 'Unknown Date';
51
-
52
- body.appendParagraph(`Date: ${msgDate}`)
53
- .setHeading(DocumentApp.ParagraphHeading.HEADING3);
54
-
55
- const snippet = msg.snippet || "No content.";
56
- body.appendParagraph(snippet);
57
- }
58
-
59
- body.appendParagraph(''); // Spacing
60
- }
61
-
62
- doc.saveAndClose();
63
-
64
- console.log(`Successfully created document "${doc.getName()}"`);
65
- console.log(`Document URL: ${doc.getUrl()}`);
66
-
67
- } catch (error) {
68
- console.error(`Error: ${error.message}`);
69
- }