@astrale-os/kernel-protocol 0.5.0-beta.9 → 0.5.0

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 (203) hide show
  1. package/dist/artifact/index.d.ts +1 -1
  2. package/dist/artifact/index.js +1 -1
  3. package/dist/artifact/ref.d.ts +2 -0
  4. package/dist/artifact/ref.js +4 -3
  5. package/dist/codecs/index.d.ts +1 -2
  6. package/dist/codecs/index.js +1 -2
  7. package/dist/codecs/media-types.d.ts +0 -1
  8. package/dist/codecs/media-types.js +0 -1
  9. package/dist/errors/index.d.ts +1 -0
  10. package/dist/errors/index.js +1 -0
  11. package/dist/errors/retry.d.ts +25 -0
  12. package/dist/errors/retry.js +46 -0
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/invocation/admission.d.ts +2 -2
  16. package/dist/invocation/admission.js +5 -5
  17. package/dist/invocation/binary.d.ts +1 -0
  18. package/dist/invocation/binary.js +21 -4
  19. package/dist/invocation/binding-codecs.d.ts +3 -2
  20. package/dist/invocation/binding-codecs.js +1 -6
  21. package/dist/invocation/delegate.d.ts +0 -20
  22. package/dist/invocation/delegate.js +2 -34
  23. package/dist/invocation/http/binary.d.ts +3 -1
  24. package/dist/invocation/http/binary.js +6 -1
  25. package/dist/invocation/http/index.d.ts +2 -1
  26. package/dist/invocation/http/index.js +2 -1
  27. package/dist/invocation/http/negotiation.js +3 -10
  28. package/dist/invocation/http/request.d.ts +11 -0
  29. package/dist/invocation/http/request.js +69 -0
  30. package/dist/invocation/index.d.ts +5 -4
  31. package/dist/invocation/index.js +4 -3
  32. package/dist/invocation/maintenance.d.ts +14 -0
  33. package/dist/invocation/maintenance.js +18 -0
  34. package/dist/invocation/redirect.d.ts +10 -12
  35. package/dist/invocation/redirect.js +10 -22
  36. package/dist/invocation/request.d.ts +3 -0
  37. package/dist/invocation/request.js +9 -2
  38. package/dist/invocation/response.d.ts +6 -1
  39. package/dist/invocation/response.js +26 -15
  40. package/dist/invocation/routing.d.ts +14 -4
  41. package/dist/invocation/routing.js +35 -20
  42. package/dist/invocation/schema.d.ts +6 -4
  43. package/dist/invocation/schema.js +5 -11
  44. package/dist/invocation/service-reason.d.ts +20 -15
  45. package/dist/invocation/service-reason.js +27 -19
  46. package/dist/invocation/stale-route.d.ts +3 -3
  47. package/dist/invocation/target.d.ts +5 -2
  48. package/dist/invocation/target.js +25 -3
  49. package/dist/invocation/websocket/binary.js +1 -0
  50. package/dist/invocation/websocket/index.d.ts +1 -1
  51. package/dist/invocation/websocket/index.js +1 -1
  52. package/dist/invocation/websocket/subprotocol.d.ts +1 -2
  53. package/dist/invocation/websocket/subprotocol.js +3 -4
  54. package/dist/issuer/claims.d.ts +7 -0
  55. package/dist/issuer/claims.js +7 -0
  56. package/dist/issuer/index.d.ts +2 -0
  57. package/dist/issuer/index.js +2 -0
  58. package/dist/issuer/paths.d.ts +2 -0
  59. package/dist/issuer/paths.js +1 -0
  60. package/dist/issuer/workflow-engine.d.ts +18 -0
  61. package/dist/issuer/workflow-engine.js +59 -0
  62. package/dist/{publication → release}/bindings/callable.js +6 -6
  63. package/dist/release/bindings/invocation.js +13 -0
  64. package/dist/release/bindings/view.d.ts +11 -0
  65. package/dist/release/bindings/view.js +54 -0
  66. package/dist/release/build.d.ts +6 -0
  67. package/dist/release/build.js +7 -0
  68. package/dist/release/bundle-admission.d.ts +21 -0
  69. package/dist/{publication → release}/bundle-admission.js +15 -20
  70. package/dist/{publication → release}/bundle-descriptor.js +6 -6
  71. package/dist/release/bundle-evidence.d.ts +3 -0
  72. package/dist/release/bundle-evidence.js +13 -0
  73. package/dist/{publication → release}/bundle.d.ts +5 -2
  74. package/dist/{publication → release}/bundle.js +13 -7
  75. package/dist/{publication → release}/delivery/attestation.d.ts +1 -0
  76. package/dist/{publication → release}/delivery/claims.d.ts +7 -2
  77. package/dist/{publication → release}/delivery/claims.js +20 -13
  78. package/dist/release/delivery/delivery.d.ts +11 -0
  79. package/dist/{publication → release}/delivery/delivery.js +7 -12
  80. package/dist/{publication → release}/delivery/error.d.ts +2 -0
  81. package/dist/{publication → release}/delivery/error.js +1 -0
  82. package/dist/{publication → release}/delivery/index.d.ts +5 -1
  83. package/dist/{publication → release}/delivery/index.js +4 -0
  84. package/dist/{publication → release}/delivery/limits.d.ts +2 -1
  85. package/dist/release/delivery/limits.js +5 -0
  86. package/dist/{publication → release}/delivery/request.d.ts +10 -9
  87. package/dist/{publication → release}/delivery/request.js +9 -10
  88. package/dist/{publication → release}/delivery-declaration.js +4 -4
  89. package/dist/release/delivery-endpoint.js +22 -0
  90. package/dist/release/digest.d.ts +13 -0
  91. package/dist/{publication/etag.js → release/digest.js} +1 -6
  92. package/dist/release/error.d.ts +7 -0
  93. package/dist/release/error.js +11 -0
  94. package/dist/release/index.d.ts +17 -0
  95. package/dist/release/index.js +10 -0
  96. package/dist/release/issuer.d.ts +5 -0
  97. package/dist/release/issuer.js +34 -0
  98. package/dist/release/manifest.d.ts +6 -0
  99. package/dist/release/manifest.js +22 -0
  100. package/dist/release/reference.d.ts +26 -0
  101. package/dist/release/reference.js +70 -0
  102. package/dist/release/release.d.ts +60 -0
  103. package/dist/release/release.js +125 -0
  104. package/dist/release/requirements.d.ts +41 -0
  105. package/dist/release/requirements.js +306 -0
  106. package/dist/{publication → release}/route.js +24 -24
  107. package/dist/{publication → release}/schema.js +4 -4
  108. package/dist/release/served.d.ts +27 -0
  109. package/dist/release/served.js +56 -0
  110. package/dist/release/shape.d.ts +8 -0
  111. package/dist/routes/admission.d.ts +1 -1
  112. package/dist/routes/admission.js +7 -85
  113. package/dist/routes/binding.d.ts +3 -3
  114. package/dist/routes/binding.js +11 -21
  115. package/dist/routes/compiler.d.ts +2 -2
  116. package/dist/routes/compiler.js +7 -3
  117. package/dist/routes/credential.d.ts +12 -2
  118. package/dist/routes/credential.js +9 -4
  119. package/dist/routes/decode.d.ts +5 -2
  120. package/dist/routes/decode.js +24 -17
  121. package/dist/routes/deployment-retired.d.ts +30 -0
  122. package/dist/routes/deployment-retired.js +96 -0
  123. package/dist/routes/encode.js +12 -5
  124. package/dist/routes/entries.d.ts +23 -0
  125. package/dist/routes/entries.js +150 -0
  126. package/dist/routes/evidence.d.ts +3 -3
  127. package/dist/routes/evidence.js +7 -33
  128. package/dist/routes/header.d.ts +9 -0
  129. package/dist/routes/header.js +34 -0
  130. package/dist/routes/index.d.ts +2 -2
  131. package/dist/routes/index.js +2 -2
  132. package/dist/routes/mapping.d.ts +1 -1
  133. package/dist/routes/mapping.js +4 -4
  134. package/dist/routes/match.d.ts +1 -1
  135. package/dist/routes/placement.d.ts +2 -2
  136. package/dist/routes/plan.d.ts +1 -1
  137. package/dist/routes/request.d.ts +3 -1
  138. package/dist/routes/reservation.d.ts +4 -2
  139. package/dist/routes/reservation.js +14 -8
  140. package/dist/routes/routing.d.ts +18 -6
  141. package/dist/routes/routing.js +28 -49
  142. package/dist/routes/select.d.ts +1 -1
  143. package/dist/routes/table.d.ts +3 -2
  144. package/dist/routes/target.d.ts +5 -0
  145. package/dist/routes/target.js +16 -0
  146. package/dist/routes/template.d.ts +0 -1
  147. package/dist/routes/template.js +0 -3
  148. package/dist/runtime/artifact.d.ts +5 -4
  149. package/dist/runtime/artifact.js +7 -4
  150. package/dist/runtime/index.d.ts +1 -1
  151. package/dist/runtime/index.js +1 -1
  152. package/dist/schema/installation/codec.d.ts +9 -0
  153. package/dist/schema/installation/codec.js +82 -0
  154. package/dist/schema/installation/index.d.ts +2 -0
  155. package/dist/schema/installation/index.js +1 -0
  156. package/dist/schema/installation/introspection/codec.d.ts +5 -2
  157. package/dist/schema/installation/introspection/codec.js +319 -193
  158. package/dist/schema/installation/introspection/index.d.ts +2 -2
  159. package/dist/schema/installation/introspection/index.js +1 -1
  160. package/dist/schema/installation/introspection/result.d.ts +84 -25
  161. package/dist/schema/installation/result.d.ts +15 -0
  162. package/dist/schema/installation/result.js +1 -0
  163. package/package.json +21 -75
  164. package/dist/codecs/msgpack.d.ts +0 -4
  165. package/dist/codecs/msgpack.js +0 -50
  166. package/dist/publication/bindings/invocation.js +0 -13
  167. package/dist/publication/bindings/view.d.ts +0 -7
  168. package/dist/publication/bindings/view.js +0 -28
  169. package/dist/publication/bundle-admission.d.ts +0 -4
  170. package/dist/publication/bundle-evidence.d.ts +0 -3
  171. package/dist/publication/bundle-evidence.js +0 -13
  172. package/dist/publication/content.d.ts +0 -10
  173. package/dist/publication/delivery/delivery.d.ts +0 -9
  174. package/dist/publication/delivery/limits.js +0 -4
  175. package/dist/publication/delivery-endpoint.js +0 -22
  176. package/dist/publication/error.d.ts +0 -7
  177. package/dist/publication/error.js +0 -11
  178. package/dist/publication/etag.d.ts +0 -6
  179. package/dist/publication/index.d.ts +0 -17
  180. package/dist/publication/index.js +0 -10
  181. package/dist/publication/manifest.d.ts +0 -6
  182. package/dist/publication/manifest.js +0 -30
  183. package/dist/publication/publication.d.ts +0 -36
  184. package/dist/publication/publication.js +0 -133
  185. package/dist/publication/reference.d.ts +0 -17
  186. package/dist/publication/reference.js +0 -68
  187. package/dist/publication/requirements.d.ts +0 -26
  188. package/dist/publication/requirements.js +0 -180
  189. package/dist/routes/release-unavailable.d.ts +0 -8
  190. package/dist/routes/release-unavailable.js +0 -32
  191. /package/dist/{publication → release}/bindings/callable.d.ts +0 -0
  192. /package/dist/{publication → release}/bindings/index.d.ts +0 -0
  193. /package/dist/{publication → release}/bindings/index.js +0 -0
  194. /package/dist/{publication → release}/bindings/invocation.d.ts +0 -0
  195. /package/dist/{publication → release}/bundle-descriptor.d.ts +0 -0
  196. /package/dist/{publication → release}/declaration.d.ts +0 -0
  197. /package/dist/{publication → release}/declaration.js +0 -0
  198. /package/dist/{publication → release}/delivery/attestation.js +0 -0
  199. /package/dist/{publication → release}/delivery-declaration.d.ts +0 -0
  200. /package/dist/{publication → release}/delivery-endpoint.d.ts +0 -0
  201. /package/dist/{publication → release}/route.d.ts +0 -0
  202. /package/dist/{publication → release}/schema.d.ts +0 -0
  203. /package/dist/{publication/content.js → release/shape.js} +0 -0
@@ -0,0 +1,18 @@
1
+ import { isNodeId } from '@astrale-os/kernel-core/graph/node';
2
+ import { exactFields, invalid, portableObject, required } from './admission.js';
3
+ /** An Execution identifier: 1-255 ASCII characters drawn from `[A-Za-z0-9._-]`. */
4
+ export const MAXIMUM_MAINTENANCE_SESSION_LENGTH = 255;
5
+ const FIELDS = new Set(['session']);
6
+ /** Admit one exact maintenance guard without deciding whether its session is active. */
7
+ export function acceptMaintenance(input) {
8
+ const object = portableObject(input, 'maintenance');
9
+ exactFields(object, FIELDS, 'maintenance');
10
+ required(object, ['session'], 'maintenance');
11
+ // An Execution identifier shares the Core NodeId grammar; it names no graph Node.
12
+ const session = object.session;
13
+ if (!isNodeId(session)) {
14
+ invalid('maintenance.session must contain 1-255 ASCII characters drawn from [A-Za-z0-9._-]');
15
+ }
16
+ const admitted = session;
17
+ return Object.freeze({ session: admitted });
18
+ }
@@ -1,18 +1,15 @@
1
1
  import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
- import type { Key } from '@astrale-os/kernel-dsl/v1';
3
- import type { InvocationEndpoints } from '../publication/bindings/invocation.js';
4
- import type { Etag } from '../publication/etag.js';
5
- import type { PublicationRef } from '../publication/reference.js';
2
+ import type { Key, Revision } from '@astrale-os/kernel-dsl/v1';
3
+ import type { InvocationEndpoints } from '../release/bindings/invocation.js';
4
+ import type { ReleaseDigest } from '../release/digest.js';
6
5
  import type { RequestId } from './correlation.js';
7
6
  import type { InvocationId } from './identity.js';
7
+ import type { Via } from './routing.js';
8
8
  import type { Trace } from './trace.js';
9
9
  export interface Route {
10
10
  readonly endpoint: InvocationEndpoints;
11
- readonly via: {
12
- readonly kind: 'via';
13
- readonly publication: PublicationRef;
14
- readonly issuer: IssuerId;
15
- };
11
+ /** Routed witness of the exact installed release. */
12
+ readonly via: Via;
16
13
  }
17
14
  export interface Redirection {
18
15
  readonly route: Route;
@@ -26,15 +23,16 @@ export interface RedirectResponse {
26
23
  readonly invocation: InvocationId;
27
24
  readonly redirect: Redirection;
28
25
  }
26
+ /** Remote binding of one installed release: the endpoint and the release it routes to. */
29
27
  export interface RemoteBindingInput {
30
28
  readonly endpoint: InvocationEndpoints;
31
29
  readonly issuer: IssuerId;
32
30
  readonly subject: string;
33
- readonly revision: PublicationRef['revision'];
34
- readonly etag: Etag;
31
+ readonly revision: Revision;
32
+ readonly release: ReleaseDigest;
35
33
  }
36
34
  export declare function acceptRoute(input: unknown): Route;
37
- /** Project one semantic remote binding into its admitted protocol route representation. */
35
+ /** Project one installed release binding into its routed witness. */
38
36
  export declare function remoteRoute(origin: Key.Origin, binding: RemoteBindingInput, issuer: IssuerId): Route;
39
37
  export declare function acceptRedirection(input: unknown): Redirection;
40
38
  export declare function decodeRedirectResponse(input: unknown): RedirectResponse;
@@ -1,49 +1,37 @@
1
- import { acceptInvocationEndpoints } from '../publication/bindings/invocation.js';
2
- import { PublicationError } from '../publication/error.js';
3
- import { acceptReference } from '../publication/reference.js';
4
- import { exactFields, invalid, issuerId, invocation, portableObject, requestId, required, string, } from './admission.js';
1
+ import { acceptInvocationEndpoints } from '../release/bindings/index.js';
2
+ import { ReleaseError } from '../release/error.js';
3
+ import { exactFields, invalid, invocation, portableObject, requestId, required, string, } from './admission.js';
4
+ import { acceptVia } from './routing.js';
5
5
  import { admitTrace } from './trace.js';
6
6
  const ROUTE_FIELDS = new Set(['endpoint', 'via']);
7
- const VIA_FIELDS = new Set(['kind', 'publication', 'issuer']);
8
7
  const REDIRECTION_FIELDS = new Set(['route', 'credential', 'trace']);
9
8
  const REDIRECT_RESPONSE_FIELDS = new Set(['kind', 'requestId', 'invocation', 'redirect']);
10
9
  export function acceptRoute(input) {
11
10
  const object = portableObject(input, 'route');
12
11
  exactFields(object, ROUTE_FIELDS, 'route');
13
12
  required(object, ['endpoint', 'via'], 'route');
13
+ const via = acceptVia(portableObject(object.via, 'route.via'), 'route.via');
14
14
  try {
15
- const via = portableObject(object.via, 'route.via');
16
- exactFields(via, VIA_FIELDS, 'route.via');
17
- required(via, ['kind', 'publication', 'issuer'], 'route.via');
18
- if (via.kind !== 'via')
19
- invalid('route.via.kind must be "via"');
20
- return Object.freeze({
21
- endpoint: acceptInvocationEndpoints(object.endpoint),
22
- via: Object.freeze({
23
- kind: 'via',
24
- publication: acceptReference(via.publication),
25
- issuer: issuerId(via.issuer, 'route.via.issuer'),
26
- }),
27
- });
15
+ return Object.freeze({ endpoint: acceptInvocationEndpoints(object.endpoint), via });
28
16
  }
29
17
  catch (cause) {
30
- if (cause instanceof PublicationError) {
18
+ if (cause instanceof ReleaseError) {
31
19
  return invalid(`route is invalid: ${cause.message}`);
32
20
  }
33
21
  throw cause;
34
22
  }
35
23
  }
36
- /** Project one semantic remote binding into its admitted protocol route representation. */
24
+ /** Project one installed release binding into its routed witness. */
37
25
  export function remoteRoute(origin, binding, issuer) {
38
26
  return acceptRoute({
39
27
  endpoint: binding.endpoint,
40
28
  via: {
41
29
  kind: 'via',
42
- publication: {
30
+ release: {
43
31
  origin,
44
32
  identity: { issuer: binding.issuer, subject: binding.subject },
45
33
  revision: binding.revision,
46
- etag: binding.etag,
34
+ digest: binding.release,
47
35
  },
48
36
  issuer,
49
37
  },
@@ -1,6 +1,7 @@
1
1
  import type { RequestId } from './correlation.js';
2
2
  import type { Delegate } from './delegate.js';
3
3
  import type { IdempotencyKey } from './idempotency.js';
4
+ import type { Maintenance } from './maintenance.js';
4
5
  import type { Routing } from './routing.js';
5
6
  import type { SchemaTarget } from './schema.js';
6
7
  import type { Call } from './target.js';
@@ -15,6 +16,8 @@ export interface Request {
15
16
  readonly routing?: Routing;
16
17
  /** Source-Kernel precondition; forbidden on an already-routed destination request. */
17
18
  readonly schema?: SchemaTarget;
19
+ /** Source-Kernel maintenance session guard; forbidden on an already-routed destination request. */
20
+ readonly maintenance?: Maintenance;
18
21
  readonly idempotencyKey?: IdempotencyKey;
19
22
  readonly trace?: Trace;
20
23
  }
@@ -1,9 +1,10 @@
1
1
  import { exactFields, invalid, portableObject, requestId, required, string } from './admission.js';
2
2
  import { acceptDelegate } from './delegate.js';
3
3
  import { acceptIdempotencyKey } from './idempotency.js';
4
+ import { acceptMaintenance } from './maintenance.js';
4
5
  import { acceptRouting } from './routing.js';
5
6
  import { acceptSchemaTarget } from './schema.js';
6
- import { acceptCall } from './target.js';
7
+ import { callFromSnapshot } from './target.js';
7
8
  import { admitTrace } from './trace.js';
8
9
  const REQUEST_FIELDS = new Set([
9
10
  'version',
@@ -13,6 +14,7 @@ const REQUEST_FIELDS = new Set([
13
14
  'delegate',
14
15
  'routing',
15
16
  'schema',
17
+ 'maintenance',
16
18
  'idempotencyKey',
17
19
  'trace',
18
20
  ]);
@@ -26,12 +28,16 @@ export function decodeRequest(input) {
26
28
  const delegate = object.delegate === undefined ? undefined : acceptDelegate(object.delegate);
27
29
  const routing = object.routing === undefined ? undefined : acceptRouting(object.routing);
28
30
  const schema = object.schema === undefined ? undefined : acceptSchemaTarget(object.schema);
31
+ const maintenance = object.maintenance === undefined ? undefined : acceptMaintenance(object.maintenance);
29
32
  const trace = object.trace === undefined ? undefined : admitTrace(object.trace);
30
33
  if (routing?.kind === 'via') {
31
34
  if (delegate !== undefined)
32
35
  invalid('destination requests with via routing forbid delegate');
33
36
  if (schema !== undefined)
34
37
  invalid('destination requests with via routing forbid schema target');
38
+ if (maintenance !== undefined) {
39
+ invalid('destination requests with via routing forbid maintenance');
40
+ }
35
41
  }
36
42
  else if ((credential === undefined) !== (delegate === undefined)) {
37
43
  invalid('source requests require credential and delegate together');
@@ -39,11 +45,12 @@ export function decodeRequest(input) {
39
45
  return Object.freeze({
40
46
  version: 2,
41
47
  requestId: requestId(object.requestId, 'request.requestId'),
42
- call: acceptCall(object.call),
48
+ call: callFromSnapshot(object.call),
43
49
  ...(credential === undefined ? {} : { credential }),
44
50
  ...(delegate === undefined ? {} : { delegate }),
45
51
  ...(routing === undefined ? {} : { routing }),
46
52
  ...(schema === undefined ? {} : { schema }),
53
+ ...(maintenance === undefined ? {} : { maintenance }),
47
54
  ...(object.idempotencyKey === undefined
48
55
  ? {}
49
56
  : {
@@ -1,4 +1,5 @@
1
1
  import type { Value } from '@astrale-os/kernel-dsl/value';
2
+ import { type NodeId } from '@astrale-os/kernel-core/graph/node';
2
3
  import type { ErrorPayload } from '../errors/index.js';
3
4
  import type { RequestId } from './correlation.js';
4
5
  import type { InvocationId } from './identity.js';
@@ -15,5 +16,9 @@ export interface SuccessResponse<Result extends Value = Value> extends ResponseC
15
16
  export interface ErrorResponse extends ResponseContext {
16
17
  readonly error: ErrorPayload;
17
18
  }
18
- export type Response<Result extends Value = Value> = SuccessResponse<Result> | ErrorResponse | RedirectResponse | Stale | Miss;
19
+ /** A durable call's answer: the Execution it started or recovered, never a handler result. */
20
+ export interface ExecutionResponse extends ResponseContext {
21
+ readonly execution: NodeId;
22
+ }
23
+ export type Response<Result extends Value = Value> = SuccessResponse<Result> | ErrorResponse | ExecutionResponse | RedirectResponse | Stale | Miss;
19
24
  export declare function decodeResponse(input: unknown): Response;
@@ -1,17 +1,18 @@
1
+ import { isNodeId } from '@astrale-os/kernel-core/graph/node';
1
2
  import { errorPayload, exactFields, invalid, invocation, portableObject, requestId, required, } from './admission.js';
2
3
  import { decodeMiss } from './miss.js';
3
4
  import { decodeRedirectResponse } from './redirect.js';
4
5
  import { decodeStale } from './stale-route.js';
5
6
  const SUCCESS_FIELDS = new Set(['result', 'requestId', 'invocation']);
6
7
  const ERROR_FIELDS = new Set(['error', 'requestId', 'invocation']);
8
+ const EXECUTION_FIELDS = new Set(['execution', 'requestId', 'invocation']);
7
9
  export function decodeResponse(input) {
8
10
  const object = portableObject(input, 'response');
9
- const hasResult = Object.hasOwn(object, 'result');
10
- const hasError = Object.hasOwn(object, 'error');
11
- if (hasResult || hasError) {
12
- if (hasResult === hasError)
13
- invalid('response must contain exactly one of result or error');
14
- if (hasResult) {
11
+ const outcomes = ['result', 'error', 'execution'].filter((field) => Object.hasOwn(object, field));
12
+ if (outcomes.length > 1)
13
+ invalid('response must contain exactly one of result, error or execution');
14
+ switch (outcomes[0]) {
15
+ case 'result':
15
16
  exactFields(object, SUCCESS_FIELDS, 'response');
16
17
  required(object, ['requestId', 'invocation'], 'response');
17
18
  return Object.freeze({
@@ -19,14 +20,24 @@ export function decodeResponse(input) {
19
20
  requestId: requestId(object.requestId, 'response.requestId'),
20
21
  invocation: invocation(object.invocation, 'response.invocation'),
21
22
  });
22
- }
23
- exactFields(object, ERROR_FIELDS, 'response');
24
- required(object, ['requestId', 'error', 'invocation'], 'response');
25
- return Object.freeze({
26
- error: errorPayload(object.error, 'response.error'),
27
- requestId: requestId(object.requestId, 'response.requestId'),
28
- invocation: invocation(object.invocation, 'response.invocation'),
29
- });
23
+ case 'error':
24
+ exactFields(object, ERROR_FIELDS, 'response');
25
+ required(object, ['requestId', 'error', 'invocation'], 'response');
26
+ return Object.freeze({
27
+ error: errorPayload(object.error, 'response.error'),
28
+ requestId: requestId(object.requestId, 'response.requestId'),
29
+ invocation: invocation(object.invocation, 'response.invocation'),
30
+ });
31
+ case 'execution':
32
+ exactFields(object, EXECUTION_FIELDS, 'response');
33
+ required(object, ['requestId', 'execution', 'invocation'], 'response');
34
+ if (!isNodeId(object.execution))
35
+ invalid('response.execution must be a NodeId');
36
+ return Object.freeze({
37
+ execution: object.execution,
38
+ requestId: requestId(object.requestId, 'response.requestId'),
39
+ invocation: invocation(object.invocation, 'response.invocation'),
40
+ });
30
41
  }
31
42
  if (object.kind === 'redirect')
32
43
  return decodeRedirectResponse(object);
@@ -34,5 +45,5 @@ export function decodeResponse(input) {
34
45
  return decodeStale(object);
35
46
  if (object.kind === 'miss')
36
47
  return decodeMiss(object);
37
- return invalid('response must contain a result, error, redirect, stale, or miss control');
48
+ return invalid('response must contain a result, error, execution, redirect, stale, or miss control');
38
49
  }
@@ -1,14 +1,24 @@
1
1
  import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
- import type { Etag } from '../publication/etag.js';
3
- import type { PublicationRef } from '../publication/reference.js';
2
+ import type { Revision } from '@astrale-os/kernel-dsl/v1/addressing';
3
+ import type { Object as ValueObject } from '@astrale-os/kernel-dsl/value';
4
+ import type { Sha256Digest } from '../release/digest.js';
5
+ import type { ReleaseRef } from '../release/reference.js';
6
+ /** Routed witness of one installed release: the Kernel routed this call to that exact release. */
4
7
  export interface Via {
5
8
  readonly kind: 'via';
6
- readonly publication: PublicationRef;
9
+ readonly release: ReleaseRef;
7
10
  readonly issuer: IssuerId;
8
11
  }
9
12
  export interface Refresh {
10
13
  readonly kind: 'refresh';
11
- readonly stale: Etag;
14
+ readonly stale: Sha256Digest;
12
15
  }
16
+ /** A received `Via` names one exact `ReleaseRef`. */
13
17
  export type Routing = Via | Refresh;
14
18
  export declare function acceptRouting(input: unknown): Routing;
19
+ /** Admit one routed witness carrying one exact `release`. */
20
+ export declare function acceptVia(object: ValueObject, label: string): Via;
21
+ /** Schema revision of the release the witness routed the call to. */
22
+ export declare function routedRevision(via: Via): Revision;
23
+ /** Digest of the release the witness routed this call to. */
24
+ export declare function routedDigest(via: Via): Sha256Digest;
@@ -1,27 +1,12 @@
1
- import { PublicationError } from '../publication/error.js';
2
- import { acceptReference } from '../publication/reference.js';
1
+ import { ReleaseError } from '../release/error.js';
2
+ import { acceptReference } from '../release/reference.js';
3
3
  import { digest, exactFields, invalid, issuerId, portableObject, required } from './admission.js';
4
- const VIA_FIELDS = new Set(['kind', 'publication', 'issuer']);
4
+ const VIA_FIELDS = new Set(['kind', 'release', 'issuer']);
5
5
  const REFRESH_FIELDS = new Set(['kind', 'stale']);
6
6
  export function acceptRouting(input) {
7
7
  const object = portableObject(input, 'routing');
8
- if (object.kind === 'via') {
9
- exactFields(object, VIA_FIELDS, 'via routing');
10
- required(object, ['kind', 'publication', 'issuer'], 'via routing');
11
- try {
12
- return Object.freeze({
13
- kind: 'via',
14
- publication: acceptReference(object.publication),
15
- issuer: issuerId(object.issuer, 'via routing.issuer'),
16
- });
17
- }
18
- catch (cause) {
19
- if (cause instanceof PublicationError) {
20
- return invalid(`via routing.publication is invalid: ${cause.message}`);
21
- }
22
- throw cause;
23
- }
24
- }
8
+ if (object.kind === 'via')
9
+ return acceptVia(object, 'via routing');
25
10
  if (object.kind === 'refresh') {
26
11
  exactFields(object, REFRESH_FIELDS, 'refresh routing');
27
12
  required(object, ['kind', 'stale'], 'refresh routing');
@@ -32,3 +17,33 @@ export function acceptRouting(input) {
32
17
  }
33
18
  return invalid('routing.kind must be "via" or "refresh"');
34
19
  }
20
+ /** Admit one routed witness carrying one exact `release`. */
21
+ export function acceptVia(object, label) {
22
+ exactFields(object, VIA_FIELDS, label);
23
+ if (object.kind !== 'via')
24
+ invalid(`${label}.kind must be "via"`);
25
+ required(object, ['release', 'issuer'], label);
26
+ return Object.freeze({
27
+ kind: 'via',
28
+ release: acceptReleaseReference(object.release, `${label}.release`),
29
+ issuer: issuerId(object.issuer, `${label}.issuer`),
30
+ });
31
+ }
32
+ /** Schema revision of the release the witness routed the call to. */
33
+ export function routedRevision(via) {
34
+ return via.release.revision;
35
+ }
36
+ /** Digest of the release the witness routed this call to. */
37
+ export function routedDigest(via) {
38
+ return via.release.digest;
39
+ }
40
+ function acceptReleaseReference(input, label) {
41
+ try {
42
+ return acceptReference(input);
43
+ }
44
+ catch (cause) {
45
+ if (cause instanceof ReleaseError)
46
+ return invalid(`${label} is invalid: ${cause.message}`);
47
+ throw cause;
48
+ }
49
+ }
@@ -1,9 +1,11 @@
1
- import type { Revision } from '@astrale-os/kernel-dsl/v1/addressing';
2
- import type { Sha256Digest } from '../publication/index.js';
3
- /** Source-Kernel precondition for the active Schema Registration selected by one invocation. */
1
+ import type { Sha256Digest } from '../release/digest.js';
2
+ /**
3
+ * Source-Kernel precondition for one invocation: the exact installed artifact target, or the
4
+ * fingerprint of the callable meaning the static consumer compiled against.
5
+ */
4
6
  export type SchemaTarget = {
5
7
  readonly target: Sha256Digest;
6
8
  } | {
7
- readonly revision: Revision;
9
+ readonly fingerprint: Sha256Digest;
8
10
  };
9
11
  export declare function acceptSchemaTarget(input: unknown): SchemaTarget;
@@ -1,19 +1,13 @@
1
- import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
2
1
  import { digest, exactFields, invalid, portableObject } from './admission.js';
3
- const FIELDS = new Set(['target', 'revision']);
2
+ const FIELDS = new Set(['target', 'fingerprint']);
4
3
  export function acceptSchemaTarget(input) {
5
4
  const object = portableObject(input, 'schema target');
6
5
  exactFields(object, FIELDS, 'schema target');
7
- const hasTarget = Object.hasOwn(object, 'target');
8
- const hasRevision = Object.hasOwn(object, 'revision');
9
- if (hasTarget === hasRevision) {
10
- invalid('schema target must contain exactly one of target or revision');
6
+ if ([...FIELDS].filter((field) => Object.hasOwn(object, field)).length !== 1) {
7
+ invalid('schema target must contain exactly one of target or fingerprint');
11
8
  }
12
- if (hasTarget) {
9
+ if (Object.hasOwn(object, 'target')) {
13
10
  return Object.freeze({ target: digest(object.target, 'schema target.target') });
14
11
  }
15
- if (typeof object.revision !== 'string' || !patterns.revision.test(object.revision)) {
16
- invalid('schema target.revision must be a Schema revision');
17
- }
18
- return Object.freeze({ revision: object.revision });
12
+ return Object.freeze({ fingerprint: digest(object.fingerprint, 'schema target.fingerprint') });
19
13
  }
@@ -1,23 +1,21 @@
1
1
  import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
2
  import type { Revision } from '@astrale-os/kernel-dsl/v1/addressing';
3
3
  import type { Object as ValueObject } from '@astrale-os/kernel-dsl/value';
4
- import { type Key } from '@astrale-os/kernel-dsl/v1/addressing';
4
+ import { Key } from '@astrale-os/kernel-dsl/v1/addressing';
5
5
  import type { ErrorPayload, ErrorReason } from '../errors/index.js';
6
+ import type { ReleaseDigest, Sha256Digest } from '../release/digest.js';
6
7
  import { CODES } from '../errors/index.js';
7
- export declare const SCHEMA_UPGRADE_REQUIRED: 'SCHEMA_UPGRADE_REQUIRED';
8
- export declare const SCHEMA_REVISION_MISMATCH: 'SCHEMA_REVISION_MISMATCH';
8
+ export declare const CALLABLE_FINGERPRINT_MISMATCH: 'CALLABLE_FINGERPRINT_MISMATCH';
9
9
  export declare const SERVICE_TRUST_REQUIRED: 'SERVICE_TRUST_REQUIRED';
10
10
  export declare const SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE';
11
11
  export declare const SERVICE_CANDIDATE_INVALID: 'SERVICE_CANDIDATE_INVALID';
12
- export type SchemaUpgradeRequiredDetails = ValueObject & {
12
+ /** A deployment answers that it no longer serves: it runs no other code in its place. */
13
+ export declare const DEPLOYMENT_RETIRED: 'DEPLOYMENT_RETIRED';
14
+ export type CallableFingerprintMismatchDetails = ValueObject & {
13
15
  readonly origin: Key.Origin;
14
- readonly installedRevision: Revision;
15
- readonly candidateRevision: Revision;
16
- };
17
- export type SchemaRevisionMismatchDetails = ValueObject & {
18
- readonly origin: Key.Origin;
19
- readonly expectedRevision: Revision;
20
- readonly installedRevision: Revision;
16
+ readonly callable: Key.Callable;
17
+ readonly expectedFingerprint: Sha256Digest;
18
+ readonly installedFingerprint: Sha256Digest;
21
19
  };
22
20
  export type ServiceTrustRequiredDetails = ValueObject & {
23
21
  readonly origin: Key.Origin;
@@ -31,18 +29,25 @@ export type ServiceUnavailableDetails = ValueObject & {
31
29
  export type ServiceCandidateInvalidDetails = ValueObject & {
32
30
  readonly origin: Key.Origin;
33
31
  };
34
- export type SchemaUpgradeRequiredReason = ErrorReason<typeof SCHEMA_UPGRADE_REQUIRED, SchemaUpgradeRequiredDetails>;
35
- export type SchemaRevisionMismatchReason = ErrorReason<typeof SCHEMA_REVISION_MISMATCH, SchemaRevisionMismatchDetails>;
32
+ export type DeploymentRetiredDetails = ValueObject & {
33
+ readonly deployment: IssuerId;
34
+ readonly release?: ReleaseDigest;
35
+ };
36
+ export type CallableFingerprintMismatchReason = ErrorReason<typeof CALLABLE_FINGERPRINT_MISMATCH, CallableFingerprintMismatchDetails>;
36
37
  export type ServiceTrustRequiredReason = ErrorReason<typeof SERVICE_TRUST_REQUIRED, ServiceTrustRequiredDetails>;
37
38
  export type ServiceUnavailableReason = ErrorReason<typeof SERVICE_UNAVAILABLE, ServiceUnavailableDetails>;
38
39
  export type ServiceCandidateInvalidReason = ErrorReason<typeof SERVICE_CANDIDATE_INVALID, ServiceCandidateInvalidDetails>;
39
- export type ServiceReason = SchemaRevisionMismatchReason | SchemaUpgradeRequiredReason | ServiceTrustRequiredReason | ServiceUnavailableReason | ServiceCandidateInvalidReason;
40
+ export type DeploymentRetiredReason = ErrorReason<typeof DEPLOYMENT_RETIRED, DeploymentRetiredDetails>;
41
+ export type ServiceReason = CallableFingerprintMismatchReason | ServiceTrustRequiredReason | ServiceUnavailableReason | ServiceCandidateInvalidReason | DeploymentRetiredReason;
40
42
  export type ServiceErrorPayload = (Omit<ErrorPayload, 'code' | 'data'> & {
41
43
  readonly code: typeof CODES.CONFLICT;
42
- readonly data: SchemaRevisionMismatchReason | SchemaUpgradeRequiredReason | ServiceTrustRequiredReason;
44
+ readonly data: CallableFingerprintMismatchReason | ServiceTrustRequiredReason;
43
45
  }) | (Omit<ErrorPayload, 'code' | 'data'> & {
44
46
  readonly code: typeof CODES.BACKEND_UNAVAILABLE;
45
47
  readonly data: ServiceUnavailableReason | ServiceCandidateInvalidReason;
48
+ }) | (Omit<ErrorPayload, 'code' | 'data'> & {
49
+ readonly code: typeof CODES.NOT_FOUND;
50
+ readonly data: DeploymentRetiredReason;
46
51
  });
47
52
  /** Recognize only exact outer-code, reason-code, and detail-shape combinations. */
48
53
  export declare function serviceErrorOf(input: ErrorPayload): ServiceErrorPayload | undefined;
@@ -1,24 +1,21 @@
1
1
  import { AuthValueError } from '@astrale-os/kernel-core/auth/error';
2
2
  import { accept as acceptIssuer } from '@astrale-os/kernel-core/auth/issuer';
3
- import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
3
+ import { Key, patterns } from '@astrale-os/kernel-dsl/v1/addressing';
4
4
  import { CODES, reasonOf } from '../errors/index.js';
5
- export const SCHEMA_UPGRADE_REQUIRED = 'SCHEMA_UPGRADE_REQUIRED';
6
- export const SCHEMA_REVISION_MISMATCH = 'SCHEMA_REVISION_MISMATCH';
5
+ export const CALLABLE_FINGERPRINT_MISMATCH = 'CALLABLE_FINGERPRINT_MISMATCH';
7
6
  export const SERVICE_TRUST_REQUIRED = 'SERVICE_TRUST_REQUIRED';
8
7
  export const SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE';
9
8
  export const SERVICE_CANDIDATE_INVALID = 'SERVICE_CANDIDATE_INVALID';
9
+ /** A deployment answers that it no longer serves: it runs no other code in its place. */
10
+ export const DEPLOYMENT_RETIRED = 'DEPLOYMENT_RETIRED';
10
11
  /** Recognize only exact outer-code, reason-code, and detail-shape combinations. */
11
12
  export function serviceErrorOf(input) {
12
13
  const reason = reasonOf(input.data);
13
14
  if (reason === undefined)
14
15
  return undefined;
15
16
  switch (reason.code) {
16
- case SCHEMA_REVISION_MISMATCH:
17
- return input.code === CODES.CONFLICT && schemaRevisionMismatchDetails(reason.details)
18
- ? input
19
- : undefined;
20
- case SCHEMA_UPGRADE_REQUIRED:
21
- return input.code === CODES.CONFLICT && schemaUpgradeDetails(reason.details)
17
+ case CALLABLE_FINGERPRINT_MISMATCH:
18
+ return input.code === CODES.CONFLICT && callableFingerprintMismatchDetails(reason.details)
22
19
  ? input
23
20
  : undefined;
24
21
  case SERVICE_TRUST_REQUIRED:
@@ -33,21 +30,22 @@ export function serviceErrorOf(input) {
33
30
  return input.code === CODES.BACKEND_UNAVAILABLE && candidateInvalidDetails(reason.details)
34
31
  ? input
35
32
  : undefined;
33
+ case DEPLOYMENT_RETIRED:
34
+ return input.code === CODES.NOT_FOUND && deploymentRetiredDetails(reason.details)
35
+ ? input
36
+ : undefined;
36
37
  default:
37
38
  return undefined;
38
39
  }
39
40
  }
40
- function schemaRevisionMismatchDetails(input) {
41
- return (exact(input, ['origin', 'expectedRevision', 'installedRevision']) &&
41
+ function callableFingerprintMismatchDetails(input) {
42
+ return (exact(input, ['origin', 'callable', 'expectedFingerprint', 'installedFingerprint']) &&
42
43
  isOrigin(input.origin) &&
43
- isRevision(input.expectedRevision) &&
44
- isRevision(input.installedRevision));
45
- }
46
- function schemaUpgradeDetails(input) {
47
- return (exact(input, ['origin', 'installedRevision', 'candidateRevision']) &&
48
- isOrigin(input.origin) &&
49
- isRevision(input.installedRevision) &&
50
- isRevision(input.candidateRevision));
44
+ typeof input.callable === 'string' &&
45
+ Key.is(input.callable) &&
46
+ Key.origin(input.callable) === input.origin &&
47
+ isDigest(input.expectedFingerprint) &&
48
+ isDigest(input.installedFingerprint));
51
49
  }
52
50
  function serviceTrustDetails(input) {
53
51
  return (exact(input, ['origin', 'installedIssuer', 'candidateIssuer']) &&
@@ -61,6 +59,13 @@ function serviceUnavailableDetails(input) {
61
59
  function candidateInvalidDetails(input) {
62
60
  return exact(input, ['origin']) && isOrigin(input.origin);
63
61
  }
62
+ function deploymentRetiredDetails(input) {
63
+ return Object.hasOwn(input, 'release')
64
+ ? exact(input, ['deployment', 'release']) &&
65
+ isIssuer(input.deployment) &&
66
+ isDigest(input.release)
67
+ : exact(input, ['deployment']) && isIssuer(input.deployment);
68
+ }
64
69
  function exact(input, fields) {
65
70
  return (Object.keys(input).length === fields.length &&
66
71
  fields.every((field) => Object.hasOwn(input, field)));
@@ -71,6 +76,9 @@ function isOrigin(input) {
71
76
  function isRevision(input) {
72
77
  return typeof input === 'string' && patterns.revision.test(input);
73
78
  }
79
+ function isDigest(input) {
80
+ return typeof input === 'string' && /^sha256:[0-9a-f]{64}$/u.test(input);
81
+ }
74
82
  function isIssuer(input) {
75
83
  if (typeof input !== 'string')
76
84
  return false;
@@ -1,4 +1,4 @@
1
- import type { Etag } from '../publication/index.js';
1
+ import type { ReleaseDigest } from '../release/digest.js';
2
2
  import type { RequestId } from './correlation.js';
3
3
  import type { InvocationId } from './identity.js';
4
4
  /** Reserved pre-handler routing control; never an application ErrorResponse. */
@@ -6,7 +6,7 @@ export interface Stale {
6
6
  readonly kind: 'stale';
7
7
  readonly requestId: RequestId;
8
8
  readonly invocation: InvocationId;
9
- readonly current: Etag;
9
+ readonly current: ReleaseDigest;
10
10
  }
11
11
  export declare function decodeStale(input: unknown): Stale;
12
- export declare function stale(requestId: RequestId, assigned: InvocationId, current: Etag): Stale;
12
+ export declare function stale(requestId: RequestId, assigned: InvocationId, current: ReleaseDigest): Stale;
@@ -1,3 +1,6 @@
1
- import type { Call } from '@astrale-os/kernel-core/callable';
2
- export type { Call } from '@astrale-os/kernel-core/callable';
1
+ import type { Value } from '@astrale-os/kernel-dsl/value';
2
+ import { type Call } from '@astrale-os/kernel-core/callable';
3
+ export { Target, targetPath, type Call } from '@astrale-os/kernel-core/callable';
3
4
  export declare function acceptCall(input: unknown): Call;
5
+ /** Package-private: consume the detached portable subtree captured by request admission. */
6
+ export declare function callFromSnapshot(input: Value): Call;
@@ -1,11 +1,33 @@
1
- import { exactFields, nodePath, portableObject, required } from './admission.js';
1
+ import { Target } from '@astrale-os/kernel-core/callable';
2
+ import { PathError } from '@astrale-os/kernel-core/path';
3
+ import { exactFields, invalid, nodePath, portableObject, required } from './admission.js';
4
+ export { Target, targetPath } from '@astrale-os/kernel-core/callable';
2
5
  const CALL_FIELDS = new Set(['target', 'input']);
3
6
  export function acceptCall(input) {
4
- const object = portableObject(input, 'call');
7
+ return callFromSnapshot(portableObject(input, 'call'));
8
+ }
9
+ /** Package-private: consume the detached portable subtree captured by request admission. */
10
+ export function callFromSnapshot(input) {
11
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
12
+ invalid('call must be an object');
13
+ }
14
+ const object = input;
5
15
  exactFields(object, CALL_FIELDS, 'call');
6
16
  required(object, ['target', 'input'], 'call');
7
17
  return Object.freeze({
8
- target: nodePath(object.target, 'call.target'),
18
+ target: acceptTarget(object.target),
9
19
  input: object.input,
10
20
  });
11
21
  }
22
+ function acceptTarget(input) {
23
+ if (typeof input === 'string')
24
+ return nodePath(input, 'call.target');
25
+ try {
26
+ return Target(input);
27
+ }
28
+ catch (cause) {
29
+ if (cause instanceof TypeError || cause instanceof PathError)
30
+ return invalid('call.target must be a canonical NodePath or declaration Target');
31
+ throw cause;
32
+ }
33
+ }