@qoniai/qoni 0.7.0 → 0.8.0

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.
package/dist/index.d.cts CHANGED
@@ -138,11 +138,16 @@ interface UserRef {
138
138
  interface DelegateTokenBaseInput {
139
139
  /** Audit label for this delegation. Defaults to "sdk". */
140
140
  agent?: string;
141
+ /** Compatibility alias for agent. Supplying conflicting labels is an error. */
142
+ agentKey?: string;
141
143
  /** Fine-grained scopes. May be combined with `products`. */
142
144
  scopes?: readonly string[];
143
145
  /** Per-product authorization sugar — expands to the product's scope set. */
144
146
  products?: readonly QoniProductName[];
147
+ /** Integer seconds (60–86400), or a whole-number duration such as "15m". */
145
148
  expiresIn?: string | number;
149
+ /** Not supported: the delegation service does not enforce a deny policy. */
150
+ deniedScopes?: never;
146
151
  idempotencyKey?: string;
147
152
  }
148
153
  /**
@@ -156,11 +161,11 @@ type DelegateTokenSilentInput = DelegateTokenBaseInput & {
156
161
  state?: string;
157
162
  } & ({
158
163
  user: UserRef;
159
- /** @deprecated Use user. */
164
+ /** Alternative to user.id. A nonempty userId takes precedence. */
160
165
  userId?: string;
161
166
  } | {
162
167
  user?: UserRef;
163
- /** @deprecated Use user. */
168
+ /** Alternative to user.id. A nonempty userId takes precedence. */
164
169
  userId: string;
165
170
  });
166
171
  interface DelegateTokenInteractiveInput extends DelegateTokenBaseInput {
@@ -168,7 +173,7 @@ interface DelegateTokenInteractiveInput extends DelegateTokenBaseInput {
168
173
  redirectUri: string;
169
174
  state: string;
170
175
  user?: UserRef;
171
- /** @deprecated Use user. */
176
+ /** Alternative to user.id; optional for interactive authorization. */
172
177
  userId?: string;
173
178
  }
174
179
  type DelegateTokenInput = DelegateTokenSilentInput | DelegateTokenInteractiveInput;
@@ -1074,7 +1079,13 @@ type GenAuthUserDeleteBatchInput = GenAuthAdminTokenOverrideInput & {
1074
1079
  };
1075
1080
  declare function createGenAuthNamespace(transport: QoniTransport, options?: {
1076
1081
  adminTokenFor?: GenAuthAdminTokenProvider;
1082
+ delegateTokenFor?: (input: DelegateTokenInput) => Promise<QoniResponse<DelegateAgentResponse>>;
1077
1083
  }): {
1084
+ delegateAgent: {
1085
+ (input: DelegateTokenSilentInput): Promise<DelegateTokenSilentResponse>;
1086
+ (input: DelegateTokenInteractiveInput): Promise<InteractiveDelegationResponse>;
1087
+ (input: DelegateTokenInput): Promise<DelegateAgentResponse>;
1088
+ };
1078
1089
  userInfo: <T = unknown>(input: GenAuthAccessTokenInput) => Promise<QoniResponse<T>>;
1079
1090
  jwks: <T = unknown>() => Promise<QoniResponse<T>>;
1080
1091
  discovery: <T = unknown>() => Promise<QoniResponse<T>>;
package/dist/index.d.ts CHANGED
@@ -138,11 +138,16 @@ interface UserRef {
138
138
  interface DelegateTokenBaseInput {
139
139
  /** Audit label for this delegation. Defaults to "sdk". */
140
140
  agent?: string;
141
+ /** Compatibility alias for agent. Supplying conflicting labels is an error. */
142
+ agentKey?: string;
141
143
  /** Fine-grained scopes. May be combined with `products`. */
142
144
  scopes?: readonly string[];
143
145
  /** Per-product authorization sugar — expands to the product's scope set. */
144
146
  products?: readonly QoniProductName[];
147
+ /** Integer seconds (60–86400), or a whole-number duration such as "15m". */
145
148
  expiresIn?: string | number;
149
+ /** Not supported: the delegation service does not enforce a deny policy. */
150
+ deniedScopes?: never;
146
151
  idempotencyKey?: string;
147
152
  }
148
153
  /**
@@ -156,11 +161,11 @@ type DelegateTokenSilentInput = DelegateTokenBaseInput & {
156
161
  state?: string;
157
162
  } & ({
158
163
  user: UserRef;
159
- /** @deprecated Use user. */
164
+ /** Alternative to user.id. A nonempty userId takes precedence. */
160
165
  userId?: string;
161
166
  } | {
162
167
  user?: UserRef;
163
- /** @deprecated Use user. */
168
+ /** Alternative to user.id. A nonempty userId takes precedence. */
164
169
  userId: string;
165
170
  });
166
171
  interface DelegateTokenInteractiveInput extends DelegateTokenBaseInput {
@@ -168,7 +173,7 @@ interface DelegateTokenInteractiveInput extends DelegateTokenBaseInput {
168
173
  redirectUri: string;
169
174
  state: string;
170
175
  user?: UserRef;
171
- /** @deprecated Use user. */
176
+ /** Alternative to user.id; optional for interactive authorization. */
172
177
  userId?: string;
173
178
  }
174
179
  type DelegateTokenInput = DelegateTokenSilentInput | DelegateTokenInteractiveInput;
@@ -1074,7 +1079,13 @@ type GenAuthUserDeleteBatchInput = GenAuthAdminTokenOverrideInput & {
1074
1079
  };
1075
1080
  declare function createGenAuthNamespace(transport: QoniTransport, options?: {
1076
1081
  adminTokenFor?: GenAuthAdminTokenProvider;
1082
+ delegateTokenFor?: (input: DelegateTokenInput) => Promise<QoniResponse<DelegateAgentResponse>>;
1077
1083
  }): {
1084
+ delegateAgent: {
1085
+ (input: DelegateTokenSilentInput): Promise<DelegateTokenSilentResponse>;
1086
+ (input: DelegateTokenInteractiveInput): Promise<InteractiveDelegationResponse>;
1087
+ (input: DelegateTokenInput): Promise<DelegateAgentResponse>;
1088
+ };
1078
1089
  userInfo: <T = unknown>(input: GenAuthAccessTokenInput) => Promise<QoniResponse<T>>;
1079
1090
  jwks: <T = unknown>() => Promise<QoniResponse<T>>;
1080
1091
  discovery: <T = unknown>() => Promise<QoniResponse<T>>;
package/dist/index.js CHANGED
@@ -1564,7 +1564,14 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
1564
1564
 
1565
1565
  // src/genauth.ts
1566
1566
  function createGenAuthNamespace(transport, options = {}) {
1567
+ async function delegateAgent(input) {
1568
+ if (!options.delegateTokenFor) {
1569
+ throw new QoniValidationError("GenAuth delegateAgent requires a Qoni delegation provider");
1570
+ }
1571
+ return (await options.delegateTokenFor(input)).data;
1572
+ }
1567
1573
  return {
1574
+ delegateAgent,
1568
1575
  userInfo: (input) => transport.genauthJson("GET", "/oidc/me", input.accessToken),
1569
1576
  jwks: () => transport.genauthJson("GET", "/oidc/.well-known/jwks.json"),
1570
1577
  discovery: () => transport.genauthJson("GET", "/oidc/.well-known/openid-configuration"),
@@ -2200,7 +2207,8 @@ var Qoni = class {
2200
2207
  webAgentSSE: (...args) => this.webAgentSSE(...args)
2201
2208
  };
2202
2209
  this.genauth = createGenAuthNamespace(transport, {
2203
- adminTokenFor: () => this.genauthAdminTokenFor()
2210
+ adminTokenFor: () => this.genauthAdminTokenFor(),
2211
+ delegateTokenFor: (input) => this.delegateToken(input)
2204
2212
  });
2205
2213
  this.qoni = createQoniNamespace(
2206
2214
  transport,
@@ -2913,19 +2921,43 @@ function toRecord(value) {
2913
2921
  }
2914
2922
  function normalizeDelegateTokenInput(input) {
2915
2923
  const body = toRecord(input);
2924
+ if (input.deniedScopes !== void 0) {
2925
+ throw new QoniValidationError(
2926
+ "delegateToken: deniedScopes is not supported by the delegation service. Do not rely on this field to prevent mailbox or browser actions."
2927
+ );
2928
+ }
2929
+ if (input.agent !== void 0 && input.agentKey !== void 0 && input.agent !== input.agentKey) {
2930
+ throw new QoniValidationError("delegateToken: agent and agentKey must identify the same Agent");
2931
+ }
2916
2932
  if (!body.userId && isJsonObject(input.user) && typeof input.user.id === "string") {
2917
2933
  body.userId = input.user.id;
2918
2934
  }
2919
2935
  if (input.mode !== "interactive" && !body.userId) {
2920
2936
  throw new QoniValidationError(
2921
- 'delegateToken: silent mode requires a user \u2014 pass `user: { id: <genauthUserId> }` (or the deprecated top-level `userId`). Interactive mode (`mode: "interactive"`) does not need one.'
2937
+ 'delegateToken: silent mode requires a user \u2014 pass `user: { id: <genauthUserId> }` (or the top-level `userId`). Interactive mode (`mode: "interactive"`) does not need one.'
2922
2938
  );
2923
2939
  }
2924
- body.agent = input.agent ?? "sdk";
2940
+ body.agent = input.agent ?? input.agentKey ?? "sdk";
2941
+ delete body.agentKey;
2942
+ if (input.expiresIn !== void 0) body.expiresIn = delegationDurationSeconds(input.expiresIn);
2925
2943
  body.scopes = resolveDelegationScopes(input.scopes, input.products);
2926
2944
  delete body.products;
2927
2945
  return body;
2928
2946
  }
2947
+ function delegationDurationSeconds(value) {
2948
+ let seconds = value;
2949
+ if (typeof value === "string") {
2950
+ const duration = /^(\d+)(s|m|h|d)?$/.exec(value);
2951
+ const units = { s: 1, m: 60, h: 3600, d: 86400 };
2952
+ seconds = duration ? Number(duration[1]) * units[duration[2] ?? "s"] : NaN;
2953
+ }
2954
+ if (typeof seconds !== "number" || !Number.isInteger(seconds) || seconds < 60 || seconds > 86400) {
2955
+ throw new QoniValidationError(
2956
+ 'delegateToken: expiresIn must be 60\u201386400 integer seconds or a whole-number duration such as "15m"'
2957
+ );
2958
+ }
2959
+ return seconds;
2960
+ }
2929
2961
  function resolveDelegationScopes(scopes, products) {
2930
2962
  const resolved = /* @__PURE__ */ new Set();
2931
2963
  for (const product of products ?? []) {
@@ -2949,6 +2981,11 @@ function resolveDelegationScopes(scopes, products) {
2949
2981
  return [...resolved];
2950
2982
  }
2951
2983
  function validateScopeFormat(scope) {
2984
+ if (scope.startsWith("mailbox.") || scope === "web.search" || scope === "web.extract") {
2985
+ throw new QoniValidationError(
2986
+ `delegateToken: legacy action scope "${scope}" has no enforced mapping in this delegation service. It will not be expanded into broader product permissions.`
2987
+ );
2988
+ }
2952
2989
  if (KNOWN_SCOPES.includes(scope)) return;
2953
2990
  const prefixed = KNOWN_SCOPES.find((known) => known.endsWith(`.${scope}`) || known === `webagent.${scope}`);
2954
2991
  if (prefixed) {