@absolutejs/voice 0.0.22-beta.387 → 0.0.22-beta.388

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.
@@ -6557,6 +6557,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
6557
6557
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
6558
6558
  const jobPollMs = options.jobPollMs ?? 1200;
6559
6559
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
6560
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
6560
6561
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
6561
6562
  const fetchImpl = options.fetch ?? fetch;
6562
6563
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -6598,70 +6599,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
6598
6599
  actionCount: 0,
6599
6600
  actions,
6600
6601
  jobs: [],
6602
+ passes: 0,
6601
6603
  ok: realCallProfileGate2?.status === "pass",
6602
6604
  realCallProfileGate: realCallProfileGate2,
6603
6605
  startFailures: []
6604
6606
  };
6605
6607
  }
6606
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
6607
- for (const action of actions) {
6608
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
6609
- }
6610
- const starts = await Promise.allSettled(actions.map(async (action) => {
6611
- if (!action.href) {
6612
- throw new Error("Recovery action is missing href.");
6613
- }
6614
- const body = await fetchJson(action.href, { method: "POST" });
6615
- return { action, ...body };
6616
- }));
6617
- const startedJobs = starts.flatMap((result) => {
6618
- if (result.status === "rejected") {
6619
- return [];
6620
- }
6621
- return result.value.jobId ? [result.value] : [];
6622
- });
6623
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
6624
- {
6625
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
6626
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
6627
- }
6628
- ] : []);
6629
- const pollJob = async (jobId) => {
6630
- const deadline = Date.now() + jobTimeoutMs;
6631
- while (Date.now() < deadline) {
6632
- const body = await fetchJson(resolveJobHref(jobId));
6633
- const job = body.job;
6634
- if (!job) {
6635
- throw new Error(`Recovery job ${jobId} was not found.`);
6608
+ const allJobs = [];
6609
+ const allStartFailures = [];
6610
+ let realCallProfileGate = null;
6611
+ let passes = 0;
6612
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
6613
+ passes = pass;
6614
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
6615
+ for (const action of actions) {
6616
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
6617
+ }
6618
+ const starts = await Promise.allSettled(actions.map(async (action) => {
6619
+ if (!action.href) {
6620
+ throw new Error("Recovery action is missing href.");
6621
+ }
6622
+ const body = await fetchJson(action.href, { method: "POST" });
6623
+ return { action, ...body };
6624
+ }));
6625
+ const startedJobs = starts.flatMap((result) => {
6626
+ if (result.status === "rejected") {
6627
+ return [];
6636
6628
  }
6637
- if (job.status === "pass" || job.status === "fail") {
6638
- return job;
6629
+ return result.value.jobId ? [result.value] : [];
6630
+ });
6631
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
6632
+ {
6633
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
6634
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
6639
6635
  }
6640
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
6636
+ ] : []);
6637
+ allStartFailures.push(...startFailures);
6638
+ const pollJob = async (jobId) => {
6639
+ const deadline = Date.now() + jobTimeoutMs;
6640
+ while (Date.now() < deadline) {
6641
+ const body = await fetchJson(resolveJobHref(jobId));
6642
+ const job = body.job;
6643
+ if (!job) {
6644
+ throw new Error(`Recovery job ${jobId} was not found.`);
6645
+ }
6646
+ if (job.status === "pass" || job.status === "fail") {
6647
+ return job;
6648
+ }
6649
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
6650
+ }
6651
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
6652
+ };
6653
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
6654
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
6655
+ const jobs = jobResults.map((result, index) => ({
6656
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
6657
+ jobId: startedJobs[index]?.jobId,
6658
+ result: result.status === "fulfilled" ? result.value : {
6659
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
6660
+ status: "fail"
6661
+ }
6662
+ }));
6663
+ allJobs.push(...jobs);
6664
+ if (refreshHref !== false) {
6665
+ await fetchJson(refreshHref, { method: "POST" });
6641
6666
  }
6642
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
6643
- };
6644
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
6645
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
6646
- const jobs = jobResults.map((result, index) => ({
6647
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
6648
- jobId: startedJobs[index]?.jobId,
6649
- result: result.status === "fulfilled" ? result.value : {
6650
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
6651
- status: "fail"
6667
+ realCallProfileGate = await getGate(true);
6668
+ if (realCallProfileGate?.status === "pass") {
6669
+ break;
6652
6670
  }
6653
- }));
6654
- if (refreshHref !== false) {
6655
- await fetchJson(refreshHref, { method: "POST" });
6656
6671
  }
6657
- const realCallProfileGate = await getGate(true);
6658
6672
  return {
6659
- actionCount: actions.length,
6673
+ actionCount: actions.length * passes,
6660
6674
  actions,
6661
- jobs,
6662
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
6675
+ jobs: allJobs,
6676
+ passes,
6677
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
6663
6678
  realCallProfileGate,
6664
- startFailures
6679
+ startFailures: allStartFailures
6665
6680
  };
6666
6681
  };
6667
6682
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/dist/index.js CHANGED
@@ -17076,6 +17076,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
17076
17076
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
17077
17077
  const jobPollMs = options.jobPollMs ?? 1200;
17078
17078
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
17079
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
17079
17080
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
17080
17081
  const fetchImpl = options.fetch ?? fetch;
17081
17082
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -17117,70 +17118,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
17117
17118
  actionCount: 0,
17118
17119
  actions,
17119
17120
  jobs: [],
17121
+ passes: 0,
17120
17122
  ok: realCallProfileGate2?.status === "pass",
17121
17123
  realCallProfileGate: realCallProfileGate2,
17122
17124
  startFailures: []
17123
17125
  };
17124
17126
  }
17125
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
17126
- for (const action of actions) {
17127
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
17128
- }
17129
- const starts = await Promise.allSettled(actions.map(async (action) => {
17130
- if (!action.href) {
17131
- throw new Error("Recovery action is missing href.");
17132
- }
17133
- const body = await fetchJson(action.href, { method: "POST" });
17134
- return { action, ...body };
17135
- }));
17136
- const startedJobs = starts.flatMap((result) => {
17137
- if (result.status === "rejected") {
17138
- return [];
17127
+ const allJobs = [];
17128
+ const allStartFailures = [];
17129
+ let realCallProfileGate = null;
17130
+ let passes = 0;
17131
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
17132
+ passes = pass;
17133
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
17134
+ for (const action of actions) {
17135
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
17136
+ }
17137
+ const starts = await Promise.allSettled(actions.map(async (action) => {
17138
+ if (!action.href) {
17139
+ throw new Error("Recovery action is missing href.");
17140
+ }
17141
+ const body = await fetchJson(action.href, { method: "POST" });
17142
+ return { action, ...body };
17143
+ }));
17144
+ const startedJobs = starts.flatMap((result) => {
17145
+ if (result.status === "rejected") {
17146
+ return [];
17147
+ }
17148
+ return result.value.jobId ? [result.value] : [];
17149
+ });
17150
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
17151
+ {
17152
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
17153
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
17154
+ }
17155
+ ] : []);
17156
+ allStartFailures.push(...startFailures);
17157
+ const pollJob = async (jobId) => {
17158
+ const deadline = Date.now() + jobTimeoutMs;
17159
+ while (Date.now() < deadline) {
17160
+ const body = await fetchJson(resolveJobHref(jobId));
17161
+ const job = body.job;
17162
+ if (!job) {
17163
+ throw new Error(`Recovery job ${jobId} was not found.`);
17164
+ }
17165
+ if (job.status === "pass" || job.status === "fail") {
17166
+ return job;
17167
+ }
17168
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
17169
+ }
17170
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
17171
+ };
17172
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
17173
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
17174
+ const jobs = jobResults.map((result, index) => ({
17175
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
17176
+ jobId: startedJobs[index]?.jobId,
17177
+ result: result.status === "fulfilled" ? result.value : {
17178
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
17179
+ status: "fail"
17180
+ }
17181
+ }));
17182
+ allJobs.push(...jobs);
17183
+ if (refreshHref !== false) {
17184
+ await fetchJson(refreshHref, { method: "POST" });
17139
17185
  }
17140
- return result.value.jobId ? [result.value] : [];
17141
- });
17142
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
17143
- {
17144
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
17145
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
17146
- }
17147
- ] : []);
17148
- const pollJob = async (jobId) => {
17149
- const deadline = Date.now() + jobTimeoutMs;
17150
- while (Date.now() < deadline) {
17151
- const body = await fetchJson(resolveJobHref(jobId));
17152
- const job = body.job;
17153
- if (!job) {
17154
- throw new Error(`Recovery job ${jobId} was not found.`);
17155
- }
17156
- if (job.status === "pass" || job.status === "fail") {
17157
- return job;
17158
- }
17159
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
17160
- }
17161
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
17162
- };
17163
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
17164
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
17165
- const jobs = jobResults.map((result, index) => ({
17166
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
17167
- jobId: startedJobs[index]?.jobId,
17168
- result: result.status === "fulfilled" ? result.value : {
17169
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
17170
- status: "fail"
17186
+ realCallProfileGate = await getGate(true);
17187
+ if (realCallProfileGate?.status === "pass") {
17188
+ break;
17171
17189
  }
17172
- }));
17173
- if (refreshHref !== false) {
17174
- await fetchJson(refreshHref, { method: "POST" });
17175
17190
  }
17176
- const realCallProfileGate = await getGate(true);
17177
17191
  return {
17178
- actionCount: actions.length,
17192
+ actionCount: actions.length * passes,
17179
17193
  actions,
17180
- jobs,
17181
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
17194
+ jobs: allJobs,
17195
+ passes,
17196
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
17182
17197
  realCallProfileGate,
17183
- startFailures
17198
+ startFailures: allStartFailures
17184
17199
  };
17185
17200
  };
17186
17201
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
@@ -492,6 +492,7 @@ export type VoiceRealCallProfileRecoveryLoopReport = {
492
492
  actionCount: number;
493
493
  actions: VoiceRealCallProfileRecoveryLoopAction[];
494
494
  jobs: VoiceRealCallProfileRecoveryLoopJobResult[];
495
+ passes: number;
495
496
  ok: boolean;
496
497
  realCallProfileGate: VoiceProductionReadinessCheck | null;
497
498
  startFailures: VoiceRealCallProfileRecoveryLoopStartFailure[];
@@ -504,6 +505,7 @@ export type VoiceRealCallProfileRecoveryLoopOptions = {
504
505
  jobPollMs?: number;
505
506
  jobTimeoutMs?: number;
506
507
  logger?: Pick<Console, 'log'>;
508
+ maxPasses?: number;
507
509
  readinessCheckLabel?: string;
508
510
  readinessHref?: string;
509
511
  recoveryActionsHref?: string;
@@ -4144,6 +4144,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4144
4144
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
4145
4145
  const jobPollMs = options.jobPollMs ?? 1200;
4146
4146
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
4147
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
4147
4148
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
4148
4149
  const fetchImpl = options.fetch ?? fetch;
4149
4150
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -4185,70 +4186,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4185
4186
  actionCount: 0,
4186
4187
  actions,
4187
4188
  jobs: [],
4189
+ passes: 0,
4188
4190
  ok: realCallProfileGate2?.status === "pass",
4189
4191
  realCallProfileGate: realCallProfileGate2,
4190
4192
  startFailures: []
4191
4193
  };
4192
4194
  }
4193
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
4194
- for (const action of actions) {
4195
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4196
- }
4197
- const starts = await Promise.allSettled(actions.map(async (action) => {
4198
- if (!action.href) {
4199
- throw new Error("Recovery action is missing href.");
4200
- }
4201
- const body = await fetchJson(action.href, { method: "POST" });
4202
- return { action, ...body };
4203
- }));
4204
- const startedJobs = starts.flatMap((result) => {
4205
- if (result.status === "rejected") {
4206
- return [];
4207
- }
4208
- return result.value.jobId ? [result.value] : [];
4209
- });
4210
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4211
- {
4212
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4213
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4214
- }
4215
- ] : []);
4216
- const pollJob = async (jobId) => {
4217
- const deadline = Date.now() + jobTimeoutMs;
4218
- while (Date.now() < deadline) {
4219
- const body = await fetchJson(resolveJobHref(jobId));
4220
- const job = body.job;
4221
- if (!job) {
4222
- throw new Error(`Recovery job ${jobId} was not found.`);
4195
+ const allJobs = [];
4196
+ const allStartFailures = [];
4197
+ let realCallProfileGate = null;
4198
+ let passes = 0;
4199
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
4200
+ passes = pass;
4201
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
4202
+ for (const action of actions) {
4203
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4204
+ }
4205
+ const starts = await Promise.allSettled(actions.map(async (action) => {
4206
+ if (!action.href) {
4207
+ throw new Error("Recovery action is missing href.");
4208
+ }
4209
+ const body = await fetchJson(action.href, { method: "POST" });
4210
+ return { action, ...body };
4211
+ }));
4212
+ const startedJobs = starts.flatMap((result) => {
4213
+ if (result.status === "rejected") {
4214
+ return [];
4223
4215
  }
4224
- if (job.status === "pass" || job.status === "fail") {
4225
- return job;
4216
+ return result.value.jobId ? [result.value] : [];
4217
+ });
4218
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4219
+ {
4220
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4221
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4226
4222
  }
4227
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4223
+ ] : []);
4224
+ allStartFailures.push(...startFailures);
4225
+ const pollJob = async (jobId) => {
4226
+ const deadline = Date.now() + jobTimeoutMs;
4227
+ while (Date.now() < deadline) {
4228
+ const body = await fetchJson(resolveJobHref(jobId));
4229
+ const job = body.job;
4230
+ if (!job) {
4231
+ throw new Error(`Recovery job ${jobId} was not found.`);
4232
+ }
4233
+ if (job.status === "pass" || job.status === "fail") {
4234
+ return job;
4235
+ }
4236
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4237
+ }
4238
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4239
+ };
4240
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4241
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4242
+ const jobs = jobResults.map((result, index) => ({
4243
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4244
+ jobId: startedJobs[index]?.jobId,
4245
+ result: result.status === "fulfilled" ? result.value : {
4246
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4247
+ status: "fail"
4248
+ }
4249
+ }));
4250
+ allJobs.push(...jobs);
4251
+ if (refreshHref !== false) {
4252
+ await fetchJson(refreshHref, { method: "POST" });
4228
4253
  }
4229
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4230
- };
4231
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4232
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4233
- const jobs = jobResults.map((result, index) => ({
4234
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4235
- jobId: startedJobs[index]?.jobId,
4236
- result: result.status === "fulfilled" ? result.value : {
4237
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4238
- status: "fail"
4254
+ realCallProfileGate = await getGate(true);
4255
+ if (realCallProfileGate?.status === "pass") {
4256
+ break;
4239
4257
  }
4240
- }));
4241
- if (refreshHref !== false) {
4242
- await fetchJson(refreshHref, { method: "POST" });
4243
4258
  }
4244
- const realCallProfileGate = await getGate(true);
4245
4259
  return {
4246
- actionCount: actions.length,
4260
+ actionCount: actions.length * passes,
4247
4261
  actions,
4248
- jobs,
4249
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4262
+ jobs: allJobs,
4263
+ passes,
4264
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4250
4265
  realCallProfileGate,
4251
- startFailures
4266
+ startFailures: allStartFailures
4252
4267
  };
4253
4268
  };
4254
4269
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/dist/vue/index.js CHANGED
@@ -4065,6 +4065,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4065
4065
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
4066
4066
  const jobPollMs = options.jobPollMs ?? 1200;
4067
4067
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
4068
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
4068
4069
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
4069
4070
  const fetchImpl = options.fetch ?? fetch;
4070
4071
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -4106,70 +4107,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4106
4107
  actionCount: 0,
4107
4108
  actions,
4108
4109
  jobs: [],
4110
+ passes: 0,
4109
4111
  ok: realCallProfileGate2?.status === "pass",
4110
4112
  realCallProfileGate: realCallProfileGate2,
4111
4113
  startFailures: []
4112
4114
  };
4113
4115
  }
4114
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
4115
- for (const action of actions) {
4116
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4117
- }
4118
- const starts = await Promise.allSettled(actions.map(async (action) => {
4119
- if (!action.href) {
4120
- throw new Error("Recovery action is missing href.");
4121
- }
4122
- const body = await fetchJson(action.href, { method: "POST" });
4123
- return { action, ...body };
4124
- }));
4125
- const startedJobs = starts.flatMap((result) => {
4126
- if (result.status === "rejected") {
4127
- return [];
4128
- }
4129
- return result.value.jobId ? [result.value] : [];
4130
- });
4131
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4132
- {
4133
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4134
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4135
- }
4136
- ] : []);
4137
- const pollJob = async (jobId) => {
4138
- const deadline = Date.now() + jobTimeoutMs;
4139
- while (Date.now() < deadline) {
4140
- const body = await fetchJson(resolveJobHref(jobId));
4141
- const job = body.job;
4142
- if (!job) {
4143
- throw new Error(`Recovery job ${jobId} was not found.`);
4116
+ const allJobs = [];
4117
+ const allStartFailures = [];
4118
+ let realCallProfileGate = null;
4119
+ let passes = 0;
4120
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
4121
+ passes = pass;
4122
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
4123
+ for (const action of actions) {
4124
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4125
+ }
4126
+ const starts = await Promise.allSettled(actions.map(async (action) => {
4127
+ if (!action.href) {
4128
+ throw new Error("Recovery action is missing href.");
4129
+ }
4130
+ const body = await fetchJson(action.href, { method: "POST" });
4131
+ return { action, ...body };
4132
+ }));
4133
+ const startedJobs = starts.flatMap((result) => {
4134
+ if (result.status === "rejected") {
4135
+ return [];
4144
4136
  }
4145
- if (job.status === "pass" || job.status === "fail") {
4146
- return job;
4137
+ return result.value.jobId ? [result.value] : [];
4138
+ });
4139
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4140
+ {
4141
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4142
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4147
4143
  }
4148
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4144
+ ] : []);
4145
+ allStartFailures.push(...startFailures);
4146
+ const pollJob = async (jobId) => {
4147
+ const deadline = Date.now() + jobTimeoutMs;
4148
+ while (Date.now() < deadline) {
4149
+ const body = await fetchJson(resolveJobHref(jobId));
4150
+ const job = body.job;
4151
+ if (!job) {
4152
+ throw new Error(`Recovery job ${jobId} was not found.`);
4153
+ }
4154
+ if (job.status === "pass" || job.status === "fail") {
4155
+ return job;
4156
+ }
4157
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4158
+ }
4159
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4160
+ };
4161
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4162
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4163
+ const jobs = jobResults.map((result, index) => ({
4164
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4165
+ jobId: startedJobs[index]?.jobId,
4166
+ result: result.status === "fulfilled" ? result.value : {
4167
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4168
+ status: "fail"
4169
+ }
4170
+ }));
4171
+ allJobs.push(...jobs);
4172
+ if (refreshHref !== false) {
4173
+ await fetchJson(refreshHref, { method: "POST" });
4149
4174
  }
4150
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4151
- };
4152
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4153
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4154
- const jobs = jobResults.map((result, index) => ({
4155
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4156
- jobId: startedJobs[index]?.jobId,
4157
- result: result.status === "fulfilled" ? result.value : {
4158
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4159
- status: "fail"
4175
+ realCallProfileGate = await getGate(true);
4176
+ if (realCallProfileGate?.status === "pass") {
4177
+ break;
4160
4178
  }
4161
- }));
4162
- if (refreshHref !== false) {
4163
- await fetchJson(refreshHref, { method: "POST" });
4164
4179
  }
4165
- const realCallProfileGate = await getGate(true);
4166
4180
  return {
4167
- actionCount: actions.length,
4181
+ actionCount: actions.length * passes,
4168
4182
  actions,
4169
- jobs,
4170
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4183
+ jobs: allJobs,
4184
+ passes,
4185
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4171
4186
  realCallProfileGate,
4172
- startFailures
4187
+ startFailures: allStartFailures
4173
4188
  };
4174
4189
  };
4175
4190
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.387",
3
+ "version": "0.0.22-beta.388",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",