@effindomv2/fui-as 0.1.37 → 0.1.41

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 (119) 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 +3 -0
  6. package/package.json +23 -13
  7. package/scripts/build.sh +70 -46
  8. package/scripts/check-runtime-dependency.sh +0 -19
  9. package/scripts/framework-host-services.ts +1 -1
  10. package/scripts/generate-host-events.ts +15 -241
  11. package/scripts/generate-host-services.ts +13 -261
  12. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  13. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  14. package/scripts/hostgen/common.ts +73 -0
  15. package/scripts/hostgen/registry.ts +159 -0
  16. package/src/Fui.ts +65 -5
  17. package/src/FuiExports.ts +12 -1
  18. package/src/bindings/ui.ts +157 -8
  19. package/src/controls/Button.ts +98 -71
  20. package/src/controls/Checkbox.ts +23 -21
  21. package/src/controls/ComboBox.ts +1201 -0
  22. package/src/controls/ContextMenu.ts +279 -165
  23. package/src/controls/Dialog.ts +91 -17
  24. package/src/controls/Dropdown.ts +101 -333
  25. package/src/controls/NavLink.ts +29 -6
  26. package/src/controls/Popup.ts +10 -5
  27. package/src/controls/ProgressBar.ts +1 -2
  28. package/src/controls/RadioButton.ts +34 -13
  29. package/src/controls/RadioGroup.ts +32 -19
  30. package/src/controls/Slider.ts +21 -23
  31. package/src/controls/Switch.ts +34 -17
  32. package/src/controls/index.ts +2 -1
  33. package/src/controls/internal/ButtonPresenter.ts +5 -2
  34. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  35. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  36. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  37. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  38. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  39. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  40. package/src/controls/internal/SelectablePopupList.ts +463 -0
  41. package/src/controls/internal/SliderPresenter.ts +5 -5
  42. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  43. package/src/controls/internal/TextInputCore.ts +307 -41
  44. package/src/controls/internal/TextInputPresenter.ts +2 -2
  45. package/src/controls/templating.ts +10 -0
  46. package/src/core/Application.ts +17 -8
  47. package/src/core/Bitmap.ts +192 -3
  48. package/src/core/ContextMenuManager.ts +128 -44
  49. package/src/core/DebugApi.ts +17 -2
  50. package/src/core/DragGesture.ts +8 -7
  51. package/src/core/EventRouter.ts +332 -12
  52. package/src/core/Fetch.ts +15 -7
  53. package/src/core/FetchFfi.ts +1 -15
  54. package/src/core/File.ts +75 -34
  55. package/src/core/FocusAdornerManager.ts +3 -3
  56. package/src/core/FocusVisibility.ts +7 -0
  57. package/src/core/FrameScheduler.ts +104 -5
  58. package/src/core/ImageSampling.ts +43 -0
  59. package/src/core/Logger.ts +2 -0
  60. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  61. package/src/core/Node.ts +1039 -97
  62. package/src/core/Platform.ts +16 -16
  63. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  64. package/src/core/Theme.ts +34 -20
  65. package/src/core/ToolTipManager.ts +3 -3
  66. package/src/core/Typography.ts +411 -19
  67. package/src/core/Worker.ts +69 -33
  68. package/src/core/event_exports.ts +123 -5
  69. package/src/core/ffi.ts +5 -865
  70. package/src/core/generated/FetchHostAbi.ts +10 -0
  71. package/src/core/generated/FrameworkHostServices.ts +1 -1
  72. package/src/core/generated/HostAbi.ts +220 -0
  73. package/src/core/generated/UiAbi.ts +356 -0
  74. package/src/core/generated/UiEnums.ts +186 -0
  75. package/src/drawing/DrawCallback.ts +17 -1
  76. package/src/drawing/DrawContext.ts +140 -32
  77. package/src/nodes/CustomDrawable.ts +8 -1
  78. package/src/nodes/FlexBox.ts +40 -11
  79. package/src/nodes/Grid.ts +5 -5
  80. package/src/nodes/Image.ts +20 -1
  81. package/src/nodes/RichText.ts +36 -25
  82. package/src/nodes/ScrollBar.ts +2 -1
  83. package/src/nodes/ScrollBox.ts +7 -2
  84. package/src/nodes/ScrollView.ts +17 -6
  85. package/src/nodes/Svg.ts +18 -1
  86. package/src/nodes/Text.ts +1 -1
  87. package/src/nodes/TextCore.ts +115 -20
  88. package/src/nodes/index.ts +1 -1
  89. package/src/text/DynamicTextLayout.ts +275 -0
  90. package/src/text/TextLayout.ts +308 -0
  91. package/src/worker/Worker.ts +3 -36
  92. package/src/worker/WorkerJob.ts +5 -9
  93. package/src/worker/ffi.ts +0 -6
  94. package/browser/src/common-harness/host-imports.ts +0 -447
  95. package/browser/src/common-harness/interop.ts +0 -39
  96. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  97. package/browser/src/common-harness/managed-harness-canvas-host.ts +0 -72
  98. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  99. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  100. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  101. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  102. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  103. package/browser/src/common-harness/managed-harness.ts +0 -1472
  104. package/browser/src/common-harness/managed-history.ts +0 -168
  105. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  106. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  107. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  108. package/browser/src/common-harness/types.ts +0 -206
  109. package/browser/src/common-harness/ui-chrome.ts +0 -206
  110. package/browser/src/common-harness/ui-imports.ts +0 -559
  111. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  112. package/browser/src/file-processing-worker.ts +0 -89
  113. package/browser/src/host-events.ts +0 -118
  114. package/browser/src/host-services.ts +0 -483
  115. package/browser/src/persisted-ui-state.ts +0 -206
  116. package/browser/src/routed-app-conventions.ts +0 -292
  117. package/browser/src/worker-bootstrap.ts +0 -916
  118. package/browser/src/worker-manager.ts +0 -234
  119. 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();