@zappdev/cli 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.
Files changed (209) hide show
  1. package/README.md +55 -0
  2. package/dist/zapp-cli.js +9471 -0
  3. package/native/src/app/app.zc +490 -0
  4. package/native/src/event/event.zc +24 -0
  5. package/native/src/event/events.zc +70 -0
  6. package/native/src/platform/darwin/backend.zc +923 -0
  7. package/native/src/platform/darwin/backend_bootstrap.zc +9 -0
  8. package/native/src/platform/darwin/bootstrap.zc +9 -0
  9. package/native/src/platform/darwin/engine_jsc.zc +86 -0
  10. package/native/src/platform/darwin/engine_qjs.zc +92 -0
  11. package/native/src/platform/darwin/platform.zc +156 -0
  12. package/native/src/platform/darwin/webview.zc +550 -0
  13. package/native/src/platform/darwin/webview_bootstrap.zc +9 -0
  14. package/native/src/platform/darwin/window.zc +1223 -0
  15. package/native/src/platform/darwin/worker/common.zc +223 -0
  16. package/native/src/platform/darwin/worker/core/base64_core.zc +29 -0
  17. package/native/src/platform/darwin/worker/core/crypto_core.zc +19 -0
  18. package/native/src/platform/darwin/worker/core/encoding_core.zc +32 -0
  19. package/native/src/platform/darwin/worker/core/fetch_core.zc +145 -0
  20. package/native/src/platform/darwin/worker/core/url_core.zc +69 -0
  21. package/native/src/platform/darwin/worker/core/websocket_core.zc +179 -0
  22. package/native/src/platform/darwin/worker/dispatch.zc +55 -0
  23. package/native/src/platform/darwin/worker/jsc/base64_jsc.zc +39 -0
  24. package/native/src/platform/darwin/worker/jsc/crypto_jsc.zc +49 -0
  25. package/native/src/platform/darwin/worker/jsc/encoding_jsc.zc +86 -0
  26. package/native/src/platform/darwin/worker/jsc/fetch_jsc.zc +149 -0
  27. package/native/src/platform/darwin/worker/jsc/url_jsc.zc +54 -0
  28. package/native/src/platform/darwin/worker/jsc/websocket_jsc.zc +127 -0
  29. package/native/src/platform/darwin/worker/jsc.zc +670 -0
  30. package/native/src/platform/darwin/worker/mod.zc +30 -0
  31. package/native/src/platform/darwin/worker/qjs/fetch_qjs.zc +233 -0
  32. package/native/src/platform/darwin/worker/qjs/qjs_macros.zc +23 -0
  33. package/native/src/platform/darwin/worker/qjs/websocket_qjs.zc +223 -0
  34. package/native/src/platform/darwin/worker/qjs.zc +1053 -0
  35. package/native/src/platform/darwin/worker/timers.zc +149 -0
  36. package/native/src/platform/darwin/worker/timers_qjs.zc +209 -0
  37. package/native/src/platform/platform.zc +64 -0
  38. package/native/src/platform/shared/log.zc +156 -0
  39. package/native/src/platform/shared/worker/qjs/base64_qjs.zc +38 -0
  40. package/native/src/platform/shared/worker/qjs/crypto_qjs.zc +44 -0
  41. package/native/src/platform/shared/worker/qjs/encoding_qjs.zc +95 -0
  42. package/native/src/platform/shared/worker/qjs/url_qjs.zc +65 -0
  43. package/native/src/platform/shared/worker_registry.zc +206 -0
  44. package/native/src/platform/window.zc +446 -0
  45. package/native/src/platform/windows/backend.zc +452 -0
  46. package/native/src/platform/windows/backend_bootstrap.zc +9 -0
  47. package/native/src/platform/windows/bootstrap.zc +9 -0
  48. package/native/src/platform/windows/engine_qjs.zc +60 -0
  49. package/native/src/platform/windows/platform.zc +387 -0
  50. package/native/src/platform/windows/webview.zc +1175 -0
  51. package/native/src/platform/windows/webview_bootstrap.zc +9 -0
  52. package/native/src/platform/windows/window.zc +1271 -0
  53. package/native/src/platform/windows/worker/common.zc +409 -0
  54. package/native/src/platform/windows/worker/core/base64_core.zc +52 -0
  55. package/native/src/platform/windows/worker/core/crypto_core.zc +34 -0
  56. package/native/src/platform/windows/worker/core/encoding_core.zc +60 -0
  57. package/native/src/platform/windows/worker/core/fetch_core.zc +274 -0
  58. package/native/src/platform/windows/worker/core/url_core.zc +216 -0
  59. package/native/src/platform/windows/worker/core/websocket_core.zc +343 -0
  60. package/native/src/platform/windows/worker/dispatch.zc +34 -0
  61. package/native/src/platform/windows/worker/mod.zc +46 -0
  62. package/native/src/platform/windows/worker/qjs/fetch_qjs.zc +255 -0
  63. package/native/src/platform/windows/worker/qjs/websocket_qjs.zc +263 -0
  64. package/native/src/platform/windows/worker/qjs.zc +1049 -0
  65. package/native/src/platform/windows/worker/timers_qjs.zc +288 -0
  66. package/native/src/platform/worker.zc +8 -0
  67. package/native/src/service/service.zc +228 -0
  68. package/native/vendor/quickjs-ng/.gitattributes +4 -0
  69. package/native/vendor/quickjs-ng/.github/dependabot.yml +7 -0
  70. package/native/vendor/quickjs-ng/.github/workflows/ci.yml +812 -0
  71. package/native/vendor/quickjs-ng/.github/workflows/docs.yml +49 -0
  72. package/native/vendor/quickjs-ng/.github/workflows/release.yml +162 -0
  73. package/native/vendor/quickjs-ng/.github/workflows/test-docs.yml +23 -0
  74. package/native/vendor/quickjs-ng/.github/workflows/tsan.yml +32 -0
  75. package/native/vendor/quickjs-ng/.github/workflows/valgrind.yml +33 -0
  76. package/native/vendor/quickjs-ng/.gitmodules +5 -0
  77. package/native/vendor/quickjs-ng/CMakeLists.txt +553 -0
  78. package/native/vendor/quickjs-ng/LICENSE +24 -0
  79. package/native/vendor/quickjs-ng/Makefile +149 -0
  80. package/native/vendor/quickjs-ng/amalgam.js +53 -0
  81. package/native/vendor/quickjs-ng/api-test.c +927 -0
  82. package/native/vendor/quickjs-ng/builtin-array-fromasync.h +119 -0
  83. package/native/vendor/quickjs-ng/builtin-array-fromasync.js +36 -0
  84. package/native/vendor/quickjs-ng/builtin-iterator-zip-keyed.h +332 -0
  85. package/native/vendor/quickjs-ng/builtin-iterator-zip-keyed.js +194 -0
  86. package/native/vendor/quickjs-ng/builtin-iterator-zip.h +337 -0
  87. package/native/vendor/quickjs-ng/builtin-iterator-zip.js +210 -0
  88. package/native/vendor/quickjs-ng/ctest.c +17 -0
  89. package/native/vendor/quickjs-ng/cutils.h +2013 -0
  90. package/native/vendor/quickjs-ng/cxxtest.cc +2 -0
  91. package/native/vendor/quickjs-ng/dtoa.c +1619 -0
  92. package/native/vendor/quickjs-ng/dtoa.h +87 -0
  93. package/native/vendor/quickjs-ng/examples/fib.c +67 -0
  94. package/native/vendor/quickjs-ng/examples/fib_module.js +10 -0
  95. package/native/vendor/quickjs-ng/examples/hello.js +1 -0
  96. package/native/vendor/quickjs-ng/examples/hello_module.js +6 -0
  97. package/native/vendor/quickjs-ng/examples/meson.build +17 -0
  98. package/native/vendor/quickjs-ng/examples/pi_bigint.js +118 -0
  99. package/native/vendor/quickjs-ng/examples/point.c +154 -0
  100. package/native/vendor/quickjs-ng/examples/test_fib.js +8 -0
  101. package/native/vendor/quickjs-ng/examples/test_point.js +43 -0
  102. package/native/vendor/quickjs-ng/fuzz.c +51 -0
  103. package/native/vendor/quickjs-ng/gen/function_source.c +81 -0
  104. package/native/vendor/quickjs-ng/gen/hello.c +53 -0
  105. package/native/vendor/quickjs-ng/gen/hello_module.c +106 -0
  106. package/native/vendor/quickjs-ng/gen/repl.c +3053 -0
  107. package/native/vendor/quickjs-ng/gen/standalone.c +324 -0
  108. package/native/vendor/quickjs-ng/gen/test_fib.c +81 -0
  109. package/native/vendor/quickjs-ng/libregexp-opcode.h +58 -0
  110. package/native/vendor/quickjs-ng/libregexp.c +2687 -0
  111. package/native/vendor/quickjs-ng/libregexp.h +98 -0
  112. package/native/vendor/quickjs-ng/libunicode-table.h +4707 -0
  113. package/native/vendor/quickjs-ng/libunicode.c +1746 -0
  114. package/native/vendor/quickjs-ng/libunicode.h +126 -0
  115. package/native/vendor/quickjs-ng/list.h +107 -0
  116. package/native/vendor/quickjs-ng/lre-test.c +73 -0
  117. package/native/vendor/quickjs-ng/meson.build +684 -0
  118. package/native/vendor/quickjs-ng/meson_options.txt +6 -0
  119. package/native/vendor/quickjs-ng/qjs-wasi-reactor.c +208 -0
  120. package/native/vendor/quickjs-ng/qjs.c +748 -0
  121. package/native/vendor/quickjs-ng/qjsc.c +673 -0
  122. package/native/vendor/quickjs-ng/quickjs-atom.h +267 -0
  123. package/native/vendor/quickjs-ng/quickjs-c-atomics.h +54 -0
  124. package/native/vendor/quickjs-ng/quickjs-libc.c +4986 -0
  125. package/native/vendor/quickjs-ng/quickjs-libc.h +79 -0
  126. package/native/vendor/quickjs-ng/quickjs-opcode.h +369 -0
  127. package/native/vendor/quickjs-ng/quickjs.c +60259 -0
  128. package/native/vendor/quickjs-ng/quickjs.h +1419 -0
  129. package/native/vendor/quickjs-ng/repl.js +1927 -0
  130. package/native/vendor/quickjs-ng/run-test262.c +2417 -0
  131. package/native/vendor/quickjs-ng/standalone.js +129 -0
  132. package/native/vendor/quickjs-ng/tests/assert.js +49 -0
  133. package/native/vendor/quickjs-ng/tests/bug1221.js +16 -0
  134. package/native/vendor/quickjs-ng/tests/bug1296.js +12 -0
  135. package/native/vendor/quickjs-ng/tests/bug1297.js +22 -0
  136. package/native/vendor/quickjs-ng/tests/bug1301.js +21 -0
  137. package/native/vendor/quickjs-ng/tests/bug1302.js +24 -0
  138. package/native/vendor/quickjs-ng/tests/bug1305.js +26 -0
  139. package/native/vendor/quickjs-ng/tests/bug1318.js +54 -0
  140. package/native/vendor/quickjs-ng/tests/bug1352.js +8 -0
  141. package/native/vendor/quickjs-ng/tests/bug1354.js +6 -0
  142. package/native/vendor/quickjs-ng/tests/bug1355.js +58 -0
  143. package/native/vendor/quickjs-ng/tests/bug1368.js +9 -0
  144. package/native/vendor/quickjs-ng/tests/bug39/1.js +6 -0
  145. package/native/vendor/quickjs-ng/tests/bug39/2.js +6 -0
  146. package/native/vendor/quickjs-ng/tests/bug39/3.js +7 -0
  147. package/native/vendor/quickjs-ng/tests/bug488-upstream.js +7 -0
  148. package/native/vendor/quickjs-ng/tests/bug633/0.js +7 -0
  149. package/native/vendor/quickjs-ng/tests/bug633/1.js +4 -0
  150. package/native/vendor/quickjs-ng/tests/bug633/2.js +4 -0
  151. package/native/vendor/quickjs-ng/tests/bug633/3.js +4 -0
  152. package/native/vendor/quickjs-ng/tests/bug645/0.js +4 -0
  153. package/native/vendor/quickjs-ng/tests/bug645/1.js +9 -0
  154. package/native/vendor/quickjs-ng/tests/bug645/2.js +7 -0
  155. package/native/vendor/quickjs-ng/tests/bug648.js +13 -0
  156. package/native/vendor/quickjs-ng/tests/bug652.js +4 -0
  157. package/native/vendor/quickjs-ng/tests/bug741.js +19 -0
  158. package/native/vendor/quickjs-ng/tests/bug775.js +7 -0
  159. package/native/vendor/quickjs-ng/tests/bug776.js +7 -0
  160. package/native/vendor/quickjs-ng/tests/bug832.js +2 -0
  161. package/native/vendor/quickjs-ng/tests/bug858.js +26 -0
  162. package/native/vendor/quickjs-ng/tests/bug904.js +6 -0
  163. package/native/vendor/quickjs-ng/tests/bug988.js +7 -0
  164. package/native/vendor/quickjs-ng/tests/bug999.js +3 -0
  165. package/native/vendor/quickjs-ng/tests/destructured-export.js +8 -0
  166. package/native/vendor/quickjs-ng/tests/detect_module/0.js +1 -0
  167. package/native/vendor/quickjs-ng/tests/detect_module/1.js +2 -0
  168. package/native/vendor/quickjs-ng/tests/detect_module/2.js +1 -0
  169. package/native/vendor/quickjs-ng/tests/detect_module/3.js +8 -0
  170. package/native/vendor/quickjs-ng/tests/detect_module/4.js +3 -0
  171. package/native/vendor/quickjs-ng/tests/empty.js +0 -0
  172. package/native/vendor/quickjs-ng/tests/fixture_cyclic_import.js +2 -0
  173. package/native/vendor/quickjs-ng/tests/fixture_string_exports.js +12 -0
  174. package/native/vendor/quickjs-ng/tests/function_source.js +14 -0
  175. package/native/vendor/quickjs-ng/tests/microbench.js +1267 -0
  176. package/native/vendor/quickjs-ng/tests/null_or_undefined.js +38 -0
  177. package/native/vendor/quickjs-ng/tests/str-pad-leak.js +5 -0
  178. package/native/vendor/quickjs-ng/tests/test_bigint.js +107 -0
  179. package/native/vendor/quickjs-ng/tests/test_bjson.js +366 -0
  180. package/native/vendor/quickjs-ng/tests/test_builtin.js +1314 -0
  181. package/native/vendor/quickjs-ng/tests/test_closure.js +220 -0
  182. package/native/vendor/quickjs-ng/tests/test_cyclic_import.js +12 -0
  183. package/native/vendor/quickjs-ng/tests/test_domexception.js +35 -0
  184. package/native/vendor/quickjs-ng/tests/test_language.js +755 -0
  185. package/native/vendor/quickjs-ng/tests/test_loop.js +367 -0
  186. package/native/vendor/quickjs-ng/tests/test_queue_microtask.js +39 -0
  187. package/native/vendor/quickjs-ng/tests/test_std.js +340 -0
  188. package/native/vendor/quickjs-ng/tests/test_string_exports.js +25 -0
  189. package/native/vendor/quickjs-ng/tests/test_worker.js +43 -0
  190. package/native/vendor/quickjs-ng/tests/test_worker_module.js +30 -0
  191. package/native/vendor/quickjs-ng/tests.conf +14 -0
  192. package/native/vendor/quickjs-ng/unicode_download.sh +19 -0
  193. package/native/vendor/quickjs-ng/unicode_gen.c +3108 -0
  194. package/native/vendor/quickjs-ng/unicode_gen_def.h +310 -0
  195. package/native/vendor/quickjs-ng/update-version.sh +32 -0
  196. package/native/vendor/webview2/include/WebView2.h +60636 -0
  197. package/native/vendor/webview2/include/WebView2EnvironmentOptions.h +406 -0
  198. package/package.json +33 -0
  199. package/src/backend.ts +139 -0
  200. package/src/build-config.ts +87 -0
  201. package/src/build.ts +276 -0
  202. package/src/common.ts +195 -0
  203. package/src/config.ts +89 -0
  204. package/src/dev.ts +164 -0
  205. package/src/generate.ts +200 -0
  206. package/src/icons.ts +116 -0
  207. package/src/init.ts +190 -0
  208. package/src/package.ts +150 -0
  209. package/src/zapp-cli.ts +263 -0
package/src/dev.ts ADDED
@@ -0,0 +1,164 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+ import { existsSync } from "node:fs";
4
+ import { createInterface } from "node:readline";
5
+ import { mkdir } from "node:fs/promises";
6
+ import {
7
+ buildFileNeedsQjs,
8
+ ensureQjsLib,
9
+ killChild,
10
+ nativeIncludeArgs,
11
+ preferredJsTool,
12
+ resolveNativeDir,
13
+ runCmd,
14
+ runPackageScript,
15
+ sleep,
16
+ spawnPackageScript,
17
+ spawnStreaming,
18
+ } from "./common";
19
+ import { buildAssetManifest, generateAssetsZc } from "./build";
20
+ import { generateBuildConfigZc } from "./build-config";
21
+ import { resolveAndBundleBackend } from "./backend";
22
+ import { runGenerate } from "./generate";
23
+ import type { ResolvedZappConfig } from "./config";
24
+
25
+ const waitForUrl = async (url: string, timeoutMs: number) => {
26
+ const start = Date.now();
27
+ while (Date.now() - start < timeoutMs) {
28
+ try {
29
+ const res = await fetch(url);
30
+ if (res.ok) return;
31
+ } catch {
32
+ await sleep(100);
33
+ }
34
+ }
35
+ throw new Error(`Timed out waiting for ${url}`);
36
+ };
37
+
38
+ export const runDev = async ({
39
+ root,
40
+ frontendDir,
41
+ buildFile,
42
+ nativeOut,
43
+ devUrl,
44
+ withBrotli,
45
+ embedAssets,
46
+ backendScript,
47
+ logLevel,
48
+ config,
49
+ }: {
50
+ root: string;
51
+ frontendDir: string;
52
+ buildFile: string;
53
+ nativeOut: string;
54
+ devUrl: string;
55
+ withBrotli: boolean;
56
+ embedAssets: boolean;
57
+ backendScript?: string;
58
+ logLevel?: "error" | "warn" | "info" | "debug" | "trace";
59
+ config: ResolvedZappConfig;
60
+ }) => {
61
+ process.stdout.write(`[zapp] starting dev orchestration (${preferredJsTool()})\n`);
62
+ if (withBrotli && !embedAssets) {
63
+ process.stdout.write("[zapp] note: --brotli has no effect without --embed-assets\n");
64
+ }
65
+
66
+ await runGenerate({ root, frontendDir });
67
+
68
+ const vite = embedAssets ? null : spawnPackageScript("dev", { cwd: frontendDir });
69
+ let app: any = null;
70
+ let shuttingDown = false;
71
+
72
+ const shutdown = (reason: "user" | "app-exit" | "error" = "error") => {
73
+ if (shuttingDown) return;
74
+ shuttingDown = true;
75
+ if (reason === "user") {
76
+ process.stdout.write("\n[zapp] gracefully exiting...\n");
77
+ } else if (reason === "app-exit") {
78
+ process.stdout.write("[zapp] app exited, cleaning up...\n");
79
+ }
80
+ killChild(app);
81
+ killChild(vite);
82
+ setTimeout(() => process.exit(0), 1500);
83
+ };
84
+
85
+ process.on("SIGINT", () => shutdown("user"));
86
+ process.on("SIGTERM", () => shutdown("user"));
87
+ process.on("SIGHUP", () => shutdown("user"));
88
+
89
+ if (process.platform === "win32" && process.stdin.isTTY) {
90
+ const rl = createInterface({ input: process.stdin });
91
+ rl.on("SIGINT", () => shutdown("user"));
92
+ rl.on("close", () => shutdown("user"));
93
+ }
94
+
95
+ try {
96
+ const assetDir = path.join(frontendDir, "dist");
97
+ if (embedAssets) {
98
+ process.stdout.write("[zapp] dev embedded mode: building static frontend assets\n");
99
+ await runPackageScript("build", { cwd: frontendDir });
100
+ } else {
101
+ await waitForUrl(devUrl, 30000);
102
+ }
103
+
104
+ const backendScriptPath = await resolveAndBundleBackend({ root, frontendDir, backendScript });
105
+
106
+ const buildConfigFile = await generateBuildConfigZc({
107
+ root,
108
+ mode: embedAssets ? "dev-embedded" : "dev",
109
+ assetDir,
110
+ devUrl,
111
+ backendScriptPath,
112
+ logLevel,
113
+ });
114
+
115
+ await mkdir(path.dirname(nativeOut), { recursive: true });
116
+ const needsQjs = await buildFileNeedsQjs(buildFile);
117
+ const qjsLib = needsQjs ? await ensureQjsLib(root, "dev") : null;
118
+ const zcArgs = ["build", buildFile, buildConfigFile, "-DZAPP_BUILD_DEV", "--debug", ...nativeIncludeArgs()];
119
+ const manifest = embedAssets
120
+ ? await buildAssetManifest({ assetDir, withBrotli })
121
+ : { v: 1, generatedAt: new Date().toISOString(), assets: [] as any[], embedded: false };
122
+ const assetsFile = await generateAssetsZc(root, manifest, assetDir);
123
+ zcArgs.push(assetsFile);
124
+ // On Windows, embed the application manifest for comctl32 v6
125
+ if (process.platform === "win32") {
126
+ const manifestSrc = path.join(root, "config", "windows", "app.manifest");
127
+ if (existsSync(manifestSrc)) {
128
+ const buildDir = path.join(root, ".zapp", "build");
129
+ await mkdir(buildDir, { recursive: true });
130
+ const rcPath = path.join(buildDir, "app.rc");
131
+ await Bun.write(rcPath, `1 24 "${manifestSrc.replace(/\\/g, "/")}"\n`);
132
+ const resPath = path.join(buildDir, "app_manifest.o");
133
+ await runCmd("windres", [rcPath, "-O", "coff", "-o", resPath]);
134
+ zcArgs.push(resPath);
135
+ }
136
+ }
137
+
138
+ zcArgs.push("-o", nativeOut);
139
+ if (qjsLib) {
140
+ zcArgs.push("-L", path.dirname(qjsLib), "-lqjs");
141
+ }
142
+ await runCmd("zc", zcArgs, { cwd: root, env: { ZAPP_NATIVE: resolveNativeDir() } });
143
+ app = spawnStreaming(nativeOut, [], {
144
+ cwd: root,
145
+ });
146
+
147
+ app.exited.then((code: number | null) => {
148
+ if (!shuttingDown) {
149
+ shutdown("app-exit");
150
+ }
151
+ });
152
+ vite?.exited.then(() => {
153
+ if (!shuttingDown) {
154
+ shutdown("error");
155
+ }
156
+ });
157
+ } catch (error) {
158
+ shutdown("error");
159
+ throw error;
160
+ }
161
+
162
+ await new Promise(() => {});
163
+ return path.resolve(root);
164
+ };
@@ -0,0 +1,200 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+
5
+ type ServiceMethod = {
6
+ name: string;
7
+ args: { name: string; type: string }[];
8
+ returnType: string;
9
+ };
10
+
11
+ type ServiceDef = {
12
+ name: string;
13
+ methods: ServiceMethod[];
14
+ };
15
+
16
+ const ZEN_TO_TS_TYPES: Record<string, string> = {
17
+ string: "string",
18
+ int: "number",
19
+ float: "number",
20
+ double: "number",
21
+ bool: "boolean",
22
+ void: "void",
23
+ };
24
+
25
+ function mapType(zenType: string): string {
26
+ return ZEN_TO_TS_TYPES[zenType.trim()] ?? "unknown";
27
+ }
28
+
29
+ /**
30
+ * Scans .zc files for service_add registrations and extracts method signatures.
31
+ *
32
+ * Looks for patterns like:
33
+ * app.service.add("name", handler_fn);
34
+ *
35
+ * Then finds the corresponding function definition:
36
+ * fn handler_fn(app: App*, args: string) -> string { ... }
37
+ */
38
+ async function scanServices(srcDir: string): Promise<ServiceDef[]> {
39
+ const services: ServiceDef[] = [];
40
+ const files: string[] = [];
41
+
42
+ async function walk(dir: string) {
43
+ const entries = await fs.readdir(dir, { withFileTypes: true });
44
+ for (const entry of entries) {
45
+ if (entry.name.startsWith(".") || entry.name === "vendor") continue;
46
+ const full = path.join(dir, entry.name);
47
+ if (entry.isDirectory()) {
48
+ await walk(full);
49
+ } else if (entry.name.endsWith(".zc")) {
50
+ files.push(full);
51
+ }
52
+ }
53
+ }
54
+
55
+ await walk(srcDir);
56
+
57
+ const allContent: { file: string; content: string }[] = [];
58
+ for (const file of files) {
59
+ const content = await fs.readFile(file, "utf8");
60
+ allContent.push({ file, content });
61
+ }
62
+
63
+ // Find service.add("name", handler) registrations
64
+ const addPattern = /\.service\.add\(\s*"([^"]+)"\s*,\s*(\w+)\s*\)/g;
65
+ const registrations: { serviceName: string; handlerName: string }[] = [];
66
+
67
+ for (const { content } of allContent) {
68
+ let match;
69
+ while ((match = addPattern.exec(content)) !== null) {
70
+ registrations.push({ serviceName: match[1], handlerName: match[2] });
71
+ }
72
+ }
73
+
74
+ // For each registration, find the function signature
75
+ for (const { serviceName, handlerName } of registrations) {
76
+ const fnPattern = new RegExp(
77
+ `fn\\s+${handlerName}\\s*\\(([^)]*)\\)\\s*->\\s*(\\w+)`,
78
+ );
79
+
80
+ let found = false;
81
+ for (const { content } of allContent) {
82
+ const fnMatch = fnPattern.exec(content);
83
+ if (!fnMatch) continue;
84
+
85
+ const rawArgs = fnMatch[1];
86
+ const returnType = mapType(fnMatch[2]);
87
+
88
+ const args: { name: string; type: string }[] = [];
89
+ for (const part of rawArgs.split(",")) {
90
+ const trimmed = part.trim();
91
+ if (!trimmed) continue;
92
+ const [name, type] = trimmed.split(":").map((s) => s.trim());
93
+ if (name === "app" || type === "App*") continue;
94
+ args.push({ name, type: mapType(type) });
95
+ }
96
+
97
+ services.push({
98
+ name: serviceName,
99
+ methods: [{ name: serviceName, args, returnType }],
100
+ });
101
+ found = true;
102
+ break;
103
+ }
104
+
105
+ if (!found) {
106
+ process.stderr.write(
107
+ `[zapp] warning: could not find function '${handlerName}' for service '${serviceName}'\n`,
108
+ );
109
+ }
110
+ }
111
+
112
+ return services;
113
+ }
114
+
115
+ function generateBindingFile(service: ServiceDef): string {
116
+ const lines: string[] = [
117
+ `// Auto-generated by \`zapp generate\`. Do not edit.`,
118
+ `import { Services } from "@zapp/runtime";`,
119
+ ``,
120
+ ];
121
+
122
+ const className = service.name.charAt(0).toUpperCase() + service.name.slice(1);
123
+
124
+ lines.push(`export class ${className} {`);
125
+ for (const method of service.methods) {
126
+ const params = method.args
127
+ .map((a) => `${a.name}: ${a.type}`)
128
+ .join(", ");
129
+ const argsExpr = method.args.length === 0
130
+ ? "{}"
131
+ : method.args.length === 1
132
+ ? method.args[0].name
133
+ : `{ ${method.args.map((a) => a.name).join(", ")} }`;
134
+
135
+ lines.push(
136
+ ` static async ${method.name}(${params}): Promise<${method.returnType}> {`,
137
+ );
138
+ lines.push(
139
+ ` return Services.invoke<${method.returnType}>("${method.name}", ${argsExpr});`,
140
+ );
141
+ lines.push(` }`);
142
+ }
143
+ lines.push(`}`);
144
+ lines.push(``);
145
+
146
+ return lines.join("\n");
147
+ }
148
+
149
+ export async function runGenerate({
150
+ root,
151
+ outDir,
152
+ frontendDir,
153
+ }: {
154
+ root: string;
155
+ outDir?: string;
156
+ frontendDir?: string;
157
+ }) {
158
+ const base = frontendDir ?? root;
159
+ const srcDir = path.join(base, "src");
160
+ const hasSrcDir = await fs.stat(srcDir).then(() => true, () => false);
161
+ const targetDir = outDir
162
+ ? path.resolve(root, outDir)
163
+ : path.join(hasSrcDir ? srcDir : base, "generated");
164
+
165
+ process.stdout.write(`[zapp] scanning for service registrations...\n`);
166
+ const zappDir = path.join(root, "zapp");
167
+ const scanDir = await fs.stat(zappDir).then(() => zappDir, () => root);
168
+ const services = await scanServices(scanDir);
169
+
170
+ if (services.length === 0) {
171
+ process.stdout.write(
172
+ `[zapp] no service registrations found. Nothing to generate.\n`,
173
+ );
174
+ return;
175
+ }
176
+
177
+ await fs.mkdir(targetDir, { recursive: true });
178
+
179
+ const indexExports: string[] = [];
180
+ for (const service of services) {
181
+ const className =
182
+ service.name.charAt(0).toUpperCase() + service.name.slice(1);
183
+ const fileName = `${className}.ts`;
184
+ const filePath = path.join(targetDir, fileName);
185
+ const content = generateBindingFile(service);
186
+ await fs.writeFile(filePath, content, "utf8");
187
+ process.stdout.write(`[zapp] generated ${path.relative(root, filePath)}\n`);
188
+ indexExports.push(`export { ${className} } from "./${className}";`);
189
+ }
190
+
191
+ const indexPath = path.join(targetDir, "index.ts");
192
+ await fs.writeFile(
193
+ indexPath,
194
+ `// Auto-generated by \`zapp generate\`. Do not edit.\n${indexExports.join("\n")}\n`,
195
+ "utf8",
196
+ );
197
+ process.stdout.write(
198
+ `[zapp] generated ${services.length} binding(s) in ${path.relative(root, targetDir)}/\n`,
199
+ );
200
+ }
package/src/icons.ts ADDED
@@ -0,0 +1,116 @@
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { runCmd } from "./common";
5
+
6
+ /**
7
+ * macOS icon sizes for iconset.
8
+ * Each entry: [size, scale] → produces icon_{size}x{size}{@{scale}x}.png
9
+ */
10
+ const ICNS_SIZES: [number, number][] = [
11
+ [16, 1], [16, 2],
12
+ [32, 1], [32, 2],
13
+ [128, 1], [128, 2],
14
+ [256, 1], [256, 2],
15
+ [512, 1], [512, 2],
16
+ ];
17
+
18
+ /**
19
+ * Generate .icns from a PNG source using sips + iconutil (macOS only).
20
+ */
21
+ export async function generateIcns(pngPath: string, outputDir: string): Promise<string | null> {
22
+ if (process.platform !== "darwin") return null;
23
+ if (!existsSync(pngPath)) {
24
+ console.warn(`[zapp] icon source not found: ${pngPath}`);
25
+ return null;
26
+ }
27
+
28
+ const iconsetDir = path.join(outputDir, "AppIcon.iconset");
29
+ await mkdir(iconsetDir, { recursive: true });
30
+
31
+ // Generate all required sizes
32
+ for (const [size, scale] of ICNS_SIZES) {
33
+ const pixels = size * scale;
34
+ const suffix = scale > 1 ? `@${scale}x` : "";
35
+ const filename = `icon_${size}x${size}${suffix}.png`;
36
+ const outPath = path.join(iconsetDir, filename);
37
+ try {
38
+ await runCmd("sips", ["-z", String(pixels), String(pixels), pngPath, "--out", outPath]);
39
+ } catch (err) {
40
+ console.warn(`[zapp] failed to resize icon to ${pixels}x${pixels}: ${err}`);
41
+ return null;
42
+ }
43
+ }
44
+
45
+ // Convert iconset to icns
46
+ const icnsPath = path.join(outputDir, "AppIcon.icns");
47
+ try {
48
+ await runCmd("iconutil", ["-c", "icns", iconsetDir, "-o", icnsPath]);
49
+ } catch (err) {
50
+ console.warn(`[zapp] iconutil failed: ${err}`);
51
+ return null;
52
+ }
53
+
54
+ // Clean up iconset folder
55
+ await rm(iconsetDir, { recursive: true, force: true });
56
+
57
+ return icnsPath;
58
+ }
59
+
60
+ /**
61
+ * Compile a .icon folder (macOS Tahoe liquid glass) to Assets.car using actool.
62
+ * Requires Xcode 26 command line tools.
63
+ */
64
+ export async function compileIconAsset(iconFolder: string, outputDir: string): Promise<string | null> {
65
+ if (process.platform !== "darwin") return null;
66
+ if (!existsSync(iconFolder)) {
67
+ console.warn(`[zapp] .icon folder not found: ${iconFolder}`);
68
+ return null;
69
+ }
70
+
71
+ try {
72
+ await runCmd("actool", [
73
+ "--compile", outputDir,
74
+ "--platform", "macosx",
75
+ "--minimum-deployment-target", "26.0",
76
+ iconFolder,
77
+ ]);
78
+ const carPath = path.join(outputDir, "Assets.car");
79
+ if (existsSync(carPath)) {
80
+ return carPath;
81
+ }
82
+ console.warn("[zapp] actool did not produce Assets.car");
83
+ return null;
84
+ } catch (err) {
85
+ console.warn(`[zapp] actool failed (requires Xcode 26): ${err}`);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Generate .ico from a PNG source for Windows.
92
+ * Tries ImageMagick first, falls back to a basic single-size copy.
93
+ */
94
+ export async function generateIco(pngPath: string, outputPath: string): Promise<string | null> {
95
+ if (!existsSync(pngPath)) {
96
+ console.warn(`[zapp] icon source not found: ${pngPath}`);
97
+ return null;
98
+ }
99
+
100
+ // Try ImageMagick (magick or convert)
101
+ for (const cmd of ["magick", "convert"]) {
102
+ try {
103
+ await runCmd(cmd, [
104
+ pngPath,
105
+ "-define", "icon:auto-resize=256,128,64,48,32,16",
106
+ outputPath,
107
+ ]);
108
+ if (existsSync(outputPath)) return outputPath;
109
+ } catch {
110
+ // Try next command
111
+ }
112
+ }
113
+
114
+ console.warn("[zapp] ImageMagick not found — cannot generate .ico. Install ImageMagick for Windows icon support.");
115
+ return null;
116
+ }
package/src/init.ts ADDED
@@ -0,0 +1,190 @@
1
+ import path from "node:path";
2
+ import { spawnStreaming } from "./common";
3
+ import { mkdir } from "node:fs/promises";
4
+
5
+ export const runInit = async ({
6
+ root,
7
+ name,
8
+ template,
9
+ }: {
10
+ root: string;
11
+ name: string;
12
+ template: string;
13
+ }) => {
14
+ const projectDir = path.resolve(root, name);
15
+ const zappDir = path.join(projectDir, "zapp");
16
+ const configDir = path.join(projectDir, "config");
17
+ const darwinConfigDir = path.join(configDir, "darwin");
18
+ const windowsConfigDir = path.join(configDir, "windows");
19
+
20
+ console.log(`Scaffolding Zapp project in ${projectDir}...`);
21
+
22
+ await mkdir(projectDir, { recursive: true });
23
+
24
+ console.log(`Creating Vite project with template: ${template}...`);
25
+ await spawnStreaming("bun", ["create", "vite", ".", "--template", template], { cwd: projectDir }).exited;
26
+
27
+ await mkdir(zappDir, { recursive: true });
28
+ await mkdir(darwinConfigDir, { recursive: true });
29
+ await mkdir(windowsConfigDir, { recursive: true });
30
+
31
+ // Add Zapp dependencies to package.json
32
+ const pkgPath = path.join(projectDir, "package.json");
33
+ let pkgObj: Record<string, unknown> = {};
34
+ try {
35
+ const pkgFile = Bun.file(pkgPath);
36
+ if (await pkgFile.exists()) {
37
+ pkgObj = JSON.parse(await pkgFile.text());
38
+ }
39
+ } catch {
40
+ console.error(`Warning: Could not read ${pkgPath}`);
41
+ }
42
+
43
+ const devDeps = (pkgObj.devDependencies ?? {}) as Record<string, string>;
44
+ devDeps["@zapp/cli"] = "latest";
45
+ devDeps["@zapp/vite"] = "latest";
46
+ pkgObj.devDependencies = devDeps;
47
+
48
+ const deps = (pkgObj.dependencies ?? {}) as Record<string, string>;
49
+ deps["@zapp/runtime"] = "latest";
50
+ pkgObj.dependencies = deps;
51
+
52
+ await Bun.write(pkgPath, JSON.stringify(pkgObj, null, 2));
53
+
54
+ // --- Zen-C entry point ---
55
+ await Bun.write(path.join(zappDir, "app.zc"), `import "app/app.zc";
56
+
57
+ fn on_ready(id: int, handle: void*) -> void {
58
+ Window{id: id, handle: handle}.show();
59
+ }
60
+
61
+ fn run_app() -> int {
62
+ let config = AppConfig{
63
+ name: "${name}",
64
+ applicationShouldTerminateAfterLastWindowClosed: true,
65
+ webContentInspectable: -1, // -1 = inherit from build (dev=on, prod=off)
66
+ maxWorkers: 0,
67
+ qjsStackSize: 0,
68
+ };
69
+ let app = App::new(config);
70
+
71
+ let opts = window_options_default("${name}");
72
+ opts.visible = false;
73
+ let win = app.window.create(&opts);
74
+ win.on_ready(on_ready);
75
+
76
+ return app.run();
77
+ }
78
+ `);
79
+
80
+ // --- Zapp config ---
81
+ await Bun.write(path.join(zappDir, "zapp.config.ts"), `import { defineConfig } from "@zapp/cli/config";
82
+
83
+ export default defineConfig({
84
+ name: "${name}",
85
+ identifier: "com.zapp.${name.toLowerCase().replace(/[^a-z0-9]/g, "")}",
86
+ version: "0.1.0",
87
+ });
88
+ `);
89
+
90
+ // --- Build entry ---
91
+ await Bun.write(path.join(zappDir, "build.zc"), `// --- Platform Tags ---
92
+ //> macos: define: apple
93
+ //> windows: define: windows
94
+
95
+ // --- macOS Directives ---
96
+ //> macos: framework: Cocoa
97
+ //> macos: framework: WebKit
98
+ //> macos: framework: CoreFoundation
99
+ //> macos: framework: JavaScriptCore
100
+ //> macos: framework: Security
101
+ //> macos: link: -lcompression
102
+ //> macos: cflags: -fobjc-arc -x objective-c
103
+ // To use QuickJS instead of JSC on macOS, uncomment:
104
+ // //> macos: define: ZAPP_WORKER_ENGINE_QJS
105
+
106
+ // --- Windows Directives ---
107
+ //> windows: cflags: -DUNICODE -D_UNICODE -DCINTERFACE -DCOBJMACROS
108
+ //> windows: link: -lole32 -lshell32 -luuid -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshlwapi
109
+ //> windows: link: -lwinhttp -lbcrypt -ladvapi32 -lrpcrt4 -lcrypt32 -lversion
110
+ // Uncomment to enable Zapp Workers on Windows (adds ~760 KB):
111
+ // //> windows: define: ZAPP_WORKER_ENGINE_QJS
112
+
113
+ import "app.zc";
114
+
115
+ fn main() -> int {
116
+ return run_app();
117
+ }
118
+ `);
119
+
120
+ // --- macOS Info.plist ---
121
+ await Bun.write(path.join(darwinConfigDir, "Info.plist"), `<?xml version="1.0" encoding="UTF-8"?>
122
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
123
+ <plist version="1.0">
124
+ <dict>
125
+ <key>CFBundleName</key>
126
+ <string>${name}</string>
127
+ <key>CFBundleIdentifier</key>
128
+ <string>com.zapp.${name.toLowerCase().replace(/[^a-z0-9]/g, "")}</string>
129
+ <key>CFBundleVersion</key>
130
+ <string>1.0.0</string>
131
+ <key>CFBundleShortVersionString</key>
132
+ <string>1.0.0</string>
133
+ <key>CFBundlePackageType</key>
134
+ <string>APPL</string>
135
+ <key>LSMinimumSystemVersion</key>
136
+ <string>13.0</string>
137
+ <key>NSHighResolutionCapable</key>
138
+ <true/>
139
+ </dict>
140
+ </plist>
141
+ `);
142
+
143
+ // --- Windows app.manifest ---
144
+ await Bun.write(path.join(windowsConfigDir, "app.manifest"), `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
145
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
146
+ <assemblyIdentity
147
+ type="win32"
148
+ name="com.zapp.${name.toLowerCase().replace(/[^a-z0-9]/g, "")}"
149
+ version="1.0.0.0"
150
+ />
151
+ <description>${name}</description>
152
+
153
+ <!-- Enable visual styles (modern controls) -->
154
+ <dependency>
155
+ <dependentAssembly>
156
+ <assemblyIdentity
157
+ type="win32"
158
+ name="Microsoft.Windows.Common-Controls"
159
+ version="6.0.0.0"
160
+ processorArchitecture="*"
161
+ publicKeyToken="6595b64144ccf1df"
162
+ language="*"
163
+ />
164
+ </dependentAssembly>
165
+ </dependency>
166
+
167
+ <!-- High-DPI awareness (Per-Monitor V2) -->
168
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
169
+ <windowsSettings>
170
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
171
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
172
+ </windowsSettings>
173
+ </application>
174
+
175
+ <!-- Declare supported OS versions -->
176
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
177
+ <application>
178
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> <!-- Windows 10/11 -->
179
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> <!-- Windows 8.1 -->
180
+ </application>
181
+ </compatibility>
182
+ </assembly>
183
+ `);
184
+
185
+ console.log(`\nProject ${name} scaffolded successfully!`);
186
+ console.log(`Next steps:`);
187
+ console.log(` cd ${name}`);
188
+ console.log(` bun install`);
189
+ console.log(` zapp dev`);
190
+ };