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.
- package/bin/catalyst.js +105 -1
- package/dist/cli/README.md +119 -0
- package/dist/cli/appConfig.js +50 -0
- package/dist/cli/buildErrors.js +170 -0
- package/dist/cli/buildWarnings.js +28 -0
- package/dist/cli/devOutput.js +524 -0
- package/dist/cli/diagnostic.js +388 -0
- package/dist/cli/hints.js +69 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/spinner.js +141 -0
- package/dist/cli/theme.js +155 -0
- package/dist/errors/index.js +7 -3
- package/dist/errors/registry.js +8 -0
- package/dist/errors-index.json +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.jsx +1 -1
- package/dist/logger.js +61 -6
- package/dist/native/androidProject/app/build.gradle.kts +2 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +2 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +9 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashActivity.kt +65 -34
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashOverlay.kt +109 -0
- package/dist/native/androidSetup.js +46 -57
- package/dist/native/buildAndroid/assets.js +22 -13
- package/dist/native/buildAndroid/build.js +98 -26
- package/dist/native/buildAndroid/config.js +43 -12
- package/dist/native/buildAndroid/index.js +49 -191
- package/dist/native/buildAppAndroid.js +19 -8
- package/dist/native/buildAppIos.js +35 -16
- package/dist/native/buildIos/assets.js +1 -6
- package/dist/native/buildIos/build.js +61 -94
- package/dist/native/buildIos/config.js +26 -8
- package/dist/native/buildIos/index.js +143 -64
- package/dist/native/buildIos/plugins.js +12 -15
- package/dist/native/internalPluginUtils.js +0 -1
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Constants/ConfigConstants.swift +2 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashHTMLView.swift +31 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +17 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +41 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +2 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +3 -0
- package/dist/native/pluginComposerAndroid.js +2 -3
- package/dist/native/renameAndroidProject.js +42 -20
- package/dist/native/setupEmulatorIos.js +28 -49
- package/dist/native/setupServer.js +12 -15
- package/dist/native/terminalProgress.js +186 -147
- package/dist/native/utils.js +335 -54
- package/dist/otel.js +15 -9
- package/dist/scripts/build.js +314 -42
- package/dist/scripts/generateOfflineManifest.js +11 -1
- package/dist/scripts/loadEnvironmentVariables.js +24 -2
- package/dist/scripts/preServerInit.js +32 -10
- package/dist/scripts/preflight.js +115 -11
- package/dist/scripts/scriptUtils.js +34 -0
- package/dist/scripts/serve.js +65 -9
- package/dist/scripts/start.js +58 -21
- package/dist/server/expressServer.js +167 -32
- package/dist/server/renderer/document/Body.d.ts +0 -11
- package/dist/server/renderer/document/Body.jsx +0 -10
- package/dist/server/renderer/document/Head.d.ts +0 -13
- package/dist/server/renderer/document/Head.jsx +0 -12
- package/dist/server/renderer/handler.jsx +87 -11
- package/dist/server/utils/validator.js +14 -11
- package/dist/vite/manifest-categorization-plugin.js +7 -5
- package/dist/vite/resolveDevServerConfig.js +9 -2
- package/dist/vite/vite.config.client.js +19 -1
- package/dist/vite/vite.config.js +96 -19
- package/dist/vite/vite.config.server.js +21 -0
- package/dist/web-router/components/MetaTag.jsx +20 -14
- package/dist/web-router/components/MetaTag.test.jsx +108 -32
- package/dist/web-router/components/RouterDataProvider.d.ts +25 -1
- package/dist/web-router/components/RouterDataProvider.jsx +35 -1
- package/dist/web-router/components/RouterDataProvider.test.jsx +44 -0
- package/dist/web-router/context.d.ts +15 -1
- package/dist/web-router/context.jsx +11 -0
- package/dist/webmcp/WebMcpProvider.jsx +234 -0
- package/dist/webmcp/declarative.js +207 -0
- package/dist/webmcp/declarative.test.js +122 -0
- package/dist/webmcp/errors.js +59 -0
- package/dist/webmcp/errors.test.js +47 -0
- package/dist/webmcp/index.js +40 -0
- package/dist/webmcp/native.js +37 -0
- package/dist/webmcp/native.test.js +41 -0
- package/dist/webmcp/pageInfo.js +119 -0
- package/dist/webmcp/pageInfo.test.js +85 -0
- package/dist/webmcp/registry.dom.test.js +175 -0
- package/dist/webmcp/registry.js +540 -0
- package/dist/webmcp/registry.test.js +228 -0
- package/dist/webmcp/routeContext.js +11 -0
- package/dist/webmcp/shim-entry.js +8 -0
- package/dist/webmcp/shim.js +149 -0
- package/dist/webmcp/useTool.js +124 -0
- package/package.json +4 -7
- package/test/cli/buildErrors.test.ts +173 -0
- package/test/cli/buildWarnings.test.cjs +62 -0
- package/test/cli/devOutput.test.ts +549 -0
- package/test/cli/diagnostic.test.ts +434 -0
- package/test/cli/output.test.cjs +90 -0
- package/test/cli/spinner.test.ts +177 -0
- package/test/cli/theme.test.ts +163 -0
- package/test/native/redactSecrets.test.ts +72 -0
- package/test/native/utils.test.ts +532 -0
- package/test/scripts/build.test.ts +275 -0
- package/test/scripts/preflight.test.ts +100 -2
- package/test/scripts/serve.test.ts +102 -0
- package/test/server/fixtures/template/src/js/routes/index.jsx +8 -0
- package/test/server/handler.test.ts +17 -0
- package/test/server/logger.test.ts +185 -0
- package/test/server/manifestCache.test.ts +13 -0
- package/test/server/otel.test.ts +556 -0
- package/test/server/validator.test.ts +19 -8
- package/vitest.config.ts +4 -0
- package/dist/scripts/checkVersion.js +0 -3
- 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
|
})
|