@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,23 @@
1
+ import { type IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
+ import { NodePath } from '@astrale-os/kernel-core/path';
3
+ import { Key } from '@astrale-os/kernel-dsl/v1/addressing';
4
+ import { type Object as ValueObject } from '@astrale-os/kernel-dsl/value';
5
+ import type { ErrorPayload } from '../errors/index.js';
6
+ import type { Etag } from '../publication/index.js';
7
+ import { type RequestId } from './correlation.js';
8
+ import { type InvocationId } from './identity.js';
9
+ export declare function portableObject(input: unknown, label: string): ValueObject;
10
+ export declare function exactDataObject(input: unknown, label: string): Record<string, unknown>;
11
+ export declare function exactFields(object: Record<string, unknown>, allowed: ReadonlySet<string>, label: string): void;
12
+ export declare function required(object: Record<string, unknown>, fields: readonly string[], label: string): void;
13
+ export declare function requestId(input: unknown, label: string): RequestId;
14
+ export declare function invocation(input: unknown, label: string): InvocationId;
15
+ export declare function nodePath(input: unknown, label: string): NodePath;
16
+ export declare function issuerId(input: unknown, label: string): IssuerId;
17
+ export declare function string(input: unknown, label: string): string;
18
+ export declare function count(input: unknown, label: string): number;
19
+ export declare function digest(input: unknown, label: string): Etag;
20
+ export declare function callableKey(input: unknown, label: string): Key.Callable;
21
+ export declare function errorPayload(input: unknown, label: string): ErrorPayload;
22
+ export declare function acceptHeaders(input: unknown): Readonly<Record<string, string>>;
23
+ export declare function invalid(message: string): never;
@@ -0,0 +1,177 @@
1
+ import { AuthValueError } from '@astrale-os/kernel-core/auth/error';
2
+ import { accept as acceptIssuer } from '@astrale-os/kernel-core/auth/issuer';
3
+ import { NodePath, PathError } from '@astrale-os/kernel-core/path';
4
+ import { callableAddress, Key } from '@astrale-os/kernel-dsl/v1/addressing';
5
+ import { accept as acceptValue, BoundaryError, } from '@astrale-os/kernel-dsl/value';
6
+ import { CODES, acceptPayload, ProtocolError } from '../errors/index.js';
7
+ import { acceptRequestId } from './correlation.js';
8
+ import { acceptInvocationId, InvocationError } from './identity.js';
9
+ export function portableObject(input, label) {
10
+ let cloned;
11
+ try {
12
+ cloned = acceptValue(input);
13
+ }
14
+ catch (cause) {
15
+ if (cause instanceof BoundaryError) {
16
+ return invalid(`${label} must be a portable value object`);
17
+ }
18
+ throw cause;
19
+ }
20
+ if (!isValueObject(cloned))
21
+ invalid(`${label} must be an object`);
22
+ return cloned;
23
+ }
24
+ export function exactDataObject(input, label) {
25
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
26
+ invalid(`${label} must be an object`);
27
+ }
28
+ const prototype = Object.getPrototypeOf(input);
29
+ if (prototype !== Object.prototype && prototype !== null) {
30
+ invalid(`${label} must be a plain object`);
31
+ }
32
+ const output = {};
33
+ for (const key of Reflect.ownKeys(input)) {
34
+ if (typeof key !== 'string')
35
+ invalid(`${label} cannot contain symbol fields`);
36
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
37
+ if (descriptor === undefined || !descriptor.enumerable || !('value' in descriptor)) {
38
+ invalid(`${label}.${key} must be an enumerable data field`);
39
+ }
40
+ output[key] = descriptor.value;
41
+ }
42
+ return output;
43
+ }
44
+ export function exactFields(object, allowed, label) {
45
+ for (const key of Object.keys(object)) {
46
+ if (!allowed.has(key))
47
+ invalid(`${label} contains unexpected field ${JSON.stringify(key)}`);
48
+ }
49
+ }
50
+ export function required(object, fields, label) {
51
+ for (const field of fields) {
52
+ if (!Object.hasOwn(object, field))
53
+ invalid(`${label}.${field} is required`);
54
+ }
55
+ }
56
+ export function requestId(input, label) {
57
+ try {
58
+ return acceptRequestId(input, label);
59
+ }
60
+ catch (cause) {
61
+ if (cause instanceof TypeError)
62
+ return invalid(cause.message);
63
+ throw cause;
64
+ }
65
+ }
66
+ export function invocation(input, label) {
67
+ try {
68
+ return acceptInvocationId(input);
69
+ }
70
+ catch (cause) {
71
+ if (cause instanceof InvocationError)
72
+ return invalid(`${label} must be a valid Invocation`);
73
+ throw cause;
74
+ }
75
+ }
76
+ export function nodePath(input, label) {
77
+ if (typeof input !== 'string')
78
+ invalid(`${label} must be a NodePath string`);
79
+ try {
80
+ return NodePath(input);
81
+ }
82
+ catch (cause) {
83
+ if (cause instanceof PathError)
84
+ return invalid(`${label} must be a valid NodePath`);
85
+ throw cause;
86
+ }
87
+ }
88
+ export function issuerId(input, label) {
89
+ if (typeof input !== 'string')
90
+ invalid(`${label} must be an issuer identifier`);
91
+ try {
92
+ return acceptIssuer(input);
93
+ }
94
+ catch (cause) {
95
+ if (cause instanceof AuthValueError)
96
+ return invalid(`${label} must be a valid issuer identifier`);
97
+ throw cause;
98
+ }
99
+ }
100
+ export function string(input, label) {
101
+ if (typeof input !== 'string')
102
+ invalid(`${label} must be a string`);
103
+ return input;
104
+ }
105
+ export function count(input, label) {
106
+ if (!Number.isSafeInteger(input) || input < 0) {
107
+ invalid(`${label} must be a non-negative safe integer`);
108
+ }
109
+ return input;
110
+ }
111
+ export function digest(input, label) {
112
+ const value = string(input, label);
113
+ if (!/^sha256:[0-9a-f]{64}$/u.test(value))
114
+ invalid(`${label} must be a SHA-256 digest`);
115
+ return value;
116
+ }
117
+ export function callableKey(input, label) {
118
+ if (typeof input !== 'string')
119
+ invalid(`${label} must be a callable Key string`);
120
+ try {
121
+ const callable = Key(input);
122
+ callableAddress(callable);
123
+ return callable;
124
+ }
125
+ catch (cause) {
126
+ if (cause instanceof TypeError)
127
+ return invalid(`${label} must be a valid callable Key`);
128
+ throw cause;
129
+ }
130
+ }
131
+ export function errorPayload(input, label) {
132
+ try {
133
+ return acceptPayload(input, label);
134
+ }
135
+ catch (cause) {
136
+ if (cause instanceof TypeError)
137
+ return invalid(cause.message);
138
+ throw cause;
139
+ }
140
+ }
141
+ export function acceptHeaders(input) {
142
+ const object = exactDataObject(input, 'binary response.headers');
143
+ const headers = {};
144
+ for (const [name, rawValue] of Object.entries(object)) {
145
+ if (!HEADER_NAME.test(name)) {
146
+ invalid(`binary response header name ${JSON.stringify(name)} is invalid`);
147
+ }
148
+ const value = string(rawValue, `binary response.headers.${name}`);
149
+ if (hasControlCharacter(value)) {
150
+ invalid(`binary response header ${JSON.stringify(name)} contains a control character`);
151
+ }
152
+ const lower = name.toLowerCase();
153
+ if (Object.hasOwn(headers, lower)) {
154
+ invalid(`binary response header ${JSON.stringify(name)} occurs more than once`);
155
+ }
156
+ if (lower === 'content-length' || lower === 'transfer-encoding' || lower === 'connection') {
157
+ invalid(`binary response header ${JSON.stringify(name)} is transport-reserved`);
158
+ }
159
+ headers[lower] = value;
160
+ }
161
+ return Object.freeze(headers);
162
+ }
163
+ export function invalid(message) {
164
+ throw new ProtocolError({ code: CODES.INVALID_REQUEST, message });
165
+ }
166
+ function isValueObject(value) {
167
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
168
+ }
169
+ const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
170
+ function hasControlCharacter(input) {
171
+ for (let index = 0; index < input.length; index += 1) {
172
+ const code = input.charCodeAt(index);
173
+ if (code <= 0x1f || code === 0x7f)
174
+ return true;
175
+ }
176
+ return false;
177
+ }
@@ -0,0 +1,11 @@
1
+ import type { RequestId } from './correlation.js';
2
+ import type { InvocationId } from './identity.js';
3
+ export interface BinaryResponse {
4
+ readonly kind: 'binary';
5
+ readonly requestId: RequestId;
6
+ readonly invocation: InvocationId;
7
+ readonly body: Uint8Array;
8
+ readonly mediaType: string;
9
+ readonly headers?: Readonly<Record<string, string>>;
10
+ }
11
+ export declare function decodeBinaryResponse(input: unknown): BinaryResponse;
@@ -0,0 +1,24 @@
1
+ import { acceptHeaders, exactDataObject, exactFields, invalid, invocation, requestId, required, string, } from './admission.js';
2
+ const FIELDS = new Set(['kind', 'requestId', 'invocation', 'body', 'mediaType', 'headers']);
3
+ const MEDIA_TYPE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
4
+ export function decodeBinaryResponse(input) {
5
+ const object = exactDataObject(input, 'binary response');
6
+ exactFields(object, FIELDS, 'binary response');
7
+ required(object, ['kind', 'requestId', 'invocation', 'body', 'mediaType'], 'binary response');
8
+ if (object.kind !== 'binary')
9
+ invalid('binary response.kind must be "binary"');
10
+ if (!(object.body instanceof Uint8Array))
11
+ invalid('binary response.body must be a Uint8Array');
12
+ const mediaType = string(object.mediaType, 'binary response.mediaType');
13
+ if (!MEDIA_TYPE.test(mediaType))
14
+ invalid('binary response.mediaType is invalid');
15
+ const headers = object.headers === undefined ? undefined : acceptHeaders(object.headers);
16
+ return Object.freeze({
17
+ kind: 'binary',
18
+ requestId: requestId(object.requestId, 'binary response.requestId'),
19
+ invocation: invocation(object.invocation, 'binary response.invocation'),
20
+ body: new Uint8Array(object.body),
21
+ mediaType,
22
+ ...(headers === undefined ? {} : { headers }),
23
+ });
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { Codec } from '../codecs/codec.js';
2
+ export type CodecProfile = 'json' | 'msgpack';
3
+ export interface SupportedCodec {
4
+ readonly codec: Codec;
5
+ readonly profile: CodecProfile;
6
+ }
7
+ export declare function codecProfile(codec: Codec): CodecProfile | undefined;
8
+ export declare function admitSupportedCodecs(supported: readonly Codec[]): readonly SupportedCodec[];
@@ -0,0 +1,27 @@
1
+ import { json } from '../codecs/json.js';
2
+ import { isMsgpackCodec } from '../codecs/msgpack.js';
3
+ export function codecProfile(codec) {
4
+ if (codec === json)
5
+ return 'json';
6
+ if (isMsgpackCodec(codec))
7
+ return 'msgpack';
8
+ return undefined;
9
+ }
10
+ export function admitSupportedCodecs(supported) {
11
+ if (!Array.isArray(supported))
12
+ throw new TypeError('supported codecs must be an array.');
13
+ const admitted = [];
14
+ const profiles = new Set();
15
+ for (const codec of supported) {
16
+ const profile = codecProfile(codec);
17
+ if (profile === undefined)
18
+ throw new TypeError('supported contains a foreign codec.');
19
+ if (profiles.has(profile))
20
+ throw new TypeError(`supported contains duplicate ${profile}.`);
21
+ profiles.add(profile);
22
+ admitted.push(Object.freeze({ codec, profile }));
23
+ }
24
+ if (!profiles.has('json'))
25
+ throw new TypeError('supported must contain the canonical JSON codec.');
26
+ return Object.freeze(admitted);
27
+ }
@@ -0,0 +1,8 @@
1
+ import type { RequestId } from './correlation.js';
2
+ import type { Version } from './version.js';
3
+ export interface CancelRequest {
4
+ readonly version: Version;
5
+ readonly kind: 'cancel';
6
+ readonly requestId: RequestId;
7
+ }
8
+ export declare function decodeCancelRequest(input: unknown): CancelRequest;
@@ -0,0 +1,16 @@
1
+ import { exactFields, invalid, portableObject, requestId, required } from './admission.js';
2
+ const FIELDS = new Set(['version', 'kind', 'requestId']);
3
+ export function decodeCancelRequest(input) {
4
+ const object = portableObject(input, 'cancel request');
5
+ exactFields(object, FIELDS, 'cancel request');
6
+ required(object, ['version', 'kind', 'requestId'], 'cancel request');
7
+ if (object.version !== 2)
8
+ invalid('cancel request.version must be 2');
9
+ if (object.kind !== 'cancel')
10
+ invalid('cancel request.kind must be "cancel"');
11
+ return Object.freeze({
12
+ version: 2,
13
+ kind: 'cancel',
14
+ requestId: requestId(object.requestId, 'cancel request.requestId'),
15
+ });
16
+ }
@@ -0,0 +1,2 @@
1
+ export type RequestId = string | number | null;
2
+ export declare function acceptRequestId(input: unknown, label: string): RequestId;
@@ -0,0 +1,7 @@
1
+ export function acceptRequestId(input, label) {
2
+ if (input === null || typeof input === 'string')
3
+ return input;
4
+ if (typeof input === 'number' && Number.isFinite(input) && !Object.is(input, -0))
5
+ return input;
6
+ throw new TypeError(`${label} must be a string, finite number, or null`);
7
+ }
@@ -0,0 +1,24 @@
1
+ export declare namespace Delegate {
2
+ type Expr = {
3
+ readonly kind: 'identity';
4
+ readonly self: true;
5
+ } | {
6
+ readonly kind: 'identity';
7
+ readonly credential: string;
8
+ } | {
9
+ readonly kind: 'union';
10
+ readonly operands: readonly Expr[];
11
+ } | {
12
+ readonly kind: 'intersect';
13
+ readonly operands: readonly Expr[];
14
+ } | {
15
+ readonly kind: 'exclude';
16
+ readonly base: Expr;
17
+ readonly excluded: readonly Expr[];
18
+ };
19
+ }
20
+ export interface Delegate {
21
+ readonly ttlSeconds: number;
22
+ readonly attenuation?: Delegate.Expr;
23
+ }
24
+ export declare function acceptDelegate(input: unknown): Delegate;
@@ -0,0 +1,43 @@
1
+ import { AuthValueError, grant } from '@astrale-os/kernel-core/auth';
2
+ import { exactDataObject, exactFields, invalid, required } from './admission.js';
3
+ const FIELDS = new Set(['ttlSeconds', 'attenuation']);
4
+ export function acceptDelegate(input) {
5
+ const object = exactDataObject(input, 'delegate');
6
+ exactFields(object, FIELDS, 'delegate');
7
+ required(object, ['ttlSeconds'], 'delegate');
8
+ if (!Number.isSafeInteger(object.ttlSeconds) || object.ttlSeconds < 1) {
9
+ invalid('delegate.ttlSeconds must be a positive safe integer');
10
+ }
11
+ let attenuation;
12
+ if (object.attenuation !== undefined) {
13
+ try {
14
+ attenuation = grant.acceptUnresolved({ v: 1, expr: object.attenuation }).expr;
15
+ }
16
+ catch (cause) {
17
+ if (cause instanceof AuthValueError)
18
+ invalid('delegate.attenuation must be a valid expression');
19
+ throw cause;
20
+ }
21
+ requireTextCredentials(attenuation);
22
+ }
23
+ return Object.freeze({
24
+ ttlSeconds: object.ttlSeconds,
25
+ ...(attenuation === undefined ? {} : { attenuation: attenuation }),
26
+ });
27
+ }
28
+ function requireTextCredentials(input) {
29
+ if (input.kind === 'identity') {
30
+ if ('credential' in input && typeof input.credential !== 'string') {
31
+ invalid('delegate.attenuation credentials must be non-empty text');
32
+ }
33
+ return;
34
+ }
35
+ if (input.kind === 'exclude') {
36
+ requireTextCredentials(input.base);
37
+ for (const operand of input.excluded)
38
+ requireTextCredentials(operand);
39
+ return;
40
+ }
41
+ for (const operand of input.operands)
42
+ requireTextCredentials(operand);
43
+ }
@@ -0,0 +1,12 @@
1
+ import { type InvocationId } from '../identity.js';
2
+ export declare const BINARY_HEADERS_FIELD: 'x-astrale-binary-headers';
3
+ export declare const REQUEST_ID_FIELD: 'x-astrale-request-id';
4
+ export declare const INVOCATION_FIELD: 'x-astrale-invocation';
5
+ export declare const MAXIMUM_INVOCATION_FIELD_BYTES = 8192;
6
+ /** Encode one admitted InvocationId as percent-encoded source plus opaque ID. */
7
+ export declare function encodeInvocationId(input: InvocationId): string;
8
+ /** Decode one complete finite-binary InvocationId field. */
9
+ export declare function decodeInvocationId(input: string | null): InvocationId;
10
+ export declare function isApplicationBinaryHeader(name: string): boolean;
11
+ export declare function encodeBinaryHeaderNames(headers: Readonly<Record<string, string>> | undefined): string;
12
+ export declare function decodeBinaryHeaderNames(input: string | null): readonly string[];
@@ -0,0 +1,132 @@
1
+ import { CODES, ProtocolError } from '../../errors/index.js';
2
+ import { acceptInvocationId } from '../identity.js';
3
+ export const BINARY_HEADERS_FIELD = 'x-astrale-binary-headers';
4
+ export const REQUEST_ID_FIELD = 'x-astrale-request-id';
5
+ export const INVOCATION_FIELD = 'x-astrale-invocation';
6
+ export const MAXIMUM_INVOCATION_FIELD_BYTES = 8_192;
7
+ const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
8
+ const MAXIMUM_MANIFEST_BYTES = 8_192;
9
+ const MAXIMUM_MANIFEST_NAMES = 128;
10
+ const EMPTY_MANIFEST = '-';
11
+ const RESERVED = new Set([
12
+ 'authorization',
13
+ 'cache-control',
14
+ 'connection',
15
+ 'content-encoding',
16
+ 'content-length',
17
+ 'content-type',
18
+ 'cookie',
19
+ 'date',
20
+ 'etag',
21
+ 'host',
22
+ 'keep-alive',
23
+ 'location',
24
+ 'proxy-authenticate',
25
+ 'proxy-authorization',
26
+ 'referrer-policy',
27
+ 'server',
28
+ 'set-cookie',
29
+ 'trailer',
30
+ 'transfer-encoding',
31
+ 'upgrade',
32
+ 'vary',
33
+ 'www-authenticate',
34
+ EMPTY_MANIFEST,
35
+ BINARY_HEADERS_FIELD,
36
+ INVOCATION_FIELD,
37
+ REQUEST_ID_FIELD,
38
+ 'x-astrale-stream-framing',
39
+ ]);
40
+ /** Encode one admitted InvocationId as percent-encoded source plus opaque ID. */
41
+ export function encodeInvocationId(input) {
42
+ let source;
43
+ try {
44
+ source = encodeURIComponent(input.source);
45
+ }
46
+ catch {
47
+ return invalid('HTTP binary InvocationId field is invalid.');
48
+ }
49
+ const encoded = `${source};${input.id}`;
50
+ if (new TextEncoder().encode(encoded).byteLength > MAXIMUM_INVOCATION_FIELD_BYTES) {
51
+ invalid('HTTP binary InvocationId field is invalid.');
52
+ }
53
+ return encoded;
54
+ }
55
+ /** Decode one complete finite-binary InvocationId field. */
56
+ export function decodeInvocationId(input) {
57
+ if (input === null ||
58
+ input.length === 0 ||
59
+ new TextEncoder().encode(input).byteLength > MAXIMUM_INVOCATION_FIELD_BYTES) {
60
+ return invalid('HTTP binary InvocationId field is invalid.');
61
+ }
62
+ const separator = input.indexOf(';');
63
+ if (separator < 1 || input.indexOf(';', separator + 1) !== -1) {
64
+ invalid('HTTP binary InvocationId field is invalid.');
65
+ }
66
+ let source;
67
+ try {
68
+ source = decodeURIComponent(input.slice(0, separator));
69
+ }
70
+ catch {
71
+ return invalid('HTTP binary InvocationId field is invalid.');
72
+ }
73
+ try {
74
+ return acceptInvocationId({ source, id: input.slice(separator + 1) });
75
+ }
76
+ catch {
77
+ return invalid('HTTP binary InvocationId field is invalid.');
78
+ }
79
+ }
80
+ export function isApplicationBinaryHeader(name) {
81
+ const normalized = name.toLowerCase();
82
+ return (HEADER_NAME.test(normalized) &&
83
+ !normalized.startsWith('access-control-') &&
84
+ !normalized.startsWith('sec-websocket-') &&
85
+ !RESERVED.has(normalized));
86
+ }
87
+ export function encodeBinaryHeaderNames(headers) {
88
+ const names = [];
89
+ const seen = new Set();
90
+ for (const input of Object.keys(headers ?? {})) {
91
+ const name = input.toLowerCase();
92
+ if (!HEADER_NAME.test(name) || seen.has(name))
93
+ invalid();
94
+ seen.add(name);
95
+ if (!isApplicationBinaryHeader(name))
96
+ continue;
97
+ names.push(name);
98
+ }
99
+ if (names.length > MAXIMUM_MANIFEST_NAMES)
100
+ invalid();
101
+ const manifest = names.length === 0 ? EMPTY_MANIFEST : names.sort().join(', ');
102
+ if (new TextEncoder().encode(manifest).byteLength > MAXIMUM_MANIFEST_BYTES)
103
+ invalid();
104
+ return manifest;
105
+ }
106
+ export function decodeBinaryHeaderNames(input) {
107
+ if (input === EMPTY_MANIFEST)
108
+ return Object.freeze([]);
109
+ if (input === null || input === '')
110
+ invalid();
111
+ if (new TextEncoder().encode(input).byteLength > MAXIMUM_MANIFEST_BYTES)
112
+ invalid();
113
+ const names = input.split(',');
114
+ if (names.length > MAXIMUM_MANIFEST_NAMES)
115
+ invalid();
116
+ const admitted = [];
117
+ const seen = new Set();
118
+ for (const item of names) {
119
+ const name = item.trim().toLowerCase();
120
+ if (!isApplicationBinaryHeader(name) || seen.has(name))
121
+ invalid();
122
+ seen.add(name);
123
+ admitted.push(name);
124
+ }
125
+ return Object.freeze(admitted);
126
+ }
127
+ function invalid(message = 'HTTP binary application-header manifest is invalid.') {
128
+ throw new ProtocolError({
129
+ code: CODES.INVALID_REQUEST,
130
+ message,
131
+ });
132
+ }
@@ -0,0 +1,4 @@
1
+ export { BINARY_HEADERS_FIELD, decodeBinaryHeaderNames, decodeInvocationId, encodeBinaryHeaderNames, encodeInvocationId, isApplicationBinaryHeader, MAXIMUM_INVOCATION_FIELD_BYTES, INVOCATION_FIELD, REQUEST_ID_FIELD, } from './binary.js';
2
+ export { codecForContentType, negotiateResponseCodec } from './negotiation.js';
3
+ export { encodeStreamFrame, LENGTH_PREFIX_V1, STREAM_FRAMING_FIELD } from './stream.js';
4
+ export { BAGGAGE_FIELD, decodeTraceHeaders, encodeTraceHeaders, TRACEPARENT_FIELD, TRACESTATE_FIELD, } from './trace.js';
@@ -0,0 +1,4 @@
1
+ export { BINARY_HEADERS_FIELD, decodeBinaryHeaderNames, decodeInvocationId, encodeBinaryHeaderNames, encodeInvocationId, isApplicationBinaryHeader, MAXIMUM_INVOCATION_FIELD_BYTES, INVOCATION_FIELD, REQUEST_ID_FIELD, } from './binary.js';
2
+ export { codecForContentType, negotiateResponseCodec } from './negotiation.js';
3
+ export { encodeStreamFrame, LENGTH_PREFIX_V1, STREAM_FRAMING_FIELD } from './stream.js';
4
+ export { BAGGAGE_FIELD, decodeTraceHeaders, encodeTraceHeaders, TRACEPARENT_FIELD, TRACESTATE_FIELD, } from './trace.js';
@@ -0,0 +1,3 @@
1
+ import type { Codec } from '../../codecs/index.js';
2
+ export declare function codecForContentType(contentType: string | null, supported: readonly Codec[]): Codec | undefined;
3
+ export declare function negotiateResponseCodec(accept: string | null, requestCodec: Codec, supported: readonly Codec[]): Codec | undefined;