@qlever-llc/trellis 0.6.1 → 0.7.0-rc.2

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 (244) hide show
  1. package/README.md +6 -2
  2. package/esm/auth/device_activation.d.ts +194 -0
  3. package/esm/auth/device_activation.d.ts.map +1 -0
  4. package/esm/auth/{workload_activation.js → device_activation.js} +85 -60
  5. package/esm/auth/mod.d.ts +2 -2
  6. package/esm/auth/mod.d.ts.map +1 -1
  7. package/esm/auth/mod.js +2 -2
  8. package/esm/auth/protocol.d.ts +175 -72
  9. package/esm/auth/protocol.d.ts.map +1 -1
  10. package/esm/auth/protocol.js +126 -78
  11. package/esm/contracts/mod.d.ts +19 -3
  12. package/esm/contracts/mod.d.ts.map +1 -1
  13. package/esm/contracts/mod.js +28 -4
  14. package/esm/contracts/protocol.d.ts +34 -0
  15. package/esm/contracts/protocol.d.ts.map +1 -1
  16. package/esm/contracts/protocol.js +15 -0
  17. package/esm/server/deno.d.ts +1 -6
  18. package/esm/server/deno.d.ts.map +1 -1
  19. package/esm/server/deno.js +1 -16
  20. package/esm/server/mod.d.ts +3 -2
  21. package/esm/server/mod.d.ts.map +1 -1
  22. package/esm/server/mod.js +2 -2
  23. package/esm/server/node.d.ts +1 -6
  24. package/esm/server/node.d.ts.map +1 -1
  25. package/esm/server/node.js +1 -16
  26. package/esm/server/service.d.ts +32 -10
  27. package/esm/server/service.d.ts.map +1 -1
  28. package/esm/server/service.js +188 -41
  29. package/esm/server/transfer.d.ts +41 -0
  30. package/esm/server/transfer.d.ts.map +1 -0
  31. package/esm/server/transfer.js +418 -0
  32. package/esm/telemetry/init.d.ts +4 -0
  33. package/esm/telemetry/init.d.ts.map +1 -0
  34. package/esm/telemetry/init.js +7 -0
  35. package/esm/telemetry/mod.d.ts +1 -2
  36. package/esm/telemetry/mod.d.ts.map +1 -1
  37. package/esm/telemetry/mod.js +1 -2
  38. package/esm/telemetry/runtime.d.ts.map +1 -1
  39. package/esm/telemetry/runtime.js +9 -5
  40. package/esm/telemetry/trellis.d.ts +0 -1
  41. package/esm/telemetry/trellis.d.ts.map +1 -1
  42. package/esm/telemetry/trellis.js +0 -6
  43. package/esm/trellis/_sdk/auth/api.d.ts.map +1 -1
  44. package/esm/trellis/_sdk/auth/api.js +132 -110
  45. package/esm/trellis/_sdk/auth/contract.d.ts +1 -1
  46. package/esm/trellis/_sdk/auth/contract.d.ts.map +1 -1
  47. package/esm/trellis/_sdk/auth/contract.js +2 -2
  48. package/esm/trellis/_sdk/auth/schemas.d.ts +4212 -3069
  49. package/esm/trellis/_sdk/auth/schemas.d.ts.map +1 -1
  50. package/esm/trellis/_sdk/auth/schemas.js +144 -125
  51. package/esm/trellis/_sdk/auth/types.d.ts +417 -267
  52. package/esm/trellis/_sdk/auth/types.d.ts.map +1 -1
  53. package/esm/trellis/_sdk/auth/types.js +1 -1
  54. package/esm/trellis/_sdk/core/contract.d.ts +1 -1
  55. package/esm/trellis/_sdk/core/contract.d.ts.map +1 -1
  56. package/esm/trellis/_sdk/core/contract.js +2 -2
  57. package/esm/trellis/_sdk/core/schemas.d.ts +122 -0
  58. package/esm/trellis/_sdk/core/schemas.d.ts.map +1 -1
  59. package/esm/trellis/_sdk/core/schemas.js +4 -4
  60. package/esm/trellis/_sdk/core/types.d.ts +18 -1
  61. package/esm/trellis/_sdk/core/types.d.ts.map +1 -1
  62. package/esm/trellis/_sdk/core/types.js +1 -1
  63. package/esm/trellis/_sdk/state/_dnt.polyfills.d.ts +12 -0
  64. package/esm/trellis/_sdk/state/_dnt.polyfills.d.ts.map +1 -0
  65. package/esm/trellis/_sdk/state/_dnt.polyfills.js +15 -0
  66. package/esm/trellis/_sdk/state/api.d.ts +10 -0
  67. package/esm/trellis/_sdk/state/api.d.ts.map +1 -0
  68. package/esm/trellis/_sdk/state/api.js +71 -0
  69. package/esm/trellis/_sdk/state/contract.d.ts +8 -0
  70. package/esm/trellis/_sdk/state/contract.d.ts.map +1 -0
  71. package/esm/trellis/_sdk/state/contract.js +59 -0
  72. package/esm/trellis/_sdk/state/mod.d.ts +7 -0
  73. package/esm/trellis/_sdk/state/mod.d.ts.map +1 -0
  74. package/esm/trellis/_sdk/state/mod.js +5 -0
  75. package/esm/trellis/_sdk/state/package.json +3 -0
  76. package/esm/trellis/_sdk/state/schemas.d.ts +1437 -0
  77. package/esm/trellis/_sdk/state/schemas.d.ts.map +1 -0
  78. package/esm/trellis/_sdk/state/schemas.js +62 -0
  79. package/esm/trellis/_sdk/state/types.d.ts +206 -0
  80. package/esm/trellis/_sdk/state/types.d.ts.map +1 -0
  81. package/esm/trellis/_sdk/state/types.js +3 -0
  82. package/esm/trellis/client_connect.d.ts +53 -0
  83. package/esm/trellis/client_connect.d.ts.map +1 -0
  84. package/esm/trellis/client_connect.js +300 -0
  85. package/esm/trellis/contract.d.ts +1 -7
  86. package/esm/trellis/contract.d.ts.map +1 -1
  87. package/esm/trellis/contract.js +1 -12
  88. package/esm/trellis/device.d.ts +41 -0
  89. package/esm/trellis/device.d.ts.map +1 -0
  90. package/esm/trellis/device.js +209 -0
  91. package/esm/trellis/errors/AuthError.d.ts +1 -1
  92. package/esm/trellis/errors/AuthError.js +9 -9
  93. package/esm/trellis/errors/StoreError.d.ts +22 -0
  94. package/esm/trellis/errors/StoreError.d.ts.map +1 -0
  95. package/esm/trellis/errors/StoreError.js +41 -0
  96. package/esm/trellis/errors/TransferError.d.ts +22 -0
  97. package/esm/trellis/errors/TransferError.d.ts.map +1 -0
  98. package/esm/trellis/errors/TransferError.js +41 -0
  99. package/esm/trellis/errors/index.d.ts +8 -0
  100. package/esm/trellis/errors/index.d.ts.map +1 -1
  101. package/esm/trellis/errors/index.js +8 -0
  102. package/esm/trellis/index.d.ts +10 -4
  103. package/esm/trellis/index.d.ts.map +1 -1
  104. package/esm/trellis/index.js +6 -4
  105. package/esm/trellis/kv.d.ts +2 -0
  106. package/esm/trellis/kv.d.ts.map +1 -1
  107. package/esm/trellis/kv.js +6 -0
  108. package/esm/trellis/models/trellis/TrellisError.d.ts +15 -1
  109. package/esm/trellis/models/trellis/TrellisError.d.ts.map +1 -1
  110. package/esm/trellis/models/trellis/TrellisError.js +4 -0
  111. package/esm/trellis/runtime_transport.d.ts +12 -0
  112. package/esm/trellis/runtime_transport.d.ts.map +1 -0
  113. package/esm/trellis/runtime_transport.js +35 -0
  114. package/esm/trellis/sdk/state.d.ts +4 -0
  115. package/esm/trellis/sdk/state.d.ts.map +1 -0
  116. package/esm/trellis/sdk/state.js +3 -0
  117. package/esm/trellis/store.d.ts +51 -0
  118. package/esm/trellis/store.d.ts.map +1 -0
  119. package/esm/trellis/store.js +310 -0
  120. package/esm/trellis/tracing.js +1 -1
  121. package/esm/trellis/transfer.d.ts +118 -0
  122. package/esm/trellis/transfer.d.ts.map +1 -0
  123. package/esm/trellis/transfer.js +357 -0
  124. package/esm/trellis/trellis.d.ts +3 -0
  125. package/esm/trellis/trellis.d.ts.map +1 -1
  126. package/esm/trellis/trellis.js +48 -17
  127. package/package.json +7 -2
  128. package/script/auth/device_activation.d.ts +194 -0
  129. package/script/auth/device_activation.d.ts.map +1 -0
  130. package/script/auth/{workload_activation.js → device_activation.js} +99 -74
  131. package/script/auth/mod.d.ts +2 -2
  132. package/script/auth/mod.d.ts.map +1 -1
  133. package/script/auth/mod.js +84 -76
  134. package/script/auth/protocol.d.ts +175 -72
  135. package/script/auth/protocol.d.ts.map +1 -1
  136. package/script/auth/protocol.js +129 -81
  137. package/script/contracts/mod.d.ts +19 -3
  138. package/script/contracts/mod.d.ts.map +1 -1
  139. package/script/contracts/mod.js +30 -4
  140. package/script/contracts/protocol.d.ts +34 -0
  141. package/script/contracts/protocol.d.ts.map +1 -1
  142. package/script/contracts/protocol.js +16 -1
  143. package/script/telemetry/init.d.ts +4 -0
  144. package/script/telemetry/init.d.ts.map +1 -0
  145. package/script/telemetry/init.js +11 -0
  146. package/script/telemetry/mod.d.ts +1 -2
  147. package/script/telemetry/mod.d.ts.map +1 -1
  148. package/script/telemetry/mod.js +1 -4
  149. package/script/telemetry/runtime.d.ts.map +1 -1
  150. package/script/telemetry/runtime.js +9 -28
  151. package/script/telemetry/trellis.d.ts +0 -1
  152. package/script/telemetry/trellis.d.ts.map +1 -1
  153. package/script/telemetry/trellis.js +0 -7
  154. package/script/trellis/_sdk/auth/api.d.ts.map +1 -1
  155. package/script/trellis/_sdk/auth/api.js +132 -110
  156. package/script/trellis/_sdk/auth/contract.d.ts +1 -1
  157. package/script/trellis/_sdk/auth/contract.d.ts.map +1 -1
  158. package/script/trellis/_sdk/auth/contract.js +2 -2
  159. package/script/trellis/_sdk/auth/schemas.d.ts +4212 -3069
  160. package/script/trellis/_sdk/auth/schemas.d.ts.map +1 -1
  161. package/script/trellis/_sdk/auth/schemas.js +144 -125
  162. package/script/trellis/_sdk/auth/types.d.ts +417 -267
  163. package/script/trellis/_sdk/auth/types.d.ts.map +1 -1
  164. package/script/trellis/_sdk/auth/types.js +1 -1
  165. package/script/trellis/_sdk/core/contract.d.ts +1 -1
  166. package/script/trellis/_sdk/core/contract.d.ts.map +1 -1
  167. package/script/trellis/_sdk/core/contract.js +2 -2
  168. package/script/trellis/_sdk/core/schemas.d.ts +122 -0
  169. package/script/trellis/_sdk/core/schemas.d.ts.map +1 -1
  170. package/script/trellis/_sdk/core/schemas.js +4 -4
  171. package/script/trellis/_sdk/core/types.d.ts +18 -1
  172. package/script/trellis/_sdk/core/types.d.ts.map +1 -1
  173. package/script/trellis/_sdk/core/types.js +1 -1
  174. package/script/trellis/_sdk/state/_dnt.polyfills.d.ts +12 -0
  175. package/script/trellis/_sdk/state/_dnt.polyfills.d.ts.map +1 -0
  176. package/script/trellis/_sdk/state/_dnt.polyfills.js +16 -0
  177. package/script/trellis/_sdk/state/api.d.ts +10 -0
  178. package/script/trellis/_sdk/state/api.d.ts.map +1 -0
  179. package/script/trellis/_sdk/state/api.js +74 -0
  180. package/script/trellis/_sdk/state/contract.d.ts +8 -0
  181. package/script/trellis/_sdk/state/contract.d.ts.map +1 -0
  182. package/script/trellis/_sdk/state/contract.js +62 -0
  183. package/script/trellis/_sdk/state/mod.d.ts +7 -0
  184. package/script/trellis/_sdk/state/mod.d.ts.map +1 -0
  185. package/script/trellis/_sdk/state/mod.js +30 -0
  186. package/script/trellis/_sdk/state/package.json +3 -0
  187. package/script/trellis/_sdk/state/schemas.d.ts +1437 -0
  188. package/script/trellis/_sdk/state/schemas.d.ts.map +1 -0
  189. package/script/trellis/_sdk/state/schemas.js +65 -0
  190. package/script/trellis/_sdk/state/types.d.ts +206 -0
  191. package/script/trellis/_sdk/state/types.d.ts.map +1 -0
  192. package/script/trellis/_sdk/state/types.js +6 -0
  193. package/script/trellis/client_connect.d.ts +53 -0
  194. package/script/trellis/client_connect.d.ts.map +1 -0
  195. package/script/trellis/client_connect.js +304 -0
  196. package/script/trellis/contract.d.ts +1 -7
  197. package/script/trellis/contract.d.ts.map +1 -1
  198. package/script/trellis/contract.js +1 -12
  199. package/script/trellis/device.d.ts +41 -0
  200. package/script/trellis/device.d.ts.map +1 -0
  201. package/script/trellis/device.js +213 -0
  202. package/script/trellis/errors/AuthError.d.ts +1 -1
  203. package/script/trellis/errors/AuthError.js +9 -9
  204. package/script/trellis/errors/StoreError.d.ts +22 -0
  205. package/script/trellis/errors/StoreError.d.ts.map +1 -0
  206. package/script/trellis/errors/StoreError.js +48 -0
  207. package/script/trellis/errors/TransferError.d.ts +22 -0
  208. package/script/trellis/errors/TransferError.d.ts.map +1 -0
  209. package/script/trellis/errors/TransferError.js +48 -0
  210. package/script/trellis/errors/index.d.ts +8 -0
  211. package/script/trellis/errors/index.d.ts.map +1 -1
  212. package/script/trellis/errors/index.js +13 -1
  213. package/script/trellis/index.d.ts +10 -4
  214. package/script/trellis/index.d.ts.map +1 -1
  215. package/script/trellis/index.js +17 -6
  216. package/script/trellis/kv.d.ts +2 -0
  217. package/script/trellis/kv.d.ts.map +1 -1
  218. package/script/trellis/kv.js +6 -0
  219. package/script/trellis/models/trellis/TrellisError.d.ts +15 -1
  220. package/script/trellis/models/trellis/TrellisError.d.ts.map +1 -1
  221. package/script/trellis/models/trellis/TrellisError.js +4 -0
  222. package/script/trellis/runtime_transport.d.ts +12 -0
  223. package/script/trellis/runtime_transport.d.ts.map +1 -0
  224. package/script/trellis/runtime_transport.js +37 -0
  225. package/script/trellis/store.d.ts +51 -0
  226. package/script/trellis/store.d.ts.map +1 -0
  227. package/script/trellis/store.js +316 -0
  228. package/script/trellis/tracing.js +1 -1
  229. package/script/trellis/transfer.d.ts +118 -0
  230. package/script/trellis/transfer.d.ts.map +1 -0
  231. package/script/trellis/transfer.js +367 -0
  232. package/script/trellis/trellis.d.ts +3 -0
  233. package/script/trellis/trellis.d.ts.map +1 -1
  234. package/script/trellis/trellis.js +48 -17
  235. package/esm/auth/workload_activation.d.ts +0 -192
  236. package/esm/auth/workload_activation.d.ts.map +0 -1
  237. package/esm/trellis/workload.d.ts +0 -45
  238. package/esm/trellis/workload.d.ts.map +0 -1
  239. package/esm/trellis/workload.js +0 -144
  240. package/script/auth/workload_activation.d.ts +0 -192
  241. package/script/auth/workload_activation.d.ts.map +0 -1
  242. package/script/trellis/workload.d.ts +0 -45
  243. package/script/trellis/workload.d.ts.map +0 -1
  244. package/script/trellis/workload.js +0 -172
@@ -0,0 +1,300 @@
1
+ import * as dntShim from "../_dnt.shims.js";
2
+ import { jwtAuthenticator } from "@nats-io/nats-core";
3
+ import { BindResponseSchema, buildLoginUrl, getOrCreateSessionKey, } from "./auth.js";
4
+ import { createAuth } from "./auth.js";
5
+ import { canonicalizeJsonValue, sha256, utf8 } from "./auth.js";
6
+ import { loadDefaultRuntimeTransport, } from "./runtime_transport.js";
7
+ import { Trellis } from "./trellis.js";
8
+ import { Type } from "typebox";
9
+ import { Value } from "typebox/value";
10
+ import { bindFlowSig, getPublicSessionKey, oauthInitSig, signBytes, } from "../auth/browser/session.js";
11
+ const ClientBootstrapReadySchema = Type.Object({
12
+ status: Type.Literal("ready"),
13
+ connectInfo: Type.Object({
14
+ sessionKey: Type.String({ minLength: 1 }),
15
+ contractId: Type.String({ minLength: 1 }),
16
+ contractDigest: Type.String({ minLength: 1 }),
17
+ transport: Type.Object({
18
+ natsServers: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
19
+ inboxPrefix: Type.String({ minLength: 1 }),
20
+ sentinel: Type.Object({
21
+ jwt: Type.String({ minLength: 1 }),
22
+ seed: Type.String({ minLength: 1 }),
23
+ }, { additionalProperties: false }),
24
+ }, { additionalProperties: false }),
25
+ auth: Type.Object({
26
+ mode: Type.Literal("binding_token"),
27
+ bindingToken: Type.String({ minLength: 1 }),
28
+ expiresAt: Type.String({ format: "date-time" }),
29
+ }, { additionalProperties: false }),
30
+ }, { additionalProperties: false }),
31
+ }, { additionalProperties: true });
32
+ const ClientBootstrapAuthRequiredSchema = Type.Object({
33
+ status: Type.Literal("auth_required"),
34
+ }, { additionalProperties: true });
35
+ const ClientBootstrapNotReadySchema = Type.Object({
36
+ status: Type.Literal("not_ready"),
37
+ reason: Type.String({ minLength: 1 }),
38
+ }, { additionalProperties: true });
39
+ function isBrowserRuntime() {
40
+ return typeof dntShim.dntGlobalThis !== "undefined" && typeof document !== "undefined";
41
+ }
42
+ const defaultDeps = {
43
+ loadTransport: loadDefaultRuntimeTransport,
44
+ now: () => Date.now(),
45
+ };
46
+ function normalizeTrellisUrl(trellisUrl) {
47
+ return new URL(trellisUrl).toString().replace(/\/$/, "");
48
+ }
49
+ function resolveCurrentUrl(auth) {
50
+ if (auth?.currentUrl instanceof URL)
51
+ return auth.currentUrl;
52
+ if (typeof auth?.currentUrl === "string")
53
+ return new URL(auth.currentUrl);
54
+ if (isBrowserRuntime())
55
+ return new URL(globalThis.location.href);
56
+ return null;
57
+ }
58
+ function resolveRedirectTo(auth, currentUrl) {
59
+ if (auth.redirectTo) {
60
+ return new URL(auth.redirectTo, currentUrl.origin).toString();
61
+ }
62
+ const queryRedirect = currentUrl.searchParams.get("redirectTo");
63
+ if (queryRedirect) {
64
+ return new URL(queryRedirect, currentUrl.origin).toString();
65
+ }
66
+ if (auth.landingPath) {
67
+ return new URL(auth.landingPath, currentUrl.origin).toString();
68
+ }
69
+ return currentUrl.toString();
70
+ }
71
+ function encodeJsonForQuery(value) {
72
+ const json = canonicalizeJsonValue(value);
73
+ const bytes = new TextEncoder().encode(json);
74
+ let binary = "";
75
+ for (const byte of bytes)
76
+ binary += String.fromCharCode(byte);
77
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
78
+ }
79
+ async function signDomainValue(sign, prefix, value) {
80
+ const digest = await sha256(utf8(`${prefix}:${value}`));
81
+ const signature = await sign(digest);
82
+ const binary = String.fromCharCode(...signature);
83
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
84
+ }
85
+ async function resolveClientIdentity(auth) {
86
+ if (auth?.mode === "session_key") {
87
+ const sessionAuth = await createAuth({ sessionKeySeed: auth.sessionKeySeed });
88
+ return {
89
+ mode: "session_key",
90
+ sessionKey: sessionAuth.sessionKey,
91
+ sign: sessionAuth.sign,
92
+ oauthInitSig: sessionAuth.oauthInitSig,
93
+ bindingTokenSig: sessionAuth.natsConnectSigForBindingToken,
94
+ bootstrapSig: (iat) => signDomainValue(sessionAuth.sign, "bootstrap-client", String(iat)),
95
+ bindFlowSig: (flowId) => signDomainValue(sessionAuth.sign, "bind-flow", flowId),
96
+ };
97
+ }
98
+ const handle = auth?.handle ?? await getOrCreateSessionKey();
99
+ return {
100
+ mode: "browser",
101
+ sessionKey: getPublicSessionKey(handle),
102
+ sign: (data) => signBytes(handle, data),
103
+ oauthInitSig: (redirectTo, context) => oauthInitSig(handle, redirectTo, context),
104
+ bindingTokenSig: (bindingToken) => signDomainValue((data) => signBytes(handle, data), "nats-connect", bindingToken),
105
+ bootstrapSig: (iat) => signDomainValue((data) => signBytes(handle, data), "bootstrap-client", String(iat)),
106
+ bindFlowSig: (flowId) => bindFlowSig(handle, flowId),
107
+ };
108
+ }
109
+ async function bindClientFlow(args) {
110
+ const response = await fetch(`${args.trellisUrl}/auth/flow/${encodeURIComponent(args.flowId)}/bind`, {
111
+ method: "POST",
112
+ headers: { "Content-Type": "application/json" },
113
+ body: JSON.stringify({ sessionKey: args.sessionKey, sig: args.sig }),
114
+ });
115
+ if (!response.ok) {
116
+ throw new Error(`Client bind failed: ${response.status} ${await response.text()}`);
117
+ }
118
+ const payload = await response.json();
119
+ const parsed = Value.Parse(BindResponseSchema, payload);
120
+ if (parsed.status !== "bound") {
121
+ throw new Error(`Client bind did not complete: ${parsed.status}`);
122
+ }
123
+ }
124
+ async function fetchClientBootstrap(args) {
125
+ const response = await fetch(new URL("/bootstrap/client", args.trellisUrl), {
126
+ method: "POST",
127
+ headers: { "Content-Type": "application/json" },
128
+ body: JSON.stringify({
129
+ sessionKey: args.sessionKey,
130
+ iat: args.iat,
131
+ sig: args.bootstrapSig,
132
+ }),
133
+ });
134
+ const payload = await response.json();
135
+ if (!response.ok) {
136
+ const reason = typeof payload?.reason === "string"
137
+ ? payload.reason
138
+ : `http_${response.status}`;
139
+ throw new Error(`Client bootstrap failed: ${reason}`);
140
+ }
141
+ if (Value.Check(ClientBootstrapReadySchema, payload)) {
142
+ return payload;
143
+ }
144
+ if (Value.Check(ClientBootstrapAuthRequiredSchema, payload)) {
145
+ return payload;
146
+ }
147
+ if (Value.Check(ClientBootstrapNotReadySchema, payload)) {
148
+ return payload;
149
+ }
150
+ throw new Error("Client bootstrap returned an invalid response");
151
+ }
152
+ function cleanupBrowserCallbackUrl(currentUrl) {
153
+ if (!isBrowserRuntime())
154
+ return;
155
+ if (!currentUrl.searchParams.has("flowId") && !currentUrl.searchParams.has("authError")) {
156
+ return;
157
+ }
158
+ currentUrl.searchParams.delete("flowId");
159
+ currentUrl.searchParams.delete("authError");
160
+ globalThis.history.replaceState({}, "", currentUrl.pathname + currentUrl.search);
161
+ }
162
+ async function buildSessionKeyLoginUrl(args) {
163
+ const url = new URL(`${args.trellisUrl}/auth/login`);
164
+ url.searchParams.set("redirectTo", args.redirectTo);
165
+ url.searchParams.set("sessionKey", args.sessionKey);
166
+ url.searchParams.set("sig", args.oauthInitSig);
167
+ url.searchParams.set("contract", encodeJsonForQuery(args.contract));
168
+ if (args.provider) {
169
+ url.searchParams.set("provider", args.provider);
170
+ }
171
+ if (args.context !== undefined) {
172
+ url.searchParams.set("context", encodeJsonForQuery(args.context));
173
+ }
174
+ return url.toString();
175
+ }
176
+ export async function connectClientWithDeps(args, deps) {
177
+ const trellisUrl = normalizeTrellisUrl(args.trellisUrl);
178
+ const identity = await resolveClientIdentity(args.auth);
179
+ const currentUrl = args.auth?.mode === "session_key" ? null : resolveCurrentUrl(args.auth);
180
+ const callbackFlowId = args.auth?.mode === "session_key"
181
+ ? args.auth.flowId
182
+ : currentUrl?.searchParams.get("flowId") ?? undefined;
183
+ if (callbackFlowId) {
184
+ await bindClientFlow({
185
+ trellisUrl,
186
+ sessionKey: identity.sessionKey,
187
+ flowId: callbackFlowId,
188
+ sig: await identity.bindFlowSig(callbackFlowId),
189
+ });
190
+ if (currentUrl)
191
+ cleanupBrowserCallbackUrl(currentUrl);
192
+ }
193
+ const bootstrapIat = Math.floor(deps.now() / 1_000);
194
+ const initialBootstrap = await fetchClientBootstrap({
195
+ trellisUrl,
196
+ sessionKey: identity.sessionKey,
197
+ iat: bootstrapIat,
198
+ bootstrapSig: await identity.bootstrapSig(bootstrapIat),
199
+ });
200
+ const bootstrap = initialBootstrap.status === "auth_required"
201
+ ? await resolveAuthRequired(args, identity, currentUrl, deps)
202
+ : initialBootstrap;
203
+ if (bootstrap.status !== "ready") {
204
+ if (bootstrap.status === "not_ready") {
205
+ throw new Error(`Client bootstrap is not ready: ${bootstrap.reason}`);
206
+ }
207
+ throw new Error("Client bootstrap still requires authentication");
208
+ }
209
+ const transport = await deps.loadTransport();
210
+ const token = JSON.stringify({
211
+ v: 1,
212
+ sessionKey: identity.sessionKey,
213
+ bindingToken: bootstrap.connectInfo.auth.bindingToken,
214
+ sig: await identity.bindingTokenSig(bootstrap.connectInfo.auth.bindingToken),
215
+ });
216
+ const nc = await transport.connect({
217
+ servers: bootstrap.connectInfo.transport.natsServers,
218
+ token,
219
+ inboxPrefix: bootstrap.connectInfo.transport.inboxPrefix,
220
+ authenticator: jwtAuthenticator(bootstrap.connectInfo.transport.sentinel.jwt, new TextEncoder().encode(bootstrap.connectInfo.transport.sentinel.seed)),
221
+ });
222
+ const clientOpts = {
223
+ ...(typeof args.name === "string" ? { name: args.name } : {}),
224
+ ...(args.log ? { log: args.log } : {}),
225
+ ...(typeof args.timeout === "number" ? { timeout: args.timeout } : {}),
226
+ ...(typeof args.stream === "string" ? { stream: args.stream } : {}),
227
+ ...(args.noResponderRetry ? { noResponderRetry: args.noResponderRetry } : {}),
228
+ };
229
+ return new Trellis(clientOpts.name ?? "client", nc, {
230
+ sessionKey: identity.sessionKey,
231
+ sign: identity.sign,
232
+ }, {
233
+ log: clientOpts.log,
234
+ timeout: clientOpts.timeout,
235
+ stream: clientOpts.stream,
236
+ noResponderRetry: clientOpts.noResponderRetry,
237
+ api: args.contract.API.trellis,
238
+ });
239
+ }
240
+ async function resolveAuthRequired(args, identity, currentUrl, deps) {
241
+ const browserAuth = args.auth?.mode === "session_key"
242
+ ? {}
243
+ : args.auth ?? {};
244
+ const redirectTo = args.auth?.mode === "session_key"
245
+ ? args.auth.redirectTo
246
+ : currentUrl
247
+ ? resolveRedirectTo(browserAuth, currentUrl)
248
+ : undefined;
249
+ if (!redirectTo) {
250
+ throw new Error("Client authentication requires a redirectTo URL");
251
+ }
252
+ const loginUrl = args.auth?.mode === "session_key"
253
+ ? await buildSessionKeyLoginUrl({
254
+ trellisUrl: normalizeTrellisUrl(args.trellisUrl),
255
+ redirectTo,
256
+ sessionKey: identity.sessionKey,
257
+ contract: args.contract.CONTRACT,
258
+ provider: args.auth.provider,
259
+ context: args.auth.context,
260
+ oauthInitSig: await identity.oauthInitSig(redirectTo, args.auth.context),
261
+ })
262
+ : await buildLoginUrl({
263
+ authUrl: normalizeTrellisUrl(args.trellisUrl),
264
+ redirectTo,
265
+ handle: browserAuth.handle ?? await getOrCreateSessionKey(),
266
+ provider: browserAuth.provider,
267
+ contract: args.contract.CONTRACT,
268
+ context: browserAuth.context,
269
+ });
270
+ const continuation = await args.onAuthRequired?.({
271
+ loginUrl,
272
+ sessionKey: identity.sessionKey,
273
+ mode: identity.mode,
274
+ });
275
+ if (continuation && typeof continuation === "object" && "flowId" in continuation) {
276
+ await bindClientFlow({
277
+ trellisUrl: normalizeTrellisUrl(args.trellisUrl),
278
+ sessionKey: identity.sessionKey,
279
+ flowId: continuation.flowId,
280
+ sig: await identity.bindFlowSig(continuation.flowId),
281
+ });
282
+ const bootstrapIat = Math.floor(deps.now() / 1_000);
283
+ return await fetchClientBootstrap({
284
+ trellisUrl: normalizeTrellisUrl(args.trellisUrl),
285
+ sessionKey: identity.sessionKey,
286
+ iat: bootstrapIat,
287
+ bootstrapSig: await identity.bootstrapSig(bootstrapIat),
288
+ });
289
+ }
290
+ if (isBrowserRuntime()) {
291
+ globalThis.location.href = loginUrl;
292
+ throw new Error("Redirecting to Trellis login");
293
+ }
294
+ throw new Error("Client authentication required and no auth continuation was provided");
295
+ }
296
+ export class TrellisClient {
297
+ static connect(args) {
298
+ return connectClientWithDeps(args, defaultDeps);
299
+ }
300
+ }
@@ -1,11 +1,5 @@
1
- import type { NatsConnection } from "@nats-io/nats-core";
2
1
  import { type DefinedContract as BaseDefinedContract, type ContractApiViews, type ContractDependencyUse, type ContractModule, type ContractUseFn, type DefineContractInput, type EmptyApi, type MergeApis, type OwnedApiFromSource, type SdkContractModule, type TrellisApiLike, type TrellisContractV1, type UsedApiFromUses, type UseSpec } from "../contracts/mod.js";
3
- import type { ClientOpts } from "./client.js";
4
- import type { Trellis, TrellisAuth } from "./trellis.js";
5
- type RuntimeContractMethods<TOwnedApi extends TrellisApiLike, TTrellisApi extends TrellisApiLike> = {
6
- createClient(nats: NatsConnection, auth: TrellisAuth, opts?: ClientOpts): Trellis<TTrellisApi>;
7
- };
8
- export type DefinedContract<TOwnedApi extends TrellisApiLike, TUsedApi extends TrellisApiLike, TTrellisApi extends TrellisApiLike, TContractId extends string = string> = BaseDefinedContract<TOwnedApi, TUsedApi, TTrellisApi, TContractId> & RuntimeContractMethods<TOwnedApi, TTrellisApi>;
2
+ export type DefinedContract<TOwnedApi extends TrellisApiLike, TUsedApi extends TrellisApiLike, TTrellisApi extends TrellisApiLike, TContractId extends string = string> = BaseDefinedContract<TOwnedApi, TUsedApi, TTrellisApi, TContractId>;
9
3
  export declare function defineContract<const T extends DefineContractInput<any, any, any, any, any, any>>(source: T): DefinedContract<OwnedApiFromSource<T> & TrellisApiLike, UsedApiFromUses<T["uses"]> & TrellisApiLike, MergeApis<OwnedApiFromSource<T>, UsedApiFromUses<T["uses"]>> & TrellisApiLike, T["id"]>;
10
4
  export type { ContractApiViews, ContractDependencyUse, ContractModule, ContractUseFn, DefineContractInput, EmptyApi, SdkContractModule, TrellisApiLike, TrellisContractV1, UseSpec, };
11
5
  //# sourceMappingURL=contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/trellis/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EACL,KAAK,eAAe,IAAI,mBAAmB,EAC3C,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EAExB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,OAAO,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AASzD,KAAK,sBAAsB,CACzB,SAAS,SAAS,cAAc,EAChC,WAAW,SAAS,cAAc,IAChC;IACF,YAAY,CACV,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,WAAW,EACjB,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB,CAAC;AAOF,MAAM,MAAM,eAAe,CACzB,SAAS,SAAS,cAAc,EAChC,QAAQ,SAAS,cAAc,EAC/B,WAAW,SAAS,cAAc,EAClC,WAAW,SAAS,MAAM,GAAG,MAAM,IACjC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,GAAG,sBAAsB,CAC7F,SAAS,EACT,WAAW,CACZ,CAAC;AAwBF,wBAAgB,cAAc,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC9F,MAAM,EAAE,CAAC,GACR,eAAe,CAChB,kBAAkB,CAAC,CAAC,CAAC,GAAG,cAAc,EACtC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,cAAc,EAC3C,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,cAAc,EAC7E,CAAC,CAAC,IAAI,CAAC,CACR,CAWA;AAED,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,OAAO,GACR,CAAC"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/trellis/contract.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,eAAe,IAAI,mBAAmB,EAC3C,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EAExB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,OAAO,EACb,MAAM,qBAAqB,CAAC;AAS7B,MAAM,MAAM,eAAe,CACzB,SAAS,SAAS,cAAc,EAChC,QAAQ,SAAS,cAAc,EAC/B,WAAW,SAAS,cAAc,EAClC,WAAW,SAAS,MAAM,GAAG,MAAM,IACjC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;AAEvE,wBAAgB,cAAc,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAC9F,MAAM,EAAE,CAAC,GACR,eAAe,CAChB,kBAAkB,CAAC,CAAC,CAAC,GAAG,cAAc,EACtC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,cAAc,EAC3C,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,cAAc,EAC7E,CAAC,CAAC,IAAI,CAAC,CACR,CAOA;AAED,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,QAAQ,EACR,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,OAAO,GACR,CAAC"}
@@ -1,15 +1,4 @@
1
1
  import { defineContract as defineContractBase, } from "../contracts/mod.js";
2
- import { createClient } from "./client.js";
3
- function withRuntimeHelpers(contract) {
4
- const runtimeContract = contract;
5
- const createRuntimeClient = createClient;
6
- runtimeContract.createClient = (nats, auth, opts) => createRuntimeClient(runtimeContract, nats, auth, opts);
7
- return runtimeContract;
8
- }
9
2
  export function defineContract(source) {
10
- const contract = defineContractBase(source);
11
- // TypeScript's recursion limit trips over this cast in Svelte/tsserver, but
12
- // the runtime helper augmentation preserves the underlying contract shape.
13
- // @ts-ignore TS2589
14
- return withRuntimeHelpers(contract);
3
+ return defineContractBase(source);
15
4
  }
@@ -0,0 +1,41 @@
1
+ import { type NatsConnection } from "@nats-io/nats-core";
2
+ import type { TrellisAPI } from "./contracts.js";
3
+ import { Trellis } from "./trellis.js";
4
+ type DeviceContract<TApi extends TrellisAPI = TrellisAPI> = {
5
+ CONTRACT_ID: string;
6
+ CONTRACT_DIGEST: string;
7
+ API: {
8
+ trellis: TApi;
9
+ };
10
+ };
11
+ type DeviceConnectTransport = {
12
+ connect(options: {
13
+ servers: string | string[];
14
+ token?: string;
15
+ authenticator?: unknown;
16
+ inboxPrefix?: string;
17
+ }): Promise<NatsConnection>;
18
+ };
19
+ type DeviceConnectDeps = {
20
+ loadTransport(): Promise<DeviceConnectTransport>;
21
+ now(): number;
22
+ };
23
+ export type DeviceActivationController = {
24
+ url: string;
25
+ waitForOnlineApproval(opts?: {
26
+ signal?: AbortSignal;
27
+ }): Promise<void>;
28
+ acceptConfirmationCode(code: string): Promise<void>;
29
+ };
30
+ export type TrellisDeviceConnectArgs<TApi extends TrellisAPI = TrellisAPI> = {
31
+ trellisUrl: string;
32
+ contract: DeviceContract<TApi>;
33
+ rootSecret: Uint8Array | string;
34
+ onActivationRequired?(activation: DeviceActivationController): Promise<void>;
35
+ };
36
+ export declare function connectDeviceWithDeps<TApi extends TrellisAPI>(args: TrellisDeviceConnectArgs<TApi>, deps: DeviceConnectDeps): Promise<Trellis<TApi>>;
37
+ export declare const TrellisDevice: {
38
+ connect<TApi extends TrellisAPI>(args: TrellisDeviceConnectArgs<TApi>): Promise<Trellis<TApi>>;
39
+ };
40
+ export {};
41
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/trellis/device.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAa3E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAIvC,KAAK,cAAc,CAAC,IAAI,SAAS,UAAU,GAAG,UAAU,IAAI;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE;QACH,OAAO,EAAE,IAAI,CAAC;KACf,CAAC;CACH,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,OAAO,CAAC,OAAO,EAAE;QACf,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC7B,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,aAAa,IAAI,OAAO,CAAC,sBAAsB,CAAC,CAAC;IACjD,GAAG,IAAI,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,wBAAwB,CAAC,IAAI,SAAS,UAAU,GAAG,UAAU,IAAI;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC;IAC/B,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC;IAChC,oBAAoB,CAAC,CAAC,UAAU,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E,CAAC;AA0GF,wBAAsB,qBAAqB,CAAC,IAAI,SAAS,UAAU,EACjE,IAAI,EAAE,wBAAwB,CAAC,IAAI,CAAC,EACpC,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CA2HxB;AAED,eAAO,MAAM,aAAa;YAChB,IAAI,SAAS,UAAU,QACvB,wBAAwB,CAAC,IAAI,CAAC,GACnC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;CAG1B,CAAC"}
@@ -0,0 +1,209 @@
1
+ import { jwtAuthenticator } from "@nats-io/nats-core";
2
+ import { buildDeviceActivationPayload, buildDeviceActivationUrl, createDeviceNatsAuthToken, deriveDeviceIdentity, signDeviceWaitRequest, verifyDeviceConfirmationCode, waitForDeviceActivation, } from "../auth/device_activation.js";
3
+ import { importEd25519PrivateKeyFromSeedBase64url } from "../auth/keys.js";
4
+ import { base64urlDecode, base64urlEncode, toArrayBuffer } from "../auth/utils.js";
5
+ import { loadDefaultRuntimeTransport } from "./runtime_transport.js";
6
+ import { Trellis } from "./trellis.js";
7
+ import { Type } from "typebox";
8
+ import { Value } from "typebox/value";
9
+ const DeviceBootstrapReadySchema = Type.Object({
10
+ status: Type.Literal("ready"),
11
+ connectInfo: Type.Object({
12
+ instanceId: Type.String({ minLength: 1 }),
13
+ profileId: Type.String({ minLength: 1 }),
14
+ contractId: Type.String({ minLength: 1 }),
15
+ contractDigest: Type.String({ minLength: 1 }),
16
+ transport: Type.Object({
17
+ natsServers: Type.Array(Type.String({ minLength: 1 }), { minItems: 1 }),
18
+ sentinel: Type.Object({
19
+ jwt: Type.String({ minLength: 1 }),
20
+ seed: Type.String({ minLength: 1 }),
21
+ }, { additionalProperties: false }),
22
+ }, { additionalProperties: false }),
23
+ auth: Type.Object({
24
+ mode: Type.Literal("device_identity"),
25
+ iatSkewSeconds: Type.Integer({ minimum: 1 }),
26
+ }, { additionalProperties: false }),
27
+ }, { additionalProperties: false }),
28
+ }, { additionalProperties: false });
29
+ const DeviceBootstrapActivationRequiredSchema = Type.Object({
30
+ status: Type.Literal("activation_required"),
31
+ }, { additionalProperties: false });
32
+ const DeviceBootstrapNotReadySchema = Type.Object({
33
+ status: Type.Literal("not_ready"),
34
+ reason: Type.String({ minLength: 1 }),
35
+ }, { additionalProperties: false });
36
+ function normalizeRootSecret(rootSecret) {
37
+ if (typeof rootSecret === "string") {
38
+ const decoded = base64urlDecode(rootSecret.trim());
39
+ if (decoded.length === 0)
40
+ throw new Error("rootSecret must not be empty");
41
+ return decoded;
42
+ }
43
+ if (rootSecret.length === 0)
44
+ throw new Error("rootSecret must not be empty");
45
+ return rootSecret;
46
+ }
47
+ async function signIdentityBytes(identitySeed, data) {
48
+ const privateKey = await importEd25519PrivateKeyFromSeedBase64url(base64urlEncode(identitySeed));
49
+ return new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, toArrayBuffer(data)));
50
+ }
51
+ const defaultDeps = {
52
+ loadTransport: loadDefaultRuntimeTransport,
53
+ now: () => Date.now(),
54
+ };
55
+ function activationRequiredError() {
56
+ return new Error("Device activation required but no activation handler was provided");
57
+ }
58
+ function isConnectInfoUnavailable(error) {
59
+ const message = error instanceof Error ? error.message : String(error);
60
+ return message.includes("404") || message.includes("unknown_device") || message.includes("activation_required");
61
+ }
62
+ async function fetchDeviceBootstrap(args) {
63
+ const request = await signDeviceWaitRequest({
64
+ publicIdentityKey: args.publicIdentityKey,
65
+ nonce: "connect-info",
66
+ identitySeed: args.identitySeed,
67
+ contractDigest: args.contractDigest,
68
+ iat: args.iat,
69
+ });
70
+ const bootstrapRequest = {
71
+ publicIdentityKey: request.publicIdentityKey,
72
+ contractDigest: request.contractDigest,
73
+ iat: request.iat,
74
+ sig: request.sig,
75
+ };
76
+ const response = await fetch(new URL("/bootstrap/device", args.trellisUrl), {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/json" },
79
+ body: JSON.stringify(bootstrapRequest),
80
+ });
81
+ if (!response.ok) {
82
+ throw new Error(`Device bootstrap failed: ${response.status} ${await response.text()}`);
83
+ }
84
+ const payload = await response.json();
85
+ if (Value.Check(DeviceBootstrapReadySchema, payload))
86
+ return payload;
87
+ if (Value.Check(DeviceBootstrapActivationRequiredSchema, payload))
88
+ return payload;
89
+ if (Value.Check(DeviceBootstrapNotReadySchema, payload))
90
+ return payload;
91
+ throw new Error("Device bootstrap returned an invalid response");
92
+ }
93
+ export async function connectDeviceWithDeps(args, deps) {
94
+ const rootSecret = normalizeRootSecret(args.rootSecret);
95
+ const identity = await deriveDeviceIdentity(rootSecret);
96
+ const contractDigest = args.contract.CONTRACT_DIGEST;
97
+ let connectInfo = null;
98
+ try {
99
+ const bootstrap = await fetchDeviceBootstrap({
100
+ trellisUrl: args.trellisUrl,
101
+ publicIdentityKey: identity.publicIdentityKey,
102
+ identitySeed: identity.identitySeed,
103
+ contractDigest,
104
+ });
105
+ if (bootstrap.status === "ready") {
106
+ connectInfo = bootstrap.connectInfo;
107
+ }
108
+ else if (bootstrap.status === "not_ready") {
109
+ throw new Error(bootstrap.reason);
110
+ }
111
+ }
112
+ catch (error) {
113
+ if (!isConnectInfoUnavailable(error))
114
+ throw error;
115
+ }
116
+ if (!connectInfo) {
117
+ if (!args.onActivationRequired)
118
+ throw activationRequiredError();
119
+ const nonce = crypto.randomUUID();
120
+ const payload = await buildDeviceActivationPayload({
121
+ activationKey: identity.activationKey,
122
+ publicIdentityKey: identity.publicIdentityKey,
123
+ nonce,
124
+ });
125
+ const activationUrl = buildDeviceActivationUrl({
126
+ trellisUrl: args.trellisUrl,
127
+ payload,
128
+ });
129
+ let activationCompleted = false;
130
+ let onlineConnectInfo = null;
131
+ await args.onActivationRequired({
132
+ url: activationUrl,
133
+ waitForOnlineApproval: async (opts) => {
134
+ if (activationCompleted)
135
+ return;
136
+ const activation = await waitForDeviceActivation({
137
+ trellisUrl: args.trellisUrl,
138
+ publicIdentityKey: identity.publicIdentityKey,
139
+ nonce,
140
+ identitySeed: identity.identitySeed,
141
+ contractDigest,
142
+ signal: opts?.signal,
143
+ });
144
+ onlineConnectInfo = activation.connectInfo;
145
+ activationCompleted = true;
146
+ },
147
+ acceptConfirmationCode: async (code) => {
148
+ if (activationCompleted)
149
+ return;
150
+ const ok = await verifyDeviceConfirmationCode({
151
+ activationKey: identity.activationKey,
152
+ publicIdentityKey: identity.publicIdentityKey,
153
+ nonce,
154
+ confirmationCode: code,
155
+ });
156
+ if (!ok) {
157
+ throw new Error("Invalid device confirmation code");
158
+ }
159
+ activationCompleted = true;
160
+ },
161
+ });
162
+ if (!activationCompleted) {
163
+ throw new Error("Device activation did not complete");
164
+ }
165
+ if (onlineConnectInfo) {
166
+ connectInfo = onlineConnectInfo;
167
+ }
168
+ else {
169
+ const bootstrap = await fetchDeviceBootstrap({
170
+ trellisUrl: args.trellisUrl,
171
+ publicIdentityKey: identity.publicIdentityKey,
172
+ identitySeed: identity.identitySeed,
173
+ contractDigest,
174
+ });
175
+ if (bootstrap.status !== "ready") {
176
+ throw new Error(`Device bootstrap is not ready: ${bootstrap.status}`);
177
+ }
178
+ connectInfo = bootstrap.connectInfo;
179
+ }
180
+ }
181
+ if (!connectInfo) {
182
+ throw new Error("Device bootstrap did not return runtime connect info");
183
+ }
184
+ const transport = await deps.loadTransport();
185
+ const iat = Math.floor(deps.now() / 1_000);
186
+ const authToken = await createDeviceNatsAuthToken({
187
+ publicIdentityKey: identity.publicIdentityKey,
188
+ identitySeed: identity.identitySeed,
189
+ contractDigest,
190
+ iat,
191
+ });
192
+ const nc = await transport.connect({
193
+ servers: connectInfo.transport.natsServers,
194
+ token: JSON.stringify(authToken),
195
+ inboxPrefix: `_INBOX.${identity.publicIdentityKey.slice(0, 16)}`,
196
+ authenticator: jwtAuthenticator(connectInfo.transport.sentinel.jwt, new TextEncoder().encode(connectInfo.transport.sentinel.seed)),
197
+ });
198
+ return new Trellis(args.contract.CONTRACT_ID, nc, {
199
+ sessionKey: identity.publicIdentityKey,
200
+ sign: (data) => signIdentityBytes(identity.identitySeed, data),
201
+ }, {
202
+ api: args.contract.API.trellis,
203
+ });
204
+ }
205
+ export const TrellisDevice = {
206
+ connect(args) {
207
+ return connectDeviceWithDeps(args, defaultDeps);
208
+ },
209
+ };
@@ -4,7 +4,7 @@ export declare const AuthErrorDataSchema: Type.TObject<{
4
4
  id: Type.TString;
5
5
  type: Type.TLiteral<"AuthError">;
6
6
  message: Type.TString;
7
- reason: Type.TUnion<[Type.TLiteral<"invalid_request">, Type.TLiteral<"missing_session_key">, Type.TLiteral<"missing_proof">, Type.TLiteral<"session_not_found">, Type.TLiteral<"session_expired">, Type.TLiteral<"invalid_signature">, Type.TLiteral<"user_not_found">, Type.TLiteral<"user_inactive">, Type.TLiteral<"unknown_workload">, Type.TLiteral<"workload_profile_not_found">, Type.TLiteral<"workload_profile_disabled">, Type.TLiteral<"workload_activation_revoked">, Type.TLiteral<"unknown_service">, Type.TLiteral<"service_disabled">, Type.TLiteral<"iat_out_of_range">, Type.TLiteral<"invalid_binding_token">, Type.TLiteral<"session_corrupted">, Type.TLiteral<"session_already_bound">, Type.TLiteral<"authtoken_already_used">, Type.TLiteral<"oauth_session_key_mismatch">, Type.TLiteral<"service_role_on_user">, Type.TLiteral<"reply_subject_mismatch">, Type.TLiteral<"insufficient_permissions">, Type.TLiteral<"forbidden">, Type.TLiteral<"missing_handoff_id">, Type.TLiteral<"missing_profile_id">, Type.TLiteral<"workload_activation_handoff_not_found">, Type.TLiteral<"workload_activation_handoff_expired">, Type.TLiteral<"workload_activation_rejected">, Type.TLiteral<"workload_identity_key_mismatch">, Type.TLiteral<"invalid_workload_qr_mac">]>;
7
+ reason: Type.TUnion<[Type.TLiteral<"invalid_request">, Type.TLiteral<"missing_session_key">, Type.TLiteral<"missing_proof">, Type.TLiteral<"session_not_found">, Type.TLiteral<"session_expired">, Type.TLiteral<"invalid_signature">, Type.TLiteral<"user_not_found">, Type.TLiteral<"user_inactive">, Type.TLiteral<"unknown_device">, Type.TLiteral<"device_profile_not_found">, Type.TLiteral<"device_profile_disabled">, Type.TLiteral<"device_activation_revoked">, Type.TLiteral<"unknown_service">, Type.TLiteral<"service_disabled">, Type.TLiteral<"iat_out_of_range">, Type.TLiteral<"invalid_binding_token">, Type.TLiteral<"session_corrupted">, Type.TLiteral<"session_already_bound">, Type.TLiteral<"authtoken_already_used">, Type.TLiteral<"oauth_session_key_mismatch">, Type.TLiteral<"service_role_on_user">, Type.TLiteral<"reply_subject_mismatch">, Type.TLiteral<"insufficient_permissions">, Type.TLiteral<"forbidden">, Type.TLiteral<"missing_handoff_id">, Type.TLiteral<"missing_profile_id">, Type.TLiteral<"device_activation_handoff_not_found">, Type.TLiteral<"device_activation_handoff_expired">, Type.TLiteral<"device_activation_rejected">, Type.TLiteral<"device_identity_key_mismatch">, Type.TLiteral<"invalid_device_qr_mac">]>;
8
8
  context: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
9
9
  traceId: Type.TOptional<Type.TString>;
10
10
  }>;
@@ -13,10 +13,10 @@ export const AuthErrorDataSchema = Type.Object({
13
13
  Type.Literal("invalid_signature"),
14
14
  Type.Literal("user_not_found"),
15
15
  Type.Literal("user_inactive"),
16
- Type.Literal("unknown_workload"),
17
- Type.Literal("workload_profile_not_found"),
18
- Type.Literal("workload_profile_disabled"),
19
- Type.Literal("workload_activation_revoked"),
16
+ Type.Literal("unknown_device"),
17
+ Type.Literal("device_profile_not_found"),
18
+ Type.Literal("device_profile_disabled"),
19
+ Type.Literal("device_activation_revoked"),
20
20
  Type.Literal("unknown_service"),
21
21
  Type.Literal("service_disabled"),
22
22
  Type.Literal("iat_out_of_range"),
@@ -31,11 +31,11 @@ export const AuthErrorDataSchema = Type.Object({
31
31
  Type.Literal("forbidden"),
32
32
  Type.Literal("missing_handoff_id"),
33
33
  Type.Literal("missing_profile_id"),
34
- Type.Literal("workload_activation_handoff_not_found"),
35
- Type.Literal("workload_activation_handoff_expired"),
36
- Type.Literal("workload_activation_rejected"),
37
- Type.Literal("workload_identity_key_mismatch"),
38
- Type.Literal("invalid_workload_qr_mac"),
34
+ Type.Literal("device_activation_handoff_not_found"),
35
+ Type.Literal("device_activation_handoff_expired"),
36
+ Type.Literal("device_activation_rejected"),
37
+ Type.Literal("device_identity_key_mismatch"),
38
+ Type.Literal("invalid_device_qr_mac"),
39
39
  ]),
40
40
  context: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
41
41
  traceId: Type.Optional(Type.String()),
@@ -0,0 +1,22 @@
1
+ import Type, { type Static } from "typebox";
2
+ import { TrellisError } from "./TrellisError.js";
3
+ export declare const StoreErrorDataSchema: Type.TObject<{
4
+ id: Type.TString;
5
+ type: Type.TLiteral<"StoreError">;
6
+ message: Type.TString;
7
+ operation: Type.TOptional<Type.TString>;
8
+ context: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
9
+ traceId: Type.TOptional<Type.TString>;
10
+ }>;
11
+ export type StoreErrorData = Static<typeof StoreErrorDataSchema>;
12
+ export declare class StoreError extends TrellisError<StoreErrorData> {
13
+ readonly name: "StoreError";
14
+ readonly operation?: string;
15
+ constructor(options: ErrorOptions & {
16
+ operation?: string;
17
+ context?: Record<string, unknown>;
18
+ id?: string;
19
+ });
20
+ toSerializable(): StoreErrorData;
21
+ }
22
+ //# sourceMappingURL=StoreError.d.ts.map