@astrale-os/kernel-protocol 0.5.0-beta.67 → 0.5.0-beta.69

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 (225) hide show
  1. package/dist/codecs/index.d.ts +1 -2
  2. package/dist/codecs/index.js +1 -2
  3. package/dist/codecs/media-types.d.ts +0 -1
  4. package/dist/codecs/media-types.js +0 -1
  5. package/dist/index.d.ts +3 -1
  6. package/dist/index.js +3 -1
  7. package/dist/invocation/admission.d.ts +2 -2
  8. package/dist/invocation/admission.js +5 -5
  9. package/dist/invocation/binding-codecs.d.ts +1 -1
  10. package/dist/invocation/binding-codecs.js +1 -6
  11. package/dist/invocation/http/negotiation.js +3 -10
  12. package/dist/invocation/index.d.ts +5 -2
  13. package/dist/invocation/index.js +4 -2
  14. package/dist/invocation/legacy/index.d.ts +29 -0
  15. package/dist/invocation/legacy/index.js +28 -0
  16. package/dist/invocation/legacy/route.d.ts +14 -0
  17. package/dist/invocation/legacy/route.js +25 -0
  18. package/dist/invocation/legacy/via.d.ts +30 -0
  19. package/dist/invocation/legacy/via.js +15 -0
  20. package/dist/invocation/redirect.d.ts +15 -12
  21. package/dist/invocation/redirect.js +9 -21
  22. package/dist/invocation/request.js +2 -2
  23. package/dist/invocation/routing.d.ts +19 -5
  24. package/dist/invocation/routing.js +50 -20
  25. package/dist/invocation/schema.d.ts +3 -8
  26. package/dist/invocation/schema.js +3 -10
  27. package/dist/invocation/service-reason.d.ts +13 -13
  28. package/dist/invocation/service-reason.js +13 -12
  29. package/dist/invocation/stale-route.d.ts +1 -1
  30. package/dist/invocation/target.d.ts +3 -0
  31. package/dist/invocation/target.js +8 -1
  32. package/dist/invocation/websocket/index.d.ts +1 -1
  33. package/dist/invocation/websocket/index.js +1 -1
  34. package/dist/invocation/websocket/subprotocol.d.ts +1 -2
  35. package/dist/invocation/websocket/subprotocol.js +3 -4
  36. package/dist/issuer/claims.d.ts +7 -0
  37. package/dist/issuer/claims.js +7 -0
  38. package/dist/issuer/index.d.ts +2 -0
  39. package/dist/issuer/index.js +2 -0
  40. package/dist/issuer/paths.d.ts +2 -0
  41. package/dist/issuer/paths.js +1 -0
  42. package/dist/issuer/workflow-engine.d.ts +18 -0
  43. package/dist/issuer/workflow-engine.js +59 -0
  44. package/dist/{publication → release}/bindings/callable.js +6 -6
  45. package/dist/release/bindings/invocation.js +13 -0
  46. package/dist/{publication → release}/bindings/view.d.ts +1 -1
  47. package/dist/{publication → release}/bindings/view.js +10 -10
  48. package/dist/release/build.d.ts +6 -0
  49. package/dist/release/build.js +7 -0
  50. package/dist/release/bundle-admission.d.ts +21 -0
  51. package/dist/{publication → release}/bundle-admission.js +18 -18
  52. package/dist/{publication → release}/bundle-descriptor.js +6 -6
  53. package/dist/release/bundle-evidence.d.ts +3 -0
  54. package/dist/release/bundle-evidence.js +13 -0
  55. package/dist/{publication → release}/bundle.d.ts +2 -2
  56. package/dist/{publication → release}/bundle.js +9 -9
  57. package/dist/{publication → release}/delivery/attestation.d.ts +1 -0
  58. package/dist/release/delivery/claims.d.ts +21 -0
  59. package/dist/release/delivery/claims.js +68 -0
  60. package/dist/release/delivery/delivery.d.ts +11 -0
  61. package/dist/release/delivery/delivery.js +24 -0
  62. package/dist/{publication → release}/delivery/error.d.ts +2 -0
  63. package/dist/{publication → release}/delivery/error.js +1 -0
  64. package/dist/{publication → release}/delivery/index.d.ts +6 -1
  65. package/dist/{publication → release}/delivery/index.js +5 -0
  66. package/dist/{publication → release}/delivery/limits.d.ts +2 -1
  67. package/dist/release/delivery/limits.js +5 -0
  68. package/dist/release/delivery/request.d.ts +21 -0
  69. package/dist/release/delivery/request.js +60 -0
  70. package/dist/{publication → release}/delivery-declaration.js +4 -4
  71. package/dist/release/delivery-endpoint.js +22 -0
  72. package/dist/release/digest.d.ts +13 -0
  73. package/dist/{publication/etag.js → release/digest.js} +1 -10
  74. package/dist/release/error.d.ts +7 -0
  75. package/dist/release/error.js +11 -0
  76. package/dist/release/index.d.ts +21 -0
  77. package/dist/release/index.js +14 -0
  78. package/dist/release/issuer.d.ts +5 -0
  79. package/dist/release/issuer.js +34 -0
  80. package/dist/release/legacy/bindings.d.ts +14 -0
  81. package/dist/release/legacy/bindings.js +11 -0
  82. package/dist/release/legacy/bundle-admission.d.ts +6 -0
  83. package/dist/release/legacy/bundle-admission.js +10 -0
  84. package/dist/release/legacy/bundle-evidence.d.ts +8 -0
  85. package/dist/release/legacy/bundle-evidence.js +11 -0
  86. package/dist/release/legacy/bundle.d.ts +24 -0
  87. package/dist/release/legacy/bundle.js +31 -0
  88. package/dist/release/legacy/delivery/attestation.d.ts +6 -0
  89. package/dist/release/legacy/delivery/attestation.js +1 -0
  90. package/dist/{publication → release/legacy}/delivery/claims.d.ts +9 -0
  91. package/dist/{publication → release/legacy}/delivery/claims.js +7 -2
  92. package/dist/release/legacy/delivery/delivery.d.ts +18 -0
  93. package/dist/{publication → release/legacy}/delivery/delivery.js +6 -2
  94. package/dist/release/legacy/delivery/error.d.ts +16 -0
  95. package/dist/release/legacy/delivery/error.js +6 -0
  96. package/dist/release/legacy/delivery/index.d.ts +13 -0
  97. package/dist/release/legacy/delivery/index.js +13 -0
  98. package/dist/release/legacy/delivery/limits.d.ts +21 -0
  99. package/dist/release/legacy/delivery/limits.js +22 -0
  100. package/dist/release/legacy/delivery/request.d.ts +26 -0
  101. package/dist/{publication → release/legacy}/delivery/request.js +8 -3
  102. package/dist/release/legacy/delivery-declaration.d.ts +9 -0
  103. package/dist/release/legacy/delivery-declaration.js +11 -0
  104. package/dist/release/legacy/error.d.ts +26 -0
  105. package/dist/release/legacy/error.js +114 -0
  106. package/dist/release/legacy/etag.d.ts +12 -0
  107. package/dist/release/legacy/etag.js +14 -0
  108. package/dist/release/legacy/index.d.ts +75 -0
  109. package/dist/{publication → release/legacy}/index.js +7 -2
  110. package/dist/release/legacy/profiles.d.ts +23 -0
  111. package/dist/release/legacy/profiles.js +36 -0
  112. package/dist/release/legacy/projection.d.ts +13 -0
  113. package/dist/release/legacy/projection.js +25 -0
  114. package/dist/release/legacy/publication.d.ts +90 -0
  115. package/dist/release/legacy/publication.js +124 -0
  116. package/dist/release/legacy/reference.d.ts +47 -0
  117. package/dist/{publication → release/legacy}/reference.js +27 -3
  118. package/dist/release/legacy/requirements.d.ts +30 -0
  119. package/dist/release/legacy/requirements.js +20 -0
  120. package/dist/{publication → release}/manifest.js +6 -6
  121. package/dist/release/reference.d.ts +26 -0
  122. package/dist/release/reference.js +70 -0
  123. package/dist/release/release.d.ts +60 -0
  124. package/dist/release/release.js +123 -0
  125. package/dist/{publication → release}/requirements.d.ts +11 -4
  126. package/dist/{publication → release}/requirements.js +58 -39
  127. package/dist/{publication → release}/route.js +24 -24
  128. package/dist/{publication → release}/schema.js +4 -4
  129. package/dist/release/served.d.ts +26 -0
  130. package/dist/release/served.js +65 -0
  131. package/dist/release/shape.d.ts +8 -0
  132. package/dist/routes/admission.d.ts +1 -1
  133. package/dist/routes/admission.js +7 -85
  134. package/dist/routes/binding.d.ts +3 -3
  135. package/dist/routes/binding.js +10 -19
  136. package/dist/routes/compiler.d.ts +2 -2
  137. package/dist/routes/compiler.js +3 -2
  138. package/dist/routes/credential.d.ts +12 -1
  139. package/dist/routes/credential.js +11 -3
  140. package/dist/routes/decode.d.ts +5 -2
  141. package/dist/routes/decode.js +19 -6
  142. package/dist/routes/deployment-retired.d.ts +30 -0
  143. package/dist/routes/deployment-retired.js +96 -0
  144. package/dist/routes/encode.js +7 -6
  145. package/dist/routes/entries.d.ts +23 -0
  146. package/dist/routes/entries.js +150 -0
  147. package/dist/routes/evidence.d.ts +3 -3
  148. package/dist/routes/evidence.js +7 -34
  149. package/dist/routes/header.d.ts +9 -0
  150. package/dist/routes/header.js +34 -0
  151. package/dist/routes/index.d.ts +5 -2
  152. package/dist/routes/index.js +5 -2
  153. package/dist/routes/legacy/index.d.ts +29 -0
  154. package/dist/routes/legacy/index.js +29 -0
  155. package/dist/routes/legacy/routing.d.ts +24 -0
  156. package/dist/routes/legacy/routing.js +45 -0
  157. package/dist/routes/legacy/table.d.ts +34 -0
  158. package/dist/routes/legacy/table.js +50 -0
  159. package/dist/routes/mapping.d.ts +1 -1
  160. package/dist/routes/mapping.js +4 -4
  161. package/dist/routes/match.d.ts +1 -1
  162. package/dist/routes/placement.d.ts +2 -2
  163. package/dist/routes/plan.d.ts +1 -1
  164. package/dist/routes/request.d.ts +5 -3
  165. package/dist/routes/reservation.d.ts +4 -2
  166. package/dist/routes/reservation.js +4 -3
  167. package/dist/routes/routing.d.ts +30 -6
  168. package/dist/routes/routing.js +34 -49
  169. package/dist/routes/select.d.ts +1 -1
  170. package/dist/routes/table.d.ts +3 -2
  171. package/dist/routes/target.d.ts +5 -0
  172. package/dist/routes/target.js +16 -0
  173. package/dist/runtime/artifact.d.ts +2 -2
  174. package/dist/runtime/artifact.js +2 -2
  175. package/dist/schema/installation/codec.d.ts +6 -1
  176. package/dist/schema/installation/codec.js +10 -7
  177. package/dist/schema/installation/introspection/codec.d.ts +11 -2
  178. package/dist/schema/installation/introspection/codec.js +184 -47
  179. package/dist/schema/installation/introspection/index.d.ts +1 -1
  180. package/dist/schema/installation/introspection/result.d.ts +61 -1
  181. package/dist/schema/installation/legacy/index.d.ts +39 -0
  182. package/dist/schema/installation/legacy/index.js +39 -0
  183. package/dist/schema/installation/legacy/installation.d.ts +9 -0
  184. package/dist/schema/installation/legacy/installation.js +28 -0
  185. package/dist/schema/installation/legacy/introspection.d.ts +10 -0
  186. package/dist/schema/installation/legacy/introspection.js +70 -0
  187. package/dist/schema/installation/legacy/msgpack.d.ts +1 -0
  188. package/dist/schema/installation/legacy/msgpack.js +143 -0
  189. package/package.json +63 -36
  190. package/dist/codecs/msgpack.d.ts +0 -4
  191. package/dist/codecs/msgpack.js +0 -53
  192. package/dist/publication/bindings/invocation.js +0 -13
  193. package/dist/publication/bundle-admission.d.ts +0 -4
  194. package/dist/publication/bundle-evidence.d.ts +0 -3
  195. package/dist/publication/bundle-evidence.js +0 -13
  196. package/dist/publication/content.d.ts +0 -10
  197. package/dist/publication/delivery/delivery.d.ts +0 -9
  198. package/dist/publication/delivery/limits.js +0 -4
  199. package/dist/publication/delivery/request.d.ts +0 -15
  200. package/dist/publication/delivery-endpoint.js +0 -22
  201. package/dist/publication/error.d.ts +0 -7
  202. package/dist/publication/error.js +0 -11
  203. package/dist/publication/etag.d.ts +0 -6
  204. package/dist/publication/index.d.ts +0 -17
  205. package/dist/publication/legacy-requirements.d.ts +0 -12
  206. package/dist/publication/legacy-requirements.js +0 -23
  207. package/dist/publication/publication.d.ts +0 -38
  208. package/dist/publication/publication.js +0 -146
  209. package/dist/publication/reference.d.ts +0 -17
  210. package/dist/routes/release-unavailable.d.ts +0 -8
  211. package/dist/routes/release-unavailable.js +0 -32
  212. /package/dist/{publication → release}/bindings/callable.d.ts +0 -0
  213. /package/dist/{publication → release}/bindings/index.d.ts +0 -0
  214. /package/dist/{publication → release}/bindings/index.js +0 -0
  215. /package/dist/{publication → release}/bindings/invocation.d.ts +0 -0
  216. /package/dist/{publication → release}/bundle-descriptor.d.ts +0 -0
  217. /package/dist/{publication → release}/declaration.d.ts +0 -0
  218. /package/dist/{publication → release}/declaration.js +0 -0
  219. /package/dist/{publication → release}/delivery/attestation.js +0 -0
  220. /package/dist/{publication → release}/delivery-declaration.d.ts +0 -0
  221. /package/dist/{publication → release}/delivery-endpoint.d.ts +0 -0
  222. /package/dist/{publication → release}/manifest.d.ts +0 -0
  223. /package/dist/{publication → release}/route.d.ts +0 -0
  224. /package/dist/{publication → release}/schema.d.ts +0 -0
  225. /package/dist/{publication/content.js → release/shape.js} +0 -0
@@ -1,53 +1,83 @@
1
+ import { is as isIssuer } from '@astrale-os/kernel-core/auth/issuer';
1
2
  import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
2
3
  import * as bundle from '@astrale-os/kernel-dsl/v1/bundle/transport';
3
4
  import { revision as schemaRevision } from '@astrale-os/kernel-dsl/v1/schema/revision';
4
- import { admitCallableBinding, admitViewBinding } from '../../../publication/bindings/index.js';
5
- import * as publication from '../../../publication/index.js';
6
- export const MEDIA_TYPE = 'application/vnd.astrale.schema-introspection.v2+msgpack';
7
- const encodedBundles = new WeakMap();
5
+ import { json } from '@astrale-os/kernel-dsl/value';
6
+ import { admitCallableBinding, admitViewBinding } from '../../../release/bindings/index.js';
7
+ import { withPublicationError } from '../../../release/legacy/error.js';
8
+ import * as publication from '../../../release/legacy/index.js';
9
+ export const MEDIA_TYPE = 'application/vnd.astrale.schema-introspection.v3+json';
10
+ const encoder = new TextEncoder();
11
+ const serializedBundles = new WeakMap();
8
12
  const admittedBundles = new Map();
9
13
  const MAXIMUM_ADMITTED_BUNDLES = 128;
14
+ // The Bundle travels as its JSON text inside the evidence; string escaping at most doubles it.
15
+ const BUNDLE_ESCAPE_FACTOR = 2;
16
+ // Encoding is bounded by the Server response policy, not by the default portable-value limits.
17
+ const ENCODE_LIMITS = Object.freeze({
18
+ maxBytes: Number.MAX_SAFE_INTEGER,
19
+ maxStringBytes: Number.MAX_SAFE_INTEGER,
20
+ });
10
21
  export async function encode(input, result) {
11
22
  const request = admitRequest(input);
12
- const wire = request.kind === 'bundle'
13
- ? encodeDomainBundle(result)
14
- : request.kind === 'dependencies'
15
- ? encodeDomainDependencies(result)
16
- : encodeDomainInfo(result);
17
- proveRequestedOrigin(request, result);
18
- // Non-literal specifiers keep msgpackr's Node-typed declarations out of consumers (see codecs/msgpack.ts).
19
- const { pack } = await import('msgpackr/pack');
20
- return new Uint8Array(pack(wire));
23
+ const wire = request.kind === 'installed'
24
+ ? acceptInstalledReleases(result)
25
+ : request.kind === 'bundle'
26
+ ? encodeDomainBundle(result)
27
+ : request.kind === 'dependencies'
28
+ ? encodeDomainDependencies(result)
29
+ : encodeDomainInfo(result);
30
+ if (request.kind !== 'installed') {
31
+ proveRequestedOrigin(request, result);
32
+ }
33
+ return encoder.encode(json.serialize(wire, { limits: ENCODE_LIMITS }));
21
34
  }
22
35
  export async function decode(input, bytes, limits) {
23
36
  const request = admitRequest(input);
24
37
  if (!(bytes instanceof Uint8Array))
25
38
  invalid('Schema introspection evidence must be binary.');
26
- admitLimit(limits.maximumPublicationBytes, 'Publication');
27
- if (request.kind === 'bundle')
28
- admitLimit(limits.maximumBundleBytes, 'Bundle');
39
+ admitLimits(request, limits);
29
40
  const maximumEvidenceBytes = limits.maximumPublicationBytes +
30
- (request.kind === 'bundle' ? limits.maximumBundleBytes : 0) +
41
+ (request.kind === 'bundle' ? BUNDLE_ESCAPE_FACTOR * limits.maximumBundleBytes : 0) +
31
42
  4_096;
32
43
  if (bytes.byteLength > maximumEvidenceBytes) {
33
44
  invalid('Schema introspection evidence exceeds its declared bounds.');
34
45
  }
35
- const { unpack } = await import('msgpackr');
36
46
  let decoded;
37
47
  try {
38
- decoded = unpack(bytes);
48
+ decoded = json.decode(bytes, {
49
+ limits: {
50
+ maxBytes: maximumEvidenceBytes,
51
+ ...(request.kind === 'bundle' ? { maxStringBytes: limits.maximumBundleBytes } : {}),
52
+ },
53
+ });
39
54
  }
40
55
  catch (cause) {
41
56
  throw new TypeError('Schema introspection evidence is malformed.', { cause });
42
57
  }
43
- const result = (request.kind === 'bundle'
58
+ return acceptEvidence(request, decoded, limits);
59
+ }
60
+ /**
61
+ * Package-private: admit one decoded evidence value for an admitted request. Shared with the
62
+ * pre-R1 representation in `../legacy/`, which decodes other bytes into this same wire value.
63
+ */
64
+ export function acceptEvidence(request, decoded, limits) {
65
+ if (request.kind === 'installed')
66
+ return acceptInstalledReleases(decoded);
67
+ const result = request.kind === 'bundle'
44
68
  ? decodeDomainBundle(decoded, limits.maximumBundleBytes)
45
69
  : request.kind === 'dependencies'
46
70
  ? acceptDomainDependencies(decoded)
47
- : acceptDomainInfo(decoded));
71
+ : acceptDomainInfo(decoded);
48
72
  proveRequestedOrigin(request, result);
49
73
  return result;
50
74
  }
75
+ /** Package-private: admit the evidence bounds a request declares. Shared with `../legacy/`. */
76
+ export function admitLimits(request, limits) {
77
+ admitLimit(limits.maximumPublicationBytes, 'Publication');
78
+ if (request.kind === 'bundle')
79
+ admitLimit(limits.maximumBundleBytes, 'Bundle');
80
+ }
51
81
  function proveRequestedOrigin(request, result) {
52
82
  const origin = request.kind === 'inspect'
53
83
  ? result.origin
@@ -102,49 +132,148 @@ function acceptDependencyBinding(input) {
102
132
  binding: expected,
103
133
  });
104
134
  }
135
+ /** Admit the installed listing: origin-ordered, one release per origin, each pin of one kind. */
136
+ function acceptInstalledReleases(input) {
137
+ if (!Array.isArray(input))
138
+ invalid('Installed releases are invalid.');
139
+ const releases = input.map(acceptInstalledRelease);
140
+ for (const [index, installed] of releases.entries()) {
141
+ const previous = releases[index - 1];
142
+ if (previous !== undefined && previous.origin >= installed.origin) {
143
+ invalid('Installed releases must be origin-ordered and unique.');
144
+ }
145
+ }
146
+ return Object.freeze(releases);
147
+ }
148
+ function acceptInstalledRelease(input) {
149
+ const value = record(input, 'Installed release');
150
+ exact(value, ['inFlight', 'issuer', 'origin', 'pin', 'revision', 'url'], 'Installed release');
151
+ if (typeof value.origin !== 'string' || !patterns.origin.test(value.origin)) {
152
+ invalid('Installed release origin is invalid.');
153
+ }
154
+ if (typeof value.revision !== 'string' || !patterns.revision.test(value.revision)) {
155
+ invalid('Installed release revision is invalid.');
156
+ }
157
+ const issuer = issuerId(value.issuer, 'Installed release issuer');
158
+ return Object.freeze({
159
+ origin: value.origin,
160
+ revision: value.revision,
161
+ issuer,
162
+ url: deploymentUrl(value.url),
163
+ pin: installedPin(value.pin),
164
+ inFlight: inFlightIssuers(value.inFlight, issuer),
165
+ });
166
+ }
167
+ function installedPin(input) {
168
+ const value = record(input, 'Installed pin');
169
+ if (value.kind === 'release') {
170
+ exact(value, ['build', 'kind', 'release'], 'Installed release pin');
171
+ return Object.freeze({
172
+ kind: 'release',
173
+ release: digest(value.release, 'Installed release'),
174
+ build: digest(value.build, 'Installed build'),
175
+ });
176
+ }
177
+ // Deprecated legacy v2/v3 pin: deleted with the `legacy` variant of `InstalledPin`, in the
178
+ // breaking release that deletes `runtime/schema/installation/source/legacy/`.
179
+ if (value.kind === 'legacy') {
180
+ exact(value, ['document', 'etag', 'kind'], 'Installed legacy pin');
181
+ if (value.document !== 2 && value.document !== 3) {
182
+ invalid('Installed legacy pin document version is invalid.');
183
+ }
184
+ return Object.freeze({
185
+ kind: 'legacy',
186
+ document: value.document,
187
+ etag: digest(value.etag, 'Installed legacy Publication'),
188
+ });
189
+ }
190
+ return invalid('Installed pin kind is invalid.');
191
+ }
192
+ function inFlightIssuers(input, current) {
193
+ if (!Array.isArray(input))
194
+ invalid('Installed release in-flight issuers are invalid.');
195
+ const entries = input.map((entry) => {
196
+ const value = record(entry, 'In-flight issuer');
197
+ exact(value, ['issuer', 'release', 'replacedAt'], 'In-flight issuer');
198
+ if (typeof value.replacedAt !== 'string' || !isInstant(value.replacedAt)) {
199
+ invalid('In-flight issuer replacement instant is invalid.');
200
+ }
201
+ return Object.freeze({
202
+ issuer: issuerId(value.issuer, 'In-flight issuer'),
203
+ release: digest(value.release, 'In-flight release'),
204
+ replacedAt: value.replacedAt,
205
+ });
206
+ });
207
+ for (const [index, entry] of entries.entries()) {
208
+ if (entry.issuer === current)
209
+ invalid('An in-flight issuer cannot be the current issuer.');
210
+ const previous = entries[index - 1];
211
+ if (previous !== undefined && previous.issuer >= entry.issuer) {
212
+ invalid('In-flight issuers must be issuer-ordered and unique.');
213
+ }
214
+ }
215
+ return Object.freeze(entries);
216
+ }
217
+ function issuerId(input, label) {
218
+ if (!isIssuer(input))
219
+ invalid(`${label} is invalid.`);
220
+ return input;
221
+ }
222
+ /** An HTTP(S) origin, spelled exactly as `URL.origin` spells it. */
223
+ function deploymentUrl(input) {
224
+ let url;
225
+ try {
226
+ url = typeof input === 'string' ? new URL(input) : undefined;
227
+ }
228
+ catch {
229
+ url = undefined;
230
+ }
231
+ if (url === undefined || (url.protocol !== 'https:' && url.protocol !== 'http:')) {
232
+ invalid('Installed release URL is invalid.');
233
+ }
234
+ if (url.origin !== input)
235
+ invalid('Installed release URL must be an origin.');
236
+ return url.origin;
237
+ }
238
+ /** A UTC millisecond instant, spelled exactly as `Date.prototype.toISOString` spells it. */
239
+ function isInstant(input) {
240
+ const time = Date.parse(input);
241
+ return Number.isFinite(time) && new Date(time).toISOString() === input;
242
+ }
105
243
  function encodeDomainBundle(input) {
106
244
  exact(input, ['bundle', 'domain'], 'Domain Bundle result');
107
245
  const admittedDomain = acceptDomainInfo(input.domain);
108
246
  const domain = encodeDomainInfo(admittedDomain);
109
247
  proveBundle(input.bundle, admittedDomain);
110
- const encoded = encodedBundles.get(input.bundle) ?? bundle.encode(input.bundle);
111
- if (!encodedBundles.has(input.bundle))
112
- encodedBundles.set(input.bundle, encoded);
113
- return Object.freeze({ domain, bundle: encoded });
248
+ const text = serializedBundles.get(input.bundle) ?? bundle.serialize(input.bundle);
249
+ if (!serializedBundles.has(input.bundle))
250
+ serializedBundles.set(input.bundle, text);
251
+ return Object.freeze({ domain, bundle: text });
114
252
  }
115
253
  function decodeDomainBundle(input, maximum) {
116
254
  const value = record(input, 'Domain Bundle result');
117
255
  exact(value, ['bundle', 'domain'], 'Domain Bundle result');
118
256
  const domain = acceptDomainInfo(value.domain);
119
- const encoded = boundedBytes(value.bundle, maximum, 'Bundle');
257
+ const text = boundedText(value.bundle, maximum, 'Bundle');
120
258
  const cached = admittedBundles.get(domain.generation);
121
- if (cached !== undefined && sameBytes(cached.bytes, encoded)) {
259
+ if (cached !== undefined && cached.text === text) {
122
260
  return Object.freeze({ domain, bundle: cached.bundle });
123
261
  }
124
262
  let admitted;
125
263
  try {
126
- admitted = bundle.decode(encoded);
264
+ admitted = bundle.decode(text);
127
265
  }
128
266
  catch (cause) {
129
267
  throw new TypeError('Installed Domain Bundle is invalid.', { cause });
130
268
  }
131
269
  proveBundle(admitted, domain);
132
270
  admittedBundles.delete(domain.generation);
133
- admittedBundles.set(domain.generation, Object.freeze({ bytes: encoded, bundle: admitted }));
271
+ admittedBundles.set(domain.generation, Object.freeze({ text, bundle: admitted }));
134
272
  if (admittedBundles.size > MAXIMUM_ADMITTED_BUNDLES) {
135
273
  admittedBundles.delete(admittedBundles.keys().next().value);
136
274
  }
137
275
  return Object.freeze({ domain, bundle: admitted });
138
276
  }
139
- function sameBytes(left, right) {
140
- if (left.byteLength !== right.byteLength)
141
- return false;
142
- for (let index = 0; index < left.byteLength; index += 1) {
143
- if (left[index] !== right[index])
144
- return false;
145
- }
146
- return true;
147
- }
148
277
  function encodeDomainInfo(input) {
149
278
  return acceptDomainInfo(input);
150
279
  }
@@ -188,14 +317,15 @@ function publicationReference(input, origin, revision) {
188
317
  function bindings(input, origin) {
189
318
  const value = record(input, 'Domain bindings');
190
319
  exact(value, ['callables', 'views'], 'Domain bindings');
191
- if (!Array.isArray(value.callables) || !Array.isArray(value.views)) {
320
+ const { callables, views } = value;
321
+ if (!Array.isArray(callables) || !Array.isArray(views)) {
192
322
  invalid('Domain bindings are invalid.');
193
323
  }
194
324
  try {
195
- return Object.freeze({
196
- callables: Object.freeze(value.callables.map((entry) => admitCallableBinding(entry, origin))),
197
- views: Object.freeze(value.views.map((entry) => admitViewBinding(entry, origin))),
198
- });
325
+ return withPublicationError(() => Object.freeze({
326
+ callables: Object.freeze(callables.map((entry) => admitCallableBinding(entry, origin))),
327
+ views: Object.freeze(views.map((entry) => admitViewBinding(entry, origin))),
328
+ }));
199
329
  }
200
330
  catch (cause) {
201
331
  throw new TypeError('Domain bindings are invalid.', { cause });
@@ -220,8 +350,13 @@ function capabilities(input) {
220
350
  throw new TypeError('Domain capabilities are invalid.', { cause });
221
351
  }
222
352
  }
223
- function admitRequest(input) {
353
+ /** Package-private: admit one introspection request. Shared with `../legacy/`. */
354
+ export function admitRequest(input) {
224
355
  const value = record(input, 'Schema introspection request');
356
+ if (value.kind === 'installed') {
357
+ exact(value, ['kind'], 'Schema introspection request');
358
+ return Object.freeze({ kind: 'installed' });
359
+ }
225
360
  exact(value, ['kind', 'origin'], 'Schema introspection request');
226
361
  if (value.kind !== 'inspect' && value.kind !== 'bundle' && value.kind !== 'dependencies') {
227
362
  invalid('Schema introspection kind is invalid.');
@@ -237,11 +372,13 @@ function digest(input, label) {
237
372
  }
238
373
  return input;
239
374
  }
240
- function boundedBytes(input, maximum, label) {
241
- if (!(input instanceof Uint8Array) || input.byteLength === 0 || input.byteLength > maximum) {
375
+ function boundedText(input, maximum, label) {
376
+ if (typeof input !== 'string' ||
377
+ input.length === 0 ||
378
+ encoder.encode(input).byteLength > maximum) {
242
379
  invalid(`${label} evidence is invalid or exceeds its bound.`);
243
380
  }
244
- return Uint8Array.from(input);
381
+ return input;
245
382
  }
246
383
  function admitLimit(input, label) {
247
384
  if (!Number.isSafeInteger(input) || input < 1)
@@ -1,2 +1,2 @@
1
1
  export { MEDIA_TYPE, acceptDomainDependencies, acceptDomainInfo, decode, encode } from './codec.js';
2
- export type { DependencyBinding, Digest, DomainBundle, DomainDependencies, DomainInfo, Limits, ResultFor, } from './result.js';
2
+ export type { DependencyBinding, Digest, DomainBundle, DomainDependencies, DomainInfo, InFlightIssuer, InstalledPin, InstalledRelease, Limits, ResultFor, } from './result.js';
@@ -1,12 +1,21 @@
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
1
2
  import type { introspection } from '@astrale-os/kernel-core/schema';
2
3
  import type { bundle, Key, Revision } from '@astrale-os/kernel-dsl/v1';
3
- import type { Publication, PublicationRef, PublicationRequirements } from '../../../publication/index.js';
4
+ import type { BuildDigest, ReleaseDigest } from '../../../release/index.js';
5
+ import type { Publication, PublicationRef, PublicationRequirements } from '../../../release/legacy/index.js';
4
6
  export type Digest = `sha256:${string}`;
5
7
  /** Lightweight proof of one exact ready installed Domain generation. */
6
8
  export interface DomainInfo {
7
9
  readonly origin: Key.Origin;
8
10
  readonly revision: Revision;
9
11
  readonly generation: Digest;
12
+ /**
13
+ * Installed Publication reference: the pinned document's own for a legacy pin, the reference of
14
+ * the frozen v3 projection for a v4 pin.
15
+ *
16
+ * @deprecated Successor: `DomainInfo.release`, the installed `ReleaseRef`, emitted by the
17
+ * breaking release (AM-1).
18
+ */
10
19
  readonly publication: PublicationRef | null;
11
20
  readonly readiness: Digest;
12
21
  readonly capabilities: {
@@ -36,7 +45,57 @@ export interface DomainDependencies {
36
45
  readonly domain: DomainInfo;
37
46
  readonly dependencies: readonly DependencyBinding[];
38
47
  }
48
+ /**
49
+ * What one installation pins, one meaning per variant: a `DomainRelease` v4 by its release digest
50
+ * beside the build digest it runs, or a legacy v2/v3 Publication by its strong ETag.
51
+ */
52
+ export type InstalledPin = ReleasePin | LegacyPin;
53
+ /** A `DomainRelease` v4 pin: its release digest beside the build digest it runs. */
54
+ interface ReleasePin {
55
+ readonly kind: 'release';
56
+ readonly release: ReleaseDigest;
57
+ readonly build: BuildDigest;
58
+ }
59
+ /**
60
+ * A legacy v2/v3 Publication pin: its document version and strong ETag.
61
+ *
62
+ * @deprecated Successor: the `release` pin. Short-term consumer: the installations that still pin
63
+ * a v2/v3 Publication, such as the astrale Host's v3 installs until Shell is republished as a
64
+ * release. Deleted, with its codec branch, in the separate breaking release that deletes
65
+ * `runtime/schema/installation/source/legacy/`, once no installation pins a v2/v3 Publication
66
+ * (the conditions of `protocol/release/legacy/index.ts`).
67
+ */
68
+ interface LegacyPin {
69
+ readonly kind: 'legacy';
70
+ readonly document: 2 | 3;
71
+ readonly etag: Digest;
72
+ }
73
+ /**
74
+ * The issuer of a release one installation replaced, as the Kernel records it: accepted while its
75
+ * work drains, and listed until a later consented install re-pins or prunes it, drained or not.
76
+ */
77
+ export interface InFlightIssuer {
78
+ readonly issuer: IssuerId;
79
+ /** Pinned document digest of the replaced release. */
80
+ readonly release: Digest;
81
+ /** UTC millisecond ISO 8601 instant of the activation commit that replaced it. */
82
+ readonly replacedAt: string;
83
+ }
84
+ /** One ready installation the Kernel pins to a deployment, as its caller can read it. */
85
+ export interface InstalledRelease {
86
+ readonly origin: Key.Origin;
87
+ readonly revision: Revision;
88
+ /** Issuer of the installation's Domain principal. */
89
+ readonly issuer: IssuerId;
90
+ /** The deployment URL, an HTTP(S) origin, the Kernel discovered the pinned document at. */
91
+ readonly url: string;
92
+ readonly pin: InstalledPin;
93
+ /** Issuer-ordered, distinct, never the current issuer. */
94
+ readonly inFlight: readonly InFlightIssuer[];
95
+ }
39
96
  export type ResultFor<Input extends introspection.Introspection> = Input extends {
97
+ readonly kind: 'installed';
98
+ } ? readonly InstalledRelease[] : Input extends {
40
99
  readonly kind: 'bundle';
41
100
  } ? DomainBundle : Input extends {
42
101
  readonly kind: 'dependencies';
@@ -45,3 +104,4 @@ export interface Limits {
45
104
  readonly maximumPublicationBytes: number;
46
105
  readonly maximumBundleBytes: number;
47
106
  }
107
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Read-only decoders for the schema evidence of Hosts older than beta.127: MessagePack
3
+ * installation v1 (`application/vnd.astrale.schema-installation.v1+msgpack`) and introspection v2
4
+ * (`application/vnd.astrale.schema-introspection.v2+msgpack`). Each decodes its bytes into the same
5
+ * wire value as the canonical JSON evidence and admits it with the canonical checks, so it returns
6
+ * exactly the canonical `InstallResult` and introspection results. Nothing here encodes: Servers
7
+ * emit only the canonical JSON evidence.
8
+ *
9
+ * @deprecated Use `installation.decode` / `installation.introspection.decode` with their
10
+ * `MEDIA_TYPE`, the JSON evidence every Host from beta.127 serves.
11
+ *
12
+ * Short-term consumers, all through `@astrale-os/kernel-client` `SchemaApi`, which selects these
13
+ * decoders by the received media type:
14
+ * - Admin managing the 1Pact Host (Kernel beta.117) and the astrale Hosts on beta.121 and beta.126
15
+ * (`schema.inspect` before every Manager call, child `install`/`inspect`, `bundle` reads);
16
+ * - the CLI used by 1Pact developers, and `adapter-astrale` 0.5.0-beta.148, which runs
17
+ * `astrale introspect` before every 1Pact deploy (`introspect`, `view`,
18
+ * `domain install --direct`, cold-cache `call`);
19
+ * - the Console back-office (`schema.bundle` of every installed Domain);
20
+ * - SDK Project tooling (`pnpm dev`, the Environment installation reconcile), which runs
21
+ * `astrale introspect` and `astrale domain install --direct` through that same CLI against our
22
+ * beta.121 and beta.126 Hosts.
23
+ *
24
+ * Removal, in a separate breaking release: once no supported Host is older than beta.127 (the
25
+ * 1Pact Host upgraded; astrale-01, shared-01 and admin-users replaced or upgraded). Then delete, in
26
+ * the same PR:
27
+ * - this folder with its tests, golden evidence and laws, its `legacy/` entry in
28
+ * `../.spec/layout.ts`, and the `./schema/installation/legacy` subpaths (source and `dist`) of
29
+ * `protocol/package.json` with the `schema/installation/legacy/**` include of
30
+ * `protocol/tsconfig.json`;
31
+ * - the legacy media-type branches of `client/schema/schema.ts` and
32
+ * `client/schema/introspection.ts`, and `client/schema/__tests__/legacy-evidence.test.ts`;
33
+ * - the legacy paragraphs of `client/schema/.spec/architecture.md` and
34
+ * `../.spec/architecture.md`;
35
+ * - the package-private exports of `../codec.ts` (`accept`) and `../introspection/codec.ts`
36
+ * (`acceptEvidence`, `admitLimits`, `admitRequest`), which return to module scope.
37
+ */
38
+ export { MEDIA_TYPE, decode } from './installation.js';
39
+ export * as introspection from './introspection.js';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Read-only decoders for the schema evidence of Hosts older than beta.127: MessagePack
3
+ * installation v1 (`application/vnd.astrale.schema-installation.v1+msgpack`) and introspection v2
4
+ * (`application/vnd.astrale.schema-introspection.v2+msgpack`). Each decodes its bytes into the same
5
+ * wire value as the canonical JSON evidence and admits it with the canonical checks, so it returns
6
+ * exactly the canonical `InstallResult` and introspection results. Nothing here encodes: Servers
7
+ * emit only the canonical JSON evidence.
8
+ *
9
+ * @deprecated Use `installation.decode` / `installation.introspection.decode` with their
10
+ * `MEDIA_TYPE`, the JSON evidence every Host from beta.127 serves.
11
+ *
12
+ * Short-term consumers, all through `@astrale-os/kernel-client` `SchemaApi`, which selects these
13
+ * decoders by the received media type:
14
+ * - Admin managing the 1Pact Host (Kernel beta.117) and the astrale Hosts on beta.121 and beta.126
15
+ * (`schema.inspect` before every Manager call, child `install`/`inspect`, `bundle` reads);
16
+ * - the CLI used by 1Pact developers, and `adapter-astrale` 0.5.0-beta.148, which runs
17
+ * `astrale introspect` before every 1Pact deploy (`introspect`, `view`,
18
+ * `domain install --direct`, cold-cache `call`);
19
+ * - the Console back-office (`schema.bundle` of every installed Domain);
20
+ * - SDK Project tooling (`pnpm dev`, the Environment installation reconcile), which runs
21
+ * `astrale introspect` and `astrale domain install --direct` through that same CLI against our
22
+ * beta.121 and beta.126 Hosts.
23
+ *
24
+ * Removal, in a separate breaking release: once no supported Host is older than beta.127 (the
25
+ * 1Pact Host upgraded; astrale-01, shared-01 and admin-users replaced or upgraded). Then delete, in
26
+ * the same PR:
27
+ * - this folder with its tests, golden evidence and laws, its `legacy/` entry in
28
+ * `../.spec/layout.ts`, and the `./schema/installation/legacy` subpaths (source and `dist`) of
29
+ * `protocol/package.json` with the `schema/installation/legacy/**` include of
30
+ * `protocol/tsconfig.json`;
31
+ * - the legacy media-type branches of `client/schema/schema.ts` and
32
+ * `client/schema/introspection.ts`, and `client/schema/__tests__/legacy-evidence.test.ts`;
33
+ * - the legacy paragraphs of `client/schema/.spec/architecture.md` and
34
+ * `../.spec/architecture.md`;
35
+ * - the package-private exports of `../codec.ts` (`accept`) and `../introspection/codec.ts`
36
+ * (`acceptEvidence`, `admitLimits`, `admitRequest`), which return to module scope.
37
+ */
38
+ export { MEDIA_TYPE, decode } from './installation.js';
39
+ export * as introspection from './introspection.js';
@@ -0,0 +1,9 @@
1
+ import type { InstallResult } from '../result.js';
2
+ /** @deprecated Read-only: Hosts older than beta.127 serve this; current Hosts serve `installation.MEDIA_TYPE`. */
3
+ export declare const MEDIA_TYPE: 'application/vnd.astrale.schema-installation.v1+msgpack';
4
+ /**
5
+ * Decode version-one (MessagePack) installation evidence into the canonical result.
6
+ *
7
+ * @deprecated Read-only; use `installation.decode` with `installation.MEDIA_TYPE`.
8
+ */
9
+ export declare function decode(bytes: Uint8Array, maximumBytes: number): Promise<InstallResult>;
@@ -0,0 +1,28 @@
1
+ import { accept as acceptValue, defaultLimits } from '@astrale-os/kernel-dsl/value';
2
+ import { accept } from '../codec.js';
3
+ import { decode as decodeMessagePack } from './msgpack.js';
4
+ /** @deprecated Read-only: Hosts older than beta.127 serve this; current Hosts serve `installation.MEDIA_TYPE`. */
5
+ export const MEDIA_TYPE = 'application/vnd.astrale.schema-installation.v1+msgpack';
6
+ /**
7
+ * Decode version-one (MessagePack) installation evidence into the canonical result.
8
+ *
9
+ * @deprecated Read-only; use `installation.decode` with `installation.MEDIA_TYPE`.
10
+ */
11
+ export async function decode(bytes, maximumBytes) {
12
+ if (!(bytes instanceof Uint8Array) ||
13
+ !Number.isSafeInteger(maximumBytes) ||
14
+ maximumBytes < 1 ||
15
+ bytes.byteLength > maximumBytes) {
16
+ throw new TypeError('Schema installation evidence is invalid or exceeds its bound.');
17
+ }
18
+ let value;
19
+ try {
20
+ value = acceptValue(decodeMessagePack(bytes, defaultLimits.maxDepth), {
21
+ limits: { maxBytes: maximumBytes },
22
+ });
23
+ }
24
+ catch (cause) {
25
+ throw new TypeError('Schema installation evidence is malformed.', { cause });
26
+ }
27
+ return accept(value);
28
+ }
@@ -0,0 +1,10 @@
1
+ import type { introspection } from '@astrale-os/kernel-core/schema';
2
+ import type { Limits, ResultFor } from '../introspection/result.js';
3
+ /** @deprecated Read-only: Hosts older than beta.127 serve this; current Hosts serve `introspection.MEDIA_TYPE`. */
4
+ export declare const MEDIA_TYPE: 'application/vnd.astrale.schema-introspection.v2+msgpack';
5
+ /**
6
+ * Decode version-two (MessagePack) introspection evidence into the canonical result.
7
+ *
8
+ * @deprecated Read-only; use `introspection.decode` with `introspection.MEDIA_TYPE`.
9
+ */
10
+ export declare function decode<Input extends introspection.Introspection>(input: Input, bytes: Uint8Array, limits: Limits): Promise<ResultFor<Input>>;
@@ -0,0 +1,70 @@
1
+ import { accept as acceptValue, defaultLimits } from '@astrale-os/kernel-dsl/value';
2
+ import { acceptEvidence, admitLimits, admitRequest } from '../introspection/codec.js';
3
+ import { decode as decodeMessagePack } from './msgpack.js';
4
+ /** @deprecated Read-only: Hosts older than beta.127 serve this; current Hosts serve `introspection.MEDIA_TYPE`. */
5
+ export const MEDIA_TYPE = 'application/vnd.astrale.schema-introspection.v2+msgpack';
6
+ const utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
7
+ /**
8
+ * Decode version-two (MessagePack) introspection evidence into the canonical result.
9
+ *
10
+ * @deprecated Read-only; use `introspection.decode` with `introspection.MEDIA_TYPE`.
11
+ */
12
+ export async function decode(input, bytes, limits) {
13
+ const request = admitRequest(input);
14
+ if (!(bytes instanceof Uint8Array))
15
+ invalid('Schema introspection evidence must be binary.');
16
+ admitLimits(request, limits);
17
+ // Version two carried the Bundle as raw bytes: no escaping allowance.
18
+ const maximumEvidenceBytes = limits.maximumPublicationBytes +
19
+ (request.kind === 'bundle' ? limits.maximumBundleBytes : 0) +
20
+ 4_096;
21
+ if (bytes.byteLength > maximumEvidenceBytes) {
22
+ invalid('Schema introspection evidence exceeds its declared bounds.');
23
+ }
24
+ let wire;
25
+ try {
26
+ wire = decodeMessagePack(bytes, defaultLimits.maxDepth);
27
+ }
28
+ catch (cause) {
29
+ throw new TypeError('Schema introspection evidence is malformed.', { cause });
30
+ }
31
+ if (request.kind === 'bundle')
32
+ wire = bundleAsText(wire);
33
+ let decoded;
34
+ try {
35
+ decoded = acceptValue(wire, {
36
+ limits: {
37
+ maxBytes: maximumEvidenceBytes,
38
+ ...(request.kind === 'bundle' ? { maxStringBytes: limits.maximumBundleBytes } : {}),
39
+ },
40
+ });
41
+ }
42
+ catch (cause) {
43
+ throw new TypeError('Schema introspection evidence is malformed.', { cause });
44
+ }
45
+ return acceptEvidence(request, decoded, limits);
46
+ }
47
+ /**
48
+ * Version two carried the Bundle as the UTF-8 bytes of its canonical JSON; the current wire value
49
+ * carries that JSON as text. Translate only that field; every other check stays canonical.
50
+ */
51
+ function bundleAsText(wire) {
52
+ if (wire === null || typeof wire !== 'object' || Array.isArray(wire))
53
+ return wire;
54
+ if (!Object.hasOwn(wire, 'bundle'))
55
+ return wire;
56
+ const { bundle } = wire;
57
+ if (!(bundle instanceof Uint8Array))
58
+ invalid('Bundle evidence is invalid or exceeds its bound.');
59
+ let text;
60
+ try {
61
+ text = utf8.decode(bundle);
62
+ }
63
+ catch (cause) {
64
+ throw new TypeError('Installed Domain Bundle is invalid.', { cause });
65
+ }
66
+ return { ...wire, bundle: text };
67
+ }
68
+ function invalid(message) {
69
+ throw new TypeError(message);
70
+ }
@@ -0,0 +1 @@
1
+ export declare function decode(bytes: Uint8Array, maximumDepth: number): unknown;