@irpclib/irpc 1.2.0 → 1.2.2

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/enum.d.ts CHANGED
@@ -28,6 +28,7 @@ declare const IRPC_FILE_STATUS: {
28
28
  readonly IDLE: "idle";
29
29
  readonly PENDING: "pending";
30
30
  readonly SUCCESS: "success";
31
+ readonly ABORTED: "aborted";
31
32
  readonly ERROR: "error";
32
33
  };
33
34
  //#endregion
package/dist/enum.js CHANGED
@@ -28,6 +28,7 @@ const IRPC_FILE_STATUS = {
28
28
  IDLE: "idle",
29
29
  PENDING: "pending",
30
30
  SUCCESS: "success",
31
+ ABORTED: "aborted",
31
32
  ERROR: "error"
32
33
  };
33
34
 
package/dist/file.d.ts CHANGED
@@ -26,6 +26,33 @@ declare class IRPCFile {
26
26
  data: Blob;
27
27
  constructor(meta: IRPCFileMeta, data?: Blob);
28
28
  }
29
+ declare class IRPCBlob {
30
+ url: string;
31
+ meta?: {
32
+ type?: string;
33
+ size?: number;
34
+ name?: string;
35
+ } | undefined;
36
+ protected state: IRPCFileState;
37
+ data: Blob;
38
+ private pipes;
39
+ private promise;
40
+ private controller?;
41
+ get status(): IRPCFileStatus;
42
+ get error(): Error | undefined;
43
+ get downloaded(): number;
44
+ get success(): boolean;
45
+ get completed(): boolean;
46
+ constructor(url: string, meta?: {
47
+ type?: string;
48
+ size?: number;
49
+ name?: string;
50
+ } | undefined);
51
+ load(): Promise<Blob>;
52
+ pipe(fn: IRPCFilePipe): IRPCFileUnpipe;
53
+ then<TResult1 = Blob, TResult2 = never>(onfulfilled?: ((value: Blob) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
54
+ catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<Blob | TResult>;
55
+ }
29
56
  declare class IRPCFileStream extends IRPCFile {
30
57
  private pipes;
31
58
  private buffer;
@@ -34,4 +61,4 @@ declare class IRPCFileStream extends IRPCFile {
34
61
  pipe(fn: IRPCFilePipe): IRPCFileUnpipe;
35
62
  }
36
63
  //#endregion
37
- export { IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe };
64
+ export { IRPCBlob, IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe };
package/dist/file.js CHANGED
@@ -1,6 +1,6 @@
1
- import { IRPC_FILE_STATUS } from "./enum.js";
1
+ import { IRPC_FILE_STATUS, IRPC_STATUS } from "./enum.js";
2
2
  import { IRPC_STORE } from "./store.js";
3
- import { mutable } from "@anchorlib/core";
3
+ import { mutable, onCleanup } from "@anchorlib/core";
4
4
 
5
5
  //#region src/file.ts
6
6
  var IRPCFile = class {
@@ -33,6 +33,92 @@ var IRPCFile = class {
33
33
  this.state.status = data ? IRPC_FILE_STATUS.SUCCESS : IRPC_FILE_STATUS.PENDING;
34
34
  }
35
35
  };
36
+ var IRPCBlob = class {
37
+ state = mutable({
38
+ status: IRPC_FILE_STATUS.PENDING,
39
+ downloaded: 0
40
+ });
41
+ data;
42
+ pipes = /* @__PURE__ */ new Set();
43
+ promise;
44
+ controller;
45
+ get status() {
46
+ return this.state.status;
47
+ }
48
+ get error() {
49
+ return this.state.error;
50
+ }
51
+ get downloaded() {
52
+ return this.state.downloaded;
53
+ }
54
+ get success() {
55
+ return this.status === IRPC_FILE_STATUS.SUCCESS;
56
+ }
57
+ get completed() {
58
+ return [IRPC_FILE_STATUS.SUCCESS, IRPC_FILE_STATUS.ERROR].includes(this.status);
59
+ }
60
+ constructor(url, meta) {
61
+ this.url = url;
62
+ this.meta = meta;
63
+ this.data = new Blob([], { type: meta?.type ?? "" });
64
+ }
65
+ load() {
66
+ if (this.promise) return this.promise;
67
+ this.controller = new AbortController();
68
+ this.state.status = IRPC_FILE_STATUS.PENDING;
69
+ this.promise = fetch(this.url, { signal: this.controller.signal }).then(async (response) => {
70
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
71
+ if (response.body && this.meta?.size) {
72
+ const reader = response.body.getReader();
73
+ const chunks = [];
74
+ let downloaded = 0;
75
+ while (true) {
76
+ const { done, value } = await reader.read();
77
+ if (done) break;
78
+ if (value) {
79
+ chunks.push(value);
80
+ downloaded += value.byteLength;
81
+ this.state.downloaded = downloaded;
82
+ this.pipes.forEach((fn) => {
83
+ try {
84
+ fn(value);
85
+ } catch (error) {
86
+ IRPC_STORE.error(error, [{ url: this.url }]);
87
+ }
88
+ });
89
+ }
90
+ }
91
+ this.data = new Blob(chunks, { type: this.meta?.type ?? "" });
92
+ } else {
93
+ this.data = await response.blob();
94
+ this.state.downloaded = this.data.size;
95
+ }
96
+ this.state.status = IRPC_FILE_STATUS.SUCCESS;
97
+ return this.data;
98
+ }).catch((error) => {
99
+ IRPC_STORE.error(error, [{ url: this.url }]);
100
+ this.state.error = error;
101
+ /* v8 ignore next */
102
+ this.state.status = this.controller?.signal?.aborted ? IRPC_STATUS.ABORTED : IRPC_FILE_STATUS.ERROR;
103
+ throw error;
104
+ });
105
+ onCleanup(() => {
106
+ /* v8 ignore next */
107
+ this.controller?.abort();
108
+ });
109
+ return this.promise;
110
+ }
111
+ pipe(fn) {
112
+ this.pipes.add(fn);
113
+ return () => this.pipes.delete(fn);
114
+ }
115
+ then(onfulfilled, onrejected) {
116
+ return this.load().then(onfulfilled, onrejected);
117
+ }
118
+ catch(onrejected) {
119
+ return this.load().catch(onrejected);
120
+ }
121
+ };
36
122
  var IRPCFileStream = class extends IRPCFile {
37
123
  pipes = /* @__PURE__ */ new Set();
38
124
  buffer;
@@ -83,4 +169,4 @@ var IRPCFileStream = class extends IRPCFile {
83
169
  };
84
170
 
85
171
  //#endregion
86
- export { IRPCFile, IRPCFileStream };
172
+ export { IRPCBlob, IRPCFile, IRPCFileStream };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IRPC_BASE_CONTEXT, IRPC_FILE_STATUS, IRPC_PACKET_TYPE, IRPC_STATUS, IRPC_STORE_EVENT } from "./enum.js";
2
- import { IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe } from "./file.js";
3
- import { IRPCFilePointer, IRPCFileQueue, IRPCPacketJson, IRPCPacketQueues, IRPC_FILE_IDENTIFIER, PacketStream, decode, encode, isFilePointer } from "./packet.js";
2
+ import { IRPCBlob, IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe } from "./file.js";
3
+ import { IRPCBlobPointer, IRPCFilePointer, IRPCFileQueue, IRPCPacketJson, IRPCPacketQueues, IRPC_BLOB_IDENTIFIER, IRPC_FILE_IDENTIFIER, PacketStream, decode, decodeBlobs, encode, encodeBlobs, isBlobPointer, isFilePointer } from "./packet.js";
4
4
  import { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall } from "./call.js";
5
5
  import { IRPCTransport } from "./transport.js";
6
6
  import { IRPCArraySchema, IRPCCallConfig, IRPCContext, IRPCContextProvider, IRPCCredentials, IRPCCredentialsFactory, IRPCCrudField, IRPCCrudMeta, IRPCCrudMethod, IRPCCrudOptions, IRPCCrudStubs, IRPCData, IRPCDataSchema, IRPCDeclareConfig, IRPCDeclareInit, IRPCDefined, IRPCDriver, IRPCEntityId, IRPCFunction, IRPCHandler, IRPCInferInit, IRPCInit, IRPCInputs, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackageConfig, IRPCPackageInfo, IRPCPacketAnswer, IRPCPacketBase, IRPCPacketCall, IRPCPacketClose, IRPCPacketError, IRPCPacketEvent, IRPCPacketStream, IRPCPacketType, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCReadable, IRPCRequest, IRPCRequests, IRPCResponse, IRPCReturnOf, IRPCSchema, IRPCSpec, IRPCSpecStore, IRPCStatus, IRPCStreamInit, IRPCStub, IRPCStubStore, StreamCleanup, StreamConstructor, TransportConfig } from "./types.js";
@@ -17,4 +17,4 @@ import { IRPCHook, IRPCRouter } from "./router.js";
17
17
  import { IRPCStream } from "./stream.js";
18
18
  import { IRPCStore, IRPCStoreEvent, IRPCStoreSubscriber, IRPC_STORE } from "./store.js";
19
19
  import { plan } from "@anchorlib/core";
20
- export { CALL_ERROR, CRUD_ERROR, CallError, CrudError, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, HANDLER_ERROR, HOOK_ERROR, HandlerError, HookError, IRPCAdapter, IRPCArraySchema, IRPCCacheEntry, IRPCCacher, IRPCCall, IRPCCallConfig, IRPCContext, IRPCContextProvider, IRPCCredentials, IRPCCredentialsFactory, IRPCCrudField, IRPCCrudMeta, IRPCCrudMethod, IRPCCrudOptions, IRPCCrudStubs, IRPCData, IRPCDataSchema, IRPCDeclareConfig, IRPCDeclareInit, IRPCDefined, IRPCDriver, IRPCEntityId, IRPCError, IRPCErrorType, IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFilePointer, IRPCFileQueue, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe, IRPCFunction, IRPCHandler, IRPCHook, IRPCHookArgs, IRPCInferInit, IRPCInit, IRPCInputs, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackage, IRPCPackageConfig, IRPCPackageInfo, IRPCPacketAnswer, IRPCPacketBase, IRPCPacketCall, IRPCPacketClose, IRPCPacketError, IRPCPacketEvent, IRPCPacketJson, IRPCPacketQueues, IRPCPacketStream, IRPCPacketType, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCReadable, IRPCReader, IRPCRequest, IRPCRequests, IRPCResolver, IRPCResponse, IRPCReturnOf, IRPCRouter, IRPCSchema, IRPCSpec, IRPCSpecHook, IRPCSpecStore, IRPCStatus, IRPCStore, IRPCStoreEvent, IRPCStoreSubscriber, IRPCStream, IRPCStreamInit, IRPCStub, IRPCStubStore, IRPCTransport, IRPC_BASE_CONTEXT, IRPC_ERROR_TYPE, IRPC_FILE_IDENTIFIER, IRPC_FILE_STATUS, IRPC_PACKET_TYPE, IRPC_STATUS, IRPC_STORE, IRPC_STORE_EVENT, PacketStream, RESOLVE_ERROR, RemoteState, ResolveError, STUB_ERROR, StreamCleanup, StreamConstructor, StubError, TRANSPORT_ERROR, TransportConfig, TransportError, createContext, createContextStore, createCredentials, createPackage, credential, decode, encode, getAbortController, getAbortSignal, getContext, getCredentials, intercept, isFilePointer, plan, setContext, setContextProvider, stream, withContext };
20
+ export { CALL_ERROR, CRUD_ERROR, CallError, CrudError, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, HANDLER_ERROR, HOOK_ERROR, HandlerError, HookError, IRPCAdapter, IRPCArraySchema, IRPCBlob, IRPCBlobPointer, IRPCCacheEntry, IRPCCacher, IRPCCall, IRPCCallConfig, IRPCContext, IRPCContextProvider, IRPCCredentials, IRPCCredentialsFactory, IRPCCrudField, IRPCCrudMeta, IRPCCrudMethod, IRPCCrudOptions, IRPCCrudStubs, IRPCData, IRPCDataSchema, IRPCDeclareConfig, IRPCDeclareInit, IRPCDefined, IRPCDriver, IRPCEntityId, IRPCError, IRPCErrorType, IRPCFile, IRPCFileMeta, IRPCFilePipe, IRPCFilePointer, IRPCFileQueue, IRPCFileState, IRPCFileStatus, IRPCFileStream, IRPCFileUnpipe, IRPCFunction, IRPCHandler, IRPCHook, IRPCHookArgs, IRPCInferInit, IRPCInit, IRPCInputs, IRPCObject, IRPCObjectSchema, IRPCOutput, IRPCPackage, IRPCPackageConfig, IRPCPackageInfo, IRPCPacketAnswer, IRPCPacketBase, IRPCPacketCall, IRPCPacketClose, IRPCPacketError, IRPCPacketEvent, IRPCPacketJson, IRPCPacketQueues, IRPCPacketStream, IRPCPacketType, IRPCParseResult, IRPCPayload, IRPCPrimitive, IRPCPrimitiveSchema, IRPCReadable, IRPCReader, IRPCRequest, IRPCRequests, IRPCResolver, IRPCResponse, IRPCReturnOf, IRPCRouter, IRPCSchema, IRPCSpec, IRPCSpecHook, IRPCSpecStore, IRPCStatus, IRPCStore, IRPCStoreEvent, IRPCStoreSubscriber, IRPCStream, IRPCStreamInit, IRPCStub, IRPCStubStore, IRPCTransport, IRPC_BASE_CONTEXT, IRPC_BLOB_IDENTIFIER, IRPC_ERROR_TYPE, IRPC_FILE_IDENTIFIER, IRPC_FILE_STATUS, IRPC_PACKET_TYPE, IRPC_STATUS, IRPC_STORE, IRPC_STORE_EVENT, PacketStream, RESOLVE_ERROR, RemoteState, ResolveError, STUB_ERROR, StreamCleanup, StreamConstructor, StubError, TRANSPORT_ERROR, TransportConfig, TransportError, createContext, createContextStore, createCredentials, createPackage, credential, decode, decodeBlobs, encode, encodeBlobs, getAbortController, getAbortSignal, getContext, getCredentials, intercept, isBlobPointer, isFilePointer, plan, setContext, setContextProvider, stream, withContext };
package/dist/index.js CHANGED
@@ -8,14 +8,14 @@ import { IRPCReader } from "./reader.js";
8
8
  import { IRPCTransport } from "./transport.js";
9
9
  import { IRPCPackage, createPackage, intercept } from "./module.js";
10
10
  import { IRPCRouter } from "./router.js";
11
+ import { IRPCBlob, IRPCFile, IRPCFileStream } from "./file.js";
12
+ import { IRPC_BLOB_IDENTIFIER, IRPC_FILE_IDENTIFIER, decode, decodeBlobs, encode, encodeBlobs, isBlobPointer, isFilePointer } from "./packet.js";
11
13
  import { IRPCStream } from "./stream.js";
12
14
  import { IRPCStore, IRPC_STORE } from "./store.js";
13
15
  import { DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, IRPCCall } from "./call.js";
14
16
  import { createCredentials, credential, getCredentials } from "./credential.js";
15
- import { IRPCFile, IRPCFileStream } from "./file.js";
16
- import { IRPC_FILE_IDENTIFIER, decode, encode, isFilePointer } from "./packet.js";
17
17
  import { IRPCResolver } from "./resolver.js";
18
18
  import { IRPCDriver } from "./types.js";
19
19
  import { plan } from "@anchorlib/core";
20
20
 
21
- export { CALL_ERROR, CRUD_ERROR, CallError, CrudError, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, HANDLER_ERROR, HOOK_ERROR, HandlerError, HookError, IRPCAdapter, IRPCCacher, IRPCCall, IRPCDriver, IRPCError, IRPCFile, IRPCFileStream, IRPCPackage, IRPCReader, IRPCResolver, IRPCRouter, IRPCStore, IRPCStream, IRPCTransport, IRPC_BASE_CONTEXT, IRPC_ERROR_TYPE, IRPC_FILE_IDENTIFIER, IRPC_FILE_STATUS, IRPC_PACKET_TYPE, IRPC_STATUS, IRPC_STORE, IRPC_STORE_EVENT, RESOLVE_ERROR, RemoteState, ResolveError, STUB_ERROR, StubError, TRANSPORT_ERROR, TransportError, createContext, createContextStore, createCredentials, createPackage, credential, decode, encode, getAbortController, getAbortSignal, getContext, getCredentials, intercept, isFilePointer, plan, setContext, setContextProvider, stream, withContext };
21
+ export { CALL_ERROR, CRUD_ERROR, CallError, CrudError, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_MODE, HANDLER_ERROR, HOOK_ERROR, HandlerError, HookError, IRPCAdapter, IRPCBlob, IRPCCacher, IRPCCall, IRPCDriver, IRPCError, IRPCFile, IRPCFileStream, IRPCPackage, IRPCReader, IRPCResolver, IRPCRouter, IRPCStore, IRPCStream, IRPCTransport, IRPC_BASE_CONTEXT, IRPC_BLOB_IDENTIFIER, IRPC_ERROR_TYPE, IRPC_FILE_IDENTIFIER, IRPC_FILE_STATUS, IRPC_PACKET_TYPE, IRPC_STATUS, IRPC_STORE, IRPC_STORE_EVENT, RESOLVE_ERROR, RemoteState, ResolveError, STUB_ERROR, StubError, TRANSPORT_ERROR, TransportError, createContext, createContextStore, createCredentials, createPackage, credential, decode, decodeBlobs, encode, encodeBlobs, getAbortController, getAbortSignal, getContext, getCredentials, intercept, isBlobPointer, isFilePointer, plan, setContext, setContextProvider, stream, withContext };
package/dist/module.js CHANGED
@@ -132,7 +132,7 @@ var IRPCPackage = class {
132
132
  * @returns {IRPCReader<IRPCData>} - The reader for the call.
133
133
  */
134
134
  function prepare(getArgs, deferred, debounce = 0) {
135
- const reader = new IRPCReader(uuid(), spec.seed(), deferred ? IRPC_STATUS.IDLE : IRPC_STATUS.PENDING);
135
+ const reader = new IRPCReader(uuid(), spec.seed(), deferred ? IRPC_STATUS.IDLE : IRPC_STATUS.PENDING, true);
136
136
  if (isBrowser()) {
137
137
  const observer = createObserver(() => {
138
138
  observer.reset();
@@ -143,6 +143,8 @@ var IRPCPackage = class {
143
143
  const args = observer.run(getArgs);
144
144
  if (!coalesce) return execute(args, reader);
145
145
  schedule(() => {
146
+ if (reader.status === IRPC_STATUS.PENDING) reader.close();
147
+ reader.resume();
146
148
  execute(args, reader);
147
149
  });
148
150
  };
package/dist/packet.d.ts CHANGED
@@ -3,11 +3,21 @@ import { IRPCData } from "./types.js";
3
3
 
4
4
  //#region src/packet.d.ts
5
5
  declare const IRPC_FILE_IDENTIFIER: "IRPC_PACKET_FILE";
6
+ declare const IRPC_BLOB_IDENTIFIER: "IRPC_PACKET_BLOB";
6
7
  type IRPCFilePointer = {
7
8
  id: string;
8
9
  type: typeof IRPC_FILE_IDENTIFIER;
9
10
  meta: IRPCFileMeta;
10
11
  };
12
+ type IRPCBlobPointer = {
13
+ type: typeof IRPC_BLOB_IDENTIFIER;
14
+ url: string;
15
+ meta?: {
16
+ type?: string;
17
+ size?: number;
18
+ name?: string;
19
+ };
20
+ };
11
21
  type IRPCFileQueue = {
12
22
  file: IRPCFilePointer;
13
23
  data: Blob;
@@ -26,7 +36,25 @@ type PacketStream = {
26
36
  resolved: number;
27
37
  };
28
38
  declare function isFilePointer(data: IRPCData): boolean;
39
+ declare function isBlobPointer(data: IRPCData): boolean;
29
40
  declare function encode(data: IRPCData): IRPCPacketQueues;
30
41
  declare function decode(packet: IRPCPacketJson): PacketStream;
42
+ /**
43
+ * Recursively replaces all IRPCBlobPointer objects in a data tree with IRPCBlob instances.
44
+ * Used by client-side transports to materialize blob references after receiving response data.
45
+ * Eagerly calls .load() to kick off the fetch — the caller awaits the thenable to get the loaded blob.
46
+ *
47
+ * @param data - The data tree potentially containing IRPCBlobPointer objects.
48
+ * @returns The data tree with blob pointers replaced by IRPCBlob instances.
49
+ */
50
+ declare function decodeBlobs<T>(data: unknown): T;
51
+ /**
52
+ * Recursively replaces all IRPCBlob instances in a data tree with IRPCBlobPointer objects.
53
+ * Used by IRPCStream to encode blobs before packets are JSON-serialized for transport.
54
+ *
55
+ * @param data - The data tree potentially containing IRPCBlob instances.
56
+ * @returns A new data tree with IRPCBlob instances replaced by IRPCBlobPointer objects.
57
+ */
58
+ declare function encodeBlobs(data: unknown): unknown;
31
59
  //#endregion
32
- export { IRPCFilePointer, IRPCFileQueue, IRPCPacketJson, IRPCPacketQueues, IRPC_FILE_IDENTIFIER, PacketStream, decode, encode, isFilePointer };
60
+ export { IRPCBlobPointer, IRPCFilePointer, IRPCFileQueue, IRPCPacketJson, IRPCPacketQueues, IRPC_BLOB_IDENTIFIER, IRPC_FILE_IDENTIFIER, PacketStream, decode, decodeBlobs, encode, encodeBlobs, isBlobPointer, isFilePointer };
package/dist/packet.js CHANGED
@@ -1,11 +1,15 @@
1
- import { IRPCFile, IRPCFileStream } from "./file.js";
1
+ import { IRPCBlob, IRPCFile, IRPCFileStream } from "./file.js";
2
2
  import { isArray, isObject, uuid } from "@anchorlib/core";
3
3
 
4
4
  //#region src/packet.ts
5
5
  const IRPC_FILE_IDENTIFIER = "IRPC_PACKET_FILE";
6
+ const IRPC_BLOB_IDENTIFIER = "IRPC_PACKET_BLOB";
6
7
  function isFilePointer(data) {
7
8
  return isObject(data) && data.type === IRPC_FILE_IDENTIFIER;
8
9
  }
10
+ function isBlobPointer(data) {
11
+ return isObject(data) && data.type === IRPC_BLOB_IDENTIFIER;
12
+ }
9
13
  function encode(data) {
10
14
  const json = {
11
15
  data,
@@ -15,7 +19,8 @@ function encode(data) {
15
19
  json,
16
20
  queues: []
17
21
  };
18
- if (data instanceof IRPCFile) {
22
+ if (data instanceof IRPCBlob) json.data = createBlobPointer(data);
23
+ else if (data instanceof IRPCFile) {
19
24
  const { pointer, queue } = createPointer(data);
20
25
  json.data = pointer;
21
26
  json.files.push(pointer);
@@ -52,6 +57,13 @@ function createPointer(file) {
52
57
  }
53
58
  };
54
59
  }
60
+ function createBlobPointer(blob) {
61
+ return {
62
+ type: IRPC_BLOB_IDENTIFIER,
63
+ url: blob.url,
64
+ meta: blob.meta
65
+ };
66
+ }
55
67
  /**
56
68
  * Replace all IRPCFile inside an object with IRPCPacketFile.
57
69
  * @param {Record<string, unknown> | unknown[]} data - The object to encode.
@@ -60,7 +72,8 @@ function createPointer(file) {
60
72
  */
61
73
  function encodePointers(data, pointers, queues) {
62
74
  if (isArray(data)) data.forEach((item, i) => {
63
- if (item instanceof IRPCFile) {
75
+ if (item instanceof IRPCBlob) data[i] = createBlobPointer(item);
76
+ else if (item instanceof IRPCFile) {
64
77
  const { pointer, queue } = createPointer(item);
65
78
  data[i] = pointer;
66
79
  pointers.push(pointer);
@@ -68,7 +81,8 @@ function encodePointers(data, pointers, queues) {
68
81
  } else if (isObject(item) || isArray(item)) encodePointers(item, pointers, queues);
69
82
  });
70
83
  else if (isObject(data)) Object.entries(data).forEach(([key, value]) => {
71
- if (value instanceof IRPCFile) {
84
+ if (value instanceof IRPCBlob) data[key] = createBlobPointer(value);
85
+ else if (value instanceof IRPCFile) {
72
86
  const { pointer, queue } = createPointer(value);
73
87
  data[key] = pointer;
74
88
  pointers.push(pointer);
@@ -95,6 +109,41 @@ function decodePointers(data, files) {
95
109
  } else if (isObject(value) || isArray(value)) decodePointers(value, files);
96
110
  });
97
111
  }
112
+ /**
113
+ * Recursively replaces all IRPCBlobPointer objects in a data tree with IRPCBlob instances.
114
+ * Used by client-side transports to materialize blob references after receiving response data.
115
+ * Eagerly calls .load() to kick off the fetch — the caller awaits the thenable to get the loaded blob.
116
+ *
117
+ * @param data - The data tree potentially containing IRPCBlobPointer objects.
118
+ * @returns The data tree with blob pointers replaced by IRPCBlob instances.
119
+ */
120
+ function decodeBlobs(data) {
121
+ if (!data || typeof data !== "object") return data;
122
+ if (isBlobPointer(data)) {
123
+ const pointer = data;
124
+ const blob = new IRPCBlob(pointer.url, pointer.meta);
125
+ blob.load();
126
+ return blob;
127
+ }
128
+ if (Array.isArray(data)) for (let i = 0; i < data.length; i++) data[i] = decodeBlobs(data[i]);
129
+ else for (const [key, value] of Object.entries(data)) data[key] = decodeBlobs(value);
130
+ return data;
131
+ }
132
+ /**
133
+ * Recursively replaces all IRPCBlob instances in a data tree with IRPCBlobPointer objects.
134
+ * Used by IRPCStream to encode blobs before packets are JSON-serialized for transport.
135
+ *
136
+ * @param data - The data tree potentially containing IRPCBlob instances.
137
+ * @returns A new data tree with IRPCBlob instances replaced by IRPCBlobPointer objects.
138
+ */
139
+ function encodeBlobs(data) {
140
+ if (data instanceof IRPCBlob) return createBlobPointer(data);
141
+ if (!data || typeof data !== "object") return data;
142
+ if (Array.isArray(data)) return data.map((item) => encodeBlobs(item));
143
+ const result = {};
144
+ for (const [key, value] of Object.entries(data)) result[key] = encodeBlobs(value);
145
+ return result;
146
+ }
98
147
 
99
148
  //#endregion
100
- export { IRPC_FILE_IDENTIFIER, decode, encode, isFilePointer };
149
+ export { IRPC_BLOB_IDENTIFIER, IRPC_FILE_IDENTIFIER, decode, decodeBlobs, encode, encodeBlobs, isBlobPointer, isFilePointer };
package/dist/stream.js CHANGED
@@ -2,6 +2,7 @@ import { CallError, HandlerError, ResolveError } from "./error.js";
2
2
  import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
3
3
  import { getAbortController, getAbortSignal } from "./context.js";
4
4
  import { RemoteState } from "./state.js";
5
+ import { encodeBlobs } from "./packet.js";
5
6
  import { IRPC_STORE } from "./store.js";
6
7
 
7
8
  //#region src/stream.ts
@@ -65,7 +66,7 @@ var IRPCStream = class {
65
66
  }
66
67
  const { result } = response;
67
68
  if (result instanceof RemoteState) {
68
- this.value = result.data;
69
+ this.value = encodeBlobs(result.data);
69
70
  if (result.status === IRPC_STATUS.SUCCESS || result.status === IRPC_STATUS.ERROR) {
70
71
  if (result.status === IRPC_STATUS.ERROR) {
71
72
  this.error = HandlerError.failed(result.error).json();
@@ -90,7 +91,7 @@ var IRPCStream = class {
90
91
  id,
91
92
  name,
92
93
  type: IRPC_PACKET_TYPE.ANSWER,
93
- data: result.data,
94
+ data: encodeBlobs(result.data),
94
95
  status: result.status,
95
96
  createdAt: Date.now()
96
97
  });
@@ -105,7 +106,7 @@ var IRPCStream = class {
105
106
  data: {
106
107
  type,
107
108
  keys,
108
- value
109
+ value: encodeBlobs(value)
109
110
  },
110
111
  type: IRPC_PACKET_TYPE.EVENT,
111
112
  status: state.status,
@@ -140,7 +141,7 @@ var IRPCStream = class {
140
141
  };
141
142
  abortSignal?.addEventListener("abort", abortStream, { once: true });
142
143
  } else {
143
- this.value = result;
144
+ this.value = encodeBlobs(result);
144
145
  if (response.error) {
145
146
  this.error = response.error;
146
147
  this.status = IRPC_STATUS.ERROR;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IRPC_PACKET_TYPE, IRPC_STATUS } from "./enum.js";
2
- import { IRPCFile } from "./file.js";
2
+ import { IRPCBlob, IRPCFile } from "./file.js";
3
3
  import { IRPCFilePointer } from "./packet.js";
4
4
  import { IRPCTransport } from "./transport.js";
5
5
  import { RemoteState } from "./state.js";
@@ -114,12 +114,12 @@ type IRPCObject = {
114
114
  * Represents all possible data types in IRPC, including primitives, objects, and arrays.
115
115
  * This is a recursive type that allows nested structures.
116
116
  */
117
- type IRPCData = IRPCPrimitive | IRPCObject | IRPCFile | IRPCData[];
117
+ type IRPCData = IRPCPrimitive | IRPCObject | IRPCFile | IRPCBlob | IRPCData[];
118
118
  /**
119
119
  * Represents all possible defined data types in IRPC, including primitives, objects, and arrays.
120
120
  * This is a recursive type that allows nested structures.
121
121
  */
122
- type IRPCDefined = string | number | boolean | IRPCObject | IRPCFile | IRPCDefined[];
122
+ type IRPCDefined = string | number | boolean | IRPCObject | IRPCFile | IRPCBlob | IRPCDefined[];
123
123
  /**
124
124
  * Union type of all primitive Zod schema types used for validation.
125
125
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@irpclib/irpc",
4
- "version": "1.2.0",
4
+ "version": "1.2.2",
5
5
  "types": "./dist/index.d.ts",
6
6
  "module": "./dist/index.js",
7
7
  "exports": {
@@ -36,7 +36,7 @@
36
36
  "zod": "^4.1.5"
37
37
  },
38
38
  "peerDependencies": {
39
- "@anchorlib/core": "1.2.0",
39
+ "@anchorlib/core": "1.2.2",
40
40
  "typescript": "^5.9.3"
41
41
  },
42
42
  "optionalDependencies": {