@voicethere/client 0.7.10 → 0.7.11
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 +6 -0
- package/dist/browser/session-provision-poll.d.ts +54 -0
- package/dist/browser/session-provision-poll.d.ts.map +1 -0
- package/dist/browser/session-provision-poll.js +155 -0
- package/dist/browser/session-provision-poll.js.map +1 -0
- package/dist/browser/session-provision.d.ts +2 -0
- package/dist/browser/session-provision.d.ts.map +1 -1
- package/dist/browser/session-provision.js +1 -68
- package/dist/browser/session-provision.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## 0.7.11 — 2026-07-23
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Adaptive session provisioning polling** — replace fixed 1s status polls with backoff + jitter, and honor Session API `retry_after_ms` hints to cut control-plane load during wait queues.
|
|
10
|
+
|
|
5
11
|
## 0.7.10 — 2026-07-21
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DebugConsole } from "./debug-console.js";
|
|
2
|
+
import type { SessionStatusResponse, StartSessionResult } from "./session-provision.js";
|
|
3
|
+
/** Multipliers applied to {@link StartSessionOptions.pollIntervalMs} between polls without progress. */
|
|
4
|
+
export declare const SESSION_POLL_BACKOFF_MULTIPLIERS: readonly [1, 2, 3, 5];
|
|
5
|
+
/** Upper bound for adaptive poll spacing (ms). */
|
|
6
|
+
export declare const SESSION_POLL_INTERVAL_CAP_MS = 5000;
|
|
7
|
+
/** Clamp bounds for optional server `retry_after_ms` hints. */
|
|
8
|
+
export declare const SESSION_POLL_RETRY_AFTER_MIN_MS = 250;
|
|
9
|
+
export declare const SESSION_POLL_RETRY_AFTER_MAX_MS = 5000;
|
|
10
|
+
export type SessionPollProgressSnapshot = {
|
|
11
|
+
status: string;
|
|
12
|
+
queuePosition?: number | null;
|
|
13
|
+
};
|
|
14
|
+
export type SessionPollRuntime = {
|
|
15
|
+
sleep: (ms: number) => Promise<void>;
|
|
16
|
+
now: () => number;
|
|
17
|
+
random: () => number;
|
|
18
|
+
};
|
|
19
|
+
export declare const defaultSessionPollRuntime: SessionPollRuntime;
|
|
20
|
+
export declare function clampSessionPollRetryAfterMs(retryAfterMs: number, bounds?: {
|
|
21
|
+
minMs?: number;
|
|
22
|
+
maxMs?: number;
|
|
23
|
+
}): number;
|
|
24
|
+
/**
|
|
25
|
+
* Returns true when adaptive backoff should reset to the base interval — status
|
|
26
|
+
* changed or a capacity-wait queue position moved.
|
|
27
|
+
*/
|
|
28
|
+
export declare function shouldResetSessionPollBackoff(previous: SessionPollProgressSnapshot | null, current: SessionPollProgressSnapshot): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Computes the delay before the next GET /sessions/:id poll.
|
|
31
|
+
* `attemptIndex` is the number of consecutive waits without progress (0 = first wait).
|
|
32
|
+
*/
|
|
33
|
+
export declare function computeSessionPollDelayMs(input: {
|
|
34
|
+
baseIntervalMs: number;
|
|
35
|
+
attemptIndex: number;
|
|
36
|
+
retryAfterMs?: number | null;
|
|
37
|
+
/** When false, omit jitter (deterministic tests). Default true. */
|
|
38
|
+
jitter?: boolean;
|
|
39
|
+
/** Returns [0, 1). Used for jitter when `jitter` is true. */
|
|
40
|
+
random?: () => number;
|
|
41
|
+
}): number;
|
|
42
|
+
export declare function pollSessionStatus(input: {
|
|
43
|
+
apiBase: string;
|
|
44
|
+
jobId: string;
|
|
45
|
+
headers: Record<string, string>;
|
|
46
|
+
pollIntervalMs: number;
|
|
47
|
+
pollTimeoutMs: number;
|
|
48
|
+
onStatus?: (status: SessionStatusResponse) => void;
|
|
49
|
+
onQueuePosition?: (position: number, status: SessionStatusResponse) => void;
|
|
50
|
+
onSessionError?: import("../session-errors.js").SessionErrorHandler;
|
|
51
|
+
debug?: DebugConsole;
|
|
52
|
+
runtime?: SessionPollRuntime;
|
|
53
|
+
}): Promise<StartSessionResult>;
|
|
54
|
+
//# sourceMappingURL=session-provision-poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provision-poll.d.ts","sourceRoot":"","sources":["../../src/browser/session-provision-poll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAEhC,wGAAwG;AACxG,eAAO,MAAM,gCAAgC,uBAAwB,CAAC;AAEtE,kDAAkD;AAClD,eAAO,MAAM,4BAA4B,OAAQ,CAAC;AAElD,+DAA+D;AAC/D,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAErD,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAMF,eAAO,MAAM,yBAAyB,EAAE,kBAIvC,CAAC;AAEF,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9C,MAAM,CAOR;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,2BAA2B,GAAG,IAAI,EAC5C,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAeT;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB,GAAG,MAAM,CA8BT;AAWD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACnD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,OAAO,sBAAsB,EAAE,mBAAmB,CAAC;IACpE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAyG9B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { ProvisionedRunnerModeType } from "./session-modes.js";
|
|
2
|
+
import { createLocalSessionError, emitSessionError, mapProvisioningFailureCode, } from "../session-errors.js";
|
|
3
|
+
/** Multipliers applied to {@link StartSessionOptions.pollIntervalMs} between polls without progress. */
|
|
4
|
+
export const SESSION_POLL_BACKOFF_MULTIPLIERS = [1, 2, 3, 5];
|
|
5
|
+
/** Upper bound for adaptive poll spacing (ms). */
|
|
6
|
+
export const SESSION_POLL_INTERVAL_CAP_MS = 5_000;
|
|
7
|
+
/** Clamp bounds for optional server `retry_after_ms` hints. */
|
|
8
|
+
export const SESSION_POLL_RETRY_AFTER_MIN_MS = 250;
|
|
9
|
+
export const SESSION_POLL_RETRY_AFTER_MAX_MS = 5_000;
|
|
10
|
+
function defaultSleep(ms) {
|
|
11
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
export const defaultSessionPollRuntime = {
|
|
14
|
+
sleep: defaultSleep,
|
|
15
|
+
now: () => Date.now(),
|
|
16
|
+
random: () => Math.random(),
|
|
17
|
+
};
|
|
18
|
+
export function clampSessionPollRetryAfterMs(retryAfterMs, bounds = {}) {
|
|
19
|
+
const minMs = bounds.minMs ?? SESSION_POLL_RETRY_AFTER_MIN_MS;
|
|
20
|
+
const maxMs = bounds.maxMs ?? SESSION_POLL_RETRY_AFTER_MAX_MS;
|
|
21
|
+
if (!Number.isFinite(retryAfterMs)) {
|
|
22
|
+
return minMs;
|
|
23
|
+
}
|
|
24
|
+
return Math.min(maxMs, Math.max(minMs, Math.round(retryAfterMs)));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns true when adaptive backoff should reset to the base interval — status
|
|
28
|
+
* changed or a capacity-wait queue position moved.
|
|
29
|
+
*/
|
|
30
|
+
export function shouldResetSessionPollBackoff(previous, current) {
|
|
31
|
+
if (!previous) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (previous.status !== current.status) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (current.status === "waiting" &&
|
|
38
|
+
current.queuePosition != null &&
|
|
39
|
+
previous.queuePosition !== current.queuePosition) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Computes the delay before the next GET /sessions/:id poll.
|
|
46
|
+
* `attemptIndex` is the number of consecutive waits without progress (0 = first wait).
|
|
47
|
+
*/
|
|
48
|
+
export function computeSessionPollDelayMs(input) {
|
|
49
|
+
const baseMs = Math.max(1, Math.round(input.baseIntervalMs));
|
|
50
|
+
const capMs = Math.min(SESSION_POLL_INTERVAL_CAP_MS, baseMs * SESSION_POLL_BACKOFF_MULTIPLIERS.at(-1));
|
|
51
|
+
let delayMs;
|
|
52
|
+
if (input.retryAfterMs != null &&
|
|
53
|
+
Number.isFinite(input.retryAfterMs) &&
|
|
54
|
+
input.retryAfterMs > 0) {
|
|
55
|
+
delayMs = clampSessionPollRetryAfterMs(input.retryAfterMs);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const multiplierIndex = Math.min(Math.max(0, input.attemptIndex), SESSION_POLL_BACKOFF_MULTIPLIERS.length - 1);
|
|
59
|
+
const multiplier = SESSION_POLL_BACKOFF_MULTIPLIERS[multiplierIndex];
|
|
60
|
+
delayMs = Math.min(baseMs * multiplier, capMs);
|
|
61
|
+
}
|
|
62
|
+
if (input.jitter !== false) {
|
|
63
|
+
const random = input.random ?? Math.random;
|
|
64
|
+
// ±10% jitter to avoid synchronized client polls.
|
|
65
|
+
delayMs = Math.round(delayMs * (0.9 + random() * 0.2));
|
|
66
|
+
}
|
|
67
|
+
return Math.max(1, delayMs);
|
|
68
|
+
}
|
|
69
|
+
function toPollProgressSnapshot(status) {
|
|
70
|
+
return {
|
|
71
|
+
status: status.status,
|
|
72
|
+
queuePosition: status.queue_position,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export async function pollSessionStatus(input) {
|
|
76
|
+
const runtime = input.runtime ?? defaultSessionPollRuntime;
|
|
77
|
+
const started = runtime.now();
|
|
78
|
+
const url = `${input.apiBase.replace(/\/$/, "")}/sessions/${input.jobId}`;
|
|
79
|
+
let pollAttemptIndex = 0;
|
|
80
|
+
let previousProgress = null;
|
|
81
|
+
while (runtime.now() - started < input.pollTimeoutMs) {
|
|
82
|
+
const res = await fetch(url, { headers: input.headers });
|
|
83
|
+
if (!res.ok) {
|
|
84
|
+
const body = await res.text().catch(() => "");
|
|
85
|
+
const message = `GET session status failed (${res.status}): ${body}`;
|
|
86
|
+
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
87
|
+
code: "PROVISIONING_FAILED",
|
|
88
|
+
message,
|
|
89
|
+
sessionId: input.jobId,
|
|
90
|
+
}));
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
code: "HTTP_ERROR",
|
|
94
|
+
message,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const status = (await res.json());
|
|
98
|
+
input.onStatus?.(status);
|
|
99
|
+
if (status.status === "waiting" &&
|
|
100
|
+
status.queue_position != null &&
|
|
101
|
+
status.queue_position > 0) {
|
|
102
|
+
input.onQueuePosition?.(status.queue_position, status);
|
|
103
|
+
}
|
|
104
|
+
input.debug?.info("provision", status.status, status.failure_message ?? undefined, status);
|
|
105
|
+
if (status.status === "ready" && status.credentials) {
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
credentials: {
|
|
109
|
+
...status.credentials,
|
|
110
|
+
mode: status.credentials.mode ?? ProvisionedRunnerModeType.Voice,
|
|
111
|
+
},
|
|
112
|
+
jobId: input.jobId,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (status.status === "failed") {
|
|
116
|
+
const code = mapProvisioningFailureCode(status.failure_code ?? "ORCHESTRATOR_ERROR");
|
|
117
|
+
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
118
|
+
code,
|
|
119
|
+
message: status.failure_message ?? "Session provisioning failed",
|
|
120
|
+
sessionId: input.jobId,
|
|
121
|
+
projectId: status.project_id,
|
|
122
|
+
buildId: status.build_id ?? undefined,
|
|
123
|
+
}));
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
code: status.failure_code ?? "ORCHESTRATOR_ERROR",
|
|
127
|
+
message: status.failure_message ?? "Session provisioning failed",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const currentProgress = toPollProgressSnapshot(status);
|
|
131
|
+
if (shouldResetSessionPollBackoff(previousProgress, currentProgress)) {
|
|
132
|
+
pollAttemptIndex = 0;
|
|
133
|
+
}
|
|
134
|
+
const delayMs = computeSessionPollDelayMs({
|
|
135
|
+
baseIntervalMs: input.pollIntervalMs,
|
|
136
|
+
attemptIndex: pollAttemptIndex,
|
|
137
|
+
retryAfterMs: status.retry_after_ms,
|
|
138
|
+
random: runtime.random,
|
|
139
|
+
});
|
|
140
|
+
previousProgress = currentProgress;
|
|
141
|
+
pollAttemptIndex += 1;
|
|
142
|
+
await runtime.sleep(delayMs);
|
|
143
|
+
}
|
|
144
|
+
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
145
|
+
code: "PROVISIONING_TIMEOUT",
|
|
146
|
+
message: "Session provisioning timed out",
|
|
147
|
+
sessionId: input.jobId,
|
|
148
|
+
}));
|
|
149
|
+
return {
|
|
150
|
+
ok: false,
|
|
151
|
+
code: "TIMEOUT",
|
|
152
|
+
message: "Session provisioning timed out",
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=session-provision-poll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provision-poll.js","sourceRoot":"","sources":["../../src/browser/session-provision-poll.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,6 +31,8 @@ export type SessionStatusResponse = {
|
|
|
31
31
|
queue_last_seen_at?: string | null;
|
|
32
32
|
/** Rough ETA in seconds based on queue position. */
|
|
33
33
|
estimated_wait_seconds?: number | null;
|
|
34
|
+
/** Optional server hint for time until the next status poll (ms). */
|
|
35
|
+
retry_after_ms?: number | null;
|
|
34
36
|
credentials?: SessionCredentials | null;
|
|
35
37
|
created_at: string;
|
|
36
38
|
updated_at: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-provision.d.ts","sourceRoot":"","sources":["../../src/browser/session-provision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"session-provision.d.ts","sourceRoot":"","sources":["../../src/browser/session-provision.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { pollSessionStatus } from "./session-provision-poll.js";
|
|
1
2
|
import { ProvisionedRunnerModeType, } from "./session-modes.js";
|
|
2
3
|
import { createLocalSessionError, emitSessionError, mapProvisioningFailureCode, } from "../session-errors.js";
|
|
3
4
|
export function isTerminalSessionJobStatus(status) {
|
|
@@ -6,74 +7,6 @@ export function isTerminalSessionJobStatus(status) {
|
|
|
6
7
|
export function isCapacityWaitStatus(status) {
|
|
7
8
|
return status.status === "waiting";
|
|
8
9
|
}
|
|
9
|
-
function sleep(ms) {
|
|
10
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
-
}
|
|
12
|
-
async function pollSessionStatus(input) {
|
|
13
|
-
const started = Date.now();
|
|
14
|
-
const url = `${input.apiBase.replace(/\/$/, "")}/sessions/${input.jobId}`;
|
|
15
|
-
while (Date.now() - started < input.pollTimeoutMs) {
|
|
16
|
-
const res = await fetch(url, { headers: input.headers });
|
|
17
|
-
if (!res.ok) {
|
|
18
|
-
const body = await res.text().catch(() => "");
|
|
19
|
-
const message = `GET session status failed (${res.status}): ${body}`;
|
|
20
|
-
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
21
|
-
code: "PROVISIONING_FAILED",
|
|
22
|
-
message,
|
|
23
|
-
sessionId: input.jobId,
|
|
24
|
-
}));
|
|
25
|
-
return {
|
|
26
|
-
ok: false,
|
|
27
|
-
code: "HTTP_ERROR",
|
|
28
|
-
message,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const status = (await res.json());
|
|
32
|
-
input.onStatus?.(status);
|
|
33
|
-
if (status.status === "waiting" &&
|
|
34
|
-
status.queue_position != null &&
|
|
35
|
-
status.queue_position > 0) {
|
|
36
|
-
input.onQueuePosition?.(status.queue_position, status);
|
|
37
|
-
}
|
|
38
|
-
input.debug?.info("provision", status.status, status.failure_message ?? undefined, status);
|
|
39
|
-
if (status.status === "ready" && status.credentials) {
|
|
40
|
-
return {
|
|
41
|
-
ok: true,
|
|
42
|
-
credentials: {
|
|
43
|
-
...status.credentials,
|
|
44
|
-
mode: status.credentials.mode ?? ProvisionedRunnerModeType.Voice,
|
|
45
|
-
},
|
|
46
|
-
jobId: input.jobId,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
if (status.status === "failed") {
|
|
50
|
-
const code = mapProvisioningFailureCode(status.failure_code ?? "ORCHESTRATOR_ERROR");
|
|
51
|
-
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
52
|
-
code,
|
|
53
|
-
message: status.failure_message ?? "Session provisioning failed",
|
|
54
|
-
sessionId: input.jobId,
|
|
55
|
-
projectId: status.project_id,
|
|
56
|
-
buildId: status.build_id ?? undefined,
|
|
57
|
-
}));
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
code: status.failure_code ?? "ORCHESTRATOR_ERROR",
|
|
61
|
-
message: status.failure_message ?? "Session provisioning failed",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
await sleep(input.pollIntervalMs);
|
|
65
|
-
}
|
|
66
|
-
emitSessionError(input.onSessionError, createLocalSessionError({
|
|
67
|
-
code: "PROVISIONING_TIMEOUT",
|
|
68
|
-
message: "Session provisioning timed out",
|
|
69
|
-
sessionId: input.jobId,
|
|
70
|
-
}));
|
|
71
|
-
return {
|
|
72
|
-
ok: false,
|
|
73
|
-
code: "TIMEOUT",
|
|
74
|
-
message: "Session provisioning timed out",
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
10
|
function emitProvisionError(options, input) {
|
|
78
11
|
emitSessionError(options.onSessionError, createLocalSessionError({
|
|
79
12
|
code: mapProvisioningFailureCode(input.code),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-provision.js","sourceRoot":"","sources":["../../src/browser/session-provision.ts"],"names":[],"mappings":"AACA,OAAO,EACL,yBAAyB,GAE1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"session-provision.js","sourceRoot":"","sources":["../../src/browser/session-provision.ts"],"names":[],"mappings":"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"}
|