@astrale-os/kernel-client 0.6.0-beta.82 → 0.6.0-beta.83

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.
Files changed (58) hide show
  1. package/dist/client/admission.js +1 -9
  2. package/dist/client/client.js +3 -1
  3. package/dist/client/index.d.ts +1 -1
  4. package/dist/client/index.js +1 -1
  5. package/dist/client/routing-control.d.ts +11 -3
  6. package/dist/client/routing-control.js +18 -3
  7. package/dist/errors/stale-route-error.d.ts +14 -3
  8. package/dist/errors/stale-route-error.js +15 -3
  9. package/dist/errors/transport-error.d.ts +3 -2
  10. package/dist/index.d.ts +1 -1
  11. package/dist/schema/introspection.js +10 -6
  12. package/dist/schema/schema.js +10 -2
  13. package/dist/session/content.js +4 -27
  14. package/dist/session/context/index.d.ts +1 -1
  15. package/dist/session/context/index.js +1 -1
  16. package/dist/session/context/limits.d.ts +1 -0
  17. package/dist/session/context/limits.js +1 -0
  18. package/dist/session/discovery/bundle.js +1 -1
  19. package/dist/session/discovery/discovery.d.ts +12 -4
  20. package/dist/session/discovery/discovery.js +28 -19
  21. package/dist/session/discovery/index.d.ts +8 -1
  22. package/dist/session/discovery/index.js +5 -8
  23. package/dist/session/discovery/legacy/index.d.ts +38 -0
  24. package/dist/session/discovery/legacy/index.js +38 -0
  25. package/dist/session/discovery/legacy/intrinsic.d.ts +33 -0
  26. package/dist/session/discovery/legacy/intrinsic.js +67 -0
  27. package/dist/session/discovery/legacy/projection.d.ts +8 -0
  28. package/dist/session/discovery/legacy/projection.js +17 -0
  29. package/dist/session/discovery/{publication.d.ts → legacy/publication.d.ts} +8 -5
  30. package/dist/session/discovery/{publication.js → legacy/publication.js} +13 -60
  31. package/dist/session/discovery/release.d.ts +29 -0
  32. package/dist/session/discovery/release.js +81 -0
  33. package/dist/session/discovery/response.d.ts +3 -0
  34. package/dist/session/discovery/response.js +59 -0
  35. package/dist/session/discovery/snapshot.d.ts +9 -2
  36. package/dist/session/discovery/snapshot.js +2 -2
  37. package/dist/session/discovery/transport.d.ts +1 -3
  38. package/dist/session/exchange/options.d.ts +13 -10
  39. package/dist/session/exchange/options.js +6 -5
  40. package/dist/session/exchange/presentation.d.ts +3 -2
  41. package/dist/session/index.d.ts +1 -1
  42. package/dist/session/reference.d.ts +3 -6
  43. package/dist/session/reference.js +0 -4
  44. package/dist/session/routing/dispatch.js +36 -37
  45. package/dist/session/session.d.ts +18 -3
  46. package/dist/session/session.js +21 -10
  47. package/dist/session/session.typecheck.js +14 -3
  48. package/dist/transport/auto/transport.d.ts +0 -1
  49. package/dist/transport/auto/transport.js +0 -2
  50. package/dist/transport/http/response.d.ts +1 -1
  51. package/dist/transport/http/response.js +2 -6
  52. package/dist/transport/http/transport.d.ts +5 -3
  53. package/dist/transport/http/transport.js +40 -19
  54. package/dist/transport/index.d.ts +1 -1
  55. package/dist/transport/transport.d.ts +10 -2
  56. package/dist/transport/websocket/transport.d.ts +0 -3
  57. package/dist/transport/websocket/transport.js +1 -16
  58. package/package.json +6 -7
@@ -0,0 +1,33 @@
1
+ import type { release } from '@astrale-os/kernel-protocol';
2
+ import type { SessionOperation } from '../../context/index.js';
3
+ import type { IntrinsicRelease } from '../release.js';
4
+ import type { PublicationDiscovery } from './publication.js';
5
+ /** Reprobe absent `release.json` after this interval so Host upgrades become visible. */
6
+ export declare const RELEASE_ABSENCE_MAXIMUM_AGE_MS = 60000;
7
+ export interface ReleaseReader {
8
+ current(): release.DomainRelease | undefined;
9
+ revalidate(operation: SessionOperation): Promise<release.DomainRelease | undefined>;
10
+ invalidate(): void;
11
+ }
12
+ /**
13
+ * The intrinsic release of a Host whichever document it serves. `release.json` is read first; the
14
+ * version 3 Publication at `domain.json` is read in its place only when that URL answers its exact
15
+ * 404, or, while no `DomainRelease` has been admitted since construction, the last `invalidate()`
16
+ * or the last 404, when its Fetch fails before any response, which is how a browser reports a
17
+ * cross-origin 404 without CORS headers. Once a release is admitted, a failed Fetch is the release
18
+ * acquisition failure it is. An observed absence is trusted for `RELEASE_ABSENCE_MAXIMUM_AGE_MS`.
19
+ *
20
+ * @deprecated Successor: `ReleaseDiscovery` alone, once no supported Host predates intrinsic
21
+ * `release.json`. Deleted with this folder (see `index.ts`).
22
+ */
23
+ export declare class IntrinsicReleaseDiscovery {
24
+ private readonly releases;
25
+ private readonly publications;
26
+ private absentUntil;
27
+ private revision;
28
+ constructor(releases: ReleaseReader, publications: PublicationDiscovery);
29
+ revalidate(operation: SessionOperation): Promise<IntrinsicRelease>;
30
+ invalidate(): void;
31
+ }
32
+ /** The digest designating one intrinsic release: its release digest, or a pre-v4 ETag. */
33
+ export declare function designation(input: IntrinsicRelease): string;
@@ -0,0 +1,67 @@
1
+ import { TransportError } from '../../../errors/index.js';
2
+ /** Reprobe absent `release.json` after this interval so Host upgrades become visible. */
3
+ export const RELEASE_ABSENCE_MAXIMUM_AGE_MS = 60_000;
4
+ /**
5
+ * The intrinsic release of a Host whichever document it serves. `release.json` is read first; the
6
+ * version 3 Publication at `domain.json` is read in its place only when that URL answers its exact
7
+ * 404, or, while no `DomainRelease` has been admitted since construction, the last `invalidate()`
8
+ * or the last 404, when its Fetch fails before any response, which is how a browser reports a
9
+ * cross-origin 404 without CORS headers. Once a release is admitted, a failed Fetch is the release
10
+ * acquisition failure it is. An observed absence is trusted for `RELEASE_ABSENCE_MAXIMUM_AGE_MS`.
11
+ *
12
+ * @deprecated Successor: `ReleaseDiscovery` alone, once no supported Host predates intrinsic
13
+ * `release.json`. Deleted with this folder (see `index.ts`).
14
+ */
15
+ export class IntrinsicReleaseDiscovery {
16
+ releases;
17
+ publications;
18
+ absentUntil;
19
+ revision = 0;
20
+ constructor(releases, publications) {
21
+ this.releases = releases;
22
+ this.publications = publications;
23
+ }
24
+ async revalidate(operation) {
25
+ const revision = this.revision;
26
+ if (this.absentUntil === undefined || Date.now() >= this.absentUntil) {
27
+ const admitted = this.releases.current() !== undefined;
28
+ let served;
29
+ try {
30
+ served = await this.releases.revalidate(operation);
31
+ }
32
+ catch (cause) {
33
+ if (admitted || !isUnreadableAbsence(cause))
34
+ throw cause;
35
+ served = undefined;
36
+ }
37
+ if (revision !== this.revision)
38
+ return this.revalidate(operation);
39
+ this.absentUntil = undefined;
40
+ if (served !== undefined)
41
+ return served;
42
+ }
43
+ const legacy = await this.publications.revalidate(operation);
44
+ if (revision !== this.revision)
45
+ return this.revalidate(operation);
46
+ this.absentUntil ??= Date.now() + RELEASE_ABSENCE_MAXIMUM_AGE_MS;
47
+ return legacy;
48
+ }
49
+ invalidate() {
50
+ this.revision += 1;
51
+ this.absentUntil = undefined;
52
+ this.releases.invalidate();
53
+ this.publications.invalidate();
54
+ }
55
+ }
56
+ /** The digest designating one intrinsic release: its release digest, or a pre-v4 ETag. */
57
+ export function designation(input) {
58
+ return input.version === 4 ? input.digest : input.etag;
59
+ }
60
+ /** A `release.json` request that failed with the Fetch-standard network error, before any response. */
61
+ function isUnreadableAbsence(cause) {
62
+ return (cause instanceof TransportError &&
63
+ cause.phase === 'unknown' &&
64
+ cause.context.kind === 'acquisition' &&
65
+ cause.context.resource === 'release' &&
66
+ cause.cause instanceof TypeError);
67
+ }
@@ -0,0 +1,8 @@
1
+ import type { publication } from '@astrale-os/kernel-protocol';
2
+ import type { IntrinsicRelease } from '../release.js';
3
+ /**
4
+ * Retain one Publication object per admitted release; bundle and View provenance use its ETag.
5
+ *
6
+ * @deprecated Use `IntrinsicRelease`.
7
+ */
8
+ export declare function publicationOf(input: IntrinsicRelease): publication.Publication;
@@ -0,0 +1,17 @@
1
+ import * as releaseLegacy from '@astrale-os/kernel-protocol/release/legacy';
2
+ const projections = new WeakMap();
3
+ /**
4
+ * Retain one Publication object per admitted release; bundle and View provenance use its ETag.
5
+ *
6
+ * @deprecated Use `IntrinsicRelease`.
7
+ */
8
+ export function publicationOf(input) {
9
+ if (input.version !== 4)
10
+ return input;
11
+ const retained = projections.get(input);
12
+ if (retained !== undefined)
13
+ return retained;
14
+ const projected = releaseLegacy.publication(input);
15
+ projections.set(input, projected);
16
+ return projected;
17
+ }
@@ -1,14 +1,19 @@
1
1
  import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
2
  import type { publication } from '@astrale-os/kernel-protocol';
3
- import type { Fetch } from '../../transport/http/index.js';
4
- import type { SessionOperation } from '../context/index.js';
5
- import { type SessionPolicy } from '../context/index.js';
3
+ import type { Fetch } from '../../../transport/http/index.js';
4
+ import type { SessionOperation } from '../../context/index.js';
5
+ import { type SessionPolicy } from '../../context/index.js';
6
6
  export interface PublicationDiscoveryOptions {
7
7
  readonly kernel: IssuerId;
8
8
  readonly fetch: Fetch;
9
9
  readonly policy: Readonly<SessionPolicy>;
10
10
  readonly maximumBytes: number;
11
11
  }
12
+ /**
13
+ * Conditionally revalidate `domain.json` by ETag when intrinsic release discovery is unavailable.
14
+ *
15
+ * @deprecated Use `ReleaseDiscovery`.
16
+ */
12
17
  export declare class PublicationDiscovery {
13
18
  private readonly options;
14
19
  private publication;
@@ -19,5 +24,3 @@ export declare class PublicationDiscovery {
19
24
  invalidate(): void;
20
25
  private fetch;
21
26
  }
22
- export declare function boundedResponse(response: Response, maximum: number, signal: AbortSignal): Promise<Uint8Array>;
23
- export declare function requireExactResponse(response: Response, expected: string, label: string): void;
@@ -1,8 +1,14 @@
1
1
  import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
2
  import * as publicationCodec from '@astrale-os/kernel-protocol/publication/publication';
3
- import { ClientError } from '../../errors/index.js';
4
- import { allowEndpoint } from '../context/index.js';
5
- import { runAcquisition } from './transport.js';
3
+ import { ClientError } from '../../../errors/index.js';
4
+ import { allowEndpoint } from '../../context/index.js';
5
+ import { boundedResponse, requireExactResponse } from '../response.js';
6
+ import { runAcquisition } from '../transport.js';
7
+ /**
8
+ * Conditionally revalidate `domain.json` by ETag when intrinsic release discovery is unavailable.
9
+ *
10
+ * @deprecated Use `ReleaseDiscovery`.
11
+ */
6
12
  export class PublicationDiscovery {
7
13
  options;
8
14
  publication;
@@ -54,6 +60,10 @@ export class PublicationDiscovery {
54
60
  }
55
61
  const body = await runAcquisition(operation, 'publication', 'receive', 'Kernel Publication response could not be read.', (signal) => boundedResponse(response, this.options.maximumBytes, signal));
56
62
  const admitted = publicationCodec.accept(codecs.json.decode(body));
63
+ // Version 2 is only a retained Kernel-side format; a live Kernel serves version 3.
64
+ if (admitted.version !== 3) {
65
+ throw new ClientError('Kernel Publication version 2 is no longer supported; the Kernel must serve Publication version 3.');
66
+ }
57
67
  if (admitted.identity.issuer !== this.options.kernel) {
58
68
  throw new ClientError('Publication issuer differs from the requested Kernel issuer.');
59
69
  }
@@ -64,60 +74,3 @@ export class PublicationDiscovery {
64
74
  return admitted;
65
75
  }
66
76
  }
67
- export async function boundedResponse(response, maximum, signal) {
68
- const declared = response.headers.get('content-length');
69
- if (declared !== null) {
70
- const length = Number(declared);
71
- if (!Number.isSafeInteger(length) || length < 0) {
72
- throw new ClientError('Response Content-Length is invalid.');
73
- }
74
- if (length > maximum)
75
- throw new ClientError('Response exceeds its byte limit.');
76
- }
77
- if (response.body === null)
78
- return new Uint8Array();
79
- const reader = response.body.getReader();
80
- const chunks = [];
81
- let total = 0;
82
- const abort = () => void reader.cancel(signal.reason).catch(() => undefined);
83
- if (signal.aborted)
84
- abort();
85
- else
86
- signal.addEventListener('abort', abort, { once: true });
87
- try {
88
- while (true) {
89
- if (signal.aborted)
90
- throw signal.reason;
91
- const next = await reader.read();
92
- if (next.done)
93
- break;
94
- total += next.value.byteLength;
95
- if (total > maximum) {
96
- await reader.cancel('response exceeds byte limit').catch(() => undefined);
97
- throw new ClientError('Response exceeds its byte limit.');
98
- }
99
- chunks.push(next.value);
100
- }
101
- }
102
- finally {
103
- signal.removeEventListener('abort', abort);
104
- reader.releaseLock();
105
- }
106
- return join(chunks, total);
107
- }
108
- export function requireExactResponse(response, expected, label) {
109
- if (response.redirected || response.url !== expected) {
110
- throw new ClientError(`${label} did not return the exact requested URL.`);
111
- }
112
- }
113
- function join(parts, length) {
114
- if (parts.length === 1)
115
- return new Uint8Array(parts[0]);
116
- const output = new Uint8Array(length);
117
- let offset = 0;
118
- for (const part of parts) {
119
- output.set(part, offset);
120
- offset += part.byteLength;
121
- }
122
- return output;
123
- }
@@ -0,0 +1,29 @@
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
+ import type { release } from '@astrale-os/kernel-protocol';
3
+ import type { Fetch } from '../../transport/http/index.js';
4
+ import type { SessionOperation } from '../context/index.js';
5
+ import { type SessionPolicy } from '../context/index.js';
6
+ /** The source Kernel's release, or its version 3 Publication when `release.json` is unavailable. */
7
+ export type IntrinsicRelease = release.DomainRelease | release.legacy.Publication;
8
+ export interface ReleaseDiscoveryOptions {
9
+ readonly kernel: IssuerId;
10
+ readonly fetch: Fetch;
11
+ readonly policy: Readonly<SessionPolicy>;
12
+ readonly maximumBytes: number;
13
+ }
14
+ /**
15
+ * Conditionally revalidate by release digest. Only an exact 404 means absence;
16
+ * all other unsuccessful responses and Fetch failures remain errors.
17
+ */
18
+ export declare class ReleaseDiscovery {
19
+ private readonly options;
20
+ private release;
21
+ private revision;
22
+ constructor(options: ReleaseDiscoveryOptions);
23
+ /** The release admitted since construction, the last `invalidate()` or the last 404, if any. */
24
+ current(): release.DomainRelease | undefined;
25
+ /** The admitted release, or undefined when the Host answers `release.json` with its exact 404. */
26
+ revalidate(operation: SessionOperation): Promise<release.DomainRelease | undefined>;
27
+ invalidate(): void;
28
+ private fetch;
29
+ }
@@ -0,0 +1,81 @@
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
+ import * as releaseCodec from '@astrale-os/kernel-protocol/release';
3
+ import { ClientError } from '../../errors/index.js';
4
+ import { allowEndpoint } from '../context/index.js';
5
+ import { boundedResponse, requireExactResponse } from './response.js';
6
+ import { runAcquisition } from './transport.js';
7
+ /**
8
+ * Conditionally revalidate by release digest. Only an exact 404 means absence;
9
+ * all other unsuccessful responses and Fetch failures remain errors.
10
+ */
11
+ export class ReleaseDiscovery {
12
+ options;
13
+ release;
14
+ revision = 0;
15
+ constructor(options) {
16
+ this.options = options;
17
+ }
18
+ /** The release admitted since construction, the last `invalidate()` or the last 404, if any. */
19
+ current() {
20
+ return this.release;
21
+ }
22
+ /** The admitted release, or undefined when the Host answers `release.json` with its exact 404. */
23
+ async revalidate(operation) {
24
+ const revision = this.revision;
25
+ const previous = this.release;
26
+ const discovered = await this.fetch(operation, previous);
27
+ if (revision !== this.revision)
28
+ return this.revalidate(operation);
29
+ const retained = discovered !== undefined && previous?.digest === discovered.digest ? previous : discovered;
30
+ this.release = retained;
31
+ return retained;
32
+ }
33
+ invalidate() {
34
+ this.revision += 1;
35
+ this.release = undefined;
36
+ }
37
+ async fetch(operation, previous) {
38
+ const url = releaseCodec.url(this.options.kernel);
39
+ allowEndpoint(url, this.options.policy);
40
+ const response = await runAcquisition(operation, 'release', 'unknown', 'Kernel release could not be retrieved.', (signal) => this.options.fetch(url, {
41
+ headers: {
42
+ accept: releaseCodec.MEDIA_TYPE,
43
+ ...(previous === undefined ? {} : { 'if-none-match': `"${previous.digest}"` }),
44
+ },
45
+ redirect: 'manual',
46
+ signal,
47
+ }));
48
+ requireExactResponse(response, url, 'Release discovery');
49
+ if (response.status === 404) {
50
+ // Cleanup must not hold discovery open on an injected body cancellation.
51
+ void response.body?.cancel().catch(() => undefined);
52
+ return undefined;
53
+ }
54
+ if (response.status === 304) {
55
+ if (previous === undefined) {
56
+ throw new ClientError('Release discovery returned an unexpected not-modified response.');
57
+ }
58
+ const etag = response.headers.get('etag');
59
+ if (etag !== null && etag !== `"${previous.digest}"`) {
60
+ throw new ClientError('Release not-modified ETag differs from the retained release digest.');
61
+ }
62
+ return previous;
63
+ }
64
+ if (!response.ok) {
65
+ throw new ClientError(`Release discovery returned HTTP ${response.status}.`);
66
+ }
67
+ if (response.headers.get('content-type') !== releaseCodec.MEDIA_TYPE) {
68
+ throw new ClientError('Release response media type differs from the DomainRelease v4 media type.');
69
+ }
70
+ const body = await runAcquisition(operation, 'release', 'receive', 'Kernel release response could not be read.', (signal) => boundedResponse(response, this.options.maximumBytes, signal));
71
+ const admitted = releaseCodec.accept(codecs.json.decode(body));
72
+ if (admitted.identity.issuer !== this.options.kernel) {
73
+ throw new ClientError('Release issuer differs from the requested Kernel issuer.');
74
+ }
75
+ const etag = response.headers.get('etag');
76
+ if (etag !== null && etag !== `"${admitted.digest}"`) {
77
+ throw new ClientError('Release response ETag differs from its release digest.');
78
+ }
79
+ return admitted;
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ /** Read external bytes within one Session operation's byte and lifetime bounds. */
2
+ export declare function boundedResponse(response: Response, maximum: number, signal: AbortSignal): Promise<Uint8Array>;
3
+ export declare function requireExactResponse(response: Response, expected: string, label: string): void;
@@ -0,0 +1,59 @@
1
+ import { ClientError } from '../../errors/index.js';
2
+ /** Read external bytes within one Session operation's byte and lifetime bounds. */
3
+ export async function boundedResponse(response, maximum, signal) {
4
+ const declared = response.headers.get('content-length');
5
+ if (declared !== null) {
6
+ const length = Number(declared);
7
+ if (!Number.isSafeInteger(length) || length < 0) {
8
+ throw new ClientError('Response Content-Length is invalid.');
9
+ }
10
+ if (length > maximum)
11
+ throw new ClientError('Response exceeds its byte limit.');
12
+ }
13
+ if (response.body === null)
14
+ return new Uint8Array();
15
+ const reader = response.body.getReader();
16
+ const chunks = [];
17
+ let total = 0;
18
+ const abort = () => void reader.cancel(signal.reason).catch(() => undefined);
19
+ if (signal.aborted)
20
+ abort();
21
+ else
22
+ signal.addEventListener('abort', abort, { once: true });
23
+ try {
24
+ while (true) {
25
+ if (signal.aborted)
26
+ throw signal.reason;
27
+ const next = await reader.read();
28
+ if (next.done)
29
+ break;
30
+ total += next.value.byteLength;
31
+ if (total > maximum) {
32
+ await reader.cancel('response exceeds byte limit').catch(() => undefined);
33
+ throw new ClientError('Response exceeds its byte limit.');
34
+ }
35
+ chunks.push(next.value);
36
+ }
37
+ }
38
+ finally {
39
+ signal.removeEventListener('abort', abort);
40
+ reader.releaseLock();
41
+ }
42
+ return join(chunks, total);
43
+ }
44
+ export function requireExactResponse(response, expected, label) {
45
+ if (response.redirected || response.url !== expected) {
46
+ throw new ClientError(`${label} did not return the exact requested URL.`);
47
+ }
48
+ }
49
+ function join(parts, length) {
50
+ if (parts.length === 1)
51
+ return new Uint8Array(parts[0]);
52
+ const output = new Uint8Array(length);
53
+ let offset = 0;
54
+ for (const part of parts) {
55
+ output.set(part, offset);
56
+ offset += part.byteLength;
57
+ }
58
+ return output;
59
+ }
@@ -2,11 +2,18 @@ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
2
  import type { bundle } from '@astrale-os/kernel-dsl/v1';
3
3
  import type { publication } from '@astrale-os/kernel-protocol';
4
4
  import type { ViewCatalog } from '../views/index.js';
5
- /** Coherent public observation acquired through one Client Session at one Publication ETag. */
5
+ import type { IntrinsicRelease } from './release.js';
6
+ /** Coherent public observation acquired through one Client Session at one intrinsic release. */
6
7
  export interface SessionSnapshot {
7
8
  readonly kernel: IssuerId;
9
+ readonly release: IntrinsicRelease;
10
+ /**
11
+ * Bundle and root View provenance use this v3 document's ETag.
12
+ *
13
+ * @deprecated Use `release`.
14
+ */
8
15
  readonly publication: publication.Publication;
9
16
  readonly bundle: bundle.Bundle;
10
17
  readonly views: ViewCatalog;
11
18
  }
12
- export declare function createSessionSnapshot(kernel: IssuerId, publication: publication.Publication, artifact: bundle.Bundle, views: ViewCatalog): SessionSnapshot;
19
+ export declare function createSessionSnapshot(kernel: IssuerId, release: IntrinsicRelease, publication: publication.Publication, artifact: bundle.Bundle, views: ViewCatalog): SessionSnapshot;
@@ -1,3 +1,3 @@
1
- export function createSessionSnapshot(kernel, publication, artifact, views) {
2
- return Object.freeze({ kernel, publication, bundle: artifact, views });
1
+ export function createSessionSnapshot(kernel, release, publication, artifact, views) {
2
+ return Object.freeze({ kernel, release, publication, bundle: artifact, views });
3
3
  }
@@ -1,6 +1,4 @@
1
- import type { TransportPhase } from '../../errors/index.js';
1
+ import type { AcquisitionResource, TransportPhase } from '../../errors/index.js';
2
2
  import type { SessionOperation } from '../context/index.js';
3
- type AcquisitionResource = 'publication' | 'bundle';
4
3
  /** Normalize only unknown failures from one injected acquisition capability. */
5
4
  export declare function runAcquisition<Result>(operation: SessionOperation, resource: AcquisitionResource, phase: Extract<TransportPhase, 'receive' | 'unknown'>, message: string, task: (signal: AbortSignal) => Result | PromiseLike<Result>): Promise<Result>;
6
- export {};
@@ -1,19 +1,20 @@
1
1
  import { type IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
2
  import type { SessionControlOptions } from '../context/index.js';
3
3
  import type { SessionAuth } from '../routing/index.js';
4
- /** Choose how authenticated Calls to remote Domains present the Session's source authority. */
5
- export interface SessionExchangeOptions {
6
- /**
7
- * `auto` exchanges through each callable's declaring Domain; `source` keeps the source credential.
8
- * Defaults to `auto`, including legacy empty objects and options containing only `ttlSeconds`.
9
- */
10
- readonly mode?: 'auto' | 'source';
4
+ /**
5
+ * Choose how authenticated Calls to remote Domains present the Session's source authority: `auto`
6
+ * exchanges through each callable's declaring Domain; `source` keeps the source credential.
7
+ */
8
+ export type SessionExchangeOptions = {
9
+ readonly mode: 'auto';
11
10
  /**
12
11
  * Delegation lifetime requested for each Domain credential. The source credential's remaining
13
- * lifetime and the Domain's own ceiling may shorten it. Applies only in `auto` mode.
12
+ * lifetime and the Domain's own ceiling may shorten it.
14
13
  */
15
14
  readonly ttlSeconds?: number;
16
- }
15
+ } | {
16
+ readonly mode: 'source';
17
+ };
17
18
  /** One explicit exchange through a named Domain issuer. */
18
19
  export interface SessionExchangeRequest extends SessionControlOptions {
19
20
  /** Requested delegation lifetime; the source credential and the Domain may shorten it. */
@@ -29,6 +30,8 @@ export interface ExchangeRequest {
29
30
  * Admit exchange options against the Session auth they extend. Every Domain credential must still
30
31
  * cover the Session delegation, so a request that could never cover it is rejected.
31
32
  */
32
- export declare function admitExchangeOptions(input: SessionExchangeOptions | undefined, auth: SessionAuth | undefined): Readonly<Required<Pick<SessionExchangeOptions, 'ttlSeconds'>>> | undefined;
33
+ export declare function admitExchangeOptions(input: SessionExchangeOptions | undefined, auth: SessionAuth | undefined): Readonly<{
34
+ ttlSeconds: number;
35
+ }> | undefined;
33
36
  /** Admit an explicit exchange: the canonical absolute issuer URL of the named Domain and its lifetime. */
34
37
  export declare function admitExchangeRequest(domain: string, input: SessionExchangeRequest | undefined): ExchangeRequest;
@@ -12,14 +12,15 @@ export function admitExchangeOptions(input, auth) {
12
12
  if (input === null || typeof input !== 'object' || Array.isArray(input)) {
13
13
  throw new TypeError('Session exchange options are invalid.');
14
14
  }
15
- if (Reflect.ownKeys(input).some((key) => key !== 'mode' && key !== 'ttlSeconds')) {
16
- throw new TypeError('Session exchange options contain unexpected fields.');
17
- }
18
- const mode = input.mode === undefined ? 'auto' : input.mode;
15
+ const mode = input.mode;
19
16
  if (mode !== 'auto' && mode !== 'source') {
20
17
  throw new TypeError('Session exchange mode is invalid.');
21
18
  }
22
- if (mode === 'source')
19
+ const fields = mode === 'auto' ? ['mode', 'ttlSeconds'] : ['mode'];
20
+ if (Reflect.ownKeys(input).some((key) => typeof key !== 'string' || !fields.includes(key))) {
21
+ throw new TypeError('Session exchange options contain unexpected fields.');
22
+ }
23
+ if (input.mode === 'source')
23
24
  return undefined;
24
25
  if (auth === undefined)
25
26
  throw new TypeError('Session exchange requires Session auth.');
@@ -1,6 +1,5 @@
1
1
  import type { AuthExchange, SessionAuth } from '../routing/index.js';
2
2
  import type { ExchangeDependencies } from './exchange.js';
3
- import type { SessionExchangeOptions } from './options.js';
4
3
  /** The per-Call exchange a Session applies after resolving its source authority. */
5
4
  export interface SessionExchange extends AuthExchange {
6
5
  clear(): void;
@@ -15,4 +14,6 @@ export interface SessionExchange extends AuthExchange {
15
14
  */
16
15
  export declare function createSessionExchange(dependencies: ExchangeDependencies & {
17
16
  readonly auth: SessionAuth;
18
- }, options: Readonly<Required<Pick<SessionExchangeOptions, 'ttlSeconds'>>>): SessionExchange;
17
+ }, options: Readonly<{
18
+ ttlSeconds: number;
19
+ }>): SessionExchange;
@@ -2,7 +2,7 @@ export type { SessionControlOptions, SessionPolicy } from './context/index.js';
2
2
  export type { ContentApi, ContentLocation, ContentUploadOptions, ContentUploadResult, } from './content.js';
3
3
  export { bindSession, type BoundClientSession, type BoundSessionControls, type BoundSessionSource, } from './bound.js';
4
4
  export { createSessionCredentialProvider, type SessionCredential, type SessionCredentialProvider, type SessionCredentialProviderOptions, type SessionCredentialState, } from './credential/index.js';
5
- export type { SessionSnapshot } from './discovery/index.js';
5
+ export type { IntrinsicRelease, SessionSnapshot } from './discovery/index.js';
6
6
  export type { SessionExchangeOptions, SessionExchangeRequest } from './exchange/index.js';
7
7
  export type { Auth, PersistedRoute, SessionRouteArtifact, SessionRouteStore, SessionAuth, SessionEnvelopeTransport, SessionRequestOptions, SessionRequestOptionsWithBody, } from './routing/index.js';
8
8
  export { ClientSession, connect, type ClientSessionOptions, type ConnectOptions, } from './session.js';
@@ -1,6 +1,6 @@
1
1
  import type { Path as PathValue, PathLike } from '@astrale-os/kernel-core/path';
2
2
  import type { CallableInputOf, CallableInputModeOf, CallableOutputOf, CallableResultOf, Domain, ResolvedFunction, ExecutableMethod } from '@astrale-os/kernel-dsl/v1';
3
- import type { MethodAddress, Revision } from '@astrale-os/kernel-dsl/v1/addressing';
3
+ import type { MethodAddress } from '@astrale-os/kernel-dsl/v1/addressing';
4
4
  import type { MeaningFingerprint } from '@astrale-os/kernel-dsl/v1/schema/fingerprint';
5
5
  import { type Key as KeyType } from '@astrale-os/kernel-dsl/v1/addressing';
6
6
  import type { Binary, Input } from '../client/index.js';
@@ -10,8 +10,6 @@ declare const REFERENCE_INPUT_MODE: unique symbol;
10
10
  declare const REFERENCE_OUTPUT: unique symbol;
11
11
  export interface CallableReference<InputValue = Input, Output = unknown, InputMode extends 'value' | 'binary' = 'value' | 'binary'> {
12
12
  readonly origin: KeyType.Origin;
13
- /** @deprecated Informational only; dispatch pins `fingerprint` and revision pinning will be removed. */
14
- readonly revision?: Revision;
15
13
  /** Meaning fingerprint of the callable; a generated reference always carries one. */
16
14
  readonly fingerprint?: MeaningFingerprint;
17
15
  readonly callable: KeyType.Callable;
@@ -24,7 +22,6 @@ export interface CallableReference<InputValue = Input, Output = unknown, InputMo
24
22
  export interface InstanceCallableReference<InputValue = Input, Output = unknown> {
25
23
  (receiver: PathLike): CallableReference<InputValue, Output>;
26
24
  readonly origin: KeyType.Origin;
27
- readonly revision?: Revision;
28
25
  readonly fingerprint?: MeaningFingerprint;
29
26
  readonly callable: KeyType.Callable;
30
27
  readonly outputMode: 'value' | 'stream' | 'binary';
@@ -42,9 +39,9 @@ type ReferenceOutput<Callable> = Callable extends {
42
39
  type ExecutableStaticMethod = ExecutableMethod<unknown, unknown, true>;
43
40
  type ExecutableInstanceMethod = ExecutableMethod<unknown, unknown, false>;
44
41
  /** Project one exact resolved Function or static Method into a typed Client reference. */
45
- export declare function reference<const Callable extends ResolvedFunction | ExecutableStaticMethod>(domain: Pick<Domain, 'origin' | 'revision'>, callable: Callable): CallableReference<ReferenceInput<Callable>, ReferenceOutput<Callable>, CallableInputModeOf<Callable>>;
42
+ export declare function reference<const Callable extends ResolvedFunction | ExecutableStaticMethod>(domain: Pick<Domain, 'origin'>, callable: Callable): CallableReference<ReferenceInput<Callable>, ReferenceOutput<Callable>, CallableInputModeOf<Callable>>;
46
43
  /** Project one exact resolved instance Method into a receiver-bound typed Client reference. */
47
- export declare function reference<const Callable extends ExecutableInstanceMethod>(domain: Pick<Domain, 'origin' | 'revision'>, callable: Callable): InstanceCallableReference<ReferenceInput<Callable>, ReferenceOutput<Callable>>;
44
+ export declare function reference<const Callable extends ExecutableInstanceMethod>(domain: Pick<Domain, 'origin'>, callable: Callable): InstanceCallableReference<ReferenceInput<Callable>, ReferenceOutput<Callable>>;
48
45
  /**
49
46
  * Reference a Kernel instance Method by its Core coordinate, without a meaning pin, as the Session
50
47
  * addresses every Kernel syscall. Loading the Session therefore never resolves the Kernel Schema.
@@ -9,7 +9,6 @@ export function reference(domain, callable) {
9
9
  invalid();
10
10
  const common = Object.freeze({
11
11
  origin: domain.origin,
12
- revision: domain.revision,
13
12
  fingerprint: callableFingerprint(callable),
14
13
  callable: callable.key,
15
14
  outputMode: callable.output.mode,
@@ -45,8 +44,6 @@ export function admitReference(input) {
45
44
  invalid();
46
45
  if (typeof input.origin !== 'string' ||
47
46
  !patterns.origin.test(input.origin) ||
48
- (input.revision !== undefined &&
49
- (typeof input.revision !== 'string' || !patterns.revision.test(input.revision))) ||
50
47
  typeof input.callable !== 'string' ||
51
48
  !Key.is(input.callable) ||
52
49
  Key.origin(input.callable) !== input.origin ||
@@ -59,7 +56,6 @@ export function admitReference(input) {
59
56
  invalid();
60
57
  return Object.freeze({
61
58
  origin: input.origin,
62
- ...(input.revision === undefined ? {} : { revision: input.revision }),
63
59
  ...(input.fingerprint === undefined ? {} : { fingerprint: input.fingerprint }),
64
60
  callable: input.callable,
65
61
  target,