@astrale-os/kernel-client 0.6.0-beta.40 → 0.6.0-beta.42
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/schema/index.d.ts +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/schema/operation.js +1 -1
- package/dist/schema/schema.d.ts +2 -0
- package/dist/schema/schema.js +14 -4
- package/dist/session/routing/cache.js +7 -5
- package/dist/session/session.d.ts +1 -0
- package/dist/session/session.js +4 -2
- package/dist/session/session.typecheck.js +0 -1
- package/dist/store/store.typecheck.js +0 -5
- package/dist/transport/auto/warning.js +5 -2
- package/dist/transport/http/transport.js +3 -2
- package/dist/transport/websocket/connection.js +5 -2
- package/dist/transport/websocket/transport.js +1 -1
- package/package.json +5 -5
- package/CHANGELOG.md +0 -1009
package/dist/schema/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createSchema, type SchemaApi, type SchemaCall, type SchemaOptions, type DomainBundle, type DomainInfo, type InstallResult, } from './schema.js';
|
|
1
|
+
export { createSchema, DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS, type SchemaApi, type SchemaCall, type SchemaOptions, type DomainBundle, type DomainInfo, type InstallResult, } from './schema.js';
|
|
2
2
|
export { acceptOperationId, createOperationId } from './operation.js';
|
|
3
3
|
export type { DomainRequest, InstallReceipt, InstallRequest, OperationId, UninstallReceipt, UninstallRequest, } from './contracts.js';
|
package/dist/schema/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createSchema, } from './schema.js';
|
|
1
|
+
export { createSchema, DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS, } from './schema.js';
|
|
2
2
|
export { acceptOperationId, createOperationId } from './operation.js';
|
package/dist/schema/operation.js
CHANGED
|
@@ -5,5 +5,5 @@ export function acceptOperationId(input) {
|
|
|
5
5
|
}
|
|
6
6
|
/** Allocate one cryptographically generated operation identity for a new lifecycle request. */
|
|
7
7
|
export function createOperationId() {
|
|
8
|
-
return acceptOperationId(
|
|
8
|
+
return acceptOperationId(crypto.randomUUID());
|
|
9
9
|
}
|
package/dist/schema/schema.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { RequestOptions } from '../transport/index.js';
|
|
|
5
5
|
import type { InstallRequest, UninstallReceipt, UninstallRequest } from './contracts.js';
|
|
6
6
|
export type SchemaCall = (call: Call, options?: RequestOptions) => Promise<Result>;
|
|
7
7
|
export interface SchemaOptions extends protocolSchema.installation.introspection.Limits {
|
|
8
|
+
readonly lifecycleTimeoutMs?: number;
|
|
8
9
|
}
|
|
9
10
|
export type DomainInfo = protocolSchema.installation.introspection.DomainInfo;
|
|
10
11
|
export type DomainBundle = protocolSchema.installation.introspection.DomainBundle;
|
|
@@ -15,5 +16,6 @@ export interface SchemaApi {
|
|
|
15
16
|
inspect(origin: Key.Origin, options?: RequestOptions): Promise<DomainInfo>;
|
|
16
17
|
bundle(origin: Key.Origin, options?: RequestOptions): Promise<DomainBundle>;
|
|
17
18
|
}
|
|
19
|
+
export declare const DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS = 110000;
|
|
18
20
|
/** Bind the canonical Schema lifecycle syscalls to one caller-scoped value-call capability. */
|
|
19
21
|
export declare function createSchema(call: SchemaCall, limits: SchemaOptions): SchemaApi;
|
package/dist/schema/schema.js
CHANGED
|
@@ -7,6 +7,7 @@ import { acceptOutput } from './admission.js';
|
|
|
7
7
|
import { introspect } from './introspection.js';
|
|
8
8
|
const INSTALL_PATH = Path.project(install).raw;
|
|
9
9
|
const UNINSTALL_PATH = Path.project(uninstall).raw;
|
|
10
|
+
export const DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS = 110_000;
|
|
10
11
|
/** Bind the canonical Schema lifecycle syscalls to one caller-scoped value-call capability. */
|
|
11
12
|
export function createSchema(call, limits) {
|
|
12
13
|
if (typeof call !== 'function')
|
|
@@ -14,7 +15,7 @@ export function createSchema(call, limits) {
|
|
|
14
15
|
const admittedLimits = admitLimits(limits);
|
|
15
16
|
return Object.freeze({
|
|
16
17
|
async install(request, options) {
|
|
17
|
-
const result = await call(invocation(INSTALL_PATH, request), options);
|
|
18
|
+
const result = await call(invocation(INSTALL_PATH, request), lifecycleOptions(options, admittedLimits.lifecycleTimeoutMs));
|
|
18
19
|
if (result.kind !== 'binary') {
|
|
19
20
|
throw new TypeError('Schema install returned a non-binary result.');
|
|
20
21
|
}
|
|
@@ -30,7 +31,7 @@ export function createSchema(call, limits) {
|
|
|
30
31
|
return decoded;
|
|
31
32
|
},
|
|
32
33
|
async uninstall(request, options) {
|
|
33
|
-
const output = value(await call(invocation(UNINSTALL_PATH, request), options));
|
|
34
|
+
const output = value(await call(invocation(UNINSTALL_PATH, request), lifecycleOptions(options, admittedLimits.lifecycleTimeoutMs)));
|
|
34
35
|
return acceptOutput(uninstallOutput, output);
|
|
35
36
|
},
|
|
36
37
|
async inspect(origin, options) {
|
|
@@ -68,13 +69,22 @@ function value(result) {
|
|
|
68
69
|
return result.value;
|
|
69
70
|
}
|
|
70
71
|
function admitLimits(input) {
|
|
72
|
+
const lifecycleTimeoutMs = input?.lifecycleTimeoutMs ?? DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS;
|
|
71
73
|
if (input === null ||
|
|
72
74
|
typeof input !== 'object' ||
|
|
73
75
|
!Number.isSafeInteger(input.maximumPublicationBytes) ||
|
|
74
76
|
input.maximumPublicationBytes < 1 ||
|
|
75
77
|
!Number.isSafeInteger(input.maximumBundleBytes) ||
|
|
76
|
-
input.maximumBundleBytes < 1
|
|
78
|
+
input.maximumBundleBytes < 1 ||
|
|
79
|
+
!Number.isSafeInteger(lifecycleTimeoutMs) ||
|
|
80
|
+
lifecycleTimeoutMs < 1 ||
|
|
81
|
+
lifecycleTimeoutMs > 2_147_483_647) {
|
|
77
82
|
throw new TypeError('Schema introspection limits are invalid.');
|
|
78
83
|
}
|
|
79
|
-
return Object.freeze({ ...input });
|
|
84
|
+
return Object.freeze({ ...input, lifecycleTimeoutMs });
|
|
85
|
+
}
|
|
86
|
+
function lifecycleOptions(options, timeoutMs) {
|
|
87
|
+
if (options?.timeoutMs !== undefined)
|
|
88
|
+
return options;
|
|
89
|
+
return Object.freeze({ ...options, timeoutMs });
|
|
80
90
|
}
|
|
@@ -228,12 +228,13 @@ function refreshBoundary(credentialExpiry) {
|
|
|
228
228
|
: credentialExpiry - DESTINATION_CREDENTIAL_REFRESH_SKEW_MS;
|
|
229
229
|
}
|
|
230
230
|
async function routeIntegrity(sourceCredential, key, learned) {
|
|
231
|
-
|
|
231
|
+
const webCrypto = typeof crypto === 'undefined' ? undefined : crypto;
|
|
232
|
+
if (webCrypto?.subtle === undefined) {
|
|
232
233
|
throw new TypeError('Persistent Session route integrity requires Web Crypto HMAC support.');
|
|
233
234
|
}
|
|
234
235
|
const algorithm = Object.freeze({ name: 'HMAC', hash: 'SHA-256' });
|
|
235
|
-
const cryptographicKey = await
|
|
236
|
-
const signature = await
|
|
236
|
+
const cryptographicKey = await webCrypto.subtle.importKey('raw', new TextEncoder().encode(sourceCredential), algorithm, false, ['sign']);
|
|
237
|
+
const signature = await webCrypto.subtle.sign(algorithm, cryptographicKey, new TextEncoder().encode(integrityMaterial(key, learned)));
|
|
237
238
|
return encodeBase64Url(new Uint8Array(signature));
|
|
238
239
|
}
|
|
239
240
|
async function matchesIntegrity(sourceCredential, key, learned, expected) {
|
|
@@ -281,10 +282,11 @@ function canonicalExpr(input) {
|
|
|
281
282
|
return { kind: input.kind, operands: input.operands.map(canonicalExpr) };
|
|
282
283
|
}
|
|
283
284
|
async function digestText(input) {
|
|
284
|
-
|
|
285
|
+
const webCrypto = typeof crypto === 'undefined' ? undefined : crypto;
|
|
286
|
+
if (webCrypto?.subtle === undefined) {
|
|
285
287
|
throw new TypeError('Session route caching requires Web Crypto SHA-256 support.');
|
|
286
288
|
}
|
|
287
|
-
const bytes = await
|
|
289
|
+
const bytes = await webCrypto.subtle.digest('SHA-256', new TextEncoder().encode(input));
|
|
288
290
|
return [...new Uint8Array(bytes)].map((byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
289
291
|
}
|
|
290
292
|
function jwtExpiry(input) {
|
|
@@ -30,6 +30,7 @@ export interface ClientSessionOptions {
|
|
|
30
30
|
readonly maximumBundleBytes?: number;
|
|
31
31
|
readonly maximumResponseBytes?: number;
|
|
32
32
|
readonly timeoutMs?: number;
|
|
33
|
+
readonly schemaLifecycleTimeoutMs?: number;
|
|
33
34
|
}
|
|
34
35
|
export type ConnectOptions = Omit<ClientSessionOptions, 'kernel' | 'policy'> & {
|
|
35
36
|
readonly url: string;
|
package/dist/session/session.js
CHANGED
|
@@ -4,7 +4,7 @@ import { SessionError } from '../errors/index.js';
|
|
|
4
4
|
import { createDomainBinding } from '../graph/domain/binding.js';
|
|
5
5
|
import { createGraph } from '../graph/index.js';
|
|
6
6
|
import { ClientPool } from '../pool/index.js';
|
|
7
|
-
import { createSchema } from '../schema/index.js';
|
|
7
|
+
import { createSchema, DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS } from '../schema/index.js';
|
|
8
8
|
import { AutoTransport } from '../transport/auto/index.js';
|
|
9
9
|
import { HttpTransport } from '../transport/http/index.js';
|
|
10
10
|
import { WebSocketTransport } from '../transport/websocket/index.js';
|
|
@@ -46,11 +46,12 @@ export class ClientSession {
|
|
|
46
46
|
const maximumBundleBytes = positive(options.maximumBundleBytes ?? DEFAULT_MAXIMUM_BUNDLE_BYTES, 'maximumBundleBytes');
|
|
47
47
|
const maximumResponseBytes = positive(options.maximumResponseBytes ?? DEFAULT_MAXIMUM_RESPONSE_BYTES, 'maximumResponseBytes');
|
|
48
48
|
const timeoutMs = platformDuration(options.timeoutMs ?? DEFAULT_TIMEOUT_MS, 'timeoutMs');
|
|
49
|
+
const schemaLifecycleTimeoutMs = platformDuration(options.schemaLifecycleTimeoutMs ?? DEFAULT_SCHEMA_LIFECYCLE_TIMEOUT_MS, 'schemaLifecycleTimeoutMs');
|
|
49
50
|
this.timeoutMs = timeoutMs;
|
|
50
51
|
const websocketAvailable = options.client !== undefined ||
|
|
51
52
|
options.pool !== undefined ||
|
|
52
53
|
options.websocket !== undefined ||
|
|
53
|
-
typeof
|
|
54
|
+
typeof WebSocket === 'function';
|
|
54
55
|
const client = options.client ??
|
|
55
56
|
defaultClientFactory({
|
|
56
57
|
fetch: fetchImpl,
|
|
@@ -92,6 +93,7 @@ export class ClientSession {
|
|
|
92
93
|
this.schema = createSchema((request, options) => this.dispatch(request, options), {
|
|
93
94
|
maximumPublicationBytes,
|
|
94
95
|
maximumBundleBytes,
|
|
96
|
+
lifecycleTimeoutMs: schemaLifecycleTimeoutMs,
|
|
95
97
|
});
|
|
96
98
|
this.graph = createGraph((call, options) => this.call(call, options));
|
|
97
99
|
}
|
|
@@ -28,4 +28,3 @@ const abstractDomain = schema.resolve(defineSchema('client.reference.typecheck',
|
|
|
28
28
|
}));
|
|
29
29
|
// @ts-expect-error Abstract Methods are declarations and cannot become Client references.
|
|
30
30
|
reference(abstractDomain, abstractDomain.classes.AbstractItem.methods.abstractMethod);
|
|
31
|
-
void {};
|
|
@@ -13,9 +13,6 @@ const edgeProjection = edgeSource.select({
|
|
|
13
13
|
const read = store.read(nodeGraph, options);
|
|
14
14
|
const peek = store.peek(nodeProjection, options);
|
|
15
15
|
const snapshot = store.getSnapshot(edgeProjection, options);
|
|
16
|
-
// @ts-expect-error ready is the successful local snapshot state; fresh is retired.
|
|
17
|
-
const retiredFreshSnapshot = { state: 'fresh' };
|
|
18
|
-
void retiredFreshSnapshot;
|
|
19
16
|
store.subscribe(nodeGraph, () => undefined, options);
|
|
20
17
|
store.observe(nodeProjection, (value) => {
|
|
21
18
|
if ('response' in value && value.response !== undefined) {
|
|
@@ -35,6 +32,4 @@ store.subscribe(nodeGraph, () => undefined);
|
|
|
35
32
|
store.getSnapshot(nodeGraph);
|
|
36
33
|
// @ts-expect-error Query-page identity is required for observations.
|
|
37
34
|
store.observe(nodeGraph, () => undefined);
|
|
38
|
-
// @ts-expect-error watch is reserved for a future remote-liveness protocol.
|
|
39
|
-
store.watch(nodeGraph, () => undefined, options);
|
|
40
35
|
void {};
|
|
@@ -9,8 +9,11 @@ export function emitWarning(warning, observer) {
|
|
|
9
9
|
try {
|
|
10
10
|
if (observer)
|
|
11
11
|
observer(warning);
|
|
12
|
-
else
|
|
13
|
-
globalThis
|
|
12
|
+
else {
|
|
13
|
+
const runtimeConsole = globalThis
|
|
14
|
+
.console;
|
|
15
|
+
runtimeConsole?.warn(formatClientWarning(warning));
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
catch {
|
|
16
19
|
// Warning observation cannot change invocation settlement.
|
|
@@ -141,7 +141,7 @@ export class HttpTransport {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
async fetchResponse(codec, encoded, trace, control) {
|
|
144
|
-
|
|
144
|
+
const init = {
|
|
145
145
|
method: 'POST',
|
|
146
146
|
redirect: 'manual',
|
|
147
147
|
credentials: 'omit',
|
|
@@ -155,7 +155,8 @@ export class HttpTransport {
|
|
|
155
155
|
},
|
|
156
156
|
body: toBody(encoded),
|
|
157
157
|
signal: control.signal,
|
|
158
|
-
}
|
|
158
|
+
};
|
|
159
|
+
return this.fetchImpl(this.url, init);
|
|
159
160
|
}
|
|
160
161
|
async decodeBinary(response, requestId, binaryId, streaming, control) {
|
|
161
162
|
if (binaryId !== String(requestId)) {
|
|
@@ -81,7 +81,7 @@ export class WebSocketConnection {
|
|
|
81
81
|
control.close();
|
|
82
82
|
return Promise.reject(invocationFailure(`WebSocket request exceeds maximumRequestBytes (${this.maximumRequestBytes}).`, 'send'));
|
|
83
83
|
}
|
|
84
|
-
if (this.socket
|
|
84
|
+
if (socketBufferedAmount(this.socket) + encodedBytes > this.maximumBufferedBytes) {
|
|
85
85
|
control.close();
|
|
86
86
|
return Promise.reject(invocationFailure(`WebSocket connection exceeds maximumBufferedBytes (${this.maximumBufferedBytes}).`, 'send'));
|
|
87
87
|
}
|
|
@@ -105,7 +105,7 @@ export class WebSocketConnection {
|
|
|
105
105
|
const encoded = this.codec.encode(protocol.decodeCancelRequest({ version: protocol.VERSION, kind: 'cancel', requestId }));
|
|
106
106
|
const bytes = frameSize(encoded);
|
|
107
107
|
if (bytes > this.maximumRequestBytes ||
|
|
108
|
-
this.socket
|
|
108
|
+
socketBufferedAmount(this.socket) + bytes > this.maximumBufferedBytes) {
|
|
109
109
|
throw new WebSocketBindingFailure('WebSocket cancellation exceeds the connection send policy.', 1008, 'Client send policy exceeded');
|
|
110
110
|
}
|
|
111
111
|
this.socket.send(socketData(encoded));
|
|
@@ -180,6 +180,9 @@ export class WebSocketConnection {
|
|
|
180
180
|
this.shutdown(invocationFailure(message, this.opened ? 'receive' : 'connect'));
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
function socketBufferedAmount(socket) {
|
|
184
|
+
return socket.bufferedAmount ?? 0;
|
|
185
|
+
}
|
|
183
186
|
async function normalizeFrame(input) {
|
|
184
187
|
if (typeof input === 'string')
|
|
185
188
|
return input;
|
|
@@ -140,7 +140,7 @@ function admitCodecName(input) {
|
|
|
140
140
|
throw new TypeError('codec must be "json" or "msgpack".');
|
|
141
141
|
}
|
|
142
142
|
function admitWebSocket(input) {
|
|
143
|
-
const implementation = input ??
|
|
143
|
+
const implementation = input ?? (typeof WebSocket === 'undefined' ? undefined : WebSocket);
|
|
144
144
|
if (typeof implementation !== 'function') {
|
|
145
145
|
throw new TypeError('WebSocketTransport requires a WebSocket implementation.');
|
|
146
146
|
}
|
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.42",
|
|
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.31",
|
|
69
|
+
"@astrale-os/kernel-dsl": "0.2.0-beta.24",
|
|
70
|
+
"@astrale-os/kernel-protocol": "0.5.0-beta.34",
|
|
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.36",
|
|
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",
|