From f80849c875ea8f2618658a841c0c179d7ab7b4f7 Mon Sep 17 00:00:00 2001 From: datadog-bits-staging <264369727+datadog-bits-staging@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:37:39 +0000 Subject: [PATCH] Add sea foam Kelly app Co-authored-by: setnilson <110850377+setnilson@users.noreply.github.com> --- .../.gitignore | 28 ++ .../AGENTS.md | 44 +++ .../CLAUDE.md | 1 + .../docs/agents/backend-functions.md | 91 ++++++ .../docs/agents/build-upload-auth.md | 156 +++++++++++ .../docs/agents/cicd.md | 42 +++ .../docs/agents/data.md | 61 +++++ .../docs/agents/runtime-context.md | 30 ++ .../docs/agents/upgrading.md | 63 +++++ .../docs/agents/workflow-automation.md | 150 ++++++++++ .../eslint.config.mjs | 23 ++ .../index.html | 15 + .../package.json | 43 +++ .../public/favicon.ico | Bin 0 -> 40436 bytes .../src/App.css | 20 ++ .../src/App.tsx | 16 ++ .../src/main.tsx | 24 ++ .../tsconfig.json | 26 ++ .../vite.config.ts | 57 ++++ package-lock.json | 259 +++++++++++++++++- 20 files changed, 1140 insertions(+), 9 deletions(-) create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/.gitignore create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/AGENTS.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/CLAUDE.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/backend-functions.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/build-upload-auth.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/cicd.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/data.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/runtime-context.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/upgrading.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/workflow-automation.md create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/eslint.config.mjs create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/index.html create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/package.json create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/public/favicon.ico create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.css create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.tsx create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/main.tsx create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/tsconfig.json create mode 100644 apps/sarah-s-app-thu-aug-20-12-29-57-pm/vite.config.ts diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/.gitignore b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/.gitignore new file mode 100644 index 0000000..6dde684 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/.gitignore @@ -0,0 +1,28 @@ +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Environment variables +.env +.env.local +.env.*.local diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/AGENTS.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/AGENTS.md new file mode 100644 index 0000000..fcfd196 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/AGENTS.md @@ -0,0 +1,44 @@ +# sarah-s-app-thu-aug-20-12-29-57-pm + +This is a Datadog App scaffolded with `npm create @datadog/apps`. + +Datadog Apps are React + TypeScript applications bundled by Vite and embedded in the Datadog product experience. Frontend code runs in the browser. Backend functions live in `*.backend.*` files and execute through Datadog's managed runtime. + +## DRUIDS components + +This app uses the published `@datadog/druids` package. In scaffolded apps, import components from `@datadog/druids/[category]/[Component]`; do not import private web-ui paths like `@druids/ui/...`. + +- The scaffold already wraps the app in `DruidsEnvironment` and imports `@datadog/druids/styles.css` at the React root. Keep that setup in place if you restructure the root. +- Discover available components by browsing the installed package. Treat the `exports` map in `node_modules/@datadog/druids/package.json` as the source of truth for public entry points, and explore the corresponding category directories under `node_modules/@datadog/druids/dist` instead of relying on a hard-coded component list. +- After choosing an exported component, inspect its TypeScript definitions under `node_modules/@datadog/druids/dist/**/*.d.mts` before inventing props or imports. For icons, only use entry points that are present in the installed package. +- Start with the [DRUIDS docs](https://druids.datadoghq.com/), especially [Foundations](https://druids.datadoghq.com/foundations), [Spacing & Layout](https://druids.datadoghq.com/foundations/spacing-and-layout), [Typography](https://druids.datadoghq.com/foundations/typography), and [Components](https://druids.datadoghq.com/components). The docs cover the full internal DRUIDS library; the public `@datadog/druids` package exposes only a subset for Datadog Apps. +- Use the docs after confirming the local API to understand design intent, layout guidance, examples, and component selection tradeoffs. +- Prefer appropriate DRUIDS layout and typography components over ad hoc HTML wrappers or inline CSS. Use component props and design tokens for spacing, size, and variants. + +## Read relevant guides + +- For embedded Datadog app context, routing, navigation, browser storage, or parent-page constraints, read `docs/agents/runtime-context.md`. +- For writing backend functions in `*.backend.ts` or `*.backend.js` files, or for calling backend functions from frontend code, read `docs/agents/backend-functions.md`. +- For local development, auth, deploy, publish, `DD_APPS_PUBLISH`, or deploy troubleshooting, read `docs/agents/build-upload-auth.md`. +- For GitHub Actions CI/CD setup, read `docs/agents/cicd.md`. +- For choosing between DDSQL and Action Catalog, configuring Connections, or querying app datastores, read `docs/agents/data.md`. +- For triggering or polling a Workflow Automation workflow from a backend function, read `docs/agents/workflow-automation.md`. +- For upgrading `@datadog/vite-plugin` or `@datadog/action-catalog`, read `docs/agents/upgrading.md`. + +## General rules + +- Keep privileged Datadog API calls, third-party calls, and secret-dependent work in backend functions. +- Do not hardcode API keys, app keys, OAuth tokens, passwords, or third-party credentials. +- Set the `apps.name` and `apps.description` fields in the `datadogVitePlugin` configuration in `vite.config.ts` to concise, user-facing values that describe the app. +- Prefer the generated npm scripts in `package.json`; this scaffold uses npm. +- Never change `apps.identifier` in `vite.config.ts`. It is this app's permanent identity, not the app's UUID from App Builder — changing it makes the next upload create a duplicate app. +- Before inventing package imports or component props, inspect installed package exports and TypeScript definitions. + +## Broader Datadog Apps guidance + +- For scaffolding a new app from scratch, use the Datadog Apps agent skill when it is available. +- Use Playwright when available for browser validation, including screenshots for visual design changes. +- Use the Datadog `pup` CLI (https://github.com/DataDog/pup) when available for Datadog API inspection and troubleshooting. +- Use Datadog MCP tools when available for Datadog-specific lookup, diagnostics, or API-backed workflows. + +Primary docs: https://docs.datadoghq.com/actions/datadog_apps/ diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/CLAUDE.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/backend-functions.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/backend-functions.md new file mode 100644 index 0000000..cf5f067 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/backend-functions.md @@ -0,0 +1,91 @@ +# Backend Functions + +Backend functions are Datadog-managed server-side functions. They do not ship to the browser. + +## File and export conventions + +- Backend files match `*.backend.ts`, `*.backend.js`, `*.backend.tsx`, or `*.backend.jsx`. +- Export named async functions from backend files. +- Frontend code imports backend functions like normal ES modules. +- The Datadog Vite plugin rewrites backend imports into proxy calls at build time. + +## Runtime behavior + +- Local development bundles backend functions through Vite middleware and executes them through Datadog preview infrastructure. +- Deployed apps call backend functions through the embedded Datadog runtime bridge. +- Backend functions do not run in the user's local Node.js process. + +## Backend helper library + +Datadog provides `@datadog/apps-backend`, a library of tools to make backend function development easier. Check it out when working on backend functions. + +## Security rules + +- Keep privileged Datadog API calls, third-party calls, and secret-dependent work in backend functions. +- Never hardcode API keys, app keys, OAuth tokens, passwords, private keys, or third-party credentials. +- Frontend code should not call Datadog APIs or other privileged services directly. +- Validate frontend inputs before using them in backend function calls. + +## Action Catalog + +Prefer `@datadog/action-catalog` for supported Datadog, cloud, SaaS, and HTTP workflows. Check the installed package exports before generating imports; do not invent subpaths. + +```ts +// src/listHosts.backend.ts +import { listHosts, type ListHostsResponse } from '@datadog/action-catalog/dd/hosts'; + +export async function getHosts(filter?: string): Promise { + return listHosts({ + inputs: { + filter: filter ?? '*', + count: 10, + include_hosts_metadata: true, + }, + }); +} +``` + +```tsx +// src/App.tsx +import { useQuery } from '@tanstack/react-query'; + +import { getHosts } from './listHosts.backend'; + +function HostCount() { + const hostsQuery = useQuery({ + queryKey: ['hosts', '*'], + queryFn: () => getHosts('*'), + }); + + if (hostsQuery.isLoading) { + return Loading hosts...; + } + + if (hostsQuery.isError) { + return Unable to load hosts.; + } + + return {hostsQuery.data?.host_list?.length ?? 0}; +} +``` + +Always wrap backend function proxies before passing them to React Query or +another framework callback. Do not use `queryFn: getHosts`: React Query calls +its callback with a context object containing an `AbortSignal`, and the +embedded app bridge cannot structured-clone that object for a backend +invocation. Pass only the cloneable arguments the backend function declares: + +```tsx +// Correct +queryFn: () => getHosts('*'), + +// Incorrect +queryFn: getHosts, +``` + +The generated app wraps React in `QueryClientProvider` in `src/main.tsx`. Prefer React Query for backend function calls that need loading, error, caching, retry, refetch, or deduplication behavior. + +## Broader data workflows + +- For choosing between DDSQL and Action Catalog, configuring Connections, or querying app datastores, read `docs/agents/data.md`. +- For triggering or polling a Workflow Automation workflow, read `docs/agents/workflow-automation.md`. diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/build-upload-auth.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/build-upload-auth.md new file mode 100644 index 0000000..9b125b3 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/build-upload-auth.md @@ -0,0 +1,156 @@ +# Build, Auth, Deploy, And Publish + +This scaffold uses npm. Prefer the scripts in `package.json` over ad hoc commands. + +## Scripts + +- `npm run dev` — start the local Vite dev server. +- `npm run typecheck` — TypeScript check without emitting files. Use this for credential-free validation. +- `npm run lint` / `npm run lint:fix` — run ESLint. +- `npm run build` — production build (does not deploy by default). +- `npm run deploy` — build, upload, and publish the app live. +- `npm run publish` — publish the most recently uploaded version without rebuilding. + +## App identity (do not change) + +- `apps.identifier` in `vite.config.ts` is this app's permanent identity. It was generated once when the project was scaffolded. +- Every upload updates the app that matches this identifier. **Never change it.** Changing it — including pasting the app's UUID from App Builder into it — makes the next upload create a brand-new duplicate app instead of updating the existing one. +- `apps.identifier` is NOT the app UUID shown in App Builder. The UUID is assigned by Datadog; the identifier is a stable local key. Do not copy one into the other. + +## Auth + +Apps use **OAuth by default**. On first `npm run dev`, a browser window opens for Datadog login and the token is cached in the system keyring. No credentials needed in advance. + +**API key fallback** — use when OAuth isn't available (headless CI, restricted networks): + +1. Confirm `.env.local` is gitignored — the scaffold includes `*.local` in `.gitignore` by default. +2. Create `.env.local` at the project root with placeholders: + +``` +DD_API_KEY=REPLACE_WITH_YOUR_API_KEY +DD_APP_KEY=REPLACE_WITH_YOUR_APP_KEY +``` + +3. Open the file for editing — do not ask users to paste key values into the conversation: + +```bash +cursor .env.local 2>/dev/null || code .env.local 2>/dev/null || open .env.local +``` + +4. Direct the user to replace the placeholders. Keys are at: + - API keys: `https://app.datadoghq.com/organization-settings/api-keys` + - Application keys: `https://app.datadoghq.com/organization-settings/application-keys` — the key needs **two scopes**: **Actions API Access** and **Apps** + +Vite reads `.env.local` automatically once real values are in place. + +**`.env.local` not being picked up** — the scaffolded `vite.config.ts` reads credentials via `process.env`, which does not include `.env.local` at config evaluation time. Fix with Vite's `loadEnv`: + +```ts +import { defineConfig, loadEnv } from 'vite'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ''); + return { + plugins: [datadogVitePlugin({ auth: { apiKey: env.DD_API_KEY, appKey: env.DD_APP_KEY } })], + }; +}); +``` + +Restart the dev server after updating `vite.config.ts`. + +## Non-US1 site + +Set `auth.site` in `vite.config.ts` so local dev and deploys target the right site: + +```ts +datadogVitePlugin({ auth: { site: '' } }); +``` + +## Deploy + +Build, upload, and publish in one command: + +```bash +npm run deploy +``` + +With API keys (CI environments): + +```bash +export DD_API_KEY="" +export DD_APP_KEY="" +npm run deploy +``` + +A successful deploy prints a Datadog URL for the app. + +**Deploy without publishing** — upload as a draft without going live: + +```bash +npm run deploy -- --no-publish +# or +DD_APPS_PUBLISH=false npm run deploy +``` + +`DD_APPS_PUBLISH=false` is useful in CI when publishing is a separate step. + +## Publish + +Publish the most recently uploaded version without rebuilding: + +```bash +npm run publish +``` + +Publish a specific version by ID: + +```bash +npm run publish -- --version +``` + +When using `--version` without a cache file, set `DD_APPS_IDENTIFIER` to the app's identifier. + +**Older scaffolded projects** — if `deploy` and `publish` scripts are missing, add them to `package.json` (requires `@datadog/vite-plugin` >= 3.2.0): + +```json +{ "scripts": { "deploy": "datadog-apps deploy", "publish": "datadog-apps publish" } } +``` + +## Troubleshooting + +**401 / missing authentication token / credentials not configured** + +- OAuth: if the browser window didn't open, the environment may not support it. Fall back to API keys. +- API keys: verify `DD_API_KEY` and `DD_APP_KEY` are set and the application key has both **Actions API Access** and **Apps** scopes. +- Check `auth.site` in `vite.config.ts` matches the credentials' Datadog site. + +**403 "you do not have access to this app" on upload** + +The application key needs both scopes enabled — not just one: + +1. **Actions API Access** — required for backend function execution. +2. **Apps** (or **App Builder**) — required for uploading and publishing. + +Go to `https://app.datadoghq.com/organization-settings/application-keys`, confirm both scopes, or create a new key with both. + +**Build succeeds but nothing deploys** + +- Use `npm run deploy`, not `npm run build`, when the intent is to deploy. +- Check whether `DD_APPS_PUBLISH` is set to `false` in the environment — the app uploads as a draft but does not go live. Unset the variable or run `npm run publish` separately. +- Confirm `dryRun` in `vite.config.ts` is not `true`. +- Confirm `DD_APPS_UPLOAD_ASSETS` is set — `npm run deploy` does this automatically. + +**Build fails with missing credentials** + +- Current scaffold versions may make `npm run build` exercise Datadog deploy behavior. +- Cache OAuth credentials first (`npm run dev`), or set `DD_API_KEY` and `DD_APP_KEY`. +- For credential-free validation, prefer `npm run typecheck`. + +**Node or scaffolding errors** + +- Check `package.json` `engines.node` for the supported Node.js versions. +- Use Volta, nvm, or fnm to switch versions. Prefer a current Node 22 release when debugging. + +**Datadog site mismatch** + +- Inspect `vite.config.ts` for the configured site and confirm CI uses the same value. diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/cicd.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/cicd.md new file mode 100644 index 0000000..8928185 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/cicd.md @@ -0,0 +1,42 @@ +# CI/CD + +## GitHub Actions + +Use `DataDog/apps-github-action` to deploy on pushes to the deployment branch. Keep `app-directory` aligned with the app's path in the repository. + +```yaml +name: Continuous Deployment +on: + push: + branches: + - main + +permissions: + contents: read + +jobs: + deploy-app: + name: Deploy Datadog App + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + + - name: Deploy + uses: DataDog/apps-github-action@v0.0.2 + with: + datadog-api-key: ${{ secrets.DATADOG_API_KEY }} + datadog-app-key: ${{ secrets.DATADOG_APP_KEY }} + app-directory: . +``` + +Store `DATADOG_API_KEY` and `DATADOG_APP_KEY` as [encrypted secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) in the repository or organization settings. The application key needs both **Actions API Access** and **Apps** scopes. + +For non-US1 organizations, configure the Datadog site in `vite.config.ts` and ensure CI and local config are aligned. diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/data.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/data.md new file mode 100644 index 0000000..9be21be --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/data.md @@ -0,0 +1,61 @@ +# Data Access + +Backend functions have two primary data access paths: DDSQL and Action Catalog. + +## Choosing a path + +| Situation | Use | +| --- | --- | +| Read from Datadog-visible data (logs, metrics, hosts, spans, etc.) and need filtering, projection, joins, aggregation, or pagination | DDSQL | +| Mutation — create, update, delete, trigger, invoke, send | Action Catalog | +| Product workflow that maps to an existing typed action | Action Catalog | +| Simple single-record read where the typed action response is exactly what the UI needs | Action Catalog | +| External system or integration that is not DDSQL-visible | Action Catalog | +| Read from an app datastore | DDSQL (prefer for queries) or datastore actions (for simple reads/writes) | + +## DDSQL + +DDSQL-visible datasets include logs, APM spans, RUM events, hosts, containers, services, cloud inventory, audit trail, CI pipelines, and more. Check the [DDSQL Data Directory](https://docs.datadoghq.com/ddsql_reference/data_directory/) for the full list. + +Before writing queries: + +- Confirm the table name, columns, and types with a small bounded probe before writing app code. +- Verify the exact DDSQL execution API or action import against installed package exports — do not invent subpaths. +- Use fixed SQL templates. Do not accept raw frontend SQL or raw `ORDER BY` fields. +- Clamp limits and use `LIMIT` in all list queries. + +### App datastores + +Datastores are queryable via DDSQL. Confirm the datastore ID and column schema with a bounded probe first: + +```sql +SELECT key, summary, status +FROM dd.actions_datastores( + id => '', + columns => ARRAY ['key', 'summary', 'status'] +) AS ( + key VARCHAR, + summary VARCHAR, + status VARCHAR +) +WHERE status = 'Open' +ORDER BY key +LIMIT 100; +``` + +For writes and deletes, use datastore actions from `@datadog/action-catalog` directly. + +Public docs: [Datastores](https://docs.datadoghq.com/actions/datastores/), [DDSQL Reference](https://docs.datadoghq.com/ddsql_reference/) + +## Connections + +Connections are reusable auth configuration for Action Catalog actions that need credentials (custom HTTP, integrations without a Datadog tile, etc.). + +- Find or create connections at `https://app.datadoghq.com/actions/connections`. +- Copy the Connection ID from the connection details — treat it as configuration, not a secret. +- Do not ask users to paste API keys, passwords, OAuth secrets, or private keys into an AI conversation. +- Have a human create or update connections in the Datadog UI. + +Integrations such as GitHub, Jira, Slack, PagerDuty, and Statuspage may inherit credentials from their Datadog integration tile and may not need an explicit connection. + +Public docs: [Connections](https://docs.datadoghq.com/actions/connections/), [Action Catalog](https://docs.datadoghq.com/actions/action_interface/) diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/runtime-context.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/runtime-context.md new file mode 100644 index 0000000..6d30e71 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/runtime-context.md @@ -0,0 +1,30 @@ +# Runtime Context + +Datadog Apps are embedded inside the Datadog UI. Treat this app as iframe-hosted browser code with Datadog-managed backend execution. + +## Iframe embedding + +- The app runs inside an iframe owned by the Datadog UI. +- The current App Builder iframe sandbox allows scripts, popups, popups escaping the sandbox, same-origin access, forms, and top navigation only by user activation. +- Do not assume app code can control, read, or directly navigate the parent Datadog page. +- Prefer hash-based routing for in-app routes. Avoid `BrowserRouter` and routers that depend on reliable top-level History API behavior. +- In-app links should use hash URLs such as `href="#/details"`. +- External URLs can open popups or top-level navigation only through browser-supported, user-initiated flows. +- Verify the current Datadog embedding sandbox before relying on a specific browser capability. Datadog controls the sandbox attributes and may change them. + +## Storage and cookies + +- Browser storage is scoped to the app's origin and is not shared with the parent Datadog UI. +- Storage can differ between standalone local development and embedded runtime because modern browsers partition storage by top-level site and iframe origin. +- Third-party cookies are unreliable for durable app state. +- For durable server-side state, use backend functions and a Datadog-supported durable storage mechanism. + +## Backend call transport + +Frontend code imports backend functions like normal ES modules, but those imports are rewritten by the Datadog Vite plugin. The function body does not run in the browser. + +- In local dev, backend calls go through Vite middleware and Datadog preview infrastructure. +- In deployed apps, backend calls go through the Datadog iframe/runtime bridge. +- Do not build app logic that depends on backend functions running in the local Node.js process. + +For backend implementation details, read `backend-functions.md`. diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/upgrading.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/upgrading.md new file mode 100644 index 0000000..19b5c4e --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/upgrading.md @@ -0,0 +1,63 @@ +# Upgrading + +## Primary packages + +- `@datadog/vite-plugin`: Vite integration for local development, build, and deploy. +- `@datadog/action-catalog`: typed Action Catalog client for backend functions. +- `@datadog/apps-backend`: Datadog-provided helpers for backend function development. + +Preserve the existing package manager and lockfile style. + +## Before upgrading + +Check release notes before changing versions: + +```bash +npm view @datadog/vite-plugin version repository.url homepage +npm view @datadog/action-catalog version repository.url homepage +npm view @datadog/apps-backend version repository.url homepage +``` + +Inspect the current app: + +```bash +npm outdated @datadog/vite-plugin @datadog/action-catalog @datadog/apps-backend +npm ls @datadog/vite-plugin @datadog/action-catalog @datadog/apps-backend +``` + +## Upgrade + +```bash +npm install @datadog/action-catalog@latest +npm install @datadog/apps-backend@latest +npm install -D @datadog/vite-plugin@latest +``` + +After upgrading, verify: + +- `vite.config.ts` Datadog plugin config, especially `auth.site`. +- `package.json` scripts — check for new `deploy` and `publish` scripts added in `@datadog/vite-plugin` 3.2.0+. +- Backend function imports from `@datadog/action-catalog/...`. +- Project-local instructions in `AGENTS.md`. + +## Compare against a fresh scaffold + +To pull in changes from the latest scaffolder without guessing: + +```bash +tmp_dir="$(mktemp -d)" +npm create @datadog/apps@latest -- "$tmp_dir/base-app" --template vite-react -y --skip-post-scaffold +diff -ru "$tmp_dir/base-app/package.json" package.json +diff -ru "$tmp_dir/base-app/vite.config.ts" vite.config.ts +diff -ru "$tmp_dir/base-app/src" src +``` + +Port only the specific changes needed. Do not replace the app wholesale. + +## Verify + +```bash +npm run typecheck +npm run lint +npm run build +``` diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/workflow-automation.md b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/workflow-automation.md new file mode 100644 index 0000000..2ec9bcd --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/docs/agents/workflow-automation.md @@ -0,0 +1,150 @@ +# Workflow Automation + +Use this when a backend function needs to trigger a Datadog Workflow Automation workflow and poll for the result. + +## Requirements + +- Use a backend function, not frontend code. Backend functions can call `@datadog/action-catalog`. +- Use the generic HTTP action from `@datadog/action-catalog/http/http`. +- The HTTP action requires a Connection ID for an HTTP connection configured in your Datadog org. Find or create one at `https://app.datadoghq.com/actions/connections`. +- The target workflow must be published and must include an API trigger (`apiTrigger` in the workflow spec). +- Workflow inputs must match the workflow input schema exactly. + +## Inspect the workflow first + +Before wiring an app to a workflow, confirm its published shape: + +```bash +workflow_id="" +curl -sS --fail-with-body \ + "https://api.datadoghq.com/api/v2/workflows/${workflow_id}" \ + -H "DD-API-KEY: ${DD_API_KEY}" \ + -H "DD-APPLICATION-KEY: ${DD_APP_KEY}" \ + -H "Accept: application/json" \ +| jq "{ + id: .data.id, + name: .data.attributes.name, + published: .data.attributes.published, + inputSchema: .data.attributes.spec.inputSchema, + outputSchema: .data.attributes.spec.outputSchema, + hasApiTrigger: any(.data.attributes.spec.triggers[]?; has(\"apiTrigger\")) + }" +``` + +Confirm `published` is `true`, `hasApiTrigger` is `true`, and `inputSchema.parameters` matches the inputs the app will send. + +## Trigger and poll pattern + +Inputs go under `meta.payload`. Replace `YOUR_HTTP_CONNECTION_ID` with the ID from your Datadog org. + +```ts +import { request } from '@datadog/action-catalog/http/http'; + +const DATADOG_HTTP_CONNECTION_ID = ''; +const DATADOG_API_BASE_URL = 'https://api.datadoghq.com'; +const DEFAULT_POLL_TIMEOUT_MS = 120_000; +const INITIAL_POLL_DELAY_MS = 250; +const POLL_DELAY_MULTIPLIER = 1.05; + +const jsonHeaders = [ + { key: 'Accept', value: ['application/json'] }, + { key: 'Content-Type', value: ['application/json'] }, +]; + +type WorkflowInstanceResponse = { + data?: { + id?: string; + attributes?: { + endTimestamp?: string | null; + outputs?: unknown; + instanceStatus?: { detailsKind?: string; displayName?: string }; + }; + }; +}; + +type WorkflowRunResult = { + instanceId?: string; + statusKind?: string; + displayStatus?: string; + outputs?: unknown; + body: unknown; +}; + +function workflowInstancesUrl(workflowId: string) { + return `${DATADOG_API_BASE_URL}/api/v2/workflows/${workflowId}/instances`; +} + +function isRunningStatus(statusKind?: string) { + return !statusKind || statusKind === 'IN_PROGRESS'; +} + +function toRunResult(body: unknown): WorkflowRunResult { + const response = body as WorkflowInstanceResponse | undefined; + return { + instanceId: response?.data?.id, + statusKind: response?.data?.attributes?.instanceStatus?.detailsKind, + displayStatus: response?.data?.attributes?.instanceStatus?.displayName, + outputs: response?.data?.attributes?.outputs, + body, + }; +} + +export async function triggerWorkflow( + workflowId: string, + payload: Record, +): Promise { + const response = await request({ + connectionId: DATADOG_HTTP_CONNECTION_ID, + inputs: { + verb: 'POST', + url: workflowInstancesUrl(workflowId), + requestHeaders: jsonHeaders, + responseParsing: 'json', + errorOnStatus: ['400-599'], + body: JSON.stringify({ meta: { payload } }), + }, + }); + return toRunResult(response.body); +} + +export async function pollWorkflowInstance( + workflowId: string, + instanceId: string, + timeoutMs = DEFAULT_POLL_TIMEOUT_MS, +): Promise { + const expiresAt = Date.now() + timeoutMs; + let delay = INITIAL_POLL_DELAY_MS / POLL_DELAY_MULTIPLIER; + + while (Date.now() < expiresAt) { + delay *= POLL_DELAY_MULTIPLIER; + const jitter = delay * 0.4 * (Math.random() - 0.5); + await new Promise((resolve) => setTimeout(resolve, delay + jitter)); + + const response = await request({ + connectionId: DATADOG_HTTP_CONNECTION_ID, + inputs: { + verb: 'GET', + url: `${workflowInstancesUrl(workflowId)}/${instanceId}`, + requestHeaders: jsonHeaders, + responseParsing: 'json', + errorOnStatus: ['400-599'], + }, + }); + + const result = toRunResult(response.body); + if (!isRunningStatus(result.statusKind)) { + return result; + } + } + + throw new Error(`Workflow instance ${instanceId} did not finish in time`); +} +``` + +Treat `statusKind === "SUCCEEDED"` as success. All other terminal statuses (`INSTANCE_ERROR`, `STEP_ERROR`, `CANCELED`) are failures — return the full response body so the caller can inspect `errorDetail` and partial outputs. + +## Troubleshooting + +- `input parameter not in workflow input schema`: update `meta.payload` keys to match `inputSchema.parameters`. +- `Expected trigger type TRIGGER_TYPE_API not found`: add and publish an API trigger on the workflow. +- 401/403: confirm `DD_API_KEY` and `DD_APP_KEY` are set with Actions API Access, and the app site matches the API host. diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/eslint.config.mjs b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/eslint.config.mjs new file mode 100644 index 0000000..83eea8d --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/eslint.config.mjs @@ -0,0 +1,23 @@ +import js from '@eslint/js'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + ...tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]); diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/index.html b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/index.html new file mode 100644 index 0000000..d9a4f52 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/index.html @@ -0,0 +1,15 @@ + + + + + + + sarah-s-app-thu-aug-20-12-29-57-pm + + + +
+ + + + diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/package.json b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/package.json new file mode 100644 index 0000000..cb13f0f --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/package.json @@ -0,0 +1,43 @@ +{ + "name": "sarah-s-app-thu-aug-20-12-29-57-pm", + "version": "0.0.1", + "private": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "upload": "DD_APPS_UPLOAD_ASSETS=1 vite build", + "lint": "eslint . --report-unused-disable-directives --max-warnings 0", + "lint:fix": "eslint . --fix", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@datadog/action-catalog": "^0.0.3", + "@datadog/apps-backend": "^0.0.1", + "@datadog/druids": "^0.0.7", + "@tanstack/react-query": "^5.101.2", + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "optionalDependencies": { + "@napi-rs/keyring": "^1.3.0" + }, + "devDependencies": { + "@datadog/vite-plugin": "^3.2.1", + "@eslint/js": "^9.39.2", + "@types/node": "^24.12.2", + "@types/react": "^19.2.9", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.2", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^16.5.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.54.0", + "vite": "^7.3.1" + } +} \ No newline at end of file diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/public/favicon.ico b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e04da3edae6cd9b517f9fdae7b168a5e0639363a GIT binary patch literal 40436 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 Wr-m%w@EQ6F^Nzg(a)BQHqwzm~gQmj( literal 0 HcmV?d00001 diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.css b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.css new file mode 100644 index 0000000..3076e9e --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.css @@ -0,0 +1,20 @@ +.kelly-page { + align-items: center; + display: flex; + justify-content: center; + min-height: 100vh; + text-align: center; +} + +.kelly-title { + color: #71e6c6; + font-size: 72px; + line-height: 1; + margin: 0; +} + +@media (max-width: 480px) { + .kelly-title { + font-size: 48px; + } +} diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.tsx b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.tsx new file mode 100644 index 0000000..383ac1b --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/App.tsx @@ -0,0 +1,16 @@ +import { Spacing } from '@datadog/druids/layout/Spacing'; +import { Text } from '@datadog/druids/typography/Text'; + +import './App.css'; + +function App() { + return ( + + + kelly + + + ); +} + +export default App; diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/main.tsx b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/main.tsx new file mode 100644 index 0000000..8f2c295 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/src/main.tsx @@ -0,0 +1,24 @@ +import '@datadog/druids/styles.css'; + +import { DruidsEnvironment } from '@datadog/druids/layout/DruidsEnvironment'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +import App from './App'; + +const queryClient = new QueryClient(); +const appRoot = document.getElementById('app'); +if (appRoot) { + createRoot(appRoot).render( + + + + + + + , + ); +} else { + console.error('Missing #app div for createRoot.'); +} diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/tsconfig.json b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/tsconfig.json new file mode 100644 index 0000000..7a01668 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/apps/sarah-s-app-thu-aug-20-12-29-57-pm/vite.config.ts b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/vite.config.ts new file mode 100644 index 0000000..f0b0d85 --- /dev/null +++ b/apps/sarah-s-app-thu-aug-20-12-29-57-pm/vite.config.ts @@ -0,0 +1,57 @@ +import { datadogVitePlugin } from '@datadog/vite-plugin'; +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +import { version } from './package.json'; + +const hasDatadogApiKeys = Boolean( + (process.env.DD_API_KEY || process.env.DATADOG_API_KEY) && + (process.env.DD_APP_KEY || process.env.DATADOG_APP_KEY), +); +const datadogSite = process.env.DATADOG_SITE || process.env.DD_SITE || 'datadoghq.com'; + +process.env.DATADOG_SITE ||= datadogSite; +process.env.DD_SITE ||= datadogSite; + +export default defineConfig({ + base: './', + build: { + sourcemap: true, + }, + plugins: [ + react(), + datadogVitePlugin({ + logLevel: 'debug', + auth: { + site: datadogSite, + apiKey: process.env.DD_API_KEY, + appKey: process.env.DD_APP_KEY, + }, + apps: { + enable: true, + authOverrides: { + method: hasDatadogApiKeys ? 'apiKey' : 'oauth', + }, + // Stable identity for this app, generated once when the project was scaffolded. + // DO NOT change this value, and DO NOT set it to the app's UUID from App Builder. + // Every upload targets the app matching this identifier — changing it makes the + // next upload create a brand-new app instead of updating this one. + identifier: '830bc1ff-f937-4ab4-8001-17db0aa5eb7f', + }, + errorTracking: { + enable: hasDatadogApiKeys, + sourcemaps: { + minifiedPathPrefix: '/', + releaseVersion: version, + service: 'sarah-s-app-thu-aug-20-12-29-57-pm', + } + }, + metadata: { + name: 'sarah-s-app-thu-aug-20-12-29-57-pm', + }, + metrics: { + enable: hasDatadogApiKeys, + }, + }), + ], +}); diff --git a/package-lock.json b/package-lock.json index 3e4710d..a7aa833 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,82 @@ "@napi-rs/keyring": "^1.3.0" } }, + "apps/my-first-app/node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "apps/my-first-app/node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "apps/sarah-s-app-thu-aug-20-12-29-57-pm": { + "version": "0.0.1", + "dependencies": { + "@datadog/action-catalog": "^0.0.3", + "@datadog/apps-backend": "^0.0.1", + "@datadog/druids": "^0.0.7", + "@tanstack/react-query": "^5.101.2", + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "devDependencies": { + "@datadog/vite-plugin": "^3.2.1", + "@eslint/js": "^9.39.2", + "@types/node": "^24.12.2", + "@types/react": "^19.2.9", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.2", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^16.5.0", + "typescript": "^5.9.3", + "typescript-eslint": "^8.54.0", + "vite": "^7.3.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@napi-rs/keyring": "^1.3.0" + } + }, + "apps/sarah-s-app-thu-aug-20-12-29-57-pm/node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "apps/sarah-s-app-thu-aug-20-12-29-57-pm/node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -74,6 +150,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -255,6 +332,7 @@ "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.29.8" }, @@ -318,6 +396,7 @@ "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.8", @@ -337,6 +416,7 @@ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" @@ -351,6 +431,29 @@ "integrity": "sha512-gRi29i6SYbICcWBPH2fW+6iifvjQ22kVmJf4n/PCM4c1hDd0OUekmnrgyGtLb+3YNspUzM/3vBgArbXJfHnSMw==", "license": "Apache-2.0" }, + "node_modules/@datadog/apps-backend": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@datadog/apps-backend/-/apps-backend-0.0.1.tgz", + "integrity": "sha512-utOkUOd0hb3q58wWDUbm+GAvYGI8fv3bNpikUj723HAZtAO4wd47c/2uWOANIBsnhQqA9QOnomqex+LmBxhDTA==", + "license": "Apache-2.0" + }, + "node_modules/@datadog/druids": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@datadog/druids/-/druids-0.0.7.tgz", + "integrity": "sha512-pzmDIgwWXsuNLa6Kr4TlbHLkx8szvmLNCWYHQSWSdRKDhP8AZgGyk4XI/HwXy7aONThOHuaCTI94c+AP6l1CwA==", + "license": "SEE LICENSE IN EULA.txt", + "dependencies": { + "@popperjs/core": "2.11.8", + "@tippyjs/react": "4.2.6", + "react-popper": "2.3.0", + "react-table": "7.8.0", + "tippy.js": "6.3.7" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, "node_modules/@datadog/js-instrumentation-wasm": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@datadog/js-instrumentation-wasm/-/js-instrumentation-wasm-1.0.8.tgz", @@ -1452,6 +1555,17 @@ "node": "^22.20 || ^24.12 || >=25" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", @@ -1852,6 +1966,19 @@ "react": "^18 || ^19" } }, + "node_modules/@tippyjs/react": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz", + "integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==", + "license": "MIT", + "dependencies": { + "tippy.js": "^6.3.1" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1927,6 +2054,7 @@ "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1976,6 +2104,7 @@ "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.67.0", "@typescript-eslint/types": "8.67.0", @@ -2201,6 +2330,7 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2321,6 +2451,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.12", "caniuse-lite": "^1.0.30001809", @@ -2541,6 +2672,7 @@ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3217,7 +3349,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -3370,6 +3501,18 @@ "dev": true, "license": "MIT" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3734,24 +3877,60 @@ } }, "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { - "scheduler": "^0.27.0" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^19.2.8" + "react": "^18.3.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "license": "MIT", + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" } }, "node_modules/react-refresh": { @@ -3764,6 +3943,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17.0.0-0 || ^18.0.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -3853,6 +4045,10 @@ "dev": true, "license": "MIT" }, + "node_modules/sarah-s-app-thu-aug-20-12-29-57-pm": { + "resolved": "apps/sarah-s-app-thu-aug-20-12-29-57-pm", + "link": true + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -3996,6 +4192,15 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -4028,6 +4233,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4036,6 +4242,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", @@ -4113,6 +4343,7 @@ "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", @@ -4182,6 +4413,15 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -4241,6 +4481,7 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }