catalyst-core 1.0.1 → 1.0.2-canary.2

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 (114) hide show
  1. package/bin/catalyst.js +105 -1
  2. package/dist/cli/README.md +119 -0
  3. package/dist/cli/appConfig.js +50 -0
  4. package/dist/cli/buildErrors.js +170 -0
  5. package/dist/cli/buildWarnings.js +28 -0
  6. package/dist/cli/devOutput.js +524 -0
  7. package/dist/cli/diagnostic.js +388 -0
  8. package/dist/cli/hints.js +69 -0
  9. package/dist/cli/package.json +3 -0
  10. package/dist/cli/spinner.js +141 -0
  11. package/dist/cli/theme.js +155 -0
  12. package/dist/errors/index.js +7 -3
  13. package/dist/errors/registry.js +8 -0
  14. package/dist/errors-index.json +7 -0
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.jsx +1 -1
  17. package/dist/logger.js +61 -6
  18. package/dist/native/androidProject/app/build.gradle.kts +2 -1
  19. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +2 -0
  20. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +9 -0
  21. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashActivity.kt +65 -34
  22. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashOverlay.kt +109 -0
  23. package/dist/native/androidSetup.js +46 -57
  24. package/dist/native/buildAndroid/assets.js +22 -13
  25. package/dist/native/buildAndroid/build.js +98 -26
  26. package/dist/native/buildAndroid/config.js +43 -12
  27. package/dist/native/buildAndroid/index.js +49 -191
  28. package/dist/native/buildAppAndroid.js +19 -8
  29. package/dist/native/buildAppIos.js +35 -16
  30. package/dist/native/buildIos/assets.js +1 -6
  31. package/dist/native/buildIos/build.js +61 -94
  32. package/dist/native/buildIos/config.js +26 -8
  33. package/dist/native/buildIos/index.js +143 -64
  34. package/dist/native/buildIos/plugins.js +12 -15
  35. package/dist/native/internalPluginUtils.js +0 -1
  36. package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Constants/ConfigConstants.swift +2 -0
  37. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashHTMLView.swift +31 -0
  38. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +17 -1
  39. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +41 -1
  40. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +2 -0
  41. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +3 -0
  42. package/dist/native/pluginComposerAndroid.js +2 -3
  43. package/dist/native/renameAndroidProject.js +42 -20
  44. package/dist/native/setupEmulatorIos.js +28 -49
  45. package/dist/native/setupServer.js +12 -15
  46. package/dist/native/terminalProgress.js +186 -147
  47. package/dist/native/utils.js +335 -54
  48. package/dist/otel.js +15 -9
  49. package/dist/scripts/build.js +314 -42
  50. package/dist/scripts/generateOfflineManifest.js +11 -1
  51. package/dist/scripts/loadEnvironmentVariables.js +24 -2
  52. package/dist/scripts/preServerInit.js +32 -10
  53. package/dist/scripts/preflight.js +115 -11
  54. package/dist/scripts/scriptUtils.js +34 -0
  55. package/dist/scripts/serve.js +65 -9
  56. package/dist/scripts/start.js +58 -21
  57. package/dist/server/expressServer.js +167 -32
  58. package/dist/server/renderer/document/Body.d.ts +0 -11
  59. package/dist/server/renderer/document/Body.jsx +0 -10
  60. package/dist/server/renderer/document/Head.d.ts +0 -13
  61. package/dist/server/renderer/document/Head.jsx +0 -12
  62. package/dist/server/renderer/handler.jsx +87 -11
  63. package/dist/server/utils/validator.js +14 -11
  64. package/dist/vite/manifest-categorization-plugin.js +7 -5
  65. package/dist/vite/resolveDevServerConfig.js +9 -2
  66. package/dist/vite/vite.config.client.js +19 -1
  67. package/dist/vite/vite.config.js +96 -19
  68. package/dist/vite/vite.config.server.js +21 -0
  69. package/dist/web-router/components/MetaTag.jsx +20 -14
  70. package/dist/web-router/components/MetaTag.test.jsx +108 -32
  71. package/dist/web-router/components/RouterDataProvider.d.ts +25 -1
  72. package/dist/web-router/components/RouterDataProvider.jsx +35 -1
  73. package/dist/web-router/components/RouterDataProvider.test.jsx +44 -0
  74. package/dist/web-router/context.d.ts +15 -1
  75. package/dist/web-router/context.jsx +11 -0
  76. package/dist/webmcp/WebMcpProvider.jsx +234 -0
  77. package/dist/webmcp/declarative.js +207 -0
  78. package/dist/webmcp/declarative.test.js +122 -0
  79. package/dist/webmcp/errors.js +59 -0
  80. package/dist/webmcp/errors.test.js +47 -0
  81. package/dist/webmcp/index.js +40 -0
  82. package/dist/webmcp/native.js +37 -0
  83. package/dist/webmcp/native.test.js +41 -0
  84. package/dist/webmcp/pageInfo.js +119 -0
  85. package/dist/webmcp/pageInfo.test.js +85 -0
  86. package/dist/webmcp/registry.dom.test.js +175 -0
  87. package/dist/webmcp/registry.js +540 -0
  88. package/dist/webmcp/registry.test.js +228 -0
  89. package/dist/webmcp/routeContext.js +11 -0
  90. package/dist/webmcp/shim-entry.js +8 -0
  91. package/dist/webmcp/shim.js +149 -0
  92. package/dist/webmcp/useTool.js +124 -0
  93. package/package.json +4 -7
  94. package/test/cli/buildErrors.test.ts +173 -0
  95. package/test/cli/buildWarnings.test.cjs +62 -0
  96. package/test/cli/devOutput.test.ts +549 -0
  97. package/test/cli/diagnostic.test.ts +434 -0
  98. package/test/cli/output.test.cjs +90 -0
  99. package/test/cli/spinner.test.ts +177 -0
  100. package/test/cli/theme.test.ts +163 -0
  101. package/test/native/redactSecrets.test.ts +72 -0
  102. package/test/native/utils.test.ts +532 -0
  103. package/test/scripts/build.test.ts +275 -0
  104. package/test/scripts/preflight.test.ts +100 -2
  105. package/test/scripts/serve.test.ts +102 -0
  106. package/test/server/fixtures/template/src/js/routes/index.jsx +8 -0
  107. package/test/server/handler.test.ts +17 -0
  108. package/test/server/logger.test.ts +185 -0
  109. package/test/server/manifestCache.test.ts +13 -0
  110. package/test/server/otel.test.ts +556 -0
  111. package/test/server/validator.test.ts +19 -8
  112. package/vitest.config.ts +4 -0
  113. package/dist/scripts/checkVersion.js +0 -3
  114. package/dist/scripts/cleanCache.js +0 -30
@@ -0,0 +1,185 @@
1
+ import { Writable } from "node:stream"
2
+ import winston from "winston"
3
+ import { afterEach, beforeEach, expect, it, vi } from "vitest"
4
+
5
+ // The rotating file transport, replaced by one that records its options and
6
+ // what it was given, so enabling file logging touches no disk.
7
+ const rotated = vi.hoisted(() => ({
8
+ options: [] as Record<string, unknown>[],
9
+ records: [] as Record<string, unknown>[],
10
+ }))
11
+ vi.mock("winston-daily-rotate-file", async () => {
12
+ const { default: winston } = await import("winston")
13
+ // winston re-exports winston-transport at runtime; its typings do not.
14
+ const { Transport } = winston as unknown as {
15
+ Transport: new (options?: object) => object
16
+ }
17
+ class FakeRotate extends Transport {
18
+ constructor(options: Record<string, unknown>) {
19
+ super(options)
20
+ rotated.options.push(options)
21
+ }
22
+ log(info: Record<string, unknown>, done: () => void) {
23
+ rotated.records.push(info)
24
+ done()
25
+ }
26
+ }
27
+ return { default: FakeRotate }
28
+ })
29
+
30
+ // What the file logs receive: every winston logger the app logger creates
31
+ // gets an extra in-memory transport, so the test reads the same serialized
32
+ // JSON lines a DailyRotateFile would write.
33
+ let written: Record<string, unknown>[]
34
+ let loggers: winston.Logger[]
35
+ let consoleLines: string[]
36
+
37
+ beforeEach(() => {
38
+ written = []
39
+ loggers = []
40
+ consoleLines = []
41
+ rotated.options.length = 0
42
+ rotated.records.length = 0
43
+ const createLogger = winston.createLogger
44
+ vi.spyOn(winston, "createLogger").mockImplementation((options) => {
45
+ const logger = createLogger(options)
46
+ const stream = new Writable({
47
+ write(chunk, _encoding, done) {
48
+ written.push(JSON.parse(String(chunk)))
49
+ done()
50
+ },
51
+ })
52
+ logger.add(new winston.transports.Stream({ stream }))
53
+ loggers.push(logger)
54
+ return logger
55
+ })
56
+ vi.spyOn(console, "log").mockImplementation((line: string) => {
57
+ consoleLines.push(String(line).replace(/\u001b\[[0-9;]*m/g, ""))
58
+ })
59
+ })
60
+
61
+ afterEach(() => {
62
+ vi.restoreAllMocks()
63
+ })
64
+
65
+ type AppLogger = Record<"info" | "error" | "debug", (msg: unknown) => void>
66
+
67
+ async function appLogger(config: Record<string, unknown> = {}): Promise<AppLogger> {
68
+ vi.resetModules()
69
+ const { configureLogger } = await import("../../src/logger.js")
70
+ return configureLogger({ enableFileLogging: false, enableConsoleLogging: false, ...config } as never)
71
+ }
72
+
73
+ it("keeps a plain object's fields queryable in the file log", async () => {
74
+ const logger = await appLogger()
75
+ const payload = { message: "upstream failed", url: "/api/orders" }
76
+ logger.error(payload)
77
+
78
+ await vi.waitFor(() => expect(written).toHaveLength(1))
79
+ expect(written[0]).toMatchObject({ message: "upstream failed", url: "/api/orders", level: "error" })
80
+ // The caller's object is not written on.
81
+ expect(payload).toEqual({ message: "upstream failed", url: "/api/orders" })
82
+ })
83
+
84
+ it("logs a circular object, and an Error inside it as its stack", async () => {
85
+ const logger = await appLogger()
86
+ const payload: Record<string, unknown> = { message: "bad", cause: new Error("root cause") }
87
+ payload.self = payload
88
+ logger.info(payload)
89
+
90
+ await vi.waitFor(() => expect(written).toHaveLength(1))
91
+ expect(written[0]).toMatchObject({ message: "bad", self: "[Circular]", level: "info" })
92
+ expect(written[0].cause).toMatch(/^Error: root cause\n\s+at /)
93
+ })
94
+
95
+ it("logs strings and Errors as text", async () => {
96
+ const logger = await appLogger()
97
+ logger.info("plain words")
98
+ logger.error(new Error("boom"))
99
+
100
+ await vi.waitFor(() => expect(written).toHaveLength(2))
101
+ expect(written[0].message).toBe("plain words")
102
+ expect(written[1].message).toMatch(/^Error: boom\n\s+at /)
103
+ })
104
+
105
+ it("echoes each level to the terminal with its label", async () => {
106
+ const logger = await appLogger()
107
+ logger.info("ready")
108
+ logger.error("failed")
109
+ logger.debug("details")
110
+ expect(consoleLines).toEqual(["INFO: ready", "ERROR: failed", "DEBUG: details"])
111
+
112
+ await vi.waitFor(() => expect(written).toHaveLength(2))
113
+ expect(written.map((record) => record.level)).toEqual(["info", "error"])
114
+ })
115
+
116
+ it("makes debug a no-op when debug logs are off", async () => {
117
+ for (const enableDebugLogs of [false, "false"]) {
118
+ consoleLines.length = 0
119
+ const logger = await appLogger({ enableDebugLogs })
120
+ logger.debug("hidden")
121
+ expect(consoleLines).toEqual([])
122
+ }
123
+ })
124
+
125
+ it("adds the console transport and a rotating file per level when enabled", async () => {
126
+ const srcPath = process.env.src_path
127
+ process.env.src_path = "/app"
128
+ // The console transport is real but kept quiet.
129
+ const consoleLog = vi
130
+ .spyOn(winston.transports.Console.prototype, "log")
131
+ .mockImplementation(((_info: unknown, done: () => void) => done()) as never)
132
+ try {
133
+ const logger = await appLogger({ enableFileLogging: "true", enableConsoleLogging: true })
134
+ logger.info({ message: "booted", port: 3005 })
135
+
136
+ expect(rotated.options.map((options) => options.filename)).toEqual([
137
+ "/app/logs/info/%DATE%.info.log",
138
+ "/app/logs/debug/%DATE%.debug.log",
139
+ "/app/logs/error/%DATE%.error.log",
140
+ ])
141
+ expect(rotated.options.map((options) => options.level)).toEqual(["info", "debug", "error"])
142
+ for (const created of loggers) {
143
+ expect(
144
+ created.transports.some((transport) => transport instanceof winston.transports.Console)
145
+ ).toBe(true)
146
+ }
147
+ await vi.waitFor(() => expect(rotated.records).toHaveLength(1))
148
+ expect(rotated.records[0]).toMatchObject({ message: "booted", port: 3005, level: "info" })
149
+ expect(consoleLog).toHaveBeenCalled()
150
+ } finally {
151
+ if (srcPath === undefined) delete process.env.src_path
152
+ else process.env.src_path = srcPath
153
+ }
154
+ })
155
+
156
+ it("renders values JSON cannot, instead of dropping or throwing on them", async () => {
157
+ const logger = await appLogger()
158
+ const bare = new Error("no stack")
159
+ bare.stack = ""
160
+ logger.info({ count: BigInt("9007199254740993") })
161
+ logger.info(bare)
162
+ logger.info(function namedHandler() {})
163
+ logger.info({
164
+ toJSON() {
165
+ throw new Error("cannot serialise")
166
+ },
167
+ })
168
+ logger.info([1, "two"])
169
+ logger.info(42)
170
+
171
+ expect(consoleLines).toEqual([
172
+ 'INFO: {"count":"9007199254740993"}',
173
+ "INFO: Error: no stack",
174
+ expect.stringMatching(/^INFO: function namedHandler\(\) \{\s*\}$/),
175
+ "INFO: [object Object]",
176
+ 'INFO: [1,"two"]',
177
+ "INFO: 42",
178
+ ])
179
+ await vi.waitFor(() => expect(written).toHaveLength(6))
180
+ // Only a plain object becomes a structured record; the rest stay text.
181
+ expect(JSON.stringify(written[0])).toContain('"count":"9007199254740993"')
182
+ expect(written[3].message).toBe("[object Object]")
183
+ expect(written[4].message).toBe('[1,"two"]')
184
+ expect(written[5].message).toBe("42")
185
+ })
@@ -68,4 +68,17 @@ describe("manifestCache (production load path)", () => {
68
68
  expect(mod.getManifest()).toBeNull()
69
69
  warn.mockRestore()
70
70
  })
71
+
72
+ it("catches and warns when manifest JSON is malformed", async () => {
73
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => {})
74
+ const vite = path.join(tmp, "build", ".vite")
75
+ fs.writeFileSync(path.join(vite, "manifest.json"), "invalid json{")
76
+ const mod = await import("../../src/server/manifestCache.js")
77
+ expect(mod.getManifest()).toBeNull()
78
+ expect(warn).toHaveBeenCalledWith(
79
+ "Could not load build manifests:",
80
+ expect.any(String),
81
+ )
82
+ warn.mockRestore()
83
+ })
71
84
  })