@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 +9 -2
- package/package.json +1 -1
- package/src/services/content/contentservice.js +3 -2
- package/src/services/enums/contentenums.js +1 -3
- package/src/services/enums/scriptenums.js +31 -4
- package/src/services/enums/xmlenums.js +14 -0
- package/src/services/scriptapp/app.js +14 -7
- package/src/services/scriptapp/fakeauthorizationinfo.js +4 -4
- package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +92 -12
- package/src/services/spreadsheetapp/fakespreadsheet.js +360 -62
- package/src/services/spreadsheetapp/fakespreadsheettheme.js +53 -0
- package/src/services/urlfetchapp/app.js +216 -175
- package/src/services/xmlservice/app.js +3 -78
- package/src/services/xmlservice/fakeattribute.js +15 -0
- package/src/services/xmlservice/fakecdata.js +40 -0
- package/src/services/xmlservice/fakecomment.js +34 -0
- package/src/services/xmlservice/fakecontent.js +51 -0
- package/src/services/xmlservice/fakedoctype.js +68 -0
- package/src/services/xmlservice/fakedocument.js +110 -13
- package/src/services/xmlservice/fakeelement.js +297 -82
- package/src/services/xmlservice/fakeentityref.js +54 -0
- package/src/services/xmlservice/fakeformat.js +67 -22
- package/src/services/xmlservice/fakeprocessinginstruction.js +44 -0
- package/src/services/xmlservice/faketext.js +39 -0
- package/src/services/xmlservice/fakexmlservice.js +118 -0
- package/src/support/sxfetch.js +60 -0
- package/tools/omlx.env.example +6 -0
- package/tools/omlx_mcp_server.cjs +157 -0
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
|
[](https://youtu.be/oEjpIrkYpEM)
|
|
165
167
|
|
|
168
|
+
## Watch the explainer about delegating work to local LLMs to save token costs
|
|
169
|
+
|
|
170
|
+
[](https://youtu.be/tcvU2NLEaNE)
|
|
171
|
+
|
|
166
172
|
## Watch the gf_agent video on natural language automation
|
|
167
173
|
|
|
168
174
|
[](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.
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
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 {
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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 {
|
|
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
|
|
11
|
+
return AuthorizationStatus.NOT_REQUIRED;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
getAuthorizationUrl() {
|
|
15
|
-
// Return
|
|
16
|
-
return
|
|
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
|
-
|
|
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) {
|
|
310
|
-
|
|
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) {
|
|
336
|
-
|
|
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) {
|
|
345
|
-
|
|
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) {
|
|
354
|
-
|
|
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
|
-
|
|
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() {
|
|
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() {
|
|
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;
|