@effindomv2/fui-as 0.1.37 → 0.1.39

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 (118) hide show
  1. package/browser/routed-app-conventions.ts +1 -1
  2. package/browser/src/common-harness.ts +1 -27
  3. package/browser/src/index.ts +19 -26
  4. package/browser/src/routed-harness.ts +1 -194
  5. package/browser/src/shared-browser.ts +6 -0
  6. package/package.json +23 -13
  7. package/scripts/build.sh +47 -46
  8. package/scripts/framework-host-services.ts +1 -1
  9. package/scripts/generate-host-events.ts +15 -241
  10. package/scripts/generate-host-services.ts +13 -261
  11. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  12. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  13. package/scripts/hostgen/common.ts +73 -0
  14. package/scripts/hostgen/registry.ts +159 -0
  15. package/src/Fui.ts +65 -5
  16. package/src/FuiExports.ts +12 -1
  17. package/src/bindings/ui.ts +157 -8
  18. package/src/controls/Button.ts +98 -71
  19. package/src/controls/Checkbox.ts +23 -21
  20. package/src/controls/ComboBox.ts +1201 -0
  21. package/src/controls/ContextMenu.ts +279 -165
  22. package/src/controls/Dialog.ts +91 -17
  23. package/src/controls/Dropdown.ts +101 -333
  24. package/src/controls/NavLink.ts +29 -6
  25. package/src/controls/Popup.ts +10 -5
  26. package/src/controls/ProgressBar.ts +1 -2
  27. package/src/controls/RadioButton.ts +34 -13
  28. package/src/controls/RadioGroup.ts +32 -19
  29. package/src/controls/Slider.ts +21 -23
  30. package/src/controls/Switch.ts +34 -17
  31. package/src/controls/index.ts +2 -1
  32. package/src/controls/internal/ButtonPresenter.ts +5 -2
  33. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  34. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  35. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  36. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  37. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  38. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  39. package/src/controls/internal/SelectablePopupList.ts +463 -0
  40. package/src/controls/internal/SliderPresenter.ts +5 -5
  41. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  42. package/src/controls/internal/TextInputCore.ts +307 -41
  43. package/src/controls/internal/TextInputPresenter.ts +2 -2
  44. package/src/controls/templating.ts +10 -0
  45. package/src/core/Application.ts +17 -8
  46. package/src/core/Bitmap.ts +192 -3
  47. package/src/core/ContextMenuManager.ts +128 -44
  48. package/src/core/DebugApi.ts +17 -2
  49. package/src/core/DragGesture.ts +8 -7
  50. package/src/core/EventRouter.ts +332 -12
  51. package/src/core/Fetch.ts +15 -7
  52. package/src/core/FetchFfi.ts +1 -15
  53. package/src/core/File.ts +75 -34
  54. package/src/core/FocusAdornerManager.ts +3 -3
  55. package/src/core/FocusVisibility.ts +7 -0
  56. package/src/core/FrameScheduler.ts +104 -5
  57. package/src/core/ImageSampling.ts +43 -0
  58. package/src/core/Logger.ts +2 -0
  59. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  60. package/src/core/Node.ts +1039 -97
  61. package/src/core/Platform.ts +16 -16
  62. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  63. package/src/core/Theme.ts +34 -20
  64. package/src/core/ToolTipManager.ts +3 -3
  65. package/src/core/Typography.ts +411 -19
  66. package/src/core/Worker.ts +69 -33
  67. package/src/core/event_exports.ts +123 -5
  68. package/src/core/ffi.ts +5 -865
  69. package/src/core/generated/FetchHostAbi.ts +10 -0
  70. package/src/core/generated/FrameworkHostServices.ts +1 -1
  71. package/src/core/generated/HostAbi.ts +220 -0
  72. package/src/core/generated/UiAbi.ts +356 -0
  73. package/src/core/generated/UiEnums.ts +186 -0
  74. package/src/drawing/DrawCallback.ts +17 -1
  75. package/src/drawing/DrawContext.ts +140 -32
  76. package/src/nodes/CustomDrawable.ts +8 -1
  77. package/src/nodes/FlexBox.ts +40 -11
  78. package/src/nodes/Grid.ts +5 -5
  79. package/src/nodes/Image.ts +20 -1
  80. package/src/nodes/RichText.ts +36 -25
  81. package/src/nodes/ScrollBar.ts +2 -1
  82. package/src/nodes/ScrollBox.ts +7 -2
  83. package/src/nodes/ScrollView.ts +17 -6
  84. package/src/nodes/Svg.ts +18 -1
  85. package/src/nodes/Text.ts +1 -1
  86. package/src/nodes/TextCore.ts +115 -20
  87. package/src/nodes/index.ts +1 -1
  88. package/src/text/DynamicTextLayout.ts +275 -0
  89. package/src/text/TextLayout.ts +308 -0
  90. package/src/worker/Worker.ts +3 -36
  91. package/src/worker/WorkerJob.ts +5 -9
  92. package/src/worker/ffi.ts +0 -6
  93. package/browser/src/common-harness/host-imports.ts +0 -447
  94. package/browser/src/common-harness/interop.ts +0 -39
  95. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  96. package/browser/src/common-harness/managed-harness-canvas-host.ts +0 -72
  97. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  98. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  99. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  100. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  101. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  102. package/browser/src/common-harness/managed-harness.ts +0 -1472
  103. package/browser/src/common-harness/managed-history.ts +0 -168
  104. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  105. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  106. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  107. package/browser/src/common-harness/types.ts +0 -206
  108. package/browser/src/common-harness/ui-chrome.ts +0 -206
  109. package/browser/src/common-harness/ui-imports.ts +0 -559
  110. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  111. package/browser/src/file-processing-worker.ts +0 -89
  112. package/browser/src/host-events.ts +0 -118
  113. package/browser/src/host-services.ts +0 -483
  114. package/browser/src/persisted-ui-state.ts +0 -206
  115. package/browser/src/routed-app-conventions.ts +0 -292
  116. package/browser/src/worker-bootstrap.ts +0 -916
  117. package/browser/src/worker-manager.ts +0 -234
  118. package/browser/src/worker-types.ts +0 -68
@@ -1,249 +1,23 @@
1
- import { build } from "esbuild";
2
- import { promises as fs } from "node:fs";
3
- import os from "node:os";
4
1
  import path from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
6
- import { listHostEventMethods, type NormalizedHostEventMethod } from "../browser/src/host-events";
7
- import type { HostServiceTypeName } from "../browser/src/host-services";
8
-
9
- const SCRIPT_PATH = fileURLToPath(import.meta.url);
10
- const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
11
-
12
- function toPosix(value: string): string {
13
- return value.split(path.sep).join("/");
14
- }
15
-
16
- function relativeImport(fromFile: string, targetFile: string): string {
17
- let relative = path.relative(path.dirname(fromFile), targetFile);
18
- relative = relative.replace(/\.[^.]+$/, "");
19
- if (!relative.startsWith(".")) {
20
- relative = `./${relative}`;
21
- }
22
- return toPosix(relative);
23
- }
24
-
25
- function sourcePathForHeader(sourceModulePath: string): string {
26
- const relative = path.relative(process.cwd(), sourceModulePath);
27
- return toPosix(relative.startsWith(".") ? relative : `./${relative}`);
28
- }
29
-
30
- function asTypeName(type: HostServiceTypeName): string {
31
- switch (type) {
32
- case "string":
33
- return "string";
34
- case "bool":
35
- return "bool";
36
- case "i32":
37
- return "i32";
38
- case "u32":
39
- return "u32";
40
- case "i64":
41
- return "i64";
42
- case "u64":
43
- return "u64";
44
- case "f64":
45
- return "f64";
46
- case "bytes":
47
- return "Uint8Array";
48
- case "i32_array":
49
- return "Int32Array";
50
- case "u32_array":
51
- return "Uint32Array";
52
- case "i64_array":
53
- return "Int64Array";
54
- case "u64_array":
55
- return "Uint64Array";
56
- case "f64_array":
57
- return "Float64Array";
58
- case "void":
59
- return "void";
60
- }
61
- }
62
-
63
- function callbackTypeFor(method: NormalizedHostEventMethod): string {
64
- if (method.args.length == 0) {
65
- return "Callback0";
66
- }
67
- if (method.args.length == 1) {
68
- return `Callback1<${asTypeName(method.args[0])}>`;
69
- }
70
- if (method.args.length == 2) {
71
- return `Callback2<${asTypeName(method.args[0])}, ${asTypeName(method.args[1])}>`;
72
- }
73
- throw new Error(`Host event ${method.serviceName}.${method.methodName} uses ${String(method.args.length)} args; only 0-2 are supported right now.`);
74
- }
75
-
76
- function emitExportArgs(method: NormalizedHostEventMethod): string {
77
- const parts: Array<string> = [];
78
- method.args.forEach((type, index) => {
79
- if (
80
- type === "string" ||
81
- type === "bytes" ||
82
- type === "i32_array" ||
83
- type === "u32_array" ||
84
- type === "i64_array" ||
85
- type === "u64_array" ||
86
- type === "f64_array"
87
- ) {
88
- parts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
89
- return;
90
- }
91
- parts.push(`arg${String(index)}: ${asTypeName(type)}`);
92
- });
93
- return parts.join(", ");
94
- }
95
-
96
- function emitDecodedArgs(method: NormalizedHostEventMethod): Array<string> {
97
- const lines: Array<string> = [];
98
- method.args.forEach((type, index) => {
99
- if (type !== "string") {
100
- if (type === "bytes") {
101
- lines.push(` const arg${String(index)} = new Uint8Array(<i32>arg${String(index)}Len);`);
102
- lines.push(` if (arg${String(index)}Len > 0) {`);
103
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len);`);
104
- lines.push(" }");
105
- return;
106
- }
107
- if (type === "i32_array") {
108
- lines.push(` const arg${String(index)} = new Int32Array(<i32>arg${String(index)}Len);`);
109
- lines.push(` if (arg${String(index)}Len > 0) {`);
110
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 2);`);
111
- lines.push(" }");
112
- return;
113
- }
114
- if (type === "f64_array") {
115
- lines.push(` const arg${String(index)} = new Float64Array(<i32>arg${String(index)}Len);`);
116
- lines.push(` if (arg${String(index)}Len > 0) {`);
117
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
118
- lines.push(" }");
119
- return;
120
- }
121
- if (type === "u32_array") {
122
- lines.push(` const arg${String(index)} = new Uint32Array(<i32>arg${String(index)}Len);`);
123
- lines.push(` if (arg${String(index)}Len > 0) {`);
124
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 2);`);
125
- lines.push(" }");
126
- return;
127
- }
128
- if (type === "i64_array") {
129
- lines.push(` const arg${String(index)} = new Int64Array(<i32>arg${String(index)}Len);`);
130
- lines.push(` if (arg${String(index)}Len > 0) {`);
131
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
132
- lines.push(" }");
133
- return;
134
- }
135
- if (type === "u64_array") {
136
- lines.push(` const arg${String(index)} = new Uint64Array(<i32>arg${String(index)}Len);`);
137
- lines.push(` if (arg${String(index)}Len > 0) {`);
138
- lines.push(` memory.copy(arg${String(index)}.dataStart, arg${String(index)}Ptr, <usize>arg${String(index)}Len << 3);`);
139
- lines.push(" }");
140
- }
141
- return;
142
- }
143
- lines.push(
144
- ` const arg${String(index)} = arg${String(index)}Len == 0 ? "" : String.UTF8.decodeUnsafe(arg${String(index)}Ptr, <usize>arg${String(index)}Len, false);`,
145
- );
146
- });
147
- return lines;
148
- }
149
-
150
- function emitCallbackInvokeArgs(method: NormalizedHostEventMethod): string {
151
- return method.args
152
- .map((_type, index) => method.args[index] === "string" ? `arg${String(index)}` : `arg${String(index)}`)
153
- .join(", ");
154
- }
155
-
156
- function emitHandlerBlock(method: NormalizedHostEventMethod): string {
157
- const eventName = method.eventName;
158
- const publicName = eventName.length == 0 ? eventName : `${eventName[0].toUpperCase()}${eventName.slice(1)}`;
159
- const callbackType = callbackTypeFor(method);
160
- const exportArgs = emitExportArgs(method);
161
- const decodedArgs = emitDecodedArgs(method);
162
- const invokeArgs = emitCallbackInvokeArgs(method);
163
- const directSignature = `${callbackType} | null`;
164
- return [
165
- `let __${eventName}Handler: ${directSignature} = null;`,
166
- "",
167
- `export function on${publicName}(callback: ${directSignature}): void {`,
168
- ` __${eventName}Handler = callback;`,
169
- `}`,
170
- "",
171
- `export function clear${publicName}(): void {`,
172
- ` __${eventName}Handler = null;`,
173
- `}`,
174
- "",
175
- `export function ${method.exportName}(${exportArgs}): void {`,
176
- ` const callback = __${eventName}Handler;`,
177
- ` if (callback === null) {`,
178
- ` return;`,
179
- ` }`,
180
- ...decodedArgs,
181
- method.args.length == 0
182
- ? ` callback.invoke();`
183
- : ` callback.invoke(${invokeArgs});`,
184
- `}`,
185
- ].join("\n");
186
- }
187
-
188
- async function loadHostEvents(modulePath: string, exportName: string): Promise<unknown> {
189
- const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-events-"));
190
- const bundledFile = path.join(tempDir, "host-events.mjs");
191
- try {
192
- await build({
193
- entryPoints: [modulePath],
194
- outfile: bundledFile,
195
- bundle: true,
196
- format: "esm",
197
- platform: "node",
198
- target: "node20",
199
- logLevel: "silent",
200
- });
201
- const loaded = await import(pathToFileURL(bundledFile).href);
202
- if (!(exportName in loaded)) {
203
- throw new Error(`Host-events module does not export "${exportName}".`);
204
- }
205
- return loaded[exportName as keyof typeof loaded];
206
- } finally {
207
- await fs.rm(tempDir, { recursive: true, force: true });
208
- }
209
- }
210
-
211
- function emitBindingsFile(
212
- sourceModulePath: string,
213
- exportName: string,
214
- outputPath: string,
215
- methods: ReturnType<typeof listHostEventMethods>,
216
- primitivesImportOverride: string | undefined,
217
- ): string {
218
- const callbackImport = primitivesImportOverride ?? relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
219
- const blocks: Array<string> = [
220
- "// @ts-nocheck",
221
- `// Generated by scripts/generate-host-events.ts from ${sourcePathForHeader(sourceModulePath)}#${exportName}.`,
222
- `import { Callback0, Callback1, Callback2 } from "${callbackImport}";`,
223
- "",
224
- ];
225
- methods.forEach((method, index) => {
226
- blocks.push(emitHandlerBlock(method));
227
- if (index + 1 < methods.length) {
228
- blocks.push("");
229
- blocks.push("");
230
- }
231
- });
232
- return `${blocks.join("\n")}\n`;
233
- }
2
+ import { generateAssemblyScriptHostEventsFile } from "./hostgen/assemblyscript-host-events";
234
3
 
235
4
  async function main(): Promise<void> {
236
- const [moduleArg, exportName, outputArg, primitivesImportArg] = process.argv.slice(2);
237
- if (moduleArg === undefined || exportName === undefined || outputArg === undefined) {
5
+ const args = process.argv.slice(2);
6
+ if (args.length < 3) {
238
7
  throw new Error("Usage: generate-host-events <module-path> <export-name> <output-path> [primitives-import]");
239
8
  }
240
- const modulePath = path.resolve(process.cwd(), moduleArg);
241
- const outputPath = path.resolve(process.cwd(), outputArg);
242
- const registry = await loadHostEvents(modulePath, exportName);
243
- const methods = listHostEventMethods(registry as never);
244
- const content = emitBindingsFile(modulePath, exportName, outputPath, methods, primitivesImportArg);
245
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
246
- await fs.writeFile(outputPath, content, "utf8");
9
+ const [moduleArg, exportName, outputArg, primitivesImportArg] = args as [
10
+ string,
11
+ string,
12
+ string,
13
+ string | undefined,
14
+ ];
15
+ await generateAssemblyScriptHostEventsFile(
16
+ path.resolve(process.cwd(), moduleArg),
17
+ exportName,
18
+ path.resolve(process.cwd(), outputArg),
19
+ primitivesImportArg,
20
+ );
247
21
  }
248
22
 
249
23
  await main();
@@ -1,275 +1,27 @@
1
- import { build } from "esbuild";
2
- import { promises as fs } from "node:fs";
3
- import os from "node:os";
4
1
  import path from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
6
- import { listHostServiceMethods, type HostServiceTypeName } from "../browser/src/host-services";
7
-
8
- const SCRIPT_PATH = fileURLToPath(import.meta.url);
9
- const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
10
-
11
- function toPosix(value: string): string {
12
- return value.split(path.sep).join("/");
13
- }
14
-
15
- function relativeImport(fromFile: string, targetFile: string): string {
16
- let relative = path.relative(path.dirname(fromFile), targetFile);
17
- relative = relative.replace(/\.[^.]+$/, "");
18
- if (!relative.startsWith(".")) {
19
- relative = `./${relative}`;
20
- }
21
- return toPosix(relative);
22
- }
23
-
24
- function sourcePathForHeader(sourceModulePath: string): string {
25
- const relative = path.relative(process.cwd(), sourceModulePath);
26
- return toPosix(relative.startsWith(".") ? relative : `./${relative}`);
27
- }
28
-
29
- function asTypeName(type: HostServiceTypeName): string {
30
- switch (type) {
31
- case "string":
32
- return "string";
33
- case "bool":
34
- return "bool";
35
- case "i32":
36
- return "i32";
37
- case "u32":
38
- return "u32";
39
- case "i64":
40
- return "i64";
41
- case "u64":
42
- return "u64";
43
- case "f64":
44
- return "f64";
45
- case "bytes":
46
- return "Uint8Array";
47
- case "i32_array":
48
- return "Int32Array";
49
- case "u32_array":
50
- return "Uint32Array";
51
- case "i64_array":
52
- return "Int64Array";
53
- case "u64_array":
54
- return "Uint64Array";
55
- case "f64_array":
56
- return "Float64Array";
57
- case "void":
58
- return "void";
59
- }
60
- }
61
-
62
- function isPointerLengthType(type: HostServiceTypeName): boolean {
63
- return type === "string" ||
64
- type === "bytes" ||
65
- type === "i32_array" ||
66
- type === "u32_array" ||
67
- type === "i64_array" ||
68
- type === "u64_array" ||
69
- type === "f64_array";
70
- }
71
-
72
- function returnsBufferType(type: HostServiceTypeName): boolean {
73
- return type === "string" ||
74
- type === "bytes" ||
75
- type === "i32_array" ||
76
- type === "u32_array" ||
77
- type === "i64_array" ||
78
- type === "u64_array" ||
79
- type === "f64_array";
80
- }
81
-
82
- function emitExternalSignature(
83
- importName: string,
84
- args: readonly HostServiceTypeName[],
85
- returns: HostServiceTypeName,
86
- moduleName: string,
87
- ): string {
88
- const signatureParts: Array<string> = [];
89
- args.forEach((type, index) => {
90
- if (isPointerLengthType(type)) {
91
- signatureParts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
92
- return;
93
- }
94
- signatureParts.push(`arg${String(index)}: ${asTypeName(type)}`);
95
- });
96
- if (returnsBufferType(returns)) {
97
- signatureParts.push("resultPtr: usize", "resultCap: u32");
98
- }
99
- const returnType = returnsBufferType(returns) ? "u32" : asTypeName(returns);
100
- return [
101
- `@external("${moduleName}", "${importName}")`,
102
- `declare function __host_${importName}(${signatureParts.join(", ")}): ${returnType};`,
103
- ].join("\n");
104
- }
105
-
106
- function emitWrapper(importName: string, args: readonly HostServiceTypeName[], returns: HostServiceTypeName): string {
107
- const wrapperArgs = args.map((type, index) => `arg${String(index)}: ${asTypeName(type)}`);
108
- const lines: Array<string> = [];
109
- args.forEach((type, index) => {
110
- if (type === "string") {
111
- lines.push(` const arg${String(index)}Bytes = Uint8Array.wrap(String.UTF8.encode(arg${String(index)}, false));`);
112
- return;
113
- }
114
- if (type === "bytes" || type === "i32_array" || type === "u32_array" || type === "i64_array" || type === "u64_array" || type === "f64_array") {
115
- lines.push(` const arg${String(index)}Bytes = arg${String(index)};`);
116
- }
117
- });
118
- const callArgs: Array<string> = [];
119
- args.forEach((type, index) => {
120
- if (type === "string") {
121
- callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
122
- callArgs.push(`<u32>arg${String(index)}Bytes.length`);
123
- return;
124
- }
125
- if (type === "bytes") {
126
- callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
127
- callArgs.push(`<u32>arg${String(index)}Bytes.length`);
128
- return;
129
- }
130
- if (type === "i32_array" || type === "u32_array" || type === "i64_array" || type === "u64_array" || type === "f64_array") {
131
- callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
132
- callArgs.push(`<u32>arg${String(index)}Bytes.length`);
133
- return;
134
- }
135
- callArgs.push(`arg${String(index)}`);
136
- });
137
- if (returnsBufferType(returns)) {
138
- lines.push(" const resultPtr = hostServiceResultBufferPtr();");
139
- lines.push(" const resultCap = hostServiceResultBufferSize();");
140
- callArgs.push("resultPtr", "resultCap");
141
- lines.push(` const resultLen = __host_${importName}(${callArgs.join(", ")});`);
142
- if (returns === "string") {
143
- lines.push(` return decodeHostServiceStringResult(resultPtr, resultLen, "${importName}");`);
144
- } else if (returns === "bytes") {
145
- lines.push(` return decodeHostServiceBytesResult(resultPtr, resultLen, "${importName}");`);
146
- } else if (returns === "i32_array") {
147
- lines.push(` return decodeHostServiceI32ArrayResult(resultPtr, resultLen, "${importName}");`);
148
- } else if (returns === "u32_array") {
149
- lines.push(` return decodeHostServiceU32ArrayResult(resultPtr, resultLen, "${importName}");`);
150
- } else if (returns === "i64_array") {
151
- lines.push(` return decodeHostServiceI64ArrayResult(resultPtr, resultLen, "${importName}");`);
152
- } else if (returns === "u64_array") {
153
- lines.push(` return decodeHostServiceU64ArrayResult(resultPtr, resultLen, "${importName}");`);
154
- } else {
155
- lines.push(` return decodeHostServiceF64ArrayResult(resultPtr, resultLen, "${importName}");`);
156
- }
157
- } else if (returns === "void") {
158
- lines.push(` __host_${importName}(${callArgs.join(", ")});`);
159
- } else {
160
- lines.push(` return __host_${importName}(${callArgs.join(", ")});`);
161
- }
162
- return [
163
- `export function ${importName}(${wrapperArgs.join(", ")}): ${returns === "string" ? "string" : asTypeName(returns)} {`,
164
- ...lines,
165
- "}",
166
- ].join("\n");
167
- }
168
-
169
- async function loadHostServices(modulePath: string, exportName: string): Promise<unknown> {
170
- const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-services-"));
171
- const bundledFile = path.join(tempDir, "host-services.mjs");
172
- try {
173
- await build({
174
- entryPoints: [modulePath],
175
- outfile: bundledFile,
176
- bundle: true,
177
- format: "esm",
178
- platform: "node",
179
- target: "node20",
180
- logLevel: "silent",
181
- });
182
- const loaded = await import(pathToFileURL(bundledFile).href);
183
- if (!(exportName in loaded)) {
184
- throw new Error(`Host-services module does not export "${exportName}".`);
185
- }
186
- return loaded[exportName as keyof typeof loaded];
187
- } finally {
188
- await fs.rm(tempDir, { recursive: true, force: true });
189
- }
190
- }
191
-
192
- function emitBindingsFile(
193
- sourceModulePath: string,
194
- exportName: string,
195
- outputPath: string,
196
- methods: ReturnType<typeof listHostServiceMethods>,
197
- primitivesImportOverride: string | undefined,
198
- moduleName: string,
199
- ): string {
200
- const runtimeImport = primitivesImportOverride ?? relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
201
- const blocks: Array<string> = [
202
- "// @ts-nocheck",
203
- `// Generated by scripts/generate-host-services.ts from ${sourcePathForHeader(sourceModulePath)}#${exportName}.`,
204
- ];
205
- if (methods.some((method) => returnsBufferType(method.returns))) {
206
- const helpers = [
207
- "hostServiceResultBufferPtr",
208
- "hostServiceResultBufferSize",
209
- ...new Set(
210
- methods
211
- .map((method) => method.returns)
212
- .filter((type) => returnsBufferType(type))
213
- .map((type) => {
214
- if (type === "string") {
215
- return "decodeHostServiceStringResult";
216
- }
217
- if (type === "bytes") {
218
- return "decodeHostServiceBytesResult";
219
- }
220
- if (type === "i32_array") {
221
- return "decodeHostServiceI32ArrayResult";
222
- }
223
- if (type === "u32_array") {
224
- return "decodeHostServiceU32ArrayResult";
225
- }
226
- if (type === "i64_array") {
227
- return "decodeHostServiceI64ArrayResult";
228
- }
229
- if (type === "u64_array") {
230
- return "decodeHostServiceU64ArrayResult";
231
- }
232
- return "decodeHostServiceF64ArrayResult";
233
- }),
234
- ),
235
- ];
236
- blocks.push(`import { ${helpers.join(", ")} } from "${runtimeImport}";`);
237
- blocks.push("");
238
- } else {
239
- blocks.push("");
240
- }
241
- methods.forEach((method, index) => {
242
- blocks.push(emitExternalSignature(method.importName, method.args, method.returns, moduleName));
243
- blocks.push("");
244
- blocks.push(emitWrapper(method.importName, method.args, method.returns));
245
- if (index + 1 < methods.length) {
246
- blocks.push("");
247
- }
248
- });
249
- return `${blocks.join("\n")}\n`;
250
- }
2
+ import { generateAssemblyScriptHostServicesFile } from "./hostgen/assemblyscript-host-services";
251
3
 
252
4
  async function main(): Promise<void> {
253
- const [moduleArg, exportName, outputArg, primitivesImportArg, hostModuleArg] = process.argv.slice(2);
254
- if (moduleArg === undefined || exportName === undefined || outputArg === undefined) {
5
+ const args = process.argv.slice(2);
6
+ if (args.length < 3) {
255
7
  throw new Error(
256
8
  "Usage: generate-host-services <module-path> <export-name> <output-path> [primitives-import] [host-import-module]",
257
9
  );
258
10
  }
259
- const modulePath = path.resolve(process.cwd(), moduleArg);
260
- const outputPath = path.resolve(process.cwd(), outputArg);
261
- const registry = await loadHostServices(modulePath, exportName);
262
- const methods = listHostServiceMethods(registry as never);
263
- const content = emitBindingsFile(
264
- modulePath,
11
+ const [moduleArg, exportName, outputArg, primitivesImportArg, hostModuleArg] = args as [
12
+ string,
13
+ string,
14
+ string,
15
+ string | undefined,
16
+ string | undefined,
17
+ ];
18
+ await generateAssemblyScriptHostServicesFile(
19
+ path.resolve(process.cwd(), moduleArg),
265
20
  exportName,
266
- outputPath,
267
- methods,
21
+ path.resolve(process.cwd(), outputArg),
268
22
  primitivesImportArg,
269
23
  hostModuleArg ?? "fui_host_service",
270
24
  );
271
- await fs.mkdir(path.dirname(outputPath), { recursive: true });
272
- await fs.writeFile(outputPath, content, "utf8");
273
25
  }
274
26
 
275
27
  await main();