Compare commits

...

210 Commits

Author SHA1 Message Date
dan sutton
4e70c979ce handle fields from "select * from orders" type queries
```clojure
util=> (sql-parsing/field-references nil "select * from fields")
{:used-fields #{},
 :returned-fields [{:type :all-columns, :table {:table "fields"}}],
 :errors #{}}
```

when this went through our old field prediction work we ended up with

```
 [{:name nil
  :display-name nil
  :base-type :type/*
  :effective-type :type/*
  :semantic-type nil
  :position 0}]
```

Which then failed to insert into the database
2026-04-09 17:07:13 -05:00
dan sutton
09fee616b7 cleanup module config
this causes tests to fail locally:

```clojure
modules-test=> (clojure.test/run-tests 'metabase.core.modules-test)

Testing metabase.core.modules-test
FAIL in (model-boundaries-test) (modules_test.clj:200)
Model boundary enforcement
 No model boundary violations
enterprise/backend/src/metabase_enterprise/replacement/swap/viz.clj (module enterprise/replacement) references :model/DashboardCardSeries (defined in dashboards) — not-imported violation
expected: (nil? violation-type)
  actual: (not (nil? :not-imported))

FAIL in (modules-config-up-to-date-test) (modules_test.clj:169)
Please update .clj-kondo/config/modules/config.edn 🥰
[Pro Tip: use (dev.deps-graph/print-kondo-config-diff) to see the changes you need to make in a nicer format]
 Add #{metabase.lib-be.source-swap} to [lib-be :api]
used by {metabase.lib-be.source-swap #{metabase-enterprise.replacement.swap.viz}}
expected: (empty? missing)
  actual: (not (empty? #{metabase.lib-be.source-swap}))

Ran 10 tests containing 3665 assertions.
2 failures, 0 errors.
{:test 10, :pass 3663, :fail 2, :error 0, :type :summary}
```

but without these changes they fail in CI:

```clojure
FAIL in metabase.core.modules-test/modules-config-up-to-date-test (modules_test.clj:171)
Please update .clj-kondo/config/modules/config.edn 🥰
[Pro Tip: use (dev.deps-graph/print-kondo-config-diff) to see the changes you need to make in a nicer format]
 Remove #{metabase.lib-be.source-swap} from [lib-be :api]
expected: (empty? extraneous)
  actual: (not (empty? #{metabase.lib-be.source-swap}))

FAIL in metabase.core.modules-test/model-config-not-stale-test (modules_test.clj:230)
Model exports and imports should not list models that are unused.

'enterprise/replacement' imports models that aren't used — remove them from :model-imports.
expected: (empty? (sort stale))
  actual: (not (empty? (:model/DashboardCardSeries)))
```

I don't care. i just want them to stop fighting
2026-04-09 16:20:09 -05:00
dpsutton
7f5d65cd08 Merge branch 'master' into transform-importing-creates-fields 2026-04-09 15:48:20 -05:00
Edward Paget
3392486525 Enforce session timeout server-side with last_active_at tracking (#71866)
* Enforce session timeout server-side with last_active_at tracking

Add a last_active_at column to core_session and use it to enforce
session inactivity timeouts on the server side (behind the
session-timeout-config premium feature). Activity updates are throttled
via an atomic compare-and-set cache in session.core to avoid a DB write
on every request. The session cleanup task prunes idle sessions and
expired cache entries.

* Move EE-dependent session timeout tests to enterprise test file

Session timeout tests that require the :session-timeout-config premium
feature fail in OSS mode because enable-session-timeout-config? checks
config/ee-available? which is always false without enterprise code.

* Remove trailing blank lines from test files
2026-04-09 15:48:18 -05:00
Edward Paget
71e5ad58dc Fix nested WHERE clause in remove-unsynced! for TransformTag (#72286)
Fix nested WHERE clause in remove-unsynced! for TransformTag (UXW-3710)

Mixing a {:where ...} HoneySQL map with kv-pair conditions in t2/delete!
caused toucan2 to misinterpret the map as a condition key, producing invalid
SQL with a WHERE inside a WHERE. Convert other-conditions into the {:where}
clause directly and remove unreachable scoped-path code that had the same bug.
2026-04-09 15:47:40 -05:00
Alex Yarosh
a96f3bc9d5 docs: Update links in CONTRIBUTING.md to latest version (#72300)
Update links in CONTRIBUTING.md to latest version
2026-04-09 16:35:49 -04:00
Alexander Polyankin
46e35e97ba Fix serialization export perf with no content models (#72290) 2026-04-09 12:26:36 -07:00
Cam Saul
629c10e733 Replace 'MLv2' with 'Lib' or 'MBQL 5' in docstrings and function names (#72121)
Replace usages of 'MLv2' with 'Lib' or 'MBQL 5'
2026-04-09 12:26:17 -07:00
metamben
5d0d3c47c0 Enable commented out drill-thru tests (#71845)
Closes QUE2-140
2026-04-09 21:54:05 +03:00
metamben
5f5d472fe7 Use locale independent formatting to get the day of the week (#71620)
* Use locale independent formatting to get the day of the week

Closes #57794

* Use MBQL 5 in the test
2026-04-09 21:53:49 +03:00
Aleksandr Lesnenko
afe5a73498 Increase stacked series gap, make it dynamic (#72118)
* increase stacked series gap, make it dynamic

* Update Loki Snapshots

* review

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-09 13:59:32 -04:00
github-automation-metabase
b9e2e96185 Update translations 2026-04-07 (#72070)
* Update translation sources 2026-04-07

* Update translations

---------

Co-authored-by: github-actions <github-actions@github.com>
2026-04-09 17:47:56 +00:00
Timothy S. Dean
8d5bd8ebcb Don't trigger result-metadata write based on non-default parameters (#72007)
If you have a parameterized card with computed columns, there is no Field from which to pull fingerprints, and the query-processor runs a fingerprint process based on the results of the query. Because the query is parameterized, the result is specific to whatever random parameter was used. `record-metadata!` saw this difference and updated the result-metadata on the card.

The fix: we will no longer persist `result_metadata` when non-default parameterized values are present.
2026-04-09 11:41:51 -06:00
Kevin Kelleher
2e71a55212 Fix colors in metrics viewer (#71874) 2026-04-09 17:19:16 +00:00
Edward Paget
fccf343325 Fix dashboard PUT response (#72173)
Extract shared `apply-card-permission-filters` helper used by both GET and
PUT paths to ensure consistent card visibility filtering. Also fix
`set-download-perms-on-dashcards` to handle empty dataset_query maps
gracefully.

Fixes: UXW-3571
2026-04-09 11:11:18 -05:00
Nicolò Pretto
2a8821b6a7 Address lack of continuity after admin completes database connection and x-ray dashboard steps (#72072)
* wip modal

* change button text

* polish

* update copy
2026-04-09 18:04:36 +02:00
dan sutton
498c3cb891 Seed metabase_field rows for transform target tables during import
When a transform is imported via serdes, the before-insert hook creates
a provisional metabase_table row but no field rows. Cards referencing
transform output fields fail import because portable references like
`[db schema table field]` can't resolve to integer IDs.

Add field prediction and seeding so that importing a transform also
populates its target table's fields:

- predict-target-fields: pure function that predicts output columns.
  MBQL uses lib/returned-columns; native SQL uses sql-parsing/
  field-references with type resolution via the metadata provider.
  Python transforms return nil (output is runtime-determined).

- sync-target-fields!: reconciles metabase_field rows against predicted
  fields — creates missing, retires stale, leaves matching untouched.

- seed-target-fields!: convenience wrapper chaining predict → sync,
  called from Transform's before-insert hook.

The prediction path is entirely metadata-provider-based with no app-DB
queries, making it reusable for offline validation (checker on a
workspace branch backed by YAML metadata).
2026-04-09 10:38:28 -05:00
John Swanson
6c65769bc5 fix n+1s in db, table, group creation (#72087)
* fix n+1s in db, table, group creation

this batches permissions changes to fix cluster lock timeouts with large numbers of groups, database, and tables.

When we created a new group, table, or database, we obtained O(NxM) fine-grained cluster locks, and did NxM separate inserts into data permissions.

This does batch operations instead. Obtains a single cluster-wide shared lock for batch operations (which actually might not be necessary at all for database and group creation - think it's still needed for creating a table though), then does the insert in batches.

Reduces the total number of queries dramatically:

- group creation: scaling with NxM -> ~10

- db creation: scaling with NxM -> ~10

- table creation: scaling with NxM -> ~8

* Fixup tests, improve perf more

* comment cleanup

* fix test

* remove dead code + unnecessary api epxansion

* Fix cluster lock contention

This is a fairly big change on its own, but it's fairly straightforward
and an additional performance improvement that should help with
concurrent processes trying to modify permissions at the same time.

Essentially, I realized that currently our cluster lock system provides
exactly one type of lock, an exclusive lock. But that's not the only
type of potential lock! Both Postgres and MariaDB/MySQL support locking
a row as *shared*. This conflicts with exclusive locks (`FOR UPDATE`)
but does not conflict with other shared locks.

This way, we can design a two-level lock system in which:

- most operations require:
  - a *shared* lock on `::batch-permissions-update`, and
  - an *exclusive* lock on `::batch-permissions-update-<db>`

- some operations require only an *exclusive* lock on
  `::batch-permissions-update`.

As one example, modifying permissions with the graph API requires an
exclusive lock on `::batch-permissions-update`. We don't want sync
adding a table at the same time that someone is modifying permissions,
e.g. moving from granular -> coalesced or vice versa.

On the other hand, sync itself just requires a *shared* lock on
`::batch-permissions-update`, because it can happily coexist with other
syncs that are for different databases. But they'll all take exclusive
locks on `::batch-permissions-update-<db>`, so any permissions changes
they're making to the database will be serialized.
2026-04-09 08:37:14 -07:00
Kamil Mielnik
42c4a2f3e9 Remove accidental semicolon in JSX (#72242) 2026-04-09 15:18:59 +00:00
Chris Truter
58916bb3ce Fix module update method to insert missing :model-X entries (#72247) 2026-04-09 17:10:20 +02:00
lbrdnk
50e9fe7a7a Adjust format-native-query to accept legacy mbql (#72217)
* Adjust format-native-query to accept legacy mbql

* Add test

* Restore permissive extraction form

* assertion

* Docstring
2026-04-09 16:53:58 +02:00
Ngoc Khuat
d6ccee4d52 GDGT-2143 Use subscription creator's perms for attachment check (#72156) 2026-04-09 14:25:36 +00:00
Aleksandr Lesnenko
d3ba36d66d Change config of js execution context for static viz (#72196)
* static viz bundle should be sandboxed

* update color selector

* split execution contexts
2026-04-09 14:05:01 +00:00
Maksym Yakubych
3b995f986f fix: sql edit tools creating new questions instead of editing (#70255) 2026-04-09 14:03:44 +00:00
Nathan Voxland
6a7e27195f Fix: allow moving dashboard questions to their parent collection (#71550)
Allow moving dashboard questions to their parent dashboard's collection

The MoveModal's isSameDestination() was comparing the question's
collection_id to the target collection, blocking the move. But for
dashboard questions, the real "location" is the dashboard — moving to the
collection is a valid operation that detaches the question from the
dashboard. Added \!movingItem.dashboard_id to the collection check.
2026-04-09 19:26:47 +05:30
Nathan Voxland
dc38720b20 Add keyboard shortcut (g s) to navigate to Data Studio (#71328)
* Add keyboard shortcut (g s) to navigate to Data Studio

Closes UXW-3155

* adding permissions check, e2e test coverage

* e2e adjustment

---------

Co-authored-by: Nick Fitzpatrick <nickfitz.582@gmail.com>
2026-04-09 19:25:03 +05:30
Timofey Kachalov
6078e48144 feat(sdk,embed-js): Improve EntityPicker mobile UX (#71815) 2026-04-09 17:54:23 +04:00
Denis Berezin
605968c6e9 Fix styling for chart png download in Firefox (#72198) 2026-04-09 16:24:59 +03:00
Uladzimir Havenchyk
77fca900be deps: upgrade yaml (#71988)
chore: upgrade yaml
2026-04-09 14:57:30 +03:00
Nick Fitzpatrick
f31336e113 Uxw 3547 add analytics events for metrics viewer and metrics homepage (#72091)
* Adding events

* tests

* remove page view events

* re-adding comment since it's relevant again
2026-04-09 16:40:29 +05:30
Phoomparin Mano
75582f7983 fix(sdk-bundle): only render SDK components when EE plugins are loaded (#70737)
* only render children if plugin is ready

* fix: wait for ee plugins to initialize before rendering children

* add unit tests for race condition

* fix: avoid module cache and always re-dispatch plugin ready

* fix: initialize EE plugins synchronously during render instead of useEffect

Moves plugin initialization out of useEffect into the render body,
eliminating the extra frame delay. Uses reduxStore.dispatch directly
following the same pattern as use-init-data-internal.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:23:37 +07:00
Chris Truter
e54b1d968c Delete agent APIs for Workspaces (#71827) 2026-04-09 08:02:24 +00:00
Chris Truter
398840c0a1 OAuth token cleanup task (#72081) 2026-04-09 09:33:58 +02:00
Chris Truter
70552d31d8 Add OAuth and MCP throttling (#71884) 2026-04-09 09:33:45 +02:00
Timofey Kachalov
a2d99db064 Mock i18n FE translations to use EN texts (#72044) 2026-04-09 10:58:24 +04:00
da
f09bc46311 Fixes the find and replace menu from dissapearing (#71780)
Fixes the find and remove menu from dissapearing
2026-04-08 21:36:36 -03:00
appleby
80af5a2b55 BOT-1282 Report combined in + out tokens in :metabot-usage metering (#72186)
* BOT-1282 Report combined in + out tokens in :metabot-usage metering

* Add some additional tests for :metabot-usage reporting
2026-04-08 22:21:56 +00:00
github-automation-metabase
7382721f95 Auto-update documentation for master (#72195)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-08 17:35:05 -04:00
Maksym Yakubych
ee7663ccd1 feat: metabase as an AI provider (#71655) 2026-04-08 20:39:30 +00:00
Uladzimir Havenchyk
a5cf9c43c5 bundle: exclude lazy loaded libs from vendor bundle again (#72061) 2026-04-08 23:02:42 +03:00
bryan
db672b4043 Copy model metadata overrides to Fields during models-to-transforms (#72129) 2026-04-08 13:51:19 -06:00
Aleksandr Lesnenko
bc41b0d2c3 remove data studio link from the metrics viewer (#72122)
* remove data studio link from the metrics viewer

* update spec
2026-04-08 19:44:31 +00:00
Edward Paget
910a4b3e09 Require authentication for eid-translation API endpoints (#72174)
The /api/eid-translation routes were unauthenticated despite only being
used by frontend routes behind the IsAuthenticated guard. Wrap with +auth
and remove the stale "public facing" comment.
2026-04-08 13:41:01 -05:00
John Swanson
a224d29129 Don't allow pausing JWT if it's on via env var (#69386)
* Don't allow pausing JWT if it's on via env var

This separate the `jwt-enabled` setting into two parts:

- `jwt-enabled`, which is settable via an env var, and means "do I want
JWT to run or not"?

- `jwt-enabled-and-configured`, which is not settable at all, and simply
computes `(and (jwt-enabled) (jwt-configured))`. This one means "JWT is
running".

This way, if you set MB_JWT_ENABLED via the env, we don't give you the
option to pause or disable JWT anymore. If it's configured, it's on.

In some ways `MB_JWT_ENABLED` feels useless here, when set alone - it's
not really doing anything other than blocking the user from pausing JWT.
But I think in context it makes sense: you want to be able to configure
JWT solely via the env, but if you do that partially, it makes sense
that we just don't let you change the env-configured bits.

* kondo

* fix
2026-04-08 11:37:17 -07:00
Jeff Bruemmer
2867945fd4 docs - fix anchor links (#72176)
fix anchor links
2026-04-08 13:23:26 -04:00
technomancy
657669dcc5 Implement table privileges support for SQLite. (#71950)
* Implement table privileges support for SQLite.

This is based on code from https://github.com/metabase/metabase/pull/57000
but breaks it down into more manageable chunks.

* Set SQLite insert/update/delete privileges to true.

Unclear why the original PR hard-coded these to false; SQLite has no
permissions system which could prevent writes.

Getting this wrong doesn't affect anything in the backend; it's not
used anywhere outside the frontend, so I suppose it's somewhat
unrealistic to expect it to cause a test failure.
2026-04-08 10:22:03 -07:00
John Swanson
177a6515b7 Query parameter cleanup (#72048)
* Normalize query parameters in preprocessing

* Filter metadata results by user permissions

* Simplify permission checking logic

* Use server-controlled query context

* Validate query parameters at API boundaries

* Code review improvements

* Fixed failing test

* Fixed failing test

* Revert SQL driver to read persisted-info from query object

Instead of having the SQL driver load persisted-info from the metadata
provider, retain the old behavior where it reads :persisted-info/native
from the query map (set by fetch-source-query middleware).

---------

Co-authored-by: Edward Paget <ed.paget@gmail.com>
Co-authored-by: Nathan Voxland <nathan@metabase.com>
2026-04-08 12:04:28 -05:00
Alexander Polyankin
25969cb31e Fix source replacement with null effective_type (#72124) 2026-04-08 16:29:14 +00:00
Ben Grabow
6d45a39d94 Ghy-3343 Re-export instance analytics content (#72063)
* Re-export instance analytics in new serdes format
2026-04-08 11:00:42 -05:00
Alex Yarosh
97f95e40cd docs: more typos (#72168)
more typos
2026-04-08 11:49:12 -04:00
Nick Fitzpatrick
53a13043ae Hide Regenerate Key button if form field is readonly (#72105)
* Hide Regenerate Key button if form field is readonly

* adjust unit test
2026-04-08 20:59:07 +05:30
Edward Paget
241181c7f8 Add README for built-in MCP server (#71834)
Documents the MCP server capabilities, endpoint, client setup
(Claude Code, Claude Desktop, Cursor), OAuth authentication,
scopes, available tools, JSON-RPC methods, and architecture.
2026-04-08 10:11:22 -05:00
John Swanson
5dde87ebf7 Fix search filters for published tables (#71323)
When:

- a table was published,
- a user had data permissions for that table, and
- the user did NOT have permissions on the collection the table was
  published to

Then the search endpoint would not return that table, even though it's
queryable and viewable via data permissions.

This fixes the filtering, so that the table is discoverable via search.
2026-04-08 08:04:15 -07:00
Anderson
31d3ebe6fb [AI Controls] Add tooltip explaining most-permissive group access (#72132)
Add tooltip explaining most-permissive group access
2026-04-08 11:59:18 -03:00
Ngoc Khuat
9d50b86f16 Fix validation in public dashboard action endpoint (#72088) 2026-04-08 13:21:47 +00:00
YB
fdd2d558ba GDGT-26 Fix discard-setting-changes helper for settings that use init (#72069)
The issue reported that discard-setting-changes is broken for settings that use :init (e.g. query-analysis-native-disabled). The report predicted that calling the setting getter inside the macro would return nil, and that the value would be corrupted afterward.

do-with-discarded-setting-changes! used setting/read-setting to capture the "original" value before the thunk. read-setting binds *disable-init* to true, so for settings whose value comes from an :init function (and which haven't been initialized yet), it returns nil. This nil is then passed as both:

(outer capture) The "temporary" value to set during the thunk (via do-with-temporary-setting-value!)
(inner capture) The value to restore in the finally block (via the :skip-init? flag)
The key fix here is to use setting/get instead of setting/read-setting. We also remove the :skip-init? flag since it was there to match the semantics of read-setting and suppress the init call.
2026-04-08 17:23:40 +08:00
Romeo Van Snick
fc07abb93c Fix race condition in test and reset mocks (#72150) 2026-04-08 11:23:27 +02:00
Chris Truter
8a869177f4 Standardize where models live (#72096) 2026-04-08 08:06:38 +00:00
Romeo Van Snick
ed21122ef5 Content translations for parameters values without custom labels (#72025)
* Translate parameter values too

* Allow translating arrays of values

* Add e2e test for translations without label

* Fix types
2026-04-08 07:36:55 +02:00
Anderson
c989e01952 Metabot AI Usage Limits page (#71699)
* Implement static UI for Usage controls page

* Added initial ai-controls tables

* Set sidenav menu items / Add placeholders for new pages

* Implement Metabot Customization page UI

* - Added metabot/permissions GET and PUT apis
- Added settings for metabot name/tone/icon

* Fixed migration ids

* fixup! bad merge

* Add :scope metadata to all Agent Loop tools and create metabase.metabot.scope namespace

- Create metabase.metabot.scope with *current-user-scope* (defaults to #{}),
  scope-matches? with hierarchical wildcard support, and unrestricted set
- Add :scope to all 38 Agent Loop tool-fns with specific action scopes
  (e.g. "agent:sql:create", "agent:viz:edit") — wildcards are grant-side only
- Propagate :scope through wrap-tools-with-state in tools.clj
- :capabilities remain unchanged (orthogonal data-permission gating)
- Add tests for scope-matches?, default scope, and stub

Done: tool-scope/phase-1-tool-scope-declarations

* Add scope-based authorization middleware for Agent Loop tools

- Filter tools by scope in get-tools-for-profile (profiles.clj)
- Check scope before tool invocation in wrap-tools-with-state (tools.clj),
  returning a generic denial message (scope details logged, not leaked)
- Bind *current-user-scope* to unrestricted in run-agent-loop (temporary
  until phase 4 wires real permission resolution)
- Add scope enforcement tests covering filtering, denial, wildcards,
  no-scope passthrough, and default empty scope behavior

Done: tool-scope/phase-2-authorization-middleware

* Gate profile system prompts by metabot permission types

- Add *current-user-metabot-permissions* dynamic var to scope.clj
- Bind all-yes permissions in run-agent-loop (temporary, phase 4 wires real resolution)
- Derive has_sql_generation, has_nql, has_query_tools, has_other_tools
  boolean flags from permissions and pass to Selmer template context
- Gate internal.selmer sections: critical constraints, verification protocol,
  query building steps, SQL construction, SQL anti-patterns, SQL examples,
  NLQ guidance, dashboard routing, and query-focused common scenarios
- General sections (identity, context grounding, communication style, search
  example, entity linking) remain ungated for all permission levels
- 7 tests, 43 assertions covering all permission combinations

Done: tool-scope/phase-3-profile-prompt-gating

* Wire user-metabot-perms->scopes to metabot_permissions data model

- Implement resolve-user-permissions: looks up user's groups, queries
  MetabotPermissions, resolves most-permissive value across groups
- Implement real user-metabot-perms->scopes: maps :yes permissions to
  wildcard scope sets, always includes always-granted-scopes
- Replace temporary unrestricted/all-yes bindings in run-agent-loop with
  real permission resolution from api/*current-user-id*
- Superusers get scope/unrestricted and all-yes-permissions
- Permission resolution done once at reify construction, not per-reduce
- Agent API scope binding deferred (tracked in rdm task agent-api-scope-binding)
- 38 new test assertions covering permission→scope mapping, single-group,
  multi-group most-permissive-wins, and default scenarios

Done: tool-scope/phase-4-permissions-to-scopes

* Update permissions expected request format

* Include metabot and semantic-search tool options on the backend

* Connect access controls UI to the backend

* Wire up the backend for the Metabot Customize page

* Polish access controls page / Create MetabotSystemPromptsPage

* Added metabot-system-prompt

* Connect System prompt page to the backend

* Remove model selection + clean up

* Use custom metabot name

* Use metabot-icon in the UI + added metabot-show-illustrations toggle

* Removed metabase custom prompt

* Add defscope macro, scope registry, and api-scope module

Introduce a `defscope` macro that registers scopes with translatable
descriptions in a global registry, replacing inline scope strings across
all tool and endpoint metadata with typed var references.

Key changes:
- New `metabase.api-scope.core` module with defscope, scope registry,
  scope-matches?, and public API (all-scopes, scope-description)
- All 25 agent scopes declared via defscope in metabase.metabot.scope
- 3 EE workspace scopes declared in metabase-enterprise.workspaces.scope
- validate-tool-var! rejects tools with unregistered scopes at startup
- defendpoint accepts var references for :scope metadata (not just strings)
- enforce-scope logs dev-time warnings for unregistered endpoint scopes
- MCP scope enforcement now handled by defendpoint middleware (removed
  redundant pre-dispatch check); enforce-authentication preserves existing
  token-scopes on synthetic requests
- Scope enforcement error messages no longer leak required scope names
- Consolidated duplicate scope-matching logic in mcp/tools.clj

Done: tool-scope/phase-5-defscope-registry

* Make semantic_queries optional in search tool schemas

When semantic search is not enabled, the feature gate strips
semantic_queries from the tool definition sent to the LLM, but Malli
validation still required it — causing an "Invalid input" error.

* Add missing agent:query scope declaration

The agent API query endpoint requires "agent:query" but it was never
registered via defscope, only its sub-scopes were.

* Block agent profiles by permission and add prompt gating to slackbot

Add profile-level permission checks in run-agent-loop that throw 403
when users lack required permissions for specialized profiles (:sql,
:nlq, :transforms_codegen, :document-generate-content). Add explicit
denial messages to internal.selmer and slackbot.selmer templates when
features are disabled, directing users toward available capabilities
like search instead.

* Enforce per-group metabot on/off switch and remove semantic-search permission

Add base permission/metabot check in check-metabot-access! that blocks
all agent profiles when the group-level metabot permission is :no.
Remove permission/metabot-semantic-search from the permissions model
and frontend. Fix permissions API test request body format and update
all-perm-types to match current permission set.

Done: task/enforce-metabot-group-onoff

* Integrate custom system prompts into internal, SQL, and NLQ profiles

Read the metabot-system-prompt admin setting in build-system-message-content
and pass it as a custom_instructions Selmer variable. Add {% if %} blocks
at the end of internal, sql-querying-only, and natural-language-querying-only
templates to render custom instructions when set. Use |safe filter to
avoid HTML-escaping admin-provided content.

Done: task/integrate-custom-system-prompts

* Added AI usage backend APIs and tables

* Add new system prompts pages

* Update the ai feature access page

* Update side nav for Usage controls menu item + Clean up

* Make the page headings margin top consistent

* Update Customization page / Remove metabot-tone

* Add default metabot permissions for magic user groups

Insert default metabot permission rows (all yes/medium) for the four
magic groups: Administrators, All Users, Data Analysts, and All Tenant
Users. Also fix missing remarks on group_id column in metabot_group_limit
table to pass migration linter.

* Add GET /api/metabot/permissions/user-permissions endpoint

Return the current user's resolved metabot permissions (most permissive
across all their groups). Superusers get all-yes permissions. Response
uses simplified key names without the permission/ namespace prefix.

Done: frontend-ai-permission-gating/phase-1-backend-user-permissions-endpoint

* Add useUserMetabotPermissions hook for granular AI permission checks

Add RTK Query endpoint for GET /api/metabot/permissions/user-permissions
and a useUserMetabotPermissions() hook that returns granular permission
booleans (canUseMetabot, canUseSqlGeneration, canUseNlq, canUseOtherTools)
combining the global metabot-enabled setting with per-user group permissions.

Done: frontend-ai-permission-gating/phase-2-frontend-permissions-hook

* Gate metabot UI entry points with granular user permissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in 6 primary metabot UI entry points: sidebar button (canUseMetabot),
AI exploration menu (canUseNlq), Fix SQL button and inline prompt
(canUseSqlGeneration), chart analysis and data studio buttons
(canUseMetabot). Update tests to mock the permissions API endpoint.

Done: frontend-ai-permission-gating/phase-3-gate-ui-entry-points

* Migrate remaining components from useMetabotEnabledEmbeddingAware to useUserMetabotPermissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in all remaining metabot UI components: Metabot shell, MetabotQueryBuilder
(canUseNlq), MetabotQuickLinks, AppBarLarge, CodeMirrorEditor
(canUseSqlGeneration), query builder context registration, MetabotEmbed,
and CommandSuggestion. Add permissions API mock to shared test utils and
all affected test files. SelectEmbedExperienceStep intentionally kept on
the old hook (uses forceEmbedding for embedding feature config check).

Done: frontend-ai-permission-gating/phase-4-migrate-remaining-embedding-aware-uses

* Remove usage limits menu item

* Fix migration to work with mysql

* Fix clj-kondo warnings on feat/ai-controls branch

- Add api-scope to mcp module and users to metabot module allowed deps
- Add response schema to /user-permissions defendpoint
- Suppress aliased-namespace-symbol warnings on potemkin import-vars
- Remove unused make-tool-var helper and prefix unused bindings with _
- Rename make-tool-var to make-tool-var! (uses alter-meta!)

* Address code review findings from clojure-review

- Add response schemas to GET / and PUT / permissions endpoints
- Add ^:parallel to 22 pure tests across 3 test files
- Move check-metabot-access! guard out of let binding for clarity
- Fix indentation, alignment, :refer :all, and stale kondo ignores

* Route agent API scope references through metabase.metabot.core

Re-export scope vars from metabase.metabot.core so external modules
use the public API namespace instead of metabot.scope directly.

* Address PR review comments

- Fix indent in api-scope module config
- Re-export workspace scopes from workspaces.core instead of using
  workspaces.scope directly in agent_api/workspace.clj
- Extract wrap-with-scope-check into its own function in tools.clj
- Fix comment indentation in core_test.clj

* Add 'Settings and general limits' section on Usage limits page

* Add 'Group and tenant limits' section

* Tighten user-permissions response schema with exact permission fields

Use keyword keys and enum values matching the defined permission types
instead of loose [:map-of :string :string]. Also fix simplify-permissions
to produce keyword keys/values instead of strings.

* Separate foreign keys and indexes into distinct migration steps

Extract inline foreign key constraints from createTable into their own
changesets so FKs can be dropped independently of indexes/unique
constraints. Affects metabot_permissions, metabot_instance_limit, and
metabot_group_limit tables.

* Added api_usage_log tracking table and populate it through usage

* Add api-scope to driver-affecting-overrides

api-scope is a simple scope-registry utility that only depends on util.
It was triggering driver tests via its enterprise/workspaces dependent.

* Connect the backend API / Remove dollar limit type

* Initial llm limit checks

* Fix CI failures from AI controls + master merge

- Add MetabotPermissions/GroupLimit/InstanceLimit to copy entities
- Fix module config: sort api-scope, remove extraneous APIs/uses,
  add PermissionsGroup to metabot model-imports
- Fix MCP scope-matches? returning false instead of nil on non-match
- Remove :capabilities gate from autogen dashboard tool
- Bind scope to unrestricted in profiles capability tests
- Update prompt gating tests to match master's template wording
- Update prompts test to match metabot_name template variable
- Update scope resolution tests for migration defaults (yes/medium)

* Remove usage limit code from AI controls branch

Usage limits (instance, tenant, and group) are being developed on a
separate branch. Remove all related backend code: models, API endpoints,
settings, migrations, and tests.

Done: ai-controls-to-enterprise/phase-1-remove-usage-limits

* Fix copy entities: deduplicate MetabotPrompt, move AI models to core section

* Move permission resolution and MetabotPermissions model to enterprise

Introduce the :ai-controls premium feature flag and restructure metabot
permission resolution for the OSS/EE split:

- Register enable-ai-controls? in premium_features/settings.clj
- Convert resolve-user-permissions to defenterprise (OSS returns defaults)
- Create EE implementation with full DB-backed group resolution
- Move MetabotPermissions Toucan2 model registration to EE namespace
- Keep pure permission type definitions and defaults in OSS
- Split tests: OSS verifies defaults, EE verifies group-based resolution

Done: ai-controls-to-enterprise/phase-2-defenterprise-permission-resolution

* Move permission admin APIs to enterprise with input validation

Move GET/PUT /api/metabot/permissions admin endpoints to
/api/ee/ai-controls/permissions, gated by :ai-controls feature.
Keep user-permissions endpoint in OSS. Add schema validation on
PUT perm_type/perm_value fields, fix 402 feature-gate test to use
correct assertion pattern, and add ^:parallel to all test defs.

* Move AI controls pages into new plugin

* Add error toast for instance limit update

* Remove unused permission/metabot-model (model size) feature

The metabot-model permission (large/medium/small) was defined
speculatively but never wired into model selection and had no UI.
Remove it from permission definitions, defaults, API schemas,
migration seed data, frontend types, and all tests.

Done: ai-controls-to-enterprise/phase-4-move-model-loading

* Gate metabot customization settings behind :ai-controls feature

Add :feature :ai-controls to branding settings (metabot-name,
metabot-icon, metabot-show-illustrations) and system prompt settings
(metabot-chat-system-prompt, metabot-nlq-system-prompt,
metabot-sql-system-prompt). Without the feature, reads return defaults
and writes are rejected.

Done: ai-controls-to-enterprise/phase-5-move-customization-settings

* Refactor MetabotPermissions: move model to OSS, replace deprecated APIs

- Move Toucan2 model registration from enterprise to
  metabase.metabot.models.metabot-permissions (OSS)
- Replace mdb/update-or-insert! with t2/exists?+update!/insert!
- Replace user/group-ids with a subquery join through
  PermissionsGroupMembership
- Update model resolution to point to OSS namespace

* Move MetabotPermissions model to EE, fix test failures for ai-controls feature

- Move MetabotPermissions Toucan2 model registration from OSS to
  enterprise (metabase-enterprise.metabot.models.metabot-permissions)
- Move permission defs (metabot-permissions, perm-types, perm-type-defaults)
  into metabase.metabot.scope alongside related permission/scope code
- Add :ai-controls to snowplow stats ping and session token features test
- Add MetabotPermissions to serialization excluded-models and entity-id
  not-exported lists
- Update serialization baseline with new metabot settings
- Fix modules config: add api routes, model imports/exports for
  enterprise/metabot; remove stale users dep from metabot module

* Fix kondo warnings in settings_test: use true?, remove ^:parallel

* Add usage limit models and API as enterprise-only endpoints

Restore the usage limit code (instance, tenant, and group limits) that
was removed in 2db9f363, now scoped under enterprise modules and the
/api/ee/ai-controls/usage/ route prefix with :ai-controls feature gate.

* Address clojure-review findings: per-type perm validation, privacy, metadata

- Validate perm_value per perm_type in PUT endpoint (not just flat enum)
- Make format-native-query private
- Add ^{:added "0.61.0"} to enable-ai-controls?
- Add clarifying comment on enforce-scope dev-time warning

* - Add AI usage limits enforcement, logging, and cleanup
- Rename metabot-limit-type to metabot-limit-unit
- Add scheduled task to trim ai_usage_log rows older than 3 months

* Move ai controls RTK endpoints to enterprise folder

* Fix input debounced state issue

* Clean up on Group and tenant limits tabs

* - Add AI usage limits enforcement, logging, and cleanup
- Rename metabot-limit-type to metabot-limit-unit
- Add scheduled task to trim ai_usage_log rows older than 3 months

* Cleaned up ai_controls migrations

* Merged from ai-controls

* Fix CI test failures caused by ai-controls permission system

The new check-metabot-access! gate in run-agent-loop rejects
non-superusers whose permissions default to :no. Honor pre-bound
*current-user-metabot-permissions* so tests can bind permissions
directly. Also fix scope error message expectation, gate custom
prompt settings behind :ai-controls feature, increase poll timeout
for closing-connection test, and split large deftests into smaller
parallel tests.

* Move EE-dependent permissions test to enterprise, ignore metabot_permissions in H2 dump comparison

The user-permissions test that creates :model/MetabotPermissions belongs
in EE tests since the model is EE-only. The metabot_permissions table is
created by migration in all editions but only included in H2 dumps when
EE is available, so add it to the ignored tables in the dump comparison
utility.

* Fixed namespace reference

* Make cache ttl be 10 seconds

* Fix FE test failures by mocking metabot user-permissions endpoint

The new useUserMetabotPermissions hook calls /api/metabot/permissions/user-permissions
in many component trees. Tests that render these components were failing due to
unmocked API calls. Added setupUserMetabotPermissionsEndpoint helper and called it
in each affected test's setup.

* Mark update-permissions-test as non-parallel to fix MariaDB CI failures

* Fix prettier formatting in use-user-metabot-permissions test

* Fix TypeScript errors: missing settings defaults, stale token feature refs, type mismatches

* Fix eslint: remove unused createMockTokenFeatures imports, fix import ordering

* Compute token max in millions of tokens

* If any groups have a null limit, return null as the limit

* Improve state handling on group and limits components

* Add unit tests

* Fix metabot/new quicklink / don't call user-permissions if metabot is globally disabled

* Remove test flakiness

* Address PR review comments: remove scope re-exports, use enums, split tests

- Remove potemkin re-exports from metabot.scope; consumers now use
  api-scope directly for scope-matches?, unrestricted, registered-scope?,
  all-scopes, and scope-description
- Use proper malli enums for perm_type/perm_value in EE permissions
  response schema instead of bare :keyword
- Sort required-feature->message alphabetically in EE routes
- Fix api-scope indent in kondo config
- Delete unused keep-me comment in EE metabot routes
- Split large deftests into focused tests in permissions_test,
  scope_resolution_test, and profiles_test
- Remove unnecessary blank lines between testing blocks in api_test

* Exclude new AI usage limit models from serialization and update module config

Add AiUsageLog, MetabotGroupLimit, MetabotInstanceLimit, and
MetabotPermissions to serialization excluded-models, entity-id
not-exported, and copy-test exclusion lists. Update enterprise/metabot
module config with routes API, app-db/users dependencies, and
PermissionsGroup model import. Add new metabot settings to
serialization baseline.

* Fix kondo warnings: sort namespace requires alphabetically

* Fix namespace require ordering in agent core

* Fix add missing init module

* Fix module config for AI usage limit models

Update enterprise/metabot module: remove stale app-db and users deps,
add AiUsageLog model import. Export AiUsageLog from metabot module.
Remove stale internal-stats.metabot from internal-stats API.

* Add e2e tests for basic ai controls

* Fix serialization baseline: rename metabot-limit-type to metabot-limit-unit

* Fix e2e tests

* Add e2e tests for ai usage limits

* Polish tests

* Fix e2e tests

* Fix bug on custom quota-reached message input

* Fix issues with auto-saving and debounced requests

* Address further review comments

* Set max limit number on inputs

* Fix some tests

* Fix flaky MetabotFeatureAccessPage test by awaiting permission state

* Fix unit test

* Add anthropic api mock to test group quota limit more properly

* Remove stale metabot dependency from testing-api module config

* Fix nql→nlq typo in permission type and OSS default permissions

The permission type `permission/metabot-nql` was misspelled throughout
the codebase (should be `nlq` for Natural Language Query). Also fixed
the prompt file reference `nql_search.md` → `nlq_search.md` which was
a runtime bug, and updated the OSS fallback for resolve-user-permissions
to return all-yes (permissive) instead of all-no defaults.

* Move DB query helpers from permissions API into MetabotPermissions model

Move permissions-for-group and all-permissions from the API namespace
into the model namespace where they belong as data-access functions.

* Fix flaky resolve-user-permissions-nil-test

Return all-yes-permissions for nil user-id in both OSS and EE paths
(no user means no restrictions). Wrap test with with-premium-features
to make it deterministic regardless of token presence.

* Fix defenterprise-schema nil return schema causing OSS test failures

defenterprise-schema for log-ai-usage! had no return schema, which
produced nil and crashed malli validation in OSS mode. This broke the
report-token-usage-xf transducer pipeline, preventing snowplow events
from firing in downstream transducers.

* Renamed "conversations" to "messages"

* Add tenant limits e2e test / Address a few other review comments

* Fix more review comments

* Clean up on other pages to reuse new hook

* Move translation maps outside their functions / Fix config.edn issue

* Do not reset limits when switching limit types

* Fix e2e test

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-04-07 22:13:53 -03:00
Ryan Laurie
0b237d0918 Fix release permissions (#72140)
* more permission constriction

* more permission constriction
2026-04-07 17:07:57 -06:00
Ryan Laurie
b1819f54f4 update release workflow permissions (#72138) 2026-04-07 16:59:49 -06:00
Timothy S. Dean
69a5020367 Adjust CLAUDE.md backend test instructions (#72136) 2026-04-07 16:58:48 -06:00
Ryan Laurie
e01f66612a update release workflow permissions (#72137) 2026-04-07 16:33:15 -06:00
github-automation-metabase
69a776ea2d Auto-update documentation for master (#72131)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-07 17:23:02 -04:00
Oleksandr Solovyov
3671d3bd80 metabot: ability to make requests via ai proxy w/o having api key configured (#71714)
* metabot: ability to make requests via ai proxy w/o having api key configured

* modernize logic: BYOK is more important than ai proxy

* dry error messages and tests

* metabot token reporting

* byok setting

* track that ai-proxied messages are marked in the db

* only count ai_proxied in tokens reported

* fix ci concerns

* metabase/ as indicator of requests going through ai-proxy

* Port resolve-auth and test fixes from #71655

* Add claude-list-models-auth-preferences-test similar to claude-auth-preferences

* Add conventional -test suffix to auth-preferences tests

* BOT-1262 Add :feature :metabase-ai-provider guard to llm-proxy-base-url defsetting

Rather than moving the defsetting into ee add a :feature :metabase-ai-provider guard and leave it in oss.

* Fix trailing whitespace

* Fix logic in llm-provider-configured?

Logic now matches metabase.metabot.self.core/resolve-auth, i.e. no proxy fallback if llm-metabot-provider is unconfigured.

* Remove unused func

* Add more tests for metabase proxied models

Add tests for:
- validate-metabot-provider!
- /list-models endpoint provider resolution for both direct and metabase/ prefix
- slackbot streaming ai-proxy? propagation to store-message!

* Add metabot.provider-util namespace for parsing model-and-provider strings

And use it instead of ad-hoc parsing with str/split in various places.

* Remove unused metabot.core/routes

Re-exported from metabot.api/routes but nothing references it.

* Prefer with-redefs over mt/with-temporary-setting-values in tests to override env vars too

Specifically for tests that set llm-anthropic-api-key to ignore any value set in the env.

* Fix token usage reporting for openai

OpenAI returns token usage in :input_tokens and :output_tokens, map those back to out internal :promptTokens and
:completionTokens, respectively and also drop any nested token usage *_details that get returned to prevent an
exception in accumulate-usage-xf when attempting to (merge-with +).

    java.lang.ClassCastException: class clojure.lang.PersistentArrayMap cannot be cast to class java.lang.Number (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
	at clojure.lang.Numbers.add(Numbers.java:155)
	at clojure.core$_PLUS_.invokeStatic(core.clj:994)
	at clojure.core$_PLUS_.invoke(core.clj:986)
	at clojure.core$merge_with$merge_entry__6053.invoke(core.clj:3087)
	at clojure.core$reduce1.invokeStatic(core.clj:946)

* Review feedback: use str/replace-first

* Review feedback: ensure metabot-provider-schema matches supported-metabot-providers

---------

Co-authored-by: Mike Appleby <86076+appleby@users.noreply.github.com>
2026-04-07 13:25:37 -07:00
Tyler Plude
742b2190cd [BOT-1272] Fix missing metabot-id in streaming request (#72114)
pass metabot id to native streaming; unit test
2026-04-07 13:22:05 -07:00
Alex Yarosh
9b76829304 docs: the great detypoing (#72100)
* typos

* embedding
2026-04-07 13:15:59 -04:00
Jeff Bruemmer
0ffb9c891b docs - clarify one dashboard per page (#72098)
* clarify one dashboard per page

* questions
2026-04-07 13:14:34 -04:00
lbrdnk
a0a787ea54 Convert legacy into mbql5 on API level (#71944)
* Convert legacy into mbql5 on API level

* tests

* idempotence

* whitespace

* Review comments
2026-04-07 19:08:10 +02:00
Nathan Voxland
9f7c0c3a27 Search indexing memory and CPU improvements (#71999) 2026-04-07 11:30:01 -05:00
Cam Saul
4ae75fc5e6 Replace leftover usages of 'pMBQL' with 'MBQL 5' (#72066)
* Replace the term 'pMBQL' with 'MBQL 5'

* Restore deleted file
2026-04-07 09:29:27 -07:00
John Swanson
b1734f09ae No "login from new device" emails for to_session (#69854)
* No "login from new device" emails for `to_session`

Prevent the /api/sso/to_session endpoint from sending "login from new
device" emails. This endpoint is used for programmatic JWT-to-session
token exchange, so new device notifications are noise. Login history is
still recorded for audit purposes.

* kondo fix

* fix tests
2026-04-07 09:10:17 -07:00
Jeff Bruemmer
84329d9f08 Fix typo in settings documentation (#72074) 2026-04-07 11:30:04 -04:00
Timofey Kachalov
acfdee530b fix(sdk,embed-js): Fix wrong join on dropdown items styles (#71993) 2026-04-07 19:20:18 +04:00
Nick Fitzpatrick
d029b634ae Metric Viewer Menu Dividers (#71906) 2026-04-07 17:34:38 +05:30
Nick Fitzpatrick
122500f230 Update strings in metric view (#71912) 2026-04-07 17:34:27 +05:30
Nick Fitzpatrick
277d629317 Update Measure Icon in Data Studio and Metric Explorer (#71924) 2026-04-07 17:34:17 +05:30
Nick Fitzpatrick
05a0cd9db2 Hide the separator in breakout menu if not needed (#71937) 2026-04-07 17:34:05 +05:30
YB
12e3515671 GDGT-1431 Delete notifications orphaned from deleted cards (#71897)
When a card with an alert is permanently deleted, the Notification record and its Quartz cron trigger survive.
The trigger keeps firing and crashing every time, clogging logs and task history indefinitely.

The root cause is that Card's before-delete hook cleans up ParameterCards, ModerationReviews, and Revisions — but not Notifications. The notification_card table has a deleteCascade FK to report_card, so the NotificationCard payload row gets cascade-deleted, but the Notification row itself survives with a dangling payload_id.

There's 2 fixes in this PR:

Add notification cleanup to Card's before-delete hook. Materializes notification IDs first because Notification's own before-delete deletes the NotificationCard, which would invalidate a delete that depends on a subquery by the time the actual DELETE executes.

For notifications orphaned before this fix is deployed, guard in send-notification-sync! checks if the NotificationCard payload still exists before hydration. If not, it deletes the notification and throws a clean error.
We fully delete rather than deactivate (setting :active false) because an orphaned notification with a deleted card has no value — its handlers, subscriptions, and recipients would just be dead rows. This matches what happens when users manually delete an alert via delete-alert-and-notify!.
2026-04-07 10:02:06 +08:00
technomancy
69c17552f7 Add technomancy (me) to slack and GH team map json. (#71698)
Add technomancy (me) to slack map json file and github team map.

This is one of the steps of the onboarding guide for day 1.

[ci skip]
2026-04-06 16:36:27 -07:00
github-automation-metabase
3dc0f3eb08 Auto-update documentation for master (#72064)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-06 17:19:12 -04:00
Uladzimir Havenchyk
fcde592db4 deps: upgrade markdown-it (#71984) 2026-04-06 23:59:54 +03:00
Uladzimir Havenchyk
05fd40e2fb deps: upgrade dompurify (#71986) 2026-04-06 23:35:06 +03:00
bryan
351e4be6c8 Bundle airgap public key in standard Metabase JAR (#72046) 2026-04-06 19:39:54 +00:00
bryan
3cba66ca73 Guard export-viz-click-behavior-link against nil link models (#72003) 2026-04-06 15:54:46 +00:00
John Swanson
85de150a57 Increase debounce delay to 500ms (#70083)
[UXW-3154: Increase debounce delay for search API calls](https://linear.app/metabase/issue/UXW-3154/increase-debounce-delay-for-search-api-calls)

The current delay (300ms) is a bit too short. Increasing it to 500ms
should still be a snappy UX but send fewer requests while the user is
typing.
2026-04-06 15:16:11 +00:00
Braden Shepherdson
d4821e78e3 [MBQL lib] Add user docs for :fields lists (#71444) 2026-04-06 14:02:19 +00:00
Uladzimir Havenchyk
b34aa1c692 chore: upgrade qs (#70785)
deps: upgrade qs
2026-04-06 14:49:53 +03:00
Uladzimir Havenchyk
27e2412d69 deps: upgrade jspdf (#71987) 2026-04-06 13:04:36 +03:00
Uladzimir Havenchyk
f414178f3f deps: upgrade diff (#71981) 2026-04-06 13:03:19 +03:00
Uladzimir Havenchyk
492c1f3ae6 deps: upgrade lodash and lodash-es (#71982) 2026-04-06 13:02:57 +03:00
Uladzimir Havenchyk
5489604670 chore: replace vulnerable underscore with a util (#70784)
* chore: replace vulnerable underscore with a util

* missed another function

* remove underscore
2026-04-06 12:56:55 +03:00
metamben
cdcf995f5f Use effective/base type info for casts if database-type is missing (#71771)
* Use effective/base type info for casts if database-type is missing

Closes #68065
Closes #71551
2026-04-06 09:52:19 +03:00
Romeo Van Snick
9db030e42c Fix flaky test in unreferenced entries (#71134)
* Fix flaky test

* Fix flaky test

* Fix flaky test

* Add reference for table too
2026-04-05 09:20:57 +02:00
Anderson
672bd07e35 Metabot AI Controls (#71387)
* Implement static UI for Usage controls page

* Added initial ai-controls tables

* Set sidenav menu items / Add placeholders for new pages

* Implement Metabot Customization page UI

* - Added metabot/permissions GET and PUT apis
- Added settings for metabot name/tone/icon

* Fixed migration ids

* fixup! bad merge

* Add :scope metadata to all Agent Loop tools and create metabase.metabot.scope namespace

- Create metabase.metabot.scope with *current-user-scope* (defaults to #{}),
  scope-matches? with hierarchical wildcard support, and unrestricted set
- Add :scope to all 38 Agent Loop tool-fns with specific action scopes
  (e.g. "agent:sql:create", "agent:viz:edit") — wildcards are grant-side only
- Propagate :scope through wrap-tools-with-state in tools.clj
- :capabilities remain unchanged (orthogonal data-permission gating)
- Add tests for scope-matches?, default scope, and stub

Done: tool-scope/phase-1-tool-scope-declarations

* Add scope-based authorization middleware for Agent Loop tools

- Filter tools by scope in get-tools-for-profile (profiles.clj)
- Check scope before tool invocation in wrap-tools-with-state (tools.clj),
  returning a generic denial message (scope details logged, not leaked)
- Bind *current-user-scope* to unrestricted in run-agent-loop (temporary
  until phase 4 wires real permission resolution)
- Add scope enforcement tests covering filtering, denial, wildcards,
  no-scope passthrough, and default empty scope behavior

Done: tool-scope/phase-2-authorization-middleware

* Gate profile system prompts by metabot permission types

- Add *current-user-metabot-permissions* dynamic var to scope.clj
- Bind all-yes permissions in run-agent-loop (temporary, phase 4 wires real resolution)
- Derive has_sql_generation, has_nql, has_query_tools, has_other_tools
  boolean flags from permissions and pass to Selmer template context
- Gate internal.selmer sections: critical constraints, verification protocol,
  query building steps, SQL construction, SQL anti-patterns, SQL examples,
  NLQ guidance, dashboard routing, and query-focused common scenarios
- General sections (identity, context grounding, communication style, search
  example, entity linking) remain ungated for all permission levels
- 7 tests, 43 assertions covering all permission combinations

Done: tool-scope/phase-3-profile-prompt-gating

* Wire user-metabot-perms->scopes to metabot_permissions data model

- Implement resolve-user-permissions: looks up user's groups, queries
  MetabotPermissions, resolves most-permissive value across groups
- Implement real user-metabot-perms->scopes: maps :yes permissions to
  wildcard scope sets, always includes always-granted-scopes
- Replace temporary unrestricted/all-yes bindings in run-agent-loop with
  real permission resolution from api/*current-user-id*
- Superusers get scope/unrestricted and all-yes-permissions
- Permission resolution done once at reify construction, not per-reduce
- Agent API scope binding deferred (tracked in rdm task agent-api-scope-binding)
- 38 new test assertions covering permission→scope mapping, single-group,
  multi-group most-permissive-wins, and default scenarios

Done: tool-scope/phase-4-permissions-to-scopes

* Update permissions expected request format

* Include metabot and semantic-search tool options on the backend

* Connect access controls UI to the backend

* Wire up the backend for the Metabot Customize page

* Polish access controls page / Create MetabotSystemPromptsPage

* Added metabot-system-prompt

* Connect System prompt page to the backend

* Remove model selection + clean up

* Use custom metabot name

* Use metabot-icon in the UI + added metabot-show-illustrations toggle

* Removed metabase custom prompt

* Add defscope macro, scope registry, and api-scope module

Introduce a `defscope` macro that registers scopes with translatable
descriptions in a global registry, replacing inline scope strings across
all tool and endpoint metadata with typed var references.

Key changes:
- New `metabase.api-scope.core` module with defscope, scope registry,
  scope-matches?, and public API (all-scopes, scope-description)
- All 25 agent scopes declared via defscope in metabase.metabot.scope
- 3 EE workspace scopes declared in metabase-enterprise.workspaces.scope
- validate-tool-var! rejects tools with unregistered scopes at startup
- defendpoint accepts var references for :scope metadata (not just strings)
- enforce-scope logs dev-time warnings for unregistered endpoint scopes
- MCP scope enforcement now handled by defendpoint middleware (removed
  redundant pre-dispatch check); enforce-authentication preserves existing
  token-scopes on synthetic requests
- Scope enforcement error messages no longer leak required scope names
- Consolidated duplicate scope-matching logic in mcp/tools.clj

Done: tool-scope/phase-5-defscope-registry

* Make semantic_queries optional in search tool schemas

When semantic search is not enabled, the feature gate strips
semantic_queries from the tool definition sent to the LLM, but Malli
validation still required it — causing an "Invalid input" error.

* Add missing agent:query scope declaration

The agent API query endpoint requires "agent:query" but it was never
registered via defscope, only its sub-scopes were.

* Block agent profiles by permission and add prompt gating to slackbot

Add profile-level permission checks in run-agent-loop that throw 403
when users lack required permissions for specialized profiles (:sql,
:nlq, :transforms_codegen, :document-generate-content). Add explicit
denial messages to internal.selmer and slackbot.selmer templates when
features are disabled, directing users toward available capabilities
like search instead.

* Enforce per-group metabot on/off switch and remove semantic-search permission

Add base permission/metabot check in check-metabot-access! that blocks
all agent profiles when the group-level metabot permission is :no.
Remove permission/metabot-semantic-search from the permissions model
and frontend. Fix permissions API test request body format and update
all-perm-types to match current permission set.

Done: task/enforce-metabot-group-onoff

* Integrate custom system prompts into internal, SQL, and NLQ profiles

Read the metabot-system-prompt admin setting in build-system-message-content
and pass it as a custom_instructions Selmer variable. Add {% if %} blocks
at the end of internal, sql-querying-only, and natural-language-querying-only
templates to render custom instructions when set. Use |safe filter to
avoid HTML-escaping admin-provided content.

Done: task/integrate-custom-system-prompts

* Added AI usage backend APIs and tables

* Add new system prompts pages

* Update the ai feature access page

* Update side nav for Usage controls menu item + Clean up

* Make the page headings margin top consistent

* Update Customization page / Remove metabot-tone

* Add default metabot permissions for magic user groups

Insert default metabot permission rows (all yes/medium) for the four
magic groups: Administrators, All Users, Data Analysts, and All Tenant
Users. Also fix missing remarks on group_id column in metabot_group_limit
table to pass migration linter.

* Add GET /api/metabot/permissions/user-permissions endpoint

Return the current user's resolved metabot permissions (most permissive
across all their groups). Superusers get all-yes permissions. Response
uses simplified key names without the permission/ namespace prefix.

Done: frontend-ai-permission-gating/phase-1-backend-user-permissions-endpoint

* Add useUserMetabotPermissions hook for granular AI permission checks

Add RTK Query endpoint for GET /api/metabot/permissions/user-permissions
and a useUserMetabotPermissions() hook that returns granular permission
booleans (canUseMetabot, canUseSqlGeneration, canUseNlq, canUseOtherTools)
combining the global metabot-enabled setting with per-user group permissions.

Done: frontend-ai-permission-gating/phase-2-frontend-permissions-hook

* Gate metabot UI entry points with granular user permissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in 6 primary metabot UI entry points: sidebar button (canUseMetabot),
AI exploration menu (canUseNlq), Fix SQL button and inline prompt
(canUseSqlGeneration), chart analysis and data studio buttons
(canUseMetabot). Update tests to mock the permissions API endpoint.

Done: frontend-ai-permission-gating/phase-3-gate-ui-entry-points

* Migrate remaining components from useMetabotEnabledEmbeddingAware to useUserMetabotPermissions

Replace useMetabotEnabledEmbeddingAware with useUserMetabotPermissions
in all remaining metabot UI components: Metabot shell, MetabotQueryBuilder
(canUseNlq), MetabotQuickLinks, AppBarLarge, CodeMirrorEditor
(canUseSqlGeneration), query builder context registration, MetabotEmbed,
and CommandSuggestion. Add permissions API mock to shared test utils and
all affected test files. SelectEmbedExperienceStep intentionally kept on
the old hook (uses forceEmbedding for embedding feature config check).

Done: frontend-ai-permission-gating/phase-4-migrate-remaining-embedding-aware-uses

* Remove usage limits menu item

* Fix migration to work with mysql

* Fix clj-kondo warnings on feat/ai-controls branch

- Add api-scope to mcp module and users to metabot module allowed deps
- Add response schema to /user-permissions defendpoint
- Suppress aliased-namespace-symbol warnings on potemkin import-vars
- Remove unused make-tool-var helper and prefix unused bindings with _
- Rename make-tool-var to make-tool-var! (uses alter-meta!)

* Address code review findings from clojure-review

- Add response schemas to GET / and PUT / permissions endpoints
- Add ^:parallel to 22 pure tests across 3 test files
- Move check-metabot-access! guard out of let binding for clarity
- Fix indentation, alignment, :refer :all, and stale kondo ignores

* Route agent API scope references through metabase.metabot.core

Re-export scope vars from metabase.metabot.core so external modules
use the public API namespace instead of metabot.scope directly.

* Address PR review comments

- Fix indent in api-scope module config
- Re-export workspace scopes from workspaces.core instead of using
  workspaces.scope directly in agent_api/workspace.clj
- Extract wrap-with-scope-check into its own function in tools.clj
- Fix comment indentation in core_test.clj

* Tighten user-permissions response schema with exact permission fields

Use keyword keys and enum values matching the defined permission types
instead of loose [:map-of :string :string]. Also fix simplify-permissions
to produce keyword keys/values instead of strings.

* Separate foreign keys and indexes into distinct migration steps

Extract inline foreign key constraints from createTable into their own
changesets so FKs can be dropped independently of indexes/unique
constraints. Affects metabot_permissions, metabot_instance_limit, and
metabot_group_limit tables.

* Add api-scope to driver-affecting-overrides

api-scope is a simple scope-registry utility that only depends on util.
It was triggering driver tests via its enterprise/workspaces dependent.

* Fix CI failures from AI controls + master merge

- Add MetabotPermissions/GroupLimit/InstanceLimit to copy entities
- Fix module config: sort api-scope, remove extraneous APIs/uses,
  add PermissionsGroup to metabot model-imports
- Fix MCP scope-matches? returning false instead of nil on non-match
- Remove :capabilities gate from autogen dashboard tool
- Bind scope to unrestricted in profiles capability tests
- Update prompt gating tests to match master's template wording
- Update prompts test to match metabot_name template variable
- Update scope resolution tests for migration defaults (yes/medium)

* Remove usage limit code from AI controls branch

Usage limits (instance, tenant, and group) are being developed on a
separate branch. Remove all related backend code: models, API endpoints,
settings, migrations, and tests.

Done: ai-controls-to-enterprise/phase-1-remove-usage-limits

* Fix copy entities: deduplicate MetabotPrompt, move AI models to core section

* Move permission resolution and MetabotPermissions model to enterprise

Introduce the :ai-controls premium feature flag and restructure metabot
permission resolution for the OSS/EE split:

- Register enable-ai-controls? in premium_features/settings.clj
- Convert resolve-user-permissions to defenterprise (OSS returns defaults)
- Create EE implementation with full DB-backed group resolution
- Move MetabotPermissions Toucan2 model registration to EE namespace
- Keep pure permission type definitions and defaults in OSS
- Split tests: OSS verifies defaults, EE verifies group-based resolution

Done: ai-controls-to-enterprise/phase-2-defenterprise-permission-resolution

* Move permission admin APIs to enterprise with input validation

Move GET/PUT /api/metabot/permissions admin endpoints to
/api/ee/ai-controls/permissions, gated by :ai-controls feature.
Keep user-permissions endpoint in OSS. Add schema validation on
PUT perm_type/perm_value fields, fix 402 feature-gate test to use
correct assertion pattern, and add ^:parallel to all test defs.

* Move AI controls pages into new plugin

* Remove unused permission/metabot-model (model size) feature

The metabot-model permission (large/medium/small) was defined
speculatively but never wired into model selection and had no UI.
Remove it from permission definitions, defaults, API schemas,
migration seed data, frontend types, and all tests.

Done: ai-controls-to-enterprise/phase-4-move-model-loading

* Gate metabot customization settings behind :ai-controls feature

Add :feature :ai-controls to branding settings (metabot-name,
metabot-icon, metabot-show-illustrations) and system prompt settings
(metabot-chat-system-prompt, metabot-nlq-system-prompt,
metabot-sql-system-prompt). Without the feature, reads return defaults
and writes are rejected.

Done: ai-controls-to-enterprise/phase-5-move-customization-settings

* Refactor MetabotPermissions: move model to OSS, replace deprecated APIs

- Move Toucan2 model registration from enterprise to
  metabase.metabot.models.metabot-permissions (OSS)
- Replace mdb/update-or-insert! with t2/exists?+update!/insert!
- Replace user/group-ids with a subquery join through
  PermissionsGroupMembership
- Update model resolution to point to OSS namespace

* Move MetabotPermissions model to EE, fix test failures for ai-controls feature

- Move MetabotPermissions Toucan2 model registration from OSS to
  enterprise (metabase-enterprise.metabot.models.metabot-permissions)
- Move permission defs (metabot-permissions, perm-types, perm-type-defaults)
  into metabase.metabot.scope alongside related permission/scope code
- Add :ai-controls to snowplow stats ping and session token features test
- Add MetabotPermissions to serialization excluded-models and entity-id
  not-exported lists
- Update serialization baseline with new metabot settings
- Fix modules config: add api routes, model imports/exports for
  enterprise/metabot; remove stale users dep from metabot module

* Fix kondo warnings in settings_test: use true?, remove ^:parallel

* Address clojure-review findings: per-type perm validation, privacy, metadata

- Validate perm_value per perm_type in PUT endpoint (not just flat enum)
- Make format-native-query private
- Add ^{:added "0.61.0"} to enable-ai-controls?
- Add clarifying comment on enforce-scope dev-time warning

* Cleaned up ai_controls migrations

* Fix CI test failures caused by ai-controls permission system

The new check-metabot-access! gate in run-agent-loop rejects
non-superusers whose permissions default to :no. Honor pre-bound
*current-user-metabot-permissions* so tests can bind permissions
directly. Also fix scope error message expectation, gate custom
prompt settings behind :ai-controls feature, increase poll timeout
for closing-connection test, and split large deftests into smaller
parallel tests.

* Move EE-dependent permissions test to enterprise, ignore metabot_permissions in H2 dump comparison

The user-permissions test that creates :model/MetabotPermissions belongs
in EE tests since the model is EE-only. The metabot_permissions table is
created by migration in all editions but only included in H2 dumps when
EE is available, so add it to the ignored tables in the dump comparison
utility.

* Fix FE test failures by mocking metabot user-permissions endpoint

The new useUserMetabotPermissions hook calls /api/metabot/permissions/user-permissions
in many component trees. Tests that render these components were failing due to
unmocked API calls. Added setupUserMetabotPermissionsEndpoint helper and called it
in each affected test's setup.

* Mark update-permissions-test as non-parallel to fix MariaDB CI failures

* Fix prettier formatting in use-user-metabot-permissions test

* Fix TypeScript errors: missing settings defaults, stale token feature refs, type mismatches

* Fix eslint: remove unused createMockTokenFeatures imports, fix import ordering

* Add unit tests

* Fix metabot/new quicklink / don't call user-permissions if metabot is globally disabled

* Remove test flakiness

* Address PR review comments: remove scope re-exports, use enums, split tests

- Remove potemkin re-exports from metabot.scope; consumers now use
  api-scope directly for scope-matches?, unrestricted, registered-scope?,
  all-scopes, and scope-description
- Use proper malli enums for perm_type/perm_value in EE permissions
  response schema instead of bare :keyword
- Sort required-feature->message alphabetically in EE routes
- Fix api-scope indent in kondo config
- Delete unused keep-me comment in EE metabot routes
- Split large deftests into focused tests in permissions_test,
  scope_resolution_test, and profiles_test
- Remove unnecessary blank lines between testing blocks in api_test

* Fix kondo warnings: sort namespace requires alphabetically

* Fix namespace require ordering in agent core

* Add e2e tests for basic ai controls

* Fix e2e tests

* Fix issues with auto-saving and debounced requests

* Address further review comments

* Fix some tests

* Fix flaky MetabotFeatureAccessPage test by awaiting permission state

* Fix nql→nlq typo in permission type and OSS default permissions

The permission type `permission/metabot-nql` was misspelled throughout
the codebase (should be `nlq` for Natural Language Query). Also fixed
the prompt file reference `nql_search.md` → `nlq_search.md` which was
a runtime bug, and updated the OSS fallback for resolve-user-permissions
to return all-yes (permissive) instead of all-no defaults.

* Move DB query helpers from permissions API into MetabotPermissions model

Move permissions-for-group and all-permissions from the API namespace
into the model namespace where they belong as data-access functions.

* Fix flaky resolve-user-permissions-nil-test

Return all-yes-permissions for nil user-id in both OSS and EE paths
(no user means no restrictions). Wrap test with with-premium-features
to make it deterministic regardless of token presence.

---------

Co-authored-by: Nathan Voxland <nathan@metabase.com>
Co-authored-by: Edward Paget <edpaget@users.noreply.github.com>
Co-authored-by: Edward Paget <ed.paget@gmail.com>
2026-04-03 16:42:29 -07:00
Alex Yarosh
c05bc7d17b docs: stacked series and sticky columns (#71952)
* v60 viz improvements

* Optimized 4 images, saved 715.3 KB (43.9% smaller)

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Update docs/questions/visualizations/line-bar-and-area-charts.md

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-04-03 22:19:50 +00:00
github-automation-metabase
eb3ef366ad Auto-update documentation for master (#72004)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-03 21:16:12 +00:00
Edward Paget
d1d42b9a22 Fix missing :lib/uuid in join metric projection tests (#72000)
The two tests added in #71741 were missing required :lib/uuid keys
in their projection maps, causing API schema validation failures.
2026-04-03 20:03:00 +00:00
John Swanson
c49a43c8fe support expr with multiple instances of same metric (#71930)
support expr with multiple instances of same metric
2026-04-03 09:57:23 -08:00
John Swanson
1f90a2172f Fix metric explorer for metrics with joins (#71741) 2026-04-03 09:56:45 -08:00
Nemanja Glumac
cad0841958 Fix Trivy sarif failing to upload (#71994) 2026-04-03 17:02:22 +00:00
Nemanja Glumac
69fad10cda Fix unsafe inline expression interpolation in CI workflows (#71991)
* fix: extract 20 unsafe expressions from run blocks to env vars

Move ${{ }} expressions used in shell `run:` blocks into step-level
`env:` mappings to prevent script injection. Affected workflows:
athena, drivers, e2e-stress-test, frontend-unit-test-stress-test,
perf-test, pr-env, pr-env-destroy, release-embedding-sdk, repro-bot,
tag-for-release.

This is part 2 of a split from #71610 as requested.

* fix: use C-style for loop for burn_in variable expansion

Bash brace expansion ({1..N}) happens before variable expansion, so
{1..$VAR} doesn't expand as a range. Use a C-style for loop instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: extract inline PGPASSWORD secret to env var

The PR_ENV_DB_PASSWORD was left inline in run blocks while other
secrets in the same steps were already extracted to env mappings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename REF_NAME env var to IS_MASTER_OR_RELEASE in drivers.yml

The variable holds a boolean expression result, not a ref name. The
new name matches the CLI flag it's passed to (--is-master-or-release).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: extract remaining inline workflow_dispatch inputs to env vars

Extracts spec, grep, burn_in, version, and issue_number inputs that
were still interpolated directly in run blocks. Also simplifies env
var names by dropping the redundant INPUT_ prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: place env block before run block in athena.yml

Consistent with the convention used in all other steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Chris Nyhuis <cnyhuis@vigilantnow.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:35:30 +02:00
Mahatthana (Kelvin) Nomsawadi
e82a4d027c Add token renewal mechanism for guest embeds (#71075)
* An empty commit for the integration branch

* Extract JWT utilities into dedicated lib/jwt.ts module (#71515)

* Extract JWT utilities into dedicated lib/jwt.ts module

Moves `isJWT`, `decodeJwt`, `extractResourceIdFromJwtToken`, and `redactJwt`
from `lib/utils.ts` into a new `lib/jwt.ts`. Improves JWT decoding with safer
UTF-8 handling via `TextDecoder`. Updates all import sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Rename utils.unit.spec.ts to jwt.unit.spec.ts after moving extractResourceIdFromJwtToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove redactJwt and show full error strings in JWT error messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix AI slop

* Fix unit test

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix SdkError too wide (#71569)

* Extract JWT utilities into dedicated lib/jwt.ts module

Moves `isJWT`, `decodeJwt`, `extractResourceIdFromJwtToken`, and `redactJwt`
from `lib/utils.ts` into a new `lib/jwt.ts`. Improves JWT decoding with safer
UTF-8 handling via `TextDecoder`. Updates all import sites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Rename utils.unit.spec.ts to jwt.unit.spec.ts after moving extractResourceIdFromJwtToken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Remove redactJwt and show full error strings in JWT error messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix AI slop

* Fix unit test

* Add Storybook stories for SdkError component

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix mage error

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add token renewal infrastructure for guest embeds (#71516)

* Add token renewal infrastructure for guest embeds

Implements the full token renewal mechanism across three layers:

- embed.ts: adds _callGuestTokenProvider(), _fetchInitialGuestToken(), and
  _refreshGuestToken() to MetabaseEmbedElement; fetches tokens from
  guestEmbedProviderUri and serves refresh requests via postMessage

- request-session-token.ts + Redux store (auth.ts, guest-embed.ts): SDK-side
  postMessage protocol for requestGuestTokenRefresh / submitRefreshedGuestToken;
  getOrRefreshGuestSession thunk checks JWT expiry and refreshes transparently

- Plugin hooks (api.ts, embedding-sdk.ts, api.js, override-requests-for-embeds):
  wires refresh handler into the API request pipeline; adds x-metabase-guest-embed
  header and :entityIdentifier URL substitution

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* clean up AI slop

* Fix another AI slop

* Fix more slop

* fix embed wizard parameter not affecting the preview for 1 render

* Fix string custom-context not working

* Address review feedback

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Wire token renewal into dashboard/question components and add E2E tests (#71517)

* Wire token renewal into dashboard/question components and add E2E tests

- SdkDashboard and SdkQuestionProvider now dispatch setInitialGuestToken on
  mount, storing the raw JWT in Redux so the request interceptor can refresh it
  transparently when it expires
- use-load-question and use-common-dashboard-params updated for guest embed mode
- SdkIframeEmbedRoute: new route component for the iframe embed SDK
- guest-token-refresh.cy.spec.ts: comprehensive E2E Cypress suite covering
  token expiry, automatic refresh, error paths for both iframe and SDK modes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix _fetchInitialGuestToken to omit dashboardId/questionId from settings

In the guest embed token flow the JWT already encodes the resource, so
dashboardId and questionId should not be forwarded to the iframe via
setSettings. Pass them as undefined to override the values that
_updateSettings merges in from this.properties.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix Kelvin slop

* Make E2E tests consistent

* Make test more complete

* add custom context assertions

* address review feedback

* Address review

* Fix failed E2E tests

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix that one test that's always failing

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 16:00:46 +01:00
Mahatthana (Kelvin) Nomsawadi
7f785d6c98 Fix flaky metrics-editing.cy.spec.js test (#71983)
Fix that one test that's always failing
2026-04-03 15:08:36 +01:00
Timofey Kachalov
207548a864 Handle multi-touch events to improve pinch-to-zoom functionality on mobile devices for visualizations (#71932) 2026-04-03 15:52:44 +04:00
Sébastien
2a7c8331be fix(sdk-bundle): <metabase-question/> in guest embed no longer calls pulseform input (#71885) 2026-04-03 11:17:04 +02:00
dagecko
c27db353ba fix: pin 87 unpinned actions to full commit SHAs (#71913)
Pin all third-party GitHub Actions to immutable 40-character commit
SHAs to prevent supply chain attacks via tag rewriting. Version
comments added for readability.
2026-04-03 10:47:55 +02:00
Timofey Kachalov
63d5eb845a fix(sdk,embed-js): Adjust disabled state opacity for EditableText component (#71836) 2026-04-03 12:44:34 +04:00
Eric Normand
cbc40606ef Block database routing when transforms already exist (#71931)
Prevent enabling database routing on a database that already has transforms, since running transforms on databases with DB routing is not supported
Backend: added validation check in PUT /api/ee/database-routing/router-database/:id that returns 400 if transforms exist for the database
Frontend: disables the routing toggle in the UI when transforms exist, with a descriptive message
Also wired through the existing but unused database-id filter param in the transforms list API
2026-04-03 00:07:14 +00:00
github-automation-metabase
a7ca6bbca8 Auto-update documentation for master (#71949)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-04-02 18:06:12 -04:00
Alex Yarosh
a63333ac04 docs: metrics (#71778)
* first draft

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* viewer

* metrics edits

* metrics explorer

* data studio

* Update docs/data-modeling/metrics.md

* typo

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* Apply suggestions from code review

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>

* edits

---------

Co-authored-by: Jeff Bruemmer <jeff.bruemmer@gmail.com>
2026-04-02 22:04:37 +00:00
John Swanson
93db322a84 Hide dimensions and filters without access in the metric explorer (#71692) 2026-04-02 13:50:36 -08:00
Alexander Polyankin
940c3e343f fix escape logic in serdes (#71940)
* fix escape logic in serdes

* fix

* fixes
2026-04-02 21:11:30 +00:00
Edward Paget
9f5aaf4a81 Add GitHub Action to auto-resolve backport conflicts with Claude (#71158)
* Add GitHub Action to auto-resolve backport conflicts with Claude

Adds a new workflow triggered by the `auto-resolve-conflicts` label (or
manual dispatch) that uses Claude Code to automatically resolve cherry-pick
conflicts on backport PRs. This eliminates the manual step of checking out
the branch, running backport.sh, and hand-resolving conflicts.

* Add kondo and typescript checks

* Add syntax validation and formatting to backport conflict resolver

- Prepare backend (JDK/Clojure) and frontend (Node/Bun) environments
- Run cljfmt and eslint --fix to auto-format resolved files
- Run clj-kondo and tsc to validate syntax before completing cherry-pick
- Claude loops on errors until files are clean before committing

* Make Claude max turns configurable in backport conflict resolver, default to 50

* Fix Bash permission pattern for cljfmt in backport conflict resolver

Use colon-star syntax (cljfmt:*) to match other Bash tool permission
patterns, fixing permission denial for ./bin/mage cljfmt-files.

* Detect yarn vs bun for older release branches in backport resolver

Release branches ≤58 use yarn, ≥59 use bun. Detect the package manager
by checking for bun.lock after checkout, then use it for the CLJS build
step, allowed_tools permissions, and Claude prompt instructions.

* Set NODE_VERSION=22 for claude-code-base-action step

The action internally runs setup-node with 18.x by default, overriding
the Node 22 installed by prepare-frontend. This caused yarn engine
check failures on older release branches. The action respects a
NODE_VERSION env var, so set it to 22.

* Improve backport resolver: detect max turns, add commit details to success message

- Bump default max turns from 50 to 75
- Detect when Claude exits due to max_turns exceeded and post a specific
  comment explaining the issue with a link to re-run the workflow
- Include a commit link and collapsible changed files list in the success comment
2026-04-02 13:45:21 -07:00
Oreon Lothamer
0eef3193ce Use assume role for AWS credentials in workflows (#71608) 2026-04-02 22:15:16 +02:00
Jeff Bruemmer
6dbb28bb3e docs - Fix formatting of Agent API link in documentation (#71941)
Fix formatting of Agent API link in documentation
2026-04-02 19:23:35 +00:00
Jeff Bruemmer
7034d54542 docs - upload edits (#71684) 2026-04-02 14:49:10 -04:00
Riley Thompson
bdda38ff17 Reject multi statement queries (#71573)
* reject multi statement queries

* add dialect

* update pr

* use multimethod

* mark driver tests

* remove join for clickhouse

* kebab case

* fix e2e test
2026-04-02 18:24:02 +00:00
Aleksandr Lesnenko
293b99e321 fix unnecessary data grid row remounting (#71877)
* fix unnecessary data grid row remounting

* spec
2026-04-02 18:21:26 +00:00
John Swanson
1c26f9d31c Fix document visibility in trash (#71903) 2026-04-02 09:17:25 -08:00
Phoomparin Mano
71b8317b65 Add MCP apps CORS settings page and settings (#71763)
* feat: add MCP apps CORS settings page at /admin/metabot/mcp

Add a new settings page where admins can enable CORS for popular MCP
app clients (Claude, ChatGPT, Cursor/VS Code) via checkboxes, and
add custom domains for self-hosted clients like LibreChat.

Backend: new `common-mcp-apps-cors-origins` (CSV) and
`custom-mcp-apps-cors-origins` (string) settings in mcp/settings.clj.
Security middleware merges these with embedding SDK origins in
`access-control-headers`. Special handling for vscode-webview:// prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address design review — use toggles, fix casing, remove sublines

- Switch from Checkbox to Switch toggles for MCP client selection
- "MCP Apps" → "MCP apps" in sidebar and page title
- Remove redundant description sublines from client options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract MCP CORS tests to separate security_mcp_test file

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add unit tests for mcp apps

* rename: common-mcp-apps-cors-origins → mcp-apps-cors-enabled-clients

The setting stores client keys (claude, vscode, chatgpt), not origins.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename: custom-mcp-apps-cors-origins → mcp-apps-cors-custom-origins

Consistent mcp-apps-cors- prefix for both settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add mcp to driver-affecting-overrides

MCP settings are infrastructure, not driver logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add cors origins parsing improvements from @crisptrutski

Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>

* move mcp-apps-sandbox-origin? from security middleware to mcp.settings

Encapsulates MCP sandbox origin logic in the mcp module, uses condp
for extensibility when more non-standard schemes are added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add oauth-server to mage modules list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add mcp.core as public API, keep mcp.settings internal

External consumers (e.g. security middleware) now go through mcp.core
instead of reaching into mcp.settings directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* move application logic from mcp.settings to mcp.core

mcp.settings now only contains pure config (defsettings + cors-origins).
vscode-webview-enabled? and sandbox-origin? live in mcp.core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* remove useEffect state sync from MCP apps settings

Local state is kept for optimistic UI updates, but the useEffect sync
from savedValue is unnecessary since these settings won't change from
an external source concurrently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename "MCP app clients" to "MCP clients" in descriptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename "vscode" client key to "cursor-vscode"

Aligns the key with the label "Cursor and VS Code" across frontend,
backend domain mapping, and tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* expand test to toggle all MCP clients

Use findByLabelText instead of testIds, and verify each toggle
accumulates the enabled clients list correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add init for mcp

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Chris Truter <crisptrutski@users.noreply.github.com>
2026-04-02 16:29:09 +00:00
Sébastien
f01f753ff2 fixed translation issue with funnels (#71810) 2026-04-02 17:45:16 +02:00
Timofey Kachalov
a6453de48d feat(sdk,embed-js): Add touch device support for collection name description icon (#71762) 2026-04-02 19:42:56 +04:00
Aleksandr Lesnenko
a3ccc35af6 hides overview tab of the metrics pages if no dimensions (#71675)
* hides overview tab of the metrics pages if no dimensions

* hide caching tab if no write access to metric

* update specs based on latest changes from master
2026-04-02 10:28:56 -04:00
Chris Truter
d07b1772b8 Move table_transform_target_fk migrations from v60 to v61 (#71717) 2026-04-02 16:14:49 +02:00
Phoomparin Mano
05ec8451c5 Move CORS settings to OSS and show Security tab in Embedding (#71648)
* move the cors settings to oss

* add quick test for cors on oss

* test: update security middleware tests for OSS CORS behaviour

Add a dedicated test verifying CORS headers are sent when origins are
configured even without embedding features enabled (OSS path).

Update existing cases to reflect that removing the :embedding-sdk
feature flag from embedding-app-origins-sdk means the getter now
returns the actual stored value in all contexts (previously returned ""
without the premium feature active in tests).

Remove 16 redundant test cases where sdk=true with non-empty origins
produced identical results to sdk=false with non-empty origins — now
that CORS is enabled whenever origins are configured, the sdk flag is
only meaningful when origins are empty.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* restore tests

* fix: remove redundant enable flags from CORS check and add OSS CORS test

The enable-embedding-sdk and enable-embedding-simple flags are no longer
needed to gate CORS headers — having configured origins is sufficient,
and localhost is always allowed via the existing passthrough. This
addresses PR review feedback to simplify the logic and add missing
frontend test coverage for CORS settings in OSS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update e2e tests for OSS/Starter CORS settings visibility

Security tab and CORS settings are now available on all plans,
not just EE. Update assertions to expect them to exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:05:52 +00:00
Chris Truter
6f850e24db Clean up workspace schema and user-name code (#71808)
* Clean up workspace schema and user-name code

1. Single source of truth for the prefix — "mb__isolation" is defined once in metabase.driver.util (private), not three places
2. Matching methods live next to the constant — workspace-isolated-schema? (in-memory) and workspace-isolated-schema-clause (HoneySQL) so callers don't need to know the prefix or construct patterns themselves
3. Removed the requiring-resolve hack in the redshift test
4. Removed duplicated name generation — enterprise workspaces/util had identical copies of isolation-namespace-name, isolation-user-name, and instance-uuid-slug; callers now use driver.u directly

Closes [GDGT-2157: redshift tests require enterprise alias](https://linear.app/metabase/issue/GDGT-2157/redshift-tests-require-enterprise-alias)

* Avoid dynamic string building

* Use escape characters since _ is a wildcard

* Tighten up a bit more

* Remove stale include
2026-04-02 08:17:52 -04:00
github-automation-metabase
c08d322874 Update translations 2026-04-01 (#71841)
* Update translation sources 2026-04-01

* Update translations

* skipping offending test

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Katya Voropaieva <110612633+KorowkaMu@users.noreply.github.com>
Co-authored-by: Sébastien <sebastien@metabase.com>
2026-04-02 11:48:13 +00:00
Chris Truter
33a9177696 Test to catch incorrect migration versions (#71743) 2026-04-02 11:12:44 +00:00
Stas Gavrylov
beed12ea36 GDGT-2089: Adjust viz settings sidebar styling (#71472)
* GDGT-2089: Adjust settings sidebar styling

* GDGT-2089: Fix PR comments. Adjust styling
2026-04-02 13:51:56 +03:00
Sébastien
23eaffd14a add new-native to mb-question doc (#71890) 2026-04-02 09:33:04 +00:00
Stas Gavrylov
96f8785d12 GDGT-2047: Add ability to proxy /api requests to remote server (#71090)
* Add ability to proxy /api requests to remote server

* Tighten security. Add tests

* Move proxy code to dev/. Add mention in docs
2026-04-02 11:48:20 +03:00
Nick Fitzpatrick
0642892b42 Adding Details tab to table section (#71722)
* Adding Details tab to table section

* e2e adjustments

* remove grow on tab list

* fix e2e tests
2026-04-02 12:42:57 +05:30
Timofey Kachalov
0336557161 feat(sdk,embed-js): Improve touch device support in dropdown components in Notebook Editor (#71562) 2026-04-02 11:12:47 +04:00
Timofey Kachalov
8c2ca60ccb Fix duplicated identifiers error (#71883)
Fix duplicated identifiers
2026-04-02 01:38:10 -04:00
Timofey Kachalov
a3340c17a6 feat(sdk,embed-js): Improve mobile range selection experience for visualizations (#71374) 2026-04-02 08:45:00 +04:00
Alexander Polyankin
bd82202177 Representations baseline (#71879)
* Omit UUIDs in SerDes MBQL 5 export unless required

* Test fixes 🔧

* Final test fixes 🔧

* cljfmt

* fixes

* fixes

* fx

* fixes

* es

* fix

---------

Co-authored-by: Cam Saul <github@camsaul.com>
2026-04-02 00:22:41 -04:00
Cam Saul
22a21845f1 Omit UUIDs in SerDes MBQL 5 export unless required (#71862)
* Omit UUIDs in SerDes MBQL 5 export unless required

* Test fixes 🔧

* Final test fixes 🔧

* cljfmt
2026-04-01 21:43:06 -04:00
metatyb
e75d0484e8 chore: add metatyb to team and github-slack-map (#71803) 2026-04-02 09:30:45 +08:00
Nemanja Glumac
43db728f71 Clean up two module violations in metabase api (#71786)
* Move DataSegregationStrategy type to metabase-types/api/permissions

This is a permissions concept, not an embedding one. Moving it to
metabase-types resolves a module boundary violation (basic/api importing
from shared/other) and breaks a circular dependency between metabase/api
and metabase/embedding/embedding-hub.


Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 00:39:48 +02:00
Nemanja Glumac
9537481bb2 Improve sync-fork workflow ergonomics (#71875) 2026-04-02 00:38:49 +02:00
Eric Normand
74a08a7a2d Fix usage_info IN clause exceeding 65K parameter limit (GHY-2413) (#71856)
- Fixes `GET /api/database/:id/usage_info` failing when a database has more than 65K tables
- The segment count query was passing all table IDs as parameters to an `IN` clause, which exceeds PostgreSQL's 65,535 parameter limit
- Replaced the `IN` clause with a subselect (`SELECT id FROM metabase_table WHERE db_id = ?`), eliminating the parameter explosion
- Removed the now-unnecessary `select-pks-set` call and `table-ids` argument from `database-usage-query`
2026-04-01 20:42:52 +00:00
Alex Yarosh
db1f2bbe75 Update description for logarithm value parameter (#71851) 2026-04-01 19:37:57 +00:00
Aleksandr Lesnenko
6d190f1f62 do not suggest city as a geo dimension for tabs with map viz (#71768)
* do not suggest city as a geo dimension for tabs with map viz

* update e2e spec
2026-04-01 19:32:56 +00:00
appleby
4ff8b2722e BOT-1223 Fix zero-arity tool call schemas for anthropic provider (#71618)
* BOT-1223 Fix zero-arity tool call schemas for anthropic provider

Anthropic is stricter than openrouter about schemas for tool calls with no arguments. A schema like `[:maybe :map]`
produces a JSON schema like `{"oneOf": [{"type": "object", ...}, {"type": "null"}]}` but Anthropic's API requires
input_schema to be `{"type": "object", ...}` at the top level. A oneOf with a null variant is invalid.

* Handle nil arguments in metabase.metabot.self.core/run-tool
2026-04-01 12:05:14 -07:00
Jeff Bruemmer
75befd8877 docs - links (#71853)
links

Frontload links to ref docs.
2026-04-01 14:04:00 -04:00
Filipe Silva
34a7791680 fix: accept metered transforms product types in cloud-add-ons endpoint (#71664) 2026-04-01 18:56:42 +01:00
Nemanja Glumac
6a72335427 Allow test quarantine in Metabase internal forks (#71837) 2026-04-01 19:15:37 +02:00
Nick Fitzpatrick
a86098bc57 Rename Data Structure to Tables (#71744) 2026-04-01 22:12:00 +05:30
Eric Normand
23410f5c24 Fix sync_schema silently dropping when executor is busy (GHY-3254) (#71756)
* Add failing test reproducing GHY-3254: sync_schema silently drops when executor is busy

The quick-task executor uses a single thread. If any task gets stuck
(e.g., a hanging JDBC connection), all subsequent manual sync requests
queue forever while the endpoint returns 200.

* Add per-task timeout to quick-task executor

Each task submitted to the quick-task executor now runs on a child
thread with a configurable timeout (*task-timeout-ms*, default 2h).
If a task exceeds the timeout, it is cancelled and the executor moves
on to the next queued task. This prevents a single stuck task from
blocking all subsequent manual sync requests.

The stuck thread's stack trace is logged for diagnostics.

* Use MB_QUICK_TASK_TIMEOUT_MINUTES env var for task timeout

Replace the dynamic var with a real env var (default 120 minutes).

* Use config/config-int for MB_QUICK_TASK_TIMEOUT_MINUTES

Read the env var via metabase.config.core instead of System/getenv,
consistent with how other config values are read in the codebase.
2026-04-01 11:39:44 -05:00
Timofey Kachalov
2aae5c688b feat(sdk,embed-js): Enhance getEventTarget to support TouchEvent for improved mobile compatibility when showing a drill popup (#71636) 2026-04-01 20:36:55 +04:00
Nick Fitzpatrick
6af2a17af7 Subtract scrollbar with from pivot table header when apropriate (#71654) 2026-04-01 21:39:41 +05:30
Nick Fitzpatrick
6c297776ee Skipping GridMapPdfExport loki test (#71833) 2026-04-01 21:38:01 +05:30
dpsutton
ee5d80d008 middleware to prevent middleware that hits app-db (#71775)
currently it is just two middleware: impersonation and db routing

this is necessary as we want to move into a world where we can compile a
query from yaml files without an app db file. These middleware are not
impportant for our purposes here.

This is also temporary work. This is slated to be fixed up in

[QUE2-488: Introduce an app-db protocol for QP
middleware](https://linear.app/metabase/issue/QUE2-488/introduce-an-app-db-protocol-for-qp-middleware)
2026-04-01 09:29:07 -05:00
Edward Paget
1038b9d7bd Remove metabot describe API, openai client, and unused deps (#71774)
The metabot describe endpoints (card and dashboard autodescription) are
no longer needed. This removes the API routes, task implementations,
OpenAI client wrapper, tests, and the now-unused openai-clojure and
lambdaisland/uri dependencies.
2026-04-01 08:46:13 -05:00
Nick Fitzpatrick
a4d498d247 add data image check to urlTransform (#71588)
* add data image check to urlTransform

* de-duplicate regex
2026-04-01 19:03:47 +05:30
Nick Fitzpatrick
3b4897a341 show error message on remote sync pull changes or branches error (#71668)
* show error message on remote sync pull changes or branches error

* test adjustments
2026-04-01 19:03:31 +05:30
Jeff Bruemmer
134065e95e docs - images and notes (#71773)
* images and notes

* Optimized 2 images, saved 118.8 KB (41.4% smaller)

---------

Co-authored-by: Metabase Automation <github-automation@metabase.com>
2026-04-01 13:01:48 +00:00
Nick Fitzpatrick
fdb8d17d50 Revert April Fools Joke (#71801)
* Add CI check

* Revert "Fix metabot padding (#71680)"

This reverts commit 5886b7b7c6.
2026-04-01 05:38:24 -06:00
Timofey Kachalov
efc354420f feat(SDK, EmbedJS): Add touch support for Popover targets to improve mobile usability (#71484) 2026-04-01 12:59:39 +04:00
Tyler Plude
878fc365a3 [BOT-1233] Fix get-table-details arg flag typos (#71679)
* fix get-table-details arg flags

* new unit test
2026-03-31 22:59:18 -07:00
Alexander Polyankin
9ada59a804 Serdes fixes for python transforms (#71735)
* fixes

* fixes
2026-03-31 21:43:55 -04:00
appleby
4544ff5e6b BOT-1221 Resolve metabase://transform links to new data-studio path (#71613) 2026-04-01 00:30:55 +00:00
appleby
734dba5964 BOT-1226 Only run metabot internal tasks when the llm-metabot-provider-lite is configured (#71667)
* BOT-1226 Only run metabot internal tasks when the llm-metabot-provider-lite is configured

The agent loop was already checking llm-metabot-internal-tasks-enabled? before running internal background tasks like
user intent classification. Switch the default value for llm-metabot-internal-tasks-enabled? from true to false and
update the :getter to check that the corresponding llm-metabot-provider-lite is configured.

* Review feedback: ensure llm-provider-configured? returns boolean
2026-03-31 23:10:11 +00:00
Cam Saul
b6a9236b1d Remove legacy MBQL conversion steps from SerDes (#71614)
* SerDes use MBQL 5 [WIP]

* Update serialization baselines

* Test fixes 🔧

* More test fixes 🔧

* Test fixes 🔧
2026-03-31 19:03:43 -04:00
github-automation-metabase
ef14caecf1 Auto-update documentation for master (#71781)
Update generated docs (master)

Co-authored-by: github-actions <github-actions@github.com>
2026-03-31 18:27:33 -04:00
appleby
9080614933 BOT-1199 ORDERS table link points to an invalid path (#71543)
* BOT-1199 Add table-specific link examples to the internal metabot prompt

* Fix resolve-table-link to resolve to valid ad-hoc question link

The docstring mentioned avoiding a db lookup for the database id, but a link like `/table/<id>` is not a valid
metabase link.

* Cache metabase.metabot.agent.links/resolve-table-link result for 10 minutes

* Remove regex guard around parse-long in resolve-table-link

parse-long already returns nil if the parse fails.

* Fix require for application-database-metadata-provider

* Fix resolve-entity-link-test for tables and split it out into a new test
2026-03-31 15:17:02 -07:00
Cam Saul
da8bc93939 Fix driver-related code scanning alerts (#71666) 2026-03-31 18:00:30 -04:00
Oleksandr Solovyov
9c23e9d35a [metabot] add repr for measures and segments (#71656)
* [metabot] add repr for measures and segments

* Wire up measures and segments correctly

* Improve measures and segments representation

---------

Co-authored-by: Mihael Konjevic <konjevic@gmail.com>
2026-03-31 21:20:17 +00:00
Sloan Sparger
686087f2ec [Metabot] Fix Admin AI sidebar disappearing (#71597)
reorganize page components so sidebar does disappear while loading metabot settings pages
2026-03-31 20:36:42 +00:00
Tyler Plude
e0ba625be4 [BOT-1247] Fix empty string content blocks in anthropic API (#71765)
init
2026-03-31 12:57:49 -07:00
Nemanja Glumac
9038c23075 Run cross-version script only on the main repo (#71754) 2026-03-31 21:55:10 +02:00
Nemanja Glumac
2e28c3f930 Ask for explicit input to override fork changes (#71753) 2026-03-31 21:52:30 +02:00
Fraser
0f42846b53 Clean UI utils and component violations (#71465) 2026-04-01 08:47:17 +13:00
Fraser
afc7fdbeb6 Add subset of files to app-misc module and make schema a lib module (#71700) 2026-04-01 08:46:56 +13:00
Noah Moss
adc3c025c5 Let Metabot write a brief response after using autogen_dashboard (#71758)
remove final-response? from autogen dashboard tool to allow for a brief description
2026-03-31 15:33:51 -04:00
Ryan Laurie
5886b7b7c6 Fix metabot padding (#71680)
* avante garde monetization

* fetch market prices

* limited fun

* obey the linter
2026-03-31 12:50:00 -06:00
Sloan Sparger
aa84a83fba [Metabot] Remove metabot v3 token feature (#71600)
remove metabot v3 token feature
2026-03-31 18:21:13 +00:00
Sloan Sparger
6c14871bae [Slackbot] Make slackbot auth req messages in channel an ephemeral message (#71603)
make slackbot auth req messages in channel an ephemeral message
2026-03-31 12:55:08 -05:00
Edward Paget
9e576f007b Fix git sync to remove stale YAML files from managed export directories (#71739)
Exports are always full, so any .yaml file in a managed directory that isn't
in the current write set should be removed. This fixes stale files persisting
after entity renames, moves between collections, or deletions.

The git source now accepts a set of managed top-level directories. During
write-files!, existing files in managed dirs not in the write set are cleaned
up automatically. This replaces the previous path-prefix-based collection
scoping and the explicit build-all-removal-paths machinery, which couldn't
handle renames or cross-collection moves.
2026-03-31 12:48:56 -05:00
Sloan Sparger
400a33e4ef [Metabot] Hide embedded settings if embedding is not available (#71594)
hide embedded metabot settings if embedding is not available
2026-03-31 12:17:12 -05:00
Noah Moss
201824c6b2 Remove capability check for create-autogenerated-dashboard-tool; clarify system prompt (#71589)
* fix autogen_dashboard tool by removing capability checck, clarify system prompt around dashboards

* fix tests

* fix test
2026-03-31 13:13:30 -04:00
Edward Paget
fa31def1bc Fix support access grant password reset failing for deactivated users (#71598)
* Fix support access grant password reset failing for deactivated users

When a support user was deactivated (e.g. after a previous grant expired),
creating a new grant would leave the user inactive. The password reset
token authentication would succeed, the :after method would consume the
token and change the password, but create-session! would then reject the
login because is_active was false. with-fallback would swallow this and
fall back to emailed-secret-password-reset which also failed, resulting
in an opaque "Invalid reset token" error with the token irreversibly
consumed.

Fix: fetch-or-create-support-user! now reactivates deactivated users.

Also adds tests for the deactivated user flow, pre-existing auth identity
cascade, with-fallback error swallowing behavior, and direct login! calls
for easier debugging.

* fixup! cljfmt
2026-03-31 11:00:45 -05:00
Nicola Mometto
c134074716 Dedupe slack channels (#71653)
dedupe slack channels
2026-03-31 16:39:30 +01:00
lbrdnk
237c3fe056 Remove stale archived_message instructions (#71715) 2026-03-31 16:17:00 +02:00
Nick Fitzpatrick
01af8eb57f updating styling for Database Destination Connection Modal (#71586) 2026-03-31 19:38:54 +05:30
Edward Paget
e60356ffd7 Fix notification send test retry config key (:max-attempts → :max-retries) (#71689)
The test overrode :max-attempts but diehard uses :max-retries, so the
override was silently ignored and all 6 retries ran with exponential
backoff (~31s). With the correct key the test finishes in ~1s.

Done: task/fix-notification-retry-key
2026-03-31 14:08:49 +00:00
Kevin Kelleher
d825c4dab3 Metrics home page and viewer cleanup (#71514) 2026-03-31 13:41:56 +00:00
lbrdnk
bee5aa840a Native query context formatting (#71566)
* Extend the schema for context received in requests

* Fix native question context formatting

* Adjust context schema

* Tests

* test

* Tests

* extract dialect

* remarks
2026-03-31 15:12:05 +02:00
Kevin Kelleher
214f776ad4 Metrics explorer dimension picker and tab creation fixes (#71536) 2026-03-31 07:13:57 -04:00
Timofey Kachalov
a4c66d0626 Update permissions in embedding SDK tests to avoid permission errors in some cases (#71705) 2026-03-31 13:56:11 +04:00
Nemanja Glumac
cc7506cc6f Allow Metabase forks (including private ones) to sync automatically (#71663)
Add a new sync-fork workflow that automatically syncs metabase internal forks
with upstream metabase/metabase (master + 5 most recent release branches).
Force-syncs from upstream as source of truth, skips push when no diff to
avoid unnecessary CI runs. Only runs on forks, never on the public repo.

Guard all scheduled workflows with repository check so they only run on
schedule for metabase/metabase, not on forks (manual dispatch
still works on both).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 06:05:11 +00:00
Alexander Polyankin
8f62ee5f3a Serdes skip default values (#71544) 2026-03-31 03:42:08 +00:00
Fraser
f922a58520 Clean dead code (#71463) 2026-03-31 16:02:41 +13:00
metabase-bot[bot]
1179147e3d [chore] Update E2E test timings with recent data (#71621) 2026-03-31 14:03:04 +13:00
Fraser
b2a016926e Push shared redux actions down (#71130) 2026-03-31 13:53:24 +13:00
Maz Ameli
a9f7e0504a Change label for stacked series setting (#71541)
change label for stacked series setting
2026-03-30 23:01:28 +00:00
Edward Paget
4b032951ad Include lock name in cluster lock log and error messages (#71677) 2026-03-30 21:09:03 +00:00
Braden Shepherdson
43ab4ba9d3 [MBQL lib] Add API docs for returned-columns and other metadata (#71443)
Part of QUE2-366.
2026-03-30 21:03:49 +00:00
Nemanja Glumac
610ff28b80 Fix a collection picker flake in cross-version tests (#71599) 2026-03-30 21:26:39 +02:00
Alexander Polyankin
4bbdb37c66 Serdes file and folder human readable names (#71583) 2026-03-30 18:52:39 +00:00
Nick Fitzpatrick
b56c33b6be adjust styling on parameter placeholders (#71577) 2026-03-30 23:40:08 +05:30
Nick Fitzpatrick
a14c0b00b9 don't recompute sensible chart groupings while sidebar is mounted (#71563) 2026-03-30 23:39:46 +05:30
Eric Normand
94cdeffdab Fix field filter dropdowns for users without create-queries permission GHY-1605 (#71046)
## Summary

Field filter dropdowns on native query cards show as text inputs ("Enter some text") for users who have view-data permission but lack create-queries permission on the underlying table.

**Root cause:** Two issues combined to break this:

1. **Frontend couldn't determine dropdown type** — The frontend needs `has_field_values` from field metadata to decide whether to show a dropdown vs text input. This metadata came from `GET /api/database/:id`, which returns 403 for users without `create-queries` permission. Without it, the widget falls back to a text input.

2. **Backend param values endpoint was too restrictive** — Even if the frontend showed a dropdown, the card param values endpoint (`GET /api/card/:id/params/:key/values`) would fail because `search-values-from-field-id` does `api/read-check` on the Field (which requires `create-queries`), and the QP permission check also required `create-queries`.

**Fix:**

- **Hydrate `param_fields` on Card API response** (matching existing Dashboard pattern) — includes `has_field_values` so the frontend can show the correct widget type without needing `GET /api/database/:id`
- **Populate metadata store with `param_fields`** on card load in the query builder
- **Bind `*param-values-query*`** in card param values endpoints (like dashboards already do) so the QP only checks `view-data` permission
- **Skip `api/read-check` on Field** in `search-values-from-field-id` when `*param-values-query*` is true
2026-03-30 12:55:14 -05:00
da
480ef19d1f Document reverse tunneling for SSH connections (#71628)
Added section on reverse tunneling for SSH connections.
2026-03-30 15:54:09 +00:00
Sébastien
65d66d2887 No request to pulse if user canot manage subscriptions (#71575) 2026-03-30 15:36:05 +02:00
Mahatthana (Kelvin) Nomsawadi
a1c3983037 Fix flaky test (#71623)
Attempt to fix flake
2026-03-30 14:26:33 +01:00
Jeff Bruemmer
424541b425 docs - mcp and AI updates (#71601) 2026-03-30 09:04:43 -04:00
Fraser
cc486a476e Lift dashboard only visualizations (#71271)
* Extract Dashboard-only visualizations

  Moved 4 dashboard-only visualization types from visualizations/visualizations/ to dashboard/visualizations/:
  - Heading/ — imports dashboard context, selectors, parameter components
  - IFrameViz/ — imports dashboard selectors
  - LinkViz/ — imports dashboard selectors
  - DashCardPlaceholder.tsx — imports dashboard actions

  Created dashboard/visualizations/register.js — registers these 4 viz types separately from the main visualizations/register.js

  Updated app.js — calls both registerVisualizations() and registerDashboardVisualizations()

  Updated 3 external imports to use the new paths:
  - dashboard/actions/utils.ts — IFrameViz utils
  - parameters/utils/mapping-options.ts — IFrameViz utils
  - PublicOrEmbeddedDashboardView-card-filters.stories.tsx — Heading

  Fixed 1 relative import in DashCardPlaceholder.tsx to use absolute path for VisualizationProps type.

  Violation progression: 157 → 111 → 86 → 78

* List DashboardChartSettings up

* Fix test setup

* Register dashboared viz in embedding
2026-03-30 22:01:47 +13:00
2120 changed files with 599156 additions and 498050 deletions

View File

@@ -68,6 +68,7 @@
:metabase/modules {:level :warning} ; more config in `.clj-kondo/config/modules/config.edn`
:metabase/namespace-name {:level :warning}
:metabase/no-jsqlparser-imports {:level :warning}
:metabase/toucan-model-ns {:level :warning}
:metabase/require-shape-checker {:level :warning}
:metabase/tests-must-live-in-known-modules {:level :warning}
:metabase/validate-defendpoint-route-uses-kebab-case {:level :warning}
@@ -471,14 +472,15 @@
metabase.premium-features.settings/define-premium-feature clojure.core/def
metabase.pulse.dashboard-subscription-test/with-dashboard-sub-for-card clojure.core/let
metabase.pulse.send-test/with-pulse-for-card clojure.core/let
metabase.query-processor.pipeline-queries-test/pmbql-query clojure.core/->
metabase.query-processor.pipeline-queries-test/run-pmbql-query clojure.core/->
metabase.query-processor.pipeline-queries-test/mbql5-query clojure.core/->
metabase.query-processor.pipeline-queries-test/run-mbql5-query clojure.core/->
metabase.query-processor.error-type/deferror clojure.core/def
metabase.query-processor.middleware.cache.impl/with-reducible-deserialized-results clojure.core/let
metabase.query-processor.middleware.process-userland-query-test/with-query-execution! clojure.core/let
metabase.query-processor.setup/with-qp-setup clojure.core/let
metabase.search.api-test/do-test-users clojure.core/let
metabase.settings.core/define-multi-setting clojure.core/def
metabase.api-scope.core/defscope clj-kondo.lint-as/def-catch-all
metabase.settings.core/defsetting clj-kondo.lint-as/def-catch-all
metabase.settings.models.setting.multi-setting/define-multi-setting clojure.core/def
metabase.settings.models.setting/defsetting clj-kondo.lint-as/def-catch-all
@@ -589,6 +591,8 @@
metabase.lib.schema.mbql-clause/define-catn-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
metabase.lib.schema.mbql-clause/define-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
metabase.lib.schema.mbql-clause/define-tuple-mbql-clause hooks.metabase.lib.schema.mbql-clause/define-mbql-clause
methodical.core/defmethod hooks.metabase.toucan.table-name/lint-defmethod
methodical.macros/defmethod hooks.metabase.toucan.table-name/lint-defmethod
metabase.lib.test-util.macros/$ids hooks.metabase.test.data/$ids
metabase.lib.test-util.macros/mbql-query hooks.metabase.test.data/mbql-query
metabase.lib.test-util.macros/mbql-5-query hooks.metabase.test.data/mbql-query

View File

@@ -156,7 +156,6 @@
agent-api
{:team "Metabot"
:api #{metabase.agent-api.api}
:friends #{enterprise/agent-api}
:uses #{api
auth-identity
lib
@@ -216,8 +215,7 @@
analyze
{:team "Graphy"
:api #{metabase.analyze.core
metabase.analyze.query-results}
:api #{metabase.analyze.core}
:uses #{config
driver
lib
@@ -242,7 +240,8 @@
metabase.api.settings
metabase.api.util
metabase.api.util.handlers}
:uses #{config
:uses #{api-scope
config
events
models
settings
@@ -290,6 +289,11 @@
metabase.api-routes.core}
:uses :any}
api-scope
{:team "Metabot"
:api #{metabase.api-scope.core
metabase.api-scope.init}}
app-db
{:team "UX West"
:api #{metabase.app-db.cluster-lock ; TODO FIXME
@@ -999,10 +1003,10 @@
internal-stats
{:team "UX West"
:api #{metabase.internal-stats.core
metabase.internal-stats.metabot}
:api #{metabase.internal-stats.core}
:uses #{app-db
models}
models
util}
:model-imports #{:model/Card
:model/Dashboard
:model/MetabotMessage
@@ -1095,8 +1099,7 @@
llm
{:team "Metabot"
:api #{metabase.llm.anthropic
metabase.llm.api
:api #{metabase.llm.api
metabase.llm.init
metabase.llm.settings}
:uses #{analytics
@@ -1104,6 +1107,7 @@
driver
lib
lib-be
metabot
models
request
settings
@@ -1140,13 +1144,17 @@
mcp
{:team "Metabot"
:api #{metabase.mcp.api}
:api #{metabase.mcp.api
metabase.mcp.core
metabase.mcp.init}
:uses #{agent-api
api
api-scope
config
oauth-server
request
server
settings
system
util}}
@@ -1170,14 +1178,17 @@
{:team "Metabot"
:api #{metabase.metabot.api
metabase.metabot.api.entity-analysis
metabase.metabot.init}
metabase.metabot.core
metabase.metabot.init
metabase.metabot.self
metabase.metabot.settings}
:friends #{agent-api
enterprise/metabot
slackbot}
:uses #{activity-feed
analytics
analyze
api
api-scope
app-db
audit-app
channel
@@ -1185,7 +1196,7 @@
config
documents
driver
internal-stats
legacy-mbql
lib
lib-be
llm
@@ -1211,7 +1222,7 @@
transforms-base
util
warehouses}
:model-exports #{:model/MetabotMessage}
:model-exports #{:model/AiUsageLog :model/MetabotMessage}
:model-imports #{:model/Card
:model/Collection
:model/Dashboard
@@ -1235,11 +1246,16 @@
lib-metric
models
parameters
permissions
premium-features
query-processor
request
server
util}
:model-imports #{:model/Card :model/Field :model/Measure}}
:model-imports #{:model/Card
:model/Field
:model/Measure
:model/Table}}
model-persistence
{:team "Graphy"
@@ -1273,33 +1289,15 @@
metabase.models.interface
metabase.models.resolution
metabase.models.serialization
metabase.models.transforms.transform
metabase.models.util.spec-update
metabase.models.visualization-settings}
:uses #{api
app-db
classloader
collections
events
legacy-mbql
lib
lib-be
permissions
query-processor
remote-sync
search
settings
transforms
transforms-base
util
warehouse-schema}
:friends #{transforms}
:model-exports #{:model/Transform
:model/TransformJob
:model/TransformJobTransformTag
:model/TransformRun
:model/TransformTag
:model/TransformTransformTag}
util}
:model-imports #{:model/Card
:model/Collection
:model/Dashboard
@@ -1376,8 +1374,11 @@
:uses #{api
appearance
models
request
settings
system
task
tracing
util}}
;; TODO -- too many API namespaces
@@ -1444,7 +1445,6 @@
{:team "UX West"
:api #{metabase.permissions-rest.api}
:uses #{api
app-db
audit-app
events
permissions
@@ -1538,7 +1538,10 @@
request
tiles
util}
:model-imports #{:model/Card :model/Dashboard :model/Document}}
:model-imports #{:model/Card
:model/Dashboard
:model/DashboardCard
:model/Document}}
pulse
{:team "Gadget"
@@ -1629,6 +1632,7 @@
:model/Field
:model/ModerationReview
:model/NativeQuerySnippet
:model/Notification
:model/Revision
:model/Table
:model/User}}
@@ -1766,6 +1770,7 @@
config
embedding
permissions ; FIXME
premium-features
session
settings
users
@@ -1887,6 +1892,7 @@
embedding
initialization-status
lib-be
mcp
oauth-server
premium-features
query-processor
@@ -1898,7 +1904,8 @@
tracing
users
util
enterprise/sso}}
enterprise/sso}
:model-imports #{:model/Session}}
session
{:team "UX West"
@@ -2000,7 +2007,8 @@
source-swap
{:team "Graphy"
:api #{metabase.source-swap.core
metabase.source-swap.schema}
metabase.source-swap.schema
metabase.source-swap.util}
:uses #{driver
lib
sql-tools
@@ -2141,7 +2149,7 @@
premium-features
search
util}
:model-imports #{:model/Card :model/Dashboard}}
:model-imports #{:model/AiUsageLog :model/Card :model/Dashboard}}
tiles
{:team "UX West"
@@ -2187,32 +2195,41 @@
metabase.transforms.util}
:uses #{analytics
api
app-db
channel
collections
database-routing
driver
events
lib
lib-be
models
permissions
premium-features
queries
query-processor
remote-sync
revisions
search
settings
task
task-history
tracing
transforms-base
util}
util
warehouse-schema}
:friends #{models
enterprise/transforms-python
enterprise/workspaces}
:model-imports #{:model/Database
:model/Field
:model/Table
:model/Transform
:model-exports #{:model/Transform
:model/TransformJob
:model/TransformJobTransformTag
:model/TransformTransformTag
:model/TransformRun
:model/TransformTag}
:model-imports #{:model/Collection
:model/Database
:model/Field
:model/Table
:model/User}}
transforms-base
@@ -2220,19 +2237,22 @@
:api #{metabase.transforms-base.init
metabase.transforms-base.interface
metabase.transforms-base.util}
;; TODO regressions — transforms-base should not depend on :model/Transform or
;; :model/TransformRun directly. Remove once the module is agnostic to global
;; versus workspace transforms. See https://linear.app/metabase/issue/BOT-1143
:model-imports #{:model/Database
:model/DatabaseRouter
:model/Field
:model/Table
;; TODO regressions — transforms-base should not depend on
;; these directly. Remove once the module is agnostic to
;; global versus workspace transforms.
:model/Transform
:model/TransformRun}
:uses #{driver
events
lib
lib-be
models
query-processor
sql-parsing
sync
util
warehouse-schema}
@@ -2621,13 +2641,6 @@
:model/Sandbox
:model/Table}}
enterprise/agent-api
{:team "Metabot"
:api #{metabase-enterprise.agent-api.workspace}
:uses #{api
util
enterprise/workspaces}}
enterprise/analytics
{:team "UX West"
:api #{}
@@ -2817,7 +2830,7 @@
warehouse-schema
warehouses}
:model-exports #{:model/DatabaseRouter}
:model-imports #{:model/Database}}
:model-imports #{:model/Database :model/Transform}}
enterprise/dependencies
{:team "Graphy"
@@ -2944,24 +2957,30 @@
premium-features}
:model-imports #{:model/Card :model/Collection :model/Table}}
enterprise/metabot
{:team "Metabot"
:api #{}
:friends #{enterprise/agent-api
slackbot
:api #{metabase-enterprise.metabot.api
metabase-enterprise.metabot.api.routes
metabase-enterprise.metabot.init}
:friends #{slackbot
mcp
metabot}
:uses #{api
lib-be
metabot
models
permissions
premium-features
settings
task
util
enterprise/dependencies
enterprise/transforms-python}
:model-imports #{:model/Card :model/Transform}}
:model-imports #{:model/AiUsageLog
:model/Card
:model/PermissionsGroup
:model/PermissionsGroupMembership
:model/Transform}}
enterprise/permission-debug
{:team "UX West"
@@ -3029,6 +3048,7 @@
task
transforms
util
warehouse-schema
enterprise/dependencies}
:model-imports #{:model/Card
:model/Dashboard
@@ -3133,6 +3153,7 @@
collections
config
events
lib
logger
models
plugins
@@ -3286,8 +3307,8 @@
:api #{metabase-enterprise.workspaces.api
metabase-enterprise.workspaces.init}
:model-exports #{:model/Workspace :model/WorkspaceMergeTransform}
:friends #{enterprise/agent-api}
:uses #{api
api-scope
app-db
config
database-routing
@@ -3303,7 +3324,6 @@
request
settings
sql-tools
system
transforms
transforms-base
util

View File

@@ -0,0 +1,43 @@
(ns hooks.metabase.toucan.table-name
"Lint that toucan2 model namespaces (those defining `t2/table-name`) live
under `metabase[-enterprise]/<module>/models[/…]`.
This wraps the methodical `defmethod` hook: we delegate to
`hooks.methodical.macros/defmethod` for the actual analysis (so arg bindings
etc. still work), and additionally check the file location when the
multimethod being defined is `t2/table-name`."
(:require
[clj-kondo.hooks-api :as hooks]))
(defn- models-file? [filename]
(boolean
(and filename
(re-find #"/(?:metabase|metabase_enterprise)/[^/]+/models(?:/|\.clj[cs]?$)"
filename))))
(defn- table-name-dispatch? [sym]
(and (symbol? sym)
(= (name sym) "table-name")
(#{"t2" "toucan2.core"} (namespace sym))))
(defn lint-defmethod
"Hook registered at the project level on `methodical.core/defmethod` (and
`methodical.macros/defmethod`). Performs the table-name check, then
delegates to the vendored methodical hook so kondo's normal analysis of the
defmethod body still runs."
[{:keys [filename node] :as input}]
(let [[_defmethod multimethod & _args] (:children node)
dispatch (when multimethod
(try (hooks/sexpr multimethod) (catch Exception _ nil)))]
(when (and (table-name-dispatch? dispatch)
(not (models-file? filename)))
(hooks/reg-finding!
(assoc (meta multimethod)
:message (format "Toucan2 `table-name` defmethods must live under `<module>/models[/…]` (got %s)"
filename)
:type :metabase/toucan-model-ns))))
;; Delegate to the vendored methodical defmethod hook so kondo's normal
;; analysis of the defmethod body still runs. Resolved lazily because
;; `hooks.methodical.macros` isn't on the classpath during `.clj-kondo/test`
;; unit-test runs.
((requiring-resolve 'hooks.methodical.macros/defmethod) input))

View File

@@ -0,0 +1,46 @@
(ns hooks.metabase.toucan.table-name-test
(:require
[clj-kondo.hooks-api :as hooks]
[clj-kondo.impl.utils]
[clojure.test :refer :all]
[hooks.metabase.toucan.table-name :as toucan.table-name]))
;; `hooks.methodical.macros` isn't on the classpath under `.clj-kondo/test`,
;; so stub it out — `lint-defmethod` delegates to it via `requiring-resolve`.
;; If it ever becomes available, warn so we remember to delete this stub.
(if (find-ns 'hooks.methodical.macros)
(println (str "WARNING: hooks.methodical.macros is now on the test classpath; "
"the stub in " *ns* " is no longer needed and can be removed."))
(do (create-ns 'hooks.methodical.macros)
(intern 'hooks.methodical.macros 'defmethod (fn [input] input))))
(defn- lint-defmethod [form filename]
(binding [clj-kondo.impl.utils/*ctx* {:config {:linters {:metabase/toucan-model-ns {:level :warning}}}
:ignores (atom nil)
:findings (atom [])
:namespaces (atom {})}]
(toucan.table-name/lint-defmethod {:node (hooks/parse-string (pr-str form))
:filename filename})
@(:findings clj-kondo.impl.utils/*ctx*)))
(deftest ^:parallel table-name-must-live-in-models-namespace-test
(testing "defmethod t2/table-name outside a models namespace is flagged"
(is (=? [{:type :metabase/toucan-model-ns}]
(lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/core.clj"))))
(testing "defmethod t2/table-name inside <module>/models.clj is allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/models.clj"))))
(testing "defmethod t2/table-name inside <module>/models/<model>.clj is allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/models/bar.clj"))))
(testing "enterprise modules are also allowed"
(is (empty? (lint-defmethod '(defmethod t2/table-name :model/Foo [_] :foo)
"/repo/enterprise/backend/src/metabase_enterprise/foo/models.clj"))))
(testing "toucan2.core/table-name is also recognized"
(is (=? [{:type :metabase/toucan-model-ns}]
(lint-defmethod '(defmethod toucan2.core/table-name :model/Foo [_] :foo)
"/repo/src/metabase/foo/core.clj"))))
(testing "unrelated defmethods are ignored"
(is (empty? (lint-defmethod '(defmethod t2/primary-keys :model/Foo [_] [:id])
"/repo/src/metabase/foo/core.clj")))))

View File

@@ -188,8 +188,8 @@
metabase.query-processor.error-type/deferror [[:block 1]]
metabase.query-processor.middleware.cache-backend.interface/with-cached-results [[:block 4]]
metabase.query-processor.middleware.process-userland-query-test/with-query-execution [[:block 1]]
metabase.query-processor.pipeline-queries-test/pmbql-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/run-pmbql-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/mbql5-query [[:block 1]]
metabase.query-processor.pipeline-queries-test/run-mbql5-query [[:block 1]]
metabase.query-processor.store/cached [[:block 1]]
metabase.query-processor.store/with-metadata-provider [[:inner 0]]
metabase.query-processor.streaming/streaming-response [[:block 1]]

10
.config/wt.toml Normal file
View File

@@ -0,0 +1,10 @@
# worktrunk project config — https://worktrunk.dev
# Hooks run when switching, creating, merging, or removing worktrees.
# Setup tasks for newly-created worktrees. Run once, blocking, in the
# new worktree's cwd before post-start background tasks kick off.
[pre-start]
# Trust mise config so it loads the project's tool versions without prompting.
trust-mise = "mise trust"
# Install JS deps so husky git hooks get wired up in the new worktree.
bun-install = "bun install"

View File

@@ -7,9 +7,7 @@ inputs:
output-name:
required: true
default: test
aws-access-key-id:
required: true
aws-secret-access-key:
role-to-assume:
required: true
aws-region:
required: true
@@ -30,8 +28,7 @@ runs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ inputs.aws-access-key-id }}
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
role-to-assume: ${{ inputs.role-to-assume }}
aws-region: ${{ inputs.aws-region }}
- name: zip test results
env:
@@ -50,13 +47,18 @@ runs:
aws s3 cp ${OUTPUT_FILE}.zip s3://$BUCKET/$DATE/$GITHUB_RUN_ID/$GITHUB_RUN_ATTEMPT/
- name: Upload results to Trunk
if: always() # Important! This step must always run because it controls job's exit code.
# Important!
# 1. This step must always run because it controls job's exit code.
# 2. There's no point in running this outside the organization
if: always() && github.repository_owner == 'metabase'
uses: trunk-io/analytics-uploader@main
env:
TRUNK_REPO_URL: git@github.com:metabase/metabase.git
with:
junit-paths: ${{ inputs.input-path }}
variant: ${{ inputs.variant || inputs.output-name }}
org-slug: metabase
dry-run: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == true }} # quarantine, but do not upload results
dry-run: ${{ (github.event_name == 'pull_request' && github.event.pull_request.draft == true) || github.repository != 'metabase/metabase' }} # quarantine, but do not upload results
token: ${{ inputs.trunk-api-token }}
use-cache: true
previous-step-outcome: ${{ inputs.previous-step-outcome }}

View File

@@ -200,6 +200,10 @@ cljs:
- ".github/workflows/cljs.yml"
- "shadow-cljs.edn"
serialization_baseline:
- ".github/workflows/serialization.yml"
- "test_resources/serialization_baseline/**"
openapi:
- ".github/workflows/openapi.yml"
- "src/metabase/api/**"

6
.github/team.json vendored
View File

@@ -7,7 +7,8 @@
"bshepherdson",
"metamben",
"rileythomp",
"camsaul"
"camsaul",
"technomancy"
]
},
{
@@ -106,7 +107,8 @@
"kamilmielnik",
"stasgavrylov",
"uladzimirdev",
"egorgrushin"
"egorgrushin",
"metatyb"
]
},
{

View File

@@ -120,8 +120,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -244,8 +243,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -355,8 +353,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}

View File

@@ -8,6 +8,10 @@ on:
required: true
type: string
permissions:
id-token: write
contents: read
jobs:
deploy:
name: Publish Athena JDBC Driver
@@ -18,21 +22,24 @@ jobs:
uses: actions/checkout@v4
- name: Set up Babashka
uses: turtlequeue/setup-babashka@v1.7.0
uses: turtlequeue/setup-babashka@2d4df498c7a578b4f3e906283f9af913590bdec7 # v1.7.0
with:
babashka-version: 1.12.197
- name: Set up AWS CLI
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_MAVEN_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_MAVEN_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_MAVEN_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Deploy Athena JDBC driver
run: ./bin/athena.bb ${{ github.event.inputs.version }} --deploy
run: ./bin/athena.bb "${VERSION}" --deploy
env:
VERSION: ${{ github.event.inputs.version }}
- name: Verify deployment
env:
VERSION: ${{ github.event.inputs.version }}
run: |
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc/${{ github.event.inputs.version }}/
echo "Athena JDBC driver version ${{ github.event.inputs.version }} has been deployed"
aws s3 ls s3://metabase-maven-downloads/com/metabase/athena-jdbc/"${VERSION}"/
echo "Athena JDBC driver version ${VERSION} has been deployed"

View File

@@ -10,7 +10,7 @@ jobs:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: tibdex/github-app-token@v2.1.0
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
id: generate-token
with:
app_id: ${{ secrets.METABASE_BOT_APP_ID }}

View File

@@ -57,7 +57,7 @@ jobs:
- name: Upload coverage to codecov.io
if: always()
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./target/coverage/codecov.json
@@ -104,7 +104,7 @@ jobs:
- name: Upload coverage to codecov.io
if: always()
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./target/coverage/codecov.json

View File

@@ -138,14 +138,13 @@ jobs:
input-path: ./target/junit/
output-name: be-tests-java-${{ matrix.java-version }}-${{ matrix.job.edition }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-java-tests.outcome }}
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@3eeb9fc888e82e8be2fb356bbeec2750231672bc # v1
if: failure()
with:
path: "target/junit/**/*_test.xml"

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
- uses: jesusvasquez333/verify-pr-label-action@657d111bbbe13e22bbd55870f1813c699bde1401 # v1.4.0
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
valid-labels: 'no-backport, backport, double-backport, triple-backport'

View File

@@ -39,6 +39,7 @@ concurrency:
jobs:
get-cache-keys:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- uses: actions/checkout@v4

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -43,7 +43,7 @@ jobs:
- name: Compile CLJS
run: NODE_ENV=development bun run build-pure:cljs
- name: Publish to Chromatic
uses: chromaui/action@v10.2.0
uses: chromaui/action@c03d144dc72e609acbb960489fdd1e1a4a23b34f # v10.2.0
env:
PUBLISH_CHROMATIC: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
STORYBOOK_BUILD_TIMEOUT: 900000

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -8,6 +8,7 @@ on:
jobs:
codespell:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
permissions:
@@ -67,7 +68,7 @@ jobs:
- name: Fix spelling errors with Claude Code
if: steps.codespell_check.outcome == 'failure'
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@3ac52d0da9f8ec9ca7b4dc23bb477e36ef9c77a9 # v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.claude-app-token.outputs.token }}
@@ -82,7 +83,7 @@ jobs:
- name: Create Pull Request
if: steps.codespell_check.outcome == 'failure'
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: claude-fix/codespell-${{ steps.date.outputs.date }}

View File

@@ -84,19 +84,19 @@ jobs:
- name: Move the Uberjar to the context dir
run: mv ./metabase.jar bin/docker/.
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: Set up Docker Buildx
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Build multi-arch container
if: contains(steps.platforms.outputs.result, 'arm')
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: bin/docker/.
platforms: ${{ steps.platforms.outputs.result }}
@@ -122,7 +122,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}
@@ -142,7 +142,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_USERNAME || secrets.DOCKERHUB_USERNAME }}
password: ${{ inputs.release == 'true' && secrets.DOCKERHUB_RELEASE_TOKEN || secrets.DOCKERHUB_TOKEN }}

View File

@@ -106,7 +106,7 @@ jobs:
edition: ee
- name: Post PR comment
uses: marocchino/sticky-pull-request-comment@v2
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
with:
header: docker-build-status
message: |

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -7,6 +7,7 @@ on:
jobs:
fe-test-coverage:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 60
steps:
@@ -22,7 +23,7 @@ jobs:
--coverage \
--testTimeout=60000
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5
with:
files: ./coverage/lcov.info
flags: front-end

View File

@@ -25,7 +25,7 @@ on:
jobs:
# Generate matrix for scheduled/PR runs
generate-matrix:
if: github.event_name != 'workflow_dispatch'
if: github.event_name != 'workflow_dispatch' && github.repository == 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
@@ -47,7 +47,7 @@ jobs:
# Matrix-based tests for scheduled/PR runs
test-matrix:
if: github.event_name != 'workflow_dispatch'
if: github.event_name != 'workflow_dispatch' && github.repository == 'metabase/metabase'
needs: generate-matrix
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
@@ -102,7 +102,11 @@ jobs:
# Notify Slack on nightly failures
notify-on-failure:
if: always() && github.event_name == 'schedule' && needs.test-matrix.result == 'failure'
if: >-
always()
&& github.event_name == 'schedule'
&& github.repository == 'metabase/metabase'
&& needs.test-matrix.result == 'failure'
needs: [test-matrix]
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5

View File

@@ -7,6 +7,7 @@ on:
jobs:
notify-disabled-drivers:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:

View File

@@ -27,7 +27,7 @@ jobs:
- name: Run link checker
id: link-check
uses: lycheeverse/lychee-action@v2.3.0
uses: lycheeverse/lychee-action@f613c4a64e50d792e0b31ec34bbcbba12263c6a6 # v2.3.0
with:
fail: true
args: docs --config ./.lychee/config.toml --offline

View File

@@ -7,6 +7,7 @@ on:
jobs:
update-master-docs:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Call documentation update for master
uses: ./.github/workflows/docs-generate-base.yml
with:

View File

@@ -13,6 +13,7 @@ on:
jobs:
update-release-docs:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Call documentation update for release branch
uses: ./.github/workflows/docs-generate-base.yml
with:

View File

@@ -707,7 +707,7 @@ jobs:
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC

View File

@@ -72,8 +72,8 @@ jobs:
# All decision logic is in mage - it analyzes module dependencies,
# branch context, labels, and file changes to decide what runs.
MAGE_ARGS=(
--git-ref="${{ github.event.pull_request.base.ref || github.ref_name }}"
--is-master-or-release="${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') }}"
--git-ref="${BASE_REF}"
--is-master-or-release="${IS_MASTER_OR_RELEASE}"
--pr-labels="${{ join(github.event.pull_request.labels.*.name, ',') }}"
--skip="${{ inputs.skip }}"
)
@@ -87,6 +87,9 @@ jobs:
echo "Run './bin/mage -driver-decisions -h' locally for the full list of drivers and labels."
# Second call: output only key=value lines for GITHUB_OUTPUT
./bin/mage -driver-decisions "${MAGE_ARGS[@]}" --github-output-only >> $GITHUB_OUTPUT
env:
BASE_REF: ${{ github.event.pull_request.base.ref || github.ref_name }}
IS_MASTER_OR_RELEASE: ${{ github.ref_name == 'master' || startsWith(github.ref_name, 'release-') }}
shell: bash
be-tests-athena-ee:
@@ -134,8 +137,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -200,8 +202,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -258,7 +259,7 @@ jobs:
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
- name: Start ClickHouse in Docker
uses: hoverkraft-tech/compose-action@v2.0.0
uses: hoverkraft-tech/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
with:
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
down-flags: "--volumes"
@@ -283,8 +284,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -338,8 +338,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -387,8 +386,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -436,8 +434,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -486,8 +483,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -517,8 +513,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -549,8 +544,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -680,8 +674,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -761,8 +754,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -844,8 +836,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -890,8 +881,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -976,8 +966,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1081,8 +1070,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1169,8 +1157,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1254,8 +1241,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1335,8 +1321,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1388,8 +1373,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1430,8 +1414,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1507,8 +1490,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -1553,8 +1535,7 @@ jobs:
# input-path: ./target/junit/
# output-name: ${{ github.job }}
# bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
# aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
# role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
# aws-region: ${{ vars.AWS_REGION }}
# trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
@@ -1568,7 +1549,7 @@ jobs:
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.CI_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -1634,8 +1615,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}

View File

@@ -151,10 +151,12 @@ jobs:
GREP: ${{ github.event.inputs.grep }}
CYPRESS_BROWSER: ${{ steps.cypress-prep.outputs.chrome-path }}
FAIL_FAST: ${{ inputs.fail_fast }}
SPEC: ${{ github.event.inputs.spec }}
BURN_IN: ${{ github.event.inputs.burn_in }}
run: |
node e2e/runner/run_cypress_ci.js ${{ needs.determine-test-type.outputs.type }} \
--spec '${{ github.event.inputs.spec }}' \
--env burn=${{ github.event.inputs.burn_in }} \
--spec "${SPEC}" \
--env burn="${BURN_IN}" \
--config-file e2e/support/cypress-stress-test.config.js
- name: Upload Cypress Artifacts upon failure

View File

@@ -166,8 +166,7 @@ jobs:
# Related to our custom upload to S3
output-name: e2e-${{ inputs.name }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
# Related to Trunk uploader
variant: e2e-tests

View File

@@ -94,8 +94,7 @@ jobs:
input-path: ./target/junit
output-name: e2e-embedding-sdk-react-${{ matrix.react-version }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}
@@ -248,8 +247,7 @@ jobs:
input-path: ./target/junit
output-name: e2e-embedding-sdk
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}

View File

@@ -106,6 +106,8 @@ jobs:
SAMPLE_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -114,7 +116,7 @@ jobs:
# We have to use the latest version that does not have this issue https://github.com/docker/compose/pull/12752
- name: Set up Docker Compose
uses: docker/setup-compose-action@v1
uses: docker/setup-compose-action@2fe291b7677a45ee1269ec56a42604c143505e7e # v1
with:
version: latest
@@ -213,6 +215,8 @@ jobs:
SAMPLE_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -277,6 +281,7 @@ jobs:
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4
@@ -362,6 +367,8 @@ jobs:
HOST_APP_ENVIRONMENT: ${{ matrix.environment }}
permissions:
id-token: write
contents: read
actions: read
steps:
- uses: actions/checkout@v4

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -33,7 +33,7 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: jesusvasquez333/verify-pr-label-action@v1.4.0
- uses: jesusvasquez333/verify-pr-label-action@657d111bbbe13e22bbd55870f1813c699bde1401 # v1.4.0
id: verify-release-notes-labels
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'

View File

@@ -18,7 +18,7 @@ jobs:
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send escalated issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.23.0
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844 # v1.23.0
with:
payload: |
{

View File

@@ -47,11 +47,16 @@ jobs:
run: bun run build:cljs
- name: Stress-test ${{ github.event.inputs.spec }} ${{ github.event.inputs.burn_in }} times
run: |
for i in {1..${{ github.event.inputs.burn_in }}}; do
for ((i=1; i<=BURN_IN; i++)); do
bun run jest --runInBand --ci --silent --no-cache \
--testPathPatterns '${{ github.event.inputs.spec }}' \
--testNamePattern '${{ github.event.inputs.grep }}'
--testPathPatterns "${SPEC}" \
--testNamePattern "${GREP}"
if [[ "$?" != 0 ]]; then
echo "Failed after $i attempts" && break;
fi
done
env:
BURN_IN: ${{ github.event.inputs.burn_in }}
SPEC: ${{ github.event.inputs.spec }}
GREP: ${{ github.event.inputs.grep }}

View File

@@ -103,8 +103,7 @@ jobs:
input-path: ./target/junit
output-name: frontend-unit
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.fe-tests.outcome }}

View File

@@ -47,7 +47,7 @@ jobs:
run: clojure -X:dev:ci:test:ee:ee-dev :only '${{ inputs.test }}'
- name: Publish Test Report (JUnit)
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@3eeb9fc888e82e8be2fb356bbeec2750231672bc # v1
if: failure()
with:
path: "target/junit/**/*_test.xml"

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -7,6 +7,7 @@ on:
jobs:
js-stats:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -19,7 +19,7 @@ jobs:
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send the issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.26.0
uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0
with:
payload: |
{

View File

@@ -6,10 +6,12 @@ on:
- 'master'
- 'release-**'
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
lint-migrations:
if: github.event.action != 'labeled' && github.event.action != 'unlabeled'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
steps:
@@ -18,3 +20,19 @@ jobs:
uses: ./.github/actions/prepare-backend
- name: Verify Liquibase Migrations
run: ./bin/mage lint-migrations
check-migration-versions:
if: github.event_name == 'pull_request'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
filter: blob:none
fetch-depth: 0
- name: Check migration versions
env:
BASE_REF: ${{ github.base_ref }}
CURRENT_VERSION: ${{ vars.CURRENT_VERSION }}
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
run: bin/check-migration-versions

View File

@@ -8,6 +8,12 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
permissions:
id-token: write
contents: read
checks: write
actions: read
jobs:
# Only run if the PR has the minimal-tests label
check-label:
@@ -70,8 +76,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -118,8 +123,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -151,8 +155,7 @@ jobs:
input-path: ./target/junit
output-name: frontend-unit
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.run-tests.outcome }}

View File

@@ -7,11 +7,12 @@ on:
jobs:
module-boundaries-stats:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
- name: Install dependencies
run: bun install --frozen-lockfile
- name: get module boundary stats

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -17,13 +17,13 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2
with:
registries: "${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}"
- name: Retrieve uberjar artifact for ee
@@ -38,7 +38,7 @@ jobs:
jar xf target/uberjar/metabase.jar
mv target/uberjar/metabase.jar bin/docker/metabase.jar
- name: Build container
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: bin/docker/
platforms: linux/amd64
@@ -56,7 +56,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Tailscale
uses: tailscale/github-action@v2
uses: tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2
with:
oauth-client-id: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_SECRET }}
@@ -67,12 +67,14 @@ jobs:
id: create-oidc-token
shell: bash
run: |
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${{ secrets.PR_ENV_K8S_AUDIENCE }}"
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${PR_ENV_K8S_AUDIENCE}"
IDTOKEN=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" -H "Accept: application/json; api-version=2.0" "$OIDC_URL_WITH_AUDIENCE" | jq -r .value)
echo "::add-mask::${IDTOKEN}"
echo "idToken=${IDTOKEN}" >>$GITHUB_OUTPUT
env:
PR_ENV_K8S_AUDIENCE: ${{ secrets.PR_ENV_K8S_AUDIENCE }}
- name: Setup Kube Context
uses: azure/k8s-set-context@v2
uses: azure/k8s-set-context@0b53301dcc7ddd3f6d35f97fb67228ac4e5ab2fd # v2
with:
method: kubeconfig
kubeconfig: |
@@ -95,7 +97,7 @@ jobs:
namespace: default
user: oidc-token
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -108,7 +110,7 @@ jobs:
id: split
run: echo "::set-output name=fragment::${SHA:5}"
- name: Render Deployment YAML
uses: nowactions/envsubst@v1
uses: nowactions/envsubst@c4b8e3977354d294659a7ec164279ce8fba5c2e1 # v1
with:
input: ./perf-test-pr.yml.tmpl
output: ./perf-test-pr.yml

View File

@@ -28,7 +28,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Tailscale
uses: tailscale/github-action@v2
uses: tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2
with:
oauth-client-id: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_SECRET }}
@@ -39,12 +39,14 @@ jobs:
id: create-oidc-token
shell: bash
run: |
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${{ secrets.PR_ENV_K8S_AUDIENCE }}"
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${PR_ENV_K8S_AUDIENCE}"
IDTOKEN=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" -H "Accept: application/json; api-version=2.0" "$OIDC_URL_WITH_AUDIENCE" | jq -r .value)
echo "::add-mask::${IDTOKEN}"
echo "idToken=${IDTOKEN}" >>$GITHUB_OUTPUT
env:
PR_ENV_K8S_AUDIENCE: ${{ secrets.PR_ENV_K8S_AUDIENCE }}
- name: Setup Kube Context
uses: azure/k8s-set-context@v2
uses: azure/k8s-set-context@0b53301dcc7ddd3f6d35f97fb67228ac4e5ab2fd # v2
with:
method: kubeconfig
kubeconfig: |
@@ -73,7 +75,7 @@ jobs:
kubectl delete ns hosting-pr${{ env.PR_NUMBER }}
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
with:
version: 'latest'
- name: Destroy PR Review ENV (self-hosting)
@@ -87,9 +89,15 @@ jobs:
sudo apt-get install -y postgresql-client
- name: Drop app database if exists
run: |
PGPASSWORD='${{ secrets.PR_ENV_DB_PASSWORD }}' \
PGPASSWORD="${PR_ENV_DB_PASSWORD}" \
psql \
-h ${{ secrets.PR_ENV_DB_HOST }} \
-U ${{ secrets.PR_ENV_DB_USER }} \
-d ${{ secrets.PR_ENV_DB_NAME }} \
-h "${PR_ENV_DB_HOST}" \
-U "${PR_ENV_DB_USER}" \
-d "${PR_ENV_DB_NAME}" \
-c "DROP DATABASE IF EXISTS hosting_pr${{ env.PR_NUMBER }};"
env:
PR_ENV_DB_PASSWORD: ${{ secrets.PR_ENV_DB_PASSWORD }}
PR_ENV_DB_HOST: ${{ secrets.PR_ENV_DB_HOST }}
PR_ENV_DB_USER: ${{ secrets.PR_ENV_DB_USER }}
PR_ENV_DB_NAME: ${{ secrets.PR_ENV_DB_NAME }}

View File

@@ -28,13 +28,13 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
aws-region: us-east-1
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2
with:
registries: "${{ secrets.PR_ENV_AWS_ACCOUNT_ID }}"
- name: Retrieve uberjar artifact for ee
@@ -49,7 +49,7 @@ jobs:
jar xf target/uberjar/metabase.jar
mv target/uberjar/metabase.jar bin/docker/metabase.jar
- name: Build container
uses: docker/build-push-action@v6
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: bin/docker/
platforms: linux/amd64
@@ -67,7 +67,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- name: Tailscale
uses: tailscale/github-action@v2
uses: tailscale/github-action@4e4c49acaa9818630ce0bd7a564372c17e33fb4d # v2
with:
oauth-client-id: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.PR_ENV_TAILSCALE_OAUTH_SECRET }}
@@ -78,12 +78,14 @@ jobs:
id: create-oidc-token
shell: bash
run: |
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${{ secrets.PR_ENV_K8S_AUDIENCE }}"
export OIDC_URL_WITH_AUDIENCE="$ACTIONS_ID_TOKEN_REQUEST_URL&audience=${PR_ENV_K8S_AUDIENCE}"
IDTOKEN=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" -H "Accept: application/json; api-version=2.0" "$OIDC_URL_WITH_AUDIENCE" | jq -r .value)
echo "::add-mask::${IDTOKEN}"
echo "idToken=${IDTOKEN}" >>$GITHUB_OUTPUT
env:
PR_ENV_K8S_AUDIENCE: ${{ secrets.PR_ENV_K8S_AUDIENCE }}
- name: Setup Kube Context
uses: azure/k8s-set-context@v2
uses: azure/k8s-set-context@0b53301dcc7ddd3f6d35f97fb67228ac4e5ab2fd # v2
with:
method: kubeconfig
kubeconfig: |
@@ -106,7 +108,7 @@ jobs:
namespace: default
user: oidc-token
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
role-to-assume: ${{ secrets.PR_ENV_IAM_ROLE }}
role-session-name: GitHub_to_AWS_via_FederatedOIDC
@@ -117,19 +119,24 @@ jobs:
sudo apt-get install -y postgresql-client
- name: Create app database if not exists
run: |
export PGPASSWORD='${{ secrets.PR_ENV_DB_PASSWORD }}'
export PGUSER=${{ secrets.PR_ENV_DB_USER }}
export PGHOST=${{ secrets.PR_ENV_DB_HOST }}
export PGDATABASE=${{ secrets.PR_ENV_DB_NAME }}
export PGPASSWORD="${PR_ENV_DB_PASSWORD}"
export PGUSER="${PR_ENV_DB_USER}"
export PGHOST="${PR_ENV_DB_HOST}"
export PGDATABASE="${PR_ENV_DB_NAME}"
psql -tc "SELECT 1 FROM pg_database WHERE datname = 'hosting_pr${{ github.event.number }}'" \
| grep -q 1 \
|| psql -c "CREATE DATABASE hosting_pr${{ github.event.number }}"
env:
PR_ENV_DB_PASSWORD: ${{ secrets.PR_ENV_DB_PASSWORD }}
PR_ENV_DB_USER: ${{ secrets.PR_ENV_DB_USER }}
PR_ENV_DB_HOST: ${{ secrets.PR_ENV_DB_HOST }}
PR_ENV_DB_NAME: ${{ secrets.PR_ENV_DB_NAME }}
- name: Download Deployment YAML template
if: ${{ !inputs.self_hosting }}
run: aws s3 cp s3://metabase-pr-env/metabase.yml.tmpl ./metabase.yml.tmpl
- name: Render Deployment YAML
if: ${{ !inputs.self_hosting }}
uses: nowactions/envsubst@v1
uses: nowactions/envsubst@c4b8e3977354d294659a7ec164279ce8fba5c2e1 # v1
with:
input: ./metabase.yml.tmpl
output: ./metabase.yml
@@ -148,7 +155,7 @@ jobs:
run: aws s3 cp s3://metabase-pr-env/values.yaml.tmpl ./values.yaml.tmpl
- name: Render Helm values (self-hosting)
if: ${{ inputs.self_hosting }}
uses: nowactions/envsubst@v1
uses: nowactions/envsubst@c4b8e3977354d294659a7ec164279ce8fba5c2e1 # v1
with:
input: ./values.yaml.tmpl
output: ./values.yaml
@@ -168,15 +175,18 @@ jobs:
- name: Setup Helm
if: ${{ inputs.self_hosting }}
uses: azure/setup-helm@v4
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
with:
version: 'latest'
- name: Login to Quay.io
if: ${{ inputs.self_hosting }}
run: |
helm registry login quay.io \
--username ${{ secrets.QUAY_USERNAME }} \
--password ${{ secrets.QUAY_PASSWORD }}
--username "${QUAY_USERNAME}" \
--password "${QUAY_PASSWORD}"
env:
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
- name: Deploy PR Review ENV (self-hosting)
if: ${{ inputs.self_hosting }}
run: |
@@ -191,7 +201,7 @@ jobs:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
needs: [deploy_pr]
steps:
- uses: marocchino/sticky-pull-request-comment@v2
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
with:
recreate: true
message: |

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -383,12 +383,14 @@ jobs:
- name: Publish to NPM
working-directory: sdk
run: |
echo //registry.npmjs.org/:_authToken=${{ secrets.NPM_RELEASE_TOKEN }} > .npmrc
echo //registry.npmjs.org/:_authToken="${NPM_RELEASE_TOKEN}" > .npmrc
TAG="${{ steps.compute-tag.outputs.masterTag }}"
echo "Resolved npm tag: $TAG"
npm publish --tag "$TAG"
env:
NPM_RELEASE_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
# ⚠️ This step should only be executed on the latest stable (gold) release branch.
# Only uncomment this when the new release branch becomes stable (gold).
# - name: Add `latest` tag to the latest release branch (`release-x.55.x`) deployment

View File

@@ -21,6 +21,10 @@ on:
type: number # needs to be a number to pass variables
required: true
permissions:
id-token: write
contents: read
jobs:
update-release-log:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
@@ -29,10 +33,9 @@ jobs:
if: ${{ github.event_name != 'push' || github.repository == 'metabase/metabase' }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v4
with:

View File

@@ -14,7 +14,6 @@ on:
jobs:
check-milestone:
permissions: write-all
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
@@ -22,7 +21,7 @@ jobs:
with:
sparse-checkout: release
- name: Install bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -27,7 +27,7 @@ jobs:
with:
sparse-checkout: release
- name: Install bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -53,6 +53,11 @@ on:
type: boolean
default: false
permissions:
id-token: write
contents: read
actions: read
jobs:
green-check-stub:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
@@ -146,10 +151,9 @@ jobs:
edition: ${{ fromJson(needs.check-version.outputs.edition_matrix) }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Determine the source path
uses: actions/github-script@v7
@@ -243,7 +247,7 @@ jobs:
fi
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
username: ${{ secrets.DOCKERHUB_RELEASE_USERNAME }}
password: ${{ secrets.DOCKERHUB_RELEASE_TOKEN }}
@@ -255,7 +259,8 @@ jobs:
$DOCKERHUB_REPO\:$DOCKERHUB_VERSION
push-git-tags:
permissions: write-all
permissions:
contents: write
needs: check-version
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
@@ -299,10 +304,9 @@ jobs:
AWS_REGION: ${{ vars.AWS_REGION }}
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v4
with:

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}
@@ -37,7 +37,7 @@ jobs:
with:
sparse-checkout: "release"
- name: Install bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}
@@ -56,7 +56,7 @@ jobs:
with:
sparse-checkout: "release"
- name: Install bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}
@@ -75,7 +75,7 @@ jobs:
with:
sparse-checkout: "release"
- name: Install bun
uses: oven-sh/setup-bun@v2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -46,6 +46,13 @@ on:
type: boolean
default: false
permissions:
id-token: write
contents: write
checks: write
issues: write
actions: read
jobs:
check-version:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
@@ -186,10 +193,9 @@ jobs:
edition: [oss, ee]
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v4
with:
@@ -285,7 +291,6 @@ jobs:
release: "true"
push-tags:
permissions: write-all
needs: [verify-s3-download, containerize-oss, containerize-ee, check-version]
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
@@ -448,7 +453,6 @@ jobs:
needs: add-extra-tags
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 15
permissions: write-all
env:
AWS_S3_DOWNLOADS_BUCKET: ${{ vars.AWS_S3_DOWNLOADS_BUCKET }}
DOCKERHUB_OWNER: ${{ vars.DOCKERHUB_OWNER }}
@@ -530,10 +534,9 @@ jobs:
echo "skipping version info update for nightly patch"
exit 0
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v4
with:
@@ -622,7 +625,6 @@ jobs:
manage-milestones:
if: ${{ !inputs.auto }}
permissions: write-all
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
needs: [draft-release-notes, publish-version-info] # this will ensure that the milestone stays open until the release notes are published
timeout-minutes: 15

View File

@@ -33,13 +33,15 @@ jobs:
id: issue
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
ISSUE_NUMBER="${{ github.event.inputs.issue_number }}"
ISSUE_NUMBER="${DISPATCH_ISSUE_NUMBER}"
else
ISSUE_NUMBER="${{ github.event.issue.number }}"
fi
echo "number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
echo "Issue: https://github.com/${{ github.repository }}/issues/$ISSUE_NUMBER"
env:
DISPATCH_ISSUE_NUMBER: ${{ github.event.inputs.issue_number }}
- uses: actions/checkout@v4
- uses: ./.github/actions/prepare-backend
- uses: ./.github/actions/prepare-frontend
@@ -128,7 +130,7 @@ jobs:
- name: Run repro-bot with Claude Code
id: claude
continue-on-error: true
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@3ac52d0da9f8ec9ca7b4dc23bb477e36ef9c77a9 # v1
env:
CLAUDE_CODE_DEBUG_LOGS_DIR: /tmp/claude-debug-logs
CLAUDE_WORKING_DIR: ${{ github.workspace }}/repro-bot

View File

@@ -0,0 +1,321 @@
name: Auto-resolve backport conflicts
run-name: Auto-resolve conflicts on PR #${{ github.event.pull_request.number || github.event.inputs.pr_number }}
on:
pull_request:
types: [labeled]
workflow_dispatch:
inputs:
pr_number:
description: "PR number of the backport PR with conflicts"
required: true
type: string
max_turns:
description: "Maximum number of Claude turns"
required: false
type: number
default: 75
jobs:
resolve-conflicts:
if: |
(github.event_name == 'pull_request' && github.event.label.name == 'auto-resolve-conflicts') ||
(github.event_name == 'workflow_dispatch')
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
permissions:
contents: write
pull-requests: write
id-token: write
env:
GIT_EDITOR: "true"
steps:
- name: Get PR data
id: pr
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
let pr;
if (context.eventName === 'workflow_dispatch') {
const { data } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: Number('${{ github.event.inputs.pr_number }}'),
});
pr = data;
} else {
pr = context.payload.pull_request;
}
core.setOutput('number', pr.number);
core.setOutput('head_ref', pr.head.ref);
core.setOutput('base_ref', pr.base.ref);
- name: Checkout PR branch
uses: actions/checkout@v4
with:
ref: ${{ steps.pr.outputs.head_ref }}
fetch-depth: 0
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Detect package manager
id: pkg
run: |
if [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
echo "mgr=bun" >> $GITHUB_OUTPUT
else
echo "mgr=yarn" >> $GITHUB_OUTPUT
fi
- name: Validate backport PR
id: validate
run: |
if [ ! -f "./backport.sh" ]; then
echo "::error::No backport.sh found on this branch. This workflow only works on backport PRs with conflicts."
exit 1
fi
echo "backport.sh found, proceeding with conflict resolution"
cat ./backport.sh
- name: Set up git configuration
run: |
git config --global user.name "Metabase bot"
git config --global user.email "metabase-bot@metabase.com"
git config --global core.editor "true"
- name: Run backport script to create cherry-pick conflicts
id: backport
run: |
bash ./backport.sh 2>&1 || true
# Check if we're in a cherry-pick state with conflicts
CONFLICTED=$(git diff --name-only --diff-filter=U || true)
if [ -n "$CONFLICTED" ]; then
echo "has_conflicts=true" >> $GITHUB_OUTPUT
echo "conflicted_files<<EOF" >> $GITHUB_OUTPUT
echo "$CONFLICTED" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "::notice::Found conflicts in: $CONFLICTED"
else
echo "has_conflicts=false" >> $GITHUB_OUTPUT
echo "::notice::Cherry-pick applied cleanly, no conflicts to resolve"
fi
- name: Push clean cherry-pick (no conflicts)
if: steps.backport.outputs.has_conflicts == 'false'
run: git push --force
- name: Prepare back-end environment
if: steps.backport.outputs.has_conflicts == 'true'
uses: ./.github/actions/prepare-backend
- name: Prepare front-end environment
if: steps.backport.outputs.has_conflicts == 'true'
uses: ./.github/actions/prepare-frontend
- name: Build CLJS for type checking
if: steps.backport.outputs.has_conflicts == 'true'
run: ${{ steps.pkg.outputs.mgr }} run build-pure:cljs
- name: Generate GitHub App Token for Claude
if: steps.backport.outputs.has_conflicts == 'true'
uses: actions/create-github-app-token@v1
id: claude-app-token
with:
app-id: ${{ secrets.METABASE_BOT_APP_ID }}
private-key: ${{ secrets.METABASE_BOT_APP_PRIVATE_KEY }}
- name: Resolve conflicts with Claude
if: steps.backport.outputs.has_conflicts == 'true'
id: claude
uses: anthropics/claude-code-base-action@beta
env:
NODE_VERSION: "22"
with:
# TODO: get a dedicated API key, sharing repro bot's key for now
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_BACKPORT_FIX }}
model: "claude-sonnet-4-20250514"
max_turns: "${{ github.event.inputs.max_turns || '75' }}"
allowed_tools: "Read,Edit,Write,Bash(git:*),Bash(grep:*),Bash(find:*),Bash(ls:*),Bash(cat:*),Bash(clojure:*),Bash(${{ steps.pkg.outputs.mgr }} run type-check-pure:*),Bash(${{ steps.pkg.outputs.mgr }} run lint-eslint:*),Bash(./bin/mage cljfmt:*),Glob,Grep"
prompt: |
You are resolving git cherry-pick merge conflicts for a Metabase backport PR.
## Context
A commit from the `master` branch is being cherry-picked onto the `${{ steps.pr.outputs.base_ref }}` release branch.
The cherry-pick has failed with conflicts in these files:
${{ steps.backport.outputs.conflicted_files }}
## How to read conflict markers
```
<<<<<<< HEAD
(code from the release branch - the current state)
=======
(code from master - the change being backported)
>>>>>>> <commit-sha>
```
## Common conflict patterns in Metabase backports
- **Module/namespace renames**: A Clojure namespace was renamed or moved between versions. Check what the correct namespace is on the release branch.
- **Test file divergence**: Tests may have been added or reordered differently. Keep the backported test changes while preserving existing release-branch tests.
- **Import path changes**: Frontend imports may reference different paths. Check actual file locations on this branch.
- **Translation files (.po)**: For translation conflicts, prefer the release branch version of existing translations and add new translations from the backported change.
- **Minor context differences**: Surrounding code changed slightly. Adapt the backported change to fit the release branch context.
## Instructions
1. Read each conflicted file to understand the conflict
2. For context, you may search the codebase to understand the current state of the release branch (e.g., check if a module exists at a certain path)
3. Resolve each conflict by editing the file to remove ALL conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) and leave only the correct resolved code
4. The goal is to apply the INTENT of the backported change while adapting it to the release branch's code structure
5. Verify no conflict markers remain by searching for them
6. Auto-format resolved files:
- **Clojure/ClojureScript** (.clj, .cljc, .cljs): run cljfmt to fix formatting:
```
./bin/mage cljfmt-files <file1> <file2> ...
```
- **TypeScript/JavaScript** (.ts, .tsx, .js, .jsx): run eslint with auto-fix:
```
${{ steps.pkg.outputs.mgr }} run lint-eslint --fix
```
7. Validate resolved files before committing:
- **Clojure/ClojureScript** (.clj, .cljc, .cljs): run clj-kondo to verify syntax:
```
clojure -M:kondo --lint <file1> <file2> ...
```
- **TypeScript/JavaScript** (.ts, .tsx, .js, .jsx): run the type checker:
```
${{ steps.pkg.outputs.mgr }} run type-check-pure
```
If either tool reports errors or warnings, fix them before proceeding. CI treats kondo warnings as errors.
Repeat steps 6-7 until all errors are resolved.
7. Once all files are clean, stage and complete the cherry-pick:
```
git add -A
git cherry-pick --continue
```
8. Push the result:
```
git push --force
```
IMPORTANT: Do not skip any conflicted files. Every conflict marker must be resolved.
- name: Check for max turns exceeded
if: always() && steps.claude.outcome != 'skipped'
id: check-failure
run: |
if [ -f "${{ steps.claude.outputs.execution_file }}" ]; then
SUBTYPE=$(jq -r '.[] | select(.type == "result") | .subtype' "${{ steps.claude.outputs.execution_file }}" | tail -1)
if [ "$SUBTYPE" = "error_max_turns" ]; then
echo "max_turns_exceeded=true" >> $GITHUB_OUTPUT
else
echo "max_turns_exceeded=false" >> $GITHUB_OUTPUT
fi
else
echo "max_turns_exceeded=false" >> $GITHUB_OUTPUT
fi
- name: Get commit details
if: always() && steps.claude.outcome == 'success' && steps.check-failure.outputs.max_turns_exceeded != 'true'
id: commit-info
run: |
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
FILES=$(git diff --name-only HEAD~1)
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
echo "$FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Comment on PR (success)
if: always() && steps.claude.outcome == 'success' && steps.check-failure.outputs.max_turns_exceeded != 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
const sha = '${{ steps.commit-info.outputs.sha }}';
const changedFiles = `${{ steps.commit-info.outputs.changed_files }}`.trim().split('\n').map(f => `- \`${f}\``).join('\n');
await github.rest.issues.createComment({
issue_number: ${{ steps.pr.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: [
'🤖 Backport conflicts have been automatically resolved and force-pushed.',
'',
`**Commit:** [\`${sha.substring(0, 10)}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${sha})`,
'',
'<details>',
'<summary>Changed files</summary>',
'',
changedFiles,
'',
'</details>',
'',
'Please review the changes to make sure the resolution is correct before merging.',
].join('\n')
});
- name: Comment on PR (max turns exceeded)
if: always() && steps.check-failure.outputs.max_turns_exceeded == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
await github.rest.issues.createComment({
issue_number: ${{ steps.pr.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: [
'🤖 Auto-resolution of backport conflicts stopped because Claude reached the maximum number of turns (${{ github.event.inputs.max_turns || '75' }}).',
'',
'The conflicts may be too complex for automatic resolution, or Claude needed more turns to finish.',
'',
'You can try again with more turns by manually running the [resolve-backport-conflicts workflow](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions/workflows/resolve-backport-conflicts.yml) with a higher `max_turns` value, or resolve the conflicts manually by checking out the branch and running `./backport.sh`.',
].join('\n')
});
- name: Comment on PR (failure)
if: always() && steps.claude.outcome == 'failure' && steps.check-failure.outputs.max_turns_exceeded != 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
await github.rest.issues.createComment({
issue_number: ${{ steps.pr.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: [
'🤖 Auto-resolution of backport conflicts failed.',
'',
'Manual resolution is still required. Check out the branch and run `./backport.sh` to resolve conflicts manually.',
].join('\n')
});
- name: Comment on PR (clean cherry-pick)
if: always() && steps.backport.outputs.has_conflicts == 'false'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
await github.rest.issues.createComment({
issue_number: ${{ steps.pr.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: '🤖 Cherry-pick applied cleanly with no conflicts. Force-pushed the resolved branch.'
});
- name: Remove label
if: always()
uses: actions/github-script@v7
with:
github-token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
script: |
try {
await github.rest.issues.removeLabel({
issue_number: ${{ steps.pr.outputs.number }},
owner: context.repo.owner,
repo: context.repo.repo,
name: 'auto-resolve-conflicts'
});
} catch (e) {
console.log('Label already removed or not found:', e.message);
}

View File

@@ -10,6 +10,14 @@ on:
merge_group:
workflow_dispatch:
permissions:
id-token: write
contents: read
actions: read
checks: write
pull-requests: write
security-events: write
jobs:
files-changed:
if: ${{ !contains(github.event.pull_request.labels.*.name,'ci skip') && !contains(github.event.pull_request.labels.*.name,'minimal-tests') }}
@@ -32,7 +40,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -19,7 +19,7 @@ jobs:
core.setOutput('issue_title', ${{ toJson(github.event.issue.title) }}.replaceAll(/"/g, '\\"'));
- name: Send the issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.26.0
uses: slackapi/slack-github-action@70cd7be8e40a46e8b0eced40b0de447bdb42f68e # v1.26.0
with:
payload: |
{

View File

@@ -61,8 +61,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -135,8 +134,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -209,8 +207,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}
@@ -284,8 +281,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
previous-step-outcome: ${{ steps.test-driver.outputs.test-outcome }}

39
.github/workflows/serialization.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Serialization
on:
push:
branches:
- 'master'
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
files-changed:
name: Check which files changed
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 3
outputs:
serialization_baseline: ${{ steps.changes.outputs.serialization_baseline }}
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
id: changes
with:
token: ${{ github.token }}
filters: .github/file-paths.yaml
validate-serialization-baseline:
needs: files-changed
if: needs.files-changed.outputs.serialization_baseline == 'true'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Prepare front-end environment
uses: ./.github/actions/prepare-frontend
- name: Validate serialization baseline schema
run: bunx @metabase/representations validate-schema --folder test_resources/serialization_baseline

View File

@@ -9,6 +9,10 @@ on:
type: string
required: true
permissions:
id-token: write
contents: read
jobs:
set-announce-url:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
@@ -21,10 +25,9 @@ jobs:
exit 1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- name: Update announcement url
uses: actions/github-script@v7

View File

@@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

View File

@@ -51,7 +51,7 @@ jobs:
category: release-helper
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: "1.3.7"
token: ${{ secrets.GITHUB_TOKEN }}
@@ -71,7 +71,7 @@ jobs:
bun install --yarn
sed -i -E 's/< ([0-9]+)\.0\.0"/< \1"/g; s/< ([0-9]+)\.0"/< \1"/g' yarn.lock || true
working-directory: release
- uses: snyk/actions/setup@0.4.0
- uses: snyk/actions/setup@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0
- name: Run snyk test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
@@ -148,7 +148,7 @@ jobs:
category: driver-vertica
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/setup@0.4.0
- uses: snyk/actions/setup@b98d498629f1c368650224d6d212bf7dfa89e4bf # 0.4.0
- name: Download pom files
uses: actions/download-artifact@v4
with:

196
.github/workflows/sync-fork.yml vendored Normal file
View File

@@ -0,0 +1,196 @@
name: Sync Fork with Upstream
on:
schedule:
- cron: "0 2 * * *" # Every day at 2 AM UTC
workflow_dispatch:
inputs:
force:
description: "⚠️ DESTRUCTIVE: Force-push from upstream, overwriting any fork-only commits"
required: false
default: false
type: boolean
release_branch_count:
description: "Number of most recent release branches to sync (0 to skip)"
required: false
default: "5"
type: number
jobs:
workflow-summary:
# ONLY run on internal metabase org forks — NEVER on the public repo or external forks
if: github.repository_owner == 'metabase' && github.repository != 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 5
steps:
- name: Generate workflow summary
run: |
echo '**Inputs:**' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '- `release_branch_count`: ${{ inputs.release_branch_count || 5 }}' >> $GITHUB_STEP_SUMMARY
echo '- `force`: ${{ inputs.force || false }}' >> $GITHUB_STEP_SUMMARY
echo '- `trigger`: ${{ github.event_name }}' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo 'triggered by: @${{ github.event.sender.login }}' >> $GITHUB_STEP_SUMMARY
else
echo 'triggered by: schedule' >> $GITHUB_STEP_SUMMARY
fi
sync-master:
# ONLY run on internal metabase org forks — NEVER on the public repo or external forks
if: github.repository_owner == 'metabase' && github.repository != 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: master
fetch-depth: 0
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Add upstream remote
run: git remote add metabase https://github.com/metabase/metabase.git
- name: Fetch upstream master
run: git fetch metabase master
- name: Sync master from upstream
id: sync
env:
FORCE: ${{ inputs.force || 'false' }}
run: |
if git diff --quiet master metabase/master; then
echo "master is already up to date — skipping push"
echo "detail=up to date" >> "$GITHUB_OUTPUT"
exit 0
fi
git checkout master
if [ "$FORCE" = "true" ]; then
echo "⚠️ Force-syncing master (overwriting fork-only commits)"
git reset --hard metabase/master
git push origin master --force
echo "detail=force-synced" >> "$GITHUB_OUTPUT"
else
if git pull metabase master; then
git push origin master
echo "detail=synced" >> "$GITHUB_OUTPUT"
else
echo "::warning::master has diverged from upstream — skipping (use force to override)"
echo "detail=diverged (skipped)" >> "$GITHUB_OUTPUT"
fi
fi
- name: Summary
if: always()
env:
REPO: ${{ github.repository }}
run: |
if [ "${{ steps.sync.outcome }}" = "success" ]; then
echo "| [\`master\`](https://github.com/${REPO}/tree/master) | ✅ ${{ steps.sync.outputs.detail }} |" >> $GITHUB_STEP_SUMMARY
else
echo "| [\`master\`](https://github.com/${REPO}/tree/master) | ❌ failed |" >> $GITHUB_STEP_SUMMARY
fi
list-release-branches:
# ONLY run on internal metabase org forks — NEVER on the public repo or external forks
if: github.repository_owner == 'metabase' && github.repository != 'metabase/metabase'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
outputs:
branches: ${{ steps.discover.outputs.branches }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Add upstream remote
run: git remote add metabase https://github.com/metabase/metabase.git
- name: Fetch upstream release branches
run: git fetch metabase 'refs/heads/release-*:refs/remotes/metabase/release-*'
- name: Discover release branches to sync
id: discover
env:
BRANCH_COUNT: ${{ inputs.release_branch_count || '5' }}
run: |
# Get only canonical release branches (release-x.NN.x), skip ad-hoc branches like backports
branches=$(git branch -r --list 'metabase/release-*' | sed 's|^ *||' | grep -E '^metabase/release-x\.[0-9]+\.x$' | sort -t. -k2,2rn | head -n "$BRANCH_COUNT")
json="[]"
for branch in $branches; do
branch_name="${branch#metabase/}"
json=$(echo "$json" | jq -c --arg b "$branch_name" '. + [$b]')
done
echo "branches=$json" >> "$GITHUB_OUTPUT"
echo "Release branches to sync: $json"
sync-release-branch:
needs: list-release-branches
if: needs.list-release-branches.outputs.branches != '[]'
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
strategy:
fail-fast: false
matrix:
branch: ${{ fromJson(needs.list-release-branches.outputs.branches) }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
- name: Add upstream remote
run: git remote add metabase https://github.com/metabase/metabase.git
- name: Fetch upstream branch
run: git fetch metabase "${{ matrix.branch }}"
- name: Sync ${{ matrix.branch }} from upstream
id: sync
env:
FORCE: ${{ inputs.force || 'false' }}
run: |
branch_name="${{ matrix.branch }}"
# Check if branch exists on origin
if git rev-parse --verify "origin/$branch_name" &>/dev/null; then
if git diff --quiet "origin/$branch_name" "metabase/$branch_name"; then
echo "$branch_name is already up to date — skipping push"
echo "detail=up to date" >> "$GITHUB_OUTPUT"
exit 0
fi
fi
if [ "$FORCE" = "true" ]; then
echo "⚠️ Force-syncing $branch_name (overwriting fork-only commits)"
git checkout -B "$branch_name" "metabase/$branch_name"
git push origin "$branch_name" --force
echo "detail=force-synced" >> "$GITHUB_OUTPUT"
else
git checkout -B "$branch_name" "origin/$branch_name" 2>/dev/null || git checkout -B "$branch_name" "metabase/$branch_name"
if git pull metabase "$branch_name"; then
git push origin "$branch_name"
echo "detail=synced" >> "$GITHUB_OUTPUT"
else
echo "::warning::$branch_name has diverged from upstream — skipping (use force to override)"
echo "detail=diverged (skipped)" >> "$GITHUB_OUTPUT"
fi
fi
- name: Summary
if: always()
env:
REPO: ${{ github.repository }}
run: |
branch="${{ matrix.branch }}"
if [ "${{ steps.sync.outcome }}" = "success" ]; then
echo "| [\`${branch}\`](https://github.com/${REPO}/tree/${branch}) | ✅ ${{ steps.sync.outputs.detail }} |" >> $GITHUB_STEP_SUMMARY
else
echo "| [\`${branch}\`](https://github.com/${REPO}/tree/${branch}) | ❌ failed |" >> $GITHUB_STEP_SUMMARY
fi

View File

@@ -16,6 +16,13 @@ on:
type: boolean
default: false
permissions:
id-token: write
contents: write
checks: write
issues: write
actions: read
jobs:
start-message:
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
@@ -198,10 +205,9 @@ jobs:
timeout-minutes: 15
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4
with:
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_RELEASE_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/checkout@v4
with:
@@ -226,8 +232,10 @@ jobs:
return version_path;
- name: Upload to S3
run: |
aws s3 cp ./metabase.jar s3://${{ secrets.AWS_S3_CLOUD_DEPLOY_BUCKET }}/${{ steps.version_path.outputs.result }}/metabase.jar
aws s3 cp ./metabase.jar s3://"${AWS_S3_CLOUD_DEPLOY_BUCKET}"/${{ steps.version_path.outputs.result }}/metabase.jar
env:
AWS_S3_CLOUD_DEPLOY_BUCKET: ${{ secrets.AWS_S3_CLOUD_DEPLOY_BUCKET }}
run-pre-release-tests:
name: Run pre-release tests for ${{ inputs.version }}
needs: tag-uberjar-for-release

View File

@@ -30,7 +30,7 @@ jobs:
echo "team_name=${team_name^^}_SLACK_ISSUES_WEBHOOK_URL" >> "$GITHUB_OUTPUT"
- name: Send the issue ${{ github.event.issue.number }} to Slack
id: slack
uses: slackapi/slack-github-action@v1.23.0
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844 # v1.23.0
with:
payload: |
{

View File

@@ -24,6 +24,12 @@ on:
- snowflake
- sqlserver
permissions:
id-token: write
contents: read
checks: write
actions: read
jobs:
be-tests-bigquery-transforms:
@@ -64,8 +70,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -99,7 +104,7 @@ jobs:
run: |
sudo echo "127.0.0.1 server.clickhouseconnect.test" | sudo tee -a /etc/hosts
- name: Start ClickHouse in Docker
uses: hoverkraft-tech/compose-action@v2.0.0
uses: hoverkraft-tech/compose-action@802a148945af6399a338c7906c267331b39a71af # v2.0.0
with:
compose-file: "modules/drivers/clickhouse/docker-compose.yml"
down-flags: "--volumes"
@@ -124,8 +129,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -175,8 +179,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -230,8 +233,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -286,8 +288,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -332,8 +333,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -387,8 +387,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner
@@ -440,8 +439,7 @@ jobs:
input-path: ./target/junit/
output-name: ${{ github.job }}
bucket: ${{ vars.AWS_S3_TEST_RESULTS_BUCKET }}
aws-access-key-id: ${{ secrets.AWS_TEST_RESULTS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_TEST_RESULTS_SECRET_ACCESS_KEY }}
role-to-assume: ${{ secrets.CI_TEST_RESULTS_IAM_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
trunk-api-token: ${{ secrets.TRUNK_API_TOKEN }}
- name: Cleanup python-runner

View File

@@ -7,6 +7,7 @@ on:
jobs:
harvest:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
permissions:
contents: read
@@ -35,7 +36,7 @@ jobs:
# Hence, the three minute timeout and continue-on-error.
- name: Trigger pre-translations
timeout-minutes: 3
uses: crowdin/github-action@v2
uses: crowdin/github-action@ce33ce793a5cbc401d9cd748716e03fc90c001f1 # v2
# https://crowdin.github.io/crowdin-cli/commands/crowdin-pre-translate
with:
command: "pre-translate"

View File

@@ -10,6 +10,7 @@ permissions:
jobs:
synchronize-translations:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Synchronize Translations
env:
BRANCH_NAME: update-translations-${{ github.run_id }}
@@ -33,7 +34,7 @@ jobs:
run: sudo apt-get install gettext
- name: Crowdin download
uses: crowdin/github-action@v2
uses: crowdin/github-action@ce33ce793a5cbc401d9cd748716e03fc90c001f1 # v2
id: crowdin-download
with:
config: locales/crowdin.yml
@@ -74,7 +75,7 @@ jobs:
git push -u origin $BRANCH_NAME
- name: Crowdin upload
uses: crowdin/github-action@v2
uses: crowdin/github-action@ce33ce793a5cbc401d9cd748716e03fc90c001f1 # v2
id: crowdin-upload
env:
GITHUB_TOKEN: ${{ github.token }}
@@ -105,13 +106,13 @@ jobs:
echo "::set-output name=pr_number::$PR_NUMBER"
- name: Auto approve PR
uses: juliangruber/approve-pull-request-action@v1
uses: juliangruber/approve-pull-request-action@f83b836abaed1b3ab639ae61f9117c52ae405f65 # v1
with:
github-token: ${{ github.token }}
number: ${{ steps.create_pr.outputs.pr_number }}
- name: Enable Pull Request Automerge
uses: peter-evans/enable-pull-request-automerge@v2
uses: peter-evans/enable-pull-request-automerge@684fed02ccc9b5eefcf7d40b65b3cd44255bd5bc # v2
with:
token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
pull-request-number: ${{ steps.create_pr.outputs.pr_number }}

View File

@@ -18,6 +18,7 @@ on:
jobs:
collect-and-average-timings:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 30
env:
@@ -64,7 +65,7 @@ jobs:
echo "Branch name will be: $BRANCH_NAME"
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6
with:
token: ${{ steps.app-token.outputs.token }}
branch: ${{ env.BRANCH_NAME }}

View File

@@ -17,6 +17,7 @@ permissions:
jobs:
bump-deps:
if: ${{ github.event_name != 'schedule' || github.repository == 'metabase/metabase' }}
name: Open PRs to Bump Backend Deps
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
timeout-minutes: 20

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Test which files changed
uses: dorny/paths-filter@v3.0.0
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
id: changes
with:
token: ${{ github.token }}

2
.gitignore vendored
View File

@@ -145,8 +145,6 @@ modules/drivers/*/.lsp/*
.github/static-viz-sources.yaml
# airgap
/resources/airgap/
# swc
.swc/

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -6,10 +6,16 @@ For detailed guidance on writing and reviewing code and documentation, see the s
## Clojure
### clojure-mcp tools
- **[clojure-eval](.claude/skills/clojure-eval/SKILL.md)** - Always use this to evaluate Clojure code, **run tests**, and verify edits/compile. Prefer this over shell commands.
- **[clojure-write](.claude/skills/clojure-write/SKILL.md)** - Clojure/ClojureScript development with REPL-driven workflow and coding conventions
- **[clojure-review](.claude/skills/clojure-review/SKILL.md)** - Clojure/ClojureScript code review guidelines and style enforcement
### clojure-mcp-lite tools
- **clj-nrepl-eval** - This is another good mechanism for running Clojure code on an nrepl server.
## TypeScript
- **[typescript-write](.claude/skills/typescript-write/SKILL.md)** - TypeScript/JavaScript development and best practices
@@ -28,7 +34,7 @@ For detailed guidance on writing and reviewing code and documentation, see the s
## Running Backend Tests
Use `./bin/test-agent` to run Clojure tests. It produces clean, plain-text output with no progress bars or ANSI codes.
If you do not have `clojure-eval` available to you or `clj-nrepl-eval`, do not fall back to `clj -X:dev:test` directly. Instead, use `./bin/test-agent`. It produces clean, plain-text output with no progress bars or ANSI codes.
```bash
./bin/test-agent :only '[metabase.foo-test]' # run a namespace
@@ -36,7 +42,7 @@ Use `./bin/test-agent` to run Clojure tests. It produces clean, plain-text outpu
./bin/test-agent :only '[metabase.foo-test metabase.bar-test]' # multiple namespaces
```
Do not use `clj -X:dev:test` directly — its progress-bar output is hard to parse.
Once again, do not use `clj -X:dev:test` directly — its progress-bar output is hard to parse.
## Tool Preferences

116
bin/check-migration-versions Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail
# Requires: BASE_REF, CURRENT_VERSION (for master)
# Optional: PR_LABELS (comma-separated, for backport handling)
if [[ -z "${BASE_REF:-}" ]]; then
echo "ERROR: BASE_REF is not set." >&2
exit 1
fi
# No format validation on BASE_REF — it comes from GitHub Actions (github.event.pull_request.base.ref),
# and we don't want to be opinionated about branch naming conventions.
# Ensure origin/${BASE_REF} is reachable
if ! git rev-parse "origin/${BASE_REF}" >/dev/null 2>&1; then
git fetch --depth=1 origin -- "$BASE_REF" || {
echo "ERROR: Could not resolve origin/${BASE_REF}" >&2
exit 1
}
fi
echo "BASE_REF: ${BASE_REF}"
# Determine the expected version and constraint
if [[ "$BASE_REF" =~ ^release-x\.([0-9]+)\.x$ ]]; then
constraint="<="
version="${BASH_REMATCH[1]}"
elif [[ "$BASE_REF" == "master" ]]; then
labels="${PR_LABELS:-}"
if [[ -z "${CURRENT_VERSION:-}" ]]; then
echo "ERROR: CURRENT_VERSION is not set." >&2
exit 1
fi
if ! [[ "$CURRENT_VERSION" =~ ^[1-9][0-9]*$ ]]; then
echo "ERROR: CURRENT_VERSION must be a positive integer, got: '${CURRENT_VERSION}'" >&2
exit 1
fi
echo "CURRENT_VERSION: ${CURRENT_VERSION}"
matches=0
for l in backport double-backport triple-backport; do
if [[ ",$labels," == *",$l,"* ]]; then
# N.B. not ((matches++)) — that exits 1 when matches=0 under set -e
matches=$((matches + 1))
fi
done
constraint="="
version=$((CURRENT_VERSION + 1))
# Backport labels shift the target version back so migrations land on the correct release branch.
[[ ",$labels," == *",backport,"* ]] && version=$((CURRENT_VERSION))
[[ ",$labels," == *",double-backport,"* ]] && version=$((CURRENT_VERSION - 1))
[[ ",$labels," == *",triple-backport,"* ]] && version=$((CURRENT_VERSION - 2))
[[ $matches -gt 1 ]] && echo "NOTE: Multiple backport labels detected; using the earliest version (v${version})." >&2
else
echo "Target branch '${BASE_REF}' is not master or a release branch — skipping."
exit 0
fi
expected_dir=$(printf "%03d" "$version")
echo "Expected directory: ${constraint} ${expected_dir}"
echo ""
# Check that new migration files are in the correct directory
changed_files=$(git diff --name-only --diff-filter=A "origin/${BASE_REF}..HEAD" -- 'resources/migrations/**/*.yaml') || {
echo "ERROR: git diff failed — could not compare against origin/${BASE_REF}" >&2
exit 1
}
if [[ -z "$changed_files" ]]; then
echo "No new migration files changed."
exit 0
fi
errors=0
while IFS= read -r file; do
[[ -z "$file" ]] && continue
dir=$(echo "$file" | sed -n 's|resources/migrations/\([0-9][0-9]*\)/.*|\1|p')
[[ -z "$dir" ]] && continue
dir_version=$((10#$dir))
dir=$(printf "%03d" "$dir_version")
if [[ "$constraint" == "=" && "$dir_version" -ne "$version" ]] ||
[[ "$constraint" == "<=" && "$dir_version" -gt "$version" ]]; then
echo "ERROR: ${file} is in ${dir} but expected ${constraint} ${expected_dir}" >&2
errors=$((errors + 1))
fi
done <<<"$changed_files"
if [[ "$errors" -gt 0 ]]; then
echo "" >&2
echo "${errors} error(s) found." >&2
echo "" >&2
echo "Why: Migration files must be in the directory matching the target release version." >&2
echo "A misplaced migration misrepresents when it was introduced and will break downgrades spanning more than one version." >&2
echo "" >&2
if [[ "$constraint" == "<=" ]]; then
echo "To fix: move the file(s) to resources/migrations/${expected_dir}/ or any earlier version directory." >&2
else
echo "To fix: move the file(s) to resources/migrations/${expected_dir}/." >&2
fi
echo "" >&2
echo "If this PR will be backported, ensure the correct backport label is applied" >&2
echo "(backport, double-backport, or triple-backport) and re-run CI, even if you" >&2
echo "plan to create the backport PRs manually. Base the label on the oldest" >&2
echo "release you're targeting, and simply close any unwanted PRs." >&2
echo "" >&2
echo "If you are backporting more than 3 releases back, or this is an exceptional" >&2
echo "circumstance, you will need to either update this script or force merge." >&2
exit 1
fi
echo "All new migration files are in the correct directory."

364
bun.lock
View File

@@ -92,7 +92,7 @@
"d3-scale": "^4.0.2",
"dayjs": "^1.11.18",
"detect-package-manager": "^3.0.2",
"diff": "^5.2.0",
"diff": "^5.2.2",
"echarts": "6.0.0",
"events": "^3.3.0",
"fast-text-encoding": "^1.0.6",
@@ -111,7 +111,7 @@
"js-cookie": "^2.1.2",
"js-yaml": "^4.1.1",
"json5": "2.2.2",
"jspdf": "^4.2.0",
"jspdf": "^4.2.1",
"kbar": "0.1.0-beta.45",
"leaflet": "^1.2.0",
"leaflet-draw": "^0.4.9",
@@ -273,7 +273,7 @@
"@types/use-sync-external-store": "^1.5.0",
"ajv": "^8.5.0",
"babel-loader": "^9.1.2",
"babel-plugin-ttag": "^1.7.26",
"babel-plugin-ttag": "^1.8.16",
"chalk": "^4",
"chokidar-cli": "^3.0.0",
"chromatic": "^10.2.0",
@@ -328,7 +328,7 @@
"json-to-pretty-yaml": "^1.2.2",
"jsonwebtoken": "^9.0.3",
"knex": "^2.4.2",
"lint-staged": "^14",
"lint-staged": "^16.4.0",
"loki": "^0.35.1",
"mini-css-extract-plugin": "2.7.2",
"mocha": "^10.8.1",
@@ -347,7 +347,7 @@
"open": "^10.1.0",
"open-cli": "^8.0.0",
"openapi-typescript": "^7.10.0",
"patch-package": "^8.0.0",
"patch-package": "^8.0.1",
"pg": "^8.8.0",
"postcss": "^8.5.6",
"postcss-discard-comments": "^7.0.4",
@@ -379,7 +379,7 @@
"ts-dedent": "^2.2.0",
"ts-morph": "^25.0.0",
"tsx": "^4.19.3",
"typedoc": "0.28.13",
"typedoc": "^0.28.18",
"typedoc-plugin-dt-links": "^2.0.21",
"typedoc-plugin-markdown": "^4.9.0",
"typedoc-plugin-mdn-links": "^5.0.9",
@@ -416,15 +416,21 @@
"cross-spawn": "^7.0.5",
"d3-color": "^3.1.0",
"debug": "^4.3.4",
"diff": "^5.2.2",
"dompurify": "^3.3.2",
"elliptic": "^6.6.1",
"follow-redirects": "^1.15.5",
"json5": "2.2.2",
"lodash": "^4.17.23",
"lodash-es": "^4.17.23",
"markdown-it": "^14.1.1",
"mdast-util-to-hast": "^13.2.1",
"nth-check": "2.0.1",
"on-headers": "^1.1.0",
"prosemirror-model": "^1.25.4",
"prosemirror-state": "^1.4.4",
"prosemirror-view": "^1.40.1",
"qs": "^6.14.2",
"webpack": "5.105.0",
"webpack-dev-middleware": "^5.3.4",
"webpack-dev-server": "^5.2.1",
@@ -978,7 +984,7 @@
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
"@gerrit0/mini-shiki": ["@gerrit0/mini-shiki@3.13.0", "", { "dependencies": { "@shikijs/engine-oniguruma": "^3.13.0", "@shikijs/langs": "^3.13.0", "@shikijs/themes": "^3.13.0", "@shikijs/types": "^3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-mCrNvZNYNrwKer5PWLF6cOc0OEe2eKzgy976x+IT2tynwJYl+7UpHTSeXQJGijgTcoOf+f359L946unWlYRnsg=="],
"@gerrit0/mini-shiki": ["@gerrit0/mini-shiki@3.23.0", "", { "dependencies": { "@shikijs/engine-oniguruma": "^3.23.0", "@shikijs/langs": "^3.23.0", "@shikijs/themes": "^3.23.0", "@shikijs/types": "^3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg=="],
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.3.3", "", {}, "sha512-vArVDtrvdzFewu1hnjUm4jX1NBITlSCeO81EdWq676MxQbyxsGcDPAgohaSA+Wvr4HjPSvsg2/1s2zYxUtXebg=="],
@@ -1354,13 +1360,13 @@
"@rushstack/ts-command-line": ["@rushstack/ts-command-line@4.23.7", "", { "dependencies": { "@rushstack/terminal": "0.15.2", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="],
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="],
"@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="],
"@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="],
"@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="],
"@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="],
"@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="],
"@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="],
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
@@ -2188,7 +2194,7 @@
"babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="],
"babel-plugin-ttag": ["babel-plugin-ttag@1.8.8", "", { "dependencies": { "@babel/generator": "^7.12.5", "@babel/template": "^7.10.4", "@babel/types": "^7.12.6", "ajv": "6.12.3", "babel-plugin-macros": "^2.8.0", "dedent": "0.6.0", "gettext-parser": "6.0.0", "mkdirp": "^1.0.4", "plural-forms": "^0.5.3" } }, "sha512-0xTObf1Guk5XdfSfyKm35fUEoT5CK/qwnbvXsD8YVBclVzDpTEChhEgbhg5/5yz446suiZbwtdJQaYCJF9q8WQ=="],
"babel-plugin-ttag": ["babel-plugin-ttag@1.8.16", "", { "dependencies": { "@babel/generator": "^7.12.5", "@babel/template": "^7.10.4", "@babel/types": "^7.12.6", "ajv": "6.12.3", "babel-plugin-macros": "^2.8.0", "dedent": "1.5.1", "gettext-parser": "6.0.0", "mkdirp": "^1.0.4", "plural-forms": "^0.5.3" } }, "sha512-UmA4KAvg3K1nzTBaqWox945CS3C1zjJu6lGZjmbOYW3NO2ps6mlIm8fnj9wjzNm2Y2nzUuD73aiAK9Rd3vTZgQ=="],
"babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.2.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg=="],
@@ -2464,7 +2470,7 @@
"content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
"content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="],
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
@@ -2678,7 +2684,7 @@
"decompress-response": ["decompress-response@3.3.0", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M="],
"dedent": ["dedent@0.6.0", "", {}, "sha1-Dm2o8M5Sg471zsXI+TlrDBtko8s="],
"dedent": ["dedent@1.5.1", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg=="],
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
@@ -2756,7 +2762,7 @@
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
"diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="],
"diff": ["diff@5.2.2", "", {}, "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A=="],
"diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="],
@@ -2784,7 +2790,7 @@
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
"dompurify": ["dompurify@3.3.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q=="],
"dompurify": ["dompurify@3.3.3", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA=="],
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
@@ -2836,7 +2842,7 @@
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
"entities": ["entities@4.4.0", "", {}, "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="],
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
@@ -3614,7 +3620,7 @@
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"json-stable-stringify": ["json-stable-stringify@1.1.1", "", { "dependencies": { "call-bind": "^1.0.5", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg=="],
"json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="],
@@ -3634,7 +3640,7 @@
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
"jspdf": ["jspdf@4.2.0", "", { "dependencies": { "@babel/runtime": "^7.28.6", "fast-png": "^6.2.0", "fflate": "^0.8.1" }, "optionalDependencies": { "canvg": "^3.0.11", "core-js": "^3.6.0", "dompurify": "^3.3.1", "html2canvas": "^1.0.0-rc.5" } }, "sha512-hR/hnRevAXXlrjeqU5oahOE+Ln9ORJUB5brLHHqH67A+RBQZuFr5GkbI9XQI8OUFSEezKegsi45QRpc4bGj75Q=="],
"jspdf": ["jspdf@4.2.1", "", { "dependencies": { "@babel/runtime": "^7.28.6", "fast-png": "^6.2.0", "fflate": "^0.8.1" }, "optionalDependencies": { "canvg": "^3.0.11", "core-js": "^3.6.0", "dompurify": "^3.3.1", "html2canvas": "^1.0.0-rc.5" } }, "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ=="],
"jsprim": ["jsprim@2.0.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ=="],
@@ -3684,7 +3690,7 @@
"linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="],
"lint-staged": ["lint-staged@14.0.1", "", { "dependencies": { "chalk": "5.3.0", "commander": "11.0.0", "debug": "4.3.4", "execa": "7.2.0", "lilconfig": "2.1.0", "listr2": "6.6.1", "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", "yaml": "2.3.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw=="],
"lint-staged": ["lint-staged@16.4.0", "", { "dependencies": { "commander": "^14.0.3", "listr2": "^9.0.5", "picomatch": "^4.0.3", "string-argv": "^0.3.2", "tinyexec": "^1.0.4", "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw=="],
"listr2": ["listr2@3.14.0", "", { "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", "rxjs": "^7.5.1", "through": "^2.3.8", "wrap-ansi": "^7.0.0" } }, "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g=="],
@@ -3696,9 +3702,9 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
"lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
@@ -3772,7 +3778,7 @@
"mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="],
"markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="],
"markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="],
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
@@ -3916,6 +3922,8 @@
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
"mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
@@ -4164,7 +4172,7 @@
"patch-console": ["patch-console@1.0.0", "", {}, "sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA=="],
"patch-package": ["patch-package@8.0.0", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^9.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "rimraf": "^2.6.3", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.0.33", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA=="],
"patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
@@ -4218,8 +4226,6 @@
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
"pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="],
"pinkie": ["pinkie@2.0.4", "", {}, "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="],
@@ -4234,7 +4240,7 @@
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
"plural-forms": ["plural-forms@0.5.3", "", {}, "sha512-t/hkjsTeDwaK9n/z6tUiSHySTC8sPnTiS5YF3Y5p4L+eomzXh7O0vEemkjwb68/82w0Rjw4uED3X84X7vXf9lg=="],
"plural-forms": ["plural-forms@0.5.5", "", {}, "sha512-rJw4xp22izsfJOVqta5Hyvep2lR3xPkFUtj7dyQtpf/FbxUiX7PQCajTn2EHDRylizH5N/Uqqodfdu22I0ju+g=="],
"pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="],
@@ -4442,7 +4448,7 @@
"qified": ["qified@0.5.0", "", { "dependencies": { "hookified": "^1.12.1" } }, "sha512-Zj6Q/Vc/SQ+Fzc87N90jJUzBzxD7MVQ2ZvGyMmYtnl2u1a07CejAhvtk4ZwASos+SiHKCAIylyGHJKIek75QBw=="],
"qs": ["qs@6.12.3", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ=="],
"qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
"query-string": ["query-string@4.3.4", "", { "dependencies": { "object-assign": "^4.1.0", "strict-uri-encode": "^1.0.0" } }, "sha1-u7aTucqRXCMlFbIosaArYJBD2+s="],
@@ -4682,7 +4688,7 @@
"rgbcolor": ["rgbcolor@1.0.1", "", {}, "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw=="],
"rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="],
@@ -5050,7 +5056,7 @@
"tinycolor2": ["tinycolor2@1.4.2", "", {}, "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="],
"tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
"tinyexec": ["tinyexec@1.0.4", "", {}, "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
@@ -5154,7 +5160,7 @@
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
"typedoc": ["typedoc@0.28.13", "", { "dependencies": { "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "yaml": "^2.8.1" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-dNWY8msnYB2a+7Audha+aTF1Pu3euiE7ySp53w8kEsXoYw7dMouV5A1UsTUY345aB152RHnmRMDiovuBi7BD+w=="],
"typedoc": ["typedoc@0.28.18", "", { "dependencies": { "@gerrit0/mini-shiki": "^3.23.0", "lunr": "^2.3.9", "markdown-it": "^14.1.1", "minimatch": "^10.2.4", "yaml": "^2.8.2" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-NTWTUOFRQ9+SGKKTuWKUioUkjxNwtS3JDRPVKZAXGHZy2wCA8bdv2iJiyeePn0xkmK+TCCqZFT0X7+2+FLjngA=="],
"typedoc-plugin-dt-links": ["typedoc-plugin-dt-links@2.0.21", "", {}, "sha512-BfHcksx9FKaatArJRaP9M+MhftPgN1eRlbMPikK1m4o9XmjrgCBB6re9rlntOjILSLoM2SBi5Ze0teZDAOnp5g=="],
@@ -5224,7 +5230,7 @@
"update-notifier": ["update-notifier@5.1.0", "", { "dependencies": { "boxen": "^5.0.0", "chalk": "^4.1.0", "configstore": "^5.0.1", "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", "is-ci": "^2.0.0", "is-installed-globally": "^0.4.0", "is-npm": "^5.0.0", "is-yarn-global": "^0.3.0", "latest-version": "^5.1.0", "pupa": "^2.1.1", "semver": "^7.3.4", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" } }, "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw=="],
"uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"uri-js-replace": ["uri-js-replace@1.0.1", "", {}, "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g=="],
@@ -5392,7 +5398,7 @@
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@2.3.1", "", {}, "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ=="],
"yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
"yaml-ast-parser": ["yaml-ast-parser@0.0.43", "", {}, "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="],
@@ -5462,8 +5468,6 @@
"@csstools/selector-specificity/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
"@cypress/request/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
"@cypress/request/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
@@ -5714,6 +5718,8 @@
"@svgr/core/cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" } }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="],
"@svgr/hast-util-to-babel-ast/entities": ["entities@4.4.0", "", {}, "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA=="],
"@svgr/plugin-svgo/cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" } }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="],
"@svgr/plugin-svgo/deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
@@ -5858,8 +5864,6 @@
"@vitest/expect/@vitest/utils": ["@vitest/utils@2.0.5", "", { "dependencies": { "@vitest/pretty-format": "2.0.5", "estree-walker": "^3.0.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ=="],
"@vue/compiler-core/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
@@ -5918,12 +5922,8 @@
"body-parser/bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
"body-parser/content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
"boxen/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="],
"brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
@@ -6028,12 +6028,8 @@
"cypress/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="],
"cypress/lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
"cypress/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
"cypress-multi-reporters/lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
"cypress-multi-reporters/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"cypress-split/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
@@ -6082,8 +6078,6 @@
"del/is-glob": ["is-glob@4.0.1", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg=="],
"del/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"del/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"detect-package-manager/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
@@ -6096,8 +6090,6 @@
"dom-serializer/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
"domhandler/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
"domutils/domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
@@ -6192,12 +6184,12 @@
"express/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
"express/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="],
"express/cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
"express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
"express/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
@@ -6224,8 +6216,6 @@
"find-test-names/acorn-walk": ["acorn-walk@8.2.0", "", {}, "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="],
"flat-cache/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"fork-ts-checker-webpack-plugin/chokidar": ["chokidar@3.5.3", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="],
@@ -6248,8 +6238,6 @@
"get-intrinsic/es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"gettext-parser/readable-stream": ["readable-stream@4.4.2", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA=="],
"gettext-parser/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"giget/consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
@@ -6316,8 +6304,6 @@
"import-jsx/resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="],
"import-jsx/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"ink/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
"ink/scheduler": ["scheduler@0.20.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="],
@@ -6450,8 +6436,6 @@
"json-schema-to-ts/@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"json-stable-stringify/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
"jsonwebtoken/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="],
"jspdf/@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
@@ -6464,8 +6448,6 @@
"kbar/fuse.js": ["fuse.js@6.6.2", "", {}, "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA=="],
"klaw-sync/graceful-fs": ["graceful-fs@4.2.4", "", {}, "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="],
"knex/colorette": ["colorette@2.0.19", "", {}, "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="],
"knex/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="],
@@ -6478,15 +6460,9 @@
"less/tslib": ["tslib@2.3.1", "", {}, "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="],
"lint-staged/chalk": ["chalk@5.3.0", "", {}, "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="],
"lint-staged/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="],
"lint-staged/commander": ["commander@11.0.0", "", {}, "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ=="],
"lint-staged/execa": ["execa@7.2.0", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^3.0.7", "strip-final-newline": "^3.0.0" } }, "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA=="],
"lint-staged/listr2": ["listr2@6.6.1", "", { "dependencies": { "cli-truncate": "^3.1.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^5.0.1", "rfdc": "^1.3.0", "wrap-ansi": "^8.1.0" } }, "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg=="],
"lint-staged/micromatch": ["micromatch@4.0.5", "", { "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" } }, "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA=="],
"lint-staged/listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="],
"listr2/colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
@@ -6598,13 +6574,15 @@
"nypm/consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
"oas-linter/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"nypm/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
"oas-resolver/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"oas-linter/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"oas-resolver/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"oas-resolver/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
"oas-validator/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"oas-validator/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"object-is/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
@@ -6654,13 +6632,11 @@
"patch-package/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
"patch-package/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
"patch-package/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="],
"patch-package/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="],
"patch-package/tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="],
"patch-package/yaml": ["yaml@2.4.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg=="],
"patch-package/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"path-scurry/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="],
@@ -6678,7 +6654,7 @@
"postcss-import/resolve": ["resolve@1.17.0", "", { "dependencies": { "path-parse": "^1.0.6" } }, "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w=="],
"postcss-load-config/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"postcss-load-config/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"postcss-nesting/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
@@ -6704,8 +6680,6 @@
"public-encrypt/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"qs/side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="],
"randombytes/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"randomfill/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
@@ -6902,7 +6876,7 @@
"svgo/css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
"swagger2openapi/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"swagger2openapi/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"swagger2openapi/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
@@ -6952,9 +6926,7 @@
"typed-array-length/is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
"typedoc/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"typedoc/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
"typedoc/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"unbox-primitive/which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
@@ -6962,8 +6934,6 @@
"unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"union/qs": ["qs@6.12.1", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ=="],
"unist-util-is/@types/unist": ["@types/unist@3.0.2", "", {}, "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="],
"unist-util-position/@types/unist": ["@types/unist@3.0.2", "", {}, "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ=="],
@@ -7116,6 +7086,8 @@
"@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"@eslint/eslintrc/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"@eslint/plugin-kit/@eslint/core/@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@ferocia-oss/osnap/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
@@ -7194,7 +7166,7 @@
"@loki/runner/cosmiconfig/parse-json": ["parse-json@5.1.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ=="],
"@loki/runner/cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"@loki/runner/cosmiconfig/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"@loki/target-chrome-docker/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
@@ -7220,6 +7192,8 @@
"@microsoft/api-extractor/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"@microsoft/tsdoc-config/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"@nicolo-ribaudo/eslint-scope-5-internals/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="],
"@redocly/cli/glob/minimatch": ["minimatch@10.2.0", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w=="],
@@ -7244,8 +7218,6 @@
"@rspack/plugin-react-refresh/error-stack-parser/stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
"@rushstack/node-core-library/ajv/uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"@rushstack/node-core-library/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"@storybook/addon-docs/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
@@ -7364,6 +7336,8 @@
"accepts/mime-types/mime-db": ["mime-db@1.44.0", "", {}, "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="],
"ajv-formats/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"assert/call-bind/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"assert/object.assign/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
@@ -7388,15 +7362,15 @@
"babel-plugin-macros/cosmiconfig/parse-json": ["parse-json@5.1.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ=="],
"babel-plugin-macros/cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"babel-plugin-macros/cosmiconfig/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"babel-plugin-macros/resolve/is-core-module": ["is-core-module@2.2.0", "", { "dependencies": { "has": "^1.0.3" } }, "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ=="],
"babel-plugin-ttag/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig": ["cosmiconfig@6.0.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.7.2" } }, "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="],
"babel-plugin-ttag/babel-plugin-macros/@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
"babel-plugin-ttag/babel-plugin-macros/resolve": ["resolve@1.17.0", "", { "dependencies": { "path-parse": "^1.0.6" } }, "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig": ["cosmiconfig@6.0.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.7.2" } }, "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="],
"better-opn/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="],
@@ -7404,8 +7378,6 @@
"better-opn/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"body-parser/qs/side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="],
"browserify-sign/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="],
"browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
@@ -7542,9 +7514,9 @@
"eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"express/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
"eslint/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"express/qs/side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="],
"express/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
"fast-glob/glob-parent/is-glob": ["is-glob@4.0.1", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg=="],
@@ -7568,7 +7540,7 @@
"fork-ts-checker-webpack-plugin/cosmiconfig/parse-json": ["parse-json@5.1.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ=="],
"fork-ts-checker-webpack-plugin/cosmiconfig/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
"fork-ts-checker-webpack-plugin/cosmiconfig/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"fork-ts-checker-webpack-plugin/schema-utils/@types/json-schema": ["@types/json-schema@7.0.9", "", {}, "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="],
@@ -7768,10 +7740,6 @@
"jsdom/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],
"json-stable-stringify/call-bind/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"json-stable-stringify/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"jsonwebtoken/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"jsx-ast-utils/array-includes/call-bind": ["call-bind@1.0.2", "", { "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" } }, "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA=="],
@@ -7784,27 +7752,15 @@
"jsx-ast-utils/array-includes/is-string": ["is-string@1.0.5", "", {}, "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ=="],
"lint-staged/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
"lint-staged/execa/human-signals": ["human-signals@4.3.1", "", {}, "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="],
"lint-staged/execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="],
"lint-staged/execa/npm-run-path": ["npm-run-path@5.1.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q=="],
"lint-staged/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
"lint-staged/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
"lint-staged/listr2/cli-truncate": ["cli-truncate@3.1.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" } }, "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA=="],
"lint-staged/listr2/cli-truncate": ["cli-truncate@5.2.0", "", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="],
"lint-staged/listr2/colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
"lint-staged/listr2/log-update": ["log-update@5.0.1", "", { "dependencies": { "ansi-escapes": "^5.0.0", "cli-cursor": "^4.0.0", "slice-ansi": "^5.0.0", "strip-ansi": "^7.0.1", "wrap-ansi": "^8.0.1" } }, "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw=="],
"lint-staged/listr2/log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="],
"lint-staged/listr2/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
"lint-staged/listr2/rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"lint-staged/micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"lint-staged/listr2/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
"log-update/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
@@ -7960,8 +7916,6 @@
"patch-package/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
"patch-package/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"pbkdf2/create-hash/ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="],
"pbkdf2/ripemd160/hash-base": ["hash-base@2.0.2", "", { "dependencies": { "inherits": "^2.0.1" } }, "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw=="],
@@ -7976,12 +7930,6 @@
"public-encrypt/parse-asn1/asn1.js": ["asn1.js@5.4.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0", "safer-buffer": "^2.1.0" } }, "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA=="],
"qs/side-channel/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
"qs/side-channel/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"qs/side-channel/object-inspect": ["object-inspect@1.13.1", "", {}, "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="],
"react-dnd-html5-backend/dnd-core/redux": ["redux@4.2.0", "", { "dependencies": { "@babel/runtime": "^7.9.2" } }, "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA=="],
"react-dnd/dnd-core/redux": ["redux@4.2.0", "", { "dependencies": { "@babel/runtime": "^7.9.2" } }, "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA=="],
@@ -8032,6 +7980,8 @@
"schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"serve-index/http-errors/depd": ["depd@1.1.2", "", {}, "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="],
"serve-index/http-errors/inherits": ["inherits@2.0.3", "", {}, "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="],
@@ -8070,6 +8020,8 @@
"swagger2openapi/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"table/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"terser-webpack-plugin/@jridgewell/trace-mapping/@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"terser-webpack-plugin/@jridgewell/trace-mapping/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.4.15", "", {}, "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="],
@@ -8096,7 +8048,7 @@
"typed-array-length/is-typed-array/which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
"typedoc/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"typedoc/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
"unbox-primitive/which-boxed-primitive/is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
@@ -8104,8 +8056,6 @@
"unbox-primitive/which-boxed-primitive/is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
"union/qs/side-channel": ["side-channel@1.0.6", "", { "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" } }, "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA=="],
"update-notifier/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"webpack-dev-middleware/mime-types/mime-db": ["mime-db@1.51.0", "", {}, "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="],
@@ -8232,6 +8182,8 @@
"@babel/register/find-cache-dir/pkg-dir/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="],
"@eslint/eslintrc/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"@ferocia-oss/osnap/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"@ferocia-oss/osnap/execa/onetime/mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
@@ -8254,8 +8206,6 @@
"@loki/target-native-android-emulator/tempy/del/is-glob": ["is-glob@4.0.1", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg=="],
"@loki/target-native-android-emulator/tempy/del/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"@loki/target-native-android-emulator/tempy/del/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@loki/target-native-android-emulator/tempy/unique-string/crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="],
@@ -8266,17 +8216,15 @@
"@loki/target-native-ios-simulator/tempy/del/is-glob": ["is-glob@4.0.1", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg=="],
"@loki/target-native-ios-simulator/tempy/del/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
"@loki/target-native-ios-simulator/tempy/del/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"@loki/target-native-ios-simulator/tempy/unique-string/crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="],
"@microsoft/api-extractor/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"@redocly/cli/glob/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
"@microsoft/tsdoc-config/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"@rushstack/node-core-library/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"@redocly/cli/glob/minimatch/brace-expansion": ["brace-expansion@5.0.2", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw=="],
"@rushstack/node-core-library/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
@@ -8314,6 +8262,8 @@
"@vitest/expect/@vitest/utils/estree-walker/@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
"ajv-formats/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"assert/call-bind/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"assert/call-bind/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
@@ -8324,19 +8274,13 @@
"assert/object.assign/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"babel-loader/schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"babel-plugin-macros/cosmiconfig/import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/import-fresh": ["import-fresh@3.2.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/parse-json": ["parse-json@5.1.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/yaml": ["yaml@1.10.0", "", {}, "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg=="],
"body-parser/qs/side-channel/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
"body-parser/qs/side-channel/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"body-parser/qs/side-channel/object-inspect": ["object-inspect@1.13.1", "", {}, "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
"chokidar-cli/yargs/cliui/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
@@ -8388,11 +8332,7 @@
"eslint-plugin-import-x/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"express/qs/side-channel/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
"express/qs/side-channel/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"express/qs/side-channel/object-inspect": ["object-inspect@1.13.1", "", {}, "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="],
"eslint/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"find-cypress-specs/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
@@ -8400,6 +8340,8 @@
"fork-ts-checker-webpack-plugin/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"fork-ts-checker-webpack-plugin/schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"fork-ts-checker-webpack-plugin/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"htmlparser2/domutils/dom-serializer/domelementtype": ["domelementtype@2.0.2", "", {}, "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA=="],
@@ -8508,14 +8450,6 @@
"jest-runtime/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"json-stable-stringify/call-bind/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"json-stable-stringify/call-bind/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
"json-stable-stringify/call-bind/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"json-stable-stringify/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"jsonwebtoken/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"jsx-ast-utils/array-includes/es-abstract/array-buffer-byte-length": ["array-buffer-byte-length@1.0.1", "", { "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" } }, "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg=="],
@@ -8594,27 +8528,23 @@
"jsx-ast-utils/array-includes/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"lint-staged/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
"lint-staged/listr2/cli-truncate/slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
"lint-staged/execa/onetime/mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
"lint-staged/listr2/cli-truncate/string-width": ["string-width@8.2.0", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
"lint-staged/listr2/cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
"lint-staged/listr2/log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
"lint-staged/listr2/cli-truncate/string-width": ["string-width@5.1.0", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ=="],
"lint-staged/listr2/log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
"lint-staged/listr2/log-update/ansi-escapes": ["ansi-escapes@5.0.0", "", { "dependencies": { "type-fest": "^1.0.2" } }, "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA=="],
"lint-staged/listr2/log-update/cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
"lint-staged/listr2/log-update/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
"lint-staged/listr2/log-update/strip-ansi": ["strip-ansi@7.0.1", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw=="],
"lint-staged/listr2/log-update/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"lint-staged/listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"lint-staged/listr2/wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"lint-staged/listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
"lint-staged/listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.0.1", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw=="],
"lint-staged/listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"mini-css-extract-plugin/schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"mocha/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
@@ -8726,20 +8656,10 @@
"openapi-typescript/@redocly/openapi-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
"patch-package/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"pbkdf2/create-hash/ripemd160/hash-base": ["hash-base@3.1.0", "", { "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" } }, "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA=="],
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"qs/side-channel/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"qs/side-channel/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"qs/side-channel/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
"qs/side-channel/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"react-docgen-typescript-plugin/find-cache-dir/make-dir/semver": ["semver@6.3.0", "", { "bin": { "semver": "./bin/semver.js" } }, "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="],
"redoc/@redocly/openapi-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
@@ -8754,20 +8674,20 @@
"renderkid/css-select/domutils/domelementtype": ["domelementtype@2.2.0", "", {}, "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="],
"schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"stylelint/file-entry-cache/flat-cache/flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"table/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"terser-webpack-plugin/schema-utils/ajv-formats/ajv": ["ajv@8.10.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw=="],
"typedoc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"union/qs/side-channel/call-bind": ["call-bind@1.0.7", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" } }, "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w=="],
"union/qs/side-channel/get-intrinsic": ["get-intrinsic@1.2.4", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" } }, "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ=="],
"union/qs/side-channel/object-inspect": ["object-inspect@1.13.1", "", {}, "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="],
"typedoc/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
"update-notifier/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"webpack-dev-middleware/schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"webpack-dev-server/schema-utils/ajv-formats/ajv": ["ajv@8.10.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw=="],
"webpack/browserslist/update-browserslist-db/escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
@@ -8866,6 +8786,8 @@
"@storybook/addon-webpack5-compiler-babel/babel-loader/find-cache-dir/pkg-dir/find-up": ["find-up@6.3.0", "", { "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" } }, "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw=="],
"@storybook/addon-webpack5-compiler-babel/babel-loader/schema-utils/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"@storybook/builder-webpack5/css-loader/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
"@storybook/builder-webpack5/terser-webpack-plugin/schema-utils/ajv-formats/ajv": ["ajv@8.10.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw=="],
@@ -8876,15 +8798,7 @@
"assert/object.assign/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"babel-plugin-ttag/babel-plugin-macros/cosmiconfig/import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"body-parser/qs/side-channel/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"body-parser/qs/side-channel/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"body-parser/qs/side-channel/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
"body-parser/qs/side-channel/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"babel-loader/schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"chokidar-cli/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
@@ -8896,13 +8810,7 @@
"es-get-iterator/stop-iteration-iterator/internal-slot/side-channel/object-inspect": ["object-inspect@1.13.0", "", {}, "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g=="],
"express/qs/side-channel/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"express/qs/side-channel/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"express/qs/side-channel/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
"express/qs/side-channel/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"fork-ts-checker-webpack-plugin/schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"is-typed-array/es-abstract/function.prototype.name/define-properties/has-property-descriptors": ["has-property-descriptors@1.0.0", "", { "dependencies": { "get-intrinsic": "^1.1.1" } }, "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ=="],
@@ -9016,23 +8924,23 @@
"jsx-ast-utils/array-includes/es-abstract/which-typed-array/for-each": ["for-each@0.3.3", "", { "dependencies": { "is-callable": "^1.1.3" } }, "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw=="],
"lint-staged/listr2/cli-truncate/slice-ansi/ansi-styles": ["ansi-styles@6.1.0", "", {}, "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ=="],
"lint-staged/listr2/cli-truncate/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"lint-staged/listr2/cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
"lint-staged/listr2/cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"lint-staged/listr2/cli-truncate/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"lint-staged/listr2/cli-truncate/string-width/get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
"lint-staged/listr2/cli-truncate/string-width/strip-ansi": ["strip-ansi@7.0.1", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw=="],
"lint-staged/listr2/cli-truncate/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"lint-staged/listr2/log-update/ansi-escapes/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="],
"lint-staged/listr2/log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
"lint-staged/listr2/log-update/cli-cursor/restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
"lint-staged/listr2/log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"lint-staged/listr2/log-update/slice-ansi/ansi-styles": ["ansi-styles@6.1.0", "", {}, "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ=="],
"lint-staged/listr2/log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"lint-staged/listr2/log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
"lint-staged/listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.3.0", "", {}, "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="],
"lint-staged/listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"mini-css-extract-plugin/schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"mochawesome-merge/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.2", "", { "dependencies": { "jackspeak": "^4.2.3" } }, "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg=="],
@@ -9074,8 +8982,6 @@
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"qs/side-channel/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"redoc/@redocly/openapi-core/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"reg-cli/meow/redent/strip-indent/get-stdin": ["get-stdin@4.0.1", "", {}, "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="],
@@ -9084,13 +8990,13 @@
"renderkid/css-select/domutils/dom-serializer/entities": ["entities@2.0.3", "", {}, "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="],
"union/qs/side-channel/call-bind/set-function-length": ["set-function-length@1.2.1", "", { "dependencies": { "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" } }, "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g=="],
"terser-webpack-plugin/schema-utils/ajv-formats/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"union/qs/side-channel/get-intrinsic/has-proto": ["has-proto@1.0.1", "", {}, "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="],
"webpack-dev-middleware/schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"union/qs/side-channel/get-intrinsic/has-symbols": ["has-symbols@1.0.3", "", {}, "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="],
"webpack-dev-server/schema-utils/ajv-formats/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"union/qs/side-channel/get-intrinsic/hasown": ["hasown@2.0.1", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA=="],
"webpack/schema-utils/ajv-formats/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"which-typed-array/es-abstract/function.prototype.name/define-properties/has-property-descriptors": ["has-property-descriptors@1.0.0", "", { "dependencies": { "get-intrinsic": "^1.1.1" } }, "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ=="],
@@ -9132,14 +9038,14 @@
"@storybook/addon-webpack5-compiler-babel/babel-loader/find-cache-dir/pkg-dir/find-up/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
"body-parser/qs/side-channel/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"@storybook/addon-webpack5-compiler-babel/babel-loader/schema-utils/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"@storybook/builder-webpack5/terser-webpack-plugin/schema-utils/ajv-formats/ajv/uri-js": ["uri-js@4.4.0", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g=="],
"chokidar-cli/yargs/cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"chokidar-cli/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"express/qs/side-channel/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"jest-config/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
"jest-config/glob/jackspeak/@isaacs/cliui/string-width/strip-ansi": ["strip-ansi@7.0.1", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw=="],
@@ -9186,12 +9092,26 @@
"jsx-ast-utils/array-includes/es-abstract/typed-array-length/is-typed-array/has-tostringtag": ["has-tostringtag@1.0.0", "", { "dependencies": { "has-symbols": "^1.0.2" } }, "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ=="],
"union/qs/side-channel/call-bind/set-function-length/gopd": ["gopd@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.1.3" } }, "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA=="],
"lint-staged/listr2/cli-truncate/slice-ansi/is-fullwidth-code-point/get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
"lint-staged/listr2/log-update/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"lint-staged/listr2/log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"lint-staged/listr2/log-update/slice-ansi/is-fullwidth-code-point/get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="],
"terser-webpack-plugin/schema-utils/ajv-formats/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"webpack-dev-server/schema-utils/ajv-formats/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"webpack/schema-utils/ajv-formats/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"@babel/register/find-cache-dir/pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"@storybook/addon-webpack5-compiler-babel/babel-loader/find-cache-dir/pkg-dir/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="],
"@storybook/builder-webpack5/terser-webpack-plugin/schema-utils/ajv-formats/ajv/uri-js/punycode": ["punycode@2.1.1", "", {}, "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="],
"chokidar-cli/yargs/cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="],
"@storybook/addon-webpack5-compiler-babel/babel-loader/find-cache-dir/pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],

View File

@@ -89,11 +89,11 @@
:git/url "https://github.com/eerohele/pp"}
io.github.metabase/macaw {:mvn/version "0.2.36"} ; Parse native SQL queries
io.netty/netty-codec-http {:mvn/version "4.2.9.Final"} ; override of transitive dep from aws s3
io.netty/netty-codec-http2 {:mvn/version "4.2.9.Final"} ; override of transitive dep from aws s3
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"} ; prometheus jvm collector
junegunn/grouper {:mvn/version "0.1.1"} ; Batch processing helper
kixi/stats {:mvn/version "0.5.7" ; Various statistic measures implemented as transducers
:exclusions [org.clojure/data.avl]}
lambdaisland/uri {:mvn/version "1.19.155"} ; Used by openai-clojure
medley/medley {:mvn/version "1.4.0"} ; lightweight lib of useful functions
metabase/connection-pool {:mvn/version "1.2.0"} ; simple wrapper around C3P0. JDBC connection pools
metabase/saml20-clj {:mvn/version "4.2.1"
@@ -108,8 +108,6 @@
:git/sha "b4a2d78647a049870b316753d7aae7fb4ade3ae5"} ; patched to resolve an issue causing OOM
net.carcdr/oidc-provider {:mvn/version "0.6.2"} ; OAuth/OIDC server abstract implementation
net.cgrand/macrovich {:mvn/version "0.2.2"} ; utils for writing macros for both Clojure & ClojureScript
net.clojars.wkok/openai-clojure {:mvn/version "0.23.0"
:exclusions [lambdaisland/uri]} ; OpenAI
net.redhogs.cronparser/cron-parser-core {:mvn/version "3.5" ; describe Cron schedule in human-readable language
:exclusions [org.slf4j/slf4j-api]}
net.sf.cssbox/cssbox {:mvn/version "5.0.2" ; HTML / CSS rendering
@@ -330,7 +328,7 @@
:test
{:extra-paths ["test_config"]
:exec-fn metabase.test-runner/find-and-run-tests-cli
:exec-args {:exclude-tags [:metabot-v3/e2e]}
:exec-args {}
:jvm-opts ["-Dmb.run.mode=test"
"-Dmacaw.run.mode=test"
"-Dmb.db.in.memory=true"
@@ -903,7 +901,7 @@
:extra-paths ["dev/src"]
:main-opts ["-m" "dev.migrate"]}
;; run tests against MLv2. Very fast since this is almost all ^:parallel
;; run tests against Lib. Very fast since this is almost all ^:parallel
:test/lib
{:exec-args {:only ["test/metabase/lib"
"test/metabase/lib_be"]}}
@@ -930,7 +928,7 @@
{:exec-args {:only ["enterprise/backend/test/metabase_enterprise/transforms"
"enterprise/backend/test/metabase_enterprise/transforms_python"]}}
;; test all MBQL related stuff: MLv2, the legacy shared `metabase.legacy-mbql` code, and the QP
;; test all MBQL related stuff: Lib, the legacy shared `metabase.legacy-mbql` code, and the QP
;;
;; clj -X:dev:ee:ee-dev:test:test/mbql
:test/mbql

View File

@@ -23,8 +23,8 @@
(.-dataset_query (current-card)))
(defn current-query
"Gets the MLv2 query for the currently loaded question.
"Gets the MBQL 5 query for the currently loaded question.
Hack: This relies on a dev-mode-only global property that's set whenever a Question object is converted to MLv2."
Hack: This relies on a dev-mode-only global property that's set whenever a Question object is converted to Lib/MBQL 5."
[]
(.-__MLv2_query js/window))
(.-__lib_query js/window))

View File

@@ -13,6 +13,7 @@
(:require
[clojure.string :as str]
[dev.deps-graph :as deps-graph]
[rewrite-clj.node :as r.node]
[rewrite-clj.parser :as r.parser]
[rewrite-clj.zip :as z]))
@@ -124,15 +125,34 @@
(reduce
(fn [root config-key]
(let [mod-cfg (some-> (find-module-config root module-sym) z/right)
val-zloc (when mod-cfg (find-key-value mod-cfg config-key))]
;; Only update keys that already exist and are sets (skip :bypass, :any, etc.)
(if (and val-zloc (set? (z/sexpr val-zloc)))
(let [computed (get-in boundaries [config-key module-sym] #{})]
(if (seq computed)
(z/root (z/replace val-zloc (r.parser/parse-string (model-set-str computed))))
;; Empty — remove the key-value pair
(-> val-zloc z/remove z/remove z/root)))
root)))
val-zloc (when mod-cfg (find-key-value mod-cfg config-key))
computed (get-in boundaries [config-key module-sym] #{})]
(cond
;; Key exists with a non-set value (e.g. :bypass, :any) — leave it alone.
(and val-zloc (not (set? (z/sexpr val-zloc))))
root
;; Key exists with a set value — replace or remove.
val-zloc
(if (seq computed)
(z/root (z/replace val-zloc (r.parser/parse-string (model-set-str computed))))
(-> val-zloc z/remove z/remove z/root))
;; Key missing and nothing to add.
(empty? computed)
root
;; Key missing — append `<newline><indent>:key <set>` to the module config map,
;; splicing new children directly to avoid the separator spaces `append-child` inserts.
:else
(let [m-node (z/node mod-cfg)
new-children (concat (r.node/children m-node)
[(r.node/newlines 1)
(r.node/spaces 3)
(r.node/keyword-node config-key)
(r.node/spaces 1)
(r.parser/parse-string (model-set-str computed))])]
(z/root (z/replace mod-cfg (r.node/replace-children m-node new-children)))))))
root
[:model-exports :model-imports]))
(z/root root-zloc)

View File

@@ -118,7 +118,7 @@
[part]
(-> part
(assoc-in [:card :include_csv] true)
result-attachment
(result-attachment (mt/user->id :crowberto))
first
:content
slurp

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
(ns dev.server.middleware.proxy
"Development-only middleware to proxy API requests to a remote backend."
(:require
[clj-http.client :as http]
[clojure.string :as str]
[metabase.config.core :as config]
[metabase.util :as u]
[metabase.util.log :as log]))
(set! *warn-on-reflection* true)
(defn- remote-api-url
"Remote backend URL from MB_REMOTE_API_URL env var. Read at runtime, not compile time."
[]
(config/config-str :mb-remote-api-url))
(defn- api-call?
"Is this ring request an API call (exact `/api` or path starts with `/api/`)?"
[{:keys [^String uri]}]
(or (= uri "/api")
(str/starts-with? uri "/api/")))
(defn- build-target-url
"Build the full URL for the remote backend."
[uri query-string]
(str (remote-api-url) uri (when query-string (str "?" query-string))))
(defn- request-method->clj-http-fn
"Convert Ring request method keyword to clj-http function."
[method]
(case method
:get http/get
:post http/post
:put http/put
:delete http/delete
:patch http/patch
:head http/head
:options http/options
http/get))
(def ^:private allowed-proxy-request-headers
#{"accept"
"accept-language"
"authorization"
"content-type"
"cookie"
"user-agent"})
(def ^:private blocked-proxy-request-headers
#{"connection"
"content-length"
"forwarded"
"host"
"proxy-authenticate"
"proxy-authorization"
"te"
"trailer"
"transfer-encoding"
"upgrade"
"x-real-ip"})
(defn- blocked-proxy-request-header?
[header-name]
(or (contains? blocked-proxy-request-headers header-name)
(str/starts-with? header-name "x-forwarded-")
(str/starts-with? header-name "proxy-")))
(defn- proxy-headers
"Filter and prepare headers for proxying using an explicit allowlist and origin rewrite."
[headers]
(let [forwarded-headers
(reduce-kv
(fn [result header-name value]
(let [normalized-name (u/lower-case-en (name header-name))]
(if (and (contains? allowed-proxy-request-headers normalized-name)
(not (blocked-proxy-request-header? normalized-name)))
(assoc result normalized-name value)
result)))
{}
headers)]
(assoc forwarded-headers "origin" (remote-api-url))))
(defn- rewrite-set-cookie
"Remove domain and secure attributes from Set-Cookie header so cookies work on localhost."
[cookie-value]
(-> cookie-value
(str/replace #";\s*[Dd]omain=[^;]*" "")
(str/replace #";\s*[Ss]ecure" "")
(str/replace #";\s*[Ss]ame[Ss]ite=[^;]*" "; SameSite=Lax")))
(defn- find-set-cookie-key
"Find the Set-Cookie header key (case-insensitive)."
[headers]
(some (fn [k] (when (= "set-cookie" (u/lower-case-en (name k))) k))
(keys headers)))
(defn- rewrite-response-cookies
"Rewrite Set-Cookie headers to work with localhost."
[headers]
(if-let [cookie-key (find-set-cookie-key headers)]
(let [cookies (get headers cookie-key)
rewritten (if (sequential? cookies)
(mapv rewrite-set-cookie cookies)
(rewrite-set-cookie cookies))]
(-> headers
(dissoc cookie-key)
(assoc "Set-Cookie" rewritten)))
headers))
(defn- clj-http-cookie->set-cookie-header
"Convert a clj-http cookie map entry to a Set-Cookie header string."
[[cookie-name {:keys [value path expires max-age http-only]}]]
(str cookie-name "=" value
(when path (str "; Path=" path))
(when expires (str "; Expires=" expires))
(when max-age (str "; Max-Age=" max-age))
(when http-only "; HttpOnly")
"; SameSite=Lax"))
(def ^:private localhost-hosts
#{"127.0.0.1" "::1" "localhost"})
(defn- strip-port
[host]
(cond
(nil? host)
nil
(str/starts-with? host "[")
(or (second (re-find #"^\[([^\]]+)\](?::\d+)?$" host)) host)
(> (count (re-seq #":" host)) 1)
host
:else
(first (str/split host #":" 2))))
(defn- localhost-request?
[{:keys [headers server-name]}]
(let [host (or (get headers "host") server-name)]
(contains? localhost-hosts (some-> host strip-port u/lower-case-en))))
(defn- add-cookies-to-headers
"Convert clj-http :cookies map to Set-Cookie headers."
[headers cookies]
(if (seq cookies)
(let [set-cookie-headers (mapv clj-http-cookie->set-cookie-header cookies)]
(assoc headers "Set-Cookie" set-cookie-headers))
headers))
(defn- rewrite-response-headers
[request raw-headers cookies]
(let [localhost? (localhost-request? request)]
(cond-> (dissoc raw-headers "transfer-encoding" "content-encoding")
localhost? rewrite-response-cookies
(and localhost? (seq cookies)) (add-cookies-to-headers cookies))))
(defn- proxy-request
"Proxy a request to the remote backend and return the response."
{:arglists '([request])}
[{:keys [request-method uri query-string headers body], :as request}]
(let [target-url (build-target-url uri query-string)
http-fn (request-method->clj-http-fn request-method)
opts (cond-> {:headers (proxy-headers headers)
:throw-exceptions false
:as :stream
:redirect-strategy :none}
body (assoc :body body))]
(log/debugf "Proxying %s %s to %s" (name request-method) uri target-url)
(try
(let [response (http-fn target-url opts)
raw-headers (:headers response)
cookies (:cookies response)
rewritten-headers (rewrite-response-headers request raw-headers cookies)]
(log/infof "Proxy %s %s -> %s (cookies-returned=%d)"
(name request-method) uri (:status response) (count cookies))
{:status (:status response)
:headers rewritten-headers
:body (:body response)})
(catch Exception e
(log/errorf e "Error proxying request to %s" target-url)
{:status 502
:body "Proxy error"}))))
(defn wrap-remote-api-proxy
"Middleware that proxies API requests to a remote backend when MB_REMOTE_API_URL is set.
Only active in dev mode."
[handler]
(if-let [url (and config/is-dev? (remote-api-url))]
(do
(log/infof "Remote API proxy enabled, forwarding /api/* to %s" url)
(fn [request respond raise]
(if (api-call? request)
(try
(respond (proxy-request request))
(catch Exception e
(raise e)))
(handler request respond raise))))
handler))

View File

@@ -0,0 +1,129 @@
(ns dev.server.middleware.proxy-test
(:require
[clj-http.client :as http]
[clojure.string :as str]
[clojure.test :refer :all]
[dev.server.middleware.proxy :as mw.proxy]
[metabase.config.core :as config]
[metabase.test :as mt]))
(def ^:private remote-url "https://remote.example.com")
(defn- proxy-request
[request]
(@#'mw.proxy/proxy-request request))
(defn- run-remote-api-proxy
[wrapped-handler request]
(let [response (atom nil)]
(wrapped-handler request
(fn [resp] (reset! response resp))
(fn [e] (throw e)))
@response))
(deftest proxy-headers-allowlist-test
(let [captured-opts (atom nil)]
(with-redefs [config/config-str (fn [_] remote-url)
http/get (fn [_url opts]
(reset! captured-opts opts)
{:status 200
:headers {}
:body "ok"})]
(proxy-request {:request-method :get
:uri "/api/card/1"
:headers {"Accept" "application/json"
"Accept-Language" "en-US"
"Authorization" "Bearer abc"
"Content-Type" "application/json"
"Cookie" "metabase.SESSION=abc"
"User-Agent" "ring-test"
"X-Forwarded-For" "1.2.3.4"
"Host" "localhost:3000"
"X-Real-IP" "5.6.7.8"
"X-Custom" "drop-me"}})
(is (= {"accept" "application/json"
"accept-language" "en-US"
"authorization" "Bearer abc"
"content-type" "application/json"
"cookie" "metabase.SESSION=abc"
"user-agent" "ring-test"
"origin" remote-url}
(get @captured-opts :headers))))))
(deftest api-path-matching-test
(let [proxied? (atom false)
local-handler (fn [_request respond _raise]
(respond {:status 418
:headers {}
:body "local"}))]
(with-redefs [config/is-dev? true
config/config-str (fn [_] remote-url)
http/get (fn [_url _opts]
(reset! proxied? true)
{:status 200
:headers {}
:body "proxied"})]
(let [wrapped-handler (mw.proxy/wrap-remote-api-proxy local-handler)]
(reset! proxied? false)
(is (= 200
(:status (run-remote-api-proxy wrapped-handler
{:request-method :get
:uri "/api"
:headers {"host" "localhost:3000"}}))))
(is @proxied?)
(reset! proxied? false)
(is (= 418
(:status (run-remote-api-proxy wrapped-handler
{:request-method :get
:uri "/apiary"
:headers {"host" "localhost:3000"}}))))
(is (not @proxied?))))))
(deftest cookie-rewrite-localhost-only-test
(with-redefs [config/config-str (fn [_] remote-url)
http/get (fn [_url _opts]
{:status 200
:headers {"Set-Cookie" "metabase.SESSION=abc; Domain=remote.example.com; Secure; SameSite=None"}
:body "ok"})]
(let [localhost-response (proxy-request {:request-method :get
:uri "/api/session/properties"
:headers {"host" "localhost:3000"}})
localhost-cookie (get-in localhost-response [:headers "Set-Cookie"])
non-localhost-response (proxy-request {:request-method :get
:uri "/api/session/properties"
:headers {"host" "devbox.example.com:3000"}})
non-localhost-cookie (get-in non-localhost-response [:headers "Set-Cookie"])]
(is (string? localhost-cookie))
(is (str/includes? localhost-cookie "SameSite=Lax"))
(is (not (str/includes? localhost-cookie "Domain=")))
(is (not (str/includes? localhost-cookie "Secure")))
(is (= "metabase.SESSION=abc; Domain=remote.example.com; Secure; SameSite=None"
non-localhost-cookie)))))
(deftest proxy-error-response-does-not-leak-details-test
(with-redefs [config/config-str (fn [_] remote-url)
http/get (fn [_url _opts]
(throw (ex-info "Proxy error: upstream timeout 10.2.0.4" {})))]
(is (= {:status 502
:body "Proxy error"}
(select-keys (proxy-request {:request-method :get
:uri "/api/session/properties"
:headers {"host" "localhost:3000"}})
[:status :body])))))
(deftest proxy-logging-does-not-expose-cookie-values-test
(with-redefs [config/config-str (fn [_] remote-url)
http/get (fn [_url _opts]
{:status 200
:headers {}
:cookies {"metabase.SESSION" {:value "super-secret-session-token"}}
:body "ok"})]
(mt/with-log-messages-for-level [messages :info]
(proxy-request {:request-method :get
:uri "/api/session/properties"
:headers {"host" "localhost:3000"}})
(is (empty?
(filter #(str/includes? (:message %) "super-secret-session-token")
(messages)))))))

View File

@@ -61,7 +61,7 @@ Once one or more people have started to work on a feature, it should be marked [
### E) Verification and merging
All PRs that involve more than an insignificant change should be reviewed. See our [Code Review Process](https://www.metabase.com/docs/master/developers-guide/code-reviews).
All PRs that involve more than an insignificant change should be reviewed. See our [Code Review Process](https://www.metabase.com/docs/latest/developers-guide/code-reviews).
If all goes well, the feature gets coded up, verified and then the pull request gets merged! High-fives all around.
@@ -95,7 +95,7 @@ By our definition, "Bugs" are situations where the program doesn't do what it wa
There are a lot of docs, which means keeping them up to date is hard. If you notice inconsistencies, errors, or outdated information, please help us keep them current!
Note that **we cannot accept translations for documentation at this time**. We support [in-app translations](https://www.metabase.com/docs/master/configuring-metabase/localization), and only support languages that have 100% coverage. But 1) the in-app text is orders of magnitude shorter than our docs, 2) it changes at a slower pace, and 3) we have a lot of people help out. We may consider supporting docs in multiple languages in the future, but for now we need to focus our resources on improving our existing documentation (and expanding it to include all of the new features we're adding).
Note that **we cannot accept translations for documentation at this time**. We support [in-app translations](https://www.metabase.com/docs/latest/configuring-metabase/localization), and only support languages that have 100% coverage. But 1) the in-app text is orders of magnitude shorter than our docs, 2) it changes at a slower pace, and 3) we have a lot of people help out. We may consider supporting docs in multiple languages in the future, but for now we need to focus our resources on improving our existing documentation (and expanding it to include all of the new features we're adding).
### Working on features

View File

@@ -150,8 +150,9 @@ Metabase's reference documentation.
- [AI overview](./ai/start.md)
- [Metabot](./ai/metabot.md)
- [Setting up Metabot](./ai/settings.md)
- [SQL generation](./ai/sql-generation.md)
- [AI settings](./ai/settings.md)
- [Agent API](./ai/agent-api.md)
- [MCP server](./ai/mcp.md)
### Exploration and organization

View File

@@ -4,7 +4,7 @@ title: Basic actions
# Basic actions
Basic actions are "implicit" [actions](./introduction.md) that do things that people typically want to do when interacting with a database: Create, Update, Delete. Basic actions auto-track the schema of the source table backing the model. By auto-track the schema, we mean that Metabase will create action forms for people to fill out that include all of the fields from the primary source table that underlies that model.
Basic actions are "implicit" [actions](./introduction.md) that do things that people typically want to do when interacting with a database: Create, Update, Delete. Basic actions auto-track the schema of the source table backing the model. By auto-tracking the schema, we mean that Metabase will create action forms for people to fill out that include all of the fields from the primary source table that underlies that model.
If you only want to give people the option to update a subset of columns, or update columns in multiple tables, you can write a [custom action](./custom.md).
@@ -62,7 +62,7 @@ Because basic actions are made of magic, you cannot archive them. You can just t
## Basic actions from object detail view
If you have basic actions enabled for a model, you can click on an individual record to view its object detail. From that object detail model, you can click on the ellipses (**...**) and select update or delete to modify that specific record.
If you have basic actions enabled for a model, you can click on an individual record to view its object detail. From that object detail modal, you can click on the ellipses (**...**) and select update or delete to modify that specific record.
![Update record from object detail view](./images/update-record.png)

View File

@@ -55,7 +55,7 @@ There are multiple ways to run actions:
Just something to clarify here: actions, even though they are added to models, make their changes to the underlying table that a model queries. Which means that anyone who has access to the underlying table, or to questions or other models based on that table, will be able to see the effects of an action. Tools other than Metabase that are connected to that database will also pick up these changes.
In this sense, models are containers for actions; models are a way to organize actions. In fact, you could (in theory) add a [custom action](./custom.md) to a model that performs some update unrelated to its model's data. For example, you could write a custom action that updates the `Accounts` table, and add that action to a model that only queries an unrelated table (e.g., the `Orders` table). But, you know, maybe don't do that (unless you have a good reason). [Basic actions](./basic.md), however, are only be available for models that wrap a single raw table.
In this sense, models are containers for actions; models are a way to organize actions. In fact, you could (in theory) add a [custom action](./custom.md) to a model that performs some update unrelated to its model's data. For example, you could write a custom action that updates the `Accounts` table, and add that action to a model that only queries an unrelated table (e.g., the `Orders` table). But, you know, maybe don't do that (unless you have a good reason). [Basic actions](./basic.md), however, are only available for models that wrap a single raw table.
Before using actions in production, consider playing around with actions on some sample data (like the Sample Database included with Metabase) to get a feel for how they work.

View File

@@ -6,7 +6,13 @@ summary: The Agent API is a REST API for building headless, agentic BI applicati
# Agent API
The Agent API is a REST API for building headless, agentic BI applications on top of Metabase's semantic layer, scoped to an authenticated user's permissions.
The [Agent API](../api.html#tag/apiagent) is a REST API for building headless, agentic BI applications on top of Metabase's semantic layer, scoped to an authenticated user's permissions.
## Agent API endpoints and reference
Check out the API endpoint docs: [/api/agent](../api.html#tag/apiagent).
You can also point your AI to the [Agent API reference](https://github.com/metabase/metabase/blob/master/src/metabase/agent_api/reference.md) to bootstrap your agent.
## Example application
@@ -20,28 +26,63 @@ There are a few advantages to using the agent API over the Metabase API.
- Agent endpoints are explicitly supported for building agentic BI applications.
- The agent API is versioned, so your apps can rely on consistent responses.
- Supports JWT auth so requests can be scoped to a user's permissions.
- Requests are scoped to the authenticated user's permissions.
- Doesn't require you to work with MBQL, which is Metabase's querying language.
## Supported features
The Agent API supports:
- Discovering tables and metrics
- Searching for tables and metrics
- Inspecting their fields
- Constructing and executing queries
## Agent API endpoints and reference
If you want to connect an MCP-compatible AI client (like Claude Desktop) without writing custom code, see the [MCP server](./mcp.md), which builds on this API.
Check out the API endpoints: [/api/agent](../api.html#tag/apiagent).
## Row limits and pagination
You can also point your AI to the [Agent API reference](https://github.com/metabase/metabase/blob/master/src/metabase/agent_api/reference.md) to bootstrap your agent.
Queries return a maximum of 200 rows per request. To page through larger result sets, use `POST /api/agent/v1/query`, which returns a `continuation_token` when more rows are available. Pass the token back to get the next page.
## Authentication
The Agent API uses stateless JWT authentication. JWT must be configured in Metabase admin settings (**Admin** > **Settings** > **Authentication** > **JWT**).
The Agent API supports three authentication methods.
Pass a signed JWT directly in each request:
### API key
The simplest option. Create an [API key](../people-and-groups/api-keys.md) in **Admin** > **Settings** > **Authentication** > **API keys**, then pass it in each request:
```
X-API-Key: YOUR_API_KEY
```
The key's permissions are determined by the [group](../people-and-groups/managing.md#groups) assigned to that key. Unlike session or JWT authentication, API key requests aren't scoped to an individual user. Anyone using a client app that authenticates with an API key will have the same permissions as that key.
### Session token
Log in with a Metabase username and password to get a session token, then pass it in subsequent requests.
**1. Get a session token:**
```
POST /api/session
Content-Type: application/json
{"username": "analyst@example.com", "password": "your-password"}
```
The response body contains an `id` field -- that's your session token.
**2. Pass the token in each request:**
```
X-Metabase-Session: SESSION_TOKEN
```
### JWT
{% include plans-blockquote.html feature="JWT-based authentication" %}
If you've configured [JWT-based authentication](../people-and-groups/authenticating-with-jwt.md) (**Admin** > **Settings** > **Authentication** > **JWT**), you can pass a signed JWT directly:
```
Authorization: Bearer <jwt>
@@ -66,17 +107,13 @@ Example JWT payload:
}
```
### Session-based authentication
Exchange a JWT at `POST /auth/sso/to_session` to get a session token, then pass it via:
```
X-Metabase-Session: <session-token>
```
You can also exchange a JWT for a session token via `POST /auth/sso/to_session` (note: this endpoint is under `/auth`, not `/api`), then use the `X-Metabase-Session` header as described above.
## Further reading
- [MCP server](./mcp.md)
- [Agent API complete reference](https://github.com/metabase/metabase/blob/master/src/metabase/agent_api/reference.md)
- [Metabase Agent API demo](https://github.com/metabase/metabase-agent-api-demo)
- [Metabase API docs](../api.html)
- [API keys](../people-and-groups/api-keys.md)
- [JWT authentication](../people-and-groups/authenticating-with-jwt.md)

Some files were not shown because too many files have changed in this diff Show More