diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ce9eabd --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,107 @@ +# Copilot Instructions for Dataform Reservation Package + +This document captures key learnings, debugging strategies, and architectural nuances discovered during the development of the `@masthead-data/dataform-package`. + +## How to Debug + +### 1. Tracing Compilation +Dataform executes JavaScript during the compilation phase. To trace what's happening: +- Use `console.error()` for debug logs. This ensures logs go to `stderr` and don't corrupt the JSON output redirected to a file. +- Avoid `console.log()` inside Dataform definitions if you plan to pipe the output to a JSON parser, as it may inject plain text into the JSON stream. + +### 2. Inspecting the Graph +To see the final state of all actions: +```bash +cd test-project +npx @dataform/cli compile --json > compiled.json +``` +Inspect the `tables`, `operations`, and `assertions` arrays in the resulting JSON. Check `preOps` and `queries` for the injected `SET @@reservation` statements. + +### 3. Verification Script +Use the provided verification script to check invariants: +```bash +node scripts/verify_compilation.js +``` +This script validates that reservations are prepended and that assertions are skipped. + +## Testing Configuration + +### Local Integration Testing +The `test-project` is configured to use the local version of the package. In `test-project/package.json`: +```json +"dependencies": { + "@masthead-data/dataform-package": "file:../" +} +``` +**Note:** `npm ci` or `npm install` in the `test-project` caches the local package. If you make changes to `index.js` and don't see them reflected, you may need to force an update or avoid `npm ci` during rapid iteration. + +### Running Tests + +#### Matrix Testing (Default) +Run from the root to test all supported versions: +```bash +npm test +``` +This automatically runs matrix tests across v2.4.2 and latest v3.X.X versions, managing config file conflicts. + +#### Single Version (Fast Iteration) +For rapid development on the current version: +```bash +npm run test:single +``` +This runs: +1. `jest`: Unit tests for helper functions +2. `dataform compile`: Generates the actual project graph +3. `verify_compilation.js`: In-depth JSON inspection + +#### Specific Version +Test a single Dataform version: +```bash +npm test -- 2.4.2 +``` + +**Note:** Matrix tests handle `dataform.json` (v2) vs `workflow_settings.yaml` (v3) conflicts automatically with cleanup traps. + +**CI Integration:** GitHub Actions runs matrix tests on every PR. + +## Package Architecture + +### Exported Methods +1. **`autoAssignActions(config)`** - Primary method: global monkeypatch of `publish()`, `operate()`, `assert()` and `sqlxAction()` +2. **`createReservationSetter(config)`** - Secondary method: returns a function for manual per-file application +3. **`getActionName(ctx)`** - Utility: extracts action names from Dataform contexts + +### Key Implementation Details +- **Monkeypatching Strategy:** Intercepts global methods immediately after config is loaded (use `_reservations.js` prefix to run first) +- **Config Preprocessing:** Converts `actions` arrays to Sets for O(1) lookup performance +- **Builder Modification:** Always modify `contextablePreOps`/`contextableQueries` on builders, not proto objects +- **Assertions:** Explicitly skipped to avoid SQL syntax errors in BigQuery + +## Hard-Learned Dataform Nuances + +### 1. Builder vs Proto +Dataform makes a distinction between **Action Builders** (the objects returned by `publish()`, `operate()`, etc.) and the final **Proto Objects** (the serialized state). +- **Modification Point:** To ensure persistence, modifications should be made to `action.contextablePreOps` or `action.contextableQueries` on the **Builder**. If you only modify `proto.preOps`, Dataform's internal resolution logic might overwrite your changes during the final compilation phase. + +### 2. SQLX Pre-operations +In `.sqlx` files, `pre_operations { ... }` blocks are internal to Dataform. When monkeypatching, we must ensure our reservation statement is **prepended** (using `.unshift()`) so it executes before any user-defined variables or temporary functions. + +### 3. The `queries()` method +For `operations`, the SQL is often set via `.queries(["SQL"])`. This method can be called multiple times or late in the script. We monkeypatch this method on the builder instance to wrap the user's input, ensuring the reservation is always at the top of the list, regardless of when `queries()` is called. + +### 4. Assertions +Assertions in Dataform are strict. They expect a single `SELECT` statement. Prepending a `SET` statement will cause a syntax error in BigQuery because assertions are often wrapped in subqueries or views by Dataform. We explicitly skip assertions in this package. + +## Release Process + +1. Update `CHANGELOG.md` with version and changes +2. Bump version in `package.json` and `README.md` +3. Run `npm test` to verify matrix tests pass +4. Commit and push to branch +5. Create PR, ensure CI passes +6. Merge to main +7. Tag release: `npm run release --version=x.y.z` + +## Known Limitations & Future Work + +**Performance:** `findReservation` uses linear scan (acceptable for typical project sizes <1000 actions) diff --git a/.github/linters/eslint.config.mjs b/.github/linters/eslint.config.mjs index 16e5d39..c118034 100644 --- a/.github/linters/eslint.config.mjs +++ b/.github/linters/eslint.config.mjs @@ -18,6 +18,8 @@ export default [ publish: 'readonly', constant: 'readonly', ctx: 'readonly', + operate: 'readonly', + assert: 'readonly', } }, rules: { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4014d43..6501d3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,9 @@ on: jobs: lint-and-test: runs-on: ubuntu-latest + strategy: + matrix: + dataform-version: ['2.4.2', '3.0.43'] steps: - name: Checkout code @@ -25,8 +28,8 @@ jobs: - name: Run linter run: npm run lint - - name: Run tests - run: npm test + - name: Run tests (Dataform ${{ matrix.dataform-version }}) + run: npm test -- ${{ matrix.dataform-version }} dependabot: diff --git a/.gitignore b/.gitignore index 9a5aced..c2658d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,139 +1 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.* -!.env.example - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Sveltekit cache directory -.svelte-kit/ - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# Firebase cache directory -.firebase/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v3 -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -# Vite logs files -vite.config.js.timestamp-* -vite.config.ts.timestamp-* diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..b655ca3 --- /dev/null +++ b/.npmignore @@ -0,0 +1,22 @@ +# Test files and project +test/ +test-project/ +scripts/ + +# Development files +.github/ +.eslintrc.json +jest.config.js +*.log +node_modules/ + +# Git files +.git/ +.gitignore +.gitattributes + +# CI/CD +.github/ + +# Documentation (keep README, CHANGELOG, LICENSE) +CONTRIBUTING.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a8bdd..4ba7cab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security +## [0.2.0] - 2026-01-20 + +### Added + +- **`autoAssignActions()` method** - Primary integration approach that automatically assigns actions to reservations to all Dataform actions globally without requiring manual code in each action file +- **Matrix testing infrastructure** - Automated testing across multiple Dataform versions (currently - v2.4.2 and v3.0.43) +- **API Reference section** in README with comprehensive documentation of all exported methods + ## [0.1.0] - 2025-10-27 ### Changed @@ -51,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Best practices guide - Troubleshooting section -[Unreleased]: https://github.com/masthead-data/dataform-package/compare/v0.1.0...HEAD +[Unreleased]: https://github.com/masthead-data/dataform-package/compare/v0.2.0...HEAD +[0.2.0]: https://github.com/masthead-data/dataform-package/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/masthead-data/dataform-package/compare/v0.0.1...v0.1.0 [0.0.1]: https://github.com/masthead-data/dataform-package/tree/v0.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1bfa2ee..0cada5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,17 @@ We welcome contributions to the Dataform package! This document provides guideli npm test ``` + This command runs the matrix test suite which automatically: + + 1. Iterates through all supported Dataform versions (v2 and v3). + 2. Executes unit tests and integration tests. + + For faster iteration on the currently installed version in `test-project`, you can run: + + ```bash + npm run test:single + ``` + 4. **Run linting:** ```bash diff --git a/README.md b/README.md index 53b17c7..a7c8341 100644 --- a/README.md +++ b/README.md @@ -10,42 +10,77 @@ This package is designed to optimize BigQuery resource usage by automatically as * **Cost optimization**: Automatically route high-priority workloads to reserved slots and low-priority workloads to on-demand pricing * **Resource efficiency**: Ensure critical data pipelines get guaranteed compute resources while non-critical tasks use flexible pricing -* **Automated re-assignement**: Once configured, reservations are applied automatically based on action categorization +* **Automated assignement**: Once configured, actions are automatically assigned to reservations based on action categorization * **Flexible configuration**: Easy adjustment of reservation policies through configuration updates ## Getting Started -### Initial Setup +### Installation Add the dependency to your `package.json`: ```json { "dependencies": { - "@masthead-data/dataform-package": "0.1.0" + "@masthead-data/dataform-package": "0.2.0" } } ``` -and click **Install Packages** in Dataform UI. +After adding the dependency, click **Install Packages** in Dataform UI. -Then, import the package and create a setter function in your global scope under `/includes` directory: +### Automated Assignment (Recommended) + +The easiest way to integrate this package is to use automated actions assignment. Create a configuration file (e.g., `definitions/_reservations.js`) that will assign actions to reservations as specified in your configuration: ```javascript -const reservations = require("@masthead-data/dataform-package"); +const { autoAssignActions } = require("@masthead-data/dataform-package"); + +const RESERVATION_CONFIG = [ + { + tag: 'editions', + reservation: 'projects/{project}/locations/{location}/reservations/{name}', + actions: [ + 'project.dataset.table_name', + 'project.dataset.operation_name' + ] + }, + { + tag: 'on_demand', + reservation: 'none', + actions: [ + 'project.dataset.another_table' + ] + } +]; + +autoAssignActions(RESERVATION_CONFIG); +``` + +**Note:** If you have many files in the project we recommend to start the filename with an underscore (e.g., `_reservations.js`) to ensure it runs first in the Dataform queue. + +With automated assignement, you don't need to edit your individual action files — the package handles everything globally. + +### Manual Assignment (Optional) + +For more granular control, you can manually apply reservations per file. Create a setter function in your global scope under `/includes` directory: + +```javascript +const { createReservationSetter } = require("@masthead-data/dataform-package"); const RESERVATION_CONFIG = [ ... ]; -const reservation_setter = reservations.createReservationSetter(RESERVATION_CONFIG); +const reservation_setter = createReservationSetter(RESERVATION_CONFIG); module.exports = { - ... reservation_setter } ``` +Then use `${reservation_setter(ctx)}` in each action file where you want to apply reservations (see usage examples below). + ### Configuration Structure Configuration object defining reservation policies: @@ -83,7 +118,9 @@ Configuration arguments: * `null`: Use a default reservation * **actions**: Array of Dataform action names that are assigned to the reservation -### Usage examples +### Usage Examples (Manual Assignment) + +**Note:** These examples are only needed if you're using the manual assignment approach. With automatic assignment via `autoAssignActions()`, actions are automatically assigned to reservations and you don't need to edit your action files. #### `publish` actions @@ -149,7 +186,40 @@ WHEN NOT MATCHED THEN INSERT (id, value) VALUES (S.id, S.value); `); ``` -Example implementation can be found in [https://github.com/HTTPArchive/dataform](https://github.com/HTTPArchive/dataform). +## API Reference + +### `autoAssignActions(config)` + +Automatically intercepts all `publish()`, `operate()`, and `assert()` calls and assigns actions to the appropriate reservations based on action names + +* **Parameters:** + * `config` (Array): Array of reservation configuration objects +* **Returns:** `void` +* **Usage:** Call once in a definitions file (e.g., `definitions/_reservations.js`) + +### `createReservationSetter(config)` + +Creates a reservation setter function for manual assignment per action when you need fine-grained control over which actions get reservations. + +* **Parameters:** + * `config` (Array): Array of reservation configuration objects +* **Returns:** `Function` - A setter function that accepts a Dataform context and returns the appropriate `SET @@reservation` SQL statement +* **Usage:** Create in an includes file (e.g., `/includes/reservations.js`), then call in individual action files using `${reservations.reservation_setter(ctx)}` + +### `getActionName(ctx)` + +Extracts the action name from a Dataform context object. + +* **Parameters:** + * `ctx` (Object): Dataform context object +* **Returns:** `string|null` - The action name in format `database.schema.name`, or `null` if not found + +## Compatibility + +This package is tested and compatible with: + +* **Dataform v2.4.2** +* **Dataform v3 - latest version** ## Under the Hood @@ -162,7 +232,7 @@ The package supports various Dataform contexts for action name detection: ### Reservation Lookup -Actions are matched against the `RESERVATION_CONFIG` using exact string matching. The first matching reservation is applied. If no match is found, the default reservation (first entry with `null` reservation) is used. If no default is defined, no reservation override is applied. +Actions are matched against the `RESERVATION_CONFIG` using exact string matching. The action is assigned to the first matching reservation. If no match is found, the actions is assigned to the default reservation (first entry with `null` reservation). If no default is defined, no reservation override is applied. ### SQL Generation @@ -171,3 +241,9 @@ Based on the matched reservation, the system generates appropriate SQL: * **Specific Reservation**: `SET @@reservation='projects/{project}/locations/{location}/reservations/{name}';` * **On-demand**: `SET @@reservation='none';` * **Default/Null**: Empty string (no reservation override) + +### Limitations + +**Validation:** No format validation for reservation strings - relies on BigQuery errors +**Duplicate Detection:** No check if user manually added `SET @@reservation` statements +**Schema auto-detection:** Config requires explicit `database.schema.action` format - no automatic project/dataset inference diff --git a/index.js b/index.js index 457f20a..68ed98e 100644 --- a/index.js +++ b/index.js @@ -107,7 +107,186 @@ function createReservationSetter(config) { } } +/** + * Helper to apply reservation to a single action + * @param {Object} action - Dataform action object + * @param {Array} configSets - Preprocessed configuration + */ +function applyReservationToAction(action, configSets) { + // 1. Identify where the data lives + // If no .proto, assume action itself is the data container (compiled object) + const proto = action.proto || action + + // Check if it's a valid object to modify + const hasPreOpsFn = typeof action.preOps === 'function' + const allowedTypes = ['table', 'view', 'incremental', 'materialized_view'] + const hasType = proto.type && allowedTypes.includes(proto.type) + const isOperation = !!proto.queries || !!action.contextableQueries + const isAssertion = proto.type === 'assertion' || (action.constructor && action.constructor.name === 'Assertion') + + if (isAssertion || (!hasPreOpsFn && !hasType && !isOperation)) { + return + } + + // 2. Extract Action Name + let actionName = null + if (proto.target) { + const database = proto.target.database || (global.dataform && global.dataform.projectConfig && global.dataform.projectConfig.defaultDatabase) + const schema = proto.target.schema || (global.dataform && global.dataform.projectConfig && global.dataform.projectConfig.defaultSchema) + const name = proto.target.name + actionName = database && schema ? `${database}.${schema}.${name}` : name + } + + // 3. Apply Reservation + const reservation = findReservation(actionName, configSets) + if (reservation) { + const statement = reservation === 'none' + ? 'SET @@reservation=\'none\';' + : `SET @@reservation='${reservation}';` + + // For operation builders, the queries are often set AFTER the builder is created via .queries() + // We monkeypatch the .queries() method to ensure our statement is always prepended. + if (isOperation && typeof action.queries === 'function' && !action._queriesPatched) { + const originalQueriesFn = action.queries + action.queries = function (queries) { + let queriesArray = queries + if (typeof queries === 'function') { + queriesArray = (ctx) => { + const result = queries(ctx) + if (typeof result === 'string') { + return [statement, result] + } else if (Array.isArray(result)) { + return [statement, ...result] + } + return result + } + } else if (typeof queries === 'string') { + queriesArray = [statement, queries] + } else if (Array.isArray(queries)) { + // Check if already prepended to avoid duplicates + if (!queries.includes(statement)) { + queriesArray = [statement, ...queries] + } + } + return originalQueriesFn.apply(this, [queriesArray]) + } + action._queriesPatched = true + } + + // Prefer modifying data structure directly if we know it's a safe type + // This handles both Builders (via .proto) and Compiled Objects (direct) + + // 1. Try contextablePreOps (Tables/Views Builders before resolution) + if (action.contextablePreOps) { + if (Array.isArray(action.contextablePreOps)) { + if (!action.contextablePreOps.includes(statement)) { + action.contextablePreOps.unshift(statement) + } + } else if (typeof action.contextablePreOps === 'string') { + if (!action.contextablePreOps.includes(statement)) { + action.contextablePreOps = [statement, action.contextablePreOps] + } + } + } + // 2. Try contextableQueries (Operations Builders before resolution) + else if (action.contextableQueries) { + if (Array.isArray(action.contextableQueries)) { + if (!action.contextableQueries.includes(statement)) { + action.contextableQueries.unshift(statement) + } + } else if (typeof action.contextableQueries === 'string') { + if (!action.contextableQueries.includes(statement)) { + action.contextableQueries = [statement, action.contextableQueries] + } + } + } + // 3. Try proto.preOps (Compiled Tables/Views or Resolved Builders) + else if (hasType) { + if (!proto.preOps) { + proto.preOps = [] + } + if (Array.isArray(proto.preOps)) { + if (!proto.preOps.includes(statement)) { + proto.preOps.unshift(statement) + } + } else if (typeof proto.preOps === 'string') { + if (!proto.preOps.includes(statement)) { + proto.preOps = [statement, proto.preOps] + } + } else if (hasPreOpsFn) { + action.preOps(statement) + } + } + // 4. Try proto.queries (Compiled Operations or Resolved Builders) + else if (proto.queries) { + if (Array.isArray(proto.queries)) { + if (!proto.queries.includes(statement)) { + proto.queries.unshift(statement) + } + } else if (typeof proto.queries === 'string') { + if (!proto.queries.includes(statement)) { + proto.queries = [statement, proto.queries] + } + } + } + // 5. Fallback to function API (likely Tables/Views) + else if (hasPreOpsFn) { + action.preOps(statement) + } + } +} + +/** + * Automatically applies reservation configurations to all actions in the project + * @param {Array} config - Array of reservation configuration objects + */ +function autoAssignActions(config) { + const preprocessedConfig = preprocessConfig(config) + + // 1. Process existing actions (in case this is called late) + if (global.dataform && global.dataform.actions) { + global.dataform.actions.forEach(action => { + applyReservationToAction(action, preprocessedConfig) + }) + } + + // 2. Monkeypatch global functions to intercept future actions + const globalMethods = ['publish', 'operate', 'assert'] + + globalMethods.forEach(methodName => { + if (typeof global[methodName] === 'function') { + const originalMethod = global[methodName] + global[methodName] = function (...args) { + const actionBuilder = originalMethod.apply(this, args) + + // The action should be the last one added to the session + if (global.dataform && global.dataform.actions && global.dataform.actions.length > 0) { + const lastAction = global.dataform.actions[global.dataform.actions.length - 1] + applyReservationToAction(lastAction, preprocessedConfig) + } + + return actionBuilder + } + } + }) + + // 3. Monkeypatch session-level methods for SQLX + if (global.dataform && typeof global.dataform.sqlxAction === 'function') { + const originalSqlxAction = global.dataform.sqlxAction + global.dataform.sqlxAction = function (...args) { + const result = originalSqlxAction.apply(this, args) + + if (global.dataform && global.dataform.actions && global.dataform.actions.length > 0) { + const lastAction = global.dataform.actions[global.dataform.actions.length - 1] + applyReservationToAction(lastAction, preprocessedConfig) + } + return result + } + } +} + module.exports = { createReservationSetter, - getActionName + getActionName, + autoAssignActions } diff --git a/package-lock.json b/package-lock.json index 12af46e..d0ce272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@masthead-data/dataform-package", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@masthead-data/dataform-package", - "version": "0.1.0", + "version": "0.2.0", "license": "AGPL-3.0-only", "devDependencies": { "@eslint/js": "9.39.2", @@ -49,7 +49,6 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -515,40 +514,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", @@ -1285,19 +1250,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1349,17 +1301,6 @@ "@sinonjs/commons": "^3.0.1" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1487,34 +1428,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@unrs/resolver-binding-darwin-arm64": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", @@ -1529,240 +1442,12 @@ "darwin" ] }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2023,7 +1708,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -2424,7 +2108,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4860,14 +4543,6 @@ "node": ">=8.0" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 0a9e900..51e8b89 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@masthead-data/dataform-package", - "version": "0.1.0", + "version": "0.2.0", "description": "Masthead Data package for Dataform to optimize BigQuery resource usage", "main": "index.js", "scripts": { - "test": "jest && cd test-project && npm ci && npx @dataform/cli compile --json", + "test": "./scripts/test-matrix.sh", + "test:single": "jest && cd test-project && npx @dataform/cli compile --json > compiled.json && node ../scripts/verify_compilation.js", "test:watch": "jest --watch", "lint": "eslint -c .github/linters/eslint.config.mjs .", "lint:fix": "eslint -c .github/linters/eslint.config.mjs --fix .", diff --git a/scripts/test-matrix.sh b/scripts/test-matrix.sh new file mode 100755 index 0000000..1dfbbbf --- /dev/null +++ b/scripts/test-matrix.sh @@ -0,0 +1,67 @@ +#!/bin/bash +set -e + +# Use provided arguments as versions, or default to the full matrix +if [ $# -gt 0 ]; then + VERSIONS=("$@") +else + VERSIONS=("2.4.2" "3.0.43") +fi + +# Cleanup function to restore configuration files +cleanup() { + if [ -f "test-project/dataform.json.bak" ]; then + echo "Restoring dataform.json" + mv "test-project/dataform.json.bak" "test-project/dataform.json" + fi + if [ -f "test-project/workflow_settings.yaml.bak" ]; then + echo "Restoring workflow_settings.yaml" + mv "test-project/workflow_settings.yaml.bak" "test-project/workflow_settings.yaml" + fi +} + +# Ensure cleanup runs on exit (including failures) +trap cleanup EXIT INT TERM + +echo "Running matrix tests across Dataform versions..." + +for VERSION in "${VERSIONS[@]}"; do + echo "" + echo "=========================================" + echo "Testing with Dataform v$VERSION" + echo "=========================================" + + # Configuration management based on version + if [[ $VERSION == 3* ]]; then + if [ -f "test-project/dataform.json" ]; then + echo "Hiding dataform.json for v3 compatibility" + mv test-project/dataform.json test-project/dataform.json.bak + fi + elif [[ $VERSION == 2* ]]; then + if [ -f "test-project/workflow_settings.yaml" ]; then + echo "Hiding workflow_settings.yaml for v2 compatibility" + mv test-project/workflow_settings.yaml test-project/workflow_settings.yaml.bak + fi + fi + + # Install specific version + echo "Installing Dataform @$VERSION..." + cd test-project + # Use --no-save to avoid cluttering package.json/package-lock.json during matrix tests + npm install @dataform/cli@$VERSION @dataform/core@$VERSION --no-save + cd .. + + # Run tests using the single version command + npm run test:single + + # Restore files after the run so the next version has a clean state + cleanup + + echo "✓ Dataform v$VERSION tests passed" +done + +echo "" +echo "=========================================" +echo "All matrix tests passed!" +echo "=========================================" + diff --git a/scripts/verify_compilation.js b/scripts/verify_compilation.js new file mode 100644 index 0000000..7491eb0 --- /dev/null +++ b/scripts/verify_compilation.js @@ -0,0 +1,96 @@ +const fs = require('fs') +const path = require('path') + +const COMPILED_JSON_PATH = path.join(__dirname, '../test-project/compiled.json') +const EXPECTED_RESERVATION = 'SET @@reservation=\'projects/my-test-project/locations/US/reservations/automated\';' + +function verify() { + if (!fs.existsSync(COMPILED_JSON_PATH)) { + console.error('Error: compiled.json not found. Run compilation first.') + process.exit(1) + } + + let fileContent = fs.readFileSync(COMPILED_JSON_PATH, 'utf8') + + // Dataform v2.x outputs a log line before the JSON, skip it + const lines = fileContent.split('\n') + if (lines[0].startsWith('{"level":')) { + fileContent = lines.slice(1).join('\n') + } + + const compiled = JSON.parse(fileContent) + let errors = [] + + const checkTable = (name, expectedPreOps) => { + const table = compiled.tables.find(t => t.target.name === name) + if (!table) { + errors.push(`Table ${name} not found`) + return + } + + // Check first preOp + if (!table.preOps || table.preOps[0] !== EXPECTED_RESERVATION) { + errors.push(`Table ${name} missing reservation preOp. Found: ${table.preOps ? table.preOps[0] : 'none'}`) + } + + // Check second preOp if provided + if (expectedPreOps && table.preOps[1] !== expectedPreOps) { + errors.push(`Table ${name} missing original preOp. Found: ${table.preOps[1]}`) + } + } + + const checkOperation = (name, expectedQuery) => { + const op = compiled.operations.find(o => o.target.name === name) + if (!op) { + errors.push(`Operation ${name} not found`) + return + } + + if (!op.queries || op.queries[0] !== EXPECTED_RESERVATION) { + errors.push(`Operation ${name} missing reservation query. Found: ${op.queries ? op.queries[0] : 'none'}`) + } + + if (expectedQuery && !op.queries.some(q => q.includes(expectedQuery))) { + errors.push(`Operation ${name} missing original query: ${expectedQuery}`) + } + } + + const checkAssertion = (name) => { + const assertion = compiled.assertions.find(a => a.target.name === name) + if (!assertion) { + errors.push(`Assertion ${name} not found`) + return + } + + if (assertion.query.includes('SET @@reservation')) { + errors.push(`Assertion ${name} should NOT have reservation set, but it does.`) + } + } + + console.log('--- Verifying Dataform Package Integration ---') + + // Verify automated tests (Pre-initialization case) + console.log('Checking Pre-initialization actions...') + checkTable('test_table') + checkTable('test_view') + checkTable('test_incremental', 'DECLARE test_var INT64 DEFAULT 1;') + checkOperation('test_operation', 'CREATE OR REPLACE TEMP TABLE temp_val AS SELECT 1 as val;') + checkOperation('test_single_op', 'SELECT 1 as single_val') + checkAssertion('test_assertion_skipped') + + // Verify automated tests (Post-initialization case) + console.log('Checking Post-initialization actions...') + checkTable('test_table_post') + checkOperation('test_operation_post', 'SELECT 2 as op_val') + checkAssertion('test_assertion_post_skipped') + + if (errors.length > 0) { + console.error('FAIL: Verification errors found:') + errors.forEach(err => console.error(` - ${err}`)) + process.exit(1) + } else { + console.log('SUCCESS: All integration tests passed!') + } +} + +verify() diff --git a/test-project/.gitignore b/test-project/.gitignore index c2658d7..469a213 100644 --- a/test-project/.gitignore +++ b/test-project/.gitignore @@ -1 +1,3 @@ node_modules/ +compiled.json +snowflake.log diff --git a/test-project/dataform.json b/test-project/dataform.json new file mode 100644 index 0000000..cc572c0 --- /dev/null +++ b/test-project/dataform.json @@ -0,0 +1,7 @@ +{ + "warehouse": "bigquery", + "defaultDatabase": "masthead-data", + "defaultLocation": "US", + "defaultSchema": "test", + "assertionSchema": "dataform_assertions" +} diff --git a/test-project/includes/constant.js b/test-project/definitions/_reservations.js similarity index 52% rename from test-project/includes/constant.js rename to test-project/definitions/_reservations.js index 044bbc6..bb8ccb6 100644 --- a/test-project/includes/constant.js +++ b/test-project/definitions/_reservations.js @@ -3,7 +3,7 @@ * This file defines which actions should use which BigQuery reservations */ -const { createReservationSetter } = require('@masthead-data/dataform-package') +const { autoAssignActions } = require('@masthead-data/dataform-package') const RESERVATION_CONFIG = [ { @@ -11,7 +11,8 @@ const RESERVATION_CONFIG = [ reservation: 'projects/my-test-project/locations/US/reservations/production', actions: [ 'masthead-data.test.critical_dashboard', - 'masthead-data.test.realtime_metrics' + 'masthead-data.test.realtime_metrics', + 'masthead-data.test.test' ] }, { @@ -30,6 +31,21 @@ const RESERVATION_CONFIG = [ 'masthead-data.test.monthly_aggregation' ] }, + { + tag: 'automated_tests', + reservation: 'projects/my-test-project/locations/US/reservations/automated', + actions: [ + 'masthead-data.test.test_table', + 'masthead-data.test.test_view', + 'masthead-data.test.test_incremental', + 'masthead-data.test.test_operation', + 'masthead-data.test.test_single_op', + 'masthead-data.test.test_assertion_skipped', + 'masthead-data.test.test_table_post', + 'masthead-data.test.test_operation_post', + 'masthead-data.test.test_assertion_post_skipped' + ] + }, { tag: 'default', reservation: null, @@ -37,6 +53,4 @@ const RESERVATION_CONFIG = [ } ] -const setReservation = createReservationSetter(RESERVATION_CONFIG) - -module.exports = { setReservation } +autoAssignActions(RESERVATION_CONFIG) diff --git a/test-project/definitions/a_pre_initialization_tests.js b/test-project/definitions/a_pre_initialization_tests.js new file mode 100644 index 0000000..2c79d36 --- /dev/null +++ b/test-project/definitions/a_pre_initialization_tests.js @@ -0,0 +1,33 @@ +// 1. Standard Table +publish('test_table', { + type: 'table', + description: 'Standard table for testing reservations' +}).query('SELECT 1 as val') + +// 2. View +publish('test_view', { + type: 'view', + description: 'View for testing reservations' +}).query('SELECT 1 as val') + +// 3. Incremental with existing pre_operations +publish('test_incremental', { + type: 'incremental', + description: 'Incremental table with existing pre-ops' +}).preOps('DECLARE test_var INT64 DEFAULT 1;') + .query('SELECT test_var as val') + +// 4. Operation with .queries() +operate('test_operation') + .queries([ + 'CREATE OR REPLACE TEMP TABLE temp_val AS SELECT 1 as val;', + 'SELECT * FROM temp_val;' + ]) + +// 5. Operation with single query string +operate('test_single_op') + .queries('SELECT 1 as single_val') + +// 6. Assertion (should be skipped) +assert('test_assertion_skipped') + .query('SELECT 1 as val WHERE false') diff --git a/test-project/definitions/critical_dashboard.sqlx b/test-project/definitions/critical_dashboard.sqlx index 6a38097..ba8b2da 100644 --- a/test-project/definitions/critical_dashboard.sqlx +++ b/test-project/definitions/critical_dashboard.sqlx @@ -6,7 +6,9 @@ config { } pre_operations { - ${constant.setReservation(ctx)} + CREATE TEMP FUNCTION AddFourAndDivide(x INT64, y INT64) + RETURNS FLOAT64 + AS ((x + 4) / y); } SELECT diff --git a/test-project/definitions/experimental_table.sqlx b/test-project/definitions/experimental_table.sqlx index d21c9a1..62e9256 100644 --- a/test-project/definitions/experimental_table.sqlx +++ b/test-project/definitions/experimental_table.sqlx @@ -5,10 +5,6 @@ config { tags: ["development"] } -pre_operations { - ${constant.setReservation(ctx)} -} - SELECT 'experimental' as table_type, CURRENT_DATE() as created_date, diff --git a/test-project/definitions/large_data_processing.js b/test-project/definitions/large_data_processing.js index a5007e3..a64e473 100644 --- a/test-project/definitions/large_data_processing.js +++ b/test-project/definitions/large_data_processing.js @@ -3,9 +3,7 @@ publish('large_data_processing', { schema: 'test', description: 'Large batch processing job using batch reservation', tags: ['batch_processing'] -}).preOps(ctx => ` - ${constant.setReservation(ctx)} -`).query(` +}).query(` SELECT DATE(ts) as processing_date, COUNT(*) as record_count, diff --git a/test-project/definitions/monthly_aggregation.js b/test-project/definitions/monthly_aggregation.js index f00436b..995a45f 100644 --- a/test-project/definitions/monthly_aggregation.js +++ b/test-project/definitions/monthly_aggregation.js @@ -3,9 +3,7 @@ publish('monthly_aggregation', { schema: 'test', description: 'Monthly aggregation using batch reservation', tags: ['batch_processing'] -}).preOps(ctx => ` - ${constant.setReservation(ctx)} -`).query(` +}).query(` SELECT FORMAT_DATE('%Y-%m', current_date) as month, 'aggregated_metrics' as metric_category, diff --git a/test-project/definitions/no_reservation_table.sqlx b/test-project/definitions/no_reservation_table.sqlx index 5cb4c49..eb71db7 100644 --- a/test-project/definitions/no_reservation_table.sqlx +++ b/test-project/definitions/no_reservation_table.sqlx @@ -5,10 +5,6 @@ config { tags: ["default"] } -pre_operations { - ${constant.setReservation(ctx)} -} - SELECT 'no_specific_reservation' as reservation_type, CURRENT_TIMESTAMP() as created_at, diff --git a/test-project/definitions/realtime_metrics.sqlx b/test-project/definitions/realtime_metrics.sqlx index bf9c46c..cd17d5f 100644 --- a/test-project/definitions/realtime_metrics.sqlx +++ b/test-project/definitions/realtime_metrics.sqlx @@ -5,10 +5,6 @@ config { tags: ["production_critical"] } -pre_operations { - ${constant.setReservation(ctx)} -} - SELECT CURRENT_TIMESTAMP() as event_timestamp, 'realtime_metric' as metric_type, diff --git a/test-project/definitions/test_monkeypatch.js b/test-project/definitions/test_monkeypatch.js new file mode 100644 index 0000000..23bfc48 --- /dev/null +++ b/test-project/definitions/test_monkeypatch.js @@ -0,0 +1,44 @@ +/* +DEBUG SNIPPET: Run this in your Cloud Console +Then check the "Compiled SQL" of 'monkeypatch_debug_results' +*/ + +const testTableName = 'monkeypatch_test_result' +let log = [] + +// 1. Check if global methods are even there +log.push(`publish_type: ${typeof global.publish}`) + +// 2. Check if the property is configurable (can be overwritten) +const desc = Object.getOwnPropertyDescriptor(global, 'publish') +log.push(`publish_configurable: ${desc ? desc.configurable : 'N/A'}`) +log.push(`publish_writable: ${desc ? desc.writable : 'N/A'}`) + +// 3. Attempt a simple local monkeypatch +if (typeof global.publish === 'function') { + const originalPublish = global.publish + try { + global.publish = function(name, ...args) { + + const builder = originalPublish.apply(this, [name, ...args]) + // If monkeypatching works, this table will have a pre-op + if (name === testTableName) { + builder.preOps('SELECT \'MONKEY_WAS_HERE\' as check') + } + return builder + } + log.push('monkeypatch_attempt: success_set') + } catch (e) { + log.push(`monkeypatch_attempt: failed_to_set (${e.message})`) + } +} + +// 4. Trigger the test +publish(testTableName, { type: 'table' }).query('SELECT 1 as val') + +// 5. Output results to a table so you can read them in the UI +publish('monkeypatch_debug_results', { type: 'table' }).query(` + SELECT + '${log.join(' | ')}' as environment_report, + '${typeof dataform !== 'undefined'}' as has_dataform_global +`) \ No newline at end of file diff --git a/test-project/definitions/z_post_initialization_tests.js b/test-project/definitions/z_post_initialization_tests.js new file mode 100644 index 0000000..a6a84f4 --- /dev/null +++ b/test-project/definitions/z_post_initialization_tests.js @@ -0,0 +1,16 @@ +// This file runs AFTER reservations.js alphabetically (starts with 'z') +// It tests the monkeypatching logic (intercepting new actions) + +// 1. Standard Table (Monkeypatch) +publish('test_table_post', { + type: 'table', + description: 'Table created after initialization' +}).query('SELECT 2 as val') + +// 2. Operation (Monkeypatch) +operate('test_operation_post') + .queries('SELECT 2 as op_val') + +// 3. Assertion (Monkeypatch - should still be skipped) +assert('test_assertion_post_skipped') + .query('SELECT 2 as val WHERE false') diff --git a/test-project/package-lock.json b/test-project/package-lock.json index d125723..b791980 100644 --- a/test-project/package-lock.json +++ b/test-project/package-lock.json @@ -6,14 +6,16 @@ "": { "name": "test-dataform-project", "dependencies": { - "@dataform/cli": "3.0.43", "@dataform/core": "3.0.43", "@masthead-data/dataform-package": "file:../" + }, + "devDependencies": { + "@dataform/cli": "3.0.43" } }, "..": { "name": "@masthead-data/dataform-package", - "version": "0.1.0", + "version": "0.2.0", "license": "AGPL-3.0-only", "devDependencies": { "@eslint/js": "9.39.2", @@ -556,7 +558,7 @@ } }, "../node_modules/@eslint/js": { - "version": "9.39.0", + "version": "9.39.2", "dev": true, "license": "MIT", "engines": { @@ -682,7 +684,7 @@ } }, "../node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", + "version": "3.14.2", "dev": true, "license": "MIT", "dependencies": { @@ -1800,7 +1802,7 @@ } }, "../node_modules/eslint": { - "version": "9.39.0", + "version": "9.39.2", "dev": true, "license": "MIT", "dependencies": { @@ -1810,7 +1812,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.0", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2097,6 +2099,18 @@ "dev": true, "license": "ISC" }, + "../node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "../node_modules/gensync": { "version": "1.0.0-beta.2", "dev": true, @@ -2134,8 +2148,6 @@ }, "../node_modules/glob": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -2187,7 +2199,7 @@ } }, "../node_modules/globals": { - "version": "16.4.0", + "version": "17.0.0", "dev": true, "license": "MIT", "engines": { @@ -2991,8 +3003,6 @@ }, "../node_modules/js-yaml": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4205,6 +4215,7 @@ "version": "3.0.43", "resolved": "https://registry.npmjs.org/@dataform/cli/-/cli-3.0.43.tgz", "integrity": "sha512-RWGwPsU+Rt5cXfyzKM0F18KnQIFNSx5/yv/4IRAAWXxekINFHf8rsXGgORPNprWij9pZkyyvjIyhaz6cYBDuEw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@google-cloud/bigquery": "~7.1.1", @@ -4241,8 +4252,7 @@ }, "node_modules/@google-cloud/bigquery": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-7.1.1.tgz", - "integrity": "sha512-dxyF/GuTUNJSuEwtvQO4zPjoAg7SEF5E5XWiCLGrdDaldqAUduzxCAmCyoLozPuxXHK00AsxUIlbR0+k9ZdNXA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@google-cloud/common": "^4.0.0", @@ -4263,8 +4273,7 @@ }, "node_modules/@google-cloud/common": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-4.0.3.tgz", - "integrity": "sha512-fUoMo5b8iAKbrYpneIRV3z95AlxVJPrjpevxs4SKoclngWZvTXBSGpNisF5+x5m+oNGve7jfB1e6vNBZBUs7Fw==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@google-cloud/projectify": "^3.0.0", @@ -4283,8 +4292,7 @@ }, "node_modules/@google-cloud/paginator": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-4.0.1.tgz", - "integrity": "sha512-6G1ui6bWhNyHjmbYwavdN7mpVPRBtyDg/bfqBTAlwr413On2TnFNfDxc9UhTJctkgoCDgQXEKiRPLPR9USlkbQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "arrify": "^2.0.0", @@ -4296,8 +4304,7 @@ }, "node_modules/@google-cloud/precise-date": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-3.0.1.tgz", - "integrity": "sha512-crK2rgNFfvLoSgcKJY7ZBOLW91IimVNmPfi1CL+kMTf78pTJYd29XqEVedAeBu4DwCJc0EDIp1MpctLgoPq+Uw==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -4305,8 +4312,7 @@ }, "node_modules/@google-cloud/projectify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz", - "integrity": "sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -4314,8 +4320,7 @@ }, "node_modules/@google-cloud/promisify": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.1.tgz", - "integrity": "sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12" @@ -4323,6 +4328,7 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -4342,10 +4348,12 @@ }, "node_modules/@one-ini/wasm": { "version": "0.1.1", + "dev": true, "license": "MIT" }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -4354,32 +4362,27 @@ }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.1", @@ -4388,38 +4391,32 @@ }, "node_modules/@protobufjs/float": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@tootallnate/once": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -4427,8 +4424,7 @@ }, "node_modules/@types/node": { "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -4436,6 +4432,7 @@ }, "node_modules/abbrev": { "version": "2.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -4443,8 +4440,7 @@ }, "node_modules/acorn": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4455,8 +4451,7 @@ }, "node_modules/acorn-walk": { "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -4467,8 +4462,7 @@ }, "node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -4479,6 +4473,7 @@ }, "node_modules/ansi-regex": { "version": "6.2.2", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4489,6 +4484,7 @@ }, "node_modules/ansi-styles": { "version": "6.2.3", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4499,8 +4495,7 @@ }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -4512,14 +4507,12 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/arrify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4527,8 +4520,7 @@ }, "node_modules/at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, "license": "ISC", "engines": { "node": ">= 4.0.0" @@ -4536,10 +4528,12 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", + "dev": true, "funding": [ { "type": "github", @@ -4558,8 +4552,7 @@ }, "node_modules/big.js": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", - "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -4571,8 +4564,7 @@ }, "node_modules/bignumber.js": { "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -4580,8 +4572,7 @@ }, "node_modules/binary-extensions": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4592,6 +4583,7 @@ }, "node_modules/brace-expansion": { "version": "2.0.2", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -4599,8 +4591,7 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -4611,6 +4602,7 @@ }, "node_modules/buffer": { "version": "5.7.1", + "dev": true, "funding": [ { "type": "github", @@ -4633,14 +4625,12 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4652,8 +4642,7 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4668,8 +4657,7 @@ }, "node_modules/chokidar": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -4692,6 +4680,7 @@ }, "node_modules/cliui": { "version": "7.0.4", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -4701,6 +4690,7 @@ }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4708,6 +4698,7 @@ }, "node_modules/cliui/node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -4721,10 +4712,12 @@ }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -4737,6 +4730,7 @@ }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -4747,6 +4741,7 @@ }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -4762,6 +4757,7 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4772,10 +4768,12 @@ }, "node_modules/color-name": { "version": "1.1.4", + "dev": true, "license": "MIT" }, "node_modules/commander": { "version": "10.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -4783,6 +4781,7 @@ }, "node_modules/config-chain": { "version": "1.1.13", + "dev": true, "license": "MIT", "dependencies": { "ini": "^1.3.4", @@ -4791,6 +4790,7 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4803,8 +4803,7 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4820,6 +4819,7 @@ }, "node_modules/deepmerge": { "version": "4.3.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4827,8 +4827,7 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4841,8 +4840,7 @@ }, "node_modules/duplexify": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.4.1", @@ -4853,12 +4851,12 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", + "dev": true, "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -4866,6 +4864,7 @@ }, "node_modules/editorconfig": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "@one-ini/wasm": "0.1.1", @@ -4882,6 +4881,7 @@ }, "node_modules/editorconfig/node_modules/minimatch": { "version": "9.0.1", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -4895,12 +4895,12 @@ }, "node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "license": "MIT" }, "node_modules/end-of-stream": { "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -4908,8 +4908,7 @@ }, "node_modules/ent": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", - "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -4923,8 +4922,7 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4932,8 +4930,7 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4941,8 +4938,7 @@ }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4953,6 +4949,7 @@ }, "node_modules/escalade": { "version": "3.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4960,20 +4957,17 @@ }, "node_modules/extend": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, "license": "MIT" }, "node_modules/fast-text-encoding": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.6.tgz", - "integrity": "sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==", + "dev": true, "license": "Apache-2.0" }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -4984,6 +4978,7 @@ }, "node_modules/foreground-child": { "version": "3.3.1", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -4998,8 +4993,7 @@ }, "node_modules/fs-extra": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", @@ -5013,9 +5007,7 @@ }, "node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5027,8 +5019,7 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5036,8 +5027,7 @@ }, "node_modules/gaxios": { "version": "5.1.3", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", - "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "extend": "^3.0.2", @@ -5051,8 +5041,7 @@ }, "node_modules/gcp-metadata": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", - "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", + "dev": true, "license": "Apache-2.0", "dependencies": { "gaxios": "^5.0.0", @@ -5064,6 +5053,7 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -5071,8 +5061,7 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -5095,8 +5084,7 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5108,8 +5096,7 @@ }, "node_modules/glob": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -5128,8 +5115,7 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -5140,8 +5126,7 @@ }, "node_modules/google-auth-library": { "version": "8.9.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", - "integrity": "sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==", + "dev": true, "license": "Apache-2.0", "dependencies": { "arrify": "^2.0.0", @@ -5160,9 +5145,7 @@ }, "node_modules/google-p12-pem": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-4.0.1.tgz", - "integrity": "sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==", - "deprecated": "Package is no longer maintained", + "dev": true, "license": "MIT", "dependencies": { "node-forge": "^1.3.1" @@ -5176,8 +5159,7 @@ }, "node_modules/google-sql-syntax-ts": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/google-sql-syntax-ts/-/google-sql-syntax-ts-1.0.3.tgz", - "integrity": "sha512-hkO4n1dNS/GIUpSDSBmc7DbEgzFBJWKMJTI0ArHb/qNjveOP1UfAb8wAeBt9WSWkUGlnV2S0/gh+MHx8GCBI2Q==", + "dev": true, "license": "ISC", "dependencies": { "monaco-editor": "^0.44.0" @@ -5188,8 +5170,7 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5200,14 +5181,12 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/gtoken": { "version": "6.1.2", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-6.1.2.tgz", - "integrity": "sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==", + "dev": true, "license": "MIT", "dependencies": { "gaxios": "^5.0.1", @@ -5220,8 +5199,7 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5232,8 +5210,7 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -5247,8 +5224,7 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5259,8 +5235,7 @@ }, "node_modules/http-proxy-agent": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, "license": "MIT", "dependencies": { "@tootallnate/once": "2", @@ -5273,8 +5248,7 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -5286,6 +5260,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "dev": true, "funding": [ { "type": "github", @@ -5304,18 +5279,17 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", + "dev": true, "license": "ISC" }, "node_modules/is": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.2.tgz", - "integrity": "sha512-a2xr4E3s1PjDS8ORcGgXpWx6V+liNs+O3JRD2mb9aeugD7rtkkZ0zgLdYgw0tWsKhsdiezGYptSiMlVazCBTuQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5323,8 +5297,7 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -5335,8 +5308,7 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5344,6 +5316,7 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5351,8 +5324,7 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5363,8 +5335,7 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -5372,8 +5343,7 @@ }, "node_modules/is-regex": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -5390,8 +5360,7 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5402,10 +5371,12 @@ }, "node_modules/isexe": { "version": "2.0.0", + "dev": true, "license": "ISC" }, "node_modules/jackspeak": { "version": "3.4.3", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -5419,6 +5390,7 @@ }, "node_modules/js-beautify": { "version": "1.15.4", + "dev": true, "license": "MIT", "dependencies": { "config-chain": "^1.1.13", @@ -5438,6 +5410,7 @@ }, "node_modules/js-cookie": { "version": "3.0.5", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -5445,8 +5418,7 @@ }, "node_modules/js-yaml": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5457,8 +5429,7 @@ }, "node_modules/json-bigint": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, "license": "MIT", "dependencies": { "bignumber.js": "^9.0.0" @@ -5466,8 +5437,7 @@ }, "node_modules/jsonfile": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -5478,8 +5448,7 @@ }, "node_modules/jwa": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -5489,8 +5458,7 @@ }, "node_modules/jws": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -5499,14 +5467,12 @@ }, "node_modules/long": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -5517,8 +5483,7 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5526,6 +5491,7 @@ }, "node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -5539,6 +5505,7 @@ }, "node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -5546,26 +5513,27 @@ }, "node_modules/monaco-editor": { "version": "0.44.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.44.0.tgz", - "integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==", + "dev": true, "license": "MIT" }, "node_modules/moo": { "version": "0.5.2", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/ms": { "version": "2.1.3", + "dev": true, "license": "MIT" }, "node_modules/next-tick": { "version": "1.1.0", + "dev": true, "license": "ISC" }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -5584,8 +5552,7 @@ }, "node_modules/node-forge": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" @@ -5593,6 +5560,7 @@ }, "node_modules/nopt": { "version": "7.2.1", + "dev": true, "license": "ISC", "dependencies": { "abbrev": "^2.0.0" @@ -5606,8 +5574,7 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5615,6 +5582,7 @@ }, "node_modules/object-sizeof": { "version": "1.6.3", + "dev": true, "license": "MIT", "dependencies": { "buffer": "^5.6.0" @@ -5622,8 +5590,7 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -5631,6 +5598,7 @@ }, "node_modules/p-defer": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5638,16 +5606,17 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parse-duration": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/parse-duration/-/parse-duration-1.1.2.tgz", - "integrity": "sha512-p8EIONG8L0u7f8GFgfVlL4n8rnChTt8O5FSxgxMz2tjc9FMP199wxVKVB6IbKx11uTbKHACSvaLVIKNnoeNR/A==", + "dev": true, "license": "MIT" }, "node_modules/path-key": { "version": "3.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5655,6 +5624,7 @@ }, "node_modules/path-scurry": { "version": "1.11.1", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -5669,12 +5639,12 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -5685,6 +5655,7 @@ }, "node_modules/promise-batcher": { "version": "1.1.1", + "dev": true, "license": "MIT", "dependencies": { "p-defer": "^3.0.0" @@ -5695,6 +5666,7 @@ }, "node_modules/promise-batcher/node_modules/p-defer": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5702,6 +5674,7 @@ }, "node_modules/promise-pool-executor": { "version": "1.1.1", + "dev": true, "license": "MIT", "dependencies": { "debug": "^3.1.0", @@ -5715,6 +5688,7 @@ }, "node_modules/promise-pool-executor/node_modules/debug": { "version": "3.2.7", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.1" @@ -5722,12 +5696,12 @@ }, "node_modules/proto-list": { "version": "1.2.4", + "dev": true, "license": "ISC" }, "node_modules/protobufjs": { "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -5750,14 +5724,12 @@ }, "node_modules/punycode": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, "license": "MIT" }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -5770,8 +5742,7 @@ }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -5782,6 +5753,7 @@ }, "node_modules/readline-sync": { "version": "1.4.10", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" @@ -5789,6 +5761,7 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5796,8 +5769,7 @@ }, "node_modules/retry-request": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-5.0.2.tgz", - "integrity": "sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.1.1", @@ -5809,8 +5781,7 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -5829,8 +5800,7 @@ }, "node_modules/safe-regex-test": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -5846,6 +5816,7 @@ }, "node_modules/semver": { "version": "7.7.3", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5856,6 +5827,7 @@ }, "node_modules/shebang-command": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5866,6 +5838,7 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5873,6 +5846,7 @@ }, "node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -5883,8 +5857,7 @@ }, "node_modules/stream-events": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", - "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, "license": "MIT", "dependencies": { "stubs": "^3.0.0" @@ -5892,14 +5865,12 @@ }, "node_modules/stream-shift": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true, "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -5907,6 +5878,7 @@ }, "node_modules/string-width": { "version": "5.1.2", + "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -5923,6 +5895,7 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -5935,6 +5908,7 @@ }, "node_modules/string-width-cjs/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5942,10 +5916,12 @@ }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5956,6 +5932,7 @@ }, "node_modules/strip-ansi": { "version": "7.1.2", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -5970,6 +5947,7 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5980,6 +5958,7 @@ }, "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5987,18 +5966,17 @@ }, "node_modules/stubs": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true, "license": "MIT" }, "node_modules/tarjan-graph": { "version": "2.0.0", + "dev": true, "license": "MIT" }, "node_modules/teeny-request": { "version": "8.0.3", - "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.3.tgz", - "integrity": "sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==", + "dev": true, "license": "Apache-2.0", "dependencies": { "http-proxy-agent": "^5.0.0", @@ -6013,8 +5991,7 @@ }, "node_modules/tmp": { "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, "license": "MIT", "engines": { "node": ">=14.14" @@ -6022,8 +5999,7 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -6034,14 +6010,12 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, "license": "MIT" }, "node_modules/typeid-js": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/typeid-js/-/typeid-js-0.3.0.tgz", - "integrity": "sha512-A1EmvIWG6xwYRfHuYUjPltHqteZ1EiDG+HOmbIYXeHUVztmnGrPIfU9KIK1QC30x59ko0r4JsMlwzsALCyiB3Q==", + "dev": true, "license": "Apache-2.0", "dependencies": { "uuidv7": "^0.4.4" @@ -6049,14 +6023,12 @@ }, "node_modules/undici-types": { "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, "license": "MIT" }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -6064,6 +6036,7 @@ }, "node_modules/untildify": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6071,14 +6044,12 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, "license": "MIT" }, "node_modules/uuid": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -6090,8 +6061,7 @@ }, "node_modules/uuidv7": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-0.4.4.tgz", - "integrity": "sha512-jjRGChg03uGp9f6wQYSO8qXkweJwRbA5WRuEQE8xLIiehIzIIi23qZSzsyvZPCPoFqkeLtZuz7Plt1LGukAInA==", + "dev": true, "license": "Apache-2.0", "bin": { "uuidv7": "cli.js" @@ -6099,9 +6069,7 @@ }, "node_modules/vm2": { "version": "3.9.19", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", - "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", - "deprecated": "The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm.", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.7.0", @@ -6116,14 +6084,12 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -6132,6 +6098,7 @@ }, "node_modules/which": { "version": "2.0.2", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6145,6 +6112,7 @@ }, "node_modules/wrap-ansi": { "version": "8.1.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -6161,6 +6129,7 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -6176,6 +6145,7 @@ }, "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6183,6 +6153,7 @@ }, "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -6196,10 +6167,12 @@ }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6212,6 +6185,7 @@ }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6222,12 +6196,12 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/y18n": { "version": "5.0.8", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -6235,12 +6209,12 @@ }, "node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC" }, "node_modules/yargs": { "version": "16.2.0", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^7.0.2", @@ -6257,6 +6231,7 @@ }, "node_modules/yargs-parser": { "version": "20.2.9", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -6264,6 +6239,7 @@ }, "node_modules/yargs/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6271,10 +6247,12 @@ }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6287,6 +6265,7 @@ }, "node_modules/yargs/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" diff --git a/test-project/package.json b/test-project/package.json index 6b0ecb1..19b819a 100644 --- a/test-project/package.json +++ b/test-project/package.json @@ -2,8 +2,10 @@ "name": "test-dataform-project", "description": "Test Dataform project for dataform-package", "dependencies": { - "@dataform/cli": "3.0.43", "@dataform/core": "3.0.43", "@masthead-data/dataform-package": "file:../" + }, + "devDependencies": { + "@dataform/cli": "3.0.43" } } diff --git a/test/index.test.js b/test/index.test.js index ca45516..1b97024 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,6 +1,7 @@ const { createReservationSetter, - getActionName + getActionName, + autoAssignActions } = require('../index') // Example configuration for testing (previously hardcoded in the package) @@ -232,4 +233,260 @@ describe('Dataform package', () => { }) }) }) + + describe('autoAssignActions', () => { + let originalPublish + let originalOperate + let originalAssert + let originalDataform + + beforeEach(() => { + // Save original global state + originalPublish = global.publish + originalOperate = global.operate + originalAssert = global.assert + originalDataform = global.dataform + + // Reset global state + global.dataform = { + actions: [], + projectConfig: { + defaultDatabase: 'test-project', + defaultSchema: 'test-schema', + defaultLocation: 'US' + } + } + + // Mock global functions + global.publish = jest.fn((name, config) => { + const action = { + proto: { + type: config?.type || 'table', + target: { + name, + database: 'test-project', + schema: config?.schema || 'test-schema' + } + }, + preOps: jest.fn(), + contextablePreOps: [] + } + global.dataform.actions.push(action) + return action + }) + + global.operate = jest.fn((name, config) => { + const action = { + proto: { + queries: [], + target: { + name, + database: 'test-project', + schema: config?.schema || 'test-schema' + } + }, + queries: jest.fn(function (q) { + this.proto.queries = Array.isArray(q) ? q : [q] + return this + }), + contextableQueries: [] + } + global.dataform.actions.push(action) + return action + }) + + global.assert = jest.fn((name) => { + const action = { + proto: { + type: 'assertion', + target: { + name, + database: 'test-project', + schema: 'test-schema' + } + } + } + global.dataform.actions.push(action) + return action + }) + }) + + afterEach(() => { + // Restore original global state + global.publish = originalPublish + global.operate = originalOperate + global.assert = originalAssert + global.dataform = originalDataform + }) + + test('should apply reservations to existing publish actions', () => { + // Create actions before calling autoAssignActions + global.publish('test_table', { type: 'table' }) + + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.test_table'] + } + ] + + autoAssignActions(config) + + const action = global.dataform.actions[0] + expect(action.contextablePreOps).toContain('SET @@reservation=\'projects/test/locations/US/reservations/prod\';') + }) + + test('should intercept new publish actions after initialization', () => { + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.new_table'] + } + ] + + autoAssignActions(config) + + // Create action AFTER autoAssignActions + global.publish('new_table', { type: 'table' }) + + const action = global.dataform.actions[0] + expect(action.contextablePreOps).toContain('SET @@reservation=\'projects/test/locations/US/reservations/prod\';') + }) + + test('should apply reservations to operations', () => { + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.test_operation'] + } + ] + + autoAssignActions(config) + global.operate('test_operation').queries('SELECT 1') + + const action = global.dataform.actions[0] + expect(action.proto.queries[0]).toBe('SET @@reservation=\'projects/test/locations/US/reservations/prod\';') + }) + + test('should apply "none" reservation for on-demand pricing', () => { + const config = [ + { + tag: 'on-demand', + reservation: 'none', + actions: ['test-project.test-schema.ondemand_table'] + } + ] + + autoAssignActions(config) + global.publish('ondemand_table', { type: 'table' }) + + const action = global.dataform.actions[0] + expect(action.contextablePreOps).toContain('SET @@reservation=\'none\';') + }) + + test('should skip assertions', () => { + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.test_assertion'] + } + ] + + autoAssignActions(config) + global.assert('test_assertion') + + const action = global.dataform.actions[0] + expect(action.contextablePreOps).toBeUndefined() + expect(action.proto.preOps).toBeUndefined() + }) + + test('should not apply reservation to unmatched actions', () => { + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.matched_table'] + } + ] + + autoAssignActions(config) + global.publish('unmatched_table', { type: 'table' }) + + const action = global.dataform.actions[0] + expect(action.contextablePreOps).toHaveLength(0) + }) + + test('should handle multiple actions with different reservations', () => { + const config = [ + { + tag: 'prod', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.prod_table'] + }, + { + tag: 'dev', + reservation: 'none', + actions: ['test-project.test-schema.dev_table'] + } + ] + + autoAssignActions(config) + global.publish('prod_table', { type: 'table' }) + global.publish('dev_table', { type: 'table' }) + + expect(global.dataform.actions[0].contextablePreOps).toContain('SET @@reservation=\'projects/test/locations/US/reservations/prod\';') + expect(global.dataform.actions[1].contextablePreOps).toContain('SET @@reservation=\'none\';') + }) + + test('should throw error with invalid config', () => { + expect(() => autoAssignActions()).toThrow('Configuration must be a non-empty array') + expect(() => autoAssignActions(null)).toThrow('Configuration must be a non-empty array') + expect(() => autoAssignActions([])).toThrow('Configuration array cannot be empty') + }) + + test('should prepend reservation before existing preOps', () => { + global.publish('test_table', { type: 'table' }) + const action = global.dataform.actions[0] + action.contextablePreOps = ['DECLARE x INT64 DEFAULT 1;'] + + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.test_table'] + } + ] + + autoAssignActions(config) + + expect(action.contextablePreOps[0]).toBe('SET @@reservation=\'projects/test/locations/US/reservations/prod\';') + expect(action.contextablePreOps[1]).toBe('DECLARE x INT64 DEFAULT 1;') + }) + + test('should not duplicate reservation if already applied', () => { + const config = [ + { + tag: 'test', + reservation: 'projects/test/locations/US/reservations/prod', + actions: ['test-project.test-schema.test_table'] + } + ] + + autoAssignActions(config) + global.publish('test_table', { type: 'table' }) + + // Apply again (simulating multiple calls) + autoAssignActions(config) + + const action = global.dataform.actions[0] + const reservationCount = action.contextablePreOps.filter(op => + op.includes('SET @@reservation') + ).length + expect(reservationCount).toBe(1) + }) + }) })