* Create a checkpoint and return its new version id (e.g. `v3`). The create * endpoint streams NDJSON progress; we drain it to completion, then resolve * the new id as the highest sequential `vN` checkpoint (autos and the live * `Current` pointer are ignored).
(
name: string,
options: { comment?: string; signal?: AbortSignal } = {},
)
| 326 | * `Current` pointer are ignored). |
| 327 | */ |
| 328 | async createCheckpoint( |
| 329 | name: string, |
| 330 | options: { comment?: string; signal?: AbortSignal } = {}, |
| 331 | ): Promise<string> { |
| 332 | // Snapshot the existing versions first so we can identify the one THIS call |
| 333 | // creates, rather than blindly returning the current max (which a concurrent |
| 334 | // create — e.g. handle.snapshot() racing an after-run snapshot — would make |
| 335 | // ambiguous, or an eventually-consistent list would make stale). |
| 336 | const before = new Set(await this.checkpointVersions(name, options.signal)) |
| 337 | |
| 338 | const url = this.spritePath(name, '/checkpoint') |
| 339 | const response = await fetch(url, { |
| 340 | method: 'POST', |
| 341 | headers: this.headers({ 'content-type': 'application/json' }), |
| 342 | body: JSON.stringify( |
| 343 | options.comment !== undefined ? { comment: options.comment } : {}, |
| 344 | ), |
| 345 | ...(options.signal ? { signal: options.signal } : {}), |
| 346 | }) |
| 347 | if (!response.ok) await this.fail('POST', url, response) |
| 348 | // The create stream closes promptly; drain it so the checkpoint is committed |
| 349 | // before we read the list back, and mine it for the new version id. |
| 350 | const streamText = await response.text() |
| 351 | const streamVersions = [...streamText.matchAll(/\bv(\d+)\b/g)].map((m) => |
| 352 | Number(m[1]), |
| 353 | ) |
| 354 | |
| 355 | const after = await this.checkpointVersions(name, options.signal) |
| 356 | const fresh = after.filter((v) => !before.has(v)) |
| 357 | // Prefer a version that did not exist before this call; fall back to the |
| 358 | // stream-reported version, then to the overall max. |
| 359 | const candidates = fresh.length > 0 ? fresh : streamVersions |
| 360 | const pool = candidates.length > 0 ? candidates : after |
| 361 | if (pool.length === 0) { |
| 362 | throw new Error( |
| 363 | `Sprites: checkpoint created for "${name}" but no versioned checkpoint was found.`, |
| 364 | ) |
| 365 | } |
| 366 | return `v${Math.max(...pool)}` |
| 367 | } |
| 368 | |
| 369 | /** Numeric ids of the Sprite's non-auto `vN` checkpoints. */ |
| 370 | private async checkpointVersions( |
no test coverage detected