@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.
- package/LICENSE +7 -7
- package/README.md +770 -770
- package/agents/AGENTS.md +192 -192
- package/agents/project/custom.md +21 -21
- package/agents/project/next-mui.md +21 -21
- package/agents/project/next.md +24 -24
- package/agents/project/vanilla.md +22 -22
- package/agents/project/vite.md +19 -19
- package/bin/hawk-app.mjs +0 -0
- package/docs/bridge/README.md +1315 -1315
- package/docs/showcase/README.md +47 -47
- package/docs/showcase/functions/echo.ts +12 -12
- package/docs/showcase/functions/long-task.ts +31 -31
- package/docs/showcase/functions/showcase.ts +186 -186
- package/docs/showcase/hawk-app.json +73 -73
- package/docs/showcase/queries/assistant-prompts-by-day.json +6 -6
- package/docs/showcase/queries/assistant-prompts-by-day.sql +8 -8
- package/docs/showcase/queries/searches-by-day.json +6 -6
- package/docs/showcase/queries/searches-by-day.sql +7 -7
- package/docs/showcase/src/App.tsx +1436 -1436
- package/docs/showcase/src/main.tsx +10 -10
- package/docs/showcase/src/styles.css +71 -71
- package/docs/showcase/src/useHawkApp.ts +44 -44
- package/docs/showcase/src/vite-env.d.ts +1 -1
- package/docs/ui/README.md +166 -166
- package/package.json +1 -1
- package/schema/hawk-app.schema.json +611 -611
- package/src/accessibility.mjs +105 -105
- package/src/agents.mjs +145 -145
- package/src/aliases.mjs +30 -30
- package/src/app-icon.mjs +82 -82
- package/src/channel.mjs +116 -116
- package/src/cli.mjs +825 -825
- package/src/clients.mjs +221 -221
- package/src/config.mjs +148 -148
- package/src/create.mjs +186 -186
- package/src/credentials.mjs +56 -56
- package/src/data.mjs +169 -169
- package/src/deploy.mjs +349 -349
- package/src/dev.mjs +459 -459
- package/src/doctor.mjs +176 -176
- package/src/env.mjs +140 -140
- package/src/functions.mjs +313 -313
- package/src/login.mjs +166 -166
- package/src/logs.mjs +64 -64
- package/src/manifest.mjs +425 -425
- package/src/mcp-docs.mjs +168 -168
- package/src/mcp-hawksearch.mjs +342 -342
- package/src/mcp-reports.mjs +130 -130
- package/src/mcp-rules.mjs +109 -109
- package/src/mcp.mjs +539 -539
- package/src/platform.mjs +116 -116
- package/src/project-checks.mjs +153 -153
- package/src/queries.mjs +289 -289
- package/src/scope-check.mjs +91 -91
- package/src/secrets.mjs +86 -86
- package/src/slug-guard.mjs +80 -80
- package/src/sso.mjs +182 -182
- package/src/telemetry.mjs +50 -50
- package/src/upgrade.mjs +406 -406
- package/src/versions.mjs +102 -102
- package/templates/next/next.config.ts +13 -13
- package/templates/next/package.json +26 -26
- package/templates/next/src/lib/useHawkApp.ts +46 -46
- package/templates/next-mui/next.config.ts +13 -13
- package/templates/next-mui/package.json +30 -30
- package/templates/next-mui/src/app/layout.tsx +19 -19
- package/templates/next-mui/src/app/page.tsx +78 -78
- package/templates/next-mui/src/app/providers.tsx +10 -10
- package/templates/preact/package.json +26 -26
- package/templates/preact/src/useHawkApp.ts +44 -44
- package/templates/preact/src/vite-env.d.ts +1 -1
- package/templates/preact/tsconfig.json +17 -17
- package/templates/preact/vite.config.ts +11 -11
- package/templates/react/package.json +29 -29
- package/templates/react/src/useHawkApp.ts +44 -44
- package/templates/react/src/vite-env.d.ts +1 -1
- package/templates/react/tsconfig.json +16 -16
- package/templates/react/vite.config.ts +11 -11
- package/templates/vanilla/package.json +15 -15
package/src/versions.mjs
CHANGED
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
import { DEFAULT_ALIAS, checkAlias } from './aliases.mjs';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Versions, aliases and "why": the developer's view of who sees which version.
|
|
5
|
-
* hawk-app versions each version with its status and the aliases on it
|
|
6
|
-
* hawk-app alias set <name> <v> point an alias at a version that isn't approved (staff only)
|
|
7
|
-
* hawk-app alias delete <name> delete an alias of yours (not approved, no clients on it)
|
|
8
|
-
* hawk-app why <client> what a client's users see, and why
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const STATUS_NOTE = {
|
|
12
|
-
draft: 'staff only',
|
|
13
|
-
waiting: 'staff only, waiting for approval',
|
|
14
|
-
rejected: 'staff only',
|
|
15
|
-
approved: '',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/** `hawk-app versions`: newest first, with status and aliases. */
|
|
19
|
-
export async function versionsCommand({ client, manifest, log = console.log }) {
|
|
20
|
-
const { versions = [] } = await client.listVersions(manifest.slug);
|
|
21
|
-
if (!versions.length) {
|
|
22
|
-
log(`${manifest.slug} has no versions yet. Deploy one with \`npm run app:deploy\`.`);
|
|
23
|
-
return 0;
|
|
24
|
-
}
|
|
25
|
-
log(`${manifest.slug}: versions, newest first`);
|
|
26
|
-
for (const v of versions) {
|
|
27
|
-
const aliases = v.aliases?.length ? ` ${v.aliases.join(', ')}` : '';
|
|
28
|
-
const note = STATUS_NOTE[v.status] ? ` (${STATUS_NOTE[v.status]})` : '';
|
|
29
|
-
const branch = v.deployedFor ? ` deployed for ${v.deployedFor}` : '';
|
|
30
|
-
log(` v${String(v.version).padEnd(4)} ${v.status.padEnd(9)}${aliases}${note}${branch}`);
|
|
31
|
-
if (v.status === 'rejected' && v.rejectedReason) log(` rejected: ${v.rejectedReason}`);
|
|
32
|
-
}
|
|
33
|
-
log('Clients get the version of their alias (default without one); users see it only when it\'s approved.');
|
|
34
|
-
return 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** `hawk-app alias set <name> <version>` / `hawk-app alias delete <name>`. */
|
|
38
|
-
export async function aliasCommand({ client, manifest, sub, args = [], log = console.log }) {
|
|
39
|
-
const usage = 'Usage:\n hawk-app alias set <name> <version> (e.g. hawk-app alias set staging 15)\n hawk-app alias delete <name>';
|
|
40
|
-
const slug = manifest.slug;
|
|
41
|
-
switch (sub) {
|
|
42
|
-
case 'set': {
|
|
43
|
-
const name = checkAlias(args[0], { flag: 'alias set' });
|
|
44
|
-
const version = Number(String(args[1] ?? '').replace(/^v/, ''));
|
|
45
|
-
if (!Number.isInteger(version) || version < 1) throw new Error(usage);
|
|
46
|
-
await client.setAlias(slug, name, version);
|
|
47
|
-
log(`✔ ${name} → v${version}. Clients on ${name}: only Bridgeline staff in them see it until v${version} is approved.`);
|
|
48
|
-
return 0;
|
|
49
|
-
}
|
|
50
|
-
case 'delete': {
|
|
51
|
-
const name = checkAlias(args[0], { flag: 'alias delete' });
|
|
52
|
-
await client.deleteAlias(slug, name);
|
|
53
|
-
log(`✔ Deleted ${name}.`);
|
|
54
|
-
return 0;
|
|
55
|
-
}
|
|
56
|
-
default:
|
|
57
|
-
throw new Error(usage);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* `hawk-app why <client>`: what the client's users see of the app, and why (the same rules as the platform's catalog):
|
|
63
|
-
* the app's state and visibility, the client list, the client's alias and that version's status.
|
|
64
|
-
*/
|
|
65
|
-
export async function whyCommand({ client, manifest, clientId, log = console.log }) {
|
|
66
|
-
if (!clientId) throw new Error('Usage: hawk-app why <client id> (e.g. hawk-app why e3141)');
|
|
67
|
-
const app = await client.status(manifest.slug);
|
|
68
|
-
const row = (app.clients ?? []).find((c) => c.id === clientId);
|
|
69
|
-
const aliasName = row?.alias ?? DEFAULT_ALIAS;
|
|
70
|
-
const alias = (app.aliases ?? []).find((a) => a.name === aliasName);
|
|
71
|
-
const say = (verdict, ...reasons) => {
|
|
72
|
-
log(`${clientId}: ${verdict}`);
|
|
73
|
-
for (const reason of reasons) log(` - ${reason}`);
|
|
74
|
-
return 0;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
if (app.adminLocked) return say('nobody sees the app', 'an administrator locked it.');
|
|
78
|
-
if (app.status === 'Disabled') return say('nobody sees the app', 'it is disabled.');
|
|
79
|
-
if (app.visibility === 'internal') return say('its users don\'t see the app; Bridgeline staff do', 'visibility is "internal": Bridgeline staff only.');
|
|
80
|
-
if (app.visibility === 'allowList' && !row?.enabled) {
|
|
81
|
-
return say("its users don't see the app", `it isn't on the app's client list (visibility "allowList"). Add it: hawk-app clients add ${clientId}`);
|
|
82
|
-
}
|
|
83
|
-
if (app.visibility === 'all' && row && !row.enabled) {
|
|
84
|
-
return say("its users don't see the app", `it's excluded (visibility "all"). Add it back: hawk-app clients add ${clientId}`);
|
|
85
|
-
}
|
|
86
|
-
const listed = app.visibility === 'all' ? 'visibility "all": every client that isn\'t excluded' : 'it\'s on the app\'s client list';
|
|
87
|
-
if (aliasName === DEFAULT_ALIAS) {
|
|
88
|
-
if (!app.liveVersion || app.status !== 'Live') {
|
|
89
|
-
return say("its users don't see the app yet", listed, `it follows ${DEFAULT_ALIAS}, and nothing is released to everyone yet: Bridgeline staff in it see the newest version.`);
|
|
90
|
-
}
|
|
91
|
-
return say(`its users see v${app.liveVersion}`, listed, `it follows ${DEFAULT_ALIAS} (the version released to everyone).`);
|
|
92
|
-
}
|
|
93
|
-
if (!alias) return say('its users see the default version', listed, `its alias "${aliasName}" no longer exists, so it follows ${DEFAULT_ALIAS}.`);
|
|
94
|
-
if (alias.status !== 'approved') {
|
|
95
|
-
return say(
|
|
96
|
-
`its users don't see the app; Bridgeline staff in it see v${alias.version}`,
|
|
97
|
-
listed,
|
|
98
|
-
`it's on ${aliasName}, which points at v${alias.version}: ${alias.status ?? 'draft'}, not approved yet.`,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return say(`its users see v${alias.version}`, listed, `it's on ${aliasName}, which points at v${alias.version} (approved).`);
|
|
102
|
-
}
|
|
1
|
+
import { DEFAULT_ALIAS, checkAlias } from './aliases.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Versions, aliases and "why": the developer's view of who sees which version.
|
|
5
|
+
* hawk-app versions each version with its status and the aliases on it
|
|
6
|
+
* hawk-app alias set <name> <v> point an alias at a version that isn't approved (staff only)
|
|
7
|
+
* hawk-app alias delete <name> delete an alias of yours (not approved, no clients on it)
|
|
8
|
+
* hawk-app why <client> what a client's users see, and why
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const STATUS_NOTE = {
|
|
12
|
+
draft: 'staff only',
|
|
13
|
+
waiting: 'staff only, waiting for approval',
|
|
14
|
+
rejected: 'staff only',
|
|
15
|
+
approved: '',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** `hawk-app versions`: newest first, with status and aliases. */
|
|
19
|
+
export async function versionsCommand({ client, manifest, log = console.log }) {
|
|
20
|
+
const { versions = [] } = await client.listVersions(manifest.slug);
|
|
21
|
+
if (!versions.length) {
|
|
22
|
+
log(`${manifest.slug} has no versions yet. Deploy one with \`npm run app:deploy\`.`);
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
log(`${manifest.slug}: versions, newest first`);
|
|
26
|
+
for (const v of versions) {
|
|
27
|
+
const aliases = v.aliases?.length ? ` ${v.aliases.join(', ')}` : '';
|
|
28
|
+
const note = STATUS_NOTE[v.status] ? ` (${STATUS_NOTE[v.status]})` : '';
|
|
29
|
+
const branch = v.deployedFor ? ` deployed for ${v.deployedFor}` : '';
|
|
30
|
+
log(` v${String(v.version).padEnd(4)} ${v.status.padEnd(9)}${aliases}${note}${branch}`);
|
|
31
|
+
if (v.status === 'rejected' && v.rejectedReason) log(` rejected: ${v.rejectedReason}`);
|
|
32
|
+
}
|
|
33
|
+
log('Clients get the version of their alias (default without one); users see it only when it\'s approved.');
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** `hawk-app alias set <name> <version>` / `hawk-app alias delete <name>`. */
|
|
38
|
+
export async function aliasCommand({ client, manifest, sub, args = [], log = console.log }) {
|
|
39
|
+
const usage = 'Usage:\n hawk-app alias set <name> <version> (e.g. hawk-app alias set staging 15)\n hawk-app alias delete <name>';
|
|
40
|
+
const slug = manifest.slug;
|
|
41
|
+
switch (sub) {
|
|
42
|
+
case 'set': {
|
|
43
|
+
const name = checkAlias(args[0], { flag: 'alias set' });
|
|
44
|
+
const version = Number(String(args[1] ?? '').replace(/^v/, ''));
|
|
45
|
+
if (!Number.isInteger(version) || version < 1) throw new Error(usage);
|
|
46
|
+
await client.setAlias(slug, name, version);
|
|
47
|
+
log(`✔ ${name} → v${version}. Clients on ${name}: only Bridgeline staff in them see it until v${version} is approved.`);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
case 'delete': {
|
|
51
|
+
const name = checkAlias(args[0], { flag: 'alias delete' });
|
|
52
|
+
await client.deleteAlias(slug, name);
|
|
53
|
+
log(`✔ Deleted ${name}.`);
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(usage);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* `hawk-app why <client>`: what the client's users see of the app, and why (the same rules as the platform's catalog):
|
|
63
|
+
* the app's state and visibility, the client list, the client's alias and that version's status.
|
|
64
|
+
*/
|
|
65
|
+
export async function whyCommand({ client, manifest, clientId, log = console.log }) {
|
|
66
|
+
if (!clientId) throw new Error('Usage: hawk-app why <client id> (e.g. hawk-app why e3141)');
|
|
67
|
+
const app = await client.status(manifest.slug);
|
|
68
|
+
const row = (app.clients ?? []).find((c) => c.id === clientId);
|
|
69
|
+
const aliasName = row?.alias ?? DEFAULT_ALIAS;
|
|
70
|
+
const alias = (app.aliases ?? []).find((a) => a.name === aliasName);
|
|
71
|
+
const say = (verdict, ...reasons) => {
|
|
72
|
+
log(`${clientId}: ${verdict}`);
|
|
73
|
+
for (const reason of reasons) log(` - ${reason}`);
|
|
74
|
+
return 0;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
if (app.adminLocked) return say('nobody sees the app', 'an administrator locked it.');
|
|
78
|
+
if (app.status === 'Disabled') return say('nobody sees the app', 'it is disabled.');
|
|
79
|
+
if (app.visibility === 'internal') return say('its users don\'t see the app; Bridgeline staff do', 'visibility is "internal": Bridgeline staff only.');
|
|
80
|
+
if (app.visibility === 'allowList' && !row?.enabled) {
|
|
81
|
+
return say("its users don't see the app", `it isn't on the app's client list (visibility "allowList"). Add it: hawk-app clients add ${clientId}`);
|
|
82
|
+
}
|
|
83
|
+
if (app.visibility === 'all' && row && !row.enabled) {
|
|
84
|
+
return say("its users don't see the app", `it's excluded (visibility "all"). Add it back: hawk-app clients add ${clientId}`);
|
|
85
|
+
}
|
|
86
|
+
const listed = app.visibility === 'all' ? 'visibility "all": every client that isn\'t excluded' : 'it\'s on the app\'s client list';
|
|
87
|
+
if (aliasName === DEFAULT_ALIAS) {
|
|
88
|
+
if (!app.liveVersion || app.status !== 'Live') {
|
|
89
|
+
return say("its users don't see the app yet", listed, `it follows ${DEFAULT_ALIAS}, and nothing is released to everyone yet: Bridgeline staff in it see the newest version.`);
|
|
90
|
+
}
|
|
91
|
+
return say(`its users see v${app.liveVersion}`, listed, `it follows ${DEFAULT_ALIAS} (the version released to everyone).`);
|
|
92
|
+
}
|
|
93
|
+
if (!alias) return say('its users see the default version', listed, `its alias "${aliasName}" no longer exists, so it follows ${DEFAULT_ALIAS}.`);
|
|
94
|
+
if (alias.status !== 'approved') {
|
|
95
|
+
return say(
|
|
96
|
+
`its users don't see the app; Bridgeline staff in it see v${alias.version}`,
|
|
97
|
+
listed,
|
|
98
|
+
`it's on ${aliasName}, which points at v${alias.version}: ${alias.status ?? 'draft'}, not approved yet.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return say(`its users see v${alias.version}`, listed, `it's on ${aliasName}, which points at v${alias.version} (approved).`);
|
|
102
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { NextConfig } from 'next';
|
|
2
|
-
|
|
3
|
-
// Hawk Apps are static files on the apps CDN, one folder per deploy (/<slug>/v<n>/): static export, no server
|
|
4
|
-
// features (no SSR, API routes or middleware). All data comes through the bridge.
|
|
5
|
-
// `hawk-app deploy` builds with HAWK_APP_BASE_PATH=/<slug>/v<n>; local dev uses /<slug>.
|
|
6
|
-
const nextConfig: NextConfig = {
|
|
7
|
-
output: 'export',
|
|
8
|
-
basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__',
|
|
9
|
-
trailingSlash: true,
|
|
10
|
-
images: { unoptimized: true },
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default nextConfig;
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
// Hawk Apps are static files on the apps CDN, one folder per deploy (/<slug>/v<n>/): static export, no server
|
|
4
|
+
// features (no SSR, API routes or middleware). All data comes through the bridge.
|
|
5
|
+
// `hawk-app deploy` builds with HAWK_APP_BASE_PATH=/<slug>/v<n>; local dev uses /<slug>.
|
|
6
|
+
const nextConfig: NextConfig = {
|
|
7
|
+
output: 'export',
|
|
8
|
+
basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__',
|
|
9
|
+
trailingSlash: true,
|
|
10
|
+
images: { unoptimized: true },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default nextConfig;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "__SLUG__",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "next dev -p 3100",
|
|
7
|
-
"build": "next build",
|
|
8
|
-
"app:dev": "hawk-app dev",
|
|
9
|
-
"app:deploy": "hawk-app deploy",
|
|
10
|
-
"app:audit": "npm audit --omit=dev --audit-level=high"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@bridgeline-digital/hawk-app-bridge": "^0.1.0",
|
|
14
|
-
"@bridgeline-digital/hawk-app-ui": "^0.1.0",
|
|
15
|
-
"next": "^16.4.0",
|
|
16
|
-
"react": "^19.1.0",
|
|
17
|
-
"react-dom": "^19.1.0"
|
|
18
|
-
},
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"@bridgeline-digital/hawk-app-cli": "^__CLI_VERSION__",
|
|
21
|
-
"@types/node": "^20",
|
|
22
|
-
"@types/react": "^19.1.0",
|
|
23
|
-
"@types/react-dom": "^19.1.0",
|
|
24
|
-
"typescript": "^5.6.0"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "__SLUG__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev -p 3100",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"app:dev": "hawk-app dev",
|
|
9
|
+
"app:deploy": "hawk-app deploy",
|
|
10
|
+
"app:audit": "npm audit --omit=dev --audit-level=high"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@bridgeline-digital/hawk-app-bridge": "^0.1.0",
|
|
14
|
+
"@bridgeline-digital/hawk-app-ui": "^0.1.0",
|
|
15
|
+
"next": "^16.4.0",
|
|
16
|
+
"react": "^19.1.0",
|
|
17
|
+
"react-dom": "^19.1.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@bridgeline-digital/hawk-app-cli": "^__CLI_VERSION__",
|
|
21
|
+
"@types/node": "^20",
|
|
22
|
+
"@types/react": "^19.1.0",
|
|
23
|
+
"@types/react-dom": "^19.1.0",
|
|
24
|
+
"typescript": "^5.6.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createApp, type HawkApp, type HawkAppContext } from '@bridgeline-digital/hawk-app-bridge/client';
|
|
4
|
-
import { useEffect, useState } from 'react';
|
|
5
|
-
|
|
6
|
-
/** Shown when the app is opened directly instead of inside a host. */
|
|
7
|
-
function notEmbeddedMessage(): string {
|
|
8
|
-
return `Open this app from the Apps page in HawkSearch. Developing locally? Add ?devUrl=${window.location.href} to the app's address in HawkSearch.`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type HawkAppState =
|
|
12
|
-
| { status: 'connecting' }
|
|
13
|
-
| { status: 'ready'; app: HawkApp; context: HawkAppContext }
|
|
14
|
-
| { status: 'error'; message: string };
|
|
15
|
-
|
|
16
|
-
/** Connects to the host (the HawkSearch dashboard / Spark) once and keeps the context (client, engine, dates, theme) current. */
|
|
17
|
-
export function useHawkApp(): HawkAppState {
|
|
18
|
-
const [state, setState] = useState<HawkAppState>({ status: 'connecting' });
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
let app: HawkApp | undefined;
|
|
22
|
-
let cancelled = false;
|
|
23
|
-
|
|
24
|
-
createApp({ autoResize: true })
|
|
25
|
-
.then((connected) => {
|
|
26
|
-
if (cancelled) return connected.destroy();
|
|
27
|
-
app = connected;
|
|
28
|
-
setState({ status: 'ready', app: connected, context: connected.context });
|
|
29
|
-
connected.onContextChange((context) => setState({ status: 'ready', app: connected, context }));
|
|
30
|
-
})
|
|
31
|
-
.catch((error: { code?: string; message?: string }) => {
|
|
32
|
-
if (cancelled) return;
|
|
33
|
-
setState({
|
|
34
|
-
status: 'error',
|
|
35
|
-
message: error.code === 'not_embedded' ? notEmbeddedMessage() : `Could not connect to the host: ${error.message}`,
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
return () => {
|
|
40
|
-
cancelled = true;
|
|
41
|
-
app?.destroy();
|
|
42
|
-
};
|
|
43
|
-
}, []);
|
|
44
|
-
|
|
45
|
-
return state;
|
|
46
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createApp, type HawkApp, type HawkAppContext } from '@bridgeline-digital/hawk-app-bridge/client';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
/** Shown when the app is opened directly instead of inside a host. */
|
|
7
|
+
function notEmbeddedMessage(): string {
|
|
8
|
+
return `Open this app from the Apps page in HawkSearch. Developing locally? Add ?devUrl=${window.location.href} to the app's address in HawkSearch.`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type HawkAppState =
|
|
12
|
+
| { status: 'connecting' }
|
|
13
|
+
| { status: 'ready'; app: HawkApp; context: HawkAppContext }
|
|
14
|
+
| { status: 'error'; message: string };
|
|
15
|
+
|
|
16
|
+
/** Connects to the host (the HawkSearch dashboard / Spark) once and keeps the context (client, engine, dates, theme) current. */
|
|
17
|
+
export function useHawkApp(): HawkAppState {
|
|
18
|
+
const [state, setState] = useState<HawkAppState>({ status: 'connecting' });
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
let app: HawkApp | undefined;
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
|
|
24
|
+
createApp({ autoResize: true })
|
|
25
|
+
.then((connected) => {
|
|
26
|
+
if (cancelled) return connected.destroy();
|
|
27
|
+
app = connected;
|
|
28
|
+
setState({ status: 'ready', app: connected, context: connected.context });
|
|
29
|
+
connected.onContextChange((context) => setState({ status: 'ready', app: connected, context }));
|
|
30
|
+
})
|
|
31
|
+
.catch((error: { code?: string; message?: string }) => {
|
|
32
|
+
if (cancelled) return;
|
|
33
|
+
setState({
|
|
34
|
+
status: 'error',
|
|
35
|
+
message: error.code === 'not_embedded' ? notEmbeddedMessage() : `Could not connect to the host: ${error.message}`,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
cancelled = true;
|
|
41
|
+
app?.destroy();
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
return state;
|
|
46
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { NextConfig } from 'next';
|
|
2
|
-
|
|
3
|
-
// Hawk Apps are static files on the apps CDN, one folder per deploy (/<slug>/v<n>/): static export, no server
|
|
4
|
-
// features (no SSR, API routes or middleware). All data comes through the bridge.
|
|
5
|
-
// `hawk-app deploy` builds with HAWK_APP_BASE_PATH=/<slug>/v<n>; local dev uses /<slug>.
|
|
6
|
-
const nextConfig: NextConfig = {
|
|
7
|
-
output: 'export',
|
|
8
|
-
basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__',
|
|
9
|
-
trailingSlash: true,
|
|
10
|
-
images: { unoptimized: true },
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default nextConfig;
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
// Hawk Apps are static files on the apps CDN, one folder per deploy (/<slug>/v<n>/): static export, no server
|
|
4
|
+
// features (no SSR, API routes or middleware). All data comes through the bridge.
|
|
5
|
+
// `hawk-app deploy` builds with HAWK_APP_BASE_PATH=/<slug>/v<n>; local dev uses /<slug>.
|
|
6
|
+
const nextConfig: NextConfig = {
|
|
7
|
+
output: 'export',
|
|
8
|
+
basePath: process.env.HAWK_APP_BASE_PATH ?? '/__SLUG__',
|
|
9
|
+
trailingSlash: true,
|
|
10
|
+
images: { unoptimized: true },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default nextConfig;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "__SLUG__",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "next dev -p 3100",
|
|
7
|
-
"build": "next build",
|
|
8
|
-
"app:dev": "hawk-app dev",
|
|
9
|
-
"app:deploy": "hawk-app deploy",
|
|
10
|
-
"app:audit": "npm audit --omit=dev --audit-level=high"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@bridgeline-digital/hawk-app-bridge": "^0.1.0",
|
|
14
|
-
"@bridgeline-digital/hawk-app-ui": "^0.1.0",
|
|
15
|
-
"@emotion/react": "^11.14.0",
|
|
16
|
-
"@emotion/styled": "^11.14.1",
|
|
17
|
-
"@mui/icons-material": "^9.5.0",
|
|
18
|
-
"@mui/material": "^9.5.0",
|
|
19
|
-
"@mui/material-nextjs": "^9.5.0",
|
|
20
|
-
"next": "^16.4.0",
|
|
21
|
-
"react": "^19.1.0",
|
|
22
|
-
"react-dom": "^19.1.0"
|
|
23
|
-
},
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@bridgeline-digital/hawk-app-cli": "^__CLI_VERSION__",
|
|
26
|
-
"@types/node": "^20",
|
|
27
|
-
"@types/react": "^19.1.0",
|
|
28
|
-
"@types/react-dom": "^19.1.0",
|
|
29
|
-
"typescript": "^5.6.0"
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "__SLUG__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev -p 3100",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"app:dev": "hawk-app dev",
|
|
9
|
+
"app:deploy": "hawk-app deploy",
|
|
10
|
+
"app:audit": "npm audit --omit=dev --audit-level=high"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@bridgeline-digital/hawk-app-bridge": "^0.1.0",
|
|
14
|
+
"@bridgeline-digital/hawk-app-ui": "^0.1.0",
|
|
15
|
+
"@emotion/react": "^11.14.0",
|
|
16
|
+
"@emotion/styled": "^11.14.1",
|
|
17
|
+
"@mui/icons-material": "^9.5.0",
|
|
18
|
+
"@mui/material": "^9.5.0",
|
|
19
|
+
"@mui/material-nextjs": "^9.5.0",
|
|
20
|
+
"next": "^16.4.0",
|
|
21
|
+
"react": "^19.1.0",
|
|
22
|
+
"react-dom": "^19.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@bridgeline-digital/hawk-app-cli": "^__CLI_VERSION__",
|
|
26
|
+
"@types/node": "^20",
|
|
27
|
+
"@types/react": "^19.1.0",
|
|
28
|
+
"@types/react-dom": "^19.1.0",
|
|
29
|
+
"typescript": "^5.6.0"
|
|
30
|
+
}
|
|
31
31
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// Montserrat, Spark's font, bundled with the app (no request to Google Fonts at build or run time).
|
|
2
|
-
import '@bridgeline-digital/hawk-app-ui/fonts';
|
|
3
|
-
import { AppRouterCacheProvider } from '@mui/material-nextjs/v16-appRouter';
|
|
4
|
-
import type { Metadata } from 'next';
|
|
5
|
-
import Providers from './providers';
|
|
6
|
-
|
|
7
|
-
export const metadata: Metadata = { title: '__NAME__' };
|
|
8
|
-
|
|
9
|
-
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
|
10
|
-
return (
|
|
11
|
-
<html lang="en">
|
|
12
|
-
<body>
|
|
13
|
-
{/* Puts MUI's styles into the exported HTML, so the page doesn't flash unstyled. */}
|
|
14
|
-
<AppRouterCacheProvider>
|
|
15
|
-
<Providers>{children}</Providers>
|
|
16
|
-
</AppRouterCacheProvider>
|
|
17
|
-
</body>
|
|
18
|
-
</html>
|
|
19
|
-
);
|
|
1
|
+
// Montserrat, Spark's font, bundled with the app (no request to Google Fonts at build or run time).
|
|
2
|
+
import '@bridgeline-digital/hawk-app-ui/fonts';
|
|
3
|
+
import { AppRouterCacheProvider } from '@mui/material-nextjs/v16-appRouter';
|
|
4
|
+
import type { Metadata } from 'next';
|
|
5
|
+
import Providers from './providers';
|
|
6
|
+
|
|
7
|
+
export const metadata: Metadata = { title: '__NAME__' };
|
|
8
|
+
|
|
9
|
+
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
|
|
10
|
+
return (
|
|
11
|
+
<html lang="en">
|
|
12
|
+
<body>
|
|
13
|
+
{/* Puts MUI's styles into the exported HTML, so the page doesn't flash unstyled. */}
|
|
14
|
+
<AppRouterCacheProvider>
|
|
15
|
+
<Providers>{children}</Providers>
|
|
16
|
+
</AppRouterCacheProvider>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
19
|
+
);
|
|
20
20
|
}
|