@jskit-ai/create-app 0.1.2 → 0.1.6

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 (51) hide show
  1. package/bin/jskit-create-app.js +2 -2
  2. package/package.json +7 -5
  3. package/src/client/index.js +1 -0
  4. package/src/index.js +1 -0
  5. package/src/{shared → server}/index.js +56 -61
  6. package/templates/base-shell/.jskit/lock.json +31 -0
  7. package/templates/base-shell/README.md +24 -20
  8. package/templates/base-shell/app.scripts.config.mjs +1 -1
  9. package/templates/base-shell/config/public.js +30 -0
  10. package/templates/base-shell/config/server.js +1 -0
  11. package/templates/base-shell/config/surfaceAccessPolicies.js +12 -0
  12. package/templates/base-shell/eslint.config.mjs +1 -1
  13. package/templates/base-shell/gitignore +2 -0
  14. package/templates/base-shell/index.html +1 -1
  15. package/templates/base-shell/jsconfig.json +8 -0
  16. package/templates/base-shell/package.json +43 -18
  17. package/templates/base-shell/packages/main/package.descriptor.mjs +55 -0
  18. package/templates/base-shell/packages/main/package.json +12 -0
  19. package/templates/base-shell/packages/main/src/client/index.js +13 -0
  20. package/templates/base-shell/packages/main/src/client/providers/MainClientProvider.js +33 -0
  21. package/templates/base-shell/packages/main/src/server/controllers/index.js +9 -0
  22. package/templates/base-shell/packages/main/src/server/index.js +1 -0
  23. package/templates/base-shell/packages/main/src/server/providers/MainServiceProvider.js +22 -0
  24. package/templates/base-shell/packages/main/src/server/routes/index.js +9 -0
  25. package/templates/base-shell/packages/main/src/server/services/index.js +9 -0
  26. package/templates/base-shell/packages/main/src/server/support/loadAppConfig.js +55 -0
  27. package/templates/base-shell/packages/main/src/shared/index.js +8 -0
  28. package/templates/base-shell/packages/main/src/shared/schemas/index.js +20 -0
  29. package/templates/base-shell/scripts/dev-bootstrap-jskit.sh +110 -0
  30. package/templates/base-shell/scripts/just_run_verde +37 -0
  31. package/templates/base-shell/scripts/link-local-jskit-packages.sh +90 -0
  32. package/templates/base-shell/scripts/update-jskit-packages.sh +73 -0
  33. package/templates/base-shell/scripts/verdaccio/config.yaml +26 -0
  34. package/templates/base-shell/scripts/verdaccio-reset-and-publish-packages.sh +314 -0
  35. package/templates/base-shell/server/lib/runtimeEnv.js +29 -0
  36. package/templates/base-shell/server/lib/surfaceRuntime.js +10 -0
  37. package/templates/base-shell/server.js +39 -68
  38. package/templates/base-shell/src/App.vue +11 -22
  39. package/templates/base-shell/src/main.js +87 -1
  40. package/templates/base-shell/src/pages/console/index.vue +12 -0
  41. package/templates/base-shell/src/pages/console.vue +13 -0
  42. package/templates/base-shell/src/pages/home/index.vue +12 -0
  43. package/templates/base-shell/src/pages/home.vue +13 -0
  44. package/templates/base-shell/src/views/NotFound.vue +13 -0
  45. package/templates/base-shell/tests/server/{minimalShell.contract.test.js → minimalShell.validator.test.js} +44 -6
  46. package/templates/base-shell/tests/server/smoke.test.js +3 -6
  47. package/templates/base-shell/vite.config.mjs +24 -3
  48. package/templates/base-shell/vite.shared.mjs +51 -1
  49. package/README.md +0 -24
  50. package/templates/base-shell/package.json.ACTUAL_CORRECT +0 -38
  51. /package/src/{shared → server}/cliEntrypoint.js +0 -0
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ DEFAULT_VERDACCIO_CONFIG="$ROOT_DIR/scripts/verdaccio/config.yaml"
6
+ if [[ -f "$DEFAULT_VERDACCIO_CONFIG" ]]; then
7
+ VERDACCIO_CONFIG="${VERDACCIO_CONFIG:-$DEFAULT_VERDACCIO_CONFIG}"
8
+ else
9
+ VERDACCIO_CONFIG="${VERDACCIO_CONFIG:-$HOME/.config/verdaccio/config.yaml}"
10
+ fi
11
+ VERDACCIO_LISTEN="${VERDACCIO_LISTEN:-127.0.0.1:4873}"
12
+ VERDACCIO_REGISTRY="${VERDACCIO_REGISTRY:-http://$VERDACCIO_LISTEN}"
13
+ VERDACCIO_REGISTRY="${VERDACCIO_REGISTRY%/}"
14
+ VERDACCIO_LOG_FILE="${VERDACCIO_LOG_FILE:-/tmp/verdaccio-jskit.log}"
15
+ VERDACCIO_PID_FILE="${VERDACCIO_PID_FILE:-/tmp/verdaccio-jskit.pid}"
16
+ PUBLISH_CONCURRENCY="${PUBLISH_CONCURRENCY:-10}"
17
+ JSKIT_REPO_ROOT="${JSKIT_REPO_ROOT:-}"
18
+ PACKAGES_DIR="${PACKAGES_DIR:-}"
19
+ TOOLING_DIR="${TOOLING_DIR:-}"
20
+
21
+ is_valid_jskit_repo_root() {
22
+ local candidate_root="$1"
23
+ [[ -d "$candidate_root/packages" && -d "$candidate_root/packages/kernel" && -d "$candidate_root/tooling" ]]
24
+ }
25
+
26
+ resolve_jskit_repo_root() {
27
+ if [[ -n "$JSKIT_REPO_ROOT" ]]; then
28
+ echo "$JSKIT_REPO_ROOT"
29
+ return 0
30
+ fi
31
+
32
+ local current_dir="$ROOT_DIR"
33
+ while true; do
34
+ if is_valid_jskit_repo_root "$current_dir"; then
35
+ echo "$current_dir"
36
+ return 0
37
+ fi
38
+ if [[ "$current_dir" == "/" ]]; then
39
+ return 1
40
+ fi
41
+ current_dir="$(dirname "$current_dir")"
42
+ done
43
+ }
44
+
45
+ configure_source_dirs() {
46
+ if [[ -z "$PACKAGES_DIR" || -z "$TOOLING_DIR" ]]; then
47
+ local detected_root
48
+ detected_root="$(resolve_jskit_repo_root || true)"
49
+ if [[ -n "$detected_root" ]]; then
50
+ JSKIT_REPO_ROOT="$detected_root"
51
+ if [[ -z "$PACKAGES_DIR" ]]; then
52
+ PACKAGES_DIR="$JSKIT_REPO_ROOT/packages"
53
+ fi
54
+ if [[ -z "$TOOLING_DIR" ]]; then
55
+ TOOLING_DIR="$JSKIT_REPO_ROOT/tooling"
56
+ fi
57
+ fi
58
+ fi
59
+
60
+ if [[ -z "$PACKAGES_DIR" ]]; then
61
+ echo "Packages directory not configured." >&2
62
+ echo "Set PACKAGES_DIR directly, or set JSKIT_REPO_ROOT to a local jskit-ai checkout." >&2
63
+ exit 1
64
+ fi
65
+
66
+ if [[ -z "$TOOLING_DIR" ]]; then
67
+ TOOLING_DIR="/dev/null"
68
+ fi
69
+ }
70
+
71
+ resolve_storage_dir() {
72
+ if [[ -n "${VERDACCIO_STORAGE_DIR:-}" ]]; then
73
+ echo "$VERDACCIO_STORAGE_DIR"
74
+ return
75
+ fi
76
+
77
+ if [[ -f "$VERDACCIO_CONFIG" ]]; then
78
+ local configured_storage=""
79
+ configured_storage="$(sed -nE 's/^[[:space:]]*storage:[[:space:]]*([^#]+).*$/\1/p' "$VERDACCIO_CONFIG" | head -n 1 | xargs || true)"
80
+ if [[ -n "$configured_storage" ]]; then
81
+ if [[ "$configured_storage" = /* ]]; then
82
+ echo "$configured_storage"
83
+ else
84
+ echo "$(cd "$(dirname "$VERDACCIO_CONFIG")" && pwd)/$configured_storage"
85
+ fi
86
+ return
87
+ fi
88
+ fi
89
+
90
+ echo "$HOME/.local/share/verdaccio/storage"
91
+ }
92
+
93
+ stop_verdaccio() {
94
+ local listen_port
95
+ listen_port="${VERDACCIO_LISTEN##*:}"
96
+
97
+ if [[ -f "$VERDACCIO_PID_FILE" ]]; then
98
+ local pid
99
+ pid="$(cat "$VERDACCIO_PID_FILE" || true)"
100
+ if [[ -n "${pid:-}" ]] && kill -0 "$pid" 2>/dev/null; then
101
+ kill "$pid" || true
102
+ fi
103
+ rm -f "$VERDACCIO_PID_FILE"
104
+ fi
105
+
106
+ if command -v lsof >/dev/null 2>&1; then
107
+ local listener_pids
108
+ listener_pids="$(lsof -tiTCP:"$listen_port" -sTCP:LISTEN 2>/dev/null || true)"
109
+ if [[ -n "$listener_pids" ]]; then
110
+ for pid in $listener_pids; do
111
+ if [[ "$pid" != "$$" ]]; then
112
+ kill "$pid" >/dev/null 2>&1 || true
113
+ fi
114
+ done
115
+ fi
116
+
117
+ local attempts=0
118
+ while lsof -tiTCP:"$listen_port" -sTCP:LISTEN >/dev/null 2>&1; do
119
+ attempts=$((attempts + 1))
120
+ if (( attempts > 20 )); then
121
+ break
122
+ fi
123
+ sleep 0.25
124
+ done
125
+
126
+ listener_pids="$(lsof -tiTCP:"$listen_port" -sTCP:LISTEN 2>/dev/null || true)"
127
+ if [[ -n "$listener_pids" ]]; then
128
+ for pid in $listener_pids; do
129
+ if [[ "$pid" != "$$" ]]; then
130
+ kill -9 "$pid" >/dev/null 2>&1 || true
131
+ fi
132
+ done
133
+ fi
134
+ fi
135
+ }
136
+
137
+ start_verdaccio() {
138
+ local cmd=(npx --yes verdaccio --listen "$VERDACCIO_LISTEN")
139
+ local config_dir
140
+ config_dir="$(pwd)"
141
+ if [[ -f "$VERDACCIO_CONFIG" ]]; then
142
+ cmd+=(--config "$VERDACCIO_CONFIG")
143
+ config_dir="$(cd "$(dirname "$VERDACCIO_CONFIG")" && pwd)"
144
+ fi
145
+
146
+ (
147
+ cd "$config_dir"
148
+ nohup "${cmd[@]}" >"$VERDACCIO_LOG_FILE" 2>&1 &
149
+ )
150
+
151
+ local attempts=0
152
+ until curl -fsS "$VERDACCIO_REGISTRY/-/ping" >/dev/null 2>&1; do
153
+ attempts=$((attempts + 1))
154
+ if (( attempts > 60 )); then
155
+ echo "Verdaccio did not become ready at $VERDACCIO_REGISTRY within 60s." >&2
156
+ echo "See log: $VERDACCIO_LOG_FILE" >&2
157
+ exit 1
158
+ fi
159
+ sleep 1
160
+ done
161
+
162
+ local listen_port
163
+ listen_port="${VERDACCIO_LISTEN##*:}"
164
+ local listener_pid
165
+ listener_pid="$(lsof -tiTCP:"$listen_port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true)"
166
+ if [[ -n "$listener_pid" ]]; then
167
+ echo "$listener_pid" >"$VERDACCIO_PID_FILE"
168
+ fi
169
+ }
170
+
171
+ publish_packages() {
172
+ if [[ ! -d "$PACKAGES_DIR" ]]; then
173
+ echo "Packages directory not found: $PACKAGES_DIR" >&2
174
+ echo "Set PACKAGES_DIR to your monorepo packages path, or set JSKIT_REPO_ROOT." >&2
175
+ exit 1
176
+ fi
177
+
178
+ local dirs=()
179
+ if [[ -d "$TOOLING_DIR" ]]; then
180
+ while IFS= read -r dir; do
181
+ if [[ ! -f "$dir/package.json" ]]; then
182
+ continue
183
+ fi
184
+ local package_name
185
+ package_name="$(cd "$dir" && node -p "require('./package.json').name || ''" 2>/dev/null || true)"
186
+ if [[ "$package_name" == @jskit-ai/* ]]; then
187
+ dirs+=("$dir")
188
+ fi
189
+ done < <(find "$TOOLING_DIR" -mindepth 1 -maxdepth 1 -type d | sort)
190
+ fi
191
+ while IFS= read -r dir; do
192
+ dirs+=("$dir")
193
+ done < <(find "$PACKAGES_DIR" -mindepth 1 -maxdepth 1 -type d | sort)
194
+
195
+ if (( ${#dirs[@]} > 1 )); then
196
+ local deduped=()
197
+ local seen=":"
198
+ for dir in "${dirs[@]}"; do
199
+ if [[ "$seen" != *":$dir:"* ]]; then
200
+ deduped+=("$dir")
201
+ seen="${seen}${dir}:"
202
+ fi
203
+ done
204
+ dirs=("${deduped[@]}")
205
+ fi
206
+
207
+ if (( ${#dirs[@]} == 0 )); then
208
+ echo "No package directories found under $PACKAGES_DIR" >&2
209
+ exit 1
210
+ fi
211
+
212
+ local npm_userconfig
213
+ local registry_with_slash
214
+ local verdaccio_auth_token
215
+ npm_userconfig="$(mktemp)"
216
+ registry_with_slash="${VERDACCIO_REGISTRY%/}/"
217
+ verdaccio_auth_token="${VERDACCIO_AUTH_TOKEN:-dev-local-token}"
218
+ printf "@jskit-ai:registry=%s\nregistry=%s\n//%s/:_authToken=%s\n" \
219
+ "$registry_with_slash" \
220
+ "$registry_with_slash" \
221
+ "${VERDACCIO_LISTEN}" \
222
+ "$verdaccio_auth_token" >"$npm_userconfig"
223
+
224
+ if [[ ! "$PUBLISH_CONCURRENCY" =~ ^[0-9]+$ ]] || (( PUBLISH_CONCURRENCY < 1 )); then
225
+ echo "PUBLISH_CONCURRENCY must be a positive integer (got: $PUBLISH_CONCURRENCY)." >&2
226
+ exit 1
227
+ fi
228
+
229
+ echo "Publishing with concurrency=$PUBLISH_CONCURRENCY"
230
+
231
+ publish_one_package() {
232
+ local dir="$1"
233
+ local npm_userconfig="$2"
234
+ if [[ ! -f "$dir/package.json" ]]; then
235
+ return 0
236
+ fi
237
+
238
+ local package_name
239
+ local publish_dir
240
+ package_name="$(cd "$dir" && node -p "require('./package.json').name || ''")"
241
+ echo "Publishing $package_name from $dir"
242
+
243
+ publish_dir="$(mktemp -d)"
244
+ (
245
+ cd "$dir"
246
+ tar --exclude='./node_modules' -cf - .
247
+ ) | (
248
+ cd "$publish_dir"
249
+ tar -xf -
250
+ )
251
+ node -e 'const fs=require("node:fs");const p=process.argv[1];const j=JSON.parse(fs.readFileSync(p,"utf8"));delete j.private;fs.writeFileSync(p,`${JSON.stringify(j,null,2)}\n`);' "$publish_dir/package.json"
252
+ (
253
+ cd "$publish_dir"
254
+ npm publish \
255
+ --registry "$VERDACCIO_REGISTRY" \
256
+ --access public \
257
+ --workspaces=false \
258
+ --userconfig "$npm_userconfig"
259
+ )
260
+ rm -rf "$publish_dir"
261
+ }
262
+
263
+ local running_jobs=0
264
+ local publish_failed=0
265
+ for dir in "${dirs[@]}"; do
266
+ publish_one_package "$dir" "$npm_userconfig" &
267
+ running_jobs=$((running_jobs + 1))
268
+
269
+ if (( running_jobs >= PUBLISH_CONCURRENCY )); then
270
+ if ! wait -n; then
271
+ publish_failed=1
272
+ fi
273
+ running_jobs=$((running_jobs - 1))
274
+ fi
275
+ done
276
+
277
+ while (( running_jobs > 0 )); do
278
+ if ! wait -n; then
279
+ publish_failed=1
280
+ fi
281
+ running_jobs=$((running_jobs - 1))
282
+ done
283
+
284
+ if (( publish_failed != 0 )); then
285
+ echo "One or more package publishes failed." >&2
286
+ exit 1
287
+ fi
288
+
289
+ rm -f "$npm_userconfig"
290
+ }
291
+
292
+ main() {
293
+ configure_source_dirs
294
+
295
+ local storage_dir
296
+ storage_dir="$(resolve_storage_dir)"
297
+
298
+ echo "Stopping Verdaccio..."
299
+ stop_verdaccio
300
+
301
+ echo "Clearing Verdaccio storage: $storage_dir"
302
+ rm -rf "$storage_dir"
303
+ mkdir -p "$storage_dir"
304
+
305
+ echo "Starting Verdaccio at $VERDACCIO_REGISTRY..."
306
+ start_verdaccio
307
+
308
+ echo "Publishing packages from $PACKAGES_DIR and tooling in $TOOLING_DIR..."
309
+ publish_packages
310
+
311
+ echo "Done. Verdaccio is running at $VERDACCIO_REGISTRY"
312
+ }
313
+
314
+ main "$@"
@@ -1,3 +1,8 @@
1
+ import { createRequire } from "node:module";
2
+ import { surfaceRuntime } from "./surfaceRuntime.js";
3
+
4
+ const require = createRequire(import.meta.url);
5
+
1
6
  function toPort(value, fallback = 3000) {
2
7
  const parsed = Number.parseInt(String(value || "").trim(), 10);
3
8
  if (Number.isInteger(parsed) && parsed > 0) {
@@ -6,8 +11,32 @@ function toPort(value, fallback = 3000) {
6
11
  return fallback;
7
12
  }
8
13
 
14
+ let envLoaded = false;
15
+
16
+ function ensureRuntimeEnvLoaded() {
17
+ if (envLoaded) {
18
+ return;
19
+ }
20
+ try {
21
+ const dotenvModule = require("dotenv");
22
+ const loadDotEnv = dotenvModule?.config;
23
+ if (typeof loadDotEnv === "function") {
24
+ loadDotEnv();
25
+ }
26
+ } catch {
27
+ // dotenv is optional in base-shell; bundles can add it when needed.
28
+ }
29
+ envLoaded = true;
30
+ }
31
+
9
32
  function resolveRuntimeEnv() {
33
+ ensureRuntimeEnvLoaded();
34
+ const serverSurface = surfaceRuntime.normalizeSurfaceMode(
35
+ process.env.JSKIT_SERVER_SURFACE || process.env.SERVER_SURFACE
36
+ );
10
37
  return {
38
+ ...process.env,
39
+ SERVER_SURFACE: serverSurface,
11
40
  PORT: toPort(process.env.PORT, 3000),
12
41
  HOST: String(process.env.HOST || "").trim() || "0.0.0.0"
13
42
  };
@@ -0,0 +1,10 @@
1
+ import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
2
+ import { config } from "../../config/public.js";
3
+
4
+ const surfaceRuntime = createSurfaceRuntime({
5
+ allMode: config.surfaceModeAll,
6
+ surfaces: config.surfaceDefinitions,
7
+ defaultSurfaceId: config.surfaceDefaultId
8
+ });
9
+
10
+ export { surfaceRuntime };
@@ -1,86 +1,57 @@
1
1
  import Fastify from "fastify";
2
+ import { TypeBoxValidatorCompiler } from "@fastify/type-provider-typebox";
3
+ import { registerTypeBoxFormats } from "@jskit-ai/http-runtime/shared/validators/typeboxFormats";
2
4
  import { resolveRuntimeEnv } from "./server/lib/runtimeEnv.js";
3
- import { registerApiRouteDefinitions } from "@jskit-ai/server-runtime-core/apiRouteRegistration";
4
- import { createServerRuntimeFromApp, applyContributedRuntimeLifecycle } from "@jskit-ai/server-runtime-core/serverContributions";
5
5
  import path from "node:path";
6
+ import {
7
+ registerSurfaceRequestConstraint,
8
+ resolveRuntimeProfileFromSurface,
9
+ tryCreateProviderRuntimeFromApp
10
+ } from "@jskit-ai/kernel/server/platform";
11
+ import { surfaceRuntime } from "./server/lib/surfaceRuntime.js";
6
12
 
7
- function registerFallbackHealthRoute(app) {
8
- app.get("/api/v1/health", async () => {
13
+ async function createServer() {
14
+ const app = Fastify({ logger: true });
15
+ registerTypeBoxFormats();
16
+ app.setValidatorCompiler(TypeBoxValidatorCompiler);
17
+
18
+ app.get("/api/health", async () => {
9
19
  return {
10
20
  ok: true,
11
21
  app: "__APP_NAME__"
12
22
  };
13
23
  });
14
- }
15
-
16
- async function registerContributedRuntime(app, { appRoot, runtimeEnv }) {
17
- try {
18
- const composed = await createServerRuntimeFromApp({
19
- appRoot,
20
- strict: false,
21
- dependencies: {
22
- env: runtimeEnv,
23
- logger: app.log
24
- },
25
- routeConfig: {}
26
- });
27
-
28
- const routeCount = Array.isArray(composed.routes) ? composed.routes.length : 0;
29
- if (routeCount > 0) {
30
- registerApiRouteDefinitions(app, {
31
- routes: composed.routes
32
- });
33
- }
24
+ const runtimeEnv = resolveRuntimeEnv();
25
+ const appRoot = path.resolve(process.cwd());
26
+ const runtime = await tryCreateProviderRuntimeFromApp({
27
+ appRoot,
28
+ profile: resolveRuntimeProfileFromSurface({
29
+ surfaceRuntime,
30
+ serverSurface: runtimeEnv.SERVER_SURFACE,
31
+ defaultProfile: "app"
32
+ }),
33
+ env: runtimeEnv,
34
+ logger: app.log,
35
+ fastify: app
36
+ });
34
37
 
35
- const lifecycleResult = await applyContributedRuntimeLifecycle({
36
- app,
37
- runtimeResult: composed,
38
- dependencies: {
39
- env: runtimeEnv,
40
- logger: app.log
41
- }
42
- });
38
+ registerSurfaceRequestConstraint({
39
+ fastify: app,
40
+ surfaceRuntime,
41
+ serverSurface: runtimeEnv.SERVER_SURFACE,
42
+ globalUiPaths: runtime?.globalUiPaths || []
43
+ });
43
44
 
45
+ if (runtime) {
44
46
  app.log.info(
45
47
  {
46
- routeCount,
47
- pluginCount: lifecycleResult.pluginCount,
48
- workerCount: lifecycleResult.workerCount,
49
- onBootCount: lifecycleResult.onBootCount,
50
- packageOrder: composed.packageOrder
48
+ routeCount: runtime.routeCount,
49
+ surface: surfaceRuntime.normalizeSurfaceMode(runtimeEnv.SERVER_SURFACE),
50
+ providerPackages: runtime.providerPackageOrder,
51
+ packageOrder: runtime.packageOrder
51
52
  },
52
- "Registered JSKIT contributed server runtime."
53
+ "Registered JSKIT provider server runtime."
53
54
  );
54
-
55
- return {
56
- enabled: true,
57
- routeCount,
58
- pluginCount: lifecycleResult.pluginCount,
59
- workerCount: lifecycleResult.workerCount,
60
- onBootCount: lifecycleResult.onBootCount
61
- };
62
- } catch (error) {
63
- const message = String(error?.message || "");
64
- if (message.includes("Lock file not found:")) {
65
- return {
66
- enabled: false,
67
- routeCount: 0
68
- };
69
- }
70
- throw error;
71
- }
72
- }
73
-
74
- async function createServer() {
75
- const app = Fastify({ logger: true });
76
- const runtimeEnv = resolveRuntimeEnv();
77
- const appRoot = path.resolve(process.cwd());
78
- const contributed = await registerContributedRuntime(app, {
79
- appRoot,
80
- runtimeEnv
81
- });
82
- if (!contributed.enabled || contributed.routeCount < 1) {
83
- registerFallbackHealthRoute(app);
84
55
  }
85
56
 
86
57
  return app;
@@ -1,24 +1,13 @@
1
- <script setup>
2
- import { onMounted, ref } from "vue";
3
-
4
- const appTitle = "__APP_TITLE__";
5
- const health = ref("loading...");
6
-
7
- onMounted(async () => {
8
- try {
9
- const response = await fetch("/api/v1/health");
10
- const payload = await response.json();
11
- health.value = payload?.ok ? "ok" : "unhealthy";
12
- } catch {
13
- health.value = "unreachable";
14
- }
15
- });
16
- </script>
17
-
18
1
  <template>
19
- <main style="font-family: sans-serif; max-width: 48rem; margin: 3rem auto; padding: 0 1rem;">
20
- <h1>{{ appTitle }}</h1>
21
- <p>Minimal starter shell is running.</p>
22
- <p><strong>Health:</strong> {{ health }}</p>
23
- </main>
2
+ <v-app>
3
+ <v-main>
4
+ <v-container class="py-10 py-md-14">
5
+ <v-row justify="center">
6
+ <v-col cols="12" sm="11" md="10" lg="8" xl="7">
7
+ <RouterView />
8
+ </v-col>
9
+ </v-row>
10
+ </v-container>
11
+ </v-main>
12
+ </v-app>
24
13
  </template>
@@ -1,4 +1,90 @@
1
1
  import { createApp } from "vue";
2
+ import { createRouter, createWebHistory } from "vue-router/auto";
3
+ import { routes } from "vue-router/auto-routes";
4
+ import "vuetify/styles";
5
+ import { createVuetify } from "vuetify";
6
+ import * as components from "vuetify/components";
7
+ import * as directives from "vuetify/directives";
8
+ import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
2
9
  import App from "./App.vue";
10
+ import NotFoundView from "./views/NotFound.vue";
11
+ import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
12
+ import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
13
+ import {
14
+ bootstrapClientShellApp,
15
+ createShellRouter
16
+ } from "@jskit-ai/kernel/client";
17
+ import { config } from "../config/public.js";
3
18
 
4
- createApp(App).mount("#app");
19
+ const surfaceRuntime = createSurfaceRuntime({
20
+ allMode: config.surfaceModeAll,
21
+ surfaces: config.surfaceDefinitions,
22
+ defaultSurfaceId: config.surfaceDefaultId
23
+ });
24
+
25
+ const surfaceMode = surfaceRuntime.normalizeSurfaceMode(import.meta.env.VITE_SURFACE);
26
+ const { router, fallbackRoute } = createShellRouter({
27
+ createRouter,
28
+ history: createWebHistory(),
29
+ routes,
30
+ surfaceRuntime,
31
+ surfaceMode,
32
+ notFoundComponent: NotFoundView,
33
+ guard: {
34
+ surfaceDefinitions: config.surfaceDefinitions,
35
+ defaultSurfaceId: config.surfaceDefaultId,
36
+ webRootAllowed: config.webRootAllowed
37
+ }
38
+ });
39
+
40
+ const vuetify = createVuetify({
41
+ components,
42
+ directives,
43
+ theme: {
44
+ defaultTheme: "light",
45
+ themes: {
46
+ light: {
47
+ colors: {
48
+ primary: "#0f6b54",
49
+ secondary: "#3f5150",
50
+ background: "#eef3ee",
51
+ surface: "#f7fbf6",
52
+ "surface-variant": "#dfe8df",
53
+ "on-surface-variant": "#3b4c44",
54
+ error: "#9f1d1d"
55
+ }
56
+ },
57
+ dark: {
58
+ colors: {
59
+ primary: "#6fd0b5",
60
+ secondary: "#9db2af",
61
+ background: "#0f1715",
62
+ surface: "#16211e",
63
+ "surface-variant": "#253430",
64
+ "on-surface-variant": "#c5d6d2",
65
+ error: "#ffb4ab"
66
+ }
67
+ }
68
+ }
69
+ },
70
+ icons: {
71
+ defaultSet: "mdi",
72
+ aliases: mdiAliases,
73
+ sets: { mdi }
74
+ }
75
+ });
76
+
77
+ void bootstrapClientShellApp({
78
+ createApp,
79
+ rootComponent: App,
80
+ appConfig: config,
81
+ appPlugins: [vuetify],
82
+ router,
83
+ bootClientModules: bootInstalledClientModules,
84
+ surfaceRuntime,
85
+ surfaceMode,
86
+ env: import.meta.env,
87
+ fallbackRoute
88
+ }).catch((error) => {
89
+ console.error("Failed to bootstrap client app.", error);
90
+ });
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <v-card class="mx-auto" max-width="960" rounded="xl" border elevation="1">
3
+ <v-card-item class="px-6 py-5 px-md-8 py-md-7">
4
+ <v-card-title class="text-h4">console</v-card-title>
5
+ <v-card-subtitle class="text-subtitle-1 mt-2">operations surface</v-card-subtitle>
6
+ </v-card-item>
7
+ <v-divider />
8
+ <v-card-text class="px-6 py-5 px-md-8 py-md-7 text-body-1 text-medium-emphasis">
9
+ This surface is intended for operational tooling.
10
+ </v-card-text>
11
+ </v-card>
12
+ </template>
@@ -0,0 +1,13 @@
1
+ <route lang="json">
2
+ {
3
+ "meta": {
4
+ "jskit": {
5
+ "surface": "console"
6
+ }
7
+ }
8
+ }
9
+ </route>
10
+
11
+ <template>
12
+ <RouterView />
13
+ </template>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <v-card class="mx-auto" max-width="960" rounded="xl" border elevation="1">
3
+ <v-card-item class="px-6 py-5 px-md-8 py-md-7">
4
+ <v-card-title class="text-h4">welcome</v-card-title>
5
+ <v-card-subtitle class="text-subtitle-1 mt-2">starter app</v-card-subtitle>
6
+ </v-card-item>
7
+ <v-divider />
8
+ <v-card-text class="px-6 py-5 px-md-8 py-md-7 text-body-1 text-medium-emphasis">
9
+ Start by adding packages and pages to this app.
10
+ </v-card-text>
11
+ </v-card>
12
+ </template>
@@ -0,0 +1,13 @@
1
+ <route lang="json">
2
+ {
3
+ "meta": {
4
+ "jskit": {
5
+ "surface": "home"
6
+ }
7
+ }
8
+ }
9
+ </route>
10
+
11
+ <template>
12
+ <RouterView />
13
+ </template>
@@ -0,0 +1,13 @@
1
+ <script setup>
2
+ const title = "Not Found";
3
+ const message = "The page you requested does not exist.";
4
+ </script>
5
+
6
+ <template>
7
+ <v-container class="fill-height d-flex align-center justify-center">
8
+ <v-card class="pa-6 text-center" max-width="560" rounded="lg">
9
+ <v-card-title class="text-h4">{{ title }}</v-card-title>
10
+ <v-card-text class="text-medium-emphasis">{{ message }}</v-card-text>
11
+ </v-card>
12
+ </v-container>
13
+ </template>