@bridgeline-digital/hawk-app-cli 0.1.0-dev.20261020202308 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +7 -7
  2. package/README.md +770 -770
  3. package/agents/AGENTS.md +192 -192
  4. package/agents/project/custom.md +21 -21
  5. package/agents/project/next-mui.md +21 -21
  6. package/agents/project/next.md +24 -24
  7. package/agents/project/vanilla.md +22 -22
  8. package/agents/project/vite.md +19 -19
  9. package/bin/hawk-app.mjs +0 -0
  10. package/docs/bridge/README.md +1315 -1315
  11. package/docs/showcase/README.md +47 -47
  12. package/docs/showcase/functions/echo.ts +12 -12
  13. package/docs/showcase/functions/long-task.ts +31 -31
  14. package/docs/showcase/functions/showcase.ts +186 -186
  15. package/docs/showcase/hawk-app.json +73 -73
  16. package/docs/showcase/queries/assistant-prompts-by-day.json +6 -6
  17. package/docs/showcase/queries/assistant-prompts-by-day.sql +8 -8
  18. package/docs/showcase/queries/searches-by-day.json +6 -6
  19. package/docs/showcase/queries/searches-by-day.sql +7 -7
  20. package/docs/showcase/src/App.tsx +1436 -1436
  21. package/docs/showcase/src/main.tsx +10 -10
  22. package/docs/showcase/src/styles.css +71 -71
  23. package/docs/showcase/src/useHawkApp.ts +44 -44
  24. package/docs/showcase/src/vite-env.d.ts +1 -1
  25. package/docs/ui/README.md +166 -166
  26. package/package.json +1 -1
  27. package/schema/hawk-app.schema.json +611 -611
  28. package/src/accessibility.mjs +105 -105
  29. package/src/agents.mjs +145 -145
  30. package/src/aliases.mjs +30 -30
  31. package/src/app-icon.mjs +82 -82
  32. package/src/channel.mjs +116 -116
  33. package/src/cli.mjs +825 -825
  34. package/src/clients.mjs +221 -221
  35. package/src/config.mjs +148 -148
  36. package/src/create.mjs +186 -186
  37. package/src/credentials.mjs +56 -56
  38. package/src/data.mjs +169 -169
  39. package/src/deploy.mjs +349 -349
  40. package/src/dev.mjs +459 -459
  41. package/src/doctor.mjs +176 -176
  42. package/src/env.mjs +140 -140
  43. package/src/functions.mjs +313 -313
  44. package/src/login.mjs +166 -166
  45. package/src/logs.mjs +64 -64
  46. package/src/manifest.mjs +425 -425
  47. package/src/mcp-docs.mjs +168 -168
  48. package/src/mcp-hawksearch.mjs +342 -342
  49. package/src/mcp-reports.mjs +130 -130
  50. package/src/mcp-rules.mjs +109 -109
  51. package/src/mcp.mjs +539 -539
  52. package/src/platform.mjs +116 -116
  53. package/src/project-checks.mjs +153 -153
  54. package/src/queries.mjs +289 -289
  55. package/src/scope-check.mjs +91 -91
  56. package/src/secrets.mjs +86 -86
  57. package/src/slug-guard.mjs +80 -80
  58. package/src/sso.mjs +182 -182
  59. package/src/telemetry.mjs +50 -50
  60. package/src/upgrade.mjs +406 -406
  61. package/src/versions.mjs +102 -102
  62. package/templates/next/next.config.ts +13 -13
  63. package/templates/next/package.json +26 -26
  64. package/templates/next/src/lib/useHawkApp.ts +46 -46
  65. package/templates/next-mui/next.config.ts +13 -13
  66. package/templates/next-mui/package.json +30 -30
  67. package/templates/next-mui/src/app/layout.tsx +19 -19
  68. package/templates/next-mui/src/app/page.tsx +78 -78
  69. package/templates/next-mui/src/app/providers.tsx +10 -10
  70. package/templates/preact/package.json +26 -26
  71. package/templates/preact/src/useHawkApp.ts +44 -44
  72. package/templates/preact/src/vite-env.d.ts +1 -1
  73. package/templates/preact/tsconfig.json +17 -17
  74. package/templates/preact/vite.config.ts +11 -11
  75. package/templates/react/package.json +29 -29
  76. package/templates/react/src/useHawkApp.ts +44 -44
  77. package/templates/react/src/vite-env.d.ts +1 -1
  78. package/templates/react/tsconfig.json +16 -16
  79. package/templates/react/vite.config.ts +11 -11
  80. package/templates/vanilla/package.json +15 -15
package/agents/AGENTS.md CHANGED
@@ -1,192 +1,192 @@
1
- # __NAME__ (`__SLUG__`): a Hawk App
2
-
3
- A Hawk App is a static web app that runs in an iframe inside the HawkSearch dashboard (and later Spark), for one
4
- client at a time. It gets its context and all of its data through the bridge,
5
- `@bridgeline-digital/hawk-app-bridge`. It's deployed with the CLI,
6
- `@bridgeline-digital/hawk-app-cli` (`npx hawk-app`).
7
-
8
- ## Rules
9
-
10
- - **No secrets in the app.** Everything in the build is public. Never add API keys, AWS credentials, database
11
- connection strings or HawkSearch keys. The platform adds credentials server-side, and it takes the client from the
12
- signed app token, so the app can only reach the current client's data. A third-party key goes in a function secret
13
- (`npx hawk-app secrets set NAME`) and is used in a function, never in the browser.
14
- - **Settings that change without a deploy go in `config`.** Non-secret values (notification recipients, thresholds,
15
- switches) are declared in `config` in `hawk-app.json` (type, label, default) and read with `app.config.get(key)`
16
- (`ctx.config` in a function). Their values are set with `npx hawk-app config set` or in the Hawk Analytics portal,
17
- never hard-coded. They reach the browser, so never put a key or password there.
18
- - **All data goes through the bridge.** Don't call HawkSearch, the analytics API, Athena or AWS directly from the
19
- app. Use `app.analytics`, `app.dashboard`, `app.data`, `app.functions` and the others below.
20
- - **Static files only.** The build is plain HTML, JS and CSS: no SSR, API routes or middleware. Server code goes in
21
- `functions/`.
22
- - **Every asset URL is under the version's base path.** Each deploy is served from `/<slug>/v<n>/`. `hawk-app deploy`
23
- builds with `HAWK_APP_BASE_PATH=/__SLUG__/v<n>`; the build must use it (or only relative URLs). An absolute
24
- `/assets/...` URL loads nothing once deployed.
25
- - **No cookies, no `localStorage` for auth.** The app runs in a cross-site iframe where browsers block cookies. For a
26
- backend of your own, send `await app.getToken()` as a Bearer header.
27
- - **Ask for the fewest scopes.** Each one is in `hawk-app.json` and reviewed; elevated scopes are never auto-approved.
28
- - **List outside origins.** `externalOrigins` in `hawk-app.json` should list every https origin the app loads from or
29
- calls (`[]` when none). The default `["*"]` fails review. To show images from hosts you can't list (a client's
30
- product images), use `"img:*"`: images only, from any https origin, and it passes review.
31
- - **Handle a client without analytics.** When `app.context.tenant.analyticsProvisioned === false`, show a message
32
- instead of calling `app.analytics` or `app.data`.
33
- - Don't add a page heading: the host shows the app's name above the frame.
34
- - Don't edit `hawk-app.json`'s `slug` (it can't change) and don't add `status`, `clients` or `excludeClients` (set in
35
- HawkSearch and with `npx hawk-app clients`).
36
-
37
- ## The bridge
38
-
39
- ```ts
40
- import { createApp } from '@bridgeline-digital/hawk-app-bridge/client';
41
-
42
- const app = await createApp({ autoResize: true }); // rejects with code 'not_embedded' when opened outside a host
43
- render(app.context);
44
- app.onContextChange(render); // the user switched client, engine or dates
45
- ```
46
-
47
- `app.context`: `tenant.clientId`, `tenant.analyticsProvisioned`, `engine { id, name }`, `site`,
48
- `dateRange { from, to, timeZone }`, `user { id, email, roles }`, `locale`, `theme`, `app.slug`.
49
-
50
- | Call | Scope |
51
- | --- | --- |
52
- | `app.analytics.report('queries', { limit, startDate, endDate, filters })` | `reports:read` |
53
- | `app.dashboard.get('Synonym')`: HawkSearch dashboard API, no API version in the path | `hawksearch:read` |
54
- | `app.dashboard.post/put/delete(path, body)`: merchandising and relevancy resources only, never retried | `hawksearch:write` |
55
- | `app.hawksearch.search(...)`, `app.hawksearch.autocomplete(...)` on the user's engine | `hawksearch:search` |
56
- | `app.data.query('name', params)`: your SQL in `queries/<name>.sql` + `.json` | `data:read` |
57
- | `app.functions.call('name', input)`, `.start/.wait/.job` for jobs: `functions/<name>.ts` exporting `handler` | `functions:invoke` |
58
- | `app.email.send(...)` | `email:send` |
59
- | `app.storage.get/put/delete/list/batch/usage`: the app's own JSON data per client (350 KB a value, `ttlDays`, `ifVersion`); `app.storage.files.upload/url/download/list/delete`: its files (100 MB each) | `storage` |
60
- | `app.hawksearch.systemParameters.list/get/set`: names listed in `systemParameters` | `hawksearch:sysparams:write` |
61
- | `app.hawksearch.features.list/set/setFacet` | `hawksearch:features:write` |
62
- | `app.hawksearch.index.rebuild()`, `.status(id?)`, `.wait(id)`: rebuild the engine's index from its feed (needs the engine's `EnableRebuildIndex` on; one at a time); `.indexes()`, `.current()` | `hawksearch:index` |
63
- | `app.hawksearch.index.items.upsert/update/delete(...)`: items in the engine's indexes, live at once (125 a call) | `hawksearch:index:items` |
64
- | `app.hawksearch.index.create/setCurrent/deleteIndex/rebuildAll(...)`, `.call('hierarchy/upsert', …)` and other management | `hawksearch:index:manage` |
65
- | `app.hawksearch.recommendations({ widgetUids: [{ widgetGuid, uniqueId? }] })`: recommendation widgets, untracked | `hawksearch:recommendations` |
66
- | `app.ai.generate/stream/info` | `ai:invoke` |
67
- | `app.lambdas.invoke('name', input)`: names listed in `lambdas`, allowed by Engineering | `lambdas:invoke` |
68
- | `app.config.get('key')`, `app.config.all()`: keys declared in `config`, the client's value, else the global one, else the default (null when none); not secret | none |
69
- | `app.assistant.ask('question')`, `app.assistant.setContext({ ...what's on screen })` (null clears): the host's assistant (Hawk Assistant) answers in its chat, as the user; only from a click, and only while `context.features?.assistant` is true (it can load after the app: `app.onContextChange`) | `assistant` |
70
-
71
- Also: `app.reportError(error)` for errors the app catches and handles (uncaught ones are reported on their own; see
72
- them with `hawk-app errors`), `app.setTitle`, `app.toast(message, 'info'|'success'|'warning'|'error')`,
73
- `app.navigate({ intent: 'app'|'report'|'settings'|'external', ... })` (intents, never URLs), `app.getToken()`,
74
- `app.api.get('me')` (`internal: true` for Bridgeline staff, from the signed token: use it for staff-only screens).
75
- Failed data calls throw `HawkApiError` (`status`, `code`); host requests throw `HawkBridgeError` (`code`). A 403
76
- `scope_missing` means the scope is missing from `hawk-app.json` or the app wasn't deployed again after adding it
77
- (`validate` and `deploy` warn about bridge calls whose scope is missing).
78
- Look: every template uses the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`) for Spark's look in the
79
- host's colors and mode: React + MUI apps (next-mui) its provider and components, every other app its stylesheet
80
- (`/css`) and elements (`/elements`); see this app's section below. Don't restyle it or add another font. For your own
81
- styles use its `--hawk-ui-*` variables (or the host's `--hawk-*` ones with your own fallbacks).
82
- Accessibility: approvers see an axe-core check of the built pages (critical issues fail it): label form fields, give
83
- images `alt`, keep one `<main>` and headings in order. `validate --build` shows the issues.
84
- Dialogs: any `[role="dialog"]` (every MUI Dialog) covers the host's whole page on its own, even from a small frame;
85
- center dialogs in the visible part of the frame (`--hawk-visible-top`, `--hawk-visible-height`, as the templates'
86
- theme does), and use in-page dialogs, never `window.confirm`/`alert` (blocked in the sandbox).
87
-
88
- Before using a call, read its section in the bridge README for the exact options, limits and error codes: the
89
- `hawk-apps` MCP server's `docs` tool (below), or
90
- `node_modules/@bridgeline-digital/hawk-app-bridge/README.md` when the bridge is installed, otherwise
91
- https://www.npmjs.com/package/@bridgeline-digital/hawk-app-bridge. Its TypeScript types are in the same package.
92
-
93
- ## MCP server
94
-
95
- `.mcp.json` (Claude Code) and `.vscode/mcp.json` (VS Code) start the CLI's MCP server, `hawk-apps`, from
96
- `node_modules` (`npm install` first). Its tools: `docs` (the bridge, UI kit, CLI, `hawk-app.json` schema and the
97
- showcase app, which has working code for every feature), `ui_components` (the UI kit's components for this app's
98
- template: use them before writing your own), `validate`, `status` (with the review checks approvers will see),
99
- `analytics_reports` (every report `app.analytics.report()` can call, with its columns and filters),
100
- `hawksearch_api` (HawkSearch's dashboard, search, recommendation and indexing operations: fields, the bridge call, its scope),
101
- `data_tables` and `data_describe` (names and columns, never data), `logs`, `init`, `upgrade`,
102
- `deploy` (with `alias`, and `dryRun` to only show what hawk-app.json would change), `versions`, `why` (what a client sees, and why), `doctor` (what's wrong with the setup), `errors`, `usage`, and `clients` and `clients_add`, which only ever use the dev platform. Prefer them to guessing. When the
103
- user names a client ("deploy to OTC"), call `clients_add` with their words: it shows them a form to pick the exact
104
- client. With `alias` (`hawk-app clients add <id> --alias review`) the client gets that alias's version: on a version that
105
- isn't approved, only Bridgeline staff in it see the app. Only approvers change what a client whose users see the app
106
- gets. `hawk-app deploy out --alias staging` deploys off the main line: the version carries this hawk-app.json, `staging`
107
- points at it, and the version under review isn't touched. A plain `hawk-app deploy` sends hawk-app.json first, so scope
108
- changes go with the deploy. Client names aren't shared with you; don't ask for them in the chat. Its prompts (`check-before-submit`, `add-feature`,
109
- `explain-app`, `deploy-to-client`; in Claude Code `/mcp__hawk-apps__<name>`) are ready-made tasks that use them.
110
-
111
- ## Icon
112
-
113
- `icon` in `hawk-app.json` is the app's icon file, next to it: `icon.svg` by default, which `create` and `init` add.
114
- - **An .svg** is drawn in the host's own color (HawkSearch, Spark; light and dark): only its shape counts, so draw it in
115
- one color, on a square `viewBox` (e.g. `0 0 24 24`), at most 32 KB.
116
- - **A .png** is shown as is, for a full-color logo: square, 64 to 1024 px (256 is a good size), at most 256 KB.
117
- - `hawk-app validate` and `deploy` check it; `deploy` uploads it with each version, so an icon change is reviewed like
118
- other settings. Leave `icon` out for the hosts' default icon. Names from HawkSearch's icon set are no longer used.
119
-
120
- ## Views
121
-
122
- `views` in `hawk-app.json` declares pages of the build that hosts show inside other pages:
123
- - **`preview`:** shown live on the Apps page's cards, under the description.
124
- - **`"embeddable": true`:** Bridgeline users can place the view in app slots of HawkSearch pages (per engine or for
125
- every client), with values for its `params`.
126
-
127
- Each view is a route, e.g. `src/app/views/preview/page.tsx` for `"path": "views/preview"`. It reads
128
- `app.context.view` (`{ name, params, embedded, maxHeight }`). In a view:
129
- - Keep it small and fast: read stored results, not reports. Many views can share a page.
130
- - The page background is transparent (the host's shows through) and the height fits the content: never set
131
- `html, body { height: 100% }`, or it can't shrink.
132
- - Toasts and title changes are ignored; `app.navigate({ intent: 'app', slug })` opens the full app.
133
- - `"cacheMinutes": n` (0–1440) on a view caches its reads (data queries, reports, `dashboard.get`, search,
134
- recommendations) on the platform for n minutes, per client and engine. A `preview` caches 60 minutes by default
135
- (`0` turns it off); other views don't unless they say so. Pass
136
- `{ cache: 'no-store' }` on a call that writes (e.g. an analytics report that records something), or
137
- `{ cache: 'reload' }` to refresh. The full app page is never cached.
138
-
139
- A slot can also hold the **entire app** (its own page, with no `view` in the context): it then runs inside another
140
- page, at the slot's height, so don't rely on the full window either.
141
-
142
- ## Queries and functions
143
-
144
- - **Queries** (`queries/<name>.sql` + `<name>.json`, next to `hawk-app.json`): one `SELECT`/`WITH`, `?` for each
145
- parameter in order, plain table names. Every query with a date range needs `{{partitions(from, to)}}`, names its
146
- columns (no `SELECT *`) and has a `LIMIT` on lists; review flags any that don't. `npx hawk-app data tables` and
147
- `npx hawk-app data describe <table>` show the tables and columns.
148
- - **Functions** (`functions/<name>.ts`, settings under `functions` in `hawk-app.json`): Node 22 Lambda,
149
- `export const handler: HawkFunction<In, Out> = async (input, ctx) => ...` with `HawkFunction` from
150
- `@bridgeline-digital/hawk-app-bridge/server`. Use `ctx.platform` (same APIs as `app`), `ctx.secrets.NAME`,
151
- `ctx.client.id`, `ctx.user`. Stateless; 25 s, or 900 s for a job (`"job": true`). Engineering enables functions per
152
- app.
153
-
154
- ## Project
155
-
156
- __PROJECT__
157
-
158
- ## Commands
159
-
160
- ```bash
161
- npx hawk-app doctor # check the setup (Node, login, hawk-app.json, scopes the code uses) and what to fix
162
- npm run app:dev # run the app locally in a stand-in for HawkSearch (asks which client the first time)
163
- npx hawk-app validate # check hawk-app.json, queries/ and functions/ (--build also checks the build's URLs)
164
- npm run app:audit # npm audit of the production dependencies: fix high and critical before deploying
165
- npm run app:deploy # build and upload a new version, with hawk-app.json (dev unless --env prod)
166
- npx hawk-app deploy --dry-run # what hawk-app.json would change, without a version
167
- npx hawk-app status # released and newest versions, the request, the aliases
168
- npx hawk-app versions # every version with its status and aliases
169
- npx hawk-app open # the app's page in HawkSearch (review, submit for approval)
170
- npx hawk-app logs <function> # a function's logs
171
- npx hawk-app config list # config values (config set <key> <value> [--client <id>], config unset <key>)
172
- npx hawk-app errors # errors in the app's pages (deployed versions), the most frequent first
173
- npx hawk-app usage # opens, users and clients per day
174
- npx hawk-app upgrade # bring the app (and this file) up to the CLI's current template
175
- npx hawk-app upgrade --channel latest # a dev app (bridge/CLI on the `dev` tag) to the released versions
176
- ```
177
-
178
- Deploy only when asked to. Never use `--env prod` unless asked to. If the bridge or CLI is on the
179
- `dev` tag (a dev app), its features may only exist on the dev platform: don't move it to `latest` or to prod unless
180
- asked to.
181
-
182
- ## Review
183
-
184
- A new app is a draft that only Bridgeline staff see. Members of Hawk-App-Developers submit it (and each later version
185
- or settings change) for approval in HawkSearch; Hawk-App-Approvers approve it. The approve dialog runs automated
186
- checks, and a failed check forces the approver to override it:
187
-
188
- - **Bundle:** over 20 MB in total, any file over 5 MB, or 50% (and 1 MB) bigger than the released version.
189
- - **Dependencies:** high or critical `npm audit` findings in the production dependencies, or no audit.
190
- - **Outside origins:** `externalOrigins` is `["*"]`.
191
- - **Queries:** a query without `{{partitions(...)}}`; `SELECT *` and a missing `LIMIT` are flagged too.
192
- - Functions, external Lambdas and new outside origins are shown to the approver.
1
+ # __NAME__ (`__SLUG__`): a Hawk App
2
+
3
+ A Hawk App is a static web app that runs in an iframe inside the HawkSearch dashboard (and later Spark), for one
4
+ client at a time. It gets its context and all of its data through the bridge,
5
+ `@bridgeline-digital/hawk-app-bridge`. It's deployed with the CLI,
6
+ `@bridgeline-digital/hawk-app-cli` (`npx hawk-app`).
7
+
8
+ ## Rules
9
+
10
+ - **No secrets in the app.** Everything in the build is public. Never add API keys, AWS credentials, database
11
+ connection strings or HawkSearch keys. The platform adds credentials server-side, and it takes the client from the
12
+ signed app token, so the app can only reach the current client's data. A third-party key goes in a function secret
13
+ (`npx hawk-app secrets set NAME`) and is used in a function, never in the browser.
14
+ - **Settings that change without a deploy go in `config`.** Non-secret values (notification recipients, thresholds,
15
+ switches) are declared in `config` in `hawk-app.json` (type, label, default) and read with `app.config.get(key)`
16
+ (`ctx.config` in a function). Their values are set with `npx hawk-app config set` or in the Hawk Analytics portal,
17
+ never hard-coded. They reach the browser, so never put a key or password there.
18
+ - **All data goes through the bridge.** Don't call HawkSearch, the analytics API, Athena or AWS directly from the
19
+ app. Use `app.analytics`, `app.dashboard`, `app.data`, `app.functions` and the others below.
20
+ - **Static files only.** The build is plain HTML, JS and CSS: no SSR, API routes or middleware. Server code goes in
21
+ `functions/`.
22
+ - **Every asset URL is under the version's base path.** Each deploy is served from `/<slug>/v<n>/`. `hawk-app deploy`
23
+ builds with `HAWK_APP_BASE_PATH=/__SLUG__/v<n>`; the build must use it (or only relative URLs). An absolute
24
+ `/assets/...` URL loads nothing once deployed.
25
+ - **No cookies, no `localStorage` for auth.** The app runs in a cross-site iframe where browsers block cookies. For a
26
+ backend of your own, send `await app.getToken()` as a Bearer header.
27
+ - **Ask for the fewest scopes.** Each one is in `hawk-app.json` and reviewed; elevated scopes are never auto-approved.
28
+ - **List outside origins.** `externalOrigins` in `hawk-app.json` should list every https origin the app loads from or
29
+ calls (`[]` when none). The default `["*"]` fails review. To show images from hosts you can't list (a client's
30
+ product images), use `"img:*"`: images only, from any https origin, and it passes review.
31
+ - **Handle a client without analytics.** When `app.context.tenant.analyticsProvisioned === false`, show a message
32
+ instead of calling `app.analytics` or `app.data`.
33
+ - Don't add a page heading: the host shows the app's name above the frame.
34
+ - Don't edit `hawk-app.json`'s `slug` (it can't change) and don't add `status`, `clients` or `excludeClients` (set in
35
+ HawkSearch and with `npx hawk-app clients`).
36
+
37
+ ## The bridge
38
+
39
+ ```ts
40
+ import { createApp } from '@bridgeline-digital/hawk-app-bridge/client';
41
+
42
+ const app = await createApp({ autoResize: true }); // rejects with code 'not_embedded' when opened outside a host
43
+ render(app.context);
44
+ app.onContextChange(render); // the user switched client, engine or dates
45
+ ```
46
+
47
+ `app.context`: `tenant.clientId`, `tenant.analyticsProvisioned`, `engine { id, name }`, `site`,
48
+ `dateRange { from, to, timeZone }`, `user { id, email, roles }`, `locale`, `theme`, `app.slug`.
49
+
50
+ | Call | Scope |
51
+ | --- | --- |
52
+ | `app.analytics.report('queries', { limit, startDate, endDate, filters })` | `reports:read` |
53
+ | `app.dashboard.get('Synonym')`: HawkSearch dashboard API, no API version in the path | `hawksearch:read` |
54
+ | `app.dashboard.post/put/delete(path, body)`: merchandising and relevancy resources only, never retried | `hawksearch:write` |
55
+ | `app.hawksearch.search(...)`, `app.hawksearch.autocomplete(...)` on the user's engine | `hawksearch:search` |
56
+ | `app.data.query('name', params)`: your SQL in `queries/<name>.sql` + `.json` | `data:read` |
57
+ | `app.functions.call('name', input)`, `.start/.wait/.job` for jobs: `functions/<name>.ts` exporting `handler` | `functions:invoke` |
58
+ | `app.email.send(...)` | `email:send` |
59
+ | `app.storage.get/put/delete/list/batch/usage`: the app's own JSON data per client (350 KB a value, `ttlDays`, `ifVersion`); `app.storage.files.upload/url/download/list/delete`: its files (100 MB each) | `storage` |
60
+ | `app.hawksearch.systemParameters.list/get/set`: names listed in `systemParameters` | `hawksearch:sysparams:write` |
61
+ | `app.hawksearch.features.list/set/setFacet` | `hawksearch:features:write` |
62
+ | `app.hawksearch.index.rebuild()`, `.status(id?)`, `.wait(id)`: rebuild the engine's index from its feed (needs the engine's `EnableRebuildIndex` on; one at a time); `.indexes()`, `.current()` | `hawksearch:index` |
63
+ | `app.hawksearch.index.items.upsert/update/delete(...)`: items in the engine's indexes, live at once (125 a call) | `hawksearch:index:items` |
64
+ | `app.hawksearch.index.create/setCurrent/deleteIndex/rebuildAll(...)`, `.call('hierarchy/upsert', …)` and other management | `hawksearch:index:manage` |
65
+ | `app.hawksearch.recommendations({ widgetUids: [{ widgetGuid, uniqueId? }] })`: recommendation widgets, untracked | `hawksearch:recommendations` |
66
+ | `app.ai.generate/stream/info` | `ai:invoke` |
67
+ | `app.lambdas.invoke('name', input)`: names listed in `lambdas`, allowed by Engineering | `lambdas:invoke` |
68
+ | `app.config.get('key')`, `app.config.all()`: keys declared in `config`, the client's value, else the global one, else the default (null when none); not secret | none |
69
+ | `app.assistant.ask('question')`, `app.assistant.setContext({ ...what's on screen })` (null clears): the host's assistant (Hawk Assistant) answers in its chat, as the user; only from a click, and only while `context.features?.assistant` is true (it can load after the app: `app.onContextChange`) | `assistant` |
70
+
71
+ Also: `app.reportError(error)` for errors the app catches and handles (uncaught ones are reported on their own; see
72
+ them with `hawk-app errors`), `app.setTitle`, `app.toast(message, 'info'|'success'|'warning'|'error')`,
73
+ `app.navigate({ intent: 'app'|'report'|'settings'|'external', ... })` (intents, never URLs), `app.getToken()`,
74
+ `app.api.get('me')` (`internal: true` for Bridgeline staff, from the signed token: use it for staff-only screens).
75
+ Failed data calls throw `HawkApiError` (`status`, `code`); host requests throw `HawkBridgeError` (`code`). A 403
76
+ `scope_missing` means the scope is missing from `hawk-app.json` or the app wasn't deployed again after adding it
77
+ (`validate` and `deploy` warn about bridge calls whose scope is missing).
78
+ Look: every template uses the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`) for Spark's look in the
79
+ host's colors and mode: React + MUI apps (next-mui) its provider and components, every other app its stylesheet
80
+ (`/css`) and elements (`/elements`); see this app's section below. Don't restyle it or add another font. For your own
81
+ styles use its `--hawk-ui-*` variables (or the host's `--hawk-*` ones with your own fallbacks).
82
+ Accessibility: approvers see an axe-core check of the built pages (critical issues fail it): label form fields, give
83
+ images `alt`, keep one `<main>` and headings in order. `validate --build` shows the issues.
84
+ Dialogs: any `[role="dialog"]` (every MUI Dialog) covers the host's whole page on its own, even from a small frame;
85
+ center dialogs in the visible part of the frame (`--hawk-visible-top`, `--hawk-visible-height`, as the templates'
86
+ theme does), and use in-page dialogs, never `window.confirm`/`alert` (blocked in the sandbox).
87
+
88
+ Before using a call, read its section in the bridge README for the exact options, limits and error codes: the
89
+ `hawk-apps` MCP server's `docs` tool (below), or
90
+ `node_modules/@bridgeline-digital/hawk-app-bridge/README.md` when the bridge is installed, otherwise
91
+ https://www.npmjs.com/package/@bridgeline-digital/hawk-app-bridge. Its TypeScript types are in the same package.
92
+
93
+ ## MCP server
94
+
95
+ `.mcp.json` (Claude Code) and `.vscode/mcp.json` (VS Code) start the CLI's MCP server, `hawk-apps`, from
96
+ `node_modules` (`npm install` first). Its tools: `docs` (the bridge, UI kit, CLI, `hawk-app.json` schema and the
97
+ showcase app, which has working code for every feature), `ui_components` (the UI kit's components for this app's
98
+ template: use them before writing your own), `validate`, `status` (with the review checks approvers will see),
99
+ `analytics_reports` (every report `app.analytics.report()` can call, with its columns and filters),
100
+ `hawksearch_api` (HawkSearch's dashboard, search, recommendation and indexing operations: fields, the bridge call, its scope),
101
+ `data_tables` and `data_describe` (names and columns, never data), `logs`, `init`, `upgrade`,
102
+ `deploy` (with `alias`, and `dryRun` to only show what hawk-app.json would change), `versions`, `why` (what a client sees, and why), `doctor` (what's wrong with the setup), `errors`, `usage`, and `clients` and `clients_add`, which only ever use the dev platform. Prefer them to guessing. When the
103
+ user names a client ("deploy to OTC"), call `clients_add` with their words: it shows them a form to pick the exact
104
+ client. With `alias` (`hawk-app clients add <id> --alias review`) the client gets that alias's version: on a version that
105
+ isn't approved, only Bridgeline staff in it see the app. Only approvers change what a client whose users see the app
106
+ gets. `hawk-app deploy out --alias staging` deploys off the main line: the version carries this hawk-app.json, `staging`
107
+ points at it, and the version under review isn't touched. A plain `hawk-app deploy` sends hawk-app.json first, so scope
108
+ changes go with the deploy. Client names aren't shared with you; don't ask for them in the chat. Its prompts (`check-before-submit`, `add-feature`,
109
+ `explain-app`, `deploy-to-client`; in Claude Code `/mcp__hawk-apps__<name>`) are ready-made tasks that use them.
110
+
111
+ ## Icon
112
+
113
+ `icon` in `hawk-app.json` is the app's icon file, next to it: `icon.svg` by default, which `create` and `init` add.
114
+ - **An .svg** is drawn in the host's own color (HawkSearch, Spark; light and dark): only its shape counts, so draw it in
115
+ one color, on a square `viewBox` (e.g. `0 0 24 24`), at most 32 KB.
116
+ - **A .png** is shown as is, for a full-color logo: square, 64 to 1024 px (256 is a good size), at most 256 KB.
117
+ - `hawk-app validate` and `deploy` check it; `deploy` uploads it with each version, so an icon change is reviewed like
118
+ other settings. Leave `icon` out for the hosts' default icon. Names from HawkSearch's icon set are no longer used.
119
+
120
+ ## Views
121
+
122
+ `views` in `hawk-app.json` declares pages of the build that hosts show inside other pages:
123
+ - **`preview`:** shown live on the Apps page's cards, under the description.
124
+ - **`"embeddable": true`:** Bridgeline users can place the view in app slots of HawkSearch pages (per engine or for
125
+ every client), with values for its `params`.
126
+
127
+ Each view is a route, e.g. `src/app/views/preview/page.tsx` for `"path": "views/preview"`. It reads
128
+ `app.context.view` (`{ name, params, embedded, maxHeight }`). In a view:
129
+ - Keep it small and fast: read stored results, not reports. Many views can share a page.
130
+ - The page background is transparent (the host's shows through) and the height fits the content: never set
131
+ `html, body { height: 100% }`, or it can't shrink.
132
+ - Toasts and title changes are ignored; `app.navigate({ intent: 'app', slug })` opens the full app.
133
+ - `"cacheMinutes": n` (0–1440) on a view caches its reads (data queries, reports, `dashboard.get`, search,
134
+ recommendations) on the platform for n minutes, per client and engine. A `preview` caches 60 minutes by default
135
+ (`0` turns it off); other views don't unless they say so. Pass
136
+ `{ cache: 'no-store' }` on a call that writes (e.g. an analytics report that records something), or
137
+ `{ cache: 'reload' }` to refresh. The full app page is never cached.
138
+
139
+ A slot can also hold the **entire app** (its own page, with no `view` in the context): it then runs inside another
140
+ page, at the slot's height, so don't rely on the full window either.
141
+
142
+ ## Queries and functions
143
+
144
+ - **Queries** (`queries/<name>.sql` + `<name>.json`, next to `hawk-app.json`): one `SELECT`/`WITH`, `?` for each
145
+ parameter in order, plain table names. Every query with a date range needs `{{partitions(from, to)}}`, names its
146
+ columns (no `SELECT *`) and has a `LIMIT` on lists; review flags any that don't. `npx hawk-app data tables` and
147
+ `npx hawk-app data describe <table>` show the tables and columns.
148
+ - **Functions** (`functions/<name>.ts`, settings under `functions` in `hawk-app.json`): Node 22 Lambda,
149
+ `export const handler: HawkFunction<In, Out> = async (input, ctx) => ...` with `HawkFunction` from
150
+ `@bridgeline-digital/hawk-app-bridge/server`. Use `ctx.platform` (same APIs as `app`), `ctx.secrets.NAME`,
151
+ `ctx.client.id`, `ctx.user`. Stateless; 25 s, or 900 s for a job (`"job": true`). Engineering enables functions per
152
+ app.
153
+
154
+ ## Project
155
+
156
+ __PROJECT__
157
+
158
+ ## Commands
159
+
160
+ ```bash
161
+ npx hawk-app doctor # check the setup (Node, login, hawk-app.json, scopes the code uses) and what to fix
162
+ npm run app:dev # run the app locally in a stand-in for HawkSearch (asks which client the first time)
163
+ npx hawk-app validate # check hawk-app.json, queries/ and functions/ (--build also checks the build's URLs)
164
+ npm run app:audit # npm audit of the production dependencies: fix high and critical before deploying
165
+ npm run app:deploy # build and upload a new version, with hawk-app.json (dev unless --env prod)
166
+ npx hawk-app deploy --dry-run # what hawk-app.json would change, without a version
167
+ npx hawk-app status # released and newest versions, the request, the aliases
168
+ npx hawk-app versions # every version with its status and aliases
169
+ npx hawk-app open # the app's page in HawkSearch (review, submit for approval)
170
+ npx hawk-app logs <function> # a function's logs
171
+ npx hawk-app config list # config values (config set <key> <value> [--client <id>], config unset <key>)
172
+ npx hawk-app errors # errors in the app's pages (deployed versions), the most frequent first
173
+ npx hawk-app usage # opens, users and clients per day
174
+ npx hawk-app upgrade # bring the app (and this file) up to the CLI's current template
175
+ npx hawk-app upgrade --channel latest # a dev app (bridge/CLI on the `dev` tag) to the released versions
176
+ ```
177
+
178
+ Deploy only when asked to. Never use `--env prod` unless asked to. If the bridge or CLI is on the
179
+ `dev` tag (a dev app), its features may only exist on the dev platform: don't move it to `latest` or to prod unless
180
+ asked to.
181
+
182
+ ## Review
183
+
184
+ A new app is a draft that only Bridgeline staff see. Members of Hawk-App-Developers submit it (and each later version
185
+ or settings change) for approval in HawkSearch; Hawk-App-Approvers approve it. The approve dialog runs automated
186
+ checks, and a failed check forces the approver to override it:
187
+
188
+ - **Bundle:** over 20 MB in total, any file over 5 MB, or 50% (and 1 MB) bigger than the released version.
189
+ - **Dependencies:** high or critical `npm audit` findings in the production dependencies, or no audit.
190
+ - **Outside origins:** `externalOrigins` is `["*"]`.
191
+ - **Queries:** a query without `{{partitions(...)}}`; `SELECT *` and a missing `LIMIT` are flagged too.
192
+ - Functions, external Lambdas and new outside origins are shown to the approver.
@@ -1,21 +1,21 @@
1
- This app doesn't use one of the CLI's templates (it was set up with `npx hawk-app init`), so the project itself has to
2
- do what a template would:
3
-
4
- - **Bridge.** Install `@bridgeline-digital/hawk-app-bridge` and import `createApp` from
5
- `@bridgeline-digital/hawk-app-bridge/client`. Without a bundler, import it from the CDN pinned to a minor
6
- version: `https://cdn.jsdelivr.net/npm/@bridgeline-digital/hawk-app-bridge@<major.minor>/dist/client/index.js`.
7
- Connect once (`createApp`), keep the context in state, and show "Open this app from the Apps page in HawkSearch"
8
- when it rejects with `not_embedded`.
9
- - **Base path.** `hawk-app deploy` runs `npm run build` with `HAWK_APP_BASE_PATH=/__SLUG__/v<n>`. Read it in the build
10
- config, e.g. Vite `base: (process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__') + '/'`, Next.js
11
- `basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__'` with `output: 'export'`, or emit only relative URLs. `npx hawk-app validate --build` checks the output.
12
- - **Static output.** The build writes plain files to one folder (e.g. `dist/`), deployed with
13
- `npx hawk-app deploy <folder>` (it builds when package.json has a `build` script; without one it uploads the folder
14
- as it is, so use relative URLs only).
15
- - **Dev server** on `http://localhost:3100` (the dev platform accepts calls from ports 3100-3103), opened in HawkSearch
16
- with `<HawkSearch>/admin/apps/__SLUG__?devUrl=<the dev server's URL for the app>`.
17
- - **Look.** Use the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`) for Spark's look: React + MUI apps
18
- its `<HawkAppProvider>` and components, any other app its stylesheet (`/css`, or from the CDN
19
- `…/hawk-app-ui@<major.minor>/dist/css/hawk.css`) and elements (`/elements`). See the kit's README.
20
- - **Scripts.** Add `"app:dev": "hawk-app dev"`, `"app:deploy": "hawk-app deploy <folder>"` and
21
- `"app:audit": "npm audit --omit=dev --audit-level=high"` to `package.json`, with the CLI as a dev dependency.
1
+ This app doesn't use one of the CLI's templates (it was set up with `npx hawk-app init`), so the project itself has to
2
+ do what a template would:
3
+
4
+ - **Bridge.** Install `@bridgeline-digital/hawk-app-bridge` and import `createApp` from
5
+ `@bridgeline-digital/hawk-app-bridge/client`. Without a bundler, import it from the CDN pinned to a minor
6
+ version: `https://cdn.jsdelivr.net/npm/@bridgeline-digital/hawk-app-bridge@<major.minor>/dist/client/index.js`.
7
+ Connect once (`createApp`), keep the context in state, and show "Open this app from the Apps page in HawkSearch"
8
+ when it rejects with `not_embedded`.
9
+ - **Base path.** `hawk-app deploy` runs `npm run build` with `HAWK_APP_BASE_PATH=/__SLUG__/v<n>`. Read it in the build
10
+ config, e.g. Vite `base: (process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__') + '/'`, Next.js
11
+ `basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__'` with `output: 'export'`, or emit only relative URLs. `npx hawk-app validate --build` checks the output.
12
+ - **Static output.** The build writes plain files to one folder (e.g. `dist/`), deployed with
13
+ `npx hawk-app deploy <folder>` (it builds when package.json has a `build` script; without one it uploads the folder
14
+ as it is, so use relative URLs only).
15
+ - **Dev server** on `http://localhost:3100` (the dev platform accepts calls from ports 3100-3103), opened in HawkSearch
16
+ with `<HawkSearch>/admin/apps/__SLUG__?devUrl=<the dev server's URL for the app>`.
17
+ - **Look.** Use the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`) for Spark's look: React + MUI apps
18
+ its `<HawkAppProvider>` and components, any other app its stylesheet (`/css`, or from the CDN
19
+ `…/hawk-app-ui@<major.minor>/dist/css/hawk.css`) and elements (`/elements`). See the kit's README.
20
+ - **Scripts.** Add `"app:dev": "hawk-app dev"`, `"app:deploy": "hawk-app deploy <folder>"` and
21
+ `"app:audit": "npm audit --omit=dev --audit-level=high"` to `package.json`, with the CLI as a dev dependency.
@@ -1,21 +1,21 @@
1
- Created from the CLI's `next-mui` template (Next.js static export + Material UI 9 + the Hawk Apps UI kit,
2
- `@bridgeline-digital/hawk-app-ui`). `npx hawk-app upgrade` keeps it in step.
3
-
4
- - `src/app/providers.tsx` renders `<HawkAppProvider>`: it connects to the host and shows the app in Spark's theme, in
5
- the host's colors and mode. Read the app and its context with `useHawk()` (`{ app, context }`, always connected).
6
- Components that use hooks are client components (`'use client'`). `src/app/layout.tsx` imports
7
- `@bridgeline-digital/hawk-app-ui/fonts` (Montserrat): keep it.
8
- - Build screens from the kit before writing your own: `Section`, `KpiTile`/`KpiRow`, `DataTable` (sortable, totals,
9
- CSV download), `EmptyState`, `LoadingSection`, `Notice` (with `onRetry`), `ConfirmDialog`, `SideDrawer`; charts from
10
- `@bridgeline-digital/hawk-app-ui/charts` (`BarList`, `ColumnChart`, `StackedColumnChart`, `Sparkline`);
11
- `DataGrid` from `/data-grid` (add `@mui/x-data-grid@^9`) for thousands of rows. Format with `useFormat()` (the
12
- host's locale) and toast with `useToast()`, not hand-rolled helpers or a local Snackbar.
13
- - Don't restyle Spark's theme or add another font: the kit's theme is Spark's. App-specific additions go in
14
- `<HawkAppProvider theme={{ … }}>`.
15
- - `next.config.ts` has `output: 'export'`, `basePath` from `HAWK_APP_BASE_PATH` (local dev: `/__SLUG__`) and
16
- `trailingSlash`. Keep them. No server features: no `getServerSideProps`, route handlers, server actions,
17
- middleware or `next/image` optimization.
18
- - Develop: `npm run dev` (port 3100), then open
19
- `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/__SLUG__/` (dev mode
20
- allows ports 3100-3103).
21
- - Build output: `out/` (`npm run app:deploy` builds and deploys it).
1
+ Created from the CLI's `next-mui` template (Next.js static export + Material UI 9 + the Hawk Apps UI kit,
2
+ `@bridgeline-digital/hawk-app-ui`). `npx hawk-app upgrade` keeps it in step.
3
+
4
+ - `src/app/providers.tsx` renders `<HawkAppProvider>`: it connects to the host and shows the app in Spark's theme, in
5
+ the host's colors and mode. Read the app and its context with `useHawk()` (`{ app, context }`, always connected).
6
+ Components that use hooks are client components (`'use client'`). `src/app/layout.tsx` imports
7
+ `@bridgeline-digital/hawk-app-ui/fonts` (Montserrat): keep it.
8
+ - Build screens from the kit before writing your own: `Section`, `KpiTile`/`KpiRow`, `DataTable` (sortable, totals,
9
+ CSV download), `EmptyState`, `LoadingSection`, `Notice` (with `onRetry`), `ConfirmDialog`, `SideDrawer`; charts from
10
+ `@bridgeline-digital/hawk-app-ui/charts` (`BarList`, `ColumnChart`, `StackedColumnChart`, `Sparkline`);
11
+ `DataGrid` from `/data-grid` (add `@mui/x-data-grid@^9`) for thousands of rows. Format with `useFormat()` (the
12
+ host's locale) and toast with `useToast()`, not hand-rolled helpers or a local Snackbar.
13
+ - Don't restyle Spark's theme or add another font: the kit's theme is Spark's. App-specific additions go in
14
+ `<HawkAppProvider theme={{ … }}>`.
15
+ - `next.config.ts` has `output: 'export'`, `basePath` from `HAWK_APP_BASE_PATH` (local dev: `/__SLUG__`) and
16
+ `trailingSlash`. Keep them. No server features: no `getServerSideProps`, route handlers, server actions,
17
+ middleware or `next/image` optimization.
18
+ - Develop: `npm run dev` (port 3100), then open
19
+ `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/__SLUG__/` (dev mode
20
+ allows ports 3100-3103).
21
+ - Build output: `out/` (`npm run app:deploy` builds and deploys it).
@@ -1,24 +1,24 @@
1
- Created from the CLI's `__TEMPLATE__` template (Next.js static export + TypeScript). `npx hawk-app upgrade` keeps
2
- it in step.
3
-
4
- - `src/lib/useHawkApp.ts` connects to the host once and keeps the context current; build screens on it. Components
5
- that use it are client components (`'use client'`).
6
- - Spark's look comes from the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`): its stylesheet (`/css`,
7
- imported in `src/app/layout.tsx`: Montserrat, the host's colors and light or dark mode) and its elements
8
- (`/elements`, imported in the client page; safe to import on the server). Build screens from them before writing
9
- your own: classes `hawk-section` (with `hawk-section-header`, `hawk-section-title`, `hawk-section-subtitle`),
10
- `hawk-kpi-row`, `hawk-stack`, `hawk-button` (`is-outlined`, `is-small`), `hawk-notice` (`is-error`, `is-warning`,
11
- `is-success`), `hawk-input`, `hawk-select`, `hawk-chip`, `hawk-empty`, `hawk-spinner`, `hawk-muted`; elements
12
- `<hawk-kpi label value hint change good-when highlight loading>`, `<hawk-data-table>` (properties `columns`, `rows`,
13
- `totals`; attributes `download`, `max-height`, `clickable`; sortable, CSV download), `<hawk-bar-list>` (`items`,
14
- `selectable`: `hawk-select` events) and `<hawk-column-chart>` (`columns`). Data goes in as properties, settings as
15
- attributes; `src/hawk-elements.d.ts` types them in JSX. Format numbers and dates with `createFormat(context.locale)`
16
- from `/elements`. Don't add another font or restyle the kit; your own styles go in `src/app/globals.css`, using the
17
- `--hawk-ui-*` variables.
18
- - `next.config.ts` has `output: 'export'`, `basePath` from `HAWK_APP_BASE_PATH` (local dev: `/__SLUG__`) and
19
- `trailingSlash`. Keep them. No server features: no `getServerSideProps`, route handlers, server actions,
20
- middleware or `next/image` optimization.
21
- - Develop: `npm run dev` (port 3100), then open
22
- `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/__SLUG__/` (dev mode
23
- allows ports 3100-3103).
24
- - Build output: `out/` (`npm run app:deploy` builds and deploys it).
1
+ Created from the CLI's `__TEMPLATE__` template (Next.js static export + TypeScript). `npx hawk-app upgrade` keeps
2
+ it in step.
3
+
4
+ - `src/lib/useHawkApp.ts` connects to the host once and keeps the context current; build screens on it. Components
5
+ that use it are client components (`'use client'`).
6
+ - Spark's look comes from the Hawk Apps UI kit (`@bridgeline-digital/hawk-app-ui`): its stylesheet (`/css`,
7
+ imported in `src/app/layout.tsx`: Montserrat, the host's colors and light or dark mode) and its elements
8
+ (`/elements`, imported in the client page; safe to import on the server). Build screens from them before writing
9
+ your own: classes `hawk-section` (with `hawk-section-header`, `hawk-section-title`, `hawk-section-subtitle`),
10
+ `hawk-kpi-row`, `hawk-stack`, `hawk-button` (`is-outlined`, `is-small`), `hawk-notice` (`is-error`, `is-warning`,
11
+ `is-success`), `hawk-input`, `hawk-select`, `hawk-chip`, `hawk-empty`, `hawk-spinner`, `hawk-muted`; elements
12
+ `<hawk-kpi label value hint change good-when highlight loading>`, `<hawk-data-table>` (properties `columns`, `rows`,
13
+ `totals`; attributes `download`, `max-height`, `clickable`; sortable, CSV download), `<hawk-bar-list>` (`items`,
14
+ `selectable`: `hawk-select` events) and `<hawk-column-chart>` (`columns`). Data goes in as properties, settings as
15
+ attributes; `src/hawk-elements.d.ts` types them in JSX. Format numbers and dates with `createFormat(context.locale)`
16
+ from `/elements`. Don't add another font or restyle the kit; your own styles go in `src/app/globals.css`, using the
17
+ `--hawk-ui-*` variables.
18
+ - `next.config.ts` has `output: 'export'`, `basePath` from `HAWK_APP_BASE_PATH` (local dev: `/__SLUG__`) and
19
+ `trailingSlash`. Keep them. No server features: no `getServerSideProps`, route handlers, server actions,
20
+ middleware or `next/image` optimization.
21
+ - Develop: `npm run dev` (port 3100), then open
22
+ `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/__SLUG__/` (dev mode
23
+ allows ports 3100-3103).
24
+ - Build output: `out/` (`npm run app:deploy` builds and deploys it).
@@ -1,22 +1,22 @@
1
- Created from the CLI's `vanilla` template (plain HTML and JavaScript, no build step). `npx hawk-app upgrade` keeps
2
- it in step.
3
-
4
- - Everything deployed is in `public/` (`npm run app:deploy` uploads it as it is: no build step).
5
- - `public/app.js` imports the bridge from the CDN, pinned to a minor version
6
- (`https://cdn.jsdelivr.net/npm/@bridgeline-digital/hawk-app-bridge@<major.minor>/dist/client/index.js`), and
7
- the Hawk Apps UI kit's elements at the same version (`…/hawk-app-ui@<major.minor>/dist/elements/index.js`);
8
- `public/index.html` links the kit's stylesheet (`…/hawk-app-ui@<major.minor>/dist/css/hawk.css`: Spark's look,
9
- Montserrat, the host's colors and light or dark mode). Keep all three on the same version: `npx hawk-app upgrade`
10
- raises them together.
11
- - Build screens from the kit before writing your own: classes `hawk-section` (with `hawk-section-header`,
12
- `hawk-section-title`, `hawk-section-subtitle`), `hawk-kpi-row`, `hawk-stack`, `hawk-button` (`is-outlined`,
13
- `is-small`), `hawk-notice` (`is-error`, `is-warning`, `is-success`), `hawk-input`, `hawk-select`, `hawk-chip`,
14
- `hawk-empty`, `hawk-spinner`, `hawk-muted`; elements `<hawk-kpi label value hint change good-when highlight loading>`,
15
- `<hawk-data-table>` (properties `columns`, `rows`, `totals`; attributes `download`, `max-height`, `clickable`;
16
- sortable, CSV download), `<hawk-bar-list>` (`items`, `selectable`: `hawk-select` events) and `<hawk-column-chart>`
17
- (`columns`). Data goes in as properties (`el.rows = [...]`), settings as attributes. Format numbers and dates with
18
- `createFormat(context.locale)` from the elements module. Your own styles go in `public/styles.css`, using the
19
- `--hawk-ui-*` variables. `hawk-app.json` keeps `https://cdn.jsdelivr.net` in `externalOrigins` (the CDN).
20
- - Use only relative URLs (`app.js`, `styles.css`), never `/app.js`: the files are served from `/__SLUG__/v<n>/`.
21
- - Develop: `npm run app:dev` (serves `public/` on port 3100 inside a local stand-in for HawkSearch), or `npm run dev`
22
- and open `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/` (the dev mode allows ports 3100-3103).
1
+ Created from the CLI's `vanilla` template (plain HTML and JavaScript, no build step). `npx hawk-app upgrade` keeps
2
+ it in step.
3
+
4
+ - Everything deployed is in `public/` (`npm run app:deploy` uploads it as it is: no build step).
5
+ - `public/app.js` imports the bridge from the CDN, pinned to a minor version
6
+ (`https://cdn.jsdelivr.net/npm/@bridgeline-digital/hawk-app-bridge@<major.minor>/dist/client/index.js`), and
7
+ the Hawk Apps UI kit's elements at the same version (`…/hawk-app-ui@<major.minor>/dist/elements/index.js`);
8
+ `public/index.html` links the kit's stylesheet (`…/hawk-app-ui@<major.minor>/dist/css/hawk.css`: Spark's look,
9
+ Montserrat, the host's colors and light or dark mode). Keep all three on the same version: `npx hawk-app upgrade`
10
+ raises them together.
11
+ - Build screens from the kit before writing your own: classes `hawk-section` (with `hawk-section-header`,
12
+ `hawk-section-title`, `hawk-section-subtitle`), `hawk-kpi-row`, `hawk-stack`, `hawk-button` (`is-outlined`,
13
+ `is-small`), `hawk-notice` (`is-error`, `is-warning`, `is-success`), `hawk-input`, `hawk-select`, `hawk-chip`,
14
+ `hawk-empty`, `hawk-spinner`, `hawk-muted`; elements `<hawk-kpi label value hint change good-when highlight loading>`,
15
+ `<hawk-data-table>` (properties `columns`, `rows`, `totals`; attributes `download`, `max-height`, `clickable`;
16
+ sortable, CSV download), `<hawk-bar-list>` (`items`, `selectable`: `hawk-select` events) and `<hawk-column-chart>`
17
+ (`columns`). Data goes in as properties (`el.rows = [...]`), settings as attributes. Format numbers and dates with
18
+ `createFormat(context.locale)` from the elements module. Your own styles go in `public/styles.css`, using the
19
+ `--hawk-ui-*` variables. `hawk-app.json` keeps `https://cdn.jsdelivr.net` in `externalOrigins` (the CDN).
20
+ - Use only relative URLs (`app.js`, `styles.css`), never `/app.js`: the files are served from `/__SLUG__/v<n>/`.
21
+ - Develop: `npm run app:dev` (serves `public/` on port 3100 inside a local stand-in for HawkSearch), or `npm run dev`
22
+ and open `<HawkSearch>/admin/apps/__SLUG__?devUrl=http://localhost:3100/` (the dev mode allows ports 3100-3103).