@astrale-os/kernel-protocol 0.5.0-beta.10

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 (224) hide show
  1. package/LICENSE +202 -0
  2. package/dist/artifact/index.d.ts +1 -0
  3. package/dist/artifact/index.js +1 -0
  4. package/dist/artifact/ref.d.ts +10 -0
  5. package/dist/artifact/ref.js +70 -0
  6. package/dist/codecs/codec.d.ts +13 -0
  7. package/dist/codecs/codec.js +6 -0
  8. package/dist/codecs/index.d.ts +4 -0
  9. package/dist/codecs/index.js +4 -0
  10. package/dist/codecs/json.d.ts +2 -0
  11. package/dist/codecs/json.js +20 -0
  12. package/dist/codecs/media-types.d.ts +2 -0
  13. package/dist/codecs/media-types.js +2 -0
  14. package/dist/codecs/msgpack.d.ts +4 -0
  15. package/dist/codecs/msgpack.js +50 -0
  16. package/dist/errors/codes.d.ts +23 -0
  17. package/dist/errors/codes.js +22 -0
  18. package/dist/errors/http-status.d.ts +2 -0
  19. package/dist/errors/http-status.js +25 -0
  20. package/dist/errors/index.d.ts +5 -0
  21. package/dist/errors/index.js +5 -0
  22. package/dist/errors/payload.d.ts +8 -0
  23. package/dist/errors/payload.js +37 -0
  24. package/dist/errors/protocol-error.d.ts +6 -0
  25. package/dist/errors/protocol-error.js +11 -0
  26. package/dist/errors/reason.d.ts +7 -0
  27. package/dist/errors/reason.js +43 -0
  28. package/dist/index.d.ts +8 -0
  29. package/dist/index.js +8 -0
  30. package/dist/invocation/admission.d.ts +23 -0
  31. package/dist/invocation/admission.js +177 -0
  32. package/dist/invocation/binary.d.ts +11 -0
  33. package/dist/invocation/binary.js +24 -0
  34. package/dist/invocation/binding-codecs.d.ts +8 -0
  35. package/dist/invocation/binding-codecs.js +27 -0
  36. package/dist/invocation/cancellation.d.ts +8 -0
  37. package/dist/invocation/cancellation.js +16 -0
  38. package/dist/invocation/correlation.d.ts +2 -0
  39. package/dist/invocation/correlation.js +7 -0
  40. package/dist/invocation/delegate.d.ts +24 -0
  41. package/dist/invocation/delegate.js +43 -0
  42. package/dist/invocation/http/binary.d.ts +12 -0
  43. package/dist/invocation/http/binary.js +132 -0
  44. package/dist/invocation/http/index.d.ts +4 -0
  45. package/dist/invocation/http/index.js +4 -0
  46. package/dist/invocation/http/negotiation.d.ts +3 -0
  47. package/dist/invocation/http/negotiation.js +207 -0
  48. package/dist/invocation/http/stream.d.ts +5 -0
  49. package/dist/invocation/http/stream.js +15 -0
  50. package/dist/invocation/http/trace.d.ts +6 -0
  51. package/dist/invocation/http/trace.js +24 -0
  52. package/dist/invocation/idempotency.d.ts +10 -0
  53. package/dist/invocation/idempotency.js +13 -0
  54. package/dist/invocation/identity.d.ts +16 -0
  55. package/dist/invocation/identity.js +55 -0
  56. package/dist/invocation/index.d.ts +20 -0
  57. package/dist/invocation/index.js +20 -0
  58. package/dist/invocation/miss.d.ts +9 -0
  59. package/dist/invocation/miss.js +17 -0
  60. package/dist/invocation/redirect.d.ts +40 -0
  61. package/dist/invocation/redirect.js +81 -0
  62. package/dist/invocation/request.d.ts +21 -0
  63. package/dist/invocation/request.js +54 -0
  64. package/dist/invocation/response.d.ts +19 -0
  65. package/dist/invocation/response.js +38 -0
  66. package/dist/invocation/routing.d.ts +14 -0
  67. package/dist/invocation/routing.js +34 -0
  68. package/dist/invocation/schema.d.ts +9 -0
  69. package/dist/invocation/schema.js +19 -0
  70. package/dist/invocation/service-reason.d.ts +48 -0
  71. package/dist/invocation/service-reason.js +85 -0
  72. package/dist/invocation/stale-route.d.ts +12 -0
  73. package/dist/invocation/stale-route.js +19 -0
  74. package/dist/invocation/stream.d.ts +27 -0
  75. package/dist/invocation/stream.js +44 -0
  76. package/dist/invocation/target.d.ts +3 -0
  77. package/dist/invocation/target.js +11 -0
  78. package/dist/invocation/trace.d.ts +9 -0
  79. package/dist/invocation/trace.js +86 -0
  80. package/dist/invocation/version.d.ts +2 -0
  81. package/dist/invocation/version.js +1 -0
  82. package/dist/invocation/websocket/binary.d.ts +3 -0
  83. package/dist/invocation/websocket/binary.js +71 -0
  84. package/dist/invocation/websocket/index.d.ts +2 -0
  85. package/dist/invocation/websocket/index.js +2 -0
  86. package/dist/invocation/websocket/subprotocol.d.ts +10 -0
  87. package/dist/invocation/websocket/subprotocol.js +29 -0
  88. package/dist/issuer/configuration.d.ts +14 -0
  89. package/dist/issuer/configuration.js +86 -0
  90. package/dist/issuer/error.d.ts +7 -0
  91. package/dist/issuer/error.js +11 -0
  92. package/dist/issuer/exchange.d.ts +25 -0
  93. package/dist/issuer/exchange.js +129 -0
  94. package/dist/issuer/hosted.d.ts +2 -0
  95. package/dist/issuer/hosted.js +7 -0
  96. package/dist/issuer/index.d.ts +6 -0
  97. package/dist/issuer/index.js +6 -0
  98. package/dist/issuer/key-set.d.ts +6 -0
  99. package/dist/issuer/key-set.js +48 -0
  100. package/dist/issuer/paths.d.ts +6 -0
  101. package/dist/issuer/paths.js +10 -0
  102. package/dist/issuer/url.d.ts +2 -0
  103. package/dist/issuer/url.js +34 -0
  104. package/dist/publication/bindings/callable.d.ts +8 -0
  105. package/dist/publication/bindings/callable.js +34 -0
  106. package/dist/publication/bindings/index.d.ts +3 -0
  107. package/dist/publication/bindings/index.js +3 -0
  108. package/dist/publication/bindings/invocation.d.ts +5 -0
  109. package/dist/publication/bindings/invocation.js +13 -0
  110. package/dist/publication/bindings/view.d.ts +7 -0
  111. package/dist/publication/bindings/view.js +28 -0
  112. package/dist/publication/bundle-admission.d.ts +4 -0
  113. package/dist/publication/bundle-admission.js +65 -0
  114. package/dist/publication/bundle-descriptor.d.ts +9 -0
  115. package/dist/publication/bundle-descriptor.js +19 -0
  116. package/dist/publication/bundle-evidence.d.ts +3 -0
  117. package/dist/publication/bundle-evidence.js +13 -0
  118. package/dist/publication/bundle.d.ts +9 -0
  119. package/dist/publication/bundle.js +44 -0
  120. package/dist/publication/content.d.ts +10 -0
  121. package/dist/publication/content.js +55 -0
  122. package/dist/publication/declaration.d.ts +11 -0
  123. package/dist/publication/declaration.js +1 -0
  124. package/dist/publication/delivery/attestation.d.ts +6 -0
  125. package/dist/publication/delivery/attestation.js +10 -0
  126. package/dist/publication/delivery/claims.d.ts +16 -0
  127. package/dist/publication/delivery/claims.js +61 -0
  128. package/dist/publication/delivery/delivery.d.ts +9 -0
  129. package/dist/publication/delivery/delivery.js +29 -0
  130. package/dist/publication/delivery/error.d.ts +7 -0
  131. package/dist/publication/delivery/error.js +11 -0
  132. package/dist/publication/delivery/index.d.ts +6 -0
  133. package/dist/publication/delivery/index.js +6 -0
  134. package/dist/publication/delivery/limits.d.ts +4 -0
  135. package/dist/publication/delivery/limits.js +4 -0
  136. package/dist/publication/delivery/request.d.ts +15 -0
  137. package/dist/publication/delivery/request.js +52 -0
  138. package/dist/publication/delivery-declaration.d.ts +4 -0
  139. package/dist/publication/delivery-declaration.js +25 -0
  140. package/dist/publication/delivery-endpoint.d.ts +11 -0
  141. package/dist/publication/delivery-endpoint.js +22 -0
  142. package/dist/publication/error.d.ts +7 -0
  143. package/dist/publication/error.js +11 -0
  144. package/dist/publication/etag.d.ts +6 -0
  145. package/dist/publication/etag.js +79 -0
  146. package/dist/publication/index.d.ts +17 -0
  147. package/dist/publication/index.js +10 -0
  148. package/dist/publication/manifest.d.ts +6 -0
  149. package/dist/publication/manifest.js +30 -0
  150. package/dist/publication/publication.d.ts +36 -0
  151. package/dist/publication/publication.js +133 -0
  152. package/dist/publication/reference.d.ts +17 -0
  153. package/dist/publication/reference.js +68 -0
  154. package/dist/publication/requirements.d.ts +26 -0
  155. package/dist/publication/requirements.js +180 -0
  156. package/dist/publication/route.d.ts +37 -0
  157. package/dist/publication/route.js +154 -0
  158. package/dist/publication/schema.d.ts +7 -0
  159. package/dist/publication/schema.js +15 -0
  160. package/dist/routes/admission.d.ts +3 -0
  161. package/dist/routes/admission.js +89 -0
  162. package/dist/routes/binding.d.ts +6 -0
  163. package/dist/routes/binding.js +27 -0
  164. package/dist/routes/collision.d.ts +2 -0
  165. package/dist/routes/collision.js +16 -0
  166. package/dist/routes/compiler.d.ts +8 -0
  167. package/dist/routes/compiler.js +50 -0
  168. package/dist/routes/credential.d.ts +8 -0
  169. package/dist/routes/credential.js +118 -0
  170. package/dist/routes/decode.d.ts +5 -0
  171. package/dist/routes/decode.js +147 -0
  172. package/dist/routes/encode.d.ts +4 -0
  173. package/dist/routes/encode.js +145 -0
  174. package/dist/routes/error-response.d.ts +5 -0
  175. package/dist/routes/error-response.js +31 -0
  176. package/dist/routes/error.d.ts +8 -0
  177. package/dist/routes/error.js +14 -0
  178. package/dist/routes/evidence.d.ts +5 -0
  179. package/dist/routes/evidence.js +38 -0
  180. package/dist/routes/index.d.ts +19 -0
  181. package/dist/routes/index.js +16 -0
  182. package/dist/routes/mapping.d.ts +5 -0
  183. package/dist/routes/mapping.js +126 -0
  184. package/dist/routes/match.d.ts +4 -0
  185. package/dist/routes/match.js +7 -0
  186. package/dist/routes/placement.d.ts +5 -0
  187. package/dist/routes/placement.js +18 -0
  188. package/dist/routes/plan.d.ts +24 -0
  189. package/dist/routes/plan.js +1 -0
  190. package/dist/routes/release-unavailable.d.ts +8 -0
  191. package/dist/routes/release-unavailable.js +32 -0
  192. package/dist/routes/request.d.ts +26 -0
  193. package/dist/routes/request.js +53 -0
  194. package/dist/routes/reservation.d.ts +3 -0
  195. package/dist/routes/reservation.js +37 -0
  196. package/dist/routes/routing.d.ts +22 -0
  197. package/dist/routes/routing.js +87 -0
  198. package/dist/routes/scalar.d.ts +7 -0
  199. package/dist/routes/scalar.js +61 -0
  200. package/dist/routes/select.d.ts +3 -0
  201. package/dist/routes/select.js +16 -0
  202. package/dist/routes/stream.d.ts +14 -0
  203. package/dist/routes/stream.js +50 -0
  204. package/dist/routes/table.d.ts +9 -0
  205. package/dist/routes/table.js +1 -0
  206. package/dist/routes/target.d.ts +13 -0
  207. package/dist/routes/target.js +3 -0
  208. package/dist/routes/template.d.ts +8 -0
  209. package/dist/routes/template.js +227 -0
  210. package/dist/runtime/artifact.d.ts +47 -0
  211. package/dist/runtime/artifact.js +176 -0
  212. package/dist/runtime/index.d.ts +1 -0
  213. package/dist/runtime/index.js +1 -0
  214. package/dist/schema/index.d.ts +1 -0
  215. package/dist/schema/index.js +1 -0
  216. package/dist/schema/installation/index.d.ts +1 -0
  217. package/dist/schema/installation/index.js +1 -0
  218. package/dist/schema/installation/introspection/codec.d.ts +5 -0
  219. package/dist/schema/installation/introspection/codec.js +272 -0
  220. package/dist/schema/installation/introspection/index.d.ts +2 -0
  221. package/dist/schema/installation/introspection/index.js +1 -0
  222. package/dist/schema/installation/introspection/result.d.ts +33 -0
  223. package/dist/schema/installation/introspection/result.js +1 -0
  224. package/package.json +163 -0
@@ -0,0 +1,38 @@
1
+ import { errorPayload, exactFields, invalid, invocation, portableObject, requestId, required, } from './admission.js';
2
+ import { decodeMiss } from './miss.js';
3
+ import { decodeRedirectResponse } from './redirect.js';
4
+ import { decodeStale } from './stale-route.js';
5
+ const SUCCESS_FIELDS = new Set(['result', 'requestId', 'invocation']);
6
+ const ERROR_FIELDS = new Set(['error', 'requestId', 'invocation']);
7
+ export function decodeResponse(input) {
8
+ 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) {
15
+ exactFields(object, SUCCESS_FIELDS, 'response');
16
+ required(object, ['requestId', 'invocation'], 'response');
17
+ return Object.freeze({
18
+ result: object.result,
19
+ requestId: requestId(object.requestId, 'response.requestId'),
20
+ invocation: invocation(object.invocation, 'response.invocation'),
21
+ });
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
+ });
30
+ }
31
+ if (object.kind === 'redirect')
32
+ return decodeRedirectResponse(object);
33
+ if (object.kind === 'stale')
34
+ return decodeStale(object);
35
+ if (object.kind === 'miss')
36
+ return decodeMiss(object);
37
+ return invalid('response must contain a result, error, redirect, stale, or miss control');
38
+ }
@@ -0,0 +1,14 @@
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';
4
+ export interface Via {
5
+ readonly kind: 'via';
6
+ readonly publication: PublicationRef;
7
+ readonly issuer: IssuerId;
8
+ }
9
+ export interface Refresh {
10
+ readonly kind: 'refresh';
11
+ readonly stale: Etag;
12
+ }
13
+ export type Routing = Via | Refresh;
14
+ export declare function acceptRouting(input: unknown): Routing;
@@ -0,0 +1,34 @@
1
+ import { PublicationError } from '../publication/error.js';
2
+ import { acceptReference } from '../publication/reference.js';
3
+ import { digest, exactFields, invalid, issuerId, portableObject, required } from './admission.js';
4
+ const VIA_FIELDS = new Set(['kind', 'publication', 'issuer']);
5
+ const REFRESH_FIELDS = new Set(['kind', 'stale']);
6
+ export function acceptRouting(input) {
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
+ }
25
+ if (object.kind === 'refresh') {
26
+ exactFields(object, REFRESH_FIELDS, 'refresh routing');
27
+ required(object, ['kind', 'stale'], 'refresh routing');
28
+ return Object.freeze({
29
+ kind: 'refresh',
30
+ stale: digest(object.stale, 'refresh routing.stale'),
31
+ });
32
+ }
33
+ return invalid('routing.kind must be "via" or "refresh"');
34
+ }
@@ -0,0 +1,9 @@
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. */
4
+ export type SchemaTarget = {
5
+ readonly target: Sha256Digest;
6
+ } | {
7
+ readonly revision: Revision;
8
+ };
9
+ export declare function acceptSchemaTarget(input: unknown): SchemaTarget;
@@ -0,0 +1,19 @@
1
+ import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
2
+ import { digest, exactFields, invalid, portableObject } from './admission.js';
3
+ const FIELDS = new Set(['target', 'revision']);
4
+ export function acceptSchemaTarget(input) {
5
+ const object = portableObject(input, 'schema target');
6
+ 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');
11
+ }
12
+ if (hasTarget) {
13
+ return Object.freeze({ target: digest(object.target, 'schema target.target') });
14
+ }
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 });
19
+ }
@@ -0,0 +1,48 @@
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
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 Key } from '@astrale-os/kernel-dsl/v1/addressing';
5
+ import type { ErrorPayload, ErrorReason } from '../errors/index.js';
6
+ 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';
9
+ export declare const SERVICE_TRUST_REQUIRED: 'SERVICE_TRUST_REQUIRED';
10
+ export declare const SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE';
11
+ export declare const SERVICE_CANDIDATE_INVALID: 'SERVICE_CANDIDATE_INVALID';
12
+ export type SchemaUpgradeRequiredDetails = ValueObject & {
13
+ 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;
21
+ };
22
+ export type ServiceTrustRequiredDetails = ValueObject & {
23
+ readonly origin: Key.Origin;
24
+ readonly installedIssuer: IssuerId;
25
+ readonly candidateIssuer: IssuerId;
26
+ };
27
+ export type ServiceUnavailableDetails = ValueObject & {
28
+ readonly origin: Key.Origin;
29
+ readonly revision: Revision;
30
+ };
31
+ export type ServiceCandidateInvalidDetails = ValueObject & {
32
+ readonly origin: Key.Origin;
33
+ };
34
+ export type SchemaUpgradeRequiredReason = ErrorReason<typeof SCHEMA_UPGRADE_REQUIRED, SchemaUpgradeRequiredDetails>;
35
+ export type SchemaRevisionMismatchReason = ErrorReason<typeof SCHEMA_REVISION_MISMATCH, SchemaRevisionMismatchDetails>;
36
+ export type ServiceTrustRequiredReason = ErrorReason<typeof SERVICE_TRUST_REQUIRED, ServiceTrustRequiredDetails>;
37
+ export type ServiceUnavailableReason = ErrorReason<typeof SERVICE_UNAVAILABLE, ServiceUnavailableDetails>;
38
+ export type ServiceCandidateInvalidReason = ErrorReason<typeof SERVICE_CANDIDATE_INVALID, ServiceCandidateInvalidDetails>;
39
+ export type ServiceReason = SchemaRevisionMismatchReason | SchemaUpgradeRequiredReason | ServiceTrustRequiredReason | ServiceUnavailableReason | ServiceCandidateInvalidReason;
40
+ export type ServiceErrorPayload = (Omit<ErrorPayload, 'code' | 'data'> & {
41
+ readonly code: typeof CODES.CONFLICT;
42
+ readonly data: SchemaRevisionMismatchReason | SchemaUpgradeRequiredReason | ServiceTrustRequiredReason;
43
+ }) | (Omit<ErrorPayload, 'code' | 'data'> & {
44
+ readonly code: typeof CODES.BACKEND_UNAVAILABLE;
45
+ readonly data: ServiceUnavailableReason | ServiceCandidateInvalidReason;
46
+ });
47
+ /** Recognize only exact outer-code, reason-code, and detail-shape combinations. */
48
+ export declare function serviceErrorOf(input: ErrorPayload): ServiceErrorPayload | undefined;
@@ -0,0 +1,85 @@
1
+ import { AuthValueError } from '@astrale-os/kernel-core/auth/error';
2
+ import { accept as acceptIssuer } from '@astrale-os/kernel-core/auth/issuer';
3
+ import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
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';
7
+ export const SERVICE_TRUST_REQUIRED = 'SERVICE_TRUST_REQUIRED';
8
+ export const SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE';
9
+ export const SERVICE_CANDIDATE_INVALID = 'SERVICE_CANDIDATE_INVALID';
10
+ /** Recognize only exact outer-code, reason-code, and detail-shape combinations. */
11
+ export function serviceErrorOf(input) {
12
+ const reason = reasonOf(input.data);
13
+ if (reason === undefined)
14
+ return undefined;
15
+ 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)
22
+ ? input
23
+ : undefined;
24
+ case SERVICE_TRUST_REQUIRED:
25
+ return input.code === CODES.CONFLICT && serviceTrustDetails(reason.details)
26
+ ? input
27
+ : undefined;
28
+ case SERVICE_UNAVAILABLE:
29
+ return input.code === CODES.BACKEND_UNAVAILABLE && serviceUnavailableDetails(reason.details)
30
+ ? input
31
+ : undefined;
32
+ case SERVICE_CANDIDATE_INVALID:
33
+ return input.code === CODES.BACKEND_UNAVAILABLE && candidateInvalidDetails(reason.details)
34
+ ? input
35
+ : undefined;
36
+ default:
37
+ return undefined;
38
+ }
39
+ }
40
+ function schemaRevisionMismatchDetails(input) {
41
+ return (exact(input, ['origin', 'expectedRevision', 'installedRevision']) &&
42
+ 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));
51
+ }
52
+ function serviceTrustDetails(input) {
53
+ return (exact(input, ['origin', 'installedIssuer', 'candidateIssuer']) &&
54
+ isOrigin(input.origin) &&
55
+ isIssuer(input.installedIssuer) &&
56
+ isIssuer(input.candidateIssuer));
57
+ }
58
+ function serviceUnavailableDetails(input) {
59
+ return (exact(input, ['origin', 'revision']) && isOrigin(input.origin) && isRevision(input.revision));
60
+ }
61
+ function candidateInvalidDetails(input) {
62
+ return exact(input, ['origin']) && isOrigin(input.origin);
63
+ }
64
+ function exact(input, fields) {
65
+ return (Object.keys(input).length === fields.length &&
66
+ fields.every((field) => Object.hasOwn(input, field)));
67
+ }
68
+ function isOrigin(input) {
69
+ return typeof input === 'string' && patterns.origin.test(input);
70
+ }
71
+ function isRevision(input) {
72
+ return typeof input === 'string' && patterns.revision.test(input);
73
+ }
74
+ function isIssuer(input) {
75
+ if (typeof input !== 'string')
76
+ return false;
77
+ try {
78
+ return acceptIssuer(input) === input;
79
+ }
80
+ catch (cause) {
81
+ if (cause instanceof AuthValueError)
82
+ return false;
83
+ throw cause;
84
+ }
85
+ }
@@ -0,0 +1,12 @@
1
+ import type { Etag } from '../publication/index.js';
2
+ import type { RequestId } from './correlation.js';
3
+ import type { InvocationId } from './identity.js';
4
+ /** Reserved pre-handler routing control; never an application ErrorResponse. */
5
+ export interface Stale {
6
+ readonly kind: 'stale';
7
+ readonly requestId: RequestId;
8
+ readonly invocation: InvocationId;
9
+ readonly current: Etag;
10
+ }
11
+ export declare function decodeStale(input: unknown): Stale;
12
+ export declare function stale(requestId: RequestId, assigned: InvocationId, current: Etag): Stale;
@@ -0,0 +1,19 @@
1
+ import { digest, exactFields, invalid, invocation, portableObject, requestId, required, } from './admission.js';
2
+ const FIELDS = new Set(['kind', 'requestId', 'invocation', 'current']);
3
+ export function decodeStale(input) {
4
+ const object = portableObject(input, 'stale control');
5
+ exactFields(object, FIELDS, 'stale control');
6
+ required(object, ['kind', 'requestId', 'invocation', 'current'], 'stale control');
7
+ if (object.kind !== 'stale') {
8
+ invalid('stale control.kind must be "stale"');
9
+ }
10
+ return Object.freeze({
11
+ kind: 'stale',
12
+ requestId: requestId(object.requestId, 'stale control.requestId'),
13
+ invocation: invocation(object.invocation, 'stale control.invocation'),
14
+ current: digest(object.current, 'stale control.current'),
15
+ });
16
+ }
17
+ export function stale(requestId, assigned, current) {
18
+ return decodeStale({ kind: 'stale', requestId, invocation: assigned, current });
19
+ }
@@ -0,0 +1,27 @@
1
+ import type { Value } from '@astrale-os/kernel-dsl/value';
2
+ import type { ErrorPayload } from '../errors/index.js';
3
+ import type { RequestId } from './correlation.js';
4
+ import type { InvocationId } from './identity.js';
5
+ export interface StreamStartFrame {
6
+ readonly kind: 'stream.start';
7
+ readonly requestId: RequestId;
8
+ readonly invocation: InvocationId;
9
+ }
10
+ export interface StreamItemFrame {
11
+ readonly kind: 'stream.item';
12
+ readonly requestId: RequestId;
13
+ readonly index: number;
14
+ readonly value: Value;
15
+ }
16
+ export interface StreamEndFrame {
17
+ readonly kind: 'stream.end';
18
+ readonly requestId: RequestId;
19
+ readonly count: number;
20
+ }
21
+ export interface StreamErrorFrame {
22
+ readonly kind: 'stream.error';
23
+ readonly requestId: RequestId;
24
+ readonly error: ErrorPayload;
25
+ }
26
+ export type StreamFrame = StreamStartFrame | StreamItemFrame | StreamEndFrame | StreamErrorFrame;
27
+ export declare function decodeStreamFrame(input: unknown): StreamFrame;
@@ -0,0 +1,44 @@
1
+ import { count, errorPayload, exactFields, invalid, invocation, portableObject, requestId, required, } from './admission.js';
2
+ export function decodeStreamFrame(input) {
3
+ const object = portableObject(input, 'stream frame');
4
+ const kind = object.kind;
5
+ if (typeof kind !== 'string')
6
+ invalid('stream frame.kind must be a string');
7
+ switch (kind) {
8
+ case 'stream.start':
9
+ exactFields(object, new Set(['kind', 'requestId', 'invocation']), 'stream.start');
10
+ required(object, ['requestId', 'invocation'], 'stream.start');
11
+ return Object.freeze({
12
+ kind,
13
+ requestId: requestId(object.requestId, 'stream.start.requestId'),
14
+ invocation: invocation(object.invocation, 'stream.start.invocation'),
15
+ });
16
+ case 'stream.item':
17
+ exactFields(object, new Set(['kind', 'requestId', 'index', 'value']), 'stream.item');
18
+ required(object, ['requestId', 'index', 'value'], 'stream.item');
19
+ return Object.freeze({
20
+ kind,
21
+ requestId: requestId(object.requestId, 'stream.item.requestId'),
22
+ index: count(object.index, 'stream.item.index'),
23
+ value: object.value,
24
+ });
25
+ case 'stream.end':
26
+ exactFields(object, new Set(['kind', 'requestId', 'count']), 'stream.end');
27
+ required(object, ['requestId', 'count'], 'stream.end');
28
+ return Object.freeze({
29
+ kind,
30
+ requestId: requestId(object.requestId, 'stream.end.requestId'),
31
+ count: count(object.count, 'stream.end.count'),
32
+ });
33
+ case 'stream.error':
34
+ exactFields(object, new Set(['kind', 'requestId', 'error']), 'stream.error');
35
+ required(object, ['requestId', 'error'], 'stream.error');
36
+ return Object.freeze({
37
+ kind,
38
+ requestId: requestId(object.requestId, 'stream.error.requestId'),
39
+ error: errorPayload(object.error, 'stream.error.error'),
40
+ });
41
+ default:
42
+ return invalid(`unsupported stream frame kind ${JSON.stringify(kind)}`);
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ import type { Call } from '@astrale-os/kernel-core/callable';
2
+ export type { Call } from '@astrale-os/kernel-core/callable';
3
+ export declare function acceptCall(input: unknown): Call;
@@ -0,0 +1,11 @@
1
+ import { exactFields, nodePath, portableObject, required } from './admission.js';
2
+ const CALL_FIELDS = new Set(['target', 'input']);
3
+ export function acceptCall(input) {
4
+ const object = portableObject(input, 'call');
5
+ exactFields(object, CALL_FIELDS, 'call');
6
+ required(object, ['target', 'input'], 'call');
7
+ return Object.freeze({
8
+ target: nodePath(object.target, 'call.target'),
9
+ input: object.input,
10
+ });
11
+ }
@@ -0,0 +1,9 @@
1
+ export declare const MAXIMUM_TRACEPARENT_BYTES = 512;
2
+ export declare const MAXIMUM_TRACESTATE_BYTES = 512;
3
+ export declare const MAXIMUM_TRACESTATE_MEMBERS = 32;
4
+ export interface Trace {
5
+ readonly traceparent: string;
6
+ readonly tracestate?: string;
7
+ }
8
+ /** Admit portable correlation syntax without making it an invocation validity condition. */
9
+ export declare function admitTrace(input: unknown): Trace | undefined;
@@ -0,0 +1,86 @@
1
+ export const MAXIMUM_TRACEPARENT_BYTES = 512;
2
+ export const MAXIMUM_TRACESTATE_BYTES = 512;
3
+ export const MAXIMUM_TRACESTATE_MEMBERS = 32;
4
+ const TRACEPARENT_PREFIX = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})/u;
5
+ const FUTURE_SUFFIX = /^-[\x21-\x7e]+$/u;
6
+ const SIMPLE_KEY = /^[a-z][a-z0-9_*/-]{0,255}$/u;
7
+ const TENANT_ID = /^[a-z0-9][a-z0-9_*/-]{0,240}$/u;
8
+ const SYSTEM_ID = /^[a-z][a-z0-9_*/-]{0,13}$/u;
9
+ const STATE_VALUE = /^[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]$/u;
10
+ /** Admit portable correlation syntax without making it an invocation validity condition. */
11
+ export function admitTrace(input) {
12
+ if (input === null || typeof input !== 'object' || Array.isArray(input))
13
+ return undefined;
14
+ const keys = Reflect.ownKeys(input);
15
+ if (!keys.includes('traceparent') ||
16
+ keys.some((key) => key !== 'traceparent' && key !== 'tracestate')) {
17
+ return undefined;
18
+ }
19
+ let traceparent;
20
+ let tracestate;
21
+ try {
22
+ const value = input;
23
+ traceparent = value.traceparent;
24
+ tracestate = value.tracestate;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ if (typeof traceparent !== 'string' || !validTraceparent(traceparent))
30
+ return undefined;
31
+ const state = typeof tracestate === 'string' && validTracestate(tracestate) ? tracestate : undefined;
32
+ return Object.freeze({
33
+ traceparent,
34
+ ...(state === undefined || state.trim() === '' ? {} : { tracestate: state }),
35
+ });
36
+ }
37
+ function validTraceparent(input) {
38
+ if (input.length < 55 || input.length > MAXIMUM_TRACEPARENT_BYTES)
39
+ return false;
40
+ const match = TRACEPARENT_PREFIX.exec(input);
41
+ if (match === null || match.index !== 0)
42
+ return false;
43
+ const [, version, traceId, parentId, flags] = match;
44
+ if (version === undefined ||
45
+ traceId === undefined ||
46
+ parentId === undefined ||
47
+ flags === undefined ||
48
+ version === 'ff' ||
49
+ /^0+$/u.test(traceId) ||
50
+ /^0+$/u.test(parentId)) {
51
+ return false;
52
+ }
53
+ if (version === '00')
54
+ return input.length === 55 && (flags === '00' || flags === '01');
55
+ return input.length === 55 || FUTURE_SUFFIX.test(input.slice(55));
56
+ }
57
+ function validTracestate(input) {
58
+ if (input.length > MAXIMUM_TRACESTATE_BYTES)
59
+ return false;
60
+ const members = input.split(',');
61
+ if (members.length > MAXIMUM_TRACESTATE_MEMBERS)
62
+ return false;
63
+ const keys = new Set();
64
+ for (const inputMember of members) {
65
+ const member = inputMember.replace(/^[ \t]+|[ \t]+$/gu, '');
66
+ if (member === '')
67
+ continue;
68
+ const separator = member.indexOf('=');
69
+ if (separator < 1 || separator !== member.lastIndexOf('='))
70
+ return false;
71
+ const key = member.slice(0, separator);
72
+ const value = member.slice(separator + 1);
73
+ if (!validStateKey(key) || !STATE_VALUE.test(value) || keys.has(key))
74
+ return false;
75
+ keys.add(key);
76
+ }
77
+ return true;
78
+ }
79
+ function validStateKey(input) {
80
+ const separator = input.indexOf('@');
81
+ if (separator === -1)
82
+ return SIMPLE_KEY.test(input);
83
+ if (separator !== input.lastIndexOf('@'))
84
+ return false;
85
+ return TENANT_ID.test(input.slice(0, separator)) && SYSTEM_ID.test(input.slice(separator + 1));
86
+ }
@@ -0,0 +1,2 @@
1
+ export declare const VERSION: 2;
2
+ export type Version = typeof VERSION;
@@ -0,0 +1 @@
1
+ export const VERSION = 2;
@@ -0,0 +1,3 @@
1
+ import type { BinaryResponse } from '../binary.js';
2
+ export declare function encodeBinary(response: BinaryResponse): Uint8Array;
3
+ export declare function decodeBinary(input: Uint8Array): BinaryResponse | undefined;
@@ -0,0 +1,71 @@
1
+ import { CODES, ProtocolError } from '../../errors/index.js';
2
+ import { decodeBinaryResponse } from '../binary.js';
3
+ const MAGIC = new Uint8Array([0x41, 0x53, 0x54, 0x42, 0x01]);
4
+ const LENGTH_BYTES = 4;
5
+ const MAXIMUM_METADATA_BYTES = 65_536;
6
+ const encoder = new TextEncoder();
7
+ const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: false });
8
+ export function encodeBinary(response) {
9
+ const headers = response.headers === undefined
10
+ ? undefined
11
+ : Object.fromEntries(Object.entries(response.headers).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0));
12
+ const metadata = encoder.encode(JSON.stringify({
13
+ kind: response.kind,
14
+ requestId: response.requestId,
15
+ invocation: response.invocation,
16
+ mediaType: response.mediaType,
17
+ ...(headers === undefined ? {} : { headers }),
18
+ }));
19
+ if (metadata.byteLength > MAXIMUM_METADATA_BYTES)
20
+ invalid();
21
+ const output = new Uint8Array(MAGIC.byteLength + LENGTH_BYTES + metadata.byteLength + response.body.byteLength);
22
+ output.set(MAGIC, 0);
23
+ new DataView(output.buffer).setUint32(MAGIC.byteLength, metadata.byteLength);
24
+ output.set(metadata, MAGIC.byteLength + LENGTH_BYTES);
25
+ output.set(response.body, MAGIC.byteLength + LENGTH_BYTES + metadata.byteLength);
26
+ return output;
27
+ }
28
+ export function decodeBinary(input) {
29
+ if (!hasMagic(input))
30
+ return undefined;
31
+ const headerBytes = MAGIC.byteLength + LENGTH_BYTES;
32
+ if (input.byteLength < headerBytes)
33
+ invalid();
34
+ const metadataLength = new DataView(input.buffer, input.byteOffset + MAGIC.byteLength, LENGTH_BYTES).getUint32(0);
35
+ const bodyOffset = headerBytes + metadataLength;
36
+ if (metadataLength === 0 ||
37
+ metadataLength > MAXIMUM_METADATA_BYTES ||
38
+ bodyOffset > input.byteLength) {
39
+ invalid();
40
+ }
41
+ let metadata;
42
+ try {
43
+ metadata = JSON.parse(decoder.decode(input.subarray(headerBytes, bodyOffset)));
44
+ }
45
+ catch (cause) {
46
+ if (cause instanceof SyntaxError || cause instanceof TypeError)
47
+ return invalid();
48
+ throw cause;
49
+ }
50
+ if (typeof metadata !== 'object' || metadata === null || Array.isArray(metadata))
51
+ invalid();
52
+ return decodeBinaryResponse({
53
+ ...metadata,
54
+ body: input.slice(bodyOffset),
55
+ });
56
+ }
57
+ function hasMagic(input) {
58
+ if (input.byteLength < MAGIC.byteLength)
59
+ return false;
60
+ for (let index = 0; index < MAGIC.byteLength; index += 1) {
61
+ if (input[index] !== MAGIC[index])
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+ function invalid() {
67
+ throw new ProtocolError({
68
+ code: CODES.INVALID_REQUEST,
69
+ message: 'WebSocket binary response is invalid.',
70
+ });
71
+ }
@@ -0,0 +1,2 @@
1
+ export { decodeBinary, encodeBinary } from './binary.js';
2
+ export { JSON_SUBPROTOCOL, MSGPACK_SUBPROTOCOL, selectSubprotocol, subprotocolFor, type Subprotocol, type SubprotocolSelection, } from './subprotocol.js';
@@ -0,0 +1,2 @@
1
+ export { decodeBinary, encodeBinary } from './binary.js';
2
+ export { JSON_SUBPROTOCOL, MSGPACK_SUBPROTOCOL, selectSubprotocol, subprotocolFor, } from './subprotocol.js';
@@ -0,0 +1,10 @@
1
+ import type { Codec } from '../../codecs/index.js';
2
+ export declare const JSON_SUBPROTOCOL: 'astrale.invocation.v2.json';
3
+ export declare const MSGPACK_SUBPROTOCOL: 'astrale.invocation.v2.msgpack';
4
+ export type Subprotocol = typeof JSON_SUBPROTOCOL | typeof MSGPACK_SUBPROTOCOL;
5
+ export interface SubprotocolSelection {
6
+ readonly subprotocol: Subprotocol;
7
+ readonly codec: Codec;
8
+ }
9
+ export declare function subprotocolFor(codec: Codec): Subprotocol;
10
+ export declare function selectSubprotocol(offered: string | null, supported: readonly Codec[]): SubprotocolSelection | undefined;
@@ -0,0 +1,29 @@
1
+ import { admitSupportedCodecs, codecProfile } from '../binding-codecs.js';
2
+ export const JSON_SUBPROTOCOL = 'astrale.invocation.v2.json';
3
+ export const MSGPACK_SUBPROTOCOL = 'astrale.invocation.v2.msgpack';
4
+ const TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
5
+ export function subprotocolFor(codec) {
6
+ const profile = codecProfile(codec);
7
+ if (profile === undefined)
8
+ throw new TypeError('codec is not a canonical Invocation codec.');
9
+ return profile === 'json' ? JSON_SUBPROTOCOL : MSGPACK_SUBPROTOCOL;
10
+ }
11
+ export function selectSubprotocol(offered, supported) {
12
+ const admitted = admitSupportedCodecs(supported);
13
+ if (offered === null || offered.trim() === '')
14
+ return undefined;
15
+ const tokens = offered.split(',').map((token) => token.trim());
16
+ const seen = new Set();
17
+ for (const token of tokens) {
18
+ if (!TOKEN.test(token) || seen.has(token))
19
+ return undefined;
20
+ seen.add(token);
21
+ }
22
+ for (const token of tokens) {
23
+ const entry = admitted.find((candidate) => subprotocolFor(candidate.codec) === token);
24
+ if (entry !== undefined) {
25
+ return Object.freeze({ subprotocol: token, codec: entry.codec });
26
+ }
27
+ }
28
+ return undefined;
29
+ }
@@ -0,0 +1,14 @@
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
+ export interface Configuration {
3
+ readonly issuer: IssuerId;
4
+ readonly jwks_uri: string;
5
+ readonly subject_types_supported: readonly ['public'];
6
+ readonly id_token_signing_alg_values_supported: readonly [string, ...string[]];
7
+ readonly token_exchange_endpoint?: string;
8
+ }
9
+ export interface ConfigurationOptions {
10
+ readonly tokenExchange?: boolean;
11
+ }
12
+ export declare function configuration(issuer: string, algorithms: readonly [string, ...string[]], options?: ConfigurationOptions): Configuration;
13
+ export declare function acceptConfiguration(input: unknown, expectedIssuer?: string): Configuration;
14
+ export declare function acceptAlgorithms(input: unknown): readonly [string, ...string[]];