@qlever-llc/trellis 0.10.17 → 0.10.18

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 (65) hide show
  1. package/esm/auth/browser/portal.d.ts.map +1 -1
  2. package/esm/auth/browser/portal.js +2 -0
  3. package/esm/auth/browser.d.ts +2 -0
  4. package/esm/auth/browser.d.ts.map +1 -1
  5. package/esm/auth/browser.js +1 -0
  6. package/esm/auth/browser_recovery.d.ts +22 -0
  7. package/esm/auth/browser_recovery.d.ts.map +1 -0
  8. package/esm/auth/browser_recovery.js +238 -0
  9. package/esm/auth/mod.d.ts +2 -2
  10. package/esm/auth/mod.d.ts.map +1 -1
  11. package/esm/auth/mod.js +2 -2
  12. package/esm/auth/protocol.d.ts +362 -398
  13. package/esm/auth/protocol.d.ts.map +1 -1
  14. package/esm/auth/protocol.js +36 -33
  15. package/esm/browser.d.ts +2 -2
  16. package/esm/browser.d.ts.map +1 -1
  17. package/esm/browser.js +1 -1
  18. package/esm/client_connect.js +1 -1
  19. package/esm/generated-sdk/auth/contract.d.ts +1 -1
  20. package/esm/generated-sdk/auth/contract.d.ts.map +1 -1
  21. package/esm/generated-sdk/auth/contract.js +1236 -1079
  22. package/esm/generated-sdk/auth/schemas.d.ts +1428 -1578
  23. package/esm/generated-sdk/auth/schemas.d.ts.map +1 -1
  24. package/esm/generated-sdk/auth/schemas.js +725 -669
  25. package/esm/generated-sdk/auth/types.d.ts +239 -281
  26. package/esm/generated-sdk/auth/types.d.ts.map +1 -1
  27. package/esm/generated-sdk/auth/types.js +1 -1
  28. package/package.json +2 -2
  29. package/script/auth/browser/portal.d.ts.map +1 -1
  30. package/script/auth/browser/portal.js +2 -0
  31. package/script/auth/browser.d.ts +2 -0
  32. package/script/auth/browser.d.ts.map +1 -1
  33. package/script/auth/browser.js +4 -1
  34. package/script/auth/browser_recovery.d.ts +22 -0
  35. package/script/auth/browser_recovery.d.ts.map +1 -0
  36. package/script/auth/browser_recovery.js +242 -0
  37. package/script/auth/mod.d.ts +2 -2
  38. package/script/auth/mod.d.ts.map +1 -1
  39. package/script/auth/mod.js +17 -6
  40. package/script/auth/protocol.d.ts +362 -398
  41. package/script/auth/protocol.d.ts.map +1 -1
  42. package/script/auth/protocol.js +41 -37
  43. package/script/browser.d.ts +2 -2
  44. package/script/browser.d.ts.map +1 -1
  45. package/script/browser.js +4 -2
  46. package/script/client_connect.js +1 -1
  47. package/script/generated-sdk/auth/contract.d.ts +1 -1
  48. package/script/generated-sdk/auth/contract.d.ts.map +1 -1
  49. package/script/generated-sdk/auth/contract.js +1236 -1079
  50. package/script/generated-sdk/auth/schemas.d.ts +1428 -1578
  51. package/script/generated-sdk/auth/schemas.d.ts.map +1 -1
  52. package/script/generated-sdk/auth/schemas.js +725 -669
  53. package/script/generated-sdk/auth/types.d.ts +239 -281
  54. package/script/generated-sdk/auth/types.d.ts.map +1 -1
  55. package/script/generated-sdk/auth/types.js +1 -1
  56. package/src/auth/browser/portal.ts +1 -0
  57. package/src/auth/browser.ts +8 -0
  58. package/src/auth/browser_recovery.ts +319 -0
  59. package/src/auth/mod.ts +25 -2
  60. package/src/auth/protocol.ts +73 -37
  61. package/src/browser.ts +4 -0
  62. package/src/client_connect.ts +1 -1
  63. package/src/sdk/_generated/auth/contract.ts +1477 -1320
  64. package/src/sdk/_generated/auth/schemas.ts +919 -863
  65. package/src/sdk/_generated/auth/types.ts +242 -304
@@ -0,0 +1,319 @@
1
+ /** Browser auth recovery classification helpers. */
2
+
3
+ /** Stable browser-auth recovery categories for app-owned recovery flows. */
4
+ export type BrowserAuthRecoveryKind =
5
+ | "recoverable_stale_session"
6
+ | "recoverable_expired_flow"
7
+ | "recoverable_auth_required"
8
+ | "policy_denied"
9
+ | "insufficient_capabilities"
10
+ | "runtime_unavailable"
11
+ | "unknown";
12
+
13
+ /** Classification result for a browser-auth related failure. */
14
+ export type BrowserAuthRecoveryClassification = {
15
+ kind: BrowserAuthRecoveryKind;
16
+ recoverable: boolean;
17
+ reason?: string;
18
+ code?: string;
19
+ };
20
+
21
+ type ErrorSignal = {
22
+ code?: string;
23
+ reason?: string;
24
+ message?: string;
25
+ };
26
+
27
+ const MAX_ERROR_DEPTH = 8;
28
+
29
+ function isRecord(value: unknown): value is Record<string, unknown> {
30
+ return value !== null && typeof value === "object";
31
+ }
32
+
33
+ function stringProperty(
34
+ record: Record<string, unknown>,
35
+ property: string,
36
+ ): string | undefined {
37
+ const value = record[property];
38
+ return typeof value === "string" ? value : undefined;
39
+ }
40
+
41
+ function normalize(value: string): string {
42
+ return value.trim().toLowerCase().replaceAll("-", "_").replaceAll(" ", "_");
43
+ }
44
+
45
+ function signalValue(signal: ErrorSignal): string | undefined {
46
+ return signal.code ?? signal.reason ?? signal.message;
47
+ }
48
+
49
+ function classification(
50
+ kind: BrowserAuthRecoveryKind,
51
+ recoverable: boolean,
52
+ signal?: ErrorSignal,
53
+ ): BrowserAuthRecoveryClassification {
54
+ const reason = signal?.reason ?? signalValue(signal ?? {});
55
+ return {
56
+ kind,
57
+ recoverable,
58
+ ...(reason ? { reason } : {}),
59
+ ...(signal?.code ? { code: signal.code } : {}),
60
+ };
61
+ }
62
+
63
+ function maybeSerializable(value: unknown): unknown {
64
+ if (!isRecord(value)) return undefined;
65
+ const toSerializable = value.toSerializable;
66
+ if (typeof toSerializable !== "function") return undefined;
67
+ try {
68
+ return toSerializable.call(value);
69
+ } catch {
70
+ return undefined;
71
+ }
72
+ }
73
+
74
+ function pushRecordSignals(
75
+ record: Record<string, unknown>,
76
+ signals: ErrorSignal[],
77
+ queue: unknown[],
78
+ ): void {
79
+ const message = stringProperty(record, "message");
80
+ const code = stringProperty(record, "code") ??
81
+ stringProperty(record, "error");
82
+ const reason = stringProperty(record, "reason") ??
83
+ stringProperty(record, "status");
84
+ if (message || code || reason) {
85
+ signals.push({
86
+ ...(code ? { code } : {}),
87
+ ...(reason ? { reason } : {}),
88
+ ...(message ? { message } : {}),
89
+ });
90
+ }
91
+
92
+ const context = record.context;
93
+ if (isRecord(context)) {
94
+ const contextReason = stringProperty(context, "reason");
95
+ const causeMessage = stringProperty(context, "causeMessage");
96
+ const contextCode = stringProperty(context, "code");
97
+ const contextMessage = stringProperty(context, "message");
98
+ if (contextReason || causeMessage || contextCode || contextMessage) {
99
+ signals.push({
100
+ ...(contextCode ? { code: contextCode } : {}),
101
+ ...(contextReason ? { reason: contextReason } : {}),
102
+ ...(causeMessage ?? contextMessage
103
+ ? { message: causeMessage ?? contextMessage }
104
+ : {}),
105
+ });
106
+ }
107
+ for (const nested of [context.cause, context.error, context.remoteError]) {
108
+ if (nested !== undefined) queue.push(nested);
109
+ }
110
+ }
111
+
112
+ for (const nested of [record.cause, record.error, record.remoteError]) {
113
+ if (nested !== undefined) queue.push(nested);
114
+ }
115
+ }
116
+
117
+ function collectErrorSignals(error: unknown): ErrorSignal[] {
118
+ const signals: ErrorSignal[] = [];
119
+ const queue: unknown[] = [error];
120
+ const seen = new WeakSet<object>();
121
+ let depth = 0;
122
+
123
+ while (queue.length > 0 && depth < MAX_ERROR_DEPTH) {
124
+ depth += 1;
125
+ const value = queue.shift();
126
+ if (typeof value === "string") {
127
+ signals.push({ message: value });
128
+ continue;
129
+ }
130
+ if (!isRecord(value)) continue;
131
+ if (seen.has(value)) continue;
132
+ seen.add(value);
133
+
134
+ if (value instanceof Error) {
135
+ signals.push({ message: value.message });
136
+ }
137
+ pushRecordSignals(value, signals, queue);
138
+
139
+ const serialized = maybeSerializable(value);
140
+ if (serialized && serialized !== value) queue.push(serialized);
141
+ }
142
+
143
+ return signals;
144
+ }
145
+
146
+ function hasNormalizedValue(
147
+ signals: ErrorSignal[],
148
+ values: ReadonlySet<string>,
149
+ ): ErrorSignal | undefined {
150
+ for (const signal of signals) {
151
+ for (const value of [signal.code, signal.reason]) {
152
+ if (value && values.has(normalize(value))) return signal;
153
+ }
154
+ }
155
+ return undefined;
156
+ }
157
+
158
+ function hasMessagePattern(
159
+ signals: ErrorSignal[],
160
+ patterns: readonly RegExp[],
161
+ ): ErrorSignal | undefined {
162
+ for (const signal of signals) {
163
+ const message = signal.message;
164
+ if (!message) continue;
165
+ const normalized = message.toLowerCase();
166
+ if (patterns.some((pattern) => pattern.test(normalized))) return signal;
167
+ }
168
+ return undefined;
169
+ }
170
+
171
+ function insufficientPermissionsAuthSignal(
172
+ signals: ErrorSignal[],
173
+ ): ErrorSignal | undefined {
174
+ for (const signal of signals) {
175
+ const reason = signal.reason ? normalize(signal.reason) : undefined;
176
+ const code = signal.code ? normalize(signal.code) : undefined;
177
+ if (reason !== "insufficient_permissions") continue;
178
+ if (code === "trellis.bootstrap.auth_required") return signal;
179
+ const message = signal.message?.toLowerCase() ?? "";
180
+ if (
181
+ message.includes("auth required") ||
182
+ message.includes("requires sign-in") ||
183
+ message.includes("requires signin") ||
184
+ message.includes("stale") ||
185
+ message.includes("session")
186
+ ) {
187
+ return signal;
188
+ }
189
+ }
190
+ return undefined;
191
+ }
192
+
193
+ const APPROVAL_DENIED_VALUES = new Set([
194
+ "approval_denied",
195
+ "trellis.auth.approval_denied",
196
+ ]);
197
+
198
+ const INSUFFICIENT_CAPABILITIES_VALUES = new Set([
199
+ "insufficient_capabilities",
200
+ "trellis.auth.insufficient_capabilities",
201
+ ]);
202
+
203
+ const RUNTIME_UNAVAILABLE_VALUES = new Set([
204
+ "trellis.bootstrap.not_ready",
205
+ "trellis.runtime.unavailable",
206
+ "runtime_unavailable",
207
+ ]);
208
+
209
+ const EXPIRED_FLOW_VALUES = new Set([
210
+ "flow_expired",
211
+ "expired",
212
+ "trellis.auth.bind_expired",
213
+ "trellis.auth.flow_expired",
214
+ ]);
215
+
216
+ const STALE_SESSION_VALUES = new Set([
217
+ "session_not_found",
218
+ "session_expired",
219
+ "user_not_found",
220
+ "contract_not_active",
221
+ "trellis.auth.session_not_found",
222
+ "trellis.auth.session_expired",
223
+ "trellis.auth.user_not_found",
224
+ "trellis.auth.contract_not_active",
225
+ ]);
226
+
227
+ const AUTH_REQUIRED_VALUES = new Set([
228
+ "auth_required",
229
+ "trellis.bootstrap.auth_required",
230
+ "trellis.auth.login_failed",
231
+ ]);
232
+
233
+ /**
234
+ * Classifies browser auth, bootstrap, callback, and transport-like failures.
235
+ *
236
+ * The classifier accepts raw errors, serialized Trellis errors, nested causes,
237
+ * and nested remote errors. Recoverable classifications are intended for
238
+ * app-owned flows that can clear stale auth and restart sign-in without showing a
239
+ * user-facing terminal error.
240
+ */
241
+ export function classifyBrowserAuthError(
242
+ error: unknown,
243
+ ): BrowserAuthRecoveryClassification {
244
+ const signals = collectErrorSignals(error);
245
+
246
+ const approvalDenied = hasNormalizedValue(signals, APPROVAL_DENIED_VALUES) ??
247
+ hasMessagePattern(signals, [/approval .*denied/, /access .*denied/]);
248
+ if (approvalDenied) {
249
+ return classification("policy_denied", false, approvalDenied);
250
+ }
251
+
252
+ const inactiveOrInvalid = hasNormalizedValue(
253
+ signals,
254
+ new Set(["invalid_credentials", "user_inactive", "inactive_account"]),
255
+ ) ?? hasMessagePattern(signals, [
256
+ /invalid credential/,
257
+ /inactive account/,
258
+ /user inactive/,
259
+ ]);
260
+ if (inactiveOrInvalid) {
261
+ return classification("policy_denied", false, inactiveOrInvalid);
262
+ }
263
+
264
+ const insufficientCapabilities = hasNormalizedValue(
265
+ signals,
266
+ INSUFFICIENT_CAPABILITIES_VALUES,
267
+ ) ?? hasMessagePattern(signals, [/insufficient capabilit/]);
268
+ if (insufficientCapabilities) {
269
+ return classification(
270
+ "insufficient_capabilities",
271
+ false,
272
+ insufficientCapabilities,
273
+ );
274
+ }
275
+
276
+ const runtimeUnavailable = hasNormalizedValue(
277
+ signals,
278
+ RUNTIME_UNAVAILABLE_VALUES,
279
+ ) ?? hasMessagePattern(signals, [/runtime unavailable/]);
280
+ if (runtimeUnavailable) {
281
+ return classification("runtime_unavailable", false, runtimeUnavailable);
282
+ }
283
+
284
+ const expiredFlow = hasNormalizedValue(signals, EXPIRED_FLOW_VALUES) ??
285
+ hasMessagePattern(signals, [/flow .*expired/, /sign\-in .*expired/]);
286
+ if (expiredFlow) {
287
+ return classification("recoverable_expired_flow", true, expiredFlow);
288
+ }
289
+
290
+ const staleSession = hasNormalizedValue(signals, STALE_SESSION_VALUES) ??
291
+ hasMessagePattern(signals, [
292
+ /session .*expired/,
293
+ /session .*not found/,
294
+ /user .*not found/,
295
+ /contract .*not active/,
296
+ ]);
297
+ if (staleSession) {
298
+ return classification("recoverable_stale_session", true, staleSession);
299
+ }
300
+
301
+ const authRequired = hasNormalizedValue(signals, AUTH_REQUIRED_VALUES) ??
302
+ insufficientPermissionsAuthSignal(signals) ??
303
+ hasMessagePattern(signals, [
304
+ /auth required/,
305
+ /requires sign\-in/,
306
+ /requires signin/,
307
+ /requires authentication/,
308
+ ]);
309
+ if (authRequired) {
310
+ return classification("recoverable_auth_required", true, authRequired);
311
+ }
312
+
313
+ return classification("unknown", false);
314
+ }
315
+
316
+ /** Returns whether a browser auth error can silently restart sign-in. */
317
+ export function isRecoverableBrowserAuthError(error: unknown): boolean {
318
+ return classifyBrowserAuthError(error).recoverable;
319
+ }
package/src/auth/mod.ts CHANGED
@@ -43,7 +43,10 @@ export {
43
43
  export {
44
44
  type AuthConfig,
45
45
  bindFlow,
46
+ type BrowserAuthRecoveryClassification,
47
+ type BrowserAuthRecoveryKind,
46
48
  buildLoginUrl,
49
+ classifyBrowserAuthError,
47
50
  clearSessionKey,
48
51
  createRpcProof,
49
52
  fetchPortalFlowState,
@@ -52,6 +55,7 @@ export {
52
55
  getPublicSessionKey,
53
56
  hasSessionKey,
54
57
  isBindSuccessResponse,
58
+ isRecoverableBrowserAuthError,
55
59
  loadSessionKey,
56
60
  natsConnectSigForIat,
57
61
  portalFlowIdFromUrl,
@@ -225,7 +229,11 @@ export {
225
229
  ContractAnalysisSummarySchema,
226
230
  type DeploymentAuthority,
227
231
  type DeploymentAuthorityCapability,
232
+ type DeploymentAuthorityCapabilityNeed,
233
+ DeploymentAuthorityCapabilityNeedSchema,
228
234
  DeploymentAuthorityCapabilitySchema,
235
+ type DeploymentAuthorityContractNeed,
236
+ DeploymentAuthorityContractNeedSchema,
229
237
  type DeploymentAuthorityGrantOverride,
230
238
  DeploymentAuthorityGrantOverrideSchema,
231
239
  type DeploymentAuthorityKind,
@@ -234,8 +242,8 @@ export {
234
242
  DeploymentAuthorityMaterializationSchema,
235
243
  type DeploymentAuthorityMigration,
236
244
  DeploymentAuthorityMigrationSchema,
237
- type DeploymentAuthorityNeed,
238
- DeploymentAuthorityNeedSchema,
245
+ type DeploymentAuthorityNeeds,
246
+ DeploymentAuthorityNeedsSchema,
239
247
  type DeploymentAuthorityPlan,
240
248
  DeploymentAuthorityPlanSchema,
241
249
  type DeploymentAuthorityProposal,
@@ -244,11 +252,15 @@ export {
244
252
  DeploymentAuthorityReconciliationStatusSchema,
245
253
  type DeploymentAuthorityResource,
246
254
  DeploymentAuthorityResourceKindSchema,
255
+ type DeploymentAuthorityResourceNeed,
256
+ DeploymentAuthorityResourceNeedSchema,
247
257
  DeploymentAuthorityResourceSchema,
248
258
  DeploymentAuthoritySchema,
249
259
  type DeploymentAuthoritySurface,
250
260
  DeploymentAuthoritySurfaceActionSchema,
251
261
  DeploymentAuthoritySurfaceKindSchema,
262
+ type DeploymentAuthoritySurfaceNeed,
263
+ DeploymentAuthoritySurfaceNeedSchema,
252
264
  DeploymentAuthoritySurfaceSchema,
253
265
  type DeploymentAuthorityUpdate,
254
266
  DeploymentAuthorityUpdateSchema,
@@ -273,6 +285,17 @@ export {
273
285
  LoginPortalSettingsSchema,
274
286
  type LoginPortalSummary,
275
287
  LoginPortalSummarySchema,
288
+ type MaterializedAuthorityCapabilityGrant,
289
+ MaterializedAuthorityCapabilityGrantSchema,
290
+ type MaterializedAuthorityGrant,
291
+ type MaterializedAuthorityGrants,
292
+ MaterializedAuthorityGrantsSchema,
293
+ type MaterializedAuthorityNatsGrant,
294
+ MaterializedAuthorityNatsGrantSchema,
295
+ type MaterializedAuthorityNatsGrantSource,
296
+ MaterializedAuthorityNatsGrantSourceSchema,
297
+ type MaterializedAuthoritySurfaceGrant,
298
+ MaterializedAuthoritySurfaceGrantSchema,
276
299
  OpenObjectSchema,
277
300
  type ParticipantKind,
278
301
  ParticipantKindSchema,
@@ -191,30 +191,51 @@ export type DeploymentAuthorityResource = StaticDecode<
191
191
  typeof DeploymentAuthorityResourceSchema
192
192
  >;
193
193
 
194
- export const DeploymentAuthorityNeedSchema = Type.Union([
195
- Type.Object({
196
- kind: Type.Literal("contract"),
197
- contractId: Type.String({ minLength: 1 }),
198
- required: Type.Boolean(),
199
- }),
200
- Type.Object({
201
- kind: Type.Literal("surface"),
202
- surface: DeploymentAuthoritySurfaceSchema,
203
- required: Type.Boolean(),
204
- }),
205
- Type.Object({
206
- kind: Type.Literal("capability"),
207
- capability: DeploymentAuthorityCapabilitySchema,
208
- required: Type.Boolean(),
209
- }),
210
- Type.Object({
211
- kind: Type.Literal("resource"),
212
- resource: DeploymentAuthorityResourceSchema,
213
- required: Type.Boolean(),
214
- }),
215
- ]);
216
- export type DeploymentAuthorityNeed = StaticDecode<
217
- typeof DeploymentAuthorityNeedSchema
194
+ export const DeploymentAuthorityContractNeedSchema = Type.Object({
195
+ contractId: Type.String({ minLength: 1 }),
196
+ required: Type.Boolean(),
197
+ });
198
+ export type DeploymentAuthorityContractNeed = StaticDecode<
199
+ typeof DeploymentAuthorityContractNeedSchema
200
+ >;
201
+
202
+ export const DeploymentAuthoritySurfaceNeedSchema = Type.Object({
203
+ contractId: Type.String({ minLength: 1 }),
204
+ kind: DeploymentAuthoritySurfaceKindSchema,
205
+ name: Type.String({ minLength: 1 }),
206
+ action: Type.Optional(DeploymentAuthoritySurfaceActionSchema),
207
+ required: Type.Boolean(),
208
+ });
209
+ export type DeploymentAuthoritySurfaceNeed = StaticDecode<
210
+ typeof DeploymentAuthoritySurfaceNeedSchema
211
+ >;
212
+
213
+ export const DeploymentAuthorityCapabilityNeedSchema = Type.Object({
214
+ capability: DeploymentAuthorityCapabilitySchema,
215
+ required: Type.Boolean(),
216
+ });
217
+ export type DeploymentAuthorityCapabilityNeed = StaticDecode<
218
+ typeof DeploymentAuthorityCapabilityNeedSchema
219
+ >;
220
+
221
+ export const DeploymentAuthorityResourceNeedSchema = Type.Object({
222
+ kind: DeploymentAuthorityResourceKindSchema,
223
+ alias: Type.String({ minLength: 1 }),
224
+ required: Type.Boolean(),
225
+ definition: Type.Optional(OpenObjectSchema),
226
+ });
227
+ export type DeploymentAuthorityResourceNeed = StaticDecode<
228
+ typeof DeploymentAuthorityResourceNeedSchema
229
+ >;
230
+
231
+ export const DeploymentAuthorityNeedsSchema = Type.Object({
232
+ contracts: Type.Array(DeploymentAuthorityContractNeedSchema),
233
+ surfaces: Type.Array(DeploymentAuthoritySurfaceNeedSchema),
234
+ capabilities: Type.Array(DeploymentAuthorityCapabilityNeedSchema),
235
+ resources: Type.Array(DeploymentAuthorityResourceNeedSchema),
236
+ });
237
+ export type DeploymentAuthorityNeeds = StaticDecode<
238
+ typeof DeploymentAuthorityNeedsSchema
218
239
  >;
219
240
 
220
241
  export const DeploymentAuthoritySchema = Type.Object({
@@ -222,7 +243,7 @@ export const DeploymentAuthoritySchema = Type.Object({
222
243
  kind: DeploymentAuthorityKindSchema,
223
244
  disabled: Type.Boolean(),
224
245
  desiredState: Type.Object({
225
- needs: Type.Array(DeploymentAuthorityNeedSchema),
246
+ needs: DeploymentAuthorityNeedsSchema,
226
247
  capabilities: Type.Array(DeploymentAuthorityCapabilitySchema),
227
248
  resources: Type.Array(DeploymentAuthorityResourceSchema),
228
249
  surfaces: Type.Array(DeploymentAuthoritySurfaceSchema),
@@ -241,7 +262,7 @@ export const DeploymentAuthorityProposalSchema = Type.Object({
241
262
  contractId: Type.String({ minLength: 1 }),
242
263
  contractDigest: DigestSchema,
243
264
  contract: Type.Optional(OpenObjectSchema),
244
- requestedNeeds: Type.Array(DeploymentAuthorityNeedSchema),
265
+ requestedNeeds: DeploymentAuthorityNeedsSchema,
245
266
  providedSurfaces: Type.Array(DeploymentAuthoritySurfaceSchema),
246
267
  summary: Type.Optional(OpenObjectSchema),
247
268
  });
@@ -263,17 +284,21 @@ export type DeploymentResourceBinding = StaticDecode<
263
284
  >;
264
285
 
265
286
  export const MaterializedAuthoritySurfaceGrantSchema = Type.Object({
266
- kind: Type.Literal("surface"),
267
287
  contractId: Type.String({ minLength: 1 }),
268
288
  surfaceKind: DeploymentAuthoritySurfaceKindSchema,
269
289
  name: Type.String({ minLength: 1 }),
270
290
  action: Type.Optional(DeploymentAuthoritySurfaceActionSchema),
271
291
  });
292
+ export type MaterializedAuthoritySurfaceGrant = StaticDecode<
293
+ typeof MaterializedAuthoritySurfaceGrantSchema
294
+ >;
272
295
 
273
296
  export const MaterializedAuthorityCapabilityGrantSchema = Type.Object({
274
- kind: Type.Literal("capability"),
275
297
  capability: DeploymentAuthorityCapabilitySchema,
276
298
  });
299
+ export type MaterializedAuthorityCapabilityGrant = StaticDecode<
300
+ typeof MaterializedAuthorityCapabilityGrantSchema
301
+ >;
277
302
 
278
303
  export const MaterializedAuthorityNatsGrantSourceSchema = Type.Union([
279
304
  Type.Literal("owned-surface"),
@@ -282,9 +307,11 @@ export const MaterializedAuthorityNatsGrantSourceSchema = Type.Union([
282
307
  Type.Literal("platform-service"),
283
308
  Type.Literal("transfer"),
284
309
  ]);
310
+ export type MaterializedAuthorityNatsGrantSource = StaticDecode<
311
+ typeof MaterializedAuthorityNatsGrantSourceSchema
312
+ >;
285
313
 
286
314
  export const MaterializedAuthorityNatsGrantSchema = Type.Object({
287
- kind: Type.Literal("nats"),
288
315
  direction: Type.Union([Type.Literal("publish"), Type.Literal("subscribe")]),
289
316
  subject: Type.String({ minLength: 1 }),
290
317
  surface: Type.Optional(Type.Object({
@@ -296,16 +323,24 @@ export const MaterializedAuthorityNatsGrantSchema = Type.Object({
296
323
  requiredCapabilities: Type.Array(Type.String({ minLength: 1 })),
297
324
  grantSource: MaterializedAuthorityNatsGrantSourceSchema,
298
325
  });
326
+ export type MaterializedAuthorityNatsGrant = StaticDecode<
327
+ typeof MaterializedAuthorityNatsGrantSchema
328
+ >;
299
329
 
300
- export const MaterializedAuthorityGrantSchema = Type.Union([
301
- MaterializedAuthorityCapabilityGrantSchema,
302
- MaterializedAuthoritySurfaceGrantSchema,
303
- MaterializedAuthorityNatsGrantSchema,
304
- ]);
305
- export type MaterializedAuthorityGrant = StaticDecode<
306
- typeof MaterializedAuthorityGrantSchema
330
+ export const MaterializedAuthorityGrantsSchema = Type.Object({
331
+ capabilities: Type.Array(MaterializedAuthorityCapabilityGrantSchema),
332
+ surfaces: Type.Array(MaterializedAuthoritySurfaceGrantSchema),
333
+ nats: Type.Array(MaterializedAuthorityNatsGrantSchema),
334
+ });
335
+ export type MaterializedAuthorityGrants = StaticDecode<
336
+ typeof MaterializedAuthorityGrantsSchema
307
337
  >;
308
338
 
339
+ export type MaterializedAuthorityGrant =
340
+ | MaterializedAuthorityCapabilityGrant
341
+ | MaterializedAuthoritySurfaceGrant
342
+ | MaterializedAuthorityNatsGrant;
343
+
309
344
  export const DeploymentAuthorityMaterializationSchema = Type.Object({
310
345
  deploymentId: Type.String({ minLength: 1 }),
311
346
  desiredVersion: Type.String({ minLength: 1 }),
@@ -315,7 +350,7 @@ export const DeploymentAuthorityMaterializationSchema = Type.Object({
315
350
  Type.Literal("failed"),
316
351
  ]),
317
352
  resourceBindings: Type.Array(DeploymentResourceBindingSchema),
318
- grants: Type.Array(MaterializedAuthorityGrantSchema),
353
+ grants: MaterializedAuthorityGrantsSchema,
319
354
  reconciledAt: Type.Union([IsoDateStringSchema, Type.Null()]),
320
355
  error: Type.Optional(Type.String({ minLength: 1 })),
321
356
  });
@@ -1180,6 +1215,7 @@ export const PortalFlowStateSchema = Type.Union([
1180
1215
  }),
1181
1216
  Type.Object({
1182
1217
  status: Type.Literal("expired"),
1218
+ returnLocation: Type.Optional(Type.String({ minLength: 1 })),
1183
1219
  }),
1184
1220
  ]);
1185
1221
  export type PortalFlowState = StaticDecode<typeof PortalFlowStateSchema>;
package/src/browser.ts CHANGED
@@ -6,6 +6,7 @@ import "./_dnt.polyfills.js";
6
6
 
7
7
  export {
8
8
  bindFlow,
9
+ classifyBrowserAuthError,
9
10
  clearSessionKey,
10
11
  createAuth,
11
12
  createRpcProof,
@@ -14,12 +15,15 @@ export {
14
15
  getPublicSessionKey,
15
16
  hasSessionKey,
16
17
  isBindSuccessResponse,
18
+ isRecoverableBrowserAuthError,
17
19
  loadSessionKey,
18
20
  signBytes,
19
21
  } from "./auth.js";
20
22
  export type {
21
23
  BindResponse,
22
24
  BindSuccessResponse,
25
+ BrowserAuthRecoveryClassification,
26
+ BrowserAuthRecoveryKind,
23
27
  NatsConnectOptions,
24
28
  SessionKeyHandle,
25
29
  } from "./auth.js";
@@ -1353,7 +1353,7 @@ async function resolveAuthRequired<
1353
1353
 
1354
1354
  if (isBrowserRuntime()) {
1355
1355
  globalThis.location.href = loginUrl;
1356
- throw new Error("Redirecting to Trellis login");
1356
+ throw new ClientAuthHandledError();
1357
1357
  }
1358
1358
 
1359
1359
  throw new Error(