@the-open-engine/zeroshot 6.28.0 → 6.29.1

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 (283) hide show
  1. package/README.md +3 -3
  2. package/cli/index.js +20 -224
  3. package/docker/zeroshot-cluster/Dockerfile +2 -3
  4. package/lib/cluster/client.cjs +30 -7
  5. package/lib/cluster/client.d.cts +52 -0
  6. package/lib/cluster/client.d.mts +52 -0
  7. package/lib/cluster/client.d.ts +4 -6
  8. package/lib/cluster/client.mjs +32 -9
  9. package/lib/cluster/connection-state.cjs +25 -0
  10. package/lib/cluster/connection-state.d.cts +13 -0
  11. package/lib/cluster/connection-state.d.mts +13 -0
  12. package/lib/cluster/connection-state.d.ts +13 -0
  13. package/lib/cluster/connection-state.mjs +21 -0
  14. package/lib/cluster/connection-types.cjs +2 -0
  15. package/lib/cluster/connection-types.d.cts +33 -0
  16. package/lib/cluster/connection-types.d.mts +33 -0
  17. package/lib/cluster/connection-types.d.ts +33 -0
  18. package/lib/cluster/connection-types.mjs +1 -0
  19. package/lib/cluster/connection.cjs +18 -46
  20. package/lib/cluster/connection.d.cts +26 -0
  21. package/lib/cluster/connection.d.mts +26 -0
  22. package/lib/cluster/connection.d.ts +7 -25
  23. package/lib/cluster/connection.mjs +12 -42
  24. package/lib/cluster/errors.cjs +9 -1
  25. package/lib/cluster/errors.d.cts +29 -0
  26. package/lib/cluster/errors.d.mts +29 -0
  27. package/lib/cluster/errors.d.ts +4 -0
  28. package/lib/cluster/errors.mjs +7 -0
  29. package/lib/cluster/frames.d.cts +12 -0
  30. package/lib/cluster/frames.d.mts +12 -0
  31. package/lib/cluster/generated/protocol-schema.d.cts +1 -0
  32. package/lib/cluster/generated/protocol-schema.d.mts +1 -0
  33. package/lib/cluster/generated/protocol.d.cts +781 -0
  34. package/lib/cluster/generated/protocol.d.mts +781 -0
  35. package/lib/cluster/index.cjs +2 -2
  36. package/lib/cluster/index.d.cts +13 -0
  37. package/lib/cluster/index.d.mts +13 -0
  38. package/lib/cluster/index.d.ts +3 -3
  39. package/lib/cluster/index.mjs +2 -2
  40. package/lib/cluster/json-source.cjs +1 -0
  41. package/lib/cluster/json-source.d.cts +5 -0
  42. package/lib/cluster/json-source.d.mts +5 -0
  43. package/lib/cluster/json-source.d.ts +1 -0
  44. package/lib/cluster/json-source.mjs +1 -0
  45. package/lib/cluster/payload-value.cjs +1 -0
  46. package/lib/cluster/payload-value.d.cts +8 -0
  47. package/lib/cluster/payload-value.d.mts +8 -0
  48. package/lib/cluster/payload-value.d.ts +1 -0
  49. package/lib/cluster/payload-value.mjs +1 -0
  50. package/lib/cluster/queue.d.cts +15 -0
  51. package/lib/cluster/queue.d.mts +15 -0
  52. package/lib/cluster/socket.cjs +8 -0
  53. package/lib/cluster/socket.d.cts +13 -0
  54. package/lib/cluster/socket.d.mts +13 -0
  55. package/lib/cluster/socket.d.ts +2 -0
  56. package/lib/cluster/socket.mjs +7 -0
  57. package/lib/cluster/subscriptions.d.cts +73 -0
  58. package/lib/cluster/subscriptions.d.mts +73 -0
  59. package/lib/cluster/subscriptions.d.ts +5 -1
  60. package/lib/cluster/validators.cjs +1 -0
  61. package/lib/cluster/validators.d.cts +7 -0
  62. package/lib/cluster/validators.d.mts +7 -0
  63. package/lib/cluster/validators.d.ts +1 -0
  64. package/lib/cluster/validators.mjs +1 -0
  65. package/lib/cluster-worker/engine-adapter.js +1 -11
  66. package/lib/cluster-worker/engine-input.js +14 -0
  67. package/lib/hosted-session/authority.cjs +61 -0
  68. package/lib/hosted-session/authority.d.cts +3 -0
  69. package/lib/hosted-session/authority.d.mts +3 -0
  70. package/lib/hosted-session/authority.d.ts +3 -0
  71. package/lib/hosted-session/authority.mjs +57 -0
  72. package/lib/hosted-session/coordinator.cjs +78 -51
  73. package/lib/hosted-session/coordinator.d.cts +11 -0
  74. package/lib/hosted-session/coordinator.d.mts +11 -0
  75. package/lib/hosted-session/coordinator.d.ts +4 -2
  76. package/lib/hosted-session/coordinator.mjs +75 -51
  77. package/lib/hosted-session/errors.cjs +25 -0
  78. package/lib/hosted-session/errors.d.cts +10 -0
  79. package/lib/hosted-session/errors.d.mts +10 -0
  80. package/lib/hosted-session/errors.d.ts +10 -0
  81. package/lib/hosted-session/errors.mjs +19 -0
  82. package/lib/hosted-session/index.cjs +4 -1
  83. package/lib/hosted-session/index.d.cts +2 -0
  84. package/lib/hosted-session/index.d.mts +2 -0
  85. package/lib/hosted-session/index.d.ts +2 -2
  86. package/lib/hosted-session/index.mjs +1 -1
  87. package/lib/hosted-session/reconnecting-watch.cjs +131 -0
  88. package/lib/hosted-session/reconnecting-watch.d.cts +13 -0
  89. package/lib/hosted-session/reconnecting-watch.d.mts +13 -0
  90. package/lib/hosted-session/reconnecting-watch.d.ts +13 -0
  91. package/lib/hosted-session/reconnecting-watch.mjs +127 -0
  92. package/lib/hosted-session/types.d.cts +32 -0
  93. package/lib/hosted-session/types.d.mts +32 -0
  94. package/lib/hosted-session/types.d.ts +19 -7
  95. package/lib/hosted-target/access-url.cjs +44 -0
  96. package/lib/hosted-target/access-url.d.cts +2 -0
  97. package/lib/hosted-target/access-url.d.mts +2 -0
  98. package/lib/hosted-target/access-url.d.ts +2 -0
  99. package/lib/hosted-target/access-url.mjs +41 -0
  100. package/lib/hosted-target/adapter-request.cjs +12 -0
  101. package/lib/hosted-target/adapter-request.d.cts +17 -0
  102. package/lib/hosted-target/adapter-request.d.mts +17 -0
  103. package/lib/hosted-target/adapter-request.d.ts +17 -0
  104. package/lib/hosted-target/adapter-request.mjs +9 -0
  105. package/lib/hosted-target/adapter-types.cjs +2 -0
  106. package/lib/hosted-target/adapter-types.d.cts +27 -0
  107. package/lib/hosted-target/adapter-types.d.mts +27 -0
  108. package/lib/hosted-target/adapter-types.d.ts +27 -0
  109. package/lib/hosted-target/adapter-types.mjs +1 -0
  110. package/lib/hosted-target/bounds.cjs +10 -0
  111. package/lib/hosted-target/bounds.d.cts +7 -0
  112. package/lib/hosted-target/bounds.d.mts +7 -0
  113. package/lib/hosted-target/bounds.d.ts +7 -0
  114. package/lib/hosted-target/bounds.mjs +7 -0
  115. package/lib/hosted-target/capsule-error-response.cjs +76 -0
  116. package/lib/hosted-target/capsule-error-response.d.cts +2 -0
  117. package/lib/hosted-target/capsule-error-response.d.mts +2 -0
  118. package/lib/hosted-target/capsule-error-response.d.ts +2 -0
  119. package/lib/hosted-target/capsule-error-response.mjs +73 -0
  120. package/lib/hosted-target/errors.cjs +88 -0
  121. package/lib/hosted-target/errors.d.cts +36 -0
  122. package/lib/hosted-target/errors.d.mts +36 -0
  123. package/lib/hosted-target/errors.d.ts +36 -0
  124. package/lib/hosted-target/errors.mjs +75 -0
  125. package/lib/hosted-target/index.cjs +16 -0
  126. package/lib/hosted-target/index.d.cts +3 -0
  127. package/lib/hosted-target/index.d.mts +3 -0
  128. package/lib/hosted-target/index.d.ts +3 -0
  129. package/lib/hosted-target/index.mjs +2 -0
  130. package/lib/hosted-target/response-validation.cjs +169 -0
  131. package/lib/hosted-target/response-validation.d.cts +5 -0
  132. package/lib/hosted-target/response-validation.d.mts +5 -0
  133. package/lib/hosted-target/response-validation.d.ts +5 -0
  134. package/lib/hosted-target/response-validation.mjs +163 -0
  135. package/lib/hosted-target/retry-executor.cjs +51 -0
  136. package/lib/hosted-target/retry-executor.d.cts +8 -0
  137. package/lib/hosted-target/retry-executor.d.mts +8 -0
  138. package/lib/hosted-target/retry-executor.d.ts +8 -0
  139. package/lib/hosted-target/retry-executor.mjs +48 -0
  140. package/lib/hosted-target/retry.cjs +78 -0
  141. package/lib/hosted-target/retry.d.cts +9 -0
  142. package/lib/hosted-target/retry.d.mts +9 -0
  143. package/lib/hosted-target/retry.d.ts +9 -0
  144. package/lib/hosted-target/retry.mjs +73 -0
  145. package/lib/hosted-target/target-adapter.cjs +10 -0
  146. package/lib/hosted-target/target-adapter.d.cts +3 -0
  147. package/lib/hosted-target/target-adapter.d.mts +3 -0
  148. package/lib/hosted-target/target-adapter.d.ts +3 -0
  149. package/lib/hosted-target/target-adapter.mjs +7 -0
  150. package/lib/hosted-target/types.cjs +4 -0
  151. package/lib/hosted-target/types.d.cts +50 -0
  152. package/lib/hosted-target/types.d.mts +50 -0
  153. package/lib/hosted-target/types.d.ts +50 -0
  154. package/lib/hosted-target/types.mjs +1 -0
  155. package/lib/hosted-target/zero-cloud-v1-adapter.cjs +185 -0
  156. package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +13 -0
  157. package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +13 -0
  158. package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +13 -0
  159. package/lib/hosted-target/zero-cloud-v1-adapter.mjs +181 -0
  160. package/lib/target/bounded-response.cjs +51 -0
  161. package/lib/target/bounded-response.d.cts +1 -0
  162. package/lib/target/bounded-response.d.mts +1 -0
  163. package/lib/target/bounded-response.d.ts +1 -0
  164. package/lib/target/bounded-response.js +51 -0
  165. package/lib/target/bounded-response.mjs +48 -0
  166. package/lib/target/credential-lock.js +1 -3
  167. package/lib/target/credential-store.js +9 -4
  168. package/lib/target/device-flow.cjs +213 -0
  169. package/lib/target/device-flow.d.cts +54 -0
  170. package/lib/target/device-flow.d.mts +54 -0
  171. package/lib/target/device-flow.d.ts +26 -10
  172. package/lib/target/device-flow.js +162 -53
  173. package/lib/target/device-flow.mjs +203 -0
  174. package/lib/target/discovery-errors.cjs +10 -0
  175. package/lib/target/discovery-errors.d.cts +3 -0
  176. package/lib/target/discovery-errors.d.mts +3 -0
  177. package/lib/target/discovery-errors.d.ts +3 -0
  178. package/lib/target/discovery-errors.js +10 -0
  179. package/lib/target/discovery-errors.mjs +6 -0
  180. package/lib/target/discovery-sections.cjs +191 -0
  181. package/lib/target/discovery-sections.d.cts +52 -0
  182. package/lib/target/discovery-sections.d.mts +52 -0
  183. package/lib/target/discovery-sections.d.ts +52 -0
  184. package/lib/target/discovery-sections.js +191 -0
  185. package/lib/target/discovery-sections.mjs +179 -0
  186. package/lib/target/discovery-validation.cjs +109 -0
  187. package/lib/target/discovery-validation.d.cts +28 -0
  188. package/lib/target/discovery-validation.d.mts +28 -0
  189. package/lib/target/discovery-validation.d.ts +28 -0
  190. package/lib/target/discovery-validation.js +109 -0
  191. package/lib/target/discovery-validation.mjs +99 -0
  192. package/lib/target/discovery.cjs +112 -0
  193. package/lib/target/discovery.d.cts +74 -0
  194. package/lib/target/discovery.d.mts +74 -0
  195. package/lib/target/discovery.d.ts +68 -5
  196. package/lib/target/discovery.js +93 -101
  197. package/lib/target/discovery.mjs +105 -0
  198. package/lib/target/index.d.ts +6 -6
  199. package/lib/target/index.js +36 -36
  200. package/lib/target/oauth-http.d.ts +2 -0
  201. package/lib/target/oauth-http.js +25 -0
  202. package/lib/target/refresh-exchange.d.ts +11 -0
  203. package/lib/target/refresh-exchange.js +24 -0
  204. package/lib/target/refresh-revocation.d.ts +8 -0
  205. package/lib/target/refresh-revocation.js +19 -0
  206. package/lib/target/route-template.cjs +85 -0
  207. package/lib/target/route-template.d.cts +7 -0
  208. package/lib/target/route-template.d.mts +7 -0
  209. package/lib/target/route-template.d.ts +7 -0
  210. package/lib/target/route-template.js +85 -0
  211. package/lib/target/route-template.mjs +81 -0
  212. package/lib/target/session-errors.d.ts +4 -0
  213. package/lib/target/session-errors.js +12 -0
  214. package/lib/target/session-verification.d.ts +10 -0
  215. package/lib/target/session-verification.js +42 -0
  216. package/lib/target/target-registry.d.ts +7 -3
  217. package/lib/target/target-registry.js +41 -10
  218. package/lib/target/target-session-manager.d.ts +48 -0
  219. package/lib/target/target-session-manager.js +226 -0
  220. package/lib/target/target-session.d.ts +39 -31
  221. package/lib/target/target-session.js +63 -150
  222. package/lib/target/token-response.cjs +51 -0
  223. package/lib/target/token-response.d.cts +7 -0
  224. package/lib/target/token-response.d.mts +7 -0
  225. package/lib/target/token-response.d.ts +7 -0
  226. package/lib/target/token-response.js +51 -0
  227. package/lib/target/token-response.mjs +48 -0
  228. package/package.json +16 -6
  229. package/scripts/audit-production-dependencies.js +150 -0
  230. package/scripts/build-cluster.js +23 -3
  231. package/scripts/opcore-agent-gate.js +159 -0
  232. package/scripts/opcore-agent-tool-overlays.js +154 -0
  233. package/scripts/opcore-introduced-check.js +288 -0
  234. package/src/cluster/client.ts +42 -15
  235. package/src/cluster/connection-state.ts +33 -0
  236. package/src/cluster/connection-types.ts +28 -0
  237. package/src/cluster/connection.ts +37 -68
  238. package/src/cluster/errors.ts +5 -0
  239. package/src/cluster/index.ts +3 -1
  240. package/src/cluster/json-source.ts +1 -0
  241. package/src/cluster/payload-value.ts +1 -0
  242. package/src/cluster/socket.ts +13 -0
  243. package/src/cluster/subscriptions.ts +7 -2
  244. package/src/cluster/validators.ts +1 -0
  245. package/src/cluster/ws.d.ts +1 -1
  246. package/src/hosted-session/authority.ts +77 -0
  247. package/src/hosted-session/coordinator.ts +113 -56
  248. package/src/hosted-session/errors.ts +21 -0
  249. package/src/hosted-session/index.ts +14 -2
  250. package/src/hosted-session/reconnecting-watch.ts +148 -0
  251. package/src/hosted-session/types.ts +22 -8
  252. package/src/hosted-target/access-url.ts +46 -0
  253. package/src/hosted-target/adapter-request.ts +28 -0
  254. package/src/hosted-target/adapter-types.ts +39 -0
  255. package/src/hosted-target/bounds.ts +1 -0
  256. package/src/hosted-target/capsule-error-response.ts +107 -0
  257. package/src/hosted-target/errors.ts +36 -35
  258. package/src/hosted-target/index.ts +10 -23
  259. package/src/hosted-target/response-validation.ts +170 -62
  260. package/src/hosted-target/retry-executor.ts +62 -0
  261. package/src/hosted-target/retry.ts +47 -11
  262. package/src/hosted-target/target-adapter.ts +12 -8
  263. package/src/hosted-target/types.ts +19 -18
  264. package/src/hosted-target/zero-cloud-v1-adapter.ts +229 -316
  265. package/src/target/bounded-response.ts +68 -0
  266. package/src/target/credential-lock.ts +1 -3
  267. package/src/target/credential-store.ts +14 -10
  268. package/src/target/device-flow.ts +206 -71
  269. package/src/target/discovery-errors.ts +6 -0
  270. package/src/target/discovery-sections.ts +251 -0
  271. package/src/target/discovery-validation.ts +144 -0
  272. package/src/target/discovery.ts +163 -115
  273. package/src/target/index.ts +15 -11
  274. package/src/target/oauth-http.ts +27 -0
  275. package/src/target/refresh-exchange.ts +36 -0
  276. package/src/target/refresh-revocation.ts +29 -0
  277. package/src/target/route-template.ts +105 -0
  278. package/src/target/session-errors.ts +9 -0
  279. package/src/target/session-verification.ts +51 -0
  280. package/src/target/target-registry.ts +60 -27
  281. package/src/target/target-session-manager.ts +285 -0
  282. package/src/target/target-session.ts +105 -222
  283. package/src/target/token-response.ts +62 -0
@@ -0,0 +1,179 @@
1
+ import { TargetDiscoveryError } from './discovery-errors.mjs';
2
+ import { closedRecord, exact, exactStringSet, integer, parseCredentialInstall, sameOriginUrl, stringField, } from './discovery-validation.mjs';
3
+ import { routeTemplate } from './route-template.mjs';
4
+ export function parseAdapter(discovery) {
5
+ const adapter = closedRecord(discovery.adapter, 'adapter', ['name', 'major_version']);
6
+ if (!['fargate', 'docker', 'local'].includes(String(adapter.name))) {
7
+ throw new TargetDiscoveryError('adapter.name is not supported');
8
+ }
9
+ exact(adapter.major_version, 1, 'adapter.major_version');
10
+ return Object.freeze({
11
+ name: adapter.name,
12
+ majorVersion: 1,
13
+ });
14
+ }
15
+ export function validateCachePolicy(discovery) {
16
+ const cache = closedRecord(discovery.cache_policy, 'cache_policy', ['control', 'discovery']);
17
+ exact(cache.control, 'no-store', 'cache_policy.control');
18
+ if (cache.discovery !== undefined && cache.discovery !== null &&
19
+ typeof cache.discovery !== 'string') {
20
+ throw new TargetDiscoveryError('cache_policy.discovery must be a string or null');
21
+ }
22
+ }
23
+ export function parseSizes(discovery) {
24
+ const sizes = closedRecord(discovery.sizes, 'sizes', ['catalog', 'default']);
25
+ if (!Array.isArray(sizes.catalog) || sizes.catalog.length === 0 ||
26
+ new Set(sizes.catalog).size !== sizes.catalog.length ||
27
+ sizes.catalog.some((size) => !['tiny', 'small', 'standard', 'large'].includes(String(size)))) {
28
+ throw new TargetDiscoveryError('sizes.catalog contains an unsupported size');
29
+ }
30
+ if (!sizes.catalog.includes(sizes.default)) {
31
+ throw new TargetDiscoveryError('sizes.default is not in sizes.catalog');
32
+ }
33
+ return Object.freeze({
34
+ catalog: Object.freeze([...sizes.catalog]),
35
+ default: sizes.default,
36
+ });
37
+ }
38
+ export function parseExtensions(discovery, origin) {
39
+ if (discovery.extensions === undefined || discovery.extensions === null)
40
+ return null;
41
+ const extensions = closedRecord(discovery.extensions, 'extensions', [
42
+ 'connections', 'credential_install',
43
+ ]);
44
+ if (extensions.connections === undefined) {
45
+ throw new TargetDiscoveryError('extensions.connections is required');
46
+ }
47
+ const connections = closedRecord(extensions.connections, 'extensions.connections', [
48
+ 'kind', 'base_url', 'route_templates',
49
+ ]);
50
+ exact(connections.kind, 'zerocloud.connections/v1', 'extensions.connections.kind');
51
+ sameOriginUrl(connections.base_url, 'extensions.connections.base_url', origin);
52
+ const routes = closedRecord(connections.route_templates, 'extensions.connections.route_templates', [
53
+ 'list', 'create', 'update',
54
+ ]);
55
+ routeTemplate(routes.list, 'extensions.connections.route_templates.list', []);
56
+ routeTemplate(routes.create, 'extensions.connections.route_templates.create', []);
57
+ routeTemplate(routes.update, 'extensions.connections.route_templates.update', ['connection_id']);
58
+ return parseCredentialInstall(extensions.credential_install);
59
+ }
60
+ export function validateOAuthMetadata(metadata, origin, expected) {
61
+ const device = sameOriginUrl(metadata.device_authorization_endpoint, 'OAuth metadata device_authorization_endpoint', origin);
62
+ const token = sameOriginUrl(metadata.token_endpoint, 'OAuth metadata token_endpoint', origin);
63
+ const revoke = sameOriginUrl(metadata.revocation_endpoint, 'OAuth metadata revocation_endpoint', origin);
64
+ if (device !== expected[0] || token !== expected[1] || revoke !== expected[2]) {
65
+ throw new TargetDiscoveryError('OAuth metadata does not match hosted-target discovery');
66
+ }
67
+ }
68
+ export function parseEndpoint(discovery, origin) {
69
+ const protocol = closedRecord(discovery.protocol, 'protocol', ['name', 'major_version']);
70
+ exact(protocol.name, 'openengine.cluster/v1', 'protocol.name');
71
+ exact(protocol.major_version, 1, 'protocol.major_version');
72
+ const binding = closedRecord(discovery.binding, 'binding', ['scope', 'auth_location']);
73
+ exact(binding.scope, 'organization', 'binding.scope');
74
+ exact(binding.auth_location, 'authorization_header', 'binding.auth_location');
75
+ const endpoint = closedRecord(discovery.endpoint, 'endpoint', ['url', 'capabilities']);
76
+ const capabilities = exactStringSet(endpoint.capabilities, 'endpoint.capabilities', ['exec', 'log_stream']);
77
+ const pagination = closedRecord(discovery.pagination, 'pagination', [
78
+ 'default_page_size',
79
+ 'max_page_size',
80
+ ]);
81
+ const defaultPageSize = integer(pagination.default_page_size, 'pagination.default_page_size', 1);
82
+ const maxPageSize = integer(pagination.max_page_size, 'pagination.max_page_size', 1);
83
+ if (defaultPageSize > maxPageSize) {
84
+ throw new TargetDiscoveryError('pagination.default_page_size exceeds maximum');
85
+ }
86
+ validateCachePolicy(discovery);
87
+ const schemaLinks = closedRecord(discovery.schema_links, 'schema_links', [
88
+ 'self',
89
+ 'problem',
90
+ 'errors',
91
+ ]);
92
+ sameOriginUrl(schemaLinks.self, 'schema_links.self', origin);
93
+ sameOriginUrl(schemaLinks.problem, 'schema_links.problem', origin);
94
+ sameOriginUrl(schemaLinks.errors, 'schema_links.errors', origin);
95
+ return Object.freeze({
96
+ url: sameOriginUrl(endpoint.url, 'endpoint.url', origin),
97
+ capabilities,
98
+ pagination: Object.freeze({ defaultPageSize, maxPageSize }),
99
+ });
100
+ }
101
+ export function parseCapsule(discovery, origin) {
102
+ const capsule = closedRecord(discovery.capsule_protocol, 'capsule_protocol', [
103
+ 'name',
104
+ 'major_version',
105
+ 'base_url',
106
+ 'route_templates',
107
+ ]);
108
+ exact(capsule.name, 'openengine.capsules/v1', 'capsule_protocol.name');
109
+ exact(capsule.major_version, 1, 'capsule_protocol.major_version');
110
+ const routes = closedRecord(capsule.route_templates, 'capsule_protocol.route_templates', ['allocate', 'list', 'inspect', 'terminate', 'limits', 'access']);
111
+ const route = (name, variables) => routeTemplate(routes[name], `capsule_protocol.route_templates.${name}`, variables);
112
+ return Object.freeze({
113
+ baseUrl: sameOriginUrl(capsule.base_url, 'capsule_protocol.base_url', origin),
114
+ routes: Object.freeze({
115
+ allocate: route('allocate', ['org_id']),
116
+ list: route('list', ['org_id', 'cursor', 'limit']),
117
+ inspect: route('inspect', ['org_id', 'capsule_id']),
118
+ terminate: route('terminate', ['org_id', 'capsule_id']),
119
+ limits: route('limits', ['org_id']),
120
+ access: route('access', ['capsule_id']),
121
+ }),
122
+ });
123
+ }
124
+ export function parseOAuth(discovery, origin) {
125
+ const oauth = closedRecord(discovery.oauth, 'oauth', [
126
+ 'metadata_url',
127
+ 'device_authorization_endpoint',
128
+ 'token_endpoint',
129
+ 'revocation_endpoint',
130
+ 'client_id',
131
+ 'device_grant_type',
132
+ 'device_exchange_fields',
133
+ 'audience',
134
+ ]);
135
+ exact(oauth.device_grant_type, 'urn:ietf:params:oauth:grant-type:device_code', 'oauth.device_grant_type');
136
+ exactStringSet(oauth.device_exchange_fields, 'oauth.device_exchange_fields', ['device_token', 'device_label']);
137
+ exact(oauth.audience, 'capsule', 'oauth.audience');
138
+ return Object.freeze({
139
+ metadataUrl: sameOriginUrl(oauth.metadata_url, 'oauth.metadata_url', origin),
140
+ deviceAuthorizationEndpoint: sameOriginUrl(oauth.device_authorization_endpoint, 'oauth.device_authorization_endpoint', origin),
141
+ tokenEndpoint: sameOriginUrl(oauth.token_endpoint, 'oauth.token_endpoint', origin),
142
+ revocationEndpoint: sameOriginUrl(oauth.revocation_endpoint, 'oauth.revocation_endpoint', origin),
143
+ clientId: stringField(oauth, 'client_id', 'oauth.'),
144
+ deviceGrantType: 'urn:ietf:params:oauth:grant-type:device_code',
145
+ audience: 'capsule',
146
+ });
147
+ }
148
+ export function parseSession(discovery) {
149
+ const session = closedRecord(discovery.session, 'session', [
150
+ 'route_template',
151
+ 'method',
152
+ 'cache_policy',
153
+ ]);
154
+ exact(session.method, 'GET', 'session.method');
155
+ exact(session.cache_policy, 'no-store', 'session.cache_policy');
156
+ return Object.freeze({
157
+ routeTemplate: routeTemplate(session.route_template, 'session.route_template', []),
158
+ method: 'GET',
159
+ });
160
+ }
161
+ export function parseTransport(discovery) {
162
+ const transport = closedRecord(discovery.transport, 'transport', [
163
+ 'websocket_route_template',
164
+ 'unauthorized_status',
165
+ 'close_codes',
166
+ ]);
167
+ exact(transport.unauthorized_status, 401, 'transport.unauthorized_status');
168
+ const closeCodes = closedRecord(transport.close_codes, 'transport.close_codes', [
169
+ 'expired',
170
+ 'revoked',
171
+ ]);
172
+ exact(closeCodes.expired, 4401, 'transport.close_codes.expired');
173
+ exact(closeCodes.revoked, 4403, 'transport.close_codes.revoked');
174
+ return Object.freeze({
175
+ websocketRouteTemplate: routeTemplate(transport.websocket_route_template, 'transport.websocket_route_template', ['capsule_id']),
176
+ unauthorizedStatus: 401,
177
+ closeCodes: Object.freeze({ expired: 4401, revoked: 4403 }),
178
+ });
179
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.record = record;
4
+ exports.closedRecord = closedRecord;
5
+ exports.stringField = stringField;
6
+ exports.exact = exact;
7
+ exports.integer = integer;
8
+ exports.exactStringSet = exactStringSet;
9
+ exports.sameOriginUrl = sameOriginUrl;
10
+ exports.parseCredentialInstall = parseCredentialInstall;
11
+ const discovery_errors_js_1 = require("./discovery-errors.cjs");
12
+ const route_template_js_1 = require("./route-template.cjs");
13
+ function record(value, field) {
14
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
15
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an object`);
16
+ }
17
+ return value;
18
+ }
19
+ function closedRecord(value, field, fields) {
20
+ const result = record(value, field);
21
+ const unknown = Object.keys(result).find((key) => !fields.includes(key));
22
+ if (unknown !== undefined)
23
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field}.${unknown} is not supported`);
24
+ return result;
25
+ }
26
+ function stringField(source, field, parent = '') {
27
+ const value = source[field];
28
+ if (typeof value !== 'string' || value.length === 0) {
29
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${parent}${field} must be a non-empty string`);
30
+ }
31
+ return value;
32
+ }
33
+ function exact(value, expected, field) {
34
+ if (value !== expected)
35
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} is not supported`);
36
+ }
37
+ function integer(value, field, min, max = Number.MAX_SAFE_INTEGER) {
38
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
39
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} is outside the supported bounds`);
40
+ }
41
+ return value;
42
+ }
43
+ function exactStringSet(value, field, expected) {
44
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
45
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an array of strings`);
46
+ }
47
+ if (value.length !== expected.length || new Set(value).size !== value.length ||
48
+ expected.some((item) => !value.includes(item))) {
49
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} does not match the supported contract`);
50
+ }
51
+ return Object.freeze([...value]);
52
+ }
53
+ function hasForbiddenUrlComponent(url, value, protocols) {
54
+ return !protocols.includes(url.protocol) ||
55
+ url.username !== '' ||
56
+ url.password !== '' ||
57
+ url.search !== '' ||
58
+ url.hash !== '' ||
59
+ url.href !== value;
60
+ }
61
+ function sameOriginUrl(value, field, origin, protocols = ['https:', 'http:']) {
62
+ if (typeof value !== 'string' || /[\u0000-\u0020\u007f]|\s/u.test(value)) {
63
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be a canonical absolute URL`);
64
+ }
65
+ let url;
66
+ try {
67
+ url = new URL(value);
68
+ }
69
+ catch {
70
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an absolute URL`);
71
+ }
72
+ if (hasForbiddenUrlComponent(url, value, protocols)) {
73
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} contains forbidden URL components`);
74
+ }
75
+ if (url.origin !== origin) {
76
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must remain on the target origin`);
77
+ }
78
+ return url.href;
79
+ }
80
+ function parseCredentialInstall(value) {
81
+ if (value === undefined || value === null)
82
+ return null;
83
+ const extension = closedRecord(value, 'extensions.credential_install', [
84
+ 'kind', 'grant', 'install', 'upload_url_origin', 'sealed_envelope_algorithms', 'bounds',
85
+ ]);
86
+ exact(extension.kind, 'openengine.capsule-credential-install/v1', 'extensions.credential_install.kind');
87
+ const grant = closedRecord(extension.grant, 'extensions.credential_install.grant', ['route_template', 'method']);
88
+ const install = closedRecord(extension.install, 'extensions.credential_install.install', ['route_template', 'method']);
89
+ exact(grant.method, 'POST', 'extensions.credential_install.grant.method');
90
+ exact(install.method, 'PUT', 'extensions.credential_install.install.method');
91
+ exact(extension.upload_url_origin, 'same_origin', 'extensions.credential_install.upload_url_origin');
92
+ exactStringSet(extension.sealed_envelope_algorithms, 'extensions.credential_install.sealed_envelope_algorithms', ['RSA-OAEP-3072-SHA256']);
93
+ const bounds = closedRecord(extension.bounds, 'extensions.credential_install.bounds', [
94
+ 'max_envelope_bytes', 'max_body_bytes', 'grant_ttl_seconds', 'max_clock_skew_seconds',
95
+ ]);
96
+ return Object.freeze({
97
+ kind: 'openengine.capsule-credential-install/v1',
98
+ grant: Object.freeze({ routeTemplate: (0, route_template_js_1.routeTemplate)(grant.route_template, 'extensions.credential_install.grant.route_template', ['capsule_id']), method: 'POST' }),
99
+ install: Object.freeze({ routeTemplate: (0, route_template_js_1.routeTemplate)(install.route_template, 'extensions.credential_install.install.route_template', ['capsule_id']), method: 'PUT' }),
100
+ uploadUrlOrigin: 'same_origin',
101
+ sealedEnvelopeAlgorithms: Object.freeze(['RSA-OAEP-3072-SHA256']),
102
+ bounds: Object.freeze({
103
+ maxEnvelopeBytes: integer(bounds.max_envelope_bytes, 'extensions.credential_install.bounds.max_envelope_bytes', 1, 1_048_576),
104
+ maxBodyBytes: integer(bounds.max_body_bytes, 'extensions.credential_install.bounds.max_body_bytes', 1, 1_048_576),
105
+ grantTtlSeconds: integer(bounds.grant_ttl_seconds, 'extensions.credential_install.bounds.grant_ttl_seconds', 1, 3_600),
106
+ maxClockSkewSeconds: integer(bounds.max_clock_skew_seconds, 'extensions.credential_install.bounds.max_clock_skew_seconds', 0, 300),
107
+ }),
108
+ });
109
+ }
@@ -0,0 +1,28 @@
1
+ import { type RouteTemplate } from './route-template.js';
2
+ export interface CredentialInstallDescriptor {
3
+ readonly kind: 'openengine.capsule-credential-install/v1';
4
+ readonly grant: {
5
+ readonly routeTemplate: RouteTemplate;
6
+ readonly method: 'POST';
7
+ };
8
+ readonly install: {
9
+ readonly routeTemplate: RouteTemplate;
10
+ readonly method: 'PUT';
11
+ };
12
+ readonly uploadUrlOrigin: 'same_origin';
13
+ readonly sealedEnvelopeAlgorithms: readonly ['RSA-OAEP-3072-SHA256'];
14
+ readonly bounds: {
15
+ readonly maxEnvelopeBytes: number;
16
+ readonly maxBodyBytes: number;
17
+ readonly grantTtlSeconds: number;
18
+ readonly maxClockSkewSeconds: number;
19
+ };
20
+ }
21
+ export declare function record(value: unknown, field: string): Record<string, unknown>;
22
+ export declare function closedRecord(value: unknown, field: string, fields: readonly string[]): Record<string, unknown>;
23
+ export declare function stringField(source: Record<string, unknown>, field: string, parent?: string): string;
24
+ export declare function exact(value: unknown, expected: unknown, field: string): void;
25
+ export declare function integer(value: unknown, field: string, min: number, max?: number): number;
26
+ export declare function exactStringSet(value: unknown, field: string, expected: readonly string[]): readonly string[];
27
+ export declare function sameOriginUrl(value: unknown, field: string, origin: string, protocols?: readonly string[]): string;
28
+ export declare function parseCredentialInstall(value: unknown): CredentialInstallDescriptor | null;
@@ -0,0 +1,28 @@
1
+ import { type RouteTemplate } from './route-template.js';
2
+ export interface CredentialInstallDescriptor {
3
+ readonly kind: 'openengine.capsule-credential-install/v1';
4
+ readonly grant: {
5
+ readonly routeTemplate: RouteTemplate;
6
+ readonly method: 'POST';
7
+ };
8
+ readonly install: {
9
+ readonly routeTemplate: RouteTemplate;
10
+ readonly method: 'PUT';
11
+ };
12
+ readonly uploadUrlOrigin: 'same_origin';
13
+ readonly sealedEnvelopeAlgorithms: readonly ['RSA-OAEP-3072-SHA256'];
14
+ readonly bounds: {
15
+ readonly maxEnvelopeBytes: number;
16
+ readonly maxBodyBytes: number;
17
+ readonly grantTtlSeconds: number;
18
+ readonly maxClockSkewSeconds: number;
19
+ };
20
+ }
21
+ export declare function record(value: unknown, field: string): Record<string, unknown>;
22
+ export declare function closedRecord(value: unknown, field: string, fields: readonly string[]): Record<string, unknown>;
23
+ export declare function stringField(source: Record<string, unknown>, field: string, parent?: string): string;
24
+ export declare function exact(value: unknown, expected: unknown, field: string): void;
25
+ export declare function integer(value: unknown, field: string, min: number, max?: number): number;
26
+ export declare function exactStringSet(value: unknown, field: string, expected: readonly string[]): readonly string[];
27
+ export declare function sameOriginUrl(value: unknown, field: string, origin: string, protocols?: readonly string[]): string;
28
+ export declare function parseCredentialInstall(value: unknown): CredentialInstallDescriptor | null;
@@ -0,0 +1,28 @@
1
+ import { type RouteTemplate } from './route-template.js';
2
+ export interface CredentialInstallDescriptor {
3
+ readonly kind: 'openengine.capsule-credential-install/v1';
4
+ readonly grant: {
5
+ readonly routeTemplate: RouteTemplate;
6
+ readonly method: 'POST';
7
+ };
8
+ readonly install: {
9
+ readonly routeTemplate: RouteTemplate;
10
+ readonly method: 'PUT';
11
+ };
12
+ readonly uploadUrlOrigin: 'same_origin';
13
+ readonly sealedEnvelopeAlgorithms: readonly ['RSA-OAEP-3072-SHA256'];
14
+ readonly bounds: {
15
+ readonly maxEnvelopeBytes: number;
16
+ readonly maxBodyBytes: number;
17
+ readonly grantTtlSeconds: number;
18
+ readonly maxClockSkewSeconds: number;
19
+ };
20
+ }
21
+ export declare function record(value: unknown, field: string): Record<string, unknown>;
22
+ export declare function closedRecord(value: unknown, field: string, fields: readonly string[]): Record<string, unknown>;
23
+ export declare function stringField(source: Record<string, unknown>, field: string, parent?: string): string;
24
+ export declare function exact(value: unknown, expected: unknown, field: string): void;
25
+ export declare function integer(value: unknown, field: string, min: number, max?: number): number;
26
+ export declare function exactStringSet(value: unknown, field: string, expected: readonly string[]): readonly string[];
27
+ export declare function sameOriginUrl(value: unknown, field: string, origin: string, protocols?: readonly string[]): string;
28
+ export declare function parseCredentialInstall(value: unknown): CredentialInstallDescriptor | null;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.record = record;
4
+ exports.closedRecord = closedRecord;
5
+ exports.stringField = stringField;
6
+ exports.exact = exact;
7
+ exports.integer = integer;
8
+ exports.exactStringSet = exactStringSet;
9
+ exports.sameOriginUrl = sameOriginUrl;
10
+ exports.parseCredentialInstall = parseCredentialInstall;
11
+ const discovery_errors_js_1 = require("./discovery-errors.js");
12
+ const route_template_js_1 = require("./route-template.js");
13
+ function record(value, field) {
14
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
15
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an object`);
16
+ }
17
+ return value;
18
+ }
19
+ function closedRecord(value, field, fields) {
20
+ const result = record(value, field);
21
+ const unknown = Object.keys(result).find((key) => !fields.includes(key));
22
+ if (unknown !== undefined)
23
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field}.${unknown} is not supported`);
24
+ return result;
25
+ }
26
+ function stringField(source, field, parent = '') {
27
+ const value = source[field];
28
+ if (typeof value !== 'string' || value.length === 0) {
29
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${parent}${field} must be a non-empty string`);
30
+ }
31
+ return value;
32
+ }
33
+ function exact(value, expected, field) {
34
+ if (value !== expected)
35
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} is not supported`);
36
+ }
37
+ function integer(value, field, min, max = Number.MAX_SAFE_INTEGER) {
38
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
39
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} is outside the supported bounds`);
40
+ }
41
+ return value;
42
+ }
43
+ function exactStringSet(value, field, expected) {
44
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
45
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an array of strings`);
46
+ }
47
+ if (value.length !== expected.length || new Set(value).size !== value.length ||
48
+ expected.some((item) => !value.includes(item))) {
49
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} does not match the supported contract`);
50
+ }
51
+ return Object.freeze([...value]);
52
+ }
53
+ function hasForbiddenUrlComponent(url, value, protocols) {
54
+ return !protocols.includes(url.protocol) ||
55
+ url.username !== '' ||
56
+ url.password !== '' ||
57
+ url.search !== '' ||
58
+ url.hash !== '' ||
59
+ url.href !== value;
60
+ }
61
+ function sameOriginUrl(value, field, origin, protocols = ['https:', 'http:']) {
62
+ if (typeof value !== 'string' || /[\u0000-\u0020\u007f]|\s/u.test(value)) {
63
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be a canonical absolute URL`);
64
+ }
65
+ let url;
66
+ try {
67
+ url = new URL(value);
68
+ }
69
+ catch {
70
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must be an absolute URL`);
71
+ }
72
+ if (hasForbiddenUrlComponent(url, value, protocols)) {
73
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} contains forbidden URL components`);
74
+ }
75
+ if (url.origin !== origin) {
76
+ throw new discovery_errors_js_1.TargetDiscoveryError(`${field} must remain on the target origin`);
77
+ }
78
+ return url.href;
79
+ }
80
+ function parseCredentialInstall(value) {
81
+ if (value === undefined || value === null)
82
+ return null;
83
+ const extension = closedRecord(value, 'extensions.credential_install', [
84
+ 'kind', 'grant', 'install', 'upload_url_origin', 'sealed_envelope_algorithms', 'bounds',
85
+ ]);
86
+ exact(extension.kind, 'openengine.capsule-credential-install/v1', 'extensions.credential_install.kind');
87
+ const grant = closedRecord(extension.grant, 'extensions.credential_install.grant', ['route_template', 'method']);
88
+ const install = closedRecord(extension.install, 'extensions.credential_install.install', ['route_template', 'method']);
89
+ exact(grant.method, 'POST', 'extensions.credential_install.grant.method');
90
+ exact(install.method, 'PUT', 'extensions.credential_install.install.method');
91
+ exact(extension.upload_url_origin, 'same_origin', 'extensions.credential_install.upload_url_origin');
92
+ exactStringSet(extension.sealed_envelope_algorithms, 'extensions.credential_install.sealed_envelope_algorithms', ['RSA-OAEP-3072-SHA256']);
93
+ const bounds = closedRecord(extension.bounds, 'extensions.credential_install.bounds', [
94
+ 'max_envelope_bytes', 'max_body_bytes', 'grant_ttl_seconds', 'max_clock_skew_seconds',
95
+ ]);
96
+ return Object.freeze({
97
+ kind: 'openengine.capsule-credential-install/v1',
98
+ grant: Object.freeze({ routeTemplate: (0, route_template_js_1.routeTemplate)(grant.route_template, 'extensions.credential_install.grant.route_template', ['capsule_id']), method: 'POST' }),
99
+ install: Object.freeze({ routeTemplate: (0, route_template_js_1.routeTemplate)(install.route_template, 'extensions.credential_install.install.route_template', ['capsule_id']), method: 'PUT' }),
100
+ uploadUrlOrigin: 'same_origin',
101
+ sealedEnvelopeAlgorithms: Object.freeze(['RSA-OAEP-3072-SHA256']),
102
+ bounds: Object.freeze({
103
+ maxEnvelopeBytes: integer(bounds.max_envelope_bytes, 'extensions.credential_install.bounds.max_envelope_bytes', 1, 1_048_576),
104
+ maxBodyBytes: integer(bounds.max_body_bytes, 'extensions.credential_install.bounds.max_body_bytes', 1, 1_048_576),
105
+ grantTtlSeconds: integer(bounds.grant_ttl_seconds, 'extensions.credential_install.bounds.grant_ttl_seconds', 1, 3_600),
106
+ maxClockSkewSeconds: integer(bounds.max_clock_skew_seconds, 'extensions.credential_install.bounds.max_clock_skew_seconds', 0, 300),
107
+ }),
108
+ });
109
+ }
@@ -0,0 +1,99 @@
1
+ import { TargetDiscoveryError } from './discovery-errors.mjs';
2
+ import { routeTemplate } from './route-template.mjs';
3
+ export function record(value, field) {
4
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
5
+ throw new TargetDiscoveryError(`${field} must be an object`);
6
+ }
7
+ return value;
8
+ }
9
+ export function closedRecord(value, field, fields) {
10
+ const result = record(value, field);
11
+ const unknown = Object.keys(result).find((key) => !fields.includes(key));
12
+ if (unknown !== undefined)
13
+ throw new TargetDiscoveryError(`${field}.${unknown} is not supported`);
14
+ return result;
15
+ }
16
+ export function stringField(source, field, parent = '') {
17
+ const value = source[field];
18
+ if (typeof value !== 'string' || value.length === 0) {
19
+ throw new TargetDiscoveryError(`${parent}${field} must be a non-empty string`);
20
+ }
21
+ return value;
22
+ }
23
+ export function exact(value, expected, field) {
24
+ if (value !== expected)
25
+ throw new TargetDiscoveryError(`${field} is not supported`);
26
+ }
27
+ export function integer(value, field, min, max = Number.MAX_SAFE_INTEGER) {
28
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
29
+ throw new TargetDiscoveryError(`${field} is outside the supported bounds`);
30
+ }
31
+ return value;
32
+ }
33
+ export function exactStringSet(value, field, expected) {
34
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
35
+ throw new TargetDiscoveryError(`${field} must be an array of strings`);
36
+ }
37
+ if (value.length !== expected.length || new Set(value).size !== value.length ||
38
+ expected.some((item) => !value.includes(item))) {
39
+ throw new TargetDiscoveryError(`${field} does not match the supported contract`);
40
+ }
41
+ return Object.freeze([...value]);
42
+ }
43
+ function hasForbiddenUrlComponent(url, value, protocols) {
44
+ return !protocols.includes(url.protocol) ||
45
+ url.username !== '' ||
46
+ url.password !== '' ||
47
+ url.search !== '' ||
48
+ url.hash !== '' ||
49
+ url.href !== value;
50
+ }
51
+ export function sameOriginUrl(value, field, origin, protocols = ['https:', 'http:']) {
52
+ if (typeof value !== 'string' || /[\u0000-\u0020\u007f]|\s/u.test(value)) {
53
+ throw new TargetDiscoveryError(`${field} must be a canonical absolute URL`);
54
+ }
55
+ let url;
56
+ try {
57
+ url = new URL(value);
58
+ }
59
+ catch {
60
+ throw new TargetDiscoveryError(`${field} must be an absolute URL`);
61
+ }
62
+ if (hasForbiddenUrlComponent(url, value, protocols)) {
63
+ throw new TargetDiscoveryError(`${field} contains forbidden URL components`);
64
+ }
65
+ if (url.origin !== origin) {
66
+ throw new TargetDiscoveryError(`${field} must remain on the target origin`);
67
+ }
68
+ return url.href;
69
+ }
70
+ export function parseCredentialInstall(value) {
71
+ if (value === undefined || value === null)
72
+ return null;
73
+ const extension = closedRecord(value, 'extensions.credential_install', [
74
+ 'kind', 'grant', 'install', 'upload_url_origin', 'sealed_envelope_algorithms', 'bounds',
75
+ ]);
76
+ exact(extension.kind, 'openengine.capsule-credential-install/v1', 'extensions.credential_install.kind');
77
+ const grant = closedRecord(extension.grant, 'extensions.credential_install.grant', ['route_template', 'method']);
78
+ const install = closedRecord(extension.install, 'extensions.credential_install.install', ['route_template', 'method']);
79
+ exact(grant.method, 'POST', 'extensions.credential_install.grant.method');
80
+ exact(install.method, 'PUT', 'extensions.credential_install.install.method');
81
+ exact(extension.upload_url_origin, 'same_origin', 'extensions.credential_install.upload_url_origin');
82
+ exactStringSet(extension.sealed_envelope_algorithms, 'extensions.credential_install.sealed_envelope_algorithms', ['RSA-OAEP-3072-SHA256']);
83
+ const bounds = closedRecord(extension.bounds, 'extensions.credential_install.bounds', [
84
+ 'max_envelope_bytes', 'max_body_bytes', 'grant_ttl_seconds', 'max_clock_skew_seconds',
85
+ ]);
86
+ return Object.freeze({
87
+ kind: 'openengine.capsule-credential-install/v1',
88
+ grant: Object.freeze({ routeTemplate: routeTemplate(grant.route_template, 'extensions.credential_install.grant.route_template', ['capsule_id']), method: 'POST' }),
89
+ install: Object.freeze({ routeTemplate: routeTemplate(install.route_template, 'extensions.credential_install.install.route_template', ['capsule_id']), method: 'PUT' }),
90
+ uploadUrlOrigin: 'same_origin',
91
+ sealedEnvelopeAlgorithms: Object.freeze(['RSA-OAEP-3072-SHA256']),
92
+ bounds: Object.freeze({
93
+ maxEnvelopeBytes: integer(bounds.max_envelope_bytes, 'extensions.credential_install.bounds.max_envelope_bytes', 1, 1_048_576),
94
+ maxBodyBytes: integer(bounds.max_body_bytes, 'extensions.credential_install.bounds.max_body_bytes', 1, 1_048_576),
95
+ grantTtlSeconds: integer(bounds.grant_ttl_seconds, 'extensions.credential_install.bounds.grant_ttl_seconds', 1, 3_600),
96
+ maxClockSkewSeconds: integer(bounds.max_clock_skew_seconds, 'extensions.credential_install.bounds.max_clock_skew_seconds', 0, 300),
97
+ }),
98
+ });
99
+ }