@jini-ai/integrations 0.3.1 → 0.3.3
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/media-providers/dispatch/async-operation-store.d.ts +154 -0
- package/dist/media-providers/dispatch/async-operation-store.d.ts.map +1 -0
- package/dist/media-providers/dispatch/async-operation-store.js +277 -0
- package/dist/media-providers/dispatch/async-operation-store.js.map +1 -0
- package/dist/media-providers/dispatch/index.d.ts +8 -0
- package/dist/media-providers/dispatch/index.d.ts.map +1 -1
- package/dist/media-providers/dispatch/index.js +7 -0
- package/dist/media-providers/dispatch/index.js.map +1 -1
- package/dist/media-providers/dispatch/operation-runtime.d.ts +93 -0
- package/dist/media-providers/dispatch/operation-runtime.d.ts.map +1 -0
- package/dist/media-providers/dispatch/operation-runtime.js +262 -0
- package/dist/media-providers/dispatch/operation-runtime.js.map +1 -0
- package/dist/media-providers/dispatch/polling-adapter.d.ts +103 -0
- package/dist/media-providers/dispatch/polling-adapter.d.ts.map +1 -0
- package/dist/media-providers/dispatch/polling-adapter.js +30 -0
- package/dist/media-providers/dispatch/polling-adapter.js.map +1 -0
- package/dist/media-providers/dispatch/providers/imagerouter-video-async.d.ts +21 -0
- package/dist/media-providers/dispatch/providers/imagerouter-video-async.d.ts.map +1 -0
- package/dist/media-providers/dispatch/providers/imagerouter-video-async.js +141 -0
- package/dist/media-providers/dispatch/providers/imagerouter-video-async.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AsyncOperationStore` — the durable operation row behind submit-then-poll media generation.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists alongside `task-store.ts`'s `MediaTaskStore`: they model different things and
|
|
5
|
+
* only one of them survives a restart usefully. `MediaTaskStore` is a *task tracker* — it records
|
|
6
|
+
* that a generation happened and its `reconcileOnBoot` deliberately marks every still-in-flight
|
|
7
|
+
* task `'interrupted'`, because a synchronous `fetch` that died with its process genuinely cannot
|
|
8
|
+
* be resumed. This store is the opposite case: a vendor-side job that is still running *on the
|
|
9
|
+
* vendor* after our process died, and whose result is still retrievable by polling. It therefore
|
|
10
|
+
* carries the four columns a poll loop needs and a task tracker does not — `attempts`,
|
|
11
|
+
* `nextPollAt`, a worker `lease`, and an absolute `deadlineAt` — and its `reconcileOnBoot`
|
|
12
|
+
* *releases* leases rather than terminating the work.
|
|
13
|
+
*
|
|
14
|
+
* Two independent bounds are mandatory, not redundant: `maxAttempts` catches a vendor that answers
|
|
15
|
+
* "pending" forever, and `deadlineAt` catches a vendor that stops answering at all. Neither one
|
|
16
|
+
* subsumes the other.
|
|
17
|
+
*
|
|
18
|
+
* `state` is the vendor's own resumption handle (a job id, a poll URL). It is deliberately
|
|
19
|
+
* validated on every write to reject credential material: the row outlives the process and is the
|
|
20
|
+
* one place a leaked key would become durable. Credentials are re-resolved per poll tick through
|
|
21
|
+
* the signer seam instead — see `polling-adapter.ts`.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* `unknown` is a real terminal-ish state, not a failure: it means the operation may or may not
|
|
25
|
+
* have taken effect at the vendor and cannot be safely retried without an idempotency proof. It is
|
|
26
|
+
* reachable only from a crash gap or a blown deadline, and exists so those never masquerade as
|
|
27
|
+
* either success or a retryable failure.
|
|
28
|
+
*/
|
|
29
|
+
export type AsyncOperationStatus = 'submitted' | 'polling' | 'succeeded' | 'failed' | 'unknown';
|
|
30
|
+
/**
|
|
31
|
+
* The persisted shape's version discriminator. Bumped whenever `AsyncOperationRecord`'s stored
|
|
32
|
+
* shape changes; `hydrateAsyncOperationRecord` branches on it so an old row is upgraded on read
|
|
33
|
+
* rather than rescued by a migration script.
|
|
34
|
+
*
|
|
35
|
+
* This is deliberately the same mechanism `ffb5ce44` used to close the AAD gap — a version column,
|
|
36
|
+
* a read path that branches on it, and an idempotent backfill only where one is actually needed —
|
|
37
|
+
* rather than a second migration playbook.
|
|
38
|
+
*/
|
|
39
|
+
export declare const ASYNC_OPERATION_SCHEMA_VERSION = 1;
|
|
40
|
+
export declare const CREDENTIAL_IN_STATE_MESSAGE = "async operation state must never carry credential material \u2014 credentials are re-resolved per poll tick through the signer seam";
|
|
41
|
+
export interface AsyncOperationError {
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly status?: number;
|
|
44
|
+
readonly code?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The generated bytes, held base64-encoded so the row stays JSON-serializable for a durable adapter. */
|
|
47
|
+
export interface AsyncOperationResult {
|
|
48
|
+
readonly bytesBase64: string;
|
|
49
|
+
readonly providerNote: string;
|
|
50
|
+
readonly suggestedExt?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface AsyncOperationRecord {
|
|
53
|
+
/** Shape discriminator — see `ASYNC_OPERATION_SCHEMA_VERSION`. Stamped once at create and never rewritten. */
|
|
54
|
+
readonly schemaVersion: number;
|
|
55
|
+
readonly id: string;
|
|
56
|
+
readonly providerId: string;
|
|
57
|
+
readonly routeKey: string;
|
|
58
|
+
/** Opaque host-supplied scoping key (a run id, a workspace id) — never a domain noun this package knows. */
|
|
59
|
+
readonly ownerRef: string;
|
|
60
|
+
readonly status: AsyncOperationStatus;
|
|
61
|
+
readonly attempts: number;
|
|
62
|
+
/** Bound 1: caps a vendor that answers "pending" forever. */
|
|
63
|
+
readonly maxAttempts: number;
|
|
64
|
+
/** Absolute epoch ms after which this operation may not be polled again. Bound 2: caps a vendor that stops answering. */
|
|
65
|
+
readonly deadlineAt: number;
|
|
66
|
+
readonly nextPollAt: number;
|
|
67
|
+
readonly leaseOwner: string | null;
|
|
68
|
+
readonly leaseExpiresAt: number | null;
|
|
69
|
+
/** The vendor's resumption handle. Never credential material — see `CREDENTIAL_IN_STATE_MESSAGE`. */
|
|
70
|
+
readonly state: Readonly<Record<string, unknown>> | null;
|
|
71
|
+
readonly result: AsyncOperationResult | null;
|
|
72
|
+
readonly error: AsyncOperationError | null;
|
|
73
|
+
readonly createdAt: number;
|
|
74
|
+
readonly updatedAt: number;
|
|
75
|
+
}
|
|
76
|
+
export interface AsyncOperationCreateInput {
|
|
77
|
+
readonly id: string;
|
|
78
|
+
readonly providerId: string;
|
|
79
|
+
readonly routeKey: string;
|
|
80
|
+
readonly ownerRef: string;
|
|
81
|
+
readonly maxAttempts: number;
|
|
82
|
+
readonly deadlineAt: number;
|
|
83
|
+
readonly nextPollAt?: number;
|
|
84
|
+
readonly status?: AsyncOperationStatus;
|
|
85
|
+
readonly state?: Readonly<Record<string, unknown>> | null;
|
|
86
|
+
}
|
|
87
|
+
export interface AsyncOperationPatch {
|
|
88
|
+
readonly status?: AsyncOperationStatus;
|
|
89
|
+
readonly attempts?: number;
|
|
90
|
+
readonly nextPollAt?: number;
|
|
91
|
+
readonly state?: Readonly<Record<string, unknown>> | null;
|
|
92
|
+
readonly result?: AsyncOperationResult | null;
|
|
93
|
+
readonly error?: AsyncOperationError | null;
|
|
94
|
+
readonly leaseOwner?: string | null;
|
|
95
|
+
readonly leaseExpiresAt?: number | null;
|
|
96
|
+
}
|
|
97
|
+
export interface AsyncOperationClaimOptions {
|
|
98
|
+
readonly now: number;
|
|
99
|
+
readonly leaseOwner: string;
|
|
100
|
+
readonly leaseMs: number;
|
|
101
|
+
readonly limit?: number;
|
|
102
|
+
}
|
|
103
|
+
export interface AsyncOperationReconcileResult {
|
|
104
|
+
/** Rows whose worker died holding a lease — released for another worker, status untouched. */
|
|
105
|
+
readonly leasesReleased: number;
|
|
106
|
+
/** Rows that blew their absolute deadline while unattended — moved to `unknown`, never to `failed`. */
|
|
107
|
+
readonly deadlineExpired: number;
|
|
108
|
+
}
|
|
109
|
+
export interface AsyncOperationStore {
|
|
110
|
+
create(input: AsyncOperationCreateInput): Promise<AsyncOperationRecord>;
|
|
111
|
+
get(id: string): Promise<AsyncOperationRecord | null>;
|
|
112
|
+
update(id: string, patch: AsyncOperationPatch): Promise<AsyncOperationRecord | null>;
|
|
113
|
+
listByOwner(ownerRef: string): Promise<AsyncOperationRecord[]>;
|
|
114
|
+
/** Atomically leases every due, unleased, non-terminal operation and returns the leased rows. */
|
|
115
|
+
claimDue(options: AsyncOperationClaimOptions): Promise<AsyncOperationRecord[]>;
|
|
116
|
+
releaseLease(id: string): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Boot-time recovery. Unlike `MediaTaskStore.reconcileOnBoot`, this does NOT terminate in-flight
|
|
119
|
+
* work: a vendor-side job outlives our process, so the row stays pollable and only its dead
|
|
120
|
+
* lease is cleared. Rows already past their absolute deadline go to `unknown` — never `failed`,
|
|
121
|
+
* because an unattended deadline says nothing about whether the vendor did the work.
|
|
122
|
+
*/
|
|
123
|
+
reconcileOnBoot(options: {
|
|
124
|
+
now: number;
|
|
125
|
+
}): Promise<AsyncOperationReconcileResult>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Rejects credential material anywhere in an operation `state` object.
|
|
129
|
+
*
|
|
130
|
+
* @throws `Error(CREDENTIAL_IN_STATE_MESSAGE)` when any key at any depth matches
|
|
131
|
+
* `CREDENTIAL_KEY_PATTERN`.
|
|
132
|
+
* @complexity O(n) in the total number of keys, bounded by `MAX_STATE_DEPTH` against a cyclic or
|
|
133
|
+
* pathologically nested object.
|
|
134
|
+
*/
|
|
135
|
+
export declare function assertNoCredentialMaterial(state: Readonly<Record<string, unknown>> | null | undefined): void;
|
|
136
|
+
/**
|
|
137
|
+
* The branching read path for a persisted operation row.
|
|
138
|
+
*
|
|
139
|
+
* A durable adapter calls this on every row it loads. A row with no discriminator predates
|
|
140
|
+
* versioning (v0) and is upgraded in place on read; a row from a newer build is refused loudly
|
|
141
|
+
* rather than misread, because silently reinterpreting an unknown shape is how a stored operation
|
|
142
|
+
* becomes a duplicate vendor charge.
|
|
143
|
+
*
|
|
144
|
+
* @throws When `schemaVersion` is newer than this build understands.
|
|
145
|
+
* @complexity O(1).
|
|
146
|
+
*/
|
|
147
|
+
export declare function hydrateAsyncOperationRecord(raw: Record<string, unknown>): AsyncOperationRecord;
|
|
148
|
+
/**
|
|
149
|
+
* Creates the in-memory reference `AsyncOperationStore`. No persistence — a durable adapter
|
|
150
|
+
* implements the same interface; every method here is written so the equivalent SQL is a direct
|
|
151
|
+
* translation (`claimDue` in particular is a single conditional UPDATE ... RETURNING).
|
|
152
|
+
*/
|
|
153
|
+
export declare function createInMemoryAsyncOperationStore(): AsyncOperationStore;
|
|
154
|
+
//# sourceMappingURL=async-operation-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-operation-store.d.ts","sourceRoot":"","sources":["../../../src/media-providers/dispatch/async-operation-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AsyncOperationStore` — the durable operation row behind submit-then-poll media generation.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists alongside `task-store.ts`'s `MediaTaskStore`: they model different things and
|
|
5
|
+
* only one of them survives a restart usefully. `MediaTaskStore` is a *task tracker* — it records
|
|
6
|
+
* that a generation happened and its `reconcileOnBoot` deliberately marks every still-in-flight
|
|
7
|
+
* task `'interrupted'`, because a synchronous `fetch` that died with its process genuinely cannot
|
|
8
|
+
* be resumed. This store is the opposite case: a vendor-side job that is still running *on the
|
|
9
|
+
* vendor* after our process died, and whose result is still retrievable by polling. It therefore
|
|
10
|
+
* carries the four columns a poll loop needs and a task tracker does not — `attempts`,
|
|
11
|
+
* `nextPollAt`, a worker `lease`, and an absolute `deadlineAt` — and its `reconcileOnBoot`
|
|
12
|
+
* *releases* leases rather than terminating the work.
|
|
13
|
+
*
|
|
14
|
+
* Two independent bounds are mandatory, not redundant: `maxAttempts` catches a vendor that answers
|
|
15
|
+
* "pending" forever, and `deadlineAt` catches a vendor that stops answering at all. Neither one
|
|
16
|
+
* subsumes the other.
|
|
17
|
+
*
|
|
18
|
+
* `state` is the vendor's own resumption handle (a job id, a poll URL). It is deliberately
|
|
19
|
+
* validated on every write to reject credential material: the row outlives the process and is the
|
|
20
|
+
* one place a leaked key would become durable. Credentials are re-resolved per poll tick through
|
|
21
|
+
* the signer seam instead — see `polling-adapter.ts`.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The persisted shape's version discriminator. Bumped whenever `AsyncOperationRecord`'s stored
|
|
25
|
+
* shape changes; `hydrateAsyncOperationRecord` branches on it so an old row is upgraded on read
|
|
26
|
+
* rather than rescued by a migration script.
|
|
27
|
+
*
|
|
28
|
+
* This is deliberately the same mechanism `ffb5ce44` used to close the AAD gap — a version column,
|
|
29
|
+
* a read path that branches on it, and an idempotent backfill only where one is actually needed —
|
|
30
|
+
* rather than a second migration playbook.
|
|
31
|
+
*/
|
|
32
|
+
export const ASYNC_OPERATION_SCHEMA_VERSION = 1;
|
|
33
|
+
const TERMINAL_STATUSES = new Set(['succeeded', 'failed', 'unknown']);
|
|
34
|
+
const ALLOWED_TRANSITIONS = {
|
|
35
|
+
submitted: new Set(['submitted', 'polling', 'succeeded', 'failed', 'unknown']),
|
|
36
|
+
polling: new Set(['polling', 'succeeded', 'failed', 'unknown']),
|
|
37
|
+
succeeded: new Set(['succeeded']),
|
|
38
|
+
failed: new Set(['failed']),
|
|
39
|
+
unknown: new Set(['unknown']),
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Keys whose presence in `state` indicates credential material. Matched case-insensitively against
|
|
43
|
+
* every key at every depth. Deliberately a closed denylist of the shapes this package's own
|
|
44
|
+
* adapters actually produce (`ProviderCredentials.apiKey`, an `authorization` header, a raw
|
|
45
|
+
* bearer/secret/token) rather than a heuristic on values, so the failure is a loud, explainable
|
|
46
|
+
* rejection instead of a guess.
|
|
47
|
+
*/
|
|
48
|
+
const CREDENTIAL_KEY_PATTERN = /^(apikey|api_key|authorization|bearer|secret|password|access_token|accesstoken|refresh_token|refreshtoken)$/i;
|
|
49
|
+
export const CREDENTIAL_IN_STATE_MESSAGE = 'async operation state must never carry credential material — credentials are re-resolved per poll tick through the signer seam';
|
|
50
|
+
/**
|
|
51
|
+
* Rejects credential material anywhere in an operation `state` object.
|
|
52
|
+
*
|
|
53
|
+
* @throws `Error(CREDENTIAL_IN_STATE_MESSAGE)` when any key at any depth matches
|
|
54
|
+
* `CREDENTIAL_KEY_PATTERN`.
|
|
55
|
+
* @complexity O(n) in the total number of keys, bounded by `MAX_STATE_DEPTH` against a cyclic or
|
|
56
|
+
* pathologically nested object.
|
|
57
|
+
*/
|
|
58
|
+
export function assertNoCredentialMaterial(state) {
|
|
59
|
+
if (state == null)
|
|
60
|
+
return;
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const walk = (value, depth) => {
|
|
63
|
+
if (depth > 12 || value === null || typeof value !== 'object')
|
|
64
|
+
return;
|
|
65
|
+
if (seen.has(value))
|
|
66
|
+
return;
|
|
67
|
+
seen.add(value);
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
for (const entry of value)
|
|
70
|
+
walk(entry, depth + 1);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
74
|
+
if (CREDENTIAL_KEY_PATTERN.test(key)) {
|
|
75
|
+
throw new Error(CREDENTIAL_IN_STATE_MESSAGE);
|
|
76
|
+
}
|
|
77
|
+
walk(entry, depth + 1);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
walk(state, 0);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The branching read path for a persisted operation row.
|
|
84
|
+
*
|
|
85
|
+
* A durable adapter calls this on every row it loads. A row with no discriminator predates
|
|
86
|
+
* versioning (v0) and is upgraded in place on read; a row from a newer build is refused loudly
|
|
87
|
+
* rather than misread, because silently reinterpreting an unknown shape is how a stored operation
|
|
88
|
+
* becomes a duplicate vendor charge.
|
|
89
|
+
*
|
|
90
|
+
* @throws When `schemaVersion` is newer than this build understands.
|
|
91
|
+
* @complexity O(1).
|
|
92
|
+
*/
|
|
93
|
+
export function hydrateAsyncOperationRecord(raw) {
|
|
94
|
+
const stored = typeof raw.schemaVersion === 'number' ? raw.schemaVersion : 0;
|
|
95
|
+
if (stored > ASYNC_OPERATION_SCHEMA_VERSION) {
|
|
96
|
+
throw new Error(`async operation row was written at schema version ${stored}, newer than this build understands (${ASYNC_OPERATION_SCHEMA_VERSION}) — upgrade rather than risk misreading it`);
|
|
97
|
+
}
|
|
98
|
+
// v0 -> v1 added only the discriminator itself, so the upgrade is a stamp. A later version adds
|
|
99
|
+
// its own branch here; the row is never rewritten on disk just to be readable.
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
return {
|
|
102
|
+
schemaVersion: ASYNC_OPERATION_SCHEMA_VERSION,
|
|
103
|
+
id: String(raw.id),
|
|
104
|
+
providerId: String(raw.providerId),
|
|
105
|
+
routeKey: String(raw.routeKey),
|
|
106
|
+
ownerRef: String(raw.ownerRef),
|
|
107
|
+
status: raw.status ?? 'submitted',
|
|
108
|
+
attempts: typeof raw.attempts === 'number' ? raw.attempts : 0,
|
|
109
|
+
maxAttempts: Number(raw.maxAttempts),
|
|
110
|
+
deadlineAt: Number(raw.deadlineAt),
|
|
111
|
+
nextPollAt: typeof raw.nextPollAt === 'number' ? raw.nextPollAt : now,
|
|
112
|
+
leaseOwner: raw.leaseOwner ?? null,
|
|
113
|
+
leaseExpiresAt: raw.leaseExpiresAt ?? null,
|
|
114
|
+
state: raw.state ?? null,
|
|
115
|
+
result: raw.result ?? null,
|
|
116
|
+
error: raw.error ?? null,
|
|
117
|
+
createdAt: typeof raw.createdAt === 'number' ? raw.createdAt : now,
|
|
118
|
+
updatedAt: typeof raw.updatedAt === 'number' ? raw.updatedAt : now,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function cloneRecord(row) {
|
|
122
|
+
return {
|
|
123
|
+
...row,
|
|
124
|
+
state: row.state === null ? null : structuredClone(row.state),
|
|
125
|
+
result: row.result === null ? null : { ...row.result },
|
|
126
|
+
error: row.error === null ? null : { ...row.error },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function assertTransition(from, to) {
|
|
130
|
+
if (!ALLOWED_TRANSITIONS[from]) {
|
|
131
|
+
throw new RangeError(`Invalid async operation status: "${from}"`);
|
|
132
|
+
}
|
|
133
|
+
if (!ALLOWED_TRANSITIONS[from].has(to)) {
|
|
134
|
+
throw new RangeError(`Invalid async operation transition: "${from}" -> "${to}"`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates the in-memory reference `AsyncOperationStore`. No persistence — a durable adapter
|
|
139
|
+
* implements the same interface; every method here is written so the equivalent SQL is a direct
|
|
140
|
+
* translation (`claimDue` in particular is a single conditional UPDATE ... RETURNING).
|
|
141
|
+
*/
|
|
142
|
+
export function createInMemoryAsyncOperationStore() {
|
|
143
|
+
const rows = new Map();
|
|
144
|
+
const requireRow = (id) => rows.get(id);
|
|
145
|
+
return {
|
|
146
|
+
async create(input) {
|
|
147
|
+
if (rows.has(input.id)) {
|
|
148
|
+
throw new Error(`async operation "${input.id}" already exists`);
|
|
149
|
+
}
|
|
150
|
+
if (!Number.isFinite(input.maxAttempts) || input.maxAttempts < 1) {
|
|
151
|
+
throw new RangeError(`Invalid maxAttempts: ${input.maxAttempts} must be a finite number >= 1`);
|
|
152
|
+
}
|
|
153
|
+
if (!Number.isFinite(input.deadlineAt)) {
|
|
154
|
+
throw new RangeError(`Invalid deadlineAt: ${input.deadlineAt} must be a finite epoch-ms timestamp`);
|
|
155
|
+
}
|
|
156
|
+
assertNoCredentialMaterial(input.state);
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
const row = {
|
|
159
|
+
schemaVersion: ASYNC_OPERATION_SCHEMA_VERSION,
|
|
160
|
+
id: input.id,
|
|
161
|
+
providerId: input.providerId,
|
|
162
|
+
routeKey: input.routeKey,
|
|
163
|
+
ownerRef: input.ownerRef,
|
|
164
|
+
status: input.status ?? 'submitted',
|
|
165
|
+
attempts: 0,
|
|
166
|
+
maxAttempts: input.maxAttempts,
|
|
167
|
+
deadlineAt: input.deadlineAt,
|
|
168
|
+
nextPollAt: input.nextPollAt ?? now,
|
|
169
|
+
leaseOwner: null,
|
|
170
|
+
leaseExpiresAt: null,
|
|
171
|
+
state: input.state === undefined || input.state === null ? null : structuredClone(input.state),
|
|
172
|
+
result: null,
|
|
173
|
+
error: null,
|
|
174
|
+
createdAt: now,
|
|
175
|
+
updatedAt: now,
|
|
176
|
+
};
|
|
177
|
+
rows.set(row.id, row);
|
|
178
|
+
return cloneRecord(row);
|
|
179
|
+
},
|
|
180
|
+
async get(id) {
|
|
181
|
+
const row = requireRow(id);
|
|
182
|
+
return row ? cloneRecord(row) : null;
|
|
183
|
+
},
|
|
184
|
+
async update(id, patch) {
|
|
185
|
+
const existing = requireRow(id);
|
|
186
|
+
if (!existing)
|
|
187
|
+
return null;
|
|
188
|
+
if ('state' in patch)
|
|
189
|
+
assertNoCredentialMaterial(patch.state);
|
|
190
|
+
const status = patch.status ?? existing.status;
|
|
191
|
+
assertTransition(existing.status, status);
|
|
192
|
+
const next = {
|
|
193
|
+
...existing,
|
|
194
|
+
schemaVersion: existing.schemaVersion,
|
|
195
|
+
status,
|
|
196
|
+
attempts: patch.attempts ?? existing.attempts,
|
|
197
|
+
nextPollAt: patch.nextPollAt ?? existing.nextPollAt,
|
|
198
|
+
state: 'state' in patch ? (patch.state == null ? null : structuredClone(patch.state)) : existing.state,
|
|
199
|
+
result: 'result' in patch ? (patch.result ?? null) : existing.result,
|
|
200
|
+
error: 'error' in patch ? (patch.error ?? null) : existing.error,
|
|
201
|
+
leaseOwner: 'leaseOwner' in patch ? (patch.leaseOwner ?? null) : existing.leaseOwner,
|
|
202
|
+
leaseExpiresAt: 'leaseExpiresAt' in patch ? (patch.leaseExpiresAt ?? null) : existing.leaseExpiresAt,
|
|
203
|
+
updatedAt: Date.now(),
|
|
204
|
+
};
|
|
205
|
+
rows.set(id, next);
|
|
206
|
+
return cloneRecord(next);
|
|
207
|
+
},
|
|
208
|
+
async listByOwner(ownerRef) {
|
|
209
|
+
return [...rows.values()]
|
|
210
|
+
.filter((row) => row.ownerRef === ownerRef)
|
|
211
|
+
.sort((a, b) => a.createdAt - b.createdAt)
|
|
212
|
+
.map(cloneRecord);
|
|
213
|
+
},
|
|
214
|
+
async claimDue(options) {
|
|
215
|
+
const { now, leaseOwner, leaseMs } = options;
|
|
216
|
+
const limit = options.limit ?? 10;
|
|
217
|
+
const claimed = [];
|
|
218
|
+
for (const row of [...rows.values()].sort((a, b) => a.nextPollAt - b.nextPollAt)) {
|
|
219
|
+
if (claimed.length >= limit)
|
|
220
|
+
break;
|
|
221
|
+
if (TERMINAL_STATUSES.has(row.status))
|
|
222
|
+
continue;
|
|
223
|
+
if (row.nextPollAt > now)
|
|
224
|
+
continue;
|
|
225
|
+
// A live lease held by anyone (including this worker's previous tick) blocks the claim;
|
|
226
|
+
// an expired one does not, which is what makes a dead worker's rows recoverable.
|
|
227
|
+
if (row.leaseExpiresAt !== null && row.leaseExpiresAt > now)
|
|
228
|
+
continue;
|
|
229
|
+
const leased = {
|
|
230
|
+
...row,
|
|
231
|
+
leaseOwner,
|
|
232
|
+
leaseExpiresAt: now + leaseMs,
|
|
233
|
+
updatedAt: now,
|
|
234
|
+
};
|
|
235
|
+
rows.set(row.id, leased);
|
|
236
|
+
claimed.push(cloneRecord(leased));
|
|
237
|
+
}
|
|
238
|
+
return claimed;
|
|
239
|
+
},
|
|
240
|
+
async releaseLease(id) {
|
|
241
|
+
const row = requireRow(id);
|
|
242
|
+
if (!row)
|
|
243
|
+
return;
|
|
244
|
+
rows.set(id, { ...row, leaseOwner: null, leaseExpiresAt: null, updatedAt: Date.now() });
|
|
245
|
+
},
|
|
246
|
+
async reconcileOnBoot(options) {
|
|
247
|
+
const { now } = options;
|
|
248
|
+
let leasesReleased = 0;
|
|
249
|
+
let deadlineExpired = 0;
|
|
250
|
+
for (const row of [...rows.values()]) {
|
|
251
|
+
if (TERMINAL_STATUSES.has(row.status))
|
|
252
|
+
continue;
|
|
253
|
+
if (row.deadlineAt <= now) {
|
|
254
|
+
rows.set(row.id, {
|
|
255
|
+
...row,
|
|
256
|
+
status: 'unknown',
|
|
257
|
+
leaseOwner: null,
|
|
258
|
+
leaseExpiresAt: null,
|
|
259
|
+
error: {
|
|
260
|
+
message: 'async operation passed its absolute deadline while unattended — vendor-side effect is undetermined',
|
|
261
|
+
code: 'DEADLINE_EXPIRED',
|
|
262
|
+
},
|
|
263
|
+
updatedAt: now,
|
|
264
|
+
});
|
|
265
|
+
deadlineExpired += 1;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (row.leaseOwner !== null && (row.leaseExpiresAt === null || row.leaseExpiresAt <= now)) {
|
|
269
|
+
rows.set(row.id, { ...row, leaseOwner: null, leaseExpiresAt: null, updatedAt: now });
|
|
270
|
+
leasesReleased += 1;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return { leasesReleased, deadlineExpired };
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=async-operation-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-operation-store.js","sourceRoot":"","sources":["../../../src/media-providers/dispatch/async-operation-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,5 +21,13 @@ export { dispatchVendorRequest, requireApiKey } from './vendor-adapter.js';
|
|
|
21
21
|
export type { VendorAdapter, VendorCredentialGuard, VendorRequest, VendorRequestBuilder, VendorResponseParser } from './vendor-adapter.js';
|
|
22
22
|
export { createVendorAdapterRegistry, mediaVendorRegistry, VendorAdapterRegistry } from './vendor-registry.js';
|
|
23
23
|
export { createHexEnvelopeAudioParser, createRawBytesParser } from './response-parsers.js';
|
|
24
|
+
export { ASYNC_OPERATION_SCHEMA_VERSION, assertNoCredentialMaterial, createInMemoryAsyncOperationStore, CREDENTIAL_IN_STATE_MESSAGE, hydrateAsyncOperationRecord, } from './async-operation-store.js';
|
|
25
|
+
export type { AsyncOperationClaimOptions, AsyncOperationCreateInput, AsyncOperationError, AsyncOperationPatch, AsyncOperationReconcileResult, AsyncOperationRecord, AsyncOperationResult, AsyncOperationStatus, AsyncOperationStore, } from './async-operation-store.js';
|
|
26
|
+
export { createBearerSigner, withUnsignedRequestInit } from './polling-adapter.js';
|
|
27
|
+
export type { AnyPollingVendorAdapter, ExpectedLatencyClass, PollOutcome, PollingVendorAdapter, RequestSigner, SignedVendorRequest, SubmitOutcome, UnsignedVendorRequest, } from './polling-adapter.js';
|
|
28
|
+
export { DEFAULT_DEADLINE_MS, DEFAULT_GRACE_MS, DEFAULT_MAX_ATTEMPTS, DEFAULT_POLL_INTERVAL_MS, pollDueOperations, recoverAfterRestart, startOperation, } from './operation-runtime.js';
|
|
29
|
+
export type { OperationRuntimeDeps, PollDueParams, PollDueStats, RecoverParams, RecoverResult, StartOperationOutcome, StartOperationParams, } from './operation-runtime.js';
|
|
30
|
+
export { createImageRouterVideoPollingAdapter } from './providers/imagerouter-video-async.js';
|
|
31
|
+
export type { ImageRouterVideoConfig, ImageRouterVideoMeta } from './providers/imagerouter-video-async.js';
|
|
24
32
|
export type { HexEnvelopeAudioMeta, HexEnvelopeAudioParserOptions, RawBytesParserOptions } from './response-parsers.js';
|
|
25
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/media-providers/dispatch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iCAAiC,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAC9H,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAChK,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACrE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3I,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC/G,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/media-providers/dispatch/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,4 +21,11 @@ export { assertAndFetchExternalAsset, assertExternalAssetUrl, isBlockedExternalA
|
|
|
21
21
|
export { dispatchVendorRequest, requireApiKey } from './vendor-adapter.js';
|
|
22
22
|
export { createVendorAdapterRegistry, mediaVendorRegistry, VendorAdapterRegistry } from './vendor-registry.js';
|
|
23
23
|
export { createHexEnvelopeAudioParser, createRawBytesParser } from './response-parsers.js';
|
|
24
|
+
// The submit-then-poll tier (added 2026-09-02) — purely additive alongside `VendorAdapter`, for
|
|
25
|
+
// vendors that hand back a job handle instead of finishing inside one request/response. See
|
|
26
|
+
// `operation-runtime.ts`'s module doc for the persist-before-fetch design.
|
|
27
|
+
export { ASYNC_OPERATION_SCHEMA_VERSION, assertNoCredentialMaterial, createInMemoryAsyncOperationStore, CREDENTIAL_IN_STATE_MESSAGE, hydrateAsyncOperationRecord, } from './async-operation-store.js';
|
|
28
|
+
export { createBearerSigner, withUnsignedRequestInit } from './polling-adapter.js';
|
|
29
|
+
export { DEFAULT_DEADLINE_MS, DEFAULT_GRACE_MS, DEFAULT_MAX_ATTEMPTS, DEFAULT_POLL_INTERVAL_MS, pollDueOperations, recoverAfterRestart, startOperation, } from './operation-runtime.js';
|
|
30
|
+
export { createImageRouterVideoPollingAdapter } from './providers/imagerouter-video-async.js';
|
|
24
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/media-providers/dispatch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iCAAiC,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAC9H,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAYhK,sEAAsE;AACtE,0EAA0E;AAC1E,wCAAwC;AACxC,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC/G,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/media-providers/dispatch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iCAAiC,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAC9H,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAYhK,sEAAsE;AACtE,0EAA0E;AAC1E,wCAAwC;AACxC,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC/G,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC3F,gGAAgG;AAChG,4FAA4F;AAC5F,2EAA2E;AAC3E,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,iCAAiC,EACjC,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AAYpC,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAWnF,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GACf,MAAM,wBAAwB,CAAC;AAUhC,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { AsyncOperationRecord, AsyncOperationStore } from './async-operation-store.js';
|
|
2
|
+
import type { AnyPollingVendorAdapter, PollingVendorAdapter, RequestSigner } from './polling-adapter.js';
|
|
3
|
+
import type { RenderContext, RenderResult } from './types.js';
|
|
4
|
+
/** Interactive budget for the inline attempt. Deliberately NOT `FETCH_TIMEOUT_MS.GENERATE` (10min) — that is the backstop for the *request*, this is the ceiling on making a human wait. */
|
|
5
|
+
export declare const DEFAULT_GRACE_MS = 4000;
|
|
6
|
+
export declare const DEFAULT_MAX_ATTEMPTS = 60;
|
|
7
|
+
export declare const DEFAULT_DEADLINE_MS: number;
|
|
8
|
+
export declare const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
9
|
+
export interface OperationRuntimeDeps {
|
|
10
|
+
readonly store: AsyncOperationStore;
|
|
11
|
+
readonly signer: RequestSigner;
|
|
12
|
+
readonly fetchImpl?: typeof fetch;
|
|
13
|
+
readonly now?: () => number;
|
|
14
|
+
readonly newId?: () => string;
|
|
15
|
+
}
|
|
16
|
+
export interface StartOperationParams<Meta> {
|
|
17
|
+
readonly adapter: PollingVendorAdapter<Meta>;
|
|
18
|
+
readonly ctx: RenderContext;
|
|
19
|
+
readonly providerId: string;
|
|
20
|
+
readonly routeKey: string;
|
|
21
|
+
readonly ownerRef: string;
|
|
22
|
+
readonly maxAttempts?: number;
|
|
23
|
+
readonly deadlineMs?: number;
|
|
24
|
+
readonly graceMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export type StartOperationOutcome = {
|
|
27
|
+
readonly done: true;
|
|
28
|
+
readonly operationId: string;
|
|
29
|
+
readonly result: RenderResult;
|
|
30
|
+
} | {
|
|
31
|
+
readonly done: false;
|
|
32
|
+
readonly operationId: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Persists the operation row, issues the submit, and races it against the grace window.
|
|
36
|
+
*
|
|
37
|
+
* @returns `{done: true, result}` when the vendor finished inside the grace window, else
|
|
38
|
+
* `{done: false, operationId}` — the row is already durable in both cases.
|
|
39
|
+
* @complexity O(1) plus one vendor round trip.
|
|
40
|
+
*/
|
|
41
|
+
export declare function startOperation<Meta>(deps: OperationRuntimeDeps, params: StartOperationParams<Meta>): Promise<StartOperationOutcome>;
|
|
42
|
+
export interface PollDueParams {
|
|
43
|
+
/** Resolves the adapter registered for a row's `(providerId, routeKey)`. */
|
|
44
|
+
readonly adapters: (providerId: string, routeKey: string) => AnyPollingVendorAdapter | undefined;
|
|
45
|
+
/** Rehydrates the render context for a row. The row deliberately does not persist the context — it can hold reference-image data URLs, and none of it is needed to identify the vendor-side job. */
|
|
46
|
+
readonly resolveContext: (row: AsyncOperationRecord) => RenderContext;
|
|
47
|
+
readonly leaseOwner: string;
|
|
48
|
+
readonly leaseMs: number;
|
|
49
|
+
readonly limit?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface PollDueStats {
|
|
52
|
+
readonly claimed: number;
|
|
53
|
+
readonly completed: number;
|
|
54
|
+
readonly failed: number;
|
|
55
|
+
readonly pending: number;
|
|
56
|
+
readonly unknown: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One leased worker tick: claims every due operation, advances each by exactly one vendor poll,
|
|
60
|
+
* and releases the lease. Safe to run concurrently on several workers — `claimDue` is the
|
|
61
|
+
* exclusion mechanism.
|
|
62
|
+
*
|
|
63
|
+
* Both bounds are enforced here, and in this order: the absolute deadline is checked *before* any
|
|
64
|
+
* vendor call (a blown deadline must not cost another request), the attempt cap after it.
|
|
65
|
+
*
|
|
66
|
+
* @complexity O(k) vendor round trips for k claimed operations.
|
|
67
|
+
*/
|
|
68
|
+
export declare function pollDueOperations(deps: OperationRuntimeDeps, params: PollDueParams): Promise<PollDueStats>;
|
|
69
|
+
export interface RecoverParams {
|
|
70
|
+
readonly adapters: (providerId: string, routeKey: string) => AnyPollingVendorAdapter | undefined;
|
|
71
|
+
readonly now?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface RecoverResult {
|
|
74
|
+
readonly leasesReleased: number;
|
|
75
|
+
readonly deadlineExpired: number;
|
|
76
|
+
/** Rows whose submit may or may not have reached the vendor and cannot be safely re-issued. */
|
|
77
|
+
readonly unknownCrashGap: number;
|
|
78
|
+
/** Rows whose adapter declares its submit idempotent — left `submitted` for the host to re-issue. */
|
|
79
|
+
readonly resubmittable: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Boot-time recovery. Releases dead leases and expires blown deadlines via the store, then handles
|
|
83
|
+
* the one case the store cannot decide alone: a row still at `submitted`, meaning the process died
|
|
84
|
+
* between persisting the row and learning whether the vendor accepted the submit.
|
|
85
|
+
*
|
|
86
|
+
* Retry safety is gated on idempotency, not assumed. An adapter that has not declared
|
|
87
|
+
* `submitIsIdempotent` sends that row to `unknown` for reconciliation — media generation is billed
|
|
88
|
+
* per call, so a silent re-issue can double a real charge.
|
|
89
|
+
*
|
|
90
|
+
* @complexity O(n) in stored operations.
|
|
91
|
+
*/
|
|
92
|
+
export declare function recoverAfterRestart(deps: OperationRuntimeDeps, params: RecoverParams): Promise<RecoverResult>;
|
|
93
|
+
//# sourceMappingURL=operation-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-runtime.d.ts","sourceRoot":"","sources":["../../../src/media-providers/dispatch/operation-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The submit-then-poll runtime: the piece that turns a `PollingVendorAdapter` plus an
|
|
3
|
+
* `AsyncOperationStore` into an operation that survives a restart.
|
|
4
|
+
*
|
|
5
|
+
* ## Persist first, then race
|
|
6
|
+
*
|
|
7
|
+
* `startOperation` writes the operation row **before** it issues any HTTP call, then races the
|
|
8
|
+
* in-flight submit against a short interactive grace window. If the vendor answers in time the
|
|
9
|
+
* caller gets `{done: true, result}` in the same round trip — indistinguishable from the
|
|
10
|
+
* synchronous tier. If it does not, the caller gets `{done: false, operationId}` and the *same*
|
|
11
|
+
* in-flight request keeps running and writes its own outcome into the row.
|
|
12
|
+
*
|
|
13
|
+
* That ordering is what makes crash safety fall out for free rather than being bolted on: there is
|
|
14
|
+
* no window in which work exists only inside a process. It is also why this is a race and not a
|
|
15
|
+
* fork — there is exactly one submit code path, and `expectedLatencyClass` only decides how long
|
|
16
|
+
* the caller is willing to wait on it, never what happens.
|
|
17
|
+
*
|
|
18
|
+
* Human callers and agents get the identical contract: a result, or an id to come back with.
|
|
19
|
+
*
|
|
20
|
+
* ## What is deliberately NOT here
|
|
21
|
+
*
|
|
22
|
+
* No scheduler. `pollDueOperations` is a single leased tick a host drives from whatever timer or
|
|
23
|
+
* queue it already runs — this package performs no I/O of its own beyond the vendor calls it is
|
|
24
|
+
* handed a `fetchImpl` for, a standing invariant across `@jini-ai/integrations/media-providers`.
|
|
25
|
+
*/
|
|
26
|
+
import { FETCH_TIMEOUT_MS, fetchWithTimeout } from '@jini-ai/platform';
|
|
27
|
+
/** Interactive budget for the inline attempt. Deliberately NOT `FETCH_TIMEOUT_MS.GENERATE` (10min) — that is the backstop for the *request*, this is the ceiling on making a human wait. */
|
|
28
|
+
export const DEFAULT_GRACE_MS = 4_000;
|
|
29
|
+
export const DEFAULT_MAX_ATTEMPTS = 60;
|
|
30
|
+
export const DEFAULT_DEADLINE_MS = 15 * 60_000;
|
|
31
|
+
export const DEFAULT_POLL_INTERVAL_MS = 5_000;
|
|
32
|
+
function resultToPayload(result) {
|
|
33
|
+
return {
|
|
34
|
+
bytesBase64: result.bytes.toString('base64'),
|
|
35
|
+
providerNote: result.providerNote,
|
|
36
|
+
...(result.suggestedExt !== undefined ? { suggestedExt: result.suggestedExt } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function toOperationError(error) {
|
|
40
|
+
return { message: error instanceof Error ? error.message : String(error) };
|
|
41
|
+
}
|
|
42
|
+
async function performSigned(deps, request, timeoutMs) {
|
|
43
|
+
const signed = await deps.signer(request);
|
|
44
|
+
const doFetch = deps.fetchImpl;
|
|
45
|
+
if (doFetch)
|
|
46
|
+
return doFetch(signed.url, signed.init);
|
|
47
|
+
return fetchWithTimeout(signed.url, signed.init, { timeoutMs });
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Persists the operation row, issues the submit, and races it against the grace window.
|
|
51
|
+
*
|
|
52
|
+
* @returns `{done: true, result}` when the vendor finished inside the grace window, else
|
|
53
|
+
* `{done: false, operationId}` — the row is already durable in both cases.
|
|
54
|
+
* @complexity O(1) plus one vendor round trip.
|
|
55
|
+
*/
|
|
56
|
+
export async function startOperation(deps, params) {
|
|
57
|
+
const now = deps.now ?? Date.now;
|
|
58
|
+
const newId = deps.newId ?? (() => `op_${Math.random().toString(36).slice(2, 10)}_${now()}`);
|
|
59
|
+
const startedAt = now();
|
|
60
|
+
const operationId = newId();
|
|
61
|
+
// Before any HTTP call — this ordering IS the crash-safety mechanism.
|
|
62
|
+
await deps.store.create({
|
|
63
|
+
id: operationId,
|
|
64
|
+
providerId: params.providerId,
|
|
65
|
+
routeKey: params.routeKey,
|
|
66
|
+
ownerRef: params.ownerRef,
|
|
67
|
+
maxAttempts: params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
|
|
68
|
+
deadlineAt: startedAt + (params.deadlineMs ?? DEFAULT_DEADLINE_MS),
|
|
69
|
+
nextPollAt: startedAt,
|
|
70
|
+
});
|
|
71
|
+
const settle = (async () => {
|
|
72
|
+
try {
|
|
73
|
+
const request = params.adapter.buildSubmitRequest(params.ctx);
|
|
74
|
+
const resp = await performSigned(deps, request, FETCH_TIMEOUT_MS.GENERATE);
|
|
75
|
+
const outcome = await params.adapter.parseSubmitResponse(resp, params.ctx, request);
|
|
76
|
+
if (outcome.kind === 'complete') {
|
|
77
|
+
await deps.store.update(operationId, { status: 'succeeded', result: resultToPayload(outcome.result) });
|
|
78
|
+
return { done: true, operationId, result: outcome.result };
|
|
79
|
+
}
|
|
80
|
+
await deps.store.update(operationId, {
|
|
81
|
+
status: 'polling',
|
|
82
|
+
state: outcome.state,
|
|
83
|
+
nextPollAt: now() + (outcome.retryAfterMs ?? DEFAULT_POLL_INTERVAL_MS),
|
|
84
|
+
});
|
|
85
|
+
return { done: false, operationId };
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
await deps.store.update(operationId, { status: 'failed', error: toOperationError(error) });
|
|
89
|
+
return { done: false, operationId };
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
const graceMs = params.graceMs ?? DEFAULT_GRACE_MS;
|
|
93
|
+
// A `'slow'` vendor is not worth making anyone wait on: the race is still the same single code
|
|
94
|
+
// path, its window is just zero.
|
|
95
|
+
const effectiveGraceMs = params.adapter.expectedLatencyClass === 'slow' ? 0 : graceMs;
|
|
96
|
+
let graceTimer;
|
|
97
|
+
const grace = new Promise((resolve) => {
|
|
98
|
+
graceTimer = setTimeout(() => resolve({ done: false, operationId }), effectiveGraceMs);
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
// The losing side is never cancelled — an abandoned submit keeps running and settles the row
|
|
102
|
+
// the caller already holds an id for.
|
|
103
|
+
return await Promise.race([settle, grace]);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
if (graceTimer)
|
|
107
|
+
clearTimeout(graceTimer);
|
|
108
|
+
// The caller may have walked away; the row still gets written, but an unobserved rejection
|
|
109
|
+
// must not take the process down.
|
|
110
|
+
void settle.catch(() => undefined);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* One leased worker tick: claims every due operation, advances each by exactly one vendor poll,
|
|
115
|
+
* and releases the lease. Safe to run concurrently on several workers — `claimDue` is the
|
|
116
|
+
* exclusion mechanism.
|
|
117
|
+
*
|
|
118
|
+
* Both bounds are enforced here, and in this order: the absolute deadline is checked *before* any
|
|
119
|
+
* vendor call (a blown deadline must not cost another request), the attempt cap after it.
|
|
120
|
+
*
|
|
121
|
+
* @complexity O(k) vendor round trips for k claimed operations.
|
|
122
|
+
*/
|
|
123
|
+
export async function pollDueOperations(deps, params) {
|
|
124
|
+
const now = deps.now ?? Date.now;
|
|
125
|
+
const claimed = await deps.store.claimDue({
|
|
126
|
+
now: now(),
|
|
127
|
+
leaseOwner: params.leaseOwner,
|
|
128
|
+
leaseMs: params.leaseMs,
|
|
129
|
+
...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
130
|
+
});
|
|
131
|
+
let completed = 0;
|
|
132
|
+
let failed = 0;
|
|
133
|
+
let pending = 0;
|
|
134
|
+
let unknown = 0;
|
|
135
|
+
for (const row of claimed) {
|
|
136
|
+
try {
|
|
137
|
+
const at = now();
|
|
138
|
+
if (row.deadlineAt <= at) {
|
|
139
|
+
await deps.store.update(row.id, {
|
|
140
|
+
status: 'unknown',
|
|
141
|
+
error: {
|
|
142
|
+
message: 'async operation passed its absolute deadline — vendor-side effect is undetermined',
|
|
143
|
+
code: 'DEADLINE_EXPIRED',
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
unknown += 1;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (row.attempts >= row.maxAttempts) {
|
|
150
|
+
await deps.store.update(row.id, {
|
|
151
|
+
status: 'failed',
|
|
152
|
+
error: {
|
|
153
|
+
message: `async operation exhausted its ${row.maxAttempts} poll attempts without a terminal answer`,
|
|
154
|
+
code: 'ATTEMPTS_EXHAUSTED',
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
failed += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const adapter = params.adapters(row.providerId, row.routeKey);
|
|
161
|
+
if (!adapter) {
|
|
162
|
+
await deps.store.update(row.id, {
|
|
163
|
+
status: 'failed',
|
|
164
|
+
error: { message: `no polling adapter registered for "${row.providerId}" / "${row.routeKey}"`, code: 'NO_ADAPTER' },
|
|
165
|
+
});
|
|
166
|
+
failed += 1;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const ctx = params.resolveContext(row);
|
|
170
|
+
const request = adapter.buildPollRequest(row.state ?? {}, ctx);
|
|
171
|
+
// Credentials are resolved here, on this tick — never read from the row.
|
|
172
|
+
const resp = await performSigned(deps, request, FETCH_TIMEOUT_MS.QUICK);
|
|
173
|
+
const outcome = await adapter.parsePollResponse(resp, ctx, row.state ?? {});
|
|
174
|
+
if (outcome.kind === 'complete') {
|
|
175
|
+
await deps.store.update(row.id, {
|
|
176
|
+
status: 'succeeded',
|
|
177
|
+
attempts: row.attempts + 1,
|
|
178
|
+
result: resultToPayload(outcome.result),
|
|
179
|
+
});
|
|
180
|
+
completed += 1;
|
|
181
|
+
}
|
|
182
|
+
else if (outcome.kind === 'failed') {
|
|
183
|
+
await deps.store.update(row.id, {
|
|
184
|
+
status: 'failed',
|
|
185
|
+
attempts: row.attempts + 1,
|
|
186
|
+
error: { message: outcome.message, ...(outcome.code !== undefined ? { code: outcome.code } : {}) },
|
|
187
|
+
});
|
|
188
|
+
failed += 1;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
await deps.store.update(row.id, {
|
|
192
|
+
status: 'polling',
|
|
193
|
+
attempts: row.attempts + 1,
|
|
194
|
+
nextPollAt: at + (outcome.retryAfterMs ?? DEFAULT_POLL_INTERVAL_MS),
|
|
195
|
+
});
|
|
196
|
+
pending += 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
// A transport failure is not a vendor verdict: keep the row pollable and let the attempt cap
|
|
201
|
+
// or the deadline end it, rather than reporting a definitive failure we cannot support.
|
|
202
|
+
await deps.store.update(row.id, {
|
|
203
|
+
status: 'polling',
|
|
204
|
+
attempts: row.attempts + 1,
|
|
205
|
+
nextPollAt: now() + DEFAULT_POLL_INTERVAL_MS,
|
|
206
|
+
error: toOperationError(error),
|
|
207
|
+
});
|
|
208
|
+
pending += 1;
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
await deps.store.releaseLease(row.id);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return { claimed: claimed.length, completed, failed, pending, unknown };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Boot-time recovery. Releases dead leases and expires blown deadlines via the store, then handles
|
|
218
|
+
* the one case the store cannot decide alone: a row still at `submitted`, meaning the process died
|
|
219
|
+
* between persisting the row and learning whether the vendor accepted the submit.
|
|
220
|
+
*
|
|
221
|
+
* Retry safety is gated on idempotency, not assumed. An adapter that has not declared
|
|
222
|
+
* `submitIsIdempotent` sends that row to `unknown` for reconciliation — media generation is billed
|
|
223
|
+
* per call, so a silent re-issue can double a real charge.
|
|
224
|
+
*
|
|
225
|
+
* @complexity O(n) in stored operations.
|
|
226
|
+
*/
|
|
227
|
+
export async function recoverAfterRestart(deps, params) {
|
|
228
|
+
const at = params.now ?? (deps.now ?? Date.now)();
|
|
229
|
+
const reconciled = await deps.store.reconcileOnBoot({ now: at });
|
|
230
|
+
const resubmittable = [];
|
|
231
|
+
let unknownCrashGap = 0;
|
|
232
|
+
for (const row of await collectSubmitted(deps.store)) {
|
|
233
|
+
const adapter = params.adapters(row.providerId, row.routeKey);
|
|
234
|
+
if (adapter?.submitIsIdempotent === true) {
|
|
235
|
+
resubmittable.push(row.id);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
await deps.store.update(row.id, {
|
|
239
|
+
status: 'unknown',
|
|
240
|
+
error: {
|
|
241
|
+
message: 'process died before the vendor submit was confirmed, and this adapter does not declare the submit idempotent — reconcile manually rather than risk a duplicate charge',
|
|
242
|
+
code: 'CRASH_GAP_NOT_IDEMPOTENT',
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
unknownCrashGap += 1;
|
|
246
|
+
}
|
|
247
|
+
return { ...reconciled, unknownCrashGap, resubmittable };
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* The `submitted` rows a restart orphaned. Uses the store's own claim-free read path so recovery
|
|
251
|
+
* never competes with a live worker for a lease.
|
|
252
|
+
*/
|
|
253
|
+
async function collectSubmitted(store) {
|
|
254
|
+
const seen = new Map();
|
|
255
|
+
for (const row of await store.claimDue({ now: Number.MAX_SAFE_INTEGER, leaseOwner: '__recovery__', leaseMs: 0, limit: Number.MAX_SAFE_INTEGER })) {
|
|
256
|
+
if (row.status === 'submitted')
|
|
257
|
+
seen.set(row.id, row);
|
|
258
|
+
await store.releaseLease(row.id);
|
|
259
|
+
}
|
|
260
|
+
return [...seen.values()];
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=operation-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-runtime.js","sourceRoot":"","sources":["../../../src/media-providers/dispatch/operation-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `PollingVendorAdapter` — the submit-then-poll tier, added alongside (never replacing)
|
|
3
|
+
* `vendor-adapter.ts`'s `VendorAdapter`.
|
|
4
|
+
*
|
|
5
|
+
* This is purely additive. `VendorAdapter` is proven across all 18 live registrations and is
|
|
6
|
+
* untouched: a vendor that finishes inside one request/response keeps using it. This interface is
|
|
7
|
+
* for the other shape — a vendor that hands back a job handle and makes you come back for the
|
|
8
|
+
* result — which `dispatchVendorRequest`'s fixed `buildRequest -> one fetch -> parseResponse`
|
|
9
|
+
* cannot express at all.
|
|
10
|
+
*
|
|
11
|
+
* ## The credential seam
|
|
12
|
+
*
|
|
13
|
+
* The deliberate difference from `VendorAdapter`: **these builders never receive credentials.**
|
|
14
|
+
* `VendorAdapter.buildRequest(ctx, credentials)` hands the adapter a `ProviderCredentials` and the
|
|
15
|
+
* adapter writes its own `authorization` header. That is fine for one in-process round trip, but a
|
|
16
|
+
* polled operation outlives the request that started it, so a secret reachable from the adapter is
|
|
17
|
+
* a secret that wants to be persisted next to the job handle. Here the adapter builds an
|
|
18
|
+
* *unsigned* request and a `RequestSigner` attaches auth beneath it, re-resolved on every tick —
|
|
19
|
+
* which is also what makes a token that expires mid-poll a non-event.
|
|
20
|
+
*/
|
|
21
|
+
import type { MediaGenerationRequestInit, ProviderCredentials, RenderContext, RenderResult } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* Whether this vendor is expected to answer within an interactive grace window. It is a property
|
|
24
|
+
* of the *vendor*, declared once per adapter — never a property of the caller, which is what keeps
|
|
25
|
+
* the human UI and an agent on one identical contract.
|
|
26
|
+
*/
|
|
27
|
+
export type ExpectedLatencyClass = 'fast' | 'slow';
|
|
28
|
+
/** A vendor request with no auth applied. `init.headers` carries content negotiation only. */
|
|
29
|
+
export interface UnsignedVendorRequest<Meta = undefined> {
|
|
30
|
+
readonly url: string;
|
|
31
|
+
readonly init: RequestInit;
|
|
32
|
+
readonly meta: Meta;
|
|
33
|
+
}
|
|
34
|
+
/** What a submit response turned out to be: the finished artifact, or a handle to come back for. */
|
|
35
|
+
export type SubmitOutcome = {
|
|
36
|
+
readonly kind: 'complete';
|
|
37
|
+
readonly result: RenderResult;
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: 'pending';
|
|
40
|
+
readonly state: Readonly<Record<string, unknown>>;
|
|
41
|
+
readonly retryAfterMs?: number;
|
|
42
|
+
};
|
|
43
|
+
export type PollOutcome = {
|
|
44
|
+
readonly kind: 'complete';
|
|
45
|
+
readonly result: RenderResult;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: 'pending';
|
|
48
|
+
readonly retryAfterMs?: number;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: 'failed';
|
|
51
|
+
readonly message: string;
|
|
52
|
+
readonly code?: string;
|
|
53
|
+
};
|
|
54
|
+
export interface PollingVendorAdapter<Meta = undefined> {
|
|
55
|
+
readonly expectedLatencyClass: ExpectedLatencyClass;
|
|
56
|
+
/**
|
|
57
|
+
* Whether re-issuing the submit request is safe when a crash left us unable to tell whether the
|
|
58
|
+
* first one reached the vendor. Defaults to `false`, which routes that row to `unknown` for
|
|
59
|
+
* reconciliation instead. There is no unqualified retry-once rule: media generation costs real
|
|
60
|
+
* money per call, so a retry must be *proven* safe, not assumed.
|
|
61
|
+
*/
|
|
62
|
+
readonly submitIsIdempotent?: boolean;
|
|
63
|
+
buildSubmitRequest(ctx: RenderContext): UnsignedVendorRequest<Meta>;
|
|
64
|
+
parseSubmitResponse(resp: Response, ctx: RenderContext, request: UnsignedVendorRequest<Meta>): Promise<SubmitOutcome>;
|
|
65
|
+
buildPollRequest(state: Readonly<Record<string, unknown>>, ctx: RenderContext): UnsignedVendorRequest<Meta>;
|
|
66
|
+
parsePollResponse(resp: Response, ctx: RenderContext, state: Readonly<Record<string, unknown>>): Promise<PollOutcome>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A `PollingVendorAdapter` with its `Meta` erased, for heterogeneous lookup by
|
|
70
|
+
* `(providerId, routeKey)`. `Meta` never crosses this boundary — it only flows from one adapter's
|
|
71
|
+
* own `build*` to that same adapter's `parse*` — so erasing it here is sound, unlike the `never`
|
|
72
|
+
* that `VendorAdapterRegistry` uses (which is only sound because every caller re-casts).
|
|
73
|
+
*/
|
|
74
|
+
export type AnyPollingVendorAdapter = PollingVendorAdapter<any>;
|
|
75
|
+
/** A signed request, ready to hand to `fetch`. */
|
|
76
|
+
export interface SignedVendorRequest {
|
|
77
|
+
readonly url: string;
|
|
78
|
+
readonly init: RequestInit;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The broker seam: attaches auth to an unsigned request. Async because a real broker resolves from
|
|
82
|
+
* a vault / refreshes an OAuth token, and is called once per tick rather than once per operation.
|
|
83
|
+
*/
|
|
84
|
+
export type RequestSigner = (request: UnsignedVendorRequest<unknown>) => Promise<SignedVendorRequest> | SignedVendorRequest;
|
|
85
|
+
/**
|
|
86
|
+
* Reference signer for the `Authorization: Bearer <key>` scheme every vendor in this package uses.
|
|
87
|
+
*
|
|
88
|
+
* `resolve` is invoked per call, never cached here — caching a resolved key inside the signer would
|
|
89
|
+
* reintroduce exactly the mid-stream-expiry problem this seam exists to remove.
|
|
90
|
+
*
|
|
91
|
+
* @throws When `resolve` yields no `apiKey`, using `missingCredentialMessage` — the failure surfaces
|
|
92
|
+
* at signing time rather than as an opaque vendor 401.
|
|
93
|
+
* @complexity O(1) plus whatever `resolve` costs.
|
|
94
|
+
*/
|
|
95
|
+
export declare function createBearerSigner(required: {
|
|
96
|
+
resolve: () => Promise<ProviderCredentials> | ProviderCredentials;
|
|
97
|
+
missingCredentialMessage: string;
|
|
98
|
+
}): RequestSigner;
|
|
99
|
+
/** Carries a caller-supplied `dispatcher` onto an unsigned request, matching `withRequestInit`'s role on the sync tier. */
|
|
100
|
+
export declare function withUnsignedRequestInit(ctx: {
|
|
101
|
+
readonly requestInit: MediaGenerationRequestInit;
|
|
102
|
+
}, init: RequestInit): RequestInit;
|
|
103
|
+
//# sourceMappingURL=polling-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polling-adapter.d.ts","sourceRoot":"","sources":["../../../src/media-providers/dispatch/polling-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference signer for the `Authorization: Bearer <key>` scheme every vendor in this package uses.
|
|
3
|
+
*
|
|
4
|
+
* `resolve` is invoked per call, never cached here — caching a resolved key inside the signer would
|
|
5
|
+
* reintroduce exactly the mid-stream-expiry problem this seam exists to remove.
|
|
6
|
+
*
|
|
7
|
+
* @throws When `resolve` yields no `apiKey`, using `missingCredentialMessage` — the failure surfaces
|
|
8
|
+
* at signing time rather than as an opaque vendor 401.
|
|
9
|
+
* @complexity O(1) plus whatever `resolve` costs.
|
|
10
|
+
*/
|
|
11
|
+
export function createBearerSigner(required) {
|
|
12
|
+
return async (request) => {
|
|
13
|
+
const credentials = await required.resolve();
|
|
14
|
+
if (!credentials.apiKey) {
|
|
15
|
+
throw new Error(required.missingCredentialMessage);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
url: request.url,
|
|
19
|
+
init: {
|
|
20
|
+
...request.init,
|
|
21
|
+
headers: { ...request.init.headers, authorization: `Bearer ${credentials.apiKey}` },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Carries a caller-supplied `dispatcher` onto an unsigned request, matching `withRequestInit`'s role on the sync tier. */
|
|
27
|
+
export function withUnsignedRequestInit(ctx, init) {
|
|
28
|
+
return { ...init, ...(ctx.requestInit.dispatcher ? { dispatcher: ctx.requestInit.dispatcher } : {}) };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=polling-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polling-adapter.js","sourceRoot":"","sources":["../../../src/media-providers/dispatch/polling-adapter.ts"],"names":[],"mappings":"AAkFA;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAiH;IAEjH,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC;QACrD,CAAC;QACD,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE;gBACJ,GAAG,OAAO,CAAC,IAAI;gBACf,OAAO,EAAE,EAAE,GAAI,OAAO,CAAC,IAAI,CAAC,OAA8C,EAAE,aAAa,EAAE,UAAU,WAAW,CAAC,MAAM,EAAE,EAAE;aAC5H;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,2HAA2H;AAC3H,MAAM,UAAU,uBAAuB,CAAC,GAAyD,EAAE,IAAiB;IAClH,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACxG,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PollingVendorAdapter } from '../polling-adapter.js';
|
|
2
|
+
/** Non-secret endpoint configuration. Sourceable from a config file or a DB row — never the API key. */
|
|
3
|
+
export interface ImageRouterVideoConfig {
|
|
4
|
+
readonly baseUrl?: string;
|
|
5
|
+
/** Overrides the catalog-derived `ctx.wireModel` for the wire request. */
|
|
6
|
+
readonly wireModel?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ImageRouterVideoMeta {
|
|
9
|
+
readonly wireModel: string;
|
|
10
|
+
readonly size: string;
|
|
11
|
+
readonly seconds: number | 'auto';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builds the ImageRouter video polling adapter for a given endpoint configuration.
|
|
15
|
+
*
|
|
16
|
+
* @param config Non-secret endpoint overrides; defaults to ImageRouter's public base URL and the
|
|
17
|
+
* context's own `wireModel`.
|
|
18
|
+
* @complexity O(1) per built request; one vendor round trip per parse.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createImageRouterVideoPollingAdapter(config?: ImageRouterVideoConfig): PollingVendorAdapter<ImageRouterVideoMeta>;
|
|
21
|
+
//# sourceMappingURL=imagerouter-video-async.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imagerouter-video-async.d.ts","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/imagerouter-video-async.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAe,oBAAoB,EAAwC,MAAM,uBAAuB,CAAC;AAKrH,wGAAwG;AACxG,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAgBD;;;;;;GAMG;AACH,wBAAgB,oCAAoC,CAClD,MAAM,GAAE,sBAA2B,GAClC,oBAAoB,CAAC,oBAAoB,CAAC,CAqG5C"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageRouter video on the submit-then-poll tier.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this vendor
|
|
5
|
+
*
|
|
6
|
+
* `providers/imagerouter.ts`'s video adapter is `buildRequest -> one fetch -> parseResponse` with
|
|
7
|
+
* no poll loop at all: it holds a socket open for the entire generation, bounded only by the
|
|
8
|
+
* blanket `FETCH_TIMEOUT_MS.GENERATE` (10 minute) backstop, and a restart loses the work with no
|
|
9
|
+
* way to ask the vendor what happened. That sync adapter is left registered and untouched — this
|
|
10
|
+
* is an additive second registration, not a replacement.
|
|
11
|
+
*
|
|
12
|
+
* ## Both response shapes, deliberately
|
|
13
|
+
*
|
|
14
|
+
* `parseSubmitResponse` accepts either shape:
|
|
15
|
+
*
|
|
16
|
+
* - `{ data: [{ b64_json | url }] }` — the artifact arrived on the submit itself. This is exactly
|
|
17
|
+
* what the existing sync adapter expects, so today's behaviour is preserved bit for bit and the
|
|
18
|
+
* operation completes inside the grace window.
|
|
19
|
+
* - `{ id, status }` — an OpenAI-videos-style job handle, so the operation moves to `polling`.
|
|
20
|
+
*
|
|
21
|
+
* Handling both is not hedging: **which one ImageRouter actually returns for video is unverified
|
|
22
|
+
* against the live vendor** (see this file's entry in the handoff). Implementing only the job-handle
|
|
23
|
+
* shape would have been a guess that silently broke the working path; implementing both is correct
|
|
24
|
+
* under either answer and costs one `if`.
|
|
25
|
+
*
|
|
26
|
+
* ## Config is data
|
|
27
|
+
*
|
|
28
|
+
* The endpoint config is a plain object argument, not baked into the module, so the same adapter
|
|
29
|
+
* can later be instantiated from a database row to make a vendor runtime-addable with no redesign.
|
|
30
|
+
* Note what is NOT in it: the API key. Endpoint routing is config; the secret is the signer's.
|
|
31
|
+
*/
|
|
32
|
+
import { bytesFromOpenAICompatibleData, parseOpenAICompatibleJson } from '../openai-compatible.js';
|
|
33
|
+
import { imageRouterSizeFor } from './imagerouter.js';
|
|
34
|
+
import { withUnsignedRequestInit } from '../polling-adapter.js';
|
|
35
|
+
const DEFAULT_BASE_URL = 'https://api.imagerouter.io/v1/openai';
|
|
36
|
+
const TERMINAL_FAILED_STATUSES = new Set(['failed', 'error', 'cancelled', 'canceled']);
|
|
37
|
+
function trimTrailingSlash(value) {
|
|
38
|
+
return value.replace(/\/+$/, '');
|
|
39
|
+
}
|
|
40
|
+
/** Reads a vendor-supplied `Retry-After` (seconds) header, ignoring anything non-numeric. */
|
|
41
|
+
function retryAfterMsFrom(resp) {
|
|
42
|
+
const raw = resp.headers.get('retry-after');
|
|
43
|
+
if (!raw)
|
|
44
|
+
return undefined;
|
|
45
|
+
const seconds = Number(raw);
|
|
46
|
+
return Number.isFinite(seconds) && seconds > 0 ? seconds * 1_000 : undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Builds the ImageRouter video polling adapter for a given endpoint configuration.
|
|
50
|
+
*
|
|
51
|
+
* @param config Non-secret endpoint overrides; defaults to ImageRouter's public base URL and the
|
|
52
|
+
* context's own `wireModel`.
|
|
53
|
+
* @complexity O(1) per built request; one vendor round trip per parse.
|
|
54
|
+
*/
|
|
55
|
+
export function createImageRouterVideoPollingAdapter(config = {}) {
|
|
56
|
+
const baseUrl = trimTrailingSlash((config.baseUrl || DEFAULT_BASE_URL).trim());
|
|
57
|
+
return {
|
|
58
|
+
expectedLatencyClass: 'slow',
|
|
59
|
+
// ImageRouter's generation endpoint accepts no idempotency key, so a submit that may or may
|
|
60
|
+
// not have landed cannot be safely re-issued — a duplicate submit is a duplicate charge.
|
|
61
|
+
submitIsIdempotent: false,
|
|
62
|
+
buildSubmitRequest(ctx) {
|
|
63
|
+
const wireModel = (config.wireModel || ctx.wireModel).trim();
|
|
64
|
+
const seconds = typeof ctx.length === 'number' ? ctx.length : 'auto';
|
|
65
|
+
const size = imageRouterSizeFor(ctx.aspect, 'video');
|
|
66
|
+
return {
|
|
67
|
+
url: `${baseUrl}/videos/generations`,
|
|
68
|
+
init: withUnsignedRequestInit(ctx, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: { 'content-type': 'application/json' },
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
prompt: ctx.prompt || 'A short cinematic clip.',
|
|
73
|
+
model: wireModel,
|
|
74
|
+
size,
|
|
75
|
+
seconds,
|
|
76
|
+
response_format: 'b64_json',
|
|
77
|
+
}),
|
|
78
|
+
}),
|
|
79
|
+
meta: { wireModel, size, seconds },
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
async parseSubmitResponse(resp, ctx, request) {
|
|
83
|
+
const data = (await parseOpenAICompatibleJson(resp, 'imagerouter video'));
|
|
84
|
+
if (Array.isArray(data.data)) {
|
|
85
|
+
const bytes = await bytesFromOpenAICompatibleData(data, 'imagerouter video', ctx.requestInit);
|
|
86
|
+
const { wireModel, size, seconds } = request.meta;
|
|
87
|
+
return {
|
|
88
|
+
kind: 'complete',
|
|
89
|
+
result: {
|
|
90
|
+
bytes,
|
|
91
|
+
providerNote: `imagerouter/${wireModel} · ${size} · ${seconds === 'auto' ? 'auto' : `${seconds}s`} · ${bytes.length} bytes`,
|
|
92
|
+
suggestedExt: '.mp4',
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (typeof data.id === 'string' && data.id) {
|
|
97
|
+
// Only the job handle is persisted — no credential, no request body.
|
|
98
|
+
const retryAfterMs = retryAfterMsFrom(resp);
|
|
99
|
+
return { kind: 'pending', state: { jobId: data.id }, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}) };
|
|
100
|
+
}
|
|
101
|
+
throw new Error('imagerouter video submit returned neither generated data nor a job id');
|
|
102
|
+
},
|
|
103
|
+
buildPollRequest(state, ctx) {
|
|
104
|
+
const jobId = state.jobId;
|
|
105
|
+
if (typeof jobId !== 'string' || !jobId) {
|
|
106
|
+
throw new Error('imagerouter video poll requires a persisted jobId');
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
url: `${baseUrl}/videos/${encodeURIComponent(jobId)}`,
|
|
110
|
+
init: withUnsignedRequestInit(ctx, { method: 'GET', headers: { accept: 'application/json' } }),
|
|
111
|
+
meta: { wireModel: (config.wireModel || ctx.wireModel).trim(), size: imageRouterSizeFor(ctx.aspect, 'video'), seconds: typeof ctx.length === 'number' ? ctx.length : 'auto' },
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
async parsePollResponse(resp, ctx, _state) {
|
|
115
|
+
const data = (await parseOpenAICompatibleJson(resp, 'imagerouter video'));
|
|
116
|
+
const status = typeof data.status === 'string' ? data.status.toLowerCase() : '';
|
|
117
|
+
if (TERMINAL_FAILED_STATUSES.has(status)) {
|
|
118
|
+
const error = data.error;
|
|
119
|
+
const detail = typeof error?.message === 'string' && error.message ? error.message : status;
|
|
120
|
+
return { kind: 'failed', message: `imagerouter video job failed: ${detail}` };
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(data.data)) {
|
|
123
|
+
const bytes = await bytesFromOpenAICompatibleData(data, 'imagerouter video', ctx.requestInit);
|
|
124
|
+
const wireModel = (config.wireModel || ctx.wireModel).trim();
|
|
125
|
+
const size = imageRouterSizeFor(ctx.aspect, 'video');
|
|
126
|
+
const seconds = typeof ctx.length === 'number' ? ctx.length : 'auto';
|
|
127
|
+
return {
|
|
128
|
+
kind: 'complete',
|
|
129
|
+
result: {
|
|
130
|
+
bytes,
|
|
131
|
+
providerNote: `imagerouter/${wireModel} · ${size} · ${seconds === 'auto' ? 'auto' : `${seconds}s`} · ${bytes.length} bytes`,
|
|
132
|
+
suggestedExt: '.mp4',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
const retryAfterMs = retryAfterMsFrom(resp);
|
|
137
|
+
return { kind: 'pending', ...(retryAfterMs !== undefined ? { retryAfterMs } : {}) };
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=imagerouter-video-async.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imagerouter-video-async.js","sourceRoot":"","sources":["../../../../src/media-providers/dispatch/providers/imagerouter-video-async.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/integrations",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Third-party vendor integrations, each independently importable: Composio connector catalog/execution (./composio) and a multi-vendor image/video/audio generation gateway (./media-providers).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -67,11 +67,11 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"undici": "^7.25.0",
|
|
70
|
-
"@jini-ai/
|
|
71
|
-
"@jini-ai/
|
|
70
|
+
"@jini-ai/platform": "0.3.0",
|
|
71
|
+
"@jini-ai/core": "0.3.1"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"better-sqlite3": "^
|
|
74
|
+
"better-sqlite3": "^13.0.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependenciesMeta": {
|
|
77
77
|
"better-sqlite3": {
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@types/better-sqlite3": "^7.6.11",
|
|
83
|
-
"better-sqlite3": "^
|
|
83
|
+
"better-sqlite3": "^13.0.0",
|
|
84
84
|
"@vitest/coverage-v8": "^2.1.9",
|
|
85
85
|
"@jini-ai/protocol": "0.3.0"
|
|
86
86
|
},
|