@mcpher/gas-fakes 1.2.22 → 1.2.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.
package/README.RU.md CHANGED
@@ -359,6 +359,8 @@ const getParentsIterator = ({
359
359
  - [Supercharge Your Google Apps Script Caching with GasFlexCache](https://ramblings.mcpher.com/supercharge-your-google-apps-script-caching-with-gasflexcache/)
360
360
  - [Fake-Sandbox for Google Apps Script: Granular controls.](https://ramblings.mcpher.com/fake-sandbox-for-google-apps-script-granular-controls/)
361
361
  - [A Fake-Sandbox for Google Apps Script: Securely Executing Code Generated by Gemini CLI](https://ramblings.mcpher.com/gas-fakes-sandbox/)
362
+ - [A New Era for Google Apps Script: Unlocking the Future of Google Workspace Automation with Natural Language](https://medium.com/google-cloud/a-new-era-for-google-apps-script-unlocking-the-future-of-google-workspace-automation-with-natural-a9cecf87b4c6)
363
+ - [Next-Generation Google Apps Script Development: Leveraging Antigravity and Gemini 3.0](https://medium.com/google-cloud/next-generation-google-apps-script-development-leveraging-antigravity-and-gemini-3-0-c4d5affbc1a8)
362
364
  - [Modern Google Apps Script Workflow Building on the Cloud](https://medium.com/google-cloud/modern-google-apps-script-workflow-building-on-the-cloud-2255dbd32ac3)
363
365
  - [Bridging the Gap: Seamless Integration for Local Google Apps Script Development](https://medium.com/@tanaike/bridging-the-gap-seamless-integration-for-local-google-apps-script-development-9b9b973aeb02)
364
366
  - [Next-Level Google Apps Script Development](https://medium.com/google-cloud/next-level-google-apps-script-development-654be5153912)
package/README.md CHANGED
@@ -178,6 +178,8 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
178
178
  - [Supercharge Your Google Apps Script Caching with GasFlexCache](https://ramblings.mcpher.com/supercharge-your-google-apps-script-caching-with-gasflexcache/)
179
179
  - [Fake-Sandbox for Google Apps Script: Granular controls.](https://ramblings.mcpher.com/fake-sandbox-for-google-apps-script-granular-controls/)
180
180
  - [A Fake-Sandbox for Google Apps Script: Securely Executing Code Generated by Gemini CLI](https://ramblings.mcpher.com/gas-fakes-sandbox/)
181
+ - [A New Era for Google Apps Script: Unlocking the Future of Google Workspace Automation with Natural Language](https://medium.com/google-cloud/a-new-era-for-google-apps-script-unlocking-the-future-of-google-workspace-automation-with-natural-a9cecf87b4c6)
182
+ - [Next-Generation Google Apps Script Development: Leveraging Antigravity and Gemini 3.0](https://medium.com/google-cloud/next-generation-google-apps-script-development-leveraging-antigravity-and-gemini-3-0-c4d5affbc1a8)
181
183
  - [Modern Google Apps Script Workflow Building on the Cloud](https://medium.com/google-cloud/modern-google-apps-script-workflow-building-on-the-cloud-2255dbd32ac3)
182
184
  - [Bridging the Gap: Seamless Integration for Local Google Apps Script Development](https://medium.com/@tanaike/bridging-the-gap-seamless-integration-for-local-google-apps-script-development-9b9b973aeb02)
183
185
  - [Next-Level Google Apps Script Development](https://medium.com/google-cloud/next-level-google-apps-script-development-654be5153912)
package/gas-fakes.js CHANGED
@@ -6,8 +6,7 @@
6
6
 
7
7
  import fs from "fs";
8
8
  import path from "path";
9
- import { exec } from "child_process";
10
- import { promisify } from "util";
9
+ import { spawn } from "child_process";
11
10
  import { Command } from "commander";
12
11
  import dotenv from "dotenv";
13
12
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -31,9 +30,8 @@ const VERSION = pjson.version;
31
30
  // CONSTANTS & UTILITIES
32
31
  // -----------------------------------------------------------------------------
33
32
 
34
- const CLI_VERSION = "0.0.13";
35
- const MCP_VERSION = "0.0.3";
36
- const execAsync = promisify(exec);
33
+ const CLI_VERSION = "0.0.14";
34
+ const MCP_VERSION = "0.0.4";
37
35
 
38
36
  /**
39
37
  * Replaces escaped newline characters ('\\n') with actual newlines,
@@ -237,132 +235,327 @@ async function executeGasScript(options) {
237
235
  // -----------------------------------------------------------------------------
238
236
 
239
237
  /**
240
- * Defines and runs the MCP server for gas-fakes.
238
+ * Helper: Constructs the CLI arguments array for gas-fakes execution.
239
+ * Modified to return an array suitable for spawn (no shell escaping needed).
240
+ * @param {object} params Configuration parameters
241
+ * @returns {string[]} Array of CLI arguments
241
242
  */
242
- async function startMcpServer() {
243
- const server = new McpServer({
244
- name: "gas-fakes-mcp",
245
- version: MCP_VERSION,
243
+ function buildCliArguments(params) {
244
+ const {
245
+ filename,
246
+ script,
247
+ args,
248
+ sandbox,
249
+ whitelistRead,
250
+ whitelistReadWrite,
251
+ whitelistReadWriteTrash,
252
+ json,
253
+ } = params;
254
+
255
+ const cliArgs = [];
256
+
257
+ // Input source
258
+ if (filename) {
259
+ cliArgs.push("-f", filename);
260
+ }
261
+ if (script) {
262
+ cliArgs.push("-s", script);
263
+ }
264
+
265
+ // Execution arguments
266
+ if (args) {
267
+ cliArgs.push("-a", JSON.stringify(args));
268
+ }
269
+
270
+ // Sandbox & Permissions
271
+ if (sandbox) cliArgs.push("-x");
272
+ if (whitelistRead) cliArgs.push("-w", whitelistRead);
273
+ if (whitelistReadWrite) cliArgs.push("--ww", whitelistReadWrite);
274
+ if (whitelistReadWriteTrash) cliArgs.push("--wt", whitelistReadWriteTrash);
275
+ if (json) cliArgs.push("-j", JSON.stringify(json));
276
+
277
+ return cliArgs;
278
+ }
279
+
280
+ /**
281
+ * Helper: Executes the gas-fakes command via child_process.spawn.
282
+ * Uses spawn instead of exec to avoid shell interpretation of arguments.
283
+ * @param {string[]} cliArgs Arguments to pass to the command
284
+ * @returns {Promise<object>} MCP tool result object
285
+ */
286
+ async function runGasFakesProcess(cliArgs) {
287
+ return new Promise((resolve) => {
288
+ // We invoke the current node executable with the current script
289
+ const child = spawn(process.execPath, [process.argv[1], ...cliArgs], {
290
+ env: process.env,
291
+ stdio: ["ignore", "pipe", "pipe"], // ignore stdin, capture stdout/stderr
292
+ shell: false, // Important: Disable shell execution
293
+ });
294
+
295
+ let stdoutData = "";
296
+ let stderrData = "";
297
+
298
+ child.stdout.on("data", (data) => {
299
+ stdoutData += data.toString();
300
+ });
301
+
302
+ child.stderr.on("data", (data) => {
303
+ stderrData += data.toString();
304
+ });
305
+
306
+ child.on("close", (code) => {
307
+ if (code === 0) {
308
+ resolve({
309
+ content: [
310
+ { type: "text", text: stdoutData || "Execution finished." },
311
+ ],
312
+ isError: false,
313
+ });
314
+ } else {
315
+ // If there's content in stdout, it might contain the error info from gas-fakes
316
+ const output = stderrData || stdoutData || "Unknown error occurred";
317
+ resolve({
318
+ content: [{ type: "text", text: output }],
319
+ isError: true,
320
+ });
321
+ }
322
+ });
323
+
324
+ child.on("error", (err) => {
325
+ resolve({
326
+ content: [{ type: "text", text: err.message }],
327
+ isError: true,
328
+ });
329
+ });
246
330
  });
331
+ }
247
332
 
248
- const mcpToolSchema = {
333
+ /**
334
+ * Registers the default "run-gas-by-gas-fakes" tool.
335
+ */
336
+ function registerDefaultTool(server) {
337
+ const schema1 = {
249
338
  description: [
250
339
  `Use this to safely run Google Apps Script in a sandbox using gas-fakes.`,
251
340
  `# Important`,
252
341
  `- Use the extension of the Google Apps Script files as \`js\`. Don't use \`gs\``,
253
- `- When you provide the generated Google Apps Script to the tool "gas-fakes" of the MCP server "gas-development-kit-extension", please be careful of the following rule. For example, when you generated a Google Apps Script like \`function sample() { script }\`, please add \`sample();\` to execute the function. Or, you can also create a Google Apps Script without enclosing the script with \`function sample() { script }\`.`,
342
+ `- When providing script content, ensure functions are called (e.g., add \`sample();\`).`,
254
343
  ].join("\n"),
255
344
  inputSchema: {
256
345
  filename: z
257
346
  .string()
258
- .describe(
259
- `Provide a filename with the path of the file, including Google Apps Script. Write the Google Apps Script into a file and use this.`
260
- ),
347
+ .optional() // Made optional because script can be provided
348
+ .describe(`Path to the file containing Google Apps Script.`),
349
+ script: z
350
+ .string()
351
+ .optional()
352
+ .describe(`Direct GAS script content string.`),
261
353
  sandbox: z
262
354
  .boolean()
263
355
  .describe("Use to run Google Apps Script in a sandbox."),
264
356
  whitelistRead: z
265
357
  .string()
358
+ .optional()
266
359
  .describe(
267
- "Whitelist of file IDs for readonly access (comma-separated). Enables sandbox mode."
268
- )
269
- .optional(),
360
+ "Whitelist of file IDs for readonly access (comma-separated). When the file IDs and folder IDs are used or provided, use `whiteListRead`, `whitelistReadWrite`, or `whitelistReadWriteTrash` by judging from the prompt."
361
+ ),
270
362
  whitelistReadWrite: z
271
363
  .string()
364
+ .optional()
272
365
  .describe(
273
- "Whitelist of file IDs for read/write access (comma-separated). Enables sandbox mode."
274
- )
275
- .optional(),
366
+ "Whitelist of file IDs for read/write access (comma-separated). When the file IDs and folder IDs are used or provided, use `whiteListRead`, `whitelistReadWrite`, or `whitelistReadWriteTrash` by judging from the prompt."
367
+ ),
276
368
  whitelistReadWriteTrash: z
277
369
  .string()
370
+ .optional()
278
371
  .describe(
279
- "Whitelist of file IDs for read/write/trash access (comma-separated). Enables sandbox mode."
280
- )
281
- .optional(),
372
+ "Whitelist of file IDs for read/write/trash access (comma-separated). When the file IDs and folder IDs are used or provided, use `whiteListRead`, `whitelistReadWrite`, or `whitelistReadWriteTrash` by judging from the prompt."
373
+ ),
282
374
  json: z
283
375
  .object({
284
376
  whitelistItems: z
285
377
  .array(
286
378
  z.object({
287
- itemId: z
288
- .string()
289
- .describe("The file or folder ID on Google Drive."),
290
- read: z.boolean().optional().default(true),
291
- write: z.boolean().optional().default(false),
292
- trash: z.boolean().optional().default(false),
379
+ itemId: z.string(),
380
+ read: z.boolean().default(true).optional(),
381
+ write: z.boolean().default(false).optional(),
382
+ trash: z.boolean().default(false).optional(),
293
383
  })
294
384
  )
295
- .describe("A list of items to be whitelisted."),
385
+ .optional(),
296
386
  whitelistServices: z
297
387
  .array(
298
388
  z.object({
299
- className: z
300
- .string()
301
- .describe("The class name of the GAS service."),
302
- methodNames: z
303
- .array(z.string())
304
- .describe(
305
- "A list of method names for the class to be whitelisted."
306
- )
307
- .optional(),
389
+ className: z.string(),
390
+ methodNames: z.array(z.string()).optional(),
308
391
  })
309
392
  )
310
- .describe("A list of services to be whitelisted.")
311
- .optional(),
312
- blacklistServices: z
313
- .array(z.string())
314
- .describe("A list of GAS services to be blacklisted.")
315
393
  .optional(),
394
+ blacklistServices: z.array(z.string()).optional(),
316
395
  })
317
- .describe("A JSON object for advanced sandbox configuration.")
318
- .optional(),
396
+ .optional()
397
+ .describe("Advanced sandbox configuration JSON."),
319
398
  },
320
399
  };
321
400
 
322
- const mcpToolFunc = async (options = {}) => {
323
- const {
324
- filename,
325
- sandbox,
326
- whitelistRead,
327
- whitelistReadWrite,
328
- whitelistReadWriteTrash,
329
- json,
330
- } = options;
331
-
332
- if (!filename) {
401
+ server.registerTool("run-gas-by-gas-fakes", schema1, async (args) => {
402
+ if (!args.filename && !args.script) {
333
403
  return {
334
404
  content: [
335
- { type: "text", text: "Error: `filename` is a required parameter." },
405
+ {
406
+ type: "text",
407
+ text: "Error: Either `filename` or `script` is required.",
408
+ },
336
409
  ],
337
410
  isError: true,
338
411
  };
339
412
  }
413
+ const cliArgs = buildCliArguments(args);
414
+ return await runGasFakesProcess(cliArgs);
415
+ });
340
416
 
341
- try {
342
- const cliArgs = [];
343
- cliArgs.push(`-f "${filename}"`);
344
- if (sandbox) cliArgs.push("-x");
345
- if (whitelistRead) cliArgs.push(`-w "${whitelistRead}"`);
346
- if (whitelistReadWrite) cliArgs.push(`--ww "${whitelistReadWrite}"`);
347
- if (whitelistReadWriteTrash)
348
- cliArgs.push(`--wt "${whitelistReadWriteTrash}"`);
349
- if (json) cliArgs.push(`-j '${JSON.stringify(json)}'`);
350
-
351
- const command = `gas-fakes ${cliArgs.join(" ")}`;
352
- const { stdout } = await execAsync(command);
353
- return {
354
- content: [{ type: "text", text: stdout || "Execution finished." }],
355
- isError: false,
356
- };
357
- } catch (err) {
417
+ const schema2 = {
418
+ description: [
419
+ `Use this to create the tools of the MCP server using Google Apps Script as a new file. If a file \`settings.json\` or \`mcp_config.json\` or and so on for loading the MCP servers include \`--tools\` and the tool file to \`gas-fakes\` MCP server, ask the current client which you want to create a new file including the tools or you want to add the tools to the existing tool file. When a new tool file is created, show how to use the tool file as follows. When the created tool file is \`toolFile.js\`, modify the MCP server setting to \`"mcpServers": { "gas-fakes": { "command": "gas-fakes", "args": [ "mcp", "--tools", "{Absolute path}/toolFile.js" ] } }\`. Don't forget to replace \`{Absolute path}\` with the actual path.`,
420
+ ].join("\n"),
421
+ inputSchema: {
422
+ filename: z
423
+ .string()
424
+ .describe(
425
+ "Filename of the tool file built by Google Apps Script. The extension is required to be `.js`."
426
+ ),
427
+ tools: z
428
+ .array(
429
+ z
430
+ .object({
431
+ name: z.string().describe("Tool name."),
432
+ schema: z
433
+ .string()
434
+ .describe(
435
+ `JSON schema of the arguments to \`gas-script\`. This is required to be an object by creating zod. The simple sample script of Zod is \`{ description: "Use this to search files by a filename on Google Drive.", inputSchema: { filename: z.string().describe("Filename of the search file.") } }\`. Generate a JSON schema using Zod by following this sample. You are required to generate the JSON schema using Zod. This object of inputSchema will be used as "object" in Google Apps Script.`
436
+ ),
437
+ gas_script: z
438
+ .string()
439
+ .describe(
440
+ `Generated Google Apps Script. Please be careful of the following rule. For example, when you generated a Google Apps Script like \`function sample(object) { script }\`, please add \`return sample(object);\` to execute the function. This is a very important point. When the prompt says "Create a function", please create a function and add a line of script for calling the function and returning the response like \`return sample(object);\`. Or, you can also create a Google Apps Script without enclosing the script with \`function sample(object) { script }\`. When you want to return the value, show the value using \`console.log\` in the script, or return the value using \`return\`. The simple sample script is \`const { filename } = object; const files = DriveApp.getFilesByName(filename); const ar = []; while (files.hasNext()) { const file = files.next(); ar.push({ filename: file.getName(), fileId: file.getId() }); } return ar;\` This tool is required to be used for creating tools as a new file. When you want to add more tools to the existing file, add them to an array \`tools\` in the file by following the specification of \`tools\`. If you cannot find the array, please create it as a new file.`
441
+ ),
442
+ })
443
+ .describe("An object for each tool.")
444
+ )
445
+ .describe("An array including tools."),
446
+ },
447
+ };
448
+ server.registerTool("create-new-tools", schema2, async (args) => {
449
+ if (!args.filename || !args.tools) {
358
450
  return {
359
- content: [{ type: "text", text: err.message }],
451
+ content: [
452
+ {
453
+ type: "text",
454
+ text: "Error: `filename` and `tools` are required.",
455
+ },
456
+ ],
360
457
  isError: true,
361
458
  };
362
459
  }
363
- };
460
+ const tool_ar = args.tools
461
+ .map(
462
+ ({ name, schema, gas_script }) =>
463
+ `{ name: "${name}", schema: ${schema}, func: (object = {}) => { ${gas_script} } }`
464
+ )
465
+ .join(", ");
466
+ const tool_script = [
467
+ `import { z } from "zod";`,
468
+ ``,
469
+ `const tools = [${tool_ar}];`,
470
+ ].join("\n");
471
+ const absolutePath = path.resolve(process.cwd(), args.filename);
472
+ fs.writeFileSync(absolutePath, tool_script);
473
+ return {
474
+ content: [
475
+ {
476
+ type: "text",
477
+ text: `A new file including tools for gas-fakes-mcp was successfully created as "${absolutePath}".`,
478
+ },
479
+ ],
480
+ isError: false,
481
+ };
482
+ });
483
+ }
484
+
485
+ /**
486
+ * Loads and registers custom tools from an external file.
487
+ */
488
+ async function registerCustomTools(server, toolsPath) {
489
+ if (!toolsPath || !fs.existsSync(toolsPath)) {
490
+ if (toolsPath) console.error(`No tool file: ${toolsPath}`);
491
+ return;
492
+ }
493
+
494
+ const absolutePath = path.resolve(process.cwd(), toolsPath);
495
+ let toolsStr = fs.readFileSync(absolutePath, "utf8");
496
+ toolsStr = toolsStr.replace(/^import.*/gm, "");
497
+ const getTools = new Function("z", `${toolsStr} return tools || [];`);
498
+ const tools = getTools(z);
499
+
500
+ if (!tools || tools.length === 0) return;
501
+
502
+ tools.forEach((tool) => {
503
+ // Extend the custom tool schema with sandbox options
504
+ const extendedSchema = { ...tool.schema };
505
+ extendedSchema.inputSchema = {
506
+ gas_args: z
507
+ .object(tool.schema.inputSchema)
508
+ .describe("Arguments for Google Apps Script."),
509
+ sandbox: z.boolean().describe("Run in sandbox."),
510
+ whitelistRead: z.string().optional().describe("Read-only whitelist IDs."),
511
+ whitelistReadWrite: z
512
+ .string()
513
+ .optional()
514
+ .describe("Read/Write whitelist IDs."),
515
+ whitelistReadWriteTrash: z
516
+ .string()
517
+ .optional()
518
+ .describe("Read/Write/Trash whitelist IDs."),
519
+ json: z.any().optional().describe("Advanced sandbox JSON configuration."),
520
+ };
521
+
522
+ const originalFuncStr = tool.func.toString();
364
523
 
365
- server.registerTool("run-gas-by-gas-fakes", mcpToolSchema, mcpToolFunc);
524
+ const toolHandler = async (opts) => {
525
+ // Wrap the original function string to execute it with args
526
+ const wrappedScript = `return (${originalFuncStr})(args)`;
527
+
528
+ const cliArgs = buildCliArguments({
529
+ script: wrappedScript,
530
+ args: opts.gas_args,
531
+ ...opts,
532
+ });
533
+
534
+ return await runGasFakesProcess(cliArgs);
535
+ };
536
+
537
+ server.registerTool(tool.name, extendedSchema, toolHandler);
538
+ });
539
+ }
540
+
541
+ /**
542
+ * Defines and runs the MCP server for gas-fakes.
543
+ */
544
+ async function startMcpServer(options) {
545
+ const { tools } = options;
546
+
547
+ const server = new McpServer({
548
+ name: "gas-fakes-mcp",
549
+ version: MCP_VERSION,
550
+ });
551
+
552
+ // Register the built-in generic runner
553
+ registerDefaultTool(server);
554
+
555
+ // Register dynamic custom tools if provided
556
+ if (tools) {
557
+ await registerCustomTools(server, tools);
558
+ }
366
559
 
367
560
  const transport = new StdioServerTransport();
368
561
  await server.connect(transport);
@@ -561,6 +754,10 @@ async function main() {
561
754
  program
562
755
  .command("mcp")
563
756
  .description("Launch gas-fakes as an MCP server.")
757
+ .option(
758
+ "-t, --tools <string>",
759
+ "A filename of the custom MCP server tools built by Google Apps Script."
760
+ )
564
761
  .action(startMcpServer);
565
762
 
566
763
  program.showHelpAfterError("(add --help for additional information)");
package/gasfakes.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "manifest": "./appsscript.json",
3
3
  "clasp": "./.clasp.json",
4
4
  "scriptId": "28780abe-aaec-4526-a0bf-9d5c104c68b8",
5
- "documentId": null,
5
+ "documentId": "1h9IGIShgVBVUrUjjawk5MaCEQte_7t32XeEP1Z5jXKQ",
6
6
  "cache": "/tmp/gas-fakes/cache",
7
7
  "properties": "/tmp/gas-fakes/properties"
8
8
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  },
5
5
  "dependencies": {
6
6
  "@mcpher/fake-gasenum": "^1.0.2",
7
- "@mcpher/gas-flex-cache": "^1.1.2",
7
+ "@mcpher/gas-flex-cache": "^1.1.3",
8
8
  "@modelcontextprotocol/sdk": "^1.20.2",
9
9
  "@sindresorhus/is": "^7.0.1",
10
10
  "archiver": "^7.0.1",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "name": "@mcpher/gas-fakes",
35
35
  "author": "bruce mcpherson",
36
- "version": "1.2.22",
36
+ "version": "1.2.23",
37
37
  "license": "MIT",
38
38
  "main": "main.js",
39
39
  "description": "A proof of concept implementation of Apps Script Environment on Node",
@@ -73,7 +73,11 @@ class FakeDocumentApp {
73
73
  }
74
74
  return this.openById(match[1]);
75
75
  }
76
-
76
+ /**
77
+ * note that this in gas-fakes uses the documentId from gasfakes.json config file
78
+ * Returns the document to which the script is container-bound. To interact with document to which the script is not container-bound, use openById(id) or openByUrl(url) instead.
79
+ * @returns {Document}
80
+ */
77
81
  getActiveDocument() {
78
82
  const documentId = Auth.getDocumentId();
79
83
  if (!documentId) return null;
@@ -475,7 +475,16 @@ export class FakeForm {
475
475
  getPublishedUrl() {
476
476
  return `https://docs.google.com/forms/d/e/${this.getId()}/viewform`;
477
477
  }
478
-
478
+ /**
479
+ * Gets the URL to respond to the form
480
+ * https://github.com/brucemcpherson/gas-fakes/issues/111
481
+ * shorten url no longer supported by google
482
+ * @returns {string} The form URL.
483
+ */
484
+ shortenFormUrl() {
485
+ return this.getPublishedUrl()
486
+ }
487
+
479
488
  toString() {
480
489
  return 'Form';
481
490
  }
@@ -1,4 +1,5 @@
1
1
  import { Proxies } from "../../support/proxies.js";
2
+ import { Auth } from "../../support/auth.js";
2
3
  import { newFakeSpreadsheet } from "./fakespreadsheet.js";
3
4
  import {
4
5
  notYetImplemented,
@@ -34,6 +35,8 @@ export const newFakeSpreadsheetApp = (...args) => {
34
35
  */
35
36
  export class FakeSpreadsheetApp {
36
37
  constructor() {
38
+ // in the context of gas-fakes we start with the activespreadsheet being the one mentioned in gasfakes.json
39
+ this.__activeSpreadsheet = null
37
40
  const enumProps = [
38
41
  "AutoFillSeries", // AutoFillSeries An enumeration of the types of series used to calculate auto-filled values.
39
42
  "BandingTheme", // BandingTheme An enumeration of the possible banding themes.
@@ -75,24 +78,19 @@ export class FakeSpreadsheetApp {
75
78
  });
76
79
 
77
80
  const props = [
78
-
79
-
80
81
  "getActive",
81
82
  "newConditionalFormatRule",
82
- "getActiveSpreadsheet",
83
83
  "getActiveSheet",
84
84
  "getCurrentCell",
85
85
  "getActiveRange",
86
86
  "getActiveRangeList",
87
87
  "getSelection",
88
- "setActiveSpreadsheet",
89
88
  "setActiveSheet",
90
89
  "setCurrentCell",
91
90
  "setActiveRange",
92
91
  "setActiveRangeList",
93
92
  "newCellImage",
94
93
  "getUi",
95
-
96
94
  "open",
97
95
 
98
96
  "ChartAggregationType",
@@ -108,6 +106,20 @@ export class FakeSpreadsheetApp {
108
106
  toString() {
109
107
  return "SpreadsheetApp";
110
108
  }
109
+ getActiveSpreadsheet() {
110
+ if (this.__activeSpreadsheet) return this.__activeSpreadsheet;
111
+ // because this is a faked container bound app, we need to get the documentId from the config file
112
+ const documentId = Auth.getDocumentId();
113
+ if (documentId) {
114
+ return this.openById(documentId);
115
+ }
116
+ return null;
117
+ }
118
+
119
+ setActiveSpreadsheet(ss) {
120
+ this.__activeSpreadsheet = ss;
121
+ return this;
122
+ }
111
123
 
112
124
 
113
125
  enableBigQueryExecution() {