@onesi/genesis 0.0.0-stage → 0.1.13
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 +28 -2
- package/dist/beacons.d.ts +103 -0
- package/dist/beacons.js +77 -0
- package/dist/blinds-owner-enrollment.d.ts +171 -0
- package/dist/blinds-owner-enrollment.js +150 -0
- package/dist/client.d.ts +19996 -0
- package/dist/client.js +277 -0
- package/dist/context.d.ts +50 -0
- package/dist/context.js +153 -0
- package/dist/dials.d.ts +299 -0
- package/dist/dials.js +100 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +66 -0
- package/dist/flares.d.ts +170 -0
- package/dist/flares.js +56 -0
- package/dist/fleets.d.ts +186 -0
- package/dist/fleets.js +136 -0
- package/dist/flocks.d.ts +155 -0
- package/dist/flocks.js +39 -0
- package/dist/foundry-billing.d.ts +97 -0
- package/dist/foundry-billing.js +107 -0
- package/dist/gears.d.ts +107 -0
- package/dist/gears.js +20 -0
- package/dist/generated/operations.d.ts +19949 -0
- package/dist/generated/operations.js +23846 -0
- package/dist/generated/schema.d.ts +35413 -0
- package/dist/generated/schema.js +1 -0
- package/dist/genesis.d.ts +2926 -0
- package/dist/genesis.js +206 -0
- package/dist/genesis.openapi.json +49321 -0
- package/dist/henry.d.ts +168 -0
- package/dist/henry.js +45 -0
- package/dist/hives-cells.d.ts +127 -0
- package/dist/hives-cells.js +151 -0
- package/dist/hives-environment.d.ts +28 -0
- package/dist/hives-environment.js +58 -0
- package/dist/hives-functions.d.ts +36 -0
- package/dist/hives-functions.js +185 -0
- package/dist/hives-marketplace.d.ts +128 -0
- package/dist/hives-marketplace.js +187 -0
- package/dist/hives-node.d.ts +9 -0
- package/dist/hives-node.js +152 -0
- package/dist/hives-operations.d.ts +337 -0
- package/dist/hives-operations.js +41 -0
- package/dist/hives-queue-consumer.d.ts +56 -0
- package/dist/hives-queue-consumer.js +95 -0
- package/dist/hives-queue-funding.d.ts +28 -0
- package/dist/hives-queue-funding.js +81 -0
- package/dist/hives-queues.d.ts +137 -0
- package/dist/hives-queues.js +143 -0
- package/dist/hives-resources-transport.d.ts +23 -0
- package/dist/hives-resources-transport.js +204 -0
- package/dist/hives-response.d.ts +2 -0
- package/dist/hives-response.js +32 -0
- package/dist/hives-runtimes.d.ts +34 -0
- package/dist/hives-runtimes.js +32 -0
- package/dist/hives-topics.d.ts +51 -0
- package/dist/hives-topics.js +79 -0
- package/dist/hives-upload.d.ts +9 -0
- package/dist/hives-upload.js +23 -0
- package/dist/hives-volumes.d.ts +64 -0
- package/dist/hives-volumes.js +117 -0
- package/dist/idempotency.d.ts +4 -0
- package/dist/idempotency.js +17 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +25 -0
- package/dist/instances.d.ts +25 -0
- package/dist/instances.js +11 -0
- package/dist/lanes.d.ts +267 -0
- package/dist/lanes.js +41 -0
- package/dist/minds-agent.d.ts +60 -0
- package/dist/minds-agent.js +190 -0
- package/dist/minds-owner.d.ts +110 -0
- package/dist/minds-owner.js +226 -0
- package/dist/minds.d.ts +269 -0
- package/dist/minds.js +193 -0
- package/dist/relays-enrollment.d.ts +33 -0
- package/dist/relays-enrollment.js +115 -0
- package/dist/runner-grant.d.ts +10 -0
- package/dist/runner-grant.js +133 -0
- package/dist/runner.d.ts +68 -0
- package/dist/runner.js +116 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.js +1 -0
- package/dist/urls.d.ts +6 -0
- package/dist/urls.js +31 -0
- package/package.json +46 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
2
|
+
/** The service separately bounds its serialized guest stdin envelope at 10 MiB. */
|
|
3
|
+
export const MAX_FUNCTION_BODY_BYTES = 10 * 1024 * 1024;
|
|
4
|
+
export const MAX_FUNCTION_JSON_BYTES = 16 * 1024 * 1024;
|
|
5
|
+
const executionId = /^exec_[a-f0-9]{32}$/;
|
|
6
|
+
export function hivesInvokeHeaders(options) {
|
|
7
|
+
if (options.idempotencyKey === undefined)
|
|
8
|
+
return {};
|
|
9
|
+
if (!/^[A-Za-z0-9_.:@-]{1,128}$/.test(options.idempotencyKey))
|
|
10
|
+
throw new Error('Use one persisted Hives idempotency key of 1–128 identifier characters.');
|
|
11
|
+
return { 'idempotency-key': options.idempotencyKey };
|
|
12
|
+
}
|
|
13
|
+
export function hivesOperationStatus(value, expected) {
|
|
14
|
+
const row = value;
|
|
15
|
+
const keys = ['operation_id', 'state', 'code', 'funding_source', 'reservation_id', 'retry_execution'];
|
|
16
|
+
if (!executionId.test(expected) || !row || typeof row !== 'object' || Array.isArray(row)
|
|
17
|
+
|| Object.keys(row).length !== keys.length || keys.some(key => !Object.hasOwn(row, key))
|
|
18
|
+
|| row.operation_id !== expected || row.retry_execution !== false
|
|
19
|
+
|| !['prepared', 'reserve_unknown', 'reserved', 'dispatch_unknown', 'dispatch_authorized', 'dispatched', 'settle_unknown', 'settled', 'release_pending', 'released'].includes(row.state)
|
|
20
|
+
|| typeof row.code !== 'string' || !/^[a-z_]{1,80}$/.test(row.code)
|
|
21
|
+
|| row.funding_source !== null && row.funding_source !== 'sandbox_allowance' && row.funding_source !== 'stripe_test_metered'
|
|
22
|
+
|| row.reservation_id !== null && !/^gfr_[A-Za-z0-9_.:@-]{1,124}$/.test(row.reservation_id)) {
|
|
23
|
+
throw new Error('Hives returned invalid or mismatched operation status. Keep the original idempotency key.');
|
|
24
|
+
}
|
|
25
|
+
return row;
|
|
26
|
+
}
|
|
27
|
+
export class HivesOperationError extends GenesisError {
|
|
28
|
+
operation;
|
|
29
|
+
constructor(status, operation) {
|
|
30
|
+
super({ status, type: 'hives_operation_unresolved', message: 'Read the original Hives operation status. Do not invoke with a new idempotency key.', body: operation });
|
|
31
|
+
this.operation = operation;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Read only the small platform receipt; never retain reflected guest request data. */
|
|
35
|
+
export async function hivesOperationError(response) {
|
|
36
|
+
const expected = response.headers.get('x-hives-operation-id');
|
|
37
|
+
if (!expected || !executionId.test(expected) || !response.body)
|
|
38
|
+
return undefined;
|
|
39
|
+
const reader = response.body.getReader();
|
|
40
|
+
const chunks = [];
|
|
41
|
+
let size = 0;
|
|
42
|
+
try {
|
|
43
|
+
for (;;) {
|
|
44
|
+
const { value, done } = await reader.read();
|
|
45
|
+
if (done)
|
|
46
|
+
break;
|
|
47
|
+
size += value.length;
|
|
48
|
+
if (size > 2048) {
|
|
49
|
+
await reader.cancel();
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
chunks.push(value);
|
|
53
|
+
}
|
|
54
|
+
const bytes = new Uint8Array(size);
|
|
55
|
+
let offset = 0;
|
|
56
|
+
for (const chunk of chunks) {
|
|
57
|
+
bytes.set(chunk, offset);
|
|
58
|
+
offset += chunk.length;
|
|
59
|
+
}
|
|
60
|
+
return new HivesOperationError(response.status, hivesOperationStatus(JSON.parse(new TextDecoder().decode(bytes)), expected));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
reader.releaseLock();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const reservedHeaders = new Set(['host', 'connection', 'content-length', 'transfer-encoding', 'keep-alive',
|
|
70
|
+
'proxy-authenticate', 'proxy-authorization', 'te', 'trailer', 'upgrade', 'set-cookie', 'clear-site-data',
|
|
71
|
+
'authorization', 'cookie', 'forwarded', 'origin', 'referer', 'x-org-id', 'x-workspace-id', 'x-project-id',
|
|
72
|
+
'x-admin-token', 'x-tenant-id', 'x-cold-start']);
|
|
73
|
+
const platformHeader = (name) => /^x-(?:auth|genesis|life|l1fe|hives|forwarded|execution)-/.test(name);
|
|
74
|
+
const bytes = (value) => new TextEncoder().encode(value).length;
|
|
75
|
+
const fail = () => { throw new Error('Invalid function request. Check method, path, headers, exclusive body encoding and size limits.'); };
|
|
76
|
+
export function validateHivesInvocation(input) {
|
|
77
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
78
|
+
fail();
|
|
79
|
+
const r = input;
|
|
80
|
+
if (Object.keys(r).some((k) => !['method', 'path', 'headers', 'body', 'body_base64'].includes(k)))
|
|
81
|
+
fail();
|
|
82
|
+
if (r.method !== undefined && (typeof r.method !== 'string' || bytes(r.method) > 64 || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(r.method)))
|
|
83
|
+
fail();
|
|
84
|
+
if (r.path !== undefined && (typeof r.path !== 'string' || !r.path.startsWith('/') || r.path.startsWith('//') || r.path.includes('#') || bytes(r.path) > 8192 || /[\s\u0000-\u001f\u007f]/.test(r.path)))
|
|
85
|
+
fail();
|
|
86
|
+
let headerBytes = 0;
|
|
87
|
+
if (r.headers !== undefined) {
|
|
88
|
+
if (!r.headers || typeof r.headers !== 'object' || Array.isArray(r.headers) || Object.keys(r.headers).length > 128)
|
|
89
|
+
fail();
|
|
90
|
+
for (const [key, value] of Object.entries(r.headers)) {
|
|
91
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(key) || typeof value !== 'string' || /[\u0000-\u0008\u000a-\u001f\u007f]/.test(value)
|
|
92
|
+
|| reservedHeaders.has(key.toLowerCase()) || platformHeader(key.toLowerCase()))
|
|
93
|
+
fail();
|
|
94
|
+
headerBytes += bytes(key) + bytes(value);
|
|
95
|
+
if (headerBytes > 64 * 1024)
|
|
96
|
+
fail();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let size = 0;
|
|
100
|
+
if (r.body_base64 !== undefined) {
|
|
101
|
+
const value = r.body_base64;
|
|
102
|
+
if (r.body != null || typeof value !== 'string' || value.length > Math.ceil(MAX_FUNCTION_BODY_BYTES / 3) * 4
|
|
103
|
+
|| value.length % 4 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
104
|
+
fail();
|
|
105
|
+
try {
|
|
106
|
+
const bytes = atob(value);
|
|
107
|
+
if (btoa(bytes) !== value)
|
|
108
|
+
fail();
|
|
109
|
+
size = bytes.length;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
fail();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (r.body != null) {
|
|
116
|
+
let value;
|
|
117
|
+
try {
|
|
118
|
+
value = typeof r.body === 'string' ? r.body : JSON.stringify(r.body);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
fail();
|
|
122
|
+
}
|
|
123
|
+
if (value === undefined)
|
|
124
|
+
fail();
|
|
125
|
+
size = new TextEncoder().encode(value).length;
|
|
126
|
+
}
|
|
127
|
+
let serialized;
|
|
128
|
+
try {
|
|
129
|
+
serialized = JSON.stringify(r);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
fail();
|
|
133
|
+
}
|
|
134
|
+
if (size + headerBytes > MAX_FUNCTION_BODY_BYTES || !serialized || new TextEncoder().encode(serialized).length > MAX_FUNCTION_JSON_BYTES)
|
|
135
|
+
fail();
|
|
136
|
+
}
|
|
137
|
+
/** Safe display/transport headers; customer headers never establish identity. */
|
|
138
|
+
export function hivesFunctionResponseHeaders(headers) {
|
|
139
|
+
const blocked = new Set(['connection', 'keep-alive', 'transfer-encoding', 'upgrade', 'proxy-authenticate', 'proxy-authorization',
|
|
140
|
+
'te', 'trailer', 'content-length', 'content-encoding', 'set-cookie', 'clear-site-data', 'authorization', 'cookie', 'forwarded', 'host', 'x-org-id', 'x-workspace-id', 'x-project-id', 'x-admin-token', 'x-tenant-id', 'x-hives-guest-status']);
|
|
141
|
+
for (const value of (headers.get('connection') ?? '').split(','))
|
|
142
|
+
blocked.add(value.trim().toLowerCase());
|
|
143
|
+
const safe = new Headers();
|
|
144
|
+
headers.forEach((value, name) => {
|
|
145
|
+
if (!blocked.has(name) && (!platformHeader(name) || ['x-execution-id', 'x-execution-duration-ms'].includes(name)))
|
|
146
|
+
safe.set(name, value);
|
|
147
|
+
});
|
|
148
|
+
const operation = headers.get('x-hives-operation-id');
|
|
149
|
+
if (operation && executionId.test(operation) && operation === headers.get('x-execution-id'))
|
|
150
|
+
safe.set('x-hives-operation-id', operation);
|
|
151
|
+
const observation = headers.get('x-execution-observation-v1');
|
|
152
|
+
if (observation && validObservation(observation))
|
|
153
|
+
safe.set('x-execution-observation-v1', observation);
|
|
154
|
+
safe.set('cache-control', 'no-store');
|
|
155
|
+
return safe;
|
|
156
|
+
}
|
|
157
|
+
function validObservation(encoded) {
|
|
158
|
+
if (encoded.length > 2048 || !/^[A-Za-z0-9_-]+$/.test(encoded))
|
|
159
|
+
return false;
|
|
160
|
+
try {
|
|
161
|
+
const value = JSON.parse(atob(encoded.replace(/-/g, '+').replace(/_/g, '/')));
|
|
162
|
+
const numbers = ['reserved_memory_bytes', 'reserved_cpu_millis', 'admission_wait_us'];
|
|
163
|
+
const nullable = ['executor_poll_cpu_ns', 'exported_linear_memory_bytes'];
|
|
164
|
+
const keys = [...numbers, ...nullable, 'version', 'cpu_scope', 'complete_execution_cpu', 'peak_linear_memory_bytes', 'execution_resident_memory_bytes'];
|
|
165
|
+
return value && typeof value === 'object' && Object.keys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key))
|
|
166
|
+
&& value.version === 1 && value.cpu_scope === 'executor_thread_polls' && value.complete_execution_cpu === false
|
|
167
|
+
&& value.peak_linear_memory_bytes === null && value.execution_resident_memory_bytes === null
|
|
168
|
+
&& numbers.every(key => Number.isSafeInteger(value[key]) && value[key] >= 0)
|
|
169
|
+
&& nullable.every(key => value[key] === null || Number.isSafeInteger(value[key]) && value[key] >= 0);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Metadata contracts cannot accidentally expose an upstream env/ciphertext field. */
|
|
176
|
+
export function hivesFunctionMetadata(input, project, expectedId) {
|
|
177
|
+
if (!input || typeof input !== 'object' || input.project_id !== project) {
|
|
178
|
+
throw new Error('Hives returned invalid or wrong-project function metadata.');
|
|
179
|
+
}
|
|
180
|
+
const row = input;
|
|
181
|
+
if (expectedId !== undefined && row.id !== expectedId)
|
|
182
|
+
throw new Error('Hives returned mismatched function metadata.');
|
|
183
|
+
return Object.fromEntries(['id', 'tenant_id', 'project_id', 'name', 'description', 'handler', 'route', 'memory_mb', 'timeout_ms',
|
|
184
|
+
'status', 'invocations', 'avg_duration_ms', 'created_at', 'updated_at'].map((key) => [key, row[key]]));
|
|
185
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { GenesisClient } from './client.js';
|
|
2
|
+
import { type HivesResourceOptions } from './hives-resources-transport.js';
|
|
3
|
+
export interface HivesMarketplacePackage {
|
|
4
|
+
id: string;
|
|
5
|
+
slug: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
author_name: string;
|
|
9
|
+
visibility: 'public' | 'private' | 'unlisted';
|
|
10
|
+
categories: string[];
|
|
11
|
+
keywords: string[];
|
|
12
|
+
license: string;
|
|
13
|
+
installs: number;
|
|
14
|
+
stars: number;
|
|
15
|
+
verified: boolean;
|
|
16
|
+
featured: boolean;
|
|
17
|
+
free: boolean;
|
|
18
|
+
latest_version: string;
|
|
19
|
+
version_count: number;
|
|
20
|
+
owned: boolean;
|
|
21
|
+
created_at: string;
|
|
22
|
+
updated_at: string;
|
|
23
|
+
}
|
|
24
|
+
export interface HivesMarketplaceVersion {
|
|
25
|
+
id: string;
|
|
26
|
+
package_id: string;
|
|
27
|
+
version: string;
|
|
28
|
+
module_hash: string;
|
|
29
|
+
size_bytes: number;
|
|
30
|
+
readme: string;
|
|
31
|
+
changelog: string;
|
|
32
|
+
required_env: string[];
|
|
33
|
+
downloads: number;
|
|
34
|
+
deprecated: boolean;
|
|
35
|
+
published_at: string;
|
|
36
|
+
}
|
|
37
|
+
export interface HivesMarketplaceInstallation {
|
|
38
|
+
id: string;
|
|
39
|
+
package_id: string;
|
|
40
|
+
slug: string;
|
|
41
|
+
name: string;
|
|
42
|
+
project_id: string;
|
|
43
|
+
version: string | null;
|
|
44
|
+
deployment_id: string | null;
|
|
45
|
+
deployment_status: string | null;
|
|
46
|
+
pending_version: string | null;
|
|
47
|
+
pending_deployment_id: string | null;
|
|
48
|
+
pending_deployment_status: string | null;
|
|
49
|
+
operation: 'install' | 'update' | 'cleanup' | 'uninstall' | null;
|
|
50
|
+
status: 'active' | 'updating' | 'failed' | 'uninstalled';
|
|
51
|
+
latest_version: string | null;
|
|
52
|
+
installed_at: string;
|
|
53
|
+
}
|
|
54
|
+
export interface HivesMarketplaceCategory {
|
|
55
|
+
id: string;
|
|
56
|
+
slug: string;
|
|
57
|
+
name: string;
|
|
58
|
+
description: string | null;
|
|
59
|
+
icon_url: string | null;
|
|
60
|
+
package_count: number;
|
|
61
|
+
sort_order: number;
|
|
62
|
+
}
|
|
63
|
+
export interface HivesMarketplacePage<T> {
|
|
64
|
+
data: T[];
|
|
65
|
+
pagination: {
|
|
66
|
+
page: number;
|
|
67
|
+
page_size: number;
|
|
68
|
+
total: number;
|
|
69
|
+
total_pages: number;
|
|
70
|
+
has_next: boolean;
|
|
71
|
+
has_prev: boolean;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export interface HivesMarketplacePublish {
|
|
75
|
+
slug: string;
|
|
76
|
+
name: string;
|
|
77
|
+
module_hash: string;
|
|
78
|
+
version: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
visibility?: HivesMarketplacePackage['visibility'];
|
|
81
|
+
categories?: string[];
|
|
82
|
+
keywords?: string[];
|
|
83
|
+
readme?: string;
|
|
84
|
+
changelog?: string;
|
|
85
|
+
required_env?: string[];
|
|
86
|
+
license?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface HivesMarketplaceInstall {
|
|
89
|
+
version?: string | null;
|
|
90
|
+
env_overrides?: Record<string, string>;
|
|
91
|
+
}
|
|
92
|
+
export interface HivesMarketplaceSearch {
|
|
93
|
+
q?: string | null;
|
|
94
|
+
category?: string | null;
|
|
95
|
+
free_only?: boolean;
|
|
96
|
+
verified_only?: boolean;
|
|
97
|
+
sort?: 'name' | 'installs' | 'stars' | 'created' | 'updated';
|
|
98
|
+
order?: 'asc' | 'desc';
|
|
99
|
+
page?: number;
|
|
100
|
+
page_size?: number;
|
|
101
|
+
}
|
|
102
|
+
export type HivesMarketplaceDetail = HivesMarketplacePackage & {
|
|
103
|
+
versions: HivesMarketplaceVersion[];
|
|
104
|
+
};
|
|
105
|
+
/** Native Marketplace reuses verified project ownership and persisted deployment lifecycle.
|
|
106
|
+
* Writes are sent once. Read/reconcile saved state after an uncertain acknowledgement. */
|
|
107
|
+
export declare function hivesMarketplace(client: GenesisClient): {
|
|
108
|
+
list(project: string, query?: HivesMarketplaceSearch, options?: HivesResourceOptions): Promise<HivesMarketplacePage<HivesMarketplacePackage>>;
|
|
109
|
+
get(project: string, packageSlug: string, query?: {
|
|
110
|
+
version?: string | null;
|
|
111
|
+
}, options?: HivesResourceOptions): Promise<HivesMarketplaceDetail>;
|
|
112
|
+
versions(project: string, packageSlug: string, query?: {
|
|
113
|
+
page?: number;
|
|
114
|
+
page_size?: number;
|
|
115
|
+
}, options?: HivesResourceOptions): Promise<HivesMarketplacePage<HivesMarketplaceVersion>>;
|
|
116
|
+
featured(project: string, options?: HivesResourceOptions): Promise<HivesMarketplacePage<HivesMarketplacePackage>>;
|
|
117
|
+
categories(project: string, options?: HivesResourceOptions): Promise<HivesMarketplaceCategory[]>;
|
|
118
|
+
publish(project: string, input: HivesMarketplacePublish, options?: HivesResourceOptions): Promise<HivesMarketplaceDetail>;
|
|
119
|
+
install(project: string, packageSlug: string, input?: HivesMarketplaceInstall, options?: HivesResourceOptions): Promise<HivesMarketplaceInstallation>;
|
|
120
|
+
update(project: string, packageSlug: string, input?: HivesMarketplaceInstall, options?: HivesResourceOptions): Promise<HivesMarketplaceInstallation>;
|
|
121
|
+
installed(project: string, query?: {
|
|
122
|
+
page?: number;
|
|
123
|
+
page_size?: number;
|
|
124
|
+
}, options?: HivesResourceOptions): Promise<HivesMarketplacePage<HivesMarketplaceInstallation>>;
|
|
125
|
+
getInstallation(project: string, id: string, options?: HivesResourceOptions): Promise<HivesMarketplaceInstallation>;
|
|
126
|
+
reconcile(project: string, id: string, options?: HivesResourceOptions): Promise<HivesMarketplaceInstallation>;
|
|
127
|
+
uninstall(project: string, id: string, options?: HivesResourceOptions): Promise<HivesMarketplaceInstallation>;
|
|
128
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { hivesResourceCall, hivesResourceId, resourceError } from './hives-resources-transport.js';
|
|
2
|
+
function invalid() { throw resourceError(0); }
|
|
3
|
+
function record(value) { if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
|
+
invalid(); return value; }
|
|
5
|
+
function keys(value, allowed) { if (Object.keys(value).some(key => !allowed.includes(key)))
|
|
6
|
+
invalid(); }
|
|
7
|
+
function text(value, max) { if (typeof value !== 'string' || new TextEncoder().encode(value).length > max)
|
|
8
|
+
invalid(); return value; }
|
|
9
|
+
function uuid(value) { const result = text(value, 36); if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(result))
|
|
10
|
+
invalid(); return result.toLowerCase(); }
|
|
11
|
+
function slug(value) { const result = text(value, 64); if (!/^[a-z][a-z0-9-]*[a-z0-9]$/.test(result))
|
|
12
|
+
invalid(); return result; }
|
|
13
|
+
function semver(value, maxCore = 18446744073709551615n) {
|
|
14
|
+
const result = text(value, 64);
|
|
15
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(result);
|
|
16
|
+
if (!match || match.slice(1, 4).some(part => BigInt(part) > maxCore)
|
|
17
|
+
|| match[4]?.split('.').some(part => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0')))
|
|
18
|
+
invalid();
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function count(value, min = 0, max = Number.MAX_SAFE_INTEGER) { if (!Number.isSafeInteger(value) || value < min || value > max)
|
|
22
|
+
invalid(); return value; }
|
|
23
|
+
function bool(value) { if (typeof value !== 'boolean')
|
|
24
|
+
invalid(); return value; }
|
|
25
|
+
function strings(value, limit, max) { if (!Array.isArray(value) || value.length > limit)
|
|
26
|
+
invalid(); return value.map(item => text(item, max)); }
|
|
27
|
+
function date(value) { const result = text(value, 64); if (!Number.isFinite(Date.parse(result)))
|
|
28
|
+
invalid(); return result; }
|
|
29
|
+
function nullable(value, parse) { return value === null ? null : parse(value); }
|
|
30
|
+
function deployment(value) { const id = text(value, 64); if (!/^dpl_[A-Za-z0-9_-]+$/.test(id))
|
|
31
|
+
invalid(); return id; }
|
|
32
|
+
function packageRow(value) {
|
|
33
|
+
const r = record(value);
|
|
34
|
+
if (!['public', 'private', 'unlisted'].includes(String(r.visibility)) || r.visibility === 'private' && r.owned !== true)
|
|
35
|
+
invalid();
|
|
36
|
+
return { id: uuid(r.id), slug: slug(r.slug), name: text(r.name, 128), description: text(r.description, 500), author_name: text(r.author_name, 1000),
|
|
37
|
+
visibility: r.visibility, categories: strings(r.categories, 5, 64), keywords: strings(r.keywords, 20, 64), license: text(r.license, 64),
|
|
38
|
+
installs: count(r.installs), stars: count(r.stars), verified: bool(r.verified), featured: bool(r.featured), free: bool(r.free),
|
|
39
|
+
latest_version: semver(r.latest_version), version_count: count(r.version_count, 1), owned: bool(r.owned), created_at: date(r.created_at), updated_at: date(r.updated_at) };
|
|
40
|
+
}
|
|
41
|
+
function versionRow(value, expectedPackage) {
|
|
42
|
+
const r = record(value);
|
|
43
|
+
const packageId = uuid(r.package_id);
|
|
44
|
+
const hash = text(r.module_hash, 64);
|
|
45
|
+
if (expectedPackage && packageId !== expectedPackage || !/^[a-f0-9]{64}$/.test(hash))
|
|
46
|
+
invalid();
|
|
47
|
+
return { id: uuid(r.id), package_id: packageId, version: semver(r.version), module_hash: hash, size_bytes: count(r.size_bytes, 1, 10 * 1024 * 1024),
|
|
48
|
+
readme: text(r.readme, 50_000), changelog: text(r.changelog, 2000), required_env: strings(r.required_env, 50, 128), downloads: count(r.downloads), deprecated: bool(r.deprecated), published_at: date(r.published_at) };
|
|
49
|
+
}
|
|
50
|
+
function installationRow(value, project, expectedId) {
|
|
51
|
+
const r = record(value);
|
|
52
|
+
if (r.project_id !== project || expectedId && uuid(r.id) !== uuid(expectedId) || !['active', 'updating', 'failed', 'uninstalled'].includes(String(r.status))
|
|
53
|
+
|| !(r.operation === null || ['install', 'update', 'cleanup', 'uninstall'].includes(String(r.operation))))
|
|
54
|
+
invalid();
|
|
55
|
+
const result = { id: uuid(r.id), package_id: uuid(r.package_id), slug: slug(r.slug), name: text(r.name, 128), project_id: project,
|
|
56
|
+
version: nullable(r.version, semver), deployment_id: nullable(r.deployment_id, deployment), deployment_status: nullable(r.deployment_status, v => text(v, 32)),
|
|
57
|
+
pending_version: nullable(r.pending_version, semver), pending_deployment_id: nullable(r.pending_deployment_id, deployment), pending_deployment_status: nullable(r.pending_deployment_status, v => text(v, 32)),
|
|
58
|
+
operation: r.operation, status: r.status,
|
|
59
|
+
latest_version: nullable(r.latest_version, semver), installed_at: date(r.installed_at) };
|
|
60
|
+
if (result.status === 'uninstalled' && (result.operation || result.deployment_id || result.pending_deployment_id))
|
|
61
|
+
invalid();
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
function page(value, pageNumber, pageSize, parse) {
|
|
65
|
+
const r = record(value);
|
|
66
|
+
const p = record(r.pagination);
|
|
67
|
+
const total = count(p.total);
|
|
68
|
+
const pages = Math.ceil(total / pageSize);
|
|
69
|
+
if (p.page !== pageNumber || p.page_size !== pageSize || p.total_pages !== pages || p.has_next !== (pageNumber < pages) || p.has_prev !== (pageNumber > 1)
|
|
70
|
+
|| !Array.isArray(r.data) || r.data.length > pageSize)
|
|
71
|
+
invalid();
|
|
72
|
+
const data = r.data.map(parse);
|
|
73
|
+
if (new Set(data.map(row => row.id)).size !== data.length)
|
|
74
|
+
invalid();
|
|
75
|
+
return { data, pagination: { page: pageNumber, page_size: pageSize, total, total_pages: pages, has_next: pageNumber < pages, has_prev: pageNumber > 1 } };
|
|
76
|
+
}
|
|
77
|
+
function details(value, expectedSlug, exactVersion) {
|
|
78
|
+
const r = record(value);
|
|
79
|
+
const pkg = packageRow(r);
|
|
80
|
+
if (pkg.slug !== expectedSlug || !Array.isArray(r.versions) || r.versions.length > 5 || r.versions.length === 0)
|
|
81
|
+
invalid();
|
|
82
|
+
const versions = r.versions.map(row => versionRow(row, pkg.id));
|
|
83
|
+
if (new Set(versions.map(row => row.id)).size !== versions.length || exactVersion && versions.some(row => row.version !== exactVersion))
|
|
84
|
+
invalid();
|
|
85
|
+
return { ...pkg, versions };
|
|
86
|
+
}
|
|
87
|
+
function pagination(query, max) {
|
|
88
|
+
keys(record(query), ['page', 'page_size']);
|
|
89
|
+
return { page: count(query.page ?? 1, 1, 100_000), page_size: count(query.page_size ?? max, 1, max) };
|
|
90
|
+
}
|
|
91
|
+
function installInput(input) {
|
|
92
|
+
const r = record(input);
|
|
93
|
+
keys(r, ['version', 'env_overrides']);
|
|
94
|
+
const env = record(r.env_overrides ?? {});
|
|
95
|
+
if (Object.keys(env).length > 50)
|
|
96
|
+
invalid();
|
|
97
|
+
const entries = Object.entries(env).map(([key, value]) => [text(key, 128), text(value, 4096)]);
|
|
98
|
+
return { ...(r.version == null ? {} : { version: semver(r.version) }), env_overrides: Object.fromEntries(entries) };
|
|
99
|
+
}
|
|
100
|
+
function publishInput(input) {
|
|
101
|
+
const r = record(input);
|
|
102
|
+
keys(r, ['slug', 'name', 'module_hash', 'version', 'description', 'visibility', 'categories', 'keywords', 'readme', 'changelog', 'required_env', 'license']);
|
|
103
|
+
const name = text(r.name, 128);
|
|
104
|
+
const hash = text(r.module_hash, 64);
|
|
105
|
+
const visibility = r.visibility ?? 'public';
|
|
106
|
+
if (!name.trim() || !/^[a-f0-9]{64}$/.test(hash) || !['public', 'private', 'unlisted'].includes(String(visibility)))
|
|
107
|
+
invalid();
|
|
108
|
+
const required = strings(r.required_env ?? [], 50, 128);
|
|
109
|
+
if (new Set(required).size !== required.length || required.some(key => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)))
|
|
110
|
+
invalid();
|
|
111
|
+
return { slug: slug(r.slug), name, module_hash: hash, version: semver(r.version, 2147483647n), description: text(r.description ?? '', 500),
|
|
112
|
+
visibility: visibility, categories: strings(r.categories ?? [], 5, 64), keywords: strings(r.keywords ?? [], 20, 64),
|
|
113
|
+
readme: text(r.readme ?? '', 50_000), changelog: text(r.changelog ?? '', 2000), required_env: required, license: text(r.license ?? '', 64) };
|
|
114
|
+
}
|
|
115
|
+
/** Native Marketplace reuses verified project ownership and persisted deployment lifecycle.
|
|
116
|
+
* Writes are sent once. Read/reconcile saved state after an uncertain acknowledgement. */
|
|
117
|
+
export function hivesMarketplace(client) {
|
|
118
|
+
const projectInput = (project) => ({ project_id: hivesResourceId(project) });
|
|
119
|
+
const call = (operation, input, options) => hivesResourceCall(client, `hives_marketplace_${operation}`, input, options);
|
|
120
|
+
const mutate = async (operation, project, packageSlug, input, options) => {
|
|
121
|
+
const body = installInput(input);
|
|
122
|
+
const result = installationRow(await call(operation, { ...projectInput(project), slug: slug(packageSlug), body }, options), project);
|
|
123
|
+
if (result.slug !== packageSlug || result.status === 'uninstalled' || body.version && result.version !== body.version && result.pending_version !== body.version)
|
|
124
|
+
invalid();
|
|
125
|
+
return result;
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
async list(project, query = {}, options) {
|
|
129
|
+
keys(record(query), ['q', 'category', 'free_only', 'verified_only', 'sort', 'order', 'page', 'page_size']);
|
|
130
|
+
const p = { page: count(query.page ?? 1, 1, 100_000), page_size: count(query.page_size ?? 20, 1, 100) };
|
|
131
|
+
const sort = query.sort ?? 'installs';
|
|
132
|
+
const order = query.order ?? 'desc';
|
|
133
|
+
if (!['name', 'installs', 'stars', 'created', 'updated'].includes(sort) || !['asc', 'desc'].includes(order))
|
|
134
|
+
invalid();
|
|
135
|
+
const input = { ...projectInput(project), ...p, sort, order, free_only: bool(query.free_only ?? false), verified_only: bool(query.verified_only ?? false),
|
|
136
|
+
...(query.q == null ? {} : { q: text(query.q, 200) }), ...(query.category == null ? {} : { category: text(query.category, 64) }) };
|
|
137
|
+
return page(await call('list_packages', input, options), p.page, p.page_size, packageRow);
|
|
138
|
+
},
|
|
139
|
+
async get(project, packageSlug, query = {}, options) {
|
|
140
|
+
keys(record(query), ['version']);
|
|
141
|
+
const version = query.version == null ? undefined : semver(query.version);
|
|
142
|
+
return details(await call('get_package', { ...projectInput(project), slug: slug(packageSlug), ...(version === undefined ? {} : { version }) }, options), packageSlug, version);
|
|
143
|
+
},
|
|
144
|
+
async versions(project, packageSlug, query = {}, options) {
|
|
145
|
+
const p = pagination(query, 5);
|
|
146
|
+
const result = page(await call('list_versions', { ...projectInput(project), slug: slug(packageSlug), ...p }, options), p.page, p.page_size, row => versionRow(row));
|
|
147
|
+
if (new Set(result.data.map(row => row.package_id)).size > 1)
|
|
148
|
+
invalid();
|
|
149
|
+
return result;
|
|
150
|
+
},
|
|
151
|
+
async featured(project, options) { return page(await call('featured', projectInput(project), options), 1, 20, packageRow); },
|
|
152
|
+
async categories(project, options) {
|
|
153
|
+
const result = await call('categories', projectInput(project), options);
|
|
154
|
+
if (!Array.isArray(result) || result.length > 100)
|
|
155
|
+
invalid();
|
|
156
|
+
const data = result.map(value => {
|
|
157
|
+
const r = record(value);
|
|
158
|
+
return { id: uuid(r.id), slug: slug(r.slug), name: text(r.name, 128),
|
|
159
|
+
description: nullable(r.description, v => text(v, 1000)), icon_url: nullable(r.icon_url, v => text(v, 2048)), package_count: count(r.package_count), sort_order: count(r.sort_order, -2147483648, 2147483647) };
|
|
160
|
+
});
|
|
161
|
+
if (new Set(data.map(row => row.id)).size !== data.length)
|
|
162
|
+
invalid();
|
|
163
|
+
return data;
|
|
164
|
+
},
|
|
165
|
+
async publish(project, input, options) {
|
|
166
|
+
const body = publishInput(input);
|
|
167
|
+
const result = details(await call('publish', { ...projectInput(project), body }, options), body.slug);
|
|
168
|
+
if (!result.owned || !result.versions.some(v => v.version === body.version && v.module_hash === body.module_hash))
|
|
169
|
+
invalid();
|
|
170
|
+
return result;
|
|
171
|
+
},
|
|
172
|
+
async install(project, packageSlug, input = {}, options) { return mutate('install', project, packageSlug, input, options); },
|
|
173
|
+
async update(project, packageSlug, input = {}, options) { return mutate('update', project, packageSlug, input, options); },
|
|
174
|
+
async installed(project, query = {}, options) {
|
|
175
|
+
const p = pagination(query, 20);
|
|
176
|
+
return page(await call('installed', { ...projectInput(project), ...p }, options), p.page, p.page_size, row => installationRow(row, project));
|
|
177
|
+
},
|
|
178
|
+
async getInstallation(project, id, options) { return installationRow(await call('get_installation', { ...projectInput(project), installation_id: uuid(id) }, options), project, id); },
|
|
179
|
+
async reconcile(project, id, options) { return installationRow(await call('reconcile', { ...projectInput(project), installation_id: uuid(id) }, options), project, id); },
|
|
180
|
+
async uninstall(project, id, options) {
|
|
181
|
+
const result = installationRow(await call('uninstall', { ...projectInput(project), installation_id: uuid(id) }, options), project, id);
|
|
182
|
+
if (result.status !== 'uninstalled' || result.operation || result.deployment_id || result.pending_deployment_id)
|
|
183
|
+
invalid();
|
|
184
|
+
return result;
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Genesis } from './genesis.js';
|
|
2
|
+
import type { GenesisOptions } from './client.js';
|
|
3
|
+
export declare function closeHivesNodeTransport(): Promise<void>;
|
|
4
|
+
export declare function hivesOperationBudget(id: string): number;
|
|
5
|
+
/** Hives leaf transport. Caller fetch overrides stay authoritative; only native
|
|
6
|
+
* global fetch gets our Agent. Every operation retains an overall body deadline. */
|
|
7
|
+
export declare function createHivesNodeTransport(opts?: GenesisOptions, callerSignal?: AbortSignal): typeof fetch;
|
|
8
|
+
/** Node opt-in for Hives calls. Other services retain the caller transport. */
|
|
9
|
+
export declare function createHivesNodeClient(opts?: GenesisOptions, callerSignal?: AbortSignal): Genesis;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Agent } from 'undici';
|
|
2
|
+
import { Genesis } from './genesis.js';
|
|
3
|
+
import { operationTable } from './generated/operations.js';
|
|
4
|
+
// Node's native fetch accepts a compatible per-request dispatcher. Never change
|
|
5
|
+
// the global dispatcher or the portable SDK: other products retain their transport.
|
|
6
|
+
const agentOptions = { headersTimeout: 340_000, bodyTimeout: 340_000, connect: { timeout: 5_000 }, connections: 4, pipelining: 1 };
|
|
7
|
+
let agent;
|
|
8
|
+
const nativeAgent = () => agent ??= new Agent(agentOptions);
|
|
9
|
+
export async function closeHivesNodeTransport() {
|
|
10
|
+
const current = agent;
|
|
11
|
+
agent = undefined;
|
|
12
|
+
await current?.destroy();
|
|
13
|
+
}
|
|
14
|
+
export function hivesOperationBudget(id) {
|
|
15
|
+
if (['hives_list_buckets', 'hives_create_bucket', 'hives_get_bucket', 'hives_delete_bucket', 'hives_list_objects', 'hives_upload_object', 'hives_upload_object_by_key', 'hives_delete_object', 'hives_delete_object_by_key', 'hives_download_object_by_key', 'hives_get_cors_config', 'hives_set_cors_config', 'hives_generate_presigned_url', 'hives_reconcile'].includes(id))
|
|
16
|
+
return 100_000;
|
|
17
|
+
if (id === 'hives_cancel_deployment' || id === 'hives_delete_deployment' || id === 'hives_stop_deployment')
|
|
18
|
+
return 330_000;
|
|
19
|
+
if (['hives_create_deployment', 'hives_upload_deployment', 'hives_invoke_deployment', 'hives_invoke_function', 'hives_invoke_function_async'].includes(id))
|
|
20
|
+
return 40_000;
|
|
21
|
+
if (['hives_create_function', 'hives_update_function', 'hives_delete_function'].includes(id))
|
|
22
|
+
return 100_000;
|
|
23
|
+
return 20_000;
|
|
24
|
+
}
|
|
25
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
26
|
+
const templatePattern = (value) => new RegExp('^' + value.split(/(\{[^}]+\})/).map((part) => part.startsWith('{') ? '[^/]+' : escape(part)).join('') + '$');
|
|
27
|
+
/** Hives leaf transport. Caller fetch overrides stay authoritative; only native
|
|
28
|
+
* global fetch gets our Agent. Every operation retains an overall body deadline. */
|
|
29
|
+
export function createHivesNodeTransport(opts = {}, callerSignal) {
|
|
30
|
+
const supplied = opts.fetch ?? globalThis.fetch;
|
|
31
|
+
const underlying = supplied.bind(globalThis);
|
|
32
|
+
const useNative = supplied === globalThis.fetch;
|
|
33
|
+
const resolver = new Genesis(opts);
|
|
34
|
+
const routes = Object.entries(operationTable)
|
|
35
|
+
.filter(([, op]) => op.service === 'hives' && !op.internal)
|
|
36
|
+
.flatMap(([id, op]) => {
|
|
37
|
+
const direct = resolver.routing === 'direct';
|
|
38
|
+
let base;
|
|
39
|
+
try {
|
|
40
|
+
base = new URL(direct ? (opts.upstreams?.hives ?? op.upstream.base) : resolver.baseUrl);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error('Invalid Hives service address.');
|
|
44
|
+
}
|
|
45
|
+
const path = direct ? op.upstream.path : op.path;
|
|
46
|
+
const prefix = base.pathname.replace(/\/+$/, '');
|
|
47
|
+
const paths = [path];
|
|
48
|
+
if (!direct && path.startsWith('/v1/hives/'))
|
|
49
|
+
paths.push(path.replace('/v1/hives/', '/hives/v1/'));
|
|
50
|
+
return paths.map((p) => ({ id, origin: base.origin, method: direct ? op.upstream.method : op.method, pattern: templatePattern(prefix + p) }));
|
|
51
|
+
});
|
|
52
|
+
return (async (input, init = {}) => {
|
|
53
|
+
let url;
|
|
54
|
+
try {
|
|
55
|
+
url = new URL(input instanceof Request ? input.url : String(input));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error('Invalid Hives request address.');
|
|
59
|
+
}
|
|
60
|
+
const method = (init.method ?? (input instanceof Request ? input.method : 'GET')).toUpperCase();
|
|
61
|
+
const route = routes.find((entry) => entry.origin === url.origin && entry.method === method && entry.pattern.test(url.pathname));
|
|
62
|
+
if (!route)
|
|
63
|
+
return underlying(input, init);
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
let message = 'Hives transport failed. Refresh the resource and retry.';
|
|
66
|
+
const safeError = () => new Error(message);
|
|
67
|
+
const signals = [...new Set([callerSignal, init.signal, input instanceof Request ? input.signal : undefined].filter((signal) => !!signal))];
|
|
68
|
+
const canceled = () => {
|
|
69
|
+
message = 'Hives request canceled before completion. Refresh the resource before retrying.';
|
|
70
|
+
controller.abort();
|
|
71
|
+
};
|
|
72
|
+
for (const signal of signals)
|
|
73
|
+
signal.addEventListener('abort', canceled, { once: true });
|
|
74
|
+
const timer = setTimeout(() => {
|
|
75
|
+
message = 'Hives request deadline exceeded. Refresh the resource before retrying.';
|
|
76
|
+
controller.abort();
|
|
77
|
+
}, hivesOperationBudget(route.id));
|
|
78
|
+
timer.unref();
|
|
79
|
+
let abortReject = () => { };
|
|
80
|
+
let reader;
|
|
81
|
+
let bodyController;
|
|
82
|
+
const aborted = () => {
|
|
83
|
+
abortReject(safeError());
|
|
84
|
+
bodyController?.error(safeError());
|
|
85
|
+
void reader?.cancel().catch(() => { });
|
|
86
|
+
cleanup();
|
|
87
|
+
};
|
|
88
|
+
controller.signal.addEventListener('abort', aborted, { once: true });
|
|
89
|
+
const abortPromise = new Promise((_, reject) => { abortReject = reject; });
|
|
90
|
+
void abortPromise.catch(() => { });
|
|
91
|
+
const cleanup = () => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
for (const signal of signals)
|
|
94
|
+
signal.removeEventListener('abort', canceled);
|
|
95
|
+
controller.signal.removeEventListener('abort', aborted);
|
|
96
|
+
};
|
|
97
|
+
if (signals.some((signal) => signal.aborted))
|
|
98
|
+
canceled();
|
|
99
|
+
try {
|
|
100
|
+
if (controller.signal.aborted)
|
|
101
|
+
throw safeError();
|
|
102
|
+
const requestInit = {
|
|
103
|
+
...init, signal: controller.signal, cache: 'no-store',
|
|
104
|
+
redirect: init.redirect === 'manual' ? 'manual' : 'error',
|
|
105
|
+
...(useNative ? { dispatcher: nativeAgent() } : {}),
|
|
106
|
+
};
|
|
107
|
+
const pending = underlying(input, requestInit);
|
|
108
|
+
// A caller transport may ignore AbortSignal. Reject on our budget anyway,
|
|
109
|
+
// and discard any late response rather than presenting an acknowledgement.
|
|
110
|
+
void pending.then((response) => { if (controller.signal.aborted)
|
|
111
|
+
void response.body?.cancel().catch(() => { }); }, () => { });
|
|
112
|
+
const response = await Promise.race([pending, abortPromise]);
|
|
113
|
+
if (controller.signal.aborted)
|
|
114
|
+
throw safeError();
|
|
115
|
+
if (!response.body) {
|
|
116
|
+
cleanup();
|
|
117
|
+
return response;
|
|
118
|
+
}
|
|
119
|
+
reader = response.body.getReader();
|
|
120
|
+
const body = new ReadableStream({
|
|
121
|
+
start(value) { bodyController = value; },
|
|
122
|
+
async pull(value) {
|
|
123
|
+
try {
|
|
124
|
+
const next = await reader.read();
|
|
125
|
+
if (controller.signal.aborted)
|
|
126
|
+
return;
|
|
127
|
+
if (next.done) {
|
|
128
|
+
cleanup();
|
|
129
|
+
value.close();
|
|
130
|
+
}
|
|
131
|
+
else
|
|
132
|
+
value.enqueue(next.value);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
cleanup();
|
|
136
|
+
value.error(safeError());
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
async cancel() { cleanup(); await reader.cancel().catch(() => { }); },
|
|
140
|
+
});
|
|
141
|
+
return new Response(body, { status: response.status, statusText: response.statusText, headers: response.headers });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
cleanup();
|
|
145
|
+
throw safeError();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/** Node opt-in for Hives calls. Other services retain the caller transport. */
|
|
150
|
+
export function createHivesNodeClient(opts = {}, callerSignal) {
|
|
151
|
+
return new Genesis({ ...opts, fetch: createHivesNodeTransport(opts, callerSignal) });
|
|
152
|
+
}
|