@h-ai/api-client 1.0.0-beta.2 → 1.0.0-beta.21
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.en.md +148 -89
- package/README.ja.md +147 -88
- package/README.md +147 -88
- package/dist/index.d.ts +27 -14
- package/dist/index.js +117 -24
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { z } from 'zod';
|
|
|
8
8
|
// src/api-client-main.ts
|
|
9
9
|
var logger = core.logger.child({ module: "api-client", scope: "auth" });
|
|
10
10
|
var HTTPONLY_COOKIE_SENTINEL = "__httponly_cookie__";
|
|
11
|
+
var cookieTokenStorages = /* @__PURE__ */ new WeakSet();
|
|
11
12
|
function createMemoryTokenStorage() {
|
|
12
13
|
let accessToken = null;
|
|
13
14
|
let refreshToken = null;
|
|
@@ -32,7 +33,7 @@ function createMemoryTokenStorage() {
|
|
|
32
33
|
}
|
|
33
34
|
function createHttpOnlyCookieTokenStorage() {
|
|
34
35
|
let memoryAccessToken = null;
|
|
35
|
-
|
|
36
|
+
const storage = {
|
|
36
37
|
async getAccessToken() {
|
|
37
38
|
return memoryAccessToken;
|
|
38
39
|
},
|
|
@@ -51,6 +52,8 @@ function createHttpOnlyCookieTokenStorage() {
|
|
|
51
52
|
memoryAccessToken = null;
|
|
52
53
|
}
|
|
53
54
|
};
|
|
55
|
+
cookieTokenStorages.add(storage);
|
|
56
|
+
return storage;
|
|
54
57
|
}
|
|
55
58
|
var RefreshTokenPayloadSchema = z.object({
|
|
56
59
|
accessToken: z.string().min(1),
|
|
@@ -63,6 +66,7 @@ function createTokenManager(storage, refreshEndpointUrl, fetchFn, onRefreshFaile
|
|
|
63
66
|
let refreshPromise = null;
|
|
64
67
|
let refreshController = null;
|
|
65
68
|
let generation = 0;
|
|
69
|
+
let disposed = false;
|
|
66
70
|
let sessionInvalid = false;
|
|
67
71
|
let storageMutation = Promise.resolve();
|
|
68
72
|
function mutateStorage(operation) {
|
|
@@ -91,6 +95,8 @@ function createTokenManager(storage, refreshEndpointUrl, fetchFn, onRefreshFaile
|
|
|
91
95
|
});
|
|
92
96
|
}
|
|
93
97
|
async function refresh() {
|
|
98
|
+
if (disposed)
|
|
99
|
+
return null;
|
|
94
100
|
if (refreshPromise)
|
|
95
101
|
return refreshPromise;
|
|
96
102
|
const controller = new AbortController();
|
|
@@ -153,7 +159,14 @@ function createTokenManager(storage, refreshEndpointUrl, fetchFn, onRefreshFaile
|
|
|
153
159
|
return null;
|
|
154
160
|
}
|
|
155
161
|
if (!useHttpOnlyCookie && !parsed.data.refreshToken) {
|
|
156
|
-
logger.warn("Token refresh returned invalid data", {
|
|
162
|
+
logger.warn("Token refresh returned invalid data", {
|
|
163
|
+
issues: [
|
|
164
|
+
{
|
|
165
|
+
path: ["refreshToken"],
|
|
166
|
+
message: "Required"
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
});
|
|
157
170
|
await failRefresh(refreshGeneration);
|
|
158
171
|
return null;
|
|
159
172
|
}
|
|
@@ -194,6 +207,13 @@ function createTokenManager(storage, refreshEndpointUrl, fetchFn, onRefreshFaile
|
|
|
194
207
|
}
|
|
195
208
|
return {
|
|
196
209
|
storage,
|
|
210
|
+
usesCookieCredentials: cookieTokenStorages.has(storage),
|
|
211
|
+
async dispose() {
|
|
212
|
+
disposed = true;
|
|
213
|
+
invalidateRefresh()?.abort();
|
|
214
|
+
callbacks.length = 0;
|
|
215
|
+
await storageMutation;
|
|
216
|
+
},
|
|
197
217
|
get generation() {
|
|
198
218
|
return generation;
|
|
199
219
|
},
|
|
@@ -321,7 +341,9 @@ function createIamAuthActions(client) {
|
|
|
321
341
|
if (event.persisted || sent)
|
|
322
342
|
return;
|
|
323
343
|
sent = true;
|
|
324
|
-
void client.iam.auth.closeSession({}, {
|
|
344
|
+
void client.iam.auth.closeSession({}, {
|
|
345
|
+
context: { keepalive: true, autoRefresh: false }
|
|
346
|
+
}).then((result) => {
|
|
325
347
|
if (!result.success)
|
|
326
348
|
core.logger.warn("Page exit session revocation failed", { errorCode: result.error.code });
|
|
327
349
|
}).catch(() => core.logger.warn("Page exit session request did not complete"));
|
|
@@ -341,7 +363,22 @@ function createIamAuthActions(client) {
|
|
|
341
363
|
publicConfig: () => client.iam.auth.publicConfig(),
|
|
342
364
|
createCaptcha: (input) => client.iam.auth.createCaptcha(input),
|
|
343
365
|
login: async (input) => {
|
|
344
|
-
const result = input.method === "ldap" ? await client.iam.auth.loginWithLdap({
|
|
366
|
+
const result = input.method === "ldap" ? await client.iam.auth.loginWithLdap({
|
|
367
|
+
username: input.identifier,
|
|
368
|
+
password: input.password,
|
|
369
|
+
captcha: input.captcha,
|
|
370
|
+
expectedUserId: input.expectedUserId
|
|
371
|
+
}) : input.method === "email" || input.method === "sms" ? await client.iam.auth.loginWithOtp({
|
|
372
|
+
identifier: input.identifier,
|
|
373
|
+
code: input.code ?? "",
|
|
374
|
+
captcha: input.captcha,
|
|
375
|
+
expectedUserId: input.expectedUserId
|
|
376
|
+
}) : await client.iam.auth.login({
|
|
377
|
+
identifier: input.identifier,
|
|
378
|
+
password: input.password,
|
|
379
|
+
captcha: input.captcha,
|
|
380
|
+
expectedUserId: input.expectedUserId
|
|
381
|
+
});
|
|
345
382
|
return save(result);
|
|
346
383
|
},
|
|
347
384
|
register: async (input) => save(await client.iam.auth.register(input)),
|
|
@@ -408,12 +445,13 @@ function createApiClient(contract) {
|
|
|
408
445
|
});
|
|
409
446
|
}
|
|
410
447
|
};
|
|
448
|
+
let closePromise = null;
|
|
411
449
|
let initPromise = null;
|
|
412
450
|
const lifecycle = {
|
|
413
451
|
async init(config) {
|
|
414
452
|
if (initPromise)
|
|
415
453
|
return initPromise;
|
|
416
|
-
if (state.rawClient) {
|
|
454
|
+
if (state.rawClient || closePromise) {
|
|
417
455
|
return err(
|
|
418
456
|
HaiApiClientError.CONFIG_ERROR,
|
|
419
457
|
apiClientM("apiClient_alreadyInitialized")
|
|
@@ -438,7 +476,11 @@ function createApiClient(contract) {
|
|
|
438
476
|
state.transport = void 0;
|
|
439
477
|
return err(
|
|
440
478
|
HaiApiClientError.CONFIG_ERROR,
|
|
441
|
-
apiClientM("apiClient_configError", {
|
|
479
|
+
apiClientM("apiClient_configError", {
|
|
480
|
+
params: {
|
|
481
|
+
error: String(error)
|
|
482
|
+
}
|
|
483
|
+
}),
|
|
442
484
|
error
|
|
443
485
|
);
|
|
444
486
|
}
|
|
@@ -450,13 +492,22 @@ function createApiClient(contract) {
|
|
|
450
492
|
}
|
|
451
493
|
},
|
|
452
494
|
async close() {
|
|
495
|
+
if (closePromise)
|
|
496
|
+
return closePromise;
|
|
453
497
|
clearIamPageExitBindings(auth);
|
|
454
498
|
state.observers.clear();
|
|
499
|
+
const manager = state.tokenManager;
|
|
455
500
|
state.transport?.destroy();
|
|
456
501
|
state.config = null;
|
|
457
502
|
state.rawClient = null;
|
|
458
503
|
state.tokenManager = void 0;
|
|
459
504
|
state.transport = void 0;
|
|
505
|
+
closePromise = manager?.dispose() ?? Promise.resolve();
|
|
506
|
+
try {
|
|
507
|
+
await closePromise;
|
|
508
|
+
} finally {
|
|
509
|
+
closePromise = null;
|
|
510
|
+
}
|
|
460
511
|
},
|
|
461
512
|
get config() {
|
|
462
513
|
return state.config ? core.sanitize.sanitizeSensitiveFields(state.config) : null;
|
|
@@ -512,10 +563,20 @@ function createProcedureProxy(state, path) {
|
|
|
512
563
|
});
|
|
513
564
|
}
|
|
514
565
|
async function callObservedProcedure(rawClient, path, args, observers) {
|
|
515
|
-
const record = {
|
|
566
|
+
const record = {
|
|
567
|
+
requestId: core.context.get().requestId ?? core.id.uuid(),
|
|
568
|
+
procedure: path.join("."),
|
|
569
|
+
timestamp: Date.now(),
|
|
570
|
+
duration: 0,
|
|
571
|
+
success: false,
|
|
572
|
+
attempts: 0
|
|
573
|
+
};
|
|
516
574
|
const provided = args[1] && typeof args[1] === "object" ? args[1] : {};
|
|
517
575
|
const providedContext = provided.context && typeof provided.context === "object" ? provided.context : {};
|
|
518
|
-
const options = {
|
|
576
|
+
const options = {
|
|
577
|
+
...provided,
|
|
578
|
+
context: { ...providedContext, [REQUEST_OBSERVATION]: record }
|
|
579
|
+
};
|
|
519
580
|
let result = await callProcedure(rawClient, path, [args[0], options]);
|
|
520
581
|
record.duration = Math.max(0, Date.now() - record.timestamp);
|
|
521
582
|
record.success = !(result && typeof result === "object" && "success" in result && result.success === false);
|
|
@@ -528,7 +589,16 @@ async function callObservedProcedure(rawClient, path, args, observers) {
|
|
|
528
589
|
const safe = core.error.toPublic({ ...error, publicExt: error.ext }, receivedId);
|
|
529
590
|
if (error.cause && typeof error.cause === "object" && !Array.isArray(error.cause)) {
|
|
530
591
|
const metadata = {};
|
|
531
|
-
for (const key of [
|
|
592
|
+
for (const key of [
|
|
593
|
+
"orpcCode",
|
|
594
|
+
"status",
|
|
595
|
+
"transportCode",
|
|
596
|
+
"serverTime",
|
|
597
|
+
"direction",
|
|
598
|
+
"stage",
|
|
599
|
+
"limitBytes",
|
|
600
|
+
"actualBytes"
|
|
601
|
+
]) {
|
|
532
602
|
const value = Reflect.get(error.cause, key);
|
|
533
603
|
if (typeof value === "number" || typeof value === "string")
|
|
534
604
|
metadata[key] = core.sanitize.sanitizeSensitiveValue(value, key);
|
|
@@ -536,7 +606,13 @@ async function callObservedProcedure(rawClient, path, args, observers) {
|
|
|
536
606
|
if (Object.keys(metadata).length)
|
|
537
607
|
safe.cause = metadata;
|
|
538
608
|
}
|
|
539
|
-
core.logger.error("API request failed", {
|
|
609
|
+
core.logger.error("API request failed", {
|
|
610
|
+
traceId: safe.traceId,
|
|
611
|
+
requestId: record.requestId,
|
|
612
|
+
procedure: record.procedure,
|
|
613
|
+
status: record.status,
|
|
614
|
+
error: result.error
|
|
615
|
+
});
|
|
540
616
|
result = { ...result, error: safe };
|
|
541
617
|
}
|
|
542
618
|
}
|
|
@@ -627,7 +703,8 @@ function buildRawClient(contract, config, tokenManager, transport) {
|
|
|
627
703
|
const value = await confirm();
|
|
628
704
|
return typeof value === "string" || value === void 0 ? value : null;
|
|
629
705
|
} : void 0;
|
|
630
|
-
const
|
|
706
|
+
const authenticatedRequest = tokenManager?.usesCookieCredentials ? new Request(request, { credentials: "include" }) : request;
|
|
707
|
+
const response = await fetchWithRefresh(authenticatedRequest, requestInit, config, options.context.autoRefresh === false ? void 0 : tokenManager, transport, record, reauthorize);
|
|
631
708
|
if (record) {
|
|
632
709
|
record.status = response.status;
|
|
633
710
|
const traceId = response.headers.get("x-trace-id") ?? response.headers.get("x-request-id");
|
|
@@ -663,6 +740,7 @@ async function fetchWithRefresh(request, init, config, tokenManager, transport,
|
|
|
663
740
|
if (observation)
|
|
664
741
|
observation.attempts++;
|
|
665
742
|
const response = await fetchWithTimeout(request, init, config, transport);
|
|
743
|
+
request.signal.throwIfAborted();
|
|
666
744
|
if (!retryRequest)
|
|
667
745
|
return response;
|
|
668
746
|
async function retryAuthorized(accessToken2) {
|
|
@@ -692,9 +770,10 @@ async function fetchWithRefresh(request, init, config, tokenManager, transport,
|
|
|
692
770
|
const baseUrl = config.baseUrl.replace(TRAILING_SLASHES_REGEX, "");
|
|
693
771
|
const refreshPath = config.auth?.refreshPath ?? DEFAULT_REFRESH_PATH;
|
|
694
772
|
const excludedPaths = [...AUTH_ENTRY_PATHS, refreshPath];
|
|
695
|
-
if (excludedPaths.some((path) => new URL(`${baseUrl}${path.startsWith("/") ? path : `/${path}`}`).pathname === new URL(request.url).pathname))
|
|
773
|
+
if (excludedPaths.some((path) => new URL(`${baseUrl}${path.startsWith("/") ? path : `/${path}`}`).pathname === new URL(request.url).pathname)) {
|
|
696
774
|
return response;
|
|
697
|
-
|
|
775
|
+
}
|
|
776
|
+
const failure = response.headers.get("content-type")?.includes("application/json") ? await response.clone().json().catch(() => void 0) : void 0;
|
|
698
777
|
const code = failure && typeof failure === "object" && "success" in failure && failure.success === false && "error" in failure && failure.error && typeof failure.error === "object" && "code" in failure.error ? failure.error.code : void 0;
|
|
699
778
|
if (code === "hai:iam:027" && reauthorize)
|
|
700
779
|
return retryAuthorized();
|
|
@@ -707,6 +786,7 @@ async function fetchWithRefresh(request, init, config, tokenManager, transport,
|
|
|
707
786
|
}
|
|
708
787
|
if (tokenManager.generation !== identityGeneration)
|
|
709
788
|
return response;
|
|
789
|
+
request.signal.throwIfAborted();
|
|
710
790
|
const tokens = await tokenManager.refresh();
|
|
711
791
|
if (tokenManager.generation !== identityGeneration)
|
|
712
792
|
return response;
|
|
@@ -742,26 +822,39 @@ async function fetchWithTimeout(request, init, config, transport) {
|
|
|
742
822
|
const controller = new AbortController();
|
|
743
823
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
744
824
|
const userSignal = request.signal;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
}
|
|
825
|
+
const abort = () => controller.abort(userSignal.reason);
|
|
826
|
+
if (userSignal.aborted)
|
|
827
|
+
abort();
|
|
828
|
+
else
|
|
829
|
+
userSignal.addEventListener("abort", abort, { once: true });
|
|
752
830
|
try {
|
|
753
|
-
|
|
831
|
+
const response = await fetchFn(new Request(request, { signal: controller.signal }), init);
|
|
832
|
+
const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
833
|
+
const json = contentType.endsWith("/json") || contentType.endsWith("+json");
|
|
834
|
+
const body = response.body === null ? null : json || !response.ok ? await response.arrayBuffer() : response.body.pipeThrough(new TransformStream(), { signal: userSignal });
|
|
835
|
+
return new Response(body, {
|
|
836
|
+
status: response.status,
|
|
837
|
+
statusText: response.statusText,
|
|
838
|
+
headers: response.headers
|
|
839
|
+
});
|
|
754
840
|
} finally {
|
|
755
841
|
clearTimeout(timeoutId);
|
|
842
|
+
userSignal.removeEventListener("abort", abort);
|
|
756
843
|
}
|
|
757
844
|
}
|
|
758
845
|
function mapClientError(error) {
|
|
759
846
|
const time = error instanceof TransportTimeError ? error : error instanceof Error && error.cause instanceof TransportTimeError ? error.cause : void 0;
|
|
760
847
|
if (time)
|
|
761
|
-
return err(HaiApiClientError.VALIDATION_FAILED, time.message, { transportCode: time.code });
|
|
848
|
+
return err(HaiApiClientError.VALIDATION_FAILED, time.message, { transportCode: time.code, serverTime: time.serverTime });
|
|
762
849
|
const capacity = error instanceof TransportBodyTooLargeError ? error : error instanceof Error && error.cause instanceof TransportBodyTooLargeError ? error.cause : void 0;
|
|
763
|
-
if (capacity)
|
|
764
|
-
return err(HaiApiClientError.PAYLOAD_TOO_LARGE, capacity.message, {
|
|
850
|
+
if (capacity) {
|
|
851
|
+
return err(HaiApiClientError.PAYLOAD_TOO_LARGE, capacity.message, {
|
|
852
|
+
direction: capacity.direction,
|
|
853
|
+
stage: capacity.stage,
|
|
854
|
+
limitBytes: capacity.limitBytes,
|
|
855
|
+
actualBytes: capacity.actualBytes
|
|
856
|
+
});
|
|
857
|
+
}
|
|
765
858
|
if (error instanceof ORPCError) {
|
|
766
859
|
const body = error.data && typeof error.data === "object" && "body" in error.data ? error.data.body : void 0;
|
|
767
860
|
if (body && typeof body === "object" && "success" in body && body.success === false && "error" in body) {
|