@mcpher/gas-fakes 2.3.18 → 2.5.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.
Files changed (89) hide show
  1. package/README.md +15 -32
  2. package/package.json +1 -2
  3. package/src/cli/app.js +30 -2
  4. package/src/cli/server.js +32 -0
  5. package/src/cli/setup.js +24 -0
  6. package/src/cli/togas.js +176 -0
  7. package/src/index.js +2 -0
  8. package/src/services/common/fakeui.js +45 -0
  9. package/src/services/content/app.js +3 -0
  10. package/src/services/content/contentservice.js +14 -0
  11. package/src/services/content/textoutput.js +45 -0
  12. package/src/services/documentapp/fakedocumentapp.js +1 -1
  13. package/src/services/enums/contentenums.js +15 -0
  14. package/src/services/enums/htmlenums.js +13 -0
  15. package/src/services/enums/scriptenums.js +6 -0
  16. package/src/services/formapp/fakeformapp.js +5 -0
  17. package/src/services/html/app.js +9 -0
  18. package/src/services/html/consumerworker.js +129 -0
  19. package/src/services/html/googlescriptrun.js +91 -0
  20. package/src/services/html/htmloutput.js +127 -0
  21. package/src/services/html/htmloutputmetatag.js +14 -0
  22. package/src/services/html/htmlservice.js +94 -0
  23. package/src/services/html/htmltemplate.js +63 -0
  24. package/src/services/html/serverworker.js +135 -0
  25. package/src/services/html/webapp.js +266 -0
  26. package/src/services/html/worker.js +63 -0
  27. package/src/services/libhandlerapp/fakelibrary.js +2 -2
  28. package/src/services/scriptapp/app.js +44 -0
  29. package/src/services/scriptapp/fakeauthorizationinfo.js +22 -0
  30. package/src/services/slidesapp/fakeslidesapp.js +5 -0
  31. package/src/services/spreadsheetapp/fakebooleancondition.js +14 -2
  32. package/src/services/spreadsheetapp/fakegradientcondition.js +1 -1
  33. package/src/services/spreadsheetapp/fakeovergridimage.js +25 -0
  34. package/src/services/spreadsheetapp/fakesheet.js +23 -1
  35. package/src/services/spreadsheetapp/fakespreadsheet.js +68 -11
  36. package/src/services/spreadsheetapp/fakespreadsheetapp.js +70 -9
  37. package/src/services/stores/fakestores.js +5 -0
  38. package/src/support/auth.js +2 -0
  39. package/src/support/proxies.js +1 -1
  40. package/src/support/sxauth.js +20 -12
  41. package/src/support/utils.js +480 -200
  42. package/src/support/workersync/sxhtml.js +8 -0
  43. package/src/support/workersync/synchronizer.js +8 -1
  44. package/src/support/workersync/worker.js +5 -0
  45. package/api-docs/kdrive_api.json +0 -69958
  46. package/appsscript.json +0 -102
  47. package/gf_agent/README.md +0 -101
  48. package/gf_agent/SKILL.md +0 -484
  49. package/gf_agent/documentation.md +0 -105
  50. package/gf_agent/gf-agent-contributor/SKILL.md +0 -56
  51. package/gf_agent/index.md +0 -21
  52. package/gf_agent/knowledge/00-execution-context.md +0 -5
  53. package/gf_agent/knowledge/01-drive.md +0 -12
  54. package/gf_agent/knowledge/02-syntax.md +0 -14
  55. package/gf_agent/knowledge/03-auth.md +0 -15
  56. package/gf_agent/knowledge/04-advanced.md +0 -46
  57. package/gf_agent/knowledge/05-sheets-forms.md +0 -27
  58. package/gf_agent/knowledge/06-jdbc-cloudsql.md +0 -21
  59. package/gf_agent/knowledge/07-jdbc-auth-details.md +0 -30
  60. package/gf_agent/knowledge/08-docs-limitations.md +0 -4
  61. package/gf_agent/knowledge/09-orchestrator-pattern.md +0 -55
  62. package/gf_agent/knowledge/10-sandbox-security.md +0 -62
  63. package/gf_agent/knowledge/11-chart-builder-limitations.md +0 -15
  64. package/gf_agent/knowledge/12-gmail-eventual-consistency.md +0 -13
  65. package/gf_agent/knowledge/13-advanced-services-discovery.md +0 -29
  66. package/gf_agent/knowledge/14-utilities-parity.md +0 -13
  67. package/gf_agent/knowledge/15-logging-efficiency.md +0 -15
  68. package/gf_agent/knowledge/README.md +0 -16
  69. package/gf_agent/scripts/SKILL.template.md +0 -63
  70. package/gf_agent/scripts/builder.js +0 -118
  71. package/gf_agent/skills/base.md +0 -156
  72. package/gf_agent/skills/cache.md +0 -20
  73. package/gf_agent/skills/calendar.md +0 -780
  74. package/gf_agent/skills/charts.md +0 -127
  75. package/gf_agent/skills/document.md +0 -6752
  76. package/gf_agent/skills/drive.md +0 -423
  77. package/gf_agent/skills/forms.md +0 -4036
  78. package/gf_agent/skills/gmail.md +0 -576
  79. package/gf_agent/skills/jdbc.md +0 -3101
  80. package/gf_agent/skills/lock.md +0 -20
  81. package/gf_agent/skills/properties.md +0 -19
  82. package/gf_agent/skills/script.md +0 -50
  83. package/gf_agent/skills/slides.md +0 -5054
  84. package/gf_agent/skills/spreadsheet.md +0 -56075
  85. package/gf_agent/skills/urlfetch.md +0 -28
  86. package/gf_agent/skills/utilities.md +0 -50
  87. package/gf_agent/skills/xml.md +0 -270
  88. package/skills-lock.json +0 -10
  89. package/src/services/documentapp/fakeui.js +0 -27
@@ -1,87 +1,83 @@
1
+ import is, { isNonEmptyString } from "@sindresorhus/is";
2
+ import { assert } from "@sindresorhus/is";
3
+ import { slogger } from "./slogger.js";
1
4
 
5
+ const isNU = (item) => is.null(item) || is.undefined(item);
2
6
 
3
- import is from '@sindresorhus/is';
4
- import { assert } from '@sindresorhus/is'
5
- import { slogger } from './slogger.js'
6
-
7
- const isNU = (item) => is.null(item) || is.undefined(item)
8
-
9
- const arrify = (item) => is.array(item)
10
- ? item
11
- : (isNU(item) ? item : [item])
7
+ const arrify = (item) => (is.array(item) ? item : isNU(item) ? item : [item]);
12
8
 
13
9
  const fromJson = (text, failOnError = false) => {
14
10
  try {
15
- return JSON.parse(text)
11
+ return JSON.parse(text);
16
12
  } catch (err) {
17
- slogger.warn(text)
13
+ slogger.warn(text);
18
14
  if (failOnError) {
19
- throw err
15
+ throw err;
20
16
  }
21
- return null
17
+ return null;
22
18
  }
23
- }
24
-
25
-
19
+ };
26
20
 
27
- const isBlob = (item) => is.object(item) && Reflect.has(item, "copyBlob") && is.function(item.copyBlob)
21
+ const isBlob = (item) =>
22
+ is.object(item) &&
23
+ Reflect.has(item, "copyBlob") &&
24
+ is.function(item.copyBlob);
28
25
 
29
26
  /**
30
- * merge a series of url params
31
- * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
27
+ * merge a series of url params
28
+ * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
32
29
  * @returns {string} would return pa=a&pb=pb&pc=c and encoded URI
33
30
  */
34
31
  const makeUrlParams = (pob) => {
35
- return makeParams(pob).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&")
36
- }
32
+ return makeParams(pob)
33
+ .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
34
+ .join("&");
35
+ };
37
36
  /**
38
- * merge a series of url params
39
- * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
37
+ * merge a series of url params
38
+ * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
40
39
  * @returns {object} merged and dedupped paramaters reduced
41
40
  */
42
41
  const makeParamOb = (pob) => {
43
- const a = makeParams(pob)
42
+ const a = makeParams(pob);
44
43
  return a.reduce((p, [k, v]) => {
45
- p[k] = v
46
- return p
47
- }, {})
48
- }
44
+ p[k] = v;
45
+ return p;
46
+ }, {});
47
+ };
49
48
  /**
50
- * merge a series of url params
51
- * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
49
+ * merge a series of url params
50
+ * @param {object|object[]} pob an object eg [{pa:a,pb:b},{pc:c}]
52
51
  * @returns {object[]} merged and dedupped paramaters
53
52
  */
54
53
  const makeParams = (pob = []) => {
55
54
  // dups will be removed
56
55
  const mapob = arrify(pob).reduce((p, c) => {
57
- Reflect.ownKeys(c).forEach(k => p.set(k, c[k]))
58
- return p
59
- }, new Map())
60
- return Array.from(mapob.entries())
61
- }
62
-
56
+ Reflect.ownKeys(c).forEach((k) => p.set(k, c[k]));
57
+ return p;
58
+ }, new Map());
59
+ return Array.from(mapob.entries());
60
+ };
63
61
 
64
62
  const settleAsString = (data, charset) => {
65
63
  if (is.buffer(data)) {
66
- return bytesToString(Array.from(data), charset)
64
+ return bytesToString(Array.from(data), charset);
67
65
  } else if (is.array(data)) {
68
- return bytesToString(data, charset)
66
+ return bytesToString(data, charset);
69
67
  } else {
70
- assert.string(data)
71
- return data
68
+ assert.string(data);
69
+ return data;
72
70
  }
73
-
74
- }
71
+ };
75
72
 
76
73
  const settleAsBytes = (data, charset) => {
77
-
78
74
  if (is.string(data)) {
79
- return stringToBytes(data, charset)
75
+ return stringToBytes(data, charset);
80
76
  } else if (is.buffer(data)) {
81
- return Array.from(data)
77
+ return Array.from(data);
82
78
  } else {
83
79
  // Handle JSON representation of a Buffer
84
- if (is.object(data) && data.type === 'Buffer' && is.array(data.data)) {
80
+ if (is.object(data) && data.type === "Buffer" && is.array(data.data)) {
85
81
  return data.data;
86
82
  }
87
83
  // Workaround for an issue where an array of bytes passed from a worker
@@ -91,105 +87,112 @@ const settleAsBytes = (data, charset) => {
91
87
  // Check if the object's values look like a valid byte array.
92
88
  if (isByteArray(values)) return values;
93
89
  }
94
- if (!is.array(data)) slogger.log(`settleAsBytes: data is NOT an array. type: ${typeof data}, keys: ${Object.keys(data)}`);
90
+ if (!is.array(data))
91
+ slogger.log(
92
+ `settleAsBytes: data is NOT an array. type: ${typeof data}, keys: ${Object.keys(data)}`,
93
+ );
95
94
  assert.array(data);
96
95
  return data;
97
96
  }
97
+ };
98
98
 
99
- }
100
-
101
- const stringToBytes = (string, charset) => Array.from(Buffer.from(string, charset))
102
- const bytesToString = (data, charset) => Buffer.from(data).toString(charset)
99
+ const stringToBytes = (string, charset) =>
100
+ Array.from(Buffer.from(string, charset));
101
+ const bytesToString = (data, charset) => Buffer.from(data).toString(charset);
103
102
 
104
103
  const isByteArray = (arr) => {
105
- return Array.isArray(arr) && arr.every((n) => Number.isInteger(n) && n >= 0 && n <= 255);
106
- }
104
+ return (
105
+ Array.isArray(arr) &&
106
+ arr.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)
107
+ );
108
+ };
107
109
 
108
110
  /**
109
111
  * merge something like
110
112
  * sa = "a,b,f(x,y),g(a,b),h(h)"
111
113
  * sb = "c,b,f(x,z),h(h,i)"
112
- * into "a,b,f(x,y,z),g(a,b),h(h,i)
113
- * @param {...string} any number of other strings to merge
114
+ * into "a,b,f(x,y,z),g(a,b),h(h,i)
115
+ * @param {...string} any number of other strings to merge
114
116
  * @returns {string}
115
117
  */
116
118
  export const mergeParamStrings = (...args) => {
117
-
118
119
  const enhanceMap = (str, itemMap = new Map()) => {
119
120
  // extract all the items with subfields
120
- const rxSubs = /([^,(]*)(?=\()\(([^)]*)\)/g
121
+ const rxSubs = /([^,(]*)(?=\()\(([^)]*)\)/g;
121
122
  /**
122
123
  * [ [ 'f(x,y)', 'f', 'x,y' ],
123
124
  [ 'g(a,b)', 'g', 'a,b' ],
124
125
  [ 'h(h)', 'h', 'h' ] ]
125
126
  */
126
- const subs = Array.from(str.matchAll(rxSubs))
127
+ const subs = Array.from(str.matchAll(rxSubs));
127
128
 
128
129
  // there should be 3 groups for each member
129
130
  // for example fields(a,b) fields a,b - we want to set up an map that looks like fields, "a,b"
130
131
 
131
- subs.forEach(match => {
132
+ subs.forEach((match) => {
132
133
  if (match.length !== 3) {
133
- throw new Error(`Invalid format for subfield ${JSON.stringify(match)}`)
134
+ throw new Error(`Invalid format for subfield ${JSON.stringify(match)}`);
134
135
  }
135
- const [_, key, items] = match
136
- if (!itemMap.has(key)) itemMap.set(key, new Set())
137
- const item = itemMap.get(key)
138
- assert.set(item)
139
- items.split(",").forEach(f => itemMap.get(key).add(f))
140
- })
136
+ const [_, key, items] = match;
137
+ if (!itemMap.has(key)) itemMap.set(key, new Set());
138
+ const item = itemMap.get(key);
139
+ assert.set(item);
140
+ items.split(",").forEach((f) => itemMap.get(key).add(f));
141
+ });
141
142
 
142
143
  // there should be 2 groups for each member
143
144
  // for example foo - we want a map item with key foo and value null
144
145
  const rxPlains = /(?<!\([^)]*),?([^,(]+)(?=(?:,|$)(?![^(]*\)))/g;
145
- const plains = Array.from(str.matchAll(rxPlains))
146
+ const plains = Array.from(str.matchAll(rxPlains));
146
147
 
147
- plains.forEach(match => {
148
+ plains.forEach((match) => {
148
149
  if (match.length !== 2) {
149
- throw new Error(`Invalid format for field ${JSON.stringify(match)}`)
150
+ throw new Error(`Invalid format for field ${JSON.stringify(match)}`);
150
151
  }
151
- const [_, key] = match
152
- const item = itemMap.get(key)
152
+ const [_, key] = match;
153
+ const item = itemMap.get(key);
153
154
  // because whether it exists or not it should be null otherwise its a conflict a set
154
155
  if (itemMap.has(key)) {
155
- assert.null(item)
156
+ assert.null(item);
156
157
  } else {
157
- itemMap.set(key, null)
158
+ itemMap.set(key, null);
158
159
  }
159
- })
160
+ });
160
161
 
161
- return itemMap
162
- }
162
+ return itemMap;
163
+ };
163
164
 
164
- const itemMap = new Map()
165
- args.forEach(f => {
166
- assert.string(f)
167
- return enhanceMap(f.replace(/\s/g, ""), itemMap)
168
- })
165
+ const itemMap = new Map();
166
+ args.forEach((f) => {
167
+ assert.string(f);
168
+ return enhanceMap(f.replace(/\s/g, ""), itemMap);
169
+ });
169
170
 
170
171
  // now just convert that into a string
171
- return Array.from(itemMap.entries()).map(([key, value]) => {
172
-
173
- return is.null(value)
174
- ? key
175
- : `${key}(${Array.from(value.keys()).sort().join(",")})`
176
- }).sort().join(",")
177
- }
172
+ return Array.from(itemMap.entries())
173
+ .map(([key, value]) => {
174
+ return is.null(value)
175
+ ? key
176
+ : `${key}(${Array.from(value.keys()).sort().join(",")})`;
177
+ })
178
+ .sort()
179
+ .join(",");
180
+ };
178
181
 
179
182
  /**
180
183
  * translated field names from v3 to v2
181
- * @param {string} fields a comma separated fields string
184
+ * @param {string} fields a comma separated fields string
182
185
  * @returns {string} the modified string
183
186
  */
184
187
  export const translateFieldsToV2 = (fields) => {
185
- if (!is.string(fields)) return fields
188
+ if (!is.string(fields)) return fields;
186
189
  return fields
187
- .replace(/\bcreatedTime\b/g, 'createdDate')
188
- .replace(/\bmodifiedTime\b/g, 'modifiedDate')
189
- }
190
+ .replace(/\bcreatedTime\b/g, "createdDate")
191
+ .replace(/\bmodifiedTime\b/g, "modifiedDate");
192
+ };
190
193
 
191
- const capital = (str) => str.substring(0, 1).toUpperCase() + str.substring(1)
192
- const unCapital = (str) => str.substring(0, 1).toLowerCase() + str.substring(1)
194
+ const capital = (str) => str.substring(0, 1).toUpperCase() + str.substring(1);
195
+ const unCapital = (str) => str.substring(0, 1).toLowerCase() + str.substring(1);
193
196
 
194
197
  const validateHex = (cssString) => {
195
198
  const hex = normalizeColorStringToHex(cssString);
@@ -203,71 +206,75 @@ const validateHex = (cssString) => {
203
206
  hexValue,
204
207
  hex,
205
208
  ...rgb,
206
- r: Math.round(rgb.red * 255), g: Math.round(rgb.green * 255), b: Math.round(rgb.blue * 255),
209
+ r: Math.round(rgb.red * 255),
210
+ g: Math.round(rgb.green * 255),
211
+ b: Math.round(rgb.blue * 255),
207
212
  };
208
213
  };
209
214
 
210
- const robToHex = ({ red, green, blue }) => rgbToHex(red, green, blue)
211
-
215
+ const robToHex = ({ red, green, blue }) => rgbToHex(red, green, blue);
212
216
 
213
217
  const rgbToHex = (r, g, b) => {
214
218
  const toHex = (c) => {
215
- if (is.nullOrUndefined(c) || Number.isNaN(c)) return '00';
219
+ if (is.nullOrUndefined(c) || Number.isNaN(c)) return "00";
216
220
  const val = Math.round(c * 255);
217
221
  const hex = val.toString(16);
218
- return hex.length === 1 ? '0' + hex : hex;
222
+ return hex.length === 1 ? "0" + hex : hex;
219
223
  };
220
224
  const red = toHex(r);
221
225
  const green = toHex(g);
222
226
  const blue = toHex(b);
223
227
  return `#${red}${green}${blue}`;
224
- }
228
+ };
225
229
 
226
230
  /**
227
231
  * normally we'll have ".x.y.z" and we need to dig in to extract the value for x.y.z of the passed value
228
232
  * however we may also have x(y,z) in which case we just need to extract up to x - since the stuff after the brackets was for the benfit of the api
229
233
  */
230
234
  const getPlucker = (props, defaultValue) => {
231
-
232
235
  // get any bracketed values
233
236
  // clean the props
234
- props = props.trim().replace(/\s/g, "").split(".").filter(f => f).join(".")
235
-
236
- // now extract all the bracketed stuff - this will turn x.y(a,b)
237
+ props = props
238
+ .trim()
239
+ .replace(/\s/g, "")
240
+ .split(".")
241
+ .filter((f) => f)
242
+ .join(".");
243
+
244
+ // now extract all the bracketed stuff - this will turn x.y(a,b)
237
245
  // into G1 - x.y G2 a,b
238
246
  // and x.y into just x.y
239
- const regex = /([^(]*)(.*)/
247
+ const regex = /([^(]*)(.*)/;
240
248
  const match = regex.exec(props);
241
249
  if (!match) {
242
- throw `undeciperable props ${props} for plucker`
250
+ throw `undeciperable props ${props} for plucker`;
243
251
  }
244
252
  // so this would be a.b(c,d) main [a,b] sub [c,d]
245
253
  // right now only supporting single depth
246
- const main = match[1] && match[1].split(".")
247
- const subs = match[2] && match[2].replace(/\(/, "").replace(/\)/, "").split(",")
254
+ const main = match[1] && match[1].split(".");
255
+ const subs =
256
+ match[2] && match[2].replace(/\(/, "").replace(/\)/, "").split(",");
248
257
 
249
258
  const pluckSub = (v) => {
250
- if (!subs) return v
259
+ if (!subs) return v;
251
260
  return subs.reduce((p, c) => {
252
- p[c] = v && is.nonEmptyObject(v) && !isNU(v[c]) ? v[c] : defaultValue
253
- return p
254
- }, {})
255
- }
261
+ p[c] = v && is.nonEmptyObject(v) && !isNU(v[c]) ? v[c] : defaultValue;
262
+ return p;
263
+ }, {});
264
+ };
256
265
 
257
266
  // now we need a function that will extract fields to match these
258
267
  return (v) => {
259
268
  // if there are no main then we just return the plucked values from the sub
260
- if (!main) return pluckSub(v)
269
+ if (!main) return pluckSub(v);
261
270
 
262
271
  const px = main.reduce((p, c) => {
263
- const t = p && p[c]
264
- return isNU(t) ? defaultValue : t
265
- }, v)
266
- return pluckSub(px)
267
- }
268
-
269
- }
270
-
272
+ const t = p && p[c];
273
+ return isNU(t) ? defaultValue : t;
274
+ }, v);
275
+ return pluckSub(px);
276
+ };
277
+ };
271
278
 
272
279
  const hexToRgb = (hex) => {
273
280
  const bigint = parseInt(hex.slice(1), 16);
@@ -279,17 +286,17 @@ const hexToRgb = (hex) => {
279
286
  green: g / 255,
280
287
  blue: b / 255,
281
288
  };
282
- }
289
+ };
283
290
 
284
- const outside = (n, l, h) => n < l || n > h
285
- const outsideInt = (n, l, h) => outside(n, l, h) || !is.integer(n)
291
+ const outside = (n, l, h) => n < l || n > h;
292
+ const outsideInt = (n, l, h) => outside(n, l, h) || !is.integer(n);
286
293
 
287
294
  const zeroizeTime = (date) => {
288
295
  const year = date.getFullYear();
289
296
  const month = date.getMonth(); // Month is 0-indexed
290
297
  const day = date.getDate();
291
298
  return new Date(year, month, day, 0, 0, 0, 0);
292
- }
299
+ };
293
300
 
294
301
  const serialToDate = (serial) => {
295
302
  const epochCorrection = 2209161600000; // Milliseconds between 1970-01-01 and 1899-12-30
@@ -298,76 +305,192 @@ const serialToDate = (serial) => {
298
305
  return new Date(adjustedMs - epochCorrection);
299
306
  };
300
307
 
301
- const isEnum = (a) => is.object(a) && Reflect.has(a, "compareTo") && is.function(a.compareTo)
302
- const hasFunction = (a, b = toString) => !isNU(a) && a[b] && is.function(a[b])
308
+ const isEnum = (a) =>
309
+ is.object(a) && Reflect.has(a, "compareTo") && is.function(a.compareTo);
310
+ const hasFunction = (a, b = toString) => !isNU(a) && a[b] && is.function(a[b]);
303
311
 
304
312
  const stringer = (value) => {
305
- let func = is.date(value) ? "toISOString" : "toString"
313
+ let func = is.date(value) ? "toISOString" : "toString";
306
314
  if (!hasFunction(value, func)) {
307
- throw new Error(`dont know how to stringify ${value}`)
315
+ throw new Error(`dont know how to stringify ${value}`);
308
316
  }
309
- const t = value[func]()
317
+ const t = value[func]();
310
318
  // drop time portion of iso date if that's what it is
311
- return is.date(value) ? t.slice(0, 10) : t
312
-
313
- }
314
- const WHITER = { red: 1, green: 1, blue: 1 }
315
- const BLACKER = { red: 0, green: 0, blue: 0 }
316
- const BLACK = '#000000'
317
- const WHITE = '#ffffff'
319
+ return is.date(value) ? t.slice(0, 10) : t;
320
+ };
321
+ const WHITER = { red: 1, green: 1, blue: 1 };
322
+ const BLACKER = { red: 0, green: 0, blue: 0 };
323
+ const BLACK = "#000000";
324
+ const WHITE = "#ffffff";
318
325
 
319
326
  const getEnumKeys = (value) => {
320
327
  if (!isEnum(value)) {
321
- throw `Expected value to be an Enum but got ${value}`
328
+ throw `Expected value to be an Enum but got ${value}`;
322
329
  }
323
- return Object.keys(value)
324
- .filter(f => f !== "UNSUPPORTED" && !is.function(value[f]))
325
-
326
- }
330
+ return Object.keys(value).filter(
331
+ (f) => f !== "UNSUPPORTED" && !is.function(value[f]),
332
+ );
333
+ };
327
334
 
328
335
  const colorNameToHex = {
329
- 'aliceblue': '#f0f8ff', 'antiquewhite': '#faebd7', 'aqua': '#00ffff', 'aquamarine': '#7fffd4', 'azure': '#f0ffff',
330
- 'beige': '#f5f5dc', 'bisque': '#ffe4c4', 'black': '#000000', 'blanchedalmond': '#ffebcd', 'blue': '#0000ff',
331
- 'blueviolet': '#8a2be2', 'brown': '#a52a2a', 'burlywood': '#deb887', 'cadetblue': '#5f9ea0', 'chartreuse': '#7fff00',
332
- 'chocolate': '#d2691e', 'coral': '#ff7f50', 'cornflowerblue': '#6495ed', 'cornsilk': '#fff8dc', 'crimson': '#dc143c',
333
- 'cyan': '#00ffff', 'darkblue': '#00008b', 'darkcyan': '#008b8b', 'darkgoldenrod': '#b8860b', 'darkgray': '#a9a9a9',
334
- 'darkgreen': '#006400', 'darkgrey': '#a9a9a9', 'darkkhaki': '#bdb76b', 'darkmagenta': '#8b008b', 'darkolivegreen': '#556b2f',
335
- 'darkorange': '#ff8c00', 'darkorchid': '#9932cc', 'darkred': '#8b0000', 'darksalmon': '#e9967a', 'darkseagreen': '#8fbc8f',
336
- 'darkslateblue': '#483d8b', 'darkslategray': '#2f4f4f', 'darkslategrey': '#2f4f4f', 'darkturquoise': '#00ced1',
337
- 'darkviolet': '#9400d3', 'deeppink': '#ff1493', 'deepskyblue': '#00bfff', 'dimgray': '#696969', 'dimgrey': '#696969',
338
- 'dodgerblue': '#1e90ff', 'firebrick': '#b22222', 'floralwhite': '#fffaf0', 'forestgreen': '#228b22', 'fuchsia': '#ff00ff',
339
- 'gainsboro': '#dcdcdc', 'ghostwhite': '#f8f8ff', 'gold': '#ffd700', 'goldenrod': '#daa520', 'gray': '#808080',
340
- 'green': '#008000', 'greenyellow': '#adff2f', 'grey': '#808080', 'honeydew': '#f0fff0', 'hotpink': '#ff69b4',
341
- 'indianred': '#cd5c5c', 'indigo': '#4b0082', 'ivory': '#fffff0', 'khaki': '#f0e68c', 'lavender': '#e6e6fa',
342
- 'lavenderblush': '#fff0f5', 'lawngreen': '#7cfc00', 'lemonchiffon': '#fffacd', 'lightblue': '#add8e6', 'lightcoral': '#f08080',
343
- 'lightcyan': '#e0ffff', 'lightgoldenrodyellow': '#fafad2', 'lightgray': '#d3d3d3', 'lightgreen': '#90ee90',
344
- 'lightgrey': '#d3d3d3', 'lightpink': '#ffb6c1', 'lightsalmon': '#ffa07a', 'lightseagreen': '#20b2aa', 'lightskyblue': '#87cefa',
345
- 'lightslategray': '#778899', 'lightslategrey': '#778899', 'lightsteelblue': '#b0c4de', 'lightyellow': '#ffffe0',
346
- 'lime': '#00ff00', 'limegreen': '#32cd32', 'linen': '#faf0e6', 'magenta': '#ff00ff', 'maroon': '#800000',
347
- 'mediumaquamarine': '#66cdaa', 'mediumblue': '#0000cd', 'mediumorchid': '#ba55d3', 'mediumpurple': '#9370db',
348
- 'mediumseagreen': '#3cb371', 'mediumslateblue': '#7b68ee', 'mediumspringgreen': '#00fa9a', 'mediumturquoise': '#48d1cc',
349
- 'mediumvioletred': '#c71585', 'midnightblue': '#191970', 'mintcream': '#f5fffa', 'mistyrose': '#ffe4e1',
350
- 'moccasin': '#ffe4b5', 'navajowhite': '#ffdead', 'navy': '#000080', 'oldlace': '#fdf5e6', 'olive': '#808000',
351
- 'olivedrab': '#6b8e23', 'orange': '#ffa500', 'orangered': '#ff4500', 'orchid': '#da70d6', 'palegoldenrod': '#eee8aa',
352
- 'palegreen': '#98fb98', 'paleturquoise': '#afeeee', 'palevioletred': '#db7093', 'papayawhip': '#ffefd5',
353
- 'peachpuff': '#ffdab9', 'peru': '#cd853f', 'pink': '#ffc0cb', 'plum': '#dda0dd', 'powderblue': '#b0e0e6',
354
- 'purple': '#800080', 'red': '#ff0000', 'rosybrown': '#bc8f8f', 'royalblue': '#4169e1',
355
- 'saddlebrown': '#8b4513', 'salmon': '#fa8072', 'sandybrown': '#f4a460', 'seagreen': '#2e8b57', 'seashell': '#fff5ee',
356
- 'sienna': '#a0522d', 'silver': '#c0c0c0', 'skyblue': '#87ceeb', 'slateblue': '#6a5acd', 'slategray': '#708090',
357
- 'slategrey': '#708090', 'snow': '#fffafa', 'springgreen': '#00ff7f', 'steelblue': '#4682b4', 'tan': '#d2b48c',
358
- 'teal': '#008080', 'thistle': '#d8bfd8', 'tomato': '#ff6347', 'turquoise': '#40e0d0', 'violet': '#ee82ee',
359
- 'wheat': '#f5deb3', 'white': '#ffffff', 'whitesmoke': '#f5f5f5', 'yellow': '#ffff00', 'yellowgreen': '#9acd32'
336
+ aliceblue: "#f0f8ff",
337
+ antiquewhite: "#faebd7",
338
+ aqua: "#00ffff",
339
+ aquamarine: "#7fffd4",
340
+ azure: "#f0ffff",
341
+ beige: "#f5f5dc",
342
+ bisque: "#ffe4c4",
343
+ black: "#000000",
344
+ blanchedalmond: "#ffebcd",
345
+ blue: "#0000ff",
346
+ blueviolet: "#8a2be2",
347
+ brown: "#a52a2a",
348
+ burlywood: "#deb887",
349
+ cadetblue: "#5f9ea0",
350
+ chartreuse: "#7fff00",
351
+ chocolate: "#d2691e",
352
+ coral: "#ff7f50",
353
+ cornflowerblue: "#6495ed",
354
+ cornsilk: "#fff8dc",
355
+ crimson: "#dc143c",
356
+ cyan: "#00ffff",
357
+ darkblue: "#00008b",
358
+ darkcyan: "#008b8b",
359
+ darkgoldenrod: "#b8860b",
360
+ darkgray: "#a9a9a9",
361
+ darkgreen: "#006400",
362
+ darkgrey: "#a9a9a9",
363
+ darkkhaki: "#bdb76b",
364
+ darkmagenta: "#8b008b",
365
+ darkolivegreen: "#556b2f",
366
+ darkorange: "#ff8c00",
367
+ darkorchid: "#9932cc",
368
+ darkred: "#8b0000",
369
+ darksalmon: "#e9967a",
370
+ darkseagreen: "#8fbc8f",
371
+ darkslateblue: "#483d8b",
372
+ darkslategray: "#2f4f4f",
373
+ darkslategrey: "#2f4f4f",
374
+ darkturquoise: "#00ced1",
375
+ darkviolet: "#9400d3",
376
+ deeppink: "#ff1493",
377
+ deepskyblue: "#00bfff",
378
+ dimgray: "#696969",
379
+ dimgrey: "#696969",
380
+ dodgerblue: "#1e90ff",
381
+ firebrick: "#b22222",
382
+ floralwhite: "#fffaf0",
383
+ forestgreen: "#228b22",
384
+ fuchsia: "#ff00ff",
385
+ gainsboro: "#dcdcdc",
386
+ ghostwhite: "#f8f8ff",
387
+ gold: "#ffd700",
388
+ goldenrod: "#daa520",
389
+ gray: "#808080",
390
+ green: "#008000",
391
+ greenyellow: "#adff2f",
392
+ grey: "#808080",
393
+ honeydew: "#f0fff0",
394
+ hotpink: "#ff69b4",
395
+ indianred: "#cd5c5c",
396
+ indigo: "#4b0082",
397
+ ivory: "#fffff0",
398
+ khaki: "#f0e68c",
399
+ lavender: "#e6e6fa",
400
+ lavenderblush: "#fff0f5",
401
+ lawngreen: "#7cfc00",
402
+ lemonchiffon: "#fffacd",
403
+ lightblue: "#add8e6",
404
+ lightcoral: "#f08080",
405
+ lightcyan: "#e0ffff",
406
+ lightgoldenrodyellow: "#fafad2",
407
+ lightgray: "#d3d3d3",
408
+ lightgreen: "#90ee90",
409
+ lightgrey: "#d3d3d3",
410
+ lightpink: "#ffb6c1",
411
+ lightsalmon: "#ffa07a",
412
+ lightseagreen: "#20b2aa",
413
+ lightskyblue: "#87cefa",
414
+ lightslategray: "#778899",
415
+ lightslategrey: "#778899",
416
+ lightsteelblue: "#b0c4de",
417
+ lightyellow: "#ffffe0",
418
+ lime: "#00ff00",
419
+ limegreen: "#32cd32",
420
+ linen: "#faf0e6",
421
+ magenta: "#ff00ff",
422
+ maroon: "#800000",
423
+ mediumaquamarine: "#66cdaa",
424
+ mediumblue: "#0000cd",
425
+ mediumorchid: "#ba55d3",
426
+ mediumpurple: "#9370db",
427
+ mediumseagreen: "#3cb371",
428
+ mediumslateblue: "#7b68ee",
429
+ mediumspringgreen: "#00fa9a",
430
+ mediumturquoise: "#48d1cc",
431
+ mediumvioletred: "#c71585",
432
+ midnightblue: "#191970",
433
+ mintcream: "#f5fffa",
434
+ mistyrose: "#ffe4e1",
435
+ moccasin: "#ffe4b5",
436
+ navajowhite: "#ffdead",
437
+ navy: "#000080",
438
+ oldlace: "#fdf5e6",
439
+ olive: "#808000",
440
+ olivedrab: "#6b8e23",
441
+ orange: "#ffa500",
442
+ orangered: "#ff4500",
443
+ orchid: "#da70d6",
444
+ palegoldenrod: "#eee8aa",
445
+ palegreen: "#98fb98",
446
+ paleturquoise: "#afeeee",
447
+ palevioletred: "#db7093",
448
+ papayawhip: "#ffefd5",
449
+ peachpuff: "#ffdab9",
450
+ peru: "#cd853f",
451
+ pink: "#ffc0cb",
452
+ plum: "#dda0dd",
453
+ powderblue: "#b0e0e6",
454
+ purple: "#800080",
455
+ red: "#ff0000",
456
+ rosybrown: "#bc8f8f",
457
+ royalblue: "#4169e1",
458
+ saddlebrown: "#8b4513",
459
+ salmon: "#fa8072",
460
+ sandybrown: "#f4a460",
461
+ seagreen: "#2e8b57",
462
+ seashell: "#fff5ee",
463
+ sienna: "#a0522d",
464
+ silver: "#c0c0c0",
465
+ skyblue: "#87ceeb",
466
+ slateblue: "#6a5acd",
467
+ slategray: "#708090",
468
+ slategrey: "#708090",
469
+ snow: "#fffafa",
470
+ springgreen: "#00ff7f",
471
+ steelblue: "#4682b4",
472
+ tan: "#d2b48c",
473
+ teal: "#008080",
474
+ thistle: "#d8bfd8",
475
+ tomato: "#ff6347",
476
+ turquoise: "#40e0d0",
477
+ violet: "#ee82ee",
478
+ wheat: "#f5deb3",
479
+ white: "#ffffff",
480
+ whitesmoke: "#f5f5f5",
481
+ yellow: "#ffff00",
482
+ yellowgreen: "#9acd32",
360
483
  };
361
484
 
362
485
  const normalizeColorStringToHex = (color) => {
363
486
  if (!is.string(color)) return null;
364
- const lowerColor = color.toLowerCase().replace(/\s/g, '');
487
+ const lowerColor = color.toLowerCase().replace(/\s/g, "");
365
488
  if (colorNameToHex[lowerColor]) {
366
- return colorNameToHex[lowerColor].toUpperCase();
489
+ return colorNameToHex[lowerColor].toLowerCase();
367
490
  }
368
491
  // Check if it's a valid hex string
369
492
  if (/^#[0-9a-f]{6}$/i.test(lowerColor)) {
370
- return lowerColor.toUpperCase();
493
+ return lowerColor.toLowerCase();
371
494
  }
372
495
  return null; // Invalid color string
373
496
  };
@@ -375,12 +498,18 @@ const normalizeColorStringToHex = (color) => {
375
498
  const deepEqual = (obj1, obj2) => {
376
499
  if (obj1 === obj2) return true;
377
500
 
378
- if (is.nullOrUndefined(obj1) || is.nullOrUndefined(obj2) || !is.object(obj1) || !is.object(obj2)) {
501
+ if (
502
+ is.nullOrUndefined(obj1) ||
503
+ is.nullOrUndefined(obj2) ||
504
+ !is.object(obj1) ||
505
+ !is.object(obj2)
506
+ ) {
379
507
  return obj1 === obj2;
380
508
  }
381
509
 
382
510
  if (is.date(obj1) && is.date(obj2)) return obj1.getTime() === obj2.getTime();
383
- if (is.regExp(obj1) && is.regExp(obj2)) return obj1.toString() === obj2.toString();
511
+ if (is.regExp(obj1) && is.regExp(obj2))
512
+ return obj1.toString() === obj2.toString();
384
513
 
385
514
  const keys1 = Object.keys(obj1);
386
515
  const keys2 = Object.keys(obj2);
@@ -388,14 +517,16 @@ const deepEqual = (obj1, obj2) => {
388
517
  if (keys1.length !== keys2.length) return false;
389
518
 
390
519
  for (const key of keys1) {
391
- if (!Object.prototype.hasOwnProperty.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
520
+ if (
521
+ !Object.prototype.hasOwnProperty.call(obj2, key) ||
522
+ !deepEqual(obj1[key], obj2[key])
523
+ ) {
392
524
  return false;
393
525
  }
394
526
  }
395
527
 
396
528
  return true;
397
- }
398
-
529
+ };
399
530
 
400
531
  function stringCircular(obj, space = null) {
401
532
  // First pass: remove circular references
@@ -410,19 +541,21 @@ function stringCircular(obj, space = null) {
410
541
  function removeCircularReferences(obj) {
411
542
  const seen = new WeakSet();
412
543
 
413
- return JSON.parse(JSON.stringify(obj, (key, value) => {
414
- if (typeof value === 'object' && value !== null) {
415
- if (seen.has(value)) {
416
- return '[Circular]';
544
+ return JSON.parse(
545
+ JSON.stringify(obj, (key, value) => {
546
+ if (typeof value === "object" && value !== null) {
547
+ if (seen.has(value)) {
548
+ return "[Circular]";
549
+ }
550
+ seen.add(value);
417
551
  }
418
- seen.add(value);
419
- }
420
- return value;
421
- }));
552
+ return value;
553
+ }),
554
+ );
422
555
  }
423
556
 
424
557
  function stabilizeKeyOrder(obj) {
425
- if (obj === null || typeof obj !== 'object') {
558
+ if (obj === null || typeof obj !== "object") {
426
559
  return obj;
427
560
  }
428
561
 
@@ -439,13 +572,160 @@ function stabilizeKeyOrder(obj) {
439
572
 
440
573
  return result;
441
574
  }
442
- const lobify = (ob, mess = '') => {
443
- let lob = ob
444
- if (is.object(lob)) lob = stringCircular(lob)
445
- slogger.log(mess, lob)
446
- return ob
575
+ const lobify = (ob, mess = "") => {
576
+ let lob = ob;
577
+ if (is.object(lob)) lob = stringCircular(lob);
578
+ slogger.log(mess, lob);
579
+ return ob;
580
+ };
581
+
582
+ /**
583
+ * Removes or transforms import/export statements from source code.
584
+ *
585
+ * Rules:
586
+ * - `export default X` → `X` (bare expression/value retained)
587
+ * - `export const/let/var/function/class X` → `const/let/var/function/class X`
588
+ * - `export { a, b, c }` → commented out
589
+ * - `export { a, b } from '…'` → commented out
590
+ * - `export * from '…'` → commented out
591
+ * - `import { … } from '…'` → commented out
592
+ * - `import x from '…'` → commented out
593
+ * - `import '…'` → commented out (side-effect import retained as comment)
594
+ */
595
+ function stripImportsExports(source) {
596
+ const lines = source.split('\n');
597
+ const out = [];
598
+ let i = 0;
599
+
600
+ while (i < lines.length) {
601
+ const { fullStatement, lineCount } = collectStatement(lines, i);
602
+ const transformed = transformStatement(fullStatement);
603
+ out.push(transformed);
604
+ i += lineCount;
605
+ }
606
+
607
+ return out.join('\n');
608
+ }
609
+
610
+ // ---------------------------------------------------------------------------
611
+ // Collect a complete statement (handles multi-line braces / parens)
612
+ // ---------------------------------------------------------------------------
613
+ function collectStatement(lines, startIndex) {
614
+ let statement = lines[startIndex];
615
+ let lineCount = 1;
616
+
617
+ // Only bother with multi-line collection for import/export openers
618
+ const trimmed = statement.trimStart();
619
+ if (!trimmed.startsWith('import') && !trimmed.startsWith('export')) {
620
+ return { fullStatement: statement, lineCount };
621
+ }
622
+
623
+ // Keep pulling lines as long as the statement is clearly incomplete
624
+ while (isIncomplete(statement) && startIndex + lineCount < lines.length) {
625
+ statement += '\n' + lines[startIndex + lineCount];
626
+ lineCount++;
627
+ }
628
+
629
+ return { fullStatement: statement, lineCount };
447
630
  }
631
+
632
+ /**
633
+ * Returns true if the statement so far is definitely not yet finished.
634
+ * Two independent signals — either one means "keep going":
635
+ * 1. Unbalanced brackets (more opens than closes)
636
+ * 2. Last non-empty line ends with a continuation token (=>, , \ ( = | || ?? &&)
637
+ */
638
+ function isIncomplete(text) {
639
+ const stripped = removeStringsAndComments(text);
640
+
641
+ // 1. Unbalanced brackets
642
+ const opens = (stripped.match(/[\(\[\{]/g) || []).length;
643
+ const closes = (stripped.match(/[\)\]\}]/g) || []).length;
644
+ if (opens > closes) return true;
645
+
646
+ // 2. Trailing continuation token on the last non-empty line
647
+ const lastLine = text.split('\n').filter(l => l.trim()).pop() || '';
648
+ const trail = lastLine.trimEnd();
649
+ if (/(?:=>|[,\\(=|&]|\|\||\?\?|&&)$/.test(trail)) return true;
650
+
651
+ return false;
652
+ }
653
+
654
+ /** Naive removal of string literals and line comments to clean up bracket counting */
655
+ function removeStringsAndComments(text) {
656
+ return text
657
+ .replace(/`[^`]*`/gs, '""') // template literals
658
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""') // double-quoted strings
659
+ .replace(/'(?:[^'\\]|\\.)*'/g, "''") // single-quoted strings
660
+ .replace(/\/\/.*/g, ''); // line comments
661
+ }
662
+
663
+ // ---------------------------------------------------------------------------
664
+ // Transform a single (possibly multi-line) statement
665
+ // ---------------------------------------------------------------------------
666
+ function transformStatement(stmt) {
667
+ const trimmed = stmt.trimStart();
668
+
669
+ // ── export default <expr> ──────────────────────────────────────────────
670
+ if (/^export\s+default\s+/.test(trimmed)) {
671
+ return stmt.replace(/^(\s*)export\s+default\s+/, '$1');
672
+ }
673
+
674
+ // ── export <declaration> ───────────────────────────────────────────────
675
+ if (/^export\s+(const|let|var|function\*?|async\s+function\*?|class)\s+/.test(trimmed)) {
676
+ return stmt.replace(/^(\s*)export\s+/, '$1');
677
+ }
678
+
679
+ // ── export { … } [from '…'] or export * from '…' ──────────────────────
680
+ if (/^export\s*[\{*]/.test(trimmed) || /^export\s+\*/.test(trimmed)) {
681
+ return commentOut(stmt);
682
+ }
683
+
684
+ // ── import … ──────────────────────────────────────────────────────────
685
+ if (/^import\s/.test(trimmed)) {
686
+ return commentOut(stmt);
687
+ }
688
+
689
+ // Everything else — leave untouched
690
+ return stmt;
691
+ }
692
+
693
+ // ---------------------------------------------------------------------------
694
+ // Comment-out helper
695
+ // ---------------------------------------------------------------------------
696
+ function commentOut(stmt) {
697
+ return stmt
698
+ .split('\n')
699
+ .map((line, idx) => {
700
+ if (idx === 0) return line.replace(/^(\s*)/, '$1// ');
701
+ return '// ' + line;
702
+ })
703
+ .join('\n');
704
+ }
705
+
706
+
707
+
708
+
709
+ /**
710
+ * Comment out all top-level ES6 import and export statements, while preserving
711
+ * function/class/const/let/var declarations.
712
+ * Used to convert ESM files to be compatible with Apps Script global evaluation.
713
+ * @param {string} content
714
+ * @returns {string}
715
+ */
716
+ const stripEsmKeywords = (content) => {
717
+ if (!isNonEmptyString(content)) {
718
+ console.log(
719
+ "attempted to stripEsmKeywords on non-string; returning content",
720
+ content,
721
+ );
722
+ return content;
723
+ }
724
+ return stripImportsExports(content);
725
+ };
726
+
448
727
  export const Utils = {
728
+ stripEsmKeywords,
449
729
  lobify,
450
730
  stringCircular,
451
731
  hexToRgb,
@@ -483,6 +763,6 @@ export const Utils = {
483
763
  deepEqual,
484
764
  colorNameToHex,
485
765
  normalizeColorStringToHex,
486
- toHex: (n) => is.number(n) ? n.toString(16).padStart(8, '0') : n,
487
- fromHex: (n) => is.string(n) ? parseInt(n, 16) : n,
488
- }
766
+ toHex: (n) => (is.number(n) ? n.toString(16).padStart(8, "0") : n),
767
+ fromHex: (n) => (is.string(n) ? parseInt(n, 16) : n),
768
+ };