gorsee 0.2.11 → 0.2.12

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 (48) hide show
  1. package/README.md +52 -4
  2. package/dist-pkg/ai/bundle.d.ts +1 -0
  3. package/dist-pkg/ai/framework-context.d.ts +2 -0
  4. package/dist-pkg/ai/framework-context.js +6 -1
  5. package/dist-pkg/ai/ide.d.ts +1 -0
  6. package/dist-pkg/ai/ide.js +3 -0
  7. package/dist-pkg/ai/index.d.ts +10 -1
  8. package/dist-pkg/ai/index.js +13 -2
  9. package/dist-pkg/ai/mcp.js +4 -0
  10. package/dist-pkg/ai/session-pack.d.ts +8 -0
  11. package/dist-pkg/ai/session-pack.js +51 -1
  12. package/dist-pkg/ai/store.d.ts +25 -1
  13. package/dist-pkg/ai/store.js +89 -3
  14. package/dist-pkg/ai/summary.d.ts +88 -0
  15. package/dist-pkg/ai/summary.js +310 -1
  16. package/dist-pkg/build/manifest.d.ts +4 -2
  17. package/dist-pkg/build/manifest.js +32 -2
  18. package/dist-pkg/cli/cmd-ai.js +66 -0
  19. package/dist-pkg/cli/cmd-build.js +72 -26
  20. package/dist-pkg/cli/cmd-check.js +104 -11
  21. package/dist-pkg/cli/cmd-create.js +333 -7
  22. package/dist-pkg/cli/cmd-deploy.js +17 -3
  23. package/dist-pkg/cli/cmd-docs.d.ts +3 -1
  24. package/dist-pkg/cli/cmd-docs.js +5 -3
  25. package/dist-pkg/cli/cmd-start.js +8 -1
  26. package/dist-pkg/cli/cmd-upgrade.d.ts +3 -0
  27. package/dist-pkg/cli/cmd-upgrade.js +14 -2
  28. package/dist-pkg/cli/cmd-worker.d.ts +9 -0
  29. package/dist-pkg/cli/cmd-worker.js +78 -0
  30. package/dist-pkg/cli/framework-md.js +16 -4
  31. package/dist-pkg/cli/index.js +5 -0
  32. package/dist-pkg/runtime/app-config.d.ts +5 -0
  33. package/dist-pkg/runtime/app-config.js +26 -5
  34. package/dist-pkg/server/index.d.ts +2 -1
  35. package/dist-pkg/server/index.js +1 -0
  36. package/dist-pkg/server/jobs.d.ts +35 -1
  37. package/dist-pkg/server/jobs.js +226 -3
  38. package/dist-pkg/server/manifest.d.ts +30 -0
  39. package/dist-pkg/server/manifest.js +30 -1
  40. package/dist-pkg/server/redis-client.d.ts +9 -0
  41. package/dist-pkg/server/redis-client.js +4 -1
  42. package/dist-pkg/server/redis-job-queue.d.ts +2 -0
  43. package/dist-pkg/server/redis-job-queue.js +434 -16
  44. package/dist-pkg/server/worker-service.d.ts +33 -0
  45. package/dist-pkg/server/worker-service.js +135 -0
  46. package/dist-pkg/server-entry.d.ts +2 -1
  47. package/dist-pkg/server-entry.js +4 -0
  48. package/package.json +1 -1
@@ -1,11 +1,11 @@
1
1
  import { join } from "node:path";
2
- import { mkdir, rm, writeFile, readdir, stat, watch } from "node:fs/promises";
2
+ import { mkdir, rm, writeFile, readdir, stat, watch, readFile } from "node:fs/promises";
3
3
  import { createRouter } from "../router/scanner.js";
4
4
  import { buildClientBundles } from "../build/client.js";
5
5
  import { configureClientBuildBackend } from "../build/client-backend.js";
6
6
  import { initializeBuildBackends } from "../build/init.js";
7
7
  import { generateStaticPages } from "../build/ssg.js";
8
- import { createBuildManifest } from "../build/manifest.js";
8
+ import { createBuildManifest, createReleaseArtifact } from "../build/manifest.js";
9
9
  import { createHash } from "node:crypto";
10
10
  import { wrapHTML } from "../server/html-shell.js";
11
11
  import { createProjectContext } from "../runtime/project.js";
@@ -17,7 +17,7 @@ import {
17
17
  resolveAIObservabilityConfig,
18
18
  runWithAITrace
19
19
  } from "../ai/index.js";
20
- import { loadAppConfig, resolveAIConfig } from "../runtime/app-config.js";
20
+ import { loadAppConfig, resolveAIConfig, resolveAppMode } from "../runtime/app-config.js";
21
21
  import { initializeCompilerBackends } from "../compiler/init.js";
22
22
  async function hashFile(path) {
23
23
  const content = await Bun.file(path).arrayBuffer();
@@ -41,7 +41,7 @@ export async function buildProject(options = {}) {
41
41
  const { cwd, paths } = createProjectContext(options);
42
42
  initializeCompilerBackends(options.env ?? process.env);
43
43
  initializeBuildBackends(options.env ?? process.env);
44
- const appConfig = await loadAppConfig(cwd);
44
+ const appConfig = await loadAppConfig(cwd), appMode = resolveAppMode(appConfig);
45
45
  configureAIObservability(resolveAIConfig(cwd, appConfig, options.ai) ?? resolveAIObservabilityConfig(cwd));
46
46
  try {
47
47
  await runWithAITrace({
@@ -50,7 +50,7 @@ export async function buildProject(options = {}) {
50
50
  source: "build",
51
51
  message: "production build",
52
52
  phase: "build",
53
- data: { cwd, minify: options.minify ?? !0, sourcemap: options.sourcemap ?? !0 }
53
+ data: { cwd, appMode, minify: options.minify ?? !0, sourcemap: options.sourcemap ?? !0 }
54
54
  }, async () => {
55
55
  const startTime = performance.now();
56
56
  console.log(`
@@ -58,50 +58,59 @@ export async function buildProject(options = {}) {
58
58
  `);
59
59
  await rm(paths.distDir, { recursive: !0, force: !0 });
60
60
  await mkdir(paths.clientDir, { recursive: !0 });
61
- await mkdir(paths.serverDir, { recursive: !0 });
61
+ if (appMode !== "frontend")
62
+ await mkdir(paths.serverDir, { recursive: !0 });
62
63
  const routes = await createRouter(paths.routesDir);
63
- console.log(` [1/5] Found ${routes.length} route(s)`);
64
+ console.log(` [1/5] Found ${routes.length} route(s) (${appMode})`);
64
65
  await emitAIEvent({
65
66
  kind: "build.phase",
66
67
  severity: "info",
67
68
  source: "build",
68
69
  message: "routes scanned",
69
70
  phase: "scan-routes",
70
- data: { routes: routes.length }
71
+ data: { routes: routes.length, appMode }
71
72
  });
72
- const build = await buildClientBundles(routes, cwd, {
73
+ const shouldBuildClient = appMode !== "server", build = shouldBuildClient ? await buildClientBundles(routes, cwd, {
73
74
  minify: options.minify ?? !0,
74
75
  sourcemap: options.sourcemap ?? !0,
75
76
  backend: options.clientBuildBackend ?? configureClientBuildBackend(options.env ?? process.env)
76
- });
77
- console.log(` [2/5] Client bundles built (${build.entryMap.size} entries)`);
77
+ }) : { entryMap: new Map };
78
+ console.log(` [2/5] ${shouldBuildClient ? `Client bundles built (${build.entryMap.size} entries)` : "Client bundle phase skipped for server mode"}`);
78
79
  await emitAIEvent({
79
80
  kind: "build.phase",
80
81
  severity: "info",
81
82
  source: "build",
82
- message: "client bundles built",
83
+ message: shouldBuildClient ? "client bundles built" : "client bundle phase skipped",
83
84
  phase: "client-bundles",
84
- data: { entries: build.entryMap.size }
85
+ data: { entries: build.entryMap.size, skipped: !shouldBuildClient, appMode }
85
86
  });
86
- const clientSrc = join(paths.gorseeDir, "client"), clientFiles = await getAllFiles(clientSrc), hashMap = new Map;
87
- for (const file of clientFiles) {
88
- const rel = file.slice(clientSrc.length + 1), preserveRuntimePath = rel.startsWith("chunks/") || rel.endsWith(".map"), hash = preserveRuntimePath ? null : await hashFile(file), ext = rel.lastIndexOf("."), hashed = preserveRuntimePath ? rel : ext > 0 ? `${rel.slice(0, ext)}.${hash}${rel.slice(ext)}` : `${rel}.${hash}`, dest = join(paths.clientDir, hashed);
89
- await mkdir(join(dest, ".."), { recursive: !0 });
90
- await Bun.write(dest, Bun.file(file));
91
- hashMap.set(rel, hashed);
87
+ const clientSrc = join(paths.gorseeDir, "client"), hashMap = new Map;
88
+ if (shouldBuildClient) {
89
+ const clientFiles = await getAllFiles(clientSrc);
90
+ for (const file of clientFiles) {
91
+ const rel = file.slice(clientSrc.length + 1), preserveRuntimePath = rel.startsWith("chunks/") || rel.endsWith(".map"), hash = preserveRuntimePath ? null : await hashFile(file), ext = rel.lastIndexOf("."), hashed = preserveRuntimePath ? rel : ext > 0 ? `${rel.slice(0, ext)}.${hash}${rel.slice(ext)}` : `${rel}.${hash}`, dest = join(paths.clientDir, hashed);
92
+ await mkdir(join(dest, ".."), { recursive: !0 });
93
+ await Bun.write(dest, Bun.file(file));
94
+ hashMap.set(rel, hashed);
95
+ }
92
96
  }
93
97
  console.log(` [3/5] Assets hashed (${hashMap.size} files)`);
94
- const manifest = await createBuildManifest(routes, build.entryMap, hashMap);
98
+ const manifest = await createBuildManifest(routes, build.entryMap, hashMap, [], appMode);
95
99
  await writeFile(join(paths.distDir, "manifest.json"), JSON.stringify(manifest, null, 2));
96
- const serverArtifacts = await buildServerArtifacts(cwd, paths.distDir);
97
- console.log(" [4/5] Manifest + server entries generated");
98
- const ssgResult = await generateStaticPages({
100
+ const serverArtifacts = appMode === "frontend" ? { serverEntries: [] } : await buildServerArtifacts(cwd, paths.distDir);
101
+ console.log(` [4/5] ${appMode === "frontend" ? "Manifest generated for frontend mode" : "Manifest + server entries generated"}`);
102
+ const ssgResult = shouldBuildClient ? await generateStaticPages({
99
103
  routesDir: paths.routesDir,
100
104
  outDir: join(paths.distDir, "static"),
101
105
  wrapHTML: (body, htmlOptions = {}) => wrapHTML(body, void 0, htmlOptions)
102
- }), finalManifest = ssgResult.pages.size > 0 ? await createBuildManifest(routes, build.entryMap, hashMap, ssgResult.pages.keys()) : manifest;
103
- if (ssgResult.pages.size > 0)
104
- await writeFile(join(paths.distDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
106
+ }) : { pages: new Map, errors: [] };
107
+ if (appMode === "frontend")
108
+ await assertFrontendBuildContract(routes);
109
+ else if (appMode === "server")
110
+ await assertServerBuildContract(routes);
111
+ const finalManifest = ssgResult.pages.size > 0 ? await createBuildManifest(routes, build.entryMap, hashMap, ssgResult.pages.keys(), appMode) : manifest, releaseArtifact = createReleaseArtifact(finalManifest, hashMap.values(), serverArtifacts.serverEntries);
112
+ await writeFile(join(paths.distDir, "manifest.json"), JSON.stringify(finalManifest, null, 2));
113
+ await writeFile(join(paths.distDir, "release.json"), JSON.stringify(releaseArtifact, null, 2));
105
114
  if (ssgResult.errors.length > 0) {
106
115
  for (const err of ssgResult.errors)
107
116
  console.error(` SSG error: ${err}`);
@@ -135,10 +144,12 @@ export async function buildProject(options = {}) {
135
144
  phase: "summary",
136
145
  data: {
137
146
  artifact: "dist/",
147
+ appMode,
138
148
  routes: routes.length,
139
149
  entries: build.entryMap.size,
140
150
  assets: hashMap.size,
141
151
  serverEntries: serverArtifacts.serverEntries,
152
+ releaseArtifact: "release.json",
142
153
  prerenderedPages: ssgResult.pages.size,
143
154
  ssgErrors: ssgResult.errors.length,
144
155
  clientKb: Number((totalSize / 1024).toFixed(1)),
@@ -196,3 +207,38 @@ export async function runBuild(args, options = {}) {
196
207
  return watchBuildProject(options);
197
208
  return buildProject(options);
198
209
  }
210
+ async function assertFrontendBuildContract(routes) {
211
+ const apiRoutes = routes.filter((route) => route.filePath.includes("/api/")), pageRoutes = routes.filter((route) => !route.filePath.includes("/api/"));
212
+ if (pageRoutes.length === 0)
213
+ return;
214
+ const missingPrerender = [], serverOnlyViolations = [], errors = [];
215
+ if (apiRoutes.length > 0)
216
+ errors.push(`frontend mode does not allow API routes: ${apiRoutes.map((route) => route.path).join(", ")}`);
217
+ for (const route of pageRoutes) {
218
+ const source = await readFile(route.filePath, "utf-8");
219
+ if (!/export\s+const\s+prerender\s*=\s*true\b/.test(source))
220
+ missingPrerender.push(route.path);
221
+ if (source.includes('"gorsee/server"') || source.includes('"gorsee/auth"') || source.includes('"gorsee/db"') || source.includes("server(") || /\bexport\s+async\s+function\s+(load|action)\b/.test(source) || /\bexport\s+function\s+(GET|POST|PUT|PATCH|DELETE)\b/.test(source))
222
+ serverOnlyViolations.push(route.path);
223
+ }
224
+ if (missingPrerender.length > 0 || serverOnlyViolations.length > 0 || errors.length > 0) {
225
+ if (missingPrerender.length > 0)
226
+ errors.push(`frontend mode requires export const prerender = true for every page route: ${missingPrerender.join(", ")}`);
227
+ if (serverOnlyViolations.length > 0)
228
+ errors.push(`frontend mode page routes must stay browser-safe and avoid server exports/imports: ${serverOnlyViolations.join(", ")}`);
229
+ throw Error(errors.join(". "));
230
+ }
231
+ }
232
+ async function assertServerBuildContract(routes) {
233
+ const serviceRoutes = routes.filter((route) => !route.filePath.includes("/api/"));
234
+ if (serviceRoutes.length === 0)
235
+ return;
236
+ const errors = [];
237
+ for (const route of serviceRoutes) {
238
+ const source = await readFile(route.filePath, "utf-8");
239
+ if (source.includes('"gorsee/client"') || source.includes('"gorsee/forms"') || source.includes('"gorsee/routes"') || source.includes("export default function") || source.includes("export default async function"))
240
+ errors.push(route.path);
241
+ }
242
+ if (errors.length > 0)
243
+ throw Error(`server mode does not allow page/UI route behavior: ${errors.join(", ")}`);
244
+ }
@@ -11,7 +11,7 @@ import {
11
11
  emitAIDiagnostic,
12
12
  emitAIEvent
13
13
  } from "../ai/index.js";
14
- import { loadAppConfig, resolveAIConfig, resolveRuntimeTopology } from "../runtime/app-config.js";
14
+ import { loadAppConfig, resolveAIConfig, resolveAppMode, resolveRuntimeTopology } from "../runtime/app-config.js";
15
15
  const MAX_FILE_LINES = 500;
16
16
  async function getAllTsFiles(dir) {
17
17
  const files = [];
@@ -130,17 +130,18 @@ async function collectASTFacts(files, cwd) {
130
130
  }
131
131
  return facts;
132
132
  }
133
- async function checkProjectStructure(cwd) {
133
+ async function checkProjectStructure(cwd, appMode) {
134
134
  const issues = [];
135
135
  try {
136
136
  await stat(join(cwd, "routes"));
137
137
  } catch {
138
- issues.push({
139
- code: "E902",
140
- file: ".",
141
- message: "Missing routes/ directory",
142
- fix: "Create routes/ directory with at least routes/index.ts"
143
- });
138
+ if (appMode !== "server")
139
+ issues.push({
140
+ code: "E902",
141
+ file: ".",
142
+ message: "Missing routes/ directory",
143
+ fix: "Create routes/ directory with at least routes/index.ts"
144
+ });
144
145
  }
145
146
  try {
146
147
  await stat(join(cwd, "app.config.ts"));
@@ -241,6 +242,93 @@ async function checkDistributedContracts(cwd, files, astFacts) {
241
242
  });
242
243
  return issues;
243
244
  }
245
+ async function checkAppModeContracts(cwd, files, astFacts, appMode) {
246
+ const issues = [];
247
+ if (appMode === "fullstack")
248
+ return issues;
249
+ for (const file of files) {
250
+ const rel = relative(cwd, file), facts = astFacts.get(file), source = await tryReadFile(file), imports = facts?.imports ?? [];
251
+ if (appMode === "frontend") {
252
+ const serverImport = imports.find((entry) => [
253
+ "gorsee/server",
254
+ "gorsee/auth",
255
+ "gorsee/db",
256
+ "gorsee/env",
257
+ "gorsee/log"
258
+ ].includes(entry.specifier)) ?? (source && [
259
+ "gorsee/server",
260
+ "gorsee/auth",
261
+ "gorsee/db",
262
+ "gorsee/env",
263
+ "gorsee/log"
264
+ ].find((specifier) => source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) ? { specifier: [
265
+ "gorsee/server",
266
+ "gorsee/auth",
267
+ "gorsee/db",
268
+ "gorsee/env",
269
+ "gorsee/log"
270
+ ].find((specifier) => source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) } : void 0);
271
+ if (serverImport)
272
+ issues.push({
273
+ code: "W922",
274
+ file: rel,
275
+ message: `frontend app imports server-oriented surface "${serverImport.specifier}"`,
276
+ fix: 'Keep frontend mode browser-safe; move server logic to another service or switch app.mode to "fullstack"'
277
+ });
278
+ const hasApiRoute = rel.startsWith("routes/api/"), hasServerUsage = facts ? facts.hasServerCall || facts.hasRouteCacheCall || facts.hasCreateAuthCall : Boolean(source && (source.includes("server(") || source.includes("routeCache(") || source.includes("createAuth(") || source.includes('from "gorsee/server"')));
279
+ if (hasApiRoute || hasServerUsage)
280
+ issues.push({
281
+ code: "W923",
282
+ file: rel,
283
+ message: "frontend app contains server/runtime-only route behavior",
284
+ fix: 'Keep frontend mode to prerendered browser-safe routes only, or switch app.mode to "fullstack"'
285
+ });
286
+ if (rel.startsWith("routes/") && !rel.startsWith("routes/api/") && (rel.endsWith(".ts") || rel.endsWith(".tsx"))) {
287
+ const content = source ?? await readFile(file, "utf-8");
288
+ if (!/export\s+const\s+prerender\s*=\s*true\b/.test(content))
289
+ issues.push({
290
+ code: "W924",
291
+ file: rel,
292
+ message: "frontend mode page route is missing export const prerender = true",
293
+ fix: "Mark every frontend page route with `export const prerender = true` so the app stays static/browser-first"
294
+ });
295
+ }
296
+ }
297
+ if (appMode === "server") {
298
+ const clientImport = imports.find((entry) => [
299
+ "gorsee/client",
300
+ "gorsee/forms",
301
+ "gorsee/routes"
302
+ ].includes(entry.specifier)) ?? (source && [
303
+ "gorsee/client",
304
+ "gorsee/forms",
305
+ "gorsee/routes"
306
+ ].find((specifier) => source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) ? { specifier: [
307
+ "gorsee/client",
308
+ "gorsee/forms",
309
+ "gorsee/routes"
310
+ ].find((specifier) => source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) } : void 0);
311
+ if (clientImport)
312
+ issues.push({
313
+ code: "W925",
314
+ file: rel,
315
+ message: `server app imports browser-oriented surface "${clientImport.specifier}"`,
316
+ fix: 'Keep server mode UI-free; remove browser imports or switch app.mode to "fullstack"'
317
+ });
318
+ if (rel.startsWith("routes/") && !rel.startsWith("routes/api/") && (rel.endsWith(".ts") || rel.endsWith(".tsx"))) {
319
+ const content = source ?? await readFile(file, "utf-8");
320
+ if (content.includes('from "gorsee/client"') || /\bexport\s+default\s+function\b/.test(content))
321
+ issues.push({
322
+ code: "W926",
323
+ file: rel,
324
+ message: "server mode route tree should stay API/service-oriented and avoid page UI exports",
325
+ fix: 'Move browser routes out of server mode, or switch app.mode to "fullstack"'
326
+ });
327
+ }
328
+ }
329
+ }
330
+ return issues;
331
+ }
244
332
  function checkImportContracts(cwd, astFacts) {
245
333
  const issues = [];
246
334
  for (const [file, facts] of astFacts) {
@@ -485,7 +573,7 @@ async function tryReadFile(path) {
485
573
  export async function checkProject(options = {}) {
486
574
  const { cwd, paths } = createProjectContext(options);
487
575
  initializeCompilerBackends(options.env ?? process.env);
488
- const runTypeScript = options.runTypeScript ?? !0, strictSecurity = options.strictSecurity ?? !1, rewriteImports = options.rewriteImports ?? !1, rewriteLoaders = options.rewriteLoaders ?? !1, result = { errors: [], warnings: [], info: [] }, appConfig = await loadAppConfig(cwd);
576
+ const runTypeScript = options.runTypeScript ?? !0, strictSecurity = options.strictSecurity ?? !1, rewriteImports = options.rewriteImports ?? !1, rewriteLoaders = options.rewriteLoaders ?? !1, result = { errors: [], warnings: [], info: [] }, appConfig = await loadAppConfig(cwd), appMode = resolveAppMode(appConfig);
489
577
  configureAIObservability(resolveAIConfig(cwd, appConfig, options.ai));
490
578
  await emitAIEvent({
491
579
  kind: "check.start",
@@ -493,7 +581,7 @@ export async function checkProject(options = {}) {
493
581
  source: "check",
494
582
  message: "project check started",
495
583
  phase: "check",
496
- data: { cwd, runTypeScript, strictSecurity, rewriteImports, rewriteLoaders }
584
+ data: { cwd, appMode, runTypeScript, strictSecurity, rewriteImports, rewriteLoaders }
497
585
  });
498
586
  if (rewriteImports) {
499
587
  const rewrite = await rewriteCanonicalImportsInProject(cwd);
@@ -503,7 +591,7 @@ export async function checkProject(options = {}) {
503
591
  const rewrite = await rewriteLegacyLoadersInProject(cwd);
504
592
  result.info.push(rewrite.changedFiles.length > 0 ? `Rewrote legacy loader exports in ${rewrite.changedFiles.length} file(s)` : "Legacy loader rewrite found no changes");
505
593
  }
506
- const structIssues = await checkProjectStructure(cwd);
594
+ const structIssues = await checkProjectStructure(cwd, appMode);
507
595
  for (const issue of structIssues)
508
596
  if (issue.code.startsWith("E"))
509
597
  result.errors.push(issue);
@@ -512,6 +600,7 @@ export async function checkProject(options = {}) {
512
600
  try {
513
601
  const routes = await createRouter(paths.routesDir);
514
602
  result.info.push(`Found ${routes.length} route(s)`);
603
+ result.info.push(`App mode: ${appMode}`);
515
604
  } catch {
516
605
  result.info.push("Could not scan routes");
517
606
  }
@@ -534,6 +623,9 @@ export async function checkProject(options = {}) {
534
623
  const distributedIssues = await checkDistributedContracts(cwd, files, astFacts);
535
624
  for (const issue of distributedIssues)
536
625
  pushIssue(result, issue, strictSecurity);
626
+ const appModeIssues = await checkAppModeContracts(cwd, files, astFacts, appMode);
627
+ for (const issue of appModeIssues)
628
+ pushIssue(result, issue, strictSecurity);
537
629
  const dependencyIssues = await checkDependencyPolicy(cwd);
538
630
  for (const issue of dependencyIssues)
539
631
  pushIssue(result, issue, strictSecurity);
@@ -591,6 +683,7 @@ ${details}`
591
683
  errors: result.errors.length,
592
684
  warnings: result.warnings.length,
593
685
  info: result.info.length,
686
+ appMode,
594
687
  strictSecurity,
595
688
  runTypeScript,
596
689
  rewriteImports,