@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/CHANGELOG.md +25 -2
- package/README.md +30 -2
- package/README.zh-CN.md +27 -2
- package/dist/index.cjs +40 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +40 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
/**
|
|
164
|
+
/** Alternative to user.id. A nonempty userId takes precedence. */
|
|
160
165
|
userId?: string;
|
|
161
166
|
} | {
|
|
162
167
|
user?: UserRef;
|
|
163
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
164
|
+
/** Alternative to user.id. A nonempty userId takes precedence. */
|
|
160
165
|
userId?: string;
|
|
161
166
|
} | {
|
|
162
167
|
user?: UserRef;
|
|
163
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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) {
|