@effindomv2/fui-as 0.1.1 → 0.1.3

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.
@@ -255,6 +255,19 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
255
255
  return textBridge.writeAppUtf8(ptr, capacity, text, context);
256
256
  }
257
257
 
258
+ function writeAppBytes(ptr: number, capacity: number, bytes: Uint8Array, context: string): number {
259
+ if (capacity < 0) {
260
+ throw new Error(`${context} has invalid buffer capacity ${String(capacity)}.`);
261
+ }
262
+ if (bytes.length > capacity) {
263
+ throw new Error(`${context} returned ${String(bytes.length)} bytes but the shared result buffer only holds ${String(capacity)}.`);
264
+ }
265
+ if (bytes.length > 0) {
266
+ new Uint8Array(getCurrentMemory().buffer, ptr, bytes.length).set(bytes);
267
+ }
268
+ return bytes.length;
269
+ }
270
+
258
271
  function withUiUtf8(
259
272
  text: string,
260
273
  callback: (ptr: WasmHandleLike | number, len: number) => void,
@@ -480,6 +493,71 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
480
493
  method: NormalizedHostEventMethod,
481
494
  args: readonly unknown[],
482
495
  ): Array<unknown> {
496
+ function alignOffset(value: number, alignment: number): number {
497
+ return alignment <= 1 ? value : (value + alignment - 1) & ~(alignment - 1);
498
+ }
499
+
500
+ function encodeTypedArrayArg(
501
+ type: "bytes" | "i32_array" | "u32_array" | "i64_array" | "u64_array" | "f64_array",
502
+ arg: unknown,
503
+ context: string,
504
+ ): { bytes: Uint8Array; elementCount: number; alignment: number } {
505
+ if (type === "bytes") {
506
+ if (!(arg instanceof Uint8Array)) {
507
+ throw new Error(`${context} must be a Uint8Array.`);
508
+ }
509
+ return { bytes: arg, elementCount: arg.length, alignment: 1 };
510
+ }
511
+ if (type === "i32_array") {
512
+ if (!(arg instanceof Int32Array)) {
513
+ throw new Error(`${context} must be an Int32Array.`);
514
+ }
515
+ return {
516
+ bytes: new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength),
517
+ elementCount: arg.length,
518
+ alignment: 4,
519
+ };
520
+ }
521
+ if (type === "u32_array") {
522
+ if (!(arg instanceof Uint32Array)) {
523
+ throw new Error(`${context} must be a Uint32Array.`);
524
+ }
525
+ return {
526
+ bytes: new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength),
527
+ elementCount: arg.length,
528
+ alignment: 4,
529
+ };
530
+ }
531
+ if (type === "i64_array") {
532
+ if (!(arg instanceof BigInt64Array)) {
533
+ throw new Error(`${context} must be a BigInt64Array.`);
534
+ }
535
+ return {
536
+ bytes: new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength),
537
+ elementCount: arg.length,
538
+ alignment: 8,
539
+ };
540
+ }
541
+ if (type === "u64_array") {
542
+ if (!(arg instanceof BigUint64Array)) {
543
+ throw new Error(`${context} must be a BigUint64Array.`);
544
+ }
545
+ return {
546
+ bytes: new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength),
547
+ elementCount: arg.length,
548
+ alignment: 8,
549
+ };
550
+ }
551
+ if (!(arg instanceof Float64Array)) {
552
+ throw new Error(`${context} must be a Float64Array.`);
553
+ }
554
+ return {
555
+ bytes: new Uint8Array(arg.buffer, arg.byteOffset, arg.byteLength),
556
+ elementCount: arg.length,
557
+ alignment: 8,
558
+ };
559
+ }
560
+
483
561
  if (args.length != method.args.length) {
484
562
  throw new Error(`Host event ${method.serviceName}.${method.methodName} expected ${String(method.args.length)} args but received ${String(args.length)}.`);
485
563
  }
@@ -507,6 +585,22 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
507
585
  }
508
586
  continue;
509
587
  }
588
+ if (type === "bytes" || type === "i32_array" || type === "u32_array" || type === "i64_array" || type === "u64_array" || type === "f64_array") {
589
+ const payload = encodeTypedArrayArg(type, arg, context);
590
+ if (payload.bytes.length > 0) {
591
+ const alignedOffset = alignOffset(byteOffset, payload.alignment);
592
+ if (session.textBufferPtr === 0 || alignedOffset + payload.bytes.length > session.textBufferSize) {
593
+ throw new Error(`${context} exceeds the shared AssemblyScript text buffer.`);
594
+ }
595
+ const memory = new Uint8Array(session.memory.buffer, session.textBufferPtr + alignedOffset, payload.bytes.length);
596
+ memory.set(payload.bytes);
597
+ callArgs.push(session.textBufferPtr + alignedOffset, payload.elementCount);
598
+ byteOffset = alignedOffset + payload.bytes.length;
599
+ } else {
600
+ callArgs.push(0, 0);
601
+ }
602
+ continue;
603
+ }
510
604
  if (type === 'bool') {
511
605
  if (typeof arg !== 'boolean') {
512
606
  throw new Error(`${context} must be a boolean.`);
@@ -514,6 +608,19 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
514
608
  callArgs.push(arg ? 1 : 0);
515
609
  continue;
516
610
  }
611
+ if (type === "i64" || type === "u64") {
612
+ if (typeof arg !== "bigint") {
613
+ throw new Error(`${context} must be a bigint.`);
614
+ }
615
+ if (type === "i64" && (arg < -9223372036854775808n || arg > 9223372036854775807n)) {
616
+ throw new Error(`${context} must be a signed 64-bit integer.`);
617
+ }
618
+ if (type === "u64" && (arg < 0n || arg > 18446744073709551615n)) {
619
+ throw new Error(`${context} must be an unsigned 64-bit integer.`);
620
+ }
621
+ callArgs.push(arg);
622
+ continue;
623
+ }
517
624
  if (typeof arg !== 'number' || Number.isNaN(arg)) {
518
625
  throw new Error(`${context} must be a number.`);
519
626
  }
@@ -521,6 +628,10 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
521
628
  if (!Number.isInteger(arg) || arg < -2147483648 || arg > 2147483647) {
522
629
  throw new Error(`${context} must be a signed 32-bit integer.`);
523
630
  }
631
+ } else if (type === "u32") {
632
+ if (!Number.isInteger(arg) || arg < 0 || arg > 4294967295) {
633
+ throw new Error(`${context} must be an unsigned 32-bit integer.`);
634
+ }
524
635
  }
525
636
  callArgs.push(arg);
526
637
  }
@@ -703,6 +814,8 @@ export function startManagedHarness(options: ManagedHarnessOptions): void {
703
814
  const hostServiceImports = createHostServiceImportModule(hostServices, {
704
815
  readString: readAppUtf8,
705
816
  writeString: writeAppUtf8,
817
+ readBytes: readAppBytes,
818
+ writeBytes: writeAppBytes,
706
819
  });
707
820
  return {
708
821
  effindom_v2_ui: createUiImportModule({
@@ -3,6 +3,13 @@ import type { HostServiceTypeName } from "./host-services";
3
3
  type HostEventTypeValue<T extends HostServiceTypeName> =
4
4
  T extends "string" ? string :
5
5
  T extends "bool" ? boolean :
6
+ T extends "bytes" ? Uint8Array :
7
+ T extends "i32_array" ? Int32Array :
8
+ T extends "u32_array" ? Uint32Array :
9
+ T extends "i64_array" ? BigInt64Array :
10
+ T extends "u64_array" ? BigUint64Array :
11
+ T extends "f64_array" ? Float64Array :
12
+ T extends "i64" | "u64" ? bigint :
6
13
  T extends "void" ? void :
7
14
  number;
8
15
 
@@ -59,7 +66,21 @@ function buildExportName(eventName: string): string {
59
66
  }
60
67
 
61
68
  function validateEventType(type: string, context: string): asserts type is HostServiceTypeName {
62
- if (type === "string" || type === "bool" || type === "i32" || type === "f64") {
69
+ if (
70
+ type === "string" ||
71
+ type === "bool" ||
72
+ type === "i32" ||
73
+ type === "u32" ||
74
+ type === "i64" ||
75
+ type === "u64" ||
76
+ type === "f64" ||
77
+ type === "bytes" ||
78
+ type === "i32_array" ||
79
+ type === "u32_array" ||
80
+ type === "i64_array" ||
81
+ type === "u64_array" ||
82
+ type === "f64_array"
83
+ ) {
63
84
  return;
64
85
  }
65
86
  throw new Error(`${context} uses unsupported host-event type "${type}".`);
@@ -1,8 +1,29 @@
1
- export type HostServiceTypeName = "string" | "bool" | "i32" | "f64" | "void";
1
+ export type HostServiceTypeName =
2
+ | "string"
3
+ | "bool"
4
+ | "i32"
5
+ | "u32"
6
+ | "i64"
7
+ | "u64"
8
+ | "f64"
9
+ | "bytes"
10
+ | "i32_array"
11
+ | "u32_array"
12
+ | "i64_array"
13
+ | "u64_array"
14
+ | "f64_array"
15
+ | "void";
2
16
 
3
17
  type HostServiceTypeValue<T extends HostServiceTypeName> =
4
18
  T extends "string" ? string :
5
19
  T extends "bool" ? boolean :
20
+ T extends "bytes" ? Uint8Array :
21
+ T extends "i32_array" ? Int32Array :
22
+ T extends "u32_array" ? Uint32Array :
23
+ T extends "i64_array" ? BigInt64Array :
24
+ T extends "u64_array" ? BigUint64Array :
25
+ T extends "f64_array" ? Float64Array :
26
+ T extends "i64" | "u64" ? bigint :
6
27
  T extends "void" ? void :
7
28
  number;
8
29
 
@@ -14,6 +35,7 @@ export interface HostServiceMethodDefinition<
14
35
  TArgs extends readonly HostServiceTypeName[] = readonly HostServiceTypeName[],
15
36
  TResult extends HostServiceTypeName = HostServiceTypeName,
16
37
  > {
38
+ readonly importName?: string;
17
39
  readonly args: TArgs;
18
40
  readonly returns: TResult;
19
41
  readonly implementation: (...args: HostServiceArgsValues<TArgs>) => HostServiceTypeValue<TResult>;
@@ -33,6 +55,8 @@ export interface NormalizedHostServiceMethod {
33
55
  export interface HostServiceImportIo {
34
56
  readString(ptr: number, len: number): string;
35
57
  writeString(ptr: number, capacity: number, text: string, context: string): number;
58
+ readBytes(ptr: number, len: number): Uint8Array;
59
+ writeBytes(ptr: number, capacity: number, bytes: Uint8Array, context: string): number;
36
60
  }
37
61
 
38
62
  const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -63,7 +87,22 @@ function buildImportName(serviceName: string, methodName: string): string {
63
87
  }
64
88
 
65
89
  function validateServiceType(type: string, context: string): asserts type is HostServiceTypeName {
66
- if (type === "string" || type === "bool" || type === "i32" || type === "f64" || type === "void") {
90
+ if (
91
+ type === "string" ||
92
+ type === "bool" ||
93
+ type === "i32" ||
94
+ type === "u32" ||
95
+ type === "i64" ||
96
+ type === "u64" ||
97
+ type === "f64" ||
98
+ type === "bytes" ||
99
+ type === "i32_array" ||
100
+ type === "u32_array" ||
101
+ type === "i64_array" ||
102
+ type === "u64_array" ||
103
+ type === "f64_array" ||
104
+ type === "void"
105
+ ) {
67
106
  return;
68
107
  }
69
108
  throw new Error(`${context} uses unsupported host-service type "${type}".`);
@@ -79,7 +118,8 @@ export function listHostServiceMethods(services: HostServicesDefinition | undefi
79
118
  assertIdentifier(serviceName, "Host service");
80
119
  for (const [methodName, definition] of Object.entries(serviceMethods)) {
81
120
  assertIdentifier(methodName, `Host service ${serviceName} method`);
82
- const importName = buildImportName(serviceName, methodName);
121
+ const importName = definition.importName ?? buildImportName(serviceName, methodName);
122
+ assertIdentifier(importName, `Host service ${serviceName}.${methodName} import`);
83
123
  if (seenImports.has(importName)) {
84
124
  throw new Error(`Duplicate host-service import name "${importName}".`);
85
125
  }
@@ -126,6 +166,48 @@ function expectString(value: unknown, context: string): string {
126
166
  return value;
127
167
  }
128
168
 
169
+ function expectBytes(value: unknown, context: string): Uint8Array {
170
+ if (!(value instanceof Uint8Array)) {
171
+ throw new Error(`${context} must be a Uint8Array.`);
172
+ }
173
+ return value;
174
+ }
175
+
176
+ function expectInt32Array(value: unknown, context: string): Int32Array {
177
+ if (!(value instanceof Int32Array)) {
178
+ throw new Error(`${context} must be an Int32Array.`);
179
+ }
180
+ return value;
181
+ }
182
+
183
+ function expectFloat64Array(value: unknown, context: string): Float64Array {
184
+ if (!(value instanceof Float64Array)) {
185
+ throw new Error(`${context} must be a Float64Array.`);
186
+ }
187
+ return value;
188
+ }
189
+
190
+ function expectBigInt64Array(value: unknown, context: string): BigInt64Array {
191
+ if (!(value instanceof BigInt64Array)) {
192
+ throw new Error(`${context} must be a BigInt64Array.`);
193
+ }
194
+ return value;
195
+ }
196
+
197
+ function expectBigUint64Array(value: unknown, context: string): BigUint64Array {
198
+ if (!(value instanceof BigUint64Array)) {
199
+ throw new Error(`${context} must be a BigUint64Array.`);
200
+ }
201
+ return value;
202
+ }
203
+
204
+ function expectUint32Array(value: unknown, context: string): Uint32Array {
205
+ if (!(value instanceof Uint32Array)) {
206
+ throw new Error(`${context} must be a Uint32Array.`);
207
+ }
208
+ return value;
209
+ }
210
+
129
211
  function expectI32(value: unknown, context: string): number {
130
212
  const numberValue = expectNumber(value, context);
131
213
  if (!Number.isInteger(numberValue) || numberValue < -2147483648 || numberValue > 2147483647) {
@@ -134,6 +216,109 @@ function expectI32(value: unknown, context: string): number {
134
216
  return numberValue;
135
217
  }
136
218
 
219
+ function expectU32(value: unknown, context: string): number {
220
+ const numberValue = expectNumber(value, context);
221
+ if (!Number.isInteger(numberValue) || numberValue < 0 || numberValue > 4294967295) {
222
+ throw new Error(`${context} must be an unsigned 32-bit integer.`);
223
+ }
224
+ return numberValue;
225
+ }
226
+
227
+ function expectI64(value: unknown, context: string): bigint {
228
+ if (typeof value !== "bigint") {
229
+ throw new Error(`${context} must be a bigint.`);
230
+ }
231
+ if (value < -9223372036854775808n || value > 9223372036854775807n) {
232
+ throw new Error(`${context} must be a signed 64-bit integer.`);
233
+ }
234
+ return value;
235
+ }
236
+
237
+ function expectU64(value: unknown, context: string): bigint {
238
+ if (typeof value !== "bigint") {
239
+ throw new Error(`${context} must be a bigint.`);
240
+ }
241
+ if (value < 0n || value > 18446744073709551615n) {
242
+ throw new Error(`${context} must be an unsigned 64-bit integer.`);
243
+ }
244
+ return value;
245
+ }
246
+
247
+ function expectLength(value: unknown, context: string): number {
248
+ const length = expectNumber(value, context);
249
+ if (!Number.isInteger(length) || length < 0) {
250
+ throw new Error(`${context} must be a non-negative integer.`);
251
+ }
252
+ return length;
253
+ }
254
+
255
+ function bytesToI32Array(bytes: Uint8Array, context: string): Int32Array {
256
+ if ((bytes.byteLength & 3) !== 0) {
257
+ throw new Error(`${context} payload length must be divisible by 4.`);
258
+ }
259
+ const values = new Int32Array(bytes.byteLength >>> 2);
260
+ new Uint8Array(values.buffer).set(bytes);
261
+ return values;
262
+ }
263
+
264
+ function bytesToU32Array(bytes: Uint8Array, context: string): Uint32Array {
265
+ if ((bytes.byteLength & 3) !== 0) {
266
+ throw new Error(`${context} payload length must be divisible by 4.`);
267
+ }
268
+ const values = new Uint32Array(bytes.byteLength >>> 2);
269
+ new Uint8Array(values.buffer).set(bytes);
270
+ return values;
271
+ }
272
+
273
+ function bytesToF64Array(bytes: Uint8Array, context: string): Float64Array {
274
+ if ((bytes.byteLength & 7) !== 0) {
275
+ throw new Error(`${context} payload length must be divisible by 8.`);
276
+ }
277
+ const values = new Float64Array(bytes.byteLength >>> 3);
278
+ new Uint8Array(values.buffer).set(bytes);
279
+ return values;
280
+ }
281
+
282
+ function bytesToI64Array(bytes: Uint8Array, context: string): BigInt64Array {
283
+ if ((bytes.byteLength & 7) !== 0) {
284
+ throw new Error(`${context} payload length must be divisible by 8.`);
285
+ }
286
+ const values = new BigInt64Array(bytes.byteLength >>> 3);
287
+ new Uint8Array(values.buffer).set(bytes);
288
+ return values;
289
+ }
290
+
291
+ function bytesToU64Array(bytes: Uint8Array, context: string): BigUint64Array {
292
+ if ((bytes.byteLength & 7) !== 0) {
293
+ throw new Error(`${context} payload length must be divisible by 8.`);
294
+ }
295
+ const values = new BigUint64Array(bytes.byteLength >>> 3);
296
+ new Uint8Array(values.buffer).set(bytes);
297
+ return values;
298
+ }
299
+
300
+ function typedArrayBytes(
301
+ value: Uint8Array | Int32Array | Uint32Array | BigInt64Array | BigUint64Array | Float64Array,
302
+ ): Uint8Array {
303
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
304
+ }
305
+
306
+ function consumedRawArgCount(method: NormalizedHostServiceMethod): number {
307
+ let count = 0;
308
+ method.args.forEach((type) => {
309
+ count += type === "string" ||
310
+ type === "bytes" ||
311
+ type === "i32_array" ||
312
+ type === "u32_array" ||
313
+ type === "i64_array" ||
314
+ type === "u64_array" ||
315
+ type === "f64_array"
316
+ ? 2
317
+ : 1;
318
+ });
319
+ return count;
320
+ }
321
+
137
322
  function decodeHostServiceArgs(
138
323
  method: NormalizedHostServiceMethod,
139
324
  rawArgs: readonly unknown[],
@@ -150,11 +335,64 @@ function decodeHostServiceArgs(
150
335
  index += 2;
151
336
  return;
152
337
  }
338
+ if (type === "bytes") {
339
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
340
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
341
+ decodedArgs.push(len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len));
342
+ index += 2;
343
+ return;
344
+ }
345
+ if (type === "i32_array") {
346
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
347
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
348
+ const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 2);
349
+ decodedArgs.push(bytesToI32Array(payload, context));
350
+ index += 2;
351
+ return;
352
+ }
353
+ if (type === "u32_array") {
354
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
355
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
356
+ const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 2);
357
+ decodedArgs.push(bytesToU32Array(payload, context));
358
+ index += 2;
359
+ return;
360
+ }
361
+ if (type === "f64_array") {
362
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
363
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
364
+ const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
365
+ decodedArgs.push(bytesToF64Array(payload, context));
366
+ index += 2;
367
+ return;
368
+ }
369
+ if (type === "i64_array") {
370
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
371
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
372
+ const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
373
+ decodedArgs.push(bytesToI64Array(payload, context));
374
+ index += 2;
375
+ return;
376
+ }
377
+ if (type === "u64_array") {
378
+ const ptr = expectNumber(rawArgs[index], `${context} ptr`);
379
+ const len = expectLength(rawArgs[index + 1], `${context} len`);
380
+ const payload = len <= 0 ? new Uint8Array(0) : io.readBytes(ptr, len << 3);
381
+ decodedArgs.push(bytesToU64Array(payload, context));
382
+ index += 2;
383
+ return;
384
+ }
153
385
  const rawValue = rawArgs[index];
154
386
  if (type === "bool") {
155
387
  decodedArgs.push(expectNumber(rawValue, context) !== 0);
156
388
  } else if (type === "i32") {
157
389
  decodedArgs.push(expectI32(rawValue, context));
390
+ } else if (type === "u32") {
391
+ decodedArgs.push(expectU32(rawValue, context));
392
+ } else if (type === "i64") {
393
+ decodedArgs.push(expectI64(rawValue, context));
394
+ } else if (type === "u64") {
395
+ decodedArgs.push(expectU64(rawValue, context));
158
396
  } else if (type === "f64") {
159
397
  decodedArgs.push(expectNumber(rawValue, context));
160
398
  } else {
@@ -168,10 +406,10 @@ function decodeHostServiceArgs(
168
406
  export function createHostServiceImportModule(
169
407
  services: HostServicesDefinition | undefined,
170
408
  io: HostServiceImportIo,
171
- ): Record<string, (...rawArgs: Array<unknown>) => number | void> {
172
- const module: Record<string, (...rawArgs: Array<unknown>) => number | void> = {};
409
+ ): Record<string, (...rawArgs: Array<unknown>) => number | bigint | void> {
410
+ const module: Record<string, (...rawArgs: Array<unknown>) => number | bigint | void> = {};
173
411
  for (const method of listHostServiceMethods(services)) {
174
- module[method.importName] = (...rawArgs: Array<unknown>): number | void => {
412
+ module[method.importName] = (...rawArgs: Array<unknown>): number | bigint | void => {
175
413
  const decodedArgs = decodeHostServiceArgs(method, rawArgs, io);
176
414
  const result = method.implementation(...decodedArgs);
177
415
  const resultContext = `Host service ${method.serviceName}.${method.methodName} result`;
@@ -179,20 +417,62 @@ export function createHostServiceImportModule(
179
417
  return;
180
418
  }
181
419
  if (method.returns === "string") {
182
- let outputIndex = 0;
183
- method.args.forEach((type) => {
184
- outputIndex += type === "string" ? 2 : 1;
185
- });
420
+ const outputIndex = consumedRawArgCount(method);
186
421
  const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
187
422
  const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
188
423
  return io.writeString(ptr, capacity, expectString(result, resultContext), resultContext);
189
424
  }
425
+ if (method.returns === "bytes") {
426
+ const outputIndex = consumedRawArgCount(method);
427
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
428
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
429
+ return io.writeBytes(ptr, capacity, expectBytes(result, resultContext), resultContext);
430
+ }
431
+ if (method.returns === "i32_array") {
432
+ const outputIndex = consumedRawArgCount(method);
433
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
434
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
435
+ return io.writeBytes(ptr, capacity, typedArrayBytes(expectInt32Array(result, resultContext)), resultContext);
436
+ }
437
+ if (method.returns === "u32_array") {
438
+ const outputIndex = consumedRawArgCount(method);
439
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
440
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
441
+ return io.writeBytes(ptr, capacity, typedArrayBytes(expectUint32Array(result, resultContext)), resultContext);
442
+ }
443
+ if (method.returns === "f64_array") {
444
+ const outputIndex = consumedRawArgCount(method);
445
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
446
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
447
+ return io.writeBytes(ptr, capacity, typedArrayBytes(expectFloat64Array(result, resultContext)), resultContext);
448
+ }
449
+ if (method.returns === "i64_array") {
450
+ const outputIndex = consumedRawArgCount(method);
451
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
452
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
453
+ return io.writeBytes(ptr, capacity, typedArrayBytes(expectBigInt64Array(result, resultContext)), resultContext);
454
+ }
455
+ if (method.returns === "u64_array") {
456
+ const outputIndex = consumedRawArgCount(method);
457
+ const ptr = expectNumber(rawArgs[outputIndex], `${resultContext} ptr`);
458
+ const capacity = expectNumber(rawArgs[outputIndex + 1], `${resultContext} capacity`);
459
+ return io.writeBytes(ptr, capacity, typedArrayBytes(expectBigUint64Array(result, resultContext)), resultContext);
460
+ }
190
461
  if (method.returns === "bool") {
191
462
  return expectBoolean(result, resultContext) ? 1 : 0;
192
463
  }
193
464
  if (method.returns === "i32") {
194
465
  return expectI32(result, resultContext);
195
466
  }
467
+ if (method.returns === "u32") {
468
+ return expectU32(result, resultContext);
469
+ }
470
+ if (method.returns === "i64") {
471
+ return expectI64(result, resultContext);
472
+ }
473
+ if (method.returns === "u64") {
474
+ return expectU64(result, resultContext);
475
+ }
196
476
  if (method.returns === "f64") {
197
477
  return expectNumber(result, resultContext);
198
478
  }
@@ -106,6 +106,31 @@ function writeUtf8(memory: WebAssembly.Memory | null, ptr: number, capacity: num
106
106
  return encoded.length;
107
107
  }
108
108
 
109
+ function readBytes(memory: WebAssembly.Memory | null, ptr: number, len: number): Uint8Array {
110
+ if (memory === null || len <= 0) {
111
+ return new Uint8Array(0);
112
+ }
113
+ const bytes = new Uint8Array(len);
114
+ bytes.set(new Uint8Array(memory.buffer, ptr, len));
115
+ return bytes;
116
+ }
117
+
118
+ function writeBytes(memory: WebAssembly.Memory | null, ptr: number, capacity: number, bytes: Uint8Array, context: string): number {
119
+ if (memory === null) {
120
+ throw new Error(`${context} requires worker memory.`);
121
+ }
122
+ if (capacity < 0) {
123
+ throw new Error(`${context} has invalid worker host-service buffer capacity.`);
124
+ }
125
+ if (bytes.length > capacity) {
126
+ throw new Error(`${context} exceeds the worker host-service result buffer.`);
127
+ }
128
+ if (bytes.length > 0) {
129
+ new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
130
+ }
131
+ return bytes.length;
132
+ }
133
+
109
134
  function encodeTextPartsPayload(values: readonly string[]): Uint8Array {
110
135
  const encodedValues = values.map((value) => encoder.encode(value));
111
136
  let totalBytes = 4;
@@ -277,6 +302,8 @@ async function startWorker(message: WorkerBootstrapStartMessage): Promise<void>
277
302
  fui_host_service: createHostServiceImportModule(hostServices, {
278
303
  readString: (ptr, len) => readUtf8(memory, ptr, len),
279
304
  writeString: (ptr, capacity, text, context) => writeUtf8(memory, ptr, capacity, text, context),
305
+ readBytes: (ptr, len) => readBytes(memory, ptr, len),
306
+ writeBytes: (ptr, capacity, bytes, context) => writeBytes(memory, ptr, capacity, bytes, context),
280
307
  }),
281
308
  fui_worker_host: {
282
309
  fui_worker_input_length(): number {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/fui-as",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
6
6
  "description": "EffinDom v2 AssemblyScript frontend framework SDK and browser harness",