@le-space/browser 0.1.26 → 0.1.28
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 +21 -0
- package/index.d.ts +67 -1
- package/index.js +116 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,26 @@ 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
|
+
- scheduler allocation lookup
|
|
29
|
+
- deployment result inspection and polling
|
|
30
|
+
- Aleph message broadcast helpers
|
|
31
|
+
|
|
32
|
+
Lower-level helper functions remain exported too, but new extractions should
|
|
33
|
+
prefer hanging reusable behavior off the client surface unless there is a good
|
|
34
|
+
reason to keep them as standalone utilities.
|
|
35
|
+
|
|
16
36
|
## Planned v1 Scope
|
|
17
37
|
|
|
18
38
|
The first real extraction wave should cover:
|
|
@@ -23,6 +43,7 @@ The first real extraction wave should cover:
|
|
|
23
43
|
- balance fetch
|
|
24
44
|
- CRN fetch
|
|
25
45
|
- instance listing
|
|
46
|
+
- typed browser client factory
|
|
26
47
|
- Aleph message broadcast helpers
|
|
27
48
|
- deployment polling and result inspection
|
|
28
49
|
- 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,60 @@ interface DeploymentInspectionResult {
|
|
|
141
143
|
rejectionReason: string | null;
|
|
142
144
|
references: MessageReference[];
|
|
143
145
|
}
|
|
146
|
+
interface InstanceAllocationNode {
|
|
147
|
+
node_id?: string;
|
|
148
|
+
url?: string;
|
|
149
|
+
ipv6?: string | null;
|
|
150
|
+
supports_ipv6?: boolean;
|
|
151
|
+
}
|
|
152
|
+
interface InstanceAllocationPeriod {
|
|
153
|
+
start_timestamp?: string;
|
|
154
|
+
duration_seconds?: number;
|
|
155
|
+
}
|
|
156
|
+
interface InstanceAllocation {
|
|
157
|
+
source: 'scheduler' | 'manual';
|
|
158
|
+
crnHash?: string | null;
|
|
159
|
+
crnUrl?: string | null;
|
|
160
|
+
node?: InstanceAllocationNode | null;
|
|
161
|
+
vmIpv6?: string | null;
|
|
162
|
+
period?: InstanceAllocationPeriod | null;
|
|
163
|
+
}
|
|
164
|
+
interface AlephBroadcastMessage {
|
|
165
|
+
sender: string;
|
|
166
|
+
chain: AlephSenderChain;
|
|
167
|
+
signature: string;
|
|
168
|
+
type: AlephMessageType;
|
|
169
|
+
item_hash: string;
|
|
170
|
+
item_type: 'inline';
|
|
171
|
+
item_content: string;
|
|
172
|
+
time: number;
|
|
173
|
+
channel: string;
|
|
174
|
+
}
|
|
175
|
+
interface AlephBroadcastResponse {
|
|
176
|
+
publication_status?: {
|
|
177
|
+
status: string;
|
|
178
|
+
failed?: unknown[];
|
|
179
|
+
};
|
|
180
|
+
message_status?: MessageStatus;
|
|
181
|
+
[key: string]: unknown;
|
|
182
|
+
}
|
|
183
|
+
interface BroadcastResult {
|
|
184
|
+
response: AlephBroadcastResponse;
|
|
185
|
+
httpStatus: number;
|
|
186
|
+
}
|
|
187
|
+
interface AlephBrowserClient {
|
|
188
|
+
apiHost: string;
|
|
189
|
+
crnListUrl: string;
|
|
190
|
+
fetchBalance(address: string): Promise<BalanceResponse>;
|
|
191
|
+
fetchCrns(): Promise<Crn[]>;
|
|
192
|
+
fetchInstances(address: string): Promise<InstanceMessage[]>;
|
|
193
|
+
fetchMessageEnvelope(itemHash: string): Promise<AlephMessageEnvelope | null>;
|
|
194
|
+
fetchSchedulerAllocation(itemHash: string): Promise<InstanceAllocation | null>;
|
|
195
|
+
inspectDeploymentResult(itemHash: string, rootfsRef?: string): Promise<DeploymentInspectionResult>;
|
|
196
|
+
waitForDeploymentResult(itemHash: string, rootfsRef?: string, attempts?: number, delayMs?: number): Promise<DeploymentInspectionResult>;
|
|
197
|
+
broadcastInstanceMessage(message: AlephBroadcastMessage, sync?: boolean): Promise<BroadcastResult>;
|
|
198
|
+
broadcastAlephMessage(message: AlephBroadcastMessage, sync?: boolean): Promise<BroadcastResult>;
|
|
199
|
+
}
|
|
144
200
|
interface RootfsRequiredPortForward {
|
|
145
201
|
port: number;
|
|
146
202
|
tcp?: boolean;
|
|
@@ -183,13 +239,23 @@ declare function fetchWithTimeout(input: RequestInfo | URL, init?: RequestInit,
|
|
|
183
239
|
|
|
184
240
|
declare const DEFAULT_ALEPH_API_HOST = "https://api2.aleph.im";
|
|
185
241
|
declare const DEFAULT_CRN_LIST_URL = "https://crns-list.aleph.sh/crns.json";
|
|
242
|
+
declare const DEFAULT_ALEPH_SCHEDULER_API_HOST = "https://scheduler.api.aleph.cloud";
|
|
186
243
|
declare function normalizeMessageStatus(status: unknown): MessageStatus;
|
|
187
244
|
declare function fetchBalance(address: string, apiHost?: string): Promise<BalanceResponse>;
|
|
188
245
|
declare function fetchCrns(url?: string): Promise<Crn[]>;
|
|
189
246
|
declare function fetchInstances(address: string, apiHost?: string): Promise<InstanceMessage[]>;
|
|
247
|
+
declare function fetchSchedulerAllocation(itemHash: string, schedulerApiHost?: string): Promise<InstanceAllocation | null>;
|
|
190
248
|
declare function fetchMessageEnvelope(itemHash: string, apiHost?: string): Promise<AlephMessageEnvelope | null>;
|
|
191
249
|
declare function inspectDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string): Promise<DeploymentInspectionResult>;
|
|
192
250
|
declare function waitForDeploymentResult(itemHash: string, rootfsRef?: string, apiHost?: string, attempts?: number, delayMs?: number): Promise<DeploymentInspectionResult>;
|
|
251
|
+
declare function broadcastInstanceMessage(message: AlephBroadcastMessage, apiHost?: string, sync?: boolean): Promise<BroadcastResult>;
|
|
252
|
+
declare function broadcastAlephMessage(message: AlephBroadcastMessage, apiHost?: string, sync?: boolean): Promise<BroadcastResult>;
|
|
253
|
+
|
|
254
|
+
interface CreateAlephBrowserClientOptions {
|
|
255
|
+
apiHost?: string;
|
|
256
|
+
crnListUrl?: string;
|
|
257
|
+
}
|
|
258
|
+
declare function createAlephBrowserClient(options?: CreateAlephBrowserClientOptions): AlephBrowserClient;
|
|
193
259
|
|
|
194
260
|
declare const ITEM_HASH_RE: RegExp;
|
|
195
261
|
declare const DEFAULT_ROOTFS_MANIFEST_URL = "./rootfs-manifest.json";
|
|
@@ -206,4 +272,4 @@ declare const DEFAULT_ALEPH_AGGREGATE_ADDRESS = "0xFba561a84A537fCaa567bb7A2257e
|
|
|
206
272
|
declare function parseInstancePricing(payload: unknown): InstancePricing;
|
|
207
273
|
declare function fetchInstancePricing(apiHost?: string, aggregateAddress?: string): Promise<PricingState>;
|
|
208
274
|
|
|
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 };
|
|
275
|
+
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_ALEPH_SCHEDULER_API_HOST, DEFAULT_CRN_LIST_URL, DEFAULT_IPFS_GATEWAY_BASE_URL, DEFAULT_ROOTFS_MANIFEST_URL, type DeploymentInspectionResult, type GatewayProbeStatus, ITEM_HASH_RE, type InstanceAllocation, type InstanceAllocationNode, type InstanceAllocationPeriod, 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, fetchSchedulerAllocation, fetchWithTimeout, inspectDeploymentResult, loadRootfsManifest, normalizeMessageStatus, parseInstancePricing, resolveRootfsReference, validateRootfsManifest, verifyRootfsExists, waitForDeploymentResult };
|
package/index.js
CHANGED
|
@@ -50,6 +50,7 @@ async function fetchWithTimeout(input, init = {}, timeoutMs = 15e3) {
|
|
|
50
50
|
// src/aleph-api.ts
|
|
51
51
|
var DEFAULT_ALEPH_API_HOST = "https://api2.aleph.im";
|
|
52
52
|
var DEFAULT_CRN_LIST_URL = "https://crns-list.aleph.sh/crns.json";
|
|
53
|
+
var DEFAULT_ALEPH_SCHEDULER_API_HOST = "https://scheduler.api.aleph.cloud";
|
|
53
54
|
function normalizeMessageStatus(status) {
|
|
54
55
|
if (typeof status !== "string") return "unknown";
|
|
55
56
|
const normalized = status.toLowerCase();
|
|
@@ -58,6 +59,12 @@ function normalizeMessageStatus(status) {
|
|
|
58
59
|
}
|
|
59
60
|
return "unknown";
|
|
60
61
|
}
|
|
62
|
+
function asString(value) {
|
|
63
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
64
|
+
}
|
|
65
|
+
function asNumber(value) {
|
|
66
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
67
|
+
}
|
|
61
68
|
async function fetchBalance(address, apiHost = DEFAULT_ALEPH_API_HOST) {
|
|
62
69
|
const response = await fetchWithTimeout(`${apiHost}/api/v0/addresses/${address}/balance`, {
|
|
63
70
|
cache: "no-cache"
|
|
@@ -89,6 +96,30 @@ async function fetchInstances(address, apiHost = DEFAULT_ALEPH_API_HOST) {
|
|
|
89
96
|
status: typeof message.status === "string" && message.status.trim() ? message.status : message.confirmed ? "processed" : message.status
|
|
90
97
|
}));
|
|
91
98
|
}
|
|
99
|
+
async function fetchSchedulerAllocation(itemHash, schedulerApiHost = DEFAULT_ALEPH_SCHEDULER_API_HOST) {
|
|
100
|
+
const response = await fetchWithTimeout(`${schedulerApiHost}/api/v0/allocation/${itemHash}`, {
|
|
101
|
+
cache: "no-cache"
|
|
102
|
+
});
|
|
103
|
+
if (response.status === 404) return null;
|
|
104
|
+
if (!response.ok) throw new Error(`Scheduler allocation request failed: ${response.status}`);
|
|
105
|
+
const payload = await response.json();
|
|
106
|
+
const node = payload.node;
|
|
107
|
+
return {
|
|
108
|
+
source: "scheduler",
|
|
109
|
+
crnUrl: asString(node?.url),
|
|
110
|
+
node: node ? {
|
|
111
|
+
node_id: asString(node.node_id) ?? void 0,
|
|
112
|
+
url: asString(node.url) ?? void 0,
|
|
113
|
+
ipv6: asString(node.ipv6),
|
|
114
|
+
supports_ipv6: typeof node.supports_ipv6 === "boolean" ? node.supports_ipv6 : void 0
|
|
115
|
+
} : null,
|
|
116
|
+
vmIpv6: asString(payload.vm_ipv6),
|
|
117
|
+
period: payload.period ? {
|
|
118
|
+
start_timestamp: asString(payload.period.start_timestamp) ?? void 0,
|
|
119
|
+
duration_seconds: asNumber(payload.period.duration_seconds) ?? void 0
|
|
120
|
+
} : null
|
|
121
|
+
};
|
|
122
|
+
}
|
|
92
123
|
function messageTypeFromEnvelope(payload) {
|
|
93
124
|
if (!payload) return null;
|
|
94
125
|
const type = payload.type ?? payload.message?.type ?? (Array.isArray(payload.messages) ? payload.messages[0]?.type : void 0);
|
|
@@ -181,6 +212,86 @@ async function waitForDeploymentResult(itemHash, rootfsRef, apiHost = DEFAULT_AL
|
|
|
181
212
|
}
|
|
182
213
|
return lastResult;
|
|
183
214
|
}
|
|
215
|
+
function isInvalidMessageFormatResponse(response, payload) {
|
|
216
|
+
if (response.status !== 422) return false;
|
|
217
|
+
const details = payload.details;
|
|
218
|
+
if (typeof details === "string" && details.includes("InvalidMessageFormat")) return true;
|
|
219
|
+
if (details && typeof details === "object") {
|
|
220
|
+
const detailMessage = details.message;
|
|
221
|
+
if (typeof detailMessage === "string" && detailMessage.includes("InvalidMessageFormat")) return true;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
async function postBroadcastPayload(body, apiHost) {
|
|
226
|
+
const rawResponse = await fetchWithTimeout(`${apiHost}/api/v0/messages`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: { "content-type": "application/json" },
|
|
229
|
+
body: JSON.stringify(body)
|
|
230
|
+
});
|
|
231
|
+
const response = await rawResponse.json().catch(() => ({}));
|
|
232
|
+
return {
|
|
233
|
+
response,
|
|
234
|
+
httpStatus: rawResponse.status,
|
|
235
|
+
rawResponse
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function broadcastInstanceMessage(message, apiHost = DEFAULT_ALEPH_API_HOST, sync = false) {
|
|
239
|
+
const attempts = [{ sync, message }, { ...message, sync }, { ...message }];
|
|
240
|
+
for (let index = 0; index < attempts.length; index += 1) {
|
|
241
|
+
const result = await postBroadcastPayload(attempts[index], apiHost);
|
|
242
|
+
if (result.rawResponse.ok || result.httpStatus === 202) {
|
|
243
|
+
return {
|
|
244
|
+
response: result.response,
|
|
245
|
+
httpStatus: result.httpStatus
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const canRetry = index < attempts.length - 1 && isInvalidMessageFormatResponse(result.rawResponse, result.response);
|
|
249
|
+
if (!canRetry) {
|
|
250
|
+
throw new Error(`Broadcast failed: ${result.httpStatus} ${JSON.stringify(result.response)}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
throw new Error("Broadcast failed: no compatible request format was accepted");
|
|
254
|
+
}
|
|
255
|
+
async function broadcastAlephMessage(message, apiHost = DEFAULT_ALEPH_API_HOST, sync = false) {
|
|
256
|
+
return broadcastInstanceMessage(message, apiHost, sync);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/client.ts
|
|
260
|
+
function createAlephBrowserClient(options = {}) {
|
|
261
|
+
const apiHost = options.apiHost ?? DEFAULT_ALEPH_API_HOST;
|
|
262
|
+
const crnListUrl = options.crnListUrl ?? DEFAULT_CRN_LIST_URL;
|
|
263
|
+
return {
|
|
264
|
+
apiHost,
|
|
265
|
+
crnListUrl,
|
|
266
|
+
fetchBalance(address) {
|
|
267
|
+
return fetchBalance(address, apiHost);
|
|
268
|
+
},
|
|
269
|
+
fetchCrns() {
|
|
270
|
+
return fetchCrns(crnListUrl);
|
|
271
|
+
},
|
|
272
|
+
fetchInstances(address) {
|
|
273
|
+
return fetchInstances(address, apiHost);
|
|
274
|
+
},
|
|
275
|
+
fetchMessageEnvelope(itemHash) {
|
|
276
|
+
return fetchMessageEnvelope(itemHash, apiHost);
|
|
277
|
+
},
|
|
278
|
+
fetchSchedulerAllocation(itemHash) {
|
|
279
|
+
return fetchSchedulerAllocation(itemHash);
|
|
280
|
+
},
|
|
281
|
+
inspectDeploymentResult(itemHash, rootfsRef) {
|
|
282
|
+
return inspectDeploymentResult(itemHash, rootfsRef, apiHost);
|
|
283
|
+
},
|
|
284
|
+
waitForDeploymentResult(itemHash, rootfsRef, attempts, delayMs) {
|
|
285
|
+
return waitForDeploymentResult(itemHash, rootfsRef, apiHost, attempts, delayMs);
|
|
286
|
+
},
|
|
287
|
+
broadcastInstanceMessage(message, sync) {
|
|
288
|
+
return broadcastInstanceMessage(message, apiHost, sync);
|
|
289
|
+
},
|
|
290
|
+
broadcastAlephMessage(message, sync) {
|
|
291
|
+
return broadcastAlephMessage(message, apiHost, sync);
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
184
295
|
|
|
185
296
|
// src/rootfs.ts
|
|
186
297
|
var ITEM_HASH_RE = /^[a-fA-F0-9]{64}$/u;
|
|
@@ -395,15 +506,20 @@ export {
|
|
|
395
506
|
BROWSER_PACKAGE_PLAN,
|
|
396
507
|
DEFAULT_ALEPH_AGGREGATE_ADDRESS,
|
|
397
508
|
DEFAULT_ALEPH_API_HOST,
|
|
509
|
+
DEFAULT_ALEPH_SCHEDULER_API_HOST,
|
|
398
510
|
DEFAULT_CRN_LIST_URL,
|
|
399
511
|
DEFAULT_IPFS_GATEWAY_BASE_URL,
|
|
400
512
|
DEFAULT_ROOTFS_MANIFEST_URL,
|
|
401
513
|
ITEM_HASH_RE,
|
|
514
|
+
broadcastAlephMessage,
|
|
515
|
+
broadcastInstanceMessage,
|
|
516
|
+
createAlephBrowserClient,
|
|
402
517
|
fetchBalance,
|
|
403
518
|
fetchCrns,
|
|
404
519
|
fetchInstancePricing,
|
|
405
520
|
fetchInstances,
|
|
406
521
|
fetchMessageEnvelope,
|
|
522
|
+
fetchSchedulerAllocation,
|
|
407
523
|
fetchWithTimeout,
|
|
408
524
|
inspectDeploymentResult,
|
|
409
525
|
loadRootfsManifest,
|