@mcpher/gas-fakes 2.5.3 → 2.5.4

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.
package/README.md CHANGED
@@ -16,7 +16,7 @@ npm i @mcpher/gas-fakes
16
16
 
17
17
  For a complete guide on how to set up your local environment for authentication and development, please see the consolidated guide: [Getting Started with `gas-fakes`](GETTING_STARTED.md)
18
18
 
19
- Collaborators should fork the repo and use the local versions of these files - see collaborators info.
19
+ Collaborators should fork the repo and use the local versions of these files - see [collaborators info](./notes/contributing.md)
20
20
 
21
21
  ### Use exactly the same code as in Apps Script
22
22
  ## Usage
@@ -159,10 +159,16 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
159
159
 
160
160
  ## <img src="../pngs/logo.png" alt="gas-fakes logo" width="50" align="top"> Further Reading
161
161
 
162
+
163
+
162
164
  ## Watch the gas-fakes intro video
163
165
 
164
166
  [![Watch the intro video](./pngs/introvideo.png)](https://youtu.be/oEjpIrkYpEM)
165
167
 
168
+ ## Watch the explainer about delegating work to local LLMs to save token costs
169
+
170
+ [![Use local LLMs to save tokens](./pngs/hybrid_LLM_Architecture_Overview.png)](https://youtu.be/tcvU2NLEaNE)
171
+
166
172
  ## Watch the gf_agent video on natural language automation
167
173
 
168
174
  [![Use natural language with gf_agent](./pngs/gfagent.png)](https://youtu.be/lujByoX71HU)
@@ -178,6 +184,7 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
178
184
  - [getting started](GETTING_STARTED.md) - how to handle authentication for Workspace scopes.
179
185
  - [readme](README.md)
180
186
  - [apps script parity](notes/parity.md)
187
+ - [omlx setup](notes/omlx-setup.md)
181
188
  - [Natural Language Automation with Gemini Skills & MCP Server](notes/gemini-skills-mcp.md) - new skills-based agent approach.
182
189
  - [Add agent skills to gf_agent](https://ramblings.mcpher.com/add-skills-gf_agent/)
183
190
  - [gf_agent documentation](../gf_agent/README.md) - instructions for the Gemini CLI automation agent and MCP server.
@@ -202,7 +209,7 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
202
209
  - [jdbc notes](notes/jdbc-notes.md)
203
210
  - [Yes – you can run native apps script code on Azure ACA as well!](https://ramblings.mcpher.com/yes-you-can-run-native-apps-script-code-on-azure-aca-as-well/)
204
211
  - [Yes – you can run native apps script code on AWS Lambda!](https://ramblings.mcpher.com/apps-script-on-aws-lambda/)
205
- - [initial idea and thoughts](https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/)
212
+ - [initial idea and thoughts - how it all started](https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/)
206
213
  - [Inside the volatile world of a Google Document](https://ramblings.mcpher.com/inside-the-volatile-world-of-a-google-document/)
207
214
  - [Apps Script Services on Node – using apps script libraries](https://ramblings.mcpher.com/apps-script-services-on-node-using-apps-script-libraries/)
208
215
  - [Apps Script environment on Node – more services](https://ramblings.mcpher.com/apps-script-environment-on-node-more-services/)
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "name": "@mcpher/gas-fakes",
41
41
  "author": "bruce mcpherson",
42
- "version": "2.5.3",
42
+ "version": "2.5.4",
43
43
  "license": "MIT",
44
44
  "main": "main.js",
45
45
  "description": "An implementation of the Google Workspace Apps Script runtime: Run native App Script Code on Node and Cloud Run",
@@ -1,9 +1,10 @@
1
1
  import { FakeTextOutput } from "./textoutput.js";
2
- import { ContentEnums } from "../enums/contentenums.js";
2
+ // just avoid conflict with global MimeType
3
+ import { MimeType as mt } from "../enums/contentenums.js";
3
4
 
4
5
  export class FakeContentService {
5
6
  constructor() {
6
- this.MimeType = ContentEnums.MimeType;
7
+ this.MimeType = mt;
7
8
  }
8
9
 
9
10
  createTextOutput(content = "") {
@@ -1,7 +1,6 @@
1
1
  import { newFakeGasenum } from "@mcpher/fake-gasenum";
2
2
 
3
- export const ContentEnums = {
4
- MimeType: newFakeGasenum([
3
+ export const MimeType = newFakeGasenum([
5
4
  "ATOM",
6
5
  "CSV",
7
6
  "ICAL",
@@ -12,4 +11,3 @@ export const ContentEnums = {
12
11
  "VCARD",
13
12
  "XML"
14
13
  ])
15
- }
@@ -1,6 +1,33 @@
1
- export const ScriptEnums = {
2
- AuthorizationStatus: {
1
+ import { newFakeGasenum } from "@mcpher/fake-gasenum";
2
+
3
+ export const AuthorizationStatus = newFakeGasenum({
3
4
  REQUIRED: 'REQUIRED',
4
5
  NOT_REQUIRED: 'NOT_REQUIRED'
5
- }
6
- };
6
+ })
7
+ export const TriggerSource = newFakeGasenum( {
8
+ CALENDAR: 'CALENDAR',
9
+ CLOCK: 'CLOCK',
10
+ DOCUMENTS: 'DOCUMENTS',
11
+ FORMS: 'FORMS',
12
+ SPREADSHEETS: 'SPREADSHEETS'
13
+ })
14
+ export const AuthMode = newFakeGasenum( {
15
+ NONE: 'NONE',
16
+ CUSTOM_FUNCTION: 'CUSTOM_FUNCTION',
17
+ LIMITED: 'LIMITED',
18
+ FULL: 'FULL'
19
+ })
20
+ export const EventType = newFakeGasenum( {
21
+ CLOCK: 'CLOCK',
22
+ ON_OPEN: 'ON_OPEN',
23
+ ON_EDIT: 'ON_EDIT',
24
+ ON_FORM_SUBMIT: 'ON_FORM_SUBMIT',
25
+ ON_CHANGE: 'ON_CHANGE',
26
+ ON_EVENT_UPDATED: 'ON_EVENT_UPDATED'
27
+ })
28
+ export const InstallationSource = newFakeGasenum({
29
+ APPS_MARKETPLACE_DOMAIN_ADD_ON: 'APPS_MARKETPLACE_DOMAIN_ADD_ON',
30
+ NONE: 'NONE',
31
+ WEB_STORE_ADD_ON: 'WEB_STORE_ADD_ON'
32
+ })
33
+
@@ -0,0 +1,14 @@
1
+ import { newFakeGasenum } from "@mcpher/fake-gasenum";
2
+
3
+ /**
4
+ * Enum representing the possible content types found in XML.
5
+ */
6
+ export const ContentTypes = newFakeGasenum([
7
+ "CDATA",
8
+ "COMMENT",
9
+ "DOCTYPE",
10
+ "ELEMENT",
11
+ "ENTITYREF",
12
+ "PROCESSINGINSTRUCTION",
13
+ "TEXT"
14
+ ]);
@@ -7,7 +7,13 @@ import { Auth } from '../../support/auth.js'
7
7
  import { Proxies } from '../../support/proxies.js'
8
8
  import { newFakeBehavior } from './behavior.js'
9
9
  import { newFakeAuthorizationInfo } from './fakeauthorizationinfo.js'
10
- import { ScriptEnums } from '../enums/scriptenums.js'
10
+ import {
11
+ AuthMode,
12
+ AuthorizationStatus,
13
+ TriggerSource,
14
+ EventType,
15
+ InstallationSource
16
+ } from '../enums/scriptenums.js'
11
17
  import { newCacheDropin } from '@mcpher/gas-flex-cache'
12
18
  import { slogger } from "../../support/slogger.js";
13
19
  import fs from 'fs';
@@ -48,8 +54,8 @@ const getSourceOAuthToken = () => {
48
54
 
49
55
 
50
56
  const limitMode = (mode) => {
51
- if (mode !== ScriptApp.AuthMode.FULL) {
52
- throw new Error(`only ${ScriptApp.AuthMode.FULL} is supported as mode for now`)
57
+ if (mode !== ScriptApp.AuthMode.FULL.toString()) {
58
+ throw new Error(`only ${ScriptApp.AuthMode.FULL.toString()} is supported as mode for now`)
53
59
  }
54
60
 
55
61
  return mode
@@ -234,10 +240,11 @@ if (typeof globalThis[name] === typeof undefined) {
234
240
  ensureInit()
235
241
  return Auth.getActiveUser()?.id
236
242
  },
237
- AuthMode: {
238
- FULL: 'FULL'
239
- },
240
- AuthorizationStatus: ScriptEnums.AuthorizationStatus,
243
+ AuthMode,
244
+ AuthorizationStatus,
245
+ TriggerSource,
246
+ EventType,
247
+ InstallationSource,
241
248
  getAuthorizationInfo: (authMode) => {
242
249
  ensureInit();
243
250
  return newFakeAuthorizationInfo(authMode);
@@ -1,4 +1,4 @@
1
- import { ScriptEnums } from '../enums/scriptenums.js';
1
+ import { AuthorizationStatus } from '../enums/scriptenums.js';
2
2
 
3
3
  export class FakeAuthorizationInfo {
4
4
  constructor(authMode) {
@@ -8,12 +8,12 @@ export class FakeAuthorizationInfo {
8
8
  getAuthorizationStatus() {
9
9
  // gas-fakes always handles auth out-of-band via CLI,
10
10
  // so during script execution, we assume auth is not required.
11
- return ScriptEnums.AuthorizationStatus.NOT_REQUIRED;
11
+ return AuthorizationStatus.NOT_REQUIRED;
12
12
  }
13
13
 
14
14
  getAuthorizationUrl() {
15
- // Return null since we don't require inline authorization
16
- return null;
15
+ // Return empty string since we don't require inline authorization, matching live Apps Script parity
16
+ return "";
17
17
  }
18
18
  }
19
19
 
@@ -292,7 +292,34 @@ export class FakeEmbeddedChartBuilder {
292
292
  return axis;
293
293
  }
294
294
 
295
- reverseCategories() { notYetImplemented('reverseCategories'); return this; }
295
+ __extractTextStyle(textStyle) {
296
+ if (!textStyle || !textStyle.getFontFamily) return textStyle; // fallback if it's not a FakeTextStyle
297
+ const format = {};
298
+ if (textStyle.getFontFamily() !== undefined) format.fontFamily = textStyle.getFontFamily();
299
+ if (textStyle.getFontSize() !== undefined) format.fontSize = textStyle.getFontSize();
300
+ if (textStyle.isBold() !== undefined) format.bold = textStyle.isBold();
301
+ if (textStyle.isItalic() !== undefined) format.italic = textStyle.isItalic();
302
+ if (textStyle.isStrikethrough() !== undefined) format.strikethrough = textStyle.isStrikethrough();
303
+ if (textStyle.isUnderline() !== undefined) format.underline = textStyle.isUnderline();
304
+ const color = textStyle.getForegroundColorObject && textStyle.getForegroundColorObject();
305
+ if (color && color.asRgbColor) {
306
+ const rgb = color.asRgbColor();
307
+ format.foregroundColorStyle = {
308
+ rgbColor: { red: rgb.getRed() / 255, green: rgb.getGreen() / 255, blue: rgb.getBlue() / 255 }
309
+ };
310
+ }
311
+ return format;
312
+ }
313
+
314
+ reverseCategories() {
315
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "reverseCategories");
316
+ const chartType = this.__apiChart.spec.basicChart ? this.__apiChart.spec.basicChart.chartType : null;
317
+ const axisPos = chartType === "BAR" ? "LEFT_AXIS" : "BOTTOM_AXIS";
318
+ const axis = this.__getAxis(axisPos);
319
+ axis.reverseDirection = true;
320
+ return this;
321
+ }
322
+
296
323
  setBackgroundColor(color) { return this.setOption("backgroundColor", color); }
297
324
 
298
325
  setColors(colors) {
@@ -306,8 +333,17 @@ export class FakeEmbeddedChartBuilder {
306
333
  return this;
307
334
  }
308
335
 
309
- setLegendTextStyle(textStyle) { notYetImplemented('setLegendTextStyle'); return this; }
310
- setPointStyle(pointStyle) { notYetImplemented('setPointStyle'); return this; }
336
+ setLegendTextStyle(textStyle) {
337
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setLegendTextStyle");
338
+ if (!this.__apiChart.spec.basicChart) this.__apiChart.spec.basicChart = {};
339
+ this.__apiChart.spec.basicChart.legendTextStyle = this.__extractTextStyle(textStyle);
340
+ return this;
341
+ }
342
+
343
+ setPointStyle(pointStyle) {
344
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setPointStyle");
345
+ return this.setOption("pointStyle", pointStyle ? pointStyle.toString() : "NONE");
346
+ }
311
347
 
312
348
  setRange(min, max) {
313
349
  ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setRange");
@@ -332,8 +368,17 @@ export class FakeEmbeddedChartBuilder {
332
368
  return this;
333
369
  }
334
370
 
335
- setTitleTextStyle(textStyle) { notYetImplemented('setTitleTextStyle'); return this; }
336
- setXAxisTextStyle(textStyle) { notYetImplemented('setXAxisTextStyle'); return this; }
371
+ setTitleTextStyle(textStyle) {
372
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setTitleTextStyle");
373
+ this.__apiChart.spec.titleTextStyle = this.__extractTextStyle(textStyle);
374
+ return this;
375
+ }
376
+
377
+ setXAxisTextStyle(textStyle) {
378
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setXAxisTextStyle");
379
+ this.__getAxis("BOTTOM_AXIS").textStyle = this.__extractTextStyle(textStyle);
380
+ return this;
381
+ }
337
382
 
338
383
  setXAxisTitle(title) {
339
384
  ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setXAxisTitle");
@@ -341,8 +386,17 @@ export class FakeEmbeddedChartBuilder {
341
386
  return this;
342
387
  }
343
388
 
344
- setXAxisTitleTextStyle(textStyle) { notYetImplemented('setXAxisTitleTextStyle'); return this; }
345
- setYAxisTextStyle(textStyle) { notYetImplemented('setYAxisTextStyle'); return this; }
389
+ setXAxisTitleTextStyle(textStyle) {
390
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setXAxisTitleTextStyle");
391
+ this.__getAxis("BOTTOM_AXIS").titleTextStyle = this.__extractTextStyle(textStyle);
392
+ return this;
393
+ }
394
+
395
+ setYAxisTextStyle(textStyle) {
396
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setYAxisTextStyle");
397
+ this.__getAxis("LEFT_AXIS").textStyle = this.__extractTextStyle(textStyle);
398
+ return this;
399
+ }
346
400
 
347
401
  setYAxisTitle(title) {
348
402
  ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setYAxisTitle");
@@ -350,8 +404,21 @@ export class FakeEmbeddedChartBuilder {
350
404
  return this;
351
405
  }
352
406
 
353
- setYAxisTitleTextStyle(textStyle) { notYetImplemented('setYAxisTitleTextStyle'); return this; }
354
- useLogScale() { notYetImplemented('useLogScale'); return this; }
407
+ setYAxisTitleTextStyle(textStyle) {
408
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setYAxisTitleTextStyle");
409
+ this.__getAxis("LEFT_AXIS").titleTextStyle = this.__extractTextStyle(textStyle);
410
+ return this;
411
+ }
412
+
413
+ useLogScale() {
414
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "useLogScale");
415
+ const chartType = this.__apiChart.spec.basicChart ? this.__apiChart.spec.basicChart.chartType : null;
416
+ const axisPos = chartType === "BAR" ? "BOTTOM_AXIS" : "LEFT_AXIS";
417
+ const axis = this.__getAxis(axisPos);
418
+ axis.logScale = true;
419
+ return this;
420
+ }
421
+
355
422
  set3D() { return this.setOption("is3D", true); }
356
423
  enablePaging(enable, pageSize) { notYetImplemented('enablePaging'); return this; }
357
424
  enableRtlTable(enable) { notYetImplemented('enableRtlTable'); return this; }
@@ -361,7 +428,12 @@ export class FakeEmbeddedChartBuilder {
361
428
  setInitialSortingDescending(column) { notYetImplemented('setInitialSortingDescending'); return this; }
362
429
  showRowNumberColumn(show) { notYetImplemented('showRowNumberColumn'); return this; }
363
430
  useAlternatingRowStyle(use) { notYetImplemented('useAlternatingRowStyle'); return this; }
364
- setXAxisLogScale() { notYetImplemented('setXAxisLogScale'); return this; }
431
+
432
+ setXAxisLogScale() {
433
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setXAxisLogScale");
434
+ this.__getAxis("BOTTOM_AXIS").logScale = true;
435
+ return this;
436
+ }
365
437
 
366
438
  setXAxisRange(min, max) {
367
439
  ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setXAxisRange");
@@ -370,7 +442,11 @@ export class FakeEmbeddedChartBuilder {
370
442
  return this;
371
443
  }
372
444
 
373
- setYAxisLogScale() { notYetImplemented('setYAxisLogScale'); return this; }
445
+ setYAxisLogScale() {
446
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setYAxisLogScale");
447
+ this.__getAxis("LEFT_AXIS").logScale = true;
448
+ return this;
449
+ }
374
450
 
375
451
  setYAxisRange(min, max) {
376
452
  ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "setYAxisRange");
@@ -379,7 +455,11 @@ export class FakeEmbeddedChartBuilder {
379
455
  return this;
380
456
  }
381
457
 
382
- reverseDirection() { notYetImplemented('reverseDirection'); return this; }
458
+ reverseDirection() {
459
+ ScriptApp.__behavior.checkMethod("EmbeddedChartBuilder", "reverseDirection");
460
+ this.__getAxis("BOTTOM_AXIS").reverseDirection = true;
461
+ return this;
462
+ }
383
463
 
384
464
  toString() {
385
465
  const type = this.__apiChart.spec.basicChart?.chartType;