deepline 0.3.357 → 0.3.358
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/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +14 -1
- package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +52 -19
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +35 -18
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +202 -44
- package/dist/cli/{auth-5Y2YV5LY.mjs → auth-QSEB4OI6.mjs} +6 -4
- package/dist/cli/auth-VP5WIGFS.js +33 -0
- package/dist/cli/{billing-UOFR7KDB.mjs → billing-KNJ6ZHSN.mjs} +5 -5
- package/dist/cli/{billing-Y6IKDJ6H.js → billing-UE2CP5O5.js} +74 -74
- package/dist/cli/{chunk-O7IM72KW.mjs → chunk-3DP24EZA.mjs} +15 -15
- package/dist/cli/{chunk-IWBFG4XI.mjs → chunk-4IUOBUNS.mjs} +1 -1
- package/dist/cli/{chunk-C2EHMMME.js → chunk-5PJ6XYRV.js} +64 -60
- package/dist/cli/{chunk-CPQIVH6G.js → chunk-5RWJSROL.js} +8 -8
- package/dist/cli/{chunk-5UNTNKNX.js → chunk-BVPN57AD.js} +6 -6
- package/dist/cli/{chunk-PJYCAGKK.mjs → chunk-EL62MLRD.mjs} +1 -1
- package/dist/cli/{chunk-NZ6BT4NL.mjs → chunk-EVPWVNJV.mjs} +7 -7
- package/dist/cli/{chunk-SCY4UCEZ.js → chunk-GYDSDOXB.js} +75 -50
- package/dist/cli/{chunk-VXH4225Y.mjs → chunk-HGLESMAC.mjs} +27 -2
- package/dist/cli/{chunk-EWTVIN3O.js → chunk-HKRJUIRL.js} +47 -47
- package/dist/cli/{chunk-R5P3KFF5.js → chunk-HNAFCPM4.js} +8 -8
- package/dist/cli/{chunk-WQSV34KV.js → chunk-KIRDHNX5.js} +11 -11
- package/dist/cli/{chunk-NEMHJYA4.js → chunk-LOSZZCER.js} +2 -2
- package/dist/cli/{chunk-2AROJ4EZ.mjs → chunk-NMHQTEIQ.mjs} +2 -2
- package/dist/cli/{chunk-J4A4MRBK.js → chunk-OO3MYL2Y.js} +26 -26
- package/dist/cli/{chunk-SXXUOZRS.js → chunk-OVUDXZCK.js} +1 -1
- package/dist/cli/{chunk-2NZUC5OS.mjs → chunk-Q77PWYDF.mjs} +4 -4
- package/dist/cli/chunk-QFBHEQCQ.mjs +1421 -0
- package/dist/cli/{chunk-SMLKQAEB.mjs → chunk-QH6PV7IJ.mjs} +3 -3
- package/dist/cli/chunk-QILHWZFP.js +1421 -0
- package/dist/cli/{chunk-N47R7S3E.mjs → chunk-SJY4SUCH.mjs} +1 -1
- package/dist/cli/{chunk-LJTZVC2T.mjs → chunk-SPJ3IDR4.mjs} +2 -2
- package/dist/cli/{chunk-4YXJTQ5X.js → chunk-UZQWPQ6V.js} +5 -5
- package/dist/cli/{chunk-ZYPSNUDU.js → chunk-V4MVLHOW.js} +231 -231
- package/dist/cli/{chunk-VG5WMYSF.mjs → chunk-VZBNBCEB.mjs} +3 -3
- package/dist/cli/{chunk-6P235JSN.js → chunk-WVO6AJM7.js} +15 -15
- package/dist/cli/{chunk-XYD76KJB.mjs → chunk-WYVUMIEF.mjs} +8 -8
- package/dist/cli/{chunk-QYCAH7SM.mjs → chunk-YWGKGXSH.mjs} +6 -2
- package/dist/cli/client-I4RRFLPM.js +20 -0
- package/dist/cli/{client-23QTYYIR.mjs → client-M2TQV23S.mjs} +4 -4
- package/dist/cli/command-docs.d.mts +50 -0
- package/dist/cli/command-docs.d.ts +50 -0
- package/dist/cli/command-docs.js +13 -0
- package/dist/cli/command-docs.mjs +13 -0
- package/dist/cli/{csv-VKEF5C65.mjs → csv-OQUTHAMD.mjs} +3 -3
- package/dist/cli/{csv-SQIEBX66.js → csv-VSVZWQ24.js} +12 -12
- package/dist/cli/{db-Z33ZRE3N.js → db-6QNWRH47.js} +30 -30
- package/dist/cli/{db-KX7FDHL4.mjs → db-QH56PKPT.mjs} +5 -5
- package/dist/cli/{describe-5ZQ6KIJH.js → describe-BKJNG2I5.js} +21 -21
- package/dist/cli/{describe-K2SJ22B3.mjs → describe-NCUJW2JG.mjs} +16 -16
- package/dist/cli/{executions-KCZZY6SS.js → executions-63CSEYKE.js} +7 -7
- package/dist/cli/{executions-452G2REF.mjs → executions-NOZELHEN.mjs} +5 -5
- package/dist/cli/{feedback-55HU4EQY.mjs → feedback-GL7YHJAK.mjs} +3 -3
- package/dist/cli/{feedback-BM7ZNH7Y.js → feedback-T4FGM7DY.js} +7 -7
- package/dist/cli/git-4EBFGJAN.js +23 -0
- package/dist/cli/{git-CAPESVAJ.mjs → git-C3SPUEUB.mjs} +4 -4
- package/dist/cli/index.js +183 -83
- package/dist/cli/index.mjs +145 -45
- package/dist/cli/{legacy-noops-3B3MG33Y.mjs → legacy-noops-BBDNHMAW.mjs} +4 -4
- package/dist/cli/{legacy-noops-PV5AWTIZ.js → legacy-noops-W6UWORVY.js} +7 -7
- package/dist/cli/{monitors-43R6CUFK.mjs → monitors-LEGM63T3.mjs} +5 -5
- package/dist/cli/{monitors-DVFEJHQT.js → monitors-R32SKFE3.js} +86 -86
- package/dist/cli/{org-X5WSLJRD.mjs → org-RBIBSVP3.mjs} +5 -5
- package/dist/cli/{org-BGCB45VI.js → org-RYRHB22V.js} +61 -61
- package/dist/cli/{play-OFO6XZ5L.mjs → play-FMPNHLXU.mjs} +11 -11
- package/dist/cli/play-GDDOUU3I.js +153 -0
- package/dist/cli/{providers-L2MPYA3E.mjs → providers-5JHHZWMF.mjs} +5 -5
- package/dist/cli/{providers-RJYHQC4J.js → providers-WAUWYRTK.js} +8 -8
- package/dist/cli/{quickstart-NHFMGZ3B.mjs → quickstart-RYKNMAFN.mjs} +5 -5
- package/dist/cli/{quickstart-E6OWYYMT.js → quickstart-V5LHMW2X.js} +7 -7
- package/dist/cli/{search-IPTZAOUE.mjs → search-B2BB2MHO.mjs} +6 -6
- package/dist/cli/search-RQLY5D2A.js +26 -0
- package/dist/cli/{secrets-6LD4CA2X.js → secrets-BEAESK2H.js} +14 -14
- package/dist/cli/{secrets-TCOPPNXR.mjs → secrets-XJOXWVUG.mjs} +5 -5
- package/dist/cli/{sessions-QV4LUKEP.js → sessions-ELBTWKLS.js} +5 -5
- package/dist/cli/{sessions-D4JETMAW.mjs → sessions-VRDJCPKE.mjs} +4 -4
- package/dist/cli/{settings-IVJPCORG.mjs → settings-QEL2SD7N.mjs} +8 -8
- package/dist/cli/{settings-JOYKXSY7.js → settings-V5RI4UWE.js} +75 -75
- package/dist/cli/{setup-MIJZTYMH.js → setup-CS3IACGU.js} +43 -43
- package/dist/cli/{setup-WCKQK7FE.mjs → setup-FNVP5XQR.mjs} +8 -8
- package/dist/cli/{skills-GYWMUDDF.mjs → skills-73PM6I6F.mjs} +4 -4
- package/dist/cli/skills-OH3X3OUA.js +28 -0
- package/dist/cli/tools-BY6D7M4I.js +40 -0
- package/dist/cli/{tools-ILYNEPGQ.mjs → tools-N2H5HJYP.mjs} +8 -8
- package/dist/cli/{update-P3VIHMC6.mjs → update-MUMZ6L7J.mjs} +6 -6
- package/dist/cli/update-PEGDRQ3G.js +28 -0
- package/dist/cli/{workflow-A6ID4C6N.mjs → workflow-WY2CL73A.mjs} +9 -9
- package/dist/cli/{workflow-YWKK3A4X.js → workflow-X66V6L3H.js} +19 -19
- package/dist/index.js +13 -13
- package/dist/index.mjs +12 -12
- package/dist/install-integrity.json +80 -74
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +2 -2
- package/dist/release.mjs +1 -1
- package/package.json +6 -1
- package/dist/cli/auth-AABYKR2S.js +0 -31
- package/dist/cli/client-3DYV3HZ4.js +0 -20
- package/dist/cli/git-VESXOJ4M.js +0 -23
- package/dist/cli/play-ZO4HDGMC.js +0 -153
- package/dist/cli/search-2FJA6OH4.js +0 -26
- package/dist/cli/skills-JL4J5CII.js +0 -28
- package/dist/cli/tools-ZOKG4AX4.js +0 -40
- package/dist/cli/update-WUMDEAEE.js +0 -28
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.358',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -20,7 +20,7 @@ import { monitorBillingRequiresExplicitReactivation } from './billing/state';
|
|
|
20
20
|
* this off" and "we did, and here is why" must not be the same state.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
export type MonitorDeactivationOwner = 'policy' | 'fleet';
|
|
23
|
+
export type MonitorDeactivationOwner = 'policy' | 'fleet' | 'admin';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* The delivery window, in days, that `monitors health` reports against.
|
|
@@ -43,6 +43,13 @@ export type MonitorDeactivationReasonDefinition = {
|
|
|
43
43
|
|
|
44
44
|
export const MONITOR_DEACTIVATION_REASONS: readonly MonitorDeactivationReasonDefinition[] =
|
|
45
45
|
[
|
|
46
|
+
{
|
|
47
|
+
id: 'workspace_archived',
|
|
48
|
+
owner: 'admin',
|
|
49
|
+
summary: 'Deactivated because the workspace is archived.',
|
|
50
|
+
resolution:
|
|
51
|
+
'Contact a platform administrator. Credit changes never reactivate an archived workspace.',
|
|
52
|
+
},
|
|
46
53
|
{
|
|
47
54
|
id: 'insufficient_balance',
|
|
48
55
|
owner: 'policy',
|
|
@@ -122,6 +129,12 @@ export function isPolicyDeactivation(id: string | undefined | null): boolean {
|
|
|
122
129
|
return monitorDeactivationReason(id)?.owner === 'policy';
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/** Both policy and admin shutdown retain the definition; only policy owns recovery. */
|
|
133
|
+
export function isMonitorSuspension(id: string | undefined | null): boolean {
|
|
134
|
+
const owner = monitorDeactivationReason(id)?.owner;
|
|
135
|
+
return owner === 'policy' || owner === 'admin';
|
|
136
|
+
}
|
|
137
|
+
|
|
125
138
|
// ---------------------------------------------------------------------------
|
|
126
139
|
// The balance policy
|
|
127
140
|
// ---------------------------------------------------------------------------
|
|
@@ -954,6 +954,17 @@ function asyncOperationRateLimitPollDelayMs(input: {
|
|
|
954
954
|
return Math.max(input.retryAfterMs ?? 0, localBackoffMs);
|
|
955
955
|
}
|
|
956
956
|
|
|
957
|
+
/** A paced read of an already accepted job must never restart its launch. */
|
|
958
|
+
export class AsyncOperationCompanionPacingError extends Error {
|
|
959
|
+
constructor(
|
|
960
|
+
message: string,
|
|
961
|
+
readonly retryAfterMs: number,
|
|
962
|
+
) {
|
|
963
|
+
super(message);
|
|
964
|
+
this.name = 'AsyncOperationCompanionPacingError';
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
957
968
|
/**
|
|
958
969
|
* Runtime-neutral executor for a declared async lifecycle.
|
|
959
970
|
*
|
|
@@ -1074,25 +1085,47 @@ export async function executeAsyncOperationLifecycle<Response>(input: {
|
|
|
1074
1085
|
}): Promise<
|
|
1075
1086
|
{ kind: 'response'; response: Response } | { kind: 'timed_out' }
|
|
1076
1087
|
> => {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1088
|
+
let pacingAttempt = 0;
|
|
1089
|
+
while (true) {
|
|
1090
|
+
const remainingTimeoutMs = Math.max(
|
|
1091
|
+
0,
|
|
1092
|
+
polling.timeoutMs - (now() - startedAt),
|
|
1093
|
+
);
|
|
1094
|
+
if (remainingTimeoutMs <= 0) return { kind: 'timed_out' };
|
|
1095
|
+
const abortSignal = AbortSignal.timeout(Math.ceil(remainingTimeoutMs));
|
|
1096
|
+
try {
|
|
1097
|
+
const response = await input.executeAction({
|
|
1098
|
+
...request,
|
|
1099
|
+
jobId,
|
|
1100
|
+
timeoutMs: Math.ceil(remainingTimeoutMs),
|
|
1101
|
+
abortSignal,
|
|
1102
|
+
});
|
|
1103
|
+
return now() - startedAt >= polling.timeoutMs || abortSignal.aborted
|
|
1104
|
+
? { kind: 'timed_out' }
|
|
1105
|
+
: { kind: 'response', response };
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
if (abortSignal.aborted) return { kind: 'timed_out' };
|
|
1108
|
+
if (!(error instanceof AsyncOperationCompanionPacingError)) throw error;
|
|
1109
|
+
// This refusal applies only to the current companion read. The launch
|
|
1110
|
+
// already owns its job and hold. Stay inside their original deadline
|
|
1111
|
+
// and repeat this exact action/page, never the enclosing start request.
|
|
1112
|
+
pacingAttempt += 1;
|
|
1113
|
+
const delayMs = Math.max(
|
|
1114
|
+
1,
|
|
1115
|
+
asyncOperationRateLimitPollDelayMs({
|
|
1116
|
+
contract: effectiveContract,
|
|
1117
|
+
pollAttempt: request.pollAttempt,
|
|
1118
|
+
rateLimitAttempt: pacingAttempt,
|
|
1119
|
+
retryAfterMs: error.retryAfterMs,
|
|
1120
|
+
}),
|
|
1121
|
+
);
|
|
1122
|
+
const remainingAfterRefusal = Math.max(
|
|
1123
|
+
0,
|
|
1124
|
+
polling.timeoutMs - (now() - startedAt),
|
|
1125
|
+
);
|
|
1126
|
+
if (remainingAfterRefusal <= 0) return { kind: 'timed_out' };
|
|
1127
|
+
await sleep(Math.min(delayMs, remainingAfterRefusal));
|
|
1128
|
+
}
|
|
1096
1129
|
}
|
|
1097
1130
|
};
|
|
1098
1131
|
while (true) {
|
|
@@ -17609,6 +17609,22 @@ export async function readPostgresSchedulerObservationAdmissions(
|
|
|
17609
17609
|
}));
|
|
17610
17610
|
}
|
|
17611
17611
|
|
|
17612
|
+
/** Admission authority: includes live runs whose Convex projection has not landed. */
|
|
17613
|
+
export async function listPostgresSchedulerActiveRunsForOrg(
|
|
17614
|
+
client: PostgresSchedulerQueryClient,
|
|
17615
|
+
orgId: string,
|
|
17616
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'>,
|
|
17617
|
+
): Promise<string[]> {
|
|
17618
|
+
if (!orgId.trim()) throw new Error('Workspace ID is required.');
|
|
17619
|
+
const result = await client.query<{ run_id: string }>(
|
|
17620
|
+
`SELECT run_id FROM ${tableName('runs', options)}
|
|
17621
|
+
WHERE org_id = $1 AND status IN ('queued', 'running', 'waiting')
|
|
17622
|
+
ORDER BY created_at, run_id LIMIT 5`,
|
|
17623
|
+
[orgId],
|
|
17624
|
+
);
|
|
17625
|
+
return result.rows.map((row) => row.run_id);
|
|
17626
|
+
}
|
|
17627
|
+
|
|
17612
17628
|
export async function readPostgresSchedulerRunStatus(
|
|
17613
17629
|
client: PostgresSchedulerQueryClient,
|
|
17614
17630
|
runId: string,
|
|
@@ -18770,26 +18786,25 @@ async function claimPostgresSchedulerOutboxPrefix(
|
|
|
18770
18786
|
*/
|
|
18771
18787
|
export async function readPostgresSchedulerOutboxNextDueAt(
|
|
18772
18788
|
client: PostgresSchedulerQueryClient,
|
|
18773
|
-
options?: Pick<PostgresSchedulerOptions, 'schema'
|
|
18789
|
+
options?: Pick<PostgresSchedulerOptions, 'schema'> & {
|
|
18790
|
+
lane?: 'lifecycle' | 'observability';
|
|
18791
|
+
},
|
|
18774
18792
|
): Promise<Date | null> {
|
|
18793
|
+
const lanes =
|
|
18794
|
+
options?.lane === 'lifecycle'
|
|
18795
|
+
? [false]
|
|
18796
|
+
: options?.lane === 'observability'
|
|
18797
|
+
? [true]
|
|
18798
|
+
: [false, true];
|
|
18775
18799
|
const result = await client.query<{ due_at: Date | string | null }>(
|
|
18776
18800
|
`
|
|
18777
18801
|
-- Seek distinct run identities without reading their pending fact tails.
|
|
18778
|
-
WITH RECURSIVE ${runLedgerPendingRunsSql(options)}, heads AS MATERIALIZED (
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
FROM ${tableName('outbox', options)} AS pending
|
|
18785
|
-
WHERE pending.run_id = pending_run.run_id
|
|
18786
|
-
AND pending.delivered_at IS NULL
|
|
18787
|
-
AND ${searchLaneSql('pending')} = false
|
|
18788
|
-
ORDER BY ${runLedgerOrderSql('pending')}
|
|
18789
|
-
LIMIT 1
|
|
18790
|
-
) AS head
|
|
18791
|
-
UNION ALL
|
|
18792
|
-
SELECT pending_run.run_id, true AS search_only, head.*
|
|
18802
|
+
WITH RECURSIVE ${runLedgerPendingRunsSql(options, options?.lane === undefined ? undefined : lanes[0])}, heads AS MATERIALIZED (
|
|
18803
|
+
${lanes
|
|
18804
|
+
.map(
|
|
18805
|
+
(
|
|
18806
|
+
searchOnly,
|
|
18807
|
+
) => ` SELECT pending_run.run_id, ${searchOnly} AS search_only, head.*
|
|
18793
18808
|
FROM pending_runs AS pending_run
|
|
18794
18809
|
CROSS JOIN LATERAL (
|
|
18795
18810
|
SELECT pending.created_at, pending.next_attempt_at,
|
|
@@ -18797,10 +18812,12 @@ export async function readPostgresSchedulerOutboxNextDueAt(
|
|
|
18797
18812
|
FROM ${tableName('outbox', options)} AS pending
|
|
18798
18813
|
WHERE pending.run_id = pending_run.run_id
|
|
18799
18814
|
AND pending.delivered_at IS NULL
|
|
18800
|
-
AND ${searchLaneSql('pending')} =
|
|
18815
|
+
AND ${searchLaneSql('pending')} = ${searchOnly}
|
|
18801
18816
|
ORDER BY ${runLedgerOrderSql('pending')}
|
|
18802
18817
|
LIMIT 1
|
|
18803
|
-
) AS head
|
|
18818
|
+
) AS head`,
|
|
18819
|
+
)
|
|
18820
|
+
.join('\n UNION ALL\n')}
|
|
18804
18821
|
)
|
|
18805
18822
|
SELECT min(greatest(
|
|
18806
18823
|
CASE
|
|
@@ -100,7 +100,7 @@ export type RunLedgerOutboxRecoveryDeps = {
|
|
|
100
100
|
) => Promise<PostgresSchedulerOutboxProjectionResult>;
|
|
101
101
|
observabilityParallelism?: number;
|
|
102
102
|
/** Earliest pending retry time or active lease expiry in the durable outbox. */
|
|
103
|
-
readNextDueAt: () => Promise<Date | null>;
|
|
103
|
+
readNextDueAt: (lane?: 'lifecycle' | 'observability') => Promise<Date | null>;
|
|
104
104
|
/** A direct LISTEN connection. It is a hint only; a null lease is safe. */
|
|
105
105
|
acquireNotifyHub: () => PgNotifyHubLease | null;
|
|
106
106
|
/** Bounded number of independent runs this replica may deliver at once. */
|
|
@@ -201,6 +201,15 @@ export function startRunLedgerOutboxRecovery(
|
|
|
201
201
|
let requested = false;
|
|
202
202
|
let failureCount = 0;
|
|
203
203
|
let timer: RecoveryTimer | null = null;
|
|
204
|
+
let timerAtMs: number | null = null;
|
|
205
|
+
let dueClockGeneration = 0;
|
|
206
|
+
let dueClockRead: Promise<void> | null = null;
|
|
207
|
+
let dueClockRefreshRequested = false;
|
|
208
|
+
let dueClockReadGeneration = 0;
|
|
209
|
+
let dueClockKnownLane: 'both' | 'lifecycle' | 'observability' | null = null;
|
|
210
|
+
let producerHintGeneration = 0;
|
|
211
|
+
let dueClockReadHintGeneration = 0;
|
|
212
|
+
let dueClockKnownHintGeneration = -1;
|
|
204
213
|
let drainPromise: Promise<void> | null = null;
|
|
205
214
|
let wakeActiveDrain: (() => void) | null = null;
|
|
206
215
|
let cooldownUntilMs: number | null = null;
|
|
@@ -208,23 +217,31 @@ export function startRunLedgerOutboxRecovery(
|
|
|
208
217
|
let unsubscribeNotify: (() => void) | undefined;
|
|
209
218
|
let unsubscribeState: (() => void) | undefined;
|
|
210
219
|
let requestDrain: (reason: string, runId?: string) => void = () => undefined;
|
|
211
|
-
|
|
212
|
-
|
|
220
|
+
// A producer hint may reveal a lease that the claim cannot acquire. A
|
|
221
|
+
// successful scoped claim's continuation does not force a metadata query
|
|
222
|
+
// while other deliveries keep the drain busy. Zero marks that continuation.
|
|
223
|
+
const lifecycleRunHints = new Map<string, number>();
|
|
224
|
+
const observabilityRunHints = new Map<string, number>();
|
|
213
225
|
const hasRequests = () =>
|
|
214
226
|
requested ||
|
|
215
227
|
observabilityRequested ||
|
|
216
228
|
lifecycleRunHints.size > 0 ||
|
|
217
229
|
observabilityRunHints.size > 0;
|
|
218
|
-
const addRunHint = (
|
|
219
|
-
hints
|
|
230
|
+
const addRunHint = (
|
|
231
|
+
hints: Map<string, number>,
|
|
232
|
+
runId: string,
|
|
233
|
+
discoveryGeneration = producerHintGeneration,
|
|
234
|
+
) => {
|
|
235
|
+
hints.set(runId, Math.max(discoveryGeneration, hints.get(runId) ?? 0));
|
|
220
236
|
if (hints.size <= MAX_NOTIFICATION_RUN_HINTS) return true;
|
|
221
237
|
hints.clear();
|
|
222
238
|
return false;
|
|
223
239
|
};
|
|
224
|
-
const takeRunHint = (hints:
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
227
|
-
|
|
240
|
+
const takeRunHint = (hints: Map<string, number>) => {
|
|
241
|
+
const hint = hints.entries().next().value;
|
|
242
|
+
if (!hint) return undefined;
|
|
243
|
+
hints.delete(hint[0]);
|
|
244
|
+
return { runId: hint[0], discoveryGeneration: hint[1] };
|
|
228
245
|
};
|
|
229
246
|
const inFlightStartedAt = new Map<number, number>();
|
|
230
247
|
let nextDispatchId = 0;
|
|
@@ -261,6 +278,7 @@ export function startRunLedgerOutboxRecovery(
|
|
|
261
278
|
if (!timer) return;
|
|
262
279
|
clearTimer(timer);
|
|
263
280
|
timer = null;
|
|
281
|
+
timerAtMs = null;
|
|
264
282
|
};
|
|
265
283
|
|
|
266
284
|
const listenerIsAvailable = () => hubLease?.hub.state() === 'listening';
|
|
@@ -285,8 +303,8 @@ export function startRunLedgerOutboxRecovery(
|
|
|
285
303
|
|
|
286
304
|
const startDrain = (reason: string) => {
|
|
287
305
|
if (closed) return;
|
|
288
|
-
if (cooldownUntilMs !== null
|
|
289
|
-
|
|
306
|
+
if (cooldownUntilMs !== null) return;
|
|
307
|
+
// Scoped producer hints cannot cancel another run's retry/lease clock.
|
|
290
308
|
if (drainPromise) {
|
|
291
309
|
wakeActiveDrain?.();
|
|
292
310
|
return;
|
|
@@ -308,6 +326,10 @@ export function startRunLedgerOutboxRecovery(
|
|
|
308
326
|
|
|
309
327
|
requestDrain = (reason: string, runId?: string) => {
|
|
310
328
|
if (closed) return;
|
|
329
|
+
// Tag producer hints so a later missed claim can reject an older empty
|
|
330
|
+
// snapshot. Productive hints need no metadata refresh while work is busy.
|
|
331
|
+
producerHintGeneration += 1;
|
|
332
|
+
dueClockKnownLane = null;
|
|
311
333
|
if (runId === undefined) {
|
|
312
334
|
requested = true;
|
|
313
335
|
observabilityRequested = !!deps.dispatchObservabilityOnce;
|
|
@@ -354,27 +376,32 @@ export function startRunLedgerOutboxRecovery(
|
|
|
354
376
|
error: error instanceof Error ? error.message : String(error),
|
|
355
377
|
});
|
|
356
378
|
if (closed) return;
|
|
379
|
+
dueClockGeneration += 1;
|
|
380
|
+
dueClockKnownLane = null;
|
|
357
381
|
clearWakeTimer();
|
|
358
382
|
cooldownUntilMs = now() + delayMs;
|
|
383
|
+
timerAtMs = cooldownUntilMs;
|
|
359
384
|
timer = setTimer(() => {
|
|
360
385
|
timer = null;
|
|
386
|
+
timerAtMs = null;
|
|
361
387
|
cooldownUntilMs = null;
|
|
362
388
|
requestDrain('recovery_error_retry');
|
|
363
389
|
}, delayMs);
|
|
364
390
|
timer.unref?.();
|
|
365
391
|
};
|
|
366
392
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
scheduleFailureRetry('next_due', error);
|
|
375
|
-
return;
|
|
393
|
+
// A retained global probe already owns discovery for its lane. Read only
|
|
394
|
+
// the other lane, so a full lane's ready head cannot create a zero-delay loop.
|
|
395
|
+
const dueClockLane = (): 'both' | 'lifecycle' | 'observability' | null => {
|
|
396
|
+
if (requested) {
|
|
397
|
+
return deps.dispatchObservabilityOnce && !observabilityRequested
|
|
398
|
+
? 'observability'
|
|
399
|
+
: null;
|
|
376
400
|
}
|
|
377
|
-
|
|
401
|
+
return observabilityRequested ? 'lifecycle' : 'both';
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const scheduleDueWake = (dueAt: Date | null, provisional = false) => {
|
|
378
405
|
// When LISTEN is healthy the exact durable due time is sufficient. If the
|
|
379
406
|
// optional listener is absent, down, or lost an election, an indexed
|
|
380
407
|
// short safety wake discovers a fresh row that otherwise had no
|
|
@@ -383,24 +410,101 @@ export function startRunLedgerOutboxRecovery(
|
|
|
383
410
|
const delayMs = listenerIsAvailable()
|
|
384
411
|
? exactDueMs
|
|
385
412
|
: Math.min(exactDueMs ?? Infinity, notifyUnavailableSafetyWakeMs);
|
|
386
|
-
if (delayMs === null || !Number.isFinite(delayMs)) return;
|
|
413
|
+
if (provisional && (delayMs === null || !Number.isFinite(delayMs))) return;
|
|
414
|
+
const boundedDelayMs = Math.min(MAX_TIMEOUT_MS, delayMs ?? Infinity);
|
|
415
|
+
const wakeAtMs = now() + boundedDelayMs;
|
|
416
|
+
if (provisional && timerAtMs !== null && timerAtMs <= wakeAtMs) return;
|
|
387
417
|
clearWakeTimer();
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
418
|
+
if (delayMs === null || !Number.isFinite(delayMs)) return;
|
|
419
|
+
timerAtMs = wakeAtMs;
|
|
420
|
+
timer = setTimer(() => {
|
|
421
|
+
timer = null;
|
|
422
|
+
timerAtMs = null;
|
|
423
|
+
dueClockGeneration += 1;
|
|
424
|
+
if (!listenerIsAvailable()) attachNotifyHub();
|
|
425
|
+
requestDrain(dueAt ? 'next_due' : 'listener_safety_wake');
|
|
426
|
+
}, boundedDelayMs);
|
|
396
427
|
timer.unref?.();
|
|
397
428
|
};
|
|
398
429
|
|
|
430
|
+
const readNextDueClock = async (
|
|
431
|
+
generation: number,
|
|
432
|
+
lane: Exclude<ReturnType<typeof dueClockLane>, null>,
|
|
433
|
+
hintGeneration: number,
|
|
434
|
+
): Promise<void> => {
|
|
435
|
+
let dueAt: Date | null;
|
|
436
|
+
try {
|
|
437
|
+
dueAt = await deps.readNextDueAt(lane === 'both' ? undefined : lane);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
if (
|
|
440
|
+
closed ||
|
|
441
|
+
cooldownUntilMs !== null ||
|
|
442
|
+
generation !== dueClockGeneration
|
|
443
|
+
) {
|
|
444
|
+
logger.warn('[run-ledger-outbox.recovery_retry]', {
|
|
445
|
+
stage: 'next_due',
|
|
446
|
+
superseded: true,
|
|
447
|
+
error: error instanceof Error ? error.message : String(error),
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
scheduleFailureRetry('next_due', error);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (closed || cooldownUntilMs !== null) return;
|
|
455
|
+
const currentLane = dueClockLane();
|
|
456
|
+
if (
|
|
457
|
+
generation !== dueClockGeneration ||
|
|
458
|
+
lane !== currentLane ||
|
|
459
|
+
dueClockRefreshRequested
|
|
460
|
+
) {
|
|
461
|
+
dueClockRefreshRequested = true;
|
|
462
|
+
// Hints and retry completions invalidate freshness, not a known due
|
|
463
|
+
// time. Only the currently discoverable lane may supply a provisional
|
|
464
|
+
// wake, which advances the timer without caching a stale snapshot.
|
|
465
|
+
if (lane === currentLane && dueAt) scheduleDueWake(dueAt, true);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
failureCount = 0;
|
|
469
|
+
dueClockKnownLane = lane;
|
|
470
|
+
dueClockKnownHintGeneration = hintGeneration;
|
|
471
|
+
scheduleDueWake(dueAt);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const scheduleNextDue = (refresh = false): Promise<void> => {
|
|
475
|
+
if (closed || cooldownUntilMs !== null) return Promise.resolve();
|
|
476
|
+
if (dueClockRead) {
|
|
477
|
+
// A fired clock invalidates even an unchanged coalesced read. Retain
|
|
478
|
+
// that discovery debt until the old read settles.
|
|
479
|
+
dueClockRefreshRequested ||=
|
|
480
|
+
refresh || dueClockReadGeneration !== dueClockGeneration;
|
|
481
|
+
return dueClockRead;
|
|
482
|
+
}
|
|
483
|
+
const lane = dueClockLane();
|
|
484
|
+
if (lane === null || (dueClockKnownLane === lane && !refresh))
|
|
485
|
+
return Promise.resolve();
|
|
486
|
+
dueClockReadGeneration = dueClockGeneration;
|
|
487
|
+
dueClockReadHintGeneration = producerHintGeneration;
|
|
488
|
+
dueClockRead = readNextDueClock(
|
|
489
|
+
dueClockReadGeneration,
|
|
490
|
+
lane,
|
|
491
|
+
dueClockReadHintGeneration,
|
|
492
|
+
).finally(() => {
|
|
493
|
+
dueClockRead = null;
|
|
494
|
+
if (dueClockRefreshRequested) {
|
|
495
|
+
dueClockRefreshRequested = false;
|
|
496
|
+
if (!closed && cooldownUntilMs === null) void scheduleNextDue(true);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
return dueClockRead;
|
|
500
|
+
};
|
|
501
|
+
|
|
399
502
|
const drain = async (reason: string): Promise<void> => {
|
|
400
503
|
type Delivery = {
|
|
401
504
|
id: symbol;
|
|
402
505
|
observability: boolean;
|
|
403
506
|
runId?: string;
|
|
507
|
+
discoveryGeneration: number;
|
|
404
508
|
result:
|
|
405
509
|
| PromiseSettledResult<PostgresSchedulerOutboxProjectionResult>
|
|
406
510
|
| { status: 'skipped' };
|
|
@@ -435,7 +539,11 @@ export function startRunLedgerOutboxRecovery(
|
|
|
435
539
|
completed = attempted = projected = retrying = blocked = 0;
|
|
436
540
|
};
|
|
437
541
|
|
|
438
|
-
const launch = (
|
|
542
|
+
const launch = (
|
|
543
|
+
observability = false,
|
|
544
|
+
runId?: string,
|
|
545
|
+
discoveryGeneration = producerHintGeneration,
|
|
546
|
+
) => {
|
|
439
547
|
const id = Symbol();
|
|
440
548
|
pending.add(id);
|
|
441
549
|
if (observability) pendingObservability.add(id);
|
|
@@ -444,13 +552,19 @@ export function startRunLedgerOutboxRecovery(
|
|
|
444
552
|
dispatchFailed = true;
|
|
445
553
|
scheduleFailureRetry('dispatch', result.reason);
|
|
446
554
|
}
|
|
447
|
-
deliveries.push({
|
|
555
|
+
deliveries.push({
|
|
556
|
+
id,
|
|
557
|
+
observability,
|
|
558
|
+
runId,
|
|
559
|
+
discoveryGeneration,
|
|
560
|
+
result,
|
|
561
|
+
});
|
|
448
562
|
wakeActiveDrain?.();
|
|
449
563
|
};
|
|
450
564
|
// Subscribe once per delivery. Repeated Promise.race calls would retain
|
|
451
565
|
// a new reaction on a slow callback every time a faster slot completes.
|
|
452
566
|
void Promise.resolve().then(async () => {
|
|
453
|
-
if (closed || dispatchFailed) {
|
|
567
|
+
if (closed || dispatchFailed || cooldownUntilMs !== null) {
|
|
454
568
|
settle({ status: 'skipped' });
|
|
455
569
|
return;
|
|
456
570
|
}
|
|
@@ -468,32 +582,34 @@ export function startRunLedgerOutboxRecovery(
|
|
|
468
582
|
const canProbeLifecycle = () =>
|
|
469
583
|
!closed &&
|
|
470
584
|
!dispatchFailed &&
|
|
585
|
+
cooldownUntilMs === null &&
|
|
471
586
|
(requested || lifecycleRunHints.size > 0) &&
|
|
472
587
|
pending.size < parallelism &&
|
|
473
588
|
pending.size - pendingObservability.size < lifecycleParallelism;
|
|
474
589
|
const canProbeObservability = () =>
|
|
475
590
|
!closed &&
|
|
476
591
|
!dispatchFailed &&
|
|
592
|
+
cooldownUntilMs === null &&
|
|
477
593
|
(observabilityRequested || observabilityRunHints.size > 0) &&
|
|
478
594
|
pending.size < parallelism &&
|
|
479
595
|
pendingObservability.size < observabilityParallelism;
|
|
480
596
|
|
|
481
597
|
for (;;) {
|
|
482
|
-
if (!closed && !dispatchFailed) {
|
|
598
|
+
if (!closed && !dispatchFailed && cooldownUntilMs === null) {
|
|
483
599
|
// A wake grants one indexed probe, including when other slots are
|
|
484
600
|
// still delivering. Keep it pending when full so an empty result
|
|
485
601
|
// cannot consume a notification that arrived after its query began.
|
|
486
602
|
if (canProbeObservability()) {
|
|
487
|
-
const
|
|
603
|
+
const hint = observabilityRequested
|
|
488
604
|
? undefined
|
|
489
605
|
: takeRunHint(observabilityRunHints);
|
|
490
606
|
observabilityRequested = false;
|
|
491
|
-
launch(true, runId);
|
|
607
|
+
launch(true, hint?.runId, hint?.discoveryGeneration);
|
|
492
608
|
}
|
|
493
609
|
if (canProbeLifecycle()) {
|
|
494
|
-
const
|
|
610
|
+
const hint = requested ? undefined : takeRunHint(lifecycleRunHints);
|
|
495
611
|
requested = false;
|
|
496
|
-
launch(false, runId);
|
|
612
|
+
launch(false, hint?.runId, hint?.discoveryGeneration);
|
|
497
613
|
}
|
|
498
614
|
// Productive completion permits immediate use of every free slot.
|
|
499
615
|
// Empty results grant no refill, so idle work cannot spin on scans.
|
|
@@ -516,7 +632,9 @@ export function startRunLedgerOutboxRecovery(
|
|
|
516
632
|
}
|
|
517
633
|
if (pending.size === 0) {
|
|
518
634
|
reportDeliveries();
|
|
519
|
-
|
|
635
|
+
// Metadata discovery is single-flight, but never owns a claim slot.
|
|
636
|
+
// A slow snapshot must not delay a producer's next scoped claim.
|
|
637
|
+
if (!closed && !dispatchFailed) void scheduleNextDue();
|
|
520
638
|
return;
|
|
521
639
|
}
|
|
522
640
|
|
|
@@ -541,12 +659,46 @@ export function startRunLedgerOutboxRecovery(
|
|
|
541
659
|
continue;
|
|
542
660
|
}
|
|
543
661
|
const result = delivery.result.value;
|
|
662
|
+
const hintLane = delivery.observability ? 'observability' : 'lifecycle';
|
|
663
|
+
const missedProducerHint =
|
|
664
|
+
delivery.discoveryGeneration > 0 &&
|
|
665
|
+
result.attempted === 0 &&
|
|
666
|
+
(dueClockKnownHintGeneration < delivery.discoveryGeneration ||
|
|
667
|
+
(dueClockKnownLane !== 'both' && dueClockKnownLane !== hintLane));
|
|
668
|
+
if (result.attempted > 0 || missedProducerHint) dueClockKnownLane = null;
|
|
669
|
+
// A claim returning empty may have lost to an inline owner. If its
|
|
670
|
+
// notification followed the pending metadata snapshot, retain discovery
|
|
671
|
+
// debt so that snapshot cannot cache an obsolete empty result.
|
|
672
|
+
if (
|
|
673
|
+
missedProducerHint &&
|
|
674
|
+
dueClockRead &&
|
|
675
|
+
dueClockReadHintGeneration < delivery.discoveryGeneration
|
|
676
|
+
)
|
|
677
|
+
dueClockRefreshRequested = true;
|
|
678
|
+
if (result.retrying > 0) dueClockGeneration += 1;
|
|
544
679
|
completed += 1;
|
|
545
680
|
attempted += result.attempted;
|
|
546
681
|
projected += result.projected;
|
|
547
682
|
retrying += result.retrying;
|
|
548
683
|
blocked += result.failed;
|
|
549
684
|
if (completed >= parallelism) reportDeliveries();
|
|
685
|
+
// Discovery cannot wait for every independent HTTP delivery to finish.
|
|
686
|
+
// A retry commits a new clock; an empty global probe can expose that
|
|
687
|
+
// clock while another slot remains busy. A missed producer hint may
|
|
688
|
+
// expose an inline owner's lease; one later snapshot covers all earlier
|
|
689
|
+
// hints. Scoped success keeps its cheap continuation, and the idle drain
|
|
690
|
+
// still refreshes metadata after those continuations settle.
|
|
691
|
+
if (
|
|
692
|
+
!closed &&
|
|
693
|
+
!dispatchFailed &&
|
|
694
|
+
pending.size > deliveries.length &&
|
|
695
|
+
(result.retrying > 0 ||
|
|
696
|
+
missedProducerHint ||
|
|
697
|
+
((timer === null || dueClockKnownLane !== dueClockLane()) &&
|
|
698
|
+
delivery.runId === undefined &&
|
|
699
|
+
result.attempted === 0))
|
|
700
|
+
)
|
|
701
|
+
void scheduleNextDue(result.retrying > 0);
|
|
550
702
|
// Retryable rows own durable retry clocks. The SQL claim keeps their
|
|
551
703
|
// run ordered while successful slots keep unrelated runs moving.
|
|
552
704
|
if (delivery.runId !== undefined) {
|
|
@@ -556,12 +708,12 @@ export function startRunLedgerOutboxRecovery(
|
|
|
556
708
|
if (delivery.observability) {
|
|
557
709
|
if (
|
|
558
710
|
!observabilityRequested &&
|
|
559
|
-
!addRunHint(observabilityRunHints, delivery.runId)
|
|
711
|
+
!addRunHint(observabilityRunHints, delivery.runId, 0)
|
|
560
712
|
)
|
|
561
713
|
observabilityRequested = true;
|
|
562
714
|
} else if (
|
|
563
715
|
!requested &&
|
|
564
|
-
!addRunHint(lifecycleRunHints, delivery.runId)
|
|
716
|
+
!addRunHint(lifecycleRunHints, delivery.runId, 0)
|
|
565
717
|
)
|
|
566
718
|
requested = true;
|
|
567
719
|
}
|
|
@@ -594,6 +746,7 @@ export function startRunLedgerOutboxRecovery(
|
|
|
594
746
|
close: async () => {
|
|
595
747
|
if (closed) return;
|
|
596
748
|
closed = true;
|
|
749
|
+
dueClockGeneration += 1;
|
|
597
750
|
wakeActiveDrain?.();
|
|
598
751
|
clearWakeTimer();
|
|
599
752
|
cooldownUntilMs = null;
|
|
@@ -602,6 +755,7 @@ export function startRunLedgerOutboxRecovery(
|
|
|
602
755
|
unsubscribeNotify?.();
|
|
603
756
|
unsubscribeState?.();
|
|
604
757
|
await drainPromise;
|
|
758
|
+
await dueClockRead;
|
|
605
759
|
hubLease?.release();
|
|
606
760
|
},
|
|
607
761
|
};
|
|
@@ -859,10 +1013,11 @@ export function startRunLedgerOutboxRecoveryController(input: {
|
|
|
859
1013
|
runId?: string,
|
|
860
1014
|
) =>
|
|
861
1015
|
dispatchRunLedgerOutboxToLedgerBatchOnceWithClientScope(
|
|
862
|
-
(operation) =>
|
|
1016
|
+
(operation, trace) =>
|
|
863
1017
|
withRuntimeSchedulerClient(
|
|
864
1018
|
searchOnly ? observabilityPoolOptions : input.poolOptions,
|
|
865
1019
|
operation,
|
|
1020
|
+
trace,
|
|
866
1021
|
),
|
|
867
1022
|
{
|
|
868
1023
|
tokenAuthority: 'gateway',
|
|
@@ -901,9 +1056,12 @@ export function startRunLedgerOutboxRecoveryController(input: {
|
|
|
901
1056
|
notifyUnavailableSafetyWakeMs: runLedgerOutboxNotifySafetyWakeMs(
|
|
902
1057
|
schedulerOptions.schema,
|
|
903
1058
|
),
|
|
904
|
-
readNextDueAt: () =>
|
|
1059
|
+
readNextDueAt: (lane) =>
|
|
905
1060
|
withRuntimeSchedulerClient(input.poolOptions, (client) =>
|
|
906
|
-
readPostgresSchedulerOutboxNextDueAt(client,
|
|
1061
|
+
readPostgresSchedulerOutboxNextDueAt(client, {
|
|
1062
|
+
...schedulerOptions,
|
|
1063
|
+
lane,
|
|
1064
|
+
}),
|
|
907
1065
|
),
|
|
908
1066
|
});
|
|
909
1067
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EXIT_WORKSPACE_MISMATCH,
|
|
3
|
+
authStatusLine,
|
|
3
4
|
clearPendingClaim,
|
|
4
5
|
clearSatisfiedPendingClaim,
|
|
5
6
|
handleRegister,
|
|
@@ -9,16 +10,17 @@ import {
|
|
|
9
10
|
registerAuthCommands,
|
|
10
11
|
reportSetupCompleted,
|
|
11
12
|
resolvePendingAuthClaimForResume
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-HGLESMAC.mjs";
|
|
14
|
+
import "./chunk-EL62MLRD.mjs";
|
|
15
|
+
import "./chunk-VZBNBCEB.mjs";
|
|
15
16
|
import "./chunk-DLIRSHV6.mjs";
|
|
16
|
-
import "./chunk-N47R7S3E.mjs";
|
|
17
17
|
import "./chunk-NXGCO4JB.mjs";
|
|
18
|
+
import "./chunk-SJY4SUCH.mjs";
|
|
18
19
|
import "./chunk-WZYKRD7T.mjs";
|
|
19
20
|
import "./chunk-KRD6H754.mjs";
|
|
20
21
|
export {
|
|
21
22
|
EXIT_WORKSPACE_MISMATCH,
|
|
23
|
+
authStatusLine,
|
|
22
24
|
clearPendingClaim,
|
|
23
25
|
clearSatisfiedPendingClaim,
|
|
24
26
|
handleRegister,
|