@le-space/browser 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +30 -1
  2. package/index.js +96 -1
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@ interface BrowserPackagePlan {
5
5
  }
6
6
  declare const BROWSER_PACKAGE_PLAN: BrowserPackagePlan;
7
7
  type MessageStatus = 'processed' | 'pending' | 'rejected' | 'unknown';
8
+ type ReferenceStatus = MessageStatus | 'missing';
8
9
  interface BalanceResponse {
9
10
  address: string;
10
11
  balance: string;
@@ -115,6 +116,31 @@ interface InstanceMessage {
115
116
  confirmed?: boolean;
116
117
  status?: string;
117
118
  }
119
+ interface AlephMessageEnvelope {
120
+ status?: unknown;
121
+ type?: unknown;
122
+ error_code?: unknown;
123
+ details?: unknown;
124
+ message?: {
125
+ type?: unknown;
126
+ } | null;
127
+ messages?: Array<{
128
+ type?: unknown;
129
+ }> | null;
130
+ [key: string]: unknown;
131
+ }
132
+ interface MessageReference {
133
+ itemHash: string;
134
+ status: ReferenceStatus;
135
+ type: string | null;
136
+ }
137
+ interface DeploymentInspectionResult {
138
+ status: MessageStatus;
139
+ errorCode: number | null;
140
+ details: Record<string, unknown> | null;
141
+ rejectionReason: string | null;
142
+ references: MessageReference[];
143
+ }
118
144
  interface RootfsRequiredPortForward {
119
145
  port: number;
120
146
  tcp?: boolean;
@@ -161,6 +187,9 @@ declare function normalizeMessageStatus(status: unknown): MessageStatus;
161
187
  declare function fetchBalance(address: string, apiHost?: string): Promise<BalanceResponse>;
162
188
  declare function fetchCrns(url?: string): Promise<Crn[]>;
163
189
  declare function fetchInstances(address: string, apiHost?: string): Promise<InstanceMessage[]>;
190
+ declare function fetchMessageEnvelope(itemHash: string, apiHost?: string): Promise<AlephMessageEnvelope | null>;
191
+ declare function inspectDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string): Promise<DeploymentInspectionResult>;
192
+ declare function waitForDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string, attempts?: number, delayMs?: number): Promise<DeploymentInspectionResult>;
164
193
 
165
194
  declare const ITEM_HASH_RE: RegExp;
166
195
  declare const DEFAULT_ROOTFS_MANIFEST_URL = "./rootfs-manifest.json";
@@ -177,4 +206,4 @@ declare const DEFAULT_ALEPH_AGGREGATE_ADDRESS = "0xFba561a84A537fCaa567bb7A2257e
177
206
  declare function parseInstancePricing(payload: unknown): InstancePricing;
178
207
  declare function fetchInstancePricing(apiHost?: string, aggregateAddress?: string): Promise<PricingState>;
179
208
 
180
- export { 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 GatewayProbeStatus, ITEM_HASH_RE, type InstanceMessage, type InstancePricing, type LoadRootfsManifestOptions, type MessageStatus, type PaymentMode, type Price, type PricingState, type RootfsManifest, type RootfsManifestState, type RootfsRequiredPortForward, type RootfsResolution, type Tier, fetchBalance, fetchCrns, fetchInstancePricing, fetchInstances, fetchWithTimeout, loadRootfsManifest, normalizeMessageStatus, parseInstancePricing, resolveRootfsReference, validateRootfsManifest, verifyRootfsExists };
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 };
package/index.js CHANGED
@@ -89,6 +89,98 @@ async function fetchInstances(address, apiHost = DEFAULT_ALEPH_API_HOST) {
89
89
  status: typeof message.status === "string" && message.status.trim() ? message.status : message.confirmed ? "processed" : message.status
90
90
  }));
91
91
  }
92
+ function messageTypeFromEnvelope(payload) {
93
+ if (!payload) return null;
94
+ const type = payload.type ?? payload.message?.type ?? (Array.isArray(payload.messages) ? payload.messages[0]?.type : void 0);
95
+ return typeof type === "string" ? type.toUpperCase() : null;
96
+ }
97
+ function extractReferenceHashes(details) {
98
+ if (!details || typeof details !== "object" || !("errors" in details)) return [];
99
+ const errors = details.errors;
100
+ if (!Array.isArray(errors)) return [];
101
+ return errors.filter((value) => typeof value === "string");
102
+ }
103
+ function describeRejectedDeployment(payload, references, rootfsRef) {
104
+ const errorCode = typeof payload.error_code === "number" ? payload.error_code : null;
105
+ const pendingReferences = references.filter((reference) => reference.status === "pending");
106
+ const missingReferences = references.filter((reference) => reference.status === "missing");
107
+ const rootfsReference = references.find((reference) => reference.itemHash === rootfsRef);
108
+ if (rootfsReference?.status === "pending") {
109
+ return `Aleph rejected this deployment because the referenced rootfs STORE message ${rootfsReference.itemHash} is still pending and cannot yet be used by an instance. Wait for that STORE message to process, then deploy again.`;
110
+ }
111
+ if (pendingReferences.length > 0) {
112
+ return `Aleph rejected this deployment because referenced message(s) are still pending: ${pendingReferences.map((reference) => reference.itemHash).join(", ")}.`;
113
+ }
114
+ if (missingReferences.length > 0) {
115
+ return `Aleph rejected this deployment because referenced message(s) were not found on Aleph: ${missingReferences.map((reference) => reference.itemHash).join(", ")}.`;
116
+ }
117
+ const referencedHashes = extractReferenceHashes(payload.details);
118
+ if (referencedHashes.length > 0) {
119
+ return `Aleph rejected this deployment${errorCode ? ` (error ${errorCode})` : ""}. Referenced message(s): ${referencedHashes.join(", ")}.`;
120
+ }
121
+ return `Aleph rejected this deployment${errorCode ? ` (error ${errorCode})` : ""}.`;
122
+ }
123
+ async function fetchMessageEnvelope(itemHash, apiHost = DEFAULT_ALEPH_API_HOST) {
124
+ const response = await fetchWithTimeout(`${apiHost}/api/v0/messages/${itemHash}`, {
125
+ cache: "no-cache"
126
+ });
127
+ if (response.status === 404) return null;
128
+ if (!response.ok) throw new Error(`Message lookup failed: ${response.status}`);
129
+ return await response.json();
130
+ }
131
+ async function fetchReference(itemHash, apiHost) {
132
+ const payload = await fetchMessageEnvelope(itemHash, apiHost);
133
+ if (!payload) {
134
+ return {
135
+ itemHash,
136
+ status: "missing",
137
+ type: null
138
+ };
139
+ }
140
+ return {
141
+ itemHash,
142
+ status: normalizeMessageStatus(payload.status),
143
+ type: messageTypeFromEnvelope(payload)
144
+ };
145
+ }
146
+ async function inspectDeploymentResult(itemHash, rootfsRef, apiHost = DEFAULT_ALEPH_API_HOST) {
147
+ const payload = await fetchMessageEnvelope(itemHash, apiHost);
148
+ if (!payload) {
149
+ return {
150
+ status: "unknown",
151
+ errorCode: null,
152
+ details: null,
153
+ rejectionReason: `Deployment message ${itemHash} was not found on Aleph.`,
154
+ references: []
155
+ };
156
+ }
157
+ const relatedHashes = new Set(rootfsRef ? [rootfsRef] : []);
158
+ for (const referenceHash of extractReferenceHashes(payload.details)) {
159
+ relatedHashes.add(referenceHash);
160
+ }
161
+ const references = await Promise.all(Array.from(relatedHashes).map((hash) => fetchReference(hash, apiHost)));
162
+ const status = normalizeMessageStatus(payload.status);
163
+ const errorCode = typeof payload.error_code === "number" ? payload.error_code : null;
164
+ const details = payload.details && typeof payload.details === "object" ? payload.details : null;
165
+ return {
166
+ status,
167
+ errorCode,
168
+ details,
169
+ rejectionReason: status === "rejected" ? describeRejectedDeployment(payload, references, rootfsRef) : null,
170
+ references
171
+ };
172
+ }
173
+ async function waitForDeploymentResult(itemHash, rootfsRef, apiHost = DEFAULT_ALEPH_API_HOST, attempts = 15, delayMs = 2e3) {
174
+ let lastResult = await inspectDeploymentResult(itemHash, rootfsRef, apiHost);
175
+ for (let attempt = 1; attempt < attempts; attempt += 1) {
176
+ if (lastResult.status === "processed" || lastResult.status === "rejected") {
177
+ return lastResult;
178
+ }
179
+ await new Promise((resolve) => globalThis.setTimeout(resolve, delayMs));
180
+ lastResult = await inspectDeploymentResult(itemHash, rootfsRef, apiHost);
181
+ }
182
+ return lastResult;
183
+ }
92
184
 
93
185
  // src/rootfs.ts
94
186
  var ITEM_HASH_RE = /^[a-fA-F0-9]{64}$/u;
@@ -311,11 +403,14 @@ export {
311
403
  fetchCrns,
312
404
  fetchInstancePricing,
313
405
  fetchInstances,
406
+ fetchMessageEnvelope,
314
407
  fetchWithTimeout,
408
+ inspectDeploymentResult,
315
409
  loadRootfsManifest,
316
410
  normalizeMessageStatus,
317
411
  parseInstancePricing,
318
412
  resolveRootfsReference,
319
413
  validateRootfsManifest,
320
- verifyRootfsExists
414
+ verifyRootfsExists,
415
+ waitForDeploymentResult
321
416
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/browser",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Shared browser-safe Aleph deployment and polling helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",