@plexspaces/sdk 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/host.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CreateShardGroupOptions, CreateShardGroupResult, ScatterGatherOptions, ScatterGatherResult, BroadcastShardGroupOptions, ReduceShardGroupOptions, AllReduceShardGroupOptions, BarrierShardGroupOptions, MapShardGroupOptions, BulkUpdateShardGroupOptions } from './shard_types.js';
1
2
  import { ObjectType as RegistryObjectType } from './wire/registry-proto-wire.js';
2
3
  /**
3
4
  * Tuple space helper: list-in, list-out API.
@@ -192,6 +193,61 @@ export declare class ActorRef {
192
193
  * Equivalent to Cloudflare: `const room = env.CHAT_ROOM.get(roomId)`
193
194
  */
194
195
  export declare function getActorRef(actorType: string, name: string, namespace: string): ActorRef;
196
+ /**
197
+ * Message received from a channel, with payload decoded to a JS value.
198
+ */
199
+ export interface ChannelMessage {
200
+ id: string;
201
+ msgType: string;
202
+ payload: unknown;
203
+ timestamp: number;
204
+ deliveryCount: number;
205
+ headers: Record<string, string>;
206
+ }
207
+ /**
208
+ * Channel host functions for queue and pub/sub messaging patterns.
209
+ *
210
+ * Channels unify queue (one consumer) and pub/sub (all subscribers) under
211
+ * a single API. The provider (InMemory, Redis, Kafka, etc.) is a runtime concern.
212
+ *
213
+ * Parity with Python `host.channel` and Go `host.Ch()`.
214
+ */
215
+ export declare class Channel {
216
+ /** Send a message to a channel (queue semantics). Returns message ID. */
217
+ send(channelName: string, msgType: string, payload?: unknown): string;
218
+ /** Send with delay, TTL, and custom headers. Returns message ID. */
219
+ sendWithOptions(channelName: string, msgType: string, payload?: unknown, delayMs?: number, ttlMs?: number, headers?: Record<string, string>): string;
220
+ /** Receive one message from a channel. Returns null on timeout/empty. */
221
+ receive(channelName: string, timeoutMs?: number): ChannelMessage | null;
222
+ /** Publish a message to a channel (pub/sub — all subscribers receive). Returns message ID. */
223
+ publish(channelName: string, msgType: string, payload?: unknown): string;
224
+ /** Subscribe to a channel (pub/sub). Returns subscription ID. */
225
+ subscribe(channelName: string, filter?: string): string;
226
+ /** Cancel a subscription by ID. */
227
+ unsubscribe(subscriptionId: string): void;
228
+ /** Acknowledge successful processing (prevents redelivery). */
229
+ ack(channelName: string, messageId: string): void;
230
+ /** Negative-acknowledge a message. requeue=true retries; false sends to dead-letter. */
231
+ nack(channelName: string, messageId: string, requeue?: boolean): void;
232
+ /** Create a channel if it does not exist. maxSize=0 means unbounded. */
233
+ create(channelName: string, maxSize?: number, messageTtlMs?: number): void;
234
+ /** Delete a channel and all pending messages. */
235
+ delete(channelName: string): void;
236
+ /** Return the number of pending (unacked) messages in a channel. */
237
+ depth(channelName: string): number;
238
+ }
239
+ /**
240
+ * Outbox host functions for flow-controlled message dispatch.
241
+ *
242
+ * Messages are enqueued in a DB-backed channel and delivered asynchronously
243
+ * at AIMD-controlled concurrency. Parity with Python `host.outbox` and Go `host.Outbox()`.
244
+ */
245
+ export declare class Outbox {
246
+ /** Dispatch messages to target actors via the outbox relay. Returns [accepted, rejected]. */
247
+ dispatch(ctx: string, dispatcherID: string, targetActorIDs: string[], payload: Uint8Array, messageType?: string, idempotencyKey?: string, conflictResolution?: string): [number, number];
248
+ /** Get current status of a dispatcher. Returns JSON-encoded status string. */
249
+ status(ctx: string, dispatcherID: string): string;
250
+ }
195
251
  export declare class Host {
196
252
  readonly processGroups: ProcessGroups;
197
253
  /** Tuple space: list-in, list-out. Use null in patterns for wildcards. */
@@ -206,6 +262,10 @@ export declare class Host {
206
262
  readonly locks: LockClient;
207
263
  /** Blob storage client: host.blob.upload(), host.blob.download() etc. */
208
264
  readonly blob: BlobClient;
265
+ /** Channel client for queue and pub/sub messaging. */
266
+ readonly channel: Channel;
267
+ /** Outbox client for flow-controlled messaging. */
268
+ readonly outbox: Outbox;
209
269
  /**
210
270
  * Create an ergonomic HTTP client for a named service link.
211
271
  *
@@ -298,14 +358,14 @@ export declare class Host {
298
358
  * Get pool metrics (total_actors, available_actors, busy_actors, current_load, etc.). Returns null if not available.
299
359
  */
300
360
  poolGetMetrics(poolName: string): Record<string, unknown> | null;
301
- createShardGroup(request: Record<string, unknown>): Record<string, unknown>;
302
- bulkUpdateShardGroup(request: Record<string, unknown>): Record<string, unknown>;
303
- mapShardGroup(request: Record<string, unknown>): Record<string, unknown>;
304
- scatterGather(request: Record<string, unknown>): Record<string, unknown>;
305
- broadcastShardGroup(request: Record<string, unknown>): Record<string, unknown>;
306
- reduceShardGroup(request: Record<string, unknown>): Record<string, unknown>;
307
- allReduceShardGroup(request: Record<string, unknown>): Record<string, unknown>;
308
- barrierShardGroup(request: Record<string, unknown>): Record<string, unknown>;
361
+ createShardGroup(request: CreateShardGroupOptions): CreateShardGroupResult;
362
+ bulkUpdateShardGroup(request: BulkUpdateShardGroupOptions): Record<string, unknown>;
363
+ mapShardGroup(request: MapShardGroupOptions): Record<string, unknown>;
364
+ scatterGather(request: ScatterGatherOptions): ScatterGatherResult;
365
+ broadcastShardGroup(request: BroadcastShardGroupOptions): Record<string, unknown>;
366
+ reduceShardGroup(request: ReduceShardGroupOptions): Record<string, unknown>;
367
+ allReduceShardGroup(request: AllReduceShardGroupOptions): Record<string, unknown>;
368
+ barrierShardGroup(request: BarrierShardGroupOptions): Record<string, unknown>;
309
369
  spawnActors(request: Record<string, unknown>): Record<string, unknown>;
310
370
  applicationMetricsAdd(applicationId: string, metrics: Record<string, unknown>): Record<string, unknown>;
311
371
  applicationGetMetrics(applicationId: string, nodeId: string): Record<string, unknown>;
package/dist/host.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // Protobuf wire for WIT `payload` fields (matches Go WASM hostWire* / tuplespace_proto_wire).
9
9
  import { decodeReadResponseAllTuples, decodeReadResponseFirstTuple, encodeReadRequest, encodeWriteRequest, } from './wire/tuplespace-proto-wire.js';
10
10
  import { decodeHttpFetchResponseWire, encodeHttpFetchRequestWire } from './wire/http-fetch-proto-wire.js';
11
- import { decodeApplicationMetrics, decodeCreateShardGroupResponse, decodeGetApplicationStatusResponse, decodeScatterGatherResponse, encodeApplicationMetrics, encodeCreateShardGroupRequest, encodeScatterGatherRequest, } from './wire/shard-group-proto-wire.js';
11
+ import { decodeApplicationMetrics, decodeBulkUpdateShardGroupResponse, decodeCreateShardGroupResponse, decodeGetApplicationStatusResponse, decodeScatterGatherResponse, encodeApplicationMetrics, encodeBulkUpdateShardGroupRequest, encodeCreateShardGroupRequest, encodeScatterGatherRequest, encodeBroadcastShardGroupRequest, decodeBroadcastShardGroupResponse, encodeReduceShardGroupRequest, decodeReduceShardGroupResponse, encodeAllReduceShardGroupRequest, decodeAllReduceShardGroupResponse, encodeBarrierShardGroupRequest, decodeBarrierShardGroupResponse, encodeMapShardGroupRequest, decodeMapShardGroupResponse, } from './wire/shard-group-proto-wire.js';
12
12
  import { decodeWitPayloadUtf8, encodeWitPayloadUtf8 } from './wit-payload.js';
13
13
  import { encodeRegisterRequest, encodeUnregisterRequest, encodeLookupRequest, encodeDiscoverRequest, encodeHeartbeatRequest, decodeLookupResponse, decodeDiscoverResponse, ObjectType as RegistryObjectType, } from './wire/registry-proto-wire.js';
14
14
  import { firstGroupMember, firstGroupMemberOrThrow } from './process_groups.js';
@@ -24,6 +24,8 @@ import { blobUpload as hostBlobUpload, blobDownload as hostBlobDownload, blobDel
24
24
  import { poolCheckout as hostPoolCheckout, poolCheckin as hostPoolCheckin, poolGetMetrics as hostPoolGetMetrics, } from 'plexspaces:actor/host-pool@0.1.0';
25
25
  import { createShardGroup as hostCreateShardGroup, bulkUpdateShardGroup as hostBulkUpdateShardGroup, mapShardGroup as hostMapShardGroup, broadcastShardGroup as hostBroadcastShardGroup, reduceShardGroup as hostReduceShardGroup, allReduceShardGroup as hostAllReduceShardGroup, barrierShardGroup as hostBarrierShardGroup, scatterGather as hostScatterGather, spawnActors as hostSpawnActors, applicationMetricsAdd as hostApplicationMetricsAdd, applicationGetMetrics as hostApplicationGetMetrics, applicationGetStatus as hostApplicationGetStatus, } from 'plexspaces:actor/host-shard@0.1.0';
26
26
  import { httpFetch as hostHttpFetch } from 'plexspaces:actor/host-http@0.1.0';
27
+ import { channelSend as hostChannelSend, channelSendWithOptions as hostChannelSendWithOptions, channelReceive as hostChannelReceive, channelPublish as hostChannelPublish, channelSubscribe as hostChannelSubscribe, channelUnsubscribe as hostChannelUnsubscribe, channelAck as hostChannelAck, channelNack as hostChannelNack, channelCreate as hostChannelCreate, channelDelete as hostChannelDelete, channelDepth as hostChannelDepth, } from 'plexspaces:actor/channels@0.1.0';
28
+ import { outboxDispatch as hostOutboxDispatch, outboxStatus as hostOutboxStatus, } from 'plexspaces:actor/outbox@0.1.0';
27
29
  import { register as hostRegistryRegister, unregister as hostRegistryUnregister, lookup as hostRegistryLookup, lookupByAlias as hostRegistryLookupByAlias, discover as hostRegistryDiscover, heartbeat as hostRegistryHeartbeat, } from 'plexspaces:actor/registry@0.1.0';
28
30
  /**
29
31
  * Safe call helper — returns empty string if function is undefined.
@@ -543,6 +545,176 @@ export class ActorRef {
543
545
  export function getActorRef(actorType, name, namespace) {
544
546
  return new ActorRef(actorType, name, namespace);
545
547
  }
548
+ /**
549
+ * Channel host functions for queue and pub/sub messaging patterns.
550
+ *
551
+ * Channels unify queue (one consumer) and pub/sub (all subscribers) under
552
+ * a single API. The provider (InMemory, Redis, Kafka, etc.) is a runtime concern.
553
+ *
554
+ * Parity with Python `host.channel` and Go `host.Ch()`.
555
+ */
556
+ export class Channel {
557
+ /** Send a message to a channel (queue semantics). Returns message ID. */
558
+ send(channelName, msgType, payload) {
559
+ const payloadBytes = encodeWitPayloadUtf8(payload !== undefined ? JSON.stringify(payload) : '{}');
560
+ const result = safeCall(hostChannelSend, '', channelName, msgType, payloadBytes);
561
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
562
+ throw new Error(result);
563
+ }
564
+ return result;
565
+ }
566
+ /** Send with delay, TTL, and custom headers. Returns message ID. */
567
+ sendWithOptions(channelName, msgType, payload, delayMs = 0, ttlMs = 0, headers) {
568
+ const payloadBytes = encodeWitPayloadUtf8(payload !== undefined ? JSON.stringify(payload) : '{}');
569
+ const headersJson = JSON.stringify(headers ?? {});
570
+ const result = safeCall(hostChannelSendWithOptions, '', channelName, msgType, payloadBytes, BigInt(delayMs), BigInt(ttlMs), headersJson);
571
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
572
+ throw new Error(result);
573
+ }
574
+ return result;
575
+ }
576
+ /** Receive one message from a channel. Returns null on timeout/empty. */
577
+ receive(channelName, timeoutMs = 0) {
578
+ const raw = safeCall(hostChannelReceive, '', channelName, BigInt(timeoutMs));
579
+ if (!raw || raw === '' || raw === undefined)
580
+ return null;
581
+ if (typeof raw === 'string') {
582
+ if (raw.startsWith('ERROR:'))
583
+ throw new Error(raw);
584
+ try {
585
+ return JSON.parse(raw);
586
+ }
587
+ catch {
588
+ return null;
589
+ }
590
+ }
591
+ const msg = raw;
592
+ if (!msg || !msg.id)
593
+ return null;
594
+ let decodedPayload;
595
+ try {
596
+ const payloadStr = decodeWitPayloadUtf8(msg.payload);
597
+ decodedPayload = JSON.parse(payloadStr);
598
+ }
599
+ catch {
600
+ decodedPayload = msg.payload;
601
+ }
602
+ const hdrs = {};
603
+ if (Array.isArray(msg.headers)) {
604
+ for (const [k, v] of msg.headers) {
605
+ hdrs[k] = v;
606
+ }
607
+ }
608
+ return {
609
+ id: msg.id,
610
+ msgType: msg.msgType,
611
+ payload: decodedPayload,
612
+ timestamp: typeof msg.timestamp === 'bigint' ? Number(msg.timestamp) : Number(msg.timestamp ?? 0),
613
+ deliveryCount: msg.deliveryCount,
614
+ headers: hdrs,
615
+ };
616
+ }
617
+ /** Publish a message to a channel (pub/sub — all subscribers receive). Returns message ID. */
618
+ publish(channelName, msgType, payload) {
619
+ const payloadBytes = encodeWitPayloadUtf8(payload !== undefined ? JSON.stringify(payload) : '{}');
620
+ const result = safeCall(hostChannelPublish, '', channelName, msgType, payloadBytes);
621
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
622
+ throw new Error(result);
623
+ }
624
+ return result;
625
+ }
626
+ /** Subscribe to a channel (pub/sub). Returns subscription ID. */
627
+ subscribe(channelName, filter = '') {
628
+ const result = safeCall(hostChannelSubscribe, '', channelName, filter);
629
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
630
+ throw new Error(result);
631
+ }
632
+ return result;
633
+ }
634
+ /** Cancel a subscription by ID. */
635
+ unsubscribe(subscriptionId) {
636
+ const result = safeCall(hostChannelUnsubscribe, subscriptionId);
637
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
638
+ throw new Error(result);
639
+ }
640
+ }
641
+ /** Acknowledge successful processing (prevents redelivery). */
642
+ ack(channelName, messageId) {
643
+ const result = safeCall(hostChannelAck, '', channelName, messageId);
644
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
645
+ throw new Error(result);
646
+ }
647
+ }
648
+ /** Negative-acknowledge a message. requeue=true retries; false sends to dead-letter. */
649
+ nack(channelName, messageId, requeue = true) {
650
+ const result = safeCall(hostChannelNack, '', channelName, messageId, requeue);
651
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
652
+ throw new Error(result);
653
+ }
654
+ }
655
+ /** Create a channel if it does not exist. maxSize=0 means unbounded. */
656
+ create(channelName, maxSize = 0, messageTtlMs = 0) {
657
+ const result = safeCall(hostChannelCreate, '', channelName, maxSize, BigInt(messageTtlMs));
658
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
659
+ throw new Error(result);
660
+ }
661
+ }
662
+ /** Delete a channel and all pending messages. */
663
+ delete(channelName) {
664
+ const result = safeCall(hostChannelDelete, '', channelName);
665
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
666
+ throw new Error(result);
667
+ }
668
+ }
669
+ /** Return the number of pending (unacked) messages in a channel. */
670
+ depth(channelName) {
671
+ const result = safeCall(hostChannelDepth, '', channelName);
672
+ if (typeof result === 'bigint')
673
+ return Number(result);
674
+ if (typeof result === 'number')
675
+ return result;
676
+ if (typeof result === 'string') {
677
+ if (result.startsWith('ERROR:'))
678
+ throw new Error(result);
679
+ const n = parseInt(result, 10);
680
+ return isNaN(n) ? 0 : n;
681
+ }
682
+ return 0;
683
+ }
684
+ }
685
+ /**
686
+ * Outbox host functions for flow-controlled message dispatch.
687
+ *
688
+ * Messages are enqueued in a DB-backed channel and delivered asynchronously
689
+ * at AIMD-controlled concurrency. Parity with Python `host.outbox` and Go `host.Outbox()`.
690
+ */
691
+ export class Outbox {
692
+ /** Dispatch messages to target actors via the outbox relay. Returns [accepted, rejected]. */
693
+ dispatch(ctx, dispatcherID, targetActorIDs, payload, messageType = 'cast', idempotencyKey = '', conflictResolution = '') {
694
+ const result = safeCall(hostOutboxDispatch, ctx, dispatcherID, targetActorIDs, payload, messageType, idempotencyKey, conflictResolution);
695
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
696
+ throw new Error(result);
697
+ }
698
+ if (typeof result === 'string' && result.length > 0) {
699
+ try {
700
+ const parsed = JSON.parse(result);
701
+ return [parsed.accepted ?? 0, parsed.rejected ?? 0];
702
+ }
703
+ catch (_) {
704
+ return [0, 0];
705
+ }
706
+ }
707
+ return [0, 0];
708
+ }
709
+ /** Get current status of a dispatcher. Returns JSON-encoded status string. */
710
+ status(ctx, dispatcherID) {
711
+ const result = safeCall(hostOutboxStatus, ctx, dispatcherID);
712
+ if (typeof result === 'string' && result.startsWith('ERROR:')) {
713
+ throw new Error(result);
714
+ }
715
+ return typeof result === 'string' ? result : '';
716
+ }
717
+ }
546
718
  export class Host {
547
719
  constructor() {
548
720
  this.processGroups = new ProcessGroups();
@@ -558,6 +730,10 @@ export class Host {
558
730
  this.locks = new LockClient();
559
731
  /** Blob storage client: host.blob.upload(), host.blob.download() etc. */
560
732
  this.blob = new BlobClient();
733
+ /** Channel client for queue and pub/sub messaging. */
734
+ this.channel = new Channel();
735
+ /** Outbox client for flow-controlled messaging. */
736
+ this.outbox = new Outbox();
561
737
  }
562
738
  /**
563
739
  * Create an ergonomic HTTP client for a named service link.
@@ -799,71 +975,170 @@ export class Host {
799
975
  }
800
976
  }
801
977
  createShardGroup(request) {
802
- const reqBytes = encodeCreateShardGroupRequest(request);
978
+ const wireReq = {
979
+ group_id: request.groupId,
980
+ actor_type: request.actorType,
981
+ shard_count: request.shardCount,
982
+ partition_strategy: request.partitionStrategy ?? 'hash',
983
+ rebalance_policy: request.rebalancePolicy ?? 'manual',
984
+ placement: request.placement ? {
985
+ strategy: request.placement.strategy ?? 'from_registry',
986
+ node_ids: request.placement.nodeIds ?? [],
987
+ cluster: request.placement.cluster ?? '',
988
+ } : undefined,
989
+ initial_state: request.initialState,
990
+ metadata: request.metadata,
991
+ };
992
+ const reqBytes = encodeCreateShardGroupRequest(wireReq);
803
993
  const result = safeCall(hostCreateShardGroup, reqBytes);
804
994
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
805
995
  throw new Error(result);
806
996
  }
807
997
  const bytes = hostPayloadToBytes(result);
808
998
  if (bytes.length === 0)
809
- return { shard_actor_ids: [] };
999
+ return { groupId: '', actorType: '', shardActorIds: [], shardCount: 0 };
810
1000
  const decoded = decodeCreateShardGroupResponse(bytes);
811
- // Flatten: expose group fields at top level for actor convenience
812
1001
  const group = decoded.group ?? {};
813
- return { ...group, ...decoded };
1002
+ const config = (group.config ?? {});
1003
+ return {
1004
+ groupId: config.group_id ?? '',
1005
+ actorType: group.actor_type ?? '',
1006
+ shardActorIds: group.shard_actor_ids ?? [],
1007
+ shardCount: config.shard_count ?? 0,
1008
+ };
814
1009
  }
815
1010
  bulkUpdateShardGroup(request) {
816
- const result = safeCall(hostBulkUpdateShardGroup, JSON.stringify(request));
1011
+ const wireReq = {
1012
+ group_id: request.groupId,
1013
+ updates: request.updates,
1014
+ consistency_level: request.consistencyLevel === 'strong' ? 2 : request.consistencyLevel === 'sequential' ? 3 : 1,
1015
+ timeout_ms: request.timeoutMs ?? 5000,
1016
+ wait_for_responses: request.waitForResponses ?? false,
1017
+ };
1018
+ const reqBytes = encodeBulkUpdateShardGroupRequest(wireReq);
1019
+ const result = safeCall(hostBulkUpdateShardGroup, reqBytes);
817
1020
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
818
1021
  throw new Error(result);
819
1022
  }
820
- return JSON.parse(result);
1023
+ const bytes = hostPayloadToBytes(result);
1024
+ if (bytes.length === 0)
1025
+ return { updates_sent: 0, updates_succeeded: 0, updates_failed: 0, errors: [] };
1026
+ return decodeBulkUpdateShardGroupResponse(bytes);
821
1027
  }
822
1028
  mapShardGroup(request) {
823
- const result = safeCall(hostMapShardGroup, JSON.stringify(request));
1029
+ const wireReq = {
1030
+ group_id: request.groupId,
1031
+ map_function: request.mapFunction,
1032
+ timeout_ms: request.timeoutMs ?? 30000,
1033
+ min_responses: request.minResponses ?? 0,
1034
+ };
1035
+ const reqBytes = encodeMapShardGroupRequest(wireReq);
1036
+ const result = safeCall(hostMapShardGroup, reqBytes);
824
1037
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
825
1038
  throw new Error(result);
826
1039
  }
827
- return JSON.parse(result);
1040
+ const bytes = hostPayloadToBytes(result);
1041
+ if (bytes.length === 0)
1042
+ return { shard_results: [] };
1043
+ return decodeMapShardGroupResponse(bytes);
828
1044
  }
829
1045
  scatterGather(request) {
830
- const reqBytes = encodeScatterGatherRequest(request);
1046
+ const wireReq = {
1047
+ group_id: request.groupId,
1048
+ query: request.query,
1049
+ aggregation: request.aggregation ?? 'concat',
1050
+ timeout_ms: request.timeoutMs ?? 30000,
1051
+ min_responses: request.minResponses ?? 0,
1052
+ };
1053
+ const reqBytes = encodeScatterGatherRequest(wireReq);
831
1054
  const result = safeCall(hostScatterGather, reqBytes);
832
1055
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
833
1056
  throw new Error(result);
834
1057
  }
835
1058
  const bytes = hostPayloadToBytes(result);
836
1059
  if (bytes.length === 0)
837
- return { shard_responses: [] };
838
- return decodeScatterGatherResponse(bytes);
1060
+ return { shardResponses: [] };
1061
+ const decoded = decodeScatterGatherResponse(bytes);
1062
+ return { shardResponses: decoded.shard_responses.map(r => ({
1063
+ shardId: r.shard_id ?? 0,
1064
+ shardActorId: r.shard_actor_id ?? '',
1065
+ payload: r.payload ?? {},
1066
+ success: r.success ?? false,
1067
+ error: r.error ?? '',
1068
+ })) };
839
1069
  }
840
1070
  broadcastShardGroup(request) {
841
- const result = safeCall(hostBroadcastShardGroup, JSON.stringify(request));
1071
+ const wireReq = {
1072
+ group_id: request.groupId,
1073
+ message: request.message,
1074
+ timeout_ms: request.timeoutMs ?? 30000,
1075
+ min_acks: request.minAcks ?? 0,
1076
+ };
1077
+ const reqBytes = encodeBroadcastShardGroupRequest(wireReq);
1078
+ const result = safeCall(hostBroadcastShardGroup, reqBytes);
842
1079
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
843
1080
  throw new Error(result);
844
1081
  }
845
- return JSON.parse(result);
1082
+ const bytes = hostPayloadToBytes(result);
1083
+ if (bytes.length === 0)
1084
+ return { shard_responses: [] };
1085
+ return decodeBroadcastShardGroupResponse(bytes);
846
1086
  }
847
1087
  reduceShardGroup(request) {
848
- const result = safeCall(hostReduceShardGroup, JSON.stringify(request));
1088
+ const wireReq = {
1089
+ group_id: request.groupId,
1090
+ map_function: request.mapFunction,
1091
+ reduction: request.reduction,
1092
+ target: request.target,
1093
+ timeout_ms: request.timeoutMs ?? 30000,
1094
+ min_responses: request.minResponses ?? 0,
1095
+ };
1096
+ const reqBytes = encodeReduceShardGroupRequest(wireReq);
1097
+ const result = safeCall(hostReduceShardGroup, reqBytes);
849
1098
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
850
1099
  throw new Error(result);
851
1100
  }
852
- return JSON.parse(result);
1101
+ const bytes = hostPayloadToBytes(result);
1102
+ if (bytes.length === 0)
1103
+ return { shard_responses: [] };
1104
+ return decodeReduceShardGroupResponse(bytes);
853
1105
  }
854
1106
  allReduceShardGroup(request) {
855
- const result = safeCall(hostAllReduceShardGroup, JSON.stringify(request));
1107
+ const wireReq = {
1108
+ group_id: request.groupId,
1109
+ map_function: request.mapFunction,
1110
+ reduction: request.reduction,
1111
+ target: request.target,
1112
+ timeout_ms: request.timeoutMs ?? 30000,
1113
+ min_responses: request.minResponses ?? 0,
1114
+ };
1115
+ const reqBytes = encodeAllReduceShardGroupRequest(wireReq);
1116
+ const result = safeCall(hostAllReduceShardGroup, reqBytes);
856
1117
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
857
1118
  throw new Error(result);
858
1119
  }
859
- return JSON.parse(result);
1120
+ const bytes = hostPayloadToBytes(result);
1121
+ if (bytes.length === 0)
1122
+ return { shard_responses: [] };
1123
+ return decodeAllReduceShardGroupResponse(bytes);
860
1124
  }
861
1125
  barrierShardGroup(request) {
862
- const result = safeCall(hostBarrierShardGroup, JSON.stringify(request));
1126
+ const wireReq = {
1127
+ group_id: request.groupId,
1128
+ barrier_id: request.barrierId,
1129
+ round: request.round ?? 0,
1130
+ timeout_ms: request.timeoutMs ?? 30000,
1131
+ min_acks: request.minAcks ?? 0,
1132
+ };
1133
+ const reqBytes = encodeBarrierShardGroupRequest(wireReq);
1134
+ const result = safeCall(hostBarrierShardGroup, reqBytes);
863
1135
  if (typeof result === 'string' && result.startsWith('ERROR:')) {
864
1136
  throw new Error(result);
865
1137
  }
866
- return JSON.parse(result);
1138
+ const bytes = hostPayloadToBytes(result);
1139
+ if (bytes.length === 0)
1140
+ return { shard_responses: [] };
1141
+ return decodeBarrierShardGroupResponse(bytes);
867
1142
  }
868
1143
  spawnActors(request) {
869
1144
  const result = safeCall(hostSpawnActors, JSON.stringify(request));
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { PlexSpacesActor, WorkflowActor } from "./actor.js";
2
2
  export { decodeWitPayloadUtf8, encodeWitPayloadUtf8 } from "./wit-payload.js";
3
3
  export { actor, gen_server_actor, event_actor, fsm_actor, workflow_actor, handler, init_handler, run_handler, signal_handler, query_handler, getActorDefinition, } from "./decorators.js";
4
- export { Host, ProcessGroups, TupleSpace, host, pgFirst, pgFirstOrThrow, ServiceHttpClient, ActorRef, getActorRef } from "./host.js";
4
+ export { Host, ProcessGroups, TupleSpace, Channel, Outbox, host, pgFirst, pgFirstOrThrow, ServiceHttpClient, ActorRef, getActorRef } from "./host.js";
5
+ export type { ChannelMessage } from "./host.js";
5
6
  export { ActorRouter } from "./router.js";
6
7
  export { defaultRetryConfig, withRetry, type RetryConfig, } from "./workflow.js";
7
8
  export { LeaderWorkerClient, listWorkerNodeIds, } from "./leader_worker.js";
8
9
  export { ActorID } from "./actor_id.js";
10
+ export type { CreateShardGroupOptions, CreateShardGroupResult, ScatterGatherOptions, ScatterGatherResult, ShardQueryResult, BroadcastShardGroupOptions, ReduceShardGroupOptions, AllReduceShardGroupOptions, BarrierShardGroupOptions, MapShardGroupOptions, BulkUpdateShardGroupOptions, NodePlacementOptions, } from "./shard_types.js";
9
11
  export { WsThinClient, type ThinClientOptions, type ThinNodePingResult, } from "./ws_thin_client.js";
10
12
  export { AgentLoop, agentActor, defaultAgentConfig, type AgentConfig, type AgentStep, type AgentTrajectory, type AgentStepKind, } from "./agent.js";
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  export { PlexSpacesActor, WorkflowActor } from "./actor.js";
8
8
  export { decodeWitPayloadUtf8, encodeWitPayloadUtf8 } from "./wit-payload.js";
9
9
  export { actor, gen_server_actor, event_actor, fsm_actor, workflow_actor, handler, init_handler, run_handler, signal_handler, query_handler, getActorDefinition, } from "./decorators.js";
10
- export { Host, ProcessGroups, TupleSpace, host, pgFirst, pgFirstOrThrow, ServiceHttpClient, ActorRef, getActorRef } from "./host.js";
10
+ export { Host, ProcessGroups, TupleSpace, Channel, Outbox, host, pgFirst, pgFirstOrThrow, ServiceHttpClient, ActorRef, getActorRef } from "./host.js";
11
11
  export { ActorRouter } from "./router.js";
12
12
  export { defaultRetryConfig, withRetry, } from "./workflow.js";
13
13
  export { LeaderWorkerClient, listWorkerNodeIds, } from "./leader_worker.js";
@@ -0,0 +1,76 @@
1
+ export interface NodePlacementOptions {
2
+ strategy?: 'from_registry' | 'same_node' | 'node_ids';
3
+ nodeIds?: string[];
4
+ cluster?: string;
5
+ requiredLabels?: Record<string, string>;
6
+ avoidNodeIds?: string[];
7
+ affinityLabels?: Record<string, string>;
8
+ }
9
+ export interface CreateShardGroupOptions {
10
+ groupId: string;
11
+ actorType: string;
12
+ shardCount: number;
13
+ partitionStrategy?: 'hash' | 'range' | 'consistent_hash';
14
+ rebalancePolicy?: 'manual' | 'on_scale' | 'load_based';
15
+ placement?: NodePlacementOptions;
16
+ initialState?: Record<string, unknown>;
17
+ metadata?: Record<string, string>;
18
+ }
19
+ export interface CreateShardGroupResult {
20
+ groupId: string;
21
+ actorType: string;
22
+ shardActorIds: string[];
23
+ shardCount: number;
24
+ }
25
+ export interface ScatterGatherOptions {
26
+ groupId: string;
27
+ query: Record<string, unknown>;
28
+ aggregation?: 'concat' | 'merge' | 'first' | 'majority';
29
+ timeoutMs?: number;
30
+ minResponses?: number;
31
+ }
32
+ export interface ShardQueryResult {
33
+ shardId: number;
34
+ shardActorId: string;
35
+ payload: unknown;
36
+ success: boolean;
37
+ error: string;
38
+ }
39
+ export interface ScatterGatherResult {
40
+ shardResponses: ShardQueryResult[];
41
+ }
42
+ export interface BroadcastShardGroupOptions {
43
+ groupId: string;
44
+ message: Record<string, unknown>;
45
+ timeoutMs?: number;
46
+ minAcks?: number;
47
+ }
48
+ export interface ReduceShardGroupOptions {
49
+ groupId: string;
50
+ mapFunction: Record<string, unknown>;
51
+ reduction: 'sum' | 'min' | 'max' | 'product' | 'concat' | 'bool_and' | 'bool_or';
52
+ target?: string;
53
+ timeoutMs?: number;
54
+ minResponses?: number;
55
+ }
56
+ export type AllReduceShardGroupOptions = ReduceShardGroupOptions;
57
+ export interface BarrierShardGroupOptions {
58
+ groupId: string;
59
+ barrierId: string;
60
+ round?: number;
61
+ timeoutMs?: number;
62
+ minAcks?: number;
63
+ }
64
+ export interface MapShardGroupOptions {
65
+ groupId: string;
66
+ mapFunction: Record<string, unknown>;
67
+ timeoutMs?: number;
68
+ minResponses?: number;
69
+ }
70
+ export interface BulkUpdateShardGroupOptions {
71
+ groupId: string;
72
+ updates: Record<string, Record<string, unknown>>;
73
+ consistencyLevel?: 'eventual' | 'strong' | 'sequential';
74
+ timeoutMs?: number;
75
+ waitForResponses?: boolean;
76
+ }
@@ -0,0 +1,5 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (C) 2025 PlexSpaces Contributors
3
+ //
4
+ // Typed request/response types for shard group host operations.
5
+ export {};
@@ -4,4 +4,16 @@ export declare function decodeCreateShardGroupResponse(data: Uint8Array): Record
4
4
  export declare function decodeScatterGatherResponse(data: Uint8Array): Record<string, unknown>;
5
5
  export declare function decodeApplicationMetrics(data: Uint8Array): Record<string, unknown>;
6
6
  export declare function decodeGetApplicationStatusResponse(data: Uint8Array): Record<string, unknown>;
7
+ export declare function encodeBulkUpdateShardGroupRequest(req: Record<string, unknown>): Uint8Array;
8
+ export declare function decodeBulkUpdateShardGroupResponse(data: Uint8Array): Record<string, unknown>;
9
+ export declare function encodeBroadcastShardGroupRequest(req: Record<string, unknown>): Uint8Array;
10
+ export declare function encodeReduceShardGroupRequest(req: Record<string, unknown>): Uint8Array;
11
+ export declare function encodeAllReduceShardGroupRequest(req: Record<string, unknown>): Uint8Array;
12
+ export declare function encodeBarrierShardGroupRequest(req: Record<string, unknown>): Uint8Array;
13
+ export declare function encodeMapShardGroupRequest(req: Record<string, unknown>): Uint8Array;
14
+ export declare function decodeBroadcastShardGroupResponse(data: Uint8Array): Record<string, unknown>;
15
+ export declare function decodeReduceShardGroupResponse(data: Uint8Array): Record<string, unknown>;
16
+ export declare function decodeAllReduceShardGroupResponse(data: Uint8Array): Record<string, unknown>;
17
+ export declare function decodeBarrierShardGroupResponse(data: Uint8Array): Record<string, unknown>;
18
+ export declare function decodeMapShardGroupResponse(data: Uint8Array): Record<string, unknown>;
7
19
  export declare function encodeApplicationMetrics(metrics: Record<string, unknown>): Uint8Array;