@le-space/browser 0.1.26 → 0.1.27

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/README.md CHANGED
@@ -13,6 +13,25 @@ The package should stay UI-neutral. It should provide browser-safe helpers, but
13
13
  it should not own app-specific Svelte state, prepaid product logic, or wallet
14
14
  UX.
15
15
 
16
+ ## Client Surface
17
+
18
+ The preferred public entrypoint is a typed browser client factory:
19
+
20
+ - `createAlephBrowserClient({ apiHost?, crnListUrl? })`
21
+
22
+ That client should remain small and stable. It currently owns:
23
+
24
+ - balance lookup
25
+ - CRN listing
26
+ - instance listing
27
+ - message envelope lookup
28
+ - deployment result inspection and polling
29
+ - Aleph message broadcast helpers
30
+
31
+ Lower-level helper functions remain exported too, but new extractions should
32
+ prefer hanging reusable behavior off the client surface unless there is a good
33
+ reason to keep them as standalone utilities.
34
+
16
35
  ## Planned v1 Scope
17
36
 
18
37
  The first real extraction wave should cover:
@@ -23,6 +42,7 @@ The first real extraction wave should cover:
23
42
  - balance fetch
24
43
  - CRN fetch
25
44
  - instance listing
45
+ - typed browser client factory
26
46
  - Aleph message broadcast helpers
27
47
  - deployment polling and result inspection
28
48
  - runtime detail inspection
package/index.d.ts CHANGED
@@ -6,6 +6,8 @@ interface BrowserPackagePlan {
6
6
  declare const BROWSER_PACKAGE_PLAN: BrowserPackagePlan;
7
7
  type MessageStatus = 'processed' | 'pending' | 'rejected' | 'unknown';
8
8
  type ReferenceStatus = MessageStatus | 'missing';
9
+ type AlephSenderChain = 'ETH';
10
+ type AlephMessageType = 'INSTANCE' | 'FORGET' | 'AGGREGATE';
9
11
  interface BalanceResponse {
10
12
  address: string;
11
13
  balance: string;
@@ -141,6 +143,41 @@ interface DeploymentInspectionResult {
141
143
  rejectionReason: string | null;
142
144
  references: MessageReference[];
143
145
  }
146
+ interface AlephBroadcastMessage {
147
+ sender: string;
148
+ chain: AlephSenderChain;
149
+ signature: string;
150
+ type: AlephMessageType;
151
+ item_hash: string;
152
+ item_type: 'inline';
153
+ item_content: string;
154
+ time: number;
155
+ channel: string;
156
+ }
157
+ interface AlephBroadcastResponse {
158
+ publication_status?: {
159
+ status: string;
160
+ failed?: unknown[];
161
+ };
162
+ message_status?: MessageStatus;
163
+ [key: string]: unknown;
164
+ }
165
+ interface BroadcastResult {
166
+ response: AlephBroadcastResponse;
167
+ httpStatus: number;
168
+ }
169
+ interface AlephBrowserClient {
170
+ apiHost: string;
171
+ crnListUrl: string;
172
+ fetchBalance(address: string): Promise<BalanceResponse>;
173
+ fetchCrns(): Promise<Crn[]>;
174
+ fetchInstances(address: string): Promise<InstanceMessage[]>;
175
+ fetchMessageEnvelope(itemHash: string): Promise<AlephMessageEnvelope | null>;
176
+ inspectDeploymentResult(itemHash: string, rootfsRef?: string): Promise<DeploymentInspectionResult>;
177
+ waitForDeploymentResult(itemHash: string, rootfsRef?: string, attempts?: number, delayMs?: number): Promise<DeploymentInspectionResult>;
178
+ broadcastInstanceMessage(message: AlephBroadcastMessage, sync?: boolean): Promise<BroadcastResult>;
179
+ broadcastAlephMessage(message: AlephBroadcastMessage, sync?: boolean): Promise<BroadcastResult>;
180
+ }
144
181
  interface RootfsRequiredPortForward {
145
182
  port: number;
146
183
  tcp?: boolean;
@@ -190,6 +227,14 @@ declare function fetchInstances(address: string, apiHost?: string): Promise<Inst
190
227
  declare function fetchMessageEnvelope(itemHash: string, apiHost?: string): Promise<AlephMessageEnvelope | null>;
191
228
  declare function inspectDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string): Promise<DeploymentInspectionResult>;
192
229
  declare function waitForDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string, attempts?: number, delayMs?: number): Promise<DeploymentInspectionResult>;
230
+ declare function broadcastInstanceMessage(message: AlephBroadcastMessage, apiHost?: string, sync?: boolean): Promise<BroadcastResult>;
231
+ declare function broadcastAlephMessage(message: AlephBroadcastMessage, apiHost?: string, sync?: boolean): Promise<BroadcastResult>;
232
+
233
+ interface CreateAlephBrowserClientOptions {
234
+ apiHost?: string;
235
+ crnListUrl?: string;
236
+ }
237
+ declare function createAlephBrowserClient(options?: CreateAlephBrowserClientOptions): AlephBrowserClient;
193
238
 
194
239
  declare const ITEM_HASH_RE: RegExp;
195
240
  declare const DEFAULT_ROOTFS_MANIFEST_URL = "./rootfs-manifest.json";
@@ -206,4 +251,4 @@ declare const DEFAULT_ALEPH_AGGREGATE_ADDRESS = "0xFba561a84A537fCaa567bb7A2257e
206
251
  declare function parseInstancePricing(payload: unknown): InstancePricing;
207
252
  declare function fetchInstancePricing(apiHost?: string, aggregateAddress?: string): Promise<PricingState>;
208
253
 
209
- export { type AlephMessageEnvelope, BROWSER_PACKAGE_PLAN, type BalanceResponse, type BrowserExtractionPhase, type BrowserPackagePlan, type ComputeUnit, type Crn, type CrnListResponse, type CrnLocation, type CrnUsage, DEFAULT_ALEPH_AGGREGATE_ADDRESS, DEFAULT_ALEPH_API_HOST, DEFAULT_CRN_LIST_URL, DEFAULT_IPFS_GATEWAY_BASE_URL, DEFAULT_ROOTFS_MANIFEST_URL, type DeploymentInspectionResult, type GatewayProbeStatus, ITEM_HASH_RE, type InstanceMessage, type InstancePricing, type LoadRootfsManifestOptions, type MessageReference, type MessageStatus, type PaymentMode, type Price, type PricingState, type ReferenceStatus, type RootfsManifest, type RootfsManifestState, type RootfsRequiredPortForward, type RootfsResolution, type Tier, fetchBalance, fetchCrns, fetchInstancePricing, fetchInstances, fetchMessageEnvelope, fetchWithTimeout, inspectDeploymentResult, loadRootfsManifest, normalizeMessageStatus, parseInstancePricing, resolveRootfsReference, validateRootfsManifest, verifyRootfsExists, waitForDeploymentResult };
254
+ export { type AlephBroadcastMessage, type AlephBroadcastResponse, type AlephBrowserClient, type AlephMessageEnvelope, type AlephMessageType, type AlephSenderChain, BROWSER_PACKAGE_PLAN, type BalanceResponse, type BroadcastResult, type BrowserExtractionPhase, type BrowserPackagePlan, type ComputeUnit, type CreateAlephBrowserClientOptions, type Crn, type CrnListResponse, type CrnLocation, type CrnUsage, DEFAULT_ALEPH_AGGREGATE_ADDRESS, DEFAULT_ALEPH_API_HOST, DEFAULT_CRN_LIST_URL, DEFAULT_IPFS_GATEWAY_BASE_URL, DEFAULT_ROOTFS_MANIFEST_URL, type DeploymentInspectionResult, type GatewayProbeStatus, ITEM_HASH_RE, type InstanceMessage, type InstancePricing, type LoadRootfsManifestOptions, type MessageReference, type MessageStatus, type PaymentMode, type Price, type PricingState, type ReferenceStatus, type RootfsManifest, type RootfsManifestState, type RootfsRequiredPortForward, type RootfsResolution, type Tier, broadcastAlephMessage, broadcastInstanceMessage, createAlephBrowserClient, fetchBalance, fetchCrns, fetchInstancePricing, fetchInstances, fetchMessageEnvelope, fetchWithTimeout, inspectDeploymentResult, loadRootfsManifest, normalizeMessageStatus, parseInstancePricing, resolveRootfsReference, validateRootfsManifest, verifyRootfsExists, waitForDeploymentResult };
package/index.js CHANGED
@@ -181,6 +181,83 @@ async function waitForDeploymentResult(itemHash, rootfsRef, apiHost = DEFAULT_AL
181
181
  }
182
182
  return lastResult;
183
183
  }
184
+ function isInvalidMessageFormatResponse(response, payload) {
185
+ if (response.status !== 422) return false;
186
+ const details = payload.details;
187
+ if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
188
+ if (details && typeof details === "object") {
189
+ const detailMessage = details.message;
190
+ if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
191
+ }
192
+ return false;
193
+ }
194
+ async function postBroadcastPayload(body, apiHost) {
195
+ const rawResponse = await fetchWithTimeout(`${apiHost}/api/v0/messages`, {
196
+ method: "POST",
197
+ headers: { "content-type": "application/json" },
198
+ body: JSON.stringify(body)
199
+ });
200
+ const response = await rawResponse.json().catch(() => ({}));
201
+ return {
202
+ response,
203
+ httpStatus: rawResponse.status,
204
+ rawResponse
205
+ };
206
+ }
207
+ async function broadcastInstanceMessage(message, apiHost = DEFAULT_ALEPH_API_HOST, sync = false) {
208
+ const attempts = [{ sync, message }, { ...message, sync }, { ...message }];
209
+ for (let index = 0; index < attempts.length; index += 1) {
210
+ const result = await postBroadcastPayload(attempts[index], apiHost);
211
+ if (result.rawResponse.ok || result.httpStatus === 202) {
212
+ return {
213
+ response: result.response,
214
+ httpStatus: result.httpStatus
215
+ };
216
+ }
217
+ const canRetry = index < attempts.length - 1 && isInvalidMessageFormatResponse(result.rawResponse, result.response);
218
+ if (!canRetry) {
219
+ throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response)}`);
220
+ }
221
+ }
222
+ throw new Error("Broadcast failed: no compatible request format was accepted");
223
+ }
224
+ async function broadcastAlephMessage(message, apiHost = DEFAULT_ALEPH_API_HOST, sync = false) {
225
+ return broadcastInstanceMessage(message, apiHost, sync);
226
+ }
227
+
228
+ // src/client.ts
229
+ function createAlephBrowserClient(options = {}) {
230
+ const apiHost = options.apiHost ?? DEFAULT_ALEPH_API_HOST;
231
+ const crnListUrl = options.crnListUrl ?? DEFAULT_CRN_LIST_URL;
232
+ return {
233
+ apiHost,
234
+ crnListUrl,
235
+ fetchBalance(address) {
236
+ return fetchBalance(address, apiHost);
237
+ },
238
+ fetchCrns() {
239
+ return fetchCrns(crnListUrl);
240
+ },
241
+ fetchInstances(address) {
242
+ return fetchInstances(address, apiHost);
243
+ },
244
+ fetchMessageEnvelope(itemHash) {
245
+ return fetchMessageEnvelope(itemHash, apiHost);
246
+ },
247
+ inspectDeploymentResult(itemHash, rootfsRef) {
248
+ return inspectDeploymentResult(itemHash, rootfsRef, apiHost);
249
+ },
250
+ waitForDeploymentResult(itemHash, rootfsRef, attempts, delayMs) {
251
+ return waitForDeploymentResult(itemHash, rootfsRef, apiHost, attempts, delayMs);
252
+ },
253
+ broadcastInstanceMessage(message, sync) {
254
+ return broadcastInstanceMessage(message, apiHost, sync);
255
+ },
256
+ broadcastAlephMessage(message, sync) {
257
+ return broadcastAlephMessage(message, apiHost, sync);
258
+ }
259
+ };
260
+ }
184
261
 
185
262
  // src/rootfs.ts
186
263
  var ITEM_HASH_RE = /^[a-fA-F0-9]{64}$/u;
@@ -399,6 +476,9 @@ export {
399
476
  DEFAULT_IPFS_GATEWAY_BASE_URL,
400
477
  DEFAULT_ROOTFS_MANIFEST_URL,
401
478
  ITEM_HASH_RE,
479
+ broadcastAlephMessage,
480
+ broadcastInstanceMessage,
481
+ createAlephBrowserClient,
402
482
  fetchBalance,
403
483
  fetchCrns,
404
484
  fetchInstancePricing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/browser",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "Shared browser-safe Aleph deployment and polling helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",