@astrale-os/kernel-client 0.6.0-beta.77 → 0.6.0-beta.79
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/dist/session/content.d.ts +0 -3
- package/dist/session/content.js +4 -5
- package/dist/session/execution.js +7 -7
- package/dist/session/reference.d.ts +6 -9
- package/dist/session/reference.js +12 -14
- package/dist/session/session.d.ts +0 -1
- package/dist/session/session.js +15 -16
- package/dist/transport/http/byte-stream.js +1 -1
- package/dist/transport/http/transport.js +5 -2
- package/package.json +5 -5
|
@@ -3,8 +3,6 @@ import type { Ref } from '@astrale-os/kernel-protocol/artifact';
|
|
|
3
3
|
import { Path } from '@astrale-os/kernel-core/path';
|
|
4
4
|
import type { Binary, Input, Result } from '../client/index.js';
|
|
5
5
|
import type { SessionControlOptions, SessionOperation } from './context/index.js';
|
|
6
|
-
import type { CallableReference } from './reference.js';
|
|
7
|
-
import type { SessionRequestOptionsWithBody } from './routing/index.js';
|
|
8
6
|
export interface ContentLocation {
|
|
9
7
|
readonly node: string;
|
|
10
8
|
readonly property: string;
|
|
@@ -23,7 +21,6 @@ export interface ContentApi {
|
|
|
23
21
|
}
|
|
24
22
|
export interface ContentSessionDependencies {
|
|
25
23
|
readonly operation: (options: SessionControlOptions) => SessionOperation;
|
|
26
|
-
readonly invoke: (reference: CallableReference<unknown, unknown, 'binary'>, input: unknown, options: SessionRequestOptionsWithBody, operation: SessionOperation) => Promise<unknown>;
|
|
27
24
|
readonly dispatch: (target: Path, input: Input, operation: SessionOperation, body?: Blob) => Promise<Result>;
|
|
28
25
|
}
|
|
29
26
|
/** Transfer immutable content over the authenticated Session routing conversation. */
|
package/dist/session/content.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { NodeId } from '@astrale-os/kernel-core/graph/node';
|
|
2
2
|
import { Path } from '@astrale-os/kernel-core/path';
|
|
3
|
-
import { download,
|
|
3
|
+
import { download, upload } from '@astrale-os/kernel-core/schema/kernel/syscalls/references';
|
|
4
4
|
import { PropertyKey } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
5
5
|
import * as artifact from '@astrale-os/kernel-protocol/artifact';
|
|
6
6
|
import { CODES } from '@astrale-os/kernel-protocol/errors';
|
|
7
7
|
import { ClientError, ProtocolError, ResponseError, TransportError } from '../errors/index.js';
|
|
8
8
|
import { admitControlOptions, runSessionOperation } from './context/index.js';
|
|
9
|
-
import { kernelReference } from './reference.js';
|
|
10
9
|
import { requireResultKind } from './routing/index.js';
|
|
11
|
-
const UPLOAD =
|
|
10
|
+
const UPLOAD = Path.project(upload);
|
|
12
11
|
const DOWNLOAD = Path.project(download);
|
|
13
12
|
const CONTENT_TIMEOUT_MS = 30 * 60 * 1_000;
|
|
14
13
|
const MAXIMUM_ATTEMPTS = 3;
|
|
@@ -21,8 +20,8 @@ export function createContentApi(dependencies) {
|
|
|
21
20
|
let last;
|
|
22
21
|
for (let attempt = 0; attempt < MAXIMUM_ATTEMPTS; attempt++) {
|
|
23
22
|
try {
|
|
24
|
-
const result = await dependencies.
|
|
25
|
-
return uploadResult(result);
|
|
23
|
+
const result = await dependencies.dispatch(UPLOAD, { mediaType }, operation, body);
|
|
24
|
+
return uploadResult(requireResultKind('value', result).value);
|
|
26
25
|
}
|
|
27
26
|
catch (cause) {
|
|
28
27
|
last = cause;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/kernel-core/path';
|
|
2
|
-
import { execution as methods
|
|
2
|
+
import { execution as methods } from '@astrale-os/kernel-core/schema/kernel/syscalls/references';
|
|
3
3
|
import { kernelReference } from './reference.js';
|
|
4
4
|
const bound = {
|
|
5
|
-
status: kernelReference(methods.status
|
|
6
|
-
result: kernelReference(methods.result
|
|
7
|
-
cancel: kernelReference(methods.cancel
|
|
8
|
-
signal: kernelReference(methods.signal
|
|
9
|
-
resume: kernelReference(methods.resume
|
|
10
|
-
delete: kernelReference(methods.delete
|
|
5
|
+
status: kernelReference(methods.status),
|
|
6
|
+
result: kernelReference(methods.result),
|
|
7
|
+
cancel: kernelReference(methods.cancel),
|
|
8
|
+
signal: kernelReference(methods.signal),
|
|
9
|
+
resume: kernelReference(methods.resume),
|
|
10
|
+
delete: kernelReference(methods.delete),
|
|
11
11
|
};
|
|
12
12
|
function receiver(execution) {
|
|
13
13
|
return Path.id(typeof execution === 'string' ? execution : execution.id);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Path as PathValue, PathLike } from '@astrale-os/kernel-core/path';
|
|
2
|
-
import type { FunctionRef } from '@astrale-os/kernel-core/schema/kernel/syscalls/references';
|
|
3
2
|
import type { CallableInputOf, CallableInputModeOf, CallableOutputOf, CallableResultOf, Domain, ResolvedFunction, ExecutableMethod } from '@astrale-os/kernel-dsl/v1';
|
|
4
3
|
import type { MethodAddress, Revision } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
5
4
|
import type { MeaningFingerprint } from '@astrale-os/kernel-dsl/v1/schema/fingerprint';
|
|
@@ -12,7 +11,7 @@ declare const REFERENCE_OUTPUT: unique symbol;
|
|
|
12
11
|
export interface CallableReference<InputValue = Input, Output = unknown, InputMode extends 'value' | 'binary' = 'value' | 'binary'> {
|
|
13
12
|
readonly origin: KeyType.Origin;
|
|
14
13
|
/** @deprecated Informational only; dispatch pins `fingerprint` and revision pinning will be removed. */
|
|
15
|
-
readonly revision
|
|
14
|
+
readonly revision?: Revision;
|
|
16
15
|
/** Meaning fingerprint of the callable; a generated reference always carries one. */
|
|
17
16
|
readonly fingerprint?: MeaningFingerprint;
|
|
18
17
|
readonly callable: KeyType.Callable;
|
|
@@ -25,8 +24,8 @@ export interface CallableReference<InputValue = Input, Output = unknown, InputMo
|
|
|
25
24
|
export interface InstanceCallableReference<InputValue = Input, Output = unknown> {
|
|
26
25
|
(receiver: PathLike): CallableReference<InputValue, Output>;
|
|
27
26
|
readonly origin: KeyType.Origin;
|
|
28
|
-
readonly revision
|
|
29
|
-
readonly fingerprint
|
|
27
|
+
readonly revision?: Revision;
|
|
28
|
+
readonly fingerprint?: MeaningFingerprint;
|
|
30
29
|
readonly callable: KeyType.Callable;
|
|
31
30
|
readonly outputMode: 'value' | 'stream' | 'binary';
|
|
32
31
|
}
|
|
@@ -47,12 +46,10 @@ export declare function reference<const Callable extends ResolvedFunction | Exec
|
|
|
47
46
|
/** Project one exact resolved instance Method into a receiver-bound typed Client reference. */
|
|
48
47
|
export declare function reference<const Callable extends ExecutableInstanceMethod>(domain: Pick<Domain, 'origin' | 'revision'>, callable: Callable): InstanceCallableReference<ReferenceInput<Callable>, ReferenceOutput<Callable>>;
|
|
49
48
|
/**
|
|
50
|
-
* Reference a Kernel
|
|
51
|
-
*
|
|
52
|
-
* callable referenced this way answers a value.
|
|
49
|
+
* Reference a Kernel instance Method by its Core coordinate, without a meaning pin, as the Session
|
|
50
|
+
* addresses every Kernel syscall. Loading the Session therefore never resolves the Kernel Schema.
|
|
53
51
|
*/
|
|
54
|
-
export declare function kernelReference(
|
|
55
|
-
export declare function kernelReference(callable: MethodAddress, fingerprint: MeaningFingerprint): InstanceCallableReference<unknown>;
|
|
52
|
+
export declare function kernelReference(method: MethodAddress): InstanceCallableReference<unknown>;
|
|
56
53
|
export declare function admitReference(input: CallableReference<unknown>): CallableReference<unknown>;
|
|
57
54
|
export declare function matchesCallable(target: PathValue, callable: KeyType.Callable): boolean;
|
|
58
55
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Path } from '@astrale-os/kernel-core/path';
|
|
2
|
-
import { meaning } from '@astrale-os/kernel-core/schema/kernel/syscalls/references';
|
|
3
2
|
import { callableAddress, Key, MethodKey, patterns, } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
4
3
|
import { callableFingerprint } from '@astrale-os/kernel-dsl/v1/schema/fingerprint';
|
|
5
4
|
const FINGERPRINT_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
@@ -27,19 +26,18 @@ export function reference(domain, callable) {
|
|
|
27
26
|
: Path.project(callable.ref);
|
|
28
27
|
return admitReference({ ...common, target });
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Reference a Kernel instance Method by its Core coordinate, without a meaning pin, as the Session
|
|
31
|
+
* addresses every Kernel syscall. Loading the Session therefore never resolves the Kernel Schema.
|
|
32
|
+
*/
|
|
33
|
+
export function kernelReference(method) {
|
|
34
|
+
const callable = MethodKey.of(method.owner, method.name);
|
|
32
35
|
const common = Object.freeze({
|
|
33
|
-
origin: Key.origin(
|
|
34
|
-
|
|
35
|
-
fingerprint,
|
|
36
|
-
callable: key,
|
|
36
|
+
origin: Key.origin(callable),
|
|
37
|
+
callable,
|
|
37
38
|
outputMode: 'value',
|
|
38
39
|
});
|
|
39
|
-
|
|
40
|
-
return admitReference({ ...common, target: Path.project(callable) });
|
|
41
|
-
const method = key;
|
|
42
|
-
const bind = (receiver) => admitReference({ ...common, target: Path.instanceMethod(Path.from(receiver), method) });
|
|
40
|
+
const bind = (receiver) => admitReference({ ...common, target: Path.instanceMethod(Path.from(receiver), callable) });
|
|
43
41
|
return Object.assign(bind, common);
|
|
44
42
|
}
|
|
45
43
|
export function admitReference(input) {
|
|
@@ -47,8 +45,8 @@ export function admitReference(input) {
|
|
|
47
45
|
invalid();
|
|
48
46
|
if (typeof input.origin !== 'string' ||
|
|
49
47
|
!patterns.origin.test(input.origin) ||
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
(input.revision !== undefined &&
|
|
49
|
+
(typeof input.revision !== 'string' || !patterns.revision.test(input.revision))) ||
|
|
52
50
|
typeof input.callable !== 'string' ||
|
|
53
51
|
!Key.is(input.callable) ||
|
|
54
52
|
Key.origin(input.callable) !== input.origin ||
|
|
@@ -61,7 +59,7 @@ export function admitReference(input) {
|
|
|
61
59
|
invalid();
|
|
62
60
|
return Object.freeze({
|
|
63
61
|
origin: input.origin,
|
|
64
|
-
revision: input.revision,
|
|
62
|
+
...(input.revision === undefined ? {} : { revision: input.revision }),
|
|
65
63
|
...(input.fingerprint === undefined ? {} : { fingerprint: input.fingerprint }),
|
|
66
64
|
callable: input.callable,
|
|
67
65
|
target,
|
|
@@ -84,7 +84,6 @@ export declare class ClientSession {
|
|
|
84
84
|
invoke<const Reference extends CallableReference<unknown>>(reference: InputModeOfReference<Reference> extends 'binary' ? never : Reference, input: InputOfReference<Reference>, options?: SessionRequestOptions): Promise<OutputOfReference<Reference>>;
|
|
85
85
|
invoke<const Reference extends CallableReference<unknown, unknown, 'binary'>>(reference: Reference, input: InputOfReference<Reference>, options: SessionRequestOptionsWithBody): Promise<OutputOfReference<Reference>>;
|
|
86
86
|
private dispatchWith;
|
|
87
|
-
private invokeWith;
|
|
88
87
|
stream(call: Call, options?: SessionRequestOptions): Promise<import("../client/result.js").Stream>;
|
|
89
88
|
binary(call: Call, options?: SessionRequestOptions): Promise<import("@astrale-os/kernel-core/callable").BinaryOutput>;
|
|
90
89
|
close(): void;
|
package/dist/session/session.js
CHANGED
|
@@ -126,7 +126,6 @@ export class ClientSession {
|
|
|
126
126
|
});
|
|
127
127
|
this.content = createContentApi({
|
|
128
128
|
operation: (options) => this.operation(options),
|
|
129
|
-
invoke: (reference, input, options, operation) => this.invokeWith(reference, input, options, operation),
|
|
130
129
|
dispatch: (target, input, operation, body) => this.dispatcher.dispatchHttp({ target, input }, {}, operation, body),
|
|
131
130
|
});
|
|
132
131
|
this.schema = createSchema((request, options) => this.dispatch(request, options), {
|
|
@@ -172,16 +171,6 @@ export class ClientSession {
|
|
|
172
171
|
return requireResultKind('value', result).value;
|
|
173
172
|
}
|
|
174
173
|
async invoke(reference, input, options) {
|
|
175
|
-
return this.invokeWith(reference, input, options);
|
|
176
|
-
}
|
|
177
|
-
dispatchWith(call, options, requireHttp = false) {
|
|
178
|
-
const admitted = admitInvocationOptions(options);
|
|
179
|
-
const operation = this.operation(admitted.options);
|
|
180
|
-
return admitted.body === undefined && !requireHttp
|
|
181
|
-
? this.dispatcher.dispatch(call, admitted.options, operation)
|
|
182
|
-
: this.dispatcher.dispatchHttp(call, admitted.options, operation, admitted.body);
|
|
183
|
-
}
|
|
184
|
-
async invokeWith(reference, input, options, sharedOperation) {
|
|
185
174
|
const admittedReference = admitReference(reference);
|
|
186
175
|
const admitted = admitInvocationOptions(options);
|
|
187
176
|
const { anchor, steps } = admittedReference.target.ast;
|
|
@@ -189,18 +178,21 @@ export class ClientSession {
|
|
|
189
178
|
const instance = last?.kind === 'method' && last.dispatch === 'instance'
|
|
190
179
|
? Path.fromAST({ anchor, steps: steps.slice(0, -1) }).raw
|
|
191
180
|
: undefined;
|
|
192
|
-
// A generated reference pins the callable meaning
|
|
181
|
+
// A generated reference pins the callable meaning, a hand-written one its revision, and a Kernel
|
|
182
|
+
// reference nothing, like every other syscall the Session addresses.
|
|
193
183
|
const call = {
|
|
194
184
|
target: Object.freeze({
|
|
195
185
|
callable: admittedReference.callable,
|
|
196
|
-
...(admittedReference.fingerprint
|
|
197
|
-
? {
|
|
198
|
-
:
|
|
186
|
+
...(admittedReference.fingerprint !== undefined
|
|
187
|
+
? { fingerprint: admittedReference.fingerprint }
|
|
188
|
+
: admittedReference.revision !== undefined
|
|
189
|
+
? { revision: admittedReference.revision }
|
|
190
|
+
: {}),
|
|
199
191
|
...(instance === undefined ? {} : { instance }),
|
|
200
192
|
}),
|
|
201
193
|
input,
|
|
202
194
|
};
|
|
203
|
-
const operation =
|
|
195
|
+
const operation = this.operation(admitted.options);
|
|
204
196
|
const result = await (admitted.body === undefined && admittedReference.outputMode !== 'binary'
|
|
205
197
|
? this.dispatcher.dispatch(call, admitted.options, operation)
|
|
206
198
|
: this.dispatcher.dispatchHttp(call, admitted.options, operation, admitted.body));
|
|
@@ -211,6 +203,13 @@ export class ClientSession {
|
|
|
211
203
|
? narrowed.stream
|
|
212
204
|
: narrowed.value;
|
|
213
205
|
}
|
|
206
|
+
dispatchWith(call, options, requireHttp = false) {
|
|
207
|
+
const admitted = admitInvocationOptions(options);
|
|
208
|
+
const operation = this.operation(admitted.options);
|
|
209
|
+
return admitted.body === undefined && !requireHttp
|
|
210
|
+
? this.dispatcher.dispatch(call, admitted.options, operation)
|
|
211
|
+
: this.dispatcher.dispatchHttp(call, admitted.options, operation, admitted.body);
|
|
212
|
+
}
|
|
214
213
|
async stream(call, options) {
|
|
215
214
|
const result = await this.dispatch(call, options);
|
|
216
215
|
return requireResultKind('stream', result).stream;
|
|
@@ -64,7 +64,7 @@ export class HttpByteStream {
|
|
|
64
64
|
}
|
|
65
65
|
this.bytes += next.value.byteLength;
|
|
66
66
|
if (this.expectedBytes !== undefined && this.bytes > this.expectedBytes) {
|
|
67
|
-
throw new ProtocolError(`streaming binary exceeded declared
|
|
67
|
+
throw new ProtocolError(`streaming binary exceeded its declared length (${this.expectedBytes}).`);
|
|
68
68
|
}
|
|
69
69
|
if (this.bytes > this.maximumBytes) {
|
|
70
70
|
throw new ProtocolError(`streaming binary exceeds maximumResponseBytes (${this.maximumBytes}).`);
|
|
@@ -197,11 +197,14 @@ export class HttpTransport {
|
|
|
197
197
|
if (streaming) {
|
|
198
198
|
if (response.body === null)
|
|
199
199
|
throw new ProtocolError('streaming binary has no body.');
|
|
200
|
-
|
|
200
|
+
// An intermediary may rewrite Content-Length (for example when it compresses the body), so the
|
|
201
|
+
// protocol length wins; Content-Length only counts for a Server that predates the field.
|
|
202
|
+
const declared = response.headers.get(protocol.http.BINARY_LENGTH_FIELD);
|
|
203
|
+
const length = declared ?? response.headers.get('content-length');
|
|
201
204
|
const expectedBytes = length === null ? undefined : Number(length);
|
|
202
205
|
if (length !== null &&
|
|
203
206
|
(!/^(?:0|[1-9][0-9]*)$/u.test(length) || !Number.isSafeInteger(expectedBytes))) {
|
|
204
|
-
throw new ProtocolError(
|
|
207
|
+
throw new ProtocolError(`streaming binary has an invalid ${declared === null ? 'Content-Length' : protocol.http.BINARY_LENGTH_FIELD}.`);
|
|
205
208
|
}
|
|
206
209
|
if (expectedBytes !== undefined && expectedBytes > this.maximumResponseBytes) {
|
|
207
210
|
throw new ProtocolError(`streaming binary exceeds maximumResponseBytes (${this.maximumResponseBytes}).`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/kernel-client",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.79",
|
|
4
4
|
"description": "Universal Client — protocol-first, transport-agnostic",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -65,14 +65,14 @@
|
|
|
65
65
|
"access": "public"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@astrale-os/kernel-core": "0.9.0-beta.
|
|
69
|
-
"@astrale-os/kernel-dsl": "0.2.0-beta.
|
|
70
|
-
"@astrale-os/kernel-protocol": "0.5.0-beta.
|
|
68
|
+
"@astrale-os/kernel-core": "0.9.0-beta.59",
|
|
69
|
+
"@astrale-os/kernel-dsl": "0.2.0-beta.43",
|
|
70
|
+
"@astrale-os/kernel-protocol": "0.5.0-beta.65",
|
|
71
71
|
"@tanstack/pacer": "0.20.1",
|
|
72
72
|
"msgpackr": "^2.0.5"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@astrale-os/kernel-server": "0.5.0-beta.
|
|
75
|
+
"@astrale-os/kernel-server": "0.5.0-beta.67",
|
|
76
76
|
"@astrale/typescript-config": "npm:@jsr/astrale__typescript-config@~1.1.0",
|
|
77
77
|
"@types/node": "^22.0.0",
|
|
78
78
|
"typescript": "7.0.2",
|