@le-space/browser 0.1.24 → 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.
- package/index.d.ts +62 -2
- package/index.js +126 -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;
|
|
@@ -12,6 +13,35 @@ interface BalanceResponse {
|
|
|
12
13
|
details?: Record<string, string>;
|
|
13
14
|
credit_balance: number;
|
|
14
15
|
}
|
|
16
|
+
interface Price {
|
|
17
|
+
payg?: string | number | null;
|
|
18
|
+
holding?: string | number | null;
|
|
19
|
+
fixed?: string | number | null;
|
|
20
|
+
credit?: string | number | null;
|
|
21
|
+
}
|
|
22
|
+
interface ComputeUnit {
|
|
23
|
+
vcpus: number;
|
|
24
|
+
memory_mib: number;
|
|
25
|
+
disk_mib: number;
|
|
26
|
+
}
|
|
27
|
+
interface Tier {
|
|
28
|
+
id: string;
|
|
29
|
+
compute_units: number;
|
|
30
|
+
vram?: number | null;
|
|
31
|
+
model?: string | null;
|
|
32
|
+
}
|
|
33
|
+
interface InstancePricing {
|
|
34
|
+
price: {
|
|
35
|
+
storage?: Price;
|
|
36
|
+
compute_unit?: Price;
|
|
37
|
+
};
|
|
38
|
+
compute_unit: ComputeUnit;
|
|
39
|
+
tiers: Tier[];
|
|
40
|
+
}
|
|
41
|
+
interface PricingState {
|
|
42
|
+
pricing: InstancePricing | null;
|
|
43
|
+
fetchedAt: number | null;
|
|
44
|
+
}
|
|
15
45
|
interface CrnUsage {
|
|
16
46
|
cpu?: {
|
|
17
47
|
count?: number;
|
|
@@ -86,6 +116,31 @@ interface InstanceMessage {
|
|
|
86
116
|
confirmed?: boolean;
|
|
87
117
|
status?: string;
|
|
88
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
|
+
}
|
|
89
144
|
interface RootfsRequiredPortForward {
|
|
90
145
|
port: number;
|
|
91
146
|
tcp?: boolean;
|
|
@@ -132,6 +187,9 @@ declare function normalizeMessageStatus(status: unknown): MessageStatus;
|
|
|
132
187
|
declare function fetchBalance(address: string, apiHost?: string): Promise<BalanceResponse>;
|
|
133
188
|
declare function fetchCrns(url?: string): Promise<Crn[]>;
|
|
134
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>;
|
|
135
193
|
|
|
136
194
|
declare const ITEM_HASH_RE: RegExp;
|
|
137
195
|
declare const DEFAULT_ROOTFS_MANIFEST_URL = "./rootfs-manifest.json";
|
|
@@ -144,6 +202,8 @@ declare function loadRootfsManifest(url?: string | URL, options?: LoadRootfsMani
|
|
|
144
202
|
declare function verifyRootfsExists(itemHash: string, apiHost?: string): Promise<boolean>;
|
|
145
203
|
declare function resolveRootfsReference(itemHash: string, apiHost?: string, gatewayBaseUrl?: string): Promise<RootfsResolution | null>;
|
|
146
204
|
|
|
147
|
-
declare const
|
|
205
|
+
declare const DEFAULT_ALEPH_AGGREGATE_ADDRESS = "0xFba561a84A537fCaa567bb7A2257e7142701ae2A";
|
|
206
|
+
declare function parseInstancePricing(payload: unknown): InstancePricing;
|
|
207
|
+
declare function fetchInstancePricing(apiHost?: string, aggregateAddress?: string): Promise<PricingState>;
|
|
148
208
|
|
|
149
|
-
export {
|
|
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;
|
|
@@ -272,10 +364,36 @@ async function resolveRootfsReference(itemHash, apiHost = DEFAULT_ALEPH_API_HOST
|
|
|
272
364
|
}
|
|
273
365
|
|
|
274
366
|
// src/pricing.ts
|
|
275
|
-
var
|
|
367
|
+
var DEFAULT_ALEPH_AGGREGATE_ADDRESS = "0xFba561a84A537fCaa567bb7A2257e7142701ae2A";
|
|
368
|
+
function parseInstancePricing(payload) {
|
|
369
|
+
const data = payload;
|
|
370
|
+
const pricing = data.data?.pricing ?? data.pricing;
|
|
371
|
+
const instance = pricing?.instance;
|
|
372
|
+
if (!instance?.price?.compute_unit || !instance.compute_unit || !Array.isArray(instance.tiers)) {
|
|
373
|
+
throw new Error("Aleph pricing aggregate does not contain instance pricing.");
|
|
374
|
+
}
|
|
375
|
+
return instance;
|
|
376
|
+
}
|
|
377
|
+
async function fetchInstancePricing(apiHost = DEFAULT_ALEPH_API_HOST, aggregateAddress = DEFAULT_ALEPH_AGGREGATE_ADDRESS) {
|
|
378
|
+
const response = await fetchWithTimeout(`${apiHost}/api/v0/aggregates/${aggregateAddress}.json?keys=pricing`, {
|
|
379
|
+
cache: "no-cache"
|
|
380
|
+
});
|
|
381
|
+
if (!response.ok) {
|
|
382
|
+
throw new Error(`Pricing aggregate request failed: ${response.status}`);
|
|
383
|
+
}
|
|
384
|
+
const payload = await response.json();
|
|
385
|
+
const pricingAggregate = payload.data?.pricing;
|
|
386
|
+
if (!pricingAggregate) {
|
|
387
|
+
throw new Error("Pricing aggregate response did not include a pricing key.");
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
pricing: parseInstancePricing({ pricing: pricingAggregate }),
|
|
391
|
+
fetchedAt: Date.now()
|
|
392
|
+
};
|
|
393
|
+
}
|
|
276
394
|
export {
|
|
277
395
|
BROWSER_PACKAGE_PLAN,
|
|
278
|
-
|
|
396
|
+
DEFAULT_ALEPH_AGGREGATE_ADDRESS,
|
|
279
397
|
DEFAULT_ALEPH_API_HOST,
|
|
280
398
|
DEFAULT_CRN_LIST_URL,
|
|
281
399
|
DEFAULT_IPFS_GATEWAY_BASE_URL,
|
|
@@ -283,11 +401,16 @@ export {
|
|
|
283
401
|
ITEM_HASH_RE,
|
|
284
402
|
fetchBalance,
|
|
285
403
|
fetchCrns,
|
|
404
|
+
fetchInstancePricing,
|
|
286
405
|
fetchInstances,
|
|
406
|
+
fetchMessageEnvelope,
|
|
287
407
|
fetchWithTimeout,
|
|
408
|
+
inspectDeploymentResult,
|
|
288
409
|
loadRootfsManifest,
|
|
289
410
|
normalizeMessageStatus,
|
|
411
|
+
parseInstancePricing,
|
|
290
412
|
resolveRootfsReference,
|
|
291
413
|
validateRootfsManifest,
|
|
292
|
-
verifyRootfsExists
|
|
414
|
+
verifyRootfsExists,
|
|
415
|
+
waitForDeploymentResult
|
|
293
416
|
};
|