@djangocfg/api 2.1.582 → 2.1.584
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/README.md +18 -0
- package/dist/OAuthTokenResponse-WEDM6IP5.mjs +19 -0
- package/dist/OAuthTokenResponse-WEDM6IP5.mjs.map +1 -0
- package/dist/OTPVerifyResponse-P5LDNM7H.mjs +20 -0
- package/dist/OTPVerifyResponse-P5LDNM7H.mjs.map +1 -0
- package/dist/PasswordLoginResponse-33B576NG.mjs +20 -0
- package/dist/PasswordLoginResponse-33B576NG.mjs.map +1 -0
- package/dist/auth.cjs +279 -103
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +14 -41
- package/dist/auth.d.ts +14 -41
- package/dist/auth.mjs +279 -101
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +242 -54
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +13 -41
- package/dist/clients.d.ts +13 -41
- package/dist/clients.mjs +242 -54
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +242 -54
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +242 -54
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +247 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -63
- package/dist/index.d.ts +90 -63
- package/dist/index.mjs +247 -54
- package/dist/index.mjs.map +1 -1
- package/dist/storage.cjs +268 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.cts +84 -0
- package/dist/storage.d.ts +84 -0
- package/dist/storage.mjs +247 -0
- package/dist/storage.mjs.map +1 -0
- package/package.json +8 -2
- package/src/__tests__/noStorageLiterals.test.ts +27 -0
- package/src/__tests__/storage.test.ts +134 -0
- package/src/__tests__/storageSsr.test.ts +14 -0
- package/src/_api/generated/_cfg_accounts/openapi.json +1 -28
- package/src/_api/generated/_cfg_accounts/schemas/OAuthCallbackRequestRequest.ts +0 -1
- package/src/_api/generated/_cfg_accounts/schemas/OAuthTokenResponse.ts +0 -1
- package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyRequest.ts +0 -1
- package/src/_api/generated/_cfg_accounts/schemas/OTPVerifyResponse.ts +0 -1
- package/src/_api/generated/_cfg_accounts/schemas/PasswordLoginRequest.ts +0 -1
- package/src/_api/generated/_cfg_accounts/schemas/PasswordLoginResponse.ts +0 -1
- package/src/_api/generated/_cfg_totp/openapi.json +0 -5
- package/src/_api/generated/_cfg_totp/schemas/VerifyResponse.ts +0 -1
- package/src/_api/generated/helpers/auth.ts +28 -65
- package/src/_api/generated/helpers/index.ts +12 -0
- package/src/_api/generated/helpers/keys.ts +327 -0
- package/src/_api/generated/openapi.json +1 -33
- package/src/_api/generated/types.gen.ts +4 -59
- package/src/auth/__tests__/authRedirect.test.ts +1 -1
- package/src/auth/__tests__/passwordLogin.test.tsx +1 -3
- package/src/auth/__tests__/profileCache.test.ts +1 -1
- package/src/auth/context/AccountsContext.tsx +6 -8
- package/src/auth/context/AuthContext.tsx +3 -5
- package/src/auth/context/types.ts +1 -1
- package/src/auth/hooks/useAuthForm.ts +3 -5
- package/src/auth/hooks/useAuthFormState.ts +0 -4
- package/src/auth/hooks/useAuthRedirect.ts +3 -2
- package/src/auth/hooks/useGithubAuth.ts +14 -16
- package/src/auth/hooks/useLocalStorage.ts +7 -7
- package/src/auth/hooks/useProfileCache.ts +2 -1
- package/src/auth/hooks/useSessionStorage.ts +7 -7
- package/src/auth/hooks/useTwoFactorVerify.ts +2 -2
- package/src/auth/types/form.ts +0 -5
- package/src/storage.ts +21 -0
package/dist/index.mjs
CHANGED
|
@@ -78,77 +78,263 @@ var NetworkError = class extends Error {
|
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
// src/_api/generated/helpers/
|
|
82
|
-
var
|
|
83
|
-
var
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
// src/_api/generated/helpers/keys.ts
|
|
82
|
+
var k = /* @__PURE__ */ __name((key, store, personal) => ({ key, store, personal }), "k");
|
|
83
|
+
var f = /* @__PURE__ */ __name((prefix, store, personal) => ({ prefix, store, personal }), "f");
|
|
84
|
+
var CFG_KEYS = {
|
|
85
|
+
accessToken: k("cfg.auth.access_token", "local", true),
|
|
86
|
+
refreshToken: k("cfg.auth.refresh_token", "local", true),
|
|
87
|
+
apiKey: k("cfg.auth.api_key", "local", true),
|
|
88
|
+
email: k("cfg.auth.email", "local", false),
|
|
89
|
+
emailTs: k("cfg.auth.email_ts", "local", false),
|
|
90
|
+
redirectUrl: k("cfg.auth.redirect_url", "session", true),
|
|
91
|
+
redirectUrlTs: k("cfg.auth.redirect_url_ts", "session", true),
|
|
92
|
+
oauthState: k("cfg.auth.oauth_state", "session", true),
|
|
93
|
+
oauthProvider: k("cfg.auth.oauth_provider", "session", true),
|
|
94
|
+
profileCache: k("cfg.profile.cache", "local", true),
|
|
95
|
+
monitorSessionId: k("cfg.monitor.session_id", "local", false),
|
|
96
|
+
monitorSessionCookie: k("cfg.monitor.session_id", "cookie", false),
|
|
97
|
+
monitorDebug: k("cfg.monitor.debug", "local", false),
|
|
98
|
+
uiTabId: k("cfg.ui.tab_id", "session", false),
|
|
99
|
+
uiRouterSerial: k("cfg.ui.router_serial", "session", false),
|
|
100
|
+
uiState: k("cfg.ui.state", "local", false),
|
|
101
|
+
chatSpeechLanguage: k("cfg.chat.speech_language", "local", false),
|
|
102
|
+
chatRecentTools: k("cfg.chat.recent_tools", "local", false),
|
|
103
|
+
formsSpeechDebug: k("cfg.forms.speech_debug", "local", false),
|
|
104
|
+
formsSpeechPrefs: k("cfg.forms.speech_prefs", "local", false),
|
|
105
|
+
agentPlaygroundApiKeyId: k("cfg.agent.playground_api_key_id", "session", true),
|
|
106
|
+
agentPlaygroundBearer: k("cfg.agent.playground_bearer", "session", true),
|
|
107
|
+
agentEndpointDoc: k("cfg.agent.endpoint_doc", "session", false)
|
|
108
|
+
};
|
|
109
|
+
var CFG_FAMILIES = {
|
|
110
|
+
chatDraft: f("cfg.chat.draft.", "session", true),
|
|
111
|
+
dataTree: f("cfg.data.tree.", "local", false),
|
|
112
|
+
agentEndpointDraft: f("cfg.agent.endpoint_draft.", "local", false)
|
|
113
|
+
};
|
|
114
|
+
var OWNER_RE = /^[a-z][a-z0-9_]*$/;
|
|
115
|
+
function normalizeSegment(raw) {
|
|
116
|
+
return raw.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
|
|
117
|
+
}
|
|
118
|
+
__name(normalizeSegment, "normalizeSegment");
|
|
119
|
+
var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
|
|
120
|
+
function nativeStorage(store) {
|
|
121
|
+
if (!hasWindow()) return null;
|
|
122
|
+
try {
|
|
123
|
+
return store === "session" ? window.sessionStorage : window.localStorage;
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
__name(nativeStorage, "nativeStorage");
|
|
129
|
+
function cookieNames() {
|
|
130
|
+
if (!hasWindow()) return [];
|
|
131
|
+
try {
|
|
132
|
+
return document.cookie.split("; ").map((part) => decodeURIComponent(part.split("=")[0] ?? "")).filter(Boolean);
|
|
133
|
+
} catch {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
__name(cookieNames, "cookieNames");
|
|
138
|
+
function createStorage(owner, options = {}) {
|
|
139
|
+
if (!OWNER_RE.test(owner)) {
|
|
140
|
+
throw new TypeError(`createStorage: owner "${owner}" must match ${OWNER_RE}`);
|
|
141
|
+
}
|
|
142
|
+
const prefix = `${owner}.`;
|
|
143
|
+
const pattern = owner === "cfg" ? /^cfg(\.[a-z0-9_]+)+$/ : new RegExp(`^(cfg|${owner})(\\.[a-z0-9_]+)+$`);
|
|
144
|
+
const cookieMaxAge = options.cookieMaxAgeSeconds ?? 60 * 60 * 24 * 30;
|
|
145
|
+
const specs = Object.values(options.keys ?? {});
|
|
146
|
+
const families = Object.values(options.families ?? {});
|
|
147
|
+
const specByKey = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const spec of specs) specByKey.set(spec.key, [...specByKey.get(spec.key) ?? [], spec]);
|
|
149
|
+
const memory = {
|
|
150
|
+
local: /* @__PURE__ */ new Map(),
|
|
151
|
+
session: /* @__PURE__ */ new Map(),
|
|
152
|
+
cookie: /* @__PURE__ */ new Map()
|
|
153
|
+
};
|
|
154
|
+
const isValid = /* @__PURE__ */ __name((key) => pattern.test(key), "isValid");
|
|
155
|
+
const assertValid = /* @__PURE__ */ __name((key) => {
|
|
156
|
+
if (!isValid(key)) throw new TypeError(`storage key "${key}" must match ${pattern}`);
|
|
157
|
+
}, "assertValid");
|
|
158
|
+
const assertOwned = /* @__PURE__ */ __name((key) => {
|
|
159
|
+
assertValid(key);
|
|
160
|
+
if (!key.startsWith(prefix)) throw new TypeError(`storage key "${key}" is not owned by "${owner}"`);
|
|
161
|
+
}, "assertOwned");
|
|
162
|
+
function rawGet(store, key) {
|
|
163
|
+
if (!hasWindow()) return null;
|
|
164
|
+
if (store === "cookie") {
|
|
165
|
+
try {
|
|
166
|
+
const escaped = encodeURIComponent(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
167
|
+
const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${escaped}=([^;]*)`));
|
|
168
|
+
if (match?.[1] !== void 0) return decodeURIComponent(match[1]);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
return memory.cookie.get(key) ?? null;
|
|
93
172
|
}
|
|
94
|
-
},
|
|
95
|
-
set(key, value) {
|
|
96
|
-
if (!isBrowser) return;
|
|
97
173
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
174
|
+
const value = nativeStorage(store)?.getItem(key) ?? null;
|
|
175
|
+
if (value !== null) return value;
|
|
100
176
|
} catch {
|
|
101
177
|
}
|
|
178
|
+
return memory[store].get(key) ?? null;
|
|
102
179
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (!isBrowser) return null;
|
|
180
|
+
__name(rawGet, "rawGet");
|
|
181
|
+
function rawSet(store, key, value) {
|
|
182
|
+
if (!hasWindow()) return false;
|
|
107
183
|
try {
|
|
108
|
-
|
|
184
|
+
if (store === "cookie") {
|
|
185
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
186
|
+
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax${secure}`;
|
|
187
|
+
if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
|
|
188
|
+
memory.cookie.set(key, value);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const native = nativeStorage(store);
|
|
192
|
+
if (!native) throw new Error("storage unavailable");
|
|
193
|
+
native.setItem(key, value);
|
|
194
|
+
memory[store].delete(key);
|
|
195
|
+
return true;
|
|
109
196
|
} catch {
|
|
110
|
-
|
|
197
|
+
memory[store].set(key, value);
|
|
198
|
+
return false;
|
|
111
199
|
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
200
|
+
}
|
|
201
|
+
__name(rawSet, "rawSet");
|
|
202
|
+
function rawRemove(store, key) {
|
|
203
|
+
memory[store].delete(key);
|
|
204
|
+
if (!hasWindow()) return;
|
|
115
205
|
try {
|
|
116
|
-
if (
|
|
117
|
-
|
|
206
|
+
if (store === "cookie") {
|
|
207
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
208
|
+
document.cookie = `${encodeURIComponent(key)}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
|
|
209
|
+
} else {
|
|
210
|
+
nativeStorage(store)?.removeItem(key);
|
|
211
|
+
}
|
|
118
212
|
} catch {
|
|
119
213
|
}
|
|
120
214
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
215
|
+
__name(rawRemove, "rawRemove");
|
|
216
|
+
function resolveStore(key, store) {
|
|
217
|
+
return store ?? specByKey.get(key)?.[0]?.store ?? "local";
|
|
218
|
+
}
|
|
219
|
+
__name(resolveStore, "resolveStore");
|
|
220
|
+
function listKeys(store, startsWith) {
|
|
221
|
+
const out = [];
|
|
126
222
|
try {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
223
|
+
const native = nativeStorage(store);
|
|
224
|
+
if (native) {
|
|
225
|
+
for (let i = 0; i < native.length; i += 1) {
|
|
226
|
+
const name = native.key(i);
|
|
227
|
+
if (name?.startsWith(startsWith)) out.push(name);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
131
230
|
} catch {
|
|
132
|
-
return null;
|
|
133
231
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
232
|
+
for (const name of memory[store].keys()) if (name.startsWith(startsWith) && !out.includes(name)) out.push(name);
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
__name(listKeys, "listKeys");
|
|
236
|
+
const familyKey = /* @__PURE__ */ __name((family, id) => {
|
|
237
|
+
const part = normalizeSegment(id);
|
|
238
|
+
if (!part) throw new TypeError("storage family id is empty after normalisation");
|
|
239
|
+
return family.prefix + part;
|
|
240
|
+
}, "familyKey");
|
|
241
|
+
const api = {
|
|
242
|
+
owner,
|
|
243
|
+
key(...parts) {
|
|
244
|
+
const segments = parts.map(normalizeSegment);
|
|
245
|
+
if (segments.length === 0 || segments.some((segment) => !segment)) {
|
|
246
|
+
throw new TypeError(`storage key parts must be non-empty: ${JSON.stringify(parts)}`);
|
|
145
247
|
}
|
|
146
|
-
|
|
248
|
+
return `${prefix}${segments.join(".")}`;
|
|
249
|
+
},
|
|
250
|
+
familyKey,
|
|
251
|
+
isValid,
|
|
252
|
+
read(key, store) {
|
|
253
|
+
assertValid(key);
|
|
254
|
+
const target = resolveStore(key, store);
|
|
255
|
+
return rawGet(target, key);
|
|
256
|
+
},
|
|
257
|
+
write(key, value, store) {
|
|
258
|
+
assertOwned(key);
|
|
259
|
+
const target = resolveStore(key, store);
|
|
260
|
+
return rawSet(target, key, value);
|
|
261
|
+
},
|
|
262
|
+
remove(key, store) {
|
|
263
|
+
assertOwned(key);
|
|
264
|
+
const target = resolveStore(key, store);
|
|
265
|
+
rawRemove(target, key);
|
|
266
|
+
},
|
|
267
|
+
readJSON(key, store) {
|
|
268
|
+
const raw = api.read(key, store);
|
|
269
|
+
if (raw === null) return null;
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(raw);
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
writeJSON(key, value, store) {
|
|
277
|
+
return api.write(key, JSON.stringify(value), store);
|
|
278
|
+
},
|
|
279
|
+
keys(store = "local") {
|
|
280
|
+
return listKeys(store, prefix);
|
|
281
|
+
},
|
|
282
|
+
clearAll({ all = false } = {}) {
|
|
283
|
+
for (const spec of specs) {
|
|
284
|
+
if (!all && !spec.personal) continue;
|
|
285
|
+
rawRemove(spec.store, spec.key);
|
|
286
|
+
}
|
|
287
|
+
for (const family of families) {
|
|
288
|
+
if (!all && !family.personal) continue;
|
|
289
|
+
for (const name of listKeys(family.store, family.prefix)) rawRemove(family.store, name);
|
|
290
|
+
}
|
|
291
|
+
if (all) {
|
|
292
|
+
for (const store of ["local", "session"]) {
|
|
293
|
+
for (const name of listKeys(store, prefix)) rawRemove(store, name);
|
|
294
|
+
}
|
|
295
|
+
for (const name of cookieNames()) if (name.startsWith(prefix)) rawRemove("cookie", name);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
onChange(key, listener, store = "local") {
|
|
299
|
+
assertValid(key);
|
|
300
|
+
if (!hasWindow()) return () => {
|
|
301
|
+
};
|
|
302
|
+
const handler = /* @__PURE__ */ __name((event) => {
|
|
303
|
+
if (event.storageArea !== nativeStorage(store)) return;
|
|
304
|
+
if (event.key === null || event.key === key) listener(event.key === null ? null : event.newValue);
|
|
305
|
+
}, "handler");
|
|
306
|
+
window.addEventListener("storage", handler);
|
|
307
|
+
return () => window.removeEventListener("storage", handler);
|
|
147
308
|
}
|
|
148
|
-
}
|
|
309
|
+
};
|
|
310
|
+
return api;
|
|
311
|
+
}
|
|
312
|
+
__name(createStorage, "createStorage");
|
|
313
|
+
var cfgStorage = createStorage("cfg", {
|
|
314
|
+
keys: CFG_KEYS,
|
|
315
|
+
families: CFG_FAMILIES
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// src/_api/generated/helpers/auth.ts
|
|
319
|
+
var ACCESS_KEY = "cfg.auth.access_token";
|
|
320
|
+
var REFRESH_KEY = "cfg.auth.refresh_token";
|
|
321
|
+
var API_KEY_KEY = "cfg.auth.api_key";
|
|
322
|
+
var storage = true ? cfgStorage : createStorage2("cfg");
|
|
323
|
+
var isBrowser = typeof window !== "undefined";
|
|
324
|
+
var STORE_OF = {
|
|
325
|
+
localStorage: "local",
|
|
326
|
+
sessionStorage: "session",
|
|
327
|
+
cookie: "cookie"
|
|
149
328
|
};
|
|
150
329
|
function backendFor(mode) {
|
|
151
|
-
|
|
330
|
+
const store = STORE_OF[mode];
|
|
331
|
+
return {
|
|
332
|
+
get: /* @__PURE__ */ __name((key) => storage.read(key, store), "get"),
|
|
333
|
+
set(key, value) {
|
|
334
|
+
if (value === null) storage.remove(key, store);
|
|
335
|
+
else storage.write(key, value, store);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
152
338
|
}
|
|
153
339
|
__name(backendFor, "backendFor");
|
|
154
340
|
var _storageMode = "localStorage";
|
|
@@ -337,10 +523,12 @@ var auth = {
|
|
|
337
523
|
notifySessionChanged();
|
|
338
524
|
},
|
|
339
525
|
clearTokens() {
|
|
340
|
-
for (const
|
|
526
|
+
for (const mode of ["localStorage", "sessionStorage", "cookie"]) {
|
|
527
|
+
const backend = backendFor(mode);
|
|
341
528
|
backend.set(ACCESS_KEY, null);
|
|
342
529
|
backend.set(REFRESH_KEY, null);
|
|
343
530
|
}
|
|
531
|
+
storage.clearAll();
|
|
344
532
|
notifySessionChanged();
|
|
345
533
|
},
|
|
346
534
|
/** Session-aware: token PRESENT and not past its `exp` (or a live refresh
|
|
@@ -532,7 +720,7 @@ function dpopEnabled() {
|
|
|
532
720
|
}
|
|
533
721
|
}
|
|
534
722
|
__name(dpopEnabled, "dpopEnabled");
|
|
535
|
-
var _DPOP_DB = "cfg
|
|
723
|
+
var _DPOP_DB = "cfg.auth.dpop";
|
|
536
724
|
var _DPOP_STORE = "keys";
|
|
537
725
|
var _DPOP_KEY_ID = "dpop-ec-p256";
|
|
538
726
|
function _idbOpen() {
|
|
@@ -2582,6 +2770,8 @@ __name(applyRoleLogPolicy, "applyRoleLogPolicy");
|
|
|
2582
2770
|
export {
|
|
2583
2771
|
APIError,
|
|
2584
2772
|
APILogger,
|
|
2773
|
+
CFG_FAMILIES,
|
|
2774
|
+
CFG_KEYS,
|
|
2585
2775
|
CfgAccountsOtp as CfgAccounts,
|
|
2586
2776
|
API as CfgAccountsAPI,
|
|
2587
2777
|
CfgAccountsApi,
|
|
@@ -2603,6 +2793,8 @@ export {
|
|
|
2603
2793
|
CfgAccountsApi as api,
|
|
2604
2794
|
applyRoleLogPolicy,
|
|
2605
2795
|
auth,
|
|
2796
|
+
cfgStorage,
|
|
2797
|
+
createStorage,
|
|
2606
2798
|
defaultLogger,
|
|
2607
2799
|
dispatchValidationError,
|
|
2608
2800
|
dpopEnabled2 as dpopEnabled,
|
|
@@ -2612,6 +2804,7 @@ export {
|
|
|
2612
2804
|
isDev,
|
|
2613
2805
|
isProd,
|
|
2614
2806
|
isStaticBuild,
|
|
2807
|
+
normalizeSegment,
|
|
2615
2808
|
onLogLevelChange,
|
|
2616
2809
|
onValidationError,
|
|
2617
2810
|
setLogLevel
|