| 1 | /** |
| 2 | * R2 key scheme (DESIGN.md §3.2). |
| 3 | * |
| 4 | * All R2 keys are **immutable and content-addressed** — this sidesteps the |
| 5 | * 1-write/s/key limit, makes conditional-put re-runs (idempotent alarm jobs) |
| 6 | * safe, and makes cross-repo fork references safe: a fork's `objects.r2_key` |
| 7 | * may point into the parent repo's prefix, since the bytes at a key never |
| 8 | * change once written. |
| 9 | * |
| 10 | * ``` |
| 11 | * {repoId}/objects/{oid} oversize loose objects, zlib(content) |
| 12 | * {repoId}/packs/pack-{sha1}.pack v1.1 compaction output |
| 13 | * {repoId}/incoming/{pushId}.pack v1.x streaming-push tee (reserved) |
| 14 | * {repoId}/lfs/{sha256} LFS objects (reserved) |
| 15 | * ``` |
| 16 | * |
| 17 | * `list()` is never used on a hot path — the `objects` table is the sole |
| 18 | * inventory. The prefix helpers exist for the delete-purge and GC alarm jobs |
| 19 | * only. |
| 20 | */ |
| 21 | |
| 22 | /** |
| 23 | * R2 key for an oversize loose object: `{repoId}/objects/{oid}`. |
| 24 | * |
| 25 | * The stored bytes are `zlib(content)` without the loose header — byte-equal |
| 26 | * to what an `objects.location = 'row'` row would hold in `zdata`. |
| 27 | */ |
| 28 | export const objectKey = (repoId: string, oid: string): string => |
| 29 | `${repoId}/objects/${oid}`; |
| 30 | |
| 31 | /** |
| 32 | * R2 key for a compacted pack (v1.1): `{repoId}/packs/pack-{sha1}.pack`. |
| 33 | * The `sha1` is the pack's trailer checksum, making the key content-addressed. |
| 34 | */ |
| 35 | export const packKey = (repoId: string, sha: string): string => |
| 36 | `${repoId}/packs/pack-${sha}.pack`; |
| 37 | |
| 38 | /** |
| 39 | * R2 key of a clone bundle for a ref snapshot (DESIGN.md §12.2): |
| 40 | * `{repoId}/bundles/bundle-{refsHash}.pack`. Immutable and |
| 41 | * content-addressed by the refs it covers, so it is safe to cache at the |
| 42 | * edge and safe to write concurrently. |
| 43 | */ |
| 44 | export const bundleKey = (repoId: string, refsHash: string): string => |
| 45 | `${repoId}/bundles/bundle-${refsHash}.pack`; |
| 46 | |
| 47 | /** |
| 48 | * The bundle's **pre-framed** twin (DESIGN §22.4): the same pack already |
| 49 | * cut into band-1 sideband frames of exactly 65515 data bytes. Real git |
| 50 | * always negotiates `side-band-64k`, and bytes that cross into JS to be |
| 51 | * framed measured a hard ~15 MiB/s ceiling regardless of how the framing |
| 52 | * was written; this twin lets the sideband case be a platform-to-platform |
| 53 | * pipe like the raw case. Written alongside the bundle, immutable, same |
| 54 | * lifecycle (purged with the prefix). |
| 55 | */ |
| 56 | export const bundleSidebandKey = (repoId: string, refsHash: string): string => |
| 57 | `${repoId}/bundles/bundle-${refsHash}.sideband`; |
| 58 | |
| 59 | /** |
| 60 | * R2 key for a streamed incoming push pack (v1.x upgrade seam, DESIGN.md |
| 61 | * §3.6): `{repoId}/incoming/{pushId}.pack`. Reserved — unused in v1's |
| 62 | * buffered ingest. |
| 63 | */ |
| 64 | export const incomingKey = (repoId: string, pushId: string): string => |
| 65 | `${repoId}/incoming/${pushId}.pack`; |
| 66 | |
| 67 | /** |
| 68 | * Pack-id prefix of a **promoted wire pack** (DESIGN §22.5): a push whose |
| 69 | * body spilled to blob storage already holds every non-delta blob in the |
| 70 | * compaction layout (`typeSize header + zdata`), so ingest points those |
| 71 | * rows straight into the incoming object instead of copying 40 MiB of |
| 72 | * blobs through SQLite and compacting them out again later. The pack id |
| 73 | * is `wire-<receiveId>`; the object stays at its `incomingKey`. |
| 74 | */ |
| 75 | export const WIRE_PACK_PREFIX = "wire-"; |
| 76 | export const wirePackId = (receiveId: string): string => |
| 77 | `${WIRE_PACK_PREFIX}${receiveId}`; |
| 78 | export const isWirePackId = (packId: string): boolean => |
| 79 | packId.startsWith(WIRE_PACK_PREFIX); |
| 80 | |
| 81 | /** R2 key of any pack by id — compacted (`packKey`) or promoted wire pack. */ |
| 82 | export const packKeyOf = (repoId: string, packId: string): string => |
| 83 | isWirePackId(packId) |
| 84 | ? incomingKey(repoId, packId.slice(WIRE_PACK_PREFIX.length)) |
| 85 | : packKey(repoId, packId); |
| 86 | |
| 87 | /** |
| 88 | * R2 key reserved for a future LFS object (DESIGN.md §10): |
| 89 | * `{repoId}/lfs/{sha256}`. |
| 90 | */ |
| 91 | export const lfsKey = (repoId: string, sha256: string): string => |
| 92 | `${repoId}/lfs/${sha256}`; |
| 93 | |
| 94 | /** |
| 95 | * The whole-repo R2 prefix (`{repoId}/`) — the unit the delete-purge alarm |
| 96 | * job lists and deletes (retained while the Registry reports |
| 97 | * `fork_count > 0`, since forks reference keys under it). |
| 98 | */ |
| 99 | export const repoPrefix = (repoId: string): string => `${repoId}/`; |
| 100 | |
| 101 | /** |
| 102 | * Prefix of all oversize loose objects for a repo: `{repoId}/objects/`. |
| 103 | */ |
| 104 | export const objectsPrefix = (repoId: string): string => `${repoId}/objects/`; |
| 105 | |
| 106 | /** |
| 107 | * Prefix of all compacted packs for a repo (v1.1): `{repoId}/packs/`. |
| 108 | */ |
| 109 | export const packsPrefix = (repoId: string): string => `${repoId}/packs/`; |
| 110 | |
| 111 | /** |
| 112 | * Prefix of all in-flight streamed push packs for a repo (reserved): |
| 113 | * `{repoId}/incoming/`. |
| 114 | */ |
| 115 | export const incomingPrefix = (repoId: string): string => `${repoId}/incoming/`; |
| 116 | |
| 117 | /** |
| 118 | * Prefix of all LFS objects for a repo (reserved): `{repoId}/lfs/`. |
| 119 | */ |
| 120 | export const lfsPrefix = (repoId: string): string => `${repoId}/lfs/`; |
| 121 | |
| 122 | /** |
| 123 | * R2 key of a repo's head snapshot (DESIGN.md §21): a small JSON object |
| 124 | * describing the current refs, visibility, and clone bundle, written by |
| 125 | * the Repo DO after every mutation commit. Lets the Worker serve |
| 126 | * anonymous public reads (advertisement + bundle clones) without waking |
| 127 | * the DO. Lives under the repo prefix so purge drains it. |
| 128 | */ |
| 129 | export const headKey = (repoId: string): string => `${repoId}/head`; |