{"owner":"googleworkspace","repo":"apps-script-samples","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["GEMINI.md"],"skills":{"GEMINI.md":"# Apps Script Sample Development Guide\n\nThis guide outlines best practices for developing Google Apps Script projects, focusing on type safety and modern JavaScript features.\n\n## Important\n\n* For new sample directories, ensure the top-level folder is included in the [`test.yaml`](.github/workflows/test.yaml) GitHub workflow's matrix configuration.\n* Do not move or delete snippet tags: `[END apps_script_... ]` or `[END apps_script_... ]`.\n* Keep code within snippet tags self-contained. Avoid depending on helper functions defined outside the snippet tags if the snippet is intended to be copied and pasted.\n* Avoid function name collisions (e.g., multiple `onOpen` or `main` functions) by placing separate samples in their own directories or files. Do not append suffixes like `_2`, `_3` to function names. For variables, replace collisions with a more descriptive name.\n\n## Tools\n\nLint and format code using [Biome](https://biomejs.dev/).\n\n```bash\npnpm lint\npnpm format\n```\n\n## Apps Script Code Best Practices\n\nApps Script supports the V8 runtime, which enables modern ECMAScript syntax. Using these features makes your code cleaner, more readable, and less error-prone.\n\n### `let` and `const`\nUse `let` and `const` instead of `var` for block-scoped variables.\n\n*   **`const`**: Use for values that should not be reassigned.\n*   **`let`**: Use for values that will change.\n\n```javascript\nconst PI = 3.14;\nlet count = 0;\n\nif (true) {\n  let local = \"I exist only in this block\";\n}\n// local is not accessible here\n```\n\n### Arrow Functions\nUse arrow functions for concise function expressions, especially for callbacks.\n\n```javascript\nconst numbers = [1, 2, 3];\nconst squares = numbers.map(x => x * x); // [1, 4, 9]\n```\n\n### Destructuring\nUnpack values from arrays or properties from objects into distinct variables.\n\n```javascript\nconst user = { name: \"Alice\", age: 30 };\nconst { name, age } = user;\n\nconst coords = [10, 20];\nconst [x, y] = coords;\n```\n\n### Template Literals\nUse template literals for string interpolation and multi-line strings.\n\n```javascript\nconst name = \"World\";\nconst greeting = `Hello, ${name}!`;\n\nconst multiLine = `\n  This is a\n  multi-line string.\n`;\n```\n\n### Default Parameters\nSpecify default values for function parameters.\n\n```javascript\nfunction greet(name = \"Guest\") {\n  console.log(`Hello, ${name}!`);\n}\n\ngreet(); // \"Hello, Guest!\"\n```\n\n### Prefer `for...of` for Iteration\nWhile `forEach` is convenient, `for...of` loops generally offer better performance and more control (e.g., `break`, `continue`) in Apps Script, especially when dealing with large arrays.\n\n```javascript\nconst numbers = [1, 2, 3];\n\n// Using forEach (less performant for large arrays)\nnumbers.forEach(num => {\n  console.log(num);\n});\n\n// Using for...of (preferred)\nfor (const num of numbers) {\n  console.log(num);\n}\n```\n\n## Apps Script V8 Runtime\n\nIt's important to understand that the Apps Script V8 runtime is\nnot a standard Node.js or browser environment. This can lead to compatibility\nissues when incorporating third-party libraries or adapting code examples\nfrom other JavaScript environments.\n\n### Unavailable APIs\n\nThe following standard JavaScript APIs are **NOT** available in the\nApps Script V8 runtime:\n\n*   **Timers**: `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`\n*   **Streams**: `ReadableStream`, `WritableStream`, `TextEncoder`,\n    `TextDecoder`\n*   **Web APIs**: `fetch`, `FormData`, `File`, `Blob`, `URL`, `URLSearchParams`,\n    `DOMException`, `atob`, `btoa`\n*   **Crypto**: `crypto`, `SubtleCrypto`\n*   **Global Objects**: `window`, `navigator`, `performance`, `process`\n    (Node.js)\n\nInstead of the unavailable APIs, you can use the following\nApps Script APIs as alternatives:\n\n*   **Timers**: Use\n    [`Utilities.sleep(milliseconds)`](https://developers.google.com/apps-script/reference/utilities/utilities#sleepmilliseconds)\n    for synchronous pauses. Asynchronous timers are not supported.\n*   **Fetch**: Use [`UrlFetchApp.fetch(url,\n    params)`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app) to make HTTP(S)\n    requests.\n*   **atob**: Use\n    [`Utilities.base64Decode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64decodeencoded)\n    to decode Base64-encoded strings.\n*   **btoa**: Use\n    [`Utilities.base64Encode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64encodedata)\n    to encode strings in Base64.\n*   **Crypto**: Use [`Utilities`](https://developers.google.com/apps-script/reference/utilities/utilities)\n    for cryptographic functions like\n    [`computeDigest()`](https://developers.google.com/apps-script/reference/utilities/utilities#computedigestalgorithm,-value),\n    [`computeHmacSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computehmacsha256signaturevalue,-key),\n    and\n    [`computeRsaSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computersasha256signaturevalue,-key).\n\nFor some APIs, other workarounds might exist. For example, you might be able to\nuse a polyfill for `TextEncoder`.\n\n### Asynchronous Limitations\n\nThe V8 runtime supports `async` and `await` syntax and the `Promise` object.\nHowever, the Apps Script runtime environment is fundamentally\nsynchronous.\n\n*   **Microtasks (Supported)**: The runtime processes the microtask queue (where\n    `Promise.then()` callbacks and `await` resolutions occur) after the current\n    call stack clears.\n*   **Macrotasks (Not Supported)**: Apps Script does not have a\n    standard event loop for macrotasks. Functions like `setTimeout()` and\n    `setInterval()` are not available.\n*   **WebAssembly Exception**: The WebAssembly API is the only built-in\n    feature that operates in a non-blocking manner within the runtime, allowing\n    for specific asynchronous compilation patterns (WebAssembly.instantiate).\n\nAll I/O operations, such as\n[`UrlFetchApp.fetch()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app), are\nblocking. To achieve parallel network requests, use\n[`UrlFetchApp.fetchAll()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchallrequests).\n\n### Class Limitations\n\nThe V8 runtime has specific limitations regarding modern ES6+ class features:\n\n*   **Private Fields**: Private class fields (for example, `#field`) are not\n    supported and cause parsing errors. Consider using closures or `WeakMap` for\n    true encapsulation.\n*   **Static Fields**: Direct static field declarations within the class body\n    (for example, `static count = 0;`) are not supported. Assign static\n    properties to the class after its definition (for example, `MyClass.count =\n    0;`).\n\n### Module Limitations\n\n*   **ES6 Modules**: The V8 runtime does not support ES6 modules (`import` /\n    `export`). To use libraries, you must either use the [\n    Apps Script library mechanism](https://developers.google.com/apps-script/guides/libraries)\n    or bundle your code and its dependencies into a single script file. ([Issue\n    Tracker](https://issuetracker.google.com/issues/134627726))\n*   **File Execution Order**: All script files in your project are executed in a\n    global scope. It's best to avoid top-level code with side effects and ensure\n    functions and classes are defined before being used across files. Explicitly\n    order your files in the editor if dependencies exist between them.\n\n## Type Checking with JSDoc\n\nThis project uses a type checker to validate `.gs` files for errors. Since `.gs` files are technically JavaScript, we use JSDoc comments to provide type information. This ensures your code is type-safe and well-documented.\n\n### Running Checks\n\nYou can run the type checker from the root of the repository.\n\n**Check all projects:**\n```bash\npnpm run check\n```\n\n**Check a specific path:**\nTo check only projects within a specific directory (e.g., `solutions/automations`), pass the path as an argument:\n```bash\npnpm run check solutions/automations\n```\n\n### Core Concepts\n\n#### 1. Basic Types\nUse `@param` and `@return` to define function inputs and outputs.\n\n```javascript\n/**\n * Adds two numbers.\n * @param {number} a The first number.\n * @param {number} b The second number.\n * @return {number} The sum.\n */\nfunction add(a, b) {\n  return a + b;\n}\n```\n\n#### 2. Apps Script Types\nYou can reference global Apps Script types directly.\n\n```javascript\n/**\n * Gets the active sheet name.\n * @return {string} The name of the sheet.\n */\nfunction getSheetName() {\n  // Types like SpreadsheetApp, Sheet, Range are available globally\n  const sheet = SpreadsheetApp.getActiveSheet();\n  return sheet.getName();\n}\n```\n\n#### 3. Optional Parameters\nUse `[]` or `=` to denote optional parameters.\n\n```javascript\n/**\n * @param {string} name The name.\n * @param {number=} age Optional age.\n */\nfunction greet(name, age) {\n  if (age) { ... }\n}\n```\n\n### Advanced Patterns\n\n#### 1. Custom Objects (@typedef)\nFor complex objects, define a type using `@typedef`.\n\n```javascript\n/**\n * @typedef {Object} UserConfig\n * @property {string} username The user's name.\n * @property {boolean} isAdmin Whether the user is an admin.\n * @property {number} [retryCount] Optional retry attempts.\n */\n\n/**\n * Processes a user configuration.\n * @param {UserConfig} config The configuration object.\n */\nfunction processUser(config) {\n  console.log(config.username);\n}\n```\n\n#### 2. Type Casting\nSometimes the type checker cannot infer the type correctly. Use inline `@type` to cast.\n\n```javascript\nconst data = JSON.parse(jsonString);\n\n/** @type {UserConfig} */\nconst config = data;\n```\n\n#### 3. Arrays and Generics\nSpecify array contents clearly.\n\n```javascript\n/**\n * @param {string[]} names An array of strings.\n * @return {Array<number>} An array of numbers.\n */\nfunction lengths(names) {\n  return names.map(n => n.length);\n}\n```\n\n#### 4. Handling `null` and `undefined`\nBe explicit if a value can be null.\n\n```javascript\n/**\n * @param {string|null} id The ID, or null if not found.\n */\nfunction find(id) { ... }\n```\n\n### Common Issues & Fixes\n\n- **TypeScript**: DO NOT REFERENCE GoogleAppsScript in JSDocs. Instead use a locally defined type definition and link to the appropriate reference documenation page if possible.\n- **\"Property 'x' does not exist on type 'Object'\"**: This usually means you are accessing a property on a generic object. Define a `@typedef` for that object structure.\n- **Implicit 'any'**: If you see \"Parameter 'x' implicitly has an 'any' type\", it means you forgot a JSDoc `@param` tag. Add it to fix the error.\n- **Advanced Services**: To fix errors with these globals, check for existence. This helps TypeScript narrow the type and prevents runtime errors if the service is not enabled.\n\n   ```js\n   if (!AdminDirectory) {\n     console.log('AdminDirectory Advanced Service must be enabled.');\n     return;\n   }\n   ```\n\n- **Optional Properties**: Use optional chaining (`?.`) when accessing properties that might be undefined in API responses. This is often the case when when using `fields` to limit the response.\n\n   ```js\n   // Safe access\n   console.log(user.name?.fullName);\n   ```\n\n- **Error Handling**: Avoid wrapping code in `try/catch` blocks if you are only logging the error message. Let the runtime handle the error reporting for cleaner sample code.\n\n   ```js\n   // Avoid this\n   try {\n     AdminDirectory.Users.list();\n   } catch (err) {\n     console.log(err.message);\n   }\n\n   // Prefer this\n   AdminDirectory.Users.list();\n   ```"},"files":{"GEMINI.md":"# Apps Script Sample Development Guide\n\nThis guide outlines best practices for developing Google Apps Script projects, focusing on type safety and modern JavaScript features.\n\n## Important\n\n* For new sample directories, ensure the top-level folder is included in the [`test.yaml`](.github/workflows/test.yaml) GitHub workflow's matrix configuration.\n* Do not move or delete snippet tags: `[END apps_script_... ]` or `[END apps_script_... ]`.\n* Keep code within snippet tags self-contained. Avoid depending on helper functions defined outside the snippet tags if the snippet is intended to be copied and pasted.\n* Avoid function name collisions (e.g., multiple `onOpen` or `main` functions) by placing separate samples in their own directories or files. Do not append suffixes like `_2`, `_3` to function names. For variables, replace collisions with a more descriptive name.\n\n## Tools\n\nLint and format code using [Biome](https://biomejs.dev/).\n\n```bash\npnpm lint\npnpm format\n```\n\n## Apps Script Code Best Practices\n\nApps Script supports the V8 runtime, which enables modern ECMAScript syntax. Using these features makes your code cleaner, more readable, and less error-prone.\n\n### `let` and `const`\nUse `let` and `const` instead of `var` for block-scoped variables.\n\n*   **`const`**: Use for values that should not be reassigned.\n*   **`let`**: Use for values that will change.\n\n```javascript\nconst PI = 3.14;\nlet count = 0;\n\nif (true) {\n  let local = \"I exist only in this block\";\n}\n// local is not accessible here\n```\n\n### Arrow Functions\nUse arrow functions for concise function expressions, especially for callbacks.\n\n```javascript\nconst numbers = [1, 2, 3];\nconst squares = numbers.map(x => x * x); // [1, 4, 9]\n```\n\n### Destructuring\nUnpack values from arrays or properties from objects into distinct variables.\n\n```javascript\nconst user = { name: \"Alice\", age: 30 };\nconst { name, age } = user;\n\nconst coords = [10, 20];\nconst [x, y] = coords;\n```\n\n### Template Literals\nUse template literals for string interpolation and multi-line strings.\n\n```javascript\nconst name = \"World\";\nconst greeting = `Hello, ${name}!`;\n\nconst multiLine = `\n  This is a\n  multi-line string.\n`;\n```\n\n### Default Parameters\nSpecify default values for function parameters.\n\n```javascript\nfunction greet(name = \"Guest\") {\n  console.log(`Hello, ${name}!`);\n}\n\ngreet(); // \"Hello, Guest!\"\n```\n\n### Prefer `for...of` for Iteration\nWhile `forEach` is convenient, `for...of` loops generally offer better performance and more control (e.g., `break`, `continue`) in Apps Script, especially when dealing with large arrays.\n\n```javascript\nconst numbers = [1, 2, 3];\n\n// Using forEach (less performant for large arrays)\nnumbers.forEach(num => {\n  console.log(num);\n});\n\n// Using for...of (preferred)\nfor (const num of numbers) {\n  console.log(num);\n}\n```\n\n## Apps Script V8 Runtime\n\nIt's important to understand that the Apps Script V8 runtime is\nnot a standard Node.js or browser environment. This can lead to compatibility\nissues when incorporating third-party libraries or adapting code examples\nfrom other JavaScript environments.\n\n### Unavailable APIs\n\nThe following standard JavaScript APIs are **NOT** available in the\nApps Script V8 runtime:\n\n*   **Timers**: `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`\n*   **Streams**: `ReadableStream`, `WritableStream`, `TextEncoder`,\n    `TextDecoder`\n*   **Web APIs**: `fetch`, `FormData`, `File`, `Blob`, `URL`, `URLSearchParams`,\n    `DOMException`, `atob`, `btoa`\n*   **Crypto**: `crypto`, `SubtleCrypto`\n*   **Global Objects**: `window`, `navigator`, `performance`, `process`\n    (Node.js)\n\nInstead of the unavailable APIs, you can use the following\nApps Script APIs as alternatives:\n\n*   **Timers**: Use\n    [`Utilities.sleep(milliseconds)`](https://developers.google.com/apps-script/reference/utilities/utilities#sleepmilliseconds)\n    for synchronous pauses. Asynchronous timers are not supported.\n*   **Fetch**: Use [`UrlFetchApp.fetch(url,\n    params)`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app) to make HTTP(S)\n    requests.\n*   **atob**: Use\n    [`Utilities.base64Decode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64decodeencoded)\n    to decode Base64-encoded strings.\n*   **btoa**: Use\n    [`Utilities.base64Encode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64encodedata)\n    to encode strings in Base64.\n*   **Crypto**: Use [`Utilities`](https://developers.google.com/apps-script/reference/utilities/utilities)\n    for cryptographic functions like\n    [`computeDigest()`](https://developers.google.com/apps-script/reference/utilities/utilities#computedigestalgorithm,-value),\n    [`computeHmacSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computehmacsha256signaturevalue,-key),\n    and\n    [`computeRsaSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computersasha256signaturevalue,-key).\n\nFor some APIs, other workarounds might exist. For example, you might be able to\nuse a polyfill for `TextEncoder`.\n\n### Asynchronous Limitations\n\nThe V8 runtime supports `async` and `await` syntax and the `Promise` object.\nHowever, the Apps Script runtime environment is fundamentally\nsynchronous.\n\n*   **Microtasks (Supported)**: The runtime processes the microtask queue (where\n    `Promise.then()` callbacks and `await` resolutions occur) after the current\n    call stack clears.\n*   **Macrotasks (Not Supported)**: Apps Script does not have a\n    standard event loop for macrotasks. Functions like `setTimeout()` and\n    `setInterval()` are not available.\n*   **WebAssembly Exception**: The WebAssembly API is the only built-in\n    feature that operates in a non-blocking manner within the runtime, allowing\n    for specific asynchronous compilation patterns (WebAssembly.instantiate).\n\nAll I/O operations, such as\n[`UrlFetchApp.fetch()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app), are\nblocking. To achieve parallel network requests, use\n[`UrlFetchApp.fetchAll()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchallrequests).\n\n### Class Limitations\n\nThe V8 runtime has specific limitations regarding modern ES6+ class features:\n\n*   **Private Fields**: Private class fields (for example, `#field`) are not\n    supported and cause parsing errors. Consider using closures or `WeakMap` for\n    true encapsulation.\n*   **Static Fields**: Direct static field declarations within the class body\n    (for example, `static count = 0;`) are not supported. Assign static\n    properties to the class after its definition (for example, `MyClass.count =\n    0;`).\n\n### Module Limitations\n\n*   **ES6 Modules**: The V8 runtime does not support ES6 modules (`import` /\n    `export`). To use libraries, you must either use the [\n    Apps Script library mechanism](https://developers.google.com/apps-script/guides/libraries)\n    or bundle your code and its dependencies into a single script file. ([Issue\n    Tracker](https://issuetracker.google.com/issues/134627726))\n*   **File Execution Order**: All script files in your project are executed in a\n    global scope. It's best to avoid top-level code with side effects and ensure\n    functions and classes are defined before being used across files. Explicitly\n    order your files in the editor if dependencies exist between them.\n\n## Type Checking with JSDoc\n\nThis project uses a type checker to validate `.gs` files for errors. Since `.gs` files are technically JavaScript, we use JSDoc comments to provide type information. This ensures your code is type-safe and well-documented.\n\n### Running Checks\n\nYou can run the type checker from the root of the repository.\n\n**Check all projects:**\n```bash\npnpm run check\n```\n\n**Check a specific path:**\nTo check only projects within a specific directory (e.g., `solutions/automations`), pass the path as an argument:\n```bash\npnpm run check solutions/automations\n```\n\n### Core Concepts\n\n#### 1. Basic Types\nUse `@param` and `@return` to define function inputs and outputs.\n\n```javascript\n/**\n * Adds two numbers.\n * @param {number} a The first number.\n * @param {number} b The second number.\n * @return {number} The sum.\n */\nfunction add(a, b) {\n  return a + b;\n}\n```\n\n#### 2. Apps Script Types\nYou can reference global Apps Script types directly.\n\n```javascript\n/**\n * Gets the active sheet name.\n * @return {string} The name of the sheet.\n */\nfunction getSheetName() {\n  // Types like SpreadsheetApp, Sheet, Range are available globally\n  const sheet = SpreadsheetApp.getActiveSheet();\n  return sheet.getName();\n}\n```\n\n#### 3. Optional Parameters\nUse `[]` or `=` to denote optional parameters.\n\n```javascript\n/**\n * @param {string} name The name.\n * @param {number=} age Optional age.\n */\nfunction greet(name, age) {\n  if (age) { ... }\n}\n```\n\n### Advanced Patterns\n\n#### 1. Custom Objects (@typedef)\nFor complex objects, define a type using `@typedef`.\n\n```javascript\n/**\n * @typedef {Object} UserConfig\n * @property {string} username The user's name.\n * @property {boolean} isAdmin Whether the user is an admin.\n * @property {number} [retryCount] Optional retry attempts.\n */\n\n/**\n * Processes a user configuration.\n * @param {UserConfig} config The configuration object.\n */\nfunction processUser(config) {\n  console.log(config.username);\n}\n```\n\n#### 2. Type Casting\nSometimes the type checker cannot infer the type correctly. Use inline `@type` to cast.\n\n```javascript\nconst data = JSON.parse(jsonString);\n\n/** @type {UserConfig} */\nconst config = data;\n```\n\n#### 3. Arrays and Generics\nSpecify array contents clearly.\n\n```javascript\n/**\n * @param {string[]} names An array of strings.\n * @return {Array<number>} An array of numbers.\n */\nfunction lengths(names) {\n  return names.map(n => n.length);\n}\n```\n\n#### 4. Handling `null` and `undefined`\nBe explicit if a value can be null.\n\n```javascript\n/**\n * @param {string|null} id The ID, or null if not found.\n */\nfunction find(id) { ... }\n```\n\n### Common Issues & Fixes\n\n- **TypeScript**: DO NOT REFERENCE GoogleAppsScript in JSDocs. Instead use a locally defined type definition and link to the appropriate reference documenation page if possible.\n- **\"Property 'x' does not exist on type 'Object'\"**: This usually means you are accessing a property on a generic object. Define a `@typedef` for that object structure.\n- **Implicit 'any'**: If you see \"Parameter 'x' implicitly has an 'any' type\", it means you forgot a JSDoc `@param` tag. Add it to fix the error.\n- **Advanced Services**: To fix errors with these globals, check for existence. This helps TypeScript narrow the type and prevents runtime errors if the service is not enabled.\n\n   ```js\n   if (!AdminDirectory) {\n     console.log('AdminDirectory Advanced Service must be enabled.');\n     return;\n   }\n   ```\n\n- **Optional Properties**: Use optional chaining (`?.`) when accessing properties that might be undefined in API responses. This is often the case when when using `fields` to limit the response.\n\n   ```js\n   // Safe access\n   console.log(user.name?.fullName);\n   ```\n\n- **Error Handling**: Avoid wrapping code in `try/catch` blocks if you are only logging the error message. Let the runtime handle the error reporting for cleaner sample code.\n\n   ```js\n   // Avoid this\n   try {\n     AdminDirectory.Users.list();\n   } catch (err) {\n     console.log(err.message);\n   }\n\n   // Prefer this\n   AdminDirectory.Users.list();\n   ```"},"items":[{"name":"GEMINI.md","path":"GEMINI.md","title":"GEMINI.md","content":"# Apps Script Sample Development Guide\n\nThis guide outlines best practices for developing Google Apps Script projects, focusing on type safety and modern JavaScript features.\n\n## Important\n\n* For new sample directories, ensure the top-level folder is included in the [`test.yaml`](.github/workflows/test.yaml) GitHub workflow's matrix configuration.\n* Do not move or delete snippet tags: `[END apps_script_... ]` or `[END apps_script_... ]`.\n* Keep code within snippet tags self-contained. Avoid depending on helper functions defined outside the snippet tags if the snippet is intended to be copied and pasted.\n* Avoid function name collisions (e.g., multiple `onOpen` or `main` functions) by placing separate samples in their own directories or files. Do not append suffixes like `_2`, `_3` to function names. For variables, replace collisions with a more descriptive name.\n\n## Tools\n\nLint and format code using [Biome](https://biomejs.dev/).\n\n```bash\npnpm lint\npnpm format\n```\n\n## Apps Script Code Best Practices\n\nApps Script supports the V8 runtime, which enables modern ECMAScript syntax. Using these features makes your code cleaner, more readable, and less error-prone.\n\n### `let` and `const`\nUse `let` and `const` instead of `var` for block-scoped variables.\n\n*   **`const`**: Use for values that should not be reassigned.\n*   **`let`**: Use for values that will change.\n\n```javascript\nconst PI = 3.14;\nlet count = 0;\n\nif (true) {\n  let local = \"I exist only in this block\";\n}\n// local is not accessible here\n```\n\n### Arrow Functions\nUse arrow functions for concise function expressions, especially for callbacks.\n\n```javascript\nconst numbers = [1, 2, 3];\nconst squares = numbers.map(x => x * x); // [1, 4, 9]\n```\n\n### Destructuring\nUnpack values from arrays or properties from objects into distinct variables.\n\n```javascript\nconst user = { name: \"Alice\", age: 30 };\nconst { name, age } = user;\n\nconst coords = [10, 20];\nconst [x, y] = coords;\n```\n\n### Template Literals\nUse template literals for string interpolation and multi-line strings.\n\n```javascript\nconst name = \"World\";\nconst greeting = `Hello, ${name}!`;\n\nconst multiLine = `\n  This is a\n  multi-line string.\n`;\n```\n\n### Default Parameters\nSpecify default values for function parameters.\n\n```javascript\nfunction greet(name = \"Guest\") {\n  console.log(`Hello, ${name}!`);\n}\n\ngreet(); // \"Hello, Guest!\"\n```\n\n### Prefer `for...of` for Iteration\nWhile `forEach` is convenient, `for...of` loops generally offer better performance and more control (e.g., `break`, `continue`) in Apps Script, especially when dealing with large arrays.\n\n```javascript\nconst numbers = [1, 2, 3];\n\n// Using forEach (less performant for large arrays)\nnumbers.forEach(num => {\n  console.log(num);\n});\n\n// Using for...of (preferred)\nfor (const num of numbers) {\n  console.log(num);\n}\n```\n\n## Apps Script V8 Runtime\n\nIt's important to understand that the Apps Script V8 runtime is\nnot a standard Node.js or browser environment. This can lead to compatibility\nissues when incorporating third-party libraries or adapting code examples\nfrom other JavaScript environments.\n\n### Unavailable APIs\n\nThe following standard JavaScript APIs are **NOT** available in the\nApps Script V8 runtime:\n\n*   **Timers**: `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`\n*   **Streams**: `ReadableStream`, `WritableStream`, `TextEncoder`,\n    `TextDecoder`\n*   **Web APIs**: `fetch`, `FormData`, `File`, `Blob`, `URL`, `URLSearchParams`,\n    `DOMException`, `atob`, `btoa`\n*   **Crypto**: `crypto`, `SubtleCrypto`\n*   **Global Objects**: `window`, `navigator`, `performance`, `process`\n    (Node.js)\n\nInstead of the unavailable APIs, you can use the following\nApps Script APIs as alternatives:\n\n*   **Timers**: Use\n    [`Utilities.sleep(milliseconds)`](https://developers.google.com/apps-script/reference/utilities/utilities#sleepmilliseconds)\n    for synchronous pauses. Asynchronous timers are not supported.\n*   **Fetch**: Use [`UrlFetchApp.fetch(url,\n    params)`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app) to make HTTP(S)\n    requests.\n*   **atob**: Use\n    [`Utilities.base64Decode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64decodeencoded)\n    to decode Base64-encoded strings.\n*   **btoa**: Use\n    [`Utilities.base64Encode()`](https://developers.google.com/apps-script/reference/utilities/utilities#base64encodedata)\n    to encode strings in Base64.\n*   **Crypto**: Use [`Utilities`](https://developers.google.com/apps-script/reference/utilities/utilities)\n    for cryptographic functions like\n    [`computeDigest()`](https://developers.google.com/apps-script/reference/utilities/utilities#computedigestalgorithm,-value),\n    [`computeHmacSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computehmacsha256signaturevalue,-key),\n    and\n    [`computeRsaSha256Signature()`](https://developers.google.com/apps-script/reference/utilities/utilities#computersasha256signaturevalue,-key).\n\nFor some APIs, other workarounds might exist. For example, you might be able to\nuse a polyfill for `TextEncoder`.\n\n### Asynchronous Limitations\n\nThe V8 runtime supports `async` and `await` syntax and the `Promise` object.\nHowever, the Apps Script runtime environment is fundamentally\nsynchronous.\n\n*   **Microtasks (Supported)**: The runtime processes the microtask queue (where\n    `Promise.then()` callbacks and `await` resolutions occur) after the current\n    call stack clears.\n*   **Macrotasks (Not Supported)**: Apps Script does not have a\n    standard event loop for macrotasks. Functions like `setTimeout()` and\n    `setInterval()` are not available.\n*   **WebAssembly Exception**: The WebAssembly API is the only built-in\n    feature that operates in a non-blocking manner within the runtime, allowing\n    for specific asynchronous compilation patterns (WebAssembly.instantiate).\n\nAll I/O operations, such as\n[`UrlFetchApp.fetch()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app), are\nblocking. To achieve parallel network requests, use\n[`UrlFetchApp.fetchAll()`](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchallrequests).\n\n### Class Limitations\n\nThe V8 runtime has specific limitations regarding modern ES6+ class features:\n\n*   **Private Fields**: Private class fields (for example, `#field`) are not\n    supported and cause parsing errors. Consider using closures or `WeakMap` for\n    true encapsulation.\n*   **Static Fields**: Direct static field declarations within the class body\n    (for example, `static count = 0;`) are not supported. Assign static\n    properties to the class after its definition (for example, `MyClass.count =\n    0;`).\n\n### Module Limitations\n\n*   **ES6 Modules**: The V8 runtime does not support ES6 modules (`import` /\n    `export`). To use libraries, you must either use the [\n    Apps Script library mechanism](https://developers.google.com/apps-script/guides/libraries)\n    or bundle your code and its dependencies into a single script file. ([Issue\n    Tracker](https://issuetracker.google.com/issues/134627726))\n*   **File Execution Order**: All script files in your project are executed in a\n    global scope. It's best to avoid top-level code with side effects and ensure\n    functions and classes are defined before being used across files. Explicitly\n    order your files in the editor if dependencies exist between them.\n\n## Type Checking with JSDoc\n\nThis project uses a type checker to validate `.gs` files for errors. Since `.gs` files are technically JavaScript, we use JSDoc comments to provide type information. This ensures your code is type-safe and well-documented.\n\n### Running Checks\n\nYou can run the type checker from the root of the repository.\n\n**Check all projects:**\n```bash\npnpm run check\n```\n\n**Check a specific path:**\nTo check only projects within a specific directory (e.g., `solutions/automations`), pass the path as an argument:\n```bash\npnpm run check solutions/automations\n```\n\n### Core Concepts\n\n#### 1. Basic Types\nUse `@param` and `@return` to define function inputs and outputs.\n\n```javascript\n/**\n * Adds two numbers.\n * @param {number} a The first number.\n * @param {number} b The second number.\n * @return {number} The sum.\n */\nfunction add(a, b) {\n  return a + b;\n}\n```\n\n#### 2. Apps Script Types\nYou can reference global Apps Script types directly.\n\n```javascript\n/**\n * Gets the active sheet name.\n * @return {string} The name of the sheet.\n */\nfunction getSheetName() {\n  // Types like SpreadsheetApp, Sheet, Range are available globally\n  const sheet = SpreadsheetApp.getActiveSheet();\n  return sheet.getName();\n}\n```\n\n#### 3. Optional Parameters\nUse `[]` or `=` to denote optional parameters.\n\n```javascript\n/**\n * @param {string} name The name.\n * @param {number=} age Optional age.\n */\nfunction greet(name, age) {\n  if (age) { ... }\n}\n```\n\n### Advanced Patterns\n\n#### 1. Custom Objects (@typedef)\nFor complex objects, define a type using `@typedef`.\n\n```javascript\n/**\n * @typedef {Object} UserConfig\n * @property {string} username The user's name.\n * @property {boolean} isAdmin Whether the user is an admin.\n * @property {number} [retryCount] Optional retry attempts.\n */\n\n/**\n * Processes a user configuration.\n * @param {UserConfig} config The configuration object.\n */\nfunction processUser(config) {\n  console.log(config.username);\n}\n```\n\n#### 2. Type Casting\nSometimes the type checker cannot infer the type correctly. Use inline `@type` to cast.\n\n```javascript\nconst data = JSON.parse(jsonString);\n\n/** @type {UserConfig} */\nconst config = data;\n```\n\n#### 3. Arrays and Generics\nSpecify array contents clearly.\n\n```javascript\n/**\n * @param {string[]} names An array of strings.\n * @return {Array<number>} An array of numbers.\n */\nfunction lengths(names) {\n  return names.map(n => n.length);\n}\n```\n\n#### 4. Handling `null` and `undefined`\nBe explicit if a value can be null.\n\n```javascript\n/**\n * @param {string|null} id The ID, or null if not found.\n */\nfunction find(id) { ... }\n```\n\n### Common Issues & Fixes\n\n- **TypeScript**: DO NOT REFERENCE GoogleAppsScript in JSDocs. Instead use a locally defined type definition and link to the appropriate reference documenation page if possible.\n- **\"Property 'x' does not exist on type 'Object'\"**: This usually means you are accessing a property on a generic object. Define a `@typedef` for that object structure.\n- **Implicit 'any'**: If you see \"Parameter 'x' implicitly has an 'any' type\", it means you forgot a JSDoc `@param` tag. Add it to fix the error.\n- **Advanced Services**: To fix errors with these globals, check for existence. This helps TypeScript narrow the type and prevents runtime errors if the service is not enabled.\n\n   ```js\n   if (!AdminDirectory) {\n     console.log('AdminDirectory Advanced Service must be enabled.');\n     return;\n   }\n   ```\n\n- **Optional Properties**: Use optional chaining (`?.`) when accessing properties that might be undefined in API responses. This is often the case when when using `fields` to limit the response.\n\n   ```js\n   // Safe access\n   console.log(user.name?.fullName);\n   ```\n\n- **Error Handling**: Avoid wrapping code in `try/catch` blocks if you are only logging the error message. Let the runtime handle the error reporting for cleaner sample code.\n\n   ```js\n   // Avoid this\n   try {\n     AdminDirectory.Users.list();\n   } catch (err) {\n     console.log(err.message);\n   }\n\n   // Prefer this\n   AdminDirectory.Users.list();\n   ```","category":"root","tokens":2884}]}